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