12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2cb7cf3daSStefan Achatz /* 3cb7cf3daSStefan Achatz * Roccat Pyra driver for Linux 4cb7cf3daSStefan Achatz * 5cb7cf3daSStefan Achatz * Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net> 6cb7cf3daSStefan Achatz */ 7cb7cf3daSStefan Achatz 8cb7cf3daSStefan Achatz /* 9cb7cf3daSStefan Achatz */ 10cb7cf3daSStefan Achatz 11cb7cf3daSStefan Achatz /* 12cb7cf3daSStefan Achatz * Roccat Pyra is a mobile gamer mouse which comes in wired and wireless 13cb7cf3daSStefan Achatz * variant. Wireless variant is not tested. 14cb7cf3daSStefan Achatz * Userland tools can be found at http://sourceforge.net/projects/roccat 15cb7cf3daSStefan Achatz */ 16cb7cf3daSStefan Achatz 17cb7cf3daSStefan Achatz #include <linux/device.h> 18cb7cf3daSStefan Achatz #include <linux/input.h> 19cb7cf3daSStefan Achatz #include <linux/hid.h> 20cb7cf3daSStefan Achatz #include <linux/module.h> 21cb7cf3daSStefan Achatz #include <linux/slab.h> 225dc0c983SStefan Achatz #include <linux/hid-roccat.h> 23cb7cf3daSStefan Achatz #include "hid-ids.h" 245772f636SStefan Achatz #include "hid-roccat-common.h" 25cb7cf3daSStefan Achatz #include "hid-roccat-pyra.h" 26cb7cf3daSStefan Achatz 2714a057f8SStefan Achatz static uint profile_numbers[5] = {0, 1, 2, 3, 4}; 2814a057f8SStefan Achatz 295012aadaSStefan Achatz /* pyra_class is used for creating sysfs attributes via roccat char device */ 305012aadaSStefan Achatz static struct class *pyra_class; 315012aadaSStefan Achatz 32cb7cf3daSStefan Achatz static void profile_activated(struct pyra_device *pyra, 33cb7cf3daSStefan Achatz unsigned int new_profile) 34cb7cf3daSStefan Achatz { 35606185b2SDan Carpenter if (new_profile >= ARRAY_SIZE(pyra->profile_settings)) 36606185b2SDan Carpenter return; 37cb7cf3daSStefan Achatz pyra->actual_profile = new_profile; 38cb7cf3daSStefan Achatz pyra->actual_cpi = pyra->profile_settings[pyra->actual_profile].y_cpi; 39cb7cf3daSStefan Achatz } 40cb7cf3daSStefan Achatz 41cb7cf3daSStefan Achatz static int pyra_send_control(struct usb_device *usb_dev, int value, 42cb7cf3daSStefan Achatz enum pyra_control_requests request) 43cb7cf3daSStefan Achatz { 447392d73bSStefan Achatz struct roccat_common2_control control; 45cb7cf3daSStefan Achatz 46cb7cf3daSStefan Achatz if ((request == PYRA_CONTROL_REQUEST_PROFILE_SETTINGS || 47cb7cf3daSStefan Achatz request == PYRA_CONTROL_REQUEST_PROFILE_BUTTONS) && 48cb7cf3daSStefan Achatz (value < 0 || value > 4)) 49cb7cf3daSStefan Achatz return -EINVAL; 50cb7cf3daSStefan Achatz 514728f2dcSStefan Achatz control.command = ROCCAT_COMMON_COMMAND_CONTROL; 52cb7cf3daSStefan Achatz control.value = value; 53cb7cf3daSStefan Achatz control.request = request; 54cb7cf3daSStefan Achatz 557392d73bSStefan Achatz return roccat_common2_send(usb_dev, ROCCAT_COMMON_COMMAND_CONTROL, 567392d73bSStefan Achatz &control, sizeof(struct roccat_common2_control)); 57cb7cf3daSStefan Achatz } 58cb7cf3daSStefan Achatz 59cb7cf3daSStefan Achatz static int pyra_get_profile_settings(struct usb_device *usb_dev, 60cb7cf3daSStefan Achatz struct pyra_profile_settings *buf, int number) 61cb7cf3daSStefan Achatz { 62cb7cf3daSStefan Achatz int retval; 63cb7cf3daSStefan Achatz retval = pyra_send_control(usb_dev, number, 64cb7cf3daSStefan Achatz PYRA_CONTROL_REQUEST_PROFILE_SETTINGS); 65cb7cf3daSStefan Achatz if (retval) 66cb7cf3daSStefan Achatz return retval; 677392d73bSStefan Achatz return roccat_common2_receive(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS, 68be34380eSStefan Achatz buf, PYRA_SIZE_PROFILE_SETTINGS); 69cb7cf3daSStefan Achatz } 70cb7cf3daSStefan Achatz 71cb7cf3daSStefan Achatz static int pyra_get_settings(struct usb_device *usb_dev, 72cb7cf3daSStefan Achatz struct pyra_settings *buf) 73cb7cf3daSStefan Achatz { 747392d73bSStefan Achatz return roccat_common2_receive(usb_dev, PYRA_COMMAND_SETTINGS, 75be34380eSStefan Achatz buf, PYRA_SIZE_SETTINGS); 76cb7cf3daSStefan Achatz } 77cb7cf3daSStefan Achatz 78cb7cf3daSStefan Achatz static int pyra_set_settings(struct usb_device *usb_dev, 79cb7cf3daSStefan Achatz struct pyra_settings const *settings) 80cb7cf3daSStefan Achatz { 817392d73bSStefan Achatz return roccat_common2_send_with_status(usb_dev, 824728f2dcSStefan Achatz PYRA_COMMAND_SETTINGS, settings, 83be34380eSStefan Achatz PYRA_SIZE_SETTINGS); 84cb7cf3daSStefan Achatz } 85cb7cf3daSStefan Achatz 86be34380eSStefan Achatz static ssize_t pyra_sysfs_read(struct file *fp, struct kobject *kobj, 87be34380eSStefan Achatz char *buf, loff_t off, size_t count, 88be34380eSStefan Achatz size_t real_size, uint command) 89be34380eSStefan Achatz { 902cf83833SGeliang Tang struct device *dev = kobj_to_dev(kobj)->parent->parent; 91be34380eSStefan Achatz struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); 92be34380eSStefan Achatz struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 93be34380eSStefan Achatz int retval; 94be34380eSStefan Achatz 95be34380eSStefan Achatz if (off >= real_size) 96be34380eSStefan Achatz return 0; 97be34380eSStefan Achatz 98be34380eSStefan Achatz if (off != 0 || count != real_size) 99be34380eSStefan Achatz return -EINVAL; 100be34380eSStefan Achatz 101be34380eSStefan Achatz mutex_lock(&pyra->pyra_lock); 102be34380eSStefan Achatz retval = roccat_common2_receive(usb_dev, command, buf, real_size); 103be34380eSStefan Achatz mutex_unlock(&pyra->pyra_lock); 104be34380eSStefan Achatz 105be34380eSStefan Achatz if (retval) 106be34380eSStefan Achatz return retval; 107be34380eSStefan Achatz 108be34380eSStefan Achatz return real_size; 109be34380eSStefan Achatz } 110be34380eSStefan Achatz 111be34380eSStefan Achatz static ssize_t pyra_sysfs_write(struct file *fp, struct kobject *kobj, 112be34380eSStefan Achatz void const *buf, loff_t off, size_t count, 113be34380eSStefan Achatz size_t real_size, uint command) 114be34380eSStefan Achatz { 1152cf83833SGeliang Tang struct device *dev = kobj_to_dev(kobj)->parent->parent; 116be34380eSStefan Achatz struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); 117be34380eSStefan Achatz struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 118be34380eSStefan Achatz int retval; 119be34380eSStefan Achatz 120be34380eSStefan Achatz if (off != 0 || count != real_size) 121be34380eSStefan Achatz return -EINVAL; 122be34380eSStefan Achatz 123be34380eSStefan Achatz mutex_lock(&pyra->pyra_lock); 124be34380eSStefan Achatz retval = roccat_common2_send_with_status(usb_dev, command, (void *)buf, real_size); 125be34380eSStefan Achatz mutex_unlock(&pyra->pyra_lock); 126be34380eSStefan Achatz 127be34380eSStefan Achatz if (retval) 128be34380eSStefan Achatz return retval; 129be34380eSStefan Achatz 130be34380eSStefan Achatz return real_size; 131be34380eSStefan Achatz } 132be34380eSStefan Achatz 133be34380eSStefan Achatz #define PYRA_SYSFS_W(thingy, THINGY) \ 134be34380eSStefan Achatz static ssize_t pyra_sysfs_write_ ## thingy(struct file *fp, \ 135be34380eSStefan Achatz struct kobject *kobj, struct bin_attribute *attr, char *buf, \ 136be34380eSStefan Achatz loff_t off, size_t count) \ 137be34380eSStefan Achatz { \ 138be34380eSStefan Achatz return pyra_sysfs_write(fp, kobj, buf, off, count, \ 139be34380eSStefan Achatz PYRA_SIZE_ ## THINGY, PYRA_COMMAND_ ## THINGY); \ 140be34380eSStefan Achatz } 141be34380eSStefan Achatz 142be34380eSStefan Achatz #define PYRA_SYSFS_R(thingy, THINGY) \ 143be34380eSStefan Achatz static ssize_t pyra_sysfs_read_ ## thingy(struct file *fp, \ 144be34380eSStefan Achatz struct kobject *kobj, struct bin_attribute *attr, char *buf, \ 145be34380eSStefan Achatz loff_t off, size_t count) \ 146be34380eSStefan Achatz { \ 147be34380eSStefan Achatz return pyra_sysfs_read(fp, kobj, buf, off, count, \ 148be34380eSStefan Achatz PYRA_SIZE_ ## THINGY, PYRA_COMMAND_ ## THINGY); \ 149be34380eSStefan Achatz } 150be34380eSStefan Achatz 151be34380eSStefan Achatz #define PYRA_SYSFS_RW(thingy, THINGY) \ 152be34380eSStefan Achatz PYRA_SYSFS_W(thingy, THINGY) \ 153be34380eSStefan Achatz PYRA_SYSFS_R(thingy, THINGY) 154be34380eSStefan Achatz 155be34380eSStefan Achatz #define PYRA_BIN_ATTRIBUTE_RW(thingy, THINGY) \ 156a7492451SGreg Kroah-Hartman PYRA_SYSFS_RW(thingy, THINGY); \ 157a7492451SGreg Kroah-Hartman static struct bin_attribute bin_attr_##thingy = { \ 158be34380eSStefan Achatz .attr = { .name = #thingy, .mode = 0660 }, \ 159be34380eSStefan Achatz .size = PYRA_SIZE_ ## THINGY, \ 160be34380eSStefan Achatz .read = pyra_sysfs_read_ ## thingy, \ 161be34380eSStefan Achatz .write = pyra_sysfs_write_ ## thingy \ 162be34380eSStefan Achatz } 163be34380eSStefan Achatz 164be34380eSStefan Achatz #define PYRA_BIN_ATTRIBUTE_R(thingy, THINGY) \ 165a7492451SGreg Kroah-Hartman PYRA_SYSFS_R(thingy, THINGY); \ 166a7492451SGreg Kroah-Hartman static struct bin_attribute bin_attr_##thingy = { \ 167be34380eSStefan Achatz .attr = { .name = #thingy, .mode = 0440 }, \ 168be34380eSStefan Achatz .size = PYRA_SIZE_ ## THINGY, \ 169be34380eSStefan Achatz .read = pyra_sysfs_read_ ## thingy, \ 170be34380eSStefan Achatz } 171be34380eSStefan Achatz 172be34380eSStefan Achatz #define PYRA_BIN_ATTRIBUTE_W(thingy, THINGY) \ 173a7492451SGreg Kroah-Hartman PYRA_SYSFS_W(thingy, THINGY); \ 174a7492451SGreg Kroah-Hartman static struct bin_attribute bin_attr_##thingy = { \ 175be34380eSStefan Achatz .attr = { .name = #thingy, .mode = 0220 }, \ 176be34380eSStefan Achatz .size = PYRA_SIZE_ ## THINGY, \ 177be34380eSStefan Achatz .write = pyra_sysfs_write_ ## thingy \ 178be34380eSStefan Achatz } 179be34380eSStefan Achatz 180a7492451SGreg Kroah-Hartman PYRA_BIN_ATTRIBUTE_W(control, CONTROL); 181a7492451SGreg Kroah-Hartman PYRA_BIN_ATTRIBUTE_RW(info, INFO); 182a7492451SGreg Kroah-Hartman PYRA_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS); 183a7492451SGreg Kroah-Hartman PYRA_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS); 184be34380eSStefan Achatz 185cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp, 186cb7cf3daSStefan Achatz struct kobject *kobj, struct bin_attribute *attr, char *buf, 18714a057f8SStefan Achatz loff_t off, size_t count) 188cb7cf3daSStefan Achatz { 1892cf83833SGeliang Tang struct device *dev = kobj_to_dev(kobj)->parent->parent; 190be34380eSStefan Achatz struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 191be34380eSStefan Achatz ssize_t retval; 192cb7cf3daSStefan Achatz 193be34380eSStefan Achatz retval = pyra_send_control(usb_dev, *(uint *)(attr->private), 194be34380eSStefan Achatz PYRA_CONTROL_REQUEST_PROFILE_SETTINGS); 195be34380eSStefan Achatz if (retval) 196be34380eSStefan Achatz return retval; 197cb7cf3daSStefan Achatz 198be34380eSStefan Achatz return pyra_sysfs_read(fp, kobj, buf, off, count, 199be34380eSStefan Achatz PYRA_SIZE_PROFILE_SETTINGS, 200be34380eSStefan Achatz PYRA_COMMAND_PROFILE_SETTINGS); 201cb7cf3daSStefan Achatz } 202cb7cf3daSStefan Achatz 203cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp, 204cb7cf3daSStefan Achatz struct kobject *kobj, struct bin_attribute *attr, char *buf, 20514a057f8SStefan Achatz loff_t off, size_t count) 206cb7cf3daSStefan Achatz { 2072cf83833SGeliang Tang struct device *dev = kobj_to_dev(kobj)->parent->parent; 208cb7cf3daSStefan Achatz struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 209be34380eSStefan Achatz ssize_t retval; 210cb7cf3daSStefan Achatz 211be34380eSStefan Achatz retval = pyra_send_control(usb_dev, *(uint *)(attr->private), 212be34380eSStefan Achatz PYRA_CONTROL_REQUEST_PROFILE_BUTTONS); 213cb7cf3daSStefan Achatz if (retval) 214cb7cf3daSStefan Achatz return retval; 215cb7cf3daSStefan Achatz 216be34380eSStefan Achatz return pyra_sysfs_read(fp, kobj, buf, off, count, 217be34380eSStefan Achatz PYRA_SIZE_PROFILE_BUTTONS, 218be34380eSStefan Achatz PYRA_COMMAND_PROFILE_BUTTONS); 219cb7cf3daSStefan Achatz } 220cb7cf3daSStefan Achatz 221a7492451SGreg Kroah-Hartman #define PROFILE_ATTR(number) \ 222a7492451SGreg Kroah-Hartman static struct bin_attribute bin_attr_profile##number##_settings = { \ 223550dbf47SStefan Achatz .attr = { .name = "profile" #number "_settings", .mode = 0440 }, \ 224a7492451SGreg Kroah-Hartman .size = PYRA_SIZE_PROFILE_SETTINGS, \ 225a7492451SGreg Kroah-Hartman .read = pyra_sysfs_read_profilex_settings, \ 226a7492451SGreg Kroah-Hartman .private = &profile_numbers[number-1], \ 227a7492451SGreg Kroah-Hartman }; \ 228a7492451SGreg Kroah-Hartman static struct bin_attribute bin_attr_profile##number##_buttons = { \ 229550dbf47SStefan Achatz .attr = { .name = "profile" #number "_buttons", .mode = 0440 }, \ 230a7492451SGreg Kroah-Hartman .size = PYRA_SIZE_PROFILE_BUTTONS, \ 231a7492451SGreg Kroah-Hartman .read = pyra_sysfs_read_profilex_buttons, \ 232a7492451SGreg Kroah-Hartman .private = &profile_numbers[number-1], \ 233a7492451SGreg Kroah-Hartman }; 234a7492451SGreg Kroah-Hartman PROFILE_ATTR(1); 235a7492451SGreg Kroah-Hartman PROFILE_ATTR(2); 236a7492451SGreg Kroah-Hartman PROFILE_ATTR(3); 237a7492451SGreg Kroah-Hartman PROFILE_ATTR(4); 238a7492451SGreg Kroah-Hartman PROFILE_ATTR(5); 239a7492451SGreg Kroah-Hartman 240cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_write_settings(struct file *fp, 241cb7cf3daSStefan Achatz struct kobject *kobj, struct bin_attribute *attr, char *buf, 242cb7cf3daSStefan Achatz loff_t off, size_t count) 243cb7cf3daSStefan Achatz { 2442cf83833SGeliang Tang struct device *dev = kobj_to_dev(kobj)->parent->parent; 245cb7cf3daSStefan Achatz struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); 246cb7cf3daSStefan Achatz struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 247cb7cf3daSStefan Achatz int retval = 0; 248dc186b66SStefan Achatz struct pyra_roccat_report roccat_report; 249be34380eSStefan Achatz struct pyra_settings const *settings; 250cb7cf3daSStefan Achatz 251be34380eSStefan Achatz if (off != 0 || count != PYRA_SIZE_SETTINGS) 252cb7cf3daSStefan Achatz return -EINVAL; 253cb7cf3daSStefan Achatz 254be34380eSStefan Achatz settings = (struct pyra_settings const *)buf; 255606185b2SDan Carpenter if (settings->startup_profile >= ARRAY_SIZE(pyra->profile_settings)) 256606185b2SDan Carpenter return -EINVAL; 257606185b2SDan Carpenter 258606185b2SDan Carpenter mutex_lock(&pyra->pyra_lock); 259be34380eSStefan Achatz 260be34380eSStefan Achatz retval = pyra_set_settings(usb_dev, settings); 261dc186b66SStefan Achatz if (retval) { 262dc186b66SStefan Achatz mutex_unlock(&pyra->pyra_lock); 263dc186b66SStefan Achatz return retval; 264dc186b66SStefan Achatz } 265dc186b66SStefan Achatz 266be34380eSStefan Achatz profile_activated(pyra, settings->startup_profile); 267cb7cf3daSStefan Achatz 268dc186b66SStefan Achatz roccat_report.type = PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2; 269be34380eSStefan Achatz roccat_report.value = settings->startup_profile + 1; 270dc186b66SStefan Achatz roccat_report.key = 0; 271dc186b66SStefan Achatz roccat_report_event(pyra->chrdev_minor, 272dc186b66SStefan Achatz (uint8_t const *)&roccat_report); 273be34380eSStefan Achatz 274dc186b66SStefan Achatz mutex_unlock(&pyra->pyra_lock); 275be34380eSStefan Achatz return PYRA_SIZE_SETTINGS; 276cb7cf3daSStefan Achatz } 277cb7cf3daSStefan Achatz 278a7492451SGreg Kroah-Hartman PYRA_SYSFS_R(settings, SETTINGS); 279a7492451SGreg Kroah-Hartman static struct bin_attribute bin_attr_settings = 280a7492451SGreg Kroah-Hartman __BIN_ATTR(settings, (S_IWUSR | S_IRUGO), 281a7492451SGreg Kroah-Hartman pyra_sysfs_read_settings, pyra_sysfs_write_settings, 282a7492451SGreg Kroah-Hartman PYRA_SIZE_SETTINGS); 283cb7cf3daSStefan Achatz 284cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_show_actual_cpi(struct device *dev, 285cb7cf3daSStefan Achatz struct device_attribute *attr, char *buf) 286cb7cf3daSStefan Achatz { 2875012aadaSStefan Achatz struct pyra_device *pyra = 2885012aadaSStefan Achatz hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); 289cb7cf3daSStefan Achatz return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_cpi); 290cb7cf3daSStefan Achatz } 29146a58c44SGreg Kroah-Hartman static DEVICE_ATTR(actual_cpi, 0440, pyra_sysfs_show_actual_cpi, NULL); 292cb7cf3daSStefan Achatz 293cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_show_actual_profile(struct device *dev, 294cb7cf3daSStefan Achatz struct device_attribute *attr, char *buf) 295cb7cf3daSStefan Achatz { 2965012aadaSStefan Achatz struct pyra_device *pyra = 2975012aadaSStefan Achatz hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); 298be34380eSStefan Achatz struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 299be34380eSStefan Achatz struct pyra_settings settings; 300be34380eSStefan Achatz 301be34380eSStefan Achatz mutex_lock(&pyra->pyra_lock); 302be34380eSStefan Achatz roccat_common2_receive(usb_dev, PYRA_COMMAND_SETTINGS, 303be34380eSStefan Achatz &settings, PYRA_SIZE_SETTINGS); 304be34380eSStefan Achatz mutex_unlock(&pyra->pyra_lock); 305be34380eSStefan Achatz 306be34380eSStefan Achatz return snprintf(buf, PAGE_SIZE, "%d\n", settings.startup_profile); 307cb7cf3daSStefan Achatz } 30846a58c44SGreg Kroah-Hartman static DEVICE_ATTR(actual_profile, 0440, pyra_sysfs_show_actual_profile, NULL); 30946a58c44SGreg Kroah-Hartman static DEVICE_ATTR(startup_profile, 0440, pyra_sysfs_show_actual_profile, NULL); 310cb7cf3daSStefan Achatz 311cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_show_firmware_version(struct device *dev, 312cb7cf3daSStefan Achatz struct device_attribute *attr, char *buf) 313cb7cf3daSStefan Achatz { 314be34380eSStefan Achatz struct pyra_device *pyra; 315be34380eSStefan Achatz struct usb_device *usb_dev; 316be34380eSStefan Achatz struct pyra_info info; 317cb7cf3daSStefan Achatz 318be34380eSStefan Achatz dev = dev->parent->parent; 319be34380eSStefan Achatz pyra = hid_get_drvdata(dev_get_drvdata(dev)); 320be34380eSStefan Achatz usb_dev = interface_to_usbdev(to_usb_interface(dev)); 321be34380eSStefan Achatz 322be34380eSStefan Achatz mutex_lock(&pyra->pyra_lock); 323be34380eSStefan Achatz roccat_common2_receive(usb_dev, PYRA_COMMAND_INFO, 324be34380eSStefan Achatz &info, PYRA_SIZE_INFO); 325be34380eSStefan Achatz mutex_unlock(&pyra->pyra_lock); 326be34380eSStefan Achatz 327be34380eSStefan Achatz return snprintf(buf, PAGE_SIZE, "%d\n", info.firmware_version); 328cb7cf3daSStefan Achatz } 32946a58c44SGreg Kroah-Hartman static DEVICE_ATTR(firmware_version, 0440, pyra_sysfs_show_firmware_version, 33046a58c44SGreg Kroah-Hartman NULL); 331cb7cf3daSStefan Achatz 33246a58c44SGreg Kroah-Hartman static struct attribute *pyra_attrs[] = { 33346a58c44SGreg Kroah-Hartman &dev_attr_actual_cpi.attr, 33446a58c44SGreg Kroah-Hartman &dev_attr_actual_profile.attr, 33546a58c44SGreg Kroah-Hartman &dev_attr_firmware_version.attr, 33646a58c44SGreg Kroah-Hartman &dev_attr_startup_profile.attr, 33746a58c44SGreg Kroah-Hartman NULL, 338cb7cf3daSStefan Achatz }; 339cb7cf3daSStefan Achatz 340a7492451SGreg Kroah-Hartman static struct bin_attribute *pyra_bin_attributes[] = { 341a7492451SGreg Kroah-Hartman &bin_attr_control, 342a7492451SGreg Kroah-Hartman &bin_attr_info, 343a7492451SGreg Kroah-Hartman &bin_attr_profile_settings, 344a7492451SGreg Kroah-Hartman &bin_attr_profile_buttons, 345a7492451SGreg Kroah-Hartman &bin_attr_settings, 346a7492451SGreg Kroah-Hartman &bin_attr_profile1_settings, 347a7492451SGreg Kroah-Hartman &bin_attr_profile2_settings, 348a7492451SGreg Kroah-Hartman &bin_attr_profile3_settings, 349a7492451SGreg Kroah-Hartman &bin_attr_profile4_settings, 350a7492451SGreg Kroah-Hartman &bin_attr_profile5_settings, 351a7492451SGreg Kroah-Hartman &bin_attr_profile1_buttons, 352a7492451SGreg Kroah-Hartman &bin_attr_profile2_buttons, 353a7492451SGreg Kroah-Hartman &bin_attr_profile3_buttons, 354a7492451SGreg Kroah-Hartman &bin_attr_profile4_buttons, 355a7492451SGreg Kroah-Hartman &bin_attr_profile5_buttons, 356a7492451SGreg Kroah-Hartman NULL, 357a7492451SGreg Kroah-Hartman }; 358a7492451SGreg Kroah-Hartman 359a7492451SGreg Kroah-Hartman static const struct attribute_group pyra_group = { 360a7492451SGreg Kroah-Hartman .attrs = pyra_attrs, 361a7492451SGreg Kroah-Hartman .bin_attrs = pyra_bin_attributes, 362a7492451SGreg Kroah-Hartman }; 363a7492451SGreg Kroah-Hartman 364a7492451SGreg Kroah-Hartman static const struct attribute_group *pyra_groups[] = { 365a7492451SGreg Kroah-Hartman &pyra_group, 366a7492451SGreg Kroah-Hartman NULL, 367cb7cf3daSStefan Achatz }; 368cb7cf3daSStefan Achatz 369cb7cf3daSStefan Achatz static int pyra_init_pyra_device_struct(struct usb_device *usb_dev, 370cb7cf3daSStefan Achatz struct pyra_device *pyra) 371cb7cf3daSStefan Achatz { 372be34380eSStefan Achatz struct pyra_settings settings; 373cb7cf3daSStefan Achatz int retval, i; 374cb7cf3daSStefan Achatz 375cb7cf3daSStefan Achatz mutex_init(&pyra->pyra_lock); 376cb7cf3daSStefan Achatz 377be34380eSStefan Achatz retval = pyra_get_settings(usb_dev, &settings); 378cb7cf3daSStefan Achatz if (retval) 379cb7cf3daSStefan Achatz return retval; 380cb7cf3daSStefan Achatz 381cb7cf3daSStefan Achatz for (i = 0; i < 5; ++i) { 382cb7cf3daSStefan Achatz retval = pyra_get_profile_settings(usb_dev, 383cb7cf3daSStefan Achatz &pyra->profile_settings[i], i); 384cb7cf3daSStefan Achatz if (retval) 385cb7cf3daSStefan Achatz return retval; 386cb7cf3daSStefan Achatz } 387cb7cf3daSStefan Achatz 388be34380eSStefan Achatz profile_activated(pyra, settings.startup_profile); 389cb7cf3daSStefan Achatz 390cb7cf3daSStefan Achatz return 0; 391cb7cf3daSStefan Achatz } 392cb7cf3daSStefan Achatz 393cb7cf3daSStefan Achatz static int pyra_init_specials(struct hid_device *hdev) 394cb7cf3daSStefan Achatz { 395cb7cf3daSStefan Achatz struct usb_interface *intf = to_usb_interface(hdev->dev.parent); 396cb7cf3daSStefan Achatz struct usb_device *usb_dev = interface_to_usbdev(intf); 397cb7cf3daSStefan Achatz struct pyra_device *pyra; 398cb7cf3daSStefan Achatz int retval; 399cb7cf3daSStefan Achatz 400cb7cf3daSStefan Achatz if (intf->cur_altsetting->desc.bInterfaceProtocol 401cb7cf3daSStefan Achatz == USB_INTERFACE_PROTOCOL_MOUSE) { 402cb7cf3daSStefan Achatz 403cb7cf3daSStefan Achatz pyra = kzalloc(sizeof(*pyra), GFP_KERNEL); 404cb7cf3daSStefan Achatz if (!pyra) { 4054291ee30SJoe Perches hid_err(hdev, "can't alloc device descriptor\n"); 406cb7cf3daSStefan Achatz return -ENOMEM; 407cb7cf3daSStefan Achatz } 408cb7cf3daSStefan Achatz hid_set_drvdata(hdev, pyra); 409cb7cf3daSStefan Achatz 410cb7cf3daSStefan Achatz retval = pyra_init_pyra_device_struct(usb_dev, pyra); 411cb7cf3daSStefan Achatz if (retval) { 4124291ee30SJoe Perches hid_err(hdev, "couldn't init struct pyra_device\n"); 413cb7cf3daSStefan Achatz goto exit_free; 414cb7cf3daSStefan Achatz } 415cb7cf3daSStefan Achatz 4168211e460SStefan Achatz retval = roccat_connect(pyra_class, hdev, 4178211e460SStefan Achatz sizeof(struct pyra_roccat_report)); 418cb7cf3daSStefan Achatz if (retval < 0) { 4194291ee30SJoe Perches hid_err(hdev, "couldn't init char dev\n"); 420cb7cf3daSStefan Achatz } else { 421cb7cf3daSStefan Achatz pyra->chrdev_minor = retval; 422cb7cf3daSStefan Achatz pyra->roccat_claimed = 1; 423cb7cf3daSStefan Achatz } 424cb7cf3daSStefan Achatz } else { 425cb7cf3daSStefan Achatz hid_set_drvdata(hdev, NULL); 426cb7cf3daSStefan Achatz } 427cb7cf3daSStefan Achatz 428cb7cf3daSStefan Achatz return 0; 429cb7cf3daSStefan Achatz exit_free: 430cb7cf3daSStefan Achatz kfree(pyra); 431cb7cf3daSStefan Achatz return retval; 432cb7cf3daSStefan Achatz } 433cb7cf3daSStefan Achatz 434cb7cf3daSStefan Achatz static void pyra_remove_specials(struct hid_device *hdev) 435cb7cf3daSStefan Achatz { 436cb7cf3daSStefan Achatz struct usb_interface *intf = to_usb_interface(hdev->dev.parent); 437cb7cf3daSStefan Achatz struct pyra_device *pyra; 438cb7cf3daSStefan Achatz 439cb7cf3daSStefan Achatz if (intf->cur_altsetting->desc.bInterfaceProtocol 440cb7cf3daSStefan Achatz == USB_INTERFACE_PROTOCOL_MOUSE) { 441cb7cf3daSStefan Achatz pyra = hid_get_drvdata(hdev); 442cb7cf3daSStefan Achatz if (pyra->roccat_claimed) 443cb7cf3daSStefan Achatz roccat_disconnect(pyra->chrdev_minor); 444cb7cf3daSStefan Achatz kfree(hid_get_drvdata(hdev)); 445cb7cf3daSStefan Achatz } 446cb7cf3daSStefan Achatz } 447cb7cf3daSStefan Achatz 448cb7cf3daSStefan Achatz static int pyra_probe(struct hid_device *hdev, const struct hid_device_id *id) 449cb7cf3daSStefan Achatz { 450cb7cf3daSStefan Achatz int retval; 451cb7cf3daSStefan Achatz 452*93020953SGreg Kroah-Hartman if (!hid_is_usb(hdev)) 453*93020953SGreg Kroah-Hartman return -EINVAL; 454*93020953SGreg Kroah-Hartman 455cb7cf3daSStefan Achatz retval = hid_parse(hdev); 456cb7cf3daSStefan Achatz if (retval) { 4574291ee30SJoe Perches hid_err(hdev, "parse failed\n"); 458cb7cf3daSStefan Achatz goto exit; 459cb7cf3daSStefan Achatz } 460cb7cf3daSStefan Achatz 461cb7cf3daSStefan Achatz retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 462cb7cf3daSStefan Achatz if (retval) { 4634291ee30SJoe Perches hid_err(hdev, "hw start failed\n"); 464cb7cf3daSStefan Achatz goto exit; 465cb7cf3daSStefan Achatz } 466cb7cf3daSStefan Achatz 467cb7cf3daSStefan Achatz retval = pyra_init_specials(hdev); 468cb7cf3daSStefan Achatz if (retval) { 4694291ee30SJoe Perches hid_err(hdev, "couldn't install mouse\n"); 470cb7cf3daSStefan Achatz goto exit_stop; 471cb7cf3daSStefan Achatz } 472cb7cf3daSStefan Achatz return 0; 473cb7cf3daSStefan Achatz 474cb7cf3daSStefan Achatz exit_stop: 475cb7cf3daSStefan Achatz hid_hw_stop(hdev); 476cb7cf3daSStefan Achatz exit: 477cb7cf3daSStefan Achatz return retval; 478cb7cf3daSStefan Achatz } 479cb7cf3daSStefan Achatz 480cb7cf3daSStefan Achatz static void pyra_remove(struct hid_device *hdev) 481cb7cf3daSStefan Achatz { 482cb7cf3daSStefan Achatz pyra_remove_specials(hdev); 483cb7cf3daSStefan Achatz hid_hw_stop(hdev); 484cb7cf3daSStefan Achatz } 485cb7cf3daSStefan Achatz 486cb7cf3daSStefan Achatz static void pyra_keep_values_up_to_date(struct pyra_device *pyra, 487cb7cf3daSStefan Achatz u8 const *data) 488cb7cf3daSStefan Achatz { 489cb7cf3daSStefan Achatz struct pyra_mouse_event_button const *button_event; 490cb7cf3daSStefan Achatz 491cb7cf3daSStefan Achatz switch (data[0]) { 492cb7cf3daSStefan Achatz case PYRA_MOUSE_REPORT_NUMBER_BUTTON: 493cb7cf3daSStefan Achatz button_event = (struct pyra_mouse_event_button const *)data; 494cb7cf3daSStefan Achatz switch (button_event->type) { 495cb7cf3daSStefan Achatz case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2: 496cb7cf3daSStefan Achatz profile_activated(pyra, button_event->data1 - 1); 497cb7cf3daSStefan Achatz break; 498cb7cf3daSStefan Achatz case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI: 499cb7cf3daSStefan Achatz pyra->actual_cpi = button_event->data1; 500cb7cf3daSStefan Achatz break; 501cb7cf3daSStefan Achatz } 502cb7cf3daSStefan Achatz break; 503cb7cf3daSStefan Achatz } 504cb7cf3daSStefan Achatz } 505cb7cf3daSStefan Achatz 506cb7cf3daSStefan Achatz static void pyra_report_to_chrdev(struct pyra_device const *pyra, 507cb7cf3daSStefan Achatz u8 const *data) 508cb7cf3daSStefan Achatz { 509cb7cf3daSStefan Achatz struct pyra_roccat_report roccat_report; 510cb7cf3daSStefan Achatz struct pyra_mouse_event_button const *button_event; 511cb7cf3daSStefan Achatz 512cb7cf3daSStefan Achatz if (data[0] != PYRA_MOUSE_REPORT_NUMBER_BUTTON) 513cb7cf3daSStefan Achatz return; 514cb7cf3daSStefan Achatz 515cb7cf3daSStefan Achatz button_event = (struct pyra_mouse_event_button const *)data; 516cb7cf3daSStefan Achatz 517cb7cf3daSStefan Achatz switch (button_event->type) { 518cb7cf3daSStefan Achatz case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2: 519cb7cf3daSStefan Achatz case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI: 520cb7cf3daSStefan Achatz roccat_report.type = button_event->type; 521cb7cf3daSStefan Achatz roccat_report.value = button_event->data1; 522cb7cf3daSStefan Achatz roccat_report.key = 0; 523cb7cf3daSStefan Achatz roccat_report_event(pyra->chrdev_minor, 5248211e460SStefan Achatz (uint8_t const *)&roccat_report); 525cb7cf3daSStefan Achatz break; 526cb7cf3daSStefan Achatz case PYRA_MOUSE_EVENT_BUTTON_TYPE_MACRO: 527cb7cf3daSStefan Achatz case PYRA_MOUSE_EVENT_BUTTON_TYPE_SHORTCUT: 528cb7cf3daSStefan Achatz case PYRA_MOUSE_EVENT_BUTTON_TYPE_QUICKLAUNCH: 529cb7cf3daSStefan Achatz if (button_event->data2 == PYRA_MOUSE_EVENT_BUTTON_PRESS) { 530cb7cf3daSStefan Achatz roccat_report.type = button_event->type; 531cb7cf3daSStefan Achatz roccat_report.key = button_event->data1; 532d2b570a5SStefan Achatz /* 533d2b570a5SStefan Achatz * pyra reports profile numbers with range 1-5. 534d2b570a5SStefan Achatz * Keeping this behaviour. 535d2b570a5SStefan Achatz */ 536d2b570a5SStefan Achatz roccat_report.value = pyra->actual_profile + 1; 537cb7cf3daSStefan Achatz roccat_report_event(pyra->chrdev_minor, 5388211e460SStefan Achatz (uint8_t const *)&roccat_report); 539cb7cf3daSStefan Achatz } 540cb7cf3daSStefan Achatz break; 541cb7cf3daSStefan Achatz } 542cb7cf3daSStefan Achatz } 543cb7cf3daSStefan Achatz 544cb7cf3daSStefan Achatz static int pyra_raw_event(struct hid_device *hdev, struct hid_report *report, 545cb7cf3daSStefan Achatz u8 *data, int size) 546cb7cf3daSStefan Achatz { 547cb7cf3daSStefan Achatz struct usb_interface *intf = to_usb_interface(hdev->dev.parent); 548cb7cf3daSStefan Achatz struct pyra_device *pyra = hid_get_drvdata(hdev); 549cb7cf3daSStefan Achatz 550cb7cf3daSStefan Achatz if (intf->cur_altsetting->desc.bInterfaceProtocol 551cb7cf3daSStefan Achatz != USB_INTERFACE_PROTOCOL_MOUSE) 552cb7cf3daSStefan Achatz return 0; 553cb7cf3daSStefan Achatz 554901e64dbSStefan Achatz if (pyra == NULL) 555901e64dbSStefan Achatz return 0; 556901e64dbSStefan Achatz 557cb7cf3daSStefan Achatz pyra_keep_values_up_to_date(pyra, data); 558cb7cf3daSStefan Achatz 559cb7cf3daSStefan Achatz if (pyra->roccat_claimed) 560cb7cf3daSStefan Achatz pyra_report_to_chrdev(pyra, data); 561cb7cf3daSStefan Achatz 562cb7cf3daSStefan Achatz return 0; 563cb7cf3daSStefan Achatz } 564cb7cf3daSStefan Achatz 565cb7cf3daSStefan Achatz static const struct hid_device_id pyra_devices[] = { 566cb7cf3daSStefan Achatz { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, 567cb7cf3daSStefan Achatz USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, 5683fce2246SStefan Achatz { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, 5693fce2246SStefan Achatz USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) }, 570cb7cf3daSStefan Achatz { } 571cb7cf3daSStefan Achatz }; 572cb7cf3daSStefan Achatz 573cb7cf3daSStefan Achatz MODULE_DEVICE_TABLE(hid, pyra_devices); 574cb7cf3daSStefan Achatz 575cb7cf3daSStefan Achatz static struct hid_driver pyra_driver = { 576cb7cf3daSStefan Achatz .name = "pyra", 577cb7cf3daSStefan Achatz .id_table = pyra_devices, 578cb7cf3daSStefan Achatz .probe = pyra_probe, 579cb7cf3daSStefan Achatz .remove = pyra_remove, 580cb7cf3daSStefan Achatz .raw_event = pyra_raw_event 581cb7cf3daSStefan Achatz }; 582cb7cf3daSStefan Achatz 583cb7cf3daSStefan Achatz static int __init pyra_init(void) 584cb7cf3daSStefan Achatz { 5855012aadaSStefan Achatz int retval; 5865012aadaSStefan Achatz 5875012aadaSStefan Achatz /* class name has to be same as driver name */ 5885012aadaSStefan Achatz pyra_class = class_create(THIS_MODULE, "pyra"); 5895012aadaSStefan Achatz if (IS_ERR(pyra_class)) 5905012aadaSStefan Achatz return PTR_ERR(pyra_class); 59146a58c44SGreg Kroah-Hartman pyra_class->dev_groups = pyra_groups; 5925012aadaSStefan Achatz 5935012aadaSStefan Achatz retval = hid_register_driver(&pyra_driver); 5945012aadaSStefan Achatz if (retval) 5955012aadaSStefan Achatz class_destroy(pyra_class); 5965012aadaSStefan Achatz return retval; 597cb7cf3daSStefan Achatz } 598cb7cf3daSStefan Achatz 599cb7cf3daSStefan Achatz static void __exit pyra_exit(void) 600cb7cf3daSStefan Achatz { 601cb7cf3daSStefan Achatz hid_unregister_driver(&pyra_driver); 60274b643daSStefan Achatz class_destroy(pyra_class); 603cb7cf3daSStefan Achatz } 604cb7cf3daSStefan Achatz 605cb7cf3daSStefan Achatz module_init(pyra_init); 606cb7cf3daSStefan Achatz module_exit(pyra_exit); 607cb7cf3daSStefan Achatz 608cb7cf3daSStefan Achatz MODULE_AUTHOR("Stefan Achatz"); 609cb7cf3daSStefan Achatz MODULE_DESCRIPTION("USB Roccat Pyra driver"); 610cb7cf3daSStefan Achatz MODULE_LICENSE("GPL v2"); 611