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-isku.c (11408B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Roccat Isku driver for Linux
      4 *
      5 * Copyright (c) 2011 Stefan Achatz <erazor_de@users.sourceforge.net>
      6 */
      7
      8/*
      9 */
     10
     11/*
     12 * Roccat Isku is a gamer keyboard with macro keys that can be configured in
     13 * 5 profiles.
     14 */
     15
     16#include <linux/device.h>
     17#include <linux/input.h>
     18#include <linux/hid.h>
     19#include <linux/module.h>
     20#include <linux/slab.h>
     21#include <linux/hid-roccat.h>
     22#include "hid-ids.h"
     23#include "hid-roccat-common.h"
     24#include "hid-roccat-isku.h"
     25
     26static struct class *isku_class;
     27
     28static void isku_profile_activated(struct isku_device *isku, uint new_profile)
     29{
     30	isku->actual_profile = new_profile;
     31}
     32
     33static int isku_receive(struct usb_device *usb_dev, uint command,
     34		void *buf, uint size)
     35{
     36	return roccat_common2_receive(usb_dev, command, buf, size);
     37}
     38
     39static int isku_get_actual_profile(struct usb_device *usb_dev)
     40{
     41	struct isku_actual_profile buf;
     42	int retval;
     43
     44	retval = isku_receive(usb_dev, ISKU_COMMAND_ACTUAL_PROFILE,
     45			&buf, sizeof(struct isku_actual_profile));
     46	return retval ? retval : buf.actual_profile;
     47}
     48
     49static int isku_set_actual_profile(struct usb_device *usb_dev, int new_profile)
     50{
     51	struct isku_actual_profile buf;
     52
     53	buf.command = ISKU_COMMAND_ACTUAL_PROFILE;
     54	buf.size = sizeof(struct isku_actual_profile);
     55	buf.actual_profile = new_profile;
     56	return roccat_common2_send_with_status(usb_dev,
     57			ISKU_COMMAND_ACTUAL_PROFILE, &buf,
     58			sizeof(struct isku_actual_profile));
     59}
     60
     61static ssize_t isku_sysfs_show_actual_profile(struct device *dev,
     62		struct device_attribute *attr, char *buf)
     63{
     64	struct isku_device *isku =
     65			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
     66	return snprintf(buf, PAGE_SIZE, "%d\n", isku->actual_profile);
     67}
     68
     69static ssize_t isku_sysfs_set_actual_profile(struct device *dev,
     70		struct device_attribute *attr, char const *buf, size_t size)
     71{
     72	struct isku_device *isku;
     73	struct usb_device *usb_dev;
     74	unsigned long profile;
     75	int retval;
     76	struct isku_roccat_report roccat_report;
     77
     78	dev = dev->parent->parent;
     79	isku = hid_get_drvdata(dev_get_drvdata(dev));
     80	usb_dev = interface_to_usbdev(to_usb_interface(dev));
     81
     82	retval = kstrtoul(buf, 10, &profile);
     83	if (retval)
     84		return retval;
     85
     86	if (profile > 4)
     87		return -EINVAL;
     88
     89	mutex_lock(&isku->isku_lock);
     90
     91	retval = isku_set_actual_profile(usb_dev, profile);
     92	if (retval) {
     93		mutex_unlock(&isku->isku_lock);
     94		return retval;
     95	}
     96
     97	isku_profile_activated(isku, profile);
     98
     99	roccat_report.event = ISKU_REPORT_BUTTON_EVENT_PROFILE;
    100	roccat_report.data1 = profile + 1;
    101	roccat_report.data2 = 0;
    102	roccat_report.profile = profile + 1;
    103	roccat_report_event(isku->chrdev_minor, (uint8_t const *)&roccat_report);
    104
    105	mutex_unlock(&isku->isku_lock);
    106
    107	return size;
    108}
    109static DEVICE_ATTR(actual_profile, 0660, isku_sysfs_show_actual_profile,
    110		   isku_sysfs_set_actual_profile);
    111
    112static struct attribute *isku_attrs[] = {
    113	&dev_attr_actual_profile.attr,
    114	NULL,
    115};
    116
    117static ssize_t isku_sysfs_read(struct file *fp, struct kobject *kobj,
    118		char *buf, loff_t off, size_t count,
    119		size_t real_size, uint command)
    120{
    121	struct device *dev = kobj_to_dev(kobj)->parent->parent;
    122	struct isku_device *isku = hid_get_drvdata(dev_get_drvdata(dev));
    123	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
    124	int retval;
    125
    126	if (off >= real_size)
    127		return 0;
    128
    129	if (off != 0 || count > real_size)
    130		return -EINVAL;
    131
    132	mutex_lock(&isku->isku_lock);
    133	retval = isku_receive(usb_dev, command, buf, count);
    134	mutex_unlock(&isku->isku_lock);
    135
    136	return retval ? retval : count;
    137}
    138
    139static ssize_t isku_sysfs_write(struct file *fp, struct kobject *kobj,
    140		void const *buf, loff_t off, size_t count,
    141		size_t real_size, uint command)
    142{
    143	struct device *dev = kobj_to_dev(kobj)->parent->parent;
    144	struct isku_device *isku = hid_get_drvdata(dev_get_drvdata(dev));
    145	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
    146	int retval;
    147
    148	if (off != 0 || count > real_size)
    149		return -EINVAL;
    150
    151	mutex_lock(&isku->isku_lock);
    152	retval = roccat_common2_send_with_status(usb_dev, command,
    153			(void *)buf, count);
    154	mutex_unlock(&isku->isku_lock);
    155
    156	return retval ? retval : count;
    157}
    158
    159#define ISKU_SYSFS_W(thingy, THINGY) \
    160static ssize_t isku_sysfs_write_ ## thingy(struct file *fp, struct kobject *kobj, \
    161		struct bin_attribute *attr, char *buf, \
    162		loff_t off, size_t count) \
    163{ \
    164	return isku_sysfs_write(fp, kobj, buf, off, count, \
    165			ISKU_SIZE_ ## THINGY, ISKU_COMMAND_ ## THINGY); \
    166}
    167
    168#define ISKU_SYSFS_R(thingy, THINGY) \
    169static ssize_t isku_sysfs_read_ ## thingy(struct file *fp, struct kobject *kobj, \
    170		struct bin_attribute *attr, char *buf, \
    171		loff_t off, size_t count) \
    172{ \
    173	return isku_sysfs_read(fp, kobj, buf, off, count, \
    174			ISKU_SIZE_ ## THINGY, ISKU_COMMAND_ ## THINGY); \
    175}
    176
    177#define ISKU_SYSFS_RW(thingy, THINGY) \
    178ISKU_SYSFS_R(thingy, THINGY) \
    179ISKU_SYSFS_W(thingy, THINGY)
    180
    181#define ISKU_BIN_ATTR_RW(thingy, THINGY) \
    182ISKU_SYSFS_RW(thingy, THINGY); \
    183static struct bin_attribute bin_attr_##thingy = { \
    184	.attr = { .name = #thingy, .mode = 0660 }, \
    185	.size = ISKU_SIZE_ ## THINGY, \
    186	.read = isku_sysfs_read_ ## thingy, \
    187	.write = isku_sysfs_write_ ## thingy \
    188}
    189
    190#define ISKU_BIN_ATTR_R(thingy, THINGY) \
    191ISKU_SYSFS_R(thingy, THINGY); \
    192static struct bin_attribute bin_attr_##thingy = { \
    193	.attr = { .name = #thingy, .mode = 0440 }, \
    194	.size = ISKU_SIZE_ ## THINGY, \
    195	.read = isku_sysfs_read_ ## thingy, \
    196}
    197
    198#define ISKU_BIN_ATTR_W(thingy, THINGY) \
    199ISKU_SYSFS_W(thingy, THINGY); \
    200static struct bin_attribute bin_attr_##thingy = { \
    201	.attr = { .name = #thingy, .mode = 0220 }, \
    202	.size = ISKU_SIZE_ ## THINGY, \
    203	.write = isku_sysfs_write_ ## thingy \
    204}
    205
    206ISKU_BIN_ATTR_RW(macro, MACRO);
    207ISKU_BIN_ATTR_RW(keys_function, KEYS_FUNCTION);
    208ISKU_BIN_ATTR_RW(keys_easyzone, KEYS_EASYZONE);
    209ISKU_BIN_ATTR_RW(keys_media, KEYS_MEDIA);
    210ISKU_BIN_ATTR_RW(keys_thumbster, KEYS_THUMBSTER);
    211ISKU_BIN_ATTR_RW(keys_macro, KEYS_MACRO);
    212ISKU_BIN_ATTR_RW(keys_capslock, KEYS_CAPSLOCK);
    213ISKU_BIN_ATTR_RW(light, LIGHT);
    214ISKU_BIN_ATTR_RW(key_mask, KEY_MASK);
    215ISKU_BIN_ATTR_RW(last_set, LAST_SET);
    216ISKU_BIN_ATTR_W(talk, TALK);
    217ISKU_BIN_ATTR_W(talkfx, TALKFX);
    218ISKU_BIN_ATTR_W(control, CONTROL);
    219ISKU_BIN_ATTR_W(reset, RESET);
    220ISKU_BIN_ATTR_R(info, INFO);
    221
    222static struct bin_attribute *isku_bin_attributes[] = {
    223	&bin_attr_macro,
    224	&bin_attr_keys_function,
    225	&bin_attr_keys_easyzone,
    226	&bin_attr_keys_media,
    227	&bin_attr_keys_thumbster,
    228	&bin_attr_keys_macro,
    229	&bin_attr_keys_capslock,
    230	&bin_attr_light,
    231	&bin_attr_key_mask,
    232	&bin_attr_last_set,
    233	&bin_attr_talk,
    234	&bin_attr_talkfx,
    235	&bin_attr_control,
    236	&bin_attr_reset,
    237	&bin_attr_info,
    238	NULL,
    239};
    240
    241static const struct attribute_group isku_group = {
    242	.attrs = isku_attrs,
    243	.bin_attrs = isku_bin_attributes,
    244};
    245
    246static const struct attribute_group *isku_groups[] = {
    247	&isku_group,
    248	NULL,
    249};
    250
    251static int isku_init_isku_device_struct(struct usb_device *usb_dev,
    252		struct isku_device *isku)
    253{
    254	int retval;
    255
    256	mutex_init(&isku->isku_lock);
    257
    258	retval = isku_get_actual_profile(usb_dev);
    259	if (retval < 0)
    260		return retval;
    261	isku_profile_activated(isku, retval);
    262
    263	return 0;
    264}
    265
    266static int isku_init_specials(struct hid_device *hdev)
    267{
    268	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
    269	struct usb_device *usb_dev = interface_to_usbdev(intf);
    270	struct isku_device *isku;
    271	int retval;
    272
    273	if (intf->cur_altsetting->desc.bInterfaceProtocol
    274			!= ISKU_USB_INTERFACE_PROTOCOL) {
    275		hid_set_drvdata(hdev, NULL);
    276		return 0;
    277	}
    278
    279	isku = kzalloc(sizeof(*isku), GFP_KERNEL);
    280	if (!isku) {
    281		hid_err(hdev, "can't alloc device descriptor\n");
    282		return -ENOMEM;
    283	}
    284	hid_set_drvdata(hdev, isku);
    285
    286	retval = isku_init_isku_device_struct(usb_dev, isku);
    287	if (retval) {
    288		hid_err(hdev, "couldn't init struct isku_device\n");
    289		goto exit_free;
    290	}
    291
    292	retval = roccat_connect(isku_class, hdev,
    293			sizeof(struct isku_roccat_report));
    294	if (retval < 0) {
    295		hid_err(hdev, "couldn't init char dev\n");
    296	} else {
    297		isku->chrdev_minor = retval;
    298		isku->roccat_claimed = 1;
    299	}
    300
    301	return 0;
    302exit_free:
    303	kfree(isku);
    304	return retval;
    305}
    306
    307static void isku_remove_specials(struct hid_device *hdev)
    308{
    309	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
    310	struct isku_device *isku;
    311
    312	if (intf->cur_altsetting->desc.bInterfaceProtocol
    313			!= ISKU_USB_INTERFACE_PROTOCOL)
    314		return;
    315
    316	isku = hid_get_drvdata(hdev);
    317	if (isku->roccat_claimed)
    318		roccat_disconnect(isku->chrdev_minor);
    319	kfree(isku);
    320}
    321
    322static int isku_probe(struct hid_device *hdev,
    323		const struct hid_device_id *id)
    324{
    325	int retval;
    326
    327	if (!hid_is_usb(hdev))
    328		return -EINVAL;
    329
    330	retval = hid_parse(hdev);
    331	if (retval) {
    332		hid_err(hdev, "parse failed\n");
    333		goto exit;
    334	}
    335
    336	retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
    337	if (retval) {
    338		hid_err(hdev, "hw start failed\n");
    339		goto exit;
    340	}
    341
    342	retval = isku_init_specials(hdev);
    343	if (retval) {
    344		hid_err(hdev, "couldn't install keyboard\n");
    345		goto exit_stop;
    346	}
    347
    348	return 0;
    349
    350exit_stop:
    351	hid_hw_stop(hdev);
    352exit:
    353	return retval;
    354}
    355
    356static void isku_remove(struct hid_device *hdev)
    357{
    358	isku_remove_specials(hdev);
    359	hid_hw_stop(hdev);
    360}
    361
    362static void isku_keep_values_up_to_date(struct isku_device *isku,
    363		u8 const *data)
    364{
    365	struct isku_report_button const *button_report;
    366
    367	switch (data[0]) {
    368	case ISKU_REPORT_NUMBER_BUTTON:
    369		button_report = (struct isku_report_button const *)data;
    370		switch (button_report->event) {
    371		case ISKU_REPORT_BUTTON_EVENT_PROFILE:
    372			isku_profile_activated(isku, button_report->data1 - 1);
    373			break;
    374		}
    375		break;
    376	}
    377}
    378
    379static void isku_report_to_chrdev(struct isku_device const *isku,
    380		u8 const *data)
    381{
    382	struct isku_roccat_report roccat_report;
    383	struct isku_report_button const *button_report;
    384
    385	if (data[0] != ISKU_REPORT_NUMBER_BUTTON)
    386		return;
    387
    388	button_report = (struct isku_report_button const *)data;
    389
    390	roccat_report.event = button_report->event;
    391	roccat_report.data1 = button_report->data1;
    392	roccat_report.data2 = button_report->data2;
    393	roccat_report.profile = isku->actual_profile + 1;
    394	roccat_report_event(isku->chrdev_minor,
    395			(uint8_t const *)&roccat_report);
    396}
    397
    398static int isku_raw_event(struct hid_device *hdev,
    399		struct hid_report *report, u8 *data, int size)
    400{
    401	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
    402	struct isku_device *isku = hid_get_drvdata(hdev);
    403
    404	if (intf->cur_altsetting->desc.bInterfaceProtocol
    405			!= ISKU_USB_INTERFACE_PROTOCOL)
    406		return 0;
    407
    408	if (isku == NULL)
    409		return 0;
    410
    411	isku_keep_values_up_to_date(isku, data);
    412
    413	if (isku->roccat_claimed)
    414		isku_report_to_chrdev(isku, data);
    415
    416	return 0;
    417}
    418
    419static const struct hid_device_id isku_devices[] = {
    420	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) },
    421	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKUFX) },
    422	{ }
    423};
    424
    425MODULE_DEVICE_TABLE(hid, isku_devices);
    426
    427static struct hid_driver isku_driver = {
    428		.name = "isku",
    429		.id_table = isku_devices,
    430		.probe = isku_probe,
    431		.remove = isku_remove,
    432		.raw_event = isku_raw_event
    433};
    434
    435static int __init isku_init(void)
    436{
    437	int retval;
    438	isku_class = class_create(THIS_MODULE, "isku");
    439	if (IS_ERR(isku_class))
    440		return PTR_ERR(isku_class);
    441	isku_class->dev_groups = isku_groups;
    442
    443	retval = hid_register_driver(&isku_driver);
    444	if (retval)
    445		class_destroy(isku_class);
    446	return retval;
    447}
    448
    449static void __exit isku_exit(void)
    450{
    451	hid_unregister_driver(&isku_driver);
    452	class_destroy(isku_class);
    453}
    454
    455module_init(isku_init);
    456module_exit(isku_exit);
    457
    458MODULE_AUTHOR("Stefan Achatz");
    459MODULE_DESCRIPTION("USB Roccat Isku/FX driver");
    460MODULE_LICENSE("GPL v2");