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 { 457392d73bSStefan Achatz struct roccat_common2_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 524728f2dcSStefan Achatz control.command = ROCCAT_COMMON_COMMAND_CONTROL; 53cb7cf3daSStefan Achatz control.value = value; 54cb7cf3daSStefan Achatz control.request = request; 55cb7cf3daSStefan Achatz 567392d73bSStefan Achatz return roccat_common2_send(usb_dev, ROCCAT_COMMON_COMMAND_CONTROL, 577392d73bSStefan Achatz &control, sizeof(struct roccat_common2_control)); 58cb7cf3daSStefan Achatz } 59cb7cf3daSStefan Achatz 60cb7cf3daSStefan Achatz static int pyra_get_profile_settings(struct usb_device *usb_dev, 61cb7cf3daSStefan Achatz struct pyra_profile_settings *buf, int number) 62cb7cf3daSStefan Achatz { 63cb7cf3daSStefan Achatz int retval; 64cb7cf3daSStefan Achatz retval = pyra_send_control(usb_dev, number, 65cb7cf3daSStefan Achatz PYRA_CONTROL_REQUEST_PROFILE_SETTINGS); 66cb7cf3daSStefan Achatz if (retval) 67cb7cf3daSStefan Achatz return retval; 687392d73bSStefan Achatz return roccat_common2_receive(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS, 69be34380eSStefan Achatz buf, PYRA_SIZE_PROFILE_SETTINGS); 70cb7cf3daSStefan Achatz } 71cb7cf3daSStefan Achatz 72cb7cf3daSStefan Achatz static int pyra_get_settings(struct usb_device *usb_dev, 73cb7cf3daSStefan Achatz struct pyra_settings *buf) 74cb7cf3daSStefan Achatz { 757392d73bSStefan Achatz return roccat_common2_receive(usb_dev, PYRA_COMMAND_SETTINGS, 76be34380eSStefan Achatz buf, PYRA_SIZE_SETTINGS); 77cb7cf3daSStefan Achatz } 78cb7cf3daSStefan Achatz 79cb7cf3daSStefan Achatz static int pyra_set_settings(struct usb_device *usb_dev, 80cb7cf3daSStefan Achatz struct pyra_settings const *settings) 81cb7cf3daSStefan Achatz { 827392d73bSStefan Achatz return roccat_common2_send_with_status(usb_dev, 834728f2dcSStefan Achatz PYRA_COMMAND_SETTINGS, settings, 84be34380eSStefan Achatz PYRA_SIZE_SETTINGS); 85cb7cf3daSStefan Achatz } 86cb7cf3daSStefan Achatz 87be34380eSStefan Achatz static ssize_t pyra_sysfs_read(struct file *fp, struct kobject *kobj, 88be34380eSStefan Achatz char *buf, loff_t off, size_t count, 89be34380eSStefan Achatz size_t real_size, uint command) 90be34380eSStefan Achatz { 91be34380eSStefan Achatz struct device *dev = 92be34380eSStefan Achatz container_of(kobj, struct device, kobj)->parent->parent; 93be34380eSStefan Achatz struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); 94be34380eSStefan Achatz struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 95be34380eSStefan Achatz int retval; 96be34380eSStefan Achatz 97be34380eSStefan Achatz if (off >= real_size) 98be34380eSStefan Achatz return 0; 99be34380eSStefan Achatz 100be34380eSStefan Achatz if (off != 0 || count != real_size) 101be34380eSStefan Achatz return -EINVAL; 102be34380eSStefan Achatz 103be34380eSStefan Achatz mutex_lock(&pyra->pyra_lock); 104be34380eSStefan Achatz retval = roccat_common2_receive(usb_dev, command, buf, real_size); 105be34380eSStefan Achatz mutex_unlock(&pyra->pyra_lock); 106be34380eSStefan Achatz 107be34380eSStefan Achatz if (retval) 108be34380eSStefan Achatz return retval; 109be34380eSStefan Achatz 110be34380eSStefan Achatz return real_size; 111be34380eSStefan Achatz } 112be34380eSStefan Achatz 113be34380eSStefan Achatz static ssize_t pyra_sysfs_write(struct file *fp, struct kobject *kobj, 114be34380eSStefan Achatz void const *buf, loff_t off, size_t count, 115be34380eSStefan Achatz size_t real_size, uint command) 116be34380eSStefan Achatz { 117be34380eSStefan Achatz struct device *dev = 118be34380eSStefan Achatz container_of(kobj, struct device, kobj)->parent->parent; 119be34380eSStefan Achatz struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); 120be34380eSStefan Achatz struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 121be34380eSStefan Achatz int retval; 122be34380eSStefan Achatz 123be34380eSStefan Achatz if (off != 0 || count != real_size) 124be34380eSStefan Achatz return -EINVAL; 125be34380eSStefan Achatz 126be34380eSStefan Achatz mutex_lock(&pyra->pyra_lock); 127be34380eSStefan Achatz retval = roccat_common2_send_with_status(usb_dev, command, (void *)buf, real_size); 128be34380eSStefan Achatz mutex_unlock(&pyra->pyra_lock); 129be34380eSStefan Achatz 130be34380eSStefan Achatz if (retval) 131be34380eSStefan Achatz return retval; 132be34380eSStefan Achatz 133be34380eSStefan Achatz return real_size; 134be34380eSStefan Achatz } 135be34380eSStefan Achatz 136be34380eSStefan Achatz #define PYRA_SYSFS_W(thingy, THINGY) \ 137be34380eSStefan Achatz static ssize_t pyra_sysfs_write_ ## thingy(struct file *fp, \ 138be34380eSStefan Achatz struct kobject *kobj, struct bin_attribute *attr, char *buf, \ 139be34380eSStefan Achatz loff_t off, size_t count) \ 140be34380eSStefan Achatz { \ 141be34380eSStefan Achatz return pyra_sysfs_write(fp, kobj, buf, off, count, \ 142be34380eSStefan Achatz PYRA_SIZE_ ## THINGY, PYRA_COMMAND_ ## THINGY); \ 143be34380eSStefan Achatz } 144be34380eSStefan Achatz 145be34380eSStefan Achatz #define PYRA_SYSFS_R(thingy, THINGY) \ 146be34380eSStefan Achatz static ssize_t pyra_sysfs_read_ ## thingy(struct file *fp, \ 147be34380eSStefan Achatz struct kobject *kobj, struct bin_attribute *attr, char *buf, \ 148be34380eSStefan Achatz loff_t off, size_t count) \ 149be34380eSStefan Achatz { \ 150be34380eSStefan Achatz return pyra_sysfs_read(fp, kobj, buf, off, count, \ 151be34380eSStefan Achatz PYRA_SIZE_ ## THINGY, PYRA_COMMAND_ ## THINGY); \ 152be34380eSStefan Achatz } 153be34380eSStefan Achatz 154be34380eSStefan Achatz #define PYRA_SYSFS_RW(thingy, THINGY) \ 155be34380eSStefan Achatz PYRA_SYSFS_W(thingy, THINGY) \ 156be34380eSStefan Achatz PYRA_SYSFS_R(thingy, THINGY) 157be34380eSStefan Achatz 158be34380eSStefan Achatz #define PYRA_BIN_ATTRIBUTE_RW(thingy, THINGY) \ 159be34380eSStefan Achatz { \ 160be34380eSStefan Achatz .attr = { .name = #thingy, .mode = 0660 }, \ 161be34380eSStefan Achatz .size = PYRA_SIZE_ ## THINGY, \ 162be34380eSStefan Achatz .read = pyra_sysfs_read_ ## thingy, \ 163be34380eSStefan Achatz .write = pyra_sysfs_write_ ## thingy \ 164be34380eSStefan Achatz } 165be34380eSStefan Achatz 166be34380eSStefan Achatz #define PYRA_BIN_ATTRIBUTE_R(thingy, THINGY) \ 167be34380eSStefan Achatz { \ 168be34380eSStefan Achatz .attr = { .name = #thingy, .mode = 0440 }, \ 169be34380eSStefan Achatz .size = PYRA_SIZE_ ## THINGY, \ 170be34380eSStefan Achatz .read = pyra_sysfs_read_ ## thingy, \ 171be34380eSStefan Achatz } 172be34380eSStefan Achatz 173be34380eSStefan Achatz #define PYRA_BIN_ATTRIBUTE_W(thingy, THINGY) \ 174be34380eSStefan Achatz { \ 175be34380eSStefan Achatz .attr = { .name = #thingy, .mode = 0220 }, \ 176be34380eSStefan Achatz .size = PYRA_SIZE_ ## THINGY, \ 177be34380eSStefan Achatz .write = pyra_sysfs_write_ ## thingy \ 178be34380eSStefan Achatz } 179be34380eSStefan Achatz 180ecbfe7aaSStefan Achatz PYRA_SYSFS_W(control, CONTROL) 181be34380eSStefan Achatz PYRA_SYSFS_RW(info, INFO) 182ecbfe7aaSStefan Achatz PYRA_SYSFS_RW(profile_settings, PROFILE_SETTINGS) 183ecbfe7aaSStefan Achatz PYRA_SYSFS_RW(profile_buttons, PROFILE_BUTTONS) 184be34380eSStefan Achatz PYRA_SYSFS_R(settings, SETTINGS) 185be34380eSStefan Achatz 186cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp, 187cb7cf3daSStefan Achatz struct kobject *kobj, struct bin_attribute *attr, char *buf, 18814a057f8SStefan Achatz loff_t off, size_t count) 189cb7cf3daSStefan Achatz { 1905012aadaSStefan Achatz struct device *dev = 1915012aadaSStefan Achatz container_of(kobj, struct device, kobj)->parent->parent; 192be34380eSStefan Achatz struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 193be34380eSStefan Achatz ssize_t retval; 194cb7cf3daSStefan Achatz 195be34380eSStefan Achatz retval = pyra_send_control(usb_dev, *(uint *)(attr->private), 196be34380eSStefan Achatz PYRA_CONTROL_REQUEST_PROFILE_SETTINGS); 197be34380eSStefan Achatz if (retval) 198be34380eSStefan Achatz return retval; 199cb7cf3daSStefan Achatz 200be34380eSStefan Achatz return pyra_sysfs_read(fp, kobj, buf, off, count, 201be34380eSStefan Achatz PYRA_SIZE_PROFILE_SETTINGS, 202be34380eSStefan Achatz PYRA_COMMAND_PROFILE_SETTINGS); 203cb7cf3daSStefan Achatz } 204cb7cf3daSStefan Achatz 205cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp, 206cb7cf3daSStefan Achatz struct kobject *kobj, struct bin_attribute *attr, char *buf, 20714a057f8SStefan Achatz loff_t off, size_t count) 208cb7cf3daSStefan Achatz { 2095012aadaSStefan Achatz struct device *dev = 2105012aadaSStefan Achatz container_of(kobj, struct device, kobj)->parent->parent; 211cb7cf3daSStefan Achatz struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 212be34380eSStefan Achatz ssize_t retval; 213cb7cf3daSStefan Achatz 214be34380eSStefan Achatz retval = pyra_send_control(usb_dev, *(uint *)(attr->private), 215be34380eSStefan Achatz PYRA_CONTROL_REQUEST_PROFILE_BUTTONS); 216cb7cf3daSStefan Achatz if (retval) 217cb7cf3daSStefan Achatz return retval; 218cb7cf3daSStefan Achatz 219be34380eSStefan Achatz return pyra_sysfs_read(fp, kobj, buf, off, count, 220be34380eSStefan Achatz PYRA_SIZE_PROFILE_BUTTONS, 221be34380eSStefan Achatz PYRA_COMMAND_PROFILE_BUTTONS); 222cb7cf3daSStefan Achatz } 223cb7cf3daSStefan Achatz 224cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_write_settings(struct file *fp, 225cb7cf3daSStefan Achatz struct kobject *kobj, struct bin_attribute *attr, char *buf, 226cb7cf3daSStefan Achatz loff_t off, size_t count) 227cb7cf3daSStefan Achatz { 2285012aadaSStefan Achatz struct device *dev = 2295012aadaSStefan Achatz container_of(kobj, struct device, kobj)->parent->parent; 230cb7cf3daSStefan Achatz struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); 231cb7cf3daSStefan Achatz struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 232cb7cf3daSStefan Achatz int retval = 0; 233dc186b66SStefan Achatz struct pyra_roccat_report roccat_report; 234be34380eSStefan Achatz struct pyra_settings const *settings; 235cb7cf3daSStefan Achatz 236be34380eSStefan Achatz if (off != 0 || count != PYRA_SIZE_SETTINGS) 237cb7cf3daSStefan Achatz return -EINVAL; 238cb7cf3daSStefan Achatz 239cb7cf3daSStefan Achatz mutex_lock(&pyra->pyra_lock); 240be34380eSStefan Achatz 241be34380eSStefan Achatz settings = (struct pyra_settings const *)buf; 242be34380eSStefan Achatz 243be34380eSStefan Achatz retval = pyra_set_settings(usb_dev, settings); 244dc186b66SStefan Achatz if (retval) { 245dc186b66SStefan Achatz mutex_unlock(&pyra->pyra_lock); 246dc186b66SStefan Achatz return retval; 247dc186b66SStefan Achatz } 248dc186b66SStefan Achatz 249be34380eSStefan Achatz profile_activated(pyra, settings->startup_profile); 250cb7cf3daSStefan Achatz 251dc186b66SStefan Achatz roccat_report.type = PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2; 252be34380eSStefan Achatz roccat_report.value = settings->startup_profile + 1; 253dc186b66SStefan Achatz roccat_report.key = 0; 254dc186b66SStefan Achatz roccat_report_event(pyra->chrdev_minor, 255dc186b66SStefan Achatz (uint8_t const *)&roccat_report); 256be34380eSStefan Achatz 257dc186b66SStefan Achatz mutex_unlock(&pyra->pyra_lock); 258be34380eSStefan Achatz return PYRA_SIZE_SETTINGS; 259cb7cf3daSStefan Achatz } 260cb7cf3daSStefan Achatz 261cb7cf3daSStefan Achatz 262cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_show_actual_cpi(struct device *dev, 263cb7cf3daSStefan Achatz struct device_attribute *attr, char *buf) 264cb7cf3daSStefan Achatz { 2655012aadaSStefan Achatz struct pyra_device *pyra = 2665012aadaSStefan Achatz hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); 267cb7cf3daSStefan Achatz return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_cpi); 268cb7cf3daSStefan Achatz } 26946a58c44SGreg Kroah-Hartman static DEVICE_ATTR(actual_cpi, 0440, pyra_sysfs_show_actual_cpi, NULL); 270cb7cf3daSStefan Achatz 271cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_show_actual_profile(struct device *dev, 272cb7cf3daSStefan Achatz struct device_attribute *attr, char *buf) 273cb7cf3daSStefan Achatz { 2745012aadaSStefan Achatz struct pyra_device *pyra = 2755012aadaSStefan Achatz hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); 276be34380eSStefan Achatz struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); 277be34380eSStefan Achatz struct pyra_settings settings; 278be34380eSStefan Achatz 279be34380eSStefan Achatz mutex_lock(&pyra->pyra_lock); 280be34380eSStefan Achatz roccat_common2_receive(usb_dev, PYRA_COMMAND_SETTINGS, 281be34380eSStefan Achatz &settings, PYRA_SIZE_SETTINGS); 282be34380eSStefan Achatz mutex_unlock(&pyra->pyra_lock); 283be34380eSStefan Achatz 284be34380eSStefan Achatz return snprintf(buf, PAGE_SIZE, "%d\n", settings.startup_profile); 285cb7cf3daSStefan Achatz } 28646a58c44SGreg Kroah-Hartman static DEVICE_ATTR(actual_profile, 0440, pyra_sysfs_show_actual_profile, NULL); 28746a58c44SGreg Kroah-Hartman static DEVICE_ATTR(startup_profile, 0440, pyra_sysfs_show_actual_profile, NULL); 288cb7cf3daSStefan Achatz 289cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_show_firmware_version(struct device *dev, 290cb7cf3daSStefan Achatz struct device_attribute *attr, char *buf) 291cb7cf3daSStefan Achatz { 292be34380eSStefan Achatz struct pyra_device *pyra; 293be34380eSStefan Achatz struct usb_device *usb_dev; 294be34380eSStefan Achatz struct pyra_info info; 295cb7cf3daSStefan Achatz 296be34380eSStefan Achatz dev = dev->parent->parent; 297be34380eSStefan Achatz pyra = hid_get_drvdata(dev_get_drvdata(dev)); 298be34380eSStefan Achatz usb_dev = interface_to_usbdev(to_usb_interface(dev)); 299be34380eSStefan Achatz 300be34380eSStefan Achatz mutex_lock(&pyra->pyra_lock); 301be34380eSStefan Achatz roccat_common2_receive(usb_dev, PYRA_COMMAND_INFO, 302be34380eSStefan Achatz &info, PYRA_SIZE_INFO); 303be34380eSStefan Achatz mutex_unlock(&pyra->pyra_lock); 304be34380eSStefan Achatz 305be34380eSStefan Achatz return snprintf(buf, PAGE_SIZE, "%d\n", info.firmware_version); 306cb7cf3daSStefan Achatz } 30746a58c44SGreg Kroah-Hartman static DEVICE_ATTR(firmware_version, 0440, pyra_sysfs_show_firmware_version, 30846a58c44SGreg Kroah-Hartman NULL); 309cb7cf3daSStefan Achatz 31046a58c44SGreg Kroah-Hartman static struct attribute *pyra_attrs[] = { 31146a58c44SGreg Kroah-Hartman &dev_attr_actual_cpi.attr, 31246a58c44SGreg Kroah-Hartman &dev_attr_actual_profile.attr, 31346a58c44SGreg Kroah-Hartman &dev_attr_firmware_version.attr, 31446a58c44SGreg Kroah-Hartman &dev_attr_startup_profile.attr, 31546a58c44SGreg Kroah-Hartman NULL, 316cb7cf3daSStefan Achatz }; 31746a58c44SGreg Kroah-Hartman ATTRIBUTE_GROUPS(pyra); 318cb7cf3daSStefan Achatz 3195012aadaSStefan Achatz static struct bin_attribute pyra_bin_attributes[] = { 320ecbfe7aaSStefan Achatz PYRA_BIN_ATTRIBUTE_W(control, CONTROL), 321be34380eSStefan Achatz PYRA_BIN_ATTRIBUTE_RW(info, INFO), 322ecbfe7aaSStefan Achatz PYRA_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS), 323ecbfe7aaSStefan Achatz PYRA_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS), 324be34380eSStefan Achatz PYRA_BIN_ATTRIBUTE_RW(settings, SETTINGS), 3255012aadaSStefan Achatz { 326cb7cf3daSStefan Achatz .attr = { .name = "profile1_settings", .mode = 0440 }, 327be34380eSStefan Achatz .size = PYRA_SIZE_PROFILE_SETTINGS, 32814a057f8SStefan Achatz .read = pyra_sysfs_read_profilex_settings, 32914a057f8SStefan Achatz .private = &profile_numbers[0] 3305012aadaSStefan Achatz }, 3315012aadaSStefan Achatz { 332cb7cf3daSStefan Achatz .attr = { .name = "profile2_settings", .mode = 0440 }, 333be34380eSStefan Achatz .size = PYRA_SIZE_PROFILE_SETTINGS, 33414a057f8SStefan Achatz .read = pyra_sysfs_read_profilex_settings, 33514a057f8SStefan Achatz .private = &profile_numbers[1] 3365012aadaSStefan Achatz }, 3375012aadaSStefan Achatz { 338cb7cf3daSStefan Achatz .attr = { .name = "profile3_settings", .mode = 0440 }, 339be34380eSStefan Achatz .size = PYRA_SIZE_PROFILE_SETTINGS, 34014a057f8SStefan Achatz .read = pyra_sysfs_read_profilex_settings, 34114a057f8SStefan Achatz .private = &profile_numbers[2] 3425012aadaSStefan Achatz }, 3435012aadaSStefan Achatz { 344cb7cf3daSStefan Achatz .attr = { .name = "profile4_settings", .mode = 0440 }, 345be34380eSStefan Achatz .size = PYRA_SIZE_PROFILE_SETTINGS, 34614a057f8SStefan Achatz .read = pyra_sysfs_read_profilex_settings, 34714a057f8SStefan Achatz .private = &profile_numbers[3] 3485012aadaSStefan Achatz }, 3495012aadaSStefan Achatz { 350cb7cf3daSStefan Achatz .attr = { .name = "profile5_settings", .mode = 0440 }, 351be34380eSStefan Achatz .size = PYRA_SIZE_PROFILE_SETTINGS, 35214a057f8SStefan Achatz .read = pyra_sysfs_read_profilex_settings, 35314a057f8SStefan Achatz .private = &profile_numbers[4] 3545012aadaSStefan Achatz }, 3555012aadaSStefan Achatz { 356cb7cf3daSStefan Achatz .attr = { .name = "profile1_buttons", .mode = 0440 }, 357be34380eSStefan Achatz .size = PYRA_SIZE_PROFILE_BUTTONS, 35814a057f8SStefan Achatz .read = pyra_sysfs_read_profilex_buttons, 35914a057f8SStefan Achatz .private = &profile_numbers[0] 3605012aadaSStefan Achatz }, 3615012aadaSStefan Achatz { 362cb7cf3daSStefan Achatz .attr = { .name = "profile2_buttons", .mode = 0440 }, 363be34380eSStefan Achatz .size = PYRA_SIZE_PROFILE_BUTTONS, 36414a057f8SStefan Achatz .read = pyra_sysfs_read_profilex_buttons, 36514a057f8SStefan Achatz .private = &profile_numbers[1] 3665012aadaSStefan Achatz }, 3675012aadaSStefan Achatz { 368cb7cf3daSStefan Achatz .attr = { .name = "profile3_buttons", .mode = 0440 }, 369be34380eSStefan Achatz .size = PYRA_SIZE_PROFILE_BUTTONS, 37014a057f8SStefan Achatz .read = pyra_sysfs_read_profilex_buttons, 37114a057f8SStefan Achatz .private = &profile_numbers[2] 3725012aadaSStefan Achatz }, 3735012aadaSStefan Achatz { 374cb7cf3daSStefan Achatz .attr = { .name = "profile4_buttons", .mode = 0440 }, 375be34380eSStefan Achatz .size = PYRA_SIZE_PROFILE_BUTTONS, 37614a057f8SStefan Achatz .read = pyra_sysfs_read_profilex_buttons, 37714a057f8SStefan Achatz .private = &profile_numbers[3] 3785012aadaSStefan Achatz }, 3795012aadaSStefan Achatz { 380cb7cf3daSStefan Achatz .attr = { .name = "profile5_buttons", .mode = 0440 }, 381be34380eSStefan Achatz .size = PYRA_SIZE_PROFILE_BUTTONS, 38214a057f8SStefan Achatz .read = pyra_sysfs_read_profilex_buttons, 38314a057f8SStefan Achatz .private = &profile_numbers[4] 3845012aadaSStefan Achatz }, 3855012aadaSStefan Achatz __ATTR_NULL 386cb7cf3daSStefan Achatz }; 387cb7cf3daSStefan Achatz 388cb7cf3daSStefan Achatz static int pyra_init_pyra_device_struct(struct usb_device *usb_dev, 389cb7cf3daSStefan Achatz struct pyra_device *pyra) 390cb7cf3daSStefan Achatz { 391be34380eSStefan Achatz struct pyra_settings settings; 392cb7cf3daSStefan Achatz int retval, i; 393cb7cf3daSStefan Achatz 394cb7cf3daSStefan Achatz mutex_init(&pyra->pyra_lock); 395cb7cf3daSStefan Achatz 396be34380eSStefan Achatz retval = pyra_get_settings(usb_dev, &settings); 397cb7cf3daSStefan Achatz if (retval) 398cb7cf3daSStefan Achatz return retval; 399cb7cf3daSStefan Achatz 400cb7cf3daSStefan Achatz for (i = 0; i < 5; ++i) { 401cb7cf3daSStefan Achatz retval = pyra_get_profile_settings(usb_dev, 402cb7cf3daSStefan Achatz &pyra->profile_settings[i], i); 403cb7cf3daSStefan Achatz if (retval) 404cb7cf3daSStefan Achatz return retval; 405cb7cf3daSStefan Achatz } 406cb7cf3daSStefan Achatz 407be34380eSStefan Achatz profile_activated(pyra, settings.startup_profile); 408cb7cf3daSStefan Achatz 409cb7cf3daSStefan Achatz return 0; 410cb7cf3daSStefan Achatz } 411cb7cf3daSStefan Achatz 412cb7cf3daSStefan Achatz static int pyra_init_specials(struct hid_device *hdev) 413cb7cf3daSStefan Achatz { 414cb7cf3daSStefan Achatz struct usb_interface *intf = to_usb_interface(hdev->dev.parent); 415cb7cf3daSStefan Achatz struct usb_device *usb_dev = interface_to_usbdev(intf); 416cb7cf3daSStefan Achatz struct pyra_device *pyra; 417cb7cf3daSStefan Achatz int retval; 418cb7cf3daSStefan Achatz 419cb7cf3daSStefan Achatz if (intf->cur_altsetting->desc.bInterfaceProtocol 420cb7cf3daSStefan Achatz == USB_INTERFACE_PROTOCOL_MOUSE) { 421cb7cf3daSStefan Achatz 422cb7cf3daSStefan Achatz pyra = kzalloc(sizeof(*pyra), GFP_KERNEL); 423cb7cf3daSStefan Achatz if (!pyra) { 4244291ee30SJoe Perches hid_err(hdev, "can't alloc device descriptor\n"); 425cb7cf3daSStefan Achatz return -ENOMEM; 426cb7cf3daSStefan Achatz } 427cb7cf3daSStefan Achatz hid_set_drvdata(hdev, pyra); 428cb7cf3daSStefan Achatz 429cb7cf3daSStefan Achatz retval = pyra_init_pyra_device_struct(usb_dev, pyra); 430cb7cf3daSStefan Achatz if (retval) { 4314291ee30SJoe Perches hid_err(hdev, "couldn't init struct pyra_device\n"); 432cb7cf3daSStefan Achatz goto exit_free; 433cb7cf3daSStefan Achatz } 434cb7cf3daSStefan Achatz 4358211e460SStefan Achatz retval = roccat_connect(pyra_class, hdev, 4368211e460SStefan Achatz sizeof(struct pyra_roccat_report)); 437cb7cf3daSStefan Achatz if (retval < 0) { 4384291ee30SJoe Perches hid_err(hdev, "couldn't init char dev\n"); 439cb7cf3daSStefan Achatz } else { 440cb7cf3daSStefan Achatz pyra->chrdev_minor = retval; 441cb7cf3daSStefan Achatz pyra->roccat_claimed = 1; 442cb7cf3daSStefan Achatz } 443cb7cf3daSStefan Achatz } else { 444cb7cf3daSStefan Achatz hid_set_drvdata(hdev, NULL); 445cb7cf3daSStefan Achatz } 446cb7cf3daSStefan Achatz 447cb7cf3daSStefan Achatz return 0; 448cb7cf3daSStefan Achatz exit_free: 449cb7cf3daSStefan Achatz kfree(pyra); 450cb7cf3daSStefan Achatz return retval; 451cb7cf3daSStefan Achatz } 452cb7cf3daSStefan Achatz 453cb7cf3daSStefan Achatz static void pyra_remove_specials(struct hid_device *hdev) 454cb7cf3daSStefan Achatz { 455cb7cf3daSStefan Achatz struct usb_interface *intf = to_usb_interface(hdev->dev.parent); 456cb7cf3daSStefan Achatz struct pyra_device *pyra; 457cb7cf3daSStefan Achatz 458cb7cf3daSStefan Achatz if (intf->cur_altsetting->desc.bInterfaceProtocol 459cb7cf3daSStefan Achatz == USB_INTERFACE_PROTOCOL_MOUSE) { 460cb7cf3daSStefan Achatz pyra = hid_get_drvdata(hdev); 461cb7cf3daSStefan Achatz if (pyra->roccat_claimed) 462cb7cf3daSStefan Achatz roccat_disconnect(pyra->chrdev_minor); 463cb7cf3daSStefan Achatz kfree(hid_get_drvdata(hdev)); 464cb7cf3daSStefan Achatz } 465cb7cf3daSStefan Achatz } 466cb7cf3daSStefan Achatz 467cb7cf3daSStefan Achatz static int pyra_probe(struct hid_device *hdev, const struct hid_device_id *id) 468cb7cf3daSStefan Achatz { 469cb7cf3daSStefan Achatz int retval; 470cb7cf3daSStefan Achatz 471cb7cf3daSStefan Achatz retval = hid_parse(hdev); 472cb7cf3daSStefan Achatz if (retval) { 4734291ee30SJoe Perches hid_err(hdev, "parse failed\n"); 474cb7cf3daSStefan Achatz goto exit; 475cb7cf3daSStefan Achatz } 476cb7cf3daSStefan Achatz 477cb7cf3daSStefan Achatz retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 478cb7cf3daSStefan Achatz if (retval) { 4794291ee30SJoe Perches hid_err(hdev, "hw start failed\n"); 480cb7cf3daSStefan Achatz goto exit; 481cb7cf3daSStefan Achatz } 482cb7cf3daSStefan Achatz 483cb7cf3daSStefan Achatz retval = pyra_init_specials(hdev); 484cb7cf3daSStefan Achatz if (retval) { 4854291ee30SJoe Perches hid_err(hdev, "couldn't install mouse\n"); 486cb7cf3daSStefan Achatz goto exit_stop; 487cb7cf3daSStefan Achatz } 488cb7cf3daSStefan Achatz return 0; 489cb7cf3daSStefan Achatz 490cb7cf3daSStefan Achatz exit_stop: 491cb7cf3daSStefan Achatz hid_hw_stop(hdev); 492cb7cf3daSStefan Achatz exit: 493cb7cf3daSStefan Achatz return retval; 494cb7cf3daSStefan Achatz } 495cb7cf3daSStefan Achatz 496cb7cf3daSStefan Achatz static void pyra_remove(struct hid_device *hdev) 497cb7cf3daSStefan Achatz { 498cb7cf3daSStefan Achatz pyra_remove_specials(hdev); 499cb7cf3daSStefan Achatz hid_hw_stop(hdev); 500cb7cf3daSStefan Achatz } 501cb7cf3daSStefan Achatz 502cb7cf3daSStefan Achatz static void pyra_keep_values_up_to_date(struct pyra_device *pyra, 503cb7cf3daSStefan Achatz u8 const *data) 504cb7cf3daSStefan Achatz { 505cb7cf3daSStefan Achatz struct pyra_mouse_event_button const *button_event; 506cb7cf3daSStefan Achatz 507cb7cf3daSStefan Achatz switch (data[0]) { 508cb7cf3daSStefan Achatz case PYRA_MOUSE_REPORT_NUMBER_BUTTON: 509cb7cf3daSStefan Achatz button_event = (struct pyra_mouse_event_button const *)data; 510cb7cf3daSStefan Achatz switch (button_event->type) { 511cb7cf3daSStefan Achatz case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2: 512cb7cf3daSStefan Achatz profile_activated(pyra, button_event->data1 - 1); 513cb7cf3daSStefan Achatz break; 514cb7cf3daSStefan Achatz case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI: 515cb7cf3daSStefan Achatz pyra->actual_cpi = button_event->data1; 516cb7cf3daSStefan Achatz break; 517cb7cf3daSStefan Achatz } 518cb7cf3daSStefan Achatz break; 519cb7cf3daSStefan Achatz } 520cb7cf3daSStefan Achatz } 521cb7cf3daSStefan Achatz 522cb7cf3daSStefan Achatz static void pyra_report_to_chrdev(struct pyra_device const *pyra, 523cb7cf3daSStefan Achatz u8 const *data) 524cb7cf3daSStefan Achatz { 525cb7cf3daSStefan Achatz struct pyra_roccat_report roccat_report; 526cb7cf3daSStefan Achatz struct pyra_mouse_event_button const *button_event; 527cb7cf3daSStefan Achatz 528cb7cf3daSStefan Achatz if (data[0] != PYRA_MOUSE_REPORT_NUMBER_BUTTON) 529cb7cf3daSStefan Achatz return; 530cb7cf3daSStefan Achatz 531cb7cf3daSStefan Achatz button_event = (struct pyra_mouse_event_button const *)data; 532cb7cf3daSStefan Achatz 533cb7cf3daSStefan Achatz switch (button_event->type) { 534cb7cf3daSStefan Achatz case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2: 535cb7cf3daSStefan Achatz case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI: 536cb7cf3daSStefan Achatz roccat_report.type = button_event->type; 537cb7cf3daSStefan Achatz roccat_report.value = button_event->data1; 538cb7cf3daSStefan Achatz roccat_report.key = 0; 539cb7cf3daSStefan Achatz roccat_report_event(pyra->chrdev_minor, 5408211e460SStefan Achatz (uint8_t const *)&roccat_report); 541cb7cf3daSStefan Achatz break; 542cb7cf3daSStefan Achatz case PYRA_MOUSE_EVENT_BUTTON_TYPE_MACRO: 543cb7cf3daSStefan Achatz case PYRA_MOUSE_EVENT_BUTTON_TYPE_SHORTCUT: 544cb7cf3daSStefan Achatz case PYRA_MOUSE_EVENT_BUTTON_TYPE_QUICKLAUNCH: 545cb7cf3daSStefan Achatz if (button_event->data2 == PYRA_MOUSE_EVENT_BUTTON_PRESS) { 546cb7cf3daSStefan Achatz roccat_report.type = button_event->type; 547cb7cf3daSStefan Achatz roccat_report.key = button_event->data1; 548d2b570a5SStefan Achatz /* 549d2b570a5SStefan Achatz * pyra reports profile numbers with range 1-5. 550d2b570a5SStefan Achatz * Keeping this behaviour. 551d2b570a5SStefan Achatz */ 552d2b570a5SStefan Achatz roccat_report.value = pyra->actual_profile + 1; 553cb7cf3daSStefan Achatz roccat_report_event(pyra->chrdev_minor, 5548211e460SStefan Achatz (uint8_t const *)&roccat_report); 555cb7cf3daSStefan Achatz } 556cb7cf3daSStefan Achatz break; 557cb7cf3daSStefan Achatz } 558cb7cf3daSStefan Achatz } 559cb7cf3daSStefan Achatz 560cb7cf3daSStefan Achatz static int pyra_raw_event(struct hid_device *hdev, struct hid_report *report, 561cb7cf3daSStefan Achatz u8 *data, int size) 562cb7cf3daSStefan Achatz { 563cb7cf3daSStefan Achatz struct usb_interface *intf = to_usb_interface(hdev->dev.parent); 564cb7cf3daSStefan Achatz struct pyra_device *pyra = hid_get_drvdata(hdev); 565cb7cf3daSStefan Achatz 566cb7cf3daSStefan Achatz if (intf->cur_altsetting->desc.bInterfaceProtocol 567cb7cf3daSStefan Achatz != USB_INTERFACE_PROTOCOL_MOUSE) 568cb7cf3daSStefan Achatz return 0; 569cb7cf3daSStefan Achatz 570901e64dbSStefan Achatz if (pyra == NULL) 571901e64dbSStefan Achatz return 0; 572901e64dbSStefan Achatz 573cb7cf3daSStefan Achatz pyra_keep_values_up_to_date(pyra, data); 574cb7cf3daSStefan Achatz 575cb7cf3daSStefan Achatz if (pyra->roccat_claimed) 576cb7cf3daSStefan Achatz pyra_report_to_chrdev(pyra, data); 577cb7cf3daSStefan Achatz 578cb7cf3daSStefan Achatz return 0; 579cb7cf3daSStefan Achatz } 580cb7cf3daSStefan Achatz 581cb7cf3daSStefan Achatz static const struct hid_device_id pyra_devices[] = { 582cb7cf3daSStefan Achatz { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, 583cb7cf3daSStefan Achatz USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, 5843fce2246SStefan Achatz { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, 5853fce2246SStefan Achatz USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) }, 586cb7cf3daSStefan Achatz { } 587cb7cf3daSStefan Achatz }; 588cb7cf3daSStefan Achatz 589cb7cf3daSStefan Achatz MODULE_DEVICE_TABLE(hid, pyra_devices); 590cb7cf3daSStefan Achatz 591cb7cf3daSStefan Achatz static struct hid_driver pyra_driver = { 592cb7cf3daSStefan Achatz .name = "pyra", 593cb7cf3daSStefan Achatz .id_table = pyra_devices, 594cb7cf3daSStefan Achatz .probe = pyra_probe, 595cb7cf3daSStefan Achatz .remove = pyra_remove, 596cb7cf3daSStefan Achatz .raw_event = pyra_raw_event 597cb7cf3daSStefan Achatz }; 598cb7cf3daSStefan Achatz 599cb7cf3daSStefan Achatz static int __init pyra_init(void) 600cb7cf3daSStefan Achatz { 6015012aadaSStefan Achatz int retval; 6025012aadaSStefan Achatz 6035012aadaSStefan Achatz /* class name has to be same as driver name */ 6045012aadaSStefan Achatz pyra_class = class_create(THIS_MODULE, "pyra"); 6055012aadaSStefan Achatz if (IS_ERR(pyra_class)) 6065012aadaSStefan Achatz return PTR_ERR(pyra_class); 60746a58c44SGreg Kroah-Hartman pyra_class->dev_groups = pyra_groups; 6085012aadaSStefan Achatz pyra_class->dev_bin_attrs = pyra_bin_attributes; 6095012aadaSStefan Achatz 6105012aadaSStefan Achatz retval = hid_register_driver(&pyra_driver); 6115012aadaSStefan Achatz if (retval) 6125012aadaSStefan Achatz class_destroy(pyra_class); 6135012aadaSStefan Achatz return retval; 614cb7cf3daSStefan Achatz } 615cb7cf3daSStefan Achatz 616cb7cf3daSStefan Achatz static void __exit pyra_exit(void) 617cb7cf3daSStefan Achatz { 618cb7cf3daSStefan Achatz hid_unregister_driver(&pyra_driver); 61974b643daSStefan Achatz class_destroy(pyra_class); 620cb7cf3daSStefan Achatz } 621cb7cf3daSStefan Achatz 622cb7cf3daSStefan Achatz module_init(pyra_init); 623cb7cf3daSStefan Achatz module_exit(pyra_exit); 624cb7cf3daSStefan Achatz 625cb7cf3daSStefan Achatz MODULE_AUTHOR("Stefan Achatz"); 626cb7cf3daSStefan Achatz MODULE_DESCRIPTION("USB Roccat Pyra driver"); 627cb7cf3daSStefan Achatz MODULE_LICENSE("GPL v2"); 628