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