14104d13fSDmitry Torokhov /* 24104d13fSDmitry Torokhov * Copyright (c) 2001-2005 Edouard TISSERANT <edouard.tisserant@wanadoo.fr> 34104d13fSDmitry Torokhov * Copyright (c) 2004-2005 Stephane VOLTZ <svoltz@numericable.fr> 44104d13fSDmitry Torokhov * 54104d13fSDmitry Torokhov * USB Acecad "Acecad Flair" tablet support 64104d13fSDmitry Torokhov * 74104d13fSDmitry Torokhov * Changelog: 84104d13fSDmitry Torokhov * v3.2 - Added sysfs support 94104d13fSDmitry Torokhov */ 104104d13fSDmitry Torokhov 114104d13fSDmitry Torokhov /* 124104d13fSDmitry Torokhov * This program is free software; you can redistribute it and/or modify 134104d13fSDmitry Torokhov * it under the terms of the GNU General Public License as published by 144104d13fSDmitry Torokhov * the Free Software Foundation; either version 2 of the License, or 154104d13fSDmitry Torokhov * (at your option) any later version. 164104d13fSDmitry Torokhov * 174104d13fSDmitry Torokhov * This program is distributed in the hope that it will be useful, 184104d13fSDmitry Torokhov * but WITHOUT ANY WARRANTY; without even the implied warranty of 194104d13fSDmitry Torokhov * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 204104d13fSDmitry Torokhov * GNU General Public License for more details. 214104d13fSDmitry Torokhov * 224104d13fSDmitry Torokhov * You should have received a copy of the GNU General Public License 234104d13fSDmitry Torokhov * along with this program; if not, write to the Free Software 244104d13fSDmitry Torokhov * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 254104d13fSDmitry Torokhov * 264104d13fSDmitry Torokhov */ 274104d13fSDmitry Torokhov 284104d13fSDmitry Torokhov #include <linux/kernel.h> 294104d13fSDmitry Torokhov #include <linux/slab.h> 304104d13fSDmitry Torokhov #include <linux/module.h> 314104d13fSDmitry Torokhov #include <linux/init.h> 324104d13fSDmitry Torokhov #include <linux/usb/input.h> 334104d13fSDmitry Torokhov 344104d13fSDmitry Torokhov /* 354104d13fSDmitry Torokhov * Version Information 364104d13fSDmitry Torokhov */ 374104d13fSDmitry Torokhov #define DRIVER_VERSION "v3.2" 384104d13fSDmitry Torokhov #define DRIVER_DESC "USB Acecad Flair tablet driver" 394104d13fSDmitry Torokhov #define DRIVER_LICENSE "GPL" 404104d13fSDmitry Torokhov #define DRIVER_AUTHOR "Edouard TISSERANT <edouard.tisserant@wanadoo.fr>" 414104d13fSDmitry Torokhov 424104d13fSDmitry Torokhov MODULE_AUTHOR(DRIVER_AUTHOR); 434104d13fSDmitry Torokhov MODULE_DESCRIPTION(DRIVER_DESC); 444104d13fSDmitry Torokhov MODULE_LICENSE(DRIVER_LICENSE); 454104d13fSDmitry Torokhov 464104d13fSDmitry Torokhov #define USB_VENDOR_ID_ACECAD 0x0460 474104d13fSDmitry Torokhov #define USB_DEVICE_ID_FLAIR 0x0004 484104d13fSDmitry Torokhov #define USB_DEVICE_ID_302 0x0008 494104d13fSDmitry Torokhov 504104d13fSDmitry Torokhov struct usb_acecad { 514104d13fSDmitry Torokhov char name[128]; 524104d13fSDmitry Torokhov char phys[64]; 534104d13fSDmitry Torokhov struct usb_device *usbdev; 544104d13fSDmitry Torokhov struct input_dev *input; 554104d13fSDmitry Torokhov struct urb *irq; 564104d13fSDmitry Torokhov 574104d13fSDmitry Torokhov unsigned char *data; 584104d13fSDmitry Torokhov dma_addr_t data_dma; 594104d13fSDmitry Torokhov }; 604104d13fSDmitry Torokhov 614104d13fSDmitry Torokhov static void usb_acecad_irq(struct urb *urb) 624104d13fSDmitry Torokhov { 634104d13fSDmitry Torokhov struct usb_acecad *acecad = urb->context; 644104d13fSDmitry Torokhov unsigned char *data = acecad->data; 654104d13fSDmitry Torokhov struct input_dev *dev = acecad->input; 664104d13fSDmitry Torokhov int prox, status; 674104d13fSDmitry Torokhov 684104d13fSDmitry Torokhov switch (urb->status) { 694104d13fSDmitry Torokhov case 0: 704104d13fSDmitry Torokhov /* success */ 714104d13fSDmitry Torokhov break; 724104d13fSDmitry Torokhov case -ECONNRESET: 734104d13fSDmitry Torokhov case -ENOENT: 744104d13fSDmitry Torokhov case -ESHUTDOWN: 754104d13fSDmitry Torokhov /* this urb is terminated, clean up */ 76ea3e6c59SHarvey Harrison dbg("%s - urb shutting down with status: %d", __func__, urb->status); 774104d13fSDmitry Torokhov return; 784104d13fSDmitry Torokhov default: 79ea3e6c59SHarvey Harrison dbg("%s - nonzero urb status received: %d", __func__, urb->status); 804104d13fSDmitry Torokhov goto resubmit; 814104d13fSDmitry Torokhov } 824104d13fSDmitry Torokhov 834104d13fSDmitry Torokhov prox = (data[0] & 0x04) >> 2; 844104d13fSDmitry Torokhov input_report_key(dev, BTN_TOOL_PEN, prox); 854104d13fSDmitry Torokhov 864104d13fSDmitry Torokhov if (prox) { 874104d13fSDmitry Torokhov int x = data[1] | (data[2] << 8); 884104d13fSDmitry Torokhov int y = data[3] | (data[4] << 8); 894104d13fSDmitry Torokhov /* Pressure should compute the same way for flair and 302 */ 904104d13fSDmitry Torokhov int pressure = data[5] | (data[6] << 8); 914104d13fSDmitry Torokhov int touch = data[0] & 0x01; 924104d13fSDmitry Torokhov int stylus = (data[0] & 0x10) >> 4; 934104d13fSDmitry Torokhov int stylus2 = (data[0] & 0x20) >> 5; 944104d13fSDmitry Torokhov input_report_abs(dev, ABS_X, x); 954104d13fSDmitry Torokhov input_report_abs(dev, ABS_Y, y); 964104d13fSDmitry Torokhov input_report_abs(dev, ABS_PRESSURE, pressure); 974104d13fSDmitry Torokhov input_report_key(dev, BTN_TOUCH, touch); 984104d13fSDmitry Torokhov input_report_key(dev, BTN_STYLUS, stylus); 994104d13fSDmitry Torokhov input_report_key(dev, BTN_STYLUS2, stylus2); 1004104d13fSDmitry Torokhov } 1014104d13fSDmitry Torokhov 1024104d13fSDmitry Torokhov /* event termination */ 1034104d13fSDmitry Torokhov input_sync(dev); 1044104d13fSDmitry Torokhov 1054104d13fSDmitry Torokhov resubmit: 1064104d13fSDmitry Torokhov status = usb_submit_urb(urb, GFP_ATOMIC); 1074104d13fSDmitry Torokhov if (status) 108b59c82bdSGreg Kroah-Hartman dev_err(&acecad->usbdev->dev, 109b59c82bdSGreg Kroah-Hartman "can't resubmit intr, %s-%s/input0, status %d\n", 110b59c82bdSGreg Kroah-Hartman acecad->usbdev->bus->bus_name, 111b59c82bdSGreg Kroah-Hartman acecad->usbdev->devpath, status); 1124104d13fSDmitry Torokhov } 1134104d13fSDmitry Torokhov 1144104d13fSDmitry Torokhov static int usb_acecad_open(struct input_dev *dev) 1154104d13fSDmitry Torokhov { 1164104d13fSDmitry Torokhov struct usb_acecad *acecad = input_get_drvdata(dev); 1174104d13fSDmitry Torokhov 1184104d13fSDmitry Torokhov acecad->irq->dev = acecad->usbdev; 1194104d13fSDmitry Torokhov if (usb_submit_urb(acecad->irq, GFP_KERNEL)) 1204104d13fSDmitry Torokhov return -EIO; 1214104d13fSDmitry Torokhov 1224104d13fSDmitry Torokhov return 0; 1234104d13fSDmitry Torokhov } 1244104d13fSDmitry Torokhov 1254104d13fSDmitry Torokhov static void usb_acecad_close(struct input_dev *dev) 1264104d13fSDmitry Torokhov { 1274104d13fSDmitry Torokhov struct usb_acecad *acecad = input_get_drvdata(dev); 1284104d13fSDmitry Torokhov 1294104d13fSDmitry Torokhov usb_kill_urb(acecad->irq); 1304104d13fSDmitry Torokhov } 1314104d13fSDmitry Torokhov 1324104d13fSDmitry Torokhov static int usb_acecad_probe(struct usb_interface *intf, const struct usb_device_id *id) 1334104d13fSDmitry Torokhov { 1344104d13fSDmitry Torokhov struct usb_device *dev = interface_to_usbdev(intf); 1354104d13fSDmitry Torokhov struct usb_host_interface *interface = intf->cur_altsetting; 1364104d13fSDmitry Torokhov struct usb_endpoint_descriptor *endpoint; 1374104d13fSDmitry Torokhov struct usb_acecad *acecad; 1384104d13fSDmitry Torokhov struct input_dev *input_dev; 1394104d13fSDmitry Torokhov int pipe, maxp; 140b426571cSDmitry Torokhov int err; 1414104d13fSDmitry Torokhov 1424104d13fSDmitry Torokhov if (interface->desc.bNumEndpoints != 1) 1434104d13fSDmitry Torokhov return -ENODEV; 1444104d13fSDmitry Torokhov 1454104d13fSDmitry Torokhov endpoint = &interface->endpoint[0].desc; 1464104d13fSDmitry Torokhov 1474104d13fSDmitry Torokhov if (!usb_endpoint_is_int_in(endpoint)) 1484104d13fSDmitry Torokhov return -ENODEV; 1494104d13fSDmitry Torokhov 1504104d13fSDmitry Torokhov pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); 1514104d13fSDmitry Torokhov maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); 1524104d13fSDmitry Torokhov 1534104d13fSDmitry Torokhov acecad = kzalloc(sizeof(struct usb_acecad), GFP_KERNEL); 1544104d13fSDmitry Torokhov input_dev = input_allocate_device(); 1554104d13fSDmitry Torokhov if (!acecad || !input_dev) { 1564104d13fSDmitry Torokhov err = -ENOMEM; 1574104d13fSDmitry Torokhov goto fail1; 1584104d13fSDmitry Torokhov } 1594104d13fSDmitry Torokhov 160997ea58eSDaniel Mack acecad->data = usb_alloc_coherent(dev, 8, GFP_KERNEL, &acecad->data_dma); 1614104d13fSDmitry Torokhov if (!acecad->data) { 1624104d13fSDmitry Torokhov err= -ENOMEM; 1634104d13fSDmitry Torokhov goto fail1; 1644104d13fSDmitry Torokhov } 1654104d13fSDmitry Torokhov 1664104d13fSDmitry Torokhov acecad->irq = usb_alloc_urb(0, GFP_KERNEL); 1674104d13fSDmitry Torokhov if (!acecad->irq) { 1684104d13fSDmitry Torokhov err = -ENOMEM; 1694104d13fSDmitry Torokhov goto fail2; 1704104d13fSDmitry Torokhov } 1714104d13fSDmitry Torokhov 1724104d13fSDmitry Torokhov acecad->usbdev = dev; 1734104d13fSDmitry Torokhov acecad->input = input_dev; 1744104d13fSDmitry Torokhov 1754104d13fSDmitry Torokhov if (dev->manufacturer) 1764104d13fSDmitry Torokhov strlcpy(acecad->name, dev->manufacturer, sizeof(acecad->name)); 1774104d13fSDmitry Torokhov 1784104d13fSDmitry Torokhov if (dev->product) { 1794104d13fSDmitry Torokhov if (dev->manufacturer) 1804104d13fSDmitry Torokhov strlcat(acecad->name, " ", sizeof(acecad->name)); 1814104d13fSDmitry Torokhov strlcat(acecad->name, dev->product, sizeof(acecad->name)); 1824104d13fSDmitry Torokhov } 1834104d13fSDmitry Torokhov 1844104d13fSDmitry Torokhov usb_make_path(dev, acecad->phys, sizeof(acecad->phys)); 1854104d13fSDmitry Torokhov strlcat(acecad->phys, "/input0", sizeof(acecad->phys)); 1864104d13fSDmitry Torokhov 1874104d13fSDmitry Torokhov input_dev->name = acecad->name; 1884104d13fSDmitry Torokhov input_dev->phys = acecad->phys; 1894104d13fSDmitry Torokhov usb_to_input_id(dev, &input_dev->id); 1904104d13fSDmitry Torokhov input_dev->dev.parent = &intf->dev; 1914104d13fSDmitry Torokhov 1924104d13fSDmitry Torokhov input_set_drvdata(input_dev, acecad); 1934104d13fSDmitry Torokhov 1944104d13fSDmitry Torokhov input_dev->open = usb_acecad_open; 1954104d13fSDmitry Torokhov input_dev->close = usb_acecad_close; 1964104d13fSDmitry Torokhov 1977b19ada2SJiri Slaby input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 1987b19ada2SJiri Slaby input_dev->keybit[BIT_WORD(BTN_DIGI)] = BIT_MASK(BTN_TOOL_PEN) | 1997b19ada2SJiri Slaby BIT_MASK(BTN_TOUCH) | BIT_MASK(BTN_STYLUS) | 2007b19ada2SJiri Slaby BIT_MASK(BTN_STYLUS2); 2014104d13fSDmitry Torokhov 2024104d13fSDmitry Torokhov switch (id->driver_info) { 2034104d13fSDmitry Torokhov case 0: 204b426571cSDmitry Torokhov input_set_abs_params(input_dev, ABS_X, 0, 5000, 4, 0); 205b426571cSDmitry Torokhov input_set_abs_params(input_dev, ABS_Y, 0, 3750, 4, 0); 206b426571cSDmitry Torokhov input_set_abs_params(input_dev, ABS_PRESSURE, 0, 512, 0, 0); 2074104d13fSDmitry Torokhov if (!strlen(acecad->name)) 2084104d13fSDmitry Torokhov snprintf(acecad->name, sizeof(acecad->name), 2094104d13fSDmitry Torokhov "USB Acecad Flair Tablet %04x:%04x", 2104104d13fSDmitry Torokhov le16_to_cpu(dev->descriptor.idVendor), 2114104d13fSDmitry Torokhov le16_to_cpu(dev->descriptor.idProduct)); 2124104d13fSDmitry Torokhov break; 213b426571cSDmitry Torokhov 2144104d13fSDmitry Torokhov case 1: 215b426571cSDmitry Torokhov input_set_abs_params(input_dev, ABS_X, 0, 53000, 4, 0); 216b426571cSDmitry Torokhov input_set_abs_params(input_dev, ABS_Y, 0, 2250, 4, 0); 217b426571cSDmitry Torokhov input_set_abs_params(input_dev, ABS_PRESSURE, 0, 1024, 0, 0); 2184104d13fSDmitry Torokhov if (!strlen(acecad->name)) 2194104d13fSDmitry Torokhov snprintf(acecad->name, sizeof(acecad->name), 2204104d13fSDmitry Torokhov "USB Acecad 302 Tablet %04x:%04x", 2214104d13fSDmitry Torokhov le16_to_cpu(dev->descriptor.idVendor), 2224104d13fSDmitry Torokhov le16_to_cpu(dev->descriptor.idProduct)); 2234104d13fSDmitry Torokhov break; 2244104d13fSDmitry Torokhov } 2254104d13fSDmitry Torokhov 2264104d13fSDmitry Torokhov usb_fill_int_urb(acecad->irq, dev, pipe, 2274104d13fSDmitry Torokhov acecad->data, maxp > 8 ? 8 : maxp, 2284104d13fSDmitry Torokhov usb_acecad_irq, acecad, endpoint->bInterval); 2294104d13fSDmitry Torokhov acecad->irq->transfer_dma = acecad->data_dma; 2304104d13fSDmitry Torokhov acecad->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 2314104d13fSDmitry Torokhov 2324104d13fSDmitry Torokhov err = input_register_device(acecad->input); 2334104d13fSDmitry Torokhov if (err) 234a4503199SAxel Lin goto fail3; 2354104d13fSDmitry Torokhov 2364104d13fSDmitry Torokhov usb_set_intfdata(intf, acecad); 2374104d13fSDmitry Torokhov 2384104d13fSDmitry Torokhov return 0; 2394104d13fSDmitry Torokhov 240a4503199SAxel Lin fail3: usb_free_urb(acecad->irq); 241997ea58eSDaniel Mack fail2: usb_free_coherent(dev, 8, acecad->data, acecad->data_dma); 2424104d13fSDmitry Torokhov fail1: input_free_device(input_dev); 2434104d13fSDmitry Torokhov kfree(acecad); 2444104d13fSDmitry Torokhov return err; 2454104d13fSDmitry Torokhov } 2464104d13fSDmitry Torokhov 2474104d13fSDmitry Torokhov static void usb_acecad_disconnect(struct usb_interface *intf) 2484104d13fSDmitry Torokhov { 2494104d13fSDmitry Torokhov struct usb_acecad *acecad = usb_get_intfdata(intf); 2504104d13fSDmitry Torokhov 2514104d13fSDmitry Torokhov usb_set_intfdata(intf, NULL); 2525492f6f8SDmitry Torokhov 2534104d13fSDmitry Torokhov input_unregister_device(acecad->input); 2544104d13fSDmitry Torokhov usb_free_urb(acecad->irq); 2557a9b1492SLinus Torvalds usb_free_coherent(acecad->usbdev, 8, acecad->data, acecad->data_dma); 2564104d13fSDmitry Torokhov kfree(acecad); 2574104d13fSDmitry Torokhov } 2584104d13fSDmitry Torokhov 2594104d13fSDmitry Torokhov static struct usb_device_id usb_acecad_id_table [] = { 2604104d13fSDmitry Torokhov { USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_FLAIR), .driver_info = 0 }, 2614104d13fSDmitry Torokhov { USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_302), .driver_info = 1 }, 2624104d13fSDmitry Torokhov { } 2634104d13fSDmitry Torokhov }; 2644104d13fSDmitry Torokhov 2654104d13fSDmitry Torokhov MODULE_DEVICE_TABLE(usb, usb_acecad_id_table); 2664104d13fSDmitry Torokhov 2674104d13fSDmitry Torokhov static struct usb_driver usb_acecad_driver = { 2684104d13fSDmitry Torokhov .name = "usb_acecad", 2694104d13fSDmitry Torokhov .probe = usb_acecad_probe, 2704104d13fSDmitry Torokhov .disconnect = usb_acecad_disconnect, 2714104d13fSDmitry Torokhov .id_table = usb_acecad_id_table, 2724104d13fSDmitry Torokhov }; 2734104d13fSDmitry Torokhov 27408642e7cSGreg Kroah-Hartman module_usb_driver(usb_acecad_driver); 275