xref: /openbmc/linux/drivers/usb/storage/onetouch.c (revision e1cd7b80)
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, 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(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(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(onetouch->irq);
150 			break;
151 		case US_RESUME:
152 			if (usb_submit_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(endpoint))
180 		return -ENODEV;
181 
182 	pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
183 	maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
184 	maxp = min(maxp, ONETOUCH_PKT_LEN);
185 
186 	onetouch = kzalloc(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(udev, ONETOUCH_PKT_LEN,
192 					    GFP_KERNEL, &onetouch->data_dma);
193 	if (!onetouch->data)
194 		goto fail1;
195 
196 	onetouch->irq = usb_alloc_urb(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 		strlcpy(onetouch->name, udev->manufacturer,
205 			sizeof(onetouch->name));
206 	if (udev->product) {
207 		if (udev->manufacturer)
208 			strlcat(onetouch->name, " ", sizeof(onetouch->name));
209 		strlcat(onetouch->name, udev->product, sizeof(onetouch->name));
210 	}
211 
212 	if (!strlen(onetouch->name))
213 		snprintf(onetouch->name, sizeof(onetouch->name),
214 			 "Maxtor Onetouch %04x:%04x",
215 			 le16_to_cpu(udev->descriptor.idVendor),
216 			 le16_to_cpu(udev->descriptor.idProduct));
217 
218 	usb_make_path(udev, onetouch->phys, sizeof(onetouch->phys));
219 	strlcat(onetouch->phys, "/input0", sizeof(onetouch->phys));
220 
221 	input_dev->name = onetouch->name;
222 	input_dev->phys = onetouch->phys;
223 	usb_to_input_id(udev, &input_dev->id);
224 	input_dev->dev.parent = &udev->dev;
225 
226 	set_bit(EV_KEY, input_dev->evbit);
227 	set_bit(ONETOUCH_BUTTON, input_dev->keybit);
228 	clear_bit(0, input_dev->keybit);
229 
230 	input_set_drvdata(input_dev, onetouch);
231 
232 	input_dev->open = usb_onetouch_open;
233 	input_dev->close = usb_onetouch_close;
234 
235 	usb_fill_int_urb(onetouch->irq, udev, pipe, onetouch->data, maxp,
236 			 usb_onetouch_irq, onetouch, 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(onetouch->irq);
253  fail2:	usb_free_coherent(udev, ONETOUCH_PKT_LEN,
254 			  onetouch->data, onetouch->data_dma);
255  fail1:	kfree(onetouch);
256 	input_free_device(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(onetouch->irq);
266 		input_unregister_device(onetouch->dev);
267 		usb_free_urb(onetouch->irq);
268 		usb_free_coherent(onetouch->udev, ONETOUCH_PKT_LEN,
269 				  onetouch->data, 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(&us, intf, id,
282 			(id - onetouch_usb_ids) + onetouch_unusual_dev_list,
283 			&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