162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * HID driver for THQ PS3 uDraw tablet 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2016 Red Hat Inc. All Rights Reserved 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/device.h> 962306a36Sopenharmony_ci#include <linux/hid.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include "hid-ids.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ciMODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>"); 1462306a36Sopenharmony_ciMODULE_DESCRIPTION("PS3 uDraw tablet driver"); 1562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* 1862306a36Sopenharmony_ci * Protocol information from: 1962306a36Sopenharmony_ci * https://brandonw.net/udraw/ 2062306a36Sopenharmony_ci * and the source code of: 2162306a36Sopenharmony_ci * https://vvvv.org/contribution/udraw-hid 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* 2562306a36Sopenharmony_ci * The device is setup with multiple input devices: 2662306a36Sopenharmony_ci * - the touch area which works as a touchpad 2762306a36Sopenharmony_ci * - the tablet area which works as a touchpad/drawing tablet 2862306a36Sopenharmony_ci * - a joypad with a d-pad, and 7 buttons 2962306a36Sopenharmony_ci * - an accelerometer device 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cienum { 3362306a36Sopenharmony_ci TOUCH_NONE, 3462306a36Sopenharmony_ci TOUCH_PEN, 3562306a36Sopenharmony_ci TOUCH_FINGER, 3662306a36Sopenharmony_ci TOUCH_TWOFINGER 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cienum { 4062306a36Sopenharmony_ci AXIS_X, 4162306a36Sopenharmony_ci AXIS_Y, 4262306a36Sopenharmony_ci AXIS_Z 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* 4662306a36Sopenharmony_ci * Accelerometer min/max values 4762306a36Sopenharmony_ci * in order, X, Y and Z 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_cistatic struct { 5062306a36Sopenharmony_ci int min; 5162306a36Sopenharmony_ci int max; 5262306a36Sopenharmony_ci} accel_limits[] = { 5362306a36Sopenharmony_ci [AXIS_X] = { 490, 534 }, 5462306a36Sopenharmony_ci [AXIS_Y] = { 490, 534 }, 5562306a36Sopenharmony_ci [AXIS_Z] = { 492, 536 } 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define DEVICE_NAME "THQ uDraw Game Tablet for PS3" 5962306a36Sopenharmony_ci/* resolution in pixels */ 6062306a36Sopenharmony_ci#define RES_X 1920 6162306a36Sopenharmony_ci#define RES_Y 1080 6262306a36Sopenharmony_ci/* size in mm */ 6362306a36Sopenharmony_ci#define WIDTH 160 6462306a36Sopenharmony_ci#define HEIGHT 90 6562306a36Sopenharmony_ci#define PRESSURE_OFFSET 113 6662306a36Sopenharmony_ci#define MAX_PRESSURE (255 - PRESSURE_OFFSET) 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistruct udraw { 6962306a36Sopenharmony_ci struct input_dev *joy_input_dev; 7062306a36Sopenharmony_ci struct input_dev *touch_input_dev; 7162306a36Sopenharmony_ci struct input_dev *pen_input_dev; 7262306a36Sopenharmony_ci struct input_dev *accel_input_dev; 7362306a36Sopenharmony_ci struct hid_device *hdev; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* 7662306a36Sopenharmony_ci * The device's two-finger support is pretty unreliable, as 7762306a36Sopenharmony_ci * the device could report a single touch when the two fingers 7862306a36Sopenharmony_ci * are too close together, and the distance between fingers, even 7962306a36Sopenharmony_ci * though reported is not in the same unit as the touches. 8062306a36Sopenharmony_ci * 8162306a36Sopenharmony_ci * We'll make do without it, and try to report the first touch 8262306a36Sopenharmony_ci * as reliably as possible. 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_ci int last_one_finger_x; 8562306a36Sopenharmony_ci int last_one_finger_y; 8662306a36Sopenharmony_ci int last_two_finger_x; 8762306a36Sopenharmony_ci int last_two_finger_y; 8862306a36Sopenharmony_ci}; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic int clamp_accel(int axis, int offset) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci axis = clamp(axis, 9362306a36Sopenharmony_ci accel_limits[offset].min, 9462306a36Sopenharmony_ci accel_limits[offset].max); 9562306a36Sopenharmony_ci axis = (axis - accel_limits[offset].min) / 9662306a36Sopenharmony_ci ((accel_limits[offset].max - 9762306a36Sopenharmony_ci accel_limits[offset].min) * 0xFF); 9862306a36Sopenharmony_ci return axis; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic int udraw_raw_event(struct hid_device *hdev, struct hid_report *report, 10262306a36Sopenharmony_ci u8 *data, int len) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct udraw *udraw = hid_get_drvdata(hdev); 10562306a36Sopenharmony_ci int touch; 10662306a36Sopenharmony_ci int x, y, z; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (len != 27) 10962306a36Sopenharmony_ci return 0; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (data[11] == 0x00) 11262306a36Sopenharmony_ci touch = TOUCH_NONE; 11362306a36Sopenharmony_ci else if (data[11] == 0x40) 11462306a36Sopenharmony_ci touch = TOUCH_PEN; 11562306a36Sopenharmony_ci else if (data[11] == 0x80) 11662306a36Sopenharmony_ci touch = TOUCH_FINGER; 11762306a36Sopenharmony_ci else 11862306a36Sopenharmony_ci touch = TOUCH_TWOFINGER; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* joypad */ 12162306a36Sopenharmony_ci input_report_key(udraw->joy_input_dev, BTN_WEST, data[0] & 1); 12262306a36Sopenharmony_ci input_report_key(udraw->joy_input_dev, BTN_SOUTH, !!(data[0] & 2)); 12362306a36Sopenharmony_ci input_report_key(udraw->joy_input_dev, BTN_EAST, !!(data[0] & 4)); 12462306a36Sopenharmony_ci input_report_key(udraw->joy_input_dev, BTN_NORTH, !!(data[0] & 8)); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci input_report_key(udraw->joy_input_dev, BTN_SELECT, !!(data[1] & 1)); 12762306a36Sopenharmony_ci input_report_key(udraw->joy_input_dev, BTN_START, !!(data[1] & 2)); 12862306a36Sopenharmony_ci input_report_key(udraw->joy_input_dev, BTN_MODE, !!(data[1] & 16)); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci x = y = 0; 13162306a36Sopenharmony_ci switch (data[2]) { 13262306a36Sopenharmony_ci case 0x0: 13362306a36Sopenharmony_ci y = -127; 13462306a36Sopenharmony_ci break; 13562306a36Sopenharmony_ci case 0x1: 13662306a36Sopenharmony_ci y = -127; 13762306a36Sopenharmony_ci x = 127; 13862306a36Sopenharmony_ci break; 13962306a36Sopenharmony_ci case 0x2: 14062306a36Sopenharmony_ci x = 127; 14162306a36Sopenharmony_ci break; 14262306a36Sopenharmony_ci case 0x3: 14362306a36Sopenharmony_ci y = 127; 14462306a36Sopenharmony_ci x = 127; 14562306a36Sopenharmony_ci break; 14662306a36Sopenharmony_ci case 0x4: 14762306a36Sopenharmony_ci y = 127; 14862306a36Sopenharmony_ci break; 14962306a36Sopenharmony_ci case 0x5: 15062306a36Sopenharmony_ci y = 127; 15162306a36Sopenharmony_ci x = -127; 15262306a36Sopenharmony_ci break; 15362306a36Sopenharmony_ci case 0x6: 15462306a36Sopenharmony_ci x = -127; 15562306a36Sopenharmony_ci break; 15662306a36Sopenharmony_ci case 0x7: 15762306a36Sopenharmony_ci y = -127; 15862306a36Sopenharmony_ci x = -127; 15962306a36Sopenharmony_ci break; 16062306a36Sopenharmony_ci default: 16162306a36Sopenharmony_ci break; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci input_report_abs(udraw->joy_input_dev, ABS_X, x); 16562306a36Sopenharmony_ci input_report_abs(udraw->joy_input_dev, ABS_Y, y); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci input_sync(udraw->joy_input_dev); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* For pen and touchpad */ 17062306a36Sopenharmony_ci x = y = 0; 17162306a36Sopenharmony_ci if (touch != TOUCH_NONE) { 17262306a36Sopenharmony_ci if (data[15] != 0x0F) 17362306a36Sopenharmony_ci x = data[15] * 256 + data[17]; 17462306a36Sopenharmony_ci if (data[16] != 0x0F) 17562306a36Sopenharmony_ci y = data[16] * 256 + data[18]; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (touch == TOUCH_FINGER) { 17962306a36Sopenharmony_ci /* Save the last one-finger touch */ 18062306a36Sopenharmony_ci udraw->last_one_finger_x = x; 18162306a36Sopenharmony_ci udraw->last_one_finger_y = y; 18262306a36Sopenharmony_ci udraw->last_two_finger_x = -1; 18362306a36Sopenharmony_ci udraw->last_two_finger_y = -1; 18462306a36Sopenharmony_ci } else if (touch == TOUCH_TWOFINGER) { 18562306a36Sopenharmony_ci /* 18662306a36Sopenharmony_ci * We have a problem because x/y is the one for the 18762306a36Sopenharmony_ci * second finger but we want the first finger given 18862306a36Sopenharmony_ci * to user-space otherwise it'll look as if it jumped. 18962306a36Sopenharmony_ci * 19062306a36Sopenharmony_ci * See the udraw struct definition for why this was 19162306a36Sopenharmony_ci * implemented this way. 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_ci if (udraw->last_two_finger_x == -1) { 19462306a36Sopenharmony_ci /* Save the position of the 2nd finger */ 19562306a36Sopenharmony_ci udraw->last_two_finger_x = x; 19662306a36Sopenharmony_ci udraw->last_two_finger_y = y; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci x = udraw->last_one_finger_x; 19962306a36Sopenharmony_ci y = udraw->last_one_finger_y; 20062306a36Sopenharmony_ci } else { 20162306a36Sopenharmony_ci /* 20262306a36Sopenharmony_ci * Offset the 2-finger coords using the 20362306a36Sopenharmony_ci * saved data from the first finger 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_ci x = x - (udraw->last_two_finger_x 20662306a36Sopenharmony_ci - udraw->last_one_finger_x); 20762306a36Sopenharmony_ci y = y - (udraw->last_two_finger_y 20862306a36Sopenharmony_ci - udraw->last_one_finger_y); 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* touchpad */ 21362306a36Sopenharmony_ci if (touch == TOUCH_FINGER || touch == TOUCH_TWOFINGER) { 21462306a36Sopenharmony_ci input_report_key(udraw->touch_input_dev, BTN_TOUCH, 1); 21562306a36Sopenharmony_ci input_report_key(udraw->touch_input_dev, BTN_TOOL_FINGER, 21662306a36Sopenharmony_ci touch == TOUCH_FINGER); 21762306a36Sopenharmony_ci input_report_key(udraw->touch_input_dev, BTN_TOOL_DOUBLETAP, 21862306a36Sopenharmony_ci touch == TOUCH_TWOFINGER); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci input_report_abs(udraw->touch_input_dev, ABS_X, x); 22162306a36Sopenharmony_ci input_report_abs(udraw->touch_input_dev, ABS_Y, y); 22262306a36Sopenharmony_ci } else { 22362306a36Sopenharmony_ci input_report_key(udraw->touch_input_dev, BTN_TOUCH, 0); 22462306a36Sopenharmony_ci input_report_key(udraw->touch_input_dev, BTN_TOOL_FINGER, 0); 22562306a36Sopenharmony_ci input_report_key(udraw->touch_input_dev, BTN_TOOL_DOUBLETAP, 0); 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci input_sync(udraw->touch_input_dev); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* pen */ 23062306a36Sopenharmony_ci if (touch == TOUCH_PEN) { 23162306a36Sopenharmony_ci int level; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci level = clamp(data[13] - PRESSURE_OFFSET, 23462306a36Sopenharmony_ci 0, MAX_PRESSURE); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci input_report_key(udraw->pen_input_dev, BTN_TOUCH, (level != 0)); 23762306a36Sopenharmony_ci input_report_key(udraw->pen_input_dev, BTN_TOOL_PEN, 1); 23862306a36Sopenharmony_ci input_report_abs(udraw->pen_input_dev, ABS_PRESSURE, level); 23962306a36Sopenharmony_ci input_report_abs(udraw->pen_input_dev, ABS_X, x); 24062306a36Sopenharmony_ci input_report_abs(udraw->pen_input_dev, ABS_Y, y); 24162306a36Sopenharmony_ci } else { 24262306a36Sopenharmony_ci input_report_key(udraw->pen_input_dev, BTN_TOUCH, 0); 24362306a36Sopenharmony_ci input_report_key(udraw->pen_input_dev, BTN_TOOL_PEN, 0); 24462306a36Sopenharmony_ci input_report_abs(udraw->pen_input_dev, ABS_PRESSURE, 0); 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci input_sync(udraw->pen_input_dev); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* accel */ 24962306a36Sopenharmony_ci x = (data[19] + (data[20] << 8)); 25062306a36Sopenharmony_ci x = clamp_accel(x, AXIS_X); 25162306a36Sopenharmony_ci y = (data[21] + (data[22] << 8)); 25262306a36Sopenharmony_ci y = clamp_accel(y, AXIS_Y); 25362306a36Sopenharmony_ci z = (data[23] + (data[24] << 8)); 25462306a36Sopenharmony_ci z = clamp_accel(z, AXIS_Z); 25562306a36Sopenharmony_ci input_report_abs(udraw->accel_input_dev, ABS_X, x); 25662306a36Sopenharmony_ci input_report_abs(udraw->accel_input_dev, ABS_Y, y); 25762306a36Sopenharmony_ci input_report_abs(udraw->accel_input_dev, ABS_Z, z); 25862306a36Sopenharmony_ci input_sync(udraw->accel_input_dev); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* let hidraw and hiddev handle the report */ 26162306a36Sopenharmony_ci return 0; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic int udraw_open(struct input_dev *dev) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct udraw *udraw = input_get_drvdata(dev); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return hid_hw_open(udraw->hdev); 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic void udraw_close(struct input_dev *dev) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci struct udraw *udraw = input_get_drvdata(dev); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci hid_hw_close(udraw->hdev); 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic struct input_dev *allocate_and_setup(struct hid_device *hdev, 27962306a36Sopenharmony_ci const char *name) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci struct input_dev *input_dev; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci input_dev = devm_input_allocate_device(&hdev->dev); 28462306a36Sopenharmony_ci if (!input_dev) 28562306a36Sopenharmony_ci return NULL; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci input_dev->name = name; 28862306a36Sopenharmony_ci input_dev->phys = hdev->phys; 28962306a36Sopenharmony_ci input_dev->dev.parent = &hdev->dev; 29062306a36Sopenharmony_ci input_dev->open = udraw_open; 29162306a36Sopenharmony_ci input_dev->close = udraw_close; 29262306a36Sopenharmony_ci input_dev->uniq = hdev->uniq; 29362306a36Sopenharmony_ci input_dev->id.bustype = hdev->bus; 29462306a36Sopenharmony_ci input_dev->id.vendor = hdev->vendor; 29562306a36Sopenharmony_ci input_dev->id.product = hdev->product; 29662306a36Sopenharmony_ci input_dev->id.version = hdev->version; 29762306a36Sopenharmony_ci input_set_drvdata(input_dev, hid_get_drvdata(hdev)); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci return input_dev; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic bool udraw_setup_touch(struct udraw *udraw, 30362306a36Sopenharmony_ci struct hid_device *hdev) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci struct input_dev *input_dev; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci input_dev = allocate_and_setup(hdev, DEVICE_NAME " Touchpad"); 30862306a36Sopenharmony_ci if (!input_dev) 30962306a36Sopenharmony_ci return false; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_X, 0, RES_X, 1, 0); 31462306a36Sopenharmony_ci input_abs_set_res(input_dev, ABS_X, RES_X / WIDTH); 31562306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_Y, 0, RES_Y, 1, 0); 31662306a36Sopenharmony_ci input_abs_set_res(input_dev, ABS_Y, RES_Y / HEIGHT); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci set_bit(BTN_TOUCH, input_dev->keybit); 31962306a36Sopenharmony_ci set_bit(BTN_TOOL_FINGER, input_dev->keybit); 32062306a36Sopenharmony_ci set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci set_bit(INPUT_PROP_POINTER, input_dev->propbit); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci udraw->touch_input_dev = input_dev; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci return true; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic bool udraw_setup_pen(struct udraw *udraw, 33062306a36Sopenharmony_ci struct hid_device *hdev) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci struct input_dev *input_dev; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci input_dev = allocate_and_setup(hdev, DEVICE_NAME " Pen"); 33562306a36Sopenharmony_ci if (!input_dev) 33662306a36Sopenharmony_ci return false; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_X, 0, RES_X, 1, 0); 34162306a36Sopenharmony_ci input_abs_set_res(input_dev, ABS_X, RES_X / WIDTH); 34262306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_Y, 0, RES_Y, 1, 0); 34362306a36Sopenharmony_ci input_abs_set_res(input_dev, ABS_Y, RES_Y / HEIGHT); 34462306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_PRESSURE, 34562306a36Sopenharmony_ci 0, MAX_PRESSURE, 0, 0); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci set_bit(BTN_TOUCH, input_dev->keybit); 34862306a36Sopenharmony_ci set_bit(BTN_TOOL_PEN, input_dev->keybit); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci set_bit(INPUT_PROP_POINTER, input_dev->propbit); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci udraw->pen_input_dev = input_dev; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci return true; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic bool udraw_setup_accel(struct udraw *udraw, 35862306a36Sopenharmony_ci struct hid_device *hdev) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct input_dev *input_dev; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci input_dev = allocate_and_setup(hdev, DEVICE_NAME " Accelerometer"); 36362306a36Sopenharmony_ci if (!input_dev) 36462306a36Sopenharmony_ci return false; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci input_dev->evbit[0] = BIT(EV_ABS); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci /* 1G accel is reported as ~256, so clamp to 2G */ 36962306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_X, -512, 512, 0, 0); 37062306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_Y, -512, 512, 0, 0); 37162306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_Z, -512, 512, 0, 0); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci set_bit(INPUT_PROP_ACCELEROMETER, input_dev->propbit); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci udraw->accel_input_dev = input_dev; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci return true; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic bool udraw_setup_joypad(struct udraw *udraw, 38162306a36Sopenharmony_ci struct hid_device *hdev) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci struct input_dev *input_dev; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci input_dev = allocate_and_setup(hdev, DEVICE_NAME " Joypad"); 38662306a36Sopenharmony_ci if (!input_dev) 38762306a36Sopenharmony_ci return false; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci set_bit(BTN_SOUTH, input_dev->keybit); 39262306a36Sopenharmony_ci set_bit(BTN_NORTH, input_dev->keybit); 39362306a36Sopenharmony_ci set_bit(BTN_EAST, input_dev->keybit); 39462306a36Sopenharmony_ci set_bit(BTN_WEST, input_dev->keybit); 39562306a36Sopenharmony_ci set_bit(BTN_SELECT, input_dev->keybit); 39662306a36Sopenharmony_ci set_bit(BTN_START, input_dev->keybit); 39762306a36Sopenharmony_ci set_bit(BTN_MODE, input_dev->keybit); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_X, -127, 127, 0, 0); 40062306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_Y, -127, 127, 0, 0); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci udraw->joy_input_dev = input_dev; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci return true; 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic int udraw_probe(struct hid_device *hdev, const struct hid_device_id *id) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci struct udraw *udraw; 41062306a36Sopenharmony_ci int ret; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci udraw = devm_kzalloc(&hdev->dev, sizeof(struct udraw), GFP_KERNEL); 41362306a36Sopenharmony_ci if (!udraw) 41462306a36Sopenharmony_ci return -ENOMEM; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci udraw->hdev = hdev; 41762306a36Sopenharmony_ci udraw->last_two_finger_x = -1; 41862306a36Sopenharmony_ci udraw->last_two_finger_y = -1; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci hid_set_drvdata(hdev, udraw); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci ret = hid_parse(hdev); 42362306a36Sopenharmony_ci if (ret) { 42462306a36Sopenharmony_ci hid_err(hdev, "parse failed\n"); 42562306a36Sopenharmony_ci return ret; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (!udraw_setup_joypad(udraw, hdev) || 42962306a36Sopenharmony_ci !udraw_setup_touch(udraw, hdev) || 43062306a36Sopenharmony_ci !udraw_setup_pen(udraw, hdev) || 43162306a36Sopenharmony_ci !udraw_setup_accel(udraw, hdev)) { 43262306a36Sopenharmony_ci hid_err(hdev, "could not allocate interfaces\n"); 43362306a36Sopenharmony_ci return -ENOMEM; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci ret = input_register_device(udraw->joy_input_dev) || 43762306a36Sopenharmony_ci input_register_device(udraw->touch_input_dev) || 43862306a36Sopenharmony_ci input_register_device(udraw->pen_input_dev) || 43962306a36Sopenharmony_ci input_register_device(udraw->accel_input_dev); 44062306a36Sopenharmony_ci if (ret) { 44162306a36Sopenharmony_ci hid_err(hdev, "failed to register interfaces\n"); 44262306a36Sopenharmony_ci return ret; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW | HID_CONNECT_DRIVER); 44662306a36Sopenharmony_ci if (ret) { 44762306a36Sopenharmony_ci hid_err(hdev, "hw start failed\n"); 44862306a36Sopenharmony_ci return ret; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci return 0; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic const struct hid_device_id udraw_devices[] = { 45562306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_THQ, USB_DEVICE_ID_THQ_PS3_UDRAW) }, 45662306a36Sopenharmony_ci { } 45762306a36Sopenharmony_ci}; 45862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(hid, udraw_devices); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic struct hid_driver udraw_driver = { 46162306a36Sopenharmony_ci .name = "hid-udraw", 46262306a36Sopenharmony_ci .id_table = udraw_devices, 46362306a36Sopenharmony_ci .raw_event = udraw_raw_event, 46462306a36Sopenharmony_ci .probe = udraw_probe, 46562306a36Sopenharmony_ci}; 46662306a36Sopenharmony_cimodule_hid_driver(udraw_driver); 467