From d5176b413dcce85334e270021fc0d723d1714c84 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Sun, 9 Jul 2006 13:01:02 +0000 Subject: USB: Add driver for PhidgetMotorControl This driver add support for the Phidgets Inc., MotorControl via sysfs. Also some minor fixes for the InterfaceKit. Signed-off-by: Sean Young Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/Kconfig | 16 +- drivers/usb/misc/Makefile | 1 + drivers/usb/misc/phidgetmotorcontrol.c | 427 +++++++++++++++++++++++++++++++++ 3 files changed, 441 insertions(+), 3 deletions(-) create mode 100644 drivers/usb/misc/phidgetmotorcontrol.c (limited to 'drivers/usb/misc') diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 88928a4be805..8affc137d618 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -116,15 +116,25 @@ config USB_CYTHERM module will be called cytherm. config USB_PHIDGETKIT - tristate "USB PhidgetKit support" + tristate "USB PhidgetInterfaceKit support" depends on USB help - Say Y here if you want to connect a PhidgetKit USB device from - Phidgets Inc. + 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 + 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 diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index 2927260c5812..5f329176451a 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_USB_LD) += ldusb.o obj-$(CONFIG_USB_LED) += usbled.o obj-$(CONFIG_USB_LEGOTOWER) += legousbtower.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 diff --git a/drivers/usb/misc/phidgetmotorcontrol.c b/drivers/usb/misc/phidgetmotorcontrol.c new file mode 100644 index 000000000000..2972dc2eb27f --- /dev/null +++ b/drivers/usb/misc/phidgetmotorcontrol.c @@ -0,0 +1,427 @@ +/* + * USB Phidget MotorControl driver + * + * Copyright (C) 2006 Sean Young + * + * 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 +#include +#include +#include +#include + +#define DRIVER_AUTHOR "Sean Young " +#define DRIVER_DESC "USB PhidgetMotorControl Driver" + +#define USB_VENDOR_ID_GLAB 0x06c2 +#define USB_DEVICE_ID_MOTORCONTROL 0x0058 + +#define URB_INT_SIZE 8 + +struct motorcontrol { + struct usb_device *udev; + struct usb_interface *intf; + 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->intf->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->intf->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 usb_interface *intf = to_usb_interface(dev); \ + struct motorcontrol *mc = usb_get_intfdata(intf); \ + 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 usb_interface *intf = to_usb_interface(dev); \ + struct motorcontrol *mc = usb_get_intfdata(intf); \ + \ + return sprintf(buf, "%d\n", mc->speed[value]); \ +} \ +static DEVICE_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 usb_interface *intf = to_usb_interface(dev); \ + struct motorcontrol *mc = usb_get_intfdata(intf); \ + 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 usb_interface *intf = to_usb_interface(dev); \ + struct motorcontrol *mc = usb_get_intfdata(intf); \ + \ + return sprintf(buf, "%d\n", mc->acceleration[value]); \ +} \ +static DEVICE_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 usb_interface *intf = to_usb_interface(dev); \ + struct motorcontrol *mc = usb_get_intfdata(intf); \ + \ + return sprintf(buf, "%dmA\n", (int)mc->_current[value]); \ +} \ +static DEVICE_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 usb_interface *intf = to_usb_interface(dev); \ + struct motorcontrol *mc = usb_get_intfdata(intf); \ + \ + return sprintf(buf, "%d\n", (int)mc->inputs[value]); \ +} \ +static DEVICE_ATTR(input##value, S_IRUGO, show_input##value, NULL); + +show_input(0); +show_input(1); +show_input(2); +show_input(3); + +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; + + 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->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); + + if (usb_submit_urb(mc->irq, GFP_KERNEL)) { + rc = -EIO; + goto out; + } + + device_create_file(&intf->dev, &dev_attr_input0); + 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_speed0); + device_create_file(&intf->dev, &dev_attr_speed1); + + device_create_file(&intf->dev, &dev_attr_acceleration0); + device_create_file(&intf->dev, &dev_attr_acceleration1); + + device_create_file(&intf->dev, &dev_attr_current0); + device_create_file(&intf->dev, &dev_attr_current1); + + dev_info(&intf->dev, "USB Phidget MotorControl attached\n"); + + return 0; + +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); + kfree(mc); + } + + return rc; +} + +static void motorcontrol_disconnect(struct usb_interface *interface) +{ + struct motorcontrol *mc; + + 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); + + device_remove_file(&interface->dev, &dev_attr_input0); + 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_speed0); + device_remove_file(&interface->dev, &dev_attr_speed1); + + device_remove_file(&interface->dev, &dev_attr_acceleration0); + device_remove_file(&interface->dev, &dev_attr_acceleration1); + + device_remove_file(&interface->dev, &dev_attr_current0); + device_remove_file(&interface->dev, &dev_attr_current1); + + dev_info(&interface->dev, "USB Phidget MotorControl disconnected\n"); + + usb_put_dev(mc->udev); + kfree(mc); +} + +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"); -- cgit v1.2.2 From 912b24c333843514ff77ed88961c6945f0f286ce Mon Sep 17 00:00:00 2001 From: Sean Young Date: Mon, 10 Jul 2006 09:56:25 +0000 Subject: USB: Put phidgets driver in a sysfs class This patch creates a device class phidget and add the phidget drivers to them. Signed-off-by: Sean Young Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/Kconfig | 13 +- drivers/usb/misc/Makefile | 1 + drivers/usb/misc/phidget.c | 43 ++++++ drivers/usb/misc/phidget.h | 12 ++ drivers/usb/misc/phidgetkit.c | 258 ++++++++++++++++++--------------- drivers/usb/misc/phidgetmotorcontrol.c | 126 ++++++++++------ drivers/usb/misc/phidgetservo.c | 71 +++++---- 7 files changed, 334 insertions(+), 190 deletions(-) create mode 100644 drivers/usb/misc/phidget.c create mode 100644 drivers/usb/misc/phidget.h (limited to 'drivers/usb/misc') diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 8affc137d618..20539cf9394b 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -115,9 +115,16 @@ config USB_CYTHERM To compile this driver as a module, choose M here: the module will be called cytherm. +config USB_PHIDGET + tristate "USB Phidgets drivers" + depends on USB + help + Say Y here to enable the various drivers for devices from + Phidgets inc. + config USB_PHIDGETKIT tristate "USB PhidgetInterfaceKit support" - depends on USB + depends on USB_PHIDGET help Say Y here if you want to connect a PhidgetInterfaceKit USB device from Phidgets Inc. @@ -127,7 +134,7 @@ config USB_PHIDGETKIT config USB_PHIDGETMOTORCONTROL tristate "USB PhidgetMotorControl support" - depends on USB + depends on USB_PHIDGET help Say Y here if you want to connect a PhidgetMotorControl USB device from Phidgets Inc. @@ -137,7 +144,7 @@ config USB_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. diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index 5f329176451a..d12a84a1c0a4 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -13,6 +13,7 @@ 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 diff --git a/drivers/usb/misc/phidget.c b/drivers/usb/misc/phidget.c new file mode 100644 index 000000000000..735ed33f4f7f --- /dev/null +++ b/drivers/usb/misc/phidget.c @@ -0,0 +1,43 @@ +/* + * USB Phidgets class + * + * Copyright (C) 2006 Sean Young + * + * 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 +#include +#include +#include +#include + +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 "); +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 000000000000..c4011907d431 --- /dev/null +++ b/drivers/usb/misc/phidget.h @@ -0,0 +1,12 @@ +/* + * USB Phidgets class + * + * Copyright (C) 2006 Sean Young + * + * 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 bfbbbfbb92bc..3f508957def9 100644 --- a/drivers/usb/misc/phidgetkit.c +++ b/drivers/usb/misc/phidgetkit.c @@ -20,6 +20,8 @@ #include #include +#include "phidget.h" + #define DRIVER_AUTHOR "Sean Young " #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,21 @@ 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 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; \ +} \ static DEVICE_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; @@ -232,16 +238,15 @@ static void remove_lcd_files(struct interfacekit *kit) { 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); + device_remove_file(kit->dev, &dev_attr_lcd_line_1); + device_remove_file(kit->dev, &dev_attr_lcd_line_2); + device_remove_file(kit->dev, &dev_attr_backlight); } } 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; if (kit->ifkit->has_lcd == 0) @@ -253,9 +258,9 @@ 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); + device_create_file(kit->dev, &dev_attr_lcd_line_1); + device_create_file(kit->dev, &dev_attr_lcd_line_2); + device_create_file(kit->dev, &dev_attr_backlight); kit->lcd_files_on = 1; } } else { @@ -362,24 +367,24 @@ static void do_notify(void *data) for (i=0; iifkit->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; iifkit->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,10 +396,11 @@ 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));\ } \ @@ -420,8 +426,7 @@ show_set_output(16); #define show_input(value) \ 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]); \ } \ @@ -445,10 +450,11 @@ show_input(15); show_input(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]); \ } \ @@ -471,6 +477,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; ifkit = (struct driver_interfacekit *)id->driver_info; if (!ifkit) @@ -493,6 +500,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,73 +521,88 @@ 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); + device_create_file(kit->dev, &dev_attr_output1); + device_create_file(kit->dev, &dev_attr_output2); + device_create_file(kit->dev, &dev_attr_output3); + device_create_file(kit->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); + device_create_file(kit->dev, &dev_attr_output5); + device_create_file(kit->dev, &dev_attr_output6); + device_create_file(kit->dev, &dev_attr_output7); + device_create_file(kit->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); + device_create_file(kit->dev, &dev_attr_output9); + device_create_file(kit->dev, &dev_attr_output10); + device_create_file(kit->dev, &dev_attr_output11); + device_create_file(kit->dev, &dev_attr_output12); + device_create_file(kit->dev, &dev_attr_output13); + device_create_file(kit->dev, &dev_attr_output14); + device_create_file(kit->dev, &dev_attr_output15); + device_create_file(kit->dev, &dev_attr_output16); } 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); + device_create_file(kit->dev, &dev_attr_input1); + device_create_file(kit->dev, &dev_attr_input2); + device_create_file(kit->dev, &dev_attr_input3); + device_create_file(kit->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); + device_create_file(kit->dev, &dev_attr_input5); + device_create_file(kit->dev, &dev_attr_input6); + device_create_file(kit->dev, &dev_attr_input7); + device_create_file(kit->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); + device_create_file(kit->dev, &dev_attr_input9); + device_create_file(kit->dev, &dev_attr_input10); + device_create_file(kit->dev, &dev_attr_input11); + device_create_file(kit->dev, &dev_attr_input12); + device_create_file(kit->dev, &dev_attr_input13); + device_create_file(kit->dev, &dev_attr_input14); + device_create_file(kit->dev, &dev_attr_input15); + device_create_file(kit->dev, &dev_attr_input16); } 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); + device_create_file(kit->dev, &dev_attr_sensor1); + device_create_file(kit->dev, &dev_attr_sensor2); + device_create_file(kit->dev, &dev_attr_sensor3); + device_create_file(kit->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); + device_create_file(kit->dev, &dev_attr_sensor5); + device_create_file(kit->dev, &dev_attr_sensor6); + device_create_file(kit->dev, &dev_attr_sensor7); } if (ifkit->sensors == 8) - device_create_file(&intf->dev, &dev_attr_sensor8); + device_create_file(kit->dev, &dev_attr_sensor8); if (ifkit->has_lcd) - device_create_file(&intf->dev, &dev_attr_lcd); + device_create_file(kit->dev, &dev_attr_lcd); dev_info(&intf->dev, "USB PhidgetInterfaceKit %d/%d/%d attached\n", ifkit->sensors, ifkit->inputs, ifkit->outputs); @@ -592,6 +615,11 @@ out: 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); } @@ -614,72 +642,76 @@ 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); + device_remove_file(kit->dev, &dev_attr_output1); + device_remove_file(kit->dev, &dev_attr_output2); + device_remove_file(kit->dev, &dev_attr_output3); + device_remove_file(kit->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); + device_remove_file(kit->dev, &dev_attr_output5); + device_remove_file(kit->dev, &dev_attr_output6); + device_remove_file(kit->dev, &dev_attr_output7); + device_remove_file(kit->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); + device_remove_file(kit->dev, &dev_attr_output9); + device_remove_file(kit->dev, &dev_attr_output10); + device_remove_file(kit->dev, &dev_attr_output11); + device_remove_file(kit->dev, &dev_attr_output12); + device_remove_file(kit->dev, &dev_attr_output13); + device_remove_file(kit->dev, &dev_attr_output14); + device_remove_file(kit->dev, &dev_attr_output15); + device_remove_file(kit->dev, &dev_attr_output16); } 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); + device_remove_file(kit->dev, &dev_attr_input1); + device_remove_file(kit->dev, &dev_attr_input2); + device_remove_file(kit->dev, &dev_attr_input3); + device_remove_file(kit->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); + device_remove_file(kit->dev, &dev_attr_input5); + device_remove_file(kit->dev, &dev_attr_input6); + device_remove_file(kit->dev, &dev_attr_input7); + device_remove_file(kit->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); + device_remove_file(kit->dev, &dev_attr_input9); + device_remove_file(kit->dev, &dev_attr_input10); + device_remove_file(kit->dev, &dev_attr_input11); + device_remove_file(kit->dev, &dev_attr_input12); + device_remove_file(kit->dev, &dev_attr_input13); + device_remove_file(kit->dev, &dev_attr_input14); + device_remove_file(kit->dev, &dev_attr_input15); + device_remove_file(kit->dev, &dev_attr_input16); } 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); + device_remove_file(kit->dev, &dev_attr_sensor1); + device_remove_file(kit->dev, &dev_attr_sensor2); + device_remove_file(kit->dev, &dev_attr_sensor3); + device_remove_file(kit->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); + device_remove_file(kit->dev, &dev_attr_sensor5); + device_remove_file(kit->dev, &dev_attr_sensor6); + device_remove_file(kit->dev, &dev_attr_sensor7); } if (kit->ifkit->sensors == 8) - device_remove_file(&interface->dev, &dev_attr_sensor8); + device_remove_file(kit->dev, &dev_attr_sensor8); if (kit->ifkit->has_lcd) - device_remove_file(&interface->dev, &dev_attr_lcd); + device_remove_file(kit->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 index 2972dc2eb27f..d9ac7f97f8c8 100644 --- a/drivers/usb/misc/phidgetmotorcontrol.c +++ b/drivers/usb/misc/phidgetmotorcontrol.c @@ -15,6 +15,8 @@ #include #include +#include "phidget.h" + #define DRIVER_AUTHOR "Sean Young " #define DRIVER_DESC "USB PhidgetMotorControl Driver" @@ -23,9 +25,13 @@ #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]; @@ -162,14 +168,14 @@ static void do_notify(void *data) for (i=0; i<4; i++) { if (test_and_clear_bit(i, &mc->input_events)) { sprintf(sysfs_file, "input%d", i); - sysfs_notify(&mc->intf->dev.kobj, NULL, sysfs_file); + 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->intf->dev.kobj, NULL, sysfs_file); + sysfs_notify(&mc->dev->kobj, NULL, sysfs_file); } } @@ -181,11 +187,11 @@ static void do_notify(void *data) } #define show_set_speed(value) \ -static ssize_t set_speed##value(struct device *dev, \ - struct device_attribute *attr, const char *buf, size_t count) \ +static ssize_t set_speed##value(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ { \ - struct usb_interface *intf = to_usb_interface(dev); \ - struct motorcontrol *mc = usb_get_intfdata(intf); \ + struct motorcontrol *mc = dev_get_drvdata(dev); \ int speed; \ int retval; \ \ @@ -202,11 +208,11 @@ static ssize_t set_speed##value(struct device *dev, \ return retval ? retval : count; \ } \ \ -static ssize_t show_speed##value(struct device *dev, \ - struct device_attribute *attr, char *buf) \ +static ssize_t show_speed##value(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ { \ - struct usb_interface *intf = to_usb_interface(dev); \ - struct motorcontrol *mc = usb_get_intfdata(intf); \ + struct motorcontrol *mc = dev_get_drvdata(dev); \ \ return sprintf(buf, "%d\n", mc->speed[value]); \ } \ @@ -217,10 +223,10 @@ 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 device_attribute *attr, \ + const char *buf, size_t count) \ { \ - struct usb_interface *intf = to_usb_interface(dev); \ - struct motorcontrol *mc = usb_get_intfdata(intf); \ + struct motorcontrol *mc = dev_get_drvdata(dev); \ int acceleration; \ int retval; \ \ @@ -237,11 +243,11 @@ static ssize_t set_acceleration##value(struct device *dev, \ return retval ? retval : count; \ } \ \ -static ssize_t show_acceleration##value(struct device *dev, \ - struct device_attribute *attr, char *buf) \ +static ssize_t show_acceleration##value(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ { \ - struct usb_interface *intf = to_usb_interface(dev); \ - struct motorcontrol *mc = usb_get_intfdata(intf); \ + struct motorcontrol *mc = dev_get_drvdata(dev); \ \ return sprintf(buf, "%d\n", mc->acceleration[value]); \ } \ @@ -251,11 +257,11 @@ 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) \ +static ssize_t show_current##value(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ { \ - struct usb_interface *intf = to_usb_interface(dev); \ - struct motorcontrol *mc = usb_get_intfdata(intf); \ + struct motorcontrol *mc = dev_get_drvdata(dev); \ \ return sprintf(buf, "%dmA\n", (int)mc->_current[value]); \ } \ @@ -265,11 +271,11 @@ 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) \ +static ssize_t show_input##value(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ { \ - struct usb_interface *intf = to_usb_interface(dev); \ - struct motorcontrol *mc = usb_get_intfdata(intf); \ + struct motorcontrol *mc = dev_get_drvdata(dev); \ \ return sprintf(buf, "%d\n", (int)mc->inputs[value]); \ } \ @@ -287,6 +293,7 @@ static int motorcontrol_probe(struct usb_interface *intf, const struct usb_devic struct usb_endpoint_descriptor *endpoint; struct motorcontrol *mc; int pipe, maxp, rc = -ENOMEM; + int bit, value; interface = intf->cur_altsetting; if (interface->desc.bNumEndpoints != 1) @@ -306,6 +313,7 @@ static int motorcontrol_probe(struct usb_interface *intf, const struct usb_devic 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; @@ -326,26 +334,42 @@ static int motorcontrol_probe(struct usb_interface *intf, const struct usb_devic 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; } - device_create_file(&intf->dev, &dev_attr_input0); - 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(mc->dev, &dev_attr_input0); + device_create_file(mc->dev, &dev_attr_input1); + device_create_file(mc->dev, &dev_attr_input2); + device_create_file(mc->dev, &dev_attr_input3); - device_create_file(&intf->dev, &dev_attr_speed0); - device_create_file(&intf->dev, &dev_attr_speed1); + device_create_file(mc->dev, &dev_attr_speed0); + device_create_file(mc->dev, &dev_attr_speed1); - device_create_file(&intf->dev, &dev_attr_acceleration0); - device_create_file(&intf->dev, &dev_attr_acceleration1); + device_create_file(mc->dev, &dev_attr_acceleration0); + device_create_file(mc->dev, &dev_attr_acceleration1); - device_create_file(&intf->dev, &dev_attr_current0); - device_create_file(&intf->dev, &dev_attr_current1); + device_create_file(mc->dev, &dev_attr_current0); + device_create_file(mc->dev, &dev_attr_current1); - dev_info(&intf->dev, "USB Phidget MotorControl attached\n"); + dev_info(&intf->dev, "USB PhidgetMotorControl attached\n"); return 0; @@ -355,6 +379,11 @@ out: 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); } @@ -376,24 +405,27 @@ static void motorcontrol_disconnect(struct usb_interface *interface) cancel_delayed_work(&mc->do_notify); - device_remove_file(&interface->dev, &dev_attr_input0); - 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(mc->dev, &dev_attr_input0); + device_remove_file(mc->dev, &dev_attr_input1); + device_remove_file(mc->dev, &dev_attr_input2); + device_remove_file(mc->dev, &dev_attr_input3); - device_remove_file(&interface->dev, &dev_attr_speed0); - device_remove_file(&interface->dev, &dev_attr_speed1); + device_remove_file(mc->dev, &dev_attr_speed0); + device_remove_file(mc->dev, &dev_attr_speed1); - device_remove_file(&interface->dev, &dev_attr_acceleration0); - device_remove_file(&interface->dev, &dev_attr_acceleration1); + device_remove_file(mc->dev, &dev_attr_acceleration0); + device_remove_file(mc->dev, &dev_attr_acceleration1); - device_remove_file(&interface->dev, &dev_attr_current0); - device_remove_file(&interface->dev, &dev_attr_current1); + device_remove_file(mc->dev, &dev_attr_current0); + device_remove_file(mc->dev, &dev_attr_current1); - dev_info(&interface->dev, "USB Phidget MotorControl disconnected\n"); + 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 = { diff --git a/drivers/usb/misc/phidgetservo.c b/drivers/usb/misc/phidgetservo.c index c0df79c96538..66be9513fd6f 100644 --- a/drivers/usb/misc/phidgetservo.c +++ b/drivers/usb/misc/phidgetservo.c @@ -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 - * - cleanups - * - was using memory after kfree() - * Aug 8, 2004: Sean Young - * - set the highest angle as high as the hardware allows, there are - * some odd servos out there - * */ #include @@ -32,6 +24,8 @@ #include #include +#include "phidget.h" + #define DRIVER_AUTHOR "Sean Young " #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,21 +218,22 @@ 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, \ @@ -250,6 +249,7 @@ 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; dev = kzalloc(sizeof (struct phidget_servo), GFP_KERNEL); if (dev == NULL) { @@ -261,18 +261,33 @@ servo_probe(struct usb_interface *interface, const struct usb_device_id *id) dev->type = id->driver_info; usb_set_intfdata(interface, dev); - device_create_file(&interface->dev, &dev_attr_servo0); + 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)) { + int rc = PTR_ERR(dev->dev); + clear_bit(dev->dev_no, &device_no); + kfree(dev); + return rc; + } + + device_create_file(dev->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); + device_create_file(dev->dev, &dev_attr_servo1); + device_create_file(dev->dev, &dev_attr_servo2); + device_create_file(dev->dev, &dev_attr_servo3); } 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); - 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"); @@ -287,19 +302,21 @@ servo_disconnect(struct usb_interface *interface) dev = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); - device_remove_file(&interface->dev, &dev_attr_servo0); + device_remove_file(dev->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); + device_remove_file(dev->dev, &dev_attr_servo1); + device_remove_file(dev->dev, &dev_attr_servo2); + device_remove_file(dev->dev, &dev_attr_servo3); } + 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); + clear_bit(dev->dev_no, &device_no); kfree(dev); } -- cgit v1.2.2 From da308e8da700637d6271dddda08128094691b980 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Fri, 11 Aug 2006 09:28:28 +0000 Subject: USB: Phidgets should check create_device_file() return value device_create_file() could fail, add proper error paths for this condition. Signed-off-by: Sean Young Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/phidgetkit.c | 238 +++++++++++++++------------------ drivers/usb/misc/phidgetmotorcontrol.c | 83 ++++++------ drivers/usb/misc/phidgetservo.c | 74 ++++++---- 3 files changed, 199 insertions(+), 196 deletions(-) (limited to 'drivers/usb/misc') diff --git a/drivers/usb/misc/phidgetkit.c b/drivers/usb/misc/phidgetkit.c index 3f508957def9..9a8d137d39f9 100644 --- a/drivers/usb/misc/phidgetkit.c +++ b/drivers/usb/misc/phidgetkit.c @@ -193,8 +193,11 @@ static ssize_t lcd_line_##number(struct device *dev, \ struct interfacekit *kit = dev_get_drvdata(dev); \ change_string(kit, buf, number - 1); \ return count; \ -} \ -static DEVICE_ATTR(lcd_line_##number, S_IWUGO, NULL, lcd_line_##number); +} + +#define lcd_line_attr(number) \ + __ATTR(lcd_line_##number, S_IWUGO, NULL, lcd_line_##number) + set_lcd_line(1); set_lcd_line(2); @@ -232,15 +235,22 @@ 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->dev, &dev_attr_lcd_line_1); - device_remove_file(kit->dev, &dev_attr_lcd_line_2); - device_remove_file(kit->dev, &dev_attr_backlight); + + for (i=0; idev, &dev_lcd_line_attrs[i]); } } @@ -248,6 +258,7 @@ static ssize_t enable_lcd_files(struct device *dev, struct device_attribute *att { struct interfacekit *kit = dev_get_drvdata(dev); int enable; + int i, rc; if (kit->ifkit->has_lcd == 0) return -ENODEV; @@ -258,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->dev, &dev_attr_lcd_line_1); - device_create_file(kit->dev, &dev_attr_lcd_line_2); - device_create_file(kit->dev, &dev_attr_backlight); + for (i=0; idev, + &dev_lcd_line_attrs[i]); + if (rc) + goto out; + } kit->lcd_files_on = 1; } } else { @@ -271,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) @@ -403,9 +423,12 @@ static ssize_t show_output##value(struct device *dev, \ 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); @@ -423,14 +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 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); @@ -449,6 +482,13 @@ 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, \ @@ -457,8 +497,10 @@ static ssize_t show_sensor##value(struct device *dev, \ 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); @@ -469,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); @@ -477,7 +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; + int bit, value, i; ifkit = (struct driver_interfacekit *)id->driver_info; if (!ifkit) @@ -541,74 +588,49 @@ static int interfacekit_probe(struct usb_interface *intf, const struct usb_devic goto out; } - if (ifkit->outputs >= 4) { - device_create_file(kit->dev, &dev_attr_output1); - device_create_file(kit->dev, &dev_attr_output2); - device_create_file(kit->dev, &dev_attr_output3); - device_create_file(kit->dev, &dev_attr_output4); - } - if (ifkit->outputs >= 8) { - device_create_file(kit->dev, &dev_attr_output5); - device_create_file(kit->dev, &dev_attr_output6); - device_create_file(kit->dev, &dev_attr_output7); - device_create_file(kit->dev, &dev_attr_output8); - } - if (ifkit->outputs == 16) { - device_create_file(kit->dev, &dev_attr_output9); - device_create_file(kit->dev, &dev_attr_output10); - device_create_file(kit->dev, &dev_attr_output11); - device_create_file(kit->dev, &dev_attr_output12); - device_create_file(kit->dev, &dev_attr_output13); - device_create_file(kit->dev, &dev_attr_output14); - device_create_file(kit->dev, &dev_attr_output15); - device_create_file(kit->dev, &dev_attr_output16); + for (i=0; ioutputs; i++ ) { + rc = device_create_file(kit->dev, &dev_output_attrs[i]); + if (rc) + goto out2; } - if (ifkit->inputs >= 4) { - device_create_file(kit->dev, &dev_attr_input1); - device_create_file(kit->dev, &dev_attr_input2); - device_create_file(kit->dev, &dev_attr_input3); - device_create_file(kit->dev, &dev_attr_input4); - } - if (ifkit->inputs >= 8) { - device_create_file(kit->dev, &dev_attr_input5); - device_create_file(kit->dev, &dev_attr_input6); - device_create_file(kit->dev, &dev_attr_input7); - device_create_file(kit->dev, &dev_attr_input8); - } - if (ifkit->inputs == 16) { - device_create_file(kit->dev, &dev_attr_input9); - device_create_file(kit->dev, &dev_attr_input10); - device_create_file(kit->dev, &dev_attr_input11); - device_create_file(kit->dev, &dev_attr_input12); - device_create_file(kit->dev, &dev_attr_input13); - device_create_file(kit->dev, &dev_attr_input14); - device_create_file(kit->dev, &dev_attr_input15); - device_create_file(kit->dev, &dev_attr_input16); + for (i=0; iinputs; i++ ) { + rc = device_create_file(kit->dev, &dev_input_attrs[i]); + if (rc) + goto out3; } - if (ifkit->sensors >= 4) { - device_create_file(kit->dev, &dev_attr_sensor1); - device_create_file(kit->dev, &dev_attr_sensor2); - device_create_file(kit->dev, &dev_attr_sensor3); - device_create_file(kit->dev, &dev_attr_sensor4); - } - if (ifkit->sensors >= 7) { - device_create_file(kit->dev, &dev_attr_sensor5); - device_create_file(kit->dev, &dev_attr_sensor6); - device_create_file(kit->dev, &dev_attr_sensor7); + for (i=0; isensors; i++ ) { + rc = device_create_file(kit->dev, &dev_sensor_attrs[i]); + if (rc) + goto out4; } - if (ifkit->sensors == 8) - device_create_file(kit->dev, &dev_attr_sensor8); - if (ifkit->has_lcd) - device_create_file(kit->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) @@ -629,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); @@ -641,68 +664,19 @@ static void interfacekit_disconnect(struct usb_interface *interface) cancel_delayed_work(&kit->do_notify); - if (kit->ifkit->outputs >= 4) { - device_remove_file(kit->dev, &dev_attr_output1); - device_remove_file(kit->dev, &dev_attr_output2); - device_remove_file(kit->dev, &dev_attr_output3); - device_remove_file(kit->dev, &dev_attr_output4); - } - if (kit->ifkit->outputs >= 8) { - device_remove_file(kit->dev, &dev_attr_output5); - device_remove_file(kit->dev, &dev_attr_output6); - device_remove_file(kit->dev, &dev_attr_output7); - device_remove_file(kit->dev, &dev_attr_output8); - } - if (kit->ifkit->outputs == 16) { - device_remove_file(kit->dev, &dev_attr_output9); - device_remove_file(kit->dev, &dev_attr_output10); - device_remove_file(kit->dev, &dev_attr_output11); - device_remove_file(kit->dev, &dev_attr_output12); - device_remove_file(kit->dev, &dev_attr_output13); - device_remove_file(kit->dev, &dev_attr_output14); - device_remove_file(kit->dev, &dev_attr_output15); - device_remove_file(kit->dev, &dev_attr_output16); - } + for (i=0; iifkit->outputs; i++) + device_remove_file(kit->dev, &dev_output_attrs[i]); - if (kit->ifkit->inputs >= 4) { - device_remove_file(kit->dev, &dev_attr_input1); - device_remove_file(kit->dev, &dev_attr_input2); - device_remove_file(kit->dev, &dev_attr_input3); - device_remove_file(kit->dev, &dev_attr_input4); - } - if (kit->ifkit->inputs >= 8) { - device_remove_file(kit->dev, &dev_attr_input5); - device_remove_file(kit->dev, &dev_attr_input6); - device_remove_file(kit->dev, &dev_attr_input7); - device_remove_file(kit->dev, &dev_attr_input8); - } - if (kit->ifkit->inputs == 16) { - device_remove_file(kit->dev, &dev_attr_input9); - device_remove_file(kit->dev, &dev_attr_input10); - device_remove_file(kit->dev, &dev_attr_input11); - device_remove_file(kit->dev, &dev_attr_input12); - device_remove_file(kit->dev, &dev_attr_input13); - device_remove_file(kit->dev, &dev_attr_input14); - device_remove_file(kit->dev, &dev_attr_input15); - device_remove_file(kit->dev, &dev_attr_input16); - } + for (i=0; iifkit->inputs; i++) + device_remove_file(kit->dev, &dev_input_attrs[i]); - if (kit->ifkit->sensors >= 4) { - device_remove_file(kit->dev, &dev_attr_sensor1); - device_remove_file(kit->dev, &dev_attr_sensor2); - device_remove_file(kit->dev, &dev_attr_sensor3); - device_remove_file(kit->dev, &dev_attr_sensor4); - } - if (kit->ifkit->sensors >= 7) { - device_remove_file(kit->dev, &dev_attr_sensor5); - device_remove_file(kit->dev, &dev_attr_sensor6); - device_remove_file(kit->dev, &dev_attr_sensor7); - } - if (kit->ifkit->sensors == 8) - device_remove_file(kit->dev, &dev_attr_sensor8); + for (i=0; iifkit->sensors; i++) + device_remove_file(kit->dev, &dev_sensor_attrs[i]); - if (kit->ifkit->has_lcd) + if (kit->ifkit->has_lcd) { device_remove_file(kit->dev, &dev_attr_lcd); + remove_lcd_files(kit); + } device_unregister(kit->dev); diff --git a/drivers/usb/misc/phidgetmotorcontrol.c b/drivers/usb/misc/phidgetmotorcontrol.c index d9ac7f97f8c8..6b59b620d616 100644 --- a/drivers/usb/misc/phidgetmotorcontrol.c +++ b/drivers/usb/misc/phidgetmotorcontrol.c @@ -215,9 +215,12 @@ static ssize_t show_speed##value(struct device *dev, \ struct motorcontrol *mc = dev_get_drvdata(dev); \ \ return sprintf(buf, "%d\n", mc->speed[value]); \ -} \ -static DEVICE_ATTR(speed##value, S_IWUGO | S_IRUGO, \ - show_speed##value, set_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); @@ -250,9 +253,12 @@ static ssize_t show_acceleration##value(struct device *dev, \ struct motorcontrol *mc = dev_get_drvdata(dev); \ \ return sprintf(buf, "%d\n", mc->acceleration[value]); \ -} \ -static DEVICE_ATTR(acceleration##value, S_IWUGO | S_IRUGO, \ - show_acceleration##value, set_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); @@ -264,8 +270,10 @@ static ssize_t show_current##value(struct device *dev, \ struct motorcontrol *mc = dev_get_drvdata(dev); \ \ return sprintf(buf, "%dmA\n", (int)mc->_current[value]); \ -} \ -static DEVICE_ATTR(current##value, S_IRUGO, show_current##value, NULL); +} + +#define current_attr(value) \ + __ATTR(current##value, S_IRUGO, show_current##value, NULL) show_current(0); show_current(1); @@ -278,14 +286,29 @@ static ssize_t show_input##value(struct device *dev, \ struct motorcontrol *mc = dev_get_drvdata(dev); \ \ return sprintf(buf, "%d\n", (int)mc->inputs[value]); \ -} \ -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(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); @@ -293,7 +316,7 @@ static int motorcontrol_probe(struct usb_interface *intf, const struct usb_devic struct usb_endpoint_descriptor *endpoint; struct motorcontrol *mc; int pipe, maxp, rc = -ENOMEM; - int bit, value; + int bit, value, i; interface = intf->cur_altsetting; if (interface->desc.bNumEndpoints != 1) @@ -355,24 +378,18 @@ static int motorcontrol_probe(struct usb_interface *intf, const struct usb_devic goto out; } - device_create_file(mc->dev, &dev_attr_input0); - device_create_file(mc->dev, &dev_attr_input1); - device_create_file(mc->dev, &dev_attr_input2); - device_create_file(mc->dev, &dev_attr_input3); - - device_create_file(mc->dev, &dev_attr_speed0); - device_create_file(mc->dev, &dev_attr_speed1); - - device_create_file(mc->dev, &dev_attr_acceleration0); - device_create_file(mc->dev, &dev_attr_acceleration1); - - device_create_file(mc->dev, &dev_attr_current0); - device_create_file(mc->dev, &dev_attr_current1); + for (i=0; idev, &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) @@ -393,6 +410,7 @@ out: static void motorcontrol_disconnect(struct usb_interface *interface) { struct motorcontrol *mc; + int i; mc = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); @@ -405,19 +423,8 @@ static void motorcontrol_disconnect(struct usb_interface *interface) cancel_delayed_work(&mc->do_notify); - device_remove_file(mc->dev, &dev_attr_input0); - device_remove_file(mc->dev, &dev_attr_input1); - device_remove_file(mc->dev, &dev_attr_input2); - device_remove_file(mc->dev, &dev_attr_input3); - - device_remove_file(mc->dev, &dev_attr_speed0); - device_remove_file(mc->dev, &dev_attr_speed1); - - device_remove_file(mc->dev, &dev_attr_acceleration0); - device_remove_file(mc->dev, &dev_attr_acceleration1); - - device_remove_file(mc->dev, &dev_attr_current0); - device_remove_file(mc->dev, &dev_attr_current1); + for (i=0; idev, &dev_attrs[i]); device_unregister(mc->dev); diff --git a/drivers/usb/misc/phidgetservo.c b/drivers/usb/misc/phidgetservo.c index 66be9513fd6f..7163f05c5b27 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 + * Copyright (C) 2004, 2006 Sean Young * * 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 @@ -235,86 +235,108 @@ static ssize_t show_servo##value (struct device *dev, \ \ 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; + 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); do { bit = find_first_zero_bit(&device_no, sizeof(device_no)); value = test_and_set_bit(bit, &device_no); - } while(value); + } 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)) { - int rc = PTR_ERR(dev->dev); - clear_bit(dev->dev_no, &device_no); - kfree(dev); - return rc; + rc = PTR_ERR(dev->dev); + dev->dev = NULL; + goto out; } - device_create_file(dev->dev, &dev_attr_servo0); - if (dev->type & SERVO_COUNT_QUAD) { - device_create_file(dev->dev, &dev_attr_servo1); - device_create_file(dev->dev, &dev_attr_servo2); - device_create_file(dev->dev, &dev_attr_servo3); + servo_count = dev->type & SERVO_COUNT_QUAD ? 4 : 1; + + for (i=0; idev, &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)) 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(dev->dev, &dev_attr_servo0); - if (dev->type & SERVO_COUNT_QUAD) { - device_remove_file(dev->dev, &dev_attr_servo1); - device_remove_file(dev->dev, &dev_attr_servo2); - device_remove_file(dev->dev, &dev_attr_servo3); - } + if (!dev) + return; + + servo_count = dev->type & SERVO_COUNT_QUAD ? 4 : 1; + + for (i=0; idev, &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); -- cgit v1.2.2 From 066202dd48cf3296b6cc22b5fcf89aef33fa0efc Mon Sep 17 00:00:00 2001 From: "Luiz Fernando N. Capitulino" Date: Sat, 5 Aug 2006 20:37:11 -0300 Subject: USB: Make file operations structs in drivers/usb const. Making structs const prevents accidental bugs and with the proper debug options they're protected against corruption. Signed-off-by: Luiz Fernando N. Capitulino Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/auerswald.c | 2 +- drivers/usb/misc/idmouse.c | 2 +- drivers/usb/misc/ldusb.c | 2 +- drivers/usb/misc/legousbtower.c | 2 +- drivers/usb/misc/sisusbvga/sisusb.c | 2 +- drivers/usb/misc/usblcd.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/usb/misc') diff --git a/drivers/usb/misc/auerswald.c b/drivers/usb/misc/auerswald.c index 1fef36e71c57..4a329d8488b7 100644 --- a/drivers/usb/misc/auerswald.c +++ b/drivers/usb/misc/auerswald.c @@ -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/idmouse.c b/drivers/usb/misc/idmouse.c index fcd69c52aea9..8e6e195a22ba 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 f30ab1fbb3c8..741736b6c134 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, diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 7699d970e680..77c36e63c7bf 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/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index e16582f3733c..a44124c7e851 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/usblcd.c b/drivers/usb/misc/usblcd.c index e095772dd8e9..53e59e362330 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, -- cgit v1.2.2 From b0b660b8d5b446bf26a46b233adef5819d9a683c Mon Sep 17 00:00:00 2001 From: "Luiz Fernando N. Capitulino" Date: Wed, 27 Sep 2006 11:58:54 -0700 Subject: USB: usblcd: Use usb_endpoint_* functions. Signed-off-by: Luiz Fernando N. Capitulino Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usblcd.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/usb/misc') diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index 53e59e362330..dbaca9f1efad 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c @@ -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; } -- cgit v1.2.2 From 5482687b8be4dedb8a5879f07c734ff11a88a7d5 Mon Sep 17 00:00:00 2001 From: "Luiz Fernando N. Capitulino" Date: Wed, 27 Sep 2006 11:58:54 -0700 Subject: USB: ldusb: Use usb_endpoint_* functions. Signed-off-by: Luiz Fernando N. Capitulino Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/ldusb.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/usb/misc') diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index 741736b6c134..10b640339d8d 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -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"); -- cgit v1.2.2 From 4d42e1bb9e8ec7eb5e39d82aaf3ff2f3c994af84 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 28 Aug 2006 11:43:25 -0700 Subject: USB: fix __must_check warnings in drivers/usb/misc/ Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/cypress_cy7c63.c | 19 +++++++++++++++---- drivers/usb/misc/cytherm.c | 35 ++++++++++++++++++++++++++--------- drivers/usb/misc/usbled.c | 20 ++++++++++++++++---- 3 files changed, 57 insertions(+), 17 deletions(-) (limited to 'drivers/usb/misc') diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c index 9c46746d5d00..b63b5f34b2aa 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 b20bec445552..04e87acd6e46 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/usbled.c b/drivers/usb/misc/usbled.c index 0c5ee0ad6bb9..49c5c5c4c431 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; } -- cgit v1.2.2 From 03270634e242dd10cc8569d31a00659d25b2b8e7 Mon Sep 17 00:00:00 2001 From: Steven Haigh Date: Wed, 9 Aug 2006 07:42:06 +1000 Subject: USB: Add ADU support for Ontrak ADU devices This patch adds support for Ontrak ADU USB devices. Fixed for printk issues by Randy Dunlap Signed-off-by: Steven Haigh Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/Kconfig | 10 + drivers/usb/misc/Makefile | 1 + drivers/usb/misc/adutux.c | 900 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 911 insertions(+) create mode 100644 drivers/usb/misc/adutux.c (limited to 'drivers/usb/misc') diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 20539cf9394b..288d301d2bff 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 diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index d12a84a1c0a4..73fc8be0d8c0 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -3,6 +3,7 @@ # (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 diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c new file mode 100644 index 000000000000..d3963199b6ec --- /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 + * 2001 Juergen Stuber + * that was derived from USB Skeleton driver - 0.5 + * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#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"); -- cgit v1.2.2 From a5c66e4b2418278786a025a5bd9625f485b2087a Mon Sep 17 00:00:00 2001 From: Tony Olech Date: Wed, 13 Sep 2006 11:26:04 +0100 Subject: USB: ftdi-elan: client driver for ELAN Uxxx adapters This "ftdi-elan" module is one half of the "driver" for ELAN's Uxxx series adapters which are USB to PCMCIA CardBus adapters. Currently only the U132 adapter is available and it's module is called "u132-hcd". When the USB hot plug subsystem detects a Uxxx series adapter it should load this module. Upon a successful device probe() the jtag device file interface is created and the status workqueue started up. The jtag device file interface exists for the purpose of updating the firmware in the Uxxx series adapter, but as yet it had never been used. The status workqueue initializes the Uxxx and then sits there polling the Uxxx until a supported PCMCIA CardBus device is detected it will start the command and respond workqueues and then load the module that handles the device. This will initially be only the u132-hcd module. The status workqueue then just polls the Uxxx looking for card ejects. The command and respond workqueues implement a command sequencer for communicating with the firmware on the other side of the FTDI chip in the Uxxx. This "ftdi-elan" module exports some functions to interface with the sequencer. Note that this module is a USB client driver. Note that the "u132-hcd" module is a (cut-down OHCI) host controller. Thus we have a topology with the parent of a host controller being a USB client! This really stresses the USB subsystem semaphore/mutex handling in the module removal. Signed-off-by: Tony Olech Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/Kconfig | 24 + drivers/usb/misc/Makefile | 2 +- drivers/usb/misc/ftdi-elan.c | 2809 ++++++++++++++++++++++++++++++++++++++++++ drivers/usb/misc/usb_u132.h | 97 ++ 4 files changed, 2931 insertions(+), 1 deletion(-) create mode 100644 drivers/usb/misc/ftdi-elan.c create mode 100644 drivers/usb/misc/usb_u132.h (limited to 'drivers/usb/misc') diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 288d301d2bff..c29658f69e2a 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -178,6 +178,30 @@ config USB_IDMOUSE See also . +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 73fc8be0d8c0..2be70fa259bf 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -9,6 +9,7 @@ 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 @@ -21,7 +22,6 @@ 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/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c new file mode 100644 index 000000000000..b88a09497c28 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +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<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/usb_u132.h b/drivers/usb/misc/usb_u132.h new file mode 100644 index 000000000000..551ba8906d62 --- /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); -- cgit v1.2.2 From 38e2bfc94e95dd6005fdaf40dfec0157396741da Mon Sep 17 00:00:00 2001 From: Pete Zaitcev Date: Mon, 18 Sep 2006 22:49:02 -0700 Subject: USB: Dealias -110 code (more complete) The purpose of this patch is to split off the case when a device does not reply on the lower level (which is reported by HC hardware), and a case when the device accepted the request, but does not reply at upper level. This redefinition allows to diagnose issues easier, without asking the user if the -110 happened "immediately". The usbmon splits such cases already thanks to its timestamp, but it's not always available. I adjusted all drivers which I found affected (by searching for "urb"). Out of tree drivers may suffer a little bit, but I do not expect much breakage. At worst they may print a few messages. Signed-off-by: Pete Zaitcev Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/auerswald.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb/misc') diff --git a/drivers/usb/misc/auerswald.c b/drivers/usb/misc/auerswald.c index 4a329d8488b7..4fd2110b3411 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: -- cgit v1.2.2