cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

hid-roccat-savu.c (5118B)


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