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        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
 187static 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
 220static 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
 244static 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
 277static 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
 297static 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
 306static 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
 388static __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
 399static const struct hid_device_id bigben_devices[] = {
 400        { HID_USB_DEVICE(USB_VENDOR_ID_BIGBEN, USB_DEVICE_ID_BIGBEN_PS3OFMINIPAD) },
 401        { }
 402};
 403MODULE_DEVICE_TABLE(hid, bigben_devices);
 404
 405static 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};
 412module_hid_driver(bigben_driver);
 413
 414MODULE_LICENSE("GPL");
 415