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 */ 76eeba1ae1SGreg Kroah-Hartman dev_dbg(&dev->dev, "%s - urb shutting down with status: %d\n", 77eeba1ae1SGreg Kroah-Hartman __func__, urb->status); 784104d13fSDmitry Torokhov return; 794104d13fSDmitry Torokhov default: 80eeba1ae1SGreg Kroah-Hartman dev_dbg(&dev->dev, "%s - nonzero urb status received: %d\n", 81eeba1ae1SGreg Kroah-Hartman __func__, urb->status); 824104d13fSDmitry Torokhov goto resubmit; 834104d13fSDmitry Torokhov } 844104d13fSDmitry Torokhov 854104d13fSDmitry Torokhov prox = (data[0] & 0x04) >> 2; 864104d13fSDmitry Torokhov input_report_key(dev, BTN_TOOL_PEN, prox); 874104d13fSDmitry Torokhov 884104d13fSDmitry Torokhov if (prox) { 894104d13fSDmitry Torokhov int x = data[1] | (data[2] << 8); 904104d13fSDmitry Torokhov int y = data[3] | (data[4] << 8); 914104d13fSDmitry Torokhov /* Pressure should compute the same way for flair and 302 */ 924104d13fSDmitry Torokhov int pressure = data[5] | (data[6] << 8); 934104d13fSDmitry Torokhov int touch = data[0] & 0x01; 944104d13fSDmitry Torokhov int stylus = (data[0] & 0x10) >> 4; 954104d13fSDmitry Torokhov int stylus2 = (data[0] & 0x20) >> 5; 964104d13fSDmitry Torokhov input_report_abs(dev, ABS_X, x); 974104d13fSDmitry Torokhov input_report_abs(dev, ABS_Y, y); 984104d13fSDmitry Torokhov input_report_abs(dev, ABS_PRESSURE, pressure); 994104d13fSDmitry Torokhov input_report_key(dev, BTN_TOUCH, touch); 1004104d13fSDmitry Torokhov input_report_key(dev, BTN_STYLUS, stylus); 1014104d13fSDmitry Torokhov input_report_key(dev, BTN_STYLUS2, stylus2); 1024104d13fSDmitry Torokhov } 1034104d13fSDmitry Torokhov 1044104d13fSDmitry Torokhov /* event termination */ 1054104d13fSDmitry Torokhov input_sync(dev); 1064104d13fSDmitry Torokhov 1074104d13fSDmitry Torokhov resubmit: 1084104d13fSDmitry Torokhov status = usb_submit_urb(urb, GFP_ATOMIC); 1094104d13fSDmitry Torokhov if (status) 110b30662c4SGreg Kroah-Hartman dev_err(&dev->dev, 111b59c82bdSGreg Kroah-Hartman "can't resubmit intr, %s-%s/input0, status %d\n", 112b59c82bdSGreg Kroah-Hartman acecad->usbdev->bus->bus_name, 113b59c82bdSGreg Kroah-Hartman acecad->usbdev->devpath, status); 1144104d13fSDmitry Torokhov } 1154104d13fSDmitry Torokhov 1164104d13fSDmitry Torokhov static int usb_acecad_open(struct input_dev *dev) 1174104d13fSDmitry Torokhov { 1184104d13fSDmitry Torokhov struct usb_acecad *acecad = input_get_drvdata(dev); 1194104d13fSDmitry Torokhov 1204104d13fSDmitry Torokhov acecad->irq->dev = acecad->usbdev; 1214104d13fSDmitry Torokhov if (usb_submit_urb(acecad->irq, GFP_KERNEL)) 1224104d13fSDmitry Torokhov return -EIO; 1234104d13fSDmitry Torokhov 1244104d13fSDmitry Torokhov return 0; 1254104d13fSDmitry Torokhov } 1264104d13fSDmitry Torokhov 1274104d13fSDmitry Torokhov static void usb_acecad_close(struct input_dev *dev) 1284104d13fSDmitry Torokhov { 1294104d13fSDmitry Torokhov struct usb_acecad *acecad = input_get_drvdata(dev); 1304104d13fSDmitry Torokhov 1314104d13fSDmitry Torokhov usb_kill_urb(acecad->irq); 1324104d13fSDmitry Torokhov } 1334104d13fSDmitry Torokhov 1344104d13fSDmitry Torokhov static int usb_acecad_probe(struct usb_interface *intf, const struct usb_device_id *id) 1354104d13fSDmitry Torokhov { 1364104d13fSDmitry Torokhov struct usb_device *dev = interface_to_usbdev(intf); 1374104d13fSDmitry Torokhov struct usb_host_interface *interface = intf->cur_altsetting; 1384104d13fSDmitry Torokhov struct usb_endpoint_descriptor *endpoint; 1394104d13fSDmitry Torokhov struct usb_acecad *acecad; 1404104d13fSDmitry Torokhov struct input_dev *input_dev; 1414104d13fSDmitry Torokhov int pipe, maxp; 142b426571cSDmitry Torokhov int err; 1434104d13fSDmitry Torokhov 1444104d13fSDmitry Torokhov if (interface->desc.bNumEndpoints != 1) 1454104d13fSDmitry Torokhov return -ENODEV; 1464104d13fSDmitry Torokhov 1474104d13fSDmitry Torokhov endpoint = &interface->endpoint[0].desc; 1484104d13fSDmitry Torokhov 1494104d13fSDmitry Torokhov if (!usb_endpoint_is_int_in(endpoint)) 1504104d13fSDmitry Torokhov return -ENODEV; 1514104d13fSDmitry Torokhov 1524104d13fSDmitry Torokhov pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); 1534104d13fSDmitry Torokhov maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); 1544104d13fSDmitry Torokhov 1554104d13fSDmitry Torokhov acecad = kzalloc(sizeof(struct usb_acecad), GFP_KERNEL); 1564104d13fSDmitry Torokhov input_dev = input_allocate_device(); 1574104d13fSDmitry Torokhov if (!acecad || !input_dev) { 1584104d13fSDmitry Torokhov err = -ENOMEM; 1594104d13fSDmitry Torokhov goto fail1; 1604104d13fSDmitry Torokhov } 1614104d13fSDmitry Torokhov 162997ea58eSDaniel Mack acecad->data = usb_alloc_coherent(dev, 8, GFP_KERNEL, &acecad->data_dma); 1634104d13fSDmitry Torokhov if (!acecad->data) { 1644104d13fSDmitry Torokhov err= -ENOMEM; 1654104d13fSDmitry Torokhov goto fail1; 1664104d13fSDmitry Torokhov } 1674104d13fSDmitry Torokhov 1684104d13fSDmitry Torokhov acecad->irq = usb_alloc_urb(0, GFP_KERNEL); 1694104d13fSDmitry Torokhov if (!acecad->irq) { 1704104d13fSDmitry Torokhov err = -ENOMEM; 1714104d13fSDmitry Torokhov goto fail2; 1724104d13fSDmitry Torokhov } 1734104d13fSDmitry Torokhov 1744104d13fSDmitry Torokhov acecad->usbdev = dev; 1754104d13fSDmitry Torokhov acecad->input = input_dev; 1764104d13fSDmitry Torokhov 1774104d13fSDmitry Torokhov if (dev->manufacturer) 1784104d13fSDmitry Torokhov strlcpy(acecad->name, dev->manufacturer, sizeof(acecad->name)); 1794104d13fSDmitry Torokhov 1804104d13fSDmitry Torokhov if (dev->product) { 1814104d13fSDmitry Torokhov if (dev->manufacturer) 1824104d13fSDmitry Torokhov strlcat(acecad->name, " ", sizeof(acecad->name)); 1834104d13fSDmitry Torokhov strlcat(acecad->name, dev->product, sizeof(acecad->name)); 1844104d13fSDmitry Torokhov } 1854104d13fSDmitry Torokhov 1864104d13fSDmitry Torokhov usb_make_path(dev, acecad->phys, sizeof(acecad->phys)); 1874104d13fSDmitry Torokhov strlcat(acecad->phys, "/input0", sizeof(acecad->phys)); 1884104d13fSDmitry Torokhov 1894104d13fSDmitry Torokhov input_dev->name = acecad->name; 1904104d13fSDmitry Torokhov input_dev->phys = acecad->phys; 1914104d13fSDmitry Torokhov usb_to_input_id(dev, &input_dev->id); 1924104d13fSDmitry Torokhov input_dev->dev.parent = &intf->dev; 1934104d13fSDmitry Torokhov 1944104d13fSDmitry Torokhov input_set_drvdata(input_dev, acecad); 1954104d13fSDmitry Torokhov 1964104d13fSDmitry Torokhov input_dev->open = usb_acecad_open; 1974104d13fSDmitry Torokhov input_dev->close = usb_acecad_close; 1984104d13fSDmitry Torokhov 1997b19ada2SJiri Slaby input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 2007b19ada2SJiri Slaby input_dev->keybit[BIT_WORD(BTN_DIGI)] = BIT_MASK(BTN_TOOL_PEN) | 2017b19ada2SJiri Slaby BIT_MASK(BTN_TOUCH) | BIT_MASK(BTN_STYLUS) | 2027b19ada2SJiri Slaby BIT_MASK(BTN_STYLUS2); 2034104d13fSDmitry Torokhov 2044104d13fSDmitry Torokhov switch (id->driver_info) { 2054104d13fSDmitry Torokhov case 0: 206b426571cSDmitry Torokhov input_set_abs_params(input_dev, ABS_X, 0, 5000, 4, 0); 207b426571cSDmitry Torokhov input_set_abs_params(input_dev, ABS_Y, 0, 3750, 4, 0); 208b426571cSDmitry Torokhov input_set_abs_params(input_dev, ABS_PRESSURE, 0, 512, 0, 0); 2094104d13fSDmitry Torokhov if (!strlen(acecad->name)) 2104104d13fSDmitry Torokhov snprintf(acecad->name, sizeof(acecad->name), 2114104d13fSDmitry Torokhov "USB Acecad Flair Tablet %04x:%04x", 2124104d13fSDmitry Torokhov le16_to_cpu(dev->descriptor.idVendor), 2134104d13fSDmitry Torokhov le16_to_cpu(dev->descriptor.idProduct)); 2144104d13fSDmitry Torokhov break; 215b426571cSDmitry Torokhov 2164104d13fSDmitry Torokhov case 1: 217b426571cSDmitry Torokhov input_set_abs_params(input_dev, ABS_X, 0, 53000, 4, 0); 218b426571cSDmitry Torokhov input_set_abs_params(input_dev, ABS_Y, 0, 2250, 4, 0); 219b426571cSDmitry Torokhov input_set_abs_params(input_dev, ABS_PRESSURE, 0, 1024, 0, 0); 2204104d13fSDmitry Torokhov if (!strlen(acecad->name)) 2214104d13fSDmitry Torokhov snprintf(acecad->name, sizeof(acecad->name), 2224104d13fSDmitry Torokhov "USB Acecad 302 Tablet %04x:%04x", 2234104d13fSDmitry Torokhov le16_to_cpu(dev->descriptor.idVendor), 2244104d13fSDmitry Torokhov le16_to_cpu(dev->descriptor.idProduct)); 2254104d13fSDmitry Torokhov break; 2264104d13fSDmitry Torokhov } 2274104d13fSDmitry Torokhov 2284104d13fSDmitry Torokhov usb_fill_int_urb(acecad->irq, dev, pipe, 2294104d13fSDmitry Torokhov acecad->data, maxp > 8 ? 8 : maxp, 2304104d13fSDmitry Torokhov usb_acecad_irq, acecad, endpoint->bInterval); 2314104d13fSDmitry Torokhov acecad->irq->transfer_dma = acecad->data_dma; 2324104d13fSDmitry Torokhov acecad->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 2334104d13fSDmitry Torokhov 2344104d13fSDmitry Torokhov err = input_register_device(acecad->input); 2354104d13fSDmitry Torokhov if (err) 236a4503199SAxel Lin goto fail3; 2374104d13fSDmitry Torokhov 2384104d13fSDmitry Torokhov usb_set_intfdata(intf, acecad); 2394104d13fSDmitry Torokhov 2404104d13fSDmitry Torokhov return 0; 2414104d13fSDmitry Torokhov 242a4503199SAxel Lin fail3: usb_free_urb(acecad->irq); 243997ea58eSDaniel Mack fail2: usb_free_coherent(dev, 8, acecad->data, acecad->data_dma); 2444104d13fSDmitry Torokhov fail1: input_free_device(input_dev); 2454104d13fSDmitry Torokhov kfree(acecad); 2464104d13fSDmitry Torokhov return err; 2474104d13fSDmitry Torokhov } 2484104d13fSDmitry Torokhov 2494104d13fSDmitry Torokhov static void usb_acecad_disconnect(struct usb_interface *intf) 2504104d13fSDmitry Torokhov { 2514104d13fSDmitry Torokhov struct usb_acecad *acecad = usb_get_intfdata(intf); 2524104d13fSDmitry Torokhov 2534104d13fSDmitry Torokhov usb_set_intfdata(intf, NULL); 2545492f6f8SDmitry Torokhov 2554104d13fSDmitry Torokhov input_unregister_device(acecad->input); 2564104d13fSDmitry Torokhov usb_free_urb(acecad->irq); 2577a9b1492SLinus Torvalds usb_free_coherent(acecad->usbdev, 8, acecad->data, acecad->data_dma); 2584104d13fSDmitry Torokhov kfree(acecad); 2594104d13fSDmitry Torokhov } 2604104d13fSDmitry Torokhov 2614104d13fSDmitry Torokhov static struct usb_device_id usb_acecad_id_table [] = { 2624104d13fSDmitry Torokhov { USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_FLAIR), .driver_info = 0 }, 2634104d13fSDmitry Torokhov { USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_302), .driver_info = 1 }, 2644104d13fSDmitry Torokhov { } 2654104d13fSDmitry Torokhov }; 2664104d13fSDmitry Torokhov 2674104d13fSDmitry Torokhov MODULE_DEVICE_TABLE(usb, usb_acecad_id_table); 2684104d13fSDmitry Torokhov 2694104d13fSDmitry Torokhov static struct usb_driver usb_acecad_driver = { 2704104d13fSDmitry Torokhov .name = "usb_acecad", 2714104d13fSDmitry Torokhov .probe = usb_acecad_probe, 2724104d13fSDmitry Torokhov .disconnect = usb_acecad_disconnect, 2734104d13fSDmitry Torokhov .id_table = usb_acecad_id_table, 2744104d13fSDmitry Torokhov }; 2754104d13fSDmitry Torokhov 27608642e7cSGreg Kroah-Hartman module_usb_driver(usb_acecad_driver); 277