xref: /openbmc/linux/drivers/usb/storage/onetouch.c (revision b7db5733)
15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
234008dbfSMatthew Dharm /*
334008dbfSMatthew Dharm  * Support for the Maxtor OneTouch USB hard drive's button
434008dbfSMatthew Dharm  *
534008dbfSMatthew Dharm  * Current development and maintenance by:
634008dbfSMatthew Dharm  *	Copyright (c) 2005 Nick Sillik <n.sillik@temple.edu>
734008dbfSMatthew Dharm  *
834008dbfSMatthew Dharm  * Initial work by:
9d6450e19SNick Sillik  *	Copyright (c) 2003 Erik Thyren <erth7411@student.uu.se>
1034008dbfSMatthew Dharm  *
1134008dbfSMatthew Dharm  * Based on usbmouse.c (Vojtech Pavlik) and xpad.c (Marko Friedemann)
1234008dbfSMatthew Dharm  *
1334008dbfSMatthew Dharm  */
1434008dbfSMatthew Dharm #include <linux/kernel.h>
1534008dbfSMatthew Dharm #include <linux/input.h>
1634008dbfSMatthew Dharm #include <linux/slab.h>
1734008dbfSMatthew Dharm #include <linux/module.h>
18ae0dadcfSDavid Brownell #include <linux/usb/input.h>
1934008dbfSMatthew Dharm #include "usb.h"
2034008dbfSMatthew Dharm #include "debug.h"
21aa519be3SAkinobu Mita #include "scsiglue.h"
22aa519be3SAkinobu Mita 
23aa519be3SAkinobu Mita #define DRV_NAME "ums-onetouch"
2434008dbfSMatthew Dharm 
254246b06aSMaciej Grela MODULE_DESCRIPTION("Maxtor USB OneTouch hard drive button driver");
264246b06aSMaciej Grela MODULE_AUTHOR("Nick Sillik <n.sillik@temple.edu>");
274246b06aSMaciej Grela MODULE_LICENSE("GPL");
2832bca2dfSMatthias Maennich MODULE_IMPORT_NS(USB_STORAGE);
294246b06aSMaciej Grela 
309cfb95efSAlan Stern #define ONETOUCH_PKT_LEN        0x02
319cfb95efSAlan Stern #define ONETOUCH_BUTTON         KEY_PROG1
329cfb95efSAlan Stern 
339cfb95efSAlan Stern static int onetouch_connect_input(struct us_data *ss);
3443c1e98cSAdrian Bunk static void onetouch_release_input(void *onetouch_);
3534008dbfSMatthew Dharm 
3634008dbfSMatthew Dharm struct usb_onetouch {
3734008dbfSMatthew Dharm 	char name[128];
3834008dbfSMatthew Dharm 	char phys[64];
3988789672SDmitry Torokhov 	struct input_dev *dev;	/* input device interface */
4034008dbfSMatthew Dharm 	struct usb_device *udev;	/* usb device */
4134008dbfSMatthew Dharm 
4234008dbfSMatthew Dharm 	struct urb *irq;	/* urb for interrupt in report */
4334008dbfSMatthew Dharm 	unsigned char *data;	/* input data */
4434008dbfSMatthew Dharm 	dma_addr_t data_dma;
457931e1c6SMatthew Dharm 	unsigned int is_open:1;
4634008dbfSMatthew Dharm };
4734008dbfSMatthew Dharm 
489cfb95efSAlan Stern 
499cfb95efSAlan Stern /*
509cfb95efSAlan Stern  * The table of devices
519cfb95efSAlan Stern  */
529cfb95efSAlan Stern #define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \
539cfb95efSAlan Stern 		    vendorName, productName, useProtocol, useTransport, \
549cfb95efSAlan Stern 		    initFunction, flags) \
559cfb95efSAlan Stern { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
56f61870eeSSebastian Andrzej Siewior   .driver_info = (flags) }
579cfb95efSAlan Stern 
58e2c83f01SFelipe Balbi static struct usb_device_id onetouch_usb_ids[] = {
599cfb95efSAlan Stern #	include "unusual_onetouch.h"
609cfb95efSAlan Stern 	{ }		/* Terminating entry */
619cfb95efSAlan Stern };
629cfb95efSAlan Stern MODULE_DEVICE_TABLE(usb, onetouch_usb_ids);
639cfb95efSAlan Stern 
649cfb95efSAlan Stern #undef UNUSUAL_DEV
659cfb95efSAlan Stern 
669cfb95efSAlan Stern /*
679cfb95efSAlan Stern  * The flags table
689cfb95efSAlan Stern  */
699cfb95efSAlan Stern #define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \
709cfb95efSAlan Stern 		    vendor_name, product_name, use_protocol, use_transport, \
719cfb95efSAlan Stern 		    init_function, Flags) \
729cfb95efSAlan Stern { \
739cfb95efSAlan Stern 	.vendorName = vendor_name,	\
749cfb95efSAlan Stern 	.productName = product_name,	\
759cfb95efSAlan Stern 	.useProtocol = use_protocol,	\
769cfb95efSAlan Stern 	.useTransport = use_transport,	\
779cfb95efSAlan Stern 	.initFunction = init_function,	\
789cfb95efSAlan Stern }
799cfb95efSAlan Stern 
809cfb95efSAlan Stern static struct us_unusual_dev onetouch_unusual_dev_list[] = {
819cfb95efSAlan Stern #	include "unusual_onetouch.h"
829cfb95efSAlan Stern 	{ }		/* Terminating entry */
839cfb95efSAlan Stern };
849cfb95efSAlan Stern 
859cfb95efSAlan Stern #undef UNUSUAL_DEV
869cfb95efSAlan Stern 
879cfb95efSAlan Stern 
usb_onetouch_irq(struct urb * urb)887d12e780SDavid Howells static void usb_onetouch_irq(struct urb *urb)
8934008dbfSMatthew Dharm {
9034008dbfSMatthew Dharm 	struct usb_onetouch *onetouch = urb->context;
9134008dbfSMatthew Dharm 	signed char *data = onetouch->data;
9288789672SDmitry Torokhov 	struct input_dev *dev = onetouch->dev;
9362e5a330SGreg Kroah-Hartman 	int status = urb->status;
9462e5a330SGreg Kroah-Hartman 	int retval;
9534008dbfSMatthew Dharm 
9662e5a330SGreg Kroah-Hartman 	switch (status) {
9734008dbfSMatthew Dharm 	case 0:			/* success */
9834008dbfSMatthew Dharm 		break;
9934008dbfSMatthew Dharm 	case -ECONNRESET:	/* unlink */
10034008dbfSMatthew Dharm 	case -ENOENT:
10134008dbfSMatthew Dharm 	case -ESHUTDOWN:
10234008dbfSMatthew Dharm 		return;
10334008dbfSMatthew Dharm 	/* -EPIPE:  should clear the halt */
10434008dbfSMatthew Dharm 	default:		/* error */
10534008dbfSMatthew Dharm 		goto resubmit;
10634008dbfSMatthew Dharm 	}
10734008dbfSMatthew Dharm 
10888789672SDmitry Torokhov 	input_report_key(dev, ONETOUCH_BUTTON, data[0] & 0x02);
10934008dbfSMatthew Dharm 	input_sync(dev);
11088789672SDmitry Torokhov 
11134008dbfSMatthew Dharm resubmit:
11262e5a330SGreg Kroah-Hartman 	retval = usb_submit_urb (urb, GFP_ATOMIC);
11362e5a330SGreg Kroah-Hartman 	if (retval)
114802f389aSGreg Kroah-Hartman 		dev_err(&dev->dev, "can't resubmit intr, %s-%s/input0, "
115802f389aSGreg Kroah-Hartman 			"retval %d\n", onetouch->udev->bus->bus_name,
11662e5a330SGreg Kroah-Hartman 			onetouch->udev->devpath, retval);
11734008dbfSMatthew Dharm }
11834008dbfSMatthew Dharm 
usb_onetouch_open(struct input_dev * dev)11934008dbfSMatthew Dharm static int usb_onetouch_open(struct input_dev *dev)
12034008dbfSMatthew Dharm {
12109b7002dSDmitry Torokhov 	struct usb_onetouch *onetouch = input_get_drvdata(dev);
12234008dbfSMatthew Dharm 
1237931e1c6SMatthew Dharm 	onetouch->is_open = 1;
12434008dbfSMatthew Dharm 	onetouch->irq->dev = onetouch->udev;
12534008dbfSMatthew Dharm 	if (usb_submit_urb(onetouch->irq, GFP_KERNEL)) {
126802f389aSGreg Kroah-Hartman 		dev_err(&dev->dev, "usb_submit_urb failed\n");
12734008dbfSMatthew Dharm 		return -EIO;
12834008dbfSMatthew Dharm 	}
12934008dbfSMatthew Dharm 
13034008dbfSMatthew Dharm 	return 0;
13134008dbfSMatthew Dharm }
13234008dbfSMatthew Dharm 
usb_onetouch_close(struct input_dev * dev)13334008dbfSMatthew Dharm static void usb_onetouch_close(struct input_dev *dev)
13434008dbfSMatthew Dharm {
13509b7002dSDmitry Torokhov 	struct usb_onetouch *onetouch = input_get_drvdata(dev);
13634008dbfSMatthew Dharm 
13734008dbfSMatthew Dharm 	usb_kill_urb(onetouch->irq);
1387931e1c6SMatthew Dharm 	onetouch->is_open = 0;
13934008dbfSMatthew Dharm }
14034008dbfSMatthew Dharm 
1417931e1c6SMatthew Dharm #ifdef CONFIG_PM
usb_onetouch_pm_hook(struct us_data * us,int action)1427931e1c6SMatthew Dharm static void usb_onetouch_pm_hook(struct us_data *us, int action)
1437931e1c6SMatthew Dharm {
1447931e1c6SMatthew Dharm 	struct usb_onetouch *onetouch = (struct usb_onetouch *) us->extra;
1457931e1c6SMatthew Dharm 
1467931e1c6SMatthew Dharm 	if (onetouch->is_open) {
1477931e1c6SMatthew Dharm 		switch (action) {
1487931e1c6SMatthew Dharm 		case US_SUSPEND:
1497931e1c6SMatthew Dharm 			usb_kill_urb(onetouch->irq);
1507931e1c6SMatthew Dharm 			break;
1517931e1c6SMatthew Dharm 		case US_RESUME:
152e5dc8ae1SOliver Neukum 			if (usb_submit_urb(onetouch->irq, GFP_NOIO) != 0)
153802f389aSGreg Kroah-Hartman 				dev_err(&onetouch->irq->dev->dev,
154802f389aSGreg Kroah-Hartman 					"usb_submit_urb failed\n");
1557931e1c6SMatthew Dharm 			break;
1567931e1c6SMatthew Dharm 		default:
1577931e1c6SMatthew Dharm 			break;
1587931e1c6SMatthew Dharm 		}
1597931e1c6SMatthew Dharm 	}
1607931e1c6SMatthew Dharm }
1617931e1c6SMatthew Dharm #endif /* CONFIG_PM */
1627931e1c6SMatthew Dharm 
onetouch_connect_input(struct us_data * ss)1639cfb95efSAlan Stern static int onetouch_connect_input(struct us_data *ss)
16434008dbfSMatthew Dharm {
16534008dbfSMatthew Dharm 	struct usb_device *udev = ss->pusb_dev;
16634008dbfSMatthew Dharm 	struct usb_host_interface *interface;
16734008dbfSMatthew Dharm 	struct usb_endpoint_descriptor *endpoint;
16834008dbfSMatthew Dharm 	struct usb_onetouch *onetouch;
16988789672SDmitry Torokhov 	struct input_dev *input_dev;
17034008dbfSMatthew Dharm 	int pipe, maxp;
17117efe155SDmitry Torokhov 	int error = -ENOMEM;
17234008dbfSMatthew Dharm 
17334008dbfSMatthew Dharm 	interface = ss->pusb_intf->cur_altsetting;
17434008dbfSMatthew Dharm 
175d6450e19SNick Sillik 	if (interface->desc.bNumEndpoints != 3)
17634008dbfSMatthew Dharm 		return -ENODEV;
177d6450e19SNick Sillik 
178d6450e19SNick Sillik 	endpoint = &interface->endpoint[2].desc;
17966722a19SLuiz Fernando N. Capitulino 	if (!usb_endpoint_is_int_in(endpoint))
18034008dbfSMatthew Dharm 		return -ENODEV;
18134008dbfSMatthew Dharm 
18234008dbfSMatthew Dharm 	pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
183dcd2e49bSVincent Mailhol 	maxp = usb_maxpacket(udev, pipe);
184bba90aedSDan Carpenter 	maxp = min(maxp, ONETOUCH_PKT_LEN);
18534008dbfSMatthew Dharm 
18688789672SDmitry Torokhov 	onetouch = kzalloc(sizeof(struct usb_onetouch), GFP_KERNEL);
18788789672SDmitry Torokhov 	input_dev = input_allocate_device();
18888789672SDmitry Torokhov 	if (!onetouch || !input_dev)
18988789672SDmitry Torokhov 		goto fail1;
19034008dbfSMatthew Dharm 
191997ea58eSDaniel Mack 	onetouch->data = usb_alloc_coherent(udev, ONETOUCH_PKT_LEN,
1923756c7c0SOliver Neukum 					    GFP_KERNEL, &onetouch->data_dma);
19388789672SDmitry Torokhov 	if (!onetouch->data)
19488789672SDmitry Torokhov 		goto fail1;
19534008dbfSMatthew Dharm 
19634008dbfSMatthew Dharm 	onetouch->irq = usb_alloc_urb(0, GFP_KERNEL);
19788789672SDmitry Torokhov 	if (!onetouch->irq)
19888789672SDmitry Torokhov 		goto fail2;
19934008dbfSMatthew Dharm 
20034008dbfSMatthew Dharm 	onetouch->udev = udev;
20188789672SDmitry Torokhov 	onetouch->dev = input_dev;
20234008dbfSMatthew Dharm 
20334008dbfSMatthew Dharm 	if (udev->manufacturer)
204*b7db5733SWolfram Sang 		strscpy(onetouch->name, udev->manufacturer,
20588789672SDmitry Torokhov 			sizeof(onetouch->name));
20688789672SDmitry Torokhov 	if (udev->product) {
20788789672SDmitry Torokhov 		if (udev->manufacturer)
20888789672SDmitry Torokhov 			strlcat(onetouch->name, " ", sizeof(onetouch->name));
20988789672SDmitry Torokhov 		strlcat(onetouch->name, udev->product, sizeof(onetouch->name));
21088789672SDmitry Torokhov 	}
21188789672SDmitry Torokhov 
21234008dbfSMatthew Dharm 	if (!strlen(onetouch->name))
21388789672SDmitry Torokhov 		snprintf(onetouch->name, sizeof(onetouch->name),
21488789672SDmitry Torokhov 			 "Maxtor Onetouch %04x:%04x",
21588789672SDmitry Torokhov 			 le16_to_cpu(udev->descriptor.idVendor),
21688789672SDmitry Torokhov 			 le16_to_cpu(udev->descriptor.idProduct));
21788789672SDmitry Torokhov 
21888789672SDmitry Torokhov 	usb_make_path(udev, onetouch->phys, sizeof(onetouch->phys));
21988789672SDmitry Torokhov 	strlcat(onetouch->phys, "/input0", sizeof(onetouch->phys));
22088789672SDmitry Torokhov 
22188789672SDmitry Torokhov 	input_dev->name = onetouch->name;
22288789672SDmitry Torokhov 	input_dev->phys = onetouch->phys;
22388789672SDmitry Torokhov 	usb_to_input_id(udev, &input_dev->id);
22409b7002dSDmitry Torokhov 	input_dev->dev.parent = &udev->dev;
22588789672SDmitry Torokhov 
22688789672SDmitry Torokhov 	set_bit(EV_KEY, input_dev->evbit);
22788789672SDmitry Torokhov 	set_bit(ONETOUCH_BUTTON, input_dev->keybit);
22888789672SDmitry Torokhov 	clear_bit(0, input_dev->keybit);
22988789672SDmitry Torokhov 
23009b7002dSDmitry Torokhov 	input_set_drvdata(input_dev, onetouch);
23109b7002dSDmitry Torokhov 
23288789672SDmitry Torokhov 	input_dev->open = usb_onetouch_open;
23388789672SDmitry Torokhov 	input_dev->close = usb_onetouch_close;
23434008dbfSMatthew Dharm 
235bba90aedSDan Carpenter 	usb_fill_int_urb(onetouch->irq, udev, pipe, onetouch->data, maxp,
23634008dbfSMatthew Dharm 			 usb_onetouch_irq, onetouch, endpoint->bInterval);
23734008dbfSMatthew Dharm 	onetouch->irq->transfer_dma = onetouch->data_dma;
23834008dbfSMatthew Dharm 	onetouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
23934008dbfSMatthew Dharm 
24034008dbfSMatthew Dharm 	ss->extra_destructor = onetouch_release_input;
24134008dbfSMatthew Dharm 	ss->extra = onetouch;
2427931e1c6SMatthew Dharm #ifdef CONFIG_PM
2437931e1c6SMatthew Dharm 	ss->suspend_resume_hook = usb_onetouch_pm_hook;
2447931e1c6SMatthew Dharm #endif
24534008dbfSMatthew Dharm 
24617efe155SDmitry Torokhov 	error = input_register_device(onetouch->dev);
24717efe155SDmitry Torokhov 	if (error)
24817efe155SDmitry Torokhov 		goto fail3;
24934008dbfSMatthew Dharm 
25034008dbfSMatthew Dharm 	return 0;
25188789672SDmitry Torokhov 
25217efe155SDmitry Torokhov  fail3:	usb_free_urb(onetouch->irq);
253997ea58eSDaniel Mack  fail2:	usb_free_coherent(udev, ONETOUCH_PKT_LEN,
25488789672SDmitry Torokhov 			  onetouch->data, onetouch->data_dma);
25588789672SDmitry Torokhov  fail1:	kfree(onetouch);
25688789672SDmitry Torokhov 	input_free_device(input_dev);
25717efe155SDmitry Torokhov 	return error;
25834008dbfSMatthew Dharm }
25934008dbfSMatthew Dharm 
onetouch_release_input(void * onetouch_)26043c1e98cSAdrian Bunk static void onetouch_release_input(void *onetouch_)
26134008dbfSMatthew Dharm {
26234008dbfSMatthew Dharm 	struct usb_onetouch *onetouch = (struct usb_onetouch *) onetouch_;
26334008dbfSMatthew Dharm 
26434008dbfSMatthew Dharm 	if (onetouch) {
26534008dbfSMatthew Dharm 		usb_kill_urb(onetouch->irq);
26688789672SDmitry Torokhov 		input_unregister_device(onetouch->dev);
26734008dbfSMatthew Dharm 		usb_free_urb(onetouch->irq);
268997ea58eSDaniel Mack 		usb_free_coherent(onetouch->udev, ONETOUCH_PKT_LEN,
26934008dbfSMatthew Dharm 				  onetouch->data, onetouch->data_dma);
27034008dbfSMatthew Dharm 	}
27134008dbfSMatthew Dharm }
2729cfb95efSAlan Stern 
273aa519be3SAkinobu Mita static struct scsi_host_template onetouch_host_template;
274aa519be3SAkinobu Mita 
onetouch_probe(struct usb_interface * intf,const struct usb_device_id * id)2759cfb95efSAlan Stern static int onetouch_probe(struct usb_interface *intf,
2769cfb95efSAlan Stern 			 const struct usb_device_id *id)
2779cfb95efSAlan Stern {
2789cfb95efSAlan Stern 	struct us_data *us;
2799cfb95efSAlan Stern 	int result;
2809cfb95efSAlan Stern 
2819cfb95efSAlan Stern 	result = usb_stor_probe1(&us, intf, id,
282aa519be3SAkinobu Mita 			(id - onetouch_usb_ids) + onetouch_unusual_dev_list,
283aa519be3SAkinobu Mita 			&onetouch_host_template);
2849cfb95efSAlan Stern 	if (result)
2859cfb95efSAlan Stern 		return result;
2869cfb95efSAlan Stern 
2879cfb95efSAlan Stern 	/* Use default transport and protocol */
2889cfb95efSAlan Stern 
2899cfb95efSAlan Stern 	result = usb_stor_probe2(us);
2909cfb95efSAlan Stern 	return result;
2919cfb95efSAlan Stern }
2929cfb95efSAlan Stern 
2939cfb95efSAlan Stern static struct usb_driver onetouch_driver = {
294aa519be3SAkinobu Mita 	.name =		DRV_NAME,
2959cfb95efSAlan Stern 	.probe =	onetouch_probe,
2969cfb95efSAlan Stern 	.disconnect =	usb_stor_disconnect,
2979cfb95efSAlan Stern 	.suspend =	usb_stor_suspend,
2989cfb95efSAlan Stern 	.resume =	usb_stor_resume,
2999cfb95efSAlan Stern 	.reset_resume =	usb_stor_reset_resume,
3009cfb95efSAlan Stern 	.pre_reset =	usb_stor_pre_reset,
3019cfb95efSAlan Stern 	.post_reset =	usb_stor_post_reset,
3029cfb95efSAlan Stern 	.id_table =	onetouch_usb_ids,
3039cfb95efSAlan Stern 	.soft_unbind =	1,
304e73b2db6SHuajun Li 	.no_dynamic_id = 1,
3059cfb95efSAlan Stern };
3069cfb95efSAlan Stern 
307aa519be3SAkinobu Mita module_usb_stor_driver(onetouch_driver, onetouch_host_template, DRV_NAME);
308