162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Roccat Pyra driver for Linux 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci/* 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci/* 1262306a36Sopenharmony_ci * Roccat Pyra is a mobile gamer mouse which comes in wired and wireless 1362306a36Sopenharmony_ci * variant. Wireless variant is not tested. 1462306a36Sopenharmony_ci * Userland tools can be found at http://sourceforge.net/projects/roccat 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/device.h> 1862306a36Sopenharmony_ci#include <linux/input.h> 1962306a36Sopenharmony_ci#include <linux/hid.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/slab.h> 2262306a36Sopenharmony_ci#include <linux/hid-roccat.h> 2362306a36Sopenharmony_ci#include "hid-ids.h" 2462306a36Sopenharmony_ci#include "hid-roccat-common.h" 2562306a36Sopenharmony_ci#include "hid-roccat-pyra.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic uint profile_numbers[5] = {0, 1, 2, 3, 4}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic void profile_activated(struct pyra_device *pyra, 3062306a36Sopenharmony_ci unsigned int new_profile) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci if (new_profile >= ARRAY_SIZE(pyra->profile_settings)) 3362306a36Sopenharmony_ci return; 3462306a36Sopenharmony_ci pyra->actual_profile = new_profile; 3562306a36Sopenharmony_ci pyra->actual_cpi = pyra->profile_settings[pyra->actual_profile].y_cpi; 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic int pyra_send_control(struct usb_device *usb_dev, int value, 3962306a36Sopenharmony_ci enum pyra_control_requests request) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci struct roccat_common2_control control; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if ((request == PYRA_CONTROL_REQUEST_PROFILE_SETTINGS || 4462306a36Sopenharmony_ci request == PYRA_CONTROL_REQUEST_PROFILE_BUTTONS) && 4562306a36Sopenharmony_ci (value < 0 || value > 4)) 4662306a36Sopenharmony_ci return -EINVAL; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci control.command = ROCCAT_COMMON_COMMAND_CONTROL; 4962306a36Sopenharmony_ci control.value = value; 5062306a36Sopenharmony_ci control.request = request; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci return roccat_common2_send(usb_dev, ROCCAT_COMMON_COMMAND_CONTROL, 5362306a36Sopenharmony_ci &control, sizeof(struct roccat_common2_control)); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic int pyra_get_profile_settings(struct usb_device *usb_dev, 5762306a36Sopenharmony_ci struct pyra_profile_settings *buf, int number) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci int retval; 6062306a36Sopenharmony_ci retval = pyra_send_control(usb_dev, number, 6162306a36Sopenharmony_ci PYRA_CONTROL_REQUEST_PROFILE_SETTINGS); 6262306a36Sopenharmony_ci if (retval) 6362306a36Sopenharmony_ci return retval; 6462306a36Sopenharmony_ci return roccat_common2_receive(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS, 6562306a36Sopenharmony_ci buf, PYRA_SIZE_PROFILE_SETTINGS); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic int pyra_get_settings(struct usb_device *usb_dev, 6962306a36Sopenharmony_ci struct pyra_settings *buf) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci return roccat_common2_receive(usb_dev, PYRA_COMMAND_SETTINGS, 7262306a36Sopenharmony_ci buf, PYRA_SIZE_SETTINGS); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int pyra_set_settings(struct usb_device *usb_dev, 7662306a36Sopenharmony_ci struct pyra_settings const *settings) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci return roccat_common2_send_with_status(usb_dev, 7962306a36Sopenharmony_ci PYRA_COMMAND_SETTINGS, settings, 8062306a36Sopenharmony_ci PYRA_SIZE_SETTINGS); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic ssize_t pyra_sysfs_read(struct file *fp, struct kobject *kobj, 8462306a36Sopenharmony_ci char *buf, loff_t off, size_t count, 8562306a36Sopenharmony_ci size_t real_size, uint command) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj)->parent->parent; 8862306a36Sopenharmony_ci struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); 8962306a36Sopenharmony_ci struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 9062306a36Sopenharmony_ci int retval; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (off >= real_size) 9362306a36Sopenharmony_ci return 0; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (off != 0 || count != real_size) 9662306a36Sopenharmony_ci return -EINVAL; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci mutex_lock(&pyra->pyra_lock); 9962306a36Sopenharmony_ci retval = roccat_common2_receive(usb_dev, command, buf, real_size); 10062306a36Sopenharmony_ci mutex_unlock(&pyra->pyra_lock); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (retval) 10362306a36Sopenharmony_ci return retval; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci return real_size; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic ssize_t pyra_sysfs_write(struct file *fp, struct kobject *kobj, 10962306a36Sopenharmony_ci void const *buf, loff_t off, size_t count, 11062306a36Sopenharmony_ci size_t real_size, uint command) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj)->parent->parent; 11362306a36Sopenharmony_ci struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); 11462306a36Sopenharmony_ci struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 11562306a36Sopenharmony_ci int retval; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (off != 0 || count != real_size) 11862306a36Sopenharmony_ci return -EINVAL; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci mutex_lock(&pyra->pyra_lock); 12162306a36Sopenharmony_ci retval = roccat_common2_send_with_status(usb_dev, command, (void *)buf, real_size); 12262306a36Sopenharmony_ci mutex_unlock(&pyra->pyra_lock); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (retval) 12562306a36Sopenharmony_ci return retval; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci return real_size; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci#define PYRA_SYSFS_W(thingy, THINGY) \ 13162306a36Sopenharmony_cistatic ssize_t pyra_sysfs_write_ ## thingy(struct file *fp, \ 13262306a36Sopenharmony_ci struct kobject *kobj, struct bin_attribute *attr, char *buf, \ 13362306a36Sopenharmony_ci loff_t off, size_t count) \ 13462306a36Sopenharmony_ci{ \ 13562306a36Sopenharmony_ci return pyra_sysfs_write(fp, kobj, buf, off, count, \ 13662306a36Sopenharmony_ci PYRA_SIZE_ ## THINGY, PYRA_COMMAND_ ## THINGY); \ 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci#define PYRA_SYSFS_R(thingy, THINGY) \ 14062306a36Sopenharmony_cistatic ssize_t pyra_sysfs_read_ ## thingy(struct file *fp, \ 14162306a36Sopenharmony_ci struct kobject *kobj, struct bin_attribute *attr, char *buf, \ 14262306a36Sopenharmony_ci loff_t off, size_t count) \ 14362306a36Sopenharmony_ci{ \ 14462306a36Sopenharmony_ci return pyra_sysfs_read(fp, kobj, buf, off, count, \ 14562306a36Sopenharmony_ci PYRA_SIZE_ ## THINGY, PYRA_COMMAND_ ## THINGY); \ 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci#define PYRA_SYSFS_RW(thingy, THINGY) \ 14962306a36Sopenharmony_ciPYRA_SYSFS_W(thingy, THINGY) \ 15062306a36Sopenharmony_ciPYRA_SYSFS_R(thingy, THINGY) 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci#define PYRA_BIN_ATTRIBUTE_RW(thingy, THINGY) \ 15362306a36Sopenharmony_ciPYRA_SYSFS_RW(thingy, THINGY); \ 15462306a36Sopenharmony_cistatic struct bin_attribute bin_attr_##thingy = { \ 15562306a36Sopenharmony_ci .attr = { .name = #thingy, .mode = 0660 }, \ 15662306a36Sopenharmony_ci .size = PYRA_SIZE_ ## THINGY, \ 15762306a36Sopenharmony_ci .read = pyra_sysfs_read_ ## thingy, \ 15862306a36Sopenharmony_ci .write = pyra_sysfs_write_ ## thingy \ 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci#define PYRA_BIN_ATTRIBUTE_R(thingy, THINGY) \ 16262306a36Sopenharmony_ciPYRA_SYSFS_R(thingy, THINGY); \ 16362306a36Sopenharmony_cistatic struct bin_attribute bin_attr_##thingy = { \ 16462306a36Sopenharmony_ci .attr = { .name = #thingy, .mode = 0440 }, \ 16562306a36Sopenharmony_ci .size = PYRA_SIZE_ ## THINGY, \ 16662306a36Sopenharmony_ci .read = pyra_sysfs_read_ ## thingy, \ 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci#define PYRA_BIN_ATTRIBUTE_W(thingy, THINGY) \ 17062306a36Sopenharmony_ciPYRA_SYSFS_W(thingy, THINGY); \ 17162306a36Sopenharmony_cistatic struct bin_attribute bin_attr_##thingy = { \ 17262306a36Sopenharmony_ci .attr = { .name = #thingy, .mode = 0220 }, \ 17362306a36Sopenharmony_ci .size = PYRA_SIZE_ ## THINGY, \ 17462306a36Sopenharmony_ci .write = pyra_sysfs_write_ ## thingy \ 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ciPYRA_BIN_ATTRIBUTE_W(control, CONTROL); 17862306a36Sopenharmony_ciPYRA_BIN_ATTRIBUTE_RW(info, INFO); 17962306a36Sopenharmony_ciPYRA_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS); 18062306a36Sopenharmony_ciPYRA_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic ssize_t pyra_sysfs_read_profilex_settings(struct file *fp, 18362306a36Sopenharmony_ci struct kobject *kobj, struct bin_attribute *attr, char *buf, 18462306a36Sopenharmony_ci loff_t off, size_t count) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj)->parent->parent; 18762306a36Sopenharmony_ci struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 18862306a36Sopenharmony_ci ssize_t retval; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci retval = pyra_send_control(usb_dev, *(uint *)(attr->private), 19162306a36Sopenharmony_ci PYRA_CONTROL_REQUEST_PROFILE_SETTINGS); 19262306a36Sopenharmony_ci if (retval) 19362306a36Sopenharmony_ci return retval; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci return pyra_sysfs_read(fp, kobj, buf, off, count, 19662306a36Sopenharmony_ci PYRA_SIZE_PROFILE_SETTINGS, 19762306a36Sopenharmony_ci PYRA_COMMAND_PROFILE_SETTINGS); 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp, 20162306a36Sopenharmony_ci struct kobject *kobj, struct bin_attribute *attr, char *buf, 20262306a36Sopenharmony_ci loff_t off, size_t count) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj)->parent->parent; 20562306a36Sopenharmony_ci struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 20662306a36Sopenharmony_ci ssize_t retval; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci retval = pyra_send_control(usb_dev, *(uint *)(attr->private), 20962306a36Sopenharmony_ci PYRA_CONTROL_REQUEST_PROFILE_BUTTONS); 21062306a36Sopenharmony_ci if (retval) 21162306a36Sopenharmony_ci return retval; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci return pyra_sysfs_read(fp, kobj, buf, off, count, 21462306a36Sopenharmony_ci PYRA_SIZE_PROFILE_BUTTONS, 21562306a36Sopenharmony_ci PYRA_COMMAND_PROFILE_BUTTONS); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci#define PROFILE_ATTR(number) \ 21962306a36Sopenharmony_cistatic struct bin_attribute bin_attr_profile##number##_settings = { \ 22062306a36Sopenharmony_ci .attr = { .name = "profile" #number "_settings", .mode = 0440 }, \ 22162306a36Sopenharmony_ci .size = PYRA_SIZE_PROFILE_SETTINGS, \ 22262306a36Sopenharmony_ci .read = pyra_sysfs_read_profilex_settings, \ 22362306a36Sopenharmony_ci .private = &profile_numbers[number-1], \ 22462306a36Sopenharmony_ci}; \ 22562306a36Sopenharmony_cistatic struct bin_attribute bin_attr_profile##number##_buttons = { \ 22662306a36Sopenharmony_ci .attr = { .name = "profile" #number "_buttons", .mode = 0440 }, \ 22762306a36Sopenharmony_ci .size = PYRA_SIZE_PROFILE_BUTTONS, \ 22862306a36Sopenharmony_ci .read = pyra_sysfs_read_profilex_buttons, \ 22962306a36Sopenharmony_ci .private = &profile_numbers[number-1], \ 23062306a36Sopenharmony_ci}; 23162306a36Sopenharmony_ciPROFILE_ATTR(1); 23262306a36Sopenharmony_ciPROFILE_ATTR(2); 23362306a36Sopenharmony_ciPROFILE_ATTR(3); 23462306a36Sopenharmony_ciPROFILE_ATTR(4); 23562306a36Sopenharmony_ciPROFILE_ATTR(5); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic ssize_t pyra_sysfs_write_settings(struct file *fp, 23862306a36Sopenharmony_ci struct kobject *kobj, struct bin_attribute *attr, char *buf, 23962306a36Sopenharmony_ci loff_t off, size_t count) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj)->parent->parent; 24262306a36Sopenharmony_ci struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); 24362306a36Sopenharmony_ci struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 24462306a36Sopenharmony_ci int retval = 0; 24562306a36Sopenharmony_ci struct pyra_roccat_report roccat_report; 24662306a36Sopenharmony_ci struct pyra_settings const *settings; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (off != 0 || count != PYRA_SIZE_SETTINGS) 24962306a36Sopenharmony_ci return -EINVAL; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci settings = (struct pyra_settings const *)buf; 25262306a36Sopenharmony_ci if (settings->startup_profile >= ARRAY_SIZE(pyra->profile_settings)) 25362306a36Sopenharmony_ci return -EINVAL; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci mutex_lock(&pyra->pyra_lock); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci retval = pyra_set_settings(usb_dev, settings); 25862306a36Sopenharmony_ci if (retval) { 25962306a36Sopenharmony_ci mutex_unlock(&pyra->pyra_lock); 26062306a36Sopenharmony_ci return retval; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci profile_activated(pyra, settings->startup_profile); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci roccat_report.type = PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2; 26662306a36Sopenharmony_ci roccat_report.value = settings->startup_profile + 1; 26762306a36Sopenharmony_ci roccat_report.key = 0; 26862306a36Sopenharmony_ci roccat_report_event(pyra->chrdev_minor, 26962306a36Sopenharmony_ci (uint8_t const *)&roccat_report); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci mutex_unlock(&pyra->pyra_lock); 27262306a36Sopenharmony_ci return PYRA_SIZE_SETTINGS; 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ciPYRA_SYSFS_R(settings, SETTINGS); 27662306a36Sopenharmony_cistatic struct bin_attribute bin_attr_settings = 27762306a36Sopenharmony_ci __BIN_ATTR(settings, (S_IWUSR | S_IRUGO), 27862306a36Sopenharmony_ci pyra_sysfs_read_settings, pyra_sysfs_write_settings, 27962306a36Sopenharmony_ci PYRA_SIZE_SETTINGS); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic ssize_t pyra_sysfs_show_actual_cpi(struct device *dev, 28262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci struct pyra_device *pyra = 28562306a36Sopenharmony_ci hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); 28662306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_cpi); 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_cistatic DEVICE_ATTR(actual_cpi, 0440, pyra_sysfs_show_actual_cpi, NULL); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic ssize_t pyra_sysfs_show_actual_profile(struct device *dev, 29162306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci struct pyra_device *pyra = 29462306a36Sopenharmony_ci hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); 29562306a36Sopenharmony_ci struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 29662306a36Sopenharmony_ci struct pyra_settings settings; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci mutex_lock(&pyra->pyra_lock); 29962306a36Sopenharmony_ci roccat_common2_receive(usb_dev, PYRA_COMMAND_SETTINGS, 30062306a36Sopenharmony_ci &settings, PYRA_SIZE_SETTINGS); 30162306a36Sopenharmony_ci mutex_unlock(&pyra->pyra_lock); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", settings.startup_profile); 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_cistatic DEVICE_ATTR(actual_profile, 0440, pyra_sysfs_show_actual_profile, NULL); 30662306a36Sopenharmony_cistatic DEVICE_ATTR(startup_profile, 0440, pyra_sysfs_show_actual_profile, NULL); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic ssize_t pyra_sysfs_show_firmware_version(struct device *dev, 30962306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct pyra_device *pyra; 31262306a36Sopenharmony_ci struct usb_device *usb_dev; 31362306a36Sopenharmony_ci struct pyra_info info; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci dev = dev->parent->parent; 31662306a36Sopenharmony_ci pyra = hid_get_drvdata(dev_get_drvdata(dev)); 31762306a36Sopenharmony_ci usb_dev = interface_to_usbdev(to_usb_interface(dev)); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci mutex_lock(&pyra->pyra_lock); 32062306a36Sopenharmony_ci roccat_common2_receive(usb_dev, PYRA_COMMAND_INFO, 32162306a36Sopenharmony_ci &info, PYRA_SIZE_INFO); 32262306a36Sopenharmony_ci mutex_unlock(&pyra->pyra_lock); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", info.firmware_version); 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_cistatic DEVICE_ATTR(firmware_version, 0440, pyra_sysfs_show_firmware_version, 32762306a36Sopenharmony_ci NULL); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic struct attribute *pyra_attrs[] = { 33062306a36Sopenharmony_ci &dev_attr_actual_cpi.attr, 33162306a36Sopenharmony_ci &dev_attr_actual_profile.attr, 33262306a36Sopenharmony_ci &dev_attr_firmware_version.attr, 33362306a36Sopenharmony_ci &dev_attr_startup_profile.attr, 33462306a36Sopenharmony_ci NULL, 33562306a36Sopenharmony_ci}; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic struct bin_attribute *pyra_bin_attributes[] = { 33862306a36Sopenharmony_ci &bin_attr_control, 33962306a36Sopenharmony_ci &bin_attr_info, 34062306a36Sopenharmony_ci &bin_attr_profile_settings, 34162306a36Sopenharmony_ci &bin_attr_profile_buttons, 34262306a36Sopenharmony_ci &bin_attr_settings, 34362306a36Sopenharmony_ci &bin_attr_profile1_settings, 34462306a36Sopenharmony_ci &bin_attr_profile2_settings, 34562306a36Sopenharmony_ci &bin_attr_profile3_settings, 34662306a36Sopenharmony_ci &bin_attr_profile4_settings, 34762306a36Sopenharmony_ci &bin_attr_profile5_settings, 34862306a36Sopenharmony_ci &bin_attr_profile1_buttons, 34962306a36Sopenharmony_ci &bin_attr_profile2_buttons, 35062306a36Sopenharmony_ci &bin_attr_profile3_buttons, 35162306a36Sopenharmony_ci &bin_attr_profile4_buttons, 35262306a36Sopenharmony_ci &bin_attr_profile5_buttons, 35362306a36Sopenharmony_ci NULL, 35462306a36Sopenharmony_ci}; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic const struct attribute_group pyra_group = { 35762306a36Sopenharmony_ci .attrs = pyra_attrs, 35862306a36Sopenharmony_ci .bin_attrs = pyra_bin_attributes, 35962306a36Sopenharmony_ci}; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic const struct attribute_group *pyra_groups[] = { 36262306a36Sopenharmony_ci &pyra_group, 36362306a36Sopenharmony_ci NULL, 36462306a36Sopenharmony_ci}; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci/* pyra_class is used for creating sysfs attributes via roccat char device */ 36762306a36Sopenharmony_cistatic const struct class pyra_class = { 36862306a36Sopenharmony_ci .name = "pyra", 36962306a36Sopenharmony_ci .dev_groups = pyra_groups, 37062306a36Sopenharmony_ci}; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic int pyra_init_pyra_device_struct(struct usb_device *usb_dev, 37362306a36Sopenharmony_ci struct pyra_device *pyra) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci struct pyra_settings settings; 37662306a36Sopenharmony_ci int retval, i; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci mutex_init(&pyra->pyra_lock); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci retval = pyra_get_settings(usb_dev, &settings); 38162306a36Sopenharmony_ci if (retval) 38262306a36Sopenharmony_ci return retval; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci for (i = 0; i < 5; ++i) { 38562306a36Sopenharmony_ci retval = pyra_get_profile_settings(usb_dev, 38662306a36Sopenharmony_ci &pyra->profile_settings[i], i); 38762306a36Sopenharmony_ci if (retval) 38862306a36Sopenharmony_ci return retval; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci profile_activated(pyra, settings.startup_profile); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci return 0; 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic int pyra_init_specials(struct hid_device *hdev) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci struct usb_interface *intf = to_usb_interface(hdev->dev.parent); 39962306a36Sopenharmony_ci struct usb_device *usb_dev = interface_to_usbdev(intf); 40062306a36Sopenharmony_ci struct pyra_device *pyra; 40162306a36Sopenharmony_ci int retval; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (intf->cur_altsetting->desc.bInterfaceProtocol 40462306a36Sopenharmony_ci == USB_INTERFACE_PROTOCOL_MOUSE) { 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci pyra = kzalloc(sizeof(*pyra), GFP_KERNEL); 40762306a36Sopenharmony_ci if (!pyra) { 40862306a36Sopenharmony_ci hid_err(hdev, "can't alloc device descriptor\n"); 40962306a36Sopenharmony_ci return -ENOMEM; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci hid_set_drvdata(hdev, pyra); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci retval = pyra_init_pyra_device_struct(usb_dev, pyra); 41462306a36Sopenharmony_ci if (retval) { 41562306a36Sopenharmony_ci hid_err(hdev, "couldn't init struct pyra_device\n"); 41662306a36Sopenharmony_ci goto exit_free; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci retval = roccat_connect(&pyra_class, hdev, 42062306a36Sopenharmony_ci sizeof(struct pyra_roccat_report)); 42162306a36Sopenharmony_ci if (retval < 0) { 42262306a36Sopenharmony_ci hid_err(hdev, "couldn't init char dev\n"); 42362306a36Sopenharmony_ci } else { 42462306a36Sopenharmony_ci pyra->chrdev_minor = retval; 42562306a36Sopenharmony_ci pyra->roccat_claimed = 1; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci } else { 42862306a36Sopenharmony_ci hid_set_drvdata(hdev, NULL); 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci return 0; 43262306a36Sopenharmony_ciexit_free: 43362306a36Sopenharmony_ci kfree(pyra); 43462306a36Sopenharmony_ci return retval; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic void pyra_remove_specials(struct hid_device *hdev) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci struct usb_interface *intf = to_usb_interface(hdev->dev.parent); 44062306a36Sopenharmony_ci struct pyra_device *pyra; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (intf->cur_altsetting->desc.bInterfaceProtocol 44362306a36Sopenharmony_ci == USB_INTERFACE_PROTOCOL_MOUSE) { 44462306a36Sopenharmony_ci pyra = hid_get_drvdata(hdev); 44562306a36Sopenharmony_ci if (pyra->roccat_claimed) 44662306a36Sopenharmony_ci roccat_disconnect(pyra->chrdev_minor); 44762306a36Sopenharmony_ci kfree(hid_get_drvdata(hdev)); 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic int pyra_probe(struct hid_device *hdev, const struct hid_device_id *id) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci int retval; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (!hid_is_usb(hdev)) 45662306a36Sopenharmony_ci return -EINVAL; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci retval = hid_parse(hdev); 45962306a36Sopenharmony_ci if (retval) { 46062306a36Sopenharmony_ci hid_err(hdev, "parse failed\n"); 46162306a36Sopenharmony_ci goto exit; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 46562306a36Sopenharmony_ci if (retval) { 46662306a36Sopenharmony_ci hid_err(hdev, "hw start failed\n"); 46762306a36Sopenharmony_ci goto exit; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci retval = pyra_init_specials(hdev); 47162306a36Sopenharmony_ci if (retval) { 47262306a36Sopenharmony_ci hid_err(hdev, "couldn't install mouse\n"); 47362306a36Sopenharmony_ci goto exit_stop; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci return 0; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ciexit_stop: 47862306a36Sopenharmony_ci hid_hw_stop(hdev); 47962306a36Sopenharmony_ciexit: 48062306a36Sopenharmony_ci return retval; 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic void pyra_remove(struct hid_device *hdev) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci pyra_remove_specials(hdev); 48662306a36Sopenharmony_ci hid_hw_stop(hdev); 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic void pyra_keep_values_up_to_date(struct pyra_device *pyra, 49062306a36Sopenharmony_ci u8 const *data) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci struct pyra_mouse_event_button const *button_event; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci switch (data[0]) { 49562306a36Sopenharmony_ci case PYRA_MOUSE_REPORT_NUMBER_BUTTON: 49662306a36Sopenharmony_ci button_event = (struct pyra_mouse_event_button const *)data; 49762306a36Sopenharmony_ci switch (button_event->type) { 49862306a36Sopenharmony_ci case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2: 49962306a36Sopenharmony_ci profile_activated(pyra, button_event->data1 - 1); 50062306a36Sopenharmony_ci break; 50162306a36Sopenharmony_ci case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI: 50262306a36Sopenharmony_ci pyra->actual_cpi = button_event->data1; 50362306a36Sopenharmony_ci break; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci break; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic void pyra_report_to_chrdev(struct pyra_device const *pyra, 51062306a36Sopenharmony_ci u8 const *data) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci struct pyra_roccat_report roccat_report; 51362306a36Sopenharmony_ci struct pyra_mouse_event_button const *button_event; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (data[0] != PYRA_MOUSE_REPORT_NUMBER_BUTTON) 51662306a36Sopenharmony_ci return; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci button_event = (struct pyra_mouse_event_button const *)data; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci switch (button_event->type) { 52162306a36Sopenharmony_ci case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2: 52262306a36Sopenharmony_ci case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI: 52362306a36Sopenharmony_ci roccat_report.type = button_event->type; 52462306a36Sopenharmony_ci roccat_report.value = button_event->data1; 52562306a36Sopenharmony_ci roccat_report.key = 0; 52662306a36Sopenharmony_ci roccat_report_event(pyra->chrdev_minor, 52762306a36Sopenharmony_ci (uint8_t const *)&roccat_report); 52862306a36Sopenharmony_ci break; 52962306a36Sopenharmony_ci case PYRA_MOUSE_EVENT_BUTTON_TYPE_MACRO: 53062306a36Sopenharmony_ci case PYRA_MOUSE_EVENT_BUTTON_TYPE_SHORTCUT: 53162306a36Sopenharmony_ci case PYRA_MOUSE_EVENT_BUTTON_TYPE_QUICKLAUNCH: 53262306a36Sopenharmony_ci if (button_event->data2 == PYRA_MOUSE_EVENT_BUTTON_PRESS) { 53362306a36Sopenharmony_ci roccat_report.type = button_event->type; 53462306a36Sopenharmony_ci roccat_report.key = button_event->data1; 53562306a36Sopenharmony_ci /* 53662306a36Sopenharmony_ci * pyra reports profile numbers with range 1-5. 53762306a36Sopenharmony_ci * Keeping this behaviour. 53862306a36Sopenharmony_ci */ 53962306a36Sopenharmony_ci roccat_report.value = pyra->actual_profile + 1; 54062306a36Sopenharmony_ci roccat_report_event(pyra->chrdev_minor, 54162306a36Sopenharmony_ci (uint8_t const *)&roccat_report); 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci break; 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic int pyra_raw_event(struct hid_device *hdev, struct hid_report *report, 54862306a36Sopenharmony_ci u8 *data, int size) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci struct usb_interface *intf = to_usb_interface(hdev->dev.parent); 55162306a36Sopenharmony_ci struct pyra_device *pyra = hid_get_drvdata(hdev); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (intf->cur_altsetting->desc.bInterfaceProtocol 55462306a36Sopenharmony_ci != USB_INTERFACE_PROTOCOL_MOUSE) 55562306a36Sopenharmony_ci return 0; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci if (pyra == NULL) 55862306a36Sopenharmony_ci return 0; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci pyra_keep_values_up_to_date(pyra, data); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (pyra->roccat_claimed) 56362306a36Sopenharmony_ci pyra_report_to_chrdev(pyra, data); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci return 0; 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic const struct hid_device_id pyra_devices[] = { 56962306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, 57062306a36Sopenharmony_ci USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, 57162306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, 57262306a36Sopenharmony_ci USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) }, 57362306a36Sopenharmony_ci { } 57462306a36Sopenharmony_ci}; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(hid, pyra_devices); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistatic struct hid_driver pyra_driver = { 57962306a36Sopenharmony_ci .name = "pyra", 58062306a36Sopenharmony_ci .id_table = pyra_devices, 58162306a36Sopenharmony_ci .probe = pyra_probe, 58262306a36Sopenharmony_ci .remove = pyra_remove, 58362306a36Sopenharmony_ci .raw_event = pyra_raw_event 58462306a36Sopenharmony_ci}; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_cistatic int __init pyra_init(void) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci int retval; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci /* class name has to be same as driver name */ 59162306a36Sopenharmony_ci retval = class_register(&pyra_class); 59262306a36Sopenharmony_ci if (retval) 59362306a36Sopenharmony_ci return retval; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci retval = hid_register_driver(&pyra_driver); 59662306a36Sopenharmony_ci if (retval) 59762306a36Sopenharmony_ci class_unregister(&pyra_class); 59862306a36Sopenharmony_ci return retval; 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic void __exit pyra_exit(void) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci hid_unregister_driver(&pyra_driver); 60462306a36Sopenharmony_ci class_unregister(&pyra_class); 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cimodule_init(pyra_init); 60862306a36Sopenharmony_cimodule_exit(pyra_exit); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ciMODULE_AUTHOR("Stefan Achatz"); 61162306a36Sopenharmony_ciMODULE_DESCRIPTION("USB Roccat Pyra driver"); 61262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 613