diff options
author | Hanno Zulla <abos@hanno.de> | 2018-08-23 11:03:38 -0400 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2018-09-24 05:49:32 -0400 |
commit | 256a90ed9e46b270bbc4e15ef05216ff049c3721 (patch) | |
tree | 3a5688a39e1efcad87f25568499cd89550eddb17 | |
parent | 5e335542de83558e46d28de1008a1c37d5d6679a (diff) |
HID: hid-bigbenff: driver for BigBen Interactive PS3OFMINIPAD gamepad
This is a driver to fix input mapping and add LED & force feedback
support for the "BigBen Interactive Kid-friendly Wired Controller
PS3OFMINIPAD SONY" gamepad with USB id 146b:0902. It was originally
sold as a PS3 accessory and makes a very nice gamepad for Retropie.
Signed-off-by: Hanno Zulla <kontakt@hanno.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r-- | drivers/hid/Kconfig | 13 | ||||
-rw-r--r-- | drivers/hid/Makefile | 1 | ||||
-rw-r--r-- | drivers/hid/hid-bigbenff.c | 414 | ||||
-rw-r--r-- | drivers/hid/hid-ids.h | 3 |
4 files changed, 431 insertions, 0 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 61e1953ff921..5a163f13663c 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig | |||
@@ -182,6 +182,19 @@ config HID_BETOP_FF | |||
182 | Currently the following devices are known to be supported: | 182 | Currently the following devices are known to be supported: |
183 | - BETOP 2185 PC & BFM MODE | 183 | - BETOP 2185 PC & BFM MODE |
184 | 184 | ||
185 | config HID_BIGBEN_FF | ||
186 | tristate "BigBen Interactive Kids' gamepad support" | ||
187 | depends on USB_HID | ||
188 | depends on NEW_LEDS | ||
189 | depends on LEDS_CLASS | ||
190 | select INPUT_FF_MEMLESS | ||
191 | default !EXPERT | ||
192 | help | ||
193 | Support for the "Kid-friendly Wired Controller" PS3OFMINIPAD | ||
194 | gamepad made by BigBen Interactive, originally sold as a PS3 | ||
195 | accessory. This driver fixes input mapping and adds support for | ||
196 | force feedback effects and LEDs on the device. | ||
197 | |||
185 | config HID_CHERRY | 198 | config HID_CHERRY |
186 | tristate "Cherry Cymotion keyboard" | 199 | tristate "Cherry Cymotion keyboard" |
187 | depends on HID | 200 | depends on HID |
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index bd7ac53b75c5..896a51ce7ce0 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile | |||
@@ -31,6 +31,7 @@ obj-$(CONFIG_HID_ASUS) += hid-asus.o | |||
31 | obj-$(CONFIG_HID_AUREAL) += hid-aureal.o | 31 | obj-$(CONFIG_HID_AUREAL) += hid-aureal.o |
32 | obj-$(CONFIG_HID_BELKIN) += hid-belkin.o | 32 | obj-$(CONFIG_HID_BELKIN) += hid-belkin.o |
33 | obj-$(CONFIG_HID_BETOP_FF) += hid-betopff.o | 33 | obj-$(CONFIG_HID_BETOP_FF) += hid-betopff.o |
34 | obj-$(CONFIG_HID_BIGBEN_FF) += hid-bigbenff.o | ||
34 | obj-$(CONFIG_HID_CHERRY) += hid-cherry.o | 35 | obj-$(CONFIG_HID_CHERRY) += hid-cherry.o |
35 | obj-$(CONFIG_HID_CHICONY) += hid-chicony.o | 36 | obj-$(CONFIG_HID_CHICONY) += hid-chicony.o |
36 | obj-$(CONFIG_HID_CMEDIA) += hid-cmedia.o | 37 | obj-$(CONFIG_HID_CMEDIA) += hid-cmedia.o |
diff --git a/drivers/hid/hid-bigbenff.c b/drivers/hid/hid-bigbenff.c new file mode 100644 index 000000000000..3f6abd190df4 --- /dev/null +++ b/drivers/hid/hid-bigbenff.c | |||
@@ -0,0 +1,414 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0+ | ||
2 | |||
3 | /* | ||
4 | * LED & force feedback support for BigBen Interactive | ||
5 | * | ||
6 | * 0x146b:0x0902 "Bigben Interactive Bigben Game Pad" | ||
7 | * "Kid-friendly Wired Controller" PS3OFMINIPAD SONY | ||
8 | * sold for use with the PS3 | ||
9 | * | ||
10 | * Copyright (c) 2018 Hanno Zulla <kontakt@hanno.de> | ||
11 | */ | ||
12 | |||
13 | #include <linux/input.h> | ||
14 | #include <linux/slab.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/leds.h> | ||
17 | #include <linux/hid.h> | ||
18 | |||
19 | #include "hid-ids.h" | ||
20 | |||
21 | |||
22 | /* | ||
23 | * The original descriptor for 0x146b:0x0902 | ||
24 | * | ||
25 | * 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) | ||
26 | * 0x09, 0x05, // Usage (Game Pad) | ||
27 | * 0xA1, 0x01, // Collection (Application) | ||
28 | * 0x15, 0x00, // Logical Minimum (0) | ||
29 | * 0x25, 0x01, // Logical Maximum (1) | ||
30 | * 0x35, 0x00, // Physical Minimum (0) | ||
31 | * 0x45, 0x01, // Physical Maximum (1) | ||
32 | * 0x75, 0x01, // Report Size (1) | ||
33 | * 0x95, 0x0D, // Report Count (13) | ||
34 | * 0x05, 0x09, // Usage Page (Button) | ||
35 | * 0x19, 0x01, // Usage Minimum (0x01) | ||
36 | * 0x29, 0x0D, // Usage Maximum (0x0D) | ||
37 | * 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) | ||
38 | * 0x95, 0x03, // Report Count (3) | ||
39 | * 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) | ||
40 | * 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) | ||
41 | * 0x25, 0x07, // Logical Maximum (7) | ||
42 | * 0x46, 0x3B, 0x01, // Physical Maximum (315) | ||
43 | * 0x75, 0x04, // Report Size (4) | ||
44 | * 0x95, 0x01, // Report Count (1) | ||
45 | * 0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter) | ||
46 | * 0x09, 0x39, // Usage (Hat switch) | ||
47 | * 0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State) | ||
48 | * 0x65, 0x00, // Unit (None) | ||
49 | * 0x95, 0x01, // Report Count (1) | ||
50 | * 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) | ||
51 | * 0x26, 0xFF, 0x00, // Logical Maximum (255) | ||
52 | * 0x46, 0xFF, 0x00, // Physical Maximum (255) | ||
53 | * 0x09, 0x30, // Usage (X) | ||
54 | * 0x09, 0x31, // Usage (Y) | ||
55 | * 0x09, 0x32, // Usage (Z) | ||
56 | * 0x09, 0x35, // Usage (Rz) | ||
57 | * 0x75, 0x08, // Report Size (8) | ||
58 | * 0x95, 0x04, // Report Count (4) | ||
59 | * 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) | ||
60 | * 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) | ||
61 | * 0x09, 0x20, // Usage (0x20) | ||
62 | * 0x09, 0x21, // Usage (0x21) | ||
63 | * 0x09, 0x22, // Usage (0x22) | ||
64 | * 0x09, 0x23, // Usage (0x23) | ||
65 | * 0x09, 0x24, // Usage (0x24) | ||
66 | * 0x09, 0x25, // Usage (0x25) | ||
67 | * 0x09, 0x26, // Usage (0x26) | ||
68 | * 0x09, 0x27, // Usage (0x27) | ||
69 | * 0x09, 0x28, // Usage (0x28) | ||
70 | * 0x09, 0x29, // Usage (0x29) | ||
71 | * 0x09, 0x2A, // Usage (0x2A) | ||
72 | * 0x09, 0x2B, // Usage (0x2B) | ||
73 | * 0x95, 0x0C, // Report Count (12) | ||
74 | * 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) | ||
75 | * 0x0A, 0x21, 0x26, // Usage (0x2621) | ||
76 | * 0x95, 0x08, // Report Count (8) | ||
77 | * 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) | ||
78 | * 0x0A, 0x21, 0x26, // Usage (0x2621) | ||
79 | * 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) | ||
80 | * 0x26, 0xFF, 0x03, // Logical Maximum (1023) | ||
81 | * 0x46, 0xFF, 0x03, // Physical Maximum (1023) | ||
82 | * 0x09, 0x2C, // Usage (0x2C) | ||
83 | * 0x09, 0x2D, // Usage (0x2D) | ||
84 | * 0x09, 0x2E, // Usage (0x2E) | ||
85 | * 0x09, 0x2F, // Usage (0x2F) | ||
86 | * 0x75, 0x10, // Report Size (16) | ||
87 | * 0x95, 0x04, // Report Count (4) | ||
88 | * 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) | ||
89 | * 0xC0, // End Collection | ||
90 | */ | ||
91 | |||
92 | #define PID0902_RDESC_ORIG_SIZE 137 | ||
93 | |||
94 | /* | ||
95 | * The fixed descriptor for 0x146b:0x0902 | ||
96 | * | ||
97 | * - map buttons according to gamepad.rst | ||
98 | * - assign right stick from Z/Rz to Rx/Ry | ||
99 | * - map previously unused analog trigger data to Z/RZ | ||
100 | * - simplify feature and output descriptor | ||
101 | */ | ||
102 | static __u8 pid0902_rdesc_fixed[] = { | ||
103 | 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */ | ||
104 | 0x09, 0x05, /* Usage (Game Pad) */ | ||
105 | 0xA1, 0x01, /* Collection (Application) */ | ||
106 | 0x15, 0x00, /* Logical Minimum (0) */ | ||
107 | 0x25, 0x01, /* Logical Maximum (1) */ | ||
108 | 0x35, 0x00, /* Physical Minimum (0) */ | ||
109 | 0x45, 0x01, /* Physical Maximum (1) */ | ||
110 | 0x75, 0x01, /* Report Size (1) */ | ||
111 | 0x95, 0x0D, /* Report Count (13) */ | ||
112 | 0x05, 0x09, /* Usage Page (Button) */ | ||
113 | 0x09, 0x05, /* Usage (BTN_WEST) */ | ||
114 | 0x09, 0x01, /* Usage (BTN_SOUTH) */ | ||
115 | 0x09, 0x02, /* Usage (BTN_EAST) */ | ||
116 | 0x09, 0x04, /* Usage (BTN_NORTH) */ | ||
117 | 0x09, 0x07, /* Usage (BTN_TL) */ | ||
118 | 0x09, 0x08, /* Usage (BTN_TR) */ | ||
119 | 0x09, 0x09, /* Usage (BTN_TL2) */ | ||
120 | 0x09, 0x0A, /* Usage (BTN_TR2) */ | ||
121 | 0x09, 0x0B, /* Usage (BTN_SELECT) */ | ||
122 | 0x09, 0x0C, /* Usage (BTN_START) */ | ||
123 | 0x09, 0x0E, /* Usage (BTN_THUMBL) */ | ||
124 | 0x09, 0x0F, /* Usage (BTN_THUMBR) */ | ||
125 | 0x09, 0x0D, /* Usage (BTN_MODE) */ | ||
126 | 0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */ | ||
127 | 0x75, 0x01, /* Report Size (1) */ | ||
128 | 0x95, 0x03, /* Report Count (3) */ | ||
129 | 0x81, 0x01, /* Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */ | ||
130 | 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */ | ||
131 | 0x25, 0x07, /* Logical Maximum (7) */ | ||
132 | 0x46, 0x3B, 0x01, /* Physical Maximum (315) */ | ||
133 | 0x75, 0x04, /* Report Size (4) */ | ||
134 | 0x95, 0x01, /* Report Count (1) */ | ||
135 | 0x65, 0x14, /* Unit (System: English Rotation, Length: Centimeter) */ | ||
136 | 0x09, 0x39, /* Usage (Hat switch) */ | ||
137 | 0x81, 0x42, /* Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State) */ | ||
138 | 0x65, 0x00, /* Unit (None) */ | ||
139 | 0x95, 0x01, /* Report Count (1) */ | ||
140 | 0x81, 0x01, /* Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */ | ||
141 | 0x26, 0xFF, 0x00, /* Logical Maximum (255) */ | ||
142 | 0x46, 0xFF, 0x00, /* Physical Maximum (255) */ | ||
143 | 0x09, 0x30, /* Usage (X) */ | ||
144 | 0x09, 0x31, /* Usage (Y) */ | ||
145 | 0x09, 0x33, /* Usage (Rx) */ | ||
146 | 0x09, 0x34, /* Usage (Ry) */ | ||
147 | 0x75, 0x08, /* Report Size (8) */ | ||
148 | 0x95, 0x04, /* Report Count (4) */ | ||
149 | 0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */ | ||
150 | 0x95, 0x0A, /* Report Count (10) */ | ||
151 | 0x81, 0x01, /* Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */ | ||
152 | 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */ | ||
153 | 0x26, 0xFF, 0x00, /* Logical Maximum (255) */ | ||
154 | 0x46, 0xFF, 0x00, /* Physical Maximum (255) */ | ||
155 | 0x09, 0x32, /* Usage (Z) */ | ||
156 | 0x09, 0x35, /* Usage (Rz) */ | ||
157 | 0x95, 0x02, /* Report Count (2) */ | ||
158 | 0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */ | ||
159 | 0x95, 0x08, /* Report Count (8) */ | ||
160 | 0x81, 0x01, /* Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */ | ||
161 | 0x06, 0x00, 0xFF, /* Usage Page (Vendor Defined 0xFF00) */ | ||
162 | 0xB1, 0x02, /* Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) */ | ||
163 | 0x0A, 0x21, 0x26, /* Usage (0x2621) */ | ||
164 | 0x95, 0x08, /* Report Count (8) */ | ||
165 | 0x91, 0x02, /* Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) */ | ||
166 | 0x0A, 0x21, 0x26, /* Usage (0x2621) */ | ||
167 | 0x95, 0x08, /* Report Count (8) */ | ||
168 | 0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */ | ||
169 | 0xC0, /* End Collection */ | ||
170 | }; | ||
171 | |||
172 | #define NUM_LEDS 4 | ||
173 | |||
174 | struct bigben_device { | ||
175 | struct hid_device *hid; | ||
176 | struct hid_report *report; | ||
177 | u8 led_state; /* LED1 = 1 .. LED4 = 8 */ | ||
178 | u8 right_motor_on; /* right motor off/on 0/1 */ | ||
179 | u8 left_motor_force; /* left motor force 0-255 */ | ||
180 | struct led_classdev *leds[NUM_LEDS]; | ||
181 | bool work_led; | ||
182 | bool work_ff; | ||
183 | struct work_struct worker; | ||
184 | }; | ||
185 | |||
186 | |||
187 | static void bigben_worker(struct work_struct *work) | ||
188 | { | ||
189 | struct bigben_device *bigben = container_of(work, | ||
190 | struct bigben_device, worker); | ||
191 | struct hid_field *report_field = bigben->report->field[0]; | ||
192 | |||
193 | if (bigben->work_led) { | ||
194 | bigben->work_led = false; | ||
195 | report_field->value[0] = 0x01; /* 1 = led message */ | ||
196 | report_field->value[1] = 0x08; /* reserved value, always 8 */ | ||
197 | report_field->value[2] = bigben->led_state; | ||
198 | report_field->value[3] = 0x00; /* padding */ | ||
199 | report_field->value[4] = 0x00; /* padding */ | ||
200 | report_field->value[5] = 0x00; /* padding */ | ||
201 | report_field->value[6] = 0x00; /* padding */ | ||
202 | report_field->value[7] = 0x00; /* padding */ | ||
203 | hid_hw_request(bigben->hid, bigben->report, HID_REQ_SET_REPORT); | ||
204 | } | ||
205 | |||
206 | if (bigben->work_ff) { | ||
207 | bigben->work_ff = false; | ||
208 | report_field->value[0] = 0x02; /* 2 = rumble effect message */ | ||
209 | report_field->value[1] = 0x08; /* reserved value, always 8 */ | ||
210 | report_field->value[2] = bigben->right_motor_on; | ||
211 | report_field->value[3] = bigben->left_motor_force; | ||
212 | report_field->value[4] = 0xff; /* duration 0-254 (255 = nonstop) */ | ||
213 | report_field->value[5] = 0x00; /* padding */ | ||
214 | report_field->value[6] = 0x00; /* padding */ | ||
215 | report_field->value[7] = 0x00; /* padding */ | ||
216 | hid_hw_request(bigben->hid, bigben->report, HID_REQ_SET_REPORT); | ||
217 | } | ||
218 | } | ||
219 | |||
220 | static int hid_bigben_play_effect(struct input_dev *dev, void *data, | ||
221 | struct ff_effect *effect) | ||
222 | { | ||
223 | struct bigben_device *bigben = data; | ||
224 | u8 right_motor_on; | ||
225 | u8 left_motor_force; | ||
226 | |||
227 | if (effect->type != FF_RUMBLE) | ||
228 | return 0; | ||
229 | |||
230 | right_motor_on = effect->u.rumble.weak_magnitude ? 1 : 0; | ||
231 | left_motor_force = effect->u.rumble.strong_magnitude / 256; | ||
232 | |||
233 | if (right_motor_on != bigben->right_motor_on || | ||
234 | left_motor_force != bigben->left_motor_force) { | ||
235 | bigben->right_motor_on = right_motor_on; | ||
236 | bigben->left_motor_force = left_motor_force; | ||
237 | bigben->work_ff = true; | ||
238 | schedule_work(&bigben->worker); | ||
239 | } | ||
240 | |||
241 | return 0; | ||
242 | } | ||
243 | |||
244 | static void bigben_set_led(struct led_classdev *led, | ||
245 | enum led_brightness value) | ||
246 | { | ||
247 | struct device *dev = led->dev->parent; | ||
248 | struct hid_device *hid = to_hid_device(dev); | ||
249 | struct bigben_device *bigben = hid_get_drvdata(hid); | ||
250 | int n; | ||
251 | bool work; | ||
252 | |||
253 | if (!bigben) { | ||
254 | hid_err(hid, "no device data\n"); | ||
255 | return; | ||
256 | } | ||
257 | |||
258 | for (n = 0; n < NUM_LEDS; n++) { | ||
259 | if (led == bigben->leds[n]) { | ||
260 | if (value == LED_OFF) { | ||
261 | work = (bigben->led_state & BIT(n)); | ||
262 | bigben->led_state &= ~BIT(n); | ||
263 | } else { | ||
264 | work = !(bigben->led_state & BIT(n)); | ||
265 | bigben->led_state |= BIT(n); | ||
266 | } | ||
267 | |||
268 | if (work) { | ||
269 | bigben->work_led = true; | ||
270 | schedule_work(&bigben->worker); | ||
271 | } | ||
272 | return; | ||
273 | } | ||
274 | } | ||
275 | } | ||
276 | |||
277 | static enum led_brightness bigben_get_led(struct led_classdev *led) | ||
278 | { | ||
279 | struct device *dev = led->dev->parent; | ||
280 | struct hid_device *hid = to_hid_device(dev); | ||
281 | struct bigben_device *bigben = hid_get_drvdata(hid); | ||
282 | int n; | ||
283 | |||
284 | if (!bigben) { | ||
285 | hid_err(hid, "no device data\n"); | ||
286 | return LED_OFF; | ||
287 | } | ||
288 | |||
289 | for (n = 0; n < NUM_LEDS; n++) { | ||
290 | if (led == bigben->leds[n]) | ||
291 | return (bigben->led_state & BIT(n)) ? LED_ON : LED_OFF; | ||
292 | } | ||
293 | |||
294 | return LED_OFF; | ||
295 | } | ||
296 | |||
297 | static void bigben_remove(struct hid_device *hid) | ||
298 | { | ||
299 | struct bigben_device *bigben = hid_get_drvdata(hid); | ||
300 | |||
301 | cancel_work_sync(&bigben->worker); | ||
302 | hid_hw_close(hid); | ||
303 | hid_hw_stop(hid); | ||
304 | } | ||
305 | |||
306 | static int bigben_probe(struct hid_device *hid, | ||
307 | const struct hid_device_id *id) | ||
308 | { | ||
309 | struct bigben_device *bigben; | ||
310 | struct hid_input *hidinput; | ||
311 | struct list_head *report_list; | ||
312 | struct led_classdev *led; | ||
313 | char *name; | ||
314 | size_t name_sz; | ||
315 | int n, error; | ||
316 | |||
317 | bigben = devm_kzalloc(&hid->dev, sizeof(*bigben), GFP_KERNEL); | ||
318 | if (!bigben) | ||
319 | return -ENOMEM; | ||
320 | hid_set_drvdata(hid, bigben); | ||
321 | bigben->hid = hid; | ||
322 | |||
323 | error = hid_parse(hid); | ||
324 | if (error) { | ||
325 | hid_err(hid, "parse failed\n"); | ||
326 | return error; | ||
327 | } | ||
328 | |||
329 | error = hid_hw_start(hid, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); | ||
330 | if (error) { | ||
331 | hid_err(hid, "hw start failed\n"); | ||
332 | return error; | ||
333 | } | ||
334 | |||
335 | report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; | ||
336 | bigben->report = list_entry(report_list->next, | ||
337 | struct hid_report, list); | ||
338 | |||
339 | hidinput = list_first_entry(&hid->inputs, struct hid_input, list); | ||
340 | set_bit(FF_RUMBLE, hidinput->input->ffbit); | ||
341 | |||
342 | INIT_WORK(&bigben->worker, bigben_worker); | ||
343 | |||
344 | error = input_ff_create_memless(hidinput->input, bigben, | ||
345 | hid_bigben_play_effect); | ||
346 | if (error) | ||
347 | return error; | ||
348 | |||
349 | name_sz = strlen(dev_name(&hid->dev)) + strlen(":red:bigben#") + 1; | ||
350 | |||
351 | for (n = 0; n < NUM_LEDS; n++) { | ||
352 | led = devm_kzalloc( | ||
353 | &hid->dev, | ||
354 | sizeof(struct led_classdev) + name_sz, | ||
355 | GFP_KERNEL | ||
356 | ); | ||
357 | if (!led) | ||
358 | return -ENOMEM; | ||
359 | name = (void *)(&led[1]); | ||
360 | snprintf(name, name_sz, | ||
361 | "%s:red:bigben%d", | ||
362 | dev_name(&hid->dev), n + 1 | ||
363 | ); | ||
364 | led->name = name; | ||
365 | led->brightness = (n == 0) ? LED_ON : LED_OFF; | ||
366 | led->max_brightness = 1; | ||
367 | led->brightness_get = bigben_get_led; | ||
368 | led->brightness_set = bigben_set_led; | ||
369 | bigben->leds[n] = led; | ||
370 | error = devm_led_classdev_register(&hid->dev, led); | ||
371 | if (error) | ||
372 | return error; | ||
373 | } | ||
374 | |||
375 | /* initial state: LED1 is on, no rumble effect */ | ||
376 | bigben->led_state = BIT(0); | ||
377 | bigben->right_motor_on = 0; | ||
378 | bigben->left_motor_force = 0; | ||
379 | bigben->work_led = true; | ||
380 | bigben->work_ff = true; | ||
381 | schedule_work(&bigben->worker); | ||
382 | |||
383 | hid_info(hid, "LED and force feedback support for BigBen gamepad\n"); | ||
384 | |||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | static __u8 *bigben_report_fixup(struct hid_device *hid, __u8 *rdesc, | ||
389 | unsigned int *rsize) | ||
390 | { | ||
391 | if (*rsize == PID0902_RDESC_ORIG_SIZE) { | ||
392 | rdesc = pid0902_rdesc_fixed; | ||
393 | *rsize = sizeof(pid0902_rdesc_fixed); | ||
394 | } else | ||
395 | hid_warn(hid, "unexpected rdesc, please submit for review\n"); | ||
396 | return rdesc; | ||
397 | } | ||
398 | |||
399 | static const struct hid_device_id bigben_devices[] = { | ||
400 | { HID_USB_DEVICE(USB_VENDOR_ID_BIGBEN, USB_DEVICE_ID_BIGBEN_PS3OFMINIPAD) }, | ||
401 | { } | ||
402 | }; | ||
403 | MODULE_DEVICE_TABLE(hid, bigben_devices); | ||
404 | |||
405 | static struct hid_driver bigben_driver = { | ||
406 | .name = "bigben", | ||
407 | .id_table = bigben_devices, | ||
408 | .probe = bigben_probe, | ||
409 | .report_fixup = bigben_report_fixup, | ||
410 | .remove = bigben_remove, | ||
411 | }; | ||
412 | module_hid_driver(bigben_driver); | ||
413 | |||
414 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 5146ee029db4..1ff9b3fdcf46 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h | |||
@@ -229,6 +229,9 @@ | |||
229 | #define USB_VENDOR_ID_BETOP_2185V2PC 0x8380 | 229 | #define USB_VENDOR_ID_BETOP_2185V2PC 0x8380 |
230 | #define USB_VENDOR_ID_BETOP_2185V2BFM 0x20bc | 230 | #define USB_VENDOR_ID_BETOP_2185V2BFM 0x20bc |
231 | 231 | ||
232 | #define USB_VENDOR_ID_BIGBEN 0x146b | ||
233 | #define USB_DEVICE_ID_BIGBEN_PS3OFMINIPAD 0x0902 | ||
234 | |||
232 | #define USB_VENDOR_ID_BTC 0x046e | 235 | #define USB_VENDOR_ID_BTC 0x046e |
233 | #define USB_DEVICE_ID_BTC_EMPREX_REMOTE 0x5578 | 236 | #define USB_DEVICE_ID_BTC_EMPREX_REMOTE 0x5578 |
234 | #define USB_DEVICE_ID_BTC_EMPREX_REMOTE_2 0x5577 | 237 | #define USB_DEVICE_ID_BTC_EMPREX_REMOTE_2 0x5577 |