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"); 415