162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Support for the Maxtor OneTouch USB hard drive's button 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Current development and maintenance by: 662306a36Sopenharmony_ci * Copyright (c) 2005 Nick Sillik <n.sillik@temple.edu> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Initial work by: 962306a36Sopenharmony_ci * Copyright (c) 2003 Erik Thyren <erth7411@student.uu.se> 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Based on usbmouse.c (Vojtech Pavlik) and xpad.c (Marko Friedemann) 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/input.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/usb/input.h> 1962306a36Sopenharmony_ci#include "usb.h" 2062306a36Sopenharmony_ci#include "debug.h" 2162306a36Sopenharmony_ci#include "scsiglue.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define DRV_NAME "ums-onetouch" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ciMODULE_DESCRIPTION("Maxtor USB OneTouch hard drive button driver"); 2662306a36Sopenharmony_ciMODULE_AUTHOR("Nick Sillik <n.sillik@temple.edu>"); 2762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2862306a36Sopenharmony_ciMODULE_IMPORT_NS(USB_STORAGE); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define ONETOUCH_PKT_LEN 0x02 3162306a36Sopenharmony_ci#define ONETOUCH_BUTTON KEY_PROG1 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic int onetouch_connect_input(struct us_data *ss); 3462306a36Sopenharmony_cistatic void onetouch_release_input(void *onetouch_); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistruct usb_onetouch { 3762306a36Sopenharmony_ci char name[128]; 3862306a36Sopenharmony_ci char phys[64]; 3962306a36Sopenharmony_ci struct input_dev *dev; /* input device interface */ 4062306a36Sopenharmony_ci struct usb_device *udev; /* usb device */ 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci struct urb *irq; /* urb for interrupt in report */ 4362306a36Sopenharmony_ci unsigned char *data; /* input data */ 4462306a36Sopenharmony_ci dma_addr_t data_dma; 4562306a36Sopenharmony_ci unsigned int is_open:1; 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* 5062306a36Sopenharmony_ci * The table of devices 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_ci#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ 5362306a36Sopenharmony_ci vendorName, productName, useProtocol, useTransport, \ 5462306a36Sopenharmony_ci initFunction, flags) \ 5562306a36Sopenharmony_ci{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ 5662306a36Sopenharmony_ci .driver_info = (flags) } 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic struct usb_device_id onetouch_usb_ids[] = { 5962306a36Sopenharmony_ci# include "unusual_onetouch.h" 6062306a36Sopenharmony_ci { } /* Terminating entry */ 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, onetouch_usb_ids); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#undef UNUSUAL_DEV 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* 6762306a36Sopenharmony_ci * The flags table 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_ci#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ 7062306a36Sopenharmony_ci vendor_name, product_name, use_protocol, use_transport, \ 7162306a36Sopenharmony_ci init_function, Flags) \ 7262306a36Sopenharmony_ci{ \ 7362306a36Sopenharmony_ci .vendorName = vendor_name, \ 7462306a36Sopenharmony_ci .productName = product_name, \ 7562306a36Sopenharmony_ci .useProtocol = use_protocol, \ 7662306a36Sopenharmony_ci .useTransport = use_transport, \ 7762306a36Sopenharmony_ci .initFunction = init_function, \ 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic struct us_unusual_dev onetouch_unusual_dev_list[] = { 8162306a36Sopenharmony_ci# include "unusual_onetouch.h" 8262306a36Sopenharmony_ci { } /* Terminating entry */ 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci#undef UNUSUAL_DEV 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic void usb_onetouch_irq(struct urb *urb) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct usb_onetouch *onetouch = urb->context; 9162306a36Sopenharmony_ci signed char *data = onetouch->data; 9262306a36Sopenharmony_ci struct input_dev *dev = onetouch->dev; 9362306a36Sopenharmony_ci int status = urb->status; 9462306a36Sopenharmony_ci int retval; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci switch (status) { 9762306a36Sopenharmony_ci case 0: /* success */ 9862306a36Sopenharmony_ci break; 9962306a36Sopenharmony_ci case -ECONNRESET: /* unlink */ 10062306a36Sopenharmony_ci case -ENOENT: 10162306a36Sopenharmony_ci case -ESHUTDOWN: 10262306a36Sopenharmony_ci return; 10362306a36Sopenharmony_ci /* -EPIPE: should clear the halt */ 10462306a36Sopenharmony_ci default: /* error */ 10562306a36Sopenharmony_ci goto resubmit; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci input_report_key(dev, ONETOUCH_BUTTON, data[0] & 0x02); 10962306a36Sopenharmony_ci input_sync(dev); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ciresubmit: 11262306a36Sopenharmony_ci retval = usb_submit_urb (urb, GFP_ATOMIC); 11362306a36Sopenharmony_ci if (retval) 11462306a36Sopenharmony_ci dev_err(&dev->dev, "can't resubmit intr, %s-%s/input0, " 11562306a36Sopenharmony_ci "retval %d\n", onetouch->udev->bus->bus_name, 11662306a36Sopenharmony_ci onetouch->udev->devpath, retval); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic int usb_onetouch_open(struct input_dev *dev) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct usb_onetouch *onetouch = input_get_drvdata(dev); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci onetouch->is_open = 1; 12462306a36Sopenharmony_ci onetouch->irq->dev = onetouch->udev; 12562306a36Sopenharmony_ci if (usb_submit_urb(onetouch->irq, GFP_KERNEL)) { 12662306a36Sopenharmony_ci dev_err(&dev->dev, "usb_submit_urb failed\n"); 12762306a36Sopenharmony_ci return -EIO; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return 0; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic void usb_onetouch_close(struct input_dev *dev) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct usb_onetouch *onetouch = input_get_drvdata(dev); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci usb_kill_urb(onetouch->irq); 13862306a36Sopenharmony_ci onetouch->is_open = 0; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci#ifdef CONFIG_PM 14262306a36Sopenharmony_cistatic void usb_onetouch_pm_hook(struct us_data *us, int action) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci struct usb_onetouch *onetouch = (struct usb_onetouch *) us->extra; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (onetouch->is_open) { 14762306a36Sopenharmony_ci switch (action) { 14862306a36Sopenharmony_ci case US_SUSPEND: 14962306a36Sopenharmony_ci usb_kill_urb(onetouch->irq); 15062306a36Sopenharmony_ci break; 15162306a36Sopenharmony_ci case US_RESUME: 15262306a36Sopenharmony_ci if (usb_submit_urb(onetouch->irq, GFP_NOIO) != 0) 15362306a36Sopenharmony_ci dev_err(&onetouch->irq->dev->dev, 15462306a36Sopenharmony_ci "usb_submit_urb failed\n"); 15562306a36Sopenharmony_ci break; 15662306a36Sopenharmony_ci default: 15762306a36Sopenharmony_ci break; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci#endif /* CONFIG_PM */ 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic int onetouch_connect_input(struct us_data *ss) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci struct usb_device *udev = ss->pusb_dev; 16662306a36Sopenharmony_ci struct usb_host_interface *interface; 16762306a36Sopenharmony_ci struct usb_endpoint_descriptor *endpoint; 16862306a36Sopenharmony_ci struct usb_onetouch *onetouch; 16962306a36Sopenharmony_ci struct input_dev *input_dev; 17062306a36Sopenharmony_ci int pipe, maxp; 17162306a36Sopenharmony_ci int error = -ENOMEM; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci interface = ss->pusb_intf->cur_altsetting; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (interface->desc.bNumEndpoints != 3) 17662306a36Sopenharmony_ci return -ENODEV; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci endpoint = &interface->endpoint[2].desc; 17962306a36Sopenharmony_ci if (!usb_endpoint_is_int_in(endpoint)) 18062306a36Sopenharmony_ci return -ENODEV; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress); 18362306a36Sopenharmony_ci maxp = usb_maxpacket(udev, pipe); 18462306a36Sopenharmony_ci maxp = min(maxp, ONETOUCH_PKT_LEN); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci onetouch = kzalloc(sizeof(struct usb_onetouch), GFP_KERNEL); 18762306a36Sopenharmony_ci input_dev = input_allocate_device(); 18862306a36Sopenharmony_ci if (!onetouch || !input_dev) 18962306a36Sopenharmony_ci goto fail1; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci onetouch->data = usb_alloc_coherent(udev, ONETOUCH_PKT_LEN, 19262306a36Sopenharmony_ci GFP_KERNEL, &onetouch->data_dma); 19362306a36Sopenharmony_ci if (!onetouch->data) 19462306a36Sopenharmony_ci goto fail1; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci onetouch->irq = usb_alloc_urb(0, GFP_KERNEL); 19762306a36Sopenharmony_ci if (!onetouch->irq) 19862306a36Sopenharmony_ci goto fail2; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci onetouch->udev = udev; 20162306a36Sopenharmony_ci onetouch->dev = input_dev; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (udev->manufacturer) 20462306a36Sopenharmony_ci strscpy(onetouch->name, udev->manufacturer, 20562306a36Sopenharmony_ci sizeof(onetouch->name)); 20662306a36Sopenharmony_ci if (udev->product) { 20762306a36Sopenharmony_ci if (udev->manufacturer) 20862306a36Sopenharmony_ci strlcat(onetouch->name, " ", sizeof(onetouch->name)); 20962306a36Sopenharmony_ci strlcat(onetouch->name, udev->product, sizeof(onetouch->name)); 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (!strlen(onetouch->name)) 21362306a36Sopenharmony_ci snprintf(onetouch->name, sizeof(onetouch->name), 21462306a36Sopenharmony_ci "Maxtor Onetouch %04x:%04x", 21562306a36Sopenharmony_ci le16_to_cpu(udev->descriptor.idVendor), 21662306a36Sopenharmony_ci le16_to_cpu(udev->descriptor.idProduct)); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci usb_make_path(udev, onetouch->phys, sizeof(onetouch->phys)); 21962306a36Sopenharmony_ci strlcat(onetouch->phys, "/input0", sizeof(onetouch->phys)); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci input_dev->name = onetouch->name; 22262306a36Sopenharmony_ci input_dev->phys = onetouch->phys; 22362306a36Sopenharmony_ci usb_to_input_id(udev, &input_dev->id); 22462306a36Sopenharmony_ci input_dev->dev.parent = &udev->dev; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci set_bit(EV_KEY, input_dev->evbit); 22762306a36Sopenharmony_ci set_bit(ONETOUCH_BUTTON, input_dev->keybit); 22862306a36Sopenharmony_ci clear_bit(0, input_dev->keybit); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci input_set_drvdata(input_dev, onetouch); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci input_dev->open = usb_onetouch_open; 23362306a36Sopenharmony_ci input_dev->close = usb_onetouch_close; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci usb_fill_int_urb(onetouch->irq, udev, pipe, onetouch->data, maxp, 23662306a36Sopenharmony_ci usb_onetouch_irq, onetouch, endpoint->bInterval); 23762306a36Sopenharmony_ci onetouch->irq->transfer_dma = onetouch->data_dma; 23862306a36Sopenharmony_ci onetouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci ss->extra_destructor = onetouch_release_input; 24162306a36Sopenharmony_ci ss->extra = onetouch; 24262306a36Sopenharmony_ci#ifdef CONFIG_PM 24362306a36Sopenharmony_ci ss->suspend_resume_hook = usb_onetouch_pm_hook; 24462306a36Sopenharmony_ci#endif 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci error = input_register_device(onetouch->dev); 24762306a36Sopenharmony_ci if (error) 24862306a36Sopenharmony_ci goto fail3; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci return 0; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci fail3: usb_free_urb(onetouch->irq); 25362306a36Sopenharmony_ci fail2: usb_free_coherent(udev, ONETOUCH_PKT_LEN, 25462306a36Sopenharmony_ci onetouch->data, onetouch->data_dma); 25562306a36Sopenharmony_ci fail1: kfree(onetouch); 25662306a36Sopenharmony_ci input_free_device(input_dev); 25762306a36Sopenharmony_ci return error; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic void onetouch_release_input(void *onetouch_) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci struct usb_onetouch *onetouch = (struct usb_onetouch *) onetouch_; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (onetouch) { 26562306a36Sopenharmony_ci usb_kill_urb(onetouch->irq); 26662306a36Sopenharmony_ci input_unregister_device(onetouch->dev); 26762306a36Sopenharmony_ci usb_free_urb(onetouch->irq); 26862306a36Sopenharmony_ci usb_free_coherent(onetouch->udev, ONETOUCH_PKT_LEN, 26962306a36Sopenharmony_ci onetouch->data, onetouch->data_dma); 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic struct scsi_host_template onetouch_host_template; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic int onetouch_probe(struct usb_interface *intf, 27662306a36Sopenharmony_ci const struct usb_device_id *id) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct us_data *us; 27962306a36Sopenharmony_ci int result; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci result = usb_stor_probe1(&us, intf, id, 28262306a36Sopenharmony_ci (id - onetouch_usb_ids) + onetouch_unusual_dev_list, 28362306a36Sopenharmony_ci &onetouch_host_template); 28462306a36Sopenharmony_ci if (result) 28562306a36Sopenharmony_ci return result; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* Use default transport and protocol */ 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci result = usb_stor_probe2(us); 29062306a36Sopenharmony_ci return result; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic struct usb_driver onetouch_driver = { 29462306a36Sopenharmony_ci .name = DRV_NAME, 29562306a36Sopenharmony_ci .probe = onetouch_probe, 29662306a36Sopenharmony_ci .disconnect = usb_stor_disconnect, 29762306a36Sopenharmony_ci .suspend = usb_stor_suspend, 29862306a36Sopenharmony_ci .resume = usb_stor_resume, 29962306a36Sopenharmony_ci .reset_resume = usb_stor_reset_resume, 30062306a36Sopenharmony_ci .pre_reset = usb_stor_pre_reset, 30162306a36Sopenharmony_ci .post_reset = usb_stor_post_reset, 30262306a36Sopenharmony_ci .id_table = onetouch_usb_ids, 30362306a36Sopenharmony_ci .soft_unbind = 1, 30462306a36Sopenharmony_ci .no_dynamic_id = 1, 30562306a36Sopenharmony_ci}; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cimodule_usb_stor_driver(onetouch_driver, onetouch_host_template, DRV_NAME); 308