Commit | Line | Data |
---|---|---|
34008dbf MD |
1 | /* |
2 | * Support for the Maxtor OneTouch USB hard drive's button | |
3 | * | |
4 | * Current development and maintenance by: | |
5 | * Copyright (c) 2005 Nick Sillik <n.sillik@temple.edu> | |
6 | * | |
7 | * Initial work by: | |
88789672 | 8 | * Copyright (c) 2003 Erik Thyren <erth7411@student.uu.se> |
34008dbf MD |
9 | * |
10 | * Based on usbmouse.c (Vojtech Pavlik) and xpad.c (Marko Friedemann) | |
11 | * | |
12 | */ | |
13 | ||
14 | /* | |
15 | * This program is free software; you can redistribute it and/or modify | |
16 | * it under the terms of the GNU General Public License as published by | |
17 | * the Free Software Foundation; either version 2 of the License, or | |
18 | * (at your option) any later version. | |
19 | * | |
20 | * This program is distributed in the hope that it will be useful, | |
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
23 | * GNU General Public License for more details. | |
24 | * | |
25 | * You should have received a copy of the GNU General Public License | |
26 | * along with this program; if not, write to the Free Software | |
27 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
28 | * | |
29 | */ | |
30 | ||
34008dbf MD |
31 | #include <linux/kernel.h> |
32 | #include <linux/input.h> | |
33 | #include <linux/init.h> | |
34 | #include <linux/slab.h> | |
35 | #include <linux/module.h> | |
ae0dadcf | 36 | #include <linux/usb/input.h> |
34008dbf | 37 | #include "usb.h" |
34008dbf MD |
38 | #include "debug.h" |
39 | ||
9cfb95ef AS |
40 | #define ONETOUCH_PKT_LEN 0x02 |
41 | #define ONETOUCH_BUTTON KEY_PROG1 | |
42 | ||
43 | static int onetouch_connect_input(struct us_data *ss); | |
43c1e98c | 44 | static void onetouch_release_input(void *onetouch_); |
34008dbf MD |
45 | |
46 | struct usb_onetouch { | |
47 | char name[128]; | |
48 | char phys[64]; | |
88789672 | 49 | struct input_dev *dev; /* input device interface */ |
34008dbf MD |
50 | struct usb_device *udev; /* usb device */ |
51 | ||
52 | struct urb *irq; /* urb for interrupt in report */ | |
53 | unsigned char *data; /* input data */ | |
54 | dma_addr_t data_dma; | |
7931e1c6 | 55 | unsigned int is_open:1; |
34008dbf MD |
56 | }; |
57 | ||
9cfb95ef AS |
58 | |
59 | /* | |
60 | * The table of devices | |
61 | */ | |
62 | #define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ | |
63 | vendorName, productName, useProtocol, useTransport, \ | |
64 | initFunction, flags) \ | |
65 | { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ | |
66 | .driver_info = (flags)|(USB_US_TYPE_STOR<<24) } | |
67 | ||
68 | struct usb_device_id onetouch_usb_ids[] = { | |
69 | # include "unusual_onetouch.h" | |
70 | { } /* Terminating entry */ | |
71 | }; | |
72 | MODULE_DEVICE_TABLE(usb, onetouch_usb_ids); | |
73 | ||
74 | #undef UNUSUAL_DEV | |
75 | ||
76 | /* | |
77 | * The flags table | |
78 | */ | |
79 | #define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ | |
80 | vendor_name, product_name, use_protocol, use_transport, \ | |
81 | init_function, Flags) \ | |
82 | { \ | |
83 | .vendorName = vendor_name, \ | |
84 | .productName = product_name, \ | |
85 | .useProtocol = use_protocol, \ | |
86 | .useTransport = use_transport, \ | |
87 | .initFunction = init_function, \ | |
88 | } | |
89 | ||
90 | static struct us_unusual_dev onetouch_unusual_dev_list[] = { | |
91 | # include "unusual_onetouch.h" | |
92 | { } /* Terminating entry */ | |
93 | }; | |
94 | ||
95 | #undef UNUSUAL_DEV | |
96 | ||
97 | ||
7d12e780 | 98 | static void usb_onetouch_irq(struct urb *urb) |
34008dbf MD |
99 | { |
100 | struct usb_onetouch *onetouch = urb->context; | |
101 | signed char *data = onetouch->data; | |
88789672 | 102 | struct input_dev *dev = onetouch->dev; |
62e5a330 GKH |
103 | int status = urb->status; |
104 | int retval; | |
34008dbf | 105 | |
62e5a330 | 106 | switch (status) { |
34008dbf MD |
107 | case 0: /* success */ |
108 | break; | |
109 | case -ECONNRESET: /* unlink */ | |
110 | case -ENOENT: | |
111 | case -ESHUTDOWN: | |
112 | return; | |
113 | /* -EPIPE: should clear the halt */ | |
114 | default: /* error */ | |
115 | goto resubmit; | |
116 | } | |
117 | ||
88789672 | 118 | input_report_key(dev, ONETOUCH_BUTTON, data[0] & 0x02); |
34008dbf | 119 | input_sync(dev); |
88789672 | 120 | |
34008dbf | 121 | resubmit: |
62e5a330 GKH |
122 | retval = usb_submit_urb (urb, GFP_ATOMIC); |
123 | if (retval) | |
802f389a GKH |
124 | dev_err(&dev->dev, "can't resubmit intr, %s-%s/input0, " |
125 | "retval %d\n", onetouch->udev->bus->bus_name, | |
62e5a330 | 126 | onetouch->udev->devpath, retval); |
34008dbf MD |
127 | } |
128 | ||
129 | static int usb_onetouch_open(struct input_dev *dev) | |
130 | { | |
09b7002d | 131 | struct usb_onetouch *onetouch = input_get_drvdata(dev); |
34008dbf | 132 | |
7931e1c6 | 133 | onetouch->is_open = 1; |
34008dbf MD |
134 | onetouch->irq->dev = onetouch->udev; |
135 | if (usb_submit_urb(onetouch->irq, GFP_KERNEL)) { | |
802f389a | 136 | dev_err(&dev->dev, "usb_submit_urb failed\n"); |
34008dbf MD |
137 | return -EIO; |
138 | } | |
139 | ||
140 | return 0; | |
141 | } | |
142 | ||
143 | static void usb_onetouch_close(struct input_dev *dev) | |
144 | { | |
09b7002d | 145 | struct usb_onetouch *onetouch = input_get_drvdata(dev); |
34008dbf MD |
146 | |
147 | usb_kill_urb(onetouch->irq); | |
7931e1c6 | 148 | onetouch->is_open = 0; |
34008dbf MD |
149 | } |
150 | ||
7931e1c6 MD |
151 | #ifdef CONFIG_PM |
152 | static void usb_onetouch_pm_hook(struct us_data *us, int action) | |
153 | { | |
154 | struct usb_onetouch *onetouch = (struct usb_onetouch *) us->extra; | |
155 | ||
156 | if (onetouch->is_open) { | |
157 | switch (action) { | |
158 | case US_SUSPEND: | |
159 | usb_kill_urb(onetouch->irq); | |
160 | break; | |
161 | case US_RESUME: | |
162 | if (usb_submit_urb(onetouch->irq, GFP_KERNEL) != 0) | |
802f389a GKH |
163 | dev_err(&onetouch->irq->dev->dev, |
164 | "usb_submit_urb failed\n"); | |
7931e1c6 MD |
165 | break; |
166 | default: | |
167 | break; | |
168 | } | |
169 | } | |
170 | } | |
171 | #endif /* CONFIG_PM */ | |
172 | ||
9cfb95ef | 173 | static int onetouch_connect_input(struct us_data *ss) |
34008dbf MD |
174 | { |
175 | struct usb_device *udev = ss->pusb_dev; | |
176 | struct usb_host_interface *interface; | |
177 | struct usb_endpoint_descriptor *endpoint; | |
178 | struct usb_onetouch *onetouch; | |
88789672 | 179 | struct input_dev *input_dev; |
34008dbf | 180 | int pipe, maxp; |
17efe155 | 181 | int error = -ENOMEM; |
34008dbf MD |
182 | |
183 | interface = ss->pusb_intf->cur_altsetting; | |
184 | ||
d6450e19 NS |
185 | if (interface->desc.bNumEndpoints != 3) |
186 | return -ENODEV; | |
187 | ||
34008dbf | 188 | endpoint = &interface->endpoint[2].desc; |
66722a19 | 189 | if (!usb_endpoint_is_int_in(endpoint)) |
34008dbf MD |
190 | return -ENODEV; |
191 | ||
192 | pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress); | |
193 | maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); | |
194 | ||
88789672 DT |
195 | onetouch = kzalloc(sizeof(struct usb_onetouch), GFP_KERNEL); |
196 | input_dev = input_allocate_device(); | |
197 | if (!onetouch || !input_dev) | |
198 | goto fail1; | |
34008dbf | 199 | |
d6450e19 | 200 | onetouch->data = usb_buffer_alloc(udev, ONETOUCH_PKT_LEN, |
54e6ecb2 | 201 | GFP_ATOMIC, &onetouch->data_dma); |
88789672 DT |
202 | if (!onetouch->data) |
203 | goto fail1; | |
34008dbf MD |
204 | |
205 | onetouch->irq = usb_alloc_urb(0, GFP_KERNEL); | |
88789672 DT |
206 | if (!onetouch->irq) |
207 | goto fail2; | |
34008dbf MD |
208 | |
209 | onetouch->udev = udev; | |
88789672 | 210 | onetouch->dev = input_dev; |
34008dbf | 211 | |
88789672 DT |
212 | if (udev->manufacturer) |
213 | strlcpy(onetouch->name, udev->manufacturer, | |
214 | sizeof(onetouch->name)); | |
215 | if (udev->product) { | |
216 | if (udev->manufacturer) | |
217 | strlcat(onetouch->name, " ", sizeof(onetouch->name)); | |
218 | strlcat(onetouch->name, udev->product, sizeof(onetouch->name)); | |
219 | } | |
34008dbf | 220 | |
88789672 DT |
221 | if (!strlen(onetouch->name)) |
222 | snprintf(onetouch->name, sizeof(onetouch->name), | |
223 | "Maxtor Onetouch %04x:%04x", | |
224 | le16_to_cpu(udev->descriptor.idVendor), | |
225 | le16_to_cpu(udev->descriptor.idProduct)); | |
34008dbf | 226 | |
88789672 DT |
227 | usb_make_path(udev, onetouch->phys, sizeof(onetouch->phys)); |
228 | strlcat(onetouch->phys, "/input0", sizeof(onetouch->phys)); | |
34008dbf | 229 | |
88789672 DT |
230 | input_dev->name = onetouch->name; |
231 | input_dev->phys = onetouch->phys; | |
232 | usb_to_input_id(udev, &input_dev->id); | |
09b7002d | 233 | input_dev->dev.parent = &udev->dev; |
34008dbf | 234 | |
88789672 DT |
235 | set_bit(EV_KEY, input_dev->evbit); |
236 | set_bit(ONETOUCH_BUTTON, input_dev->keybit); | |
237 | clear_bit(0, input_dev->keybit); | |
34008dbf | 238 | |
09b7002d DT |
239 | input_set_drvdata(input_dev, onetouch); |
240 | ||
88789672 DT |
241 | input_dev->open = usb_onetouch_open; |
242 | input_dev->close = usb_onetouch_close; | |
34008dbf MD |
243 | |
244 | usb_fill_int_urb(onetouch->irq, udev, pipe, onetouch->data, | |
245 | (maxp > 8 ? 8 : maxp), | |
246 | usb_onetouch_irq, onetouch, endpoint->bInterval); | |
247 | onetouch->irq->transfer_dma = onetouch->data_dma; | |
248 | onetouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; | |
249 | ||
250 | ss->extra_destructor = onetouch_release_input; | |
251 | ss->extra = onetouch; | |
7931e1c6 MD |
252 | #ifdef CONFIG_PM |
253 | ss->suspend_resume_hook = usb_onetouch_pm_hook; | |
254 | #endif | |
34008dbf | 255 | |
17efe155 DT |
256 | error = input_register_device(onetouch->dev); |
257 | if (error) | |
258 | goto fail3; | |
34008dbf MD |
259 | |
260 | return 0; | |
88789672 | 261 | |
17efe155 | 262 | fail3: usb_free_urb(onetouch->irq); |
88789672 DT |
263 | fail2: usb_buffer_free(udev, ONETOUCH_PKT_LEN, |
264 | onetouch->data, onetouch->data_dma); | |
265 | fail1: kfree(onetouch); | |
266 | input_free_device(input_dev); | |
17efe155 | 267 | return error; |
34008dbf MD |
268 | } |
269 | ||
43c1e98c | 270 | static void onetouch_release_input(void *onetouch_) |
34008dbf MD |
271 | { |
272 | struct usb_onetouch *onetouch = (struct usb_onetouch *) onetouch_; | |
273 | ||
274 | if (onetouch) { | |
275 | usb_kill_urb(onetouch->irq); | |
88789672 | 276 | input_unregister_device(onetouch->dev); |
34008dbf MD |
277 | usb_free_urb(onetouch->irq); |
278 | usb_buffer_free(onetouch->udev, ONETOUCH_PKT_LEN, | |
279 | onetouch->data, onetouch->data_dma); | |
34008dbf MD |
280 | } |
281 | } | |
9cfb95ef AS |
282 | |
283 | static int onetouch_probe(struct usb_interface *intf, | |
284 | const struct usb_device_id *id) | |
285 | { | |
286 | struct us_data *us; | |
287 | int result; | |
288 | ||
289 | result = usb_stor_probe1(&us, intf, id, | |
290 | (id - onetouch_usb_ids) + onetouch_unusual_dev_list); | |
291 | if (result) | |
292 | return result; | |
293 | ||
294 | /* Use default transport and protocol */ | |
295 | ||
296 | result = usb_stor_probe2(us); | |
297 | return result; | |
298 | } | |
299 | ||
300 | static struct usb_driver onetouch_driver = { | |
301 | .name = "ums-onetouch", | |
302 | .probe = onetouch_probe, | |
303 | .disconnect = usb_stor_disconnect, | |
304 | .suspend = usb_stor_suspend, | |
305 | .resume = usb_stor_resume, | |
306 | .reset_resume = usb_stor_reset_resume, | |
307 | .pre_reset = usb_stor_pre_reset, | |
308 | .post_reset = usb_stor_post_reset, | |
309 | .id_table = onetouch_usb_ids, | |
310 | .soft_unbind = 1, | |
311 | }; | |
312 | ||
313 | static int __init onetouch_init(void) | |
314 | { | |
315 | return usb_register(&onetouch_driver); | |
316 | } | |
317 | ||
318 | static void __exit onetouch_exit(void) | |
319 | { | |
320 | usb_deregister(&onetouch_driver); | |
321 | } | |
322 | ||
323 | module_init(onetouch_init); | |
324 | module_exit(onetouch_exit); |