linux/drivers/hid/hid-bigbenff.c
<<
>>
Prefs
   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 */
 102static __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
 174struct 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
 188static 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
 224static 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
 254static 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
 287static 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
 307static 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
 316static 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
 400error_hw_stop:
 401        hid_hw_stop(hid);
 402        return error;
 403}
 404
 405static __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
 416static const struct hid_device_id bigben_devices[] = {
 417        { HID_USB_DEVICE(USB_VENDOR_ID_BIGBEN, USB_DEVICE_ID_BIGBEN_PS3OFMINIPAD) },
 418        { }
 419};
 420MODULE_DEVICE_TABLE(hid, bigben_devices);
 421
 422static 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};
 429module_hid_driver(bigben_driver);
 430
 431MODULE_LICENSE("GPL");
 432