18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * USB Synaptics device driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2002 Rob Miller (rob@inpharmatica . co . uk) 68c2ecf20Sopenharmony_ci * Copyright (c) 2003 Ron Lee (ron@debian.org) 78c2ecf20Sopenharmony_ci * cPad driver for kernel 2.4 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Copyright (c) 2004 Jan Steinhoff (cpad@jan-steinhoff . de) 108c2ecf20Sopenharmony_ci * Copyright (c) 2004 Ron Lee (ron@debian.org) 118c2ecf20Sopenharmony_ci * rewritten for kernel 2.6 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * cPad display character device part is not included. It can be found at 148c2ecf20Sopenharmony_ci * http://jan-steinhoff.de/linux/synaptics-usb.html 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Bases on: usb_skeleton.c v2.2 by Greg Kroah-Hartman 178c2ecf20Sopenharmony_ci * drivers/hid/usbhid/usbmouse.c by Vojtech Pavlik 188c2ecf20Sopenharmony_ci * drivers/input/mouse/synaptics.c by Peter Osterlund 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * Trademarks are the property of their respective owners. 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* 248c2ecf20Sopenharmony_ci * There are three different types of Synaptics USB devices: Touchpads, 258c2ecf20Sopenharmony_ci * touchsticks (or trackpoints), and touchscreens. Touchpads are well supported 268c2ecf20Sopenharmony_ci * by this driver, touchstick support has not been tested much yet, and 278c2ecf20Sopenharmony_ci * touchscreens have not been tested at all. 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * Up to three alternate settings are possible: 308c2ecf20Sopenharmony_ci * setting 0: one int endpoint for relative movement (used by usbhid.ko) 318c2ecf20Sopenharmony_ci * setting 1: one int endpoint for absolute finger position 328c2ecf20Sopenharmony_ci * setting 2 (cPad only): one int endpoint for absolute finger position and 338c2ecf20Sopenharmony_ci * two bulk endpoints for the display (in/out) 348c2ecf20Sopenharmony_ci * This driver uses setting 1. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include <linux/kernel.h> 388c2ecf20Sopenharmony_ci#include <linux/slab.h> 398c2ecf20Sopenharmony_ci#include <linux/module.h> 408c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 418c2ecf20Sopenharmony_ci#include <linux/usb.h> 428c2ecf20Sopenharmony_ci#include <linux/input.h> 438c2ecf20Sopenharmony_ci#include <linux/usb/input.h> 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define USB_VENDOR_ID_SYNAPTICS 0x06cb 468c2ecf20Sopenharmony_ci#define USB_DEVICE_ID_SYNAPTICS_TP 0x0001 /* Synaptics USB TouchPad */ 478c2ecf20Sopenharmony_ci#define USB_DEVICE_ID_SYNAPTICS_INT_TP 0x0002 /* Integrated USB TouchPad */ 488c2ecf20Sopenharmony_ci#define USB_DEVICE_ID_SYNAPTICS_CPAD 0x0003 /* Synaptics cPad */ 498c2ecf20Sopenharmony_ci#define USB_DEVICE_ID_SYNAPTICS_TS 0x0006 /* Synaptics TouchScreen */ 508c2ecf20Sopenharmony_ci#define USB_DEVICE_ID_SYNAPTICS_STICK 0x0007 /* Synaptics USB Styk */ 518c2ecf20Sopenharmony_ci#define USB_DEVICE_ID_SYNAPTICS_WP 0x0008 /* Synaptics USB WheelPad */ 528c2ecf20Sopenharmony_ci#define USB_DEVICE_ID_SYNAPTICS_COMP_TP 0x0009 /* Composite USB TouchPad */ 538c2ecf20Sopenharmony_ci#define USB_DEVICE_ID_SYNAPTICS_WTP 0x0010 /* Wireless TouchPad */ 548c2ecf20Sopenharmony_ci#define USB_DEVICE_ID_SYNAPTICS_DPAD 0x0013 /* DisplayPad */ 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define SYNUSB_TOUCHPAD (1 << 0) 578c2ecf20Sopenharmony_ci#define SYNUSB_STICK (1 << 1) 588c2ecf20Sopenharmony_ci#define SYNUSB_TOUCHSCREEN (1 << 2) 598c2ecf20Sopenharmony_ci#define SYNUSB_AUXDISPLAY (1 << 3) /* For cPad */ 608c2ecf20Sopenharmony_ci#define SYNUSB_COMBO (1 << 4) /* Composite device (TP + stick) */ 618c2ecf20Sopenharmony_ci#define SYNUSB_IO_ALWAYS (1 << 5) 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define USB_DEVICE_SYNAPTICS(prod, kind) \ 648c2ecf20Sopenharmony_ci USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, \ 658c2ecf20Sopenharmony_ci USB_DEVICE_ID_SYNAPTICS_##prod), \ 668c2ecf20Sopenharmony_ci .driver_info = (kind), 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define SYNUSB_RECV_SIZE 8 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#define XMIN_NOMINAL 1472 718c2ecf20Sopenharmony_ci#define XMAX_NOMINAL 5472 728c2ecf20Sopenharmony_ci#define YMIN_NOMINAL 1408 738c2ecf20Sopenharmony_ci#define YMAX_NOMINAL 4448 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistruct synusb { 768c2ecf20Sopenharmony_ci struct usb_device *udev; 778c2ecf20Sopenharmony_ci struct usb_interface *intf; 788c2ecf20Sopenharmony_ci struct urb *urb; 798c2ecf20Sopenharmony_ci unsigned char *data; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci /* serialize access to open/suspend */ 828c2ecf20Sopenharmony_ci struct mutex pm_mutex; 838c2ecf20Sopenharmony_ci bool is_open; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* input device related data structures */ 868c2ecf20Sopenharmony_ci struct input_dev *input; 878c2ecf20Sopenharmony_ci char name[128]; 888c2ecf20Sopenharmony_ci char phys[64]; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* characteristics of the device */ 918c2ecf20Sopenharmony_ci unsigned long flags; 928c2ecf20Sopenharmony_ci}; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic void synusb_report_buttons(struct synusb *synusb) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct input_dev *input_dev = synusb->input; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci input_report_key(input_dev, BTN_LEFT, synusb->data[1] & 0x04); 998c2ecf20Sopenharmony_ci input_report_key(input_dev, BTN_RIGHT, synusb->data[1] & 0x01); 1008c2ecf20Sopenharmony_ci input_report_key(input_dev, BTN_MIDDLE, synusb->data[1] & 0x02); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic void synusb_report_stick(struct synusb *synusb) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct input_dev *input_dev = synusb->input; 1068c2ecf20Sopenharmony_ci int x, y; 1078c2ecf20Sopenharmony_ci unsigned int pressure; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci pressure = synusb->data[6]; 1108c2ecf20Sopenharmony_ci x = (s16)(be16_to_cpup((__be16 *)&synusb->data[2]) << 3) >> 7; 1118c2ecf20Sopenharmony_ci y = (s16)(be16_to_cpup((__be16 *)&synusb->data[4]) << 3) >> 7; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (pressure > 0) { 1148c2ecf20Sopenharmony_ci input_report_rel(input_dev, REL_X, x); 1158c2ecf20Sopenharmony_ci input_report_rel(input_dev, REL_Y, -y); 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci input_report_abs(input_dev, ABS_PRESSURE, pressure); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci synusb_report_buttons(synusb); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci input_sync(input_dev); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic void synusb_report_touchpad(struct synusb *synusb) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct input_dev *input_dev = synusb->input; 1288c2ecf20Sopenharmony_ci unsigned int num_fingers, tool_width; 1298c2ecf20Sopenharmony_ci unsigned int x, y; 1308c2ecf20Sopenharmony_ci unsigned int pressure, w; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci pressure = synusb->data[6]; 1338c2ecf20Sopenharmony_ci x = be16_to_cpup((__be16 *)&synusb->data[2]); 1348c2ecf20Sopenharmony_ci y = be16_to_cpup((__be16 *)&synusb->data[4]); 1358c2ecf20Sopenharmony_ci w = synusb->data[0] & 0x0f; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (pressure > 0) { 1388c2ecf20Sopenharmony_ci num_fingers = 1; 1398c2ecf20Sopenharmony_ci tool_width = 5; 1408c2ecf20Sopenharmony_ci switch (w) { 1418c2ecf20Sopenharmony_ci case 0 ... 1: 1428c2ecf20Sopenharmony_ci num_fingers = 2 + w; 1438c2ecf20Sopenharmony_ci break; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci case 2: /* pen, pretend its a finger */ 1468c2ecf20Sopenharmony_ci break; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci case 4 ... 15: 1498c2ecf20Sopenharmony_ci tool_width = w; 1508c2ecf20Sopenharmony_ci break; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci } else { 1538c2ecf20Sopenharmony_ci num_fingers = 0; 1548c2ecf20Sopenharmony_ci tool_width = 0; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* 1588c2ecf20Sopenharmony_ci * Post events 1598c2ecf20Sopenharmony_ci * BTN_TOUCH has to be first as mousedev relies on it when doing 1608c2ecf20Sopenharmony_ci * absolute -> relative conversion 1618c2ecf20Sopenharmony_ci */ 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (pressure > 30) 1648c2ecf20Sopenharmony_ci input_report_key(input_dev, BTN_TOUCH, 1); 1658c2ecf20Sopenharmony_ci if (pressure < 25) 1668c2ecf20Sopenharmony_ci input_report_key(input_dev, BTN_TOUCH, 0); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (num_fingers > 0) { 1698c2ecf20Sopenharmony_ci input_report_abs(input_dev, ABS_X, x); 1708c2ecf20Sopenharmony_ci input_report_abs(input_dev, ABS_Y, 1718c2ecf20Sopenharmony_ci YMAX_NOMINAL + YMIN_NOMINAL - y); 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci input_report_abs(input_dev, ABS_PRESSURE, pressure); 1758c2ecf20Sopenharmony_ci input_report_abs(input_dev, ABS_TOOL_WIDTH, tool_width); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci input_report_key(input_dev, BTN_TOOL_FINGER, num_fingers == 1); 1788c2ecf20Sopenharmony_ci input_report_key(input_dev, BTN_TOOL_DOUBLETAP, num_fingers == 2); 1798c2ecf20Sopenharmony_ci input_report_key(input_dev, BTN_TOOL_TRIPLETAP, num_fingers == 3); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci synusb_report_buttons(synusb); 1828c2ecf20Sopenharmony_ci if (synusb->flags & SYNUSB_AUXDISPLAY) 1838c2ecf20Sopenharmony_ci input_report_key(input_dev, BTN_MIDDLE, synusb->data[1] & 0x08); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci input_sync(input_dev); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic void synusb_irq(struct urb *urb) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct synusb *synusb = urb->context; 1918c2ecf20Sopenharmony_ci int error; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* Check our status in case we need to bail out early. */ 1948c2ecf20Sopenharmony_ci switch (urb->status) { 1958c2ecf20Sopenharmony_ci case 0: 1968c2ecf20Sopenharmony_ci usb_mark_last_busy(synusb->udev); 1978c2ecf20Sopenharmony_ci break; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* Device went away so don't keep trying to read from it. */ 2008c2ecf20Sopenharmony_ci case -ECONNRESET: 2018c2ecf20Sopenharmony_ci case -ENOENT: 2028c2ecf20Sopenharmony_ci case -ESHUTDOWN: 2038c2ecf20Sopenharmony_ci return; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci default: 2068c2ecf20Sopenharmony_ci goto resubmit; 2078c2ecf20Sopenharmony_ci break; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (synusb->flags & SYNUSB_STICK) 2118c2ecf20Sopenharmony_ci synusb_report_stick(synusb); 2128c2ecf20Sopenharmony_ci else 2138c2ecf20Sopenharmony_ci synusb_report_touchpad(synusb); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ciresubmit: 2168c2ecf20Sopenharmony_ci error = usb_submit_urb(urb, GFP_ATOMIC); 2178c2ecf20Sopenharmony_ci if (error && error != -EPERM) 2188c2ecf20Sopenharmony_ci dev_err(&synusb->intf->dev, 2198c2ecf20Sopenharmony_ci "%s - usb_submit_urb failed with result: %d", 2208c2ecf20Sopenharmony_ci __func__, error); 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic struct usb_endpoint_descriptor * 2248c2ecf20Sopenharmony_cisynusb_get_in_endpoint(struct usb_host_interface *iface) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *endpoint; 2288c2ecf20Sopenharmony_ci int i; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci for (i = 0; i < iface->desc.bNumEndpoints; ++i) { 2318c2ecf20Sopenharmony_ci endpoint = &iface->endpoint[i].desc; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (usb_endpoint_is_int_in(endpoint)) { 2348c2ecf20Sopenharmony_ci /* we found our interrupt in endpoint */ 2358c2ecf20Sopenharmony_ci return endpoint; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci return NULL; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic int synusb_open(struct input_dev *dev) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct synusb *synusb = input_get_drvdata(dev); 2458c2ecf20Sopenharmony_ci int retval; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci retval = usb_autopm_get_interface(synusb->intf); 2488c2ecf20Sopenharmony_ci if (retval) { 2498c2ecf20Sopenharmony_ci dev_err(&synusb->intf->dev, 2508c2ecf20Sopenharmony_ci "%s - usb_autopm_get_interface failed, error: %d\n", 2518c2ecf20Sopenharmony_ci __func__, retval); 2528c2ecf20Sopenharmony_ci return retval; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci mutex_lock(&synusb->pm_mutex); 2568c2ecf20Sopenharmony_ci retval = usb_submit_urb(synusb->urb, GFP_KERNEL); 2578c2ecf20Sopenharmony_ci if (retval) { 2588c2ecf20Sopenharmony_ci dev_err(&synusb->intf->dev, 2598c2ecf20Sopenharmony_ci "%s - usb_submit_urb failed, error: %d\n", 2608c2ecf20Sopenharmony_ci __func__, retval); 2618c2ecf20Sopenharmony_ci retval = -EIO; 2628c2ecf20Sopenharmony_ci goto out; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci synusb->intf->needs_remote_wakeup = 1; 2668c2ecf20Sopenharmony_ci synusb->is_open = true; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ciout: 2698c2ecf20Sopenharmony_ci mutex_unlock(&synusb->pm_mutex); 2708c2ecf20Sopenharmony_ci usb_autopm_put_interface(synusb->intf); 2718c2ecf20Sopenharmony_ci return retval; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic void synusb_close(struct input_dev *dev) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct synusb *synusb = input_get_drvdata(dev); 2778c2ecf20Sopenharmony_ci int autopm_error; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci autopm_error = usb_autopm_get_interface(synusb->intf); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci mutex_lock(&synusb->pm_mutex); 2828c2ecf20Sopenharmony_ci usb_kill_urb(synusb->urb); 2838c2ecf20Sopenharmony_ci synusb->intf->needs_remote_wakeup = 0; 2848c2ecf20Sopenharmony_ci synusb->is_open = false; 2858c2ecf20Sopenharmony_ci mutex_unlock(&synusb->pm_mutex); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (!autopm_error) 2888c2ecf20Sopenharmony_ci usb_autopm_put_interface(synusb->intf); 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic int synusb_probe(struct usb_interface *intf, 2928c2ecf20Sopenharmony_ci const struct usb_device_id *id) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(intf); 2958c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *ep; 2968c2ecf20Sopenharmony_ci struct synusb *synusb; 2978c2ecf20Sopenharmony_ci struct input_dev *input_dev; 2988c2ecf20Sopenharmony_ci unsigned int intf_num = intf->cur_altsetting->desc.bInterfaceNumber; 2998c2ecf20Sopenharmony_ci unsigned int altsetting = min(intf->num_altsetting, 1U); 3008c2ecf20Sopenharmony_ci int error; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci error = usb_set_interface(udev, intf_num, altsetting); 3038c2ecf20Sopenharmony_ci if (error) { 3048c2ecf20Sopenharmony_ci dev_err(&udev->dev, 3058c2ecf20Sopenharmony_ci "Can not set alternate setting to %i, error: %i", 3068c2ecf20Sopenharmony_ci altsetting, error); 3078c2ecf20Sopenharmony_ci return error; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci ep = synusb_get_in_endpoint(intf->cur_altsetting); 3118c2ecf20Sopenharmony_ci if (!ep) 3128c2ecf20Sopenharmony_ci return -ENODEV; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci synusb = kzalloc(sizeof(*synusb), GFP_KERNEL); 3158c2ecf20Sopenharmony_ci input_dev = input_allocate_device(); 3168c2ecf20Sopenharmony_ci if (!synusb || !input_dev) { 3178c2ecf20Sopenharmony_ci error = -ENOMEM; 3188c2ecf20Sopenharmony_ci goto err_free_mem; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci synusb->udev = udev; 3228c2ecf20Sopenharmony_ci synusb->intf = intf; 3238c2ecf20Sopenharmony_ci synusb->input = input_dev; 3248c2ecf20Sopenharmony_ci mutex_init(&synusb->pm_mutex); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci synusb->flags = id->driver_info; 3278c2ecf20Sopenharmony_ci if (synusb->flags & SYNUSB_COMBO) { 3288c2ecf20Sopenharmony_ci /* 3298c2ecf20Sopenharmony_ci * This is a combo device, we need to set proper 3308c2ecf20Sopenharmony_ci * capability, depending on the interface. 3318c2ecf20Sopenharmony_ci */ 3328c2ecf20Sopenharmony_ci synusb->flags |= intf_num == 1 ? 3338c2ecf20Sopenharmony_ci SYNUSB_STICK : SYNUSB_TOUCHPAD; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci synusb->urb = usb_alloc_urb(0, GFP_KERNEL); 3378c2ecf20Sopenharmony_ci if (!synusb->urb) { 3388c2ecf20Sopenharmony_ci error = -ENOMEM; 3398c2ecf20Sopenharmony_ci goto err_free_mem; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci synusb->data = usb_alloc_coherent(udev, SYNUSB_RECV_SIZE, GFP_KERNEL, 3438c2ecf20Sopenharmony_ci &synusb->urb->transfer_dma); 3448c2ecf20Sopenharmony_ci if (!synusb->data) { 3458c2ecf20Sopenharmony_ci error = -ENOMEM; 3468c2ecf20Sopenharmony_ci goto err_free_urb; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci usb_fill_int_urb(synusb->urb, udev, 3508c2ecf20Sopenharmony_ci usb_rcvintpipe(udev, ep->bEndpointAddress), 3518c2ecf20Sopenharmony_ci synusb->data, SYNUSB_RECV_SIZE, 3528c2ecf20Sopenharmony_ci synusb_irq, synusb, 3538c2ecf20Sopenharmony_ci ep->bInterval); 3548c2ecf20Sopenharmony_ci synusb->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (udev->manufacturer) 3578c2ecf20Sopenharmony_ci strlcpy(synusb->name, udev->manufacturer, 3588c2ecf20Sopenharmony_ci sizeof(synusb->name)); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (udev->product) { 3618c2ecf20Sopenharmony_ci if (udev->manufacturer) 3628c2ecf20Sopenharmony_ci strlcat(synusb->name, " ", sizeof(synusb->name)); 3638c2ecf20Sopenharmony_ci strlcat(synusb->name, udev->product, sizeof(synusb->name)); 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci if (!strlen(synusb->name)) 3678c2ecf20Sopenharmony_ci snprintf(synusb->name, sizeof(synusb->name), 3688c2ecf20Sopenharmony_ci "USB Synaptics Device %04x:%04x", 3698c2ecf20Sopenharmony_ci le16_to_cpu(udev->descriptor.idVendor), 3708c2ecf20Sopenharmony_ci le16_to_cpu(udev->descriptor.idProduct)); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (synusb->flags & SYNUSB_STICK) 3738c2ecf20Sopenharmony_ci strlcat(synusb->name, " (Stick)", sizeof(synusb->name)); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci usb_make_path(udev, synusb->phys, sizeof(synusb->phys)); 3768c2ecf20Sopenharmony_ci strlcat(synusb->phys, "/input0", sizeof(synusb->phys)); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci input_dev->name = synusb->name; 3798c2ecf20Sopenharmony_ci input_dev->phys = synusb->phys; 3808c2ecf20Sopenharmony_ci usb_to_input_id(udev, &input_dev->id); 3818c2ecf20Sopenharmony_ci input_dev->dev.parent = &synusb->intf->dev; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (!(synusb->flags & SYNUSB_IO_ALWAYS)) { 3848c2ecf20Sopenharmony_ci input_dev->open = synusb_open; 3858c2ecf20Sopenharmony_ci input_dev->close = synusb_close; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci input_set_drvdata(input_dev, synusb); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci __set_bit(EV_ABS, input_dev->evbit); 3918c2ecf20Sopenharmony_ci __set_bit(EV_KEY, input_dev->evbit); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci if (synusb->flags & SYNUSB_STICK) { 3948c2ecf20Sopenharmony_ci __set_bit(EV_REL, input_dev->evbit); 3958c2ecf20Sopenharmony_ci __set_bit(REL_X, input_dev->relbit); 3968c2ecf20Sopenharmony_ci __set_bit(REL_Y, input_dev->relbit); 3978c2ecf20Sopenharmony_ci __set_bit(INPUT_PROP_POINTING_STICK, input_dev->propbit); 3988c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, ABS_PRESSURE, 0, 127, 0, 0); 3998c2ecf20Sopenharmony_ci } else { 4008c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, ABS_X, 4018c2ecf20Sopenharmony_ci XMIN_NOMINAL, XMAX_NOMINAL, 0, 0); 4028c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, ABS_Y, 4038c2ecf20Sopenharmony_ci YMIN_NOMINAL, YMAX_NOMINAL, 0, 0); 4048c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0); 4058c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, ABS_TOOL_WIDTH, 0, 15, 0, 0); 4068c2ecf20Sopenharmony_ci __set_bit(BTN_TOUCH, input_dev->keybit); 4078c2ecf20Sopenharmony_ci __set_bit(BTN_TOOL_FINGER, input_dev->keybit); 4088c2ecf20Sopenharmony_ci __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); 4098c2ecf20Sopenharmony_ci __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit); 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (synusb->flags & SYNUSB_TOUCHSCREEN) 4138c2ecf20Sopenharmony_ci __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); 4148c2ecf20Sopenharmony_ci else 4158c2ecf20Sopenharmony_ci __set_bit(INPUT_PROP_POINTER, input_dev->propbit); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci __set_bit(BTN_LEFT, input_dev->keybit); 4188c2ecf20Sopenharmony_ci __set_bit(BTN_RIGHT, input_dev->keybit); 4198c2ecf20Sopenharmony_ci __set_bit(BTN_MIDDLE, input_dev->keybit); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci usb_set_intfdata(intf, synusb); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (synusb->flags & SYNUSB_IO_ALWAYS) { 4248c2ecf20Sopenharmony_ci error = synusb_open(input_dev); 4258c2ecf20Sopenharmony_ci if (error) 4268c2ecf20Sopenharmony_ci goto err_free_dma; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci error = input_register_device(input_dev); 4308c2ecf20Sopenharmony_ci if (error) { 4318c2ecf20Sopenharmony_ci dev_err(&udev->dev, 4328c2ecf20Sopenharmony_ci "Failed to register input device, error %d\n", 4338c2ecf20Sopenharmony_ci error); 4348c2ecf20Sopenharmony_ci goto err_stop_io; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci return 0; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cierr_stop_io: 4408c2ecf20Sopenharmony_ci if (synusb->flags & SYNUSB_IO_ALWAYS) 4418c2ecf20Sopenharmony_ci synusb_close(synusb->input); 4428c2ecf20Sopenharmony_cierr_free_dma: 4438c2ecf20Sopenharmony_ci usb_free_coherent(udev, SYNUSB_RECV_SIZE, synusb->data, 4448c2ecf20Sopenharmony_ci synusb->urb->transfer_dma); 4458c2ecf20Sopenharmony_cierr_free_urb: 4468c2ecf20Sopenharmony_ci usb_free_urb(synusb->urb); 4478c2ecf20Sopenharmony_cierr_free_mem: 4488c2ecf20Sopenharmony_ci input_free_device(input_dev); 4498c2ecf20Sopenharmony_ci kfree(synusb); 4508c2ecf20Sopenharmony_ci usb_set_intfdata(intf, NULL); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci return error; 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_cistatic void synusb_disconnect(struct usb_interface *intf) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci struct synusb *synusb = usb_get_intfdata(intf); 4588c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(intf); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (synusb->flags & SYNUSB_IO_ALWAYS) 4618c2ecf20Sopenharmony_ci synusb_close(synusb->input); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci input_unregister_device(synusb->input); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci usb_free_coherent(udev, SYNUSB_RECV_SIZE, synusb->data, 4668c2ecf20Sopenharmony_ci synusb->urb->transfer_dma); 4678c2ecf20Sopenharmony_ci usb_free_urb(synusb->urb); 4688c2ecf20Sopenharmony_ci kfree(synusb); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci usb_set_intfdata(intf, NULL); 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic int synusb_suspend(struct usb_interface *intf, pm_message_t message) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci struct synusb *synusb = usb_get_intfdata(intf); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci mutex_lock(&synusb->pm_mutex); 4788c2ecf20Sopenharmony_ci usb_kill_urb(synusb->urb); 4798c2ecf20Sopenharmony_ci mutex_unlock(&synusb->pm_mutex); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci return 0; 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistatic int synusb_resume(struct usb_interface *intf) 4858c2ecf20Sopenharmony_ci{ 4868c2ecf20Sopenharmony_ci struct synusb *synusb = usb_get_intfdata(intf); 4878c2ecf20Sopenharmony_ci int retval = 0; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci mutex_lock(&synusb->pm_mutex); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci if ((synusb->is_open || (synusb->flags & SYNUSB_IO_ALWAYS)) && 4928c2ecf20Sopenharmony_ci usb_submit_urb(synusb->urb, GFP_NOIO) < 0) { 4938c2ecf20Sopenharmony_ci retval = -EIO; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci mutex_unlock(&synusb->pm_mutex); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci return retval; 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cistatic int synusb_pre_reset(struct usb_interface *intf) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci struct synusb *synusb = usb_get_intfdata(intf); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci mutex_lock(&synusb->pm_mutex); 5068c2ecf20Sopenharmony_ci usb_kill_urb(synusb->urb); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci return 0; 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_cistatic int synusb_post_reset(struct usb_interface *intf) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci struct synusb *synusb = usb_get_intfdata(intf); 5148c2ecf20Sopenharmony_ci int retval = 0; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci if ((synusb->is_open || (synusb->flags & SYNUSB_IO_ALWAYS)) && 5178c2ecf20Sopenharmony_ci usb_submit_urb(synusb->urb, GFP_NOIO) < 0) { 5188c2ecf20Sopenharmony_ci retval = -EIO; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci mutex_unlock(&synusb->pm_mutex); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci return retval; 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistatic int synusb_reset_resume(struct usb_interface *intf) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci return synusb_resume(intf); 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_cistatic const struct usb_device_id synusb_idtable[] = { 5328c2ecf20Sopenharmony_ci { USB_DEVICE_SYNAPTICS(TP, SYNUSB_TOUCHPAD) }, 5338c2ecf20Sopenharmony_ci { USB_DEVICE_SYNAPTICS(INT_TP, SYNUSB_TOUCHPAD) }, 5348c2ecf20Sopenharmony_ci { USB_DEVICE_SYNAPTICS(CPAD, 5358c2ecf20Sopenharmony_ci SYNUSB_TOUCHPAD | SYNUSB_AUXDISPLAY | SYNUSB_IO_ALWAYS) }, 5368c2ecf20Sopenharmony_ci { USB_DEVICE_SYNAPTICS(TS, SYNUSB_TOUCHSCREEN) }, 5378c2ecf20Sopenharmony_ci { USB_DEVICE_SYNAPTICS(STICK, SYNUSB_STICK) }, 5388c2ecf20Sopenharmony_ci { USB_DEVICE_SYNAPTICS(WP, SYNUSB_TOUCHPAD) }, 5398c2ecf20Sopenharmony_ci { USB_DEVICE_SYNAPTICS(COMP_TP, SYNUSB_COMBO) }, 5408c2ecf20Sopenharmony_ci { USB_DEVICE_SYNAPTICS(WTP, SYNUSB_TOUCHPAD) }, 5418c2ecf20Sopenharmony_ci { USB_DEVICE_SYNAPTICS(DPAD, SYNUSB_TOUCHPAD) }, 5428c2ecf20Sopenharmony_ci { } 5438c2ecf20Sopenharmony_ci}; 5448c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, synusb_idtable); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_cistatic struct usb_driver synusb_driver = { 5478c2ecf20Sopenharmony_ci .name = "synaptics_usb", 5488c2ecf20Sopenharmony_ci .probe = synusb_probe, 5498c2ecf20Sopenharmony_ci .disconnect = synusb_disconnect, 5508c2ecf20Sopenharmony_ci .id_table = synusb_idtable, 5518c2ecf20Sopenharmony_ci .suspend = synusb_suspend, 5528c2ecf20Sopenharmony_ci .resume = synusb_resume, 5538c2ecf20Sopenharmony_ci .pre_reset = synusb_pre_reset, 5548c2ecf20Sopenharmony_ci .post_reset = synusb_post_reset, 5558c2ecf20Sopenharmony_ci .reset_resume = synusb_reset_resume, 5568c2ecf20Sopenharmony_ci .supports_autosuspend = 1, 5578c2ecf20Sopenharmony_ci}; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_cimodule_usb_driver(synusb_driver); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ciMODULE_AUTHOR("Rob Miller <rob@inpharmatica.co.uk>, " 5628c2ecf20Sopenharmony_ci "Ron Lee <ron@debian.org>, " 5638c2ecf20Sopenharmony_ci "Jan Steinhoff <cpad@jan-steinhoff.de>"); 5648c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Synaptics USB device driver"); 5658c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 566