18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Pegasus Mobile Notetaker Pen input tablet driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2016 Martin Kepplinger <martink@posteo.de>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci/*
98c2ecf20Sopenharmony_ci * request packet (control endpoint):
108c2ecf20Sopenharmony_ci * |-------------------------------------|
118c2ecf20Sopenharmony_ci * | Report ID | Nr of bytes | command   |
128c2ecf20Sopenharmony_ci * | (1 byte)  | (1 byte)    | (n bytes) |
138c2ecf20Sopenharmony_ci * |-------------------------------------|
148c2ecf20Sopenharmony_ci * | 0x02      | n           |           |
158c2ecf20Sopenharmony_ci * |-------------------------------------|
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * data packet after set xy mode command, 0x80 0xb5 0x02 0x01
188c2ecf20Sopenharmony_ci * and pen is in range:
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * byte	byte name		value (bits)
218c2ecf20Sopenharmony_ci * --------------------------------------------
228c2ecf20Sopenharmony_ci * 0	status			0 1 0 0 0 0 X X
238c2ecf20Sopenharmony_ci * 1	color			0 0 0 0 H 0 S T
248c2ecf20Sopenharmony_ci * 2	X low
258c2ecf20Sopenharmony_ci * 3	X high
268c2ecf20Sopenharmony_ci * 4	Y low
278c2ecf20Sopenharmony_ci * 5	Y high
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci * X X	battery state:
308c2ecf20Sopenharmony_ci *	no state reported	0x00
318c2ecf20Sopenharmony_ci *	battery low		0x01
328c2ecf20Sopenharmony_ci *	battery good		0x02
338c2ecf20Sopenharmony_ci *
348c2ecf20Sopenharmony_ci * H	Hovering
358c2ecf20Sopenharmony_ci * S	Switch 1 (pen button)
368c2ecf20Sopenharmony_ci * T	Tip
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#include <linux/kernel.h>
408c2ecf20Sopenharmony_ci#include <linux/module.h>
418c2ecf20Sopenharmony_ci#include <linux/input.h>
428c2ecf20Sopenharmony_ci#include <linux/usb/input.h>
438c2ecf20Sopenharmony_ci#include <linux/slab.h>
448c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
458c2ecf20Sopenharmony_ci#include <linux/mutex.h>
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* USB HID defines */
488c2ecf20Sopenharmony_ci#define USB_REQ_GET_REPORT		0x01
498c2ecf20Sopenharmony_ci#define USB_REQ_SET_REPORT		0x09
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#define USB_VENDOR_ID_PEGASUSTECH	0x0e20
528c2ecf20Sopenharmony_ci#define USB_DEVICE_ID_PEGASUS_NOTETAKER_EN100	0x0101
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/* device specific defines */
558c2ecf20Sopenharmony_ci#define NOTETAKER_REPORT_ID		0x02
568c2ecf20Sopenharmony_ci#define NOTETAKER_SET_CMD		0x80
578c2ecf20Sopenharmony_ci#define NOTETAKER_SET_MODE		0xb5
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci#define NOTETAKER_LED_MOUSE		0x02
608c2ecf20Sopenharmony_ci#define PEN_MODE_XY			0x01
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci#define SPECIAL_COMMAND			0x80
638c2ecf20Sopenharmony_ci#define BUTTON_PRESSED			0xb5
648c2ecf20Sopenharmony_ci#define COMMAND_VERSION			0xa9
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/* in xy data packet */
678c2ecf20Sopenharmony_ci#define BATTERY_NO_REPORT		0x40
688c2ecf20Sopenharmony_ci#define BATTERY_LOW			0x41
698c2ecf20Sopenharmony_ci#define BATTERY_GOOD			0x42
708c2ecf20Sopenharmony_ci#define PEN_BUTTON_PRESSED		BIT(1)
718c2ecf20Sopenharmony_ci#define PEN_TIP				BIT(0)
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistruct pegasus {
748c2ecf20Sopenharmony_ci	unsigned char *data;
758c2ecf20Sopenharmony_ci	u8 data_len;
768c2ecf20Sopenharmony_ci	dma_addr_t data_dma;
778c2ecf20Sopenharmony_ci	struct input_dev *dev;
788c2ecf20Sopenharmony_ci	struct usb_device *usbdev;
798c2ecf20Sopenharmony_ci	struct usb_interface *intf;
808c2ecf20Sopenharmony_ci	struct urb *irq;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	/* serialize access to open/suspend */
838c2ecf20Sopenharmony_ci	struct mutex pm_mutex;
848c2ecf20Sopenharmony_ci	bool is_open;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	char name[128];
878c2ecf20Sopenharmony_ci	char phys[64];
888c2ecf20Sopenharmony_ci	struct work_struct init;
898c2ecf20Sopenharmony_ci};
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic int pegasus_control_msg(struct pegasus *pegasus, u8 *data, int len)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	const int sizeof_buf = len + 2;
948c2ecf20Sopenharmony_ci	int result;
958c2ecf20Sopenharmony_ci	int error;
968c2ecf20Sopenharmony_ci	u8 *cmd_buf;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	cmd_buf = kmalloc(sizeof_buf, GFP_KERNEL);
998c2ecf20Sopenharmony_ci	if (!cmd_buf)
1008c2ecf20Sopenharmony_ci		return -ENOMEM;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	cmd_buf[0] = NOTETAKER_REPORT_ID;
1038c2ecf20Sopenharmony_ci	cmd_buf[1] = len;
1048c2ecf20Sopenharmony_ci	memcpy(cmd_buf + 2, data, len);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	result = usb_control_msg(pegasus->usbdev,
1078c2ecf20Sopenharmony_ci				 usb_sndctrlpipe(pegasus->usbdev, 0),
1088c2ecf20Sopenharmony_ci				 USB_REQ_SET_REPORT,
1098c2ecf20Sopenharmony_ci				 USB_TYPE_VENDOR | USB_DIR_OUT,
1108c2ecf20Sopenharmony_ci				 0, 0, cmd_buf, sizeof_buf,
1118c2ecf20Sopenharmony_ci				 USB_CTRL_SET_TIMEOUT);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	kfree(cmd_buf);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	if (unlikely(result != sizeof_buf)) {
1168c2ecf20Sopenharmony_ci		error = result < 0 ? result : -EIO;
1178c2ecf20Sopenharmony_ci		dev_err(&pegasus->usbdev->dev, "control msg error: %d\n",
1188c2ecf20Sopenharmony_ci			error);
1198c2ecf20Sopenharmony_ci		return error;
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	return 0;
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic int pegasus_set_mode(struct pegasus *pegasus, u8 mode, u8 led)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	u8 cmd[] = { NOTETAKER_SET_CMD, NOTETAKER_SET_MODE, led, mode };
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	return pegasus_control_msg(pegasus, cmd, sizeof(cmd));
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic void pegasus_parse_packet(struct pegasus *pegasus)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	unsigned char *data = pegasus->data;
1358c2ecf20Sopenharmony_ci	struct input_dev *dev = pegasus->dev;
1368c2ecf20Sopenharmony_ci	u16 x, y;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	switch (data[0]) {
1398c2ecf20Sopenharmony_ci	case SPECIAL_COMMAND:
1408c2ecf20Sopenharmony_ci		/* device button pressed */
1418c2ecf20Sopenharmony_ci		if (data[1] == BUTTON_PRESSED)
1428c2ecf20Sopenharmony_ci			schedule_work(&pegasus->init);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci		break;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	/* xy data */
1478c2ecf20Sopenharmony_ci	case BATTERY_LOW:
1488c2ecf20Sopenharmony_ci		dev_warn_once(&dev->dev, "Pen battery low\n");
1498c2ecf20Sopenharmony_ci		fallthrough;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	case BATTERY_NO_REPORT:
1528c2ecf20Sopenharmony_ci	case BATTERY_GOOD:
1538c2ecf20Sopenharmony_ci		x = le16_to_cpup((__le16 *)&data[2]);
1548c2ecf20Sopenharmony_ci		y = le16_to_cpup((__le16 *)&data[4]);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci		/* pen-up event */
1578c2ecf20Sopenharmony_ci		if (x == 0 && y == 0)
1588c2ecf20Sopenharmony_ci			break;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci		input_report_key(dev, BTN_TOUCH, data[1] & PEN_TIP);
1618c2ecf20Sopenharmony_ci		input_report_key(dev, BTN_RIGHT, data[1] & PEN_BUTTON_PRESSED);
1628c2ecf20Sopenharmony_ci		input_report_key(dev, BTN_TOOL_PEN, 1);
1638c2ecf20Sopenharmony_ci		input_report_abs(dev, ABS_X, (s16)x);
1648c2ecf20Sopenharmony_ci		input_report_abs(dev, ABS_Y, y);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci		input_sync(dev);
1678c2ecf20Sopenharmony_ci		break;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	default:
1708c2ecf20Sopenharmony_ci		dev_warn_once(&pegasus->usbdev->dev,
1718c2ecf20Sopenharmony_ci			      "unknown answer from device\n");
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic void pegasus_irq(struct urb *urb)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	struct pegasus *pegasus = urb->context;
1788c2ecf20Sopenharmony_ci	struct usb_device *dev = pegasus->usbdev;
1798c2ecf20Sopenharmony_ci	int retval;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	switch (urb->status) {
1828c2ecf20Sopenharmony_ci	case 0:
1838c2ecf20Sopenharmony_ci		pegasus_parse_packet(pegasus);
1848c2ecf20Sopenharmony_ci		usb_mark_last_busy(pegasus->usbdev);
1858c2ecf20Sopenharmony_ci		break;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	case -ECONNRESET:
1888c2ecf20Sopenharmony_ci	case -ENOENT:
1898c2ecf20Sopenharmony_ci	case -ESHUTDOWN:
1908c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "%s - urb shutting down with status: %d",
1918c2ecf20Sopenharmony_ci			__func__, urb->status);
1928c2ecf20Sopenharmony_ci		return;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	default:
1958c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "%s - nonzero urb status received: %d",
1968c2ecf20Sopenharmony_ci			__func__, urb->status);
1978c2ecf20Sopenharmony_ci		break;
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	retval = usb_submit_urb(urb, GFP_ATOMIC);
2018c2ecf20Sopenharmony_ci	if (retval)
2028c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "%s - usb_submit_urb failed with result %d",
2038c2ecf20Sopenharmony_ci			__func__, retval);
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic void pegasus_init(struct work_struct *work)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	struct pegasus *pegasus = container_of(work, struct pegasus, init);
2098c2ecf20Sopenharmony_ci	int error;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	error = pegasus_set_mode(pegasus, PEN_MODE_XY, NOTETAKER_LED_MOUSE);
2128c2ecf20Sopenharmony_ci	if (error)
2138c2ecf20Sopenharmony_ci		dev_err(&pegasus->usbdev->dev, "pegasus_set_mode error: %d\n",
2148c2ecf20Sopenharmony_ci			error);
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic int pegasus_open(struct input_dev *dev)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	struct pegasus *pegasus = input_get_drvdata(dev);
2208c2ecf20Sopenharmony_ci	int error;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	error = usb_autopm_get_interface(pegasus->intf);
2238c2ecf20Sopenharmony_ci	if (error)
2248c2ecf20Sopenharmony_ci		return error;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	mutex_lock(&pegasus->pm_mutex);
2278c2ecf20Sopenharmony_ci	pegasus->irq->dev = pegasus->usbdev;
2288c2ecf20Sopenharmony_ci	if (usb_submit_urb(pegasus->irq, GFP_KERNEL)) {
2298c2ecf20Sopenharmony_ci		error = -EIO;
2308c2ecf20Sopenharmony_ci		goto err_autopm_put;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	error = pegasus_set_mode(pegasus, PEN_MODE_XY, NOTETAKER_LED_MOUSE);
2348c2ecf20Sopenharmony_ci	if (error)
2358c2ecf20Sopenharmony_ci		goto err_kill_urb;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	pegasus->is_open = true;
2388c2ecf20Sopenharmony_ci	mutex_unlock(&pegasus->pm_mutex);
2398c2ecf20Sopenharmony_ci	return 0;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cierr_kill_urb:
2428c2ecf20Sopenharmony_ci	usb_kill_urb(pegasus->irq);
2438c2ecf20Sopenharmony_ci	cancel_work_sync(&pegasus->init);
2448c2ecf20Sopenharmony_cierr_autopm_put:
2458c2ecf20Sopenharmony_ci	mutex_unlock(&pegasus->pm_mutex);
2468c2ecf20Sopenharmony_ci	usb_autopm_put_interface(pegasus->intf);
2478c2ecf20Sopenharmony_ci	return error;
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic void pegasus_close(struct input_dev *dev)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	struct pegasus *pegasus = input_get_drvdata(dev);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	mutex_lock(&pegasus->pm_mutex);
2558c2ecf20Sopenharmony_ci	usb_kill_urb(pegasus->irq);
2568c2ecf20Sopenharmony_ci	cancel_work_sync(&pegasus->init);
2578c2ecf20Sopenharmony_ci	pegasus->is_open = false;
2588c2ecf20Sopenharmony_ci	mutex_unlock(&pegasus->pm_mutex);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	usb_autopm_put_interface(pegasus->intf);
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic int pegasus_probe(struct usb_interface *intf,
2648c2ecf20Sopenharmony_ci			 const struct usb_device_id *id)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	struct usb_device *dev = interface_to_usbdev(intf);
2678c2ecf20Sopenharmony_ci	struct usb_endpoint_descriptor *endpoint;
2688c2ecf20Sopenharmony_ci	struct pegasus *pegasus;
2698c2ecf20Sopenharmony_ci	struct input_dev *input_dev;
2708c2ecf20Sopenharmony_ci	int error;
2718c2ecf20Sopenharmony_ci	int pipe;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	/* We control interface 0 */
2748c2ecf20Sopenharmony_ci	if (intf->cur_altsetting->desc.bInterfaceNumber >= 1)
2758c2ecf20Sopenharmony_ci		return -ENODEV;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	/* Sanity check that the device has an endpoint */
2788c2ecf20Sopenharmony_ci	if (intf->cur_altsetting->desc.bNumEndpoints < 1) {
2798c2ecf20Sopenharmony_ci		dev_err(&intf->dev, "Invalid number of endpoints\n");
2808c2ecf20Sopenharmony_ci		return -EINVAL;
2818c2ecf20Sopenharmony_ci	}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	endpoint = &intf->cur_altsetting->endpoint[0].desc;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	pegasus = kzalloc(sizeof(*pegasus), GFP_KERNEL);
2868c2ecf20Sopenharmony_ci	input_dev = input_allocate_device();
2878c2ecf20Sopenharmony_ci	if (!pegasus || !input_dev) {
2888c2ecf20Sopenharmony_ci		error = -ENOMEM;
2898c2ecf20Sopenharmony_ci		goto err_free_mem;
2908c2ecf20Sopenharmony_ci	}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	mutex_init(&pegasus->pm_mutex);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	pegasus->usbdev = dev;
2958c2ecf20Sopenharmony_ci	pegasus->dev = input_dev;
2968c2ecf20Sopenharmony_ci	pegasus->intf = intf;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
2998c2ecf20Sopenharmony_ci	pegasus->data_len = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	pegasus->data = usb_alloc_coherent(dev, pegasus->data_len, GFP_KERNEL,
3028c2ecf20Sopenharmony_ci					   &pegasus->data_dma);
3038c2ecf20Sopenharmony_ci	if (!pegasus->data) {
3048c2ecf20Sopenharmony_ci		error = -ENOMEM;
3058c2ecf20Sopenharmony_ci		goto err_free_mem;
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	pegasus->irq = usb_alloc_urb(0, GFP_KERNEL);
3098c2ecf20Sopenharmony_ci	if (!pegasus->irq) {
3108c2ecf20Sopenharmony_ci		error = -ENOMEM;
3118c2ecf20Sopenharmony_ci		goto err_free_dma;
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	usb_fill_int_urb(pegasus->irq, dev, pipe,
3158c2ecf20Sopenharmony_ci			 pegasus->data, pegasus->data_len,
3168c2ecf20Sopenharmony_ci			 pegasus_irq, pegasus, endpoint->bInterval);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	pegasus->irq->transfer_dma = pegasus->data_dma;
3198c2ecf20Sopenharmony_ci	pegasus->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	if (dev->manufacturer)
3228c2ecf20Sopenharmony_ci		strlcpy(pegasus->name, dev->manufacturer,
3238c2ecf20Sopenharmony_ci			sizeof(pegasus->name));
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	if (dev->product) {
3268c2ecf20Sopenharmony_ci		if (dev->manufacturer)
3278c2ecf20Sopenharmony_ci			strlcat(pegasus->name, " ", sizeof(pegasus->name));
3288c2ecf20Sopenharmony_ci		strlcat(pegasus->name, dev->product, sizeof(pegasus->name));
3298c2ecf20Sopenharmony_ci	}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	if (!strlen(pegasus->name))
3328c2ecf20Sopenharmony_ci		snprintf(pegasus->name, sizeof(pegasus->name),
3338c2ecf20Sopenharmony_ci			 "USB Pegasus Device %04x:%04x",
3348c2ecf20Sopenharmony_ci			 le16_to_cpu(dev->descriptor.idVendor),
3358c2ecf20Sopenharmony_ci			 le16_to_cpu(dev->descriptor.idProduct));
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	usb_make_path(dev, pegasus->phys, sizeof(pegasus->phys));
3388c2ecf20Sopenharmony_ci	strlcat(pegasus->phys, "/input0", sizeof(pegasus->phys));
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	INIT_WORK(&pegasus->init, pegasus_init);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	usb_set_intfdata(intf, pegasus);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	input_dev->name = pegasus->name;
3458c2ecf20Sopenharmony_ci	input_dev->phys = pegasus->phys;
3468c2ecf20Sopenharmony_ci	usb_to_input_id(dev, &input_dev->id);
3478c2ecf20Sopenharmony_ci	input_dev->dev.parent = &intf->dev;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	input_set_drvdata(input_dev, pegasus);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	input_dev->open = pegasus_open;
3528c2ecf20Sopenharmony_ci	input_dev->close = pegasus_close;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	__set_bit(EV_ABS, input_dev->evbit);
3558c2ecf20Sopenharmony_ci	__set_bit(EV_KEY, input_dev->evbit);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	__set_bit(ABS_X, input_dev->absbit);
3588c2ecf20Sopenharmony_ci	__set_bit(ABS_Y, input_dev->absbit);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	__set_bit(BTN_TOUCH, input_dev->keybit);
3618c2ecf20Sopenharmony_ci	__set_bit(BTN_RIGHT, input_dev->keybit);
3628c2ecf20Sopenharmony_ci	__set_bit(BTN_TOOL_PEN, input_dev->keybit);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
3658c2ecf20Sopenharmony_ci	__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	input_set_abs_params(input_dev, ABS_X, -1500, 1500, 8, 0);
3688c2ecf20Sopenharmony_ci	input_set_abs_params(input_dev, ABS_Y, 1600, 3000, 8, 0);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	error = input_register_device(pegasus->dev);
3718c2ecf20Sopenharmony_ci	if (error)
3728c2ecf20Sopenharmony_ci		goto err_free_urb;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	return 0;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_cierr_free_urb:
3778c2ecf20Sopenharmony_ci	usb_free_urb(pegasus->irq);
3788c2ecf20Sopenharmony_cierr_free_dma:
3798c2ecf20Sopenharmony_ci	usb_free_coherent(dev, pegasus->data_len,
3808c2ecf20Sopenharmony_ci			  pegasus->data, pegasus->data_dma);
3818c2ecf20Sopenharmony_cierr_free_mem:
3828c2ecf20Sopenharmony_ci	input_free_device(input_dev);
3838c2ecf20Sopenharmony_ci	kfree(pegasus);
3848c2ecf20Sopenharmony_ci	usb_set_intfdata(intf, NULL);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	return error;
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistatic void pegasus_disconnect(struct usb_interface *intf)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	struct pegasus *pegasus = usb_get_intfdata(intf);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	input_unregister_device(pegasus->dev);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	usb_free_urb(pegasus->irq);
3968c2ecf20Sopenharmony_ci	usb_free_coherent(interface_to_usbdev(intf),
3978c2ecf20Sopenharmony_ci			  pegasus->data_len, pegasus->data,
3988c2ecf20Sopenharmony_ci			  pegasus->data_dma);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	kfree(pegasus);
4018c2ecf20Sopenharmony_ci	usb_set_intfdata(intf, NULL);
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic int pegasus_suspend(struct usb_interface *intf, pm_message_t message)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	struct pegasus *pegasus = usb_get_intfdata(intf);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	mutex_lock(&pegasus->pm_mutex);
4098c2ecf20Sopenharmony_ci	usb_kill_urb(pegasus->irq);
4108c2ecf20Sopenharmony_ci	cancel_work_sync(&pegasus->init);
4118c2ecf20Sopenharmony_ci	mutex_unlock(&pegasus->pm_mutex);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	return 0;
4148c2ecf20Sopenharmony_ci}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_cistatic int pegasus_resume(struct usb_interface *intf)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	struct pegasus *pegasus = usb_get_intfdata(intf);
4198c2ecf20Sopenharmony_ci	int retval = 0;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	mutex_lock(&pegasus->pm_mutex);
4228c2ecf20Sopenharmony_ci	if (pegasus->is_open && usb_submit_urb(pegasus->irq, GFP_NOIO) < 0)
4238c2ecf20Sopenharmony_ci		retval = -EIO;
4248c2ecf20Sopenharmony_ci	mutex_unlock(&pegasus->pm_mutex);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	return retval;
4278c2ecf20Sopenharmony_ci}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_cistatic int pegasus_reset_resume(struct usb_interface *intf)
4308c2ecf20Sopenharmony_ci{
4318c2ecf20Sopenharmony_ci	struct pegasus *pegasus = usb_get_intfdata(intf);
4328c2ecf20Sopenharmony_ci	int retval = 0;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	mutex_lock(&pegasus->pm_mutex);
4358c2ecf20Sopenharmony_ci	if (pegasus->is_open) {
4368c2ecf20Sopenharmony_ci		retval = pegasus_set_mode(pegasus, PEN_MODE_XY,
4378c2ecf20Sopenharmony_ci					  NOTETAKER_LED_MOUSE);
4388c2ecf20Sopenharmony_ci		if (!retval && usb_submit_urb(pegasus->irq, GFP_NOIO) < 0)
4398c2ecf20Sopenharmony_ci			retval = -EIO;
4408c2ecf20Sopenharmony_ci	}
4418c2ecf20Sopenharmony_ci	mutex_unlock(&pegasus->pm_mutex);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	return retval;
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_cistatic const struct usb_device_id pegasus_ids[] = {
4478c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_PEGASUSTECH,
4488c2ecf20Sopenharmony_ci		     USB_DEVICE_ID_PEGASUS_NOTETAKER_EN100) },
4498c2ecf20Sopenharmony_ci	{ }
4508c2ecf20Sopenharmony_ci};
4518c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, pegasus_ids);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_cistatic struct usb_driver pegasus_driver = {
4548c2ecf20Sopenharmony_ci	.name		= "pegasus_notetaker",
4558c2ecf20Sopenharmony_ci	.probe		= pegasus_probe,
4568c2ecf20Sopenharmony_ci	.disconnect	= pegasus_disconnect,
4578c2ecf20Sopenharmony_ci	.suspend	= pegasus_suspend,
4588c2ecf20Sopenharmony_ci	.resume		= pegasus_resume,
4598c2ecf20Sopenharmony_ci	.reset_resume	= pegasus_reset_resume,
4608c2ecf20Sopenharmony_ci	.id_table	= pegasus_ids,
4618c2ecf20Sopenharmony_ci	.supports_autosuspend = 1,
4628c2ecf20Sopenharmony_ci};
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_cimodule_usb_driver(pegasus_driver);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ciMODULE_AUTHOR("Martin Kepplinger <martink@posteo.de>");
4678c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Pegasus Mobile Notetaker Pen tablet driver");
4688c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
469