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