diff options
Diffstat (limited to 'drivers/usb/misc')
-rw-r--r-- | drivers/usb/misc/Kconfig | 61 | ||||
-rw-r--r-- | drivers/usb/misc/Makefile | 5 | ||||
-rw-r--r-- | drivers/usb/misc/adutux.c | 900 | ||||
-rw-r--r-- | drivers/usb/misc/auerswald.c | 6 | ||||
-rw-r--r-- | drivers/usb/misc/cypress_cy7c63.c | 19 | ||||
-rw-r--r-- | drivers/usb/misc/cytherm.c | 35 | ||||
-rw-r--r-- | drivers/usb/misc/ftdi-elan.c | 2809 | ||||
-rw-r--r-- | drivers/usb/misc/idmouse.c | 2 | ||||
-rw-r--r-- | drivers/usb/misc/ldusb.c | 10 | ||||
-rw-r--r-- | drivers/usb/misc/legousbtower.c | 2 | ||||
-rw-r--r-- | drivers/usb/misc/phidget.c | 43 | ||||
-rw-r--r-- | drivers/usb/misc/phidget.h | 12 | ||||
-rw-r--r-- | drivers/usb/misc/phidgetkit.c | 316 | ||||
-rw-r--r-- | drivers/usb/misc/phidgetmotorcontrol.c | 466 | ||||
-rw-r--r-- | drivers/usb/misc/phidgetservo.c | 117 | ||||
-rw-r--r-- | drivers/usb/misc/sisusbvga/sisusb.c | 2 | ||||
-rw-r--r-- | drivers/usb/misc/usb_u132.h | 97 | ||||
-rw-r--r-- | drivers/usb/misc/usblcd.c | 10 | ||||
-rw-r--r-- | drivers/usb/misc/usbled.c | 20 |
19 files changed, 4695 insertions, 237 deletions
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 88928a4..c29658f 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -32,6 +32,16 @@ config USB_EMI26 To compile this driver as a module, choose M here: the module will be called emi26. +config USB_ADUTUX + tristate "ADU devices from Ontrak Control Systems (EXPERIMENTAL)" + depends on USB && EXPERIMENTAL + help + Say Y if you want to use an ADU device from Ontrak Control + Systems. + + To compile this driver as a module, choose M here. The module + will be called adutux. + config USB_AUERSWALD tristate "USB Auerswald ISDN support (EXPERIMENTAL)" depends on USB && EXPERIMENTAL @@ -115,19 +125,36 @@ config USB_CYTHERM To compile this driver as a module, choose M here: the module will be called cytherm. -config USB_PHIDGETKIT - tristate "USB PhidgetKit support" +config USB_PHIDGET + tristate "USB Phidgets drivers" depends on USB help - Say Y here if you want to connect a PhidgetKit USB device from - Phidgets Inc. + Say Y here to enable the various drivers for devices from + Phidgets inc. + +config USB_PHIDGETKIT + tristate "USB PhidgetInterfaceKit support" + depends on USB_PHIDGET + help + Say Y here if you want to connect a PhidgetInterfaceKit USB device + from Phidgets Inc. To compile this driver as a module, choose M here: the module will be called phidgetkit. +config USB_PHIDGETMOTORCONTROL + tristate "USB PhidgetMotorControl support" + depends on USB_PHIDGET + help + Say Y here if you want to connect a PhidgetMotorControl USB device + from Phidgets Inc. + + To compile this driver as a module, choose M here: the + module will be called phidgetmotorcontrol. + config USB_PHIDGETSERVO tristate "USB PhidgetServo support" - depends on USB + depends on USB_PHIDGET help Say Y here if you want to connect an 1 or 4 Motor PhidgetServo servo controller version 2.0 or 3.0. @@ -151,6 +178,30 @@ config USB_IDMOUSE See also <http://www.fs.tum.de/~echtler/idmouse/>. +config USB_FTDI_ELAN + tristate "Elan PCMCIA CardBus Adapter USB Client" + depends on USB + default M + help + ELAN's Uxxx series of adapters are USB to PCMCIA CardBus adapters. + Currently only the U132 adapter is available. + + The U132 is specifically designed for CardBus PC cards that contain + an OHCI host controller. Typical PC cards are the Orange Mobile 3G + Option GlobeTrotter Fusion card. The U132 adapter will *NOT* work + with PC cards that do not contain an OHCI controller. To use a U132 + adapter you will need this "ftdi-elan" module as well as the "u132-hcd" + module which is a USB host controller driver that talks to the OHCI + controller within CardBus card that are inserted in the U132 adapter. + + This driver has been tested with a CardBus OHCI USB adapter, and + worked with a USB PEN Drive inserted into the first USB port of + the PCCARD. A rather pointless thing to do, but useful for testing. + + See also the USB_U132_HCD entry "Elan U132 Adapter Host Controller" + + It is safe to say M here. + config USB_APPLEDISPLAY tristate "Apple Cinema Display support" depends on USB diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index 2927260..2be70fa 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -3,22 +3,25 @@ # (the ones that don't fit into any other categories) # +obj-$(CONFIG_USB_ADUTUX) += adutux.o obj-$(CONFIG_USB_AUERSWALD) += auerswald.o obj-$(CONFIG_USB_CYPRESS_CY7C63)+= cypress_cy7c63.o obj-$(CONFIG_USB_CYTHERM) += cytherm.o obj-$(CONFIG_USB_EMI26) += emi26.o obj-$(CONFIG_USB_EMI62) += emi62.o +obj-$(CONFIG_USB_FTDI_ELAN) += ftdi-elan.o obj-$(CONFIG_USB_IDMOUSE) += idmouse.o obj-$(CONFIG_USB_LCD) += usblcd.o obj-$(CONFIG_USB_LD) += ldusb.o obj-$(CONFIG_USB_LED) += usbled.o obj-$(CONFIG_USB_LEGOTOWER) += legousbtower.o +obj-$(CONFIG_USB_PHIDGET) += phidget.o obj-$(CONFIG_USB_PHIDGETKIT) += phidgetkit.o +obj-$(CONFIG_USB_PHIDGETMOTORCONTROL) += phidgetmotorcontrol.o obj-$(CONFIG_USB_PHIDGETSERVO) += phidgetservo.o obj-$(CONFIG_USB_RIO500) += rio500.o obj-$(CONFIG_USB_TEST) += usbtest.o obj-$(CONFIG_USB_USS720) += uss720.o -obj-$(CONFIG_USB_APPLEDISPLAY) += appledisplay.o obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/ diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c new file mode 100644 index 0000000..d396319 --- /dev/null +++ b/drivers/usb/misc/adutux.c @@ -0,0 +1,900 @@ +/* + * adutux - driver for ADU devices from Ontrak Control Systems + * This is an experimental driver. Use at your own risk. + * This driver is not supported by Ontrak Control Systems. + * + * Copyright (c) 2003 John Homppi (SCO, leave this notice here) + * + * 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. + * + * derived from the Lego USB Tower driver 0.56: + * Copyright (c) 2003 David Glance <davidgsf@sourceforge.net> + * 2001 Juergen Stuber <stuber@loria.fr> + * that was derived from USB Skeleton driver - 0.5 + * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) + * + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <asm/uaccess.h> + +#ifdef CONFIG_USB_DEBUG +static int debug = 5; +#else +static int debug = 1; +#endif + +/* Use our own dbg macro */ +#undef dbg +#define dbg(lvl, format, arg...) \ +do { \ + if (debug >= lvl) \ + printk(KERN_DEBUG __FILE__ " : " format " \n", ## arg); \ +} while (0) + + +/* Version Information */ +#define DRIVER_VERSION "v0.0.13" +#define DRIVER_AUTHOR "John Homppi" +#define DRIVER_DESC "adutux (see www.ontrak.net)" + +/* Module parameters */ +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug enabled or not"); + +/* Define these values to match your device */ +#define ADU_VENDOR_ID 0x0a07 +#define ADU_PRODUCT_ID 0x0064 + +/* table of devices that work with this driver */ +static struct usb_device_id device_table [] = { + { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID) }, /* ADU100 */ + { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+20) }, /* ADU120 */ + { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+30) }, /* ADU130 */ + { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+100) }, /* ADU200 */ + { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+108) }, /* ADU208 */ + { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+118) }, /* ADU218 */ + { }/* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +#ifdef CONFIG_USB_DYNAMIC_MINORS +#define ADU_MINOR_BASE 0 +#else +#define ADU_MINOR_BASE 67 +#endif + +/* we can have up to this number of device plugged in at once */ +#define MAX_DEVICES 16 + +#define COMMAND_TIMEOUT (2*HZ) /* 60 second timeout for a command */ + +/* Structure to hold all of our device specific stuff */ +struct adu_device { + struct semaphore sem; /* locks this structure */ + struct usb_device* udev; /* save off the usb device pointer */ + struct usb_interface* interface; + unsigned char minor; /* the starting minor number for this device */ + char serial_number[8]; + + int open_count; /* number of times this port has been opened */ + + char* read_buffer_primary; + int read_buffer_length; + char* read_buffer_secondary; + int secondary_head; + int secondary_tail; + spinlock_t buflock; + + wait_queue_head_t read_wait; + wait_queue_head_t write_wait; + + char* interrupt_in_buffer; + struct usb_endpoint_descriptor* interrupt_in_endpoint; + struct urb* interrupt_in_urb; + int read_urb_finished; + + char* interrupt_out_buffer; + struct usb_endpoint_descriptor* interrupt_out_endpoint; + struct urb* interrupt_out_urb; +}; + +/* prevent races between open() and disconnect */ +static DEFINE_MUTEX(disconnect_mutex); +static struct usb_driver adu_driver; + +static void adu_debug_data(int level, const char *function, int size, + const unsigned char *data) +{ + int i; + + if (debug < level) + return; + + printk(KERN_DEBUG __FILE__": %s - length = %d, data = ", + function, size); + for (i = 0; i < size; ++i) + printk("%.2x ", data[i]); + printk("\n"); +} + +/** + * adu_abort_transfers + * aborts transfers and frees associated data structures + */ +static void adu_abort_transfers(struct adu_device *dev) +{ + dbg(2," %s : enter", __FUNCTION__); + + if (dev == NULL) { + dbg(1," %s : dev is null", __FUNCTION__); + goto exit; + } + + if (dev->udev == NULL) { + dbg(1," %s : udev is null", __FUNCTION__); + goto exit; + } + + dbg(2," %s : udev state %d", __FUNCTION__, dev->udev->state); + if (dev->udev->state == USB_STATE_NOTATTACHED) { + dbg(1," %s : udev is not attached", __FUNCTION__); + goto exit; + } + + /* shutdown transfer */ + usb_unlink_urb(dev->interrupt_in_urb); + usb_unlink_urb(dev->interrupt_out_urb); + +exit: + dbg(2," %s : leave", __FUNCTION__); +} + +static void adu_delete(struct adu_device *dev) +{ + dbg(2, "%s enter", __FUNCTION__); + + adu_abort_transfers(dev); + + /* free data structures */ + usb_free_urb(dev->interrupt_in_urb); + usb_free_urb(dev->interrupt_out_urb); + kfree(dev->read_buffer_primary); + kfree(dev->read_buffer_secondary); + kfree(dev->interrupt_in_buffer); + kfree(dev->interrupt_out_buffer); + kfree(dev); + + dbg(2, "%s : leave", __FUNCTION__); +} + +static void adu_interrupt_in_callback(struct urb *urb, struct pt_regs *regs) +{ + struct adu_device *dev = urb->context; + + dbg(4," %s : enter, status %d", __FUNCTION__, urb->status); + adu_debug_data(5, __FUNCTION__, urb->actual_length, + urb->transfer_buffer); + + spin_lock(&dev->buflock); + + if (urb->status != 0) { + if ((urb->status != -ENOENT) && (urb->status != -ECONNRESET)) { + dbg(1," %s : nonzero status received: %d", + __FUNCTION__, urb->status); + } + goto exit; + } + + if (urb->actual_length > 0 && dev->interrupt_in_buffer[0] != 0x00) { + if (dev->read_buffer_length < + (4 * le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize)) - + (urb->actual_length)) { + memcpy (dev->read_buffer_primary + + dev->read_buffer_length, + dev->interrupt_in_buffer, urb->actual_length); + + dev->read_buffer_length += urb->actual_length; + dbg(2," %s reading %d ", __FUNCTION__, + urb->actual_length); + } else { + dbg(1," %s : read_buffer overflow", __FUNCTION__); + } + } + +exit: + dev->read_urb_finished = 1; + spin_unlock(&dev->buflock); + /* always wake up so we recover from errors */ + wake_up_interruptible(&dev->read_wait); + adu_debug_data(5, __FUNCTION__, urb->actual_length, + urb->transfer_buffer); + dbg(4," %s : leave, status %d", __FUNCTION__, urb->status); +} + +static void adu_interrupt_out_callback(struct urb *urb, struct pt_regs *regs) +{ + struct adu_device *dev = urb->context; + + dbg(4," %s : enter, status %d", __FUNCTION__, urb->status); + adu_debug_data(5,__FUNCTION__, urb->actual_length, urb->transfer_buffer); + + if (urb->status != 0) { + if ((urb->status != -ENOENT) && + (urb->status != -ECONNRESET)) { + dbg(1, " %s :nonzero status received: %d", + __FUNCTION__, urb->status); + } + goto exit; + } + + wake_up_interruptible(&dev->write_wait); +exit: + + adu_debug_data(5, __FUNCTION__, urb->actual_length, + urb->transfer_buffer); + dbg(4," %s : leave, status %d", __FUNCTION__, urb->status); +} + +static int adu_open(struct inode *inode, struct file *file) +{ + struct adu_device *dev = NULL; + struct usb_interface *interface; + int subminor; + int retval = 0; + + dbg(2,"%s : enter", __FUNCTION__); + + subminor = iminor(inode); + + mutex_lock(&disconnect_mutex); + + interface = usb_find_interface(&adu_driver, subminor); + if (!interface) { + err("%s - error, can't find device for minor %d", + __FUNCTION__, subminor); + retval = -ENODEV; + goto exit_no_device; + } + + dev = usb_get_intfdata(interface); + if (!dev) { + retval = -ENODEV; + goto exit_no_device; + } + + /* lock this device */ + if ((retval = down_interruptible(&dev->sem))) { + dbg(2, "%s : sem down failed", __FUNCTION__); + goto exit_no_device; + } + + /* increment our usage count for the device */ + ++dev->open_count; + dbg(2,"%s : open count %d", __FUNCTION__, dev->open_count); + + /* save device in the file's private structure */ + file->private_data = dev; + + /* initialize in direction */ + dev->read_buffer_length = 0; + + /* fixup first read by having urb waiting for it */ + usb_fill_int_urb(dev->interrupt_in_urb,dev->udev, + usb_rcvintpipe(dev->udev, + dev->interrupt_in_endpoint->bEndpointAddress), + dev->interrupt_in_buffer, + le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), + adu_interrupt_in_callback, dev, + dev->interrupt_in_endpoint->bInterval); + /* dev->interrupt_in_urb->transfer_flags |= URB_ASYNC_UNLINK; */ + dev->read_urb_finished = 0; + usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL); + /* we ignore failure */ + /* end of fixup for first read */ + + up(&dev->sem); + +exit_no_device: + mutex_unlock(&disconnect_mutex); + dbg(2,"%s : leave, return value %d ", __FUNCTION__, retval); + + return retval; +} + +static int adu_release_internal(struct adu_device *dev) +{ + int retval = 0; + + dbg(2," %s : enter", __FUNCTION__); + + if (dev->udev == NULL) { + /* the device was unplugged before the file was released */ + adu_delete(dev); + goto exit; + } + + /* decrement our usage count for the device */ + --dev->open_count; + dbg(2," %s : open count %d", __FUNCTION__, dev->open_count); + if (dev->open_count <= 0) { + adu_abort_transfers(dev); + dev->open_count = 0; + } + +exit: + dbg(2," %s : leave", __FUNCTION__); + return retval; +} + +static int adu_release(struct inode *inode, struct file *file) +{ + struct adu_device *dev = NULL; + int retval = 0; + + dbg(2," %s : enter", __FUNCTION__); + + if (file == NULL) { + dbg(1," %s : file is NULL", __FUNCTION__); + retval = -ENODEV; + goto exit; + } + + dev = file->private_data; + + if (dev == NULL) { + dbg(1," %s : object is NULL", __FUNCTION__); + retval = -ENODEV; + goto exit; + } + + /* lock our device */ + down(&dev->sem); /* not interruptible */ + + if (dev->open_count <= 0) { + dbg(1," %s : device not opened", __FUNCTION__); + retval = -ENODEV; + goto exit; + } + + /* do the work */ + retval = adu_release_internal(dev); + +exit: + up(&dev->sem); + dbg(2," %s : leave, return value %d", __FUNCTION__, retval); + return retval; +} + +static ssize_t adu_read(struct file *file, __user char *buffer, size_t count, + loff_t *ppos) +{ + struct adu_device *dev; + size_t bytes_read = 0; + size_t bytes_to_read = count; + int i; + int retval = 0; + int timeout = 0; + int should_submit = 0; + unsigned long flags; + DECLARE_WAITQUEUE(wait, current); + + dbg(2," %s : enter, count = %Zd, file=%p", __FUNCTION__, count, file); + + dev = file->private_data; + dbg(2," %s : dev=%p", __FUNCTION__, dev); + /* lock this object */ + if (down_interruptible(&dev->sem)) + return -ERESTARTSYS; + + /* verify that the device wasn't unplugged */ + if (dev->udev == NULL || dev->minor == 0) { + retval = -ENODEV; + err("No device or device unplugged %d", retval); + goto exit; + } + + /* verify that some data was requested */ + if (count == 0) { + dbg(1," %s : read request of 0 bytes", __FUNCTION__); + goto exit; + } + + timeout = COMMAND_TIMEOUT; + dbg(2," %s : about to start looping", __FUNCTION__); + while (bytes_to_read) { + int data_in_secondary = dev->secondary_tail - dev->secondary_head; + dbg(2," %s : while, data_in_secondary=%d, status=%d", + __FUNCTION__, data_in_secondary, + dev->interrupt_in_urb->status); + + if (data_in_secondary) { + /* drain secondary buffer */ + int amount = bytes_to_read < data_in_secondary ? bytes_to_read : data_in_secondary; + i = copy_to_user(buffer, dev->read_buffer_secondary+dev->secondary_head, amount); + if (i < 0) { + retval = -EFAULT; + goto exit; + } + dev->secondary_head += (amount - i); + bytes_read += (amount - i); + bytes_to_read -= (amount - i); + if (i) { + retval = bytes_read ? bytes_read : -EFAULT; + goto exit; + } + } else { + /* we check the primary buffer */ + spin_lock_irqsave (&dev->buflock, flags); + if (dev->read_buffer_length) { + /* we secure access to the primary */ + char *tmp; + dbg(2," %s : swap, read_buffer_length = %d", + __FUNCTION__, dev->read_buffer_length); + tmp = dev->read_buffer_secondary; + dev->read_buffer_secondary = dev->read_buffer_primary; + dev->read_buffer_primary = tmp; + dev->secondary_head = 0; + dev->secondary_tail = dev->read_buffer_length; + dev->read_buffer_length = 0; + spin_unlock_irqrestore(&dev->buflock, flags); + /* we have a free buffer so use it */ + should_submit = 1; + } else { + /* even the primary was empty - we may need to do IO */ + if (dev->interrupt_in_urb->status == -EINPROGRESS) { + /* somebody is doing IO */ + spin_unlock_irqrestore(&dev->buflock, flags); + dbg(2," %s : submitted already", __FUNCTION__); + } else { + /* we must initiate input */ + dbg(2," %s : initiate input", __FUNCTION__); + dev->read_urb_finished = 0; + + usb_fill_int_urb(dev->interrupt_in_urb,dev->udev, + usb_rcvintpipe(dev->udev, + dev->interrupt_in_endpoint->bEndpointAddress), + dev->interrupt_in_buffer, + le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), + adu_interrupt_in_callback, + dev, + dev->interrupt_in_endpoint->bInterval); + retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL); + if (!retval) { + spin_unlock_irqrestore(&dev->buflock, flags); + dbg(2," %s : submitted OK", __FUNCTION__); + } else { + if (retval == -ENOMEM) { + retval = bytes_read ? bytes_read : -ENOMEM; + } + spin_unlock_irqrestore(&dev->buflock, flags); + dbg(2," %s : submit failed", __FUNCTION__); + goto exit; + } + } + + /* we wait for I/O to complete */ + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&dev->read_wait, &wait); + if (!dev->read_urb_finished) + timeout = schedule_timeout(COMMAND_TIMEOUT); + else + set_current_state(TASK_RUNNING); + remove_wait_queue(&dev->read_wait, &wait); + + if (timeout <= 0) { + dbg(2," %s : timeout", __FUNCTION__); + retval = bytes_read ? bytes_read : -ETIMEDOUT; + goto exit; + } + + if (signal_pending(current)) { + dbg(2," %s : signal pending", __FUNCTION__); + retval = bytes_read ? bytes_read : -EINTR; + goto exit; + } + } + } + } + + retval = bytes_read; + /* if the primary buffer is empty then use it */ + if (should_submit && !dev->interrupt_in_urb->status==-EINPROGRESS) { + usb_fill_int_urb(dev->interrupt_in_urb,dev->udev, + usb_rcvintpipe(dev->udev, + dev->interrupt_in_endpoint->bEndpointAddress), + dev->interrupt_in_buffer, + le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), + adu_interrupt_in_callback, + dev, + dev->interrupt_in_endpoint->bInterval); + /* dev->interrupt_in_urb->transfer_flags |= URB_ASYNC_UNLINK; */ + dev->read_urb_finished = 0; + usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL); + /* we ignore failure */ + } + +exit: + /* unlock the device */ + up(&dev->sem); + + dbg(2," %s : leave, return value %d", __FUNCTION__, retval); + return retval; +} + +static ssize_t adu_write(struct file *file, const __user char *buffer, + size_t count, loff_t *ppos) +{ + struct adu_device *dev; + size_t bytes_written = 0; + size_t bytes_to_write; + size_t buffer_size; + int retval = 0; + int timeout = 0; + + dbg(2," %s : enter, count = %Zd", __FUNCTION__, count); + + dev = file->private_data; + + /* lock this object */ + down_interruptible(&dev->sem); + + /* verify that the device wasn't unplugged */ + if (dev->udev == NULL || dev->minor == 0) { + retval = -ENODEV; + err("No device or device unplugged %d", retval); + goto exit; + } + + /* verify that we actually have some data to write */ + if (count == 0) { + dbg(1," %s : write request of 0 bytes", __FUNCTION__); + goto exit; + } + + + while (count > 0) { + if (dev->interrupt_out_urb->status == -EINPROGRESS) { + timeout = COMMAND_TIMEOUT; + + while (timeout > 0) { + if (signal_pending(current)) { + dbg(1," %s : interrupted", __FUNCTION__); + retval = -EINTR; + goto exit; + } + up(&dev->sem); + timeout = interruptible_sleep_on_timeout(&dev->write_wait, timeout); + down_interruptible(&dev->sem); + if (timeout > 0) { + break; + } + dbg(1," %s : interrupted timeout: %d", __FUNCTION__, timeout); + } + + + dbg(1," %s : final timeout: %d", __FUNCTION__, timeout); + + if (timeout == 0) { + dbg(1, "%s - command timed out.", __FUNCTION__); + retval = -ETIMEDOUT; + goto exit; + } + + dbg(4," %s : in progress, count = %Zd", __FUNCTION__, count); + + } else { + dbg(4," %s : sending, count = %Zd", __FUNCTION__, count); + + /* write the data into interrupt_out_buffer from userspace */ + buffer_size = le16_to_cpu(dev->interrupt_out_endpoint->wMaxPacketSize); + bytes_to_write = count > buffer_size ? buffer_size : count; + dbg(4," %s : buffer_size = %Zd, count = %Zd, bytes_to_write = %Zd", + __FUNCTION__, buffer_size, count, bytes_to_write); + + if (copy_from_user(dev->interrupt_out_buffer, buffer, bytes_to_write) != 0) { + retval = -EFAULT; + goto exit; + } + + /* send off the urb */ + usb_fill_int_urb( + dev->interrupt_out_urb, + dev->udev, + usb_sndintpipe(dev->udev, dev->interrupt_out_endpoint->bEndpointAddress), + dev->interrupt_out_buffer, + bytes_to_write, + adu_interrupt_out_callback, + dev, + dev->interrupt_in_endpoint->bInterval); + /* dev->interrupt_in_urb->transfer_flags |= URB_ASYNC_UNLINK; */ + dev->interrupt_out_urb->actual_length = bytes_to_write; + retval = usb_submit_urb(dev->interrupt_out_urb, GFP_KERNEL); + if (retval < 0) { + err("Couldn't submit interrupt_out_urb %d", retval); + goto exit; + } + + buffer += bytes_to_write; + count -= bytes_to_write; + + bytes_written += bytes_to_write; + } + } + + retval = bytes_written; + +exit: + /* unlock the device */ + up(&dev->sem); + + dbg(2," %s : leave, return value %d", __FUNCTION__, retval); + + return retval; +} + +/* file operations needed when we register this driver */ +static struct file_operations adu_fops = { + .owner = THIS_MODULE, + .read = adu_read, + .write = adu_write, + .open = adu_open, + .release = adu_release, +}; + +/* + * usb class driver info in order to get a minor number from the usb core, + * and to have the device registered with devfs and the driver core + */ +static struct usb_class_driver adu_class = { + .name = "usb/adutux%d", + .fops = &adu_fops, + .minor_base = ADU_MINOR_BASE, +}; + +/** + * adu_probe + * + * Called by the usb core when a new device is connected that it thinks + * this driver might be interested in. + */ +static int adu_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct adu_device *dev = NULL; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + int retval = -ENODEV; + int in_end_size; + int out_end_size; + int i; + + dbg(2," %s : enter", __FUNCTION__); + + if (udev == NULL) { + dev_err(&interface->dev, "udev is NULL.\n"); + goto exit; + } + + /* allocate memory for our device state and intialize it */ + dev = kzalloc(sizeof(struct adu_device), GFP_KERNEL); + if (dev == NULL) { + dev_err(&interface->dev, "Out of memory\n"); + retval = -ENOMEM; + goto exit; + } + + init_MUTEX(&dev->sem); + spin_lock_init(&dev->buflock); + dev->udev = udev; + init_waitqueue_head(&dev->read_wait); + init_waitqueue_head(&dev->write_wait); + + iface_desc = &interface->altsetting[0]; + + /* set up the endpoint information */ + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (usb_endpoint_is_int_in(endpoint)) + dev->interrupt_in_endpoint = endpoint; + + if (usb_endpoint_is_int_out(endpoint)) + dev->interrupt_out_endpoint = endpoint; + } + if (dev->interrupt_in_endpoint == NULL) { + dev_err(&interface->dev, "interrupt in endpoint not found\n"); + goto error; + } + if (dev->interrupt_out_endpoint == NULL) { + dev_err(&interface->dev, "interrupt out endpoint not found\n"); + goto error; + } + + in_end_size = le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize); + out_end_size = le16_to_cpu(dev->interrupt_out_endpoint->wMaxPacketSize); + + dev->read_buffer_primary = kmalloc((4 * in_end_size), GFP_KERNEL); + if (!dev->read_buffer_primary) { + dev_err(&interface->dev, "Couldn't allocate read_buffer_primary\n"); + retval = -ENOMEM; + goto error; + } + + /* debug code prime the buffer */ + memset(dev->read_buffer_primary, 'a', in_end_size); + memset(dev->read_buffer_primary + in_end_size, 'b', in_end_size); + memset(dev->read_buffer_primary + (2 * in_end_size), 'c', in_end_size); + memset(dev->read_buffer_primary + (3 * in_end_size), 'd', in_end_size); + + dev->read_buffer_secondary = kmalloc((4 * in_end_size), GFP_KERNEL); + if (!dev->read_buffer_secondary) { + dev_err(&interface->dev, "Couldn't allocate read_buffer_secondary\n"); + retval = -ENOMEM; + goto error; + } + + /* debug code prime the buffer */ + memset(dev->read_buffer_secondary, 'e', in_end_size); + memset(dev->read_buffer_secondary + in_end_size, 'f', in_end_size); + memset(dev->read_buffer_secondary + (2 * in_end_size), 'g', in_end_size); + memset(dev->read_buffer_secondary + (3 * in_end_size), 'h', in_end_size); + + dev->interrupt_in_buffer = kmalloc(in_end_size, GFP_KERNEL); + if (!dev->interrupt_in_buffer) { + dev_err(&interface->dev, "Couldn't allocate interrupt_in_buffer\n"); + goto error; + } + + /* debug code prime the buffer */ + memset(dev->interrupt_in_buffer, 'i', in_end_size); + + dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->interrupt_in_urb) { + dev_err(&interface->dev, "Couldn't allocate interrupt_in_urb\n"); + goto error; + } + dev->interrupt_out_buffer = kmalloc(out_end_size, GFP_KERNEL); + if (!dev->interrupt_out_buffer) { + dev_err(&interface->dev, "Couldn't allocate interrupt_out_buffer\n"); + goto error; + } + dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->interrupt_out_urb) { + dev_err(&interface->dev, "Couldn't allocate interrupt_out_urb\n"); + goto error; + } + + if (!usb_string(udev, udev->descriptor.iSerialNumber, dev->serial_number, + sizeof(dev->serial_number))) { + dev_err(&interface->dev, "Could not retrieve serial number\n"); + goto error; + } + dbg(2," %s : serial_number=%s", __FUNCTION__, dev->serial_number); + + /* we can register the device now, as it is ready */ + usb_set_intfdata(interface, dev); + + retval = usb_register_dev(interface, &adu_class); + + if (retval) { + /* something prevented us from registering this driver */ + dev_err(&interface->dev, "Not able to get a minor for this device.\n"); + usb_set_intfdata(interface, NULL); + goto error; + } + + dev->minor = interface->minor; + + /* let the user know what node this device is now attached to */ + dev_info(&interface->dev, "ADU%d %s now attached to /dev/usb/adutux%d", + udev->descriptor.idProduct, dev->serial_number, + (dev->minor - ADU_MINOR_BASE)); +exit: + dbg(2," %s : leave, return value %p (dev)", __FUNCTION__, dev); + + return retval; + +error: + adu_delete(dev); + return retval; +} + +/** + * adu_disconnect + * + * Called by the usb core when the device is removed from the system. + */ +static void adu_disconnect(struct usb_interface *interface) +{ + struct adu_device *dev; + int minor; + + dbg(2," %s : enter", __FUNCTION__); + + mutex_lock(&disconnect_mutex); /* not interruptible */ + + dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + down(&dev->sem); /* not interruptible */ + + minor = dev->minor; + + /* give back our minor */ + usb_deregister_dev(interface, &adu_class); + dev->minor = 0; + + /* if the device is not opened, then we clean up right now */ + dbg(2," %s : open count %d", __FUNCTION__, dev->open_count); + if (!dev->open_count) { + up(&dev->sem); + adu_delete(dev); + } else { + dev->udev = NULL; + up(&dev->sem); + } + + mutex_unlock(&disconnect_mutex); + + dev_info(&interface->dev, "ADU device adutux%d now disconnected", + (minor - ADU_MINOR_BASE)); + + dbg(2," %s : leave", __FUNCTION__); +} + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver adu_driver = { + .name = "adutux", + .probe = adu_probe, + .disconnect = adu_disconnect, + .id_table = device_table, +}; + +static int __init adu_init(void) +{ + int result; + + dbg(2," %s : enter", __FUNCTION__); + + /* register this driver with the USB subsystem */ + result = usb_register(&adu_driver); + if (result < 0) { + err("usb_register failed for the "__FILE__" driver. " + "Error number %d", result); + goto exit; + } + + info("adutux " DRIVER_DESC " " DRIVER_VERSION); + info("adutux is an experimental driver. Use at your own risk"); + +exit: + dbg(2," %s : leave, return value %d", __FUNCTION__, result); + + return result; +} + +static void __exit adu_exit(void) +{ + dbg(2," %s : enter", __FUNCTION__); + /* deregister this driver with the USB subsystem */ + usb_deregister(&adu_driver); + dbg(2," %s : leave", __FUNCTION__); +} + +module_init(adu_init); +module_exit(adu_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/misc/auerswald.c b/drivers/usb/misc/auerswald.c index 1fef36e..4fd2110 100644 --- a/drivers/usb/misc/auerswald.c +++ b/drivers/usb/misc/auerswald.c @@ -806,7 +806,7 @@ static void auerbuf_releasebuf( pauerbuf_t bp) 0 Initial, OK -EINPROGRESS during submission until end -ENOENT if urb is unlinked --ETIMEDOUT Transfer timed out, NAK +-ETIME Device did not respond -ENOMEM Memory Overflow -ENODEV Specified USB-device or bus doesn't exist -ENXIO URB already queued @@ -832,7 +832,7 @@ static int auerswald_status_retry (int status) { switch (status) { case 0: - case -ETIMEDOUT: + case -ETIME: case -EOVERFLOW: case -EAGAIN: case -EPIPE: @@ -1858,7 +1858,7 @@ static int auerchar_release (struct inode *inode, struct file *file) /*----------------------------------------------------------------------*/ /* File operation structure */ -static struct file_operations auerswald_fops = +static const struct file_operations auerswald_fops = { .owner = THIS_MODULE, .llseek = no_llseek, diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c index 9c46746..b63b5f3 100644 --- a/drivers/usb/misc/cypress_cy7c63.c +++ b/drivers/usb/misc/cypress_cy7c63.c @@ -209,7 +209,7 @@ static int cypress_probe(struct usb_interface *interface, dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (dev == NULL) { dev_err(&interface->dev, "Out of memory!\n"); - goto error; + goto error_mem; } dev->udev = usb_get_dev(interface_to_usbdev(interface)); @@ -218,15 +218,26 @@ static int cypress_probe(struct usb_interface *interface, usb_set_intfdata(interface, dev); /* create device attribute files */ - device_create_file(&interface->dev, &dev_attr_port0); - device_create_file(&interface->dev, &dev_attr_port1); + retval = device_create_file(&interface->dev, &dev_attr_port0); + if (retval) + goto error; + retval = device_create_file(&interface->dev, &dev_attr_port1); + if (retval) + goto error; /* let the user know that the device is now attached */ dev_info(&interface->dev, "Cypress CY7C63xxx device now attached\n"); + return 0; - retval = 0; error: + device_remove_file(&interface->dev, &dev_attr_port0); + device_remove_file(&interface->dev, &dev_attr_port1); + usb_set_intfdata(interface, NULL); + usb_put_dev(dev->udev); + kfree(dev); + +error_mem: return retval; } diff --git a/drivers/usb/misc/cytherm.c b/drivers/usb/misc/cytherm.c index b20bec4..04e87ac 100644 --- a/drivers/usb/misc/cytherm.c +++ b/drivers/usb/misc/cytherm.c @@ -353,7 +353,7 @@ static int cytherm_probe(struct usb_interface *interface, dev = kzalloc (sizeof(struct usb_cytherm), GFP_KERNEL); if (dev == NULL) { dev_err (&interface->dev, "Out of memory\n"); - goto error; + goto error_mem; } dev->udev = usb_get_dev(udev); @@ -362,18 +362,35 @@ static int cytherm_probe(struct usb_interface *interface, dev->brightness = 0xFF; - device_create_file(&interface->dev, &dev_attr_brightness); - device_create_file(&interface->dev, &dev_attr_temp); - device_create_file(&interface->dev, &dev_attr_button); - device_create_file(&interface->dev, &dev_attr_port0); - device_create_file(&interface->dev, &dev_attr_port1); + retval = device_create_file(&interface->dev, &dev_attr_brightness); + if (retval) + goto error; + retval = device_create_file(&interface->dev, &dev_attr_temp); + if (retval) + goto error; + retval = device_create_file(&interface->dev, &dev_attr_button); + if (retval) + goto error; + retval = device_create_file(&interface->dev, &dev_attr_port0); + if (retval) + goto error; + retval = device_create_file(&interface->dev, &dev_attr_port1); + if (retval) + goto error; - dev_info (&interface->dev, + dev_info (&interface->dev, "Cypress thermometer device now attached\n"); return 0; - - error: +error: + device_remove_file(&interface->dev, &dev_attr_brightness); + device_remove_file(&interface->dev, &dev_attr_temp); + device_remove_file(&interface->dev, &dev_attr_button); + device_remove_file(&interface->dev, &dev_attr_port0); + device_remove_file(&interface->dev, &dev_attr_port1); + usb_set_intfdata (interface, NULL); + usb_put_dev(dev->udev); kfree(dev); +error_mem: return retval; } diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c new file mode 100644 index 0000000..b88a094 --- /dev/null +++ b/drivers/usb/misc/ftdi-elan.c @@ -0,0 +1,2809 @@ +/* +* USB FTDI client driver for Elan Digital Systems's Uxxx adapters +* +* Copyright(C) 2006 Elan Digital Systems Limited +* http://www.elandigitalsystems.com +* +* Author and Maintainer - Tony Olech - Elan Digital Systems +* tony.olech@elandigitalsystems.com +* +* 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, version 2. +* +* +* This driver was written by Tony Olech(tony.olech@elandigitalsystems.com) +* based on various USB client drivers in the 2.6.15 linux kernel +* with constant reference to the 3rd Edition of Linux Device Drivers +* published by O'Reilly +* +* The U132 adapter is a USB to CardBus adapter specifically designed +* for PC cards that contain an OHCI host controller. Typical PC cards +* are the Orange Mobile 3G Option GlobeTrotter Fusion card. +* +* The U132 adapter will *NOT *work with PC cards that do not contain +* an OHCI controller. A simple way to test whether a PC card has an +* OHCI controller as an interface is to insert the PC card directly +* into a laptop(or desktop) with a CardBus slot and if "lspci" shows +* a new USB controller and "lsusb -v" shows a new OHCI Host Controller +* then there is a good chance that the U132 adapter will support the +* PC card.(you also need the specific client driver for the PC card) +* +* Please inform the Author and Maintainer about any PC cards that +* contain OHCI Host Controller and work when directly connected to +* an embedded CardBus slot but do not work when they are connected +* via an ELAN U132 adapter. +* +*/ +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/ioctl.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/kref.h> +#include <asm/uaccess.h> +#include <linux/usb.h> +#include <linux/workqueue.h> +#include <linux/platform_device.h> +MODULE_AUTHOR("Tony Olech"); +MODULE_DESCRIPTION("FTDI ELAN driver"); +MODULE_LICENSE("GPL"); +#define INT_MODULE_PARM(n, v) static int n = v;module_param(n, int, 0444) +extern struct platform_driver u132_platform_driver; +static struct workqueue_struct *status_queue; +static struct workqueue_struct *command_queue; +static struct workqueue_struct *respond_queue; +/* +* ftdi_module_lock exists to protect access to global variables +* +*/ +static struct semaphore ftdi_module_lock; +static int ftdi_instances = 0; +static struct list_head ftdi_static_list; +/* +* end of the global variables protected by ftdi_module_lock +*/ +#include "usb_u132.h" +#define TD_DEVNOTRESP 5 +/* Define these values to match your devices*/ +#define USB_FTDI_ELAN_VENDOR_ID 0x0403 +#define USB_FTDI_ELAN_PRODUCT_ID 0xd6ea +/* table of devices that work with this driver*/ +static struct usb_device_id ftdi_elan_table[] = { + {USB_DEVICE(USB_FTDI_ELAN_VENDOR_ID, USB_FTDI_ELAN_PRODUCT_ID)}, + { /* Terminating entry */ } +}; + +MODULE_DEVICE_TABLE(usb, ftdi_elan_table); +/* only the jtag(firmware upgrade device) interface requires +* a device file and corresponding minor number, but the +* interface is created unconditionally - I suppose it could +* be configured or not according to a module parameter. +* But since we(now) require one interface per device, +* and since it unlikely that a normal installation would +* require more than a couple of elan-ftdi devices, 8 seems +* like a reasonable limit to have here, and if someone +* really requires more than 8 devices, then they can frig the +* code and recompile +*/ +#define USB_FTDI_ELAN_MINOR_BASE 192 +#define COMMAND_BITS 5 +#define COMMAND_SIZE (1<<COMMAND_BITS) +#define COMMAND_MASK (COMMAND_SIZE-1) +struct u132_command { + u8 header; + u16 length; + u8 address; + u8 width; + u32 value; + int follows; + void *buffer; +}; +#define RESPOND_BITS 5 +#define RESPOND_SIZE (1<<RESPOND_BITS) +#define RESPOND_MASK (RESPOND_SIZE-1) +struct u132_respond { + u8 header; + u8 address; + u32 *value; + int *result; + struct completion wait_completion; +}; +struct u132_target { + void *endp; + struct urb *urb; + int toggle_bits; + int error_count; + int condition_code; + int repeat_number; + int halted; + int skipped; + int actual; + int non_null; + int active; + int abandoning; + void (*callback) (void *endp, struct urb *urb, u8 *buf, int len, + int toggle_bits, int error_count, int condition_code, + int repeat_number, int halted, int skipped, int actual, + int non_null); +}; +/* Structure to hold all of our device specific stuff*/ +struct usb_ftdi { + struct list_head ftdi_list; + struct semaphore u132_lock; + int command_next; + int command_head; + struct u132_command command[COMMAND_SIZE]; + int respond_next; + int respond_head; + struct u132_respond respond[RESPOND_SIZE]; + struct u132_target target[4]; + char device_name[16]; + unsigned synchronized:1; + unsigned enumerated:1; + unsigned registered:1; + unsigned initialized:1; + unsigned card_ejected:1; + int function; + int sequence_num; + int disconnected; + int gone_away; + int stuck_status; + int status_queue_delay; + struct semaphore sw_lock; + struct usb_device *udev; + struct usb_interface *interface; + struct usb_class_driver *class; + struct work_struct status_work; + struct work_struct command_work; + struct work_struct respond_work; + struct u132_platform_data platform_data; + struct resource resources[0]; + struct platform_device platform_dev; + unsigned char *bulk_in_buffer; + size_t bulk_in_size; + size_t bulk_in_last; + size_t bulk_in_left; + __u8 bulk_in_endpointAddr; + __u8 bulk_out_endpointAddr; + struct kref kref; + u32 controlreg; + u8 response[4 + 1024]; + int expected; + int recieved; + int ed_found; +}; +#define kref_to_usb_ftdi(d) container_of(d, struct usb_ftdi, kref) +#define platform_device_to_usb_ftdi(d) container_of(d, struct usb_ftdi, \ + platform_dev) +static struct usb_driver ftdi_elan_driver; +static void ftdi_elan_delete(struct kref *kref) +{ + struct usb_ftdi *ftdi = kref_to_usb_ftdi(kref); + dev_warn(&ftdi->udev->dev, "FREEING ftdi=%p\n", ftdi); + usb_put_dev(ftdi->udev); + ftdi->disconnected += 1; + down(&ftdi_module_lock); + list_del_init(&ftdi->ftdi_list); + ftdi_instances -= 1; + up(&ftdi_module_lock); + kfree(ftdi->bulk_in_buffer); + ftdi->bulk_in_buffer = NULL; +} + +static void ftdi_elan_put_kref(struct usb_ftdi *ftdi) +{ + kref_put(&ftdi->kref, ftdi_elan_delete); +} + +static void ftdi_elan_get_kref(struct usb_ftdi *ftdi) +{ + kref_get(&ftdi->kref); +} + +static void ftdi_elan_init_kref(struct usb_ftdi *ftdi) +{ + kref_init(&ftdi->kref); +} + +static void ftdi_status_requeue_work(struct usb_ftdi *ftdi, unsigned int delta) +{ + if (delta > 0) { + if (queue_delayed_work(status_queue, &ftdi->status_work, delta)) + return; + } else if (queue_work(status_queue, &ftdi->status_work)) + return; + kref_put(&ftdi->kref, ftdi_elan_delete); + return; +} + +static void ftdi_status_queue_work(struct usb_ftdi *ftdi, unsigned int delta) +{ + if (delta > 0) { + if (queue_delayed_work(status_queue, &ftdi->status_work, delta)) + kref_get(&ftdi->kref); + } else if (queue_work(status_queue, &ftdi->status_work)) + kref_get(&ftdi->kref); + return; +} + +static void ftdi_status_cancel_work(struct usb_ftdi *ftdi) +{ + if (cancel_delayed_work(&ftdi->status_work)) + kref_put(&ftdi->kref, ftdi_elan_delete); +} + +static void ftdi_command_requeue_work(struct usb_ftdi *ftdi, unsigned int delta) +{ + if (delta > 0) { + if (queue_delayed_work(command_queue, &ftdi->command_work, + delta)) + return; + } else if (queue_work(command_queue, &ftdi->command_work)) + return; + kref_put(&ftdi->kref, ftdi_elan_delete); + return; +} + +static void ftdi_command_queue_work(struct usb_ftdi *ftdi, unsigned int delta) +{ + if (delta > 0) { + if (queue_delayed_work(command_queue, &ftdi->command_work, + delta)) + kref_get(&ftdi->kref); + } else if (queue_work(command_queue, &ftdi->command_work)) + kref_get(&ftdi->kref); + return; +} + +static void ftdi_command_cancel_work(struct usb_ftdi *ftdi) +{ + if (cancel_delayed_work(&ftdi->command_work)) + kref_put(&ftdi->kref, ftdi_elan_delete); +} + +static void ftdi_response_requeue_work(struct usb_ftdi *ftdi, + unsigned int delta) +{ + if (delta > 0) { + if (queue_delayed_work(respond_queue, &ftdi->respond_work, + delta)) + return; + } else if (queue_work(respond_queue, &ftdi->respond_work)) + return; + kref_put(&ftdi->kref, ftdi_elan_delete); + return; +} + +static void ftdi_respond_queue_work(struct usb_ftdi *ftdi, unsigned int delta) +{ + if (delta > 0) { + if (queue_delayed_work(respond_queue, &ftdi->respond_work, + delta)) + kref_get(&ftdi->kref); + } else if (queue_work(respond_queue, &ftdi->respond_work)) + kref_get(&ftdi->kref); + return; +} + +static void ftdi_response_cancel_work(struct usb_ftdi *ftdi) +{ + if (cancel_delayed_work(&ftdi->respond_work)) + kref_put(&ftdi->kref, ftdi_elan_delete); +} + +void ftdi_elan_gone_away(struct platform_device *pdev) +{ + struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev); + ftdi->gone_away += 1; + ftdi_elan_put_kref(ftdi); +} + + +EXPORT_SYMBOL_GPL(ftdi_elan_gone_away); +void ftdi_release_platform_dev(struct device *dev) +{ + dev->parent = NULL; +} + +static void ftdi_elan_do_callback(struct usb_ftdi *ftdi, + struct u132_target *target, u8 *buffer, int length); +static void ftdi_elan_kick_command_queue(struct usb_ftdi *ftdi); +static void ftdi_elan_kick_respond_queue(struct usb_ftdi *ftdi); +static int ftdi_elan_setupOHCI(struct usb_ftdi *ftdi); +static int ftdi_elan_checkingPCI(struct usb_ftdi *ftdi); +static int ftdi_elan_enumeratePCI(struct usb_ftdi *ftdi); +static int ftdi_elan_synchronize(struct usb_ftdi *ftdi); +static int ftdi_elan_stuck_waiting(struct usb_ftdi *ftdi); +static int ftdi_elan_command_engine(struct usb_ftdi *ftdi); +static int ftdi_elan_respond_engine(struct usb_ftdi *ftdi); +static int ftdi_elan_hcd_init(struct usb_ftdi *ftdi) +{ + int result; + if (ftdi->platform_dev.dev.parent) + return -EBUSY; + ftdi_elan_get_kref(ftdi); + ftdi->platform_data.potpg = 100; + ftdi->platform_data.reset = NULL; + ftdi->platform_dev.id = ftdi->sequence_num; + ftdi->platform_dev.resource = ftdi->resources; + ftdi->platform_dev.num_resources = ARRAY_SIZE(ftdi->resources); + ftdi->platform_dev.dev.platform_data = &ftdi->platform_data; + ftdi->platform_dev.dev.parent = NULL; + ftdi->platform_dev.dev.release = ftdi_release_platform_dev; + ftdi->platform_dev.dev.dma_mask = NULL; + snprintf(ftdi->device_name, sizeof(ftdi->device_name), "u132_hcd"); + ftdi->platform_dev.name = ftdi->device_name; + dev_info(&ftdi->udev->dev, "requesting module '%s'\n", "u132_hcd"); + request_module("u132_hcd"); + dev_info(&ftdi->udev->dev, "registering '%s'\n", + ftdi->platform_dev.name); + result = platform_device_register(&ftdi->platform_dev); + return result; +} + +static void ftdi_elan_abandon_completions(struct usb_ftdi *ftdi) +{ + down(&ftdi->u132_lock); + while (ftdi->respond_next > ftdi->respond_head) { + struct u132_respond *respond = &ftdi->respond[RESPOND_MASK & + ftdi->respond_head++]; + *respond->result = -ESHUTDOWN; + *respond->value = 0; + complete(&respond->wait_completion); + } up(&ftdi->u132_lock); +} + +static void ftdi_elan_abandon_targets(struct usb_ftdi *ftdi) +{ + int ed_number = 4; + down(&ftdi->u132_lock); + while (ed_number-- > 0) { + struct u132_target *target = &ftdi->target[ed_number]; + if (target->active == 1) { + target->condition_code = TD_DEVNOTRESP; + up(&ftdi->u132_lock); + ftdi_elan_do_callback(ftdi, target, NULL, 0); + down(&ftdi->u132_lock); + } + } + ftdi->recieved = 0; + ftdi->expected = 4; + ftdi->ed_found = 0; + up(&ftdi->u132_lock); +} + +static void ftdi_elan_flush_targets(struct usb_ftdi *ftdi) +{ + int ed_number = 4; + down(&ftdi->u132_lock); + while (ed_number-- > 0) { + struct u132_target *target = &ftdi->target[ed_number]; + target->abandoning = 1; + wait_1:if (target->active == 1) { + int command_size = ftdi->command_next - + ftdi->command_head; + if (command_size < COMMAND_SIZE) { + struct u132_command *command = &ftdi->command[ + COMMAND_MASK & ftdi->command_next]; + command->header = 0x80 | (ed_number << 5) | 0x4; + command->length = 0x00; + command->address = 0x00; + command->width = 0x00; + command->follows = 0; + command->value = 0; + command->buffer = &command->value; + ftdi->command_next += 1; + ftdi_elan_kick_command_queue(ftdi); + } else { + up(&ftdi->u132_lock); + msleep(100); + down(&ftdi->u132_lock); + goto wait_1; + } + } + wait_2:if (target->active == 1) { + int command_size = ftdi->command_next - + ftdi->command_head; + if (command_size < COMMAND_SIZE) { + struct u132_command *command = &ftdi->command[ + COMMAND_MASK & ftdi->command_next]; + command->header = 0x90 | (ed_number << 5); + command->length = 0x00; + command->address = 0x00; + command->width = 0x00; + command->follows = 0; + command->value = 0; + command->buffer = &command->value; + ftdi->command_next += 1; + ftdi_elan_kick_command_queue(ftdi); + } else { + up(&ftdi->u132_lock); + msleep(100); + down(&ftdi->u132_lock); + goto wait_2; + } + } + } + ftdi->recieved = 0; + ftdi->expected = 4; + ftdi->ed_found = 0; + up(&ftdi->u132_lock); +} + +static void ftdi_elan_cancel_targets(struct usb_ftdi *ftdi) +{ + int ed_number = 4; + down(&ftdi->u132_lock); + while (ed_number-- > 0) { + struct u132_target *target = &ftdi->target[ed_number]; + target->abandoning = 1; + wait:if (target->active == 1) { + int command_size = ftdi->command_next - + ftdi->command_head; + if (command_size < COMMAND_SIZE) { + struct u132_command *command = &ftdi->command[ + COMMAND_MASK & ftdi->command_next]; + command->header = 0x80 | (ed_number << 5) | 0x4; + command->length = 0x00; + command->address = 0x00; + command->width = 0x00; + command->follows = 0; + command->value = 0; + command->buffer = &command->value; + ftdi->command_next += 1; + ftdi_elan_kick_command_queue(ftdi); + } else { + up(&ftdi->u132_lock); + msleep(100); + down(&ftdi->u132_lock); + goto wait; + } + } + } + ftdi->recieved = 0; + ftdi->expected = 4; + ftdi->ed_found = 0; + up(&ftdi->u132_lock); +} + +static void ftdi_elan_kick_command_queue(struct usb_ftdi *ftdi) +{ + ftdi_command_queue_work(ftdi, 0); + return; +} + +static void ftdi_elan_command_work(void *data) +{ + struct usb_ftdi *ftdi = data; + if (ftdi->disconnected > 0) { + ftdi_elan_put_kref(ftdi); + return; + } else { + int retval = ftdi_elan_command_engine(ftdi); + if (retval == -ESHUTDOWN) { + ftdi->disconnected += 1; + } else if (retval == -ENODEV) { + ftdi->disconnected += 1; + } else if (retval) + dev_err(&ftdi->udev->dev, "command error %d\n", retval); + ftdi_command_requeue_work(ftdi, msecs_to_jiffies(10)); + return; + } +} + +static void ftdi_elan_kick_respond_queue(struct usb_ftdi *ftdi) +{ + ftdi_respond_queue_work(ftdi, 0); + return; +} + +static void ftdi_elan_respond_work(void *data) +{ + struct usb_ftdi *ftdi = data; + if (ftdi->disconnected > 0) { + ftdi_elan_put_kref(ftdi); + return; + } else { + int retval = ftdi_elan_respond_engine(ftdi); + if (retval == 0) { + } else if (retval == -ESHUTDOWN) { + ftdi->disconnected += 1; + } else if (retval == -ENODEV) { + ftdi->disconnected += 1; + } else if (retval == -ENODEV) { + ftdi->disconnected += 1; + } else if (retval == -EILSEQ) { + ftdi->disconnected += 1; + } else { + ftdi->disconnected += 1; + dev_err(&ftdi->udev->dev, "respond error %d\n", retval); + } + if (ftdi->disconnected > 0) { + ftdi_elan_abandon_completions(ftdi); + ftdi_elan_abandon_targets(ftdi); + } + ftdi_response_requeue_work(ftdi, msecs_to_jiffies(10)); + return; + } +} + + +/* +* the sw_lock is initially held and will be freed +* after the FTDI has been synchronized +* +*/ +static void ftdi_elan_status_work(void *data) +{ + struct usb_ftdi *ftdi = data; + int work_delay_in_msec = 0; + if (ftdi->disconnected > 0) { + ftdi_elan_put_kref(ftdi); + return; + } else if (ftdi->synchronized == 0) { + down(&ftdi->sw_lock); + if (ftdi_elan_synchronize(ftdi) == 0) { + ftdi->synchronized = 1; + ftdi_command_queue_work(ftdi, 1); + ftdi_respond_queue_work(ftdi, 1); + up(&ftdi->sw_lock); + work_delay_in_msec = 100; + } else { + dev_err(&ftdi->udev->dev, "synchronize failed\n"); + up(&ftdi->sw_lock); + work_delay_in_msec = 10 *1000; + } + } else if (ftdi->stuck_status > 0) { + if (ftdi_elan_stuck_waiting(ftdi) == 0) { + ftdi->stuck_status = 0; + ftdi->synchronized = 0; + } else if ((ftdi->stuck_status++ % 60) == 1) { + dev_err(&ftdi->udev->dev, "WRONG type of card inserted " + "- please remove\n"); + } else + dev_err(&ftdi->udev->dev, "WRONG type of card inserted " + "- checked %d times\n", ftdi->stuck_status); + work_delay_in_msec = 100; + } else if (ftdi->enumerated == 0) { + if (ftdi_elan_enumeratePCI(ftdi) == 0) { + ftdi->enumerated = 1; + work_delay_in_msec = 250; + } else + work_delay_in_msec = 1000; + } else if (ftdi->initialized == 0) { + if (ftdi_elan_setupOHCI(ftdi) == 0) { + ftdi->initialized = 1; + work_delay_in_msec = 500; + } else { + dev_err(&ftdi->udev->dev, "initialized failed - trying " + "again in 10 seconds\n"); + work_delay_in_msec = 10 *1000; + } + } else if (ftdi->registered == 0) { + work_delay_in_msec = 10; + if (ftdi_elan_hcd_init(ftdi) == 0) { + ftdi->registered = 1; + } else + dev_err(&ftdi->udev->dev, "register failed\n"); + work_delay_in_msec = 250; + } else { + if (ftdi_elan_checkingPCI(ftdi) == 0) { + work_delay_in_msec = 250; + } else if (ftdi->controlreg & 0x00400000) { + if (ftdi->gone_away > 0) { + dev_err(&ftdi->udev->dev, "PCI device eject con" + "firmed platform_dev.dev.parent=%p plat" + "form_dev.dev=%p\n", + ftdi->platform_dev.dev.parent, + &ftdi->platform_dev.dev); + platform_device_unregister(&ftdi->platform_dev); + ftdi->platform_dev.dev.parent = NULL; + ftdi->registered = 0; + ftdi->enumerated = 0; + ftdi->card_ejected = 0; + ftdi->initialized = 0; + ftdi->gone_away = 0; + } else + ftdi_elan_flush_targets(ftdi); + work_delay_in_msec = 250; + } else { + dev_err(&ftdi->udev->dev, "PCI device has disappeared\n" + ); + ftdi_elan_cancel_targets(ftdi); + work_delay_in_msec = 500; + ftdi->enumerated = 0; + ftdi->initialized = 0; + } + } + if (ftdi->disconnected > 0) { + ftdi_elan_put_kref(ftdi); + return; + } else { + ftdi_status_requeue_work(ftdi, + msecs_to_jiffies(work_delay_in_msec)); + return; + } +} + + +/* +* file_operations for the jtag interface +* +* the usage count for the device is incremented on open() +* and decremented on release() +*/ +static int ftdi_elan_open(struct inode *inode, struct file *file) +{ + int subminor = iminor(inode); + struct usb_interface *interface = usb_find_interface(&ftdi_elan_driver, + subminor); + if (!interface) { + printk(KERN_ERR "can't find device for minor %d\n", subminor); + return -ENODEV; + } else { + struct usb_ftdi *ftdi = usb_get_intfdata(interface); + if (!ftdi) { + return -ENODEV; + } else { + if (down_interruptible(&ftdi->sw_lock)) { + return -EINTR; + } else { + ftdi_elan_get_kref(ftdi); + file->private_data = ftdi; + return 0; + } + } + } +} + +static int ftdi_elan_release(struct inode *inode, struct file *file) +{ + struct usb_ftdi *ftdi = (struct usb_ftdi *)file->private_data; + if (ftdi == NULL) + return -ENODEV; + up(&ftdi->sw_lock); /* decrement the count on our device */ + ftdi_elan_put_kref(ftdi); + return 0; +} + + +#define FTDI_ELAN_IOC_MAGIC 0xA1 +#define FTDI_ELAN_IOCDEBUG _IOC(_IOC_WRITE, FTDI_ELAN_IOC_MAGIC, 1, 132) +static int ftdi_elan_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case FTDI_ELAN_IOCDEBUG:{ + char line[132]; + int size = strncpy_from_user(line, + (const char __user *)arg, sizeof(line)); + if (size < 0) { + return -EINVAL; + } else { + printk(KERN_ERR "TODO: ioctl %s\n", line); + return 0; + } + } + default: + return -EFAULT; + } +} + + +/* +* +* blocking bulk reads are used to get data from the device +* +*/ +static ssize_t ftdi_elan_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + char data[30 *3 + 4]; + char *d = data; + int m = (sizeof(data) - 1) / 3; + int bytes_read = 0; + int retry_on_empty = 10; + int retry_on_timeout = 5; + struct usb_ftdi *ftdi = (struct usb_ftdi *)file->private_data; + if (ftdi->disconnected > 0) { + return -ENODEV; + } + data[0] = 0; + have:if (ftdi->bulk_in_left > 0) { + if (count-- > 0) { + char *p = ++ftdi->bulk_in_last + ftdi->bulk_in_buffer; + ftdi->bulk_in_left -= 1; + if (bytes_read < m) { + d += sprintf(d, " %02X", 0x000000FF & *p); + } else if (bytes_read > m) { + } else + d += sprintf(d, " .."); + if (copy_to_user(buffer++, p, 1)) { + return -EFAULT; + } else { + bytes_read += 1; + goto have; + } + } else + return bytes_read; + } + more:if (count > 0) { + int packet_bytes = 0; + int retval = usb_bulk_msg(ftdi->udev, + usb_rcvbulkpipe(ftdi->udev, ftdi->bulk_in_endpointAddr), + ftdi->bulk_in_buffer, ftdi->bulk_in_size, + &packet_bytes, msecs_to_jiffies(50)); + if (packet_bytes > 2) { + ftdi->bulk_in_left = packet_bytes - 2; + ftdi->bulk_in_last = 1; + goto have; + } else if (retval == -ETIMEDOUT) { + if (retry_on_timeout-- > 0) { + goto more; + } else if (bytes_read > 0) { + return bytes_read; + } else + return retval; + } else if (retval == 0) { + if (retry_on_empty-- > 0) { + goto more; + } else + return bytes_read; + } else + return retval; + } else + return bytes_read; +} + +static void ftdi_elan_write_bulk_callback(struct urb *urb, struct pt_regs *regs) +{ + struct usb_ftdi *ftdi = (struct usb_ftdi *)urb->context; + if (urb->status && !(urb->status == -ENOENT || urb->status == + -ECONNRESET || urb->status == -ESHUTDOWN)) { + dev_err(&ftdi->udev->dev, "urb=%p write bulk status received: %" + "d\n", urb, urb->status); + } + usb_buffer_free(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); +} + +static int fill_buffer_with_all_queued_commands(struct usb_ftdi *ftdi, + char *buf, int command_size, int total_size) +{ + int ed_commands = 0; + int b = 0; + int I = command_size; + int i = ftdi->command_head; + while (I-- > 0) { + struct u132_command *command = &ftdi->command[COMMAND_MASK & + i++]; + int F = command->follows; + u8 *f = command->buffer; + if (command->header & 0x80) { + ed_commands |= 1 << (0x3 & (command->header >> 5)); + } + buf[b++] = command->header; + buf[b++] = (command->length >> 0) & 0x00FF; + buf[b++] = (command->length >> 8) & 0x00FF; + buf[b++] = command->address; + buf[b++] = command->width; + while (F-- > 0) { + buf[b++] = *f++; + } + } + return ed_commands; +} + +static int ftdi_elan_total_command_size(struct usb_ftdi *ftdi, int command_size) +{ + int total_size = 0; + int I = command_size; + int i = ftdi->command_head; + while (I-- > 0) { + struct u132_command *command = &ftdi->command[COMMAND_MASK & + i++]; + total_size += 5 + command->follows; + } return total_size; +} + +static int ftdi_elan_command_engine(struct usb_ftdi *ftdi) +{ + int retval; + char *buf; + int ed_commands; + int total_size; + struct urb *urb; + int command_size = ftdi->command_next - ftdi->command_head; + if (command_size == 0) + return 0; + total_size = ftdi_elan_total_command_size(ftdi, command_size); + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + dev_err(&ftdi->udev->dev, "could not get a urb to write %d comm" + "ands totaling %d bytes to the Uxxx\n", command_size, + total_size); + return -ENOMEM; + } + buf = usb_buffer_alloc(ftdi->udev, total_size, GFP_KERNEL, + &urb->transfer_dma); + if (!buf) { + dev_err(&ftdi->udev->dev, "could not get a buffer to write %d c" + "ommands totaling %d bytes to the Uxxx\n", command_size, + total_size); + usb_free_urb(urb); + return -ENOMEM; + } + ed_commands = fill_buffer_with_all_queued_commands(ftdi, buf, + command_size, total_size); + usb_fill_bulk_urb(urb, ftdi->udev, usb_sndbulkpipe(ftdi->udev, + ftdi->bulk_out_endpointAddr), buf, total_size, + ftdi_elan_write_bulk_callback, ftdi); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + if (ed_commands) { + char diag[40 *3 + 4]; + char *d = diag; + int m = total_size; + u8 *c = buf; + int s = (sizeof(diag) - 1) / 3; + diag[0] = 0; + while (s-- > 0 && m-- > 0) { + if (s > 0 || m == 0) { + d += sprintf(d, " %02X", *c++); + } else + d += sprintf(d, " .."); + } + } + retval = usb_submit_urb(urb, GFP_KERNEL); + if (retval) { + dev_err(&ftdi->udev->dev, "failed %d to submit urb %p to write " + "%d commands totaling %d bytes to the Uxxx\n", retval, + urb, command_size, total_size); + usb_buffer_free(ftdi->udev, total_size, buf, urb->transfer_dma); + usb_free_urb(urb); + return retval; + } + usb_free_urb(urb); /* release our reference to this urb, + the USB core will eventually free it entirely */ + ftdi->command_head += command_size; + ftdi_elan_kick_respond_queue(ftdi); + return 0; +} + +static void ftdi_elan_do_callback(struct usb_ftdi *ftdi, + struct u132_target *target, u8 *buffer, int length) +{ + struct urb *urb = target->urb; + int halted = target->halted; + int skipped = target->skipped; + int actual = target->actual; + int non_null = target->non_null; + int toggle_bits = target->toggle_bits; + int error_count = target->error_count; + int condition_code = target->condition_code; + int repeat_number = target->repeat_number; + void (*callback) (void *, struct urb *, u8 *, int, int, int, int, int, + int, int, int, int) = target->callback; + target->active -= 1; + target->callback = NULL; + (*callback) (target->endp, urb, buffer, length, toggle_bits, + error_count, condition_code, repeat_number, halted, skipped, + actual, non_null); +} + +static char *have_ed_set_response(struct usb_ftdi *ftdi, + struct u132_target *target, u16 ed_length, int ed_number, int ed_type, + char *b) +{ + int payload = (ed_length >> 0) & 0x07FF; + down(&ftdi->u132_lock); + target->actual = 0; + target->non_null = (ed_length >> 15) & 0x0001; + target->repeat_number = (ed_length >> 11) & 0x000F; + if (ed_type == 0x02) { + if (payload == 0 || target->abandoning > 0) { + target->abandoning = 0; + up(&ftdi->u132_lock); + ftdi_elan_do_callback(ftdi, target, 4 + ftdi->response, + payload); + ftdi->recieved = 0; + ftdi->expected = 4; + ftdi->ed_found = 0; + return ftdi->response; + } else { + ftdi->expected = 4 + payload; + ftdi->ed_found = 1; + up(&ftdi->u132_lock); + return b; + } + } else if (ed_type == 0x03) { + if (payload == 0 || target->abandoning > 0) { + target->abandoning = 0; + up(&ftdi->u132_lock); + ftdi_elan_do_callback(ftdi, target, 4 + ftdi->response, + payload); + ftdi->recieved = 0; + ftdi->expected = 4; + ftdi->ed_found = 0; + return ftdi->response; + } else { + ftdi->expected = 4 + payload; + ftdi->ed_found = 1; + up(&ftdi->u132_lock); + return b; + } + } else if (ed_type == 0x01) { + target->abandoning = 0; + up(&ftdi->u132_lock); + ftdi_elan_do_callback(ftdi, target, 4 + ftdi->response, + payload); + ftdi->recieved = 0; + ftdi->expected = 4; + ftdi->ed_found = 0; + return ftdi->response; + } else { + target->abandoning = 0; + up(&ftdi->u132_lock); + ftdi_elan_do_callback(ftdi, target, 4 + ftdi->response, + payload); + ftdi->recieved = 0; + ftdi->expected = 4; + ftdi->ed_found = 0; + return ftdi->response; + } +} + +static char *have_ed_get_response(struct usb_ftdi *ftdi, + struct u132_target *target, u16 ed_length, int ed_number, int ed_type, + char *b) +{ + down(&ftdi->u132_lock); + target->condition_code = TD_DEVNOTRESP; + target->actual = (ed_length >> 0) & 0x01FF; + target->non_null = (ed_length >> 15) & 0x0001; + target->repeat_number = (ed_length >> 11) & 0x000F; + up(&ftdi->u132_lock); + if (target->active) + ftdi_elan_do_callback(ftdi, target, NULL, 0); + target->abandoning = 0; + ftdi->recieved = 0; + ftdi->expected = 4; + ftdi->ed_found = 0; + return ftdi->response; +} + + +/* +* The engine tries to empty the FTDI fifo +* +* all responses found in the fifo data are dispatched thus +* the response buffer can only ever hold a maximum sized +* response from the Uxxx. +* +*/ +static int ftdi_elan_respond_engine(struct usb_ftdi *ftdi) +{ + u8 *b = ftdi->response + ftdi->recieved; + int bytes_read = 0; + int retry_on_empty = 1; + int retry_on_timeout = 3; + int empty_packets = 0; + read:{ + int packet_bytes = 0; + int retval = usb_bulk_msg(ftdi->udev, + usb_rcvbulkpipe(ftdi->udev, ftdi->bulk_in_endpointAddr), + ftdi->bulk_in_buffer, ftdi->bulk_in_size, + &packet_bytes, msecs_to_jiffies(500)); + char diag[30 *3 + 4]; + char *d = diag; + int m = packet_bytes; + u8 *c = ftdi->bulk_in_buffer; + int s = (sizeof(diag) - 1) / 3; + diag[0] = 0; + while (s-- > 0 && m-- > 0) { + if (s > 0 || m == 0) { + d += sprintf(d, " %02X", *c++); + } else + d += sprintf(d, " .."); + } + if (packet_bytes > 2) { + ftdi->bulk_in_left = packet_bytes - 2; + ftdi->bulk_in_last = 1; + goto have; + } else if (retval == -ETIMEDOUT) { + if (retry_on_timeout-- > 0) { + dev_err(&ftdi->udev->dev, "TIMED OUT with packe" + "t_bytes = %d with total %d bytes%s\n", + packet_bytes, bytes_read, diag); + goto more; + } else if (bytes_read > 0) { + dev_err(&ftdi->udev->dev, "ONLY %d bytes%s\n", + bytes_read, diag); + return -ENOMEM; + } else { + dev_err(&ftdi->udev->dev, "TIMED OUT with packe" + "t_bytes = %d with total %d bytes%s\n", + packet_bytes, bytes_read, diag); + return -ENOMEM; + } + } else if (retval == -EILSEQ) { + dev_err(&ftdi->udev->dev, "error = %d with packet_bytes" + " = %d with total %d bytes%s\n", retval, + packet_bytes, bytes_read, diag); + return retval; + } else if (retval) { + dev_err(&ftdi->udev->dev, "error = %d with packet_bytes" + " = %d with total %d bytes%s\n", retval, + packet_bytes, bytes_read, diag); + return retval; + } else if (packet_bytes == 2) { + unsigned char s0 = ftdi->bulk_in_buffer[0]; + unsigned char s1 = ftdi->bulk_in_buffer[1]; + empty_packets += 1; + if (s0 == 0x31 && s1 == 0x60) { + if (retry_on_empty-- > 0) { + goto more; + } else + return 0; + } else if (s0 == 0x31 && s1 == 0x00) { + if (retry_on_empty-- > 0) { + goto more; + } else + return 0; + } else { + if (retry_on_empty-- > 0) { + goto more; + } else + return 0; + } + } else if (packet_bytes == 1) { + if (retry_on_empty-- > 0) { + goto more; + } else + return 0; + } else { + if (retry_on_empty-- > 0) { + goto more; + } else + return 0; + } + } + more:{ + goto read; + } + have:if (ftdi->bulk_in_left > 0) { + u8 c = ftdi->bulk_in_buffer[++ftdi->bulk_in_last]; + bytes_read += 1; + ftdi->bulk_in_left -= 1; + if (ftdi->recieved == 0 && c == 0xFF) { + goto have; + } else + *b++ = c; + if (++ftdi->recieved < ftdi->expected) { + goto have; + } else if (ftdi->ed_found) { + int ed_number = (ftdi->response[0] >> 5) & 0x03; + u16 ed_length = (ftdi->response[2] << 8) | + ftdi->response[1]; + struct u132_target *target = &ftdi->target[ed_number]; + int payload = (ed_length >> 0) & 0x07FF; + char diag[30 *3 + 4]; + char *d = diag; + int m = payload; + u8 *c = 4 + ftdi->response; + int s = (sizeof(diag) - 1) / 3; + diag[0] = 0; + while (s-- > 0 && m-- > 0) { + if (s > 0 || m == 0) { + d += sprintf(d, " %02X", *c++); + } else + d += sprintf(d, " .."); + } + ftdi_elan_do_callback(ftdi, target, 4 + ftdi->response, + payload); + ftdi->recieved = 0; + ftdi->expected = 4; + ftdi->ed_found = 0; + b = ftdi->response; + goto have; + } else if (ftdi->expected == 8) { + u8 buscmd; + int respond_head = ftdi->respond_head++; + struct u132_respond *respond = &ftdi->respond[ + RESPOND_MASK & respond_head]; + u32 data = ftdi->response[7]; + data <<= 8; + data |= ftdi->response[6]; + data <<= 8; + data |= ftdi->response[5]; + data <<= 8; + data |= ftdi->response[4]; + *respond->value = data; + *respond->result = 0; + complete(&respond->wait_completion); + ftdi->recieved = 0; + ftdi->expected = 4; + ftdi->ed_found = 0; + b = ftdi->response; + buscmd = (ftdi->response[0] >> 0) & 0x0F; + if (buscmd == 0x00) { + } else if (buscmd == 0x02) { + } else if (buscmd == 0x06) { + } else if (buscmd == 0x0A) { + } else + dev_err(&ftdi->udev->dev, "Uxxx unknown(%0X) va" + "lue = %08X\n", buscmd, data); + goto have; + } else { + if ((ftdi->response[0] & 0x80) == 0x00) { + ftdi->expected = 8; + goto have; + } else { + int ed_number = (ftdi->response[0] >> 5) & 0x03; + int ed_type = (ftdi->response[0] >> 0) & 0x03; + u16 ed_length = (ftdi->response[2] << 8) | + ftdi->response[1]; + struct u132_target *target = &ftdi->target[ + ed_number]; + target->halted = (ftdi->response[0] >> 3) & + 0x01; + target->skipped = (ftdi->response[0] >> 2) & + 0x01; + target->toggle_bits = (ftdi->response[3] >> 6) + & 0x03; + target->error_count = (ftdi->response[3] >> 4) + & 0x03; + target->condition_code = (ftdi->response[ + 3] >> 0) & 0x0F; + if ((ftdi->response[0] & 0x10) == 0x00) { + b = have_ed_set_response(ftdi, target, + ed_length, ed_number, ed_type, + b); + goto have; + } else { + b = have_ed_get_response(ftdi, target, + ed_length, ed_number, ed_type, + b); + goto have; + } + } + } + } else + goto more; +} + + +/* +* create a urb, and a buffer for it, and copy the data to the urb +* +*/ +static ssize_t ftdi_elan_write(struct file *file, + const char __user *user_buffer, size_t count, + loff_t *ppos) +{ + int retval = 0; + struct urb *urb; + char *buf; + char data[30 *3 + 4]; + char *d = data; + const char __user *s = user_buffer; + int m = (sizeof(data) - 1) / 3; + struct usb_ftdi *ftdi = (struct usb_ftdi *)file->private_data; + if (ftdi->disconnected > 0) { + return -ENODEV; + } + if (count == 0) { + goto exit; + } + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + retval = -ENOMEM; + goto error_1; + } + buf = usb_buffer_alloc(ftdi->udev, count, GFP_KERNEL, + &urb->transfer_dma); + if (!buf) { + retval = -ENOMEM; + goto error_2; + } + if (copy_from_user(buf, user_buffer, count)) { + retval = -EFAULT; + goto error_3; + } + usb_fill_bulk_urb(urb, ftdi->udev, usb_sndbulkpipe(ftdi->udev, + ftdi->bulk_out_endpointAddr), buf, count, + ftdi_elan_write_bulk_callback, ftdi); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + retval = usb_submit_urb(urb, GFP_KERNEL); + if (retval) { + dev_err(&ftdi->udev->dev, "failed submitting write urb, error %" + "d\n", retval); + goto error_4; + } + usb_free_urb(urb); + exit:; + if (count > m) { + int I = m - 1; + while (I-- > 0) { + d += sprintf(d, " %02X", 0x000000FF & *s++); + } + d += sprintf(d, " .."); + } else { + int I = count; + while (I-- > 0) { + d += sprintf(d, " %02X", 0x000000FF & *s++); + } + } + return count; + error_4: error_3:usb_buffer_free(ftdi->udev, count, buf, + urb->transfer_dma); + error_2:usb_free_urb(urb); + error_1:return retval; +} + +static struct file_operations ftdi_elan_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = ftdi_elan_ioctl, + .read = ftdi_elan_read, + .write = ftdi_elan_write, + .open = ftdi_elan_open, + .release = ftdi_elan_release, +}; + +/* +* usb class driver info in order to get a minor number from the usb core, +* and to have the device registered with the driver core +*/ +static struct usb_class_driver ftdi_elan_jtag_class = { + .name = "ftdi-%d-jtag", + .fops = &ftdi_elan_fops, + .minor_base = USB_FTDI_ELAN_MINOR_BASE, +}; + +/* +* the following definitions are for the +* ELAN FPGA state machgine processor that +* lies on the other side of the FTDI chip +*/ +#define cPCIu132rd 0x0 +#define cPCIu132wr 0x1 +#define cPCIiord 0x2 +#define cPCIiowr 0x3 +#define cPCImemrd 0x6 +#define cPCImemwr 0x7 +#define cPCIcfgrd 0xA +#define cPCIcfgwr 0xB +#define cPCInull 0xF +#define cU132cmd_status 0x0 +#define cU132flash 0x1 +#define cPIDsetup 0x0 +#define cPIDout 0x1 +#define cPIDin 0x2 +#define cPIDinonce 0x3 +#define cCCnoerror 0x0 +#define cCCcrc 0x1 +#define cCCbitstuff 0x2 +#define cCCtoggle 0x3 +#define cCCstall 0x4 +#define cCCnoresp 0x5 +#define cCCbadpid1 0x6 +#define cCCbadpid2 0x7 +#define cCCdataoverrun 0x8 +#define cCCdataunderrun 0x9 +#define cCCbuffoverrun 0xC +#define cCCbuffunderrun 0xD +#define cCCnotaccessed 0xF +static int ftdi_elan_write_reg(struct usb_ftdi *ftdi, u32 data) +{ + wait:if (ftdi->disconnected > 0) { + return -ENODEV; + } else { + int command_size; + down(&ftdi->u132_lock); + command_size = ftdi->command_next - ftdi->command_head; + if (command_size < COMMAND_SIZE) { + struct u132_command *command = &ftdi->command[ + COMMAND_MASK & ftdi->command_next]; + command->header = 0x00 | cPCIu132wr; + command->length = 0x04; + command->address = 0x00; + command->width = 0x00; + command->follows = 4; + command->value = data; + command->buffer = &command->value; + ftdi->command_next += 1; + ftdi_elan_kick_command_queue(ftdi); + up(&ftdi->u132_lock); + return 0; + } else { + up(&ftdi->u132_lock); + msleep(100); + goto wait; + } + } +} + +static int ftdi_elan_write_config(struct usb_ftdi *ftdi, int config_offset, + u8 width, u32 data) +{ + u8 addressofs = config_offset / 4; + wait:if (ftdi->disconnected > 0) { + return -ENODEV; + } else { + int command_size; + down(&ftdi->u132_lock); + command_size = ftdi->command_next - ftdi->command_head; + if (command_size < COMMAND_SIZE) { + struct u132_command *command = &ftdi->command[ + COMMAND_MASK & ftdi->command_next]; + command->header = 0x00 | (cPCIcfgwr & 0x0F); + command->length = 0x04; + command->address = addressofs; + command->width = 0x00 | (width & 0x0F); + command->follows = 4; + command->value = data; + command->buffer = &command->value; + ftdi->command_next += 1; + ftdi_elan_kick_command_queue(ftdi); + up(&ftdi->u132_lock); + return 0; + } else { + up(&ftdi->u132_lock); + msleep(100); + goto wait; + } + } +} + +static int ftdi_elan_write_pcimem(struct usb_ftdi *ftdi, int mem_offset, + u8 width, u32 data) +{ + u8 addressofs = mem_offset / 4; + wait:if (ftdi->disconnected > 0) { + return -ENODEV; + } else { + int command_size; + down(&ftdi->u132_lock); + command_size = ftdi->command_next - ftdi->command_head; + if (command_size < COMMAND_SIZE) { + struct u132_command *command = &ftdi->command[ + COMMAND_MASK & ftdi->command_next]; + command->header = 0x00 | (cPCImemwr & 0x0F); + command->length = 0x04; + command->address = addressofs; + command->width = 0x00 | (width & 0x0F); + command->follows = 4; + command->value = data; + command->buffer = &command->value; + ftdi->command_next += 1; + ftdi_elan_kick_command_queue(ftdi); + up(&ftdi->u132_lock); + return 0; + } else { + up(&ftdi->u132_lock); + msleep(100); + goto wait; + } + } +} + +int usb_ftdi_elan_write_pcimem(struct platform_device *pdev, int mem_offset, + u8 width, u32 data) +{ + struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev); + return ftdi_elan_write_pcimem(ftdi, mem_offset, width, data); +} + + +EXPORT_SYMBOL_GPL(usb_ftdi_elan_write_pcimem); +static int ftdi_elan_read_reg(struct usb_ftdi *ftdi, u32 *data) +{ + wait:if (ftdi->disconnected > 0) { + return -ENODEV; + } else { + int command_size; + int respond_size; + down(&ftdi->u132_lock); + command_size = ftdi->command_next - ftdi->command_head; + respond_size = ftdi->respond_next - ftdi->respond_head; + if (command_size < COMMAND_SIZE && respond_size < RESPOND_SIZE) + { + struct u132_command *command = &ftdi->command[ + COMMAND_MASK & ftdi->command_next]; + struct u132_respond *respond = &ftdi->respond[ + RESPOND_MASK & ftdi->respond_next]; + int result = -ENODEV; + respond->result = &result; + respond->header = command->header = 0x00 | cPCIu132rd; + command->length = 0x04; + respond->address = command->address = cU132cmd_status; + command->width = 0x00; + command->follows = 0; + command->value = 0; + command->buffer = NULL; + respond->value = data; + init_completion(&respond->wait_completion); + ftdi->command_next += 1; + ftdi->respond_next += 1; + ftdi_elan_kick_command_queue(ftdi); + up(&ftdi->u132_lock); + wait_for_completion(&respond->wait_completion); + return result; + } else { + up(&ftdi->u132_lock); + msleep(100); + goto wait; + } + } +} + +int usb_ftdi_elan_read_reg(struct platform_device *pdev, u32 *data) +{ + struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev); + return ftdi_elan_read_reg(ftdi, data); +} + + +EXPORT_SYMBOL_GPL(usb_ftdi_elan_read_reg); +static int ftdi_elan_read_config(struct usb_ftdi *ftdi, int config_offset, + u8 width, u32 *data) +{ + u8 addressofs = config_offset / 4; + wait:if (ftdi->disconnected > 0) { + return -ENODEV; + } else { + int command_size; + int respond_size; + down(&ftdi->u132_lock); + command_size = ftdi->command_next - ftdi->command_head; + respond_size = ftdi->respond_next - ftdi->respond_head; + if (command_size < COMMAND_SIZE && respond_size < RESPOND_SIZE) + { + struct u132_command *command = &ftdi->command[ + COMMAND_MASK & ftdi->command_next]; + struct u132_respond *respond = &ftdi->respond[ + RESPOND_MASK & ftdi->respond_next]; + int result = -ENODEV; + respond->result = &result; + respond->header = command->header = 0x00 | (cPCIcfgrd & + 0x0F); + command->length = 0x04; + respond->address = command->address = addressofs; + command->width = 0x00 | (width & 0x0F); + command->follows = 0; + command->value = 0; + command->buffer = NULL; + respond->value = data; + init_completion(&respond->wait_completion); + ftdi->command_next += 1; + ftdi->respond_next += 1; + ftdi_elan_kick_command_queue(ftdi); + up(&ftdi->u132_lock); + wait_for_completion(&respond->wait_completion); + return result; + } else { + up(&ftdi->u132_lock); + msleep(100); + goto wait; + } + } +} + +static int ftdi_elan_read_pcimem(struct usb_ftdi *ftdi, int mem_offset, + u8 width, u32 *data) +{ + u8 addressofs = mem_offset / 4; + wait:if (ftdi->disconnected > 0) { + return -ENODEV; + } else { + int command_size; + int respond_size; + down(&ftdi->u132_lock); + command_size = ftdi->command_next - ftdi->command_head; + respond_size = ftdi->respond_next - ftdi->respond_head; + if (command_size < COMMAND_SIZE && respond_size < RESPOND_SIZE) + { + struct u132_command *command = &ftdi->command[ + COMMAND_MASK & ftdi->command_next]; + struct u132_respond *respond = &ftdi->respond[ + RESPOND_MASK & ftdi->respond_next]; + int result = -ENODEV; + respond->result = &result; + respond->header = command->header = 0x00 | (cPCImemrd & + 0x0F); + command->length = 0x04; + respond->address = command->address = addressofs; + command->width = 0x00 | (width & 0x0F); + command->follows = 0; + command->value = 0; + command->buffer = NULL; + respond->value = data; + init_completion(&respond->wait_completion); + ftdi->command_next += 1; + ftdi->respond_next += 1; + ftdi_elan_kick_command_queue(ftdi); + up(&ftdi->u132_lock); + wait_for_completion(&respond->wait_completion); + return result; + } else { + up(&ftdi->u132_lock); + msleep(100); + goto wait; + } + } +} + +int usb_ftdi_elan_read_pcimem(struct platform_device *pdev, int mem_offset, + u8 width, u32 *data) +{ + struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev); + if (ftdi->initialized == 0) { + return -ENODEV; + } else + return ftdi_elan_read_pcimem(ftdi, mem_offset, width, data); +} + + +EXPORT_SYMBOL_GPL(usb_ftdi_elan_read_pcimem); +static int ftdi_elan_edset_setup(struct usb_ftdi *ftdi, u8 ed_number, + void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits, + void (*callback) (void *endp, struct urb *urb, u8 *buf, int len, + int toggle_bits, int error_count, int condition_code, int repeat_number, + int halted, int skipped, int actual, int non_null)) +{ + u8 ed = ed_number - 1; + wait:if (ftdi->disconnected > 0) { + return -ENODEV; + } else if (ftdi->initialized == 0) { + return -ENODEV; + } else { + int command_size; + down(&ftdi->u132_lock); + command_size = ftdi->command_next - ftdi->command_head; + if (command_size < COMMAND_SIZE) { + struct u132_target *target = &ftdi->target[ed]; + struct u132_command *command = &ftdi->command[ + COMMAND_MASK & ftdi->command_next]; + command->header = 0x80 | (ed << 5); + command->length = 0x8007; + command->address = (toggle_bits << 6) | (ep_number << 2) + | (address << 0); + command->width = usb_maxpacket(urb->dev, urb->pipe, + usb_pipeout(urb->pipe)); + command->follows = 8; + command->value = 0; + command->buffer = urb->setup_packet; + target->callback = callback; + target->endp = endp; + target->urb = urb; + target->active = 1; + ftdi->command_next += 1; + ftdi_elan_kick_command_queue(ftdi); + up(&ftdi->u132_lock); + return 0; + } else { + up(&ftdi->u132_lock); + msleep(100); + goto wait; + } + } +} + +int usb_ftdi_elan_edset_setup(struct platform_device *pdev, u8 ed_number, + void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits, + void (*callback) (void *endp, struct urb *urb, u8 *buf, int len, + int toggle_bits, int error_count, int condition_code, int repeat_number, + int halted, int skipped, int actual, int non_null)) +{ + struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev); + return ftdi_elan_edset_setup(ftdi, ed_number, endp, urb, address, + ep_number, toggle_bits, callback); +} + + +EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_setup); +static int ftdi_elan_edset_input(struct usb_ftdi *ftdi, u8 ed_number, + void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits, + void (*callback) (void *endp, struct urb *urb, u8 *buf, int len, + int toggle_bits, int error_count, int condition_code, int repeat_number, + int halted, int skipped, int actual, int non_null)) +{ + u8 ed = ed_number - 1; + wait:if (ftdi->disconnected > 0) { + return -ENODEV; + } else if (ftdi->initialized == 0) { + return -ENODEV; + } else { + int command_size; + down(&ftdi->u132_lock); + command_size = ftdi->command_next - ftdi->command_head; + if (command_size < COMMAND_SIZE) { + struct u132_target *target = &ftdi->target[ed]; + struct u132_command *command = &ftdi->command[ + COMMAND_MASK & ftdi->command_next]; + int remaining_length = urb->transfer_buffer_length - + urb->actual_length; + command->header = 0x82 | (ed << 5); + if (remaining_length == 0) { + command->length = 0x0000; + } else if (remaining_length > 1024) { + command->length = 0x8000 | 1023; + } else + command->length = 0x8000 | (remaining_length - + 1); + command->address = (toggle_bits << 6) | (ep_number << 2) + | (address << 0); + command->width = usb_maxpacket(urb->dev, urb->pipe, + usb_pipeout(urb->pipe)); + command->follows = 0; + command->value = 0; + command->buffer = NULL; + target->callback = callback; + target->endp = endp; + target->urb = urb; + target->active = 1; + ftdi->command_next += 1; + ftdi_elan_kick_command_queue(ftdi); + up(&ftdi->u132_lock); + return 0; + } else { + up(&ftdi->u132_lock); + msleep(100); + goto wait; + } + } +} + +int usb_ftdi_elan_edset_input(struct platform_device *pdev, u8 ed_number, + void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits, + void (*callback) (void *endp, struct urb *urb, u8 *buf, int len, + int toggle_bits, int error_count, int condition_code, int repeat_number, + int halted, int skipped, int actual, int non_null)) +{ + struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev); + return ftdi_elan_edset_input(ftdi, ed_number, endp, urb, address, + ep_number, toggle_bits, callback); +} + + +EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_input); +static int ftdi_elan_edset_empty(struct usb_ftdi *ftdi, u8 ed_number, + void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits, + void (*callback) (void *endp, struct urb *urb, u8 *buf, int len, + int toggle_bits, int error_count, int condition_code, int repeat_number, + int halted, int skipped, int actual, int non_null)) +{ + u8 ed = ed_number - 1; + wait:if (ftdi->disconnected > 0) { + return -ENODEV; + } else if (ftdi->initialized == 0) { + return -ENODEV; + } else { + int command_size; + down(&ftdi->u132_lock); + command_size = ftdi->command_next - ftdi->command_head; + if (command_size < COMMAND_SIZE) { + struct u132_target *target = &ftdi->target[ed]; + struct u132_command *command = &ftdi->command[ + COMMAND_MASK & ftdi->command_next]; + command->header = 0x81 | (ed << 5); + command->length = 0x0000; + command->address = (toggle_bits << 6) | (ep_number << 2) + | (address << 0); + command->width = usb_maxpacket(urb->dev, urb->pipe, + usb_pipeout(urb->pipe)); + command->follows = 0; + command->value = 0; + command->buffer = NULL; + target->callback = callback; + target->endp = endp; + target->urb = urb; + target->active = 1; + ftdi->command_next += 1; + ftdi_elan_kick_command_queue(ftdi); + up(&ftdi->u132_lock); + return 0; + } else { + up(&ftdi->u132_lock); + msleep(100); + goto wait; + } + } +} + +int usb_ftdi_elan_edset_empty(struct platform_device *pdev, u8 ed_number, + void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits, + void (*callback) (void *endp, struct urb *urb, u8 *buf, int len, + int toggle_bits, int error_count, int condition_code, int repeat_number, + int halted, int skipped, int actual, int non_null)) +{ + struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev); + return ftdi_elan_edset_empty(ftdi, ed_number, endp, urb, address, + ep_number, toggle_bits, callback); +} + + +EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_empty); +static int ftdi_elan_edset_output(struct usb_ftdi *ftdi, u8 ed_number, + void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits, + void (*callback) (void *endp, struct urb *urb, u8 *buf, int len, + int toggle_bits, int error_count, int condition_code, int repeat_number, + int halted, int skipped, int actual, int non_null)) +{ + u8 ed = ed_number - 1; + wait:if (ftdi->disconnected > 0) { + return -ENODEV; + } else if (ftdi->initialized == 0) { + return -ENODEV; + } else { + int command_size; + down(&ftdi->u132_lock); + command_size = ftdi->command_next - ftdi->command_head; + if (command_size < COMMAND_SIZE) { + u8 *b; + u16 urb_size; + int i = 0; + char data[30 *3 + 4]; + char *d = data; + int m = (sizeof(data) - 1) / 3; + int l = 0; + struct u132_target *target = &ftdi->target[ed]; + struct u132_command *command = &ftdi->command[ + COMMAND_MASK & ftdi->command_next]; + command->header = 0x81 | (ed << 5); + command->address = (toggle_bits << 6) | (ep_number << 2) + | (address << 0); + command->width = usb_maxpacket(urb->dev, urb->pipe, + usb_pipeout(urb->pipe)); + command->follows = min(1024, + urb->transfer_buffer_length - + urb->actual_length); + command->value = 0; + command->buffer = urb->transfer_buffer + + urb->actual_length; + command->length = 0x8000 | (command->follows - 1); + b = command->buffer; + urb_size = command->follows; + data[0] = 0; + while (urb_size-- > 0) { + if (i > m) { + } else if (i++ < m) { + int w = sprintf(d, " %02X", *b++); + d += w; + l += w; + } else + d += sprintf(d, " .."); + } + target->callback = callback; + target->endp = endp; + target->urb = urb; + target->active = 1; + ftdi->command_next += 1; + ftdi_elan_kick_command_queue(ftdi); + up(&ftdi->u132_lock); + return 0; + } else { + up(&ftdi->u132_lock); + msleep(100); + goto wait; + } + } +} + +int usb_ftdi_elan_edset_output(struct platform_device *pdev, u8 ed_number, + void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits, + void (*callback) (void *endp, struct urb *urb, u8 *buf, int len, + int toggle_bits, int error_count, int condition_code, int repeat_number, + int halted, int skipped, int actual, int non_null)) +{ + struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev); + return ftdi_elan_edset_output(ftdi, ed_number, endp, urb, address, + ep_number, toggle_bits, callback); +} + + +EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_output); +static int ftdi_elan_edset_single(struct usb_ftdi *ftdi, u8 ed_number, + void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits, + void (*callback) (void *endp, struct urb *urb, u8 *buf, int len, + int toggle_bits, int error_count, int condition_code, int repeat_number, + int halted, int skipped, int actual, int non_null)) +{ + u8 ed = ed_number - 1; + wait:if (ftdi->disconnected > 0) { + return -ENODEV; + } else if (ftdi->initialized == 0) { + return -ENODEV; + } else { + int command_size; + down(&ftdi->u132_lock); + command_size = ftdi->command_next - ftdi->command_head; + if (command_size < COMMAND_SIZE) { + int remaining_length = urb->transfer_buffer_length - + urb->actual_length; + struct u132_target *target = &ftdi->target[ed]; + struct u132_command *command = &ftdi->command[ + COMMAND_MASK & ftdi->command_next]; + command->header = 0x83 | (ed << 5); + if (remaining_length == 0) { + command->length = 0x0000; + } else if (remaining_length > 1024) { + command->length = 0x8000 | 1023; + } else + command->length = 0x8000 | (remaining_length - + 1); + command->address = (toggle_bits << 6) | (ep_number << 2) + | (address << 0); + command->width = usb_maxpacket(urb->dev, urb->pipe, + usb_pipeout(urb->pipe)); + command->follows = 0; + command->value = 0; + command->buffer = NULL; + target->callback = callback; + target->endp = endp; + target->urb = urb; + target->active = 1; + ftdi->command_next += 1; + ftdi_elan_kick_command_queue(ftdi); + up(&ftdi->u132_lock); + return 0; + } else { + up(&ftdi->u132_lock); + msleep(100); + goto wait; + } + } +} + +int usb_ftdi_elan_edset_single(struct platform_device *pdev, u8 ed_number, + void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits, + void (*callback) (void *endp, struct urb *urb, u8 *buf, int len, + int toggle_bits, int error_count, int condition_code, int repeat_number, + int halted, int skipped, int actual, int non_null)) +{ + struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev); + return ftdi_elan_edset_single(ftdi, ed_number, endp, urb, address, + ep_number, toggle_bits, callback); +} + + +EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_single); +static int ftdi_elan_edset_flush(struct usb_ftdi *ftdi, u8 ed_number, + void *endp) +{ + u8 ed = ed_number - 1; + if (ftdi->disconnected > 0) { + return -ENODEV; + } else if (ftdi->initialized == 0) { + return -ENODEV; + } else { + struct u132_target *target = &ftdi->target[ed]; + down(&ftdi->u132_lock); + if (target->abandoning > 0) { + up(&ftdi->u132_lock); + return 0; + } else { + target->abandoning = 1; + wait_1:if (target->active == 1) { + int command_size = ftdi->command_next - + ftdi->command_head; + if (command_size < COMMAND_SIZE) { + struct u132_command *command = + &ftdi->command[COMMAND_MASK & + ftdi->command_next]; + command->header = 0x80 | (ed << 5) | + 0x4; + command->length = 0x00; + command->address = 0x00; + command->width = 0x00; + command->follows = 0; + command->value = 0; + command->buffer = &command->value; + ftdi->command_next += 1; + ftdi_elan_kick_command_queue(ftdi); + } else { + up(&ftdi->u132_lock); + msleep(100); + down(&ftdi->u132_lock); + goto wait_1; + } + } + up(&ftdi->u132_lock); + return 0; + } + } +} + +int usb_ftdi_elan_edset_flush(struct platform_device *pdev, u8 ed_number, + void *endp) +{ + struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev); + return ftdi_elan_edset_flush(ftdi, ed_number, endp); +} + + +EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_flush); +static int ftdi_elan_flush_input_fifo(struct usb_ftdi *ftdi) +{ + int retry_on_empty = 10; + int retry_on_timeout = 5; + int retry_on_status = 20; + more:{ + int packet_bytes = 0; + int retval = usb_bulk_msg(ftdi->udev, + usb_rcvbulkpipe(ftdi->udev, ftdi->bulk_in_endpointAddr), + ftdi->bulk_in_buffer, ftdi->bulk_in_size, + &packet_bytes, msecs_to_jiffies(100)); + if (packet_bytes > 2) { + char diag[30 *3 + 4]; + char *d = diag; + int m = (sizeof(diag) - 1) / 3; + char *b = ftdi->bulk_in_buffer; + int bytes_read = 0; + diag[0] = 0; + while (packet_bytes-- > 0) { + char c = *b++; + if (bytes_read < m) { + d += sprintf(d, " %02X", + 0x000000FF & c); + } else if (bytes_read > m) { + } else + d += sprintf(d, " .."); + bytes_read += 1; + continue; + } + goto more; + } else if (packet_bytes > 1) { + char s1 = ftdi->bulk_in_buffer[0]; + char s2 = ftdi->bulk_in_buffer[1]; + if (s1 == 0x31 && s2 == 0x60) { + return 0; + } else if (retry_on_status-- > 0) { + goto more; + } else { + dev_err(&ftdi->udev->dev, "STATUS ERROR retry l" + "imit reached\n"); + return -EFAULT; + } + } else if (packet_bytes > 0) { + char b1 = ftdi->bulk_in_buffer[0]; + dev_err(&ftdi->udev->dev, "only one byte flushed from F" + "TDI = %02X\n", b1); + if (retry_on_status-- > 0) { + goto more; + } else { + dev_err(&ftdi->udev->dev, "STATUS ERROR retry l" + "imit reached\n"); + return -EFAULT; + } + } else if (retval == -ETIMEDOUT) { + if (retry_on_timeout-- > 0) { + goto more; + } else { + dev_err(&ftdi->udev->dev, "TIMED OUT retry limi" + "t reached\n"); + return -ENOMEM; + } + } else if (retval == 0) { + if (retry_on_empty-- > 0) { + goto more; + } else { + dev_err(&ftdi->udev->dev, "empty packet retry l" + "imit reached\n"); + return -ENOMEM; + } + } else { + dev_err(&ftdi->udev->dev, "error = %d\n", retval); + return retval; + } + } + return -1; +} + + +/* +* send the long flush sequence +* +*/ +static int ftdi_elan_synchronize_flush(struct usb_ftdi *ftdi) +{ + int retval; + struct urb *urb; + char *buf; + int I = 257; + int i = 0; + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + dev_err(&ftdi->udev->dev, "could not alloc a urb for flush sequ" + "ence\n"); + return -ENOMEM; + } + buf = usb_buffer_alloc(ftdi->udev, I, GFP_KERNEL, &urb->transfer_dma); + if (!buf) { + dev_err(&ftdi->udev->dev, "could not get a buffer for flush seq" + "uence\n"); + usb_free_urb(urb); + return -ENOMEM; + } + while (I-- > 0) + buf[i++] = 0x55; + usb_fill_bulk_urb(urb, ftdi->udev, usb_sndbulkpipe(ftdi->udev, + ftdi->bulk_out_endpointAddr), buf, i, + ftdi_elan_write_bulk_callback, ftdi); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + retval = usb_submit_urb(urb, GFP_KERNEL); + if (retval) { + dev_err(&ftdi->udev->dev, "failed to submit urb containing the " + "flush sequence\n"); + usb_buffer_free(ftdi->udev, i, buf, urb->transfer_dma); + usb_free_urb(urb); + return -ENOMEM; + } + usb_free_urb(urb); + return 0; +} + + +/* +* send the reset sequence +* +*/ +static int ftdi_elan_synchronize_reset(struct usb_ftdi *ftdi) +{ + int retval; + struct urb *urb; + char *buf; + int I = 4; + int i = 0; + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + dev_err(&ftdi->udev->dev, "could not get a urb for the reset se" + "quence\n"); + return -ENOMEM; + } + buf = usb_buffer_alloc(ftdi->udev, I, GFP_KERNEL, &urb->transfer_dma); + if (!buf) { + dev_err(&ftdi->udev->dev, "could not get a buffer for the reset" + " sequence\n"); + usb_free_urb(urb); + return -ENOMEM; + } + buf[i++] = 0x55; + buf[i++] = 0xAA; + buf[i++] = 0x5A; + buf[i++] = 0xA5; + usb_fill_bulk_urb(urb, ftdi->udev, usb_sndbulkpipe(ftdi->udev, + ftdi->bulk_out_endpointAddr), buf, i, + ftdi_elan_write_bulk_callback, ftdi); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + retval = usb_submit_urb(urb, GFP_KERNEL); + if (retval) { + dev_err(&ftdi->udev->dev, "failed to submit urb containing the " + "reset sequence\n"); + usb_buffer_free(ftdi->udev, i, buf, urb->transfer_dma); + usb_free_urb(urb); + return -ENOMEM; + } + usb_free_urb(urb); + return 0; +} + +static int ftdi_elan_synchronize(struct usb_ftdi *ftdi) +{ + int retval; + int long_stop = 10; + int retry_on_timeout = 5; + int retry_on_empty = 10; + int err_count = 0; + retval = ftdi_elan_flush_input_fifo(ftdi); + if (retval) + return retval; + ftdi->bulk_in_left = 0; + ftdi->bulk_in_last = -1; + while (long_stop-- > 0) { + int read_stop; + int read_stuck; + retval = ftdi_elan_synchronize_flush(ftdi); + if (retval) + return retval; + retval = ftdi_elan_flush_input_fifo(ftdi); + if (retval) + return retval; + reset:retval = ftdi_elan_synchronize_reset(ftdi); + if (retval) + return retval; + read_stop = 100; + read_stuck = 10; + read:{ + int packet_bytes = 0; + retval = usb_bulk_msg(ftdi->udev, + usb_rcvbulkpipe(ftdi->udev, + ftdi->bulk_in_endpointAddr), + ftdi->bulk_in_buffer, ftdi->bulk_in_size, + &packet_bytes, msecs_to_jiffies(500)); + if (packet_bytes > 2) { + char diag[30 *3 + 4]; + char *d = diag; + int m = (sizeof(diag) - 1) / 3; + char *b = ftdi->bulk_in_buffer; + int bytes_read = 0; + unsigned char c = 0; + diag[0] = 0; + while (packet_bytes-- > 0) { + c = *b++; + if (bytes_read < m) { + d += sprintf(d, " %02X", c); + } else if (bytes_read > m) { + } else + d += sprintf(d, " .."); + bytes_read += 1; + continue; + } + if (c == 0x7E) { + return 0; + } else { + if (c == 0x55) { + goto read; + } else if (read_stop-- > 0) { + goto read; + } else { + dev_err(&ftdi->udev->dev, "retr" + "y limit reached\n"); + continue; + } + } + } else if (packet_bytes > 1) { + unsigned char s1 = ftdi->bulk_in_buffer[0]; + unsigned char s2 = ftdi->bulk_in_buffer[1]; + if (s1 == 0x31 && s2 == 0x00) { + if (read_stuck-- > 0) { + goto read; + } else + goto reset; + } else if (s1 == 0x31 && s2 == 0x60) { + if (read_stop-- > 0) { + goto read; + } else { + dev_err(&ftdi->udev->dev, "retr" + "y limit reached\n"); + continue; + } + } else { + if (read_stop-- > 0) { + goto read; + } else { + dev_err(&ftdi->udev->dev, "retr" + "y limit reached\n"); + continue; + } + } + } else if (packet_bytes > 0) { + if (read_stop-- > 0) { + goto read; + } else { + dev_err(&ftdi->udev->dev, "retry limit " + "reached\n"); + continue; + } + } else if (retval == -ETIMEDOUT) { + if (retry_on_timeout-- > 0) { + goto read; + } else { + dev_err(&ftdi->udev->dev, "TIMED OUT re" + "try limit reached\n"); + continue; + } + } else if (retval == 0) { + if (retry_on_empty-- > 0) { + goto read; + } else { + dev_err(&ftdi->udev->dev, "empty packet" + " retry limit reached\n"); + continue; + } + } else { + err_count += 1; + dev_err(&ftdi->udev->dev, "error = %d\n", + retval); + if (read_stop-- > 0) { + goto read; + } else { + dev_err(&ftdi->udev->dev, "retry limit " + "reached\n"); + continue; + } + } + } + } + dev_err(&ftdi->udev->dev, "failed to synchronize\n"); + return -EFAULT; +} + +static int ftdi_elan_stuck_waiting(struct usb_ftdi *ftdi) +{ + int retry_on_empty = 10; + int retry_on_timeout = 5; + int retry_on_status = 50; + more:{ + int packet_bytes = 0; + int retval = usb_bulk_msg(ftdi->udev, + usb_rcvbulkpipe(ftdi->udev, ftdi->bulk_in_endpointAddr), + ftdi->bulk_in_buffer, ftdi->bulk_in_size, + &packet_bytes, msecs_to_jiffies(1000)); + if (packet_bytes > 2) { + char diag[30 *3 + 4]; + char *d = diag; + int m = (sizeof(diag) - 1) / 3; + char *b = ftdi->bulk_in_buffer; + int bytes_read = 0; + diag[0] = 0; + while (packet_bytes-- > 0) { + char c = *b++; + if (bytes_read < m) { + d += sprintf(d, " %02X", + 0x000000FF & c); + } else if (bytes_read > m) { + } else + d += sprintf(d, " .."); + bytes_read += 1; + continue; + } + goto more; + } else if (packet_bytes > 1) { + char s1 = ftdi->bulk_in_buffer[0]; + char s2 = ftdi->bulk_in_buffer[1]; + if (s1 == 0x31 && s2 == 0x60) { + return 0; + } else if (retry_on_status-- > 0) { + msleep(5); + goto more; + } else + return -EFAULT; + } else if (packet_bytes > 0) { + char b1 = ftdi->bulk_in_buffer[0]; + dev_err(&ftdi->udev->dev, "only one byte flushed from F" + "TDI = %02X\n", b1); + if (retry_on_status-- > 0) { + msleep(5); + goto more; + } else { + dev_err(&ftdi->udev->dev, "STATUS ERROR retry l" + "imit reached\n"); + return -EFAULT; + } + } else if (retval == -ETIMEDOUT) { + if (retry_on_timeout-- > 0) { + goto more; + } else { + dev_err(&ftdi->udev->dev, "TIMED OUT retry limi" + "t reached\n"); + return -ENOMEM; + } + } else if (retval == 0) { + if (retry_on_empty-- > 0) { + goto more; + } else { + dev_err(&ftdi->udev->dev, "empty packet retry l" + "imit reached\n"); + return -ENOMEM; + } + } else { + dev_err(&ftdi->udev->dev, "error = %d\n", retval); + return -ENOMEM; + } + } + return -1; +} + +static int ftdi_elan_checkingPCI(struct usb_ftdi *ftdi) +{ + int UxxxStatus = ftdi_elan_read_reg(ftdi, &ftdi->controlreg); + if (UxxxStatus) + return UxxxStatus; + if (ftdi->controlreg & 0x00400000) { + if (ftdi->card_ejected) { + } else { + ftdi->card_ejected = 1; + dev_err(&ftdi->udev->dev, "CARD EJECTED - controlreg = " + "%08X\n", ftdi->controlreg); + } + return -ENODEV; + } else { + u8 fn = ftdi->function - 1; + int activePCIfn = fn << 8; + u32 pcidata; + u32 pciVID; + u32 pciPID; + int reg = 0; + UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0, + &pcidata); + if (UxxxStatus) + return UxxxStatus; + pciVID = pcidata & 0xFFFF; + pciPID = (pcidata >> 16) & 0xFFFF; + if (pciVID == ftdi->platform_data.vendor && pciPID == + ftdi->platform_data.device) { + return 0; + } else { + dev_err(&ftdi->udev->dev, "vendor=%04X pciVID=%04X devi" + "ce=%04X pciPID=%04X\n", + ftdi->platform_data.vendor, pciVID, + ftdi->platform_data.device, pciPID); + return -ENODEV; + } + } +} + +static int ftdi_elan_enumeratePCI(struct usb_ftdi *ftdi) +{ + u32 latence_timer; + u32 controlreg; + int UxxxStatus; + u32 pcidata; + int reg = 0; + int foundOHCI = 0; + u8 fn; + int activePCIfn = 0; + u32 pciVID = 0; + u32 pciPID = 0; + UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg); + if (UxxxStatus) + return UxxxStatus; + UxxxStatus = ftdi_elan_write_reg(ftdi, 0x00000000L); + if (UxxxStatus) + return UxxxStatus; + msleep(750); + UxxxStatus = ftdi_elan_write_reg(ftdi, 0x00000200L | 0x100); + if (UxxxStatus) + return UxxxStatus; + UxxxStatus = ftdi_elan_write_reg(ftdi, 0x00000200L | 0x500); + if (UxxxStatus) + return UxxxStatus; + UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg); + if (UxxxStatus) + return UxxxStatus; + UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000020CL | 0x000); + if (UxxxStatus) + return UxxxStatus; + UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000020DL | 0x000); + if (UxxxStatus) + return UxxxStatus; + msleep(250); + UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000020FL | 0x000); + if (UxxxStatus) + return UxxxStatus; + UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg); + if (UxxxStatus) + return UxxxStatus; + UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000025FL | 0x800); + if (UxxxStatus) + return UxxxStatus; + UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg); + if (UxxxStatus) + return UxxxStatus; + UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg); + if (UxxxStatus) + return UxxxStatus; + msleep(1000); + for (fn = 0; (fn < 4) && (!foundOHCI); fn++) { + activePCIfn = fn << 8; + ftdi->function = fn + 1; + UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0, + &pcidata); + if (UxxxStatus) + return UxxxStatus; + pciVID = pcidata & 0xFFFF; + pciPID = (pcidata >> 16) & 0xFFFF; + if ((pciVID == 0x1045) && (pciPID == 0xc861)) { + foundOHCI = 1; + } else if ((pciVID == 0x1033) && (pciPID == 0x0035)) { + foundOHCI = 1; + } else if ((pciVID == 0x10b9) && (pciPID == 0x5237)) { + foundOHCI = 1; + } else if ((pciVID == 0x11c1) && (pciPID == 0x5802)) { + foundOHCI = 1; + } else if ((pciVID == 0x11AB) && (pciPID == 0x1FA6)) { + } + } + if (foundOHCI == 0) { + return -ENXIO; + } + ftdi->platform_data.vendor = pciVID; + ftdi->platform_data.device = pciPID; + UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000025FL | 0x2800); + if (UxxxStatus) + return UxxxStatus; + reg = 16; + UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0, + 0xFFFFFFFF); + if (UxxxStatus) + return UxxxStatus; + UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0, + &pcidata); + if (UxxxStatus) + return UxxxStatus; + UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0, + 0xF0000000); + if (UxxxStatus) + return UxxxStatus; + UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0, + &pcidata); + if (UxxxStatus) + return UxxxStatus; + reg = 12; + UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0, + &latence_timer); + if (UxxxStatus) + return UxxxStatus; + latence_timer &= 0xFFFF00FF; + latence_timer |= 0x00001600; + UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0x00, + latence_timer); + if (UxxxStatus) + return UxxxStatus; + UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0, + &pcidata); + if (UxxxStatus) + return UxxxStatus; + reg = 4; + UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0x00, + 0x06); + if (UxxxStatus) + return UxxxStatus; + UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0, + &pcidata); + if (UxxxStatus) + return UxxxStatus; + return 0; +} + +static int ftdi_elan_setupOHCI(struct usb_ftdi *ftdi) +{ + u32 pcidata; + int U132Status; + int reg; + int reset_repeat = 0; + do_reset:reg = 8; + U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x0e, 0x01); + if (U132Status) + return U132Status; + reset_check:{ + U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata); + if (U132Status) + return U132Status; + if (pcidata & 1) { + msleep(500); + if (reset_repeat++ > 100) { + reset_repeat = 0; + goto do_reset; + } else + goto reset_check; + } + } + goto dump_regs; + msleep(500); + reg = 0x28; + U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x11000000); + if (U132Status) + return U132Status; + U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata); + if (U132Status) + return U132Status; + reg = 0x40; + U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x2edf); + if (U132Status) + return U132Status; + U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata); + if (U132Status) + return U132Status; + reg = 0x34; + U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x2edf2edf); + if (U132Status) + return U132Status; + U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata); + if (U132Status) + return U132Status; + reg = 4; + U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0xA0); + if (U132Status) + return U132Status; + U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata); + if (U132Status) + return U132Status; + msleep(250); + reg = 8; + U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x0e, 0x04); + if (U132Status) + return U132Status; + U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata); + if (U132Status) + return U132Status; + reg = 0x28; + U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata); + if (U132Status) + return U132Status; + reg = 8; + U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata); + if (U132Status) + return U132Status; + reg = 0x48; + U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x00001200); + if (U132Status) + return U132Status; + U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata); + if (U132Status) + return U132Status; + reg = 0x54; + U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata); + if (U132Status) + return U132Status; + reg = 0x58; + U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata); + if (U132Status) + return U132Status; + reg = 0x34; + U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x28002edf); + if (U132Status) + return U132Status; + U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata); + if (U132Status) + return U132Status; + msleep(100); + reg = 0x50; + U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x10000); + if (U132Status) + return U132Status; + reg = 0x54; + power_check:U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata); + if (U132Status) + return U132Status; + if (!(pcidata & 1)) { + msleep(500); + goto power_check; + } + msleep(3000); + reg = 0x54; + U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata); + if (U132Status) + return U132Status; + reg = 0x58; + U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata); + if (U132Status) + return U132Status; + reg = 0x54; + U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x02); + if (U132Status) + return U132Status; + U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata); + if (U132Status) + return U132Status; + reg = 0x54; + U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x10); + if (U132Status) + return U132Status; + U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata); + if (U132Status) + return U132Status; + msleep(750); + reg = 0x54; + if (0) { + U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x02); + if (U132Status) + return U132Status; + } + if (0) { + U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata); + if (U132Status) + return U132Status; + } + reg = 0x54; + U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata); + if (U132Status) + return U132Status; + reg = 0x58; + U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata); + if (U132Status) + return U132Status; + dump_regs:for (reg = 0; reg <= 0x54; reg += 4) { + U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata); + if (U132Status) + return U132Status; + } + return 0; +} + + +/* +* we use only the first bulk-in and bulk-out endpoints +*/ +static int ftdi_elan_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + size_t buffer_size; + int i; + int retval = -ENOMEM; + struct usb_ftdi *ftdi = kmalloc(sizeof(struct usb_ftdi), GFP_KERNEL); + if (ftdi == NULL) { + printk(KERN_ERR "Out of memory\n"); + return -ENOMEM; + } + memset(ftdi, 0x00, sizeof(struct usb_ftdi)); + down(&ftdi_module_lock); + list_add_tail(&ftdi->ftdi_list, &ftdi_static_list); + ftdi->sequence_num = ++ftdi_instances; + up(&ftdi_module_lock); + ftdi_elan_init_kref(ftdi); + init_MUTEX(&ftdi->sw_lock); + ftdi->udev = usb_get_dev(interface_to_usbdev(interface)); + ftdi->interface = interface; + init_MUTEX(&ftdi->u132_lock); + ftdi->expected = 4; + iface_desc = interface->cur_altsetting; + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + if (!ftdi->bulk_in_endpointAddr && + ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + == USB_DIR_IN) && ((endpoint->bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)) + { + buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + ftdi->bulk_in_size = buffer_size; + ftdi->bulk_in_endpointAddr = endpoint->bEndpointAddress; + ftdi->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); + if (!ftdi->bulk_in_buffer) { + dev_err(&ftdi->udev->dev, "Could not allocate b" + "ulk_in_buffer\n"); + retval = -ENOMEM; + goto error; + } + } + if (!ftdi->bulk_out_endpointAddr && + ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + == USB_DIR_OUT) && ((endpoint->bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)) + { + ftdi->bulk_out_endpointAddr = + endpoint->bEndpointAddress; + } + } + if (!(ftdi->bulk_in_endpointAddr && ftdi->bulk_out_endpointAddr)) { + dev_err(&ftdi->udev->dev, "Could not find both bulk-in and bulk" + "-out endpoints\n"); + retval = -ENODEV; + goto error; + } + dev_info(&ftdi->udev->dev, "interface %d has I=%02X O=%02X\n", + iface_desc->desc.bInterfaceNumber, ftdi->bulk_in_endpointAddr, + ftdi->bulk_out_endpointAddr); + usb_set_intfdata(interface, ftdi); + if (iface_desc->desc.bInterfaceNumber == 0 && + ftdi->bulk_in_endpointAddr == 0x81 && + ftdi->bulk_out_endpointAddr == 0x02) { + retval = usb_register_dev(interface, &ftdi_elan_jtag_class); + if (retval) { + dev_err(&ftdi->udev->dev, "Not able to get a minor for " + "this device.\n"); + usb_set_intfdata(interface, NULL); + retval = -ENOMEM; + goto error; + } else { + ftdi->class = &ftdi_elan_jtag_class; + dev_info(&ftdi->udev->dev, "USB FDTI=%p JTAG interface " + "%d now attached to ftdi%d\n", ftdi, + iface_desc->desc.bInterfaceNumber, + interface->minor); + return 0; + } + } else if (iface_desc->desc.bInterfaceNumber == 1 && + ftdi->bulk_in_endpointAddr == 0x83 && + ftdi->bulk_out_endpointAddr == 0x04) { + ftdi->class = NULL; + dev_info(&ftdi->udev->dev, "USB FDTI=%p ELAN interface %d now a" + "ctivated\n", ftdi, iface_desc->desc.bInterfaceNumber); + INIT_WORK(&ftdi->status_work, ftdi_elan_status_work, + (void *)ftdi); + INIT_WORK(&ftdi->command_work, ftdi_elan_command_work, + (void *)ftdi); + INIT_WORK(&ftdi->respond_work, ftdi_elan_respond_work, + (void *)ftdi); + ftdi_status_queue_work(ftdi, msecs_to_jiffies(3 *1000)); + return 0; + } else { + dev_err(&ftdi->udev->dev, + "Could not find ELAN's U132 device\n"); + retval = -ENODEV; + goto error; + } + error:if (ftdi) { + ftdi_elan_put_kref(ftdi); + } + return retval; +} + +static void ftdi_elan_disconnect(struct usb_interface *interface) +{ + struct usb_ftdi *ftdi = usb_get_intfdata(interface); + ftdi->disconnected += 1; + if (ftdi->class) { + int minor = interface->minor; + struct usb_class_driver *class = ftdi->class; + usb_set_intfdata(interface, NULL); + usb_deregister_dev(interface, class); + dev_info(&ftdi->udev->dev, "USB FTDI U132 jtag interface on min" + "or %d now disconnected\n", minor); + } else { + ftdi_status_cancel_work(ftdi); + ftdi_command_cancel_work(ftdi); + ftdi_response_cancel_work(ftdi); + ftdi_elan_abandon_completions(ftdi); + ftdi_elan_abandon_targets(ftdi); + if (ftdi->registered) { + platform_device_unregister(&ftdi->platform_dev); + ftdi->synchronized = 0; + ftdi->enumerated = 0; + ftdi->registered = 0; + } + flush_workqueue(status_queue); + flush_workqueue(command_queue); + flush_workqueue(respond_queue); + ftdi->disconnected += 1; + usb_set_intfdata(interface, NULL); + dev_info(&ftdi->udev->dev, "USB FTDI U132 host controller inter" + "face now disconnected\n"); + } + ftdi_elan_put_kref(ftdi); +} + +static struct usb_driver ftdi_elan_driver = { + .name = "ftdi-elan", + .probe = ftdi_elan_probe, + .disconnect = ftdi_elan_disconnect, + .id_table = ftdi_elan_table, +}; +static int __init ftdi_elan_init(void) +{ + int result; + printk(KERN_INFO "driver %s built at %s on %s\n", ftdi_elan_driver.name, + __TIME__, __DATE__); + init_MUTEX(&ftdi_module_lock); + INIT_LIST_HEAD(&ftdi_static_list); + status_queue = create_singlethread_workqueue("ftdi-status-control"); + command_queue = create_singlethread_workqueue("ftdi-command-engine"); + respond_queue = create_singlethread_workqueue("ftdi-respond-engine"); + result = usb_register(&ftdi_elan_driver); + if (result) + printk(KERN_ERR "usb_register failed. Error number %d\n", + result); + return result; +} + +static void __exit ftdi_elan_exit(void) +{ + struct usb_ftdi *ftdi; + struct usb_ftdi *temp; + usb_deregister(&ftdi_elan_driver); + printk(KERN_INFO "ftdi_u132 driver deregistered\n"); + list_for_each_entry_safe(ftdi, temp, &ftdi_static_list, ftdi_list) { + ftdi_status_cancel_work(ftdi); + ftdi_command_cancel_work(ftdi); + ftdi_response_cancel_work(ftdi); + } flush_workqueue(status_queue); + destroy_workqueue(status_queue); + status_queue = NULL; + flush_workqueue(command_queue); + destroy_workqueue(command_queue); + command_queue = NULL; + flush_workqueue(respond_queue); + destroy_workqueue(respond_queue); + respond_queue = NULL; +} + + +module_init(ftdi_elan_init); +module_exit(ftdi_elan_exit); diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index fcd69c5..8e6e195 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -98,7 +98,7 @@ static int idmouse_probe(struct usb_interface *interface, static void idmouse_disconnect(struct usb_interface *interface); /* file operation pointers */ -static struct file_operations idmouse_fops = { +static const struct file_operations idmouse_fops = { .owner = THIS_MODULE, .read = idmouse_read, .open = idmouse_open, diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index f30ab1f..10b6403 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -589,7 +589,7 @@ exit: } /* file operations needed when we register this driver */ -static struct file_operations ld_usb_fops = { +static const struct file_operations ld_usb_fops = { .owner = THIS_MODULE, .read = ld_usb_read, .write = ld_usb_write, @@ -657,15 +657,11 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id * for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; - if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) && - ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) { + if (usb_endpoint_is_int_in(endpoint)) dev->interrupt_in_endpoint = endpoint; - } - if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) && - ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) { + if (usb_endpoint_is_int_out(endpoint)) dev->interrupt_out_endpoint = endpoint; - } } if (dev->interrupt_in_endpoint == NULL) { dev_err(&intf->dev, "Interrupt in endpoint not found\n"); diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 7699d97..77c36e6 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -259,7 +259,7 @@ static void tower_disconnect (struct usb_interface *interface); static DEFINE_MUTEX (disconnect_mutex); /* file operations needed when we register this driver */ -static struct file_operations tower_fops = { +static const struct file_operations tower_fops = { .owner = THIS_MODULE, .read = tower_read, .write = tower_write, diff --git a/drivers/usb/misc/phidget.c b/drivers/usb/misc/phidget.c new file mode 100644 index 0000000..735ed33 --- /dev/null +++ b/drivers/usb/misc/phidget.c @@ -0,0 +1,43 @@ +/* + * USB Phidgets class + * + * Copyright (C) 2006 Sean Young <sean@mess.org> + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/device.h> + +struct class *phidget_class; + +static int __init init_phidget(void) +{ + phidget_class = class_create(THIS_MODULE, "phidget"); + + if (IS_ERR(phidget_class)) + return PTR_ERR(phidget_class); + + return 0; +} + +static void __exit cleanup_phidget(void) +{ + class_destroy(phidget_class); +} + +EXPORT_SYMBOL_GPL(phidget_class); + +module_init(init_phidget); +module_exit(cleanup_phidget); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Sean Young <sean@mess.org>"); +MODULE_DESCRIPTION("Container module for phidget class"); + diff --git a/drivers/usb/misc/phidget.h b/drivers/usb/misc/phidget.h new file mode 100644 index 0000000..c401190 --- /dev/null +++ b/drivers/usb/misc/phidget.h @@ -0,0 +1,12 @@ +/* + * USB Phidgets class + * + * Copyright (C) 2006 Sean Young <sean@mess.org> + * + * 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. + */ + +extern struct class *phidget_class; diff --git a/drivers/usb/misc/phidgetkit.c b/drivers/usb/misc/phidgetkit.c index bfbbbfb..9a8d137 100644 --- a/drivers/usb/misc/phidgetkit.c +++ b/drivers/usb/misc/phidgetkit.c @@ -20,6 +20,8 @@ #include <linux/module.h> #include <linux/usb.h> +#include "phidget.h" + #define DRIVER_AUTHOR "Sean Young <sean@mess.org>" #define DRIVER_DESC "USB PhidgetInterfaceKit Driver" @@ -57,11 +59,15 @@ ifkit(8, 8, 4, 0); ifkit(0, 8, 8, 1); ifkit(0, 16, 16, 0); +static unsigned long device_no; + struct interfacekit { struct usb_device *udev; struct usb_interface *intf; struct driver_interfacekit *ifkit; + struct device *dev; unsigned long outputs; + int dev_no; u8 inputs[MAX_INTERFACES]; u16 sensors[MAX_INTERFACES]; u8 lcd_files_on; @@ -180,21 +186,24 @@ exit: } #define set_lcd_line(number) \ -static ssize_t lcd_line_##number(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ -{ \ - struct usb_interface *intf = to_usb_interface(dev); \ - struct interfacekit *kit = usb_get_intfdata(intf); \ - change_string(kit, buf, number - 1); \ - return count; \ -} \ -static DEVICE_ATTR(lcd_line_##number, S_IWUGO, NULL, lcd_line_##number); +static ssize_t lcd_line_##number(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + struct interfacekit *kit = dev_get_drvdata(dev); \ + change_string(kit, buf, number - 1); \ + return count; \ +} + +#define lcd_line_attr(number) \ + __ATTR(lcd_line_##number, S_IWUGO, NULL, lcd_line_##number) + set_lcd_line(1); set_lcd_line(2); static ssize_t set_backlight(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct usb_interface *intf = to_usb_interface(dev); - struct interfacekit *kit = usb_get_intfdata(intf); + struct interfacekit *kit = dev_get_drvdata(dev); int enabled; unsigned char *buffer; int retval = -ENOMEM; @@ -226,23 +235,30 @@ exit: kfree(buffer); return retval; } -static DEVICE_ATTR(backlight, S_IWUGO, NULL, set_backlight); + +static struct device_attribute dev_lcd_line_attrs[] = { + lcd_line_attr(1), + lcd_line_attr(2), + __ATTR(backlight, S_IWUGO, NULL, set_backlight) +}; static void remove_lcd_files(struct interfacekit *kit) { + int i; + if (kit->lcd_files_on) { dev_dbg(&kit->udev->dev, "Removing lcd files\n"); - device_remove_file(&kit->intf->dev, &dev_attr_lcd_line_1); - device_remove_file(&kit->intf->dev, &dev_attr_lcd_line_2); - device_remove_file(&kit->intf->dev, &dev_attr_backlight); + + for (i=0; i<ARRAY_SIZE(dev_lcd_line_attrs); i++) + device_remove_file(kit->dev, &dev_lcd_line_attrs[i]); } } static ssize_t enable_lcd_files(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct usb_interface *intf = to_usb_interface(dev); - struct interfacekit *kit = usb_get_intfdata(intf); + struct interfacekit *kit = dev_get_drvdata(dev); int enable; + int i, rc; if (kit->ifkit->has_lcd == 0) return -ENODEV; @@ -253,9 +269,12 @@ static ssize_t enable_lcd_files(struct device *dev, struct device_attribute *att if (enable) { if (!kit->lcd_files_on) { dev_dbg(&kit->udev->dev, "Adding lcd files\n"); - device_create_file(&kit->intf->dev, &dev_attr_lcd_line_1); - device_create_file(&kit->intf->dev, &dev_attr_lcd_line_2); - device_create_file(&kit->intf->dev, &dev_attr_backlight); + for (i=0; i<ARRAY_SIZE(dev_lcd_line_attrs); i++) { + rc = device_create_file(kit->dev, + &dev_lcd_line_attrs[i]); + if (rc) + goto out; + } kit->lcd_files_on = 1; } } else { @@ -266,7 +285,13 @@ static ssize_t enable_lcd_files(struct device *dev, struct device_attribute *att } return count; +out: + while (i-- > 0) + device_remove_file(kit->dev, &dev_lcd_line_attrs[i]); + + return rc; } + static DEVICE_ATTR(lcd, S_IWUGO, NULL, enable_lcd_files); static void interfacekit_irq(struct urb *urb, struct pt_regs *regs) @@ -362,24 +387,24 @@ static void do_notify(void *data) for (i=0; i<kit->ifkit->inputs; i++) { if (test_and_clear_bit(i, &kit->input_events)) { sprintf(sysfs_file, "input%d", i + 1); - sysfs_notify(&kit->intf->dev.kobj, NULL, sysfs_file); + sysfs_notify(&kit->dev->kobj, NULL, sysfs_file); } } for (i=0; i<kit->ifkit->sensors; i++) { if (test_and_clear_bit(i, &kit->sensor_events)) { sprintf(sysfs_file, "sensor%d", i + 1); - sysfs_notify(&kit->intf->dev.kobj, NULL, sysfs_file); + sysfs_notify(&kit->dev->kobj, NULL, sysfs_file); } } } #define show_set_output(value) \ -static ssize_t set_output##value(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ +static ssize_t set_output##value(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ { \ - struct usb_interface *intf = to_usb_interface(dev); \ - struct interfacekit *kit = usb_get_intfdata(intf); \ + struct interfacekit *kit = dev_get_drvdata(dev); \ int enabled; \ int retval; \ \ @@ -391,15 +416,19 @@ static ssize_t set_output##value(struct device *dev, struct device_attribute *at return retval ? retval : count; \ } \ \ -static ssize_t show_output##value(struct device *dev, struct device_attribute *attr, char *buf) \ +static ssize_t show_output##value(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ { \ - struct usb_interface *intf = to_usb_interface(dev); \ - struct interfacekit *kit = usb_get_intfdata(intf); \ + struct interfacekit *kit = dev_get_drvdata(dev); \ \ return sprintf(buf, "%d\n", !!test_bit(value - 1, &kit->outputs));\ -} \ -static DEVICE_ATTR(output##value, S_IWUGO | S_IRUGO, \ - show_output##value, set_output##value); +} + +#define output_attr(value) \ + __ATTR(output##value, S_IWUGO | S_IRUGO, \ + show_output##value, set_output##value) + show_set_output(1); show_set_output(2); show_set_output(3); @@ -417,15 +446,24 @@ show_set_output(14); show_set_output(15); show_set_output(16); +static struct device_attribute dev_output_attrs[] = { + output_attr(1), output_attr(2), output_attr(3), output_attr(4), + output_attr(5), output_attr(6), output_attr(7), output_attr(8), + output_attr(9), output_attr(10), output_attr(11), output_attr(12), + output_attr(13), output_attr(14), output_attr(15), output_attr(16) +}; + #define show_input(value) \ -static ssize_t show_input##value(struct device *dev, struct device_attribute *attr, char *buf) \ +static ssize_t show_input##value(struct device *dev, \ + struct device_attribute *attr, char *buf) \ { \ - struct usb_interface *intf = to_usb_interface(dev); \ - struct interfacekit *kit = usb_get_intfdata(intf); \ + struct interfacekit *kit = dev_get_drvdata(dev); \ \ return sprintf(buf, "%d\n", (int)kit->inputs[value - 1]); \ -} \ -static DEVICE_ATTR(input##value, S_IRUGO, show_input##value, NULL); +} + +#define input_attr(value) \ + __ATTR(input##value, S_IRUGO, show_input##value, NULL) show_input(1); show_input(2); @@ -444,15 +482,25 @@ show_input(14); show_input(15); show_input(16); +static struct device_attribute dev_input_attrs[] = { + input_attr(1), input_attr(2), input_attr(3), input_attr(4), + input_attr(5), input_attr(6), input_attr(7), input_attr(8), + input_attr(9), input_attr(10), input_attr(11), input_attr(12), + input_attr(13), input_attr(14), input_attr(15), input_attr(16) +}; + #define show_sensor(value) \ -static ssize_t show_sensor##value(struct device *dev, struct device_attribute *attr, char *buf) \ +static ssize_t show_sensor##value(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ { \ - struct usb_interface *intf = to_usb_interface(dev); \ - struct interfacekit *kit = usb_get_intfdata(intf); \ + struct interfacekit *kit = dev_get_drvdata(dev); \ \ return sprintf(buf, "%d\n", (int)kit->sensors[value - 1]); \ -} \ -static DEVICE_ATTR(sensor##value, S_IRUGO, show_sensor##value, NULL); +} + +#define sensor_attr(value) \ + __ATTR(sensor##value, S_IRUGO, show_sensor##value, NULL) show_sensor(1); show_sensor(2); @@ -463,6 +511,11 @@ show_sensor(6); show_sensor(7); show_sensor(8); +static struct device_attribute dev_sensor_attrs[] = { + sensor_attr(1), sensor_attr(2), sensor_attr(3), sensor_attr(4), + sensor_attr(5), sensor_attr(6), sensor_attr(7), sensor_attr(8) +}; + static int interfacekit_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *dev = interface_to_usbdev(intf); @@ -471,6 +524,7 @@ static int interfacekit_probe(struct usb_interface *intf, const struct usb_devic struct interfacekit *kit; struct driver_interfacekit *ifkit; int pipe, maxp, rc = -ENOMEM; + int bit, value, i; ifkit = (struct driver_interfacekit *)id->driver_info; if (!ifkit) @@ -493,6 +547,7 @@ static int interfacekit_probe(struct usb_interface *intf, const struct usb_devic if (!kit) goto out; + kit->dev_no = -1; kit->ifkit = ifkit; kit->data = usb_buffer_alloc(dev, URB_INT_SIZE, SLAB_ATOMIC, &kit->data_dma); if (!kit->data) @@ -513,85 +568,80 @@ static int interfacekit_probe(struct usb_interface *intf, const struct usb_devic usb_set_intfdata(intf, kit); + do { + bit = find_first_zero_bit(&device_no, sizeof(device_no)); + value = test_and_set_bit(bit, &device_no); + } while(value); + kit->dev_no = bit; + + kit->dev = device_create(phidget_class, &kit->udev->dev, 0, + "interfacekit%d", kit->dev_no); + if (IS_ERR(kit->dev)) { + rc = PTR_ERR(kit->dev); + kit->dev = NULL; + goto out; + } + dev_set_drvdata(kit->dev, kit); + if (usb_submit_urb(kit->irq, GFP_KERNEL)) { rc = -EIO; goto out; } - if (ifkit->outputs >= 4) { - device_create_file(&intf->dev, &dev_attr_output1); - device_create_file(&intf->dev, &dev_attr_output2); - device_create_file(&intf->dev, &dev_attr_output3); - device_create_file(&intf->dev, &dev_attr_output4); - } - if (ifkit->outputs >= 8) { - device_create_file(&intf->dev, &dev_attr_output5); - device_create_file(&intf->dev, &dev_attr_output6); - device_create_file(&intf->dev, &dev_attr_output7); - device_create_file(&intf->dev, &dev_attr_output8); - } - if (ifkit->outputs == 16) { - device_create_file(&intf->dev, &dev_attr_output9); - device_create_file(&intf->dev, &dev_attr_output10); - device_create_file(&intf->dev, &dev_attr_output11); - device_create_file(&intf->dev, &dev_attr_output12); - device_create_file(&intf->dev, &dev_attr_output13); - device_create_file(&intf->dev, &dev_attr_output14); - device_create_file(&intf->dev, &dev_attr_output15); - device_create_file(&intf->dev, &dev_attr_output16); + for (i=0; i<ifkit->outputs; i++ ) { + rc = device_create_file(kit->dev, &dev_output_attrs[i]); + if (rc) + goto out2; } - if (ifkit->inputs >= 4) { - device_create_file(&intf->dev, &dev_attr_input1); - device_create_file(&intf->dev, &dev_attr_input2); - device_create_file(&intf->dev, &dev_attr_input3); - device_create_file(&intf->dev, &dev_attr_input4); - } - if (ifkit->inputs >= 8) { - device_create_file(&intf->dev, &dev_attr_input5); - device_create_file(&intf->dev, &dev_attr_input6); - device_create_file(&intf->dev, &dev_attr_input7); - device_create_file(&intf->dev, &dev_attr_input8); - } - if (ifkit->inputs == 16) { - device_create_file(&intf->dev, &dev_attr_input9); - device_create_file(&intf->dev, &dev_attr_input10); - device_create_file(&intf->dev, &dev_attr_input11); - device_create_file(&intf->dev, &dev_attr_input12); - device_create_file(&intf->dev, &dev_attr_input13); - device_create_file(&intf->dev, &dev_attr_input14); - device_create_file(&intf->dev, &dev_attr_input15); - device_create_file(&intf->dev, &dev_attr_input16); + for (i=0; i<ifkit->inputs; i++ ) { + rc = device_create_file(kit->dev, &dev_input_attrs[i]); + if (rc) + goto out3; } - if (ifkit->sensors >= 4) { - device_create_file(&intf->dev, &dev_attr_sensor1); - device_create_file(&intf->dev, &dev_attr_sensor2); - device_create_file(&intf->dev, &dev_attr_sensor3); - device_create_file(&intf->dev, &dev_attr_sensor4); - } - if (ifkit->sensors >= 7) { - device_create_file(&intf->dev, &dev_attr_sensor5); - device_create_file(&intf->dev, &dev_attr_sensor6); - device_create_file(&intf->dev, &dev_attr_sensor7); + for (i=0; i<ifkit->sensors; i++ ) { + rc = device_create_file(kit->dev, &dev_sensor_attrs[i]); + if (rc) + goto out4; } - if (ifkit->sensors == 8) - device_create_file(&intf->dev, &dev_attr_sensor8); - if (ifkit->has_lcd) - device_create_file(&intf->dev, &dev_attr_lcd); + if (ifkit->has_lcd) { + rc = device_create_file(kit->dev, &dev_attr_lcd); + if (rc) + goto out4; + + } dev_info(&intf->dev, "USB PhidgetInterfaceKit %d/%d/%d attached\n", ifkit->sensors, ifkit->inputs, ifkit->outputs); return 0; +out4: + while (i-- > 0) + device_remove_file(kit->dev, &dev_sensor_attrs[i]); + + i = ifkit->inputs; +out3: + while (i-- > 0) + device_remove_file(kit->dev, &dev_input_attrs[i]); + + i = ifkit->outputs; +out2: + while (i-- > 0) + device_remove_file(kit->dev, &dev_output_attrs[i]); out: if (kit) { if (kit->irq) usb_free_urb(kit->irq); if (kit->data) usb_buffer_free(dev, URB_INT_SIZE, kit->data, kit->data_dma); + if (kit->dev) + device_unregister(kit->dev); + if (kit->dev_no >= 0) + clear_bit(kit->dev_no, &device_no); + kfree(kit); } @@ -601,6 +651,7 @@ out: static void interfacekit_disconnect(struct usb_interface *interface) { struct interfacekit *kit; + int i; kit = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); @@ -613,73 +664,28 @@ static void interfacekit_disconnect(struct usb_interface *interface) cancel_delayed_work(&kit->do_notify); - if (kit->ifkit->outputs >= 4) { - device_remove_file(&interface->dev, &dev_attr_output1); - device_remove_file(&interface->dev, &dev_attr_output2); - device_remove_file(&interface->dev, &dev_attr_output3); - device_remove_file(&interface->dev, &dev_attr_output4); - } - if (kit->ifkit->outputs >= 8) { - device_remove_file(&interface->dev, &dev_attr_output5); - device_remove_file(&interface->dev, &dev_attr_output6); - device_remove_file(&interface->dev, &dev_attr_output7); - device_remove_file(&interface->dev, &dev_attr_output8); - } - if (kit->ifkit->outputs == 16) { - device_remove_file(&interface->dev, &dev_attr_output9); - device_remove_file(&interface->dev, &dev_attr_output10); - device_remove_file(&interface->dev, &dev_attr_output11); - device_remove_file(&interface->dev, &dev_attr_output12); - device_remove_file(&interface->dev, &dev_attr_output13); - device_remove_file(&interface->dev, &dev_attr_output14); - device_remove_file(&interface->dev, &dev_attr_output15); - device_remove_file(&interface->dev, &dev_attr_output16); - } + for (i=0; i<kit->ifkit->outputs; i++) + device_remove_file(kit->dev, &dev_output_attrs[i]); - if (kit->ifkit->inputs >= 4) { - device_remove_file(&interface->dev, &dev_attr_input1); - device_remove_file(&interface->dev, &dev_attr_input2); - device_remove_file(&interface->dev, &dev_attr_input3); - device_remove_file(&interface->dev, &dev_attr_input4); - } - if (kit->ifkit->inputs >= 8) { - device_remove_file(&interface->dev, &dev_attr_input5); - device_remove_file(&interface->dev, &dev_attr_input6); - device_remove_file(&interface->dev, &dev_attr_input7); - device_remove_file(&interface->dev, &dev_attr_input8); - } - if (kit->ifkit->inputs == 16) { - device_remove_file(&interface->dev, &dev_attr_input9); - device_remove_file(&interface->dev, &dev_attr_input10); - device_remove_file(&interface->dev, &dev_attr_input11); - device_remove_file(&interface->dev, &dev_attr_input12); - device_remove_file(&interface->dev, &dev_attr_input13); - device_remove_file(&interface->dev, &dev_attr_input14); - device_remove_file(&interface->dev, &dev_attr_input15); - device_remove_file(&interface->dev, &dev_attr_input16); - } + for (i=0; i<kit->ifkit->inputs; i++) + device_remove_file(kit->dev, &dev_input_attrs[i]); - if (kit->ifkit->sensors >= 4) { - device_remove_file(&interface->dev, &dev_attr_sensor1); - device_remove_file(&interface->dev, &dev_attr_sensor2); - device_remove_file(&interface->dev, &dev_attr_sensor3); - device_remove_file(&interface->dev, &dev_attr_sensor4); - } - if (kit->ifkit->sensors >= 7) { - device_remove_file(&interface->dev, &dev_attr_sensor5); - device_remove_file(&interface->dev, &dev_attr_sensor6); - device_remove_file(&interface->dev, &dev_attr_sensor7); + for (i=0; i<kit->ifkit->sensors; i++) + device_remove_file(kit->dev, &dev_sensor_attrs[i]); + + if (kit->ifkit->has_lcd) { + device_remove_file(kit->dev, &dev_attr_lcd); + remove_lcd_files(kit); } - if (kit->ifkit->sensors == 8) - device_remove_file(&interface->dev, &dev_attr_sensor8); - if (kit->ifkit->has_lcd) - device_remove_file(&interface->dev, &dev_attr_lcd); + device_unregister(kit->dev); dev_info(&interface->dev, "USB PhidgetInterfaceKit %d/%d/%d detached\n", kit->ifkit->sensors, kit->ifkit->inputs, kit->ifkit->outputs); usb_put_dev(kit->udev); + clear_bit(kit->dev_no, &device_no); + kfree(kit); } diff --git a/drivers/usb/misc/phidgetmotorcontrol.c b/drivers/usb/misc/phidgetmotorcontrol.c new file mode 100644 index 0000000..6b59b62 --- /dev/null +++ b/drivers/usb/misc/phidgetmotorcontrol.c @@ -0,0 +1,466 @@ +/* + * USB Phidget MotorControl driver + * + * Copyright (C) 2006 Sean Young <sean@mess.org> + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/usb.h> + +#include "phidget.h" + +#define DRIVER_AUTHOR "Sean Young <sean@mess.org>" +#define DRIVER_DESC "USB PhidgetMotorControl Driver" + +#define USB_VENDOR_ID_GLAB 0x06c2 +#define USB_DEVICE_ID_MOTORCONTROL 0x0058 + +#define URB_INT_SIZE 8 + +static unsigned long device_no; + +struct motorcontrol { + struct usb_device *udev; + struct usb_interface *intf; + struct device *dev; + int dev_no; + u8 inputs[4]; + s8 desired_speed[2]; + s8 speed[2]; + s16 _current[2]; + s8 acceleration[2]; + struct urb *irq; + unsigned char *data; + dma_addr_t data_dma; + + struct work_struct do_notify; + unsigned long input_events; + unsigned long speed_events; + unsigned long exceed_events; +}; + +static struct usb_device_id id_table[] = { + { USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_MOTORCONTROL) }, + {} +}; +MODULE_DEVICE_TABLE(usb, id_table); + +static int set_motor(struct motorcontrol *mc, int motor) +{ + u8 *buffer; + int speed, speed2, acceleration; + int retval; + + buffer = kzalloc(8, GFP_KERNEL); + if (!buffer) { + dev_err(&mc->intf->dev, "%s - out of memory\n", __FUNCTION__); + return -ENOMEM; + } + + acceleration = mc->acceleration[motor] * 10; + /* -127 <= speed <= 127 */ + speed = (mc->desired_speed[motor] * 127) / 100; + /* -0x7300 <= speed2 <= 0x7300 */ + speed2 = (mc->desired_speed[motor] * 230 * 128) / 100; + + buffer[0] = motor; + buffer[1] = speed; + buffer[2] = acceleration >> 8; + buffer[3] = acceleration; + buffer[4] = speed2 >> 8; + buffer[5] = speed2; + + retval = usb_control_msg(mc->udev, + usb_sndctrlpipe(mc->udev, 0), + 0x09, 0x21, 0x0200, 0x0000, buffer, 8, 2000); + + if (retval != 8) + dev_err(&mc->intf->dev, "usb_control_msg returned %d\n", + retval); + kfree(buffer); + + return retval < 0 ? retval : 0; +} + +static void motorcontrol_irq(struct urb *urb, struct pt_regs *regs) +{ + struct motorcontrol *mc = urb->context; + unsigned char *buffer = mc->data; + int i, level; + 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; + } + + /* digital inputs */ + for (i=0; i<4; i++) { + level = (buffer[0] >> i) & 1; + if (mc->inputs[i] != level) { + mc->inputs[i] = level; + set_bit(i, &mc->input_events); + } + } + + /* motor speed */ + if (buffer[2] == 0) { + for (i=0; i<2; i++) { + level = ((s8)buffer[4+i]) * 100 / 127; + if (mc->speed[i] != level) { + mc->speed[i] = level; + set_bit(i, &mc->speed_events); + } + } + } else { + int index = buffer[3] & 1; + + level = ((s8)buffer[4] << 8) | buffer[5]; + level = level * 100 / 29440; + if (mc->speed[index] != level) { + mc->speed[index] = level; + set_bit(index, &mc->speed_events); + } + + level = ((s8)buffer[6] << 8) | buffer[7]; + mc->_current[index] = level * 100 / 1572; + } + + if (buffer[1] & 1) + set_bit(0, &mc->exceed_events); + + if (buffer[1] & 2) + set_bit(1, &mc->exceed_events); + + if (mc->input_events || mc->exceed_events || mc->speed_events) + schedule_work(&mc->do_notify); + +resubmit: + status = usb_submit_urb(urb, SLAB_ATOMIC); + if (status) + dev_err(&mc->intf->dev, + "can't resubmit intr, %s-%s/motorcontrol0, status %d", + mc->udev->bus->bus_name, + mc->udev->devpath, status); +} + +static void do_notify(void *data) +{ + struct motorcontrol *mc = data; + int i; + char sysfs_file[8]; + + for (i=0; i<4; i++) { + if (test_and_clear_bit(i, &mc->input_events)) { + sprintf(sysfs_file, "input%d", i); + sysfs_notify(&mc->dev->kobj, NULL, sysfs_file); + } + } + + for (i=0; i<2; i++) { + if (test_and_clear_bit(i, &mc->speed_events)) { + sprintf(sysfs_file, "speed%d", i); + sysfs_notify(&mc->dev->kobj, NULL, sysfs_file); + } + } + + for (i=0; i<2; i++) { + if (test_and_clear_bit(i, &mc->exceed_events)) + dev_warn(&mc->intf->dev, + "motor #%d exceeds 1.5 Amp current limit\n", i); + } +} + +#define show_set_speed(value) \ +static ssize_t set_speed##value(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + struct motorcontrol *mc = dev_get_drvdata(dev); \ + int speed; \ + int retval; \ + \ + if (sscanf(buf, "%d", &speed) < 1) \ + return -EINVAL; \ + \ + if (speed < -100 || speed > 100) \ + return -EINVAL; \ + \ + mc->desired_speed[value] = speed; \ + \ + retval = set_motor(mc, value); \ + \ + return retval ? retval : count; \ +} \ + \ +static ssize_t show_speed##value(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct motorcontrol *mc = dev_get_drvdata(dev); \ + \ + return sprintf(buf, "%d\n", mc->speed[value]); \ +} + +#define speed_attr(value) \ + __ATTR(speed##value, S_IWUGO | S_IRUGO, \ + show_speed##value, set_speed##value) + +show_set_speed(0); +show_set_speed(1); + +#define show_set_acceleration(value) \ +static ssize_t set_acceleration##value(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + struct motorcontrol *mc = dev_get_drvdata(dev); \ + int acceleration; \ + int retval; \ + \ + if (sscanf(buf, "%d", &acceleration) < 1) \ + return -EINVAL; \ + \ + if (acceleration < 0 || acceleration > 100) \ + return -EINVAL; \ + \ + mc->acceleration[value] = acceleration; \ + \ + retval = set_motor(mc, value); \ + \ + return retval ? retval : count; \ +} \ + \ +static ssize_t show_acceleration##value(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct motorcontrol *mc = dev_get_drvdata(dev); \ + \ + return sprintf(buf, "%d\n", mc->acceleration[value]); \ +} + +#define acceleration_attr(value) \ + __ATTR(acceleration##value, S_IWUGO | S_IRUGO, \ + show_acceleration##value, set_acceleration##value) + +show_set_acceleration(0); +show_set_acceleration(1); + +#define show_current(value) \ +static ssize_t show_current##value(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct motorcontrol *mc = dev_get_drvdata(dev); \ + \ + return sprintf(buf, "%dmA\n", (int)mc->_current[value]); \ +} + +#define current_attr(value) \ + __ATTR(current##value, S_IRUGO, show_current##value, NULL) + +show_current(0); +show_current(1); + +#define show_input(value) \ +static ssize_t show_input##value(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct motorcontrol *mc = dev_get_drvdata(dev); \ + \ + return sprintf(buf, "%d\n", (int)mc->inputs[value]); \ +} + +#define input_attr(value) \ + __ATTR(input##value, S_IRUGO, show_input##value, NULL) + +show_input(0); +show_input(1); +show_input(2); +show_input(3); + +static struct device_attribute dev_attrs[] = { + input_attr(0), + input_attr(1), + input_attr(2), + input_attr(3), + speed_attr(0), + speed_attr(1), + acceleration_attr(0), + acceleration_attr(1), + current_attr(0), + current_attr(1) +}; + +static int motorcontrol_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + struct motorcontrol *mc; + int pipe, maxp, rc = -ENOMEM; + int bit, value, i; + + interface = intf->cur_altsetting; + if (interface->desc.bNumEndpoints != 1) + return -ENODEV; + + endpoint = &interface->endpoint[0].desc; + if (!(endpoint->bEndpointAddress & 0x80)) + return -ENODEV; + + /* + * bmAttributes + */ + pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + + mc = kzalloc(sizeof(*mc), GFP_KERNEL); + if (!mc) + goto out; + + mc->dev_no = -1; + mc->data = usb_buffer_alloc(dev, URB_INT_SIZE, SLAB_ATOMIC, &mc->data_dma); + if (!mc->data) + goto out; + + mc->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!mc->irq) + goto out; + + mc->udev = usb_get_dev(dev); + mc->intf = intf; + mc->acceleration[0] = mc->acceleration[1] = 10; + INIT_WORK(&mc->do_notify, do_notify, mc); + usb_fill_int_urb(mc->irq, mc->udev, pipe, mc->data, + maxp > URB_INT_SIZE ? URB_INT_SIZE : maxp, + motorcontrol_irq, mc, endpoint->bInterval); + mc->irq->transfer_dma = mc->data_dma; + mc->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + usb_set_intfdata(intf, mc); + + do { + bit = find_first_zero_bit(&device_no, sizeof(device_no)); + value = test_and_set_bit(bit, &device_no); + } while(value); + mc->dev_no = bit; + + mc->dev = device_create(phidget_class, &mc->udev->dev, 0, + "motorcontrol%d", mc->dev_no); + if (IS_ERR(mc->dev)) { + rc = PTR_ERR(mc->dev); + mc->dev = NULL; + goto out; + } + + dev_set_drvdata(mc->dev, mc); + + if (usb_submit_urb(mc->irq, GFP_KERNEL)) { + rc = -EIO; + goto out; + } + + for (i=0; i<ARRAY_SIZE(dev_attrs); i++) { + rc = device_create_file(mc->dev, &dev_attrs[i]); + if (rc) + goto out2; + } + + dev_info(&intf->dev, "USB PhidgetMotorControl attached\n"); + + return 0; +out2: + while (i-- > 0) + device_remove_file(mc->dev, &dev_attrs[i]); +out: + if (mc) { + if (mc->irq) + usb_free_urb(mc->irq); + if (mc->data) + usb_buffer_free(dev, URB_INT_SIZE, mc->data, mc->data_dma); + if (mc->dev) + device_unregister(mc->dev); + if (mc->dev_no >= 0) + clear_bit(mc->dev_no, &device_no); + + kfree(mc); + } + + return rc; +} + +static void motorcontrol_disconnect(struct usb_interface *interface) +{ + struct motorcontrol *mc; + int i; + + mc = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + if (!mc) + return; + + usb_kill_urb(mc->irq); + usb_free_urb(mc->irq); + usb_buffer_free(mc->udev, URB_INT_SIZE, mc->data, mc->data_dma); + + cancel_delayed_work(&mc->do_notify); + + for (i=0; i<ARRAY_SIZE(dev_attrs); i++) + device_remove_file(mc->dev, &dev_attrs[i]); + + device_unregister(mc->dev); + + usb_put_dev(mc->udev); + clear_bit(mc->dev_no, &device_no); + kfree(mc); + + dev_info(&interface->dev, "USB PhidgetMotorControl detached\n"); +} + +static struct usb_driver motorcontrol_driver = { + .name = "phidgetmotorcontrol", + .probe = motorcontrol_probe, + .disconnect = motorcontrol_disconnect, + .id_table = id_table +}; + +static int __init motorcontrol_init(void) +{ + int retval = 0; + + retval = usb_register(&motorcontrol_driver); + if (retval) + err("usb_register failed. Error number %d", retval); + + return retval; +} + +static void __exit motorcontrol_exit(void) +{ + usb_deregister(&motorcontrol_driver); +} + +module_init(motorcontrol_init); +module_exit(motorcontrol_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/misc/phidgetservo.c b/drivers/usb/misc/phidgetservo.c index c0df79c9..7163f05 100644 --- a/drivers/usb/misc/phidgetservo.c +++ b/drivers/usb/misc/phidgetservo.c @@ -1,7 +1,7 @@ /* * USB PhidgetServo driver 1.0 * - * Copyright (C) 2004 Sean Young <sean@mess.org> + * Copyright (C) 2004, 2006 Sean Young <sean@mess.org> * * 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 @@ -15,14 +15,6 @@ * * CAUTION: Generally you should use 0 < degrees < 180 as anything else * is probably beyond the range of your servo and may damage it. - * - * Jun 16, 2004: Sean Young <sean@mess.org> - * - cleanups - * - was using memory after kfree() - * Aug 8, 2004: Sean Young <sean@mess.org> - * - set the highest angle as high as the hardware allows, there are - * some odd servos out there - * */ #include <linux/kernel.h> @@ -32,6 +24,8 @@ #include <linux/module.h> #include <linux/usb.h> +#include "phidget.h" + #define DRIVER_AUTHOR "Sean Young <sean@mess.org>" #define DRIVER_DESC "USB PhidgetServo Driver" @@ -70,8 +64,12 @@ static struct usb_device_id id_table[] = { MODULE_DEVICE_TABLE(usb, id_table); +static int unsigned long device_no; + struct phidget_servo { struct usb_device *udev; + struct device *dev; + int dev_no; ulong type; int pulse[4]; int degrees[4]; @@ -203,16 +201,16 @@ change_position_v20(struct phidget_servo *servo, int servo_no, int degrees, } #define show_set(value) \ -static ssize_t set_servo##value (struct device *dev, struct device_attribute *attr, \ +static ssize_t set_servo##value (struct device *dev, \ + struct device_attribute *attr, \ const char *buf, size_t count) \ { \ int degrees, minutes, retval; \ - struct usb_interface *intf = to_usb_interface (dev); \ - struct phidget_servo *servo = usb_get_intfdata (intf); \ + struct phidget_servo *servo = dev_get_drvdata(dev); \ \ minutes = 0; \ /* must at least convert degrees */ \ - if (sscanf (buf, "%d.%d", °rees, &minutes) < 1) { \ + if (sscanf(buf, "%d.%d", °rees, &minutes) < 1) { \ return -EINVAL; \ } \ \ @@ -220,86 +218,127 @@ static ssize_t set_servo##value (struct device *dev, struct device_attribute *at return -EINVAL; \ \ if (servo->type & SERVO_VERSION_30) \ - retval = change_position_v30 (servo, value, degrees, \ + retval = change_position_v30(servo, value, degrees, \ minutes); \ else \ - retval = change_position_v20 (servo, value, degrees, \ + retval = change_position_v20(servo, value, degrees, \ minutes); \ \ return retval < 0 ? retval : count; \ } \ \ -static ssize_t show_servo##value (struct device *dev, struct device_attribute *attr, char *buf) \ +static ssize_t show_servo##value (struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ { \ - struct usb_interface *intf = to_usb_interface (dev); \ - struct phidget_servo *servo = usb_get_intfdata (intf); \ + struct phidget_servo *servo = dev_get_drvdata(dev); \ \ - return sprintf (buf, "%d.%02d\n", servo->degrees[value], \ + return sprintf(buf, "%d.%02d\n", servo->degrees[value], \ servo->minutes[value]); \ -} \ -static DEVICE_ATTR(servo##value, S_IWUGO | S_IRUGO, \ - show_servo##value, set_servo##value); +} +#define servo_attr(value) \ + __ATTR(servo##value, S_IWUGO | S_IRUGO, \ + show_servo##value, set_servo##value) show_set(0); show_set(1); show_set(2); show_set(3); +static struct device_attribute dev_attrs[] = { + servo_attr(0), servo_attr(1), servo_attr(2), servo_attr(3) +}; + static int servo_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct usb_device *udev = interface_to_usbdev(interface); struct phidget_servo *dev; + int bit, value, rc; + int servo_count, i; dev = kzalloc(sizeof (struct phidget_servo), GFP_KERNEL); if (dev == NULL) { dev_err(&interface->dev, "%s - out of memory\n", __FUNCTION__); - return -ENOMEM; + rc = -ENOMEM; + goto out; } dev->udev = usb_get_dev(udev); dev->type = id->driver_info; + dev->dev_no = -1; usb_set_intfdata(interface, dev); - device_create_file(&interface->dev, &dev_attr_servo0); - if (dev->type & SERVO_COUNT_QUAD) { - device_create_file(&interface->dev, &dev_attr_servo1); - device_create_file(&interface->dev, &dev_attr_servo2); - device_create_file(&interface->dev, &dev_attr_servo3); + do { + bit = find_first_zero_bit(&device_no, sizeof(device_no)); + value = test_and_set_bit(bit, &device_no); + } while (value); + dev->dev_no = bit; + + dev->dev = device_create(phidget_class, &dev->udev->dev, 0, + "servo%d", dev->dev_no); + if (IS_ERR(dev->dev)) { + rc = PTR_ERR(dev->dev); + dev->dev = NULL; + goto out; + } + + servo_count = dev->type & SERVO_COUNT_QUAD ? 4 : 1; + + for (i=0; i<servo_count; i++) { + rc = device_create_file(dev->dev, &dev_attrs[i]); + if (rc) + goto out2; } dev_info(&interface->dev, "USB %d-Motor PhidgetServo v%d.0 attached\n", - dev->type & SERVO_COUNT_QUAD ? 4 : 1, - dev->type & SERVO_VERSION_30 ? 3 : 2); + servo_count, dev->type & SERVO_VERSION_30 ? 3 : 2); - if(!(dev->type & SERVO_VERSION_30)) + if (!(dev->type & SERVO_VERSION_30)) dev_info(&interface->dev, "WARNING: v2.0 not tested! Please report if it works.\n"); return 0; +out2: + while (i-- > 0) + device_remove_file(dev->dev, &dev_attrs[i]); +out: + if (dev) { + if (dev->dev) + device_unregister(dev->dev); + if (dev->dev_no >= 0) + clear_bit(dev->dev_no, &device_no); + + kfree(dev); + } + + return rc; } static void servo_disconnect(struct usb_interface *interface) { struct phidget_servo *dev; + int servo_count, i; dev = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); - device_remove_file(&interface->dev, &dev_attr_servo0); - if (dev->type & SERVO_COUNT_QUAD) { - device_remove_file(&interface->dev, &dev_attr_servo1); - device_remove_file(&interface->dev, &dev_attr_servo2); - device_remove_file(&interface->dev, &dev_attr_servo3); - } + if (!dev) + return; + + servo_count = dev->type & SERVO_COUNT_QUAD ? 4 : 1; + + for (i=0; i<servo_count; i++) + device_remove_file(dev->dev, &dev_attrs[i]); + device_unregister(dev->dev); usb_put_dev(dev->udev); dev_info(&interface->dev, "USB %d-Motor PhidgetServo v%d.0 detached\n", - dev->type & SERVO_COUNT_QUAD ? 4 : 1, - dev->type & SERVO_VERSION_30 ? 3 : 2); + servo_count, dev->type & SERVO_VERSION_30 ? 3 : 2); + clear_bit(dev->dev_no, &device_no); kfree(dev); } diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index e16582f..a44124c 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -3179,7 +3179,7 @@ sisusb_compat_ioctl(struct file *f, unsigned int cmd, unsigned long arg) } #endif -static struct file_operations usb_sisusb_fops = { +static const struct file_operations usb_sisusb_fops = { .owner = THIS_MODULE, .open = sisusb_open, .release = sisusb_release, diff --git a/drivers/usb/misc/usb_u132.h b/drivers/usb/misc/usb_u132.h new file mode 100644 index 0000000..551ba89 --- /dev/null +++ b/drivers/usb/misc/usb_u132.h @@ -0,0 +1,97 @@ +/* +* Common Header File for the Elan Digital Systems U132 adapter +* this file should be included by both the "ftdi-u132" and +* the "u132-hcd" modules. +* +* Copyright(C) 2006 Elan Digital Systems Limited +*(http://www.elandigitalsystems.com) +* +* Author and Maintainer - Tony Olech - Elan Digital Systems +*(tony.olech@elandigitalsystems.com) +* +* 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, version 2. +* +* +* The driver was written by Tony Olech(tony.olech@elandigitalsystems.com) +* based on various USB client drivers in the 2.6.15 linux kernel +* with constant reference to the 3rd Edition of Linux Device Drivers +* published by O'Reilly +* +* The U132 adapter is a USB to CardBus adapter specifically designed +* for PC cards that contain an OHCI host controller. Typical PC cards +* are the Orange Mobile 3G Option GlobeTrotter Fusion card. +* +* The U132 adapter will *NOT *work with PC cards that do not contain +* an OHCI controller. A simple way to test whether a PC card has an +* OHCI controller as an interface is to insert the PC card directly +* into a laptop(or desktop) with a CardBus slot and if "lspci" shows +* a new USB controller and "lsusb -v" shows a new OHCI Host Controller +* then there is a good chance that the U132 adapter will support the +* PC card.(you also need the specific client driver for the PC card) +* +* Please inform the Author and Maintainer about any PC cards that +* contain OHCI Host Controller and work when directly connected to +* an embedded CardBus slot but do not work when they are connected +* via an ELAN U132 adapter. +* +* The driver consists of two modules, the "ftdi-u132" module is +* a USB client driver that interfaces to the FTDI chip within +* the U132 adapter manufactured by Elan Digital Systems, and the +* "u132-hcd" module is a USB host controller driver that talks +* to the OHCI controller within CardBus card that are inserted +* in the U132 adapter. +* +* The "ftdi-u132" module should be loaded automatically by the +* hot plug system when the U132 adapter is plugged in. The module +* initialises the adapter which mostly consists of synchronising +* the FTDI chip, before continuously polling the adapter to detect +* PC card insertions. As soon as a PC card containing a recognised +* OHCI controller is seen the "ftdi-u132" module explicitly requests +* the kernel to load the "u132-hcd" module. +* +* The "ftdi-u132" module provides the interface to the inserted +* PC card and the "u132-hcd" module uses the API to send and recieve +* data. The API features call-backs, so that part of the "u132-hcd" +* module code will run in the context of one of the kernel threads +* of the "ftdi-u132" module. +* +*/ +int ftdi_elan_switch_on_diagnostics(int number); +void ftdi_elan_gone_away(struct platform_device *pdev); +void start_usb_lock_device_tracing(void); +struct u132_platform_data { + u16 vendor; + u16 device; + u8 potpg; + void (*port_power) (struct device *dev, int is_on); + void (*reset) (struct device *dev); +}; +int usb_ftdi_elan_edset_single(struct platform_device *pdev, u8 ed_number, + void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits, + void (*callback) (void *endp, struct urb *urb, u8 *buf, int len, + int toggle_bits, int error_count, int condition_code, int repeat_number, + int halted, int skipped, int actual, int non_null)); +int usb_ftdi_elan_edset_output(struct platform_device *pdev, u8 ed_number, + void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits, + void (*callback) (void *endp, struct urb *urb, u8 *buf, int len, + int toggle_bits, int error_count, int condition_code, int repeat_number, + int halted, int skipped, int actual, int non_null)); +int usb_ftdi_elan_edset_empty(struct platform_device *pdev, u8 ed_number, + void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits, + void (*callback) (void *endp, struct urb *urb, u8 *buf, int len, + int toggle_bits, int error_count, int condition_code, int repeat_number, + int halted, int skipped, int actual, int non_null)); +int usb_ftdi_elan_edset_input(struct platform_device *pdev, u8 ed_number, + void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits, + void (*callback) (void *endp, struct urb *urb, u8 *buf, int len, + int toggle_bits, int error_count, int condition_code, int repeat_number, + int halted, int skipped, int actual, int non_null)); +int usb_ftdi_elan_edset_setup(struct platform_device *pdev, u8 ed_number, + void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits, + void (*callback) (void *endp, struct urb *urb, u8 *buf, int len, + int toggle_bits, int error_count, int condition_code, int repeat_number, + int halted, int skipped, int actual, int non_null)); +int usb_ftdi_elan_edset_flush(struct platform_device *pdev, u8 ed_number, + void *endp); diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index e095772..dbaca9f 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c @@ -239,7 +239,7 @@ error: return retval; } -static struct file_operations lcd_fops = { +static const struct file_operations lcd_fops = { .owner = THIS_MODULE, .read = lcd_read, .write = lcd_write, @@ -290,9 +290,7 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id endpoint = &iface_desc->endpoint[i].desc; if (!dev->bulk_in_endpointAddr && - (endpoint->bEndpointAddress & USB_DIR_IN) && - ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_BULK)) { + usb_endpoint_is_bulk_in(endpoint)) { /* we found a bulk in endpoint */ buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); dev->bulk_in_size = buffer_size; @@ -305,9 +303,7 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id } if (!dev->bulk_out_endpointAddr && - !(endpoint->bEndpointAddress & USB_DIR_IN) && - ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_BULK)) { + usb_endpoint_is_bulk_out(endpoint)) { /* we found a bulk out endpoint */ dev->bulk_out_endpointAddr = endpoint->bEndpointAddress; } diff --git a/drivers/usb/misc/usbled.c b/drivers/usb/misc/usbled.c index 0c5ee0a..49c5c5c 100644 --- a/drivers/usb/misc/usbled.c +++ b/drivers/usb/misc/usbled.c @@ -108,22 +108,34 @@ static int led_probe(struct usb_interface *interface, const struct usb_device_id dev = kzalloc(sizeof(struct usb_led), GFP_KERNEL); if (dev == NULL) { dev_err(&interface->dev, "Out of memory\n"); - goto error; + goto error_mem; } dev->udev = usb_get_dev(udev); usb_set_intfdata (interface, dev); - device_create_file(&interface->dev, &dev_attr_blue); - device_create_file(&interface->dev, &dev_attr_red); - device_create_file(&interface->dev, &dev_attr_green); + retval = device_create_file(&interface->dev, &dev_attr_blue); + if (retval) + goto error; + retval = device_create_file(&interface->dev, &dev_attr_red); + if (retval) + goto error; + retval = device_create_file(&interface->dev, &dev_attr_green); + if (retval) + goto error; dev_info(&interface->dev, "USB LED device now attached\n"); return 0; error: + device_remove_file(&interface->dev, &dev_attr_blue); + device_remove_file(&interface->dev, &dev_attr_red); + device_remove_file(&interface->dev, &dev_attr_green); + usb_set_intfdata (interface, NULL); + usb_put_dev(dev->udev); kfree(dev); +error_mem: return retval; } |