brintos

brintos / linux-shallow public Read only

0
0
Text · 11.3 KiB · b2e17ef Raw
467 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * HID driver for THQ PS3 uDraw tablet4 *5 * Copyright (C) 2016 Red Hat Inc. All Rights Reserved6 */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-hid22 */23 24/*25 * The device is setup with multiple input devices:26 * - the touch area which works as a touchpad27 * - the tablet area which works as a touchpad/drawing tablet28 * - a joypad with a d-pad, and 7 buttons29 * - an accelerometer device30 */31 32enum {33	TOUCH_NONE,34	TOUCH_PEN,35	TOUCH_FINGER,36	TOUCH_TWOFINGER37};38 39enum {40	AXIS_X,41	AXIS_Y,42	AXIS_Z43};44 45/*46 * Accelerometer min/max values47 * in order, X, Y and Z48 */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 192061#define RES_Y 108062/* size in mm */63#define WIDTH  16064#define HEIGHT 9065#define PRESSURE_OFFSET 11366#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, as77	 * the device could report a single touch when the two fingers78	 * are too close together, and the distance between fingers, even79	 * 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 touch82	 * 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	else118		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 the187		 * second finger but we want the first finger given188		 * to user-space otherwise it'll look as if it jumped.189		 *190		 * See the udraw struct definition for why this was191		 * 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 the203			 * saved data from the first finger204			 */205			x = x - (udraw->last_two_finger_x206				- udraw->last_one_finger_x);207			y = y - (udraw->last_two_finger_y208				- 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