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