brintos

brintos / linux-shallow public Read only

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