495 lines · c
1// SPDX-License-Identifier: GPL-2.0+2 3/*4 * LED & force feedback support for BigBen Interactive5 *6 * 0x146b:0x0902 "Bigben Interactive Bigben Game Pad"7 * "Kid-friendly Wired Controller" PS3OFMINIPAD SONY8 * sold for use with the PS39 *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:0x090224 *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 Collection90 */91 92#define PID0902_RDESC_ORIG_SIZE 13793 94/*95 * The fixed descriptor for 0x146b:0x090296 *97 * - map buttons according to gamepad.rst98 * - assign right stick from Z/Rz to Rx/Ry99 * - map previously unused analog trigger data to Z/RZ100 * - simplify feature and output descriptor101 */102static const __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 4173 174struct bigben_device {175 struct hid_device *hid;176 struct hid_report *report;177 spinlock_t lock;178 bool removed;179 u8 led_state; /* LED1 = 1 .. LED4 = 8 */180 u8 right_motor_on; /* right motor off/on 0/1 */181 u8 left_motor_force; /* left motor force 0-255 */182 struct led_classdev *leds[NUM_LEDS];183 bool work_led;184 bool work_ff;185 struct work_struct worker;186};187 188static inline void bigben_schedule_work(struct bigben_device *bigben)189{190 unsigned long flags;191 192 spin_lock_irqsave(&bigben->lock, flags);193 if (!bigben->removed)194 schedule_work(&bigben->worker);195 spin_unlock_irqrestore(&bigben->lock, flags);196}197 198static void bigben_worker(struct work_struct *work)199{200 struct bigben_device *bigben = container_of(work,201 struct bigben_device, worker);202 struct hid_field *report_field = bigben->report->field[0];203 bool do_work_led = false;204 bool do_work_ff = false;205 u8 *buf;206 u32 len;207 unsigned long flags;208 209 buf = hid_alloc_report_buf(bigben->report, GFP_KERNEL);210 if (!buf)211 return;212 213 len = hid_report_len(bigben->report);214 215 /* LED work */216 spin_lock_irqsave(&bigben->lock, flags);217 218 if (bigben->work_led) {219 bigben->work_led = false;220 do_work_led = true;221 report_field->value[0] = 0x01; /* 1 = led message */222 report_field->value[1] = 0x08; /* reserved value, always 8 */223 report_field->value[2] = bigben->led_state;224 report_field->value[3] = 0x00; /* padding */225 report_field->value[4] = 0x00; /* padding */226 report_field->value[5] = 0x00; /* padding */227 report_field->value[6] = 0x00; /* padding */228 report_field->value[7] = 0x00; /* padding */229 hid_output_report(bigben->report, buf);230 }231 232 spin_unlock_irqrestore(&bigben->lock, flags);233 234 if (do_work_led) {235 hid_hw_raw_request(bigben->hid, bigben->report->id, buf, len,236 bigben->report->type, HID_REQ_SET_REPORT);237 }238 239 /* FF work */240 spin_lock_irqsave(&bigben->lock, flags);241 242 if (bigben->work_ff) {243 bigben->work_ff = false;244 do_work_ff = true;245 report_field->value[0] = 0x02; /* 2 = rumble effect message */246 report_field->value[1] = 0x08; /* reserved value, always 8 */247 report_field->value[2] = bigben->right_motor_on;248 report_field->value[3] = bigben->left_motor_force;249 report_field->value[4] = 0xff; /* duration 0-254 (255 = nonstop) */250 report_field->value[5] = 0x00; /* padding */251 report_field->value[6] = 0x00; /* padding */252 report_field->value[7] = 0x00; /* padding */253 hid_output_report(bigben->report, buf);254 }255 256 spin_unlock_irqrestore(&bigben->lock, flags);257 258 if (do_work_ff) {259 hid_hw_raw_request(bigben->hid, bigben->report->id, buf, len,260 bigben->report->type, HID_REQ_SET_REPORT);261 }262 263 kfree(buf);264}265 266static int hid_bigben_play_effect(struct input_dev *dev, void *data,267 struct ff_effect *effect)268{269 struct hid_device *hid = input_get_drvdata(dev);270 struct bigben_device *bigben = hid_get_drvdata(hid);271 u8 right_motor_on;272 u8 left_motor_force;273 unsigned long flags;274 275 if (!bigben) {276 hid_err(hid, "no device data\n");277 return 0;278 }279 280 if (effect->type != FF_RUMBLE)281 return 0;282 283 right_motor_on = effect->u.rumble.weak_magnitude ? 1 : 0;284 left_motor_force = effect->u.rumble.strong_magnitude / 256;285 286 if (right_motor_on != bigben->right_motor_on ||287 left_motor_force != bigben->left_motor_force) {288 spin_lock_irqsave(&bigben->lock, flags);289 bigben->right_motor_on = right_motor_on;290 bigben->left_motor_force = left_motor_force;291 bigben->work_ff = true;292 spin_unlock_irqrestore(&bigben->lock, flags);293 294 bigben_schedule_work(bigben);295 }296 297 return 0;298}299 300static void bigben_set_led(struct led_classdev *led,301 enum led_brightness value)302{303 struct device *dev = led->dev->parent;304 struct hid_device *hid = to_hid_device(dev);305 struct bigben_device *bigben = hid_get_drvdata(hid);306 int n;307 bool work;308 unsigned long flags;309 310 if (!bigben) {311 hid_err(hid, "no device data\n");312 return;313 }314 315 for (n = 0; n < NUM_LEDS; n++) {316 if (led == bigben->leds[n]) {317 spin_lock_irqsave(&bigben->lock, flags);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 spin_unlock_irqrestore(&bigben->lock, flags);326 327 if (work) {328 bigben->work_led = true;329 bigben_schedule_work(bigben);330 }331 return;332 }333 }334}335 336static enum led_brightness bigben_get_led(struct led_classdev *led)337{338 struct device *dev = led->dev->parent;339 struct hid_device *hid = to_hid_device(dev);340 struct bigben_device *bigben = hid_get_drvdata(hid);341 int n;342 343 if (!bigben) {344 hid_err(hid, "no device data\n");345 return LED_OFF;346 }347 348 for (n = 0; n < NUM_LEDS; n++) {349 if (led == bigben->leds[n])350 return (bigben->led_state & BIT(n)) ? LED_ON : LED_OFF;351 }352 353 return LED_OFF;354}355 356static void bigben_remove(struct hid_device *hid)357{358 struct bigben_device *bigben = hid_get_drvdata(hid);359 unsigned long flags;360 361 spin_lock_irqsave(&bigben->lock, flags);362 bigben->removed = true;363 spin_unlock_irqrestore(&bigben->lock, flags);364 365 cancel_work_sync(&bigben->worker);366 hid_hw_stop(hid);367}368 369static int bigben_probe(struct hid_device *hid,370 const struct hid_device_id *id)371{372 struct bigben_device *bigben;373 struct hid_input *hidinput;374 struct led_classdev *led;375 char *name;376 size_t name_sz;377 int n, error;378 379 bigben = devm_kzalloc(&hid->dev, sizeof(*bigben), GFP_KERNEL);380 if (!bigben)381 return -ENOMEM;382 hid_set_drvdata(hid, bigben);383 bigben->hid = hid;384 bigben->removed = false;385 386 error = hid_parse(hid);387 if (error) {388 hid_err(hid, "parse failed\n");389 return error;390 }391 392 error = hid_hw_start(hid, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);393 if (error) {394 hid_err(hid, "hw start failed\n");395 return error;396 }397 398 bigben->report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 8);399 if (!bigben->report) {400 hid_err(hid, "no output report found\n");401 error = -ENODEV;402 goto error_hw_stop;403 }404 405 if (list_empty(&hid->inputs)) {406 hid_err(hid, "no inputs found\n");407 error = -ENODEV;408 goto error_hw_stop;409 }410 411 hidinput = list_first_entry(&hid->inputs, struct hid_input, list);412 set_bit(FF_RUMBLE, hidinput->input->ffbit);413 414 INIT_WORK(&bigben->worker, bigben_worker);415 spin_lock_init(&bigben->lock);416 417 error = input_ff_create_memless(hidinput->input, NULL,418 hid_bigben_play_effect);419 if (error)420 goto error_hw_stop;421 422 name_sz = strlen(dev_name(&hid->dev)) + strlen(":red:bigben#") + 1;423 424 for (n = 0; n < NUM_LEDS; n++) {425 led = devm_kzalloc(426 &hid->dev,427 sizeof(struct led_classdev) + name_sz,428 GFP_KERNEL429 );430 if (!led) {431 error = -ENOMEM;432 goto error_hw_stop;433 }434 name = (void *)(&led[1]);435 snprintf(name, name_sz,436 "%s:red:bigben%d",437 dev_name(&hid->dev), n + 1438 );439 led->name = name;440 led->brightness = (n == 0) ? LED_ON : LED_OFF;441 led->max_brightness = 1;442 led->brightness_get = bigben_get_led;443 led->brightness_set = bigben_set_led;444 bigben->leds[n] = led;445 error = devm_led_classdev_register(&hid->dev, led);446 if (error)447 goto error_hw_stop;448 }449 450 /* initial state: LED1 is on, no rumble effect */451 bigben->led_state = BIT(0);452 bigben->right_motor_on = 0;453 bigben->left_motor_force = 0;454 bigben->work_led = true;455 bigben->work_ff = true;456 bigben_schedule_work(bigben);457 458 hid_info(hid, "LED and force feedback support for BigBen gamepad\n");459 460 return 0;461 462error_hw_stop:463 hid_hw_stop(hid);464 return error;465}466 467static const __u8 *bigben_report_fixup(struct hid_device *hid, __u8 *rdesc,468 unsigned int *rsize)469{470 if (*rsize == PID0902_RDESC_ORIG_SIZE) {471 *rsize = sizeof(pid0902_rdesc_fixed);472 return pid0902_rdesc_fixed;473 } else474 hid_warn(hid, "unexpected rdesc, please submit for review\n");475 return rdesc;476}477 478static const struct hid_device_id bigben_devices[] = {479 { HID_USB_DEVICE(USB_VENDOR_ID_BIGBEN, USB_DEVICE_ID_BIGBEN_PS3OFMINIPAD) },480 { }481};482MODULE_DEVICE_TABLE(hid, bigben_devices);483 484static struct hid_driver bigben_driver = {485 .name = "bigben",486 .id_table = bigben_devices,487 .probe = bigben_probe,488 .report_fixup = bigben_report_fixup,489 .remove = bigben_remove,490};491module_hid_driver(bigben_driver);492 493MODULE_DESCRIPTION("LED & force feedback support for BigBen Interactive");494MODULE_LICENSE("GPL");495