18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Wacom W8001 penabled serial touchscreen driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2008 Jaya Kumar 58c2ecf20Sopenharmony_ci * Copyright (c) 2010 Red Hat, Inc. 68c2ecf20Sopenharmony_ci * Copyright (c) 2010 - 2011 Ping Cheng, Wacom. <pingc@wacom.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 98c2ecf20Sopenharmony_ci * License. See the file COPYING in the main directory of this archive for 108c2ecf20Sopenharmony_ci * more details. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Layout based on Elo serial touchscreen driver by Vojtech Pavlik 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/errno.h> 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/input/mt.h> 208c2ecf20Sopenharmony_ci#include <linux/serio.h> 218c2ecf20Sopenharmony_ci#include <linux/ctype.h> 228c2ecf20Sopenharmony_ci#include <linux/delay.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define DRIVER_DESC "Wacom W8001 serial touchscreen driver" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaya Kumar <jayakumar.lkml@gmail.com>"); 278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define W8001_MAX_PHYS 42 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define W8001_MAX_LENGTH 13 338c2ecf20Sopenharmony_ci#define W8001_LEAD_MASK 0x80 348c2ecf20Sopenharmony_ci#define W8001_LEAD_BYTE 0x80 358c2ecf20Sopenharmony_ci#define W8001_TAB_MASK 0x40 368c2ecf20Sopenharmony_ci#define W8001_TAB_BYTE 0x40 378c2ecf20Sopenharmony_ci/* set in first byte of touch data packets */ 388c2ecf20Sopenharmony_ci#define W8001_TOUCH_MASK (0x10 | W8001_LEAD_MASK) 398c2ecf20Sopenharmony_ci#define W8001_TOUCH_BYTE (0x10 | W8001_LEAD_BYTE) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define W8001_QUERY_PACKET 0x20 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define W8001_CMD_STOP '0' 448c2ecf20Sopenharmony_ci#define W8001_CMD_START '1' 458c2ecf20Sopenharmony_ci#define W8001_CMD_QUERY '*' 468c2ecf20Sopenharmony_ci#define W8001_CMD_TOUCHQUERY '%' 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* length of data packets in bytes, depends on device. */ 498c2ecf20Sopenharmony_ci#define W8001_PKTLEN_TOUCH93 5 508c2ecf20Sopenharmony_ci#define W8001_PKTLEN_TOUCH9A 7 518c2ecf20Sopenharmony_ci#define W8001_PKTLEN_TPCPEN 9 528c2ecf20Sopenharmony_ci#define W8001_PKTLEN_TPCCTL 11 /* control packet */ 538c2ecf20Sopenharmony_ci#define W8001_PKTLEN_TOUCH2FG 13 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* resolution in points/mm */ 568c2ecf20Sopenharmony_ci#define W8001_PEN_RESOLUTION 100 578c2ecf20Sopenharmony_ci#define W8001_TOUCH_RESOLUTION 10 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistruct w8001_coord { 608c2ecf20Sopenharmony_ci u8 rdy; 618c2ecf20Sopenharmony_ci u8 tsw; 628c2ecf20Sopenharmony_ci u8 f1; 638c2ecf20Sopenharmony_ci u8 f2; 648c2ecf20Sopenharmony_ci u16 x; 658c2ecf20Sopenharmony_ci u16 y; 668c2ecf20Sopenharmony_ci u16 pen_pressure; 678c2ecf20Sopenharmony_ci u8 tilt_x; 688c2ecf20Sopenharmony_ci u8 tilt_y; 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* touch query reply packet */ 728c2ecf20Sopenharmony_cistruct w8001_touch_query { 738c2ecf20Sopenharmony_ci u16 x; 748c2ecf20Sopenharmony_ci u16 y; 758c2ecf20Sopenharmony_ci u8 panel_res; 768c2ecf20Sopenharmony_ci u8 capacity_res; 778c2ecf20Sopenharmony_ci u8 sensor_id; 788c2ecf20Sopenharmony_ci}; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* 818c2ecf20Sopenharmony_ci * Per-touchscreen data. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistruct w8001 { 858c2ecf20Sopenharmony_ci struct input_dev *pen_dev; 868c2ecf20Sopenharmony_ci struct input_dev *touch_dev; 878c2ecf20Sopenharmony_ci struct serio *serio; 888c2ecf20Sopenharmony_ci struct completion cmd_done; 898c2ecf20Sopenharmony_ci int id; 908c2ecf20Sopenharmony_ci int idx; 918c2ecf20Sopenharmony_ci unsigned char response_type; 928c2ecf20Sopenharmony_ci unsigned char response[W8001_MAX_LENGTH]; 938c2ecf20Sopenharmony_ci unsigned char data[W8001_MAX_LENGTH]; 948c2ecf20Sopenharmony_ci char phys[W8001_MAX_PHYS]; 958c2ecf20Sopenharmony_ci int type; 968c2ecf20Sopenharmony_ci unsigned int pktlen; 978c2ecf20Sopenharmony_ci u16 max_touch_x; 988c2ecf20Sopenharmony_ci u16 max_touch_y; 998c2ecf20Sopenharmony_ci u16 max_pen_x; 1008c2ecf20Sopenharmony_ci u16 max_pen_y; 1018c2ecf20Sopenharmony_ci char pen_name[64]; 1028c2ecf20Sopenharmony_ci char touch_name[64]; 1038c2ecf20Sopenharmony_ci int open_count; 1048c2ecf20Sopenharmony_ci struct mutex mutex; 1058c2ecf20Sopenharmony_ci}; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic void parse_pen_data(u8 *data, struct w8001_coord *coord) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci memset(coord, 0, sizeof(*coord)); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci coord->rdy = data[0] & 0x20; 1128c2ecf20Sopenharmony_ci coord->tsw = data[0] & 0x01; 1138c2ecf20Sopenharmony_ci coord->f1 = data[0] & 0x02; 1148c2ecf20Sopenharmony_ci coord->f2 = data[0] & 0x04; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci coord->x = (data[1] & 0x7F) << 9; 1178c2ecf20Sopenharmony_ci coord->x |= (data[2] & 0x7F) << 2; 1188c2ecf20Sopenharmony_ci coord->x |= (data[6] & 0x60) >> 5; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci coord->y = (data[3] & 0x7F) << 9; 1218c2ecf20Sopenharmony_ci coord->y |= (data[4] & 0x7F) << 2; 1228c2ecf20Sopenharmony_ci coord->y |= (data[6] & 0x18) >> 3; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci coord->pen_pressure = data[5] & 0x7F; 1258c2ecf20Sopenharmony_ci coord->pen_pressure |= (data[6] & 0x07) << 7 ; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci coord->tilt_x = data[7] & 0x7F; 1288c2ecf20Sopenharmony_ci coord->tilt_y = data[8] & 0x7F; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic void parse_single_touch(u8 *data, struct w8001_coord *coord) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci coord->x = (data[1] << 7) | data[2]; 1348c2ecf20Sopenharmony_ci coord->y = (data[3] << 7) | data[4]; 1358c2ecf20Sopenharmony_ci coord->tsw = data[0] & 0x01; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic void scale_touch_coordinates(struct w8001 *w8001, 1398c2ecf20Sopenharmony_ci unsigned int *x, unsigned int *y) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci if (w8001->max_pen_x && w8001->max_touch_x) 1428c2ecf20Sopenharmony_ci *x = *x * w8001->max_pen_x / w8001->max_touch_x; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (w8001->max_pen_y && w8001->max_touch_y) 1458c2ecf20Sopenharmony_ci *y = *y * w8001->max_pen_y / w8001->max_touch_y; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic void parse_multi_touch(struct w8001 *w8001) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct input_dev *dev = w8001->touch_dev; 1518c2ecf20Sopenharmony_ci unsigned char *data = w8001->data; 1528c2ecf20Sopenharmony_ci unsigned int x, y; 1538c2ecf20Sopenharmony_ci int i; 1548c2ecf20Sopenharmony_ci int count = 0; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 1578c2ecf20Sopenharmony_ci bool touch = data[0] & (1 << i); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci input_mt_slot(dev, i); 1608c2ecf20Sopenharmony_ci input_mt_report_slot_state(dev, MT_TOOL_FINGER, touch); 1618c2ecf20Sopenharmony_ci if (touch) { 1628c2ecf20Sopenharmony_ci x = (data[6 * i + 1] << 7) | data[6 * i + 2]; 1638c2ecf20Sopenharmony_ci y = (data[6 * i + 3] << 7) | data[6 * i + 4]; 1648c2ecf20Sopenharmony_ci /* data[5,6] and [11,12] is finger capacity */ 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci /* scale to pen maximum */ 1678c2ecf20Sopenharmony_ci scale_touch_coordinates(w8001, &x, &y); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_MT_POSITION_X, x); 1708c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_MT_POSITION_Y, y); 1718c2ecf20Sopenharmony_ci count++; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* emulate single touch events when stylus is out of proximity. 1768c2ecf20Sopenharmony_ci * This is to make single touch backward support consistent 1778c2ecf20Sopenharmony_ci * across all Wacom single touch devices. 1788c2ecf20Sopenharmony_ci */ 1798c2ecf20Sopenharmony_ci if (w8001->type != BTN_TOOL_PEN && 1808c2ecf20Sopenharmony_ci w8001->type != BTN_TOOL_RUBBER) { 1818c2ecf20Sopenharmony_ci w8001->type = count == 1 ? BTN_TOOL_FINGER : KEY_RESERVED; 1828c2ecf20Sopenharmony_ci input_mt_report_pointer_emulation(dev, true); 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci input_sync(dev); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic void parse_touchquery(u8 *data, struct w8001_touch_query *query) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci memset(query, 0, sizeof(*query)); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci query->panel_res = data[1]; 1938c2ecf20Sopenharmony_ci query->sensor_id = data[2] & 0x7; 1948c2ecf20Sopenharmony_ci query->capacity_res = data[7]; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci query->x = data[3] << 9; 1978c2ecf20Sopenharmony_ci query->x |= data[4] << 2; 1988c2ecf20Sopenharmony_ci query->x |= (data[2] >> 5) & 0x3; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci query->y = data[5] << 9; 2018c2ecf20Sopenharmony_ci query->y |= data[6] << 2; 2028c2ecf20Sopenharmony_ci query->y |= (data[2] >> 3) & 0x3; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* Early days' single-finger touch models need the following defaults */ 2058c2ecf20Sopenharmony_ci if (!query->x && !query->y) { 2068c2ecf20Sopenharmony_ci query->x = 1024; 2078c2ecf20Sopenharmony_ci query->y = 1024; 2088c2ecf20Sopenharmony_ci if (query->panel_res) 2098c2ecf20Sopenharmony_ci query->x = query->y = (1 << query->panel_res); 2108c2ecf20Sopenharmony_ci query->panel_res = W8001_TOUCH_RESOLUTION; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci struct input_dev *dev = w8001->pen_dev; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* 2198c2ecf20Sopenharmony_ci * We have 1 bit for proximity (rdy) and 3 bits for tip, side, 2208c2ecf20Sopenharmony_ci * side2/eraser. If rdy && f2 are set, this can be either pen + side2, 2218c2ecf20Sopenharmony_ci * or eraser. Assume: 2228c2ecf20Sopenharmony_ci * - if dev is already in proximity and f2 is toggled → pen + side2 2238c2ecf20Sopenharmony_ci * - if dev comes into proximity with f2 set → eraser 2248c2ecf20Sopenharmony_ci * If f2 disappears after assuming eraser, fake proximity out for 2258c2ecf20Sopenharmony_ci * eraser and in for pen. 2268c2ecf20Sopenharmony_ci */ 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci switch (w8001->type) { 2298c2ecf20Sopenharmony_ci case BTN_TOOL_RUBBER: 2308c2ecf20Sopenharmony_ci if (!coord->f2) { 2318c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_PRESSURE, 0); 2328c2ecf20Sopenharmony_ci input_report_key(dev, BTN_TOUCH, 0); 2338c2ecf20Sopenharmony_ci input_report_key(dev, BTN_STYLUS, 0); 2348c2ecf20Sopenharmony_ci input_report_key(dev, BTN_STYLUS2, 0); 2358c2ecf20Sopenharmony_ci input_report_key(dev, BTN_TOOL_RUBBER, 0); 2368c2ecf20Sopenharmony_ci input_sync(dev); 2378c2ecf20Sopenharmony_ci w8001->type = BTN_TOOL_PEN; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci break; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci case BTN_TOOL_FINGER: 2428c2ecf20Sopenharmony_ci case KEY_RESERVED: 2438c2ecf20Sopenharmony_ci w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; 2448c2ecf20Sopenharmony_ci break; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci default: 2478c2ecf20Sopenharmony_ci input_report_key(dev, BTN_STYLUS2, coord->f2); 2488c2ecf20Sopenharmony_ci break; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_X, coord->x); 2528c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_Y, coord->y); 2538c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_PRESSURE, coord->pen_pressure); 2548c2ecf20Sopenharmony_ci input_report_key(dev, BTN_TOUCH, coord->tsw); 2558c2ecf20Sopenharmony_ci input_report_key(dev, BTN_STYLUS, coord->f1); 2568c2ecf20Sopenharmony_ci input_report_key(dev, w8001->type, coord->rdy); 2578c2ecf20Sopenharmony_ci input_sync(dev); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (!coord->rdy) 2608c2ecf20Sopenharmony_ci w8001->type = KEY_RESERVED; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci struct input_dev *dev = w8001->touch_dev; 2668c2ecf20Sopenharmony_ci unsigned int x = coord->x; 2678c2ecf20Sopenharmony_ci unsigned int y = coord->y; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* scale to pen maximum */ 2708c2ecf20Sopenharmony_ci scale_touch_coordinates(w8001, &x, &y); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_X, x); 2738c2ecf20Sopenharmony_ci input_report_abs(dev, ABS_Y, y); 2748c2ecf20Sopenharmony_ci input_report_key(dev, BTN_TOUCH, coord->tsw); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci input_sync(dev); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci w8001->type = coord->tsw ? BTN_TOOL_FINGER : KEY_RESERVED; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic irqreturn_t w8001_interrupt(struct serio *serio, 2828c2ecf20Sopenharmony_ci unsigned char data, unsigned int flags) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci struct w8001 *w8001 = serio_get_drvdata(serio); 2858c2ecf20Sopenharmony_ci struct w8001_coord coord; 2868c2ecf20Sopenharmony_ci unsigned char tmp; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci w8001->data[w8001->idx] = data; 2898c2ecf20Sopenharmony_ci switch (w8001->idx++) { 2908c2ecf20Sopenharmony_ci case 0: 2918c2ecf20Sopenharmony_ci if ((data & W8001_LEAD_MASK) != W8001_LEAD_BYTE) { 2928c2ecf20Sopenharmony_ci pr_debug("w8001: unsynchronized data: 0x%02x\n", data); 2938c2ecf20Sopenharmony_ci w8001->idx = 0; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci break; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci case W8001_PKTLEN_TOUCH93 - 1: 2988c2ecf20Sopenharmony_ci case W8001_PKTLEN_TOUCH9A - 1: 2998c2ecf20Sopenharmony_ci tmp = w8001->data[0] & W8001_TOUCH_BYTE; 3008c2ecf20Sopenharmony_ci if (tmp != W8001_TOUCH_BYTE) 3018c2ecf20Sopenharmony_ci break; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci if (w8001->pktlen == w8001->idx) { 3048c2ecf20Sopenharmony_ci w8001->idx = 0; 3058c2ecf20Sopenharmony_ci if (w8001->type != BTN_TOOL_PEN && 3068c2ecf20Sopenharmony_ci w8001->type != BTN_TOOL_RUBBER) { 3078c2ecf20Sopenharmony_ci parse_single_touch(w8001->data, &coord); 3088c2ecf20Sopenharmony_ci report_single_touch(w8001, &coord); 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci break; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* Pen coordinates packet */ 3148c2ecf20Sopenharmony_ci case W8001_PKTLEN_TPCPEN - 1: 3158c2ecf20Sopenharmony_ci tmp = w8001->data[0] & W8001_TAB_MASK; 3168c2ecf20Sopenharmony_ci if (unlikely(tmp == W8001_TAB_BYTE)) 3178c2ecf20Sopenharmony_ci break; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci tmp = w8001->data[0] & W8001_TOUCH_BYTE; 3208c2ecf20Sopenharmony_ci if (tmp == W8001_TOUCH_BYTE) 3218c2ecf20Sopenharmony_ci break; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci w8001->idx = 0; 3248c2ecf20Sopenharmony_ci parse_pen_data(w8001->data, &coord); 3258c2ecf20Sopenharmony_ci report_pen_events(w8001, &coord); 3268c2ecf20Sopenharmony_ci break; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci /* control packet */ 3298c2ecf20Sopenharmony_ci case W8001_PKTLEN_TPCCTL - 1: 3308c2ecf20Sopenharmony_ci tmp = w8001->data[0] & W8001_TOUCH_MASK; 3318c2ecf20Sopenharmony_ci if (tmp == W8001_TOUCH_BYTE) 3328c2ecf20Sopenharmony_ci break; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci w8001->idx = 0; 3358c2ecf20Sopenharmony_ci memcpy(w8001->response, w8001->data, W8001_MAX_LENGTH); 3368c2ecf20Sopenharmony_ci w8001->response_type = W8001_QUERY_PACKET; 3378c2ecf20Sopenharmony_ci complete(&w8001->cmd_done); 3388c2ecf20Sopenharmony_ci break; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* 2 finger touch packet */ 3418c2ecf20Sopenharmony_ci case W8001_PKTLEN_TOUCH2FG - 1: 3428c2ecf20Sopenharmony_ci w8001->idx = 0; 3438c2ecf20Sopenharmony_ci parse_multi_touch(w8001); 3448c2ecf20Sopenharmony_ci break; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci default: 3478c2ecf20Sopenharmony_ci /* 3488c2ecf20Sopenharmony_ci * ThinkPad X60 Tablet PC (pen only device) sometimes 3498c2ecf20Sopenharmony_ci * sends invalid data packets that are larger than 3508c2ecf20Sopenharmony_ci * W8001_PKTLEN_TPCPEN. Let's start over again. 3518c2ecf20Sopenharmony_ci */ 3528c2ecf20Sopenharmony_ci if (!w8001->touch_dev && w8001->idx > W8001_PKTLEN_TPCPEN - 1) 3538c2ecf20Sopenharmony_ci w8001->idx = 0; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic int w8001_command(struct w8001 *w8001, unsigned char command, 3608c2ecf20Sopenharmony_ci bool wait_response) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci int rc; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci w8001->response_type = 0; 3658c2ecf20Sopenharmony_ci init_completion(&w8001->cmd_done); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci rc = serio_write(w8001->serio, command); 3688c2ecf20Sopenharmony_ci if (rc == 0 && wait_response) { 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci wait_for_completion_timeout(&w8001->cmd_done, HZ); 3718c2ecf20Sopenharmony_ci if (w8001->response_type != W8001_QUERY_PACKET) 3728c2ecf20Sopenharmony_ci rc = -EIO; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci return rc; 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic int w8001_open(struct input_dev *dev) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci struct w8001 *w8001 = input_get_drvdata(dev); 3818c2ecf20Sopenharmony_ci int err; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci err = mutex_lock_interruptible(&w8001->mutex); 3848c2ecf20Sopenharmony_ci if (err) 3858c2ecf20Sopenharmony_ci return err; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (w8001->open_count++ == 0) { 3888c2ecf20Sopenharmony_ci err = w8001_command(w8001, W8001_CMD_START, false); 3898c2ecf20Sopenharmony_ci if (err) 3908c2ecf20Sopenharmony_ci w8001->open_count--; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci mutex_unlock(&w8001->mutex); 3948c2ecf20Sopenharmony_ci return err; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic void w8001_close(struct input_dev *dev) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci struct w8001 *w8001 = input_get_drvdata(dev); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci mutex_lock(&w8001->mutex); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (--w8001->open_count == 0) 4048c2ecf20Sopenharmony_ci w8001_command(w8001, W8001_CMD_STOP, false); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci mutex_unlock(&w8001->mutex); 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cistatic int w8001_detect(struct w8001 *w8001) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci int error; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci error = w8001_command(w8001, W8001_CMD_STOP, false); 4148c2ecf20Sopenharmony_ci if (error) 4158c2ecf20Sopenharmony_ci return error; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci msleep(250); /* wait 250ms before querying the device */ 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci return 0; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic int w8001_setup_pen(struct w8001 *w8001, char *basename, 4238c2ecf20Sopenharmony_ci size_t basename_sz) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci struct input_dev *dev = w8001->pen_dev; 4268c2ecf20Sopenharmony_ci struct w8001_coord coord; 4278c2ecf20Sopenharmony_ci int error; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* penabled? */ 4308c2ecf20Sopenharmony_ci error = w8001_command(w8001, W8001_CMD_QUERY, true); 4318c2ecf20Sopenharmony_ci if (error) 4328c2ecf20Sopenharmony_ci return error; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci __set_bit(EV_KEY, dev->evbit); 4358c2ecf20Sopenharmony_ci __set_bit(EV_ABS, dev->evbit); 4368c2ecf20Sopenharmony_ci __set_bit(BTN_TOUCH, dev->keybit); 4378c2ecf20Sopenharmony_ci __set_bit(BTN_TOOL_PEN, dev->keybit); 4388c2ecf20Sopenharmony_ci __set_bit(BTN_TOOL_RUBBER, dev->keybit); 4398c2ecf20Sopenharmony_ci __set_bit(BTN_STYLUS, dev->keybit); 4408c2ecf20Sopenharmony_ci __set_bit(BTN_STYLUS2, dev->keybit); 4418c2ecf20Sopenharmony_ci __set_bit(INPUT_PROP_DIRECT, dev->propbit); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci parse_pen_data(w8001->response, &coord); 4448c2ecf20Sopenharmony_ci w8001->max_pen_x = coord.x; 4458c2ecf20Sopenharmony_ci w8001->max_pen_y = coord.y; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0); 4488c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0); 4498c2ecf20Sopenharmony_ci input_abs_set_res(dev, ABS_X, W8001_PEN_RESOLUTION); 4508c2ecf20Sopenharmony_ci input_abs_set_res(dev, ABS_Y, W8001_PEN_RESOLUTION); 4518c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0); 4528c2ecf20Sopenharmony_ci if (coord.tilt_x && coord.tilt_y) { 4538c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0); 4548c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0); 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci w8001->id = 0x90; 4588c2ecf20Sopenharmony_ci strlcat(basename, " Penabled", basename_sz); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci return 0; 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic int w8001_setup_touch(struct w8001 *w8001, char *basename, 4648c2ecf20Sopenharmony_ci size_t basename_sz) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci struct input_dev *dev = w8001->touch_dev; 4678c2ecf20Sopenharmony_ci struct w8001_touch_query touch; 4688c2ecf20Sopenharmony_ci int error; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci /* Touch enabled? */ 4728c2ecf20Sopenharmony_ci error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true); 4738c2ecf20Sopenharmony_ci if (error) 4748c2ecf20Sopenharmony_ci return error; 4758c2ecf20Sopenharmony_ci /* 4768c2ecf20Sopenharmony_ci * Some non-touch devices may reply to the touch query. But their 4778c2ecf20Sopenharmony_ci * second byte is empty, which indicates touch is not supported. 4788c2ecf20Sopenharmony_ci */ 4798c2ecf20Sopenharmony_ci if (!w8001->response[1]) 4808c2ecf20Sopenharmony_ci return -ENXIO; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci __set_bit(EV_KEY, dev->evbit); 4838c2ecf20Sopenharmony_ci __set_bit(EV_ABS, dev->evbit); 4848c2ecf20Sopenharmony_ci __set_bit(BTN_TOUCH, dev->keybit); 4858c2ecf20Sopenharmony_ci __set_bit(INPUT_PROP_DIRECT, dev->propbit); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci parse_touchquery(w8001->response, &touch); 4888c2ecf20Sopenharmony_ci w8001->max_touch_x = touch.x; 4898c2ecf20Sopenharmony_ci w8001->max_touch_y = touch.y; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci if (w8001->max_pen_x && w8001->max_pen_y) { 4928c2ecf20Sopenharmony_ci /* if pen is supported scale to pen maximum */ 4938c2ecf20Sopenharmony_ci touch.x = w8001->max_pen_x; 4948c2ecf20Sopenharmony_ci touch.y = w8001->max_pen_y; 4958c2ecf20Sopenharmony_ci touch.panel_res = W8001_PEN_RESOLUTION; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0); 4998c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0); 5008c2ecf20Sopenharmony_ci input_abs_set_res(dev, ABS_X, touch.panel_res); 5018c2ecf20Sopenharmony_ci input_abs_set_res(dev, ABS_Y, touch.panel_res); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci switch (touch.sensor_id) { 5048c2ecf20Sopenharmony_ci case 0: 5058c2ecf20Sopenharmony_ci case 2: 5068c2ecf20Sopenharmony_ci w8001->pktlen = W8001_PKTLEN_TOUCH93; 5078c2ecf20Sopenharmony_ci w8001->id = 0x93; 5088c2ecf20Sopenharmony_ci strlcat(basename, " 1FG", basename_sz); 5098c2ecf20Sopenharmony_ci break; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci case 1: 5128c2ecf20Sopenharmony_ci case 3: 5138c2ecf20Sopenharmony_ci case 4: 5148c2ecf20Sopenharmony_ci w8001->pktlen = W8001_PKTLEN_TOUCH9A; 5158c2ecf20Sopenharmony_ci strlcat(basename, " 1FG", basename_sz); 5168c2ecf20Sopenharmony_ci w8001->id = 0x9a; 5178c2ecf20Sopenharmony_ci break; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci case 5: 5208c2ecf20Sopenharmony_ci w8001->pktlen = W8001_PKTLEN_TOUCH2FG; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); 5238c2ecf20Sopenharmony_ci error = input_mt_init_slots(dev, 2, 0); 5248c2ecf20Sopenharmony_ci if (error) { 5258c2ecf20Sopenharmony_ci dev_err(&w8001->serio->dev, 5268c2ecf20Sopenharmony_ci "failed to initialize MT slots: %d\n", error); 5278c2ecf20Sopenharmony_ci return error; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_MT_POSITION_X, 5318c2ecf20Sopenharmony_ci 0, touch.x, 0, 0); 5328c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_MT_POSITION_Y, 5338c2ecf20Sopenharmony_ci 0, touch.y, 0, 0); 5348c2ecf20Sopenharmony_ci input_set_abs_params(dev, ABS_MT_TOOL_TYPE, 5358c2ecf20Sopenharmony_ci 0, MT_TOOL_MAX, 0, 0); 5368c2ecf20Sopenharmony_ci input_abs_set_res(dev, ABS_MT_POSITION_X, touch.panel_res); 5378c2ecf20Sopenharmony_ci input_abs_set_res(dev, ABS_MT_POSITION_Y, touch.panel_res); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci strlcat(basename, " 2FG", basename_sz); 5408c2ecf20Sopenharmony_ci if (w8001->max_pen_x && w8001->max_pen_y) 5418c2ecf20Sopenharmony_ci w8001->id = 0xE3; 5428c2ecf20Sopenharmony_ci else 5438c2ecf20Sopenharmony_ci w8001->id = 0xE2; 5448c2ecf20Sopenharmony_ci break; 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci strlcat(basename, " Touchscreen", basename_sz); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci return 0; 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cistatic void w8001_set_devdata(struct input_dev *dev, struct w8001 *w8001, 5538c2ecf20Sopenharmony_ci struct serio *serio) 5548c2ecf20Sopenharmony_ci{ 5558c2ecf20Sopenharmony_ci dev->phys = w8001->phys; 5568c2ecf20Sopenharmony_ci dev->id.bustype = BUS_RS232; 5578c2ecf20Sopenharmony_ci dev->id.product = w8001->id; 5588c2ecf20Sopenharmony_ci dev->id.vendor = 0x056a; 5598c2ecf20Sopenharmony_ci dev->id.version = 0x0100; 5608c2ecf20Sopenharmony_ci dev->open = w8001_open; 5618c2ecf20Sopenharmony_ci dev->close = w8001_close; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci dev->dev.parent = &serio->dev; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci input_set_drvdata(dev, w8001); 5668c2ecf20Sopenharmony_ci} 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci/* 5698c2ecf20Sopenharmony_ci * w8001_disconnect() is the opposite of w8001_connect() 5708c2ecf20Sopenharmony_ci */ 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_cistatic void w8001_disconnect(struct serio *serio) 5738c2ecf20Sopenharmony_ci{ 5748c2ecf20Sopenharmony_ci struct w8001 *w8001 = serio_get_drvdata(serio); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci serio_close(serio); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci if (w8001->pen_dev) 5798c2ecf20Sopenharmony_ci input_unregister_device(w8001->pen_dev); 5808c2ecf20Sopenharmony_ci if (w8001->touch_dev) 5818c2ecf20Sopenharmony_ci input_unregister_device(w8001->touch_dev); 5828c2ecf20Sopenharmony_ci kfree(w8001); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci serio_set_drvdata(serio, NULL); 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci/* 5888c2ecf20Sopenharmony_ci * w8001_connect() is the routine that is called when someone adds a 5898c2ecf20Sopenharmony_ci * new serio device that supports the w8001 protocol and registers it as 5908c2ecf20Sopenharmony_ci * an input device. 5918c2ecf20Sopenharmony_ci */ 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cistatic int w8001_connect(struct serio *serio, struct serio_driver *drv) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci struct w8001 *w8001; 5968c2ecf20Sopenharmony_ci struct input_dev *input_dev_pen; 5978c2ecf20Sopenharmony_ci struct input_dev *input_dev_touch; 5988c2ecf20Sopenharmony_ci char basename[64]; 5998c2ecf20Sopenharmony_ci int err, err_pen, err_touch; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci w8001 = kzalloc(sizeof(struct w8001), GFP_KERNEL); 6028c2ecf20Sopenharmony_ci input_dev_pen = input_allocate_device(); 6038c2ecf20Sopenharmony_ci input_dev_touch = input_allocate_device(); 6048c2ecf20Sopenharmony_ci if (!w8001 || !input_dev_pen || !input_dev_touch) { 6058c2ecf20Sopenharmony_ci err = -ENOMEM; 6068c2ecf20Sopenharmony_ci goto fail1; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci w8001->serio = serio; 6108c2ecf20Sopenharmony_ci w8001->pen_dev = input_dev_pen; 6118c2ecf20Sopenharmony_ci w8001->touch_dev = input_dev_touch; 6128c2ecf20Sopenharmony_ci mutex_init(&w8001->mutex); 6138c2ecf20Sopenharmony_ci init_completion(&w8001->cmd_done); 6148c2ecf20Sopenharmony_ci snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci serio_set_drvdata(serio, w8001); 6178c2ecf20Sopenharmony_ci err = serio_open(serio, drv); 6188c2ecf20Sopenharmony_ci if (err) 6198c2ecf20Sopenharmony_ci goto fail2; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci err = w8001_detect(w8001); 6228c2ecf20Sopenharmony_ci if (err) 6238c2ecf20Sopenharmony_ci goto fail3; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci /* For backwards-compatibility we compose the basename based on 6268c2ecf20Sopenharmony_ci * capabilities and then just append the tool type 6278c2ecf20Sopenharmony_ci */ 6288c2ecf20Sopenharmony_ci strlcpy(basename, "Wacom Serial", sizeof(basename)); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci err_pen = w8001_setup_pen(w8001, basename, sizeof(basename)); 6318c2ecf20Sopenharmony_ci err_touch = w8001_setup_touch(w8001, basename, sizeof(basename)); 6328c2ecf20Sopenharmony_ci if (err_pen && err_touch) { 6338c2ecf20Sopenharmony_ci err = -ENXIO; 6348c2ecf20Sopenharmony_ci goto fail3; 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci if (!err_pen) { 6388c2ecf20Sopenharmony_ci strlcpy(w8001->pen_name, basename, sizeof(w8001->pen_name)); 6398c2ecf20Sopenharmony_ci strlcat(w8001->pen_name, " Pen", sizeof(w8001->pen_name)); 6408c2ecf20Sopenharmony_ci input_dev_pen->name = w8001->pen_name; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci w8001_set_devdata(input_dev_pen, w8001, serio); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci err = input_register_device(w8001->pen_dev); 6458c2ecf20Sopenharmony_ci if (err) 6468c2ecf20Sopenharmony_ci goto fail3; 6478c2ecf20Sopenharmony_ci } else { 6488c2ecf20Sopenharmony_ci input_free_device(input_dev_pen); 6498c2ecf20Sopenharmony_ci input_dev_pen = NULL; 6508c2ecf20Sopenharmony_ci w8001->pen_dev = NULL; 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci if (!err_touch) { 6548c2ecf20Sopenharmony_ci strlcpy(w8001->touch_name, basename, sizeof(w8001->touch_name)); 6558c2ecf20Sopenharmony_ci strlcat(w8001->touch_name, " Finger", 6568c2ecf20Sopenharmony_ci sizeof(w8001->touch_name)); 6578c2ecf20Sopenharmony_ci input_dev_touch->name = w8001->touch_name; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci w8001_set_devdata(input_dev_touch, w8001, serio); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci err = input_register_device(w8001->touch_dev); 6628c2ecf20Sopenharmony_ci if (err) 6638c2ecf20Sopenharmony_ci goto fail4; 6648c2ecf20Sopenharmony_ci } else { 6658c2ecf20Sopenharmony_ci input_free_device(input_dev_touch); 6668c2ecf20Sopenharmony_ci input_dev_touch = NULL; 6678c2ecf20Sopenharmony_ci w8001->touch_dev = NULL; 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci return 0; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cifail4: 6738c2ecf20Sopenharmony_ci if (w8001->pen_dev) 6748c2ecf20Sopenharmony_ci input_unregister_device(w8001->pen_dev); 6758c2ecf20Sopenharmony_cifail3: 6768c2ecf20Sopenharmony_ci serio_close(serio); 6778c2ecf20Sopenharmony_cifail2: 6788c2ecf20Sopenharmony_ci serio_set_drvdata(serio, NULL); 6798c2ecf20Sopenharmony_cifail1: 6808c2ecf20Sopenharmony_ci input_free_device(input_dev_pen); 6818c2ecf20Sopenharmony_ci input_free_device(input_dev_touch); 6828c2ecf20Sopenharmony_ci kfree(w8001); 6838c2ecf20Sopenharmony_ci return err; 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_cistatic const struct serio_device_id w8001_serio_ids[] = { 6878c2ecf20Sopenharmony_ci { 6888c2ecf20Sopenharmony_ci .type = SERIO_RS232, 6898c2ecf20Sopenharmony_ci .proto = SERIO_W8001, 6908c2ecf20Sopenharmony_ci .id = SERIO_ANY, 6918c2ecf20Sopenharmony_ci .extra = SERIO_ANY, 6928c2ecf20Sopenharmony_ci }, 6938c2ecf20Sopenharmony_ci { 0 } 6948c2ecf20Sopenharmony_ci}; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(serio, w8001_serio_ids); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_cistatic struct serio_driver w8001_drv = { 6998c2ecf20Sopenharmony_ci .driver = { 7008c2ecf20Sopenharmony_ci .name = "w8001", 7018c2ecf20Sopenharmony_ci }, 7028c2ecf20Sopenharmony_ci .description = DRIVER_DESC, 7038c2ecf20Sopenharmony_ci .id_table = w8001_serio_ids, 7048c2ecf20Sopenharmony_ci .interrupt = w8001_interrupt, 7058c2ecf20Sopenharmony_ci .connect = w8001_connect, 7068c2ecf20Sopenharmony_ci .disconnect = w8001_disconnect, 7078c2ecf20Sopenharmony_ci}; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_cimodule_serio_driver(w8001_drv); 710