diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-07-28 17:30:16 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-07-28 17:30:16 -0400 |
commit | 884316deb4c9fdf9becfa31831a9e40717e3026c (patch) | |
tree | 9c0af0dd0ab659acd24c00f69c89b04058fe8f47 | |
parent | 69c4289449b954f87ce33904bbb1b27dc075dcfa (diff) | |
parent | 8c2f421c1f0fa7768767ecaad497aa676fc9015a (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
Pull HID updates from Jiri Kosina:
- new hid-alps driver for ALPS Touchpad-Stick device, from Masaki Ota
- much improved and generalized HID led handling, and merge of
specialized hid-thingm driver into this generic hid-led one, from
Heiner Kallweit
- i2c-hid power management improvements from Fu Zhonghui and Guohua
Zhong
- uhid initialization race fix from Roderick Colenbrander
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (21 commits)
HID: add usb device id for Apple Magic Keyboard
HID: hid-led: fix Delcom support on big endian systems
HID: hid-led: add support for Greynut Luxafor
HID: hid-led: add support for Delcom Visual Signal Indicator G2
HID: hid-led: remove report id from struct hidled_config
HID: alps: a few cleanups
HID: remove ThingM blink(1) driver
HID: hid-led: add support for ThingM blink(1)
HID: hid-led: add support for reading from LED devices
HID: hid-led: add support for devices with multiple independent LEDs
HID: i2c-hid: set power sleep before shutdown
HID: alps: match alps devices in core
HID: thingm: simplify debug output code
HID: alps: pass correct sizes to hid_hw_raw_request()
HID: alps: struct u1_dev *priv is internal to the driver
HID: add Alps I2C HID Touchpad-Stick support
HID: led: fix config
usb: misc: remove outdated USB LED driver
HID: migrate USB LED driver from usb misc to hid
HID: i2c_hid: enable i2c-hid devices to suspend/resume asynchronously
...
-rw-r--r-- | Documentation/hid/hid-alps.txt | 139 | ||||
-rw-r--r-- | MAINTAINERS | 5 | ||||
-rw-r--r-- | drivers/hid/Kconfig | 31 | ||||
-rw-r--r-- | drivers/hid/Makefile | 3 | ||||
-rw-r--r-- | drivers/hid/hid-alps.c | 506 | ||||
-rw-r--r-- | drivers/hid/hid-apple.c | 2 | ||||
-rw-r--r-- | drivers/hid/hid-core.c | 10 | ||||
-rw-r--r-- | drivers/hid/hid-ids.h | 10 | ||||
-rw-r--r-- | drivers/hid/hid-led.c | 523 | ||||
-rw-r--r-- | drivers/hid/hid-thingm.c | 263 | ||||
-rw-r--r-- | drivers/hid/i2c-hid/i2c-hid.c | 11 | ||||
-rw-r--r-- | drivers/hid/uhid.c | 33 | ||||
-rw-r--r-- | drivers/usb/misc/Kconfig | 9 | ||||
-rw-r--r-- | drivers/usb/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/misc/usbled.c | 273 |
15 files changed, 1250 insertions, 569 deletions
diff --git a/Documentation/hid/hid-alps.txt b/Documentation/hid/hid-alps.txt new file mode 100644 index 000000000000..6b02a2447c77 --- /dev/null +++ b/Documentation/hid/hid-alps.txt | |||
@@ -0,0 +1,139 @@ | |||
1 | ALPS HID Touchpad Protocol | ||
2 | ---------------------- | ||
3 | |||
4 | Introduction | ||
5 | ------------ | ||
6 | Currently ALPS HID driver supports U1 Touchpad device. | ||
7 | |||
8 | U1 devuce basic information. | ||
9 | Vender ID 0x044E | ||
10 | Product ID 0x120B | ||
11 | Version ID 0x0121 | ||
12 | |||
13 | |||
14 | HID Descriptor | ||
15 | ------------ | ||
16 | Byte Field Value Notes | ||
17 | 0 wHIDDescLength 001E Length of HID Descriptor : 30 bytes | ||
18 | 2 bcdVersion 0100 Compliant with Version 1.00 | ||
19 | 4 wReportDescLength 00B2 Report Descriptor is 178 Bytes (0x00B2) | ||
20 | 6 wReportDescRegister 0002 Identifier to read Report Descriptor | ||
21 | 8 wInputRegister 0003 Identifier to read Input Report | ||
22 | 10 wMaxInputLength 0053 Input Report is 80 Bytes + 2 | ||
23 | 12 wOutputRegister 0000 Identifier to read Output Report | ||
24 | 14 wMaxOutputLength 0000 No Output Reports | ||
25 | 16 wCommandRegister 0005 Identifier for Command Register | ||
26 | 18 wDataRegister 0006 Identifier for Data Register | ||
27 | 20 wVendorID 044E Vendor ID 0x044E | ||
28 | 22 wProductID 120B Product ID 0x120B | ||
29 | 24 wVersionID 0121 Version 01.21 | ||
30 | 26 RESERVED 0000 RESERVED | ||
31 | |||
32 | |||
33 | Report ID | ||
34 | ------------ | ||
35 | ReportID-1 (Input Reports) (HIDUsage-Mouse) for TP&SP | ||
36 | ReportID-2 (Input Reports) (HIDUsage-keyboard) for TP | ||
37 | ReportID-3 (Input Reports) (Vendor Usage: Max 10 finger data) for TP | ||
38 | ReportID-4 (Input Reports) (Vendor Usage: ON bit data) for GP | ||
39 | ReportID-5 (Feature Reports) Feature Reports | ||
40 | ReportID-6 (Input Reports) (Vendor Usage: StickPointer data) for SP | ||
41 | ReportID-7 (Feature Reports) Flash update (Bootloader) | ||
42 | |||
43 | |||
44 | Data pattern | ||
45 | ------------ | ||
46 | Case1 ReportID_1 TP/SP Relative/Relative | ||
47 | Case2 ReportID_3 TP Absolute | ||
48 | ReportID_6 SP Absolute | ||
49 | |||
50 | |||
51 | Command Read/Write | ||
52 | ------------------ | ||
53 | To read/write to RAM, need to send a commands to the device. | ||
54 | The command format is as below. | ||
55 | |||
56 | DataByte(SET_REPORT) | ||
57 | Byte1 Command Byte | ||
58 | Byte2 Address - Byte 0 (LSB) | ||
59 | Byte3 Address - Byte 1 | ||
60 | Byte4 Address - Byte 2 | ||
61 | Byte5 Address - Byte 3 (MSB) | ||
62 | Byte6 Value Byte | ||
63 | Byte7 Checksum | ||
64 | |||
65 | Command Byte is read=0xD1/write=0xD2 . | ||
66 | Address is read/write RAM address. | ||
67 | Value Byte is writing data when you send the write commands. | ||
68 | When you read RAM, there is no meaning. | ||
69 | |||
70 | DataByte(GET_REPORT) | ||
71 | Byte1 Response Byte | ||
72 | Byte2 Address - Byte 0 (LSB) | ||
73 | Byte3 Address - Byte 1 | ||
74 | Byte4 Address - Byte 2 | ||
75 | Byte5 Address - Byte 3 (MSB) | ||
76 | Byte6 Value Byte | ||
77 | Byte7 Checksum | ||
78 | |||
79 | Read value is stored in Value Byte. | ||
80 | |||
81 | |||
82 | Packet Format | ||
83 | Touchpad data byte | ||
84 | ------------------ | ||
85 | b7 b6 b5 b4 b3 b2 b1 b0 | ||
86 | 1 0 0 SW6 SW5 SW4 SW3 SW2 SW1 | ||
87 | 2 0 0 0 Fcv Fn3 Fn2 Fn1 Fn0 | ||
88 | 3 Xa0_7 Xa0_6 Xa0_5 Xa0_4 Xa0_3 Xa0_2 Xa0_1 Xa0_0 | ||
89 | 4 Xa0_15 Xa0_14 Xa0_13 Xa0_12 Xa0_11 Xa0_10 Xa0_9 Xa0_8 | ||
90 | 5 Ya0_7 Ya0_6 Ya0_5 Ya0_4 Ya0_3 Ya0_2 Ya0_1 Ya0_0 | ||
91 | 6 Ya0_15 Ya0_14 Ya0_13 Ya0_12 Ya0_11 Ya0_10 Ya0_9 Ya0_8 | ||
92 | 7 LFB0 Zs0_6 Zs0_5 Zs0_4 Zs0_3 Zs0_2 Zs0_1 Zs0_0 | ||
93 | |||
94 | 8 Xa1_7 Xa1_6 Xa1_5 Xa1_4 Xa1_3 Xa1_2 Xa1_1 Xa1_0 | ||
95 | 9 Xa1_15 Xa1_14 Xa1_13 Xa1_12 Xa1_11 Xa1_10 Xa1_9 Xa1_8 | ||
96 | 10 Ya1_7 Ya1_6 Ya1_5 Ya1_4 Ya1_3 Ya1_2 Ya1_1 Ya1_0 | ||
97 | 11 Ya1_15 Ya1_14 Ya1_13 Ya1_12 Ya1_11 Ya1_10 Ya1_9 Ya1_8 | ||
98 | 12 LFB1 Zs1_6 Zs1_5 Zs1_4 Zs1_3 Zs1_2 Zs1_1 Zs1_0 | ||
99 | |||
100 | 13 Xa2_7 Xa2_6 Xa2_5 Xa2_4 Xa2_3 Xa2_2 Xa2_1 Xa2_0 | ||
101 | 14 Xa2_15 Xa2_14 Xa2_13 Xa2_12 Xa2_11 Xa2_10 Xa2_9 Xa2_8 | ||
102 | 15 Ya2_7 Ya2_6 Ya2_5 Ya2_4 Ya2_3 Ya2_2 Ya2_1 Ya2_0 | ||
103 | 16 Ya2_15 Ya2_14 Ya2_13 Ya2_12 Ya2_11 Ya2_10 Ya2_9 Ya2_8 | ||
104 | 17 LFB2 Zs2_6 Zs2_5 Zs2_4 Zs2_3 Zs2_2 Zs2_1 Zs2_0 | ||
105 | |||
106 | 18 Xa3_7 Xa3_6 Xa3_5 Xa3_4 Xa3_3 Xa3_2 Xa3_1 Xa3_0 | ||
107 | 19 Xa3_15 Xa3_14 Xa3_13 Xa3_12 Xa3_11 Xa3_10 Xa3_9 Xa3_8 | ||
108 | 20 Ya3_7 Ya3_6 Ya3_5 Ya3_4 Ya3_3 Ya3_2 Ya3_1 Ya3_0 | ||
109 | 21 Ya3_15 Ya3_14 Ya3_13 Ya3_12 Ya3_11 Ya3_10 Ya3_9 Ya3_8 | ||
110 | 22 LFB3 Zs3_6 Zs3_5 Zs3_4 Zs3_3 Zs3_2 Zs3_1 Zs3_0 | ||
111 | |||
112 | 23 Xa4_7 Xa4_6 Xa4_5 Xa4_4 Xa4_3 Xa4_2 Xa4_1 Xa4_0 | ||
113 | 24 Xa4_15 Xa4_14 Xa4_13 Xa4_12 Xa4_11 Xa4_10 Xa4_9 Xa4_8 | ||
114 | 25 Ya4_7 Ya4_6 Ya4_5 Ya4_4 Ya4_3 Ya4_2 Ya4_1 Ya4_0 | ||
115 | 26 Ya4_15 Ya4_14 Ya4_13 Ya4_12 Ya4_11 Ya4_10 Ya4_9 Ya4_8 | ||
116 | 27 LFB4 Zs4_6 Zs4_5 Zs4_4 Zs4_3 Zs4_2 Zs4_1 Zs4_0 | ||
117 | |||
118 | |||
119 | SW1-SW6: SW ON/OFF status | ||
120 | Xan_15-0(16bit):X Absolute data of the "n"th finger | ||
121 | Yan_15-0(16bit):Y Absolute data of the "n"th finger | ||
122 | Zsn_6-0(7bit): Operation area of the "n"th finger | ||
123 | |||
124 | |||
125 | StickPointer data byte | ||
126 | ------------------ | ||
127 | b7 b6 b5 b4 b3 b2 b1 b0 | ||
128 | Byte1 1 1 1 0 1 SW3 SW2 SW1 | ||
129 | Byte2 X7 X6 X5 X4 X3 X2 X1 X0 | ||
130 | Byte3 X15 X14 X13 X12 X11 X10 X9 X8 | ||
131 | Byte4 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 | ||
132 | Byte5 Y15 Y14 Y13 Y12 Y11 Y10 Y9 Y8 | ||
133 | Byte6 Z7 Z6 Z5 Z4 Z3 Z2 Z1 Z0 | ||
134 | Byte7 T&P Z14 Z13 Z12 Z11 Z10 Z9 Z8 | ||
135 | |||
136 | SW1-SW3: SW ON/OFF status | ||
137 | Xn_15-0(16bit):X Absolute data | ||
138 | Yn_15-0(16bit):Y Absolute data | ||
139 | Zn_14-0(15bit):Z | ||
diff --git a/MAINTAINERS b/MAINTAINERS index c1739eba622d..6cbef714a975 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -11440,11 +11440,6 @@ F: Documentation/thermal/cpu-cooling-api.txt | |||
11440 | F: drivers/thermal/cpu_cooling.c | 11440 | F: drivers/thermal/cpu_cooling.c |
11441 | F: include/linux/cpu_cooling.h | 11441 | F: include/linux/cpu_cooling.h |
11442 | 11442 | ||
11443 | THINGM BLINK(1) USB RGB LED DRIVER | ||
11444 | M: Vivien Didelot <vivien.didelot@savoirfairelinux.com> | ||
11445 | S: Maintained | ||
11446 | F: drivers/hid/hid-thingm.c | ||
11447 | |||
11448 | THINKPAD ACPI EXTRAS DRIVER | 11443 | THINKPAD ACPI EXTRAS DRIVER |
11449 | M: Henrique de Moraes Holschuh <ibm-acpi@hmh.eng.br> | 11444 | M: Henrique de Moraes Holschuh <ibm-acpi@hmh.eng.br> |
11450 | L: ibm-acpi-devel@lists.sourceforge.net | 11445 | L: ibm-acpi-devel@lists.sourceforge.net |
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 5646ca4b95de..78ac4811bd3c 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig | |||
@@ -388,6 +388,21 @@ config HID_LCPOWER | |||
388 | ---help--- | 388 | ---help--- |
389 | Support for LC-Power RC1000MCE RF remote control. | 389 | Support for LC-Power RC1000MCE RF remote control. |
390 | 390 | ||
391 | config HID_LED | ||
392 | tristate "Simple RGB LED support" | ||
393 | depends on HID | ||
394 | depends on LEDS_CLASS | ||
395 | ---help--- | ||
396 | Support for simple RGB LED devices. Currently supported are: | ||
397 | - Riso Kagaku Webmail Notifier | ||
398 | - Dream Cheeky Webmail Notifier and Friends Alert | ||
399 | - ThingM blink(1) | ||
400 | - Delcom Visual Signal Indicator Generation 2 | ||
401 | - Greynut Luxafor | ||
402 | |||
403 | To compile this driver as a module, choose M here: the | ||
404 | module will be called hid-led. | ||
405 | |||
391 | config HID_LENOVO | 406 | config HID_LENOVO |
392 | tristate "Lenovo / Thinkpad devices" | 407 | tristate "Lenovo / Thinkpad devices" |
393 | depends on HID | 408 | depends on HID |
@@ -819,11 +834,11 @@ config HID_THINGM | |||
819 | tristate "ThingM blink(1) USB RGB LED" | 834 | tristate "ThingM blink(1) USB RGB LED" |
820 | depends on HID | 835 | depends on HID |
821 | depends on LEDS_CLASS | 836 | depends on LEDS_CLASS |
837 | select HID_LED | ||
822 | ---help--- | 838 | ---help--- |
823 | Support for the ThingM blink(1) USB RGB LED. This driver registers a | 839 | Support for the ThingM blink(1) USB RGB LED. This driver has been |
824 | Linux LED class instance, plus additional sysfs attributes to control | 840 | merged into the generic hid led driver. Config symbol HID_THINGM |
825 | RGB colors, fade time and playing. The device is exposed through hidraw | 841 | just selects HID_LED and will be removed soon. |
826 | to access other functions. | ||
827 | 842 | ||
828 | config HID_THRUSTMASTER | 843 | config HID_THRUSTMASTER |
829 | tristate "ThrustMaster devices support" | 844 | tristate "ThrustMaster devices support" |
@@ -936,6 +951,14 @@ config HID_SENSOR_CUSTOM_SENSOR | |||
936 | standard sensors. | 951 | standard sensors. |
937 | Select this config option for custom/generic sensor support. | 952 | Select this config option for custom/generic sensor support. |
938 | 953 | ||
954 | config HID_ALPS | ||
955 | tristate "Alps HID device support" | ||
956 | depends on HID | ||
957 | ---help--- | ||
958 | Support for Alps I2C HID touchpads and StickPointer. | ||
959 | Say Y here if you have a Alps touchpads over i2c-hid or usbhid | ||
960 | and want support for its special functionalities. | ||
961 | |||
939 | endmenu | 962 | endmenu |
940 | 963 | ||
941 | endif # HID | 964 | endif # HID |
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index a2fb562de748..fc4b2aa47f2e 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile | |||
@@ -21,6 +21,7 @@ hid-wiimote-y := hid-wiimote-core.o hid-wiimote-modules.o | |||
21 | hid-wiimote-$(CONFIG_DEBUG_FS) += hid-wiimote-debug.o | 21 | hid-wiimote-$(CONFIG_DEBUG_FS) += hid-wiimote-debug.o |
22 | 22 | ||
23 | obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o | 23 | obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o |
24 | obj-$(CONFIG_HID_ALPS) += hid-alps.o | ||
24 | obj-$(CONFIG_HID_ACRUX) += hid-axff.o | 25 | obj-$(CONFIG_HID_ACRUX) += hid-axff.o |
25 | obj-$(CONFIG_HID_APPLE) += hid-apple.o | 26 | obj-$(CONFIG_HID_APPLE) += hid-apple.o |
26 | obj-$(CONFIG_HID_APPLEIR) += hid-appleir.o | 27 | obj-$(CONFIG_HID_APPLEIR) += hid-appleir.o |
@@ -90,12 +91,12 @@ obj-$(CONFIG_HID_SPEEDLINK) += hid-speedlink.o | |||
90 | obj-$(CONFIG_HID_STEELSERIES) += hid-steelseries.o | 91 | obj-$(CONFIG_HID_STEELSERIES) += hid-steelseries.o |
91 | obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o | 92 | obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o |
92 | obj-$(CONFIG_HID_GREENASIA) += hid-gaff.o | 93 | obj-$(CONFIG_HID_GREENASIA) += hid-gaff.o |
93 | obj-$(CONFIG_HID_THINGM) += hid-thingm.o | ||
94 | obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o | 94 | obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o |
95 | obj-$(CONFIG_HID_TIVO) += hid-tivo.o | 95 | obj-$(CONFIG_HID_TIVO) += hid-tivo.o |
96 | obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o | 96 | obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o |
97 | obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o | 97 | obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o |
98 | obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o | 98 | obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o |
99 | obj-$(CONFIG_HID_LED) += hid-led.o | ||
99 | obj-$(CONFIG_HID_XINMO) += hid-xinmo.o | 100 | obj-$(CONFIG_HID_XINMO) += hid-xinmo.o |
100 | obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o | 101 | obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o |
101 | obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o | 102 | obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o |
diff --git a/drivers/hid/hid-alps.c b/drivers/hid/hid-alps.c new file mode 100644 index 000000000000..048befde295a --- /dev/null +++ b/drivers/hid/hid-alps.c | |||
@@ -0,0 +1,506 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016 Masaki Ota <masaki.ota@jp.alps.com> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of the GNU General Public License as published by the Free | ||
6 | * Software Foundation; either version 2 of the License, or (at your option) | ||
7 | * any later version. | ||
8 | */ | ||
9 | |||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/hid.h> | ||
12 | #include <linux/input.h> | ||
13 | #include <linux/input/mt.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <asm/unaligned.h> | ||
16 | #include "hid-ids.h" | ||
17 | |||
18 | /* ALPS Device Product ID */ | ||
19 | #define HID_PRODUCT_ID_T3_BTNLESS 0xD0C0 | ||
20 | #define HID_PRODUCT_ID_COSMO 0x1202 | ||
21 | #define HID_PRODUCT_ID_U1_PTP_1 0x1207 | ||
22 | #define HID_PRODUCT_ID_U1 0x1209 | ||
23 | #define HID_PRODUCT_ID_U1_PTP_2 0x120A | ||
24 | #define HID_PRODUCT_ID_U1_DUAL 0x120B | ||
25 | #define HID_PRODUCT_ID_T4_BTNLESS 0x120C | ||
26 | |||
27 | #define DEV_SINGLEPOINT 0x01 | ||
28 | #define DEV_DUALPOINT 0x02 | ||
29 | |||
30 | #define U1_MOUSE_REPORT_ID 0x01 /* Mouse data ReportID */ | ||
31 | #define U1_ABSOLUTE_REPORT_ID 0x03 /* Absolute data ReportID */ | ||
32 | #define U1_FEATURE_REPORT_ID 0x05 /* Feature ReportID */ | ||
33 | #define U1_SP_ABSOLUTE_REPORT_ID 0x06 /* Feature ReportID */ | ||
34 | |||
35 | #define U1_FEATURE_REPORT_LEN 0x08 /* Feature Report Length */ | ||
36 | #define U1_FEATURE_REPORT_LEN_ALL 0x0A | ||
37 | #define U1_CMD_REGISTER_READ 0xD1 | ||
38 | #define U1_CMD_REGISTER_WRITE 0xD2 | ||
39 | |||
40 | #define U1_DEVTYPE_SP_SUPPORT 0x10 /* SP Support */ | ||
41 | #define U1_DISABLE_DEV 0x01 | ||
42 | #define U1_TP_ABS_MODE 0x02 | ||
43 | #define U1_SP_ABS_MODE 0x80 | ||
44 | |||
45 | #define ADDRESS_U1_DEV_CTRL_1 0x00800040 | ||
46 | #define ADDRESS_U1_DEVICE_TYP 0x00800043 | ||
47 | #define ADDRESS_U1_NUM_SENS_X 0x00800047 | ||
48 | #define ADDRESS_U1_NUM_SENS_Y 0x00800048 | ||
49 | #define ADDRESS_U1_PITCH_SENS_X 0x00800049 | ||
50 | #define ADDRESS_U1_PITCH_SENS_Y 0x0080004A | ||
51 | #define ADDRESS_U1_RESO_DWN_ABS 0x0080004E | ||
52 | #define ADDRESS_U1_PAD_BTN 0x00800052 | ||
53 | #define ADDRESS_U1_SP_BTN 0x0080009F | ||
54 | |||
55 | #define MAX_TOUCHES 5 | ||
56 | |||
57 | /** | ||
58 | * struct u1_data | ||
59 | * | ||
60 | * @input: pointer to the kernel input device | ||
61 | * @input2: pointer to the kernel input2 device | ||
62 | * @hdev: pointer to the struct hid_device | ||
63 | * | ||
64 | * @dev_ctrl: device control parameter | ||
65 | * @dev_type: device type | ||
66 | * @sen_line_num_x: number of sensor line of X | ||
67 | * @sen_line_num_y: number of sensor line of Y | ||
68 | * @pitch_x: sensor pitch of X | ||
69 | * @pitch_y: sensor pitch of Y | ||
70 | * @resolution: resolution | ||
71 | * @btn_info: button information | ||
72 | * @x_active_len_mm: active area length of X (mm) | ||
73 | * @y_active_len_mm: active area length of Y (mm) | ||
74 | * @x_max: maximum x coordinate value | ||
75 | * @y_max: maximum y coordinate value | ||
76 | * @btn_cnt: number of buttons | ||
77 | * @sp_btn_cnt: number of stick buttons | ||
78 | */ | ||
79 | struct u1_dev { | ||
80 | struct input_dev *input; | ||
81 | struct input_dev *input2; | ||
82 | struct hid_device *hdev; | ||
83 | |||
84 | u8 dev_ctrl; | ||
85 | u8 dev_type; | ||
86 | u8 sen_line_num_x; | ||
87 | u8 sen_line_num_y; | ||
88 | u8 pitch_x; | ||
89 | u8 pitch_y; | ||
90 | u8 resolution; | ||
91 | u8 btn_info; | ||
92 | u8 sp_btn_info; | ||
93 | u32 x_active_len_mm; | ||
94 | u32 y_active_len_mm; | ||
95 | u32 x_max; | ||
96 | u32 y_max; | ||
97 | u32 btn_cnt; | ||
98 | u32 sp_btn_cnt; | ||
99 | }; | ||
100 | |||
101 | static int u1_read_write_register(struct hid_device *hdev, u32 address, | ||
102 | u8 *read_val, u8 write_val, bool read_flag) | ||
103 | { | ||
104 | int ret, i; | ||
105 | u8 check_sum; | ||
106 | u8 *input; | ||
107 | u8 *readbuf; | ||
108 | |||
109 | input = kzalloc(U1_FEATURE_REPORT_LEN, GFP_KERNEL); | ||
110 | if (!input) | ||
111 | return -ENOMEM; | ||
112 | |||
113 | input[0] = U1_FEATURE_REPORT_ID; | ||
114 | if (read_flag) { | ||
115 | input[1] = U1_CMD_REGISTER_READ; | ||
116 | input[6] = 0x00; | ||
117 | } else { | ||
118 | input[1] = U1_CMD_REGISTER_WRITE; | ||
119 | input[6] = write_val; | ||
120 | } | ||
121 | |||
122 | put_unaligned_le32(address, input + 2); | ||
123 | |||
124 | /* Calculate the checksum */ | ||
125 | check_sum = U1_FEATURE_REPORT_LEN_ALL; | ||
126 | for (i = 0; i < U1_FEATURE_REPORT_LEN - 1; i++) | ||
127 | check_sum += input[i]; | ||
128 | |||
129 | input[7] = check_sum; | ||
130 | ret = hid_hw_raw_request(hdev, U1_FEATURE_REPORT_ID, input, | ||
131 | U1_FEATURE_REPORT_LEN, | ||
132 | HID_FEATURE_REPORT, HID_REQ_SET_REPORT); | ||
133 | |||
134 | if (ret < 0) { | ||
135 | dev_err(&hdev->dev, "failed to read command (%d)\n", ret); | ||
136 | goto exit; | ||
137 | } | ||
138 | |||
139 | if (read_flag) { | ||
140 | readbuf = kzalloc(U1_FEATURE_REPORT_LEN, GFP_KERNEL); | ||
141 | if (!readbuf) { | ||
142 | kfree(input); | ||
143 | return -ENOMEM; | ||
144 | } | ||
145 | |||
146 | ret = hid_hw_raw_request(hdev, U1_FEATURE_REPORT_ID, readbuf, | ||
147 | U1_FEATURE_REPORT_LEN, | ||
148 | HID_FEATURE_REPORT, HID_REQ_GET_REPORT); | ||
149 | |||
150 | if (ret < 0) { | ||
151 | dev_err(&hdev->dev, "failed read register (%d)\n", ret); | ||
152 | goto exit; | ||
153 | } | ||
154 | |||
155 | *read_val = readbuf[6]; | ||
156 | |||
157 | kfree(readbuf); | ||
158 | } | ||
159 | |||
160 | ret = 0; | ||
161 | |||
162 | exit: | ||
163 | kfree(input); | ||
164 | return ret; | ||
165 | } | ||
166 | |||
167 | static int alps_raw_event(struct hid_device *hdev, | ||
168 | struct hid_report *report, u8 *data, int size) | ||
169 | { | ||
170 | unsigned int x, y, z; | ||
171 | int i; | ||
172 | short sp_x, sp_y; | ||
173 | struct u1_dev *hdata = hid_get_drvdata(hdev); | ||
174 | |||
175 | switch (data[0]) { | ||
176 | case U1_MOUSE_REPORT_ID: | ||
177 | break; | ||
178 | case U1_FEATURE_REPORT_ID: | ||
179 | break; | ||
180 | case U1_ABSOLUTE_REPORT_ID: | ||
181 | for (i = 0; i < MAX_TOUCHES; i++) { | ||
182 | u8 *contact = &data[i * 5]; | ||
183 | |||
184 | x = get_unaligned_le16(contact + 3); | ||
185 | y = get_unaligned_le16(contact + 5); | ||
186 | z = contact[7] & 0x7F; | ||
187 | |||
188 | input_mt_slot(hdata->input, i); | ||
189 | |||
190 | if (z != 0) { | ||
191 | input_mt_report_slot_state(hdata->input, | ||
192 | MT_TOOL_FINGER, 1); | ||
193 | } else { | ||
194 | input_mt_report_slot_state(hdata->input, | ||
195 | MT_TOOL_FINGER, 0); | ||
196 | break; | ||
197 | } | ||
198 | |||
199 | input_report_abs(hdata->input, ABS_MT_POSITION_X, x); | ||
200 | input_report_abs(hdata->input, ABS_MT_POSITION_Y, y); | ||
201 | input_report_abs(hdata->input, ABS_MT_PRESSURE, z); | ||
202 | |||
203 | } | ||
204 | |||
205 | input_mt_sync_frame(hdata->input); | ||
206 | |||
207 | input_report_key(hdata->input, BTN_LEFT, | ||
208 | data[1] & 0x1); | ||
209 | input_report_key(hdata->input, BTN_RIGHT, | ||
210 | (data[1] & 0x2)); | ||
211 | input_report_key(hdata->input, BTN_MIDDLE, | ||
212 | (data[1] & 0x4)); | ||
213 | |||
214 | input_sync(hdata->input); | ||
215 | |||
216 | return 1; | ||
217 | |||
218 | case U1_SP_ABSOLUTE_REPORT_ID: | ||
219 | sp_x = get_unaligned_le16(data+2); | ||
220 | sp_y = get_unaligned_le16(data+4); | ||
221 | |||
222 | sp_x = sp_x / 8; | ||
223 | sp_y = sp_y / 8; | ||
224 | |||
225 | input_report_rel(hdata->input2, REL_X, sp_x); | ||
226 | input_report_rel(hdata->input2, REL_Y, sp_y); | ||
227 | |||
228 | input_report_key(hdata->input2, BTN_LEFT, | ||
229 | data[1] & 0x1); | ||
230 | input_report_key(hdata->input2, BTN_RIGHT, | ||
231 | (data[1] & 0x2)); | ||
232 | input_report_key(hdata->input2, BTN_MIDDLE, | ||
233 | (data[1] & 0x4)); | ||
234 | |||
235 | input_sync(hdata->input2); | ||
236 | |||
237 | return 1; | ||
238 | } | ||
239 | |||
240 | return 0; | ||
241 | } | ||
242 | |||
243 | #ifdef CONFIG_PM | ||
244 | static int alps_post_reset(struct hid_device *hdev) | ||
245 | { | ||
246 | return u1_read_write_register(hdev, ADDRESS_U1_DEV_CTRL_1, | ||
247 | NULL, U1_TP_ABS_MODE, false); | ||
248 | } | ||
249 | |||
250 | static int alps_post_resume(struct hid_device *hdev) | ||
251 | { | ||
252 | return u1_read_write_register(hdev, ADDRESS_U1_DEV_CTRL_1, | ||
253 | NULL, U1_TP_ABS_MODE, false); | ||
254 | } | ||
255 | #endif /* CONFIG_PM */ | ||
256 | |||
257 | static int alps_input_configured(struct hid_device *hdev, struct hid_input *hi) | ||
258 | { | ||
259 | struct u1_dev *data = hid_get_drvdata(hdev); | ||
260 | struct input_dev *input = hi->input, *input2; | ||
261 | struct u1_dev devInfo; | ||
262 | int ret; | ||
263 | int res_x, res_y, i; | ||
264 | |||
265 | data->input = input; | ||
266 | |||
267 | hid_dbg(hdev, "Opening low level driver\n"); | ||
268 | ret = hid_hw_open(hdev); | ||
269 | if (ret) | ||
270 | return ret; | ||
271 | |||
272 | /* Allow incoming hid reports */ | ||
273 | hid_device_io_start(hdev); | ||
274 | |||
275 | /* Device initialization */ | ||
276 | ret = u1_read_write_register(hdev, ADDRESS_U1_DEV_CTRL_1, | ||
277 | &devInfo.dev_ctrl, 0, true); | ||
278 | if (ret < 0) { | ||
279 | dev_err(&hdev->dev, "failed U1_DEV_CTRL_1 (%d)\n", ret); | ||
280 | goto exit; | ||
281 | } | ||
282 | |||
283 | devInfo.dev_ctrl &= ~U1_DISABLE_DEV; | ||
284 | devInfo.dev_ctrl |= U1_TP_ABS_MODE; | ||
285 | ret = u1_read_write_register(hdev, ADDRESS_U1_DEV_CTRL_1, | ||
286 | NULL, devInfo.dev_ctrl, false); | ||
287 | if (ret < 0) { | ||
288 | dev_err(&hdev->dev, "failed to change TP mode (%d)\n", ret); | ||
289 | goto exit; | ||
290 | } | ||
291 | |||
292 | ret = u1_read_write_register(hdev, ADDRESS_U1_NUM_SENS_X, | ||
293 | &devInfo.sen_line_num_x, 0, true); | ||
294 | if (ret < 0) { | ||
295 | dev_err(&hdev->dev, "failed U1_NUM_SENS_X (%d)\n", ret); | ||
296 | goto exit; | ||
297 | } | ||
298 | |||
299 | ret = u1_read_write_register(hdev, ADDRESS_U1_NUM_SENS_Y, | ||
300 | &devInfo.sen_line_num_y, 0, true); | ||
301 | if (ret < 0) { | ||
302 | dev_err(&hdev->dev, "failed U1_NUM_SENS_Y (%d)\n", ret); | ||
303 | goto exit; | ||
304 | } | ||
305 | |||
306 | ret = u1_read_write_register(hdev, ADDRESS_U1_PITCH_SENS_X, | ||
307 | &devInfo.pitch_x, 0, true); | ||
308 | if (ret < 0) { | ||
309 | dev_err(&hdev->dev, "failed U1_PITCH_SENS_X (%d)\n", ret); | ||
310 | goto exit; | ||
311 | } | ||
312 | |||
313 | ret = u1_read_write_register(hdev, ADDRESS_U1_PITCH_SENS_Y, | ||
314 | &devInfo.pitch_y, 0, true); | ||
315 | if (ret < 0) { | ||
316 | dev_err(&hdev->dev, "failed U1_PITCH_SENS_Y (%d)\n", ret); | ||
317 | goto exit; | ||
318 | } | ||
319 | |||
320 | ret = u1_read_write_register(hdev, ADDRESS_U1_RESO_DWN_ABS, | ||
321 | &devInfo.resolution, 0, true); | ||
322 | if (ret < 0) { | ||
323 | dev_err(&hdev->dev, "failed U1_RESO_DWN_ABS (%d)\n", ret); | ||
324 | goto exit; | ||
325 | } | ||
326 | |||
327 | ret = u1_read_write_register(hdev, ADDRESS_U1_PAD_BTN, | ||
328 | &devInfo.btn_info, 0, true); | ||
329 | if (ret < 0) { | ||
330 | dev_err(&hdev->dev, "failed U1_PAD_BTN (%d)\n", ret); | ||
331 | goto exit; | ||
332 | } | ||
333 | |||
334 | /* Check StickPointer device */ | ||
335 | ret = u1_read_write_register(hdev, ADDRESS_U1_DEVICE_TYP, | ||
336 | &devInfo.dev_type, 0, true); | ||
337 | if (ret < 0) { | ||
338 | dev_err(&hdev->dev, "failed U1_DEVICE_TYP (%d)\n", ret); | ||
339 | goto exit; | ||
340 | } | ||
341 | |||
342 | devInfo.x_active_len_mm = | ||
343 | (devInfo.pitch_x * (devInfo.sen_line_num_x - 1)) / 10; | ||
344 | devInfo.y_active_len_mm = | ||
345 | (devInfo.pitch_y * (devInfo.sen_line_num_y - 1)) / 10; | ||
346 | |||
347 | devInfo.x_max = | ||
348 | (devInfo.resolution << 2) * (devInfo.sen_line_num_x - 1); | ||
349 | devInfo.y_max = | ||
350 | (devInfo.resolution << 2) * (devInfo.sen_line_num_y - 1); | ||
351 | |||
352 | __set_bit(EV_ABS, input->evbit); | ||
353 | input_set_abs_params(input, ABS_MT_POSITION_X, 1, devInfo.x_max, 0, 0); | ||
354 | input_set_abs_params(input, ABS_MT_POSITION_Y, 1, devInfo.y_max, 0, 0); | ||
355 | |||
356 | if (devInfo.x_active_len_mm && devInfo.y_active_len_mm) { | ||
357 | res_x = (devInfo.x_max - 1) / devInfo.x_active_len_mm; | ||
358 | res_y = (devInfo.y_max - 1) / devInfo.y_active_len_mm; | ||
359 | |||
360 | input_abs_set_res(input, ABS_MT_POSITION_X, res_x); | ||
361 | input_abs_set_res(input, ABS_MT_POSITION_Y, res_y); | ||
362 | } | ||
363 | |||
364 | input_set_abs_params(input, ABS_MT_PRESSURE, 0, 64, 0, 0); | ||
365 | |||
366 | input_mt_init_slots(input, MAX_TOUCHES, INPUT_MT_POINTER); | ||
367 | |||
368 | __set_bit(EV_KEY, input->evbit); | ||
369 | if ((devInfo.btn_info & 0x0F) == (devInfo.btn_info & 0xF0) >> 4) { | ||
370 | devInfo.btn_cnt = (devInfo.btn_info & 0x0F); | ||
371 | } else { | ||
372 | /* Button pad */ | ||
373 | devInfo.btn_cnt = 1; | ||
374 | __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); | ||
375 | } | ||
376 | |||
377 | for (i = 0; i < devInfo.btn_cnt; i++) | ||
378 | __set_bit(BTN_LEFT + i, input->keybit); | ||
379 | |||
380 | |||
381 | /* Stick device initialization */ | ||
382 | if (devInfo.dev_type & U1_DEVTYPE_SP_SUPPORT) { | ||
383 | |||
384 | input2 = input_allocate_device(); | ||
385 | if (!input2) { | ||
386 | input_free_device(input2); | ||
387 | goto exit; | ||
388 | } | ||
389 | |||
390 | data->input2 = input2; | ||
391 | |||
392 | devInfo.dev_ctrl |= U1_SP_ABS_MODE; | ||
393 | ret = u1_read_write_register(hdev, ADDRESS_U1_DEV_CTRL_1, | ||
394 | NULL, devInfo.dev_ctrl, false); | ||
395 | if (ret < 0) { | ||
396 | dev_err(&hdev->dev, "failed SP mode (%d)\n", ret); | ||
397 | input_free_device(input2); | ||
398 | goto exit; | ||
399 | } | ||
400 | |||
401 | ret = u1_read_write_register(hdev, ADDRESS_U1_SP_BTN, | ||
402 | &devInfo.sp_btn_info, 0, true); | ||
403 | if (ret < 0) { | ||
404 | dev_err(&hdev->dev, "failed U1_SP_BTN (%d)\n", ret); | ||
405 | input_free_device(input2); | ||
406 | goto exit; | ||
407 | } | ||
408 | |||
409 | input2->phys = input->phys; | ||
410 | input2->name = "DualPoint Stick"; | ||
411 | input2->id.bustype = BUS_I2C; | ||
412 | input2->id.vendor = input->id.vendor; | ||
413 | input2->id.product = input->id.product; | ||
414 | input2->id.version = input->id.version; | ||
415 | input2->dev.parent = input->dev.parent; | ||
416 | |||
417 | __set_bit(EV_KEY, input2->evbit); | ||
418 | devInfo.sp_btn_cnt = (devInfo.sp_btn_info & 0x0F); | ||
419 | for (i = 0; i < devInfo.sp_btn_cnt; i++) | ||
420 | __set_bit(BTN_LEFT + i, input2->keybit); | ||
421 | |||
422 | __set_bit(EV_REL, input2->evbit); | ||
423 | __set_bit(REL_X, input2->relbit); | ||
424 | __set_bit(REL_Y, input2->relbit); | ||
425 | __set_bit(INPUT_PROP_POINTER, input2->propbit); | ||
426 | __set_bit(INPUT_PROP_POINTING_STICK, input2->propbit); | ||
427 | |||
428 | if (input_register_device(data->input2)) { | ||
429 | input_free_device(input2); | ||
430 | goto exit; | ||
431 | } | ||
432 | } | ||
433 | |||
434 | exit: | ||
435 | hid_device_io_stop(hdev); | ||
436 | hid_hw_close(hdev); | ||
437 | return ret; | ||
438 | } | ||
439 | |||
440 | static int alps_input_mapping(struct hid_device *hdev, | ||
441 | struct hid_input *hi, struct hid_field *field, | ||
442 | struct hid_usage *usage, unsigned long **bit, int *max) | ||
443 | { | ||
444 | return -1; | ||
445 | } | ||
446 | |||
447 | static int alps_probe(struct hid_device *hdev, const struct hid_device_id *id) | ||
448 | { | ||
449 | struct u1_dev *data = NULL; | ||
450 | int ret; | ||
451 | |||
452 | data = devm_kzalloc(&hdev->dev, sizeof(struct u1_dev), GFP_KERNEL); | ||
453 | if (!data) | ||
454 | return -ENOMEM; | ||
455 | |||
456 | data->hdev = hdev; | ||
457 | hid_set_drvdata(hdev, data); | ||
458 | |||
459 | hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS; | ||
460 | |||
461 | ret = hid_parse(hdev); | ||
462 | if (ret) { | ||
463 | hid_err(hdev, "parse failed\n"); | ||
464 | return ret; | ||
465 | } | ||
466 | |||
467 | ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); | ||
468 | if (ret) { | ||
469 | hid_err(hdev, "hw start failed\n"); | ||
470 | return ret; | ||
471 | } | ||
472 | |||
473 | return 0; | ||
474 | } | ||
475 | |||
476 | static void alps_remove(struct hid_device *hdev) | ||
477 | { | ||
478 | hid_hw_stop(hdev); | ||
479 | } | ||
480 | |||
481 | static const struct hid_device_id alps_id[] = { | ||
482 | { HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, | ||
483 | USB_VENDOR_ID_ALPS_JP, HID_DEVICE_ID_ALPS_U1_DUAL) }, | ||
484 | { } | ||
485 | }; | ||
486 | MODULE_DEVICE_TABLE(hid, alps_id); | ||
487 | |||
488 | static struct hid_driver alps_driver = { | ||
489 | .name = "hid-alps", | ||
490 | .id_table = alps_id, | ||
491 | .probe = alps_probe, | ||
492 | .remove = alps_remove, | ||
493 | .raw_event = alps_raw_event, | ||
494 | .input_mapping = alps_input_mapping, | ||
495 | .input_configured = alps_input_configured, | ||
496 | #ifdef CONFIG_PM | ||
497 | .resume = alps_post_resume, | ||
498 | .reset_resume = alps_post_reset, | ||
499 | #endif | ||
500 | }; | ||
501 | |||
502 | module_hid_driver(alps_driver); | ||
503 | |||
504 | MODULE_AUTHOR("Masaki Ota <masaki.ota@jp.alps.com>"); | ||
505 | MODULE_DESCRIPTION("ALPS HID driver"); | ||
506 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 884d82f9190e..2e046082210f 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c | |||
@@ -474,6 +474,8 @@ static const struct hid_device_id apple_devices[] = { | |||
474 | .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, | 474 | .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, |
475 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS), | 475 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS), |
476 | .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, | 476 | .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, |
477 | { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_ANSI), | ||
478 | .driver_data = APPLE_HAS_FN }, | ||
477 | { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI), | 479 | { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI), |
478 | .driver_data = APPLE_HAS_FN }, | 480 | .driver_data = APPLE_HAS_FN }, |
479 | { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO), | 481 | { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO), |
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 8ea3a26360e9..08f53c7fd513 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c | |||
@@ -1772,6 +1772,7 @@ static const struct hid_device_id hid_have_special_driver[] = { | |||
1772 | { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_RP_649) }, | 1772 | { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_RP_649) }, |
1773 | { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802) }, | 1773 | { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802) }, |
1774 | { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0xf705) }, | 1774 | { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0xf705) }, |
1775 | { HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, USB_VENDOR_ID_ALPS_JP, HID_DEVICE_ID_ALPS_U1_DUAL) }, | ||
1775 | { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) }, | 1776 | { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) }, |
1776 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) }, | 1777 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) }, |
1777 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICTRACKPAD) }, | 1778 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICTRACKPAD) }, |
@@ -1851,6 +1852,7 @@ static const struct hid_device_id hid_have_special_driver[] = { | |||
1851 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI) }, | 1852 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI) }, |
1852 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO) }, | 1853 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO) }, |
1853 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_JIS) }, | 1854 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_JIS) }, |
1855 | { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_ANSI) }, | ||
1854 | { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) }, | 1856 | { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) }, |
1855 | { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, | 1857 | { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, |
1856 | { HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_NOTEBOOK_KEYBOARD) }, | 1858 | { HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_NOTEBOOK_KEYBOARD) }, |
@@ -1877,8 +1879,11 @@ static const struct hid_device_id hid_have_special_driver[] = { | |||
1877 | { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3) }, | 1879 | { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3) }, |
1878 | { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_4) }, | 1880 | { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_4) }, |
1879 | { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) }, | 1881 | { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) }, |
1882 | { HID_USB_DEVICE(USB_VENDOR_ID_DELCOM, USB_DEVICE_ID_DELCOM_VISUAL_IND) }, | ||
1880 | { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) }, | 1883 | { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) }, |
1881 | { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) }, | 1884 | { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) }, |
1885 | { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, USB_DEVICE_ID_DREAM_CHEEKY_WN) }, | ||
1886 | { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, USB_DEVICE_ID_DREAM_CHEEKY_FA) }, | ||
1882 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) }, | 1887 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) }, |
1883 | { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0009) }, | 1888 | { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0009) }, |
1884 | { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0030) }, | 1889 | { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0030) }, |
@@ -1962,6 +1967,7 @@ static const struct hid_device_id hid_have_special_driver[] = { | |||
1962 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR) }, | 1967 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR) }, |
1963 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) }, | 1968 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) }, |
1964 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) }, | 1969 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) }, |
1970 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_LUXAFOR) }, | ||
1965 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500) }, | 1971 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500) }, |
1966 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_KEYBOARD) }, | 1972 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_KEYBOARD) }, |
1967 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) }, | 1973 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) }, |
@@ -2008,6 +2014,7 @@ static const struct hid_device_id hid_have_special_driver[] = { | |||
2008 | { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, | 2014 | { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, |
2009 | { HID_USB_DEVICE(USB_VENDOR_ID_PLANTRONICS, HID_ANY_ID) }, | 2015 | { HID_USB_DEVICE(USB_VENDOR_ID_PLANTRONICS, HID_ANY_ID) }, |
2010 | { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) }, | 2016 | { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) }, |
2017 | { HID_USB_DEVICE(USB_VENDOR_ID_RISO_KAGAKU, USB_DEVICE_ID_RI_KA_WEBMAIL) }, | ||
2011 | #if IS_ENABLED(CONFIG_HID_ROCCAT) | 2018 | #if IS_ENABLED(CONFIG_HID_ROCCAT) |
2012 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) }, | 2019 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) }, |
2013 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) }, | 2020 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) }, |
@@ -2348,8 +2355,6 @@ static const struct hid_device_id hid_ignore_list[] = { | |||
2348 | { HID_USB_DEVICE(USB_VENDOR_ID_DEALEXTREAME, USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701) }, | 2355 | { HID_USB_DEVICE(USB_VENDOR_ID_DEALEXTREAME, USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701) }, |
2349 | { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE) }, | 2356 | { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE) }, |
2350 | { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) }, | 2357 | { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) }, |
2351 | { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 0x0004) }, | ||
2352 | { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 0x000a) }, | ||
2353 | { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, 0x0400) }, | 2358 | { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, 0x0400) }, |
2354 | { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, 0x0401) }, | 2359 | { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, 0x0401) }, |
2355 | { HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) }, | 2360 | { HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) }, |
@@ -2486,7 +2491,6 @@ static const struct hid_device_id hid_ignore_list[] = { | |||
2486 | { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_DPAD) }, | 2491 | { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_DPAD) }, |
2487 | #endif | 2492 | #endif |
2488 | { HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) }, | 2493 | { HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) }, |
2489 | { HID_USB_DEVICE(USB_VENDOR_ID_RISO_KAGAKU, USB_DEVICE_ID_RI_KA_WEBMAIL) }, | ||
2490 | { } | 2494 | { } |
2491 | }; | 2495 | }; |
2492 | 2496 | ||
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 3eec09a134cb..4ed9a4fdfea7 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h | |||
@@ -70,6 +70,9 @@ | |||
70 | #define USB_VENDOR_ID_ALPS 0x0433 | 70 | #define USB_VENDOR_ID_ALPS 0x0433 |
71 | #define USB_DEVICE_ID_IBM_GAMEPAD 0x1101 | 71 | #define USB_DEVICE_ID_IBM_GAMEPAD 0x1101 |
72 | 72 | ||
73 | #define USB_VENDOR_ID_ALPS_JP 0x044E | ||
74 | #define HID_DEVICE_ID_ALPS_U1_DUAL 0x120B | ||
75 | |||
73 | #define USB_VENDOR_ID_ANTON 0x1130 | 76 | #define USB_VENDOR_ID_ANTON 0x1130 |
74 | #define USB_DEVICE_ID_ANTON_TOUCH_PAD 0x3101 | 77 | #define USB_DEVICE_ID_ANTON_TOUCH_PAD 0x3101 |
75 | 78 | ||
@@ -142,6 +145,7 @@ | |||
142 | #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI 0x0255 | 145 | #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI 0x0255 |
143 | #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO 0x0256 | 146 | #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO 0x0256 |
144 | #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_JIS 0x0257 | 147 | #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_JIS 0x0257 |
148 | #define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_ANSI 0x0267 | ||
145 | #define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0290 | 149 | #define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0290 |
146 | #define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0291 | 150 | #define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0291 |
147 | #define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0292 | 151 | #define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0292 |
@@ -296,6 +300,9 @@ | |||
296 | #define USB_VENDOR_ID_DEALEXTREAME 0x10c5 | 300 | #define USB_VENDOR_ID_DEALEXTREAME 0x10c5 |
297 | #define USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701 0x819a | 301 | #define USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701 0x819a |
298 | 302 | ||
303 | #define USB_VENDOR_ID_DELCOM 0x0fc5 | ||
304 | #define USB_DEVICE_ID_DELCOM_VISUAL_IND 0xb080 | ||
305 | |||
299 | #define USB_VENDOR_ID_DELORME 0x1163 | 306 | #define USB_VENDOR_ID_DELORME 0x1163 |
300 | #define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100 | 307 | #define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100 |
301 | #define USB_DEVICE_ID_DELORME_EM_LT20 0x0200 | 308 | #define USB_DEVICE_ID_DELORME_EM_LT20 0x0200 |
@@ -334,6 +341,8 @@ | |||
334 | #define USB_DEVICE_ID_ELECOM_BM084 0x0061 | 341 | #define USB_DEVICE_ID_ELECOM_BM084 0x0061 |
335 | 342 | ||
336 | #define USB_VENDOR_ID_DREAM_CHEEKY 0x1d34 | 343 | #define USB_VENDOR_ID_DREAM_CHEEKY 0x1d34 |
344 | #define USB_DEVICE_ID_DREAM_CHEEKY_WN 0x0004 | ||
345 | #define USB_DEVICE_ID_DREAM_CHEEKY_FA 0x000a | ||
337 | 346 | ||
338 | #define USB_VENDOR_ID_ELITEGROUP 0x03fc | 347 | #define USB_VENDOR_ID_ELITEGROUP 0x03fc |
339 | #define USB_DEVICE_ID_ELITEGROUP_05D8 0x05d8 | 348 | #define USB_DEVICE_ID_ELITEGROUP_05D8 0x05d8 |
@@ -680,6 +689,7 @@ | |||
680 | #define USB_DEVICE_ID_PICOLCD_BOOTLOADER 0xf002 | 689 | #define USB_DEVICE_ID_PICOLCD_BOOTLOADER 0xf002 |
681 | #define USB_DEVICE_ID_PICK16F1454 0x0042 | 690 | #define USB_DEVICE_ID_PICK16F1454 0x0042 |
682 | #define USB_DEVICE_ID_PICK16F1454_V2 0xf2f7 | 691 | #define USB_DEVICE_ID_PICK16F1454_V2 0xf2f7 |
692 | #define USB_DEVICE_ID_LUXAFOR 0xf372 | ||
683 | 693 | ||
684 | #define USB_VENDOR_ID_MICROSOFT 0x045e | 694 | #define USB_VENDOR_ID_MICROSOFT 0x045e |
685 | #define USB_DEVICE_ID_SIDEWINDER_GV 0x003b | 695 | #define USB_DEVICE_ID_SIDEWINDER_GV 0x003b |
diff --git a/drivers/hid/hid-led.c b/drivers/hid/hid-led.c new file mode 100644 index 000000000000..d8d55f37b4f5 --- /dev/null +++ b/drivers/hid/hid-led.c | |||
@@ -0,0 +1,523 @@ | |||
1 | /* | ||
2 | * Simple USB RGB LED driver | ||
3 | * | ||
4 | * Copyright 2016 Heiner Kallweit <hkallweit1@gmail.com> | ||
5 | * Based on drivers/hid/hid-thingm.c and | ||
6 | * drivers/usb/misc/usbled.c | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License as | ||
10 | * published by the Free Software Foundation, version 2. | ||
11 | */ | ||
12 | |||
13 | #include <linux/hid.h> | ||
14 | #include <linux/hidraw.h> | ||
15 | #include <linux/leds.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/mutex.h> | ||
18 | |||
19 | #include "hid-ids.h" | ||
20 | |||
21 | enum hidled_report_type { | ||
22 | RAW_REQUEST, | ||
23 | OUTPUT_REPORT | ||
24 | }; | ||
25 | |||
26 | enum hidled_type { | ||
27 | RISO_KAGAKU, | ||
28 | DREAM_CHEEKY, | ||
29 | THINGM, | ||
30 | DELCOM, | ||
31 | LUXAFOR, | ||
32 | }; | ||
33 | |||
34 | static unsigned const char riso_kagaku_tbl[] = { | ||
35 | /* R+2G+4B -> riso kagaku color index */ | ||
36 | [0] = 0, /* black */ | ||
37 | [1] = 2, /* red */ | ||
38 | [2] = 1, /* green */ | ||
39 | [3] = 5, /* yellow */ | ||
40 | [4] = 3, /* blue */ | ||
41 | [5] = 6, /* magenta */ | ||
42 | [6] = 4, /* cyan */ | ||
43 | [7] = 7 /* white */ | ||
44 | }; | ||
45 | |||
46 | #define RISO_KAGAKU_IX(r, g, b) riso_kagaku_tbl[((r)?1:0)+((g)?2:0)+((b)?4:0)] | ||
47 | |||
48 | union delcom_packet { | ||
49 | __u8 data[8]; | ||
50 | struct { | ||
51 | __u8 major_cmd; | ||
52 | __u8 minor_cmd; | ||
53 | __u8 data_lsb; | ||
54 | __u8 data_msb; | ||
55 | } tx; | ||
56 | struct { | ||
57 | __u8 cmd; | ||
58 | } rx; | ||
59 | struct { | ||
60 | __le16 family_code; | ||
61 | __le16 security_code; | ||
62 | __u8 fw_version; | ||
63 | } fw; | ||
64 | }; | ||
65 | |||
66 | #define DELCOM_GREEN_LED 0 | ||
67 | #define DELCOM_RED_LED 1 | ||
68 | #define DELCOM_BLUE_LED 2 | ||
69 | |||
70 | struct hidled_device; | ||
71 | struct hidled_rgb; | ||
72 | |||
73 | struct hidled_config { | ||
74 | enum hidled_type type; | ||
75 | const char *name; | ||
76 | const char *short_name; | ||
77 | enum led_brightness max_brightness; | ||
78 | int num_leds; | ||
79 | size_t report_size; | ||
80 | enum hidled_report_type report_type; | ||
81 | int (*init)(struct hidled_device *ldev); | ||
82 | int (*write)(struct led_classdev *cdev, enum led_brightness br); | ||
83 | }; | ||
84 | |||
85 | struct hidled_led { | ||
86 | struct led_classdev cdev; | ||
87 | struct hidled_rgb *rgb; | ||
88 | char name[32]; | ||
89 | }; | ||
90 | |||
91 | struct hidled_rgb { | ||
92 | struct hidled_device *ldev; | ||
93 | struct hidled_led red; | ||
94 | struct hidled_led green; | ||
95 | struct hidled_led blue; | ||
96 | u8 num; | ||
97 | }; | ||
98 | |||
99 | struct hidled_device { | ||
100 | const struct hidled_config *config; | ||
101 | struct hid_device *hdev; | ||
102 | struct hidled_rgb *rgb; | ||
103 | struct mutex lock; | ||
104 | }; | ||
105 | |||
106 | #define MAX_REPORT_SIZE 16 | ||
107 | |||
108 | #define to_hidled_led(arg) container_of(arg, struct hidled_led, cdev) | ||
109 | |||
110 | static bool riso_kagaku_switch_green_blue; | ||
111 | module_param(riso_kagaku_switch_green_blue, bool, S_IRUGO | S_IWUSR); | ||
112 | MODULE_PARM_DESC(riso_kagaku_switch_green_blue, | ||
113 | "switch green and blue RGB component for Riso Kagaku devices"); | ||
114 | |||
115 | static int hidled_send(struct hidled_device *ldev, __u8 *buf) | ||
116 | { | ||
117 | int ret; | ||
118 | |||
119 | mutex_lock(&ldev->lock); | ||
120 | |||
121 | if (ldev->config->report_type == RAW_REQUEST) | ||
122 | ret = hid_hw_raw_request(ldev->hdev, buf[0], buf, | ||
123 | ldev->config->report_size, | ||
124 | HID_FEATURE_REPORT, | ||
125 | HID_REQ_SET_REPORT); | ||
126 | else if (ldev->config->report_type == OUTPUT_REPORT) | ||
127 | ret = hid_hw_output_report(ldev->hdev, buf, | ||
128 | ldev->config->report_size); | ||
129 | else | ||
130 | ret = -EINVAL; | ||
131 | |||
132 | mutex_unlock(&ldev->lock); | ||
133 | |||
134 | if (ret < 0) | ||
135 | return ret; | ||
136 | |||
137 | return ret == ldev->config->report_size ? 0 : -EMSGSIZE; | ||
138 | } | ||
139 | |||
140 | /* reading data is supported for report type RAW_REQUEST only */ | ||
141 | static int hidled_recv(struct hidled_device *ldev, __u8 *buf) | ||
142 | { | ||
143 | int ret; | ||
144 | |||
145 | if (ldev->config->report_type != RAW_REQUEST) | ||
146 | return -EINVAL; | ||
147 | |||
148 | mutex_lock(&ldev->lock); | ||
149 | |||
150 | ret = hid_hw_raw_request(ldev->hdev, buf[0], buf, | ||
151 | ldev->config->report_size, | ||
152 | HID_FEATURE_REPORT, | ||
153 | HID_REQ_SET_REPORT); | ||
154 | if (ret < 0) | ||
155 | goto err; | ||
156 | |||
157 | ret = hid_hw_raw_request(ldev->hdev, buf[0], buf, | ||
158 | ldev->config->report_size, | ||
159 | HID_FEATURE_REPORT, | ||
160 | HID_REQ_GET_REPORT); | ||
161 | err: | ||
162 | mutex_unlock(&ldev->lock); | ||
163 | |||
164 | return ret < 0 ? ret : 0; | ||
165 | } | ||
166 | |||
167 | static u8 riso_kagaku_index(struct hidled_rgb *rgb) | ||
168 | { | ||
169 | enum led_brightness r, g, b; | ||
170 | |||
171 | r = rgb->red.cdev.brightness; | ||
172 | g = rgb->green.cdev.brightness; | ||
173 | b = rgb->blue.cdev.brightness; | ||
174 | |||
175 | if (riso_kagaku_switch_green_blue) | ||
176 | return RISO_KAGAKU_IX(r, b, g); | ||
177 | else | ||
178 | return RISO_KAGAKU_IX(r, g, b); | ||
179 | } | ||
180 | |||
181 | static int riso_kagaku_write(struct led_classdev *cdev, enum led_brightness br) | ||
182 | { | ||
183 | struct hidled_led *led = to_hidled_led(cdev); | ||
184 | struct hidled_rgb *rgb = led->rgb; | ||
185 | __u8 buf[MAX_REPORT_SIZE] = {}; | ||
186 | |||
187 | buf[1] = riso_kagaku_index(rgb); | ||
188 | |||
189 | return hidled_send(rgb->ldev, buf); | ||
190 | } | ||
191 | |||
192 | static int dream_cheeky_write(struct led_classdev *cdev, enum led_brightness br) | ||
193 | { | ||
194 | struct hidled_led *led = to_hidled_led(cdev); | ||
195 | struct hidled_rgb *rgb = led->rgb; | ||
196 | __u8 buf[MAX_REPORT_SIZE] = {}; | ||
197 | |||
198 | buf[1] = rgb->red.cdev.brightness; | ||
199 | buf[2] = rgb->green.cdev.brightness; | ||
200 | buf[3] = rgb->blue.cdev.brightness; | ||
201 | buf[7] = 0x1a; | ||
202 | buf[8] = 0x05; | ||
203 | |||
204 | return hidled_send(rgb->ldev, buf); | ||
205 | } | ||
206 | |||
207 | static int dream_cheeky_init(struct hidled_device *ldev) | ||
208 | { | ||
209 | __u8 buf[MAX_REPORT_SIZE] = {}; | ||
210 | |||
211 | /* Dream Cheeky magic */ | ||
212 | buf[1] = 0x1f; | ||
213 | buf[2] = 0x02; | ||
214 | buf[4] = 0x5f; | ||
215 | buf[7] = 0x1a; | ||
216 | buf[8] = 0x03; | ||
217 | |||
218 | return hidled_send(ldev, buf); | ||
219 | } | ||
220 | |||
221 | static int _thingm_write(struct led_classdev *cdev, enum led_brightness br, | ||
222 | u8 offset) | ||
223 | { | ||
224 | struct hidled_led *led = to_hidled_led(cdev); | ||
225 | __u8 buf[MAX_REPORT_SIZE] = { 1, 'c' }; | ||
226 | |||
227 | buf[2] = led->rgb->red.cdev.brightness; | ||
228 | buf[3] = led->rgb->green.cdev.brightness; | ||
229 | buf[4] = led->rgb->blue.cdev.brightness; | ||
230 | buf[7] = led->rgb->num + offset; | ||
231 | |||
232 | return hidled_send(led->rgb->ldev, buf); | ||
233 | } | ||
234 | |||
235 | static int thingm_write_v1(struct led_classdev *cdev, enum led_brightness br) | ||
236 | { | ||
237 | return _thingm_write(cdev, br, 0); | ||
238 | } | ||
239 | |||
240 | static int thingm_write(struct led_classdev *cdev, enum led_brightness br) | ||
241 | { | ||
242 | return _thingm_write(cdev, br, 1); | ||
243 | } | ||
244 | |||
245 | static const struct hidled_config hidled_config_thingm_v1 = { | ||
246 | .name = "ThingM blink(1) v1", | ||
247 | .short_name = "thingm", | ||
248 | .max_brightness = 255, | ||
249 | .num_leds = 1, | ||
250 | .report_size = 9, | ||
251 | .report_type = RAW_REQUEST, | ||
252 | .write = thingm_write_v1, | ||
253 | }; | ||
254 | |||
255 | static int thingm_init(struct hidled_device *ldev) | ||
256 | { | ||
257 | __u8 buf[MAX_REPORT_SIZE] = { 1, 'v' }; | ||
258 | int ret; | ||
259 | |||
260 | ret = hidled_recv(ldev, buf); | ||
261 | if (ret) | ||
262 | return ret; | ||
263 | |||
264 | /* Check for firmware major version 1 */ | ||
265 | if (buf[3] == '1') | ||
266 | ldev->config = &hidled_config_thingm_v1; | ||
267 | |||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | static inline int delcom_get_lednum(const struct hidled_led *led) | ||
272 | { | ||
273 | if (led == &led->rgb->red) | ||
274 | return DELCOM_RED_LED; | ||
275 | else if (led == &led->rgb->green) | ||
276 | return DELCOM_GREEN_LED; | ||
277 | else | ||
278 | return DELCOM_BLUE_LED; | ||
279 | } | ||
280 | |||
281 | static int delcom_enable_led(struct hidled_led *led) | ||
282 | { | ||
283 | union delcom_packet dp = { .tx.major_cmd = 101, .tx.minor_cmd = 12 }; | ||
284 | |||
285 | dp.tx.data_lsb = 1 << delcom_get_lednum(led); | ||
286 | dp.tx.data_msb = 0; | ||
287 | |||
288 | return hidled_send(led->rgb->ldev, dp.data); | ||
289 | } | ||
290 | |||
291 | static int delcom_set_pwm(struct hidled_led *led) | ||
292 | { | ||
293 | union delcom_packet dp = { .tx.major_cmd = 101, .tx.minor_cmd = 34 }; | ||
294 | |||
295 | dp.tx.data_lsb = delcom_get_lednum(led); | ||
296 | dp.tx.data_msb = led->cdev.brightness; | ||
297 | |||
298 | return hidled_send(led->rgb->ldev, dp.data); | ||
299 | } | ||
300 | |||
301 | static int delcom_write(struct led_classdev *cdev, enum led_brightness br) | ||
302 | { | ||
303 | struct hidled_led *led = to_hidled_led(cdev); | ||
304 | int ret; | ||
305 | |||
306 | /* | ||
307 | * enable LED | ||
308 | * We can't do this in the init function already because the device | ||
309 | * is internally reset later. | ||
310 | */ | ||
311 | ret = delcom_enable_led(led); | ||
312 | if (ret) | ||
313 | return ret; | ||
314 | |||
315 | return delcom_set_pwm(led); | ||
316 | } | ||
317 | |||
318 | static int delcom_init(struct hidled_device *ldev) | ||
319 | { | ||
320 | union delcom_packet dp = { .rx.cmd = 104 }; | ||
321 | int ret; | ||
322 | |||
323 | ret = hidled_recv(ldev, dp.data); | ||
324 | if (ret) | ||
325 | return ret; | ||
326 | /* | ||
327 | * Several Delcom devices share the same USB VID/PID | ||
328 | * Check for family id 2 for Visual Signal Indicator | ||
329 | */ | ||
330 | return le16_to_cpu(dp.fw.family_code) == 2 ? 0 : -ENODEV; | ||
331 | } | ||
332 | |||
333 | static int luxafor_write(struct led_classdev *cdev, enum led_brightness br) | ||
334 | { | ||
335 | struct hidled_led *led = to_hidled_led(cdev); | ||
336 | __u8 buf[MAX_REPORT_SIZE] = { [1] = 1 }; | ||
337 | |||
338 | buf[2] = led->rgb->num + 1; | ||
339 | buf[3] = led->rgb->red.cdev.brightness; | ||
340 | buf[4] = led->rgb->green.cdev.brightness; | ||
341 | buf[5] = led->rgb->blue.cdev.brightness; | ||
342 | |||
343 | return hidled_send(led->rgb->ldev, buf); | ||
344 | } | ||
345 | |||
346 | static const struct hidled_config hidled_configs[] = { | ||
347 | { | ||
348 | .type = RISO_KAGAKU, | ||
349 | .name = "Riso Kagaku Webmail Notifier", | ||
350 | .short_name = "riso_kagaku", | ||
351 | .max_brightness = 1, | ||
352 | .num_leds = 1, | ||
353 | .report_size = 6, | ||
354 | .report_type = OUTPUT_REPORT, | ||
355 | .write = riso_kagaku_write, | ||
356 | }, | ||
357 | { | ||
358 | .type = DREAM_CHEEKY, | ||
359 | .name = "Dream Cheeky Webmail Notifier", | ||
360 | .short_name = "dream_cheeky", | ||
361 | .max_brightness = 31, | ||
362 | .num_leds = 1, | ||
363 | .report_size = 9, | ||
364 | .report_type = RAW_REQUEST, | ||
365 | .init = dream_cheeky_init, | ||
366 | .write = dream_cheeky_write, | ||
367 | }, | ||
368 | { | ||
369 | .type = THINGM, | ||
370 | .name = "ThingM blink(1)", | ||
371 | .short_name = "thingm", | ||
372 | .max_brightness = 255, | ||
373 | .num_leds = 2, | ||
374 | .report_size = 9, | ||
375 | .report_type = RAW_REQUEST, | ||
376 | .init = thingm_init, | ||
377 | .write = thingm_write, | ||
378 | }, | ||
379 | { | ||
380 | .type = DELCOM, | ||
381 | .name = "Delcom Visual Signal Indicator G2", | ||
382 | .short_name = "delcom", | ||
383 | .max_brightness = 100, | ||
384 | .num_leds = 1, | ||
385 | .report_size = 8, | ||
386 | .report_type = RAW_REQUEST, | ||
387 | .init = delcom_init, | ||
388 | .write = delcom_write, | ||
389 | }, | ||
390 | { | ||
391 | .type = LUXAFOR, | ||
392 | .name = "Greynut Luxafor", | ||
393 | .short_name = "luxafor", | ||
394 | .max_brightness = 255, | ||
395 | .num_leds = 6, | ||
396 | .report_size = 9, | ||
397 | .report_type = OUTPUT_REPORT, | ||
398 | .write = luxafor_write, | ||
399 | }, | ||
400 | }; | ||
401 | |||
402 | static int hidled_init_led(struct hidled_led *led, const char *color_name, | ||
403 | struct hidled_rgb *rgb, unsigned int minor) | ||
404 | { | ||
405 | const struct hidled_config *config = rgb->ldev->config; | ||
406 | |||
407 | if (config->num_leds > 1) | ||
408 | snprintf(led->name, sizeof(led->name), "%s%u:%s:led%u", | ||
409 | config->short_name, minor, color_name, rgb->num); | ||
410 | else | ||
411 | snprintf(led->name, sizeof(led->name), "%s%u:%s", | ||
412 | config->short_name, minor, color_name); | ||
413 | led->cdev.name = led->name; | ||
414 | led->cdev.max_brightness = config->max_brightness; | ||
415 | led->cdev.brightness_set_blocking = config->write; | ||
416 | led->cdev.flags = LED_HW_PLUGGABLE; | ||
417 | led->rgb = rgb; | ||
418 | |||
419 | return devm_led_classdev_register(&rgb->ldev->hdev->dev, &led->cdev); | ||
420 | } | ||
421 | |||
422 | static int hidled_init_rgb(struct hidled_rgb *rgb, unsigned int minor) | ||
423 | { | ||
424 | int ret; | ||
425 | |||
426 | /* Register the red diode */ | ||
427 | ret = hidled_init_led(&rgb->red, "red", rgb, minor); | ||
428 | if (ret) | ||
429 | return ret; | ||
430 | |||
431 | /* Register the green diode */ | ||
432 | ret = hidled_init_led(&rgb->green, "green", rgb, minor); | ||
433 | if (ret) | ||
434 | return ret; | ||
435 | |||
436 | /* Register the blue diode */ | ||
437 | return hidled_init_led(&rgb->blue, "blue", rgb, minor); | ||
438 | } | ||
439 | |||
440 | static int hidled_probe(struct hid_device *hdev, const struct hid_device_id *id) | ||
441 | { | ||
442 | struct hidled_device *ldev; | ||
443 | unsigned int minor; | ||
444 | int ret, i; | ||
445 | |||
446 | ldev = devm_kzalloc(&hdev->dev, sizeof(*ldev), GFP_KERNEL); | ||
447 | if (!ldev) | ||
448 | return -ENOMEM; | ||
449 | |||
450 | ret = hid_parse(hdev); | ||
451 | if (ret) | ||
452 | return ret; | ||
453 | |||
454 | ldev->hdev = hdev; | ||
455 | mutex_init(&ldev->lock); | ||
456 | |||
457 | for (i = 0; !ldev->config && i < ARRAY_SIZE(hidled_configs); i++) | ||
458 | if (hidled_configs[i].type == id->driver_data) | ||
459 | ldev->config = &hidled_configs[i]; | ||
460 | |||
461 | if (!ldev->config) | ||
462 | return -EINVAL; | ||
463 | |||
464 | if (ldev->config->init) { | ||
465 | ret = ldev->config->init(ldev); | ||
466 | if (ret) | ||
467 | return ret; | ||
468 | } | ||
469 | |||
470 | ldev->rgb = devm_kcalloc(&hdev->dev, ldev->config->num_leds, | ||
471 | sizeof(struct hidled_rgb), GFP_KERNEL); | ||
472 | if (!ldev->rgb) | ||
473 | return -ENOMEM; | ||
474 | |||
475 | ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); | ||
476 | if (ret) | ||
477 | return ret; | ||
478 | |||
479 | minor = ((struct hidraw *) hdev->hidraw)->minor; | ||
480 | |||
481 | for (i = 0; i < ldev->config->num_leds; i++) { | ||
482 | ldev->rgb[i].ldev = ldev; | ||
483 | ldev->rgb[i].num = i; | ||
484 | ret = hidled_init_rgb(&ldev->rgb[i], minor); | ||
485 | if (ret) { | ||
486 | hid_hw_stop(hdev); | ||
487 | return ret; | ||
488 | } | ||
489 | } | ||
490 | |||
491 | hid_info(hdev, "%s initialized\n", ldev->config->name); | ||
492 | |||
493 | return 0; | ||
494 | } | ||
495 | |||
496 | static const struct hid_device_id hidled_table[] = { | ||
497 | { HID_USB_DEVICE(USB_VENDOR_ID_RISO_KAGAKU, | ||
498 | USB_DEVICE_ID_RI_KA_WEBMAIL), .driver_data = RISO_KAGAKU }, | ||
499 | { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, | ||
500 | USB_DEVICE_ID_DREAM_CHEEKY_WN), .driver_data = DREAM_CHEEKY }, | ||
501 | { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, | ||
502 | USB_DEVICE_ID_DREAM_CHEEKY_FA), .driver_data = DREAM_CHEEKY }, | ||
503 | { HID_USB_DEVICE(USB_VENDOR_ID_THINGM, | ||
504 | USB_DEVICE_ID_BLINK1), .driver_data = THINGM }, | ||
505 | { HID_USB_DEVICE(USB_VENDOR_ID_DELCOM, | ||
506 | USB_DEVICE_ID_DELCOM_VISUAL_IND), .driver_data = DELCOM }, | ||
507 | { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, | ||
508 | USB_DEVICE_ID_LUXAFOR), .driver_data = LUXAFOR }, | ||
509 | { } | ||
510 | }; | ||
511 | MODULE_DEVICE_TABLE(hid, hidled_table); | ||
512 | |||
513 | static struct hid_driver hidled_driver = { | ||
514 | .name = "hid-led", | ||
515 | .probe = hidled_probe, | ||
516 | .id_table = hidled_table, | ||
517 | }; | ||
518 | |||
519 | module_hid_driver(hidled_driver); | ||
520 | |||
521 | MODULE_LICENSE("GPL"); | ||
522 | MODULE_AUTHOR("Heiner Kallweit <hkallweit1@gmail.com>"); | ||
523 | MODULE_DESCRIPTION("Simple USB RGB LED driver"); | ||
diff --git a/drivers/hid/hid-thingm.c b/drivers/hid/hid-thingm.c deleted file mode 100644 index 9ad9c6ec5bba..000000000000 --- a/drivers/hid/hid-thingm.c +++ /dev/null | |||
@@ -1,263 +0,0 @@ | |||
1 | /* | ||
2 | * ThingM blink(1) USB RGB LED driver | ||
3 | * | ||
4 | * Copyright 2013-2014 Savoir-faire Linux Inc. | ||
5 | * Vivien Didelot <vivien.didelot@savoirfairelinux.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License as | ||
9 | * published by the Free Software Foundation, version 2. | ||
10 | */ | ||
11 | |||
12 | #include <linux/hid.h> | ||
13 | #include <linux/hidraw.h> | ||
14 | #include <linux/leds.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/mutex.h> | ||
17 | |||
18 | #include "hid-ids.h" | ||
19 | |||
20 | #define REPORT_ID 1 | ||
21 | #define REPORT_SIZE 9 | ||
22 | |||
23 | /* Firmware major number of supported devices */ | ||
24 | #define THINGM_MAJOR_MK1 '1' | ||
25 | #define THINGM_MAJOR_MK2 '2' | ||
26 | |||
27 | struct thingm_fwinfo { | ||
28 | char major; | ||
29 | unsigned numrgb; | ||
30 | unsigned first; | ||
31 | }; | ||
32 | |||
33 | static const struct thingm_fwinfo thingm_fwinfo[] = { | ||
34 | { | ||
35 | .major = THINGM_MAJOR_MK1, | ||
36 | .numrgb = 1, | ||
37 | .first = 0, | ||
38 | }, { | ||
39 | .major = THINGM_MAJOR_MK2, | ||
40 | .numrgb = 2, | ||
41 | .first = 1, | ||
42 | } | ||
43 | }; | ||
44 | |||
45 | /* A red, green or blue channel, part of an RGB chip */ | ||
46 | struct thingm_led { | ||
47 | struct thingm_rgb *rgb; | ||
48 | struct led_classdev ldev; | ||
49 | char name[32]; | ||
50 | }; | ||
51 | |||
52 | /* Basically a WS2812 5050 RGB LED chip */ | ||
53 | struct thingm_rgb { | ||
54 | struct thingm_device *tdev; | ||
55 | struct thingm_led red; | ||
56 | struct thingm_led green; | ||
57 | struct thingm_led blue; | ||
58 | u8 num; | ||
59 | }; | ||
60 | |||
61 | struct thingm_device { | ||
62 | struct hid_device *hdev; | ||
63 | struct { | ||
64 | char major; | ||
65 | char minor; | ||
66 | } version; | ||
67 | const struct thingm_fwinfo *fwinfo; | ||
68 | struct mutex lock; | ||
69 | struct thingm_rgb *rgb; | ||
70 | }; | ||
71 | |||
72 | static int thingm_send(struct thingm_device *tdev, u8 buf[REPORT_SIZE]) | ||
73 | { | ||
74 | int ret; | ||
75 | |||
76 | hid_dbg(tdev->hdev, "-> %d %c %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx\n", | ||
77 | buf[0], buf[1], buf[2], buf[3], buf[4], | ||
78 | buf[5], buf[6], buf[7], buf[8]); | ||
79 | |||
80 | mutex_lock(&tdev->lock); | ||
81 | |||
82 | ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE, | ||
83 | HID_FEATURE_REPORT, HID_REQ_SET_REPORT); | ||
84 | |||
85 | mutex_unlock(&tdev->lock); | ||
86 | |||
87 | return ret < 0 ? ret : 0; | ||
88 | } | ||
89 | |||
90 | static int thingm_recv(struct thingm_device *tdev, u8 buf[REPORT_SIZE]) | ||
91 | { | ||
92 | int ret; | ||
93 | |||
94 | /* | ||
95 | * A read consists of two operations: sending the read command | ||
96 | * and the actual read from the device. Use the mutex to protect | ||
97 | * the full sequence of both operations. | ||
98 | */ | ||
99 | mutex_lock(&tdev->lock); | ||
100 | |||
101 | ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE, | ||
102 | HID_FEATURE_REPORT, HID_REQ_SET_REPORT); | ||
103 | if (ret < 0) | ||
104 | goto err; | ||
105 | |||
106 | ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE, | ||
107 | HID_FEATURE_REPORT, HID_REQ_GET_REPORT); | ||
108 | if (ret < 0) | ||
109 | goto err; | ||
110 | |||
111 | ret = 0; | ||
112 | |||
113 | hid_dbg(tdev->hdev, "<- %d %c %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx\n", | ||
114 | buf[0], buf[1], buf[2], buf[3], buf[4], | ||
115 | buf[5], buf[6], buf[7], buf[8]); | ||
116 | err: | ||
117 | mutex_unlock(&tdev->lock); | ||
118 | return ret; | ||
119 | } | ||
120 | |||
121 | static int thingm_version(struct thingm_device *tdev) | ||
122 | { | ||
123 | u8 buf[REPORT_SIZE] = { REPORT_ID, 'v', 0, 0, 0, 0, 0, 0, 0 }; | ||
124 | int err; | ||
125 | |||
126 | err = thingm_recv(tdev, buf); | ||
127 | if (err) | ||
128 | return err; | ||
129 | |||
130 | tdev->version.major = buf[3]; | ||
131 | tdev->version.minor = buf[4]; | ||
132 | |||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | static int thingm_write_color(struct thingm_rgb *rgb) | ||
137 | { | ||
138 | u8 buf[REPORT_SIZE] = { REPORT_ID, 'c', 0, 0, 0, 0, 0, rgb->num, 0 }; | ||
139 | |||
140 | buf[2] = rgb->red.ldev.brightness; | ||
141 | buf[3] = rgb->green.ldev.brightness; | ||
142 | buf[4] = rgb->blue.ldev.brightness; | ||
143 | |||
144 | return thingm_send(rgb->tdev, buf); | ||
145 | } | ||
146 | |||
147 | static int thingm_led_set(struct led_classdev *ldev, | ||
148 | enum led_brightness brightness) | ||
149 | { | ||
150 | struct thingm_led *led = container_of(ldev, struct thingm_led, ldev); | ||
151 | |||
152 | return thingm_write_color(led->rgb); | ||
153 | } | ||
154 | |||
155 | static int thingm_init_led(struct thingm_led *led, const char *color_name, | ||
156 | struct thingm_rgb *rgb, int minor) | ||
157 | { | ||
158 | snprintf(led->name, sizeof(led->name), "thingm%d:%s:led%d", | ||
159 | minor, color_name, rgb->num); | ||
160 | led->ldev.name = led->name; | ||
161 | led->ldev.max_brightness = 255; | ||
162 | led->ldev.brightness_set_blocking = thingm_led_set; | ||
163 | led->ldev.flags = LED_HW_PLUGGABLE; | ||
164 | led->rgb = rgb; | ||
165 | return devm_led_classdev_register(&rgb->tdev->hdev->dev, &led->ldev); | ||
166 | } | ||
167 | |||
168 | static int thingm_init_rgb(struct thingm_rgb *rgb) | ||
169 | { | ||
170 | const int minor = ((struct hidraw *) rgb->tdev->hdev->hidraw)->minor; | ||
171 | int err; | ||
172 | |||
173 | /* Register the red diode */ | ||
174 | err = thingm_init_led(&rgb->red, "red", rgb, minor); | ||
175 | if (err) | ||
176 | return err; | ||
177 | |||
178 | /* Register the green diode */ | ||
179 | err = thingm_init_led(&rgb->green, "green", rgb, minor); | ||
180 | if (err) | ||
181 | return err; | ||
182 | |||
183 | /* Register the blue diode */ | ||
184 | return thingm_init_led(&rgb->blue, "blue", rgb, minor); | ||
185 | } | ||
186 | |||
187 | static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id) | ||
188 | { | ||
189 | struct thingm_device *tdev; | ||
190 | int i, err; | ||
191 | |||
192 | tdev = devm_kzalloc(&hdev->dev, sizeof(struct thingm_device), | ||
193 | GFP_KERNEL); | ||
194 | if (!tdev) | ||
195 | return -ENOMEM; | ||
196 | |||
197 | tdev->hdev = hdev; | ||
198 | hid_set_drvdata(hdev, tdev); | ||
199 | |||
200 | err = hid_parse(hdev); | ||
201 | if (err) | ||
202 | return err; | ||
203 | |||
204 | mutex_init(&tdev->lock); | ||
205 | |||
206 | err = thingm_version(tdev); | ||
207 | if (err) | ||
208 | return err; | ||
209 | |||
210 | hid_dbg(hdev, "firmware version: %c.%c\n", | ||
211 | tdev->version.major, tdev->version.minor); | ||
212 | |||
213 | for (i = 0; i < ARRAY_SIZE(thingm_fwinfo) && !tdev->fwinfo; ++i) | ||
214 | if (thingm_fwinfo[i].major == tdev->version.major) | ||
215 | tdev->fwinfo = &thingm_fwinfo[i]; | ||
216 | |||
217 | if (!tdev->fwinfo) { | ||
218 | hid_err(hdev, "unsupported firmware %c\n", tdev->version.major); | ||
219 | return -ENODEV; | ||
220 | } | ||
221 | |||
222 | tdev->rgb = devm_kzalloc(&hdev->dev, | ||
223 | sizeof(struct thingm_rgb) * tdev->fwinfo->numrgb, | ||
224 | GFP_KERNEL); | ||
225 | if (!tdev->rgb) | ||
226 | return -ENOMEM; | ||
227 | |||
228 | err = hid_hw_start(hdev, HID_CONNECT_HIDRAW); | ||
229 | if (err) | ||
230 | return err; | ||
231 | |||
232 | for (i = 0; i < tdev->fwinfo->numrgb; ++i) { | ||
233 | struct thingm_rgb *rgb = tdev->rgb + i; | ||
234 | |||
235 | rgb->tdev = tdev; | ||
236 | rgb->num = tdev->fwinfo->first + i; | ||
237 | err = thingm_init_rgb(rgb); | ||
238 | if (err) { | ||
239 | hid_hw_stop(hdev); | ||
240 | return err; | ||
241 | } | ||
242 | } | ||
243 | |||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | static const struct hid_device_id thingm_table[] = { | ||
248 | { HID_USB_DEVICE(USB_VENDOR_ID_THINGM, USB_DEVICE_ID_BLINK1) }, | ||
249 | { } | ||
250 | }; | ||
251 | MODULE_DEVICE_TABLE(hid, thingm_table); | ||
252 | |||
253 | static struct hid_driver thingm_driver = { | ||
254 | .name = "thingm", | ||
255 | .probe = thingm_probe, | ||
256 | .id_table = thingm_table, | ||
257 | }; | ||
258 | |||
259 | module_hid_driver(thingm_driver); | ||
260 | |||
261 | MODULE_LICENSE("GPL"); | ||
262 | MODULE_AUTHOR("Vivien Didelot <vivien.didelot@savoirfairelinux.com>"); | ||
263 | MODULE_DESCRIPTION("ThingM blink(1) USB RGB LED driver"); | ||
diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 2e021ba8ff05..b3ec4f2de875 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c | |||
@@ -1020,6 +1020,7 @@ static int i2c_hid_probe(struct i2c_client *client, | |||
1020 | pm_runtime_get_noresume(&client->dev); | 1020 | pm_runtime_get_noresume(&client->dev); |
1021 | pm_runtime_set_active(&client->dev); | 1021 | pm_runtime_set_active(&client->dev); |
1022 | pm_runtime_enable(&client->dev); | 1022 | pm_runtime_enable(&client->dev); |
1023 | device_enable_async_suspend(&client->dev); | ||
1023 | 1024 | ||
1024 | ret = i2c_hid_fetch_hid_descriptor(ihid); | 1025 | ret = i2c_hid_fetch_hid_descriptor(ihid); |
1025 | if (ret < 0) | 1026 | if (ret < 0) |
@@ -1106,6 +1107,14 @@ static int i2c_hid_remove(struct i2c_client *client) | |||
1106 | return 0; | 1107 | return 0; |
1107 | } | 1108 | } |
1108 | 1109 | ||
1110 | static void i2c_hid_shutdown(struct i2c_client *client) | ||
1111 | { | ||
1112 | struct i2c_hid *ihid = i2c_get_clientdata(client); | ||
1113 | |||
1114 | i2c_hid_set_power(client, I2C_HID_PWR_SLEEP); | ||
1115 | free_irq(client->irq, ihid); | ||
1116 | } | ||
1117 | |||
1109 | #ifdef CONFIG_PM_SLEEP | 1118 | #ifdef CONFIG_PM_SLEEP |
1110 | static int i2c_hid_suspend(struct device *dev) | 1119 | static int i2c_hid_suspend(struct device *dev) |
1111 | { | 1120 | { |
@@ -1230,7 +1239,7 @@ static struct i2c_driver i2c_hid_driver = { | |||
1230 | 1239 | ||
1231 | .probe = i2c_hid_probe, | 1240 | .probe = i2c_hid_probe, |
1232 | .remove = i2c_hid_remove, | 1241 | .remove = i2c_hid_remove, |
1233 | 1242 | .shutdown = i2c_hid_shutdown, | |
1234 | .id_table = i2c_hid_id_table, | 1243 | .id_table = i2c_hid_id_table, |
1235 | }; | 1244 | }; |
1236 | 1245 | ||
diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 16b6f11a0700..99ec3ff7563b 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c | |||
@@ -51,10 +51,26 @@ struct uhid_device { | |||
51 | u32 report_id; | 51 | u32 report_id; |
52 | u32 report_type; | 52 | u32 report_type; |
53 | struct uhid_event report_buf; | 53 | struct uhid_event report_buf; |
54 | struct work_struct worker; | ||
54 | }; | 55 | }; |
55 | 56 | ||
56 | static struct miscdevice uhid_misc; | 57 | static struct miscdevice uhid_misc; |
57 | 58 | ||
59 | static void uhid_device_add_worker(struct work_struct *work) | ||
60 | { | ||
61 | struct uhid_device *uhid = container_of(work, struct uhid_device, worker); | ||
62 | int ret; | ||
63 | |||
64 | ret = hid_add_device(uhid->hid); | ||
65 | if (ret) { | ||
66 | hid_err(uhid->hid, "Cannot register HID device: error %d\n", ret); | ||
67 | |||
68 | hid_destroy_device(uhid->hid); | ||
69 | uhid->hid = NULL; | ||
70 | uhid->running = false; | ||
71 | } | ||
72 | } | ||
73 | |||
58 | static void uhid_queue(struct uhid_device *uhid, struct uhid_event *ev) | 74 | static void uhid_queue(struct uhid_device *uhid, struct uhid_event *ev) |
59 | { | 75 | { |
60 | __u8 newhead; | 76 | __u8 newhead; |
@@ -498,18 +514,14 @@ static int uhid_dev_create2(struct uhid_device *uhid, | |||
498 | uhid->hid = hid; | 514 | uhid->hid = hid; |
499 | uhid->running = true; | 515 | uhid->running = true; |
500 | 516 | ||
501 | ret = hid_add_device(hid); | 517 | /* Adding of a HID device is done through a worker, to allow HID drivers |
502 | if (ret) { | 518 | * which use feature requests during .probe to work, without they would |
503 | hid_err(hid, "Cannot register HID device\n"); | 519 | * be blocked on devlock, which is held by uhid_char_write. |
504 | goto err_hid; | 520 | */ |
505 | } | 521 | schedule_work(&uhid->worker); |
506 | 522 | ||
507 | return 0; | 523 | return 0; |
508 | 524 | ||
509 | err_hid: | ||
510 | hid_destroy_device(hid); | ||
511 | uhid->hid = NULL; | ||
512 | uhid->running = false; | ||
513 | err_free: | 525 | err_free: |
514 | kfree(uhid->rd_data); | 526 | kfree(uhid->rd_data); |
515 | uhid->rd_data = NULL; | 527 | uhid->rd_data = NULL; |
@@ -550,6 +562,8 @@ static int uhid_dev_destroy(struct uhid_device *uhid) | |||
550 | uhid->running = false; | 562 | uhid->running = false; |
551 | wake_up_interruptible(&uhid->report_wait); | 563 | wake_up_interruptible(&uhid->report_wait); |
552 | 564 | ||
565 | cancel_work_sync(&uhid->worker); | ||
566 | |||
553 | hid_destroy_device(uhid->hid); | 567 | hid_destroy_device(uhid->hid); |
554 | kfree(uhid->rd_data); | 568 | kfree(uhid->rd_data); |
555 | 569 | ||
@@ -612,6 +626,7 @@ static int uhid_char_open(struct inode *inode, struct file *file) | |||
612 | init_waitqueue_head(&uhid->waitq); | 626 | init_waitqueue_head(&uhid->waitq); |
613 | init_waitqueue_head(&uhid->report_wait); | 627 | init_waitqueue_head(&uhid->report_wait); |
614 | uhid->running = false; | 628 | uhid->running = false; |
629 | INIT_WORK(&uhid->worker, uhid_device_add_worker); | ||
615 | 630 | ||
616 | file->private_data = uhid; | 631 | file->private_data = uhid; |
617 | nonseekable_open(inode, file); | 632 | nonseekable_open(inode, file); |
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 6e705971d637..eb8f8d37cd95 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig | |||
@@ -79,15 +79,6 @@ config USB_LCD | |||
79 | To compile this driver as a module, choose M here: the | 79 | To compile this driver as a module, choose M here: the |
80 | module will be called usblcd. | 80 | module will be called usblcd. |
81 | 81 | ||
82 | config USB_LED | ||
83 | tristate "USB LED driver support" | ||
84 | help | ||
85 | Say Y here if you want to connect an USBLED device to your | ||
86 | computer's USB port. | ||
87 | |||
88 | To compile this driver as a module, choose M here: the | ||
89 | module will be called usbled. | ||
90 | |||
91 | config USB_CYPRESS_CY7C63 | 82 | config USB_CYPRESS_CY7C63 |
92 | tristate "Cypress CY7C63xxx USB driver support" | 83 | tristate "Cypress CY7C63xxx USB driver support" |
93 | help | 84 | help |
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index 2769cf6351b4..3d79faaad2fb 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile | |||
@@ -15,7 +15,6 @@ obj-$(CONFIG_USB_IOWARRIOR) += iowarrior.o | |||
15 | obj-$(CONFIG_USB_ISIGHTFW) += isight_firmware.o | 15 | obj-$(CONFIG_USB_ISIGHTFW) += isight_firmware.o |
16 | obj-$(CONFIG_USB_LCD) += usblcd.o | 16 | obj-$(CONFIG_USB_LCD) += usblcd.o |
17 | obj-$(CONFIG_USB_LD) += ldusb.o | 17 | obj-$(CONFIG_USB_LD) += ldusb.o |
18 | obj-$(CONFIG_USB_LED) += usbled.o | ||
19 | obj-$(CONFIG_USB_LEGOTOWER) += legousbtower.o | 18 | obj-$(CONFIG_USB_LEGOTOWER) += legousbtower.o |
20 | obj-$(CONFIG_USB_RIO500) += rio500.o | 19 | obj-$(CONFIG_USB_RIO500) += rio500.o |
21 | obj-$(CONFIG_USB_TEST) += usbtest.o | 20 | obj-$(CONFIG_USB_TEST) += usbtest.o |
diff --git a/drivers/usb/misc/usbled.c b/drivers/usb/misc/usbled.c deleted file mode 100644 index bdef0d6eb91d..000000000000 --- a/drivers/usb/misc/usbled.c +++ /dev/null | |||
@@ -1,273 +0,0 @@ | |||
1 | /* | ||
2 | * USB LED driver | ||
3 | * | ||
4 | * Copyright (C) 2004 Greg Kroah-Hartman (greg@kroah.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License as | ||
8 | * published by the Free Software Foundation, version 2. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/errno.h> | ||
14 | #include <linux/slab.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/usb.h> | ||
17 | |||
18 | |||
19 | #define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com" | ||
20 | #define DRIVER_DESC "USB LED Driver" | ||
21 | |||
22 | enum led_type { | ||
23 | DELCOM_VISUAL_SIGNAL_INDICATOR, | ||
24 | DREAM_CHEEKY_WEBMAIL_NOTIFIER, | ||
25 | RISO_KAGAKU_LED | ||
26 | }; | ||
27 | |||
28 | /* the Webmail LED made by RISO KAGAKU CORP. decodes a color index | ||
29 | internally, we want to keep the red+green+blue sysfs api, so we decode | ||
30 | from 1-bit RGB to the riso kagaku color index according to this table... */ | ||
31 | |||
32 | static unsigned const char riso_kagaku_tbl[] = { | ||
33 | /* R+2G+4B -> riso kagaku color index */ | ||
34 | [0] = 0, /* black */ | ||
35 | [1] = 2, /* red */ | ||
36 | [2] = 1, /* green */ | ||
37 | [3] = 5, /* yellow */ | ||
38 | [4] = 3, /* blue */ | ||
39 | [5] = 6, /* magenta */ | ||
40 | [6] = 4, /* cyan */ | ||
41 | [7] = 7 /* white */ | ||
42 | }; | ||
43 | |||
44 | #define RISO_KAGAKU_IX(r,g,b) riso_kagaku_tbl[((r)?1:0)+((g)?2:0)+((b)?4:0)] | ||
45 | |||
46 | /* table of devices that work with this driver */ | ||
47 | static const struct usb_device_id id_table[] = { | ||
48 | { USB_DEVICE(0x0fc5, 0x1223), | ||
49 | .driver_info = DELCOM_VISUAL_SIGNAL_INDICATOR }, | ||
50 | { USB_DEVICE(0x1d34, 0x0004), | ||
51 | .driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER }, | ||
52 | { USB_DEVICE(0x1d34, 0x000a), | ||
53 | .driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER }, | ||
54 | { USB_DEVICE(0x1294, 0x1320), | ||
55 | .driver_info = RISO_KAGAKU_LED }, | ||
56 | { }, | ||
57 | }; | ||
58 | MODULE_DEVICE_TABLE(usb, id_table); | ||
59 | |||
60 | struct usb_led { | ||
61 | struct usb_device *udev; | ||
62 | unsigned char blue; | ||
63 | unsigned char red; | ||
64 | unsigned char green; | ||
65 | enum led_type type; | ||
66 | }; | ||
67 | |||
68 | static void change_color(struct usb_led *led) | ||
69 | { | ||
70 | int retval = 0; | ||
71 | unsigned char *buffer; | ||
72 | int actlength; | ||
73 | |||
74 | buffer = kmalloc(8, GFP_KERNEL); | ||
75 | if (!buffer) { | ||
76 | dev_err(&led->udev->dev, "out of memory\n"); | ||
77 | return; | ||
78 | } | ||
79 | |||
80 | switch (led->type) { | ||
81 | case DELCOM_VISUAL_SIGNAL_INDICATOR: { | ||
82 | unsigned char color = 0x07; | ||
83 | |||
84 | if (led->blue) | ||
85 | color &= ~0x04; | ||
86 | if (led->red) | ||
87 | color &= ~0x02; | ||
88 | if (led->green) | ||
89 | color &= ~0x01; | ||
90 | dev_dbg(&led->udev->dev, | ||
91 | "blue = %d, red = %d, green = %d, color = %.2x\n", | ||
92 | led->blue, led->red, led->green, color); | ||
93 | |||
94 | retval = usb_control_msg(led->udev, | ||
95 | usb_sndctrlpipe(led->udev, 0), | ||
96 | 0x12, | ||
97 | 0xc8, | ||
98 | (0x02 * 0x100) + 0x0a, | ||
99 | (0x00 * 0x100) + color, | ||
100 | buffer, | ||
101 | 8, | ||
102 | 2000); | ||
103 | break; | ||
104 | } | ||
105 | |||
106 | case DREAM_CHEEKY_WEBMAIL_NOTIFIER: | ||
107 | dev_dbg(&led->udev->dev, | ||
108 | "red = %d, green = %d, blue = %d\n", | ||
109 | led->red, led->green, led->blue); | ||
110 | |||
111 | buffer[0] = led->red; | ||
112 | buffer[1] = led->green; | ||
113 | buffer[2] = led->blue; | ||
114 | buffer[3] = buffer[4] = buffer[5] = 0; | ||
115 | buffer[6] = 0x1a; | ||
116 | buffer[7] = 0x05; | ||
117 | |||
118 | retval = usb_control_msg(led->udev, | ||
119 | usb_sndctrlpipe(led->udev, 0), | ||
120 | 0x09, | ||
121 | 0x21, | ||
122 | 0x200, | ||
123 | 0, | ||
124 | buffer, | ||
125 | 8, | ||
126 | 2000); | ||
127 | break; | ||
128 | |||
129 | case RISO_KAGAKU_LED: | ||
130 | buffer[0] = RISO_KAGAKU_IX(led->red, led->green, led->blue); | ||
131 | buffer[1] = 0; | ||
132 | buffer[2] = 0; | ||
133 | buffer[3] = 0; | ||
134 | buffer[4] = 0; | ||
135 | |||
136 | retval = usb_interrupt_msg(led->udev, | ||
137 | usb_sndctrlpipe(led->udev, 2), | ||
138 | buffer, 5, &actlength, 1000 /*ms timeout*/); | ||
139 | break; | ||
140 | |||
141 | default: | ||
142 | dev_err(&led->udev->dev, "unknown device type %d\n", led->type); | ||
143 | } | ||
144 | |||
145 | if (retval) | ||
146 | dev_dbg(&led->udev->dev, "retval = %d\n", retval); | ||
147 | kfree(buffer); | ||
148 | } | ||
149 | |||
150 | #define show_set(value) \ | ||
151 | static ssize_t show_##value(struct device *dev, struct device_attribute *attr,\ | ||
152 | char *buf) \ | ||
153 | { \ | ||
154 | struct usb_interface *intf = to_usb_interface(dev); \ | ||
155 | struct usb_led *led = usb_get_intfdata(intf); \ | ||
156 | \ | ||
157 | return sprintf(buf, "%d\n", led->value); \ | ||
158 | } \ | ||
159 | static ssize_t set_##value(struct device *dev, struct device_attribute *attr,\ | ||
160 | const char *buf, size_t count) \ | ||
161 | { \ | ||
162 | struct usb_interface *intf = to_usb_interface(dev); \ | ||
163 | struct usb_led *led = usb_get_intfdata(intf); \ | ||
164 | int temp = simple_strtoul(buf, NULL, 10); \ | ||
165 | \ | ||
166 | led->value = temp; \ | ||
167 | change_color(led); \ | ||
168 | return count; \ | ||
169 | } \ | ||
170 | static DEVICE_ATTR(value, S_IRUGO | S_IWUSR, show_##value, set_##value); | ||
171 | show_set(blue); | ||
172 | show_set(red); | ||
173 | show_set(green); | ||
174 | |||
175 | static int led_probe(struct usb_interface *interface, | ||
176 | const struct usb_device_id *id) | ||
177 | { | ||
178 | struct usb_device *udev = interface_to_usbdev(interface); | ||
179 | struct usb_led *dev = NULL; | ||
180 | int retval = -ENOMEM; | ||
181 | |||
182 | dev = kzalloc(sizeof(struct usb_led), GFP_KERNEL); | ||
183 | if (dev == NULL) { | ||
184 | dev_err(&interface->dev, "out of memory\n"); | ||
185 | goto error_mem; | ||
186 | } | ||
187 | |||
188 | dev->udev = usb_get_dev(udev); | ||
189 | dev->type = id->driver_info; | ||
190 | |||
191 | usb_set_intfdata(interface, dev); | ||
192 | |||
193 | retval = device_create_file(&interface->dev, &dev_attr_blue); | ||
194 | if (retval) | ||
195 | goto error; | ||
196 | retval = device_create_file(&interface->dev, &dev_attr_red); | ||
197 | if (retval) | ||
198 | goto error; | ||
199 | retval = device_create_file(&interface->dev, &dev_attr_green); | ||
200 | if (retval) | ||
201 | goto error; | ||
202 | |||
203 | if (dev->type == DREAM_CHEEKY_WEBMAIL_NOTIFIER) { | ||
204 | unsigned char *enable; | ||
205 | |||
206 | enable = kmemdup("\x1f\x02\0\x5f\0\0\x1a\x03", 8, GFP_KERNEL); | ||
207 | if (!enable) { | ||
208 | dev_err(&interface->dev, "out of memory\n"); | ||
209 | retval = -ENOMEM; | ||
210 | goto error; | ||
211 | } | ||
212 | |||
213 | retval = usb_control_msg(udev, | ||
214 | usb_sndctrlpipe(udev, 0), | ||
215 | 0x09, | ||
216 | 0x21, | ||
217 | 0x200, | ||
218 | 0, | ||
219 | enable, | ||
220 | 8, | ||
221 | 2000); | ||
222 | |||
223 | kfree(enable); | ||
224 | if (retval != 8) | ||
225 | goto error; | ||
226 | } | ||
227 | |||
228 | dev_info(&interface->dev, "USB LED device now attached\n"); | ||
229 | return 0; | ||
230 | |||
231 | error: | ||
232 | device_remove_file(&interface->dev, &dev_attr_blue); | ||
233 | device_remove_file(&interface->dev, &dev_attr_red); | ||
234 | device_remove_file(&interface->dev, &dev_attr_green); | ||
235 | usb_set_intfdata(interface, NULL); | ||
236 | usb_put_dev(dev->udev); | ||
237 | kfree(dev); | ||
238 | error_mem: | ||
239 | return retval; | ||
240 | } | ||
241 | |||
242 | static void led_disconnect(struct usb_interface *interface) | ||
243 | { | ||
244 | struct usb_led *dev; | ||
245 | |||
246 | dev = usb_get_intfdata(interface); | ||
247 | |||
248 | device_remove_file(&interface->dev, &dev_attr_blue); | ||
249 | device_remove_file(&interface->dev, &dev_attr_red); | ||
250 | device_remove_file(&interface->dev, &dev_attr_green); | ||
251 | |||
252 | /* first remove the files, then set the pointer to NULL */ | ||
253 | usb_set_intfdata(interface, NULL); | ||
254 | |||
255 | usb_put_dev(dev->udev); | ||
256 | |||
257 | kfree(dev); | ||
258 | |||
259 | dev_info(&interface->dev, "USB LED now disconnected\n"); | ||
260 | } | ||
261 | |||
262 | static struct usb_driver led_driver = { | ||
263 | .name = "usbled", | ||
264 | .probe = led_probe, | ||
265 | .disconnect = led_disconnect, | ||
266 | .id_table = id_table, | ||
267 | }; | ||
268 | |||
269 | module_usb_driver(led_driver); | ||
270 | |||
271 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
272 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
273 | MODULE_LICENSE("GPL"); | ||