162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Pegasus Mobile Notetaker Pen input tablet driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2016 Martin Kepplinger <martink@posteo.de> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci/* 962306a36Sopenharmony_ci * request packet (control endpoint): 1062306a36Sopenharmony_ci * |-------------------------------------| 1162306a36Sopenharmony_ci * | Report ID | Nr of bytes | command | 1262306a36Sopenharmony_ci * | (1 byte) | (1 byte) | (n bytes) | 1362306a36Sopenharmony_ci * |-------------------------------------| 1462306a36Sopenharmony_ci * | 0x02 | n | | 1562306a36Sopenharmony_ci * |-------------------------------------| 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * data packet after set xy mode command, 0x80 0xb5 0x02 0x01 1862306a36Sopenharmony_ci * and pen is in range: 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * byte byte name value (bits) 2162306a36Sopenharmony_ci * -------------------------------------------- 2262306a36Sopenharmony_ci * 0 status 0 1 0 0 0 0 X X 2362306a36Sopenharmony_ci * 1 color 0 0 0 0 H 0 S T 2462306a36Sopenharmony_ci * 2 X low 2562306a36Sopenharmony_ci * 3 X high 2662306a36Sopenharmony_ci * 4 Y low 2762306a36Sopenharmony_ci * 5 Y high 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * X X battery state: 3062306a36Sopenharmony_ci * no state reported 0x00 3162306a36Sopenharmony_ci * battery low 0x01 3262306a36Sopenharmony_ci * battery good 0x02 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * H Hovering 3562306a36Sopenharmony_ci * S Switch 1 (pen button) 3662306a36Sopenharmony_ci * T Tip 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#include <linux/kernel.h> 4062306a36Sopenharmony_ci#include <linux/module.h> 4162306a36Sopenharmony_ci#include <linux/input.h> 4262306a36Sopenharmony_ci#include <linux/usb/input.h> 4362306a36Sopenharmony_ci#include <linux/slab.h> 4462306a36Sopenharmony_ci#include <linux/workqueue.h> 4562306a36Sopenharmony_ci#include <linux/mutex.h> 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* USB HID defines */ 4862306a36Sopenharmony_ci#define USB_REQ_GET_REPORT 0x01 4962306a36Sopenharmony_ci#define USB_REQ_SET_REPORT 0x09 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define USB_VENDOR_ID_PEGASUSTECH 0x0e20 5262306a36Sopenharmony_ci#define USB_DEVICE_ID_PEGASUS_NOTETAKER_EN100 0x0101 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* device specific defines */ 5562306a36Sopenharmony_ci#define NOTETAKER_REPORT_ID 0x02 5662306a36Sopenharmony_ci#define NOTETAKER_SET_CMD 0x80 5762306a36Sopenharmony_ci#define NOTETAKER_SET_MODE 0xb5 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define NOTETAKER_LED_MOUSE 0x02 6062306a36Sopenharmony_ci#define PEN_MODE_XY 0x01 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define SPECIAL_COMMAND 0x80 6362306a36Sopenharmony_ci#define BUTTON_PRESSED 0xb5 6462306a36Sopenharmony_ci#define COMMAND_VERSION 0xa9 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* in xy data packet */ 6762306a36Sopenharmony_ci#define BATTERY_NO_REPORT 0x40 6862306a36Sopenharmony_ci#define BATTERY_LOW 0x41 6962306a36Sopenharmony_ci#define BATTERY_GOOD 0x42 7062306a36Sopenharmony_ci#define PEN_BUTTON_PRESSED BIT(1) 7162306a36Sopenharmony_ci#define PEN_TIP BIT(0) 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistruct pegasus { 7462306a36Sopenharmony_ci unsigned char *data; 7562306a36Sopenharmony_ci u8 data_len; 7662306a36Sopenharmony_ci dma_addr_t data_dma; 7762306a36Sopenharmony_ci struct input_dev *dev; 7862306a36Sopenharmony_ci struct usb_device *usbdev; 7962306a36Sopenharmony_ci struct usb_interface *intf; 8062306a36Sopenharmony_ci struct urb *irq; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* serialize access to open/suspend */ 8362306a36Sopenharmony_ci struct mutex pm_mutex; 8462306a36Sopenharmony_ci bool is_open; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci char name[128]; 8762306a36Sopenharmony_ci char phys[64]; 8862306a36Sopenharmony_ci struct work_struct init; 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic int pegasus_control_msg(struct pegasus *pegasus, u8 *data, int len) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci const int sizeof_buf = len + 2; 9462306a36Sopenharmony_ci int result; 9562306a36Sopenharmony_ci int error; 9662306a36Sopenharmony_ci u8 *cmd_buf; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci cmd_buf = kmalloc(sizeof_buf, GFP_KERNEL); 9962306a36Sopenharmony_ci if (!cmd_buf) 10062306a36Sopenharmony_ci return -ENOMEM; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci cmd_buf[0] = NOTETAKER_REPORT_ID; 10362306a36Sopenharmony_ci cmd_buf[1] = len; 10462306a36Sopenharmony_ci memcpy(cmd_buf + 2, data, len); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci result = usb_control_msg(pegasus->usbdev, 10762306a36Sopenharmony_ci usb_sndctrlpipe(pegasus->usbdev, 0), 10862306a36Sopenharmony_ci USB_REQ_SET_REPORT, 10962306a36Sopenharmony_ci USB_TYPE_VENDOR | USB_DIR_OUT, 11062306a36Sopenharmony_ci 0, 0, cmd_buf, sizeof_buf, 11162306a36Sopenharmony_ci USB_CTRL_SET_TIMEOUT); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci kfree(cmd_buf); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (unlikely(result != sizeof_buf)) { 11662306a36Sopenharmony_ci error = result < 0 ? result : -EIO; 11762306a36Sopenharmony_ci dev_err(&pegasus->usbdev->dev, "control msg error: %d\n", 11862306a36Sopenharmony_ci error); 11962306a36Sopenharmony_ci return error; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return 0; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic int pegasus_set_mode(struct pegasus *pegasus, u8 mode, u8 led) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci u8 cmd[] = { NOTETAKER_SET_CMD, NOTETAKER_SET_MODE, led, mode }; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return pegasus_control_msg(pegasus, cmd, sizeof(cmd)); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic void pegasus_parse_packet(struct pegasus *pegasus) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci unsigned char *data = pegasus->data; 13562306a36Sopenharmony_ci struct input_dev *dev = pegasus->dev; 13662306a36Sopenharmony_ci u16 x, y; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci switch (data[0]) { 13962306a36Sopenharmony_ci case SPECIAL_COMMAND: 14062306a36Sopenharmony_ci /* device button pressed */ 14162306a36Sopenharmony_ci if (data[1] == BUTTON_PRESSED) 14262306a36Sopenharmony_ci schedule_work(&pegasus->init); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci break; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* xy data */ 14762306a36Sopenharmony_ci case BATTERY_LOW: 14862306a36Sopenharmony_ci dev_warn_once(&dev->dev, "Pen battery low\n"); 14962306a36Sopenharmony_ci fallthrough; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci case BATTERY_NO_REPORT: 15262306a36Sopenharmony_ci case BATTERY_GOOD: 15362306a36Sopenharmony_ci x = le16_to_cpup((__le16 *)&data[2]); 15462306a36Sopenharmony_ci y = le16_to_cpup((__le16 *)&data[4]); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* pen-up event */ 15762306a36Sopenharmony_ci if (x == 0 && y == 0) 15862306a36Sopenharmony_ci break; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci input_report_key(dev, BTN_TOUCH, data[1] & PEN_TIP); 16162306a36Sopenharmony_ci input_report_key(dev, BTN_RIGHT, data[1] & PEN_BUTTON_PRESSED); 16262306a36Sopenharmony_ci input_report_key(dev, BTN_TOOL_PEN, 1); 16362306a36Sopenharmony_ci input_report_abs(dev, ABS_X, (s16)x); 16462306a36Sopenharmony_ci input_report_abs(dev, ABS_Y, y); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci input_sync(dev); 16762306a36Sopenharmony_ci break; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci default: 17062306a36Sopenharmony_ci dev_warn_once(&pegasus->usbdev->dev, 17162306a36Sopenharmony_ci "unknown answer from device\n"); 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic void pegasus_irq(struct urb *urb) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct pegasus *pegasus = urb->context; 17862306a36Sopenharmony_ci struct usb_device *dev = pegasus->usbdev; 17962306a36Sopenharmony_ci int retval; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci switch (urb->status) { 18262306a36Sopenharmony_ci case 0: 18362306a36Sopenharmony_ci pegasus_parse_packet(pegasus); 18462306a36Sopenharmony_ci usb_mark_last_busy(pegasus->usbdev); 18562306a36Sopenharmony_ci break; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci case -ECONNRESET: 18862306a36Sopenharmony_ci case -ENOENT: 18962306a36Sopenharmony_ci case -ESHUTDOWN: 19062306a36Sopenharmony_ci dev_err(&dev->dev, "%s - urb shutting down with status: %d", 19162306a36Sopenharmony_ci __func__, urb->status); 19262306a36Sopenharmony_ci return; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci default: 19562306a36Sopenharmony_ci dev_err(&dev->dev, "%s - nonzero urb status received: %d", 19662306a36Sopenharmony_ci __func__, urb->status); 19762306a36Sopenharmony_ci break; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci retval = usb_submit_urb(urb, GFP_ATOMIC); 20162306a36Sopenharmony_ci if (retval) 20262306a36Sopenharmony_ci dev_err(&dev->dev, "%s - usb_submit_urb failed with result %d", 20362306a36Sopenharmony_ci __func__, retval); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic void pegasus_init(struct work_struct *work) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct pegasus *pegasus = container_of(work, struct pegasus, init); 20962306a36Sopenharmony_ci int error; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci error = pegasus_set_mode(pegasus, PEN_MODE_XY, NOTETAKER_LED_MOUSE); 21262306a36Sopenharmony_ci if (error) 21362306a36Sopenharmony_ci dev_err(&pegasus->usbdev->dev, "pegasus_set_mode error: %d\n", 21462306a36Sopenharmony_ci error); 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic int pegasus_open(struct input_dev *dev) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct pegasus *pegasus = input_get_drvdata(dev); 22062306a36Sopenharmony_ci int error; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci error = usb_autopm_get_interface(pegasus->intf); 22362306a36Sopenharmony_ci if (error) 22462306a36Sopenharmony_ci return error; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci mutex_lock(&pegasus->pm_mutex); 22762306a36Sopenharmony_ci pegasus->irq->dev = pegasus->usbdev; 22862306a36Sopenharmony_ci if (usb_submit_urb(pegasus->irq, GFP_KERNEL)) { 22962306a36Sopenharmony_ci error = -EIO; 23062306a36Sopenharmony_ci goto err_autopm_put; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci error = pegasus_set_mode(pegasus, PEN_MODE_XY, NOTETAKER_LED_MOUSE); 23462306a36Sopenharmony_ci if (error) 23562306a36Sopenharmony_ci goto err_kill_urb; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci pegasus->is_open = true; 23862306a36Sopenharmony_ci mutex_unlock(&pegasus->pm_mutex); 23962306a36Sopenharmony_ci return 0; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cierr_kill_urb: 24262306a36Sopenharmony_ci usb_kill_urb(pegasus->irq); 24362306a36Sopenharmony_ci cancel_work_sync(&pegasus->init); 24462306a36Sopenharmony_cierr_autopm_put: 24562306a36Sopenharmony_ci mutex_unlock(&pegasus->pm_mutex); 24662306a36Sopenharmony_ci usb_autopm_put_interface(pegasus->intf); 24762306a36Sopenharmony_ci return error; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic void pegasus_close(struct input_dev *dev) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci struct pegasus *pegasus = input_get_drvdata(dev); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci mutex_lock(&pegasus->pm_mutex); 25562306a36Sopenharmony_ci usb_kill_urb(pegasus->irq); 25662306a36Sopenharmony_ci cancel_work_sync(&pegasus->init); 25762306a36Sopenharmony_ci pegasus->is_open = false; 25862306a36Sopenharmony_ci mutex_unlock(&pegasus->pm_mutex); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci usb_autopm_put_interface(pegasus->intf); 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic int pegasus_probe(struct usb_interface *intf, 26462306a36Sopenharmony_ci const struct usb_device_id *id) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct usb_device *dev = interface_to_usbdev(intf); 26762306a36Sopenharmony_ci struct usb_endpoint_descriptor *endpoint; 26862306a36Sopenharmony_ci struct pegasus *pegasus; 26962306a36Sopenharmony_ci struct input_dev *input_dev; 27062306a36Sopenharmony_ci int error; 27162306a36Sopenharmony_ci int pipe; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* We control interface 0 */ 27462306a36Sopenharmony_ci if (intf->cur_altsetting->desc.bInterfaceNumber >= 1) 27562306a36Sopenharmony_ci return -ENODEV; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* Sanity check that the device has an endpoint */ 27862306a36Sopenharmony_ci if (intf->cur_altsetting->desc.bNumEndpoints < 1) { 27962306a36Sopenharmony_ci dev_err(&intf->dev, "Invalid number of endpoints\n"); 28062306a36Sopenharmony_ci return -EINVAL; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci endpoint = &intf->cur_altsetting->endpoint[0].desc; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci pegasus = kzalloc(sizeof(*pegasus), GFP_KERNEL); 28662306a36Sopenharmony_ci input_dev = input_allocate_device(); 28762306a36Sopenharmony_ci if (!pegasus || !input_dev) { 28862306a36Sopenharmony_ci error = -ENOMEM; 28962306a36Sopenharmony_ci goto err_free_mem; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci mutex_init(&pegasus->pm_mutex); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci pegasus->usbdev = dev; 29562306a36Sopenharmony_ci pegasus->dev = input_dev; 29662306a36Sopenharmony_ci pegasus->intf = intf; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); 29962306a36Sopenharmony_ci /* Sanity check that pipe's type matches endpoint's type */ 30062306a36Sopenharmony_ci if (usb_pipe_type_check(dev, pipe)) { 30162306a36Sopenharmony_ci error = -EINVAL; 30262306a36Sopenharmony_ci goto err_free_mem; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci pegasus->data_len = usb_maxpacket(dev, pipe); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci pegasus->data = usb_alloc_coherent(dev, pegasus->data_len, GFP_KERNEL, 30862306a36Sopenharmony_ci &pegasus->data_dma); 30962306a36Sopenharmony_ci if (!pegasus->data) { 31062306a36Sopenharmony_ci error = -ENOMEM; 31162306a36Sopenharmony_ci goto err_free_mem; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci pegasus->irq = usb_alloc_urb(0, GFP_KERNEL); 31562306a36Sopenharmony_ci if (!pegasus->irq) { 31662306a36Sopenharmony_ci error = -ENOMEM; 31762306a36Sopenharmony_ci goto err_free_dma; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci usb_fill_int_urb(pegasus->irq, dev, pipe, 32162306a36Sopenharmony_ci pegasus->data, pegasus->data_len, 32262306a36Sopenharmony_ci pegasus_irq, pegasus, endpoint->bInterval); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci pegasus->irq->transfer_dma = pegasus->data_dma; 32562306a36Sopenharmony_ci pegasus->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (dev->manufacturer) 32862306a36Sopenharmony_ci strscpy(pegasus->name, dev->manufacturer, 32962306a36Sopenharmony_ci sizeof(pegasus->name)); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (dev->product) { 33262306a36Sopenharmony_ci if (dev->manufacturer) 33362306a36Sopenharmony_ci strlcat(pegasus->name, " ", sizeof(pegasus->name)); 33462306a36Sopenharmony_ci strlcat(pegasus->name, dev->product, sizeof(pegasus->name)); 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (!strlen(pegasus->name)) 33862306a36Sopenharmony_ci snprintf(pegasus->name, sizeof(pegasus->name), 33962306a36Sopenharmony_ci "USB Pegasus Device %04x:%04x", 34062306a36Sopenharmony_ci le16_to_cpu(dev->descriptor.idVendor), 34162306a36Sopenharmony_ci le16_to_cpu(dev->descriptor.idProduct)); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci usb_make_path(dev, pegasus->phys, sizeof(pegasus->phys)); 34462306a36Sopenharmony_ci strlcat(pegasus->phys, "/input0", sizeof(pegasus->phys)); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci INIT_WORK(&pegasus->init, pegasus_init); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci usb_set_intfdata(intf, pegasus); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci input_dev->name = pegasus->name; 35162306a36Sopenharmony_ci input_dev->phys = pegasus->phys; 35262306a36Sopenharmony_ci usb_to_input_id(dev, &input_dev->id); 35362306a36Sopenharmony_ci input_dev->dev.parent = &intf->dev; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci input_set_drvdata(input_dev, pegasus); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci input_dev->open = pegasus_open; 35862306a36Sopenharmony_ci input_dev->close = pegasus_close; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci __set_bit(EV_ABS, input_dev->evbit); 36162306a36Sopenharmony_ci __set_bit(EV_KEY, input_dev->evbit); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci __set_bit(ABS_X, input_dev->absbit); 36462306a36Sopenharmony_ci __set_bit(ABS_Y, input_dev->absbit); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci __set_bit(BTN_TOUCH, input_dev->keybit); 36762306a36Sopenharmony_ci __set_bit(BTN_RIGHT, input_dev->keybit); 36862306a36Sopenharmony_ci __set_bit(BTN_TOOL_PEN, input_dev->keybit); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); 37162306a36Sopenharmony_ci __set_bit(INPUT_PROP_POINTER, input_dev->propbit); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_X, -1500, 1500, 8, 0); 37462306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_Y, 1600, 3000, 8, 0); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci error = input_register_device(pegasus->dev); 37762306a36Sopenharmony_ci if (error) 37862306a36Sopenharmony_ci goto err_free_urb; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci return 0; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cierr_free_urb: 38362306a36Sopenharmony_ci usb_free_urb(pegasus->irq); 38462306a36Sopenharmony_cierr_free_dma: 38562306a36Sopenharmony_ci usb_free_coherent(dev, pegasus->data_len, 38662306a36Sopenharmony_ci pegasus->data, pegasus->data_dma); 38762306a36Sopenharmony_cierr_free_mem: 38862306a36Sopenharmony_ci input_free_device(input_dev); 38962306a36Sopenharmony_ci kfree(pegasus); 39062306a36Sopenharmony_ci usb_set_intfdata(intf, NULL); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci return error; 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic void pegasus_disconnect(struct usb_interface *intf) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci struct pegasus *pegasus = usb_get_intfdata(intf); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci input_unregister_device(pegasus->dev); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci usb_free_urb(pegasus->irq); 40262306a36Sopenharmony_ci usb_free_coherent(interface_to_usbdev(intf), 40362306a36Sopenharmony_ci pegasus->data_len, pegasus->data, 40462306a36Sopenharmony_ci pegasus->data_dma); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci kfree(pegasus); 40762306a36Sopenharmony_ci usb_set_intfdata(intf, NULL); 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic int pegasus_suspend(struct usb_interface *intf, pm_message_t message) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct pegasus *pegasus = usb_get_intfdata(intf); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci mutex_lock(&pegasus->pm_mutex); 41562306a36Sopenharmony_ci usb_kill_urb(pegasus->irq); 41662306a36Sopenharmony_ci cancel_work_sync(&pegasus->init); 41762306a36Sopenharmony_ci mutex_unlock(&pegasus->pm_mutex); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci return 0; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic int pegasus_resume(struct usb_interface *intf) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct pegasus *pegasus = usb_get_intfdata(intf); 42562306a36Sopenharmony_ci int retval = 0; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci mutex_lock(&pegasus->pm_mutex); 42862306a36Sopenharmony_ci if (pegasus->is_open && usb_submit_urb(pegasus->irq, GFP_NOIO) < 0) 42962306a36Sopenharmony_ci retval = -EIO; 43062306a36Sopenharmony_ci mutex_unlock(&pegasus->pm_mutex); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci return retval; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic int pegasus_reset_resume(struct usb_interface *intf) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci struct pegasus *pegasus = usb_get_intfdata(intf); 43862306a36Sopenharmony_ci int retval = 0; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci mutex_lock(&pegasus->pm_mutex); 44162306a36Sopenharmony_ci if (pegasus->is_open) { 44262306a36Sopenharmony_ci retval = pegasus_set_mode(pegasus, PEN_MODE_XY, 44362306a36Sopenharmony_ci NOTETAKER_LED_MOUSE); 44462306a36Sopenharmony_ci if (!retval && usb_submit_urb(pegasus->irq, GFP_NOIO) < 0) 44562306a36Sopenharmony_ci retval = -EIO; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci mutex_unlock(&pegasus->pm_mutex); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci return retval; 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic const struct usb_device_id pegasus_ids[] = { 45362306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_PEGASUSTECH, 45462306a36Sopenharmony_ci USB_DEVICE_ID_PEGASUS_NOTETAKER_EN100) }, 45562306a36Sopenharmony_ci { } 45662306a36Sopenharmony_ci}; 45762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, pegasus_ids); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistatic struct usb_driver pegasus_driver = { 46062306a36Sopenharmony_ci .name = "pegasus_notetaker", 46162306a36Sopenharmony_ci .probe = pegasus_probe, 46262306a36Sopenharmony_ci .disconnect = pegasus_disconnect, 46362306a36Sopenharmony_ci .suspend = pegasus_suspend, 46462306a36Sopenharmony_ci .resume = pegasus_resume, 46562306a36Sopenharmony_ci .reset_resume = pegasus_reset_resume, 46662306a36Sopenharmony_ci .id_table = pegasus_ids, 46762306a36Sopenharmony_ci .supports_autosuspend = 1, 46862306a36Sopenharmony_ci}; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cimodule_usb_driver(pegasus_driver); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ciMODULE_AUTHOR("Martin Kepplinger <martink@posteo.de>"); 47362306a36Sopenharmony_ciMODULE_DESCRIPTION("Pegasus Mobile Notetaker Pen tablet driver"); 47462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 475