linux/drivers/hid/hid-roccat-savu.c
<<
>>
Prefs
   1/*
   2 * Roccat Savu driver for Linux
   3 *
   4 * Copyright (c) 2012 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/* Roccat Savu is a gamer mouse with macro keys that can be configured in
  15 * 5 profiles.
  16 */
  17
  18#include <linux/device.h>
  19#include <linux/input.h>
  20#include <linux/hid.h>
  21#include <linux/module.h>
  22#include <linux/slab.h>
  23#include <linux/hid-roccat.h>
  24#include "hid-ids.h"
  25#include "hid-roccat-common.h"
  26#include "hid-roccat-savu.h"
  27
  28static struct class *savu_class;
  29
  30ROCCAT_COMMON2_BIN_ATTRIBUTE_W(control, 0x4, 0x03);
  31ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(profile, 0x5, 0x03);
  32ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(general, 0x6, 0x10);
  33ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(buttons, 0x7, 0x2f);
  34ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(macro, 0x8, 0x0823);
  35ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(info, 0x9, 0x08);
  36ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(sensor, 0xc, 0x04);
  37
  38static struct bin_attribute *savu_bin_attrs[] = {
  39        &bin_attr_control,
  40        &bin_attr_profile,
  41        &bin_attr_general,
  42        &bin_attr_buttons,
  43        &bin_attr_macro,
  44        &bin_attr_info,
  45        &bin_attr_sensor,
  46        NULL,
  47};
  48
  49static const struct attribute_group savu_group = {
  50        .bin_attrs = savu_bin_attrs,
  51};
  52
  53static const struct attribute_group *savu_groups[] = {
  54        &savu_group,
  55        NULL,
  56};
  57
  58static int savu_init_specials(struct hid_device *hdev)
  59{
  60        struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
  61        struct usb_device *usb_dev = interface_to_usbdev(intf);
  62        struct roccat_common2_device *savu;
  63        int retval;
  64
  65        if (intf->cur_altsetting->desc.bInterfaceProtocol
  66                        != USB_INTERFACE_PROTOCOL_MOUSE) {
  67                hid_set_drvdata(hdev, NULL);
  68                return 0;
  69        }
  70
  71        savu = kzalloc(sizeof(*savu), GFP_KERNEL);
  72        if (!savu) {
  73                hid_err(hdev, "can't alloc device descriptor\n");
  74                return -ENOMEM;
  75        }
  76        hid_set_drvdata(hdev, savu);
  77
  78        retval = roccat_common2_device_init_struct(usb_dev, savu);
  79        if (retval) {
  80                hid_err(hdev, "couldn't init Savu device\n");
  81                goto exit_free;
  82        }
  83
  84        retval = roccat_connect(savu_class, hdev,
  85                        sizeof(struct savu_roccat_report));
  86        if (retval < 0) {
  87                hid_err(hdev, "couldn't init char dev\n");
  88        } else {
  89                savu->chrdev_minor = retval;
  90                savu->roccat_claimed = 1;
  91        }
  92
  93        return 0;
  94exit_free:
  95        kfree(savu);
  96        return retval;
  97}
  98
  99static void savu_remove_specials(struct hid_device *hdev)
 100{
 101        struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
 102        struct roccat_common2_device *savu;
 103
 104        if (intf->cur_altsetting->desc.bInterfaceProtocol
 105                        != USB_INTERFACE_PROTOCOL_MOUSE)
 106                return;
 107
 108        savu = hid_get_drvdata(hdev);
 109        if (savu->roccat_claimed)
 110                roccat_disconnect(savu->chrdev_minor);
 111        kfree(savu);
 112}
 113
 114static int savu_probe(struct hid_device *hdev,
 115                const struct hid_device_id *id)
 116{
 117        int retval;
 118
 119        retval = hid_parse(hdev);
 120        if (retval) {
 121                hid_err(hdev, "parse failed\n");
 122                goto exit;
 123        }
 124
 125        retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
 126        if (retval) {
 127                hid_err(hdev, "hw start failed\n");
 128                goto exit;
 129        }
 130
 131        retval = savu_init_specials(hdev);
 132        if (retval) {
 133                hid_err(hdev, "couldn't install mouse\n");
 134                goto exit_stop;
 135        }
 136
 137        return 0;
 138
 139exit_stop:
 140        hid_hw_stop(hdev);
 141exit:
 142        return retval;
 143}
 144
 145static void savu_remove(struct hid_device *hdev)
 146{
 147        savu_remove_specials(hdev);
 148        hid_hw_stop(hdev);
 149}
 150
 151static void savu_report_to_chrdev(struct roccat_common2_device const *savu,
 152                u8 const *data)
 153{
 154        struct savu_roccat_report roccat_report;
 155        struct savu_mouse_report_special const *special_report;
 156
 157        if (data[0] != SAVU_MOUSE_REPORT_NUMBER_SPECIAL)
 158                return;
 159
 160        special_report = (struct savu_mouse_report_special const *)data;
 161
 162        roccat_report.type = special_report->type;
 163        roccat_report.data[0] = special_report->data[0];
 164        roccat_report.data[1] = special_report->data[1];
 165        roccat_report_event(savu->chrdev_minor,
 166                        (uint8_t const *)&roccat_report);
 167}
 168
 169static int savu_raw_event(struct hid_device *hdev,
 170                struct hid_report *report, u8 *data, int size)
 171{
 172        struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
 173        struct roccat_common2_device *savu = hid_get_drvdata(hdev);
 174
 175        if (intf->cur_altsetting->desc.bInterfaceProtocol
 176                        != USB_INTERFACE_PROTOCOL_MOUSE)
 177                return 0;
 178
 179        if (savu == NULL)
 180                return 0;
 181
 182        if (savu->roccat_claimed)
 183                savu_report_to_chrdev(savu, data);
 184
 185        return 0;
 186}
 187
 188static const struct hid_device_id savu_devices[] = {
 189        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) },
 190        { }
 191};
 192
 193MODULE_DEVICE_TABLE(hid, savu_devices);
 194
 195static struct hid_driver savu_driver = {
 196                .name = "savu",
 197                .id_table = savu_devices,
 198                .probe = savu_probe,
 199                .remove = savu_remove,
 200                .raw_event = savu_raw_event
 201};
 202
 203static int __init savu_init(void)
 204{
 205        int retval;
 206
 207        savu_class = class_create(THIS_MODULE, "savu");
 208        if (IS_ERR(savu_class))
 209                return PTR_ERR(savu_class);
 210        savu_class->dev_groups = savu_groups;
 211
 212        retval = hid_register_driver(&savu_driver);
 213        if (retval)
 214                class_destroy(savu_class);
 215        return retval;
 216}
 217
 218static void __exit savu_exit(void)
 219{
 220        hid_unregister_driver(&savu_driver);
 221        class_destroy(savu_class);
 222}
 223
 224module_init(savu_init);
 225module_exit(savu_exit);
 226
 227MODULE_AUTHOR("Stefan Achatz");
 228MODULE_DESCRIPTION("USB Roccat Savu driver");
 229MODULE_LICENSE("GPL v2");
 230