diff options
Diffstat (limited to 'drivers/usb/storage/onetouch.c')
-rw-r--r-- | drivers/usb/storage/onetouch.c | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/drivers/usb/storage/onetouch.c b/drivers/usb/storage/onetouch.c new file mode 100644 index 000000000000..89401a59f952 --- /dev/null +++ b/drivers/usb/storage/onetouch.c @@ -0,0 +1,211 @@ +/* + * Support for the Maxtor OneTouch USB hard drive's button + * + * Current development and maintenance by: + * Copyright (c) 2005 Nick Sillik <n.sillik@temple.edu> + * + * Initial work by: + * Copyright (c) 2003 Erik Thyren <erth7411@student.uu.se> + * + * Based on usbmouse.c (Vojtech Pavlik) and xpad.c (Marko Friedemann) + * + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/input.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/usb_ch9.h> +#include <linux/usb_input.h> +#include "usb.h" +#include "onetouch.h" +#include "debug.h" + +void onetouch_release_input(void *onetouch_); + +struct usb_onetouch { + char name[128]; + char phys[64]; + struct input_dev *dev; /* input device interface */ + struct usb_device *udev; /* usb device */ + + struct urb *irq; /* urb for interrupt in report */ + unsigned char *data; /* input data */ + dma_addr_t data_dma; +}; + +static void usb_onetouch_irq(struct urb *urb, struct pt_regs *regs) +{ + struct usb_onetouch *onetouch = urb->context; + signed char *data = onetouch->data; + struct input_dev *dev = onetouch->dev; + int status; + + switch (urb->status) { + case 0: /* success */ + break; + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -ESHUTDOWN: + return; + /* -EPIPE: should clear the halt */ + default: /* error */ + goto resubmit; + } + + input_regs(dev, regs); + input_report_key(dev, ONETOUCH_BUTTON, data[0] & 0x02); + input_sync(dev); + +resubmit: + status = usb_submit_urb (urb, SLAB_ATOMIC); + if (status) + err ("can't resubmit intr, %s-%s/input0, status %d", + onetouch->udev->bus->bus_name, + onetouch->udev->devpath, status); +} + +static int usb_onetouch_open(struct input_dev *dev) +{ + struct usb_onetouch *onetouch = dev->private; + + onetouch->irq->dev = onetouch->udev; + if (usb_submit_urb(onetouch->irq, GFP_KERNEL)) { + err("usb_submit_urb failed"); + return -EIO; + } + + return 0; +} + +static void usb_onetouch_close(struct input_dev *dev) +{ + struct usb_onetouch *onetouch = dev->private; + + usb_kill_urb(onetouch->irq); +} + +int onetouch_connect_input(struct us_data *ss) +{ + struct usb_device *udev = ss->pusb_dev; + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + struct usb_onetouch *onetouch; + struct input_dev *input_dev; + int pipe, maxp; + + interface = ss->pusb_intf->cur_altsetting; + + if (interface->desc.bNumEndpoints != 3) + return -ENODEV; + + endpoint = &interface->endpoint[2].desc; + if (!(endpoint->bEndpointAddress & USB_DIR_IN)) + return -ENODEV; + if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + != USB_ENDPOINT_XFER_INT) + return -ENODEV; + + pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); + + onetouch = kzalloc(sizeof(struct usb_onetouch), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!onetouch || !input_dev) + goto fail1; + + onetouch->data = usb_buffer_alloc(udev, ONETOUCH_PKT_LEN, + SLAB_ATOMIC, &onetouch->data_dma); + if (!onetouch->data) + goto fail1; + + onetouch->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!onetouch->irq) + goto fail2; + + onetouch->udev = udev; + onetouch->dev = input_dev; + + if (udev->manufacturer) + strlcpy(onetouch->name, udev->manufacturer, + sizeof(onetouch->name)); + if (udev->product) { + if (udev->manufacturer) + strlcat(onetouch->name, " ", sizeof(onetouch->name)); + strlcat(onetouch->name, udev->product, sizeof(onetouch->name)); + } + + if (!strlen(onetouch->name)) + snprintf(onetouch->name, sizeof(onetouch->name), + "Maxtor Onetouch %04x:%04x", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + + usb_make_path(udev, onetouch->phys, sizeof(onetouch->phys)); + strlcat(onetouch->phys, "/input0", sizeof(onetouch->phys)); + + input_dev->name = onetouch->name; + input_dev->phys = onetouch->phys; + usb_to_input_id(udev, &input_dev->id); + input_dev->cdev.dev = &udev->dev; + + set_bit(EV_KEY, input_dev->evbit); + set_bit(ONETOUCH_BUTTON, input_dev->keybit); + clear_bit(0, input_dev->keybit); + + input_dev->private = onetouch; + input_dev->open = usb_onetouch_open; + input_dev->close = usb_onetouch_close; + + usb_fill_int_urb(onetouch->irq, udev, pipe, onetouch->data, + (maxp > 8 ? 8 : maxp), + usb_onetouch_irq, onetouch, endpoint->bInterval); + onetouch->irq->transfer_dma = onetouch->data_dma; + onetouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + ss->extra_destructor = onetouch_release_input; + ss->extra = onetouch; + + input_register_device(onetouch->dev); + + return 0; + + fail2: usb_buffer_free(udev, ONETOUCH_PKT_LEN, + onetouch->data, onetouch->data_dma); + fail1: kfree(onetouch); + input_free_device(input_dev); + return -ENOMEM; +} + +void onetouch_release_input(void *onetouch_) +{ + struct usb_onetouch *onetouch = (struct usb_onetouch *) onetouch_; + + if (onetouch) { + usb_kill_urb(onetouch->irq); + input_unregister_device(onetouch->dev); + usb_free_urb(onetouch->irq); + usb_buffer_free(onetouch->udev, ONETOUCH_PKT_LEN, + onetouch->data, onetouch->data_dma); + } +} |