1cb7cf3daSStefan Achatz /* 2cb7cf3daSStefan Achatz * Roccat Pyra driver for Linux 3cb7cf3daSStefan Achatz * 4cb7cf3daSStefan Achatz * Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net> 5cb7cf3daSStefan Achatz */ 6cb7cf3daSStefan Achatz 7cb7cf3daSStefan Achatz /* 8cb7cf3daSStefan Achatz * This program is free software; you can redistribute it and/or modify it 9cb7cf3daSStefan Achatz * under the terms of the GNU General Public License as published by the Free 10cb7cf3daSStefan Achatz * Software Foundation; either version 2 of the License, or (at your option) 11cb7cf3daSStefan Achatz * any later version. 12cb7cf3daSStefan Achatz */ 13cb7cf3daSStefan Achatz 14cb7cf3daSStefan Achatz /* 15cb7cf3daSStefan Achatz * Roccat Pyra is a mobile gamer mouse which comes in wired and wireless 16cb7cf3daSStefan Achatz * variant. Wireless variant is not tested. 17cb7cf3daSStefan Achatz * Userland tools can be found at http://sourceforge.net/projects/roccat 18cb7cf3daSStefan Achatz */ 19cb7cf3daSStefan Achatz 20cb7cf3daSStefan Achatz #include <linux/device.h> 21cb7cf3daSStefan Achatz #include <linux/input.h> 22cb7cf3daSStefan Achatz #include <linux/hid.h> 23cb7cf3daSStefan Achatz #include <linux/module.h> 24cb7cf3daSStefan Achatz #include <linux/slab.h> 255dc0c983SStefan Achatz #include <linux/hid-roccat.h> 26cb7cf3daSStefan Achatz #include "hid-ids.h" 275772f636SStefan Achatz #include "hid-roccat-common.h" 28cb7cf3daSStefan Achatz #include "hid-roccat-pyra.h" 29cb7cf3daSStefan Achatz 3014a057f8SStefan Achatz static uint profile_numbers[5] = {0, 1, 2, 3, 4}; 3114a057f8SStefan Achatz 325012aadaSStefan Achatz /* pyra_class is used for creating sysfs attributes via roccat char device */ 335012aadaSStefan Achatz static struct class *pyra_class; 345012aadaSStefan Achatz 35cb7cf3daSStefan Achatz static void profile_activated(struct pyra_device *pyra, 36cb7cf3daSStefan Achatz unsigned int new_profile) 37cb7cf3daSStefan Achatz { 38cb7cf3daSStefan Achatz pyra->actual_profile = new_profile; 39cb7cf3daSStefan Achatz pyra->actual_cpi = pyra->profile_settings[pyra->actual_profile].y_cpi; 40cb7cf3daSStefan Achatz } 41cb7cf3daSStefan Achatz 42cb7cf3daSStefan Achatz static int pyra_send_control(struct usb_device *usb_dev, int value, 43cb7cf3daSStefan Achatz enum pyra_control_requests request) 44cb7cf3daSStefan Achatz { 45cb7cf3daSStefan Achatz struct pyra_control control; 46cb7cf3daSStefan Achatz 47cb7cf3daSStefan Achatz if ((request == PYRA_CONTROL_REQUEST_PROFILE_SETTINGS || 48cb7cf3daSStefan Achatz request == PYRA_CONTROL_REQUEST_PROFILE_BUTTONS) && 49cb7cf3daSStefan Achatz (value < 0 || value > 4)) 50cb7cf3daSStefan Achatz return -EINVAL; 51cb7cf3daSStefan Achatz 52cb7cf3daSStefan Achatz control.command = PYRA_COMMAND_CONTROL; 53cb7cf3daSStefan Achatz control.value = value; 54cb7cf3daSStefan Achatz control.request = request; 55cb7cf3daSStefan Achatz 561edd5b42SStefan Achatz return roccat_common_send(usb_dev, PYRA_COMMAND_CONTROL, 575772f636SStefan Achatz &control, sizeof(struct pyra_control)); 58cb7cf3daSStefan Achatz } 59cb7cf3daSStefan Achatz 60cb7cf3daSStefan Achatz static int pyra_receive_control_status(struct usb_device *usb_dev) 61cb7cf3daSStefan Achatz { 625772f636SStefan Achatz int retval; 63cb7cf3daSStefan Achatz struct pyra_control control; 64cb7cf3daSStefan Achatz 65cb7cf3daSStefan Achatz do { 66cb7cf3daSStefan Achatz msleep(10); 671edd5b42SStefan Achatz retval = roccat_common_receive(usb_dev, PYRA_COMMAND_CONTROL, 685772f636SStefan Achatz &control, sizeof(struct pyra_control)); 69cb7cf3daSStefan Achatz 70cb7cf3daSStefan Achatz /* requested too early, try again */ 715772f636SStefan Achatz } while (retval == -EPROTO); 72cb7cf3daSStefan Achatz 735772f636SStefan Achatz if (!retval && control.command == PYRA_COMMAND_CONTROL && 74cb7cf3daSStefan Achatz control.request == PYRA_CONTROL_REQUEST_STATUS && 75cb7cf3daSStefan Achatz control.value == 1) 76cb7cf3daSStefan Achatz return 0; 77cb7cf3daSStefan Achatz else { 784291ee30SJoe Perches hid_err(usb_dev, "receive control status: unknown response 0x%x 0x%x\n", 79cb7cf3daSStefan Achatz control.request, control.value); 805772f636SStefan Achatz return retval ? retval : -EINVAL; 81cb7cf3daSStefan Achatz } 82cb7cf3daSStefan Achatz } 83cb7cf3daSStefan Achatz 84cb7cf3daSStefan Achatz static int pyra_get_profile_settings(struct usb_device *usb_dev, 85cb7cf3daSStefan Achatz struct pyra_profile_settings *buf, int number) 86cb7cf3daSStefan Achatz { 87cb7cf3daSStefan Achatz int retval; 88cb7cf3daSStefan Achatz retval = pyra_send_control(usb_dev, number, 89cb7cf3daSStefan Achatz PYRA_CONTROL_REQUEST_PROFILE_SETTINGS); 90cb7cf3daSStefan Achatz if (retval) 91cb7cf3daSStefan Achatz return retval; 921edd5b42SStefan Achatz return roccat_common_receive(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS, 935772f636SStefan Achatz buf, sizeof(struct pyra_profile_settings)); 94cb7cf3daSStefan Achatz } 95cb7cf3daSStefan Achatz 96cb7cf3daSStefan Achatz static int pyra_get_profile_buttons(struct usb_device *usb_dev, 97cb7cf3daSStefan Achatz struct pyra_profile_buttons *buf, int number) 98cb7cf3daSStefan Achatz { 99cb7cf3daSStefan Achatz int retval; 100cb7cf3daSStefan Achatz retval = pyra_send_control(usb_dev, number, 101cb7cf3daSStefan Achatz PYRA_CONTROL_REQUEST_PROFILE_BUTTONS); 102cb7cf3daSStefan Achatz if (retval) 103cb7cf3daSStefan Achatz return retval; 1041edd5b42SStefan Achatz return roccat_common_receive(usb_dev, PYRA_COMMAND_PROFILE_BUTTONS, 1055772f636SStefan Achatz buf, sizeof(struct pyra_profile_buttons)); 106cb7cf3daSStefan Achatz } 107cb7cf3daSStefan Achatz 108cb7cf3daSStefan Achatz static int pyra_get_settings(struct usb_device *usb_dev, 109cb7cf3daSStefan Achatz struct pyra_settings *buf) 110cb7cf3daSStefan Achatz { 1111edd5b42SStefan Achatz return roccat_common_receive(usb_dev, PYRA_COMMAND_SETTINGS, 1125772f636SStefan Achatz buf, sizeof(struct pyra_settings)); 113cb7cf3daSStefan Achatz } 114cb7cf3daSStefan Achatz 115cb7cf3daSStefan Achatz static int pyra_get_info(struct usb_device *usb_dev, struct pyra_info *buf) 116cb7cf3daSStefan Achatz { 1171edd5b42SStefan Achatz return roccat_common_receive(usb_dev, PYRA_COMMAND_INFO, 1185772f636SStefan Achatz buf, sizeof(struct pyra_info)); 1195772f636SStefan Achatz } 1205772f636SStefan Achatz 1215772f636SStefan Achatz static int pyra_send(struct usb_device *usb_dev, uint command, 1225772f636SStefan Achatz void const *buf, uint size) 1235772f636SStefan Achatz { 1245772f636SStefan Achatz int retval; 1255772f636SStefan Achatz retval = roccat_common_send(usb_dev, command, buf, size); 1265772f636SStefan Achatz if (retval) 1275772f636SStefan Achatz return retval; 1285772f636SStefan Achatz return pyra_receive_control_status(usb_dev); 129cb7cf3daSStefan Achatz } 130cb7cf3daSStefan Achatz 131cb7cf3daSStefan Achatz static int pyra_set_profile_settings(struct usb_device *usb_dev, 132cb7cf3daSStefan Achatz struct pyra_profile_settings const *settings) 133cb7cf3daSStefan Achatz { 1341edd5b42SStefan Achatz return pyra_send(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS, settings, 1355772f636SStefan Achatz sizeof(struct pyra_profile_settings)); 136cb7cf3daSStefan Achatz } 137cb7cf3daSStefan Achatz 138cb7cf3daSStefan Achatz static int pyra_set_profile_buttons(struct usb_device *usb_dev, 139cb7cf3daSStefan Achatz struct pyra_profile_buttons const *buttons) 140cb7cf3daSStefan Achatz { 1411edd5b42SStefan Achatz return pyra_send(usb_dev, PYRA_COMMAND_PROFILE_BUTTONS, buttons, 1425772f636SStefan Achatz sizeof(struct pyra_profile_buttons)); 143cb7cf3daSStefan Achatz } 144cb7cf3daSStefan Achatz 145cb7cf3daSStefan Achatz static int pyra_set_settings(struct usb_device *usb_dev, 146cb7cf3daSStefan Achatz struct pyra_settings const *settings) 147cb7cf3daSStefan Achatz { 1481edd5b42SStefan Achatz return pyra_send(usb_dev, PYRA_COMMAND_SETTINGS, settings, 1495772f636SStefan Achatz sizeof(struct pyra_settings)); 150cb7cf3daSStefan Achatz } 151cb7cf3daSStefan Achatz 152cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp, 153cb7cf3daSStefan Achatz struct kobject *kobj, struct bin_attribute *attr, char *buf, 15414a057f8SStefan Achatz loff_t off, size_t count) 155cb7cf3daSStefan Achatz { 1565012aadaSStefan Achatz struct device *dev = 1575012aadaSStefan Achatz container_of(kobj, struct device, kobj)->parent->parent; 158cb7cf3daSStefan Achatz struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); 159cb7cf3daSStefan Achatz 160cb7cf3daSStefan Achatz if (off >= sizeof(struct pyra_profile_settings)) 161cb7cf3daSStefan Achatz return 0; 162cb7cf3daSStefan Achatz 163cb7cf3daSStefan Achatz if (off + count > sizeof(struct pyra_profile_settings)) 164cb7cf3daSStefan Achatz count = sizeof(struct pyra_profile_settings) - off; 165cb7cf3daSStefan Achatz 166cb7cf3daSStefan Achatz mutex_lock(&pyra->pyra_lock); 16714a057f8SStefan Achatz memcpy(buf, ((char const *)&pyra->profile_settings[*(uint *)(attr->private)]) + off, 168cb7cf3daSStefan Achatz count); 169cb7cf3daSStefan Achatz mutex_unlock(&pyra->pyra_lock); 170cb7cf3daSStefan Achatz 171cb7cf3daSStefan Achatz return count; 172cb7cf3daSStefan Achatz } 173cb7cf3daSStefan Achatz 174cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp, 175cb7cf3daSStefan Achatz struct kobject *kobj, struct bin_attribute *attr, char *buf, 17614a057f8SStefan Achatz loff_t off, size_t count) 177cb7cf3daSStefan Achatz { 1785012aadaSStefan Achatz struct device *dev = 1795012aadaSStefan Achatz container_of(kobj, struct device, kobj)->parent->parent; 180cb7cf3daSStefan Achatz struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); 181cb7cf3daSStefan Achatz 182cb7cf3daSStefan Achatz if (off >= sizeof(struct pyra_profile_buttons)) 183cb7cf3daSStefan Achatz return 0; 184cb7cf3daSStefan Achatz 185cb7cf3daSStefan Achatz if (off + count > sizeof(struct pyra_profile_buttons)) 186cb7cf3daSStefan Achatz count = sizeof(struct pyra_profile_buttons) - off; 187cb7cf3daSStefan Achatz 188cb7cf3daSStefan Achatz mutex_lock(&pyra->pyra_lock); 18914a057f8SStefan Achatz memcpy(buf, ((char const *)&pyra->profile_buttons[*(uint *)(attr->private)]) + off, 190cb7cf3daSStefan Achatz count); 191cb7cf3daSStefan Achatz mutex_unlock(&pyra->pyra_lock); 192cb7cf3daSStefan Achatz 193cb7cf3daSStefan Achatz return count; 194cb7cf3daSStefan Achatz } 195cb7cf3daSStefan Achatz 196cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_write_profile_settings(struct file *fp, 197cb7cf3daSStefan Achatz struct kobject *kobj, struct bin_attribute *attr, char *buf, 198cb7cf3daSStefan Achatz loff_t off, size_t count) 199cb7cf3daSStefan Achatz { 2005012aadaSStefan Achatz struct device *dev = 2015012aadaSStefan Achatz container_of(kobj, struct device, kobj)->parent->parent; 202cb7cf3daSStefan Achatz struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); 203cb7cf3daSStefan Achatz struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 204cb7cf3daSStefan Achatz int retval = 0; 205cb7cf3daSStefan Achatz int difference; 206cb7cf3daSStefan Achatz int profile_number; 207cb7cf3daSStefan Achatz struct pyra_profile_settings *profile_settings; 208cb7cf3daSStefan Achatz 209cb7cf3daSStefan Achatz if (off != 0 || count != sizeof(struct pyra_profile_settings)) 210cb7cf3daSStefan Achatz return -EINVAL; 211cb7cf3daSStefan Achatz 212cb7cf3daSStefan Achatz profile_number = ((struct pyra_profile_settings const *)buf)->number; 213cb7cf3daSStefan Achatz profile_settings = &pyra->profile_settings[profile_number]; 214cb7cf3daSStefan Achatz 215cb7cf3daSStefan Achatz mutex_lock(&pyra->pyra_lock); 216cb7cf3daSStefan Achatz difference = memcmp(buf, profile_settings, 217cb7cf3daSStefan Achatz sizeof(struct pyra_profile_settings)); 218cb7cf3daSStefan Achatz if (difference) { 219cb7cf3daSStefan Achatz retval = pyra_set_profile_settings(usb_dev, 220cb7cf3daSStefan Achatz (struct pyra_profile_settings const *)buf); 221cb7cf3daSStefan Achatz if (!retval) 222cb7cf3daSStefan Achatz memcpy(profile_settings, buf, 223cb7cf3daSStefan Achatz sizeof(struct pyra_profile_settings)); 224cb7cf3daSStefan Achatz } 225cb7cf3daSStefan Achatz mutex_unlock(&pyra->pyra_lock); 226cb7cf3daSStefan Achatz 227cb7cf3daSStefan Achatz if (retval) 228cb7cf3daSStefan Achatz return retval; 229cb7cf3daSStefan Achatz 230cb7cf3daSStefan Achatz return sizeof(struct pyra_profile_settings); 231cb7cf3daSStefan Achatz } 232cb7cf3daSStefan Achatz 233cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_write_profile_buttons(struct file *fp, 234cb7cf3daSStefan Achatz struct kobject *kobj, struct bin_attribute *attr, char *buf, 235cb7cf3daSStefan Achatz loff_t off, size_t count) 236cb7cf3daSStefan Achatz { 2375012aadaSStefan Achatz struct device *dev = 2385012aadaSStefan Achatz container_of(kobj, struct device, kobj)->parent->parent; 239cb7cf3daSStefan Achatz struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); 240cb7cf3daSStefan Achatz struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 241cb7cf3daSStefan Achatz int retval = 0; 242cb7cf3daSStefan Achatz int difference; 243cb7cf3daSStefan Achatz int profile_number; 244cb7cf3daSStefan Achatz struct pyra_profile_buttons *profile_buttons; 245cb7cf3daSStefan Achatz 246cb7cf3daSStefan Achatz if (off != 0 || count != sizeof(struct pyra_profile_buttons)) 247cb7cf3daSStefan Achatz return -EINVAL; 248cb7cf3daSStefan Achatz 249cb7cf3daSStefan Achatz profile_number = ((struct pyra_profile_buttons const *)buf)->number; 250cb7cf3daSStefan Achatz profile_buttons = &pyra->profile_buttons[profile_number]; 251cb7cf3daSStefan Achatz 252cb7cf3daSStefan Achatz mutex_lock(&pyra->pyra_lock); 253cb7cf3daSStefan Achatz difference = memcmp(buf, profile_buttons, 254cb7cf3daSStefan Achatz sizeof(struct pyra_profile_buttons)); 255cb7cf3daSStefan Achatz if (difference) { 256cb7cf3daSStefan Achatz retval = pyra_set_profile_buttons(usb_dev, 257cb7cf3daSStefan Achatz (struct pyra_profile_buttons const *)buf); 258cb7cf3daSStefan Achatz if (!retval) 259cb7cf3daSStefan Achatz memcpy(profile_buttons, buf, 260cb7cf3daSStefan Achatz sizeof(struct pyra_profile_buttons)); 261cb7cf3daSStefan Achatz } 262cb7cf3daSStefan Achatz mutex_unlock(&pyra->pyra_lock); 263cb7cf3daSStefan Achatz 264cb7cf3daSStefan Achatz if (retval) 265cb7cf3daSStefan Achatz return retval; 266cb7cf3daSStefan Achatz 267cb7cf3daSStefan Achatz return sizeof(struct pyra_profile_buttons); 268cb7cf3daSStefan Achatz } 269cb7cf3daSStefan Achatz 270cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_read_settings(struct file *fp, 271cb7cf3daSStefan Achatz struct kobject *kobj, struct bin_attribute *attr, char *buf, 272cb7cf3daSStefan Achatz loff_t off, size_t count) 273cb7cf3daSStefan Achatz { 2745012aadaSStefan Achatz struct device *dev = 2755012aadaSStefan Achatz container_of(kobj, struct device, kobj)->parent->parent; 276cb7cf3daSStefan Achatz struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); 277cb7cf3daSStefan Achatz 278cb7cf3daSStefan Achatz if (off >= sizeof(struct pyra_settings)) 279cb7cf3daSStefan Achatz return 0; 280cb7cf3daSStefan Achatz 281cb7cf3daSStefan Achatz if (off + count > sizeof(struct pyra_settings)) 282cb7cf3daSStefan Achatz count = sizeof(struct pyra_settings) - off; 283cb7cf3daSStefan Achatz 284cb7cf3daSStefan Achatz mutex_lock(&pyra->pyra_lock); 285cb7cf3daSStefan Achatz memcpy(buf, ((char const *)&pyra->settings) + off, count); 286cb7cf3daSStefan Achatz mutex_unlock(&pyra->pyra_lock); 287cb7cf3daSStefan Achatz 288cb7cf3daSStefan Achatz return count; 289cb7cf3daSStefan Achatz } 290cb7cf3daSStefan Achatz 291cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_write_settings(struct file *fp, 292cb7cf3daSStefan Achatz struct kobject *kobj, struct bin_attribute *attr, char *buf, 293cb7cf3daSStefan Achatz loff_t off, size_t count) 294cb7cf3daSStefan Achatz { 2955012aadaSStefan Achatz struct device *dev = 2965012aadaSStefan Achatz container_of(kobj, struct device, kobj)->parent->parent; 297cb7cf3daSStefan Achatz struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); 298cb7cf3daSStefan Achatz struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 299cb7cf3daSStefan Achatz int retval = 0; 300cb7cf3daSStefan Achatz int difference; 301dc186b66SStefan Achatz struct pyra_roccat_report roccat_report; 302cb7cf3daSStefan Achatz 303cb7cf3daSStefan Achatz if (off != 0 || count != sizeof(struct pyra_settings)) 304cb7cf3daSStefan Achatz return -EINVAL; 305cb7cf3daSStefan Achatz 306cb7cf3daSStefan Achatz mutex_lock(&pyra->pyra_lock); 307cb7cf3daSStefan Achatz difference = memcmp(buf, &pyra->settings, sizeof(struct pyra_settings)); 308cb7cf3daSStefan Achatz if (difference) { 309cb7cf3daSStefan Achatz retval = pyra_set_settings(usb_dev, 310cb7cf3daSStefan Achatz (struct pyra_settings const *)buf); 311dc186b66SStefan Achatz if (retval) { 312dc186b66SStefan Achatz mutex_unlock(&pyra->pyra_lock); 313dc186b66SStefan Achatz return retval; 314dc186b66SStefan Achatz } 315dc186b66SStefan Achatz 316cb7cf3daSStefan Achatz memcpy(&pyra->settings, buf, 317cb7cf3daSStefan Achatz sizeof(struct pyra_settings)); 318cb7cf3daSStefan Achatz 319cb7cf3daSStefan Achatz profile_activated(pyra, pyra->settings.startup_profile); 320cb7cf3daSStefan Achatz 321dc186b66SStefan Achatz roccat_report.type = PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2; 322dc186b66SStefan Achatz roccat_report.value = pyra->settings.startup_profile + 1; 323dc186b66SStefan Achatz roccat_report.key = 0; 324dc186b66SStefan Achatz roccat_report_event(pyra->chrdev_minor, 325dc186b66SStefan Achatz (uint8_t const *)&roccat_report); 326dc186b66SStefan Achatz } 327dc186b66SStefan Achatz mutex_unlock(&pyra->pyra_lock); 328cb7cf3daSStefan Achatz return sizeof(struct pyra_settings); 329cb7cf3daSStefan Achatz } 330cb7cf3daSStefan Achatz 331cb7cf3daSStefan Achatz 332cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_show_actual_cpi(struct device *dev, 333cb7cf3daSStefan Achatz struct device_attribute *attr, char *buf) 334cb7cf3daSStefan Achatz { 3355012aadaSStefan Achatz struct pyra_device *pyra = 3365012aadaSStefan Achatz hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); 337cb7cf3daSStefan Achatz return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_cpi); 338cb7cf3daSStefan Achatz } 339cb7cf3daSStefan Achatz 340cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_show_actual_profile(struct device *dev, 341cb7cf3daSStefan Achatz struct device_attribute *attr, char *buf) 342cb7cf3daSStefan Achatz { 3435012aadaSStefan Achatz struct pyra_device *pyra = 3445012aadaSStefan Achatz hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); 345cb7cf3daSStefan Achatz return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_profile); 346cb7cf3daSStefan Achatz } 347cb7cf3daSStefan Achatz 348cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_show_firmware_version(struct device *dev, 349cb7cf3daSStefan Achatz struct device_attribute *attr, char *buf) 350cb7cf3daSStefan Achatz { 3515012aadaSStefan Achatz struct pyra_device *pyra = 3525012aadaSStefan Achatz hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); 353cb7cf3daSStefan Achatz return snprintf(buf, PAGE_SIZE, "%d\n", pyra->firmware_version); 354cb7cf3daSStefan Achatz } 355cb7cf3daSStefan Achatz 356cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_show_startup_profile(struct device *dev, 357cb7cf3daSStefan Achatz struct device_attribute *attr, char *buf) 358cb7cf3daSStefan Achatz { 3595012aadaSStefan Achatz struct pyra_device *pyra = 3605012aadaSStefan Achatz hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); 361cb7cf3daSStefan Achatz return snprintf(buf, PAGE_SIZE, "%d\n", pyra->settings.startup_profile); 362cb7cf3daSStefan Achatz } 363cb7cf3daSStefan Achatz 3645012aadaSStefan Achatz static struct device_attribute pyra_attributes[] = { 3655012aadaSStefan Achatz __ATTR(actual_cpi, 0440, pyra_sysfs_show_actual_cpi, NULL), 3665012aadaSStefan Achatz __ATTR(actual_profile, 0440, pyra_sysfs_show_actual_profile, NULL), 3675012aadaSStefan Achatz __ATTR(firmware_version, 0440, 3685012aadaSStefan Achatz pyra_sysfs_show_firmware_version, NULL), 3695012aadaSStefan Achatz __ATTR(startup_profile, 0440, 3705012aadaSStefan Achatz pyra_sysfs_show_startup_profile, NULL), 3715012aadaSStefan Achatz __ATTR_NULL 372cb7cf3daSStefan Achatz }; 373cb7cf3daSStefan Achatz 3745012aadaSStefan Achatz static struct bin_attribute pyra_bin_attributes[] = { 3755012aadaSStefan Achatz { 376cb7cf3daSStefan Achatz .attr = { .name = "profile_settings", .mode = 0220 }, 377cb7cf3daSStefan Achatz .size = sizeof(struct pyra_profile_settings), 378cb7cf3daSStefan Achatz .write = pyra_sysfs_write_profile_settings 3795012aadaSStefan Achatz }, 3805012aadaSStefan Achatz { 381cb7cf3daSStefan Achatz .attr = { .name = "profile1_settings", .mode = 0440 }, 382cb7cf3daSStefan Achatz .size = sizeof(struct pyra_profile_settings), 38314a057f8SStefan Achatz .read = pyra_sysfs_read_profilex_settings, 38414a057f8SStefan Achatz .private = &profile_numbers[0] 3855012aadaSStefan Achatz }, 3865012aadaSStefan Achatz { 387cb7cf3daSStefan Achatz .attr = { .name = "profile2_settings", .mode = 0440 }, 388cb7cf3daSStefan Achatz .size = sizeof(struct pyra_profile_settings), 38914a057f8SStefan Achatz .read = pyra_sysfs_read_profilex_settings, 39014a057f8SStefan Achatz .private = &profile_numbers[1] 3915012aadaSStefan Achatz }, 3925012aadaSStefan Achatz { 393cb7cf3daSStefan Achatz .attr = { .name = "profile3_settings", .mode = 0440 }, 394cb7cf3daSStefan Achatz .size = sizeof(struct pyra_profile_settings), 39514a057f8SStefan Achatz .read = pyra_sysfs_read_profilex_settings, 39614a057f8SStefan Achatz .private = &profile_numbers[2] 3975012aadaSStefan Achatz }, 3985012aadaSStefan Achatz { 399cb7cf3daSStefan Achatz .attr = { .name = "profile4_settings", .mode = 0440 }, 400cb7cf3daSStefan Achatz .size = sizeof(struct pyra_profile_settings), 40114a057f8SStefan Achatz .read = pyra_sysfs_read_profilex_settings, 40214a057f8SStefan Achatz .private = &profile_numbers[3] 4035012aadaSStefan Achatz }, 4045012aadaSStefan Achatz { 405cb7cf3daSStefan Achatz .attr = { .name = "profile5_settings", .mode = 0440 }, 406cb7cf3daSStefan Achatz .size = sizeof(struct pyra_profile_settings), 40714a057f8SStefan Achatz .read = pyra_sysfs_read_profilex_settings, 40814a057f8SStefan Achatz .private = &profile_numbers[4] 4095012aadaSStefan Achatz }, 4105012aadaSStefan Achatz { 411cb7cf3daSStefan Achatz .attr = { .name = "profile_buttons", .mode = 0220 }, 412cb7cf3daSStefan Achatz .size = sizeof(struct pyra_profile_buttons), 413cb7cf3daSStefan Achatz .write = pyra_sysfs_write_profile_buttons 4145012aadaSStefan Achatz }, 4155012aadaSStefan Achatz { 416cb7cf3daSStefan Achatz .attr = { .name = "profile1_buttons", .mode = 0440 }, 417cb7cf3daSStefan Achatz .size = sizeof(struct pyra_profile_buttons), 41814a057f8SStefan Achatz .read = pyra_sysfs_read_profilex_buttons, 41914a057f8SStefan Achatz .private = &profile_numbers[0] 4205012aadaSStefan Achatz }, 4215012aadaSStefan Achatz { 422cb7cf3daSStefan Achatz .attr = { .name = "profile2_buttons", .mode = 0440 }, 423cb7cf3daSStefan Achatz .size = sizeof(struct pyra_profile_buttons), 42414a057f8SStefan Achatz .read = pyra_sysfs_read_profilex_buttons, 42514a057f8SStefan Achatz .private = &profile_numbers[1] 4265012aadaSStefan Achatz }, 4275012aadaSStefan Achatz { 428cb7cf3daSStefan Achatz .attr = { .name = "profile3_buttons", .mode = 0440 }, 429cb7cf3daSStefan Achatz .size = sizeof(struct pyra_profile_buttons), 43014a057f8SStefan Achatz .read = pyra_sysfs_read_profilex_buttons, 43114a057f8SStefan Achatz .private = &profile_numbers[2] 4325012aadaSStefan Achatz }, 4335012aadaSStefan Achatz { 434cb7cf3daSStefan Achatz .attr = { .name = "profile4_buttons", .mode = 0440 }, 435cb7cf3daSStefan Achatz .size = sizeof(struct pyra_profile_buttons), 43614a057f8SStefan Achatz .read = pyra_sysfs_read_profilex_buttons, 43714a057f8SStefan Achatz .private = &profile_numbers[3] 4385012aadaSStefan Achatz }, 4395012aadaSStefan Achatz { 440cb7cf3daSStefan Achatz .attr = { .name = "profile5_buttons", .mode = 0440 }, 441cb7cf3daSStefan Achatz .size = sizeof(struct pyra_profile_buttons), 44214a057f8SStefan Achatz .read = pyra_sysfs_read_profilex_buttons, 44314a057f8SStefan Achatz .private = &profile_numbers[4] 4445012aadaSStefan Achatz }, 4455012aadaSStefan Achatz { 446cb7cf3daSStefan Achatz .attr = { .name = "settings", .mode = 0660 }, 447cb7cf3daSStefan Achatz .size = sizeof(struct pyra_settings), 448cb7cf3daSStefan Achatz .read = pyra_sysfs_read_settings, 449cb7cf3daSStefan Achatz .write = pyra_sysfs_write_settings 4505012aadaSStefan Achatz }, 4515012aadaSStefan Achatz __ATTR_NULL 452cb7cf3daSStefan Achatz }; 453cb7cf3daSStefan Achatz 454cb7cf3daSStefan Achatz static int pyra_init_pyra_device_struct(struct usb_device *usb_dev, 455cb7cf3daSStefan Achatz struct pyra_device *pyra) 456cb7cf3daSStefan Achatz { 4575772f636SStefan Achatz struct pyra_info info; 458cb7cf3daSStefan Achatz int retval, i; 459cb7cf3daSStefan Achatz 460cb7cf3daSStefan Achatz mutex_init(&pyra->pyra_lock); 461cb7cf3daSStefan Achatz 4625772f636SStefan Achatz retval = pyra_get_info(usb_dev, &info); 4635772f636SStefan Achatz if (retval) 464cb7cf3daSStefan Achatz return retval; 4655772f636SStefan Achatz 4665772f636SStefan Achatz pyra->firmware_version = info.firmware_version; 467cb7cf3daSStefan Achatz 468cb7cf3daSStefan Achatz retval = pyra_get_settings(usb_dev, &pyra->settings); 469cb7cf3daSStefan Achatz if (retval) 470cb7cf3daSStefan Achatz return retval; 471cb7cf3daSStefan Achatz 472cb7cf3daSStefan Achatz for (i = 0; i < 5; ++i) { 473cb7cf3daSStefan Achatz retval = pyra_get_profile_settings(usb_dev, 474cb7cf3daSStefan Achatz &pyra->profile_settings[i], i); 475cb7cf3daSStefan Achatz if (retval) 476cb7cf3daSStefan Achatz return retval; 477cb7cf3daSStefan Achatz 478cb7cf3daSStefan Achatz retval = pyra_get_profile_buttons(usb_dev, 479cb7cf3daSStefan Achatz &pyra->profile_buttons[i], i); 480cb7cf3daSStefan Achatz if (retval) 481cb7cf3daSStefan Achatz return retval; 482cb7cf3daSStefan Achatz } 483cb7cf3daSStefan Achatz 484cb7cf3daSStefan Achatz profile_activated(pyra, pyra->settings.startup_profile); 485cb7cf3daSStefan Achatz 486cb7cf3daSStefan Achatz return 0; 487cb7cf3daSStefan Achatz } 488cb7cf3daSStefan Achatz 489cb7cf3daSStefan Achatz static int pyra_init_specials(struct hid_device *hdev) 490cb7cf3daSStefan Achatz { 491cb7cf3daSStefan Achatz struct usb_interface *intf = to_usb_interface(hdev->dev.parent); 492cb7cf3daSStefan Achatz struct usb_device *usb_dev = interface_to_usbdev(intf); 493cb7cf3daSStefan Achatz struct pyra_device *pyra; 494cb7cf3daSStefan Achatz int retval; 495cb7cf3daSStefan Achatz 496cb7cf3daSStefan Achatz if (intf->cur_altsetting->desc.bInterfaceProtocol 497cb7cf3daSStefan Achatz == USB_INTERFACE_PROTOCOL_MOUSE) { 498cb7cf3daSStefan Achatz 499cb7cf3daSStefan Achatz pyra = kzalloc(sizeof(*pyra), GFP_KERNEL); 500cb7cf3daSStefan Achatz if (!pyra) { 5014291ee30SJoe Perches hid_err(hdev, "can't alloc device descriptor\n"); 502cb7cf3daSStefan Achatz return -ENOMEM; 503cb7cf3daSStefan Achatz } 504cb7cf3daSStefan Achatz hid_set_drvdata(hdev, pyra); 505cb7cf3daSStefan Achatz 506cb7cf3daSStefan Achatz retval = pyra_init_pyra_device_struct(usb_dev, pyra); 507cb7cf3daSStefan Achatz if (retval) { 5084291ee30SJoe Perches hid_err(hdev, "couldn't init struct pyra_device\n"); 509cb7cf3daSStefan Achatz goto exit_free; 510cb7cf3daSStefan Achatz } 511cb7cf3daSStefan Achatz 5128211e460SStefan Achatz retval = roccat_connect(pyra_class, hdev, 5138211e460SStefan Achatz sizeof(struct pyra_roccat_report)); 514cb7cf3daSStefan Achatz if (retval < 0) { 5154291ee30SJoe Perches hid_err(hdev, "couldn't init char dev\n"); 516cb7cf3daSStefan Achatz } else { 517cb7cf3daSStefan Achatz pyra->chrdev_minor = retval; 518cb7cf3daSStefan Achatz pyra->roccat_claimed = 1; 519cb7cf3daSStefan Achatz } 520cb7cf3daSStefan Achatz } else { 521cb7cf3daSStefan Achatz hid_set_drvdata(hdev, NULL); 522cb7cf3daSStefan Achatz } 523cb7cf3daSStefan Achatz 524cb7cf3daSStefan Achatz return 0; 525cb7cf3daSStefan Achatz exit_free: 526cb7cf3daSStefan Achatz kfree(pyra); 527cb7cf3daSStefan Achatz return retval; 528cb7cf3daSStefan Achatz } 529cb7cf3daSStefan Achatz 530cb7cf3daSStefan Achatz static void pyra_remove_specials(struct hid_device *hdev) 531cb7cf3daSStefan Achatz { 532cb7cf3daSStefan Achatz struct usb_interface *intf = to_usb_interface(hdev->dev.parent); 533cb7cf3daSStefan Achatz struct pyra_device *pyra; 534cb7cf3daSStefan Achatz 535cb7cf3daSStefan Achatz if (intf->cur_altsetting->desc.bInterfaceProtocol 536cb7cf3daSStefan Achatz == USB_INTERFACE_PROTOCOL_MOUSE) { 537cb7cf3daSStefan Achatz pyra = hid_get_drvdata(hdev); 538cb7cf3daSStefan Achatz if (pyra->roccat_claimed) 539cb7cf3daSStefan Achatz roccat_disconnect(pyra->chrdev_minor); 540cb7cf3daSStefan Achatz kfree(hid_get_drvdata(hdev)); 541cb7cf3daSStefan Achatz } 542cb7cf3daSStefan Achatz } 543cb7cf3daSStefan Achatz 544cb7cf3daSStefan Achatz static int pyra_probe(struct hid_device *hdev, const struct hid_device_id *id) 545cb7cf3daSStefan Achatz { 546cb7cf3daSStefan Achatz int retval; 547cb7cf3daSStefan Achatz 548cb7cf3daSStefan Achatz retval = hid_parse(hdev); 549cb7cf3daSStefan Achatz if (retval) { 5504291ee30SJoe Perches hid_err(hdev, "parse failed\n"); 551cb7cf3daSStefan Achatz goto exit; 552cb7cf3daSStefan Achatz } 553cb7cf3daSStefan Achatz 554cb7cf3daSStefan Achatz retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 555cb7cf3daSStefan Achatz if (retval) { 5564291ee30SJoe Perches hid_err(hdev, "hw start failed\n"); 557cb7cf3daSStefan Achatz goto exit; 558cb7cf3daSStefan Achatz } 559cb7cf3daSStefan Achatz 560cb7cf3daSStefan Achatz retval = pyra_init_specials(hdev); 561cb7cf3daSStefan Achatz if (retval) { 5624291ee30SJoe Perches hid_err(hdev, "couldn't install mouse\n"); 563cb7cf3daSStefan Achatz goto exit_stop; 564cb7cf3daSStefan Achatz } 565cb7cf3daSStefan Achatz return 0; 566cb7cf3daSStefan Achatz 567cb7cf3daSStefan Achatz exit_stop: 568cb7cf3daSStefan Achatz hid_hw_stop(hdev); 569cb7cf3daSStefan Achatz exit: 570cb7cf3daSStefan Achatz return retval; 571cb7cf3daSStefan Achatz } 572cb7cf3daSStefan Achatz 573cb7cf3daSStefan Achatz static void pyra_remove(struct hid_device *hdev) 574cb7cf3daSStefan Achatz { 575cb7cf3daSStefan Achatz pyra_remove_specials(hdev); 576cb7cf3daSStefan Achatz hid_hw_stop(hdev); 577cb7cf3daSStefan Achatz } 578cb7cf3daSStefan Achatz 579cb7cf3daSStefan Achatz static void pyra_keep_values_up_to_date(struct pyra_device *pyra, 580cb7cf3daSStefan Achatz u8 const *data) 581cb7cf3daSStefan Achatz { 582cb7cf3daSStefan Achatz struct pyra_mouse_event_button const *button_event; 583cb7cf3daSStefan Achatz 584cb7cf3daSStefan Achatz switch (data[0]) { 585cb7cf3daSStefan Achatz case PYRA_MOUSE_REPORT_NUMBER_BUTTON: 586cb7cf3daSStefan Achatz button_event = (struct pyra_mouse_event_button const *)data; 587cb7cf3daSStefan Achatz switch (button_event->type) { 588cb7cf3daSStefan Achatz case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2: 589cb7cf3daSStefan Achatz profile_activated(pyra, button_event->data1 - 1); 590cb7cf3daSStefan Achatz break; 591cb7cf3daSStefan Achatz case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI: 592cb7cf3daSStefan Achatz pyra->actual_cpi = button_event->data1; 593cb7cf3daSStefan Achatz break; 594cb7cf3daSStefan Achatz } 595cb7cf3daSStefan Achatz break; 596cb7cf3daSStefan Achatz } 597cb7cf3daSStefan Achatz } 598cb7cf3daSStefan Achatz 599cb7cf3daSStefan Achatz static void pyra_report_to_chrdev(struct pyra_device const *pyra, 600cb7cf3daSStefan Achatz u8 const *data) 601cb7cf3daSStefan Achatz { 602cb7cf3daSStefan Achatz struct pyra_roccat_report roccat_report; 603cb7cf3daSStefan Achatz struct pyra_mouse_event_button const *button_event; 604cb7cf3daSStefan Achatz 605cb7cf3daSStefan Achatz if (data[0] != PYRA_MOUSE_REPORT_NUMBER_BUTTON) 606cb7cf3daSStefan Achatz return; 607cb7cf3daSStefan Achatz 608cb7cf3daSStefan Achatz button_event = (struct pyra_mouse_event_button const *)data; 609cb7cf3daSStefan Achatz 610cb7cf3daSStefan Achatz switch (button_event->type) { 611cb7cf3daSStefan Achatz case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2: 612cb7cf3daSStefan Achatz case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI: 613cb7cf3daSStefan Achatz roccat_report.type = button_event->type; 614cb7cf3daSStefan Achatz roccat_report.value = button_event->data1; 615cb7cf3daSStefan Achatz roccat_report.key = 0; 616cb7cf3daSStefan Achatz roccat_report_event(pyra->chrdev_minor, 6178211e460SStefan Achatz (uint8_t const *)&roccat_report); 618cb7cf3daSStefan Achatz break; 619cb7cf3daSStefan Achatz case PYRA_MOUSE_EVENT_BUTTON_TYPE_MACRO: 620cb7cf3daSStefan Achatz case PYRA_MOUSE_EVENT_BUTTON_TYPE_SHORTCUT: 621cb7cf3daSStefan Achatz case PYRA_MOUSE_EVENT_BUTTON_TYPE_QUICKLAUNCH: 622cb7cf3daSStefan Achatz if (button_event->data2 == PYRA_MOUSE_EVENT_BUTTON_PRESS) { 623cb7cf3daSStefan Achatz roccat_report.type = button_event->type; 624cb7cf3daSStefan Achatz roccat_report.key = button_event->data1; 625d2b570a5SStefan Achatz /* 626d2b570a5SStefan Achatz * pyra reports profile numbers with range 1-5. 627d2b570a5SStefan Achatz * Keeping this behaviour. 628d2b570a5SStefan Achatz */ 629d2b570a5SStefan Achatz roccat_report.value = pyra->actual_profile + 1; 630cb7cf3daSStefan Achatz roccat_report_event(pyra->chrdev_minor, 6318211e460SStefan Achatz (uint8_t const *)&roccat_report); 632cb7cf3daSStefan Achatz } 633cb7cf3daSStefan Achatz break; 634cb7cf3daSStefan Achatz } 635cb7cf3daSStefan Achatz } 636cb7cf3daSStefan Achatz 637cb7cf3daSStefan Achatz static int pyra_raw_event(struct hid_device *hdev, struct hid_report *report, 638cb7cf3daSStefan Achatz u8 *data, int size) 639cb7cf3daSStefan Achatz { 640cb7cf3daSStefan Achatz struct usb_interface *intf = to_usb_interface(hdev->dev.parent); 641cb7cf3daSStefan Achatz struct pyra_device *pyra = hid_get_drvdata(hdev); 642cb7cf3daSStefan Achatz 643cb7cf3daSStefan Achatz if (intf->cur_altsetting->desc.bInterfaceProtocol 644cb7cf3daSStefan Achatz != USB_INTERFACE_PROTOCOL_MOUSE) 645cb7cf3daSStefan Achatz return 0; 646cb7cf3daSStefan Achatz 647901e64dbSStefan Achatz if (pyra == NULL) 648901e64dbSStefan Achatz return 0; 649901e64dbSStefan Achatz 650cb7cf3daSStefan Achatz pyra_keep_values_up_to_date(pyra, data); 651cb7cf3daSStefan Achatz 652cb7cf3daSStefan Achatz if (pyra->roccat_claimed) 653cb7cf3daSStefan Achatz pyra_report_to_chrdev(pyra, data); 654cb7cf3daSStefan Achatz 655cb7cf3daSStefan Achatz return 0; 656cb7cf3daSStefan Achatz } 657cb7cf3daSStefan Achatz 658cb7cf3daSStefan Achatz static const struct hid_device_id pyra_devices[] = { 659cb7cf3daSStefan Achatz { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, 660cb7cf3daSStefan Achatz USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, 6613fce2246SStefan Achatz { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, 6623fce2246SStefan Achatz USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) }, 663cb7cf3daSStefan Achatz { } 664cb7cf3daSStefan Achatz }; 665cb7cf3daSStefan Achatz 666cb7cf3daSStefan Achatz MODULE_DEVICE_TABLE(hid, pyra_devices); 667cb7cf3daSStefan Achatz 668cb7cf3daSStefan Achatz static struct hid_driver pyra_driver = { 669cb7cf3daSStefan Achatz .name = "pyra", 670cb7cf3daSStefan Achatz .id_table = pyra_devices, 671cb7cf3daSStefan Achatz .probe = pyra_probe, 672cb7cf3daSStefan Achatz .remove = pyra_remove, 673cb7cf3daSStefan Achatz .raw_event = pyra_raw_event 674cb7cf3daSStefan Achatz }; 675cb7cf3daSStefan Achatz 676cb7cf3daSStefan Achatz static int __init pyra_init(void) 677cb7cf3daSStefan Achatz { 6785012aadaSStefan Achatz int retval; 6795012aadaSStefan Achatz 6805012aadaSStefan Achatz /* class name has to be same as driver name */ 6815012aadaSStefan Achatz pyra_class = class_create(THIS_MODULE, "pyra"); 6825012aadaSStefan Achatz if (IS_ERR(pyra_class)) 6835012aadaSStefan Achatz return PTR_ERR(pyra_class); 6845012aadaSStefan Achatz pyra_class->dev_attrs = pyra_attributes; 6855012aadaSStefan Achatz pyra_class->dev_bin_attrs = pyra_bin_attributes; 6865012aadaSStefan Achatz 6875012aadaSStefan Achatz retval = hid_register_driver(&pyra_driver); 6885012aadaSStefan Achatz if (retval) 6895012aadaSStefan Achatz class_destroy(pyra_class); 6905012aadaSStefan Achatz return retval; 691cb7cf3daSStefan Achatz } 692cb7cf3daSStefan Achatz 693cb7cf3daSStefan Achatz static void __exit pyra_exit(void) 694cb7cf3daSStefan Achatz { 695cb7cf3daSStefan Achatz hid_unregister_driver(&pyra_driver); 69674b643daSStefan Achatz class_destroy(pyra_class); 697cb7cf3daSStefan Achatz } 698cb7cf3daSStefan Achatz 699cb7cf3daSStefan Achatz module_init(pyra_init); 700cb7cf3daSStefan Achatz module_exit(pyra_exit); 701cb7cf3daSStefan Achatz 702cb7cf3daSStefan Achatz MODULE_AUTHOR("Stefan Achatz"); 703cb7cf3daSStefan Achatz MODULE_DESCRIPTION("USB Roccat Pyra driver"); 704cb7cf3daSStefan Achatz MODULE_LICENSE("GPL v2"); 705