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