11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 24104d13fSDmitry Torokhov /* 34104d13fSDmitry Torokhov * Copyright (c) 2001-2005 Edouard TISSERANT <edouard.tisserant@wanadoo.fr> 44104d13fSDmitry Torokhov * Copyright (c) 2004-2005 Stephane VOLTZ <svoltz@numericable.fr> 54104d13fSDmitry Torokhov * 64104d13fSDmitry Torokhov * USB Acecad "Acecad Flair" tablet support 74104d13fSDmitry Torokhov * 84104d13fSDmitry Torokhov * Changelog: 94104d13fSDmitry Torokhov * v3.2 - Added sysfs support 104104d13fSDmitry Torokhov */ 114104d13fSDmitry Torokhov 124104d13fSDmitry Torokhov /* 134104d13fSDmitry Torokhov */ 144104d13fSDmitry Torokhov 154104d13fSDmitry Torokhov #include <linux/kernel.h> 164104d13fSDmitry Torokhov #include <linux/slab.h> 174104d13fSDmitry Torokhov #include <linux/module.h> 184104d13fSDmitry Torokhov #include <linux/usb/input.h> 194104d13fSDmitry Torokhov 20698c03b4SJulia Lawall MODULE_AUTHOR("Edouard TISSERANT <edouard.tisserant@wanadoo.fr>"); 21698c03b4SJulia Lawall MODULE_DESCRIPTION("USB Acecad Flair tablet driver"); 229f1d1ea3SGreg Kroah-Hartman MODULE_LICENSE("GPL"); 234104d13fSDmitry Torokhov 244104d13fSDmitry Torokhov #define USB_VENDOR_ID_ACECAD 0x0460 254104d13fSDmitry Torokhov #define USB_DEVICE_ID_FLAIR 0x0004 264104d13fSDmitry Torokhov #define USB_DEVICE_ID_302 0x0008 274104d13fSDmitry Torokhov 284104d13fSDmitry Torokhov struct usb_acecad { 294104d13fSDmitry Torokhov char name[128]; 304104d13fSDmitry Torokhov char phys[64]; 31334698d4SGreg Kroah-Hartman struct usb_interface *intf; 324104d13fSDmitry Torokhov struct input_dev *input; 334104d13fSDmitry Torokhov struct urb *irq; 344104d13fSDmitry Torokhov 354104d13fSDmitry Torokhov unsigned char *data; 364104d13fSDmitry Torokhov dma_addr_t data_dma; 374104d13fSDmitry Torokhov }; 384104d13fSDmitry Torokhov 394104d13fSDmitry Torokhov static void usb_acecad_irq(struct urb *urb) 404104d13fSDmitry Torokhov { 414104d13fSDmitry Torokhov struct usb_acecad *acecad = urb->context; 424104d13fSDmitry Torokhov unsigned char *data = acecad->data; 434104d13fSDmitry Torokhov struct input_dev *dev = acecad->input; 44334698d4SGreg Kroah-Hartman struct usb_interface *intf = acecad->intf; 457edf2c9cSOliver Neukum struct usb_device *udev = interface_to_usbdev(intf); 464104d13fSDmitry Torokhov int prox, status; 474104d13fSDmitry Torokhov 484104d13fSDmitry Torokhov switch (urb->status) { 494104d13fSDmitry Torokhov case 0: 504104d13fSDmitry Torokhov /* success */ 514104d13fSDmitry Torokhov break; 524104d13fSDmitry Torokhov case -ECONNRESET: 534104d13fSDmitry Torokhov case -ENOENT: 544104d13fSDmitry Torokhov case -ESHUTDOWN: 554104d13fSDmitry Torokhov /* this urb is terminated, clean up */ 56334698d4SGreg Kroah-Hartman dev_dbg(&intf->dev, "%s - urb shutting down with status: %d\n", 57eeba1ae1SGreg Kroah-Hartman __func__, urb->status); 584104d13fSDmitry Torokhov return; 594104d13fSDmitry Torokhov default: 60334698d4SGreg Kroah-Hartman dev_dbg(&intf->dev, "%s - nonzero urb status received: %d\n", 61eeba1ae1SGreg Kroah-Hartman __func__, urb->status); 624104d13fSDmitry Torokhov goto resubmit; 634104d13fSDmitry Torokhov } 644104d13fSDmitry Torokhov 654104d13fSDmitry Torokhov prox = (data[0] & 0x04) >> 2; 664104d13fSDmitry Torokhov input_report_key(dev, BTN_TOOL_PEN, prox); 674104d13fSDmitry Torokhov 684104d13fSDmitry Torokhov if (prox) { 694104d13fSDmitry Torokhov int x = data[1] | (data[2] << 8); 704104d13fSDmitry Torokhov int y = data[3] | (data[4] << 8); 714104d13fSDmitry Torokhov /* Pressure should compute the same way for flair and 302 */ 724104d13fSDmitry Torokhov int pressure = data[5] | (data[6] << 8); 734104d13fSDmitry Torokhov int touch = data[0] & 0x01; 744104d13fSDmitry Torokhov int stylus = (data[0] & 0x10) >> 4; 754104d13fSDmitry Torokhov int stylus2 = (data[0] & 0x20) >> 5; 764104d13fSDmitry Torokhov input_report_abs(dev, ABS_X, x); 774104d13fSDmitry Torokhov input_report_abs(dev, ABS_Y, y); 784104d13fSDmitry Torokhov input_report_abs(dev, ABS_PRESSURE, pressure); 794104d13fSDmitry Torokhov input_report_key(dev, BTN_TOUCH, touch); 804104d13fSDmitry Torokhov input_report_key(dev, BTN_STYLUS, stylus); 814104d13fSDmitry Torokhov input_report_key(dev, BTN_STYLUS2, stylus2); 824104d13fSDmitry Torokhov } 834104d13fSDmitry Torokhov 844104d13fSDmitry Torokhov /* event termination */ 854104d13fSDmitry Torokhov input_sync(dev); 864104d13fSDmitry Torokhov 874104d13fSDmitry Torokhov resubmit: 884104d13fSDmitry Torokhov status = usb_submit_urb(urb, GFP_ATOMIC); 894104d13fSDmitry Torokhov if (status) 90334698d4SGreg Kroah-Hartman dev_err(&intf->dev, 91b59c82bdSGreg Kroah-Hartman "can't resubmit intr, %s-%s/input0, status %d\n", 927edf2c9cSOliver Neukum udev->bus->bus_name, 937edf2c9cSOliver Neukum udev->devpath, status); 944104d13fSDmitry Torokhov } 954104d13fSDmitry Torokhov 964104d13fSDmitry Torokhov static int usb_acecad_open(struct input_dev *dev) 974104d13fSDmitry Torokhov { 984104d13fSDmitry Torokhov struct usb_acecad *acecad = input_get_drvdata(dev); 994104d13fSDmitry Torokhov 1007edf2c9cSOliver Neukum acecad->irq->dev = interface_to_usbdev(acecad->intf); 1014104d13fSDmitry Torokhov if (usb_submit_urb(acecad->irq, GFP_KERNEL)) 1024104d13fSDmitry Torokhov return -EIO; 1034104d13fSDmitry Torokhov 1044104d13fSDmitry Torokhov return 0; 1054104d13fSDmitry Torokhov } 1064104d13fSDmitry Torokhov 1074104d13fSDmitry Torokhov static void usb_acecad_close(struct input_dev *dev) 1084104d13fSDmitry Torokhov { 1094104d13fSDmitry Torokhov struct usb_acecad *acecad = input_get_drvdata(dev); 1104104d13fSDmitry Torokhov 1114104d13fSDmitry Torokhov usb_kill_urb(acecad->irq); 1124104d13fSDmitry Torokhov } 1134104d13fSDmitry Torokhov 1144104d13fSDmitry Torokhov static int usb_acecad_probe(struct usb_interface *intf, const struct usb_device_id *id) 1154104d13fSDmitry Torokhov { 1164104d13fSDmitry Torokhov struct usb_device *dev = interface_to_usbdev(intf); 1174104d13fSDmitry Torokhov struct usb_host_interface *interface = intf->cur_altsetting; 1184104d13fSDmitry Torokhov struct usb_endpoint_descriptor *endpoint; 1194104d13fSDmitry Torokhov struct usb_acecad *acecad; 1204104d13fSDmitry Torokhov struct input_dev *input_dev; 1214104d13fSDmitry Torokhov int pipe, maxp; 122b426571cSDmitry Torokhov int err; 1234104d13fSDmitry Torokhov 1244104d13fSDmitry Torokhov if (interface->desc.bNumEndpoints != 1) 1254104d13fSDmitry Torokhov return -ENODEV; 1264104d13fSDmitry Torokhov 1274104d13fSDmitry Torokhov endpoint = &interface->endpoint[0].desc; 1284104d13fSDmitry Torokhov 1294104d13fSDmitry Torokhov if (!usb_endpoint_is_int_in(endpoint)) 1304104d13fSDmitry Torokhov return -ENODEV; 1314104d13fSDmitry Torokhov 1324104d13fSDmitry Torokhov pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); 133*948bf187SVincent Mailhol maxp = usb_maxpacket(dev, pipe); 1344104d13fSDmitry Torokhov 1354104d13fSDmitry Torokhov acecad = kzalloc(sizeof(struct usb_acecad), GFP_KERNEL); 1364104d13fSDmitry Torokhov input_dev = input_allocate_device(); 1374104d13fSDmitry Torokhov if (!acecad || !input_dev) { 1384104d13fSDmitry Torokhov err = -ENOMEM; 1394104d13fSDmitry Torokhov goto fail1; 1404104d13fSDmitry Torokhov } 1414104d13fSDmitry Torokhov 142997ea58eSDaniel Mack acecad->data = usb_alloc_coherent(dev, 8, GFP_KERNEL, &acecad->data_dma); 1434104d13fSDmitry Torokhov if (!acecad->data) { 1444104d13fSDmitry Torokhov err= -ENOMEM; 1454104d13fSDmitry Torokhov goto fail1; 1464104d13fSDmitry Torokhov } 1474104d13fSDmitry Torokhov 1484104d13fSDmitry Torokhov acecad->irq = usb_alloc_urb(0, GFP_KERNEL); 1494104d13fSDmitry Torokhov if (!acecad->irq) { 1504104d13fSDmitry Torokhov err = -ENOMEM; 1514104d13fSDmitry Torokhov goto fail2; 1524104d13fSDmitry Torokhov } 1534104d13fSDmitry Torokhov 154334698d4SGreg Kroah-Hartman acecad->intf = intf; 1554104d13fSDmitry Torokhov acecad->input = input_dev; 1564104d13fSDmitry Torokhov 1574104d13fSDmitry Torokhov if (dev->manufacturer) 1584104d13fSDmitry Torokhov strlcpy(acecad->name, dev->manufacturer, sizeof(acecad->name)); 1594104d13fSDmitry Torokhov 1604104d13fSDmitry Torokhov if (dev->product) { 1614104d13fSDmitry Torokhov if (dev->manufacturer) 1624104d13fSDmitry Torokhov strlcat(acecad->name, " ", sizeof(acecad->name)); 1634104d13fSDmitry Torokhov strlcat(acecad->name, dev->product, sizeof(acecad->name)); 1644104d13fSDmitry Torokhov } 1654104d13fSDmitry Torokhov 1664104d13fSDmitry Torokhov usb_make_path(dev, acecad->phys, sizeof(acecad->phys)); 1674104d13fSDmitry Torokhov strlcat(acecad->phys, "/input0", sizeof(acecad->phys)); 1684104d13fSDmitry Torokhov 1694104d13fSDmitry Torokhov input_dev->name = acecad->name; 1704104d13fSDmitry Torokhov input_dev->phys = acecad->phys; 1714104d13fSDmitry Torokhov usb_to_input_id(dev, &input_dev->id); 1724104d13fSDmitry Torokhov input_dev->dev.parent = &intf->dev; 1734104d13fSDmitry Torokhov 1744104d13fSDmitry Torokhov input_set_drvdata(input_dev, acecad); 1754104d13fSDmitry Torokhov 1764104d13fSDmitry Torokhov input_dev->open = usb_acecad_open; 1774104d13fSDmitry Torokhov input_dev->close = usb_acecad_close; 1784104d13fSDmitry Torokhov 1797b19ada2SJiri Slaby input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 1807b19ada2SJiri Slaby input_dev->keybit[BIT_WORD(BTN_DIGI)] = BIT_MASK(BTN_TOOL_PEN) | 1817b19ada2SJiri Slaby BIT_MASK(BTN_TOUCH) | BIT_MASK(BTN_STYLUS) | 1827b19ada2SJiri Slaby BIT_MASK(BTN_STYLUS2); 1834104d13fSDmitry Torokhov 1844104d13fSDmitry Torokhov switch (id->driver_info) { 1854104d13fSDmitry Torokhov case 0: 186b426571cSDmitry Torokhov input_set_abs_params(input_dev, ABS_X, 0, 5000, 4, 0); 187b426571cSDmitry Torokhov input_set_abs_params(input_dev, ABS_Y, 0, 3750, 4, 0); 188b426571cSDmitry Torokhov input_set_abs_params(input_dev, ABS_PRESSURE, 0, 512, 0, 0); 1894104d13fSDmitry Torokhov if (!strlen(acecad->name)) 1904104d13fSDmitry Torokhov snprintf(acecad->name, sizeof(acecad->name), 1914104d13fSDmitry Torokhov "USB Acecad Flair Tablet %04x:%04x", 1924104d13fSDmitry Torokhov le16_to_cpu(dev->descriptor.idVendor), 1934104d13fSDmitry Torokhov le16_to_cpu(dev->descriptor.idProduct)); 1944104d13fSDmitry Torokhov break; 195b426571cSDmitry Torokhov 1964104d13fSDmitry Torokhov case 1: 197b426571cSDmitry Torokhov input_set_abs_params(input_dev, ABS_X, 0, 53000, 4, 0); 198b426571cSDmitry Torokhov input_set_abs_params(input_dev, ABS_Y, 0, 2250, 4, 0); 199b426571cSDmitry Torokhov input_set_abs_params(input_dev, ABS_PRESSURE, 0, 1024, 0, 0); 2004104d13fSDmitry Torokhov if (!strlen(acecad->name)) 2014104d13fSDmitry Torokhov snprintf(acecad->name, sizeof(acecad->name), 2024104d13fSDmitry Torokhov "USB Acecad 302 Tablet %04x:%04x", 2034104d13fSDmitry Torokhov le16_to_cpu(dev->descriptor.idVendor), 2044104d13fSDmitry Torokhov le16_to_cpu(dev->descriptor.idProduct)); 2054104d13fSDmitry Torokhov break; 2064104d13fSDmitry Torokhov } 2074104d13fSDmitry Torokhov 2084104d13fSDmitry Torokhov usb_fill_int_urb(acecad->irq, dev, pipe, 2094104d13fSDmitry Torokhov acecad->data, maxp > 8 ? 8 : maxp, 2104104d13fSDmitry Torokhov usb_acecad_irq, acecad, endpoint->bInterval); 2114104d13fSDmitry Torokhov acecad->irq->transfer_dma = acecad->data_dma; 2124104d13fSDmitry Torokhov acecad->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 2134104d13fSDmitry Torokhov 2144104d13fSDmitry Torokhov err = input_register_device(acecad->input); 2154104d13fSDmitry Torokhov if (err) 216a4503199SAxel Lin goto fail3; 2174104d13fSDmitry Torokhov 2184104d13fSDmitry Torokhov usb_set_intfdata(intf, acecad); 2194104d13fSDmitry Torokhov 2204104d13fSDmitry Torokhov return 0; 2214104d13fSDmitry Torokhov 222a4503199SAxel Lin fail3: usb_free_urb(acecad->irq); 223997ea58eSDaniel Mack fail2: usb_free_coherent(dev, 8, acecad->data, acecad->data_dma); 2244104d13fSDmitry Torokhov fail1: input_free_device(input_dev); 2254104d13fSDmitry Torokhov kfree(acecad); 2264104d13fSDmitry Torokhov return err; 2274104d13fSDmitry Torokhov } 2284104d13fSDmitry Torokhov 2294104d13fSDmitry Torokhov static void usb_acecad_disconnect(struct usb_interface *intf) 2304104d13fSDmitry Torokhov { 2314104d13fSDmitry Torokhov struct usb_acecad *acecad = usb_get_intfdata(intf); 2327edf2c9cSOliver Neukum struct usb_device *udev = interface_to_usbdev(intf); 2334104d13fSDmitry Torokhov 2344104d13fSDmitry Torokhov usb_set_intfdata(intf, NULL); 2355492f6f8SDmitry Torokhov 2364104d13fSDmitry Torokhov input_unregister_device(acecad->input); 2374104d13fSDmitry Torokhov usb_free_urb(acecad->irq); 2387edf2c9cSOliver Neukum usb_free_coherent(udev, 8, acecad->data, acecad->data_dma); 2394104d13fSDmitry Torokhov kfree(acecad); 2404104d13fSDmitry Torokhov } 2414104d13fSDmitry Torokhov 242803088faSArvind Yadav static const struct usb_device_id usb_acecad_id_table[] = { 2434104d13fSDmitry Torokhov { USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_FLAIR), .driver_info = 0 }, 2444104d13fSDmitry Torokhov { USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_302), .driver_info = 1 }, 2454104d13fSDmitry Torokhov { } 2464104d13fSDmitry Torokhov }; 2474104d13fSDmitry Torokhov 2484104d13fSDmitry Torokhov MODULE_DEVICE_TABLE(usb, usb_acecad_id_table); 2494104d13fSDmitry Torokhov 2504104d13fSDmitry Torokhov static struct usb_driver usb_acecad_driver = { 2514104d13fSDmitry Torokhov .name = "usb_acecad", 2524104d13fSDmitry Torokhov .probe = usb_acecad_probe, 2534104d13fSDmitry Torokhov .disconnect = usb_acecad_disconnect, 2544104d13fSDmitry Torokhov .id_table = usb_acecad_id_table, 2554104d13fSDmitry Torokhov }; 2564104d13fSDmitry Torokhov 25708642e7cSGreg Kroah-Hartman module_usb_driver(usb_acecad_driver); 258