linux/drivers/hid/hid-udraw-ps3.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * HID driver for THQ PS3 uDraw tablet
   4 *
   5 * Copyright (C) 2016 Red Hat Inc. All Rights Reserved
   6 */
   7
   8#include <linux/device.h>
   9#include <linux/hid.h>
  10#include <linux/module.h>
  11#include "hid-ids.h"
  12
  13MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>");
  14MODULE_DESCRIPTION("PS3 uDraw tablet driver");
  15MODULE_LICENSE("GPL");
  16
  17/*
  18 * Protocol information from:
  19 * https://brandonw.net/udraw/
  20 * and the source code of:
  21 * https://vvvv.org/contribution/udraw-hid
  22 */
  23
  24/*
  25 * The device is setup with multiple input devices:
  26 * - the touch area which works as a touchpad
  27 * - the tablet area which works as a touchpad/drawing tablet
  28 * - a joypad with a d-pad, and 7 buttons
  29 * - an accelerometer device
  30 */
  31
  32enum {
  33        TOUCH_NONE,
  34        TOUCH_PEN,
  35        TOUCH_FINGER,
  36        TOUCH_TWOFINGER
  37};
  38
  39enum {
  40        AXIS_X,
  41        AXIS_Y,
  42        AXIS_Z
  43};
  44
  45/*
  46 * Accelerometer min/max values
  47 * in order, X, Y and Z
  48 */
  49static struct {
  50        int min;
  51        int max;
  52} accel_limits[] = {
  53        [AXIS_X] = { 490, 534 },
  54        [AXIS_Y] = { 490, 534 },
  55        [AXIS_Z] = { 492, 536 }
  56};
  57
  58#define DEVICE_NAME "THQ uDraw Game Tablet for PS3"
  59/* resolution in pixels */
  60#define RES_X 1920
  61#define RES_Y 1080
  62/* size in mm */
  63#define WIDTH  160
  64#define HEIGHT 90
  65#define PRESSURE_OFFSET 113
  66#define MAX_PRESSURE (255 - PRESSURE_OFFSET)
  67
  68struct udraw {
  69        struct input_dev *joy_input_dev;
  70        struct input_dev *touch_input_dev;
  71        struct input_dev *pen_input_dev;
  72        struct input_dev *accel_input_dev;
  73        struct hid_device *hdev;
  74
  75        /*
  76         * The device's two-finger support is pretty unreliable, as
  77         * the device could report a single touch when the two fingers
  78         * are too close together, and the distance between fingers, even
  79         * though reported is not in the same unit as the touches.
  80         *
  81         * We'll make do without it, and try to report the first touch
  82         * as reliably as possible.
  83         */
  84        int last_one_finger_x;
  85        int last_one_finger_y;
  86        int last_two_finger_x;
  87        int last_two_finger_y;
  88};
  89
  90static int clamp_accel(int axis, int offset)
  91{
  92        axis = clamp(axis,
  93                        accel_limits[offset].min,
  94                        accel_limits[offset].max);
  95        axis = (axis - accel_limits[offset].min) /
  96                        ((accel_limits[offset].max -
  97                          accel_limits[offset].min) * 0xFF);
  98        return axis;
  99}
 100
 101static int udraw_raw_event(struct hid_device *hdev, struct hid_report *report,
 102         u8 *data, int len)
 103{
 104        struct udraw *udraw = hid_get_drvdata(hdev);
 105        int touch;
 106        int x, y, z;
 107
 108        if (len != 27)
 109                return 0;
 110
 111        if (data[11] == 0x00)
 112                touch = TOUCH_NONE;
 113        else if (data[11] == 0x40)
 114                touch = TOUCH_PEN;
 115        else if (data[11] == 0x80)
 116                touch = TOUCH_FINGER;
 117        else
 118                touch = TOUCH_TWOFINGER;
 119
 120        /* joypad */
 121        input_report_key(udraw->joy_input_dev, BTN_WEST, data[0] & 1);
 122        input_report_key(udraw->joy_input_dev, BTN_SOUTH, !!(data[0] & 2));
 123        input_report_key(udraw->joy_input_dev, BTN_EAST, !!(data[0] & 4));
 124        input_report_key(udraw->joy_input_dev, BTN_NORTH, !!(data[0] & 8));
 125
 126        input_report_key(udraw->joy_input_dev, BTN_SELECT, !!(data[1] & 1));
 127        input_report_key(udraw->joy_input_dev, BTN_START, !!(data[1] & 2));
 128        input_report_key(udraw->joy_input_dev, BTN_MODE, !!(data[1] & 16));
 129
 130        x = y = 0;
 131        switch (data[2]) {
 132        case 0x0:
 133                y = -127;
 134                break;
 135        case 0x1:
 136                y = -127;
 137                x = 127;
 138                break;
 139        case 0x2:
 140                x = 127;
 141                break;
 142        case 0x3:
 143                y = 127;
 144                x = 127;
 145                break;
 146        case 0x4:
 147                y = 127;
 148                break;
 149        case 0x5:
 150                y = 127;
 151                x = -127;
 152                break;
 153        case 0x6:
 154                x = -127;
 155                break;
 156        case 0x7:
 157                y = -127;
 158                x = -127;
 159                break;
 160        default:
 161                break;
 162        }
 163
 164        input_report_abs(udraw->joy_input_dev, ABS_X, x);
 165        input_report_abs(udraw->joy_input_dev, ABS_Y, y);
 166
 167        input_sync(udraw->joy_input_dev);
 168
 169        /* For pen and touchpad */
 170        x = y = 0;
 171        if (touch != TOUCH_NONE) {
 172                if (data[15] != 0x0F)
 173                        x = data[15] * 256 + data[17];
 174                if (data[16] != 0x0F)
 175                        y = data[16] * 256 + data[18];
 176        }
 177
 178        if (touch == TOUCH_FINGER) {
 179                /* Save the last one-finger touch */
 180                udraw->last_one_finger_x = x;
 181                udraw->last_one_finger_y = y;
 182                udraw->last_two_finger_x = -1;
 183                udraw->last_two_finger_y = -1;
 184        } else if (touch == TOUCH_TWOFINGER) {
 185                /*
 186                 * We have a problem because x/y is the one for the
 187                 * second finger but we want the first finger given
 188                 * to user-space otherwise it'll look as if it jumped.
 189                 *
 190                 * See the udraw struct definition for why this was
 191                 * implemented this way.
 192                 */
 193                if (udraw->last_two_finger_x == -1) {
 194                        /* Save the position of the 2nd finger */
 195                        udraw->last_two_finger_x = x;
 196                        udraw->last_two_finger_y = y;
 197
 198                        x = udraw->last_one_finger_x;
 199                        y = udraw->last_one_finger_y;
 200                } else {
 201                        /*
 202                         * Offset the 2-finger coords using the
 203                         * saved data from the first finger
 204                         */
 205                        x = x - (udraw->last_two_finger_x
 206                                - udraw->last_one_finger_x);
 207                        y = y - (udraw->last_two_finger_y
 208                                - udraw->last_one_finger_y);
 209                }
 210        }
 211
 212        /* touchpad */
 213        if (touch == TOUCH_FINGER || touch == TOUCH_TWOFINGER) {
 214                input_report_key(udraw->touch_input_dev, BTN_TOUCH, 1);
 215                input_report_key(udraw->touch_input_dev, BTN_TOOL_FINGER,
 216                                touch == TOUCH_FINGER);
 217                input_report_key(udraw->touch_input_dev, BTN_TOOL_DOUBLETAP,
 218                                touch == TOUCH_TWOFINGER);
 219
 220                input_report_abs(udraw->touch_input_dev, ABS_X, x);
 221                input_report_abs(udraw->touch_input_dev, ABS_Y, y);
 222        } else {
 223                input_report_key(udraw->touch_input_dev, BTN_TOUCH, 0);
 224                input_report_key(udraw->touch_input_dev, BTN_TOOL_FINGER, 0);
 225                input_report_key(udraw->touch_input_dev, BTN_TOOL_DOUBLETAP, 0);
 226        }
 227        input_sync(udraw->touch_input_dev);
 228
 229        /* pen */
 230        if (touch == TOUCH_PEN) {
 231                int level;
 232
 233                level = clamp(data[13] - PRESSURE_OFFSET,
 234                                0, MAX_PRESSURE);
 235
 236                input_report_key(udraw->pen_input_dev, BTN_TOUCH, (level != 0));
 237                input_report_key(udraw->pen_input_dev, BTN_TOOL_PEN, 1);
 238                input_report_abs(udraw->pen_input_dev, ABS_PRESSURE, level);
 239                input_report_abs(udraw->pen_input_dev, ABS_X, x);
 240                input_report_abs(udraw->pen_input_dev, ABS_Y, y);
 241        } else {
 242                input_report_key(udraw->pen_input_dev, BTN_TOUCH, 0);
 243                input_report_key(udraw->pen_input_dev, BTN_TOOL_PEN, 0);
 244                input_report_abs(udraw->pen_input_dev, ABS_PRESSURE, 0);
 245        }
 246        input_sync(udraw->pen_input_dev);
 247
 248        /* accel */
 249        x = (data[19] + (data[20] << 8));
 250        x = clamp_accel(x, AXIS_X);
 251        y = (data[21] + (data[22] << 8));
 252        y = clamp_accel(y, AXIS_Y);
 253        z = (data[23] + (data[24] << 8));
 254        z = clamp_accel(z, AXIS_Z);
 255        input_report_abs(udraw->accel_input_dev, ABS_X, x);
 256        input_report_abs(udraw->accel_input_dev, ABS_Y, y);
 257        input_report_abs(udraw->accel_input_dev, ABS_Z, z);
 258        input_sync(udraw->accel_input_dev);
 259
 260        /* let hidraw and hiddev handle the report */
 261        return 0;
 262}
 263
 264static int udraw_open(struct input_dev *dev)
 265{
 266        struct udraw *udraw = input_get_drvdata(dev);
 267
 268        return hid_hw_open(udraw->hdev);
 269}
 270
 271static void udraw_close(struct input_dev *dev)
 272{
 273        struct udraw *udraw = input_get_drvdata(dev);
 274
 275        hid_hw_close(udraw->hdev);
 276}
 277
 278static struct input_dev *allocate_and_setup(struct hid_device *hdev,
 279                const char *name)
 280{
 281        struct input_dev *input_dev;
 282
 283        input_dev = devm_input_allocate_device(&hdev->dev);
 284        if (!input_dev)
 285                return NULL;
 286
 287        input_dev->name = name;
 288        input_dev->phys = hdev->phys;
 289        input_dev->dev.parent = &hdev->dev;
 290        input_dev->open = udraw_open;
 291        input_dev->close = udraw_close;
 292        input_dev->uniq = hdev->uniq;
 293        input_dev->id.bustype = hdev->bus;
 294        input_dev->id.vendor  = hdev->vendor;
 295        input_dev->id.product = hdev->product;
 296        input_dev->id.version = hdev->version;
 297        input_set_drvdata(input_dev, hid_get_drvdata(hdev));
 298
 299        return input_dev;
 300}
 301
 302static bool udraw_setup_touch(struct udraw *udraw,
 303                struct hid_device *hdev)
 304{
 305        struct input_dev *input_dev;
 306
 307        input_dev = allocate_and_setup(hdev, DEVICE_NAME " Touchpad");
 308        if (!input_dev)
 309                return false;
 310
 311        input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
 312
 313        input_set_abs_params(input_dev, ABS_X, 0, RES_X, 1, 0);
 314        input_abs_set_res(input_dev, ABS_X, RES_X / WIDTH);
 315        input_set_abs_params(input_dev, ABS_Y, 0, RES_Y, 1, 0);
 316        input_abs_set_res(input_dev, ABS_Y, RES_Y / HEIGHT);
 317
 318        set_bit(BTN_TOUCH, input_dev->keybit);
 319        set_bit(BTN_TOOL_FINGER, input_dev->keybit);
 320        set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
 321
 322        set_bit(INPUT_PROP_POINTER, input_dev->propbit);
 323
 324        udraw->touch_input_dev = input_dev;
 325
 326        return true;
 327}
 328
 329static bool udraw_setup_pen(struct udraw *udraw,
 330                struct hid_device *hdev)
 331{
 332        struct input_dev *input_dev;
 333
 334        input_dev = allocate_and_setup(hdev, DEVICE_NAME " Pen");
 335        if (!input_dev)
 336                return false;
 337
 338        input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
 339
 340        input_set_abs_params(input_dev, ABS_X, 0, RES_X, 1, 0);
 341        input_abs_set_res(input_dev, ABS_X, RES_X / WIDTH);
 342        input_set_abs_params(input_dev, ABS_Y, 0, RES_Y, 1, 0);
 343        input_abs_set_res(input_dev, ABS_Y, RES_Y / HEIGHT);
 344        input_set_abs_params(input_dev, ABS_PRESSURE,
 345                        0, MAX_PRESSURE, 0, 0);
 346
 347        set_bit(BTN_TOUCH, input_dev->keybit);
 348        set_bit(BTN_TOOL_PEN, input_dev->keybit);
 349
 350        set_bit(INPUT_PROP_POINTER, input_dev->propbit);
 351
 352        udraw->pen_input_dev = input_dev;
 353
 354        return true;
 355}
 356
 357static bool udraw_setup_accel(struct udraw *udraw,
 358                struct hid_device *hdev)
 359{
 360        struct input_dev *input_dev;
 361
 362        input_dev = allocate_and_setup(hdev, DEVICE_NAME " Accelerometer");
 363        if (!input_dev)
 364                return false;
 365
 366        input_dev->evbit[0] = BIT(EV_ABS);
 367
 368        /* 1G accel is reported as ~256, so clamp to 2G */
 369        input_set_abs_params(input_dev, ABS_X, -512, 512, 0, 0);
 370        input_set_abs_params(input_dev, ABS_Y, -512, 512, 0, 0);
 371        input_set_abs_params(input_dev, ABS_Z, -512, 512, 0, 0);
 372
 373        set_bit(INPUT_PROP_ACCELEROMETER, input_dev->propbit);
 374
 375        udraw->accel_input_dev = input_dev;
 376
 377        return true;
 378}
 379
 380static bool udraw_setup_joypad(struct udraw *udraw,
 381                struct hid_device *hdev)
 382{
 383        struct input_dev *input_dev;
 384
 385        input_dev = allocate_and_setup(hdev, DEVICE_NAME " Joypad");
 386        if (!input_dev)
 387                return false;
 388
 389        input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
 390
 391        set_bit(BTN_SOUTH, input_dev->keybit);
 392        set_bit(BTN_NORTH, input_dev->keybit);
 393        set_bit(BTN_EAST, input_dev->keybit);
 394        set_bit(BTN_WEST, input_dev->keybit);
 395        set_bit(BTN_SELECT, input_dev->keybit);
 396        set_bit(BTN_START, input_dev->keybit);
 397        set_bit(BTN_MODE, input_dev->keybit);
 398
 399        input_set_abs_params(input_dev, ABS_X, -127, 127, 0, 0);
 400        input_set_abs_params(input_dev, ABS_Y, -127, 127, 0, 0);
 401
 402        udraw->joy_input_dev = input_dev;
 403
 404        return true;
 405}
 406
 407static int udraw_probe(struct hid_device *hdev, const struct hid_device_id *id)
 408{
 409        struct udraw *udraw;
 410        int ret;
 411
 412        udraw = devm_kzalloc(&hdev->dev, sizeof(struct udraw), GFP_KERNEL);
 413        if (!udraw)
 414                return -ENOMEM;
 415
 416        udraw->hdev = hdev;
 417        udraw->last_two_finger_x = -1;
 418        udraw->last_two_finger_y = -1;
 419
 420        hid_set_drvdata(hdev, udraw);
 421
 422        ret = hid_parse(hdev);
 423        if (ret) {
 424                hid_err(hdev, "parse failed\n");
 425                return ret;
 426        }
 427
 428        if (!udraw_setup_joypad(udraw, hdev) ||
 429            !udraw_setup_touch(udraw, hdev) ||
 430            !udraw_setup_pen(udraw, hdev) ||
 431            !udraw_setup_accel(udraw, hdev)) {
 432                hid_err(hdev, "could not allocate interfaces\n");
 433                return -ENOMEM;
 434        }
 435
 436        ret = input_register_device(udraw->joy_input_dev) ||
 437                input_register_device(udraw->touch_input_dev) ||
 438                input_register_device(udraw->pen_input_dev) ||
 439                input_register_device(udraw->accel_input_dev);
 440        if (ret) {
 441                hid_err(hdev, "failed to register interfaces\n");
 442                return ret;
 443        }
 444
 445        ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW | HID_CONNECT_DRIVER);
 446        if (ret) {
 447                hid_err(hdev, "hw start failed\n");
 448                return ret;
 449        }
 450
 451        return 0;
 452}
 453
 454static const struct hid_device_id udraw_devices[] = {
 455        { HID_USB_DEVICE(USB_VENDOR_ID_THQ, USB_DEVICE_ID_THQ_PS3_UDRAW) },
 456        { }
 457};
 458MODULE_DEVICE_TABLE(hid, udraw_devices);
 459
 460static struct hid_driver udraw_driver = {
 461        .name = "hid-udraw",
 462        .id_table = udraw_devices,
 463        .raw_event = udraw_raw_event,
 464        .probe = udraw_probe,
 465};
 466module_hid_driver(udraw_driver);
 467