1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Support for the Maxtor OneTouch USB hard drive's button |
4 | * |
5 | * Current development and maintenance by: |
6 | * Copyright (c) 2005 Nick Sillik <n.sillik@temple.edu> |
7 | * |
8 | * Initial work by: |
9 | * Copyright (c) 2003 Erik Thyren <erth7411@student.uu.se> |
10 | * |
11 | * Based on usbmouse.c (Vojtech Pavlik) and xpad.c (Marko Friedemann) |
12 | * |
13 | */ |
14 | #include <linux/kernel.h> |
15 | #include <linux/input.h> |
16 | #include <linux/slab.h> |
17 | #include <linux/module.h> |
18 | #include <linux/usb/input.h> |
19 | #include "usb.h" |
20 | #include "debug.h" |
21 | #include "scsiglue.h" |
22 | |
23 | #define DRV_NAME "ums-onetouch" |
24 | |
25 | MODULE_DESCRIPTION("Maxtor USB OneTouch hard drive button driver" ); |
26 | MODULE_AUTHOR("Nick Sillik <n.sillik@temple.edu>" ); |
27 | MODULE_LICENSE("GPL" ); |
28 | MODULE_IMPORT_NS(USB_STORAGE); |
29 | |
30 | #define ONETOUCH_PKT_LEN 0x02 |
31 | #define ONETOUCH_BUTTON KEY_PROG1 |
32 | |
33 | static int onetouch_connect_input(struct us_data *ss); |
34 | static void onetouch_release_input(void *onetouch_); |
35 | |
36 | struct usb_onetouch { |
37 | char name[128]; |
38 | char phys[64]; |
39 | struct input_dev *dev; /* input device interface */ |
40 | struct usb_device *udev; /* usb device */ |
41 | |
42 | struct urb *irq; /* urb for interrupt in report */ |
43 | unsigned char *data; /* input data */ |
44 | dma_addr_t data_dma; |
45 | unsigned int is_open:1; |
46 | }; |
47 | |
48 | |
49 | /* |
50 | * The table of devices |
51 | */ |
52 | #define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ |
53 | vendorName, productName, useProtocol, useTransport, \ |
54 | initFunction, flags) \ |
55 | { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ |
56 | .driver_info = (flags) } |
57 | |
58 | static struct usb_device_id onetouch_usb_ids[] = { |
59 | # include "unusual_onetouch.h" |
60 | { } /* Terminating entry */ |
61 | }; |
62 | MODULE_DEVICE_TABLE(usb, onetouch_usb_ids); |
63 | |
64 | #undef UNUSUAL_DEV |
65 | |
66 | /* |
67 | * The flags table |
68 | */ |
69 | #define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ |
70 | vendor_name, product_name, use_protocol, use_transport, \ |
71 | init_function, Flags) \ |
72 | { \ |
73 | .vendorName = vendor_name, \ |
74 | .productName = product_name, \ |
75 | .useProtocol = use_protocol, \ |
76 | .useTransport = use_transport, \ |
77 | .initFunction = init_function, \ |
78 | } |
79 | |
80 | static struct us_unusual_dev onetouch_unusual_dev_list[] = { |
81 | # include "unusual_onetouch.h" |
82 | { } /* Terminating entry */ |
83 | }; |
84 | |
85 | #undef UNUSUAL_DEV |
86 | |
87 | |
88 | static void usb_onetouch_irq(struct urb *urb) |
89 | { |
90 | struct usb_onetouch *onetouch = urb->context; |
91 | signed char *data = onetouch->data; |
92 | struct input_dev *dev = onetouch->dev; |
93 | int status = urb->status; |
94 | int retval; |
95 | |
96 | switch (status) { |
97 | case 0: /* success */ |
98 | break; |
99 | case -ECONNRESET: /* unlink */ |
100 | case -ENOENT: |
101 | case -ESHUTDOWN: |
102 | return; |
103 | /* -EPIPE: should clear the halt */ |
104 | default: /* error */ |
105 | goto resubmit; |
106 | } |
107 | |
108 | input_report_key(dev, ONETOUCH_BUTTON, value: data[0] & 0x02); |
109 | input_sync(dev); |
110 | |
111 | resubmit: |
112 | retval = usb_submit_urb (urb, GFP_ATOMIC); |
113 | if (retval) |
114 | dev_err(&dev->dev, "can't resubmit intr, %s-%s/input0, " |
115 | "retval %d\n" , onetouch->udev->bus->bus_name, |
116 | onetouch->udev->devpath, retval); |
117 | } |
118 | |
119 | static int usb_onetouch_open(struct input_dev *dev) |
120 | { |
121 | struct usb_onetouch *onetouch = input_get_drvdata(dev); |
122 | |
123 | onetouch->is_open = 1; |
124 | onetouch->irq->dev = onetouch->udev; |
125 | if (usb_submit_urb(urb: onetouch->irq, GFP_KERNEL)) { |
126 | dev_err(&dev->dev, "usb_submit_urb failed\n" ); |
127 | return -EIO; |
128 | } |
129 | |
130 | return 0; |
131 | } |
132 | |
133 | static void usb_onetouch_close(struct input_dev *dev) |
134 | { |
135 | struct usb_onetouch *onetouch = input_get_drvdata(dev); |
136 | |
137 | usb_kill_urb(urb: onetouch->irq); |
138 | onetouch->is_open = 0; |
139 | } |
140 | |
141 | #ifdef CONFIG_PM |
142 | static void usb_onetouch_pm_hook(struct us_data *us, int action) |
143 | { |
144 | struct usb_onetouch *onetouch = (struct usb_onetouch *) us->extra; |
145 | |
146 | if (onetouch->is_open) { |
147 | switch (action) { |
148 | case US_SUSPEND: |
149 | usb_kill_urb(urb: onetouch->irq); |
150 | break; |
151 | case US_RESUME: |
152 | if (usb_submit_urb(urb: onetouch->irq, GFP_NOIO) != 0) |
153 | dev_err(&onetouch->irq->dev->dev, |
154 | "usb_submit_urb failed\n" ); |
155 | break; |
156 | default: |
157 | break; |
158 | } |
159 | } |
160 | } |
161 | #endif /* CONFIG_PM */ |
162 | |
163 | static int onetouch_connect_input(struct us_data *ss) |
164 | { |
165 | struct usb_device *udev = ss->pusb_dev; |
166 | struct usb_host_interface *interface; |
167 | struct usb_endpoint_descriptor *endpoint; |
168 | struct usb_onetouch *onetouch; |
169 | struct input_dev *input_dev; |
170 | int pipe, maxp; |
171 | int error = -ENOMEM; |
172 | |
173 | interface = ss->pusb_intf->cur_altsetting; |
174 | |
175 | if (interface->desc.bNumEndpoints != 3) |
176 | return -ENODEV; |
177 | |
178 | endpoint = &interface->endpoint[2].desc; |
179 | if (!usb_endpoint_is_int_in(epd: endpoint)) |
180 | return -ENODEV; |
181 | |
182 | pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress); |
183 | maxp = usb_maxpacket(udev, pipe); |
184 | maxp = min(maxp, ONETOUCH_PKT_LEN); |
185 | |
186 | onetouch = kzalloc(size: sizeof(struct usb_onetouch), GFP_KERNEL); |
187 | input_dev = input_allocate_device(); |
188 | if (!onetouch || !input_dev) |
189 | goto fail1; |
190 | |
191 | onetouch->data = usb_alloc_coherent(dev: udev, ONETOUCH_PKT_LEN, |
192 | GFP_KERNEL, dma: &onetouch->data_dma); |
193 | if (!onetouch->data) |
194 | goto fail1; |
195 | |
196 | onetouch->irq = usb_alloc_urb(iso_packets: 0, GFP_KERNEL); |
197 | if (!onetouch->irq) |
198 | goto fail2; |
199 | |
200 | onetouch->udev = udev; |
201 | onetouch->dev = input_dev; |
202 | |
203 | if (udev->manufacturer) |
204 | strscpy(onetouch->name, udev->manufacturer, |
205 | sizeof(onetouch->name)); |
206 | if (udev->product) { |
207 | if (udev->manufacturer) |
208 | strlcat(p: onetouch->name, q: " " , avail: sizeof(onetouch->name)); |
209 | strlcat(p: onetouch->name, q: udev->product, avail: sizeof(onetouch->name)); |
210 | } |
211 | |
212 | if (!strlen(onetouch->name)) |
213 | snprintf(buf: onetouch->name, size: sizeof(onetouch->name), |
214 | fmt: "Maxtor Onetouch %04x:%04x" , |
215 | le16_to_cpu(udev->descriptor.idVendor), |
216 | le16_to_cpu(udev->descriptor.idProduct)); |
217 | |
218 | usb_make_path(dev: udev, buf: onetouch->phys, size: sizeof(onetouch->phys)); |
219 | strlcat(p: onetouch->phys, q: "/input0" , avail: sizeof(onetouch->phys)); |
220 | |
221 | input_dev->name = onetouch->name; |
222 | input_dev->phys = onetouch->phys; |
223 | usb_to_input_id(dev: udev, id: &input_dev->id); |
224 | input_dev->dev.parent = &udev->dev; |
225 | |
226 | set_bit(EV_KEY, addr: input_dev->evbit); |
227 | set_bit(ONETOUCH_BUTTON, addr: input_dev->keybit); |
228 | clear_bit(nr: 0, addr: input_dev->keybit); |
229 | |
230 | input_set_drvdata(dev: input_dev, data: onetouch); |
231 | |
232 | input_dev->open = usb_onetouch_open; |
233 | input_dev->close = usb_onetouch_close; |
234 | |
235 | usb_fill_int_urb(urb: onetouch->irq, dev: udev, pipe, transfer_buffer: onetouch->data, buffer_length: maxp, |
236 | complete_fn: usb_onetouch_irq, context: onetouch, interval: endpoint->bInterval); |
237 | onetouch->irq->transfer_dma = onetouch->data_dma; |
238 | onetouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; |
239 | |
240 | ss->extra_destructor = onetouch_release_input; |
241 | ss->extra = onetouch; |
242 | #ifdef CONFIG_PM |
243 | ss->suspend_resume_hook = usb_onetouch_pm_hook; |
244 | #endif |
245 | |
246 | error = input_register_device(onetouch->dev); |
247 | if (error) |
248 | goto fail3; |
249 | |
250 | return 0; |
251 | |
252 | fail3: usb_free_urb(urb: onetouch->irq); |
253 | fail2: usb_free_coherent(dev: udev, ONETOUCH_PKT_LEN, |
254 | addr: onetouch->data, dma: onetouch->data_dma); |
255 | fail1: kfree(objp: onetouch); |
256 | input_free_device(dev: input_dev); |
257 | return error; |
258 | } |
259 | |
260 | static void onetouch_release_input(void *onetouch_) |
261 | { |
262 | struct usb_onetouch *onetouch = (struct usb_onetouch *) onetouch_; |
263 | |
264 | if (onetouch) { |
265 | usb_kill_urb(urb: onetouch->irq); |
266 | input_unregister_device(onetouch->dev); |
267 | usb_free_urb(urb: onetouch->irq); |
268 | usb_free_coherent(dev: onetouch->udev, ONETOUCH_PKT_LEN, |
269 | addr: onetouch->data, dma: onetouch->data_dma); |
270 | } |
271 | } |
272 | |
273 | static struct scsi_host_template onetouch_host_template; |
274 | |
275 | static int onetouch_probe(struct usb_interface *intf, |
276 | const struct usb_device_id *id) |
277 | { |
278 | struct us_data *us; |
279 | int result; |
280 | |
281 | result = usb_stor_probe1(pus: &us, intf, id, |
282 | unusual_dev: (id - onetouch_usb_ids) + onetouch_unusual_dev_list, |
283 | sht: &onetouch_host_template); |
284 | if (result) |
285 | return result; |
286 | |
287 | /* Use default transport and protocol */ |
288 | |
289 | result = usb_stor_probe2(us); |
290 | return result; |
291 | } |
292 | |
293 | static struct usb_driver onetouch_driver = { |
294 | .name = DRV_NAME, |
295 | .probe = onetouch_probe, |
296 | .disconnect = usb_stor_disconnect, |
297 | .suspend = usb_stor_suspend, |
298 | .resume = usb_stor_resume, |
299 | .reset_resume = usb_stor_reset_resume, |
300 | .pre_reset = usb_stor_pre_reset, |
301 | .post_reset = usb_stor_post_reset, |
302 | .id_table = onetouch_usb_ids, |
303 | .soft_unbind = 1, |
304 | .no_dynamic_id = 1, |
305 | }; |
306 | |
307 | module_usb_stor_driver(onetouch_driver, onetouch_host_template, DRV_NAME); |
308 | |