From: Jiri Kosina Subject: Elo USB touchscreen driver Patch-mainline: will be submitted for 2.6.28 References: FATE#304972 This is a driver for Elo USB touchscreen devices. Signed-off-by: Vojtech Pavlik Acked-by: Jiri Kosina --- drivers/hid/hid-core.c | 2 drivers/hid/hid-ids.h | 2 drivers/input/touchscreen/Kconfig | 12 + drivers/input/touchscreen/Makefile | 1 drivers/input/touchscreen/elousb.c | 305 +++++++++++++++++++++++++++++++++++++ 5 files changed, 322 insertions(+) --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1567,6 +1567,8 @@ static const struct hid_device_id hid_ig { HID_USB_DEVICE(USB_VENDOR_ID_DEALEXTREAME, USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701) }, { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE) }, { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_4000U) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_4500U) }, { HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) }, { HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC5UH) }, { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0001) }, --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -171,7 +171,9 @@ #define USB_VENDOR_ID_DRAGONRISE 0x0079 #define USB_VENDOR_ID_ELO 0x04E7 +#define USB_DEVICE_ID_ELO_4000U 0x0009 #define USB_DEVICE_ID_ELO_TS2700 0x0020 +#define USB_DEVICE_ID_ELO_4500U 0x0030 #define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f #define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -193,6 +193,18 @@ config TOUCHSCREEN_ELO To compile this driver as a module, choose M here: the module will be called elo. +config TOUCHSCREEN_ELOUSB + tristate "Elo USB touchscreens" + select USB + help + Say Y here if you have an Elo USB touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called elousb. + config TOUCHSCREEN_WACOM_W8001 tristate "Wacom W8001 penabled serial touchscreen" select SERIO --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dyn obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o +obj-$(CONFIG_TOUCHSCREEN_ELOUSB) += elousb.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o --- /dev/null +++ b/drivers/input/touchscreen/elousb.c @@ -0,0 +1,305 @@ +/* + * Copyright (c) 1999-2001 Vojtech Pavlik + * + * Elo USB touchscreen support + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Version Information + */ +#define DRIVER_VERSION "v1.1" +#define DRIVER_AUTHOR "Vojtech Pavlik " +#define DRIVER_DESC "Elo USB touchscreen driver" +#define DRIVER_LICENSE "GPL" + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); + +struct elousb { + char name[128]; + char phys[64]; + struct usb_device *usbdev; + struct input_dev *dev; + struct urb *irq; + + unsigned char *data; + dma_addr_t data_dma; +}; + +static void elousb_irq(struct urb *urb) +{ + struct elousb *elo = urb->context; + unsigned char *data = elo->data; + struct input_dev *dev = elo->dev; + int status; + + switch (urb->status) { + case 0: /* success */ + break; + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -ESHUTDOWN: + return; + /* -EPIPE: should clear the halt */ + default: /* error */ + goto resubmit; + } + + if (data[0] != 'T') /* Mandatory ELO packet marker */ + return; + + + input_report_abs(dev, ABS_X, ((u32)data[3] << 8) | data[2]); + input_report_abs(dev, ABS_Y, ((u32)data[5] << 8) | data[4]); + + input_report_abs(dev, ABS_PRESSURE, + (data[1] & 0x80) ? (((u32)data[7] << 8) | data[6]): 0); + + if (data[1] & 0x03) { + input_report_key(dev, BTN_TOUCH, 1); + input_sync(dev); + } + + if (data[1] & 0x04) + input_report_key(dev, BTN_TOUCH, 0); + + input_sync(dev); + +resubmit: + status = usb_submit_urb (urb, GFP_ATOMIC); + if (status) + err ("can't resubmit intr, %s-%s/input0, status %d", + elo->usbdev->bus->bus_name, + elo->usbdev->devpath, status); +} + +static int elousb_open(struct input_dev *dev) +{ + struct elousb *elo = input_get_drvdata(dev); + + elo->irq->dev = elo->usbdev; + if (usb_submit_urb(elo->irq, GFP_KERNEL)) + return -EIO; + + return 0; +} + +static void elousb_close(struct input_dev *dev) +{ + struct elousb *elo = input_get_drvdata(dev); + + usb_kill_urb(elo->irq); +} + +static int elousb_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + struct hid_descriptor *hdesc; + struct elousb *elo; + struct input_dev *input_dev; + int pipe, i; + unsigned int rsize = 0; + int error = -ENOMEM; + char *rdesc; + + interface = intf->cur_altsetting; + + if (interface->desc.bNumEndpoints != 1) + return -ENODEV; + + endpoint = &interface->endpoint[0].desc; + if (!(endpoint->bEndpointAddress & USB_DIR_IN)) + return -ENODEV; + if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) + return -ENODEV; + + if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) && + (!interface->desc.bNumEndpoints || + usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) { + err("HID class descriptor not present"); + return -ENODEV; + } + + for (i = 0; i < hdesc->bNumDescriptors; i++) + if (hdesc->desc[i].bDescriptorType == HID_DT_REPORT) + rsize = le16_to_cpu(hdesc->desc[i].wDescriptorLength); + + if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) { + err("weird size of report descriptor (%u)", rsize); + return -ENODEV; + } + + + pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); + + elo = kzalloc(sizeof(struct elousb), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!elo || !input_dev) + goto fail1; + + elo->data = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &elo->data_dma); + if (!elo->data) + goto fail1; + + elo->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!elo->irq) + goto fail2; + + if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) + goto fail3; + + elo->usbdev = dev; + elo->dev = input_dev; + + if ((error = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + HID_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, + interface->desc.bInterfaceNumber, + NULL, 0, USB_CTRL_SET_TIMEOUT)) < 0) { + err("setting HID idle timeout failed, error %d", error); + error = -ENODEV; + goto fail4; + } + + if ((error = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_RECIP_INTERFACE | USB_DIR_IN, + HID_DT_REPORT << 8, interface->desc.bInterfaceNumber, + rdesc, rsize, USB_CTRL_GET_TIMEOUT)) < rsize) { + err("reading HID report descriptor failed, error %d", error); + error = -ENODEV; + goto fail4; + } + + if (dev->manufacturer) + strlcpy(elo->name, dev->manufacturer, sizeof(elo->name)); + + if (dev->product) { + if (dev->manufacturer) + strlcat(elo->name, " ", sizeof(elo->name)); + strlcat(elo->name, dev->product, sizeof(elo->name)); + } + + if (!strlen(elo->name)) + snprintf(elo->name, sizeof(elo->name), + "Elo touchscreen %04x:%04x", + le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + + usb_make_path(dev, elo->phys, sizeof(elo->phys)); + strlcat(elo->phys, "/input0", sizeof(elo->phys)); + + input_dev->name = elo->name; + input_dev->phys = elo->phys; + usb_to_input_id(dev, &input_dev->id); + input_dev->dev.parent = &intf->dev; + + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + set_bit(BTN_TOUCH, input_dev->keybit); + input_dev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y); + set_bit(ABS_PRESSURE, input_dev->absbit); + + input_set_abs_params(input_dev, ABS_X, 0, 4000, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, 3840, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, 256, 0, 0); + + input_set_drvdata(input_dev, elo); + + input_dev->open = elousb_open; + input_dev->close = elousb_close; + + usb_fill_int_urb(elo->irq, dev, pipe, elo->data, 8, + elousb_irq, elo, endpoint->bInterval); + elo->irq->transfer_dma = elo->data_dma; + elo->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + input_register_device(elo->dev); + + usb_set_intfdata(intf, elo); + return 0; + +fail4: + kfree(rdesc); +fail3: + usb_free_urb(elo->irq); +fail2: + usb_buffer_free(dev, 8, elo->data, elo->data_dma); +fail1: + input_free_device(input_dev); + kfree(elo); + return -ENOMEM; +} + +static void elousb_disconnect(struct usb_interface *intf) +{ + struct elousb *elo = usb_get_intfdata (intf); + + usb_set_intfdata(intf, NULL); + if (elo) { + usb_kill_urb(elo->irq); + input_unregister_device(elo->dev); + usb_free_urb(elo->irq); + usb_buffer_free(interface_to_usbdev(intf), 8, elo->data, elo->data_dma); + kfree(elo); + } +} + +static struct usb_device_id elousb_id_table [] = { + { USB_DEVICE(0x04e7, 0x0009) }, /* CarrolTouch 4000U */ + { USB_DEVICE(0x04e7, 0x0030) }, /* CarrolTouch 4500U */ + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, elousb_id_table); + +static struct usb_driver elousb_driver = { + .name = "elousb", + .probe = elousb_probe, + .disconnect = elousb_disconnect, + .id_table = elousb_id_table, +}; + +static int __init elousb_init(void) +{ + int retval = usb_register(&elousb_driver); + if (retval == 0) + printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" DRIVER_DESC); + return retval; +} + +static void __exit elousb_exit(void) +{ + usb_deregister(&elousb_driver); +} + +module_init(elousb_init); +module_exit(elousb_exit);