Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /****************************************************************************** |
2 | * mtouchusb.c -- Driver for Microtouch (Now 3M) USB Touchscreens | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or | |
5 | * modify it under the terms of the GNU General Public License as | |
6 | * published by the Free Software Foundation; either version 2 of the | |
7 | * License, or (at your option) any later version. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, but | |
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
12 | * General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program; if not, write to the Free Software | |
16 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
17 | * | |
18 | * Based upon original work by Radoslaw Garbacz (usb-support@ite.pl) | |
19 | * (http://freshmeat.net/projects/3mtouchscreendriver) | |
20 | * | |
21 | * History | |
22 | * | |
23 | * 0.3 & 0.4 2002 (TEJ) tejohnson@yahoo.com | |
24 | * Updated to 2.4.18, then 2.4.19 | |
25 | * Old version still relied on stealing a minor | |
26 | * | |
27 | * 0.5 02/26/2004 (TEJ) tejohnson@yahoo.com | |
28 | * Complete rewrite using Linux Input in 2.6.3 | |
29 | * Unfortunately no calibration support at this time | |
30 | * | |
31 | * 1.4 04/25/2004 (TEJ) tejohnson@yahoo.com | |
32 | * Changed reset from standard USB dev reset to vendor reset | |
33 | * Changed data sent to host from compensated to raw coordinates | |
34 | * Eliminated vendor/product module params | |
35 | * Performed multiple successfull tests with an EXII-5010UC | |
36 | * | |
37 | * 1.5 02/27/2005 ddstreet@ieee.org | |
38 | * Added module parameter to select raw or hw-calibrated coordinate reporting | |
39 | * | |
40 | *****************************************************************************/ | |
41 | ||
42 | #include <linux/config.h> | |
43 | ||
44 | #ifdef CONFIG_USB_DEBUG | |
45 | #define DEBUG | |
46 | #else | |
47 | #undef DEBUG | |
48 | #endif | |
49 | ||
50 | #include <linux/kernel.h> | |
51 | #include <linux/slab.h> | |
52 | #include <linux/input.h> | |
53 | #include <linux/module.h> | |
54 | #include <linux/init.h> | |
55 | #include <linux/usb.h> | |
56 | ||
57 | #define MTOUCHUSB_MIN_XC 0x0 | |
58 | #define MTOUCHUSB_MAX_RAW_XC 0x4000 | |
59 | #define MTOUCHUSB_MAX_CALIB_XC 0xffff | |
60 | #define MTOUCHUSB_XC_FUZZ 0x0 | |
61 | #define MTOUCHUSB_XC_FLAT 0x0 | |
62 | #define MTOUCHUSB_MIN_YC 0x0 | |
63 | #define MTOUCHUSB_MAX_RAW_YC 0x4000 | |
64 | #define MTOUCHUSB_MAX_CALIB_YC 0xffff | |
65 | #define MTOUCHUSB_YC_FUZZ 0x0 | |
66 | #define MTOUCHUSB_YC_FLAT 0x0 | |
67 | ||
68 | #define MTOUCHUSB_ASYNC_REPORT 1 | |
69 | #define MTOUCHUSB_RESET 7 | |
70 | #define MTOUCHUSB_REPORT_DATA_SIZE 11 | |
71 | #define MTOUCHUSB_REQ_CTRLLR_ID 10 | |
72 | ||
73 | #define MTOUCHUSB_GET_RAW_XC(data) (data[8]<<8 | data[7]) | |
74 | #define MTOUCHUSB_GET_CALIB_XC(data) (data[4]<<8 | data[3]) | |
75 | #define MTOUCHUSB_GET_RAW_YC(data) (data[10]<<8 | data[9]) | |
76 | #define MTOUCHUSB_GET_CALIB_YC(data) (data[6]<<8 | data[5]) | |
77 | #define MTOUCHUSB_GET_XC(data) (raw_coordinates ? \ | |
78 | MTOUCHUSB_GET_RAW_XC(data) : \ | |
79 | MTOUCHUSB_GET_CALIB_XC(data)) | |
80 | #define MTOUCHUSB_GET_YC(data) (raw_coordinates ? \ | |
81 | MTOUCHUSB_GET_RAW_YC(data) : \ | |
82 | MTOUCHUSB_GET_CALIB_YC(data)) | |
83 | #define MTOUCHUSB_GET_TOUCHED(data) ((data[2] & 0x40) ? 1:0) | |
84 | ||
85 | #define DRIVER_VERSION "v1.5" | |
86 | #define DRIVER_AUTHOR "Todd E. Johnson, tejohnson@yahoo.com" | |
87 | #define DRIVER_DESC "3M USB Touchscreen Driver" | |
88 | #define DRIVER_LICENSE "GPL" | |
89 | ||
90 | static int raw_coordinates = 1; | |
91 | ||
92 | module_param(raw_coordinates, bool, S_IRUGO | S_IWUSR); | |
93 | MODULE_PARM_DESC(raw_coordinates, "report raw coordinate values (y, default) or hardware-calibrated coordinate values (n)"); | |
94 | ||
95 | struct mtouch_usb { | |
96 | unsigned char *data; | |
97 | dma_addr_t data_dma; | |
98 | struct urb *irq; | |
99 | struct usb_device *udev; | |
100 | struct input_dev input; | |
101 | int open; | |
102 | char name[128]; | |
103 | char phys[64]; | |
104 | }; | |
105 | ||
106 | static struct usb_device_id mtouchusb_devices [] = { | |
107 | { USB_DEVICE(0x0596, 0x0001) }, | |
108 | { } | |
109 | }; | |
110 | ||
111 | static void mtouchusb_irq(struct urb *urb, struct pt_regs *regs) | |
112 | { | |
113 | struct mtouch_usb *mtouch = urb->context; | |
114 | int retval; | |
115 | ||
116 | switch (urb->status) { | |
117 | case 0: | |
118 | /* success */ | |
119 | break; | |
120 | case -ETIMEDOUT: | |
121 | /* this urb is timing out */ | |
122 | dbg("%s - urb timed out - was the device unplugged?", | |
123 | __FUNCTION__); | |
124 | return; | |
125 | case -ECONNRESET: | |
126 | case -ENOENT: | |
127 | case -ESHUTDOWN: | |
128 | /* this urb is terminated, clean up */ | |
129 | dbg("%s - urb shutting down with status: %d", | |
130 | __FUNCTION__, urb->status); | |
131 | return; | |
132 | default: | |
133 | dbg("%s - nonzero urb status received: %d", | |
134 | __FUNCTION__, urb->status); | |
135 | goto exit; | |
136 | } | |
137 | ||
138 | input_regs(&mtouch->input, regs); | |
139 | input_report_key(&mtouch->input, BTN_TOUCH, | |
140 | MTOUCHUSB_GET_TOUCHED(mtouch->data)); | |
141 | input_report_abs(&mtouch->input, ABS_X, | |
142 | MTOUCHUSB_GET_XC(mtouch->data)); | |
143 | input_report_abs(&mtouch->input, ABS_Y, | |
144 | (raw_coordinates ? MTOUCHUSB_MAX_RAW_YC : MTOUCHUSB_MAX_CALIB_YC) | |
145 | - MTOUCHUSB_GET_YC(mtouch->data)); | |
146 | input_sync(&mtouch->input); | |
147 | ||
148 | exit: | |
149 | retval = usb_submit_urb (urb, GFP_ATOMIC); | |
150 | if (retval) | |
151 | err ("%s - usb_submit_urb failed with result: %d", | |
152 | __FUNCTION__, retval); | |
153 | } | |
154 | ||
155 | static int mtouchusb_open (struct input_dev *input) | |
156 | { | |
157 | struct mtouch_usb *mtouch = input->private; | |
158 | ||
159 | if (mtouch->open++) | |
160 | return 0; | |
161 | ||
162 | mtouch->irq->dev = mtouch->udev; | |
163 | ||
164 | if (usb_submit_urb (mtouch->irq, GFP_ATOMIC)) { | |
165 | mtouch->open--; | |
166 | return -EIO; | |
167 | } | |
168 | ||
169 | return 0; | |
170 | } | |
171 | ||
172 | static void mtouchusb_close (struct input_dev *input) | |
173 | { | |
174 | struct mtouch_usb *mtouch = input->private; | |
175 | ||
176 | if (!--mtouch->open) | |
177 | usb_kill_urb (mtouch->irq); | |
178 | } | |
179 | ||
180 | static int mtouchusb_alloc_buffers(struct usb_device *udev, struct mtouch_usb *mtouch) | |
181 | { | |
182 | dbg("%s - called", __FUNCTION__); | |
183 | ||
184 | mtouch->data = usb_buffer_alloc(udev, MTOUCHUSB_REPORT_DATA_SIZE, | |
185 | SLAB_ATOMIC, &mtouch->data_dma); | |
186 | ||
187 | if (!mtouch->data) | |
188 | return -1; | |
189 | ||
190 | return 0; | |
191 | } | |
192 | ||
193 | static void mtouchusb_free_buffers(struct usb_device *udev, struct mtouch_usb *mtouch) | |
194 | { | |
195 | dbg("%s - called", __FUNCTION__); | |
196 | ||
197 | if (mtouch->data) | |
198 | usb_buffer_free(udev, MTOUCHUSB_REPORT_DATA_SIZE, | |
199 | mtouch->data, mtouch->data_dma); | |
200 | } | |
201 | ||
202 | static int mtouchusb_probe(struct usb_interface *intf, const struct usb_device_id *id) | |
203 | { | |
204 | struct mtouch_usb *mtouch; | |
205 | struct usb_host_interface *interface; | |
206 | struct usb_endpoint_descriptor *endpoint; | |
207 | struct usb_device *udev = interface_to_usbdev (intf); | |
208 | char path[64]; | |
209 | int nRet; | |
210 | ||
211 | dbg("%s - called", __FUNCTION__); | |
212 | ||
213 | dbg("%s - setting interface", __FUNCTION__); | |
214 | interface = intf->cur_altsetting; | |
215 | ||
216 | dbg("%s - setting endpoint", __FUNCTION__); | |
217 | endpoint = &interface->endpoint[0].desc; | |
218 | ||
219 | if (!(mtouch = kmalloc (sizeof (struct mtouch_usb), GFP_KERNEL))) { | |
220 | err("%s - Out of memory.", __FUNCTION__); | |
221 | return -ENOMEM; | |
222 | } | |
223 | ||
224 | memset(mtouch, 0, sizeof(struct mtouch_usb)); | |
225 | mtouch->udev = udev; | |
226 | ||
227 | dbg("%s - allocating buffers", __FUNCTION__); | |
228 | if (mtouchusb_alloc_buffers(udev, mtouch)) { | |
229 | mtouchusb_free_buffers(udev, mtouch); | |
230 | kfree(mtouch); | |
231 | return -ENOMEM; | |
232 | } | |
233 | ||
234 | mtouch->input.private = mtouch; | |
235 | mtouch->input.open = mtouchusb_open; | |
236 | mtouch->input.close = mtouchusb_close; | |
237 | ||
238 | usb_make_path(udev, path, 64); | |
239 | sprintf(mtouch->phys, "%s/input0", path); | |
240 | ||
241 | mtouch->input.name = mtouch->name; | |
242 | mtouch->input.phys = mtouch->phys; | |
243 | mtouch->input.id.bustype = BUS_USB; | |
244 | mtouch->input.id.vendor = le16_to_cpu(udev->descriptor.idVendor); | |
245 | mtouch->input.id.product = le16_to_cpu(udev->descriptor.idProduct); | |
246 | mtouch->input.id.version = le16_to_cpu(udev->descriptor.bcdDevice); | |
247 | mtouch->input.dev = &intf->dev; | |
248 | ||
249 | mtouch->input.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); | |
250 | mtouch->input.absbit[0] = BIT(ABS_X) | BIT(ABS_Y); | |
251 | mtouch->input.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); | |
252 | ||
253 | /* Used to Scale Compensated Data and Flip Y */ | |
254 | mtouch->input.absmin[ABS_X] = MTOUCHUSB_MIN_XC; | |
255 | mtouch->input.absmax[ABS_X] = raw_coordinates ? \ | |
256 | MTOUCHUSB_MAX_RAW_XC : MTOUCHUSB_MAX_CALIB_XC; | |
257 | mtouch->input.absfuzz[ABS_X] = MTOUCHUSB_XC_FUZZ; | |
258 | mtouch->input.absflat[ABS_X] = MTOUCHUSB_XC_FLAT; | |
259 | mtouch->input.absmin[ABS_Y] = MTOUCHUSB_MIN_YC; | |
260 | mtouch->input.absmax[ABS_Y] = raw_coordinates ? \ | |
261 | MTOUCHUSB_MAX_RAW_YC : MTOUCHUSB_MAX_CALIB_YC; | |
262 | mtouch->input.absfuzz[ABS_Y] = MTOUCHUSB_YC_FUZZ; | |
263 | mtouch->input.absflat[ABS_Y] = MTOUCHUSB_YC_FLAT; | |
264 | ||
265 | if (udev->manufacturer) | |
266 | strcat(mtouch->name, udev->manufacturer); | |
267 | if (udev->product) | |
268 | sprintf(mtouch->name, "%s %s", mtouch->name, udev->product); | |
269 | ||
270 | if (!strlen(mtouch->name)) | |
271 | sprintf(mtouch->name, "USB Touchscreen %04x:%04x", | |
272 | mtouch->input.id.vendor, mtouch->input.id.product); | |
273 | ||
274 | nRet = usb_control_msg(mtouch->udev, | |
275 | usb_rcvctrlpipe(udev, 0), | |
276 | MTOUCHUSB_RESET, | |
277 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | |
278 | 1, | |
279 | 0, | |
280 | NULL, | |
281 | 0, | |
282 | USB_CTRL_SET_TIMEOUT); | |
283 | dbg("%s - usb_control_msg - MTOUCHUSB_RESET - bytes|err: %d", | |
284 | __FUNCTION__, nRet); | |
285 | ||
286 | dbg("%s - usb_alloc_urb: mtouch->irq", __FUNCTION__); | |
287 | mtouch->irq = usb_alloc_urb(0, GFP_KERNEL); | |
288 | if (!mtouch->irq) { | |
289 | dbg("%s - usb_alloc_urb failed: mtouch->irq", __FUNCTION__); | |
290 | mtouchusb_free_buffers(udev, mtouch); | |
291 | kfree(mtouch); | |
292 | return -ENOMEM; | |
293 | } | |
294 | ||
295 | dbg("%s - usb_fill_int_urb", __FUNCTION__); | |
296 | usb_fill_int_urb(mtouch->irq, | |
297 | mtouch->udev, | |
298 | usb_rcvintpipe(mtouch->udev, 0x81), | |
299 | mtouch->data, | |
300 | MTOUCHUSB_REPORT_DATA_SIZE, | |
301 | mtouchusb_irq, | |
302 | mtouch, | |
303 | endpoint->bInterval); | |
304 | ||
305 | dbg("%s - input_register_device", __FUNCTION__); | |
306 | input_register_device(&mtouch->input); | |
307 | ||
308 | nRet = usb_control_msg(mtouch->udev, | |
309 | usb_rcvctrlpipe(udev, 0), | |
310 | MTOUCHUSB_ASYNC_REPORT, | |
311 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | |
312 | 1, | |
313 | 1, | |
314 | NULL, | |
315 | 0, | |
316 | USB_CTRL_SET_TIMEOUT); | |
317 | dbg("%s - usb_control_msg - MTOUCHUSB_ASYNC_REPORT - bytes|err: %d", | |
318 | __FUNCTION__, nRet); | |
319 | ||
320 | printk(KERN_INFO "input: %s on %s\n", mtouch->name, path); | |
321 | usb_set_intfdata(intf, mtouch); | |
322 | ||
323 | return 0; | |
324 | } | |
325 | ||
326 | static void mtouchusb_disconnect(struct usb_interface *intf) | |
327 | { | |
328 | struct mtouch_usb *mtouch = usb_get_intfdata (intf); | |
329 | ||
330 | dbg("%s - called", __FUNCTION__); | |
331 | usb_set_intfdata(intf, NULL); | |
332 | if (mtouch) { | |
333 | dbg("%s - mtouch is initialized, cleaning up", __FUNCTION__); | |
334 | usb_kill_urb(mtouch->irq); | |
335 | input_unregister_device(&mtouch->input); | |
336 | usb_free_urb(mtouch->irq); | |
337 | mtouchusb_free_buffers(interface_to_usbdev(intf), mtouch); | |
338 | kfree(mtouch); | |
339 | } | |
340 | } | |
341 | ||
342 | MODULE_DEVICE_TABLE (usb, mtouchusb_devices); | |
343 | ||
344 | static struct usb_driver mtouchusb_driver = { | |
345 | .owner = THIS_MODULE, | |
346 | .name = "mtouchusb", | |
347 | .probe = mtouchusb_probe, | |
348 | .disconnect = mtouchusb_disconnect, | |
349 | .id_table = mtouchusb_devices, | |
350 | }; | |
351 | ||
352 | static int __init mtouchusb_init(void) { | |
353 | dbg("%s - called", __FUNCTION__); | |
354 | return usb_register(&mtouchusb_driver); | |
355 | } | |
356 | ||
357 | static void __exit mtouchusb_cleanup(void) { | |
358 | dbg("%s - called", __FUNCTION__); | |
359 | usb_deregister(&mtouchusb_driver); | |
360 | } | |
361 | ||
362 | module_init(mtouchusb_init); | |
363 | module_exit(mtouchusb_cleanup); | |
364 | ||
365 | MODULE_AUTHOR( DRIVER_AUTHOR ); | |
366 | MODULE_DESCRIPTION( DRIVER_DESC ); | |
367 | MODULE_LICENSE("GPL"); |