123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315 |
- /*
- * HID driver for ELO usb touchscreen 4000/4500
- *
- * Copyright (c) 2013 Jiri Slaby
- *
- * Data parsing taken from elousb driver by Vojtech Pavlik.
- *
- * This driver is licensed under the terms of GPLv2.
- */
- #include <linux/hid.h>
- #include <linux/input.h>
- #include <linux/module.h>
- #include <linux/usb.h>
- #include <linux/workqueue.h>
- #include "hid-ids.h"
- #define ELO_PERIODIC_READ_INTERVAL HZ
- #define ELO_SMARTSET_CMD_TIMEOUT 2000 /* msec */
- /* Elo SmartSet commands */
- #define ELO_FLUSH_SMARTSET_RESPONSES 0x02 /* Flush all pending smartset responses */
- #define ELO_SEND_SMARTSET_COMMAND 0x05 /* Send a smartset command */
- #define ELO_GET_SMARTSET_RESPONSE 0x06 /* Get a smartset response */
- #define ELO_DIAG 0x64 /* Diagnostics command */
- #define ELO_SMARTSET_PACKET_SIZE 8
- struct elo_priv {
- struct usb_device *usbdev;
- struct delayed_work work;
- unsigned char buffer[ELO_SMARTSET_PACKET_SIZE];
- };
- static struct workqueue_struct *wq;
- static bool use_fw_quirk = true;
- module_param(use_fw_quirk, bool, S_IRUGO);
- MODULE_PARM_DESC(use_fw_quirk, "Do periodic pokes for broken M firmwares (default = true)");
- static int elo_input_configured(struct hid_device *hdev,
- struct hid_input *hidinput)
- {
- struct input_dev *input = hidinput->input;
- /*
- * ELO devices have one Button usage in GenDesk field, which makes
- * hid-input map it to BTN_LEFT; that confuses userspace, which then
- * considers the device to be a mouse/touchpad instead of touchscreen.
- */
- clear_bit(BTN_LEFT, input->keybit);
- set_bit(BTN_TOUCH, input->keybit);
- set_bit(ABS_PRESSURE, input->absbit);
- input_set_abs_params(input, ABS_PRESSURE, 0, 256, 0, 0);
- return 0;
- }
- static void elo_process_data(struct input_dev *input, const u8 *data, int size)
- {
- int press;
- input_report_abs(input, ABS_X, (data[3] << 8) | data[2]);
- input_report_abs(input, ABS_Y, (data[5] << 8) | data[4]);
- press = 0;
- if (data[1] & 0x80)
- press = (data[7] << 8) | data[6];
- input_report_abs(input, ABS_PRESSURE, press);
- if (data[1] & 0x03) {
- input_report_key(input, BTN_TOUCH, 1);
- input_sync(input);
- }
- if (data[1] & 0x04)
- input_report_key(input, BTN_TOUCH, 0);
- input_sync(input);
- }
- static int elo_raw_event(struct hid_device *hdev, struct hid_report *report,
- u8 *data, int size)
- {
- struct hid_input *hidinput;
- if (!(hdev->claimed & HID_CLAIMED_INPUT) || list_empty(&hdev->inputs))
- return 0;
- hidinput = list_first_entry(&hdev->inputs, struct hid_input, list);
- switch (report->id) {
- case 0:
- if (data[0] == 'T') { /* Mandatory ELO packet marker */
- elo_process_data(hidinput->input, data, size);
- return 1;
- }
- break;
- default: /* unknown report */
- /* Unknown report type; pass upstream */
- hid_info(hdev, "unknown report type %d\n", report->id);
- break;
- }
- return 0;
- }
- static int elo_smartset_send_get(struct usb_device *dev, u8 command,
- void *data)
- {
- unsigned int pipe;
- u8 dir;
- if (command == ELO_SEND_SMARTSET_COMMAND) {
- pipe = usb_sndctrlpipe(dev, 0);
- dir = USB_DIR_OUT;
- } else if (command == ELO_GET_SMARTSET_RESPONSE) {
- pipe = usb_rcvctrlpipe(dev, 0);
- dir = USB_DIR_IN;
- } else
- return -EINVAL;
- return usb_control_msg(dev, pipe, command,
- dir | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, 0, data, ELO_SMARTSET_PACKET_SIZE,
- ELO_SMARTSET_CMD_TIMEOUT);
- }
- static int elo_flush_smartset_responses(struct usb_device *dev)
- {
- return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- ELO_FLUSH_SMARTSET_RESPONSES,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
- }
- static void elo_work(struct work_struct *work)
- {
- struct elo_priv *priv = container_of(work, struct elo_priv, work.work);
- struct usb_device *dev = priv->usbdev;
- unsigned char *buffer = priv->buffer;
- int ret;
- ret = elo_flush_smartset_responses(dev);
- if (ret < 0) {
- dev_err(&dev->dev, "initial FLUSH_SMARTSET_RESPONSES failed, error %d\n",
- ret);
- goto fail;
- }
- /* send Diagnostics command */
- *buffer = ELO_DIAG;
- ret = elo_smartset_send_get(dev, ELO_SEND_SMARTSET_COMMAND, buffer);
- if (ret < 0) {
- dev_err(&dev->dev, "send Diagnostics Command failed, error %d\n",
- ret);
- goto fail;
- }
- /* get the result */
- ret = elo_smartset_send_get(dev, ELO_GET_SMARTSET_RESPONSE, buffer);
- if (ret < 0) {
- dev_err(&dev->dev, "get Diagnostics Command response failed, error %d\n",
- ret);
- goto fail;
- }
- /* read the ack */
- if (*buffer != 'A') {
- ret = elo_smartset_send_get(dev, ELO_GET_SMARTSET_RESPONSE,
- buffer);
- if (ret < 0) {
- dev_err(&dev->dev, "get acknowledge response failed, error %d\n",
- ret);
- goto fail;
- }
- }
- fail:
- ret = elo_flush_smartset_responses(dev);
- if (ret < 0)
- dev_err(&dev->dev, "final FLUSH_SMARTSET_RESPONSES failed, error %d\n",
- ret);
- queue_delayed_work(wq, &priv->work, ELO_PERIODIC_READ_INTERVAL);
- }
- /*
- * Not all Elo devices need the periodic HID descriptor reads.
- * Only firmware version M needs this.
- */
- static bool elo_broken_firmware(struct usb_device *dev)
- {
- struct usb_device *hub = dev->parent;
- struct usb_device *child = NULL;
- u16 fw_lvl = le16_to_cpu(dev->descriptor.bcdDevice);
- u16 child_vid, child_pid;
- int i;
-
- if (!use_fw_quirk)
- return false;
- if (fw_lvl != 0x10d)
- return false;
- /* iterate sibling devices of the touch controller */
- usb_hub_for_each_child(hub, i, child) {
- child_vid = le16_to_cpu(child->descriptor.idVendor);
- child_pid = le16_to_cpu(child->descriptor.idProduct);
- /*
- * If one of the devices below is present attached as a sibling of
- * the touch controller then this is a newer IBM 4820 monitor that
- * does not need the IBM-requested workaround if fw level is
- * 0x010d - aka 'M'.
- * No other HW can have this combination.
- */
- if (child_vid==0x04b3) {
- switch (child_pid) {
- case 0x4676: /* 4820 21x Video */
- case 0x4677: /* 4820 51x Video */
- case 0x4678: /* 4820 2Lx Video */
- case 0x4679: /* 4820 5Lx Video */
- return false;
- }
- }
- }
- return true;
- }
- static int elo_probe(struct hid_device *hdev, const struct hid_device_id *id)
- {
- struct elo_priv *priv;
- int ret;
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
- INIT_DELAYED_WORK(&priv->work, elo_work);
- priv->usbdev = interface_to_usbdev(to_usb_interface(hdev->dev.parent));
- hid_set_drvdata(hdev, priv);
- ret = hid_parse(hdev);
- if (ret) {
- hid_err(hdev, "parse failed\n");
- goto err_free;
- }
- ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
- if (ret) {
- hid_err(hdev, "hw start failed\n");
- goto err_free;
- }
- if (elo_broken_firmware(priv->usbdev)) {
- hid_info(hdev, "broken firmware found, installing workaround\n");
- queue_delayed_work(wq, &priv->work, ELO_PERIODIC_READ_INTERVAL);
- }
- return 0;
- err_free:
- kfree(priv);
- return ret;
- }
- static void elo_remove(struct hid_device *hdev)
- {
- struct elo_priv *priv = hid_get_drvdata(hdev);
- hid_hw_stop(hdev);
- cancel_delayed_work_sync(&priv->work);
- kfree(priv);
- }
- static const struct hid_device_id elo_devices[] = {
- { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0009), },
- { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0030), },
- { }
- };
- MODULE_DEVICE_TABLE(hid, elo_devices);
- static struct hid_driver elo_driver = {
- .name = "elo",
- .id_table = elo_devices,
- .probe = elo_probe,
- .remove = elo_remove,
- .raw_event = elo_raw_event,
- .input_configured = elo_input_configured,
- };
- static int __init elo_driver_init(void)
- {
- int ret;
- wq = create_singlethread_workqueue("elousb");
- if (!wq)
- return -ENOMEM;
- ret = hid_register_driver(&elo_driver);
- if (ret)
- destroy_workqueue(wq);
- return ret;
- }
- module_init(elo_driver_init);
- static void __exit elo_driver_exit(void)
- {
- hid_unregister_driver(&elo_driver);
- destroy_workqueue(wq);
- }
- module_exit(elo_driver_exit);
- MODULE_AUTHOR("Jiri Slaby <jslaby@suse.cz>");
- MODULE_LICENSE("GPL");
|