175dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood/* 275dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood * HID driver for Steelseries SRW-S1 375dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood * 475dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood * Copyright (c) 2013 Simon Wood 575dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood */ 675dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood 775dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood/* 875dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood * This program is free software; you can redistribute it and/or modify it 975dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood * under the terms of the GNU General Public License as published by the Free 1075dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood * Software Foundation; either version 2 of the License, or (at your option) 1175dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood * any later version. 1275dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood */ 1375dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood 1475dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood#include <linux/device.h> 152e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood#include <linux/usb.h> 1675dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood#include <linux/hid.h> 1775dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood#include <linux/module.h> 1875dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood 1975dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood#include "hid-ids.h" 2075dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood 21b52b5061615b1aedf304845f3093afb283393e8aSimon Wood#if IS_BUILTIN(CONFIG_LEDS_CLASS) || \ 22b52b5061615b1aedf304845f3093afb283393e8aSimon Wood (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES)) 232e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood#define SRWS1_NUMBER_LEDS 15 242e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Woodstruct steelseries_srws1_data { 252e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood __u16 led_state; 267e41576247b782a21c05f7ea8a78a6db119ba789Jiri Kosina /* the last element is used for setting all leds simultaneously */ 277e41576247b782a21c05f7ea8a78a6db119ba789Jiri Kosina struct led_classdev *led[SRWS1_NUMBER_LEDS + 1]; 282e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood}; 292e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood#endif 302e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 315492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood/* Fixed report descriptor for Steelseries SRW-S1 wheel controller 325492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood * 335492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood * The original descriptor hides the sensitivity and assists dials 345492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood * a custom vendor usage page. This inserts a patch to make them 355492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood * appear in the 'Generic Desktop' usage. 365492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood */ 375492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood 385492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Woodstatic __u8 steelseries_srws1_rdesc_fixed[] = { 395492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x05, 0x01, /* Usage Page (Desktop) */ 405492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x09, 0x08, /* Usage (MultiAxis), Changed */ 415492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0xA1, 0x01, /* Collection (Application), */ 425492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0xA1, 0x02, /* Collection (Logical), */ 435492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x95, 0x01, /* Report Count (1), */ 445492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x05, 0x01, /* Changed Usage Page (Desktop), */ 455492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x09, 0x30, /* Changed Usage (X), */ 465492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x16, 0xF8, 0xF8, /* Logical Minimum (-1800), */ 475492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x26, 0x08, 0x07, /* Logical Maximum (1800), */ 485492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x65, 0x14, /* Unit (Degrees), */ 495492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x55, 0x0F, /* Unit Exponent (15), */ 505492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x75, 0x10, /* Report Size (16), */ 515492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x81, 0x02, /* Input (Variable), */ 525492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x09, 0x31, /* Changed Usage (Y), */ 535492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x15, 0x00, /* Logical Minimum (0), */ 545492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x26, 0xFF, 0x03, /* Logical Maximum (1023), */ 555492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x75, 0x0C, /* Report Size (12), */ 565492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x81, 0x02, /* Input (Variable), */ 575492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x09, 0x32, /* Changed Usage (Z), */ 585492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x15, 0x00, /* Logical Minimum (0), */ 595492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x26, 0xFF, 0x03, /* Logical Maximum (1023), */ 605492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x75, 0x0C, /* Report Size (12), */ 615492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x81, 0x02, /* Input (Variable), */ 625492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x05, 0x01, /* Usage Page (Desktop), */ 635492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x09, 0x39, /* Usage (Hat Switch), */ 645492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x25, 0x07, /* Logical Maximum (7), */ 655492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x35, 0x00, /* Physical Minimum (0), */ 665492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x46, 0x3B, 0x01, /* Physical Maximum (315), */ 675492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x65, 0x14, /* Unit (Degrees), */ 685492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x75, 0x04, /* Report Size (4), */ 695492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x95, 0x01, /* Report Count (1), */ 705492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x81, 0x02, /* Input (Variable), */ 715492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x25, 0x01, /* Logical Maximum (1), */ 725492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x45, 0x01, /* Physical Maximum (1), */ 735492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x65, 0x00, /* Unit, */ 745492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x75, 0x01, /* Report Size (1), */ 755492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x95, 0x03, /* Report Count (3), */ 765492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x81, 0x01, /* Input (Constant), */ 775492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x05, 0x09, /* Usage Page (Button), */ 785492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x19, 0x01, /* Usage Minimum (01h), */ 795492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x29, 0x11, /* Usage Maximum (11h), */ 805492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x95, 0x11, /* Report Count (17), */ 815492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x81, 0x02, /* Input (Variable), */ 825492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood /* ---- Dial patch starts here ---- */ 835492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x05, 0x01, /* Usage Page (Desktop), */ 845492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x09, 0x33, /* Usage (RX), */ 855492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x75, 0x04, /* Report Size (4), */ 865492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x95, 0x02, /* Report Count (2), */ 875492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x15, 0x00, /* Logical Minimum (0), */ 885492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x25, 0x0b, /* Logical Maximum (b), */ 895492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x81, 0x02, /* Input (Variable), */ 905492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x09, 0x35, /* Usage (RZ), */ 915492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x75, 0x04, /* Report Size (4), */ 925492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x95, 0x01, /* Report Count (1), */ 935492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x25, 0x03, /* Logical Maximum (3), */ 945492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x81, 0x02, /* Input (Variable), */ 955492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood /* ---- Dial patch ends here ---- */ 965492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ 975492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x09, 0x01, /* Usage (01h), */ 985492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x75, 0x04, /* Changed Report Size (4), */ 995492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x95, 0x0D, /* Changed Report Count (13), */ 1005492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x81, 0x02, /* Input (Variable), */ 1015492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0xC0, /* End Collection, */ 1025492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0xA1, 0x02, /* Collection (Logical), */ 1035492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x09, 0x02, /* Usage (02h), */ 1045492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x75, 0x08, /* Report Size (8), */ 1055492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x95, 0x10, /* Report Count (16), */ 1065492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0x91, 0x02, /* Output (Variable), */ 1075492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0xC0, /* End Collection, */ 1085492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood0xC0 /* End Collection */ 1095492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood}; 1105492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood 111b52b5061615b1aedf304845f3093afb283393e8aSimon Wood#if IS_BUILTIN(CONFIG_LEDS_CLASS) || \ 112b52b5061615b1aedf304845f3093afb283393e8aSimon Wood (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES)) 1132e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Woodstatic void steelseries_srws1_set_leds(struct hid_device *hdev, __u16 leds) 1142e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood{ 1152e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood struct list_head *report_list = &hdev->report_enum[HID_OUTPUT_REPORT].report_list; 1162e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood struct hid_report *report = list_entry(report_list->next, struct hid_report, list); 1172e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood __s32 *value = report->field[0]->value; 1182e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 1192e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood value[0] = 0x40; 1202e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood value[1] = leds & 0xFF; 1212e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood value[2] = leds >> 8; 1222e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood value[3] = 0x00; 1232e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood value[4] = 0x00; 1242e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood value[5] = 0x00; 1252e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood value[6] = 0x00; 1262e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood value[7] = 0x00; 1272e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood value[8] = 0x00; 1282e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood value[9] = 0x00; 1292e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood value[10] = 0x00; 1302e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood value[11] = 0x00; 1312e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood value[12] = 0x00; 1322e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood value[13] = 0x00; 1332e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood value[14] = 0x00; 1342e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood value[15] = 0x00; 1352e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 136d881427253da011495f4193663d809d0e9dfa215Benjamin Tissoires hid_hw_request(hdev, report, HID_REQ_SET_REPORT); 1372e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 1382e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood /* Note: LED change does not show on device until the device is read/polled */ 1392e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood} 1402e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 141e25d780581dc4d261c66e072a59c34782bd03e0aSimon Woodstatic void steelseries_srws1_led_all_set_brightness(struct led_classdev *led_cdev, 142e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood enum led_brightness value) 143e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood{ 144e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood struct device *dev = led_cdev->dev->parent; 145e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood struct hid_device *hid = container_of(dev, struct hid_device, dev); 146e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood struct steelseries_srws1_data *drv_data = hid_get_drvdata(hid); 147e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood 148e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood if (!drv_data) { 149e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood hid_err(hid, "Device data not found."); 150e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood return; 151e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood } 152e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood 153e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood if (value == LED_OFF) 154e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood drv_data->led_state = 0; 155e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood else 156e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood drv_data->led_state = (1 << (SRWS1_NUMBER_LEDS + 1)) - 1; 157e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood 158e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood steelseries_srws1_set_leds(hid, drv_data->led_state); 159e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood} 160e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood 161e25d780581dc4d261c66e072a59c34782bd03e0aSimon Woodstatic enum led_brightness steelseries_srws1_led_all_get_brightness(struct led_classdev *led_cdev) 162e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood{ 163e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood struct device *dev = led_cdev->dev->parent; 164e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood struct hid_device *hid = container_of(dev, struct hid_device, dev); 165e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood struct steelseries_srws1_data *drv_data; 166e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood 167e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood drv_data = hid_get_drvdata(hid); 168e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood 169e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood if (!drv_data) { 170e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood hid_err(hid, "Device data not found."); 171e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood return LED_OFF; 172e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood } 173e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood 174e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood return (drv_data->led_state >> SRWS1_NUMBER_LEDS) ? LED_FULL : LED_OFF; 175e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood} 176e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood 1772e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Woodstatic void steelseries_srws1_led_set_brightness(struct led_classdev *led_cdev, 1782e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood enum led_brightness value) 1792e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood{ 1802e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood struct device *dev = led_cdev->dev->parent; 1812e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood struct hid_device *hid = container_of(dev, struct hid_device, dev); 1822e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood struct steelseries_srws1_data *drv_data = hid_get_drvdata(hid); 1832e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood int i, state = 0; 1842e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 1852e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood if (!drv_data) { 1862e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood hid_err(hid, "Device data not found."); 1872e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood return; 1882e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood } 1892e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 1902e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood for (i = 0; i < SRWS1_NUMBER_LEDS; i++) { 1912e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood if (led_cdev != drv_data->led[i]) 1922e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood continue; 1932e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 1942e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood state = (drv_data->led_state >> i) & 1; 1952e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood if (value == LED_OFF && state) { 1962e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood drv_data->led_state &= ~(1 << i); 1972e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood steelseries_srws1_set_leds(hid, drv_data->led_state); 1982e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood } else if (value != LED_OFF && !state) { 1992e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood drv_data->led_state |= 1 << i; 2002e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood steelseries_srws1_set_leds(hid, drv_data->led_state); 2012e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood } 2022e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood break; 2032e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood } 2042e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood} 2052e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 2062e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Woodstatic enum led_brightness steelseries_srws1_led_get_brightness(struct led_classdev *led_cdev) 2072e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood{ 2082e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood struct device *dev = led_cdev->dev->parent; 2092e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood struct hid_device *hid = container_of(dev, struct hid_device, dev); 2102e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood struct steelseries_srws1_data *drv_data; 2112e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood int i, value = 0; 2122e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 2132e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood drv_data = hid_get_drvdata(hid); 2142e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 2152e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood if (!drv_data) { 2162e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood hid_err(hid, "Device data not found."); 2172e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood return LED_OFF; 2182e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood } 2192e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 2202e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood for (i = 0; i < SRWS1_NUMBER_LEDS; i++) 2212e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood if (led_cdev == drv_data->led[i]) { 2222e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood value = (drv_data->led_state >> i) & 1; 2232e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood break; 2242e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood } 2252e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 2262e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood return value ? LED_FULL : LED_OFF; 2272e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood} 2282e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 2292e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Woodstatic int steelseries_srws1_probe(struct hid_device *hdev, 2302e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood const struct hid_device_id *id) 2312e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood{ 2322e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood int ret, i; 2332e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood struct led_classdev *led; 2342e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood size_t name_sz; 2352e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood char *name; 2362e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 2372e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood struct steelseries_srws1_data *drv_data = kzalloc(sizeof(*drv_data), GFP_KERNEL); 2382e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 2392e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood if (drv_data == NULL) { 2402e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood hid_err(hdev, "can't alloc SRW-S1 memory\n"); 2412e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood return -ENOMEM; 2422e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood } 2432e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 2442e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood hid_set_drvdata(hdev, drv_data); 2452e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 2462e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood ret = hid_parse(hdev); 2472e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood if (ret) { 2482e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood hid_err(hdev, "parse failed\n"); 2492e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood goto err_free; 2502e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood } 2512e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 25241df7f6d43723deb7364340b44bc5d94bf717456Kees Cook if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 16)) { 25341df7f6d43723deb7364340b44bc5d94bf717456Kees Cook ret = -ENODEV; 25441df7f6d43723deb7364340b44bc5d94bf717456Kees Cook goto err_free; 25541df7f6d43723deb7364340b44bc5d94bf717456Kees Cook } 25641df7f6d43723deb7364340b44bc5d94bf717456Kees Cook 257732ae1417f7f52785e14a8267f2f314526235b98Kees Cook if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 16)) { 258732ae1417f7f52785e14a8267f2f314526235b98Kees Cook ret = -ENODEV; 259732ae1417f7f52785e14a8267f2f314526235b98Kees Cook goto err_free; 260732ae1417f7f52785e14a8267f2f314526235b98Kees Cook } 261732ae1417f7f52785e14a8267f2f314526235b98Kees Cook 2622e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 2632e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood if (ret) { 2642e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood hid_err(hdev, "hw start failed\n"); 2652e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood goto err_free; 2662e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood } 2672e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 2682e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood /* register led subsystem */ 2692e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood drv_data->led_state = 0; 270e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood for (i = 0; i < SRWS1_NUMBER_LEDS + 1; i++) 2712e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood drv_data->led[i] = NULL; 2722e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 2732e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood steelseries_srws1_set_leds(hdev, 0); 2742e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 275e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood name_sz = strlen(hdev->uniq) + 16; 276e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood 277e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood /* 'ALL', for setting all LEDs simultaneously */ 278e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL); 279e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood if (!led) { 280e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood hid_err(hdev, "can't allocate memory for LED ALL\n"); 281e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood goto err_led; 282e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood } 283e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood 284e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood name = (void *)(&led[1]); 285e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood snprintf(name, name_sz, "SRWS1::%s::RPMALL", hdev->uniq); 286e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood led->name = name; 287e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood led->brightness = 0; 288e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood led->max_brightness = 1; 289e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood led->brightness_get = steelseries_srws1_led_all_get_brightness; 290e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood led->brightness_set = steelseries_srws1_led_all_set_brightness; 291e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood 292e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood drv_data->led[SRWS1_NUMBER_LEDS] = led; 293e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood ret = led_classdev_register(&hdev->dev, led); 294e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood if (ret) 295e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood goto err_led; 2962e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 297e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood /* Each individual LED */ 2982e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood for (i = 0; i < SRWS1_NUMBER_LEDS; i++) { 2992e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL); 3002e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood if (!led) { 3012e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood hid_err(hdev, "can't allocate memory for LED %d\n", i); 3022e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood goto err_led; 3032e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood } 3042e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 3052e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood name = (void *)(&led[1]); 3062e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood snprintf(name, name_sz, "SRWS1::%s::RPM%d", hdev->uniq, i+1); 3072e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood led->name = name; 3082e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood led->brightness = 0; 3092e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood led->max_brightness = 1; 3102e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood led->brightness_get = steelseries_srws1_led_get_brightness; 3112e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood led->brightness_set = steelseries_srws1_led_set_brightness; 3122e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 3132e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood drv_data->led[i] = led; 3142e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood ret = led_classdev_register(&hdev->dev, led); 3152e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 3162e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood if (ret) { 3172e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood hid_err(hdev, "failed to register LED %d. Aborting.\n", i); 3182e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wooderr_led: 3192e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood /* Deregister all LEDs (if any) */ 320e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood for (i = 0; i < SRWS1_NUMBER_LEDS + 1; i++) { 3212e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood led = drv_data->led[i]; 3222e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood drv_data->led[i] = NULL; 3232e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood if (!led) 3242e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood continue; 3252e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood led_classdev_unregister(led); 3262e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood kfree(led); 3272e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood } 3282e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood goto out; /* but let the driver continue without LEDs */ 3292e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood } 3302e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood } 3312e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Woodout: 3322e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood return 0; 3332e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wooderr_free: 3342e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood kfree(drv_data); 3352e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood return ret; 3362e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood} 3372e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 3382e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Woodstatic void steelseries_srws1_remove(struct hid_device *hdev) 3392e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood{ 3402e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood int i; 3412e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood struct led_classdev *led; 3422e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 3432e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood struct steelseries_srws1_data *drv_data = hid_get_drvdata(hdev); 3442e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 3452e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood if (drv_data) { 3462e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood /* Deregister LEDs (if any) */ 347e25d780581dc4d261c66e072a59c34782bd03e0aSimon Wood for (i = 0; i < SRWS1_NUMBER_LEDS + 1; i++) { 3482e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood led = drv_data->led[i]; 3492e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood drv_data->led[i] = NULL; 3502e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood if (!led) 3512e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood continue; 3522e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood led_classdev_unregister(led); 3532e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood kfree(led); 3542e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood } 3552e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 3562e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood } 3572e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 3582e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood hid_hw_stop(hdev); 3592e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood kfree(drv_data); 3602e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood return; 3612e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood} 3622e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood#endif 3632e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood 36475dbb9530f73da521b871ba3c4bd32f301a62635Simon Woodstatic __u8 *steelseries_srws1_report_fixup(struct hid_device *hdev, __u8 *rdesc, 36575dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood unsigned int *rsize) 36675dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood{ 36775dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood if (*rsize >= 115 && rdesc[11] == 0x02 && rdesc[13] == 0xc8 36875dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood && rdesc[29] == 0xbb && rdesc[40] == 0xc5) { 36975dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood hid_info(hdev, "Fixing up Steelseries SRW-S1 report descriptor\n"); 3705492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood rdesc = steelseries_srws1_rdesc_fixed; 3715492606dc39b2f1ce67cf718f09ade373a35f4ebSimon Wood *rsize = sizeof(steelseries_srws1_rdesc_fixed); 37275dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood } 37375dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood return rdesc; 37475dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood} 37575dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood 37675dbb9530f73da521b871ba3c4bd32f301a62635Simon Woodstatic const struct hid_device_id steelseries_srws1_devices[] = { 37775dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1) }, 37875dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood { } 37975dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood}; 38075dbb9530f73da521b871ba3c4bd32f301a62635Simon WoodMODULE_DEVICE_TABLE(hid, steelseries_srws1_devices); 38175dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood 38275dbb9530f73da521b871ba3c4bd32f301a62635Simon Woodstatic struct hid_driver steelseries_srws1_driver = { 38375dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood .name = "steelseries_srws1", 38475dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood .id_table = steelseries_srws1_devices, 385b52b5061615b1aedf304845f3093afb283393e8aSimon Wood#if IS_BUILTIN(CONFIG_LEDS_CLASS) || \ 386b52b5061615b1aedf304845f3093afb283393e8aSimon Wood (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES)) 3872e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood .probe = steelseries_srws1_probe, 3882e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood .remove = steelseries_srws1_remove, 3892e2daff3a51f2d10155b03f461f4e29eaf80dcbdSimon Wood#endif 39075dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood .report_fixup = steelseries_srws1_report_fixup 39175dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood}; 39275dbb9530f73da521b871ba3c4bd32f301a62635Simon Wood 393bb64469af5908d89766b116d91c0691fe3c9237aWei Yongjunmodule_hid_driver(steelseries_srws1_driver); 39475dbb9530f73da521b871ba3c4bd32f301a62635Simon WoodMODULE_LICENSE("GPL"); 395