18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * HID driver for THQ PS3 uDraw tablet 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2016 Red Hat Inc. All Rights Reserved 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/device.h> 98c2ecf20Sopenharmony_ci#include <linux/hid.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include "hid-ids.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ciMODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>"); 148c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PS3 uDraw tablet driver"); 158c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* 188c2ecf20Sopenharmony_ci * Protocol information from: 198c2ecf20Sopenharmony_ci * https://brandonw.net/udraw/ 208c2ecf20Sopenharmony_ci * and the source code of: 218c2ecf20Sopenharmony_ci * https://vvvv.org/contribution/udraw-hid 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* 258c2ecf20Sopenharmony_ci * The device is setup with multiple input devices: 268c2ecf20Sopenharmony_ci * - the touch area which works as a touchpad 278c2ecf20Sopenharmony_ci * - the tablet area which works as a touchpad/drawing tablet 288c2ecf20Sopenharmony_ci * - a joypad with a d-pad, and 7 buttons 298c2ecf20Sopenharmony_ci * - an accelerometer device 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cienum { 338c2ecf20Sopenharmony_ci TOUCH_NONE, 348c2ecf20Sopenharmony_ci TOUCH_PEN, 358c2ecf20Sopenharmony_ci TOUCH_FINGER, 368c2ecf20Sopenharmony_ci TOUCH_TWOFINGER 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cienum { 408c2ecf20Sopenharmony_ci AXIS_X, 418c2ecf20Sopenharmony_ci AXIS_Y, 428c2ecf20Sopenharmony_ci AXIS_Z 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* 468c2ecf20Sopenharmony_ci * Accelerometer min/max values 478c2ecf20Sopenharmony_ci * in order, X, Y and Z 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_cistatic struct { 508c2ecf20Sopenharmony_ci int min; 518c2ecf20Sopenharmony_ci int max; 528c2ecf20Sopenharmony_ci} accel_limits[] = { 538c2ecf20Sopenharmony_ci [AXIS_X] = { 490, 534 }, 548c2ecf20Sopenharmony_ci [AXIS_Y] = { 490, 534 }, 558c2ecf20Sopenharmony_ci [AXIS_Z] = { 492, 536 } 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define DEVICE_NAME "THQ uDraw Game Tablet for PS3" 598c2ecf20Sopenharmony_ci/* resolution in pixels */ 608c2ecf20Sopenharmony_ci#define RES_X 1920 618c2ecf20Sopenharmony_ci#define RES_Y 1080 628c2ecf20Sopenharmony_ci/* size in mm */ 638c2ecf20Sopenharmony_ci#define WIDTH 160 648c2ecf20Sopenharmony_ci#define HEIGHT 90 658c2ecf20Sopenharmony_ci#define PRESSURE_OFFSET 113 668c2ecf20Sopenharmony_ci#define MAX_PRESSURE (255 - PRESSURE_OFFSET) 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistruct udraw { 698c2ecf20Sopenharmony_ci struct input_dev *joy_input_dev; 708c2ecf20Sopenharmony_ci struct input_dev *touch_input_dev; 718c2ecf20Sopenharmony_ci struct input_dev *pen_input_dev; 728c2ecf20Sopenharmony_ci struct input_dev *accel_input_dev; 738c2ecf20Sopenharmony_ci struct hid_device *hdev; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci /* 768c2ecf20Sopenharmony_ci * The device's two-finger support is pretty unreliable, as 778c2ecf20Sopenharmony_ci * the device could report a single touch when the two fingers 788c2ecf20Sopenharmony_ci * are too close together, and the distance between fingers, even 798c2ecf20Sopenharmony_ci * though reported is not in the same unit as the touches. 808c2ecf20Sopenharmony_ci * 818c2ecf20Sopenharmony_ci * We'll make do without it, and try to report the first touch 828c2ecf20Sopenharmony_ci * as reliably as possible. 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_ci int last_one_finger_x; 858c2ecf20Sopenharmony_ci int last_one_finger_y; 868c2ecf20Sopenharmony_ci int last_two_finger_x; 878c2ecf20Sopenharmony_ci int last_two_finger_y; 888c2ecf20Sopenharmony_ci}; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic int clamp_accel(int axis, int offset) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci axis = clamp(axis, 938c2ecf20Sopenharmony_ci accel_limits[offset].min, 948c2ecf20Sopenharmony_ci accel_limits[offset].max); 958c2ecf20Sopenharmony_ci axis = (axis - accel_limits[offset].min) / 968c2ecf20Sopenharmony_ci ((accel_limits[offset].max - 978c2ecf20Sopenharmony_ci accel_limits[offset].min) * 0xFF); 988c2ecf20Sopenharmony_ci return axis; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int udraw_raw_event(struct hid_device *hdev, struct hid_report *report, 1028c2ecf20Sopenharmony_ci u8 *data, int len) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct udraw *udraw = hid_get_drvdata(hdev); 1058c2ecf20Sopenharmony_ci int touch; 1068c2ecf20Sopenharmony_ci int x, y, z; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (len != 27) 1098c2ecf20Sopenharmony_ci return 0; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (data[11] == 0x00) 1128c2ecf20Sopenharmony_ci touch = TOUCH_NONE; 1138c2ecf20Sopenharmony_ci else if (data[11] == 0x40) 1148c2ecf20Sopenharmony_ci touch = TOUCH_PEN; 1158c2ecf20Sopenharmony_ci else if (data[11] == 0x80) 1168c2ecf20Sopenharmony_ci touch = TOUCH_FINGER; 1178c2ecf20Sopenharmony_ci else 1188c2ecf20Sopenharmony_ci touch = TOUCH_TWOFINGER; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* joypad */ 1218c2ecf20Sopenharmony_ci input_report_key(udraw->joy_input_dev, BTN_WEST, data[0] & 1); 1228c2ecf20Sopenharmony_ci input_report_key(udraw->joy_input_dev, BTN_SOUTH, !!(data[0] & 2)); 1238c2ecf20Sopenharmony_ci input_report_key(udraw->joy_input_dev, BTN_EAST, !!(data[0] & 4)); 1248c2ecf20Sopenharmony_ci input_report_key(udraw->joy_input_dev, BTN_NORTH, !!(data[0] & 8)); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci input_report_key(udraw->joy_input_dev, BTN_SELECT, !!(data[1] & 1)); 1278c2ecf20Sopenharmony_ci input_report_key(udraw->joy_input_dev, BTN_START, !!(data[1] & 2)); 1288c2ecf20Sopenharmony_ci input_report_key(udraw->joy_input_dev, BTN_MODE, !!(data[1] & 16)); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci x = y = 0; 1318c2ecf20Sopenharmony_ci switch (data[2]) { 1328c2ecf20Sopenharmony_ci case 0x0: 1338c2ecf20Sopenharmony_ci y = -127; 1348c2ecf20Sopenharmony_ci break; 1358c2ecf20Sopenharmony_ci case 0x1: 1368c2ecf20Sopenharmony_ci y = -127; 1378c2ecf20Sopenharmony_ci x = 127; 1388c2ecf20Sopenharmony_ci break; 1398c2ecf20Sopenharmony_ci case 0x2: 1408c2ecf20Sopenharmony_ci x = 127; 1418c2ecf20Sopenharmony_ci break; 1428c2ecf20Sopenharmony_ci case 0x3: 1438c2ecf20Sopenharmony_ci y = 127; 1448c2ecf20Sopenharmony_ci x = 127; 1458c2ecf20Sopenharmony_ci break; 1468c2ecf20Sopenharmony_ci case 0x4: 1478c2ecf20Sopenharmony_ci y = 127; 1488c2ecf20Sopenharmony_ci break; 1498c2ecf20Sopenharmony_ci case 0x5: 1508c2ecf20Sopenharmony_ci y = 127; 1518c2ecf20Sopenharmony_ci x = -127; 1528c2ecf20Sopenharmony_ci break; 1538c2ecf20Sopenharmony_ci case 0x6: 1548c2ecf20Sopenharmony_ci x = -127; 1558c2ecf20Sopenharmony_ci break; 1568c2ecf20Sopenharmony_ci case 0x7: 1578c2ecf20Sopenharmony_ci y = -127; 1588c2ecf20Sopenharmony_ci x = -127; 1598c2ecf20Sopenharmony_ci break; 1608c2ecf20Sopenharmony_ci default: 1618c2ecf20Sopenharmony_ci break; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci input_report_abs(udraw->joy_input_dev, ABS_X, x); 1658c2ecf20Sopenharmony_ci input_report_abs(udraw->joy_input_dev, ABS_Y, y); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci input_sync(udraw->joy_input_dev); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* For pen and touchpad */ 1708c2ecf20Sopenharmony_ci x = y = 0; 1718c2ecf20Sopenharmony_ci if (touch != TOUCH_NONE) { 1728c2ecf20Sopenharmony_ci if (data[15] != 0x0F) 1738c2ecf20Sopenharmony_ci x = data[15] * 256 + data[17]; 1748c2ecf20Sopenharmony_ci if (data[16] != 0x0F) 1758c2ecf20Sopenharmony_ci y = data[16] * 256 + data[18]; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (touch == TOUCH_FINGER) { 1798c2ecf20Sopenharmony_ci /* Save the last one-finger touch */ 1808c2ecf20Sopenharmony_ci udraw->last_one_finger_x = x; 1818c2ecf20Sopenharmony_ci udraw->last_one_finger_y = y; 1828c2ecf20Sopenharmony_ci udraw->last_two_finger_x = -1; 1838c2ecf20Sopenharmony_ci udraw->last_two_finger_y = -1; 1848c2ecf20Sopenharmony_ci } else if (touch == TOUCH_TWOFINGER) { 1858c2ecf20Sopenharmony_ci /* 1868c2ecf20Sopenharmony_ci * We have a problem because x/y is the one for the 1878c2ecf20Sopenharmony_ci * second finger but we want the first finger given 1888c2ecf20Sopenharmony_ci * to user-space otherwise it'll look as if it jumped. 1898c2ecf20Sopenharmony_ci * 1908c2ecf20Sopenharmony_ci * See the udraw struct definition for why this was 1918c2ecf20Sopenharmony_ci * implemented this way. 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_ci if (udraw->last_two_finger_x == -1) { 1948c2ecf20Sopenharmony_ci /* Save the position of the 2nd finger */ 1958c2ecf20Sopenharmony_ci udraw->last_two_finger_x = x; 1968c2ecf20Sopenharmony_ci udraw->last_two_finger_y = y; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci x = udraw->last_one_finger_x; 1998c2ecf20Sopenharmony_ci y = udraw->last_one_finger_y; 2008c2ecf20Sopenharmony_ci } else { 2018c2ecf20Sopenharmony_ci /* 2028c2ecf20Sopenharmony_ci * Offset the 2-finger coords using the 2038c2ecf20Sopenharmony_ci * saved data from the first finger 2048c2ecf20Sopenharmony_ci */ 2058c2ecf20Sopenharmony_ci x = x - (udraw->last_two_finger_x 2068c2ecf20Sopenharmony_ci - udraw->last_one_finger_x); 2078c2ecf20Sopenharmony_ci y = y - (udraw->last_two_finger_y 2088c2ecf20Sopenharmony_ci - udraw->last_one_finger_y); 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* touchpad */ 2138c2ecf20Sopenharmony_ci if (touch == TOUCH_FINGER || touch == TOUCH_TWOFINGER) { 2148c2ecf20Sopenharmony_ci input_report_key(udraw->touch_input_dev, BTN_TOUCH, 1); 2158c2ecf20Sopenharmony_ci input_report_key(udraw->touch_input_dev, BTN_TOOL_FINGER, 2168c2ecf20Sopenharmony_ci touch == TOUCH_FINGER); 2178c2ecf20Sopenharmony_ci input_report_key(udraw->touch_input_dev, BTN_TOOL_DOUBLETAP, 2188c2ecf20Sopenharmony_ci touch == TOUCH_TWOFINGER); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci input_report_abs(udraw->touch_input_dev, ABS_X, x); 2218c2ecf20Sopenharmony_ci input_report_abs(udraw->touch_input_dev, ABS_Y, y); 2228c2ecf20Sopenharmony_ci } else { 2238c2ecf20Sopenharmony_ci input_report_key(udraw->touch_input_dev, BTN_TOUCH, 0); 2248c2ecf20Sopenharmony_ci input_report_key(udraw->touch_input_dev, BTN_TOOL_FINGER, 0); 2258c2ecf20Sopenharmony_ci input_report_key(udraw->touch_input_dev, BTN_TOOL_DOUBLETAP, 0); 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci input_sync(udraw->touch_input_dev); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci /* pen */ 2308c2ecf20Sopenharmony_ci if (touch == TOUCH_PEN) { 2318c2ecf20Sopenharmony_ci int level; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci level = clamp(data[13] - PRESSURE_OFFSET, 2348c2ecf20Sopenharmony_ci 0, MAX_PRESSURE); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci input_report_key(udraw->pen_input_dev, BTN_TOUCH, (level != 0)); 2378c2ecf20Sopenharmony_ci input_report_key(udraw->pen_input_dev, BTN_TOOL_PEN, 1); 2388c2ecf20Sopenharmony_ci input_report_abs(udraw->pen_input_dev, ABS_PRESSURE, level); 2398c2ecf20Sopenharmony_ci input_report_abs(udraw->pen_input_dev, ABS_X, x); 2408c2ecf20Sopenharmony_ci input_report_abs(udraw->pen_input_dev, ABS_Y, y); 2418c2ecf20Sopenharmony_ci } else { 2428c2ecf20Sopenharmony_ci input_report_key(udraw->pen_input_dev, BTN_TOUCH, 0); 2438c2ecf20Sopenharmony_ci input_report_key(udraw->pen_input_dev, BTN_TOOL_PEN, 0); 2448c2ecf20Sopenharmony_ci input_report_abs(udraw->pen_input_dev, ABS_PRESSURE, 0); 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci input_sync(udraw->pen_input_dev); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* accel */ 2498c2ecf20Sopenharmony_ci x = (data[19] + (data[20] << 8)); 2508c2ecf20Sopenharmony_ci x = clamp_accel(x, AXIS_X); 2518c2ecf20Sopenharmony_ci y = (data[21] + (data[22] << 8)); 2528c2ecf20Sopenharmony_ci y = clamp_accel(y, AXIS_Y); 2538c2ecf20Sopenharmony_ci z = (data[23] + (data[24] << 8)); 2548c2ecf20Sopenharmony_ci z = clamp_accel(z, AXIS_Z); 2558c2ecf20Sopenharmony_ci input_report_abs(udraw->accel_input_dev, ABS_X, x); 2568c2ecf20Sopenharmony_ci input_report_abs(udraw->accel_input_dev, ABS_Y, y); 2578c2ecf20Sopenharmony_ci input_report_abs(udraw->accel_input_dev, ABS_Z, z); 2588c2ecf20Sopenharmony_ci input_sync(udraw->accel_input_dev); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* let hidraw and hiddev handle the report */ 2618c2ecf20Sopenharmony_ci return 0; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic int udraw_open(struct input_dev *dev) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci struct udraw *udraw = input_get_drvdata(dev); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return hid_hw_open(udraw->hdev); 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic void udraw_close(struct input_dev *dev) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct udraw *udraw = input_get_drvdata(dev); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci hid_hw_close(udraw->hdev); 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic struct input_dev *allocate_and_setup(struct hid_device *hdev, 2798c2ecf20Sopenharmony_ci const char *name) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci struct input_dev *input_dev; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci input_dev = devm_input_allocate_device(&hdev->dev); 2848c2ecf20Sopenharmony_ci if (!input_dev) 2858c2ecf20Sopenharmony_ci return NULL; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci input_dev->name = name; 2888c2ecf20Sopenharmony_ci input_dev->phys = hdev->phys; 2898c2ecf20Sopenharmony_ci input_dev->dev.parent = &hdev->dev; 2908c2ecf20Sopenharmony_ci input_dev->open = udraw_open; 2918c2ecf20Sopenharmony_ci input_dev->close = udraw_close; 2928c2ecf20Sopenharmony_ci input_dev->uniq = hdev->uniq; 2938c2ecf20Sopenharmony_ci input_dev->id.bustype = hdev->bus; 2948c2ecf20Sopenharmony_ci input_dev->id.vendor = hdev->vendor; 2958c2ecf20Sopenharmony_ci input_dev->id.product = hdev->product; 2968c2ecf20Sopenharmony_ci input_dev->id.version = hdev->version; 2978c2ecf20Sopenharmony_ci input_set_drvdata(input_dev, hid_get_drvdata(hdev)); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci return input_dev; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic bool udraw_setup_touch(struct udraw *udraw, 3038c2ecf20Sopenharmony_ci struct hid_device *hdev) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct input_dev *input_dev; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci input_dev = allocate_and_setup(hdev, DEVICE_NAME " Touchpad"); 3088c2ecf20Sopenharmony_ci if (!input_dev) 3098c2ecf20Sopenharmony_ci return false; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, ABS_X, 0, RES_X, 1, 0); 3148c2ecf20Sopenharmony_ci input_abs_set_res(input_dev, ABS_X, RES_X / WIDTH); 3158c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, ABS_Y, 0, RES_Y, 1, 0); 3168c2ecf20Sopenharmony_ci input_abs_set_res(input_dev, ABS_Y, RES_Y / HEIGHT); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci set_bit(BTN_TOUCH, input_dev->keybit); 3198c2ecf20Sopenharmony_ci set_bit(BTN_TOOL_FINGER, input_dev->keybit); 3208c2ecf20Sopenharmony_ci set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci set_bit(INPUT_PROP_POINTER, input_dev->propbit); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci udraw->touch_input_dev = input_dev; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci return true; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic bool udraw_setup_pen(struct udraw *udraw, 3308c2ecf20Sopenharmony_ci struct hid_device *hdev) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci struct input_dev *input_dev; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci input_dev = allocate_and_setup(hdev, DEVICE_NAME " Pen"); 3358c2ecf20Sopenharmony_ci if (!input_dev) 3368c2ecf20Sopenharmony_ci return false; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, ABS_X, 0, RES_X, 1, 0); 3418c2ecf20Sopenharmony_ci input_abs_set_res(input_dev, ABS_X, RES_X / WIDTH); 3428c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, ABS_Y, 0, RES_Y, 1, 0); 3438c2ecf20Sopenharmony_ci input_abs_set_res(input_dev, ABS_Y, RES_Y / HEIGHT); 3448c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, ABS_PRESSURE, 3458c2ecf20Sopenharmony_ci 0, MAX_PRESSURE, 0, 0); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci set_bit(BTN_TOUCH, input_dev->keybit); 3488c2ecf20Sopenharmony_ci set_bit(BTN_TOOL_PEN, input_dev->keybit); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci set_bit(INPUT_PROP_POINTER, input_dev->propbit); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci udraw->pen_input_dev = input_dev; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci return true; 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic bool udraw_setup_accel(struct udraw *udraw, 3588c2ecf20Sopenharmony_ci struct hid_device *hdev) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci struct input_dev *input_dev; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci input_dev = allocate_and_setup(hdev, DEVICE_NAME " Accelerometer"); 3638c2ecf20Sopenharmony_ci if (!input_dev) 3648c2ecf20Sopenharmony_ci return false; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci input_dev->evbit[0] = BIT(EV_ABS); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci /* 1G accel is reported as ~256, so clamp to 2G */ 3698c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, ABS_X, -512, 512, 0, 0); 3708c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, ABS_Y, -512, 512, 0, 0); 3718c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, ABS_Z, -512, 512, 0, 0); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci set_bit(INPUT_PROP_ACCELEROMETER, input_dev->propbit); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci udraw->accel_input_dev = input_dev; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci return true; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic bool udraw_setup_joypad(struct udraw *udraw, 3818c2ecf20Sopenharmony_ci struct hid_device *hdev) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci struct input_dev *input_dev; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci input_dev = allocate_and_setup(hdev, DEVICE_NAME " Joypad"); 3868c2ecf20Sopenharmony_ci if (!input_dev) 3878c2ecf20Sopenharmony_ci return false; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci set_bit(BTN_SOUTH, input_dev->keybit); 3928c2ecf20Sopenharmony_ci set_bit(BTN_NORTH, input_dev->keybit); 3938c2ecf20Sopenharmony_ci set_bit(BTN_EAST, input_dev->keybit); 3948c2ecf20Sopenharmony_ci set_bit(BTN_WEST, input_dev->keybit); 3958c2ecf20Sopenharmony_ci set_bit(BTN_SELECT, input_dev->keybit); 3968c2ecf20Sopenharmony_ci set_bit(BTN_START, input_dev->keybit); 3978c2ecf20Sopenharmony_ci set_bit(BTN_MODE, input_dev->keybit); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, ABS_X, -127, 127, 0, 0); 4008c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, ABS_Y, -127, 127, 0, 0); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci udraw->joy_input_dev = input_dev; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci return true; 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic int udraw_probe(struct hid_device *hdev, const struct hid_device_id *id) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci struct udraw *udraw; 4108c2ecf20Sopenharmony_ci int ret; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci udraw = devm_kzalloc(&hdev->dev, sizeof(struct udraw), GFP_KERNEL); 4138c2ecf20Sopenharmony_ci if (!udraw) 4148c2ecf20Sopenharmony_ci return -ENOMEM; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci udraw->hdev = hdev; 4178c2ecf20Sopenharmony_ci udraw->last_two_finger_x = -1; 4188c2ecf20Sopenharmony_ci udraw->last_two_finger_y = -1; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci hid_set_drvdata(hdev, udraw); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci ret = hid_parse(hdev); 4238c2ecf20Sopenharmony_ci if (ret) { 4248c2ecf20Sopenharmony_ci hid_err(hdev, "parse failed\n"); 4258c2ecf20Sopenharmony_ci return ret; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (!udraw_setup_joypad(udraw, hdev) || 4298c2ecf20Sopenharmony_ci !udraw_setup_touch(udraw, hdev) || 4308c2ecf20Sopenharmony_ci !udraw_setup_pen(udraw, hdev) || 4318c2ecf20Sopenharmony_ci !udraw_setup_accel(udraw, hdev)) { 4328c2ecf20Sopenharmony_ci hid_err(hdev, "could not allocate interfaces\n"); 4338c2ecf20Sopenharmony_ci return -ENOMEM; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci ret = input_register_device(udraw->joy_input_dev) || 4378c2ecf20Sopenharmony_ci input_register_device(udraw->touch_input_dev) || 4388c2ecf20Sopenharmony_ci input_register_device(udraw->pen_input_dev) || 4398c2ecf20Sopenharmony_ci input_register_device(udraw->accel_input_dev); 4408c2ecf20Sopenharmony_ci if (ret) { 4418c2ecf20Sopenharmony_ci hid_err(hdev, "failed to register interfaces\n"); 4428c2ecf20Sopenharmony_ci return ret; 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW | HID_CONNECT_DRIVER); 4468c2ecf20Sopenharmony_ci if (ret) { 4478c2ecf20Sopenharmony_ci hid_err(hdev, "hw start failed\n"); 4488c2ecf20Sopenharmony_ci return ret; 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci return 0; 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cistatic const struct hid_device_id udraw_devices[] = { 4558c2ecf20Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_THQ, USB_DEVICE_ID_THQ_PS3_UDRAW) }, 4568c2ecf20Sopenharmony_ci { } 4578c2ecf20Sopenharmony_ci}; 4588c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(hid, udraw_devices); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic struct hid_driver udraw_driver = { 4618c2ecf20Sopenharmony_ci .name = "hid-udraw", 4628c2ecf20Sopenharmony_ci .id_table = udraw_devices, 4638c2ecf20Sopenharmony_ci .raw_event = udraw_raw_event, 4648c2ecf20Sopenharmony_ci .probe = udraw_probe, 4658c2ecf20Sopenharmony_ci}; 4668c2ecf20Sopenharmony_cimodule_hid_driver(udraw_driver); 467