brintos

brintos / linux-shallow public Read only

0
0
Text · 5.0 KiB · 2baa47a Raw
232 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Roccat Savu driver for Linux4 *5 * Copyright (c) 2012 Stefan Achatz <erazor_de@users.sourceforge.net>6 */7 8/*9 */10 11/* Roccat Savu is a gamer mouse with macro keys that can be configured in12 * 5 profiles.13 */14 15#include <linux/device.h>16#include <linux/input.h>17#include <linux/hid.h>18#include <linux/module.h>19#include <linux/slab.h>20#include <linux/hid-roccat.h>21#include "hid-ids.h"22#include "hid-roccat-common.h"23#include "hid-roccat-savu.h"24 25ROCCAT_COMMON2_BIN_ATTRIBUTE_W(control, 0x4, 0x03);26ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(profile, 0x5, 0x03);27ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(general, 0x6, 0x10);28ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(buttons, 0x7, 0x2f);29ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(macro, 0x8, 0x0823);30ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(info, 0x9, 0x08);31ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(sensor, 0xc, 0x04);32 33static struct bin_attribute *savu_bin_attrs[] = {34	&bin_attr_control,35	&bin_attr_profile,36	&bin_attr_general,37	&bin_attr_buttons,38	&bin_attr_macro,39	&bin_attr_info,40	&bin_attr_sensor,41	NULL,42};43 44static const struct attribute_group savu_group = {45	.bin_attrs = savu_bin_attrs,46};47 48static const struct attribute_group *savu_groups[] = {49	&savu_group,50	NULL,51};52 53static const struct class savu_class = {54	.name = "savu",55	.dev_groups = savu_groups,56};57 58static int savu_init_specials(struct hid_device *hdev)59{60	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);61	struct usb_device *usb_dev = interface_to_usbdev(intf);62	struct roccat_common2_device *savu;63	int retval;64 65	if (intf->cur_altsetting->desc.bInterfaceProtocol66			!= USB_INTERFACE_PROTOCOL_MOUSE) {67		hid_set_drvdata(hdev, NULL);68		return 0;69	}70 71	savu = kzalloc(sizeof(*savu), GFP_KERNEL);72	if (!savu) {73		hid_err(hdev, "can't alloc device descriptor\n");74		return -ENOMEM;75	}76	hid_set_drvdata(hdev, savu);77 78	retval = roccat_common2_device_init_struct(usb_dev, savu);79	if (retval) {80		hid_err(hdev, "couldn't init Savu device\n");81		goto exit_free;82	}83 84	retval = roccat_connect(&savu_class, hdev,85			sizeof(struct savu_roccat_report));86	if (retval < 0) {87		hid_err(hdev, "couldn't init char dev\n");88	} else {89		savu->chrdev_minor = retval;90		savu->roccat_claimed = 1;91	}92 93	return 0;94exit_free:95	kfree(savu);96	return retval;97}98 99static void savu_remove_specials(struct hid_device *hdev)100{101	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);102	struct roccat_common2_device *savu;103 104	if (intf->cur_altsetting->desc.bInterfaceProtocol105			!= USB_INTERFACE_PROTOCOL_MOUSE)106		return;107 108	savu = hid_get_drvdata(hdev);109	if (savu->roccat_claimed)110		roccat_disconnect(savu->chrdev_minor);111	kfree(savu);112}113 114static int savu_probe(struct hid_device *hdev,115		const struct hid_device_id *id)116{117	int retval;118 119	if (!hid_is_usb(hdev))120		return -EINVAL;121 122	retval = hid_parse(hdev);123	if (retval) {124		hid_err(hdev, "parse failed\n");125		goto exit;126	}127 128	retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);129	if (retval) {130		hid_err(hdev, "hw start failed\n");131		goto exit;132	}133 134	retval = savu_init_specials(hdev);135	if (retval) {136		hid_err(hdev, "couldn't install mouse\n");137		goto exit_stop;138	}139 140	return 0;141 142exit_stop:143	hid_hw_stop(hdev);144exit:145	return retval;146}147 148static void savu_remove(struct hid_device *hdev)149{150	savu_remove_specials(hdev);151	hid_hw_stop(hdev);152}153 154static void savu_report_to_chrdev(struct roccat_common2_device const *savu,155		u8 const *data)156{157	struct savu_roccat_report roccat_report;158	struct savu_mouse_report_special const *special_report;159 160	if (data[0] != SAVU_MOUSE_REPORT_NUMBER_SPECIAL)161		return;162 163	special_report = (struct savu_mouse_report_special const *)data;164 165	roccat_report.type = special_report->type;166	roccat_report.data[0] = special_report->data[0];167	roccat_report.data[1] = special_report->data[1];168	roccat_report_event(savu->chrdev_minor,169			(uint8_t const *)&roccat_report);170}171 172static int savu_raw_event(struct hid_device *hdev,173		struct hid_report *report, u8 *data, int size)174{175	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);176	struct roccat_common2_device *savu = hid_get_drvdata(hdev);177 178	if (intf->cur_altsetting->desc.bInterfaceProtocol179			!= USB_INTERFACE_PROTOCOL_MOUSE)180		return 0;181 182	if (savu == NULL)183		return 0;184 185	if (savu->roccat_claimed)186		savu_report_to_chrdev(savu, data);187 188	return 0;189}190 191static const struct hid_device_id savu_devices[] = {192	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) },193	{ }194};195 196MODULE_DEVICE_TABLE(hid, savu_devices);197 198static struct hid_driver savu_driver = {199		.name = "savu",200		.id_table = savu_devices,201		.probe = savu_probe,202		.remove = savu_remove,203		.raw_event = savu_raw_event204};205 206static int __init savu_init(void)207{208	int retval;209 210	retval = class_register(&savu_class);211	if (retval)212		return retval;213 214	retval = hid_register_driver(&savu_driver);215	if (retval)216		class_unregister(&savu_class);217	return retval;218}219 220static void __exit savu_exit(void)221{222	hid_unregister_driver(&savu_driver);223	class_unregister(&savu_class);224}225 226module_init(savu_init);227module_exit(savu_exit);228 229MODULE_AUTHOR("Stefan Achatz");230MODULE_DESCRIPTION("USB Roccat Savu driver");231MODULE_LICENSE("GPL v2");232