aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Steinhoff <mail@jan-steinhoff.de>2012-02-03 03:21:31 -0500
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2012-02-03 03:25:03 -0500
commit8491ee1093c476ea3a9a19ab8593d8531cab40f7 (patch)
tree1a0a6de4ed6192d9676777604da55cf37fc9e509
parenta80b83b7b8456e9b475346c2e01d7e210883208c (diff)
Input: add Synaptics USB device driver
This patch adds a driver for Synaptics USB touchpad or pointing stick devices. These USB devices emulate an USB mouse by default, so one can also use the usbhid driver. However, in combination with special user space drivers this kernel driver allows one to customize the behaviour of the device. An extended version of this driver with support for the cPad background display can be found at <http://jan-steinhoff.de/linux/synaptics-usb.html>. Signed-off-by: Jan Steinhoff <mail@jan-steinhoff.de> Acked-by: Jiri Kosina <jkosina@suse.cz> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
-rw-r--r--drivers/hid/hid-core.c10
-rw-r--r--drivers/hid/hid-ids.h11
-rw-r--r--drivers/input/mouse/Kconfig17
-rw-r--r--drivers/input/mouse/Makefile1
-rw-r--r--drivers/input/mouse/synaptics_usb.c568
5 files changed, 607 insertions, 0 deletions
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 848a56c0279..b639855acd2 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1892,6 +1892,16 @@ static const struct hid_device_id hid_ignore_list[] = {
1892 { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) }, 1892 { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) },
1893 { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE) }, 1893 { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE) },
1894 { HID_USB_DEVICE(USB_VENDOR_ID_POWERCOM, USB_DEVICE_ID_POWERCOM_UPS) }, 1894 { HID_USB_DEVICE(USB_VENDOR_ID_POWERCOM, USB_DEVICE_ID_POWERCOM_UPS) },
1895#if defined(CONFIG_MOUSE_SYNAPTICS_USB) || defined(CONFIG_MOUSE_SYNAPTICS_USB_MODULE)
1896 { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP) },
1897 { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_INT_TP) },
1898 { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_CPAD) },
1899 { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_STICK) },
1900 { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_WP) },
1901 { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_COMP_TP) },
1902 { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_WTP) },
1903 { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_DPAD) },
1904#endif
1895 { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) }, 1905 { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) },
1896 { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) }, 1906 { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) },
1897 { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) }, 1907 { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 06ce996b8b6..3b683439c6c 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -633,6 +633,17 @@
633#define USB_DEVICE_ID_SYMBOL_SCANNER_1 0x0800 633#define USB_DEVICE_ID_SYMBOL_SCANNER_1 0x0800
634#define USB_DEVICE_ID_SYMBOL_SCANNER_2 0x1300 634#define USB_DEVICE_ID_SYMBOL_SCANNER_2 0x1300
635 635
636#define USB_VENDOR_ID_SYNAPTICS 0x06cb
637#define USB_DEVICE_ID_SYNAPTICS_TP 0x0001
638#define USB_DEVICE_ID_SYNAPTICS_INT_TP 0x0002
639#define USB_DEVICE_ID_SYNAPTICS_CPAD 0x0003
640#define USB_DEVICE_ID_SYNAPTICS_TS 0x0006
641#define USB_DEVICE_ID_SYNAPTICS_STICK 0x0007
642#define USB_DEVICE_ID_SYNAPTICS_WP 0x0008
643#define USB_DEVICE_ID_SYNAPTICS_COMP_TP 0x0009
644#define USB_DEVICE_ID_SYNAPTICS_WTP 0x0010
645#define USB_DEVICE_ID_SYNAPTICS_DPAD 0x0013
646
636#define USB_VENDOR_ID_THRUSTMASTER 0x044f 647#define USB_VENDOR_ID_THRUSTMASTER 0x044f
637 648
638#define USB_VENDOR_ID_TOPSEED 0x0766 649#define USB_VENDOR_ID_TOPSEED 0x0766
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index 9c1e6ee8353..9b8db821d5f 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -322,4 +322,21 @@ config MOUSE_SYNAPTICS_I2C
322 To compile this driver as a module, choose M here: the 322 To compile this driver as a module, choose M here: the
323 module will be called synaptics_i2c. 323 module will be called synaptics_i2c.
324 324
325config MOUSE_SYNAPTICS_USB
326 tristate "Synaptics USB device support"
327 depends on USB_ARCH_HAS_HCD
328 select USB
329 help
330 Say Y here if you want to use a Synaptics USB touchpad or pointing
331 stick.
332
333 While these devices emulate an USB mouse by default and can be used
334 with standard usbhid driver, this driver, together with its X.Org
335 counterpart, allows you to fully utilize capabilities of the device.
336 More information can be found at:
337 <http://jan-steinhoff.de/linux/synaptics-usb.html>
338
339 To compile this driver as a module, choose M here: the
340 module will be called synaptics_usb.
341
325endif 342endif
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
index 570c84a4a65..4718effeb8d 100644
--- a/drivers/input/mouse/Makefile
+++ b/drivers/input/mouse/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_MOUSE_PXA930_TRKBALL) += pxa930_trkball.o
18obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o 18obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o
19obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o 19obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o
20obj-$(CONFIG_MOUSE_SYNAPTICS_I2C) += synaptics_i2c.o 20obj-$(CONFIG_MOUSE_SYNAPTICS_I2C) += synaptics_i2c.o
21obj-$(CONFIG_MOUSE_SYNAPTICS_USB) += synaptics_usb.o
21obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o 22obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o
22 23
23psmouse-objs := psmouse-base.o synaptics.o 24psmouse-objs := psmouse-base.o synaptics.o
diff --git a/drivers/input/mouse/synaptics_usb.c b/drivers/input/mouse/synaptics_usb.c
new file mode 100644
index 00000000000..e559a947bb5
--- /dev/null
+++ b/drivers/input/mouse/synaptics_usb.c
@@ -0,0 +1,568 @@
1/*
2 * USB Synaptics device driver
3 *
4 * Copyright (c) 2002 Rob Miller (rob@inpharmatica . co . uk)
5 * Copyright (c) 2003 Ron Lee (ron@debian.org)
6 * cPad driver for kernel 2.4
7 *
8 * Copyright (c) 2004 Jan Steinhoff (cpad@jan-steinhoff . de)
9 * Copyright (c) 2004 Ron Lee (ron@debian.org)
10 * rewritten for kernel 2.6
11 *
12 * cPad display character device part is not included. It can be found at
13 * http://jan-steinhoff.de/linux/synaptics-usb.html
14 *
15 * Bases on: usb_skeleton.c v2.2 by Greg Kroah-Hartman
16 * drivers/hid/usbhid/usbmouse.c by Vojtech Pavlik
17 * drivers/input/mouse/synaptics.c by Peter Osterlund
18 *
19 * This program is free software; you can redistribute it and/or modify it
20 * under the terms of the GNU General Public License as published by the Free
21 * Software Foundation; either version 2 of the License, or (at your option)
22 * any later version.
23 *
24 * Trademarks are the property of their respective owners.
25 */
26
27/*
28 * There are three different types of Synaptics USB devices: Touchpads,
29 * touchsticks (or trackpoints), and touchscreens. Touchpads are well supported
30 * by this driver, touchstick support has not been tested much yet, and
31 * touchscreens have not been tested at all.
32 *
33 * Up to three alternate settings are possible:
34 * setting 0: one int endpoint for relative movement (used by usbhid.ko)
35 * setting 1: one int endpoint for absolute finger position
36 * setting 2 (cPad only): one int endpoint for absolute finger position and
37 * two bulk endpoints for the display (in/out)
38 * This driver uses setting 1.
39 */
40
41#include <linux/kernel.h>
42#include <linux/init.h>
43#include <linux/slab.h>
44#include <linux/module.h>
45#include <linux/moduleparam.h>
46#include <linux/usb.h>
47#include <linux/input.h>
48#include <linux/usb/input.h>
49
50#define USB_VENDOR_ID_SYNAPTICS 0x06cb
51#define USB_DEVICE_ID_SYNAPTICS_TP 0x0001 /* Synaptics USB TouchPad */
52#define USB_DEVICE_ID_SYNAPTICS_INT_TP 0x0002 /* Integrated USB TouchPad */
53#define USB_DEVICE_ID_SYNAPTICS_CPAD 0x0003 /* Synaptics cPad */
54#define USB_DEVICE_ID_SYNAPTICS_TS 0x0006 /* Synaptics TouchScreen */
55#define USB_DEVICE_ID_SYNAPTICS_STICK 0x0007 /* Synaptics USB Styk */
56#define USB_DEVICE_ID_SYNAPTICS_WP 0x0008 /* Synaptics USB WheelPad */
57#define USB_DEVICE_ID_SYNAPTICS_COMP_TP 0x0009 /* Composite USB TouchPad */
58#define USB_DEVICE_ID_SYNAPTICS_WTP 0x0010 /* Wireless TouchPad */
59#define USB_DEVICE_ID_SYNAPTICS_DPAD 0x0013 /* DisplayPad */
60
61#define SYNUSB_TOUCHPAD (1 << 0)
62#define SYNUSB_STICK (1 << 1)
63#define SYNUSB_TOUCHSCREEN (1 << 2)
64#define SYNUSB_AUXDISPLAY (1 << 3) /* For cPad */
65#define SYNUSB_COMBO (1 << 4) /* Composite device (TP + stick) */
66#define SYNUSB_IO_ALWAYS (1 << 5)
67
68#define USB_DEVICE_SYNAPTICS(prod, kind) \
69 USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, \
70 USB_DEVICE_ID_SYNAPTICS_##prod), \
71 .driver_info = (kind),
72
73#define SYNUSB_RECV_SIZE 8
74
75#define XMIN_NOMINAL 1472
76#define XMAX_NOMINAL 5472
77#define YMIN_NOMINAL 1408
78#define YMAX_NOMINAL 4448
79
80struct synusb {
81 struct usb_device *udev;
82 struct usb_interface *intf;
83 struct urb *urb;
84 unsigned char *data;
85
86 /* input device related data structures */
87 struct input_dev *input;
88 char name[128];
89 char phys[64];
90
91 /* characteristics of the device */
92 unsigned long flags;
93};
94
95static void synusb_report_buttons(struct synusb *synusb)
96{
97 struct input_dev *input_dev = synusb->input;
98
99 input_report_key(input_dev, BTN_LEFT, synusb->data[1] & 0x04);
100 input_report_key(input_dev, BTN_RIGHT, synusb->data[1] & 0x01);
101 input_report_key(input_dev, BTN_MIDDLE, synusb->data[1] & 0x02);
102}
103
104static void synusb_report_stick(struct synusb *synusb)
105{
106 struct input_dev *input_dev = synusb->input;
107 int x, y;
108 unsigned int pressure;
109
110 pressure = synusb->data[6];
111 x = (s16)(be16_to_cpup((__be16 *)&synusb->data[2]) << 3) >> 7;
112 y = (s16)(be16_to_cpup((__be16 *)&synusb->data[4]) << 3) >> 7;
113
114 if (pressure > 0) {
115 input_report_rel(input_dev, REL_X, x);
116 input_report_rel(input_dev, REL_Y, -y);
117 }
118
119 input_report_abs(input_dev, ABS_PRESSURE, pressure);
120
121 synusb_report_buttons(synusb);
122
123 input_sync(input_dev);
124}
125
126static void synusb_report_touchpad(struct synusb *synusb)
127{
128 struct input_dev *input_dev = synusb->input;
129 unsigned int num_fingers, tool_width;
130 unsigned int x, y;
131 unsigned int pressure, w;
132
133 pressure = synusb->data[6];
134 x = be16_to_cpup((__be16 *)&synusb->data[2]);
135 y = be16_to_cpup((__be16 *)&synusb->data[4]);
136 w = synusb->data[0] & 0x0f;
137
138 if (pressure > 0) {
139 num_fingers = 1;
140 tool_width = 5;
141 switch (w) {
142 case 0 ... 1:
143 num_fingers = 2 + w;
144 break;
145
146 case 2: /* pen, pretend its a finger */
147 break;
148
149 case 4 ... 15:
150 tool_width = w;
151 break;
152 }
153 } else {
154 num_fingers = 0;
155 tool_width = 0;
156 }
157
158 /*
159 * Post events
160 * BTN_TOUCH has to be first as mousedev relies on it when doing
161 * absolute -> relative conversion
162 */
163
164 if (pressure > 30)
165 input_report_key(input_dev, BTN_TOUCH, 1);
166 if (pressure < 25)
167 input_report_key(input_dev, BTN_TOUCH, 0);
168
169 if (num_fingers > 0) {
170 input_report_abs(input_dev, ABS_X, x);
171 input_report_abs(input_dev, ABS_Y,
172 YMAX_NOMINAL + YMIN_NOMINAL - y);
173 }
174
175 input_report_abs(input_dev, ABS_PRESSURE, pressure);
176 input_report_abs(input_dev, ABS_TOOL_WIDTH, tool_width);
177
178 input_report_key(input_dev, BTN_TOOL_FINGER, num_fingers == 1);
179 input_report_key(input_dev, BTN_TOOL_DOUBLETAP, num_fingers == 2);
180 input_report_key(input_dev, BTN_TOOL_TRIPLETAP, num_fingers == 3);
181
182 synusb_report_buttons(synusb);
183 if (synusb->flags & SYNUSB_AUXDISPLAY)
184 input_report_key(input_dev, BTN_MIDDLE, synusb->data[1] & 0x08);
185
186 input_sync(input_dev);
187}
188
189static void synusb_irq(struct urb *urb)
190{
191 struct synusb *synusb = urb->context;
192 int error;
193
194 /* Check our status in case we need to bail out early. */
195 switch (urb->status) {
196 case 0:
197 usb_mark_last_busy(synusb->udev);
198 break;
199
200 /* Device went away so don't keep trying to read from it. */
201 case -ECONNRESET:
202 case -ENOENT:
203 case -ESHUTDOWN:
204 return;
205
206 default:
207 goto resubmit;
208 break;
209 }
210
211 if (synusb->flags & SYNUSB_STICK)
212 synusb_report_stick(synusb);
213 else
214 synusb_report_touchpad(synusb);
215
216resubmit:
217 error = usb_submit_urb(urb, GFP_ATOMIC);
218 if (error && error != -EPERM)
219 dev_err(&synusb->intf->dev,
220 "%s - usb_submit_urb failed with result: %d",
221 __func__, error);
222}
223
224static struct usb_endpoint_descriptor *
225synusb_get_in_endpoint(struct usb_host_interface *iface)
226{
227
228 struct usb_endpoint_descriptor *endpoint;
229 int i;
230
231 for (i = 0; i < iface->desc.bNumEndpoints; ++i) {
232 endpoint = &iface->endpoint[i].desc;
233
234 if (usb_endpoint_is_int_in(endpoint)) {
235 /* we found our interrupt in endpoint */
236 return endpoint;
237 }
238 }
239
240 return NULL;
241}
242
243static int synusb_open(struct input_dev *dev)
244{
245 struct synusb *synusb = input_get_drvdata(dev);
246 int retval;
247
248 retval = usb_autopm_get_interface(synusb->intf);
249 if (retval) {
250 dev_err(&synusb->intf->dev,
251 "%s - usb_autopm_get_interface failed, error: %d\n",
252 __func__, retval);
253 return retval;
254 }
255
256 retval = usb_submit_urb(synusb->urb, GFP_KERNEL);
257 if (retval) {
258 dev_err(&synusb->intf->dev,
259 "%s - usb_submit_urb failed, error: %d\n",
260 __func__, retval);
261 retval = -EIO;
262 goto out;
263 }
264
265 synusb->intf->needs_remote_wakeup = 1;
266
267out:
268 usb_autopm_put_interface(synusb->intf);
269 return retval;
270}
271
272static void synusb_close(struct input_dev *dev)
273{
274 struct synusb *synusb = input_get_drvdata(dev);
275 int autopm_error;
276
277 autopm_error = usb_autopm_get_interface(synusb->intf);
278
279 usb_kill_urb(synusb->urb);
280 synusb->intf->needs_remote_wakeup = 0;
281
282 if (!autopm_error)
283 usb_autopm_put_interface(synusb->intf);
284}
285
286static int synusb_probe(struct usb_interface *intf,
287 const struct usb_device_id *id)
288{
289 struct usb_device *udev = interface_to_usbdev(intf);
290 struct usb_endpoint_descriptor *ep;
291 struct synusb *synusb;
292 struct input_dev *input_dev;
293 unsigned int intf_num = intf->cur_altsetting->desc.bInterfaceNumber;
294 unsigned int altsetting = min(intf->num_altsetting, 1U);
295 int error;
296
297 error = usb_set_interface(udev, intf_num, altsetting);
298 if (error) {
299 dev_err(&udev->dev,
300 "Can not set alternate setting to %i, error: %i",
301 altsetting, error);
302 return error;
303 }
304
305 ep = synusb_get_in_endpoint(intf->cur_altsetting);
306 if (!ep)
307 return -ENODEV;
308
309 synusb = kzalloc(sizeof(*synusb), GFP_KERNEL);
310 input_dev = input_allocate_device();
311 if (!synusb || !input_dev) {
312 error = -ENOMEM;
313 goto err_free_mem;
314 }
315
316 synusb->udev = udev;
317 synusb->intf = intf;
318 synusb->input = input_dev;
319
320 synusb->flags = id->driver_info;
321 if (synusb->flags & SYNUSB_COMBO) {
322 /*
323 * This is a combo device, we need to set proper
324 * capability, depending on the interface.
325 */
326 synusb->flags |= intf_num == 1 ?
327 SYNUSB_STICK : SYNUSB_TOUCHPAD;
328 }
329
330 synusb->urb = usb_alloc_urb(0, GFP_KERNEL);
331 if (!synusb->urb) {
332 error = -ENOMEM;
333 goto err_free_mem;
334 }
335
336 synusb->data = usb_alloc_coherent(udev, SYNUSB_RECV_SIZE, GFP_KERNEL,
337 &synusb->urb->transfer_dma);
338 if (!synusb->data) {
339 error = -ENOMEM;
340 goto err_free_urb;
341 }
342
343 usb_fill_int_urb(synusb->urb, udev,
344 usb_rcvintpipe(udev, ep->bEndpointAddress),
345 synusb->data, SYNUSB_RECV_SIZE,
346 synusb_irq, synusb,
347 ep->bInterval);
348 synusb->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
349
350 if (udev->manufacturer)
351 strlcpy(synusb->name, udev->manufacturer,
352 sizeof(synusb->name));
353
354 if (udev->product) {
355 if (udev->manufacturer)
356 strlcat(synusb->name, " ", sizeof(synusb->name));
357 strlcat(synusb->name, udev->product, sizeof(synusb->name));
358 }
359
360 if (!strlen(synusb->name))
361 snprintf(synusb->name, sizeof(synusb->name),
362 "USB Synaptics Device %04x:%04x",
363 le16_to_cpu(udev->descriptor.idVendor),
364 le16_to_cpu(udev->descriptor.idProduct));
365
366 if (synusb->flags & SYNUSB_STICK)
367 strlcat(synusb->name, " (Stick) ", sizeof(synusb->name));
368
369 usb_make_path(udev, synusb->phys, sizeof(synusb->phys));
370 strlcat(synusb->phys, "/input0", sizeof(synusb->phys));
371
372 input_dev->name = synusb->name;
373 input_dev->phys = synusb->phys;
374 usb_to_input_id(udev, &input_dev->id);
375 input_dev->dev.parent = &synusb->intf->dev;
376
377 if (!(synusb->flags & SYNUSB_IO_ALWAYS)) {
378 input_dev->open = synusb_open;
379 input_dev->close = synusb_close;
380 }
381
382 input_set_drvdata(input_dev, synusb);
383
384 __set_bit(EV_ABS, input_dev->evbit);
385 __set_bit(EV_KEY, input_dev->evbit);
386
387 if (synusb->flags & SYNUSB_STICK) {
388 __set_bit(EV_REL, input_dev->evbit);
389 __set_bit(REL_X, input_dev->relbit);
390 __set_bit(REL_Y, input_dev->relbit);
391 input_set_abs_params(input_dev, ABS_PRESSURE, 0, 127, 0, 0);
392 } else {
393 input_set_abs_params(input_dev, ABS_X,
394 XMIN_NOMINAL, XMAX_NOMINAL, 0, 0);
395 input_set_abs_params(input_dev, ABS_Y,
396 YMIN_NOMINAL, YMAX_NOMINAL, 0, 0);
397 input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0);
398 input_set_abs_params(input_dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
399 __set_bit(BTN_TOUCH, input_dev->keybit);
400 __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
401 __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
402 __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
403 }
404
405 __set_bit(BTN_LEFT, input_dev->keybit);
406 __set_bit(BTN_RIGHT, input_dev->keybit);
407 __set_bit(BTN_MIDDLE, input_dev->keybit);
408
409 usb_set_intfdata(intf, synusb);
410
411 if (synusb->flags & SYNUSB_IO_ALWAYS) {
412 error = synusb_open(input_dev);
413 if (error)
414 goto err_free_dma;
415 }
416
417 error = input_register_device(input_dev);
418 if (error) {
419 dev_err(&udev->dev,
420 "Failed to register input device, error %d\n",
421 error);
422 goto err_stop_io;
423 }
424
425 return 0;
426
427err_stop_io:
428 if (synusb->flags & SYNUSB_IO_ALWAYS)
429 synusb_close(synusb->input);
430err_free_dma:
431 usb_free_coherent(udev, SYNUSB_RECV_SIZE, synusb->data,
432 synusb->urb->transfer_dma);
433err_free_urb:
434 usb_free_urb(synusb->urb);
435err_free_mem:
436 input_free_device(input_dev);
437 kfree(synusb);
438 usb_set_intfdata(intf, NULL);
439
440 return error;
441}
442
443static void synusb_disconnect(struct usb_interface *intf)
444{
445 struct synusb *synusb = usb_get_intfdata(intf);
446 struct usb_device *udev = interface_to_usbdev(intf);
447
448 if (synusb->flags & SYNUSB_IO_ALWAYS)
449 synusb_close(synusb->input);
450
451 input_unregister_device(synusb->input);
452
453 usb_free_coherent(udev, SYNUSB_RECV_SIZE, synusb->data,
454 synusb->urb->transfer_dma);
455 usb_free_urb(synusb->urb);
456 kfree(synusb);
457
458 usb_set_intfdata(intf, NULL);
459}
460
461static int synusb_suspend(struct usb_interface *intf, pm_message_t message)
462{
463 struct synusb *synusb = usb_get_intfdata(intf);
464 struct input_dev *input_dev = synusb->input;
465
466 mutex_lock(&input_dev->mutex);
467 usb_kill_urb(synusb->urb);
468 mutex_unlock(&input_dev->mutex);
469
470 return 0;
471}
472
473static int synusb_resume(struct usb_interface *intf)
474{
475 struct synusb *synusb = usb_get_intfdata(intf);
476 struct input_dev *input_dev = synusb->input;
477 int retval = 0;
478
479 mutex_lock(&input_dev->mutex);
480
481 if ((input_dev->users || (synusb->flags & SYNUSB_IO_ALWAYS)) &&
482 usb_submit_urb(synusb->urb, GFP_NOIO) < 0) {
483 retval = -EIO;
484 }
485
486 mutex_unlock(&input_dev->mutex);
487
488 return retval;
489}
490
491static int synusb_pre_reset(struct usb_interface *intf)
492{
493 struct synusb *synusb = usb_get_intfdata(intf);
494 struct input_dev *input_dev = synusb->input;
495
496 mutex_lock(&input_dev->mutex);
497 usb_kill_urb(synusb->urb);
498
499 return 0;
500}
501
502static int synusb_post_reset(struct usb_interface *intf)
503{
504 struct synusb *synusb = usb_get_intfdata(intf);
505 struct input_dev *input_dev = synusb->input;
506 int retval = 0;
507
508 if ((input_dev->users || (synusb->flags & SYNUSB_IO_ALWAYS)) &&
509 usb_submit_urb(synusb->urb, GFP_NOIO) < 0) {
510 retval = -EIO;
511 }
512
513 mutex_unlock(&input_dev->mutex);
514
515 return retval;
516}
517
518static int synusb_reset_resume(struct usb_interface *intf)
519{
520 return synusb_resume(intf);
521}
522
523static struct usb_device_id synusb_idtable[] = {
524 { USB_DEVICE_SYNAPTICS(TP, SYNUSB_TOUCHPAD) },
525 { USB_DEVICE_SYNAPTICS(INT_TP, SYNUSB_TOUCHPAD) },
526 { USB_DEVICE_SYNAPTICS(CPAD,
527 SYNUSB_TOUCHPAD | SYNUSB_AUXDISPLAY | SYNUSB_IO_ALWAYS) },
528 { USB_DEVICE_SYNAPTICS(TS, SYNUSB_TOUCHSCREEN) },
529 { USB_DEVICE_SYNAPTICS(STICK, SYNUSB_STICK) },
530 { USB_DEVICE_SYNAPTICS(WP, SYNUSB_TOUCHPAD) },
531 { USB_DEVICE_SYNAPTICS(COMP_TP, SYNUSB_COMBO) },
532 { USB_DEVICE_SYNAPTICS(WTP, SYNUSB_TOUCHPAD) },
533 { USB_DEVICE_SYNAPTICS(DPAD, SYNUSB_TOUCHPAD) },
534 { }
535};
536MODULE_DEVICE_TABLE(usb, synusb_idtable);
537
538static struct usb_driver synusb_driver = {
539 .name = "synaptics_usb",
540 .probe = synusb_probe,
541 .disconnect = synusb_disconnect,
542 .id_table = synusb_idtable,
543 .suspend = synusb_suspend,
544 .resume = synusb_resume,
545 .pre_reset = synusb_pre_reset,
546 .post_reset = synusb_post_reset,
547 .reset_resume = synusb_reset_resume,
548 .supports_autosuspend = 1,
549};
550
551static int __init synusb_init(void)
552{
553 return usb_register(&synusb_driver);
554}
555
556static void __exit synusb_exit(void)
557{
558 usb_deregister(&synusb_driver);
559}
560
561module_init(synusb_init);
562module_exit(synusb_exit);
563
564MODULE_AUTHOR("Rob Miller <rob@inpharmatica.co.uk>, "
565 "Ron Lee <ron@debian.org>, "
566 "Jan Steinhoff <cpad@jan-steinhoff.de>");
567MODULE_DESCRIPTION("Synaptics USB device driver");
568MODULE_LICENSE("GPL");