134008dbfSMatthew Dharm /* 234008dbfSMatthew Dharm * Support for the Maxtor OneTouch USB hard drive's button 334008dbfSMatthew Dharm * 434008dbfSMatthew Dharm * Current development and maintenance by: 534008dbfSMatthew Dharm * Copyright (c) 2005 Nick Sillik <n.sillik@temple.edu> 634008dbfSMatthew Dharm * 734008dbfSMatthew Dharm * Initial work by: 8d6450e19SNick Sillik * Copyright (c) 2003 Erik Thyren <erth7411@student.uu.se> 934008dbfSMatthew Dharm * 1034008dbfSMatthew Dharm * Based on usbmouse.c (Vojtech Pavlik) and xpad.c (Marko Friedemann) 1134008dbfSMatthew Dharm * 1234008dbfSMatthew Dharm */ 1334008dbfSMatthew Dharm 1434008dbfSMatthew Dharm /* 1534008dbfSMatthew Dharm * This program is free software; you can redistribute it and/or modify 1634008dbfSMatthew Dharm * it under the terms of the GNU General Public License as published by 1734008dbfSMatthew Dharm * the Free Software Foundation; either version 2 of the License, or 1834008dbfSMatthew Dharm * (at your option) any later version. 1934008dbfSMatthew Dharm * 2034008dbfSMatthew Dharm * This program is distributed in the hope that it will be useful, 2134008dbfSMatthew Dharm * but WITHOUT ANY WARRANTY; without even the implied warranty of 2234008dbfSMatthew Dharm * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 2334008dbfSMatthew Dharm * GNU General Public License for more details. 2434008dbfSMatthew Dharm * 2534008dbfSMatthew Dharm * You should have received a copy of the GNU General Public License 2634008dbfSMatthew Dharm * along with this program; if not, write to the Free Software 2734008dbfSMatthew Dharm * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 2834008dbfSMatthew Dharm * 2934008dbfSMatthew Dharm */ 3034008dbfSMatthew Dharm 3134008dbfSMatthew Dharm #include <linux/kernel.h> 3234008dbfSMatthew Dharm #include <linux/input.h> 3334008dbfSMatthew Dharm #include <linux/init.h> 3434008dbfSMatthew Dharm #include <linux/slab.h> 3534008dbfSMatthew Dharm #include <linux/module.h> 36ae0dadcfSDavid Brownell #include <linux/usb/input.h> 3734008dbfSMatthew Dharm #include "usb.h" 3834008dbfSMatthew Dharm #include "onetouch.h" 3934008dbfSMatthew Dharm #include "debug.h" 4034008dbfSMatthew Dharm 4134008dbfSMatthew Dharm void onetouch_release_input(void *onetouch_); 4234008dbfSMatthew Dharm 4334008dbfSMatthew Dharm struct usb_onetouch { 4434008dbfSMatthew Dharm char name[128]; 4534008dbfSMatthew Dharm char phys[64]; 4688789672SDmitry Torokhov struct input_dev *dev; /* input device interface */ 4734008dbfSMatthew Dharm struct usb_device *udev; /* usb device */ 4834008dbfSMatthew Dharm 4934008dbfSMatthew Dharm struct urb *irq; /* urb for interrupt in report */ 5034008dbfSMatthew Dharm unsigned char *data; /* input data */ 5134008dbfSMatthew Dharm dma_addr_t data_dma; 527931e1c6SMatthew Dharm unsigned int is_open:1; 5334008dbfSMatthew Dharm }; 5434008dbfSMatthew Dharm 557d12e780SDavid Howells static void usb_onetouch_irq(struct urb *urb) 5634008dbfSMatthew Dharm { 5734008dbfSMatthew Dharm struct usb_onetouch *onetouch = urb->context; 5834008dbfSMatthew Dharm signed char *data = onetouch->data; 5988789672SDmitry Torokhov struct input_dev *dev = onetouch->dev; 6062e5a330SGreg Kroah-Hartman int status = urb->status; 6162e5a330SGreg Kroah-Hartman int retval; 6234008dbfSMatthew Dharm 6362e5a330SGreg Kroah-Hartman switch (status) { 6434008dbfSMatthew Dharm case 0: /* success */ 6534008dbfSMatthew Dharm break; 6634008dbfSMatthew Dharm case -ECONNRESET: /* unlink */ 6734008dbfSMatthew Dharm case -ENOENT: 6834008dbfSMatthew Dharm case -ESHUTDOWN: 6934008dbfSMatthew Dharm return; 7034008dbfSMatthew Dharm /* -EPIPE: should clear the halt */ 7134008dbfSMatthew Dharm default: /* error */ 7234008dbfSMatthew Dharm goto resubmit; 7334008dbfSMatthew Dharm } 7434008dbfSMatthew Dharm 7588789672SDmitry Torokhov input_report_key(dev, ONETOUCH_BUTTON, data[0] & 0x02); 7634008dbfSMatthew Dharm input_sync(dev); 7788789672SDmitry Torokhov 7834008dbfSMatthew Dharm resubmit: 7962e5a330SGreg Kroah-Hartman retval = usb_submit_urb (urb, GFP_ATOMIC); 8062e5a330SGreg Kroah-Hartman if (retval) 8162e5a330SGreg Kroah-Hartman err ("can't resubmit intr, %s-%s/input0, retval %d", 8234008dbfSMatthew Dharm onetouch->udev->bus->bus_name, 8362e5a330SGreg Kroah-Hartman onetouch->udev->devpath, retval); 8434008dbfSMatthew Dharm } 8534008dbfSMatthew Dharm 8634008dbfSMatthew Dharm static int usb_onetouch_open(struct input_dev *dev) 8734008dbfSMatthew Dharm { 8809b7002dSDmitry Torokhov struct usb_onetouch *onetouch = input_get_drvdata(dev); 8934008dbfSMatthew Dharm 907931e1c6SMatthew Dharm onetouch->is_open = 1; 9134008dbfSMatthew Dharm onetouch->irq->dev = onetouch->udev; 9234008dbfSMatthew Dharm if (usb_submit_urb(onetouch->irq, GFP_KERNEL)) { 9334008dbfSMatthew Dharm err("usb_submit_urb failed"); 9434008dbfSMatthew Dharm return -EIO; 9534008dbfSMatthew Dharm } 9634008dbfSMatthew Dharm 9734008dbfSMatthew Dharm return 0; 9834008dbfSMatthew Dharm } 9934008dbfSMatthew Dharm 10034008dbfSMatthew Dharm static void usb_onetouch_close(struct input_dev *dev) 10134008dbfSMatthew Dharm { 10209b7002dSDmitry Torokhov struct usb_onetouch *onetouch = input_get_drvdata(dev); 10334008dbfSMatthew Dharm 10434008dbfSMatthew Dharm usb_kill_urb(onetouch->irq); 1057931e1c6SMatthew Dharm onetouch->is_open = 0; 10634008dbfSMatthew Dharm } 10734008dbfSMatthew Dharm 1087931e1c6SMatthew Dharm #ifdef CONFIG_PM 1097931e1c6SMatthew Dharm static void usb_onetouch_pm_hook(struct us_data *us, int action) 1107931e1c6SMatthew Dharm { 1117931e1c6SMatthew Dharm struct usb_onetouch *onetouch = (struct usb_onetouch *) us->extra; 1127931e1c6SMatthew Dharm 1137931e1c6SMatthew Dharm if (onetouch->is_open) { 1147931e1c6SMatthew Dharm switch (action) { 1157931e1c6SMatthew Dharm case US_SUSPEND: 1167931e1c6SMatthew Dharm usb_kill_urb(onetouch->irq); 1177931e1c6SMatthew Dharm break; 1187931e1c6SMatthew Dharm case US_RESUME: 1197931e1c6SMatthew Dharm if (usb_submit_urb(onetouch->irq, GFP_KERNEL) != 0) 1207931e1c6SMatthew Dharm err("usb_submit_urb failed"); 1217931e1c6SMatthew Dharm break; 1227931e1c6SMatthew Dharm default: 1237931e1c6SMatthew Dharm break; 1247931e1c6SMatthew Dharm } 1257931e1c6SMatthew Dharm } 1267931e1c6SMatthew Dharm } 1277931e1c6SMatthew Dharm #endif /* CONFIG_PM */ 1287931e1c6SMatthew Dharm 12934008dbfSMatthew Dharm int onetouch_connect_input(struct us_data *ss) 13034008dbfSMatthew Dharm { 13134008dbfSMatthew Dharm struct usb_device *udev = ss->pusb_dev; 13234008dbfSMatthew Dharm struct usb_host_interface *interface; 13334008dbfSMatthew Dharm struct usb_endpoint_descriptor *endpoint; 13434008dbfSMatthew Dharm struct usb_onetouch *onetouch; 13588789672SDmitry Torokhov struct input_dev *input_dev; 13634008dbfSMatthew Dharm int pipe, maxp; 13717efe155SDmitry Torokhov int error = -ENOMEM; 13834008dbfSMatthew Dharm 13934008dbfSMatthew Dharm interface = ss->pusb_intf->cur_altsetting; 14034008dbfSMatthew Dharm 141d6450e19SNick Sillik if (interface->desc.bNumEndpoints != 3) 14234008dbfSMatthew Dharm return -ENODEV; 143d6450e19SNick Sillik 144d6450e19SNick Sillik endpoint = &interface->endpoint[2].desc; 14566722a19SLuiz Fernando N. Capitulino if (!usb_endpoint_is_int_in(endpoint)) 14634008dbfSMatthew Dharm return -ENODEV; 14734008dbfSMatthew Dharm 14834008dbfSMatthew Dharm pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress); 14934008dbfSMatthew Dharm maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); 15034008dbfSMatthew Dharm 15188789672SDmitry Torokhov onetouch = kzalloc(sizeof(struct usb_onetouch), GFP_KERNEL); 15288789672SDmitry Torokhov input_dev = input_allocate_device(); 15388789672SDmitry Torokhov if (!onetouch || !input_dev) 15488789672SDmitry Torokhov goto fail1; 15534008dbfSMatthew Dharm 156d6450e19SNick Sillik onetouch->data = usb_buffer_alloc(udev, ONETOUCH_PKT_LEN, 15754e6ecb2SChristoph Lameter GFP_ATOMIC, &onetouch->data_dma); 15888789672SDmitry Torokhov if (!onetouch->data) 15988789672SDmitry Torokhov goto fail1; 16034008dbfSMatthew Dharm 16134008dbfSMatthew Dharm onetouch->irq = usb_alloc_urb(0, GFP_KERNEL); 16288789672SDmitry Torokhov if (!onetouch->irq) 16388789672SDmitry Torokhov goto fail2; 16434008dbfSMatthew Dharm 16534008dbfSMatthew Dharm onetouch->udev = udev; 16688789672SDmitry Torokhov onetouch->dev = input_dev; 16734008dbfSMatthew Dharm 16834008dbfSMatthew Dharm if (udev->manufacturer) 16988789672SDmitry Torokhov strlcpy(onetouch->name, udev->manufacturer, 17088789672SDmitry Torokhov sizeof(onetouch->name)); 17188789672SDmitry Torokhov if (udev->product) { 17288789672SDmitry Torokhov if (udev->manufacturer) 17388789672SDmitry Torokhov strlcat(onetouch->name, " ", sizeof(onetouch->name)); 17488789672SDmitry Torokhov strlcat(onetouch->name, udev->product, sizeof(onetouch->name)); 17588789672SDmitry Torokhov } 17688789672SDmitry Torokhov 17734008dbfSMatthew Dharm if (!strlen(onetouch->name)) 17888789672SDmitry Torokhov snprintf(onetouch->name, sizeof(onetouch->name), 17988789672SDmitry Torokhov "Maxtor Onetouch %04x:%04x", 18088789672SDmitry Torokhov le16_to_cpu(udev->descriptor.idVendor), 18188789672SDmitry Torokhov le16_to_cpu(udev->descriptor.idProduct)); 18288789672SDmitry Torokhov 18388789672SDmitry Torokhov usb_make_path(udev, onetouch->phys, sizeof(onetouch->phys)); 18488789672SDmitry Torokhov strlcat(onetouch->phys, "/input0", sizeof(onetouch->phys)); 18588789672SDmitry Torokhov 18688789672SDmitry Torokhov input_dev->name = onetouch->name; 18788789672SDmitry Torokhov input_dev->phys = onetouch->phys; 18888789672SDmitry Torokhov usb_to_input_id(udev, &input_dev->id); 18909b7002dSDmitry Torokhov input_dev->dev.parent = &udev->dev; 19088789672SDmitry Torokhov 19188789672SDmitry Torokhov set_bit(EV_KEY, input_dev->evbit); 19288789672SDmitry Torokhov set_bit(ONETOUCH_BUTTON, input_dev->keybit); 19388789672SDmitry Torokhov clear_bit(0, input_dev->keybit); 19488789672SDmitry Torokhov 19509b7002dSDmitry Torokhov input_set_drvdata(input_dev, onetouch); 19609b7002dSDmitry Torokhov 19788789672SDmitry Torokhov input_dev->open = usb_onetouch_open; 19888789672SDmitry Torokhov input_dev->close = usb_onetouch_close; 19934008dbfSMatthew Dharm 20034008dbfSMatthew Dharm usb_fill_int_urb(onetouch->irq, udev, pipe, onetouch->data, 20134008dbfSMatthew Dharm (maxp > 8 ? 8 : maxp), 20234008dbfSMatthew Dharm usb_onetouch_irq, onetouch, endpoint->bInterval); 20334008dbfSMatthew Dharm onetouch->irq->transfer_dma = onetouch->data_dma; 20434008dbfSMatthew Dharm onetouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 20534008dbfSMatthew Dharm 20634008dbfSMatthew Dharm ss->extra_destructor = onetouch_release_input; 20734008dbfSMatthew Dharm ss->extra = onetouch; 2087931e1c6SMatthew Dharm #ifdef CONFIG_PM 2097931e1c6SMatthew Dharm ss->suspend_resume_hook = usb_onetouch_pm_hook; 2107931e1c6SMatthew Dharm #endif 21134008dbfSMatthew Dharm 21217efe155SDmitry Torokhov error = input_register_device(onetouch->dev); 21317efe155SDmitry Torokhov if (error) 21417efe155SDmitry Torokhov goto fail3; 21534008dbfSMatthew Dharm 21634008dbfSMatthew Dharm return 0; 21788789672SDmitry Torokhov 21817efe155SDmitry Torokhov fail3: usb_free_urb(onetouch->irq); 21988789672SDmitry Torokhov fail2: usb_buffer_free(udev, ONETOUCH_PKT_LEN, 22088789672SDmitry Torokhov onetouch->data, onetouch->data_dma); 22188789672SDmitry Torokhov fail1: kfree(onetouch); 22288789672SDmitry Torokhov input_free_device(input_dev); 22317efe155SDmitry Torokhov return error; 22434008dbfSMatthew Dharm } 22534008dbfSMatthew Dharm 22634008dbfSMatthew Dharm void onetouch_release_input(void *onetouch_) 22734008dbfSMatthew Dharm { 22834008dbfSMatthew Dharm struct usb_onetouch *onetouch = (struct usb_onetouch *) onetouch_; 22934008dbfSMatthew Dharm 23034008dbfSMatthew Dharm if (onetouch) { 23134008dbfSMatthew Dharm usb_kill_urb(onetouch->irq); 23288789672SDmitry Torokhov input_unregister_device(onetouch->dev); 23334008dbfSMatthew Dharm usb_free_urb(onetouch->irq); 23434008dbfSMatthew Dharm usb_buffer_free(onetouch->udev, ONETOUCH_PKT_LEN, 23534008dbfSMatthew Dharm onetouch->data, onetouch->data_dma); 23634008dbfSMatthew Dharm } 23734008dbfSMatthew Dharm } 238