18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Roccat Pyra driver for Linux 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci/* 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci/* 128c2ecf20Sopenharmony_ci * Roccat Pyra is a mobile gamer mouse which comes in wired and wireless 138c2ecf20Sopenharmony_ci * variant. Wireless variant is not tested. 148c2ecf20Sopenharmony_ci * Userland tools can be found at http://sourceforge.net/projects/roccat 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/device.h> 188c2ecf20Sopenharmony_ci#include <linux/input.h> 198c2ecf20Sopenharmony_ci#include <linux/hid.h> 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/hid-roccat.h> 238c2ecf20Sopenharmony_ci#include "hid-ids.h" 248c2ecf20Sopenharmony_ci#include "hid-roccat-common.h" 258c2ecf20Sopenharmony_ci#include "hid-roccat-pyra.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic uint profile_numbers[5] = {0, 1, 2, 3, 4}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* pyra_class is used for creating sysfs attributes via roccat char device */ 308c2ecf20Sopenharmony_cistatic struct class *pyra_class; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic void profile_activated(struct pyra_device *pyra, 338c2ecf20Sopenharmony_ci unsigned int new_profile) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci if (new_profile >= ARRAY_SIZE(pyra->profile_settings)) 368c2ecf20Sopenharmony_ci return; 378c2ecf20Sopenharmony_ci pyra->actual_profile = new_profile; 388c2ecf20Sopenharmony_ci pyra->actual_cpi = pyra->profile_settings[pyra->actual_profile].y_cpi; 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic int pyra_send_control(struct usb_device *usb_dev, int value, 428c2ecf20Sopenharmony_ci enum pyra_control_requests request) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct roccat_common2_control control; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci if ((request == PYRA_CONTROL_REQUEST_PROFILE_SETTINGS || 478c2ecf20Sopenharmony_ci request == PYRA_CONTROL_REQUEST_PROFILE_BUTTONS) && 488c2ecf20Sopenharmony_ci (value < 0 || value > 4)) 498c2ecf20Sopenharmony_ci return -EINVAL; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci control.command = ROCCAT_COMMON_COMMAND_CONTROL; 528c2ecf20Sopenharmony_ci control.value = value; 538c2ecf20Sopenharmony_ci control.request = request; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci return roccat_common2_send(usb_dev, ROCCAT_COMMON_COMMAND_CONTROL, 568c2ecf20Sopenharmony_ci &control, sizeof(struct roccat_common2_control)); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic int pyra_get_profile_settings(struct usb_device *usb_dev, 608c2ecf20Sopenharmony_ci struct pyra_profile_settings *buf, int number) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci int retval; 638c2ecf20Sopenharmony_ci retval = pyra_send_control(usb_dev, number, 648c2ecf20Sopenharmony_ci PYRA_CONTROL_REQUEST_PROFILE_SETTINGS); 658c2ecf20Sopenharmony_ci if (retval) 668c2ecf20Sopenharmony_ci return retval; 678c2ecf20Sopenharmony_ci return roccat_common2_receive(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS, 688c2ecf20Sopenharmony_ci buf, PYRA_SIZE_PROFILE_SETTINGS); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic int pyra_get_settings(struct usb_device *usb_dev, 728c2ecf20Sopenharmony_ci struct pyra_settings *buf) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci return roccat_common2_receive(usb_dev, PYRA_COMMAND_SETTINGS, 758c2ecf20Sopenharmony_ci buf, PYRA_SIZE_SETTINGS); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic int pyra_set_settings(struct usb_device *usb_dev, 798c2ecf20Sopenharmony_ci struct pyra_settings const *settings) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci return roccat_common2_send_with_status(usb_dev, 828c2ecf20Sopenharmony_ci PYRA_COMMAND_SETTINGS, settings, 838c2ecf20Sopenharmony_ci PYRA_SIZE_SETTINGS); 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic ssize_t pyra_sysfs_read(struct file *fp, struct kobject *kobj, 878c2ecf20Sopenharmony_ci char *buf, loff_t off, size_t count, 888c2ecf20Sopenharmony_ci size_t real_size, uint command) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct device *dev = kobj_to_dev(kobj)->parent->parent; 918c2ecf20Sopenharmony_ci struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); 928c2ecf20Sopenharmony_ci struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 938c2ecf20Sopenharmony_ci int retval; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (off >= real_size) 968c2ecf20Sopenharmony_ci return 0; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (off != 0 || count != real_size) 998c2ecf20Sopenharmony_ci return -EINVAL; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci mutex_lock(&pyra->pyra_lock); 1028c2ecf20Sopenharmony_ci retval = roccat_common2_receive(usb_dev, command, buf, real_size); 1038c2ecf20Sopenharmony_ci mutex_unlock(&pyra->pyra_lock); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (retval) 1068c2ecf20Sopenharmony_ci return retval; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return real_size; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic ssize_t pyra_sysfs_write(struct file *fp, struct kobject *kobj, 1128c2ecf20Sopenharmony_ci void const *buf, loff_t off, size_t count, 1138c2ecf20Sopenharmony_ci size_t real_size, uint command) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct device *dev = kobj_to_dev(kobj)->parent->parent; 1168c2ecf20Sopenharmony_ci struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); 1178c2ecf20Sopenharmony_ci struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 1188c2ecf20Sopenharmony_ci int retval; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (off != 0 || count != real_size) 1218c2ecf20Sopenharmony_ci return -EINVAL; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci mutex_lock(&pyra->pyra_lock); 1248c2ecf20Sopenharmony_ci retval = roccat_common2_send_with_status(usb_dev, command, (void *)buf, real_size); 1258c2ecf20Sopenharmony_ci mutex_unlock(&pyra->pyra_lock); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (retval) 1288c2ecf20Sopenharmony_ci return retval; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci return real_size; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci#define PYRA_SYSFS_W(thingy, THINGY) \ 1348c2ecf20Sopenharmony_cistatic ssize_t pyra_sysfs_write_ ## thingy(struct file *fp, \ 1358c2ecf20Sopenharmony_ci struct kobject *kobj, struct bin_attribute *attr, char *buf, \ 1368c2ecf20Sopenharmony_ci loff_t off, size_t count) \ 1378c2ecf20Sopenharmony_ci{ \ 1388c2ecf20Sopenharmony_ci return pyra_sysfs_write(fp, kobj, buf, off, count, \ 1398c2ecf20Sopenharmony_ci PYRA_SIZE_ ## THINGY, PYRA_COMMAND_ ## THINGY); \ 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci#define PYRA_SYSFS_R(thingy, THINGY) \ 1438c2ecf20Sopenharmony_cistatic ssize_t pyra_sysfs_read_ ## thingy(struct file *fp, \ 1448c2ecf20Sopenharmony_ci struct kobject *kobj, struct bin_attribute *attr, char *buf, \ 1458c2ecf20Sopenharmony_ci loff_t off, size_t count) \ 1468c2ecf20Sopenharmony_ci{ \ 1478c2ecf20Sopenharmony_ci return pyra_sysfs_read(fp, kobj, buf, off, count, \ 1488c2ecf20Sopenharmony_ci PYRA_SIZE_ ## THINGY, PYRA_COMMAND_ ## THINGY); \ 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci#define PYRA_SYSFS_RW(thingy, THINGY) \ 1528c2ecf20Sopenharmony_ciPYRA_SYSFS_W(thingy, THINGY) \ 1538c2ecf20Sopenharmony_ciPYRA_SYSFS_R(thingy, THINGY) 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci#define PYRA_BIN_ATTRIBUTE_RW(thingy, THINGY) \ 1568c2ecf20Sopenharmony_ciPYRA_SYSFS_RW(thingy, THINGY); \ 1578c2ecf20Sopenharmony_cistatic struct bin_attribute bin_attr_##thingy = { \ 1588c2ecf20Sopenharmony_ci .attr = { .name = #thingy, .mode = 0660 }, \ 1598c2ecf20Sopenharmony_ci .size = PYRA_SIZE_ ## THINGY, \ 1608c2ecf20Sopenharmony_ci .read = pyra_sysfs_read_ ## thingy, \ 1618c2ecf20Sopenharmony_ci .write = pyra_sysfs_write_ ## thingy \ 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci#define PYRA_BIN_ATTRIBUTE_R(thingy, THINGY) \ 1658c2ecf20Sopenharmony_ciPYRA_SYSFS_R(thingy, THINGY); \ 1668c2ecf20Sopenharmony_cistatic struct bin_attribute bin_attr_##thingy = { \ 1678c2ecf20Sopenharmony_ci .attr = { .name = #thingy, .mode = 0440 }, \ 1688c2ecf20Sopenharmony_ci .size = PYRA_SIZE_ ## THINGY, \ 1698c2ecf20Sopenharmony_ci .read = pyra_sysfs_read_ ## thingy, \ 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci#define PYRA_BIN_ATTRIBUTE_W(thingy, THINGY) \ 1738c2ecf20Sopenharmony_ciPYRA_SYSFS_W(thingy, THINGY); \ 1748c2ecf20Sopenharmony_cistatic struct bin_attribute bin_attr_##thingy = { \ 1758c2ecf20Sopenharmony_ci .attr = { .name = #thingy, .mode = 0220 }, \ 1768c2ecf20Sopenharmony_ci .size = PYRA_SIZE_ ## THINGY, \ 1778c2ecf20Sopenharmony_ci .write = pyra_sysfs_write_ ## thingy \ 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ciPYRA_BIN_ATTRIBUTE_W(control, CONTROL); 1818c2ecf20Sopenharmony_ciPYRA_BIN_ATTRIBUTE_RW(info, INFO); 1828c2ecf20Sopenharmony_ciPYRA_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS); 1838c2ecf20Sopenharmony_ciPYRA_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic ssize_t pyra_sysfs_read_profilex_settings(struct file *fp, 1868c2ecf20Sopenharmony_ci struct kobject *kobj, struct bin_attribute *attr, char *buf, 1878c2ecf20Sopenharmony_ci loff_t off, size_t count) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct device *dev = kobj_to_dev(kobj)->parent->parent; 1908c2ecf20Sopenharmony_ci struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 1918c2ecf20Sopenharmony_ci ssize_t retval; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci retval = pyra_send_control(usb_dev, *(uint *)(attr->private), 1948c2ecf20Sopenharmony_ci PYRA_CONTROL_REQUEST_PROFILE_SETTINGS); 1958c2ecf20Sopenharmony_ci if (retval) 1968c2ecf20Sopenharmony_ci return retval; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci return pyra_sysfs_read(fp, kobj, buf, off, count, 1998c2ecf20Sopenharmony_ci PYRA_SIZE_PROFILE_SETTINGS, 2008c2ecf20Sopenharmony_ci PYRA_COMMAND_PROFILE_SETTINGS); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp, 2048c2ecf20Sopenharmony_ci struct kobject *kobj, struct bin_attribute *attr, char *buf, 2058c2ecf20Sopenharmony_ci loff_t off, size_t count) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct device *dev = kobj_to_dev(kobj)->parent->parent; 2088c2ecf20Sopenharmony_ci struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 2098c2ecf20Sopenharmony_ci ssize_t retval; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci retval = pyra_send_control(usb_dev, *(uint *)(attr->private), 2128c2ecf20Sopenharmony_ci PYRA_CONTROL_REQUEST_PROFILE_BUTTONS); 2138c2ecf20Sopenharmony_ci if (retval) 2148c2ecf20Sopenharmony_ci return retval; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return pyra_sysfs_read(fp, kobj, buf, off, count, 2178c2ecf20Sopenharmony_ci PYRA_SIZE_PROFILE_BUTTONS, 2188c2ecf20Sopenharmony_ci PYRA_COMMAND_PROFILE_BUTTONS); 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci#define PROFILE_ATTR(number) \ 2228c2ecf20Sopenharmony_cistatic struct bin_attribute bin_attr_profile##number##_settings = { \ 2238c2ecf20Sopenharmony_ci .attr = { .name = "profile" #number "_settings", .mode = 0440 }, \ 2248c2ecf20Sopenharmony_ci .size = PYRA_SIZE_PROFILE_SETTINGS, \ 2258c2ecf20Sopenharmony_ci .read = pyra_sysfs_read_profilex_settings, \ 2268c2ecf20Sopenharmony_ci .private = &profile_numbers[number-1], \ 2278c2ecf20Sopenharmony_ci}; \ 2288c2ecf20Sopenharmony_cistatic struct bin_attribute bin_attr_profile##number##_buttons = { \ 2298c2ecf20Sopenharmony_ci .attr = { .name = "profile" #number "_buttons", .mode = 0440 }, \ 2308c2ecf20Sopenharmony_ci .size = PYRA_SIZE_PROFILE_BUTTONS, \ 2318c2ecf20Sopenharmony_ci .read = pyra_sysfs_read_profilex_buttons, \ 2328c2ecf20Sopenharmony_ci .private = &profile_numbers[number-1], \ 2338c2ecf20Sopenharmony_ci}; 2348c2ecf20Sopenharmony_ciPROFILE_ATTR(1); 2358c2ecf20Sopenharmony_ciPROFILE_ATTR(2); 2368c2ecf20Sopenharmony_ciPROFILE_ATTR(3); 2378c2ecf20Sopenharmony_ciPROFILE_ATTR(4); 2388c2ecf20Sopenharmony_ciPROFILE_ATTR(5); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic ssize_t pyra_sysfs_write_settings(struct file *fp, 2418c2ecf20Sopenharmony_ci struct kobject *kobj, struct bin_attribute *attr, char *buf, 2428c2ecf20Sopenharmony_ci loff_t off, size_t count) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct device *dev = kobj_to_dev(kobj)->parent->parent; 2458c2ecf20Sopenharmony_ci struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); 2468c2ecf20Sopenharmony_ci struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 2478c2ecf20Sopenharmony_ci int retval = 0; 2488c2ecf20Sopenharmony_ci struct pyra_roccat_report roccat_report; 2498c2ecf20Sopenharmony_ci struct pyra_settings const *settings; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (off != 0 || count != PYRA_SIZE_SETTINGS) 2528c2ecf20Sopenharmony_ci return -EINVAL; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci settings = (struct pyra_settings const *)buf; 2558c2ecf20Sopenharmony_ci if (settings->startup_profile >= ARRAY_SIZE(pyra->profile_settings)) 2568c2ecf20Sopenharmony_ci return -EINVAL; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci mutex_lock(&pyra->pyra_lock); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci retval = pyra_set_settings(usb_dev, settings); 2618c2ecf20Sopenharmony_ci if (retval) { 2628c2ecf20Sopenharmony_ci mutex_unlock(&pyra->pyra_lock); 2638c2ecf20Sopenharmony_ci return retval; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci profile_activated(pyra, settings->startup_profile); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci roccat_report.type = PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2; 2698c2ecf20Sopenharmony_ci roccat_report.value = settings->startup_profile + 1; 2708c2ecf20Sopenharmony_ci roccat_report.key = 0; 2718c2ecf20Sopenharmony_ci roccat_report_event(pyra->chrdev_minor, 2728c2ecf20Sopenharmony_ci (uint8_t const *)&roccat_report); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci mutex_unlock(&pyra->pyra_lock); 2758c2ecf20Sopenharmony_ci return PYRA_SIZE_SETTINGS; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ciPYRA_SYSFS_R(settings, SETTINGS); 2798c2ecf20Sopenharmony_cistatic struct bin_attribute bin_attr_settings = 2808c2ecf20Sopenharmony_ci __BIN_ATTR(settings, (S_IWUSR | S_IRUGO), 2818c2ecf20Sopenharmony_ci pyra_sysfs_read_settings, pyra_sysfs_write_settings, 2828c2ecf20Sopenharmony_ci PYRA_SIZE_SETTINGS); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic ssize_t pyra_sysfs_show_actual_cpi(struct device *dev, 2858c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci struct pyra_device *pyra = 2888c2ecf20Sopenharmony_ci hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); 2898c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_cpi); 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_cistatic DEVICE_ATTR(actual_cpi, 0440, pyra_sysfs_show_actual_cpi, NULL); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic ssize_t pyra_sysfs_show_actual_profile(struct device *dev, 2948c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci struct pyra_device *pyra = 2978c2ecf20Sopenharmony_ci hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); 2988c2ecf20Sopenharmony_ci struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 2998c2ecf20Sopenharmony_ci struct pyra_settings settings; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci mutex_lock(&pyra->pyra_lock); 3028c2ecf20Sopenharmony_ci roccat_common2_receive(usb_dev, PYRA_COMMAND_SETTINGS, 3038c2ecf20Sopenharmony_ci &settings, PYRA_SIZE_SETTINGS); 3048c2ecf20Sopenharmony_ci mutex_unlock(&pyra->pyra_lock); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", settings.startup_profile); 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_cistatic DEVICE_ATTR(actual_profile, 0440, pyra_sysfs_show_actual_profile, NULL); 3098c2ecf20Sopenharmony_cistatic DEVICE_ATTR(startup_profile, 0440, pyra_sysfs_show_actual_profile, NULL); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic ssize_t pyra_sysfs_show_firmware_version(struct device *dev, 3128c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci struct pyra_device *pyra; 3158c2ecf20Sopenharmony_ci struct usb_device *usb_dev; 3168c2ecf20Sopenharmony_ci struct pyra_info info; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci dev = dev->parent->parent; 3198c2ecf20Sopenharmony_ci pyra = hid_get_drvdata(dev_get_drvdata(dev)); 3208c2ecf20Sopenharmony_ci usb_dev = interface_to_usbdev(to_usb_interface(dev)); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci mutex_lock(&pyra->pyra_lock); 3238c2ecf20Sopenharmony_ci roccat_common2_receive(usb_dev, PYRA_COMMAND_INFO, 3248c2ecf20Sopenharmony_ci &info, PYRA_SIZE_INFO); 3258c2ecf20Sopenharmony_ci mutex_unlock(&pyra->pyra_lock); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", info.firmware_version); 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_cistatic DEVICE_ATTR(firmware_version, 0440, pyra_sysfs_show_firmware_version, 3308c2ecf20Sopenharmony_ci NULL); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic struct attribute *pyra_attrs[] = { 3338c2ecf20Sopenharmony_ci &dev_attr_actual_cpi.attr, 3348c2ecf20Sopenharmony_ci &dev_attr_actual_profile.attr, 3358c2ecf20Sopenharmony_ci &dev_attr_firmware_version.attr, 3368c2ecf20Sopenharmony_ci &dev_attr_startup_profile.attr, 3378c2ecf20Sopenharmony_ci NULL, 3388c2ecf20Sopenharmony_ci}; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic struct bin_attribute *pyra_bin_attributes[] = { 3418c2ecf20Sopenharmony_ci &bin_attr_control, 3428c2ecf20Sopenharmony_ci &bin_attr_info, 3438c2ecf20Sopenharmony_ci &bin_attr_profile_settings, 3448c2ecf20Sopenharmony_ci &bin_attr_profile_buttons, 3458c2ecf20Sopenharmony_ci &bin_attr_settings, 3468c2ecf20Sopenharmony_ci &bin_attr_profile1_settings, 3478c2ecf20Sopenharmony_ci &bin_attr_profile2_settings, 3488c2ecf20Sopenharmony_ci &bin_attr_profile3_settings, 3498c2ecf20Sopenharmony_ci &bin_attr_profile4_settings, 3508c2ecf20Sopenharmony_ci &bin_attr_profile5_settings, 3518c2ecf20Sopenharmony_ci &bin_attr_profile1_buttons, 3528c2ecf20Sopenharmony_ci &bin_attr_profile2_buttons, 3538c2ecf20Sopenharmony_ci &bin_attr_profile3_buttons, 3548c2ecf20Sopenharmony_ci &bin_attr_profile4_buttons, 3558c2ecf20Sopenharmony_ci &bin_attr_profile5_buttons, 3568c2ecf20Sopenharmony_ci NULL, 3578c2ecf20Sopenharmony_ci}; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic const struct attribute_group pyra_group = { 3608c2ecf20Sopenharmony_ci .attrs = pyra_attrs, 3618c2ecf20Sopenharmony_ci .bin_attrs = pyra_bin_attributes, 3628c2ecf20Sopenharmony_ci}; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic const struct attribute_group *pyra_groups[] = { 3658c2ecf20Sopenharmony_ci &pyra_group, 3668c2ecf20Sopenharmony_ci NULL, 3678c2ecf20Sopenharmony_ci}; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic int pyra_init_pyra_device_struct(struct usb_device *usb_dev, 3708c2ecf20Sopenharmony_ci struct pyra_device *pyra) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct pyra_settings settings; 3738c2ecf20Sopenharmony_ci int retval, i; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci mutex_init(&pyra->pyra_lock); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci retval = pyra_get_settings(usb_dev, &settings); 3788c2ecf20Sopenharmony_ci if (retval) 3798c2ecf20Sopenharmony_ci return retval; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci for (i = 0; i < 5; ++i) { 3828c2ecf20Sopenharmony_ci retval = pyra_get_profile_settings(usb_dev, 3838c2ecf20Sopenharmony_ci &pyra->profile_settings[i], i); 3848c2ecf20Sopenharmony_ci if (retval) 3858c2ecf20Sopenharmony_ci return retval; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci profile_activated(pyra, settings.startup_profile); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci return 0; 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cistatic int pyra_init_specials(struct hid_device *hdev) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci struct usb_interface *intf = to_usb_interface(hdev->dev.parent); 3968c2ecf20Sopenharmony_ci struct usb_device *usb_dev = interface_to_usbdev(intf); 3978c2ecf20Sopenharmony_ci struct pyra_device *pyra; 3988c2ecf20Sopenharmony_ci int retval; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (intf->cur_altsetting->desc.bInterfaceProtocol 4018c2ecf20Sopenharmony_ci == USB_INTERFACE_PROTOCOL_MOUSE) { 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci pyra = kzalloc(sizeof(*pyra), GFP_KERNEL); 4048c2ecf20Sopenharmony_ci if (!pyra) { 4058c2ecf20Sopenharmony_ci hid_err(hdev, "can't alloc device descriptor\n"); 4068c2ecf20Sopenharmony_ci return -ENOMEM; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci hid_set_drvdata(hdev, pyra); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci retval = pyra_init_pyra_device_struct(usb_dev, pyra); 4118c2ecf20Sopenharmony_ci if (retval) { 4128c2ecf20Sopenharmony_ci hid_err(hdev, "couldn't init struct pyra_device\n"); 4138c2ecf20Sopenharmony_ci goto exit_free; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci retval = roccat_connect(pyra_class, hdev, 4178c2ecf20Sopenharmony_ci sizeof(struct pyra_roccat_report)); 4188c2ecf20Sopenharmony_ci if (retval < 0) { 4198c2ecf20Sopenharmony_ci hid_err(hdev, "couldn't init char dev\n"); 4208c2ecf20Sopenharmony_ci } else { 4218c2ecf20Sopenharmony_ci pyra->chrdev_minor = retval; 4228c2ecf20Sopenharmony_ci pyra->roccat_claimed = 1; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci } else { 4258c2ecf20Sopenharmony_ci hid_set_drvdata(hdev, NULL); 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci return 0; 4298c2ecf20Sopenharmony_ciexit_free: 4308c2ecf20Sopenharmony_ci kfree(pyra); 4318c2ecf20Sopenharmony_ci return retval; 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic void pyra_remove_specials(struct hid_device *hdev) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci struct usb_interface *intf = to_usb_interface(hdev->dev.parent); 4378c2ecf20Sopenharmony_ci struct pyra_device *pyra; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci if (intf->cur_altsetting->desc.bInterfaceProtocol 4408c2ecf20Sopenharmony_ci == USB_INTERFACE_PROTOCOL_MOUSE) { 4418c2ecf20Sopenharmony_ci pyra = hid_get_drvdata(hdev); 4428c2ecf20Sopenharmony_ci if (pyra->roccat_claimed) 4438c2ecf20Sopenharmony_ci roccat_disconnect(pyra->chrdev_minor); 4448c2ecf20Sopenharmony_ci kfree(hid_get_drvdata(hdev)); 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic int pyra_probe(struct hid_device *hdev, const struct hid_device_id *id) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci int retval; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (!hid_is_usb(hdev)) 4538c2ecf20Sopenharmony_ci return -EINVAL; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci retval = hid_parse(hdev); 4568c2ecf20Sopenharmony_ci if (retval) { 4578c2ecf20Sopenharmony_ci hid_err(hdev, "parse failed\n"); 4588c2ecf20Sopenharmony_ci goto exit; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 4628c2ecf20Sopenharmony_ci if (retval) { 4638c2ecf20Sopenharmony_ci hid_err(hdev, "hw start failed\n"); 4648c2ecf20Sopenharmony_ci goto exit; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci retval = pyra_init_specials(hdev); 4688c2ecf20Sopenharmony_ci if (retval) { 4698c2ecf20Sopenharmony_ci hid_err(hdev, "couldn't install mouse\n"); 4708c2ecf20Sopenharmony_ci goto exit_stop; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci return 0; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ciexit_stop: 4758c2ecf20Sopenharmony_ci hid_hw_stop(hdev); 4768c2ecf20Sopenharmony_ciexit: 4778c2ecf20Sopenharmony_ci return retval; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic void pyra_remove(struct hid_device *hdev) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci pyra_remove_specials(hdev); 4838c2ecf20Sopenharmony_ci hid_hw_stop(hdev); 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cistatic void pyra_keep_values_up_to_date(struct pyra_device *pyra, 4878c2ecf20Sopenharmony_ci u8 const *data) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci struct pyra_mouse_event_button const *button_event; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci switch (data[0]) { 4928c2ecf20Sopenharmony_ci case PYRA_MOUSE_REPORT_NUMBER_BUTTON: 4938c2ecf20Sopenharmony_ci button_event = (struct pyra_mouse_event_button const *)data; 4948c2ecf20Sopenharmony_ci switch (button_event->type) { 4958c2ecf20Sopenharmony_ci case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2: 4968c2ecf20Sopenharmony_ci profile_activated(pyra, button_event->data1 - 1); 4978c2ecf20Sopenharmony_ci break; 4988c2ecf20Sopenharmony_ci case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI: 4998c2ecf20Sopenharmony_ci pyra->actual_cpi = button_event->data1; 5008c2ecf20Sopenharmony_ci break; 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci break; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic void pyra_report_to_chrdev(struct pyra_device const *pyra, 5078c2ecf20Sopenharmony_ci u8 const *data) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct pyra_roccat_report roccat_report; 5108c2ecf20Sopenharmony_ci struct pyra_mouse_event_button const *button_event; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci if (data[0] != PYRA_MOUSE_REPORT_NUMBER_BUTTON) 5138c2ecf20Sopenharmony_ci return; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci button_event = (struct pyra_mouse_event_button const *)data; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci switch (button_event->type) { 5188c2ecf20Sopenharmony_ci case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2: 5198c2ecf20Sopenharmony_ci case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI: 5208c2ecf20Sopenharmony_ci roccat_report.type = button_event->type; 5218c2ecf20Sopenharmony_ci roccat_report.value = button_event->data1; 5228c2ecf20Sopenharmony_ci roccat_report.key = 0; 5238c2ecf20Sopenharmony_ci roccat_report_event(pyra->chrdev_minor, 5248c2ecf20Sopenharmony_ci (uint8_t const *)&roccat_report); 5258c2ecf20Sopenharmony_ci break; 5268c2ecf20Sopenharmony_ci case PYRA_MOUSE_EVENT_BUTTON_TYPE_MACRO: 5278c2ecf20Sopenharmony_ci case PYRA_MOUSE_EVENT_BUTTON_TYPE_SHORTCUT: 5288c2ecf20Sopenharmony_ci case PYRA_MOUSE_EVENT_BUTTON_TYPE_QUICKLAUNCH: 5298c2ecf20Sopenharmony_ci if (button_event->data2 == PYRA_MOUSE_EVENT_BUTTON_PRESS) { 5308c2ecf20Sopenharmony_ci roccat_report.type = button_event->type; 5318c2ecf20Sopenharmony_ci roccat_report.key = button_event->data1; 5328c2ecf20Sopenharmony_ci /* 5338c2ecf20Sopenharmony_ci * pyra reports profile numbers with range 1-5. 5348c2ecf20Sopenharmony_ci * Keeping this behaviour. 5358c2ecf20Sopenharmony_ci */ 5368c2ecf20Sopenharmony_ci roccat_report.value = pyra->actual_profile + 1; 5378c2ecf20Sopenharmony_ci roccat_report_event(pyra->chrdev_minor, 5388c2ecf20Sopenharmony_ci (uint8_t const *)&roccat_report); 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci break; 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cistatic int pyra_raw_event(struct hid_device *hdev, struct hid_report *report, 5458c2ecf20Sopenharmony_ci u8 *data, int size) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci struct usb_interface *intf = to_usb_interface(hdev->dev.parent); 5488c2ecf20Sopenharmony_ci struct pyra_device *pyra = hid_get_drvdata(hdev); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci if (intf->cur_altsetting->desc.bInterfaceProtocol 5518c2ecf20Sopenharmony_ci != USB_INTERFACE_PROTOCOL_MOUSE) 5528c2ecf20Sopenharmony_ci return 0; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci if (pyra == NULL) 5558c2ecf20Sopenharmony_ci return 0; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci pyra_keep_values_up_to_date(pyra, data); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (pyra->roccat_claimed) 5608c2ecf20Sopenharmony_ci pyra_report_to_chrdev(pyra, data); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci return 0; 5638c2ecf20Sopenharmony_ci} 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_cistatic const struct hid_device_id pyra_devices[] = { 5668c2ecf20Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, 5678c2ecf20Sopenharmony_ci USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, 5688c2ecf20Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, 5698c2ecf20Sopenharmony_ci USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) }, 5708c2ecf20Sopenharmony_ci { } 5718c2ecf20Sopenharmony_ci}; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(hid, pyra_devices); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic struct hid_driver pyra_driver = { 5768c2ecf20Sopenharmony_ci .name = "pyra", 5778c2ecf20Sopenharmony_ci .id_table = pyra_devices, 5788c2ecf20Sopenharmony_ci .probe = pyra_probe, 5798c2ecf20Sopenharmony_ci .remove = pyra_remove, 5808c2ecf20Sopenharmony_ci .raw_event = pyra_raw_event 5818c2ecf20Sopenharmony_ci}; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic int __init pyra_init(void) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci int retval; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci /* class name has to be same as driver name */ 5888c2ecf20Sopenharmony_ci pyra_class = class_create(THIS_MODULE, "pyra"); 5898c2ecf20Sopenharmony_ci if (IS_ERR(pyra_class)) 5908c2ecf20Sopenharmony_ci return PTR_ERR(pyra_class); 5918c2ecf20Sopenharmony_ci pyra_class->dev_groups = pyra_groups; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci retval = hid_register_driver(&pyra_driver); 5948c2ecf20Sopenharmony_ci if (retval) 5958c2ecf20Sopenharmony_ci class_destroy(pyra_class); 5968c2ecf20Sopenharmony_ci return retval; 5978c2ecf20Sopenharmony_ci} 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cistatic void __exit pyra_exit(void) 6008c2ecf20Sopenharmony_ci{ 6018c2ecf20Sopenharmony_ci hid_unregister_driver(&pyra_driver); 6028c2ecf20Sopenharmony_ci class_destroy(pyra_class); 6038c2ecf20Sopenharmony_ci} 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_cimodule_init(pyra_init); 6068c2ecf20Sopenharmony_cimodule_exit(pyra_exit); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ciMODULE_AUTHOR("Stefan Achatz"); 6098c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("USB Roccat Pyra driver"); 6108c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 611