Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

HID: elo: add quirks for broken firmware

One firmare version in the devices the driver takes care of is
completely broken and needs periodic pokes from our side. We
implemented this as a periodic delayed queue. The idea of the pokes
was taken from the suse enterprise kernel, in particular from Libor's
"Elo touchscreen firmware M workaround".

I am quoting him here:
This patch adds periodic polling of the Elo USB touchscreens. Needed
as a workaround for devices with M-level firmware, otherwise these
devices are known to misbehave (as reported by Elo developers).

Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Tested-by: Petr Ostadal <postadal@suse.cz>
Cc: Oliver Neukum <oliver@neukum.org>
Cc: Vojtech Pavlik <vojtech@suse.cz>
Cc: Egbert Eich <eich@suse.com>
Cc: Libor Pechacek <lpechacek@suse.cz>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>

authored by

Jiri Slaby and committed by
Jiri Kosina
d8e5aec8 d23efc19

+143 -1
+143 -1
drivers/hid/hid-elo.c
··· 11 11 #include <linux/hid.h> 12 12 #include <linux/input.h> 13 13 #include <linux/module.h> 14 + #include <linux/usb.h> 15 + #include <linux/workqueue.h> 14 16 15 17 #include "hid-ids.h" 18 + 19 + #define ELO_PERIODIC_READ_INTERVAL HZ 20 + #define ELO_SMARTSET_CMD_TIMEOUT 2000 /* msec */ 21 + 22 + /* Elo SmartSet commands */ 23 + #define ELO_FLUSH_SMARTSET_RESPONSES 0x02 /* Flush all pending smartset responses */ 24 + #define ELO_SEND_SMARTSET_COMMAND 0x05 /* Send a smartset command */ 25 + #define ELO_GET_SMARTSET_RESPONSE 0x06 /* Get a smartset response */ 26 + #define ELO_DIAG 0x64 /* Diagnostics command */ 27 + #define ELO_SMARTSET_PACKET_SIZE 8 28 + 29 + struct elo_priv { 30 + struct usb_device *usbdev; 31 + struct delayed_work work; 32 + unsigned char buffer[ELO_SMARTSET_PACKET_SIZE]; 33 + }; 34 + 35 + static struct workqueue_struct *wq; 36 + static bool use_fw_quirk = true; 37 + module_param(use_fw_quirk, bool, S_IRUGO); 38 + MODULE_PARM_DESC(use_fw_quirk, "Do periodic pokes for broken M firmwares (default = true)"); 16 39 17 40 static void elo_input_configured(struct hid_device *hdev, 18 41 struct hid_input *hidinput) ··· 96 73 return 0; 97 74 } 98 75 76 + static int elo_smartset_send_get(struct usb_device *dev, u8 command, 77 + void *data) 78 + { 79 + unsigned int pipe; 80 + u8 dir; 81 + 82 + if (command == ELO_SEND_SMARTSET_COMMAND) { 83 + pipe = usb_sndctrlpipe(dev, 0); 84 + dir = USB_DIR_OUT; 85 + } else if (command == ELO_GET_SMARTSET_RESPONSE) { 86 + pipe = usb_rcvctrlpipe(dev, 0); 87 + dir = USB_DIR_IN; 88 + } else 89 + return -EINVAL; 90 + 91 + return usb_control_msg(dev, pipe, command, 92 + dir | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 93 + 0, 0, data, ELO_SMARTSET_PACKET_SIZE, 94 + ELO_SMARTSET_CMD_TIMEOUT); 95 + } 96 + 97 + static int elo_flush_smartset_responses(struct usb_device *dev) 98 + { 99 + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 100 + ELO_FLUSH_SMARTSET_RESPONSES, 101 + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 102 + 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); 103 + } 104 + 105 + static void elo_work(struct work_struct *work) 106 + { 107 + struct elo_priv *priv = container_of(work, struct elo_priv, work.work); 108 + struct usb_device *dev = priv->usbdev; 109 + unsigned char *buffer = priv->buffer; 110 + int ret; 111 + 112 + ret = elo_flush_smartset_responses(dev); 113 + if (ret < 0) { 114 + dev_err(&dev->dev, "initial FLUSH_SMARTSET_RESPONSES failed, error %d\n", 115 + ret); 116 + goto fail; 117 + } 118 + 119 + /* send Diagnostics command */ 120 + *buffer = ELO_DIAG; 121 + ret = elo_smartset_send_get(dev, ELO_SEND_SMARTSET_COMMAND, buffer); 122 + if (ret < 0) { 123 + dev_err(&dev->dev, "send Diagnostics Command failed, error %d\n", 124 + ret); 125 + goto fail; 126 + } 127 + 128 + /* get the result */ 129 + ret = elo_smartset_send_get(dev, ELO_GET_SMARTSET_RESPONSE, buffer); 130 + if (ret < 0) { 131 + dev_err(&dev->dev, "get Diagnostics Command response failed, error %d\n", 132 + ret); 133 + goto fail; 134 + } 135 + 136 + /* read the ack */ 137 + if (*buffer != 'A') { 138 + ret = elo_smartset_send_get(dev, ELO_GET_SMARTSET_RESPONSE, 139 + buffer); 140 + if (ret < 0) { 141 + dev_err(&dev->dev, "get acknowledge response failed, error %d\n", 142 + ret); 143 + goto fail; 144 + } 145 + } 146 + 147 + fail: 148 + ret = elo_flush_smartset_responses(dev); 149 + if (ret < 0) 150 + dev_err(&dev->dev, "final FLUSH_SMARTSET_RESPONSES failed, error %d\n", 151 + ret); 152 + queue_delayed_work(wq, &priv->work, ELO_PERIODIC_READ_INTERVAL); 153 + } 154 + 155 + /* 156 + * Not all Elo devices need the periodic HID descriptor reads. 157 + * Only firmware version M needs this. 158 + */ 159 + static bool elo_broken_firmware(struct usb_device *dev) 160 + { 161 + return use_fw_quirk && le16_to_cpu(dev->descriptor.bcdDevice) == 0x10d; 162 + } 163 + 99 164 static int elo_probe(struct hid_device *hdev, const struct hid_device_id *id) 100 165 { 166 + struct elo_priv *priv; 101 167 int ret; 168 + 169 + priv = kzalloc(sizeof(*priv), GFP_KERNEL); 170 + if (!priv) 171 + return -ENOMEM; 172 + 173 + INIT_DELAYED_WORK(&priv->work, elo_work); 174 + priv->usbdev = interface_to_usbdev(to_usb_interface(hdev->dev.parent)); 175 + 176 + hid_set_drvdata(hdev, priv); 102 177 103 178 ret = hid_parse(hdev); 104 179 if (ret) { ··· 210 89 goto err_free; 211 90 } 212 91 92 + if (elo_broken_firmware(priv->usbdev)) { 93 + hid_info(hdev, "broken firmware found, installing workaround\n"); 94 + queue_delayed_work(wq, &priv->work, ELO_PERIODIC_READ_INTERVAL); 95 + } 96 + 213 97 return 0; 214 98 err_free: 99 + kfree(priv); 215 100 return ret; 216 101 } 217 102 218 103 static void elo_remove(struct hid_device *hdev) 219 104 { 105 + struct elo_priv *priv = hid_get_drvdata(hdev); 106 + 220 107 hid_hw_stop(hdev); 108 + flush_workqueue(wq); 109 + kfree(priv); 221 110 } 222 111 223 112 static const struct hid_device_id elo_devices[] = { ··· 248 117 249 118 static int __init elo_driver_init(void) 250 119 { 251 - return hid_register_driver(&elo_driver); 120 + int ret; 121 + 122 + wq = create_singlethread_workqueue("elousb"); 123 + if (!wq) 124 + return -ENOMEM; 125 + 126 + ret = hid_register_driver(&elo_driver); 127 + if (ret) 128 + destroy_workqueue(wq); 129 + 130 + return ret; 252 131 } 253 132 module_init(elo_driver_init); 254 133 255 134 static void __exit elo_driver_exit(void) 256 135 { 257 136 hid_unregister_driver(&elo_driver); 137 + destroy_workqueue(wq); 258 138 } 259 139 module_exit(elo_driver_exit); 260 140