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 bool removed; 178 u8 led_state; /* LED1 = 1 .. LED4 = 8 */ 179 u8 right_motor_on; /* right motor off/on 0/1 */ 180 u8 left_motor_force; /* left motor force 0-255 */ 181 struct led_classdev *leds[NUM_LEDS]; 182 bool work_led; 183 bool work_ff; 184 struct work_struct worker; 185 }; 186 187 188 static void bigben_worker(struct work_struct *work) 189 { 190 struct bigben_device *bigben = container_of(work, 191 struct bigben_device, worker); 192 struct hid_field *report_field = bigben->report->field[0]; 193 194 if (bigben->removed) 195 return; 196 197 if (bigben->work_led) { 198 bigben->work_led = false; 199 report_field->value[0] = 0x01; /* 1 = led message */ 200 report_field->value[1] = 0x08; /* reserved value, always 8 */ 201 report_field->value[2] = bigben->led_state; 202 report_field->value[3] = 0x00; /* padding */ 203 report_field->value[4] = 0x00; /* padding */ 204 report_field->value[5] = 0x00; /* padding */ 205 report_field->value[6] = 0x00; /* padding */ 206 report_field->value[7] = 0x00; /* padding */ 207 hid_hw_request(bigben->hid, bigben->report, HID_REQ_SET_REPORT); 208 } 209 210 if (bigben->work_ff) { 211 bigben->work_ff = false; 212 report_field->value[0] = 0x02; /* 2 = rumble effect message */ 213 report_field->value[1] = 0x08; /* reserved value, always 8 */ 214 report_field->value[2] = bigben->right_motor_on; 215 report_field->value[3] = bigben->left_motor_force; 216 report_field->value[4] = 0xff; /* duration 0-254 (255 = nonstop) */ 217 report_field->value[5] = 0x00; /* padding */ 218 report_field->value[6] = 0x00; /* padding */ 219 report_field->value[7] = 0x00; /* padding */ 220 hid_hw_request(bigben->hid, bigben->report, HID_REQ_SET_REPORT); 221 } 222 } 223 224 static int hid_bigben_play_effect(struct input_dev *dev, void *data, 225 struct ff_effect *effect) 226 { 227 struct hid_device *hid = input_get_drvdata(dev); 228 struct bigben_device *bigben = hid_get_drvdata(hid); 229 u8 right_motor_on; 230 u8 left_motor_force; 231 232 if (!bigben) { 233 hid_err(hid, "no device data\n"); 234 return 0; 235 } 236 237 if (effect->type != FF_RUMBLE) 238 return 0; 239 240 right_motor_on = effect->u.rumble.weak_magnitude ? 1 : 0; 241 left_motor_force = effect->u.rumble.strong_magnitude / 256; 242 243 if (right_motor_on != bigben->right_motor_on || 244 left_motor_force != bigben->left_motor_force) { 245 bigben->right_motor_on = right_motor_on; 246 bigben->left_motor_force = left_motor_force; 247 bigben->work_ff = true; 248 schedule_work(&bigben->worker); 249 } 250 251 return 0; 252 } 253 254 static void bigben_set_led(struct led_classdev *led, 255 enum led_brightness value) 256 { 257 struct device *dev = led->dev->parent; 258 struct hid_device *hid = to_hid_device(dev); 259 struct bigben_device *bigben = hid_get_drvdata(hid); 260 int n; 261 bool work; 262 263 if (!bigben) { 264 hid_err(hid, "no device data\n"); 265 return; 266 } 267 268 for (n = 0; n < NUM_LEDS; n++) { 269 if (led == bigben->leds[n]) { 270 if (value == LED_OFF) { 271 work = (bigben->led_state & BIT(n)); 272 bigben->led_state &= ~BIT(n); 273 } else { 274 work = !(bigben->led_state & BIT(n)); 275 bigben->led_state |= BIT(n); 276 } 277 278 if (work) { 279 bigben->work_led = true; 280 schedule_work(&bigben->worker); 281 } 282 return; 283 } 284 } 285 } 286 287 static enum led_brightness bigben_get_led(struct led_classdev *led) 288 { 289 struct device *dev = led->dev->parent; 290 struct hid_device *hid = to_hid_device(dev); 291 struct bigben_device *bigben = hid_get_drvdata(hid); 292 int n; 293 294 if (!bigben) { 295 hid_err(hid, "no device data\n"); 296 return LED_OFF; 297 } 298 299 for (n = 0; n < NUM_LEDS; n++) { 300 if (led == bigben->leds[n]) 301 return (bigben->led_state & BIT(n)) ? LED_ON : LED_OFF; 302 } 303 304 return LED_OFF; 305 } 306 307 static void bigben_remove(struct hid_device *hid) 308 { 309 struct bigben_device *bigben = hid_get_drvdata(hid); 310 311 bigben->removed = true; 312 cancel_work_sync(&bigben->worker); 313 hid_hw_stop(hid); 314 } 315 316 static int bigben_probe(struct hid_device *hid, 317 const struct hid_device_id *id) 318 { 319 struct bigben_device *bigben; 320 struct hid_input *hidinput; 321 struct list_head *report_list; 322 struct led_classdev *led; 323 char *name; 324 size_t name_sz; 325 int n, error; 326 327 bigben = devm_kzalloc(&hid->dev, sizeof(*bigben), GFP_KERNEL); 328 if (!bigben) 329 return -ENOMEM; 330 hid_set_drvdata(hid, bigben); 331 bigben->hid = hid; 332 bigben->removed = false; 333 334 error = hid_parse(hid); 335 if (error) { 336 hid_err(hid, "parse failed\n"); 337 return error; 338 } 339 340 error = hid_hw_start(hid, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); 341 if (error) { 342 hid_err(hid, "hw start failed\n"); 343 return error; 344 } 345 346 report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; 347 bigben->report = list_entry(report_list->next, 348 struct hid_report, list); 349 350 hidinput = list_first_entry(&hid->inputs, struct hid_input, list); 351 set_bit(FF_RUMBLE, hidinput->input->ffbit); 352 353 INIT_WORK(&bigben->worker, bigben_worker); 354 355 error = input_ff_create_memless(hidinput->input, NULL, 356 hid_bigben_play_effect); 357 if (error) 358 goto error_hw_stop; 359 360 name_sz = strlen(dev_name(&hid->dev)) + strlen(":red:bigben#") + 1; 361 362 for (n = 0; n < NUM_LEDS; n++) { 363 led = devm_kzalloc( 364 &hid->dev, 365 sizeof(struct led_classdev) + name_sz, 366 GFP_KERNEL 367 ); 368 if (!led) { 369 error = -ENOMEM; 370 goto error_hw_stop; 371 } 372 name = (void *)(&led[1]); 373 snprintf(name, name_sz, 374 "%s:red:bigben%d", 375 dev_name(&hid->dev), n + 1 376 ); 377 led->name = name; 378 led->brightness = (n == 0) ? LED_ON : LED_OFF; 379 led->max_brightness = 1; 380 led->brightness_get = bigben_get_led; 381 led->brightness_set = bigben_set_led; 382 bigben->leds[n] = led; 383 error = devm_led_classdev_register(&hid->dev, led); 384 if (error) 385 goto error_hw_stop; 386 } 387 388 /* initial state: LED1 is on, no rumble effect */ 389 bigben->led_state = BIT(0); 390 bigben->right_motor_on = 0; 391 bigben->left_motor_force = 0; 392 bigben->work_led = true; 393 bigben->work_ff = true; 394 schedule_work(&bigben->worker); 395 396 hid_info(hid, "LED and force feedback support for BigBen gamepad\n"); 397 398 return 0; 399 400 error_hw_stop: 401 hid_hw_stop(hid); 402 return error; 403 } 404 405 static __u8 *bigben_report_fixup(struct hid_device *hid, __u8 *rdesc, 406 unsigned int *rsize) 407 { 408 if (*rsize == PID0902_RDESC_ORIG_SIZE) { 409 rdesc = pid0902_rdesc_fixed; 410 *rsize = sizeof(pid0902_rdesc_fixed); 411 } else 412 hid_warn(hid, "unexpected rdesc, please submit for review\n"); 413 return rdesc; 414 } 415 416 static const struct hid_device_id bigben_devices[] = { 417 { HID_USB_DEVICE(USB_VENDOR_ID_BIGBEN, USB_DEVICE_ID_BIGBEN_PS3OFMINIPAD) }, 418 { } 419 }; 420 MODULE_DEVICE_TABLE(hid, bigben_devices); 421 422 static struct hid_driver bigben_driver = { 423 .name = "bigben", 424 .id_table = bigben_devices, 425 .probe = bigben_probe, 426 .report_fixup = bigben_report_fixup, 427 .remove = bigben_remove, 428 }; 429 module_hid_driver(bigben_driver); 430 431 MODULE_LICENSE("GPL"); 432