summaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-03-27 23:54:12 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-03-27 23:54:12 +0000
commitd3e71cb08747743fce908122bab08b479eb403a5 (patch)
treecbec6948fdbdee9af81cf3ecfb504070d2745d7b /drivers/usb
parentfe7ff1706e323d0e5ed83972960a1ecc1ee538b3 (diff)
Merge with Linux 2.3.99-pre3.
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/Config.in1
-rw-r--r--drivers/usb/Makefile1
-rw-r--r--drivers/usb/hid-debug.h1
-rw-r--r--drivers/usb/hid.c54
-rw-r--r--drivers/usb/hid.h8
-rw-r--r--drivers/usb/keybdev.c21
-rw-r--r--drivers/usb/mdc800.c931
-rw-r--r--drivers/usb/mousedev.c4
-rw-r--r--drivers/usb/pegasus.c42
-rw-r--r--drivers/usb/serial/Makefile-keyspan_pda_fw2
-rw-r--r--drivers/usb/serial/keyspan_pda.S1124
-rw-r--r--drivers/usb/serial/usb-serial.c857
-rw-r--r--drivers/usb/serial/usb-serial.h116
-rw-r--r--drivers/usb/uhci.c21
-rw-r--r--drivers/usb/usb-ohci.c97
-rw-r--r--drivers/usb/usb-ohci.h137
-rw-r--r--drivers/usb/usb-storage.c324
-rw-r--r--drivers/usb/usb-uhci.c32
-rw-r--r--drivers/usb/usb.c49
-rw-r--r--drivers/usb/wacom.c485
20 files changed, 3243 insertions, 1064 deletions
diff --git a/drivers/usb/Config.in b/drivers/usb/Config.in
index 012a27187..28ad96481 100644
--- a/drivers/usb/Config.in
+++ b/drivers/usb/Config.in
@@ -43,6 +43,7 @@ comment 'USB Devices'
dep_tristate ' USB OV511 Camera support' CONFIG_USB_OV511 $CONFIG_USB
dep_tristate ' USB Kodak DC-2xx Camera support' CONFIG_USB_DC2XX $CONFIG_USB
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ dep_tristate ' USB Mustek MDC800 Digital Camera support (EXPERIMENTAL)' CONFIG_USB_MDC800 $CONFIG_USB
dep_tristate ' USB Mass Storage support (EXPERIMENTAL)' CONFIG_USB_STORAGE $CONFIG_USB m
if [ "$CONFIG_USB_STORAGE" != "n" ]; then
bool ' USB Mass Storage verbose debug' CONFIG_USB_STORAGE_DEBUG
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 6e9e99714..06b0a4648 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -74,6 +74,7 @@ obj-$(CONFIG_USB_AUDIO) += audio.o
obj-$(CONFIG_USB_CPIA) += cpia.o
obj-$(CONFIG_USB_IBMCAM) += ibmcam.o
obj-$(CONFIG_USB_DC2XX) += dc2xx.o
+obj-$(CONFIG_USB_MDC800) += mdc800.o
obj-$(CONFIG_USB_STORAGE) += usb-storage.o
obj-$(CONFIG_USB_USS720) += uss720.o
obj-$(CONFIG_USB_DABUSB) += dabusb.o
diff --git a/drivers/usb/hid-debug.h b/drivers/usb/hid-debug.h
index 8aaf4be81..b72565fb9 100644
--- a/drivers/usb/hid-debug.h
+++ b/drivers/usb/hid-debug.h
@@ -105,6 +105,7 @@ static struct hid_usage_entry hid_usage_table[] = {
{0, 0x35, "Tap"},
{0, 0x39, "TabletFunctionKey"},
{0, 0x3a, "ProgramChangeKey"},
+ {0, 0x3c, "Invert"},
{0, 0x42, "TipSwitch"},
{0, 0x43, "SecondaryTipSwitch"},
{0, 0x44, "BarrelSwitch"},
diff --git a/drivers/usb/hid.c b/drivers/usb/hid.c
index c7bb34e4d..b8af56051 100644
--- a/drivers/usb/hid.c
+++ b/drivers/usb/hid.c
@@ -802,6 +802,11 @@ static void hid_configure_usage(struct hid_device *device, struct hid_field *fie
case 0x30: /* TipPressure */
+ if (!test_bit(BTN_TOUCH, input->keybit)) {
+ device->quirks |= HID_QUIRK_NOTOUCH;
+ set_bit(EV_KEY, input->evbit);
+ set_bit(BTN_TOUCH, input->keybit);
+ }
usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX;
usage->code = ABS_PRESSURE;
clear_bit(usage->code, bit);
@@ -817,10 +822,18 @@ static void hid_configure_usage(struct hid_device *device, struct hid_field *fie
}
break;
+ case 0x3c: /* Invert */
+
+ usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
+ usage->code = BTN_TOOL_RUBBER;
+ clear_bit(usage->code, bit);
+ break;
+
case 0x33: /* Touch */
case 0x42: /* TipSwitch */
case 0x43: /* TipSwitch2 */
+ device->quirks &= ~HID_QUIRK_NOTOUCH;
usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
usage->code = BTN_TOUCH;
clear_bit(usage->code, bit);
@@ -930,7 +943,7 @@ static void hid_configure_usage(struct hid_device *device, struct hid_field *fie
}
}
-static void hid_process_event(struct input_dev *input, int flags, struct hid_usage *usage, __s32 value)
+static void hid_process_event(struct input_dev *input, int *quirks, struct hid_field *field, struct hid_usage *usage, __s32 value)
{
hid_dump_input(usage, value);
@@ -941,9 +954,30 @@ static void hid_process_event(struct input_dev *input, int flags, struct hid_usa
return;
}
+ if (usage->hid == (HID_UP_DIGITIZER | 0x003c)) { /* Invert */
+ *quirks = value ? (*quirks | HID_QUIRK_INVERT) : (*quirks & ~HID_QUIRK_INVERT);
+ return;
+ }
+
+ if (usage->hid == (HID_UP_DIGITIZER | 0x0032)) { /* InRange */
+ if (value) {
+ input_event(input, usage->type, (*quirks & HID_QUIRK_INVERT) ? BTN_TOOL_RUBBER : usage->code, 1);
+ return;
+ }
+ input_event(input, usage->type, usage->code, 0);
+ input_event(input, usage->type, BTN_TOOL_RUBBER, 0);
+ return;
+ }
+
+ if (usage->hid == (HID_UP_DIGITIZER | 0x0030) && (*quirks & HID_QUIRK_NOTOUCH)) { /* Pressure */
+ int a = field->logical_minimum;
+ int b = field->logical_maximum;
+ input_event(input, EV_KEY, BTN_TOUCH, value > a + ((b - a) >> 3));
+ }
+
input_event(input, usage->type, usage->code, value);
- if ((flags & HID_MAIN_ITEM_RELATIVE) && (usage->type == EV_KEY))
+ if ((field->flags & HID_MAIN_ITEM_RELATIVE) && (usage->type == EV_KEY))
input_event(input, usage->type, usage->code, 0);
}
@@ -986,19 +1020,21 @@ static void hid_input_field(struct hid_device *dev, struct hid_field *field, __u
} else {
if (value[n] == field->value[n]) continue;
}
- hid_process_event(&dev->input, field->flags, &field->usage[n], value[n]);
+ hid_process_event(&dev->input, &dev->quirks, field, &field->usage[n], value[n]);
} else {
if (field->value[n] >= min && field->value[n] <= max /* non-NULL value */
&& field->usage[field->value[n] - min].hid /* nonzero usage */
&& search(value, field->value[n], count))
- hid_process_event(&dev->input, field->flags, &field->usage[field->value[n] - min], 0);
+ hid_process_event(&dev->input, &dev->quirks, field,
+ &field->usage[field->value[n] - min], 0);
if (value[n] >= min && value[n] <= max /* non-NULL value */
&& field->usage[value[n] - min].hid /* nonzero usage */
&& search(field->value, value[n], count))
- hid_process_event(&dev->input, field->flags, &field->usage[value[n] - min], 1);
+ hid_process_event(&dev->input, &dev->quirks,
+ field, &field->usage[value[n] - min], 1);
}
}
@@ -1261,14 +1297,18 @@ static void hid_init_input(struct hid_device *hid)
#define USB_VENDOR_ID_WACOM 0x056a
#define USB_DEVICE_ID_WACOM_GRAPHIRE 0x0010
-#define USB_DEVICE_ID_WACOM_INTUOS 0x0021
+#define USB_DEVICE_ID_WACOM_INTUOS 0x0020
struct hid_blacklist {
__u16 idVendor;
__u16 idProduct;
} hid_blacklist[] = {
- { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 1},
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 2},
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 3},
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 4},
{ 0, 0 }
};
diff --git a/drivers/usb/hid.h b/drivers/usb/hid.h
index 4a4caf401..18e7481c2 100644
--- a/drivers/usb/hid.h
+++ b/drivers/usb/hid.h
@@ -179,6 +179,13 @@ struct hid_item {
#define HID_FEATURE_REPORT 2
/*
+ * HID device quirks.
+ */
+
+#define HID_QUIRK_INVERT 0x01
+#define HID_QUIRK_NOTOUCH 0x02
+
+/*
* This is the global enviroment of the parser. This information is
* persistent for main-items. The global enviroment can be saved and
* restored with PUSH/POP statements.
@@ -285,6 +292,7 @@ struct hid_device { /* device report descriptor */
struct urb urb; /* USB URB structure */
struct urb urbout; /* Output URB */
struct input_dev input; /* input device structure */
+ int quirks; /* Various nasty tricks the device can pull on us */
};
#define HID_GLOBAL_STACK_SIZE 4
diff --git a/drivers/usb/keybdev.c b/drivers/usb/keybdev.c
index 2aef212cb..7c94c185b 100644
--- a/drivers/usb/keybdev.c
+++ b/drivers/usb/keybdev.c
@@ -58,7 +58,8 @@ static unsigned char keybdev_mac_codes[256] =
#endif
-struct input_handler keybdev_handler;
+static struct input_handler keybdev_handler;
+static int keybdev_alt = 0;
void keybdev_ledfunc(unsigned int led)
{
@@ -93,14 +94,22 @@ void keybdev_event(struct input_handle *handle, unsigned int type, unsigned int
handle_scancode(0x1d, down);
handle_scancode(0x45, down);
} else if (code >= 96) {
- handle_scancode(0xe0, 1);
- handle_scancode(keybdev_x86_e0s[code - 96], down);
- if (code == 99) {
+ if (code == 99 && keybdev_alt) {
+ handle_scancode(84, down);
+ } else {
handle_scancode(0xe0, 1);
- handle_scancode(0x37, down);
+ handle_scancode(keybdev_x86_e0s[code - 96], down);
+ if (code == 99) {
+ handle_scancode(0xe0, 1);
+ handle_scancode(0x37, down);
+ }
}
+ } else if (code == 84) {
+ handle_scancode(43, down);
} else handle_scancode(code, down);
+ if (code == 56 || code == 100) keybdev_alt = down;
+
#elif CONFIG_ADB_KEYBOARD
if (code < 128 && keybdev_mac_codes[code])
@@ -152,7 +161,7 @@ static void keybdev_disconnect(struct input_handle *handle)
kfree(handle);
}
-struct input_handler keybdev_handler = {
+static struct input_handler keybdev_handler = {
event: keybdev_event,
connect: keybdev_connect,
disconnect: keybdev_disconnect,
diff --git a/drivers/usb/mdc800.c b/drivers/usb/mdc800.c
new file mode 100644
index 000000000..c8063c927
--- /dev/null
+++ b/drivers/usb/mdc800.c
@@ -0,0 +1,931 @@
+/*
+ * copyright (C) 1999/2000 by Henning Zabel <henning@uni-paderborn.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+/*
+ * USB-Kernel Driver for the Mustek MDC800 Digital Camera
+ * (c) 1999/2000 Henning Zabel <henning@uni-paderborn.de>
+ *
+ *
+ * The driver brings the USB functions of the MDC800 to Linux.
+ * To use the Camera you must support the USB Protocoll of the camera
+ * to the Kernel Node.
+ * The Driver uses a misc device Node. Create it with :
+ * mknod /dev/mustek c 10 171
+ *
+ * The driver supports only one camera.
+ *
+ * version 0.7.1
+ * The Init und Exit Module Function are updated.
+ * (01/03/2000)
+ *
+ * version 0.7.0
+ * Rewrite of the driver : The driver now uses URB's. The old stuff
+ * has been removed.
+ *
+ * version 0.6.0
+ * Rewrite of this driver: The Emulation of the rs232 protocoll
+ * has been removed from the driver. A special executeCommand function
+ * for this driver is included to gphoto.
+ * The driver supports two kind of communication to bulk endpoints.
+ * Either with the dev->bus->ops->bulk... or with callback function.
+ * (09/11/1999)
+ *
+ * version 0.5.0:
+ * first Version that gets a version number. Most of the needed
+ * functions work.
+ * (20/10/1999)
+ */
+
+#include <linux/version.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/module.h>
+
+#include <linux/usb.h>
+
+#define VERSION "0.7.1"
+#define RELEASE_DATE "(01/03/2000)"
+
+/* Vendor and Product Information */
+#define MDC800_VENDOR_ID 0x055f
+#define MDC800_PRODUCT_ID 0xa800
+
+/* Timeouts (msec) */
+#define TO_READ_FROM_IRQ 4000
+#define TO_GET_READY 2000
+#define TO_DOWNLOAD_GET_READY 1500
+#define TO_DOWNLOAD_GET_BUSY 1500
+#define TO_WRITE_GET_READY 3000
+#define TO_DEFAULT_COMMAND 5000
+
+/* Minor Number of the device (create with mknod /dev/mustek c 10 171) */
+#define MDC800_DEVICE_MINOR 171
+
+
+/**************************************************************************
+ Data and structs
+***************************************************************************/
+
+
+typedef enum {
+ NOT_CONNECTED, READY, WORKING, DOWNLOAD
+} mdc800_state;
+
+
+/* Data for the driver */
+struct mdc800_data
+{
+ struct usb_device * dev; // Device Data
+ mdc800_state state;
+
+ unsigned int endpoint [4];
+
+ purb_t irq_urb;
+ wait_queue_head_t irq_wait;
+ char* irq_urb_buffer;
+
+ int camera_busy; // is camera busy ?
+ int camera_request_ready; // Status to synchronize with irq
+ char camera_response [8]; // last Bytes send after busy
+
+ purb_t write_urb;
+ char* write_urb_buffer;
+ wait_queue_head_t write_wait;
+
+
+ purb_t download_urb;
+ char* download_urb_buffer;
+ wait_queue_head_t download_wait;
+ int download_left; // Bytes left to download ?
+
+
+ /* Device Data */
+ char out [64]; // Answer Buffer
+ int out_ptr; // Index to the first not readen byte
+ int out_count; // Bytes in the buffer
+
+ int open; // Camera device open ?
+ int rw_lock; // Block read <-> write
+
+ char in [8]; // Command Input Buffer
+ int in_count;
+
+ int pic_index; // Cache for the Imagesize (-1 for nothing cached )
+ int pic_len;
+};
+
+
+/* Specification of the Endpoints */
+static struct usb_endpoint_descriptor mdc800_ed [4] =
+{
+ { 0,0, 0x01, 0x02, 8, 0,0,0 },
+ { 0,0, 0x82, 0x03, 8, 0,0,0 },
+ { 0,0, 0x03, 0x02, 64, 0,0,0 },
+ { 0,0, 0x84, 0x02, 64, 0,0,0 }
+};
+
+
+/* The Variable used by the driver */
+static struct mdc800_data* mdc800=0;
+
+
+/***************************************************************************
+ The USB Part of the driver
+****************************************************************************/
+
+static int mdc800_endpoint_equals (struct usb_endpoint_descriptor *a,struct usb_endpoint_descriptor *b)
+{
+ return (
+ ( a->bEndpointAddress == b->bEndpointAddress )
+ && ( a->bmAttributes == b->bmAttributes )
+ && ( a->wMaxPacketSize == b->wMaxPacketSize )
+ );
+}
+
+
+/*
+ * Checks wether the camera responds busy
+ */
+static int mdc800_isBusy (char* ch)
+{
+ int i=0;
+ while (i<8)
+ {
+ if (ch [i] != (char)0x99)
+ return 0;
+ i++;
+ }
+ return 1;
+}
+
+
+/*
+ * Checks wether the Camera is ready
+ */
+static int mdc800_isReady (char *ch)
+{
+ int i=0;
+ while (i<8)
+ {
+ if (ch [i] != (char)0xbb)
+ return 0;
+ i++;
+ }
+ return 1;
+}
+
+
+
+/*
+ * USB IRQ Handler for InputLine
+ */
+static void mdc800_usb_irq (struct urb *urb)
+{
+ int data_received=0, wake_up;
+ unsigned char* b=urb->transfer_buffer;
+ struct mdc800_data* mdc800=urb->context;
+
+ if (urb->status >= 0)
+ {
+
+ //dbg ("%i %i %i %i %i %i %i %i \n",b[0],b[1],b[2],b[3],b[4],b[5],b[6],b[7]);
+
+ if (mdc800_isBusy (b))
+ {
+ if (!mdc800->camera_busy)
+ {
+ mdc800->camera_busy=1;
+ dbg ("gets busy");
+ }
+ }
+ else
+ {
+ if (mdc800->camera_busy && mdc800_isReady (b))
+ {
+ mdc800->camera_busy=0;
+ dbg ("gets ready");
+ }
+ }
+ if (!(mdc800_isBusy (b) || mdc800_isReady (b)))
+ {
+ /* Store Data in camera_answer field */
+ dbg ("%i %i %i %i %i %i %i %i ",b[0],b[1],b[2],b[3],b[4],b[5],b[6],b[7]);
+
+ memcpy (mdc800->camera_response,b,8);
+ data_received=1;
+ }
+ }
+ wake_up= ( mdc800->camera_request_ready > 0 )
+ &&
+ (
+ ((mdc800->camera_request_ready == 1) && (!mdc800->camera_busy))
+ ||
+ ((mdc800->camera_request_ready == 2) && data_received)
+ ||
+ ((mdc800->camera_request_ready == 3) && (mdc800->camera_busy))
+ ||
+ (urb->status < 0)
+ );
+
+ if (wake_up)
+ {
+ mdc800->camera_request_ready=0;
+ wake_up_interruptible (&mdc800->irq_wait);
+ }
+}
+
+
+/*
+ * Waits a while until the irq responds that camera is ready
+ *
+ * mode : 0: Wait for camera gets ready
+ * 1: Wait for receiving data
+ * 2: Wait for camera gets busy
+ *
+ * msec: Time to wait
+ */
+static int mdc800_usb_waitForIRQ (int mode, int msec)
+{
+ mdc800->camera_request_ready=1+mode;
+
+ interruptible_sleep_on_timeout (&mdc800->irq_wait, msec*HZ/1000);
+
+ if (mdc800->camera_request_ready>0)
+ {
+ mdc800->camera_request_ready=0;
+ err ("timeout waiting for camera.");
+ return 0;
+ }
+ return 1;
+}
+
+
+/*
+ * The write_urb callback function
+ */
+static void mdc800_usb_write_notify (struct urb *urb)
+{
+ struct mdc800_data* mdc800=urb->context;
+
+ if (urb->status != 0)
+ {
+ err ("writing command fails (status=%i)", urb->status);
+ }
+ mdc800->state=READY;
+ wake_up_interruptible (&mdc800->write_wait);
+}
+
+
+/*
+ * The download_urb callback function
+ */
+static void mdc800_usb_download_notify (struct urb *urb)
+{
+ struct mdc800_data* mdc800=urb->context;
+
+ if (urb->status == 0)
+ {
+ /* Fill output buffer with these data */
+ memcpy (mdc800->out, urb->transfer_buffer, 64);
+ mdc800->out_count=64;
+ mdc800->out_ptr=0;
+ mdc800->download_left-=64;
+ if (mdc800->download_left == 0)
+ {
+ mdc800->state=READY;
+ }
+ }
+ else
+ {
+ err ("request bytes fails (status:%i)", urb->status);
+ mdc800->state=READY;
+ }
+ wake_up_interruptible (&mdc800->download_wait);
+}
+
+
+/***************************************************************************
+ Probing for the Camera
+ ***************************************************************************/
+
+static struct usb_driver mdc800_usb_driver;
+
+/*
+ * Callback to search the Mustek MDC800 on the USB Bus
+ */
+static void* mdc800_usb_probe (struct usb_device *dev ,unsigned int ifnum )
+{
+ int i,j;
+ struct usb_interface_descriptor *intf_desc;
+ int irq_interval=0;
+
+ dbg ("(mdc800_usb_probe) called.");
+
+ if (mdc800->dev != 0)
+ {
+ warn ("only one Mustek MDC800 is supported.");
+ return 0;
+ }
+
+ if (dev->descriptor.idVendor != MDC800_VENDOR_ID)
+ return 0;
+ if (dev->descriptor.idProduct != MDC800_PRODUCT_ID)
+ return 0;
+
+ if (dev->descriptor.bNumConfigurations != 1)
+ {
+ err ("probe fails -> wrong Number of Configuration");
+ return 0;
+ }
+ intf_desc=&dev->actconfig->interface[ifnum].altsetting[0];
+
+ if (
+ ( intf_desc->bInterfaceClass != 0xff )
+ || ( intf_desc->bInterfaceSubClass != 0 )
+ || ( intf_desc->bInterfaceProtocol != 0 )
+ || ( intf_desc->bNumEndpoints != 4)
+ )
+ {
+ err ("probe fails -> wrong Interface");
+ return 0;
+ }
+
+ /* Check the Endpoints */
+ for (i=0; i<4; i++)
+ {
+ mdc800->endpoint[i]=-1;
+ for (j=0; j<4; j++)
+ {
+ if (mdc800_endpoint_equals (&intf_desc->endpoint [j],&mdc800_ed [i]))
+ {
+ mdc800->endpoint[i]=intf_desc->endpoint [j].bEndpointAddress ;
+ if (i==1)
+ {
+ irq_interval=intf_desc->endpoint [j].bInterval;
+ }
+
+ continue;
+ }
+ }
+ if (mdc800->endpoint[i] == -1)
+ {
+ err ("probe fails -> Wrong Endpoints.");
+ return 0;
+ }
+ }
+
+
+ usb_driver_claim_interface (&mdc800_usb_driver, &dev->actconfig->interface[ifnum], mdc800);
+ if (usb_set_interface (dev, ifnum, 0) < 0)
+ {
+ err ("MDC800 Configuration fails.");
+ return 0;
+ }
+
+ info ("Found Mustek MDC800 on USB.");
+
+ mdc800->dev=dev;
+ mdc800->state=READY;
+
+ /* Setup URB Structs */
+ FILL_INT_URB (
+ mdc800->irq_urb,
+ mdc800->dev,
+ usb_rcvintpipe (mdc800->dev,mdc800->endpoint [1]),
+ mdc800->irq_urb_buffer,
+ 8,
+ mdc800_usb_irq,
+ mdc800,
+ irq_interval
+ );
+
+ FILL_BULK_URB (
+ mdc800->write_urb,
+ mdc800->dev,
+ usb_sndbulkpipe (mdc800->dev, mdc800->endpoint[0]),
+ mdc800->write_urb_buffer,
+ 8,
+ mdc800_usb_write_notify,
+ mdc800
+ );
+
+ FILL_BULK_URB (
+ mdc800->download_urb,
+ mdc800->dev,
+ usb_rcvbulkpipe (mdc800->dev, mdc800->endpoint [3]),
+ mdc800->download_urb_buffer,
+ 64,
+ mdc800_usb_download_notify,
+ mdc800
+ );
+
+ return mdc800;
+}
+
+
+/*
+ * Disconnect USB device (maybe the MDC800)
+ */
+static void mdc800_usb_disconnect (struct usb_device *dev,void* ptr)
+{
+ struct mdc800_data* mdc800=(struct mdc800_data*) ptr;
+
+ dbg ("(mdc800_usb_disconnect) called");
+
+ if (mdc800->state == NOT_CONNECTED)
+ return;
+
+ mdc800->state=NOT_CONNECTED;
+ mdc800->open=0;
+ mdc800->rw_lock=0;
+
+ usb_unlink_urb (mdc800->irq_urb);
+ usb_unlink_urb (mdc800->write_urb);
+ usb_unlink_urb (mdc800->download_urb);
+
+ usb_driver_release_interface (&mdc800_usb_driver, &dev->actconfig->interface[1]);
+
+ mdc800->dev=0;
+ info ("Mustek MDC800 disconnected from USB.");
+}
+
+
+/***************************************************************************
+ The Misc device Part (file_operations)
+****************************************************************************/
+
+/*
+ * This Function calc the Answersize for a command.
+ */
+static int mdc800_getAnswerSize (char command)
+{
+ switch ((unsigned char) command)
+ {
+ case 0x2a:
+ case 0x49:
+ case 0x51:
+ case 0x0d:
+ case 0x20:
+ case 0x07:
+ case 0x01:
+ case 0x25:
+ case 0x00:
+ return 8;
+
+ case 0x05:
+ case 0x3e:
+ return mdc800->pic_len;
+
+ case 0x09:
+ return 4096;
+
+ default:
+ return 0;
+ }
+}
+
+
+/*
+ * Init the device: (1) alloc mem (2) Increase MOD Count ..
+ */
+static int mdc800_device_open (struct inode* inode, struct file *file)
+{
+ int retval=0;
+ if (mdc800->state == NOT_CONNECTED)
+ return -EBUSY;
+
+ if (mdc800->open)
+ return -EBUSY;
+
+ mdc800->rw_lock=0;
+ mdc800->in_count=0;
+ mdc800->out_count=0;
+ mdc800->out_ptr=0;
+ mdc800->pic_index=0;
+ mdc800->pic_len=-1;
+ mdc800->download_left=0;
+
+ mdc800->camera_busy=0;
+ mdc800->camera_request_ready=0;
+
+ retval=0;
+ if (usb_submit_urb (mdc800->irq_urb))
+ {
+ err ("request USB irq fails (submit_retval=%i urb_status=%i).",retval, mdc800->irq_urb->status);
+ return -EIO;
+ }
+
+ MOD_INC_USE_COUNT;
+ mdc800->open=1;
+
+ dbg ("Mustek MDC800 device opened.");
+ return 0;
+}
+
+
+/*
+ * Close the Camera and release Memory
+ */
+static int mdc800_device_release (struct inode* inode, struct file *file)
+{
+ int retval=0;
+ dbg ("Mustek MDC800 device closed.");
+
+ if (mdc800->open && (mdc800->state != NOT_CONNECTED))
+ {
+ mdc800->open=0;
+ usb_unlink_urb (mdc800->irq_urb);
+ usb_unlink_urb (mdc800->write_urb);
+ usb_unlink_urb (mdc800->download_urb);
+ }
+ else
+ {
+ retval=-EIO;
+ }
+
+ MOD_DEC_USE_COUNT;
+
+ return retval;
+}
+
+
+/*
+ * The Device read callback Function
+ */
+static ssize_t mdc800_device_read (struct file *file, char *buf, size_t len, loff_t *pos)
+{
+ int left=len, sts=len; /* single transfer size */
+ char* ptr=buf;
+
+ if (mdc800->state == NOT_CONNECTED)
+ return -EBUSY;
+
+ if (!mdc800->open || mdc800->rw_lock)
+ return -EBUSY;
+ mdc800->rw_lock=1;
+
+ while (left)
+ {
+ if (signal_pending (current)) {
+ mdc800->rw_lock=0;
+ return -EINTR;
+ }
+
+ sts=left > (mdc800->out_count-mdc800->out_ptr)?mdc800->out_count-mdc800->out_ptr:left;
+
+ if (sts <= 0)
+ {
+ /* Too less Data in buffer */
+ if (mdc800->state == DOWNLOAD)
+ {
+ mdc800->out_count=0;
+ mdc800->out_ptr=0;
+
+ /* Download -> Request new bytes */
+ if (usb_submit_urb (mdc800->download_urb))
+ {
+ err ("Can't submit download urb (status=%i)",mdc800->download_urb->status);
+ mdc800->state=READY;
+ mdc800->rw_lock=0;
+ return len-left;
+ }
+ interruptible_sleep_on_timeout (&mdc800->download_wait, TO_DOWNLOAD_GET_READY*HZ/1000);
+ if (mdc800->download_urb->status != 0)
+ {
+ err ("requesting bytes fails (status=%i)",mdc800->download_urb->status);
+ mdc800->state=READY;
+ mdc800->rw_lock=0;
+ return len-left;
+ }
+ }
+ else
+ {
+ /* No more bytes -> that's an error*/
+ mdc800->rw_lock=0;
+ return -EIO;
+ }
+ }
+ else
+ {
+ /* memcpy Bytes */
+ memcpy (ptr, &mdc800->out [mdc800->out_ptr], sts);
+ ptr+=sts;
+ left-=sts;
+ mdc800->out_ptr+=sts;
+ }
+ }
+
+ mdc800->rw_lock=0;
+ return len-left;
+}
+
+
+/*
+ * The Device write callback Function
+ * If a 8Byte Command is received, it will be send to the camera.
+ * After this the driver initiates the request for the answer or
+ * just waits until the camera becomes ready.
+ */
+static ssize_t mdc800_device_write (struct file *file, const char *buf, size_t len, loff_t *pos)
+{
+ int i=0;
+
+ if (mdc800->state != READY)
+ return -EBUSY;
+
+ if (!mdc800->open || mdc800->rw_lock)
+ return -EBUSY;
+ mdc800->rw_lock=1;
+
+ while (i<len)
+ {
+ if (signal_pending (current)) {
+ mdc800->rw_lock=0;
+ return -EINTR;
+ }
+
+ /* check for command start */
+ if (buf [i] == (char) 0x55)
+ {
+ mdc800->in_count=0;
+ mdc800->out_count=0;
+ mdc800->out_ptr=0;
+ mdc800->download_left=0;
+ }
+
+ /* save command byte */
+ if (mdc800->in_count < 8)
+ {
+ mdc800->in[mdc800->in_count]=buf[i];
+ mdc800->in_count++;
+ }
+ else
+ {
+ err ("Command is to long !\n");
+ mdc800->rw_lock=0;
+ return -EIO;
+ }
+
+ /* Command Buffer full ? -> send it to camera */
+ if (mdc800->in_count == 8)
+ {
+ int answersize;
+
+ mdc800_usb_waitForIRQ (0,TO_GET_READY);
+
+ answersize=mdc800_getAnswerSize (mdc800->in[1]);
+
+ mdc800->state=WORKING;
+ memcpy (mdc800->write_urb->transfer_buffer, mdc800->in,8);
+ if (usb_submit_urb (mdc800->write_urb))
+ {
+ err ("submitting write urb fails (status=%i)", mdc800->write_urb->status);
+ mdc800->rw_lock=0;
+ mdc800->state=READY;
+ return -EIO;
+ }
+ interruptible_sleep_on_timeout (&mdc800->write_wait, TO_DEFAULT_COMMAND*HZ/1000);
+ if (mdc800->state == WORKING)
+ {
+ usb_unlink_urb (mdc800->write_urb);
+ mdc800->state=READY;
+ mdc800->rw_lock=0;
+ return -EIO;
+ }
+
+ switch ((unsigned char) mdc800->in[1])
+ {
+ case 0x05: /* Download Image */
+ case 0x3e: /* Take shot in Fine Mode (WCam Mode) */
+ if (mdc800->pic_len < 0)
+ {
+ err ("call 0x07 before 0x05,0x3e");
+ mdc800->state=READY;
+ mdc800->rw_lock=0;
+ return -EIO;
+ }
+ mdc800->pic_len=-1;
+
+ case 0x09: /* Download Thumbnail */
+ mdc800->download_left=answersize+64;
+ mdc800->state=DOWNLOAD;
+ mdc800_usb_waitForIRQ (0,TO_DOWNLOAD_GET_BUSY);
+ break;
+
+
+ default:
+ if (answersize)
+ {
+
+ if (!mdc800_usb_waitForIRQ (1,TO_READ_FROM_IRQ))
+ {
+ err ("requesting answer from irq fails");
+ mdc800->state=READY;
+ mdc800->rw_lock=0;
+ return -EIO;
+ }
+
+ /* Write dummy data, (this is ugly but part of the USB Protokoll */
+ /* if you use endpoint 1 as bulk and not as irq */
+ memcpy (mdc800->out, mdc800->camera_response,8);
+
+ /* This is the interpreted answer */
+ memcpy (&mdc800->out[8], mdc800->camera_response,8);
+
+ mdc800->out_ptr=0;
+ mdc800->out_count=16;
+
+ /* Cache the Imagesize, if command was getImageSize */
+ if (mdc800->in [1] == (char) 0x07)
+ {
+ mdc800->pic_len=(int) 65536*(unsigned char) mdc800->camera_response[0]+256*(unsigned char) mdc800->camera_response[1]+(unsigned char) mdc800->camera_response[2];
+
+ dbg ("cached imagesize = %i",mdc800->pic_len);
+ }
+
+ }
+ else
+ {
+ if (!mdc800_usb_waitForIRQ (0,TO_DEFAULT_COMMAND))
+ {
+ err ("Command Timeout.");
+ mdc800->rw_lock=0;
+ mdc800->state=READY;
+ return -EIO;
+ }
+ }
+ mdc800->state=READY;
+ break;
+ }
+ }
+ i++;
+ }
+ mdc800->rw_lock=0;
+ return i;
+}
+
+
+/***************************************************************************
+ Init and Cleanup this driver (Structs and types)
+****************************************************************************/
+
+
+/*
+ * USB Driver Struct for this device
+ */
+static struct usb_driver mdc800_usb_driver =
+{
+ "mdc800",
+ mdc800_usb_probe,
+ mdc800_usb_disconnect,
+ { 0,0 },
+ 0,
+ 0
+};
+
+
+/* File Operations of this drivers */
+static struct file_operations mdc800_device_ops =
+{
+ 0, /* llseek */
+ mdc800_device_read,
+ mdc800_device_write,
+ 0, /* readdir */
+ 0, /* poll */
+ 0, /* ioctl, this can be used to detect USB ! */
+ 0, /* mmap */
+ mdc800_device_open,
+ 0, /* flush */
+ mdc800_device_release,
+ 0, /* async */
+ 0, /* fasync */
+ 0, /* check_media_change */
+// 0, /* revalidate */
+// 0 /* lock */
+};
+
+
+/*
+ * The Misc Device Configuration Struct
+ */
+static struct miscdevice mdc800_device =
+{
+ MDC800_DEVICE_MINOR,
+ "USB Mustek MDC800 Camera",
+ &mdc800_device_ops
+};
+
+
+/************************************************************************
+ Init and Cleanup this driver (Main Functions)
+*************************************************************************/
+
+#define try(A) if ((A) == 0) goto cleanup_on_fail;
+#define try_free_mem(A) if (A != 0) { kfree (A); A=0; }
+#define try_free_urb(A) if (A != 0) { usb_free_urb (A); A=0; }
+
+int __init usb_mdc800_init (void)
+{
+ /* Allocate Memory */
+ try (mdc800=kmalloc (sizeof (struct mdc800_data), GFP_KERNEL));
+
+ mdc800->dev=0;
+ mdc800->open=0;
+ mdc800->state=NOT_CONNECTED;
+ memset(mdc800, 0, sizeof(struct mdc800_data));
+
+ init_waitqueue_head (&mdc800->irq_wait);
+ init_waitqueue_head (&mdc800->write_wait);
+ init_waitqueue_head (&mdc800->download_wait);
+
+ try (mdc800->irq_urb_buffer=kmalloc (8, GFP_KERNEL));
+ try (mdc800->write_urb_buffer=kmalloc (8, GFP_KERNEL));
+ try (mdc800->download_urb_buffer=kmalloc (64, GFP_KERNEL));
+
+ try (mdc800->irq_urb=usb_alloc_urb (0));
+ try (mdc800->download_urb=usb_alloc_urb (0));
+ try (mdc800->write_urb=usb_alloc_urb (0));
+
+ /* Register the driver */
+ if (usb_register (&mdc800_usb_driver) < 0)
+ goto cleanup_on_fail;
+ if (misc_register (&mdc800_device) < 0)
+ goto cleanup_on_misc_register_fail;
+
+ info ("Mustek Digital Camera Driver " VERSION " (MDC800)");
+ info (RELEASE_DATE " Henning Zabel <henning@uni-paderborn.de>");
+
+ return 0;
+
+ /* Clean driver up, when something fails */
+
+cleanup_on_misc_register_fail:
+ usb_deregister (&mdc800_usb_driver);
+
+cleanup_on_fail:
+
+ if (mdc800 != 0)
+ {
+ err ("can't alloc memory!");
+
+ try_free_mem (mdc800->download_urb_buffer);
+ try_free_mem (mdc800->write_urb_buffer);
+ try_free_mem (mdc800->irq_urb_buffer);
+
+ try_free_urb (mdc800->write_urb);
+ try_free_urb (mdc800->download_urb);
+ try_free_urb (mdc800->irq_urb);
+
+ kfree (mdc800);
+ }
+ mdc800=0;
+ return -1;
+}
+
+
+void __exit usb_mdc800_cleanup (void)
+{
+ usb_deregister (&mdc800_usb_driver);
+ misc_deregister (&mdc800_device);
+
+ usb_free_urb (mdc800->irq_urb);
+ usb_free_urb (mdc800->download_urb);
+ usb_free_urb (mdc800->write_urb);
+
+ kfree (mdc800->irq_urb_buffer);
+ kfree (mdc800->write_urb_buffer);
+ kfree (mdc800->download_urb_buffer);
+
+ kfree (mdc800);
+ mdc800=0;
+}
+
+
+MODULE_AUTHOR ("Henning Zabel <henning@uni-paderborn.de>");
+MODULE_DESCRIPTION ("USB Driver for Mustek MDC800 Digital Camera");
+
+module_init (usb_mdc800_init);
+module_exit (usb_mdc800_cleanup);
diff --git a/drivers/usb/mousedev.c b/drivers/usb/mousedev.c
index 607782829..bfdc4ab3d 100644
--- a/drivers/usb/mousedev.c
+++ b/drivers/usb/mousedev.c
@@ -95,8 +95,8 @@ static void mousedev_event(struct input_handle *handle, unsigned int type, unsig
break;
case ABS_Y:
size = handle->dev->absmax[ABS_Y] - handle->dev->absmin[ABS_Y];
- list->dy += (value * CONFIG_MOUSEDEV_SCREEN_Y - list->oldy) / size;
- list->oldy += list->dy * size;
+ list->dy -= (value * CONFIG_MOUSEDEV_SCREEN_Y - list->oldy) / size;
+ list->oldy -= list->dy * size;
break;
}
break;
diff --git a/drivers/usb/pegasus.c b/drivers/usb/pegasus.c
index 2d5de6f73..764f1c055 100644
--- a/drivers/usb/pegasus.c
+++ b/drivers/usb/pegasus.c
@@ -16,12 +16,9 @@
#include <linux/usb.h>
-static const char *version = __FILE__ ": v0.3.3 2000/03/13 Written by Petko Manolov (petkan@spct.net)\n";
+static const char *version = __FILE__ ": v0.3.5 2000/03/21 Written by Petko Manolov (petkan@spct.net)\n";
-#define ADMTEK_VENDOR_ID 0x07a6
-#define ADMTEK_DEVICE_ID_PEGASUS 0x0986
-
#define PEGASUS_MTU 1500
#define PEGASUS_MAX_MTU 1536
#define PEGASUS_TX_TIMEOUT (HZ*5)
@@ -38,6 +35,13 @@ struct pegasus {
unsigned char ALIGN(intr_buff[8]);
};
+struct usb_eth_dev {
+ char *name;
+ __u16 vendor;
+ __u16 device;
+ void *private;
+};
+
static int loopback = 0;
static int multicast_filter_limit = 32;
@@ -46,6 +50,16 @@ MODULE_DESCRIPTION("ADMtek AN986 Pegasus USB Ethernet driver");
MODULE_PARM(loopback, "i");
+static struct usb_eth_dev usb_dev_id[] = {
+ { "D-Link DSB-650TX", 0x2001, 0x4001, NULL },
+ { "Linksys USB100TX", 0x066b, 0x2203, NULL },
+ { "SMC 202 USB Ethernet", 0x0707, 0x0200, NULL },
+ { "ADMtek AN986 (Pegasus) USB Ethernet", 0x07a6, 0x0986, NULL },
+ { "Accton USB 10/100 Ethernet Adapter", 0x083a, 0x1046, NULL },
+ { NULL, 0, 0, NULL }
+};
+
+
#define pegasus_get_registers(dev, indx, size, data)\
usb_control_msg(dev, usb_rcvctrlpipe(dev,0), 0xf0, 0xc0, 0, indx, data, size, HZ);
#define pegasus_set_registers(dev, indx, size, data)\
@@ -164,8 +178,8 @@ static int pegasus_start_net(struct net_device *dev, struct usb_device *usb)
return 4;
if ((partmedia & 0x1f) != 1) {
- err("party FAIL %x", partmedia);
- return 5;
+ warn("party FAIL %x", partmedia);
+ /* return 5; FIXME */
}
data[0] = 0xc9;
@@ -374,13 +388,25 @@ static void pegasus_set_rx_mode(struct net_device *net)
netif_wake_queue(net);
}
+static int check_device_ids( __u16 vendor, __u16 product )
+{
+ int i=0;
+
+ while ( usb_dev_id[i].name ) {
+ if ( (usb_dev_id[i].vendor == vendor) &&
+ (usb_dev_id[i].device == product) )
+ return 0;
+ i++;
+ }
+ return 1;
+}
+
static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum)
{
struct net_device *net;
struct pegasus *pegasus;
- if (dev->descriptor.idVendor != ADMTEK_VENDOR_ID ||
- dev->descriptor.idProduct != ADMTEK_DEVICE_ID_PEGASUS) {
+ if ( check_device_ids(dev->descriptor.idVendor, dev->descriptor.idProduct) ) {
return NULL;
}
diff --git a/drivers/usb/serial/Makefile-keyspan_pda_fw b/drivers/usb/serial/Makefile-keyspan_pda_fw
index 219708e46..c20baf728 100644
--- a/drivers/usb/serial/Makefile-keyspan_pda_fw
+++ b/drivers/usb/serial/Makefile-keyspan_pda_fw
@@ -5,7 +5,7 @@
all: keyspan_pda_fw.h
-%.asm: %.s
+%.asm: %.S
gcc -x assembler-with-cpp -P -E -o $@ $<
%.hex: %.asm
diff --git a/drivers/usb/serial/keyspan_pda.S b/drivers/usb/serial/keyspan_pda.S
new file mode 100644
index 000000000..10c99eadd
--- /dev/null
+++ b/drivers/usb/serial/keyspan_pda.S
@@ -0,0 +1,1124 @@
+/* $Id: loop.s,v 1.23 2000/03/20 09:49:06 warner Exp $
+ *
+ * Firmware for the Keyspan PDA Serial Adapter, a USB serial port based on
+ * the EzUSB microcontroller.
+ *
+ * (C) Copyright 2000 Brian Warner <warner@lothar.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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * "Keyspan PDA Serial Adapter" is probably a copyright of Keyspan, the
+ * company.
+ *
+ * This serial adapter is basically an EzUSB chip and an RS-232 line driver
+ * in a little widget that has a DB-9 on one end and a USB plug on the other.
+ * It uses the EzUSB's internal UART0 (using the pins from Port C) and timer2
+ * as a baud-rate generator. The wiring is:
+ * PC0/RxD0 <- rxd (DB9 pin 2) PC4 <- dsr pin 6
+ * PC1/TxD0 -> txd pin 3 PC5 <- ri pin 9
+ * PC2 -> rts pin 7 PC6 <- dcd pin 1
+ * PC3 <- cts pin 8 PC7 -> dtr pin 4
+ * PB1 -> line driver standby
+ *
+ * The EzUSB register constants below come from their excellent documentation
+ * and sample code (which used to be available at www.anchorchips.com, but
+ * that has now been absorbed into Cypress' site and the CD-ROM contents
+ * don't appear to be available online anymore). If we get multiple
+ * EzUSB-based drivers into the kernel, it might be useful to pull them out
+ * into a separate .h file.
+ *
+ * THEORY OF OPERATION:
+ *
+ * There are two 256-byte ring buffers, one for tx, one for rx.
+ *
+ * EP2out is pure tx data. When it appears, the data is copied into the tx
+ * ring and serial transmission is started if it wasn't already running. The
+ * "tx buffer empty" interrupt may kick off another character if the ring
+ * still has data. If the host is tx-blocked because the ring filled up,
+ * it will request a "tx unthrottle" interrupt. If sending a serial character
+ * empties the ring below the desired threshold, we set a bit that will send
+ * up the tx unthrottle message as soon as the rx buffer becomes free.
+ *
+ * EP2in (interrupt) is used to send both rx chars and rx status messages
+ * (only "tx unthrottle" at this time) back up to the host. The first byte
+ * of the rx message indicates data (0) or status msg (1). Status messages
+ * are sent before any data.
+ *
+ * Incoming serial characters are put into the rx ring by the serial
+ * interrupt, and the EP2in buffer sent if it wasn't already in transit.
+ * When the EP2in buffer returns, the interrupt prompts us to send more
+ * rx chars (or status messages) if they are pending.
+ *
+ * Device control happens through "vendor specific" control messages on EP0.
+ * All messages are destined for the "Interface" (with the index always 0,
+ * so that if their two-port device might someday use similar firmware, we
+ * can use index=1 to refer to the second port). The messages defined are:
+ *
+ * bRequest = 0 : set baud/bits/parity
+ * 1 : unused
+ * 2 : reserved for setting HW flow control (CTSRTS)
+ * 3 : get/set "modem info" (pin states: DTR, RTS, DCD, RI, etc)
+ * 4 : set break (on/off)
+ * 5 : reserved for requesting interrupts on pin state change
+ * 6 : query buffer room or chars in tx buffer
+ * 7 : request tx unthrottle interrupt
+ *
+ * The host-side driver is set to recognize the device ID values stashed in
+ * serial EEPROM (0x06cd, 0x0103), program this firmware into place, then
+ * start it running. This firmware will use EzUSB's "renumeration" trick by
+ * simulating a bus disconnect, then reconnect with a different device ID
+ * (encoded in the desc_device descriptor below). The host driver then
+ * recognizes the new device ID and glues it to the real serial driver code.
+ *
+ * USEFUL DOCS:
+ * EzUSB Technical Reference Manual: <http://www.anchorchips.com>
+ * 8051 manuals: everywhere, but try www.dalsemi.com because the EzUSB is
+ * basically the Dallas enhanced 8051 code. Remember that the EzUSB IO ports
+ * use totally different registers!
+ * USB 1.1 spec: www.usb.org
+ *
+ * HOW TO BUILD:
+ * gcc -x assembler-with-cpp -P -E -o keyspan_pda.asm keyspan_pda.s
+ * as31 -l keyspan_pda.asm
+ * mv keyspan_pda.obj keyspan_pda.hex
+ * perl ezusb_convert.pl keyspan_pda < keyspan_pda.hex > keyspan_pda_fw.h
+ * Get as31 from <http://www.pjrc.com/tech/8051/index.html>, and hack on it
+ * a bit to make it build.
+ *
+ * THANKS:
+ * Greg Kroah-Hartman, for coordinating the whole usb-serial thing.
+ * AnchorChips, for making such an incredibly useful little microcontroller.
+ * KeySpan, for making a handy, cheap ($40) widget that was so easy to take
+ * apart and trace with an ohmmeter.
+ *
+ * TODO:
+ * lots. grep for TODO. Interrupt safety needs stress-testing. Better flow
+ * control. Interrupting host upon change in DCD, etc, counting transitions.
+ * Need to find a safe device id to use (the one used by the Keyspan firmware
+ * under Windows would be ideal.. can anyone figure out what it is?). Parity.
+ * More baud rates. Oh, and the string-descriptor-length silicon bug
+ * workaround should be implemented, but I'm lazy, and the consequence is
+ * that the device name strings that show up in your kernel log will have
+ * lots of trailing binary garbage in them (appears as ????). Device strings
+ * should be made more accurate.
+ *
+ * Questions, bugs, patches to Brian.
+ *
+ * -Brian Warner <warner@lothar.com>
+ *
+ */
+
+#define HIGH(x) (((x) & 0xff00) / 256)
+#define LOW(x) ((x) & 0xff)
+
+#define dpl1 0x84
+#define dph1 0x85
+#define dps 0x86
+
+;;; our bit assignments
+#define TX_RUNNING 0
+#define DO_TX_UNTHROTTLE 1
+
+ ;; stack from 0x60 to 0x7f: should really set SP to 0x60-1, not 0x60
+#define STACK #0x60-1
+
+#define EXIF 0x91
+#define EIE 0xe8
+ .flag EUSB, EIE.0
+ .flag ES0, IE.4
+
+#define EP0CS #0x7fb4
+#define EP0STALLbit #0x01
+#define IN0BUF #0x7f00
+#define IN0BC #0x7fb5
+#define OUT0BUF #0x7ec0
+#define OUT0BC #0x7fc5
+#define IN2BUF #0x7e00
+#define IN2BC #0x7fb9
+#define IN2CS #0x7fb8
+#define OUT2BC #0x7fc9
+#define OUT2CS #0x7fc8
+#define OUT2BUF #0x7dc0
+#define IN4BUF #0x7d00
+#define IN4BC #0x7fbd
+#define IN4CS #0x7fbc
+#define OEB #0x7f9d
+#define OUTB #0x7f97
+#define OEC #0x7f9e
+#define OUTC #0x7f98
+#define PINSC #0x7f9b
+#define PORTCCFG #0x7f95
+#define IN07IRQ #0x7fa9
+#define OUT07IRQ #0x7faa
+#define IN07IEN #0x7fac
+#define OUT07IEN #0x7fad
+#define USBIRQ #0x7fab
+#define USBIEN #0x7fae
+#define USBBAV #0x7faf
+#define USBCS #0x7fd6
+#define SUDPTRH #0x7fd4
+#define SUDPTRL #0x7fd5
+#define SETUPDAT #0x7fe8
+
+ ;; usb interrupt : enable is EIE.0 (0xe8), flag is EXIF.4 (0x91)
+
+ .org 0
+ ljmp start
+ ;; interrupt vectors
+ .org 23H
+ ljmp serial_int
+ .byte 0
+
+ .org 43H
+ ljmp USB_Jump_Table
+ .byte 0 ; filled in by the USB core
+
+;;; local variables. These are not initialized properly: do it by hand.
+ .org 30H
+rx_ring_in: .byte 0
+rx_ring_out: .byte 0
+tx_ring_in: .byte 0
+tx_ring_out: .byte 0
+tx_unthrottle_threshold: .byte 0
+
+ .org 0x100H ; wants to be on a page boundary
+USB_Jump_Table:
+ ljmp ISR_Sudav ; Setup Data Available
+ .byte 0
+ ljmp 0 ; Start of Frame
+ .byte 0
+ ljmp 0 ; Setup Data Loading
+ .byte 0
+ ljmp 0 ; Global Suspend
+ .byte 0
+ ljmp 0 ; USB Reset
+ .byte 0
+ ljmp 0 ; Reserved
+ .byte 0
+ ljmp 0 ; End Point 0 In
+ .byte 0
+ ljmp 0 ; End Point 0 Out
+ .byte 0
+ ljmp 0 ; End Point 1 In
+ .byte 0
+ ljmp 0 ; End Point 1 Out
+ .byte 0
+ ljmp ISR_Ep2in
+ .byte 0
+ ljmp ISR_Ep2out
+ .byte 0
+
+
+ .org 0x200
+
+start: mov SP,STACK-1 ; set stack
+ ;; clear local variables
+ clr a
+ mov tx_ring_in, a
+ mov tx_ring_out, a
+ mov rx_ring_in, a
+ mov rx_ring_out, a
+ mov tx_unthrottle_threshold, a
+ clr TX_RUNNING
+ clr DO_TX_UNTHROTTLE
+
+ ;; clear fifo with "fe"
+ mov r1, 0
+ mov a, #0xfe
+ mov dptr, #tx_ring
+clear_tx_ring_loop:
+ movx @dptr, a
+ inc dptr
+ djnz r1, clear_tx_ring_loop
+
+ mov a, #0xfd
+ mov dptr, #rx_ring
+clear_rx_ring_loop:
+ movx @dptr, a
+ inc dptr
+ djnz r1, clear_rx_ring_loop
+
+;;; turn on the RS-232 driver chip (bring the STANDBY pin low)
+ ;; set OEB.1
+ mov a, #02H
+ mov dptr,OEB
+ movx @dptr,a
+ ;; clear PB1
+ mov a, #00H
+ mov dptr,OUTB
+ movx @dptr,a
+ ;; set OEC.[127]
+ mov a, #0x86
+ mov dptr,OEC
+ movx @dptr,a
+ ;; set PORTCCFG.[01] to route TxD0,RxD0 to serial port
+ mov dptr, PORTCCFG
+ mov a, #0x03
+ movx @dptr, a
+
+ ;; set up interrupts, autovectoring
+ mov dptr, USBBAV
+ movx a,@dptr
+ setb acc.0 ; AVEN bit to 0
+ movx @dptr, a
+
+ mov a,#0x01 ; enable SUDAV: setup data available (for ep0)
+ mov dptr, USBIRQ
+ movx @dptr, a ; clear SUDAVI
+ mov dptr, USBIEN
+ movx @dptr, a
+
+ mov dptr, IN07IEN
+ mov a,#0x04 ; enable IN2 int
+ movx @dptr, a
+
+ mov dptr, OUT07IEN
+ mov a,#0x04 ; enable OUT2 int
+ movx @dptr, a
+ mov dptr, OUT2BC
+ movx @dptr, a ; arm OUT2
+
+ mov a, #0x84 ; turn on RTS, DTR
+ mov dptr,OUTC
+ movx @dptr, a
+ ;; setup the serial port. 9600 8N1.
+ mov a,#01010011 ; mode 1, enable rx, clear int
+ mov SCON, a
+ ;; using timer2, in 16-bit baud-rate-generator mode
+ ;; (xtal 12MHz, internal fosc 24MHz)
+ ;; RCAP2H,RCAP2L = 65536 - fosc/(32*baud)
+ ;; 57600: 0xFFF2.F, say 0xFFF3
+ ;; 9600: 0xFFB1.E, say 0xFFB2
+ ;; 300: 0xF63C
+#define BAUD 9600
+#define BAUD_TIMEOUT(rate) (65536 - (24 * 1000 * 1000) / (32 * rate))
+#define BAUD_HIGH(rate) HIGH(BAUD_TIMEOUT(rate))
+#define BAUD_LOW(rate) LOW(BAUD_TIMEOUT(rate))
+
+ mov T2CON, #030h ; rclk=1,tclk=1,cp=0,tr2=0(enable later)
+ mov r3, #5
+ acall set_baud
+ setb TR2
+ mov SCON, #050h
+
+#if 0
+ mov r1, #0x40
+ mov a, #0x41
+send:
+ mov SBUF, a
+ inc a
+ anl a, #0x3F
+ orl a, #0x40
+; xrl a, #0x02
+wait1:
+ jnb TI, wait1
+ clr TI
+ djnz r1, send
+;done: sjmp done
+
+#endif
+
+ setb EUSB
+ setb EA
+ setb ES0
+ ;acall dump_stat
+
+ ;; hey, what say we RENUMERATE! (TRM p.62)
+ mov a, #0
+ mov dps, a
+ mov dptr, USBCS
+ mov a, #0x02 ; DISCON=0, DISCOE=0, RENUM=1
+ movx @dptr, a
+ ;; now presence pin is floating, simulating disconnect. wait 0.5s
+ mov r1, #46
+renum_wait1:
+ mov r2, #0
+renum_wait2:
+ mov r3, #0
+renum_wait3:
+ djnz r3, renum_wait3
+ djnz r2, renum_wait2
+ djnz r1, renum_wait1 ; wait about n*(256^2) 6MHz clocks
+ mov a, #0x06 ; DISCON=0, DISCOE=1, RENUM=1
+ movx @dptr, a
+ ;; we are back online. the host device will now re-query us
+
+
+main: sjmp main
+
+
+
+ISR_Sudav:
+ push dps
+ push dpl
+ push dph
+ push dpl1
+ push dph1
+ push acc
+ mov a,EXIF
+ clr acc.4
+ mov EXIF,a ; clear INT2 first
+ mov dptr, USBIRQ ; clear USB int
+ mov a,#01h
+ movx @dptr,a
+
+ ;; get request type
+ mov dptr, SETUPDAT
+ movx a, @dptr
+ mov r1, a ; r1 = bmRequestType
+ inc dptr
+ movx a, @dptr
+ mov r2, a ; r2 = bRequest
+ inc dptr
+ movx a, @dptr
+ mov r3, a ; r3 = wValueL
+ inc dptr
+ movx a, @dptr
+ mov r4, a ; r4 = wValueH
+
+ ;; main switch on bmRequest.type: standard or vendor
+ mov a, r1
+ anl a, #0x60
+ cjne a, #0x00, setup_bmreq_type_not_standard
+ ;; standard request: now main switch is on bRequest
+ ljmp setup_bmreq_is_standard
+
+setup_bmreq_type_not_standard:
+ ;; a still has bmreq&0x60
+ cjne a, #0x40, setup_bmreq_type_not_vendor
+ ;; Anchor reserves bRequest 0xa0-0xaf, we use small ones
+ ;; switch on bRequest. bmRequest will always be 0x41 or 0xc1
+ cjne r2, #0x00, setup_ctrl_not_00
+ ;; 00 is set baud, wValue[0] has baud rate index
+ lcall set_baud ; index in r3, carry set if error
+ jc setup_bmreq_type_not_standard__do_stall
+ ljmp setup_done_ack
+setup_bmreq_type_not_standard__do_stall:
+ ljmp setup_stall
+setup_ctrl_not_00:
+ cjne r2, #0x01, setup_ctrl_not_01
+ ;; 01 is reserved for set bits (parity). TODO
+ ljmp setup_stall
+setup_ctrl_not_01:
+ cjne r2, #0x02, setup_ctrl_not_02
+ ;; 02 is set HW flow control. TODO
+ ljmp setup_stall
+setup_ctrl_not_02:
+ cjne r2, #0x03, setup_ctrl_not_03
+ ;; 03 is control pins (RTS, DTR).
+ ljmp control_pins ; will jump to setup_done_ack,
+ ; or setup_return_one_byte
+setup_ctrl_not_03:
+ cjne r2, #0x04, setup_ctrl_not_04
+ ;; 04 is send break (really "turn break on/off"). TODO
+ cjne r3, #0x00, setup_ctrl_do_break_on
+ ;; do break off: restore PORTCCFG.1 to reconnect TxD0 to serial port
+ mov dptr, PORTCCFG
+ movx a, @dptr
+ orl a, #0x02
+ movx @dptr, a
+ ljmp setup_done_ack
+setup_ctrl_do_break_on:
+ ;; do break on: clear PORTCCFG.0, set TxD high(?) (b1 low)
+ mov dptr, OUTC
+ movx a, @dptr
+ anl a, #0xfd ; ~0x02
+ movx @dptr, a
+ mov dptr, PORTCCFG
+ movx a, @dptr
+ anl a, #0xfd ; ~0x02
+ movx @dptr, a
+ ljmp setup_done_ack
+setup_ctrl_not_04:
+ cjne r2, #0x05, setup_ctrl_not_05
+ ;; 05 is set desired interrupt bitmap. TODO
+ ljmp setup_stall
+setup_ctrl_not_05:
+ cjne r2, #0x06, setup_ctrl_not_06
+ ;; 06 is query room
+ cjne r3, #0x00, setup_ctrl_06_not_00
+ ;; 06, wValue[0]=0 is query write_room
+ mov a, tx_ring_out
+ setb c
+ subb a, tx_ring_in ; out-1-in = 255 - (in-out)
+ ljmp setup_return_one_byte
+setup_ctrl_06_not_00:
+ cjne r3, #0x01, setup_ctrl_06_not_01
+ ;; 06, wValue[0]=1 is query chars_in_buffer
+ mov a, tx_ring_in
+ clr c
+ subb a, tx_ring_out ; in-out
+ ljmp setup_return_one_byte
+setup_ctrl_06_not_01:
+ ljmp setup_stall
+setup_ctrl_not_06:
+ cjne r2, #0x07, setup_ctrl_not_07
+ ;; 07 is request tx unthrottle interrupt
+ mov tx_unthrottle_threshold, r3; wValue[0] is threshold value
+ ljmp setup_done_ack
+setup_ctrl_not_07:
+ ljmp setup_stall
+
+setup_bmreq_type_not_vendor:
+ ljmp setup_stall
+
+
+setup_bmreq_is_standard:
+ cjne r2, #0x00, setup_breq_not_00
+ ;; 00: Get_Status (sub-switch on bmRequestType: device, ep, int)
+ cjne r1, #0x80, setup_Get_Status_not_device
+ ;; Get_Status(device)
+ ;; are we self-powered? no. can we do remote wakeup? no
+ ;; so return two zero bytes. This is reusable
+setup_return_two_zero_bytes:
+ mov dptr, IN0BUF
+ clr a
+ movx @dptr, a
+ inc dptr
+ movx @dptr, a
+ mov dptr, IN0BC
+ mov a, #2
+ movx @dptr, a
+ ljmp setup_done_ack
+setup_Get_Status_not_device:
+ cjne r1, #0x82, setup_Get_Status_not_endpoint
+ ;; Get_Status(endpoint)
+ ;; must get stall bit for ep[wIndexL], return two bytes, bit in lsb 0
+ ;; for now: cheat. TODO
+ sjmp setup_return_two_zero_bytes
+setup_Get_Status_not_endpoint:
+ cjne r1, #0x81, setup_Get_Status_not_interface
+ ;; Get_Status(interface): return two zeros
+ sjmp setup_return_two_zero_bytes
+setup_Get_Status_not_interface:
+ ljmp setup_stall
+
+setup_breq_not_00:
+ cjne r2, #0x01, setup_breq_not_01
+ ;; 01: Clear_Feature (sub-switch on wValueL: stall, remote wakeup)
+ cjne r3, #0x00, setup_Clear_Feature_not_stall
+ ;; Clear_Feature(stall). should clear a stall bit. TODO
+ ljmp setup_stall
+setup_Clear_Feature_not_stall:
+ cjne r3, #0x01, setup_Clear_Feature_not_rwake
+ ;; Clear_Feature(remote wakeup). ignored.
+ ljmp setup_done_ack
+setup_Clear_Feature_not_rwake:
+ ljmp setup_stall
+
+setup_breq_not_01:
+ cjne r2, #0x03, setup_breq_not_03
+ ;; 03: Set_Feature (sub-switch on wValueL: stall, remote wakeup)
+ cjne r3, #0x00, setup_Set_Feature_not_stall
+ ;; Set_Feature(stall). Should set a stall bit. TODO
+ ljmp setup_stall
+setup_Set_Feature_not_stall:
+ cjne r3, #0x01, setup_Set_Feature_not_rwake
+ ;; Set_Feature(remote wakeup). ignored.
+ ljmp setup_done_ack
+setup_Set_Feature_not_rwake:
+ ljmp setup_stall
+
+setup_breq_not_03:
+ cjne r2, #0x06, setup_breq_not_06
+ ;; 06: Get_Descriptor (s-switch on wValueH: dev, config[n], string[n])
+ cjne r4, #0x01, setup_Get_Descriptor_not_device
+ ;; Get_Descriptor(device)
+ mov dptr, SUDPTRH
+ mov a, #HIGH(desc_device)
+ movx @dptr, a
+ mov dptr, SUDPTRL
+ mov a, #LOW(desc_device)
+ movx @dptr, a
+ ljmp setup_done_ack
+setup_Get_Descriptor_not_device:
+ cjne r4, #0x02, setup_Get_Descriptor_not_config
+ ;; Get_Descriptor(config[n])
+ cjne r3, #0x00, setup_stall; only handle n==0
+ ;; Get_Descriptor(config[0])
+ mov dptr, SUDPTRH
+ mov a, #HIGH(desc_config1)
+ movx @dptr, a
+ mov dptr, SUDPTRL
+ mov a, #LOW(desc_config1)
+ movx @dptr, a
+ ljmp setup_done_ack
+setup_Get_Descriptor_not_config:
+ cjne r4, #0x03, setup_Get_Descriptor_not_string
+ ;; Get_Descriptor(string[wValueL])
+ ;; if (wValueL >= maxstrings) stall
+ mov a, #((desc_strings_end-desc_strings)/2)
+ clr c
+ subb a,r3 ; a=4, r3 = 0..3 . if a<=0 then stall
+ jc setup_stall
+ jz setup_stall
+ mov a, r3
+ add a, r3 ; a = 2*wValueL
+ mov dptr, #desc_strings
+ add a, dpl
+ mov dpl, a
+ mov a, #0
+ addc a, dph
+ mov dph, a ; dph = desc_strings[a]. big endian! (handy)
+ ;; it looks like my adapter uses a revision of the EZUSB that
+ ;; contains "rev D errata number 8", as hinted in the EzUSB example
+ ;; code. I cannot find an actual errata description on the Cypress
+ ;; web site, but from the example code it looks like this bug causes
+ ;; the length of string descriptors to be read incorrectly, possibly
+ ;; sending back more characters than the descriptor has. The workaround
+ ;; is to manually send out all of the data. The consequence of not
+ ;; using the workaround is that the strings gathered by the kernel
+ ;; driver are too long and are filled with trailing garbage (including
+ ;; leftover strings). Writing this out by hand is a nuisance, so for
+ ;; now I will just live with the bug.
+ movx a, @dptr
+ mov r1, a
+ inc dptr
+ movx a, @dptr
+ mov r2, a
+ mov dptr, SUDPTRH
+ mov a, r1
+ movx @dptr, a
+ mov dptr, SUDPTRL
+ mov a, r2
+ movx @dptr, a
+ ;; done
+ ljmp setup_done_ack
+
+setup_Get_Descriptor_not_string:
+ ljmp setup_stall
+
+setup_breq_not_06:
+ cjne r2, #0x08, setup_breq_not_08
+ ;; Get_Configuration. always 1. return one byte.
+ ;; this is reusable
+ mov a, #1
+setup_return_one_byte:
+ mov dptr, IN0BUF
+ movx @dptr, a
+ mov a, #1
+ mov dptr, IN0BC
+ movx @dptr, a
+ ljmp setup_done_ack
+setup_breq_not_08:
+ cjne r2, #0x09, setup_breq_not_09
+ ;; 09: Set_Configuration. ignored.
+ ljmp setup_done_ack
+setup_breq_not_09:
+ cjne r2, #0x0a, setup_breq_not_0a
+ ;; 0a: Get_Interface. get the current altsetting for int[wIndexL]
+ ;; since we only have one interface, ignore wIndexL, return a 0
+ mov a, #0
+ ljmp setup_return_one_byte
+setup_breq_not_0a:
+ cjne r2, #0x0b, setup_breq_not_0b
+ ;; 0b: Set_Interface. set altsetting for interface[wIndexL]. ignored
+ ljmp setup_done_ack
+setup_breq_not_0b:
+ ljmp setup_stall
+
+
+setup_done_ack:
+ ;; now clear HSNAK
+ mov dptr, EP0CS
+ mov a, #0x02
+ movx @dptr, a
+ sjmp setup_done
+setup_stall:
+ ;; unhandled. STALL
+ ;EP0CS |= bmEPSTALL
+ mov dptr, EP0CS
+ movx a, @dptr
+ orl a, EP0STALLbit
+ movx @dptr, a
+ sjmp setup_done
+
+setup_done:
+ pop acc
+ pop dph1
+ pop dpl1
+ pop dph
+ pop dpl
+ pop dps
+ reti
+
+;;; ==============================================================
+
+set_baud: ; baud index in r3
+ ;; verify a < 10
+ mov a, r3
+ jb ACC.7, set_baud__badbaud
+ clr c
+ subb a, #10
+ jnc set_baud__badbaud
+ mov a, r3
+ rl a ; a = index*2
+ add a, #LOW(baud_table)
+ mov dpl, a
+ mov a, #HIGH(baud_table)
+ addc a, #0
+ mov dph, a
+ ;; TODO: shut down xmit/receive
+ ;; TODO: wait for current xmit char to leave
+ ;; TODO: shut down timer to avoid partial-char glitch
+ movx a,@dptr ; BAUD_HIGH
+ mov RCAP2H, a
+ mov TH2, a
+ inc dptr
+ movx a,@dptr ; BAUD_LOW
+ mov RCAP2L, a
+ mov TL2, a
+ ;; TODO: restart xmit/receive
+ ;; TODO: reenable interrupts, resume tx if pending
+ clr c ; c=0: success
+ ret
+set_baud__badbaud:
+ setb c ; c=1: failure
+ ret
+
+;;; ==================================================
+control_pins:
+ cjne r1, #0x41, control_pins_in
+control_pins_out:
+ mov a, r3 ; wValue[0] holds new bits: b7 is new DTR, b2 is new RTS
+ xrl a, #0xff ; 1 means active, 0V, +12V ?
+ anl a, #0x84
+ mov r3, a
+ mov dptr, OUTC
+ movx a, @dptr ; only change bits 7 and 2
+ anl a, #0x7b ; ~0x84
+ orl a, r3
+ movx @dptr, a ; other pins are inputs, bits ignored
+ ljmp setup_done_ack
+control_pins_in:
+ mov dptr, PINSC
+ movx a, @dptr
+ xrl a, #0xff
+ ljmp setup_return_one_byte
+
+;;; ========================================
+
+ISR_Ep2in:
+ push dps
+ push dpl
+ push dph
+ push dpl1
+ push dph1
+ push acc
+ mov a,EXIF
+ clr acc.4
+ mov EXIF,a ; clear INT2 first
+ mov dptr, IN07IRQ ; clear USB int
+ mov a,#04h
+ movx @dptr,a
+
+ ;; do stuff
+ lcall start_in
+
+ pop acc
+ pop dph1
+ pop dpl1
+ pop dph
+ pop dpl
+ pop dps
+ reti
+
+ISR_Ep2out:
+ push dps
+ push dpl
+ push dph
+ push dpl1
+ push dph1
+ push acc
+ mov a,EXIF
+ clr acc.4
+ mov EXIF,a ; clear INT2 first
+ mov dptr, OUT07IRQ ; clear USB int
+ mov a,#04h
+ movx @dptr,a
+
+ ;; do stuff
+
+ ;; copy data into buffer. for now, assume we will have enough space
+ mov dptr, OUT2BC ; get byte count
+ movx a,@dptr
+ mov r1, a
+ clr a
+ mov dps, a
+ mov dptr, OUT2BUF ; load DPTR0 with source
+ mov dph1, #HIGH(tx_ring) ; load DPTR1 with target
+ mov dpl1, tx_ring_in
+OUT_loop:
+ movx a,@dptr ; read
+ inc dps ; switch to DPTR1: target
+ inc dpl1 ; target = tx_ring_in+1
+ movx @dptr,a ; store
+ mov a,dpl1
+ cjne a, tx_ring_out, OUT_no_overflow
+ sjmp OUT_overflow
+OUT_no_overflow:
+ inc tx_ring_in ; tx_ring_in++
+ inc dps ; switch to DPTR0: source
+ inc dptr
+ djnz r1, OUT_loop
+ sjmp OUT_done
+OUT_overflow:
+ ;; signal overflow
+ ;; fall through
+OUT_done:
+ ;; ack
+ mov dptr,OUT2BC
+ movx @dptr,a
+
+ ;; start tx
+ acall maybe_start_tx
+ ;acall dump_stat
+
+ pop acc
+ pop dph1
+ pop dpl1
+ pop dph
+ pop dpl
+ pop dps
+ reti
+
+dump_stat:
+ ;; fill in EP4in with a debugging message:
+ ;; tx_ring_in, tx_ring_out, rx_ring_in, rx_ring_out
+ ;; tx_active
+ ;; tx_ring[0..15]
+ ;; 0xfc
+ ;; rx_ring[0..15]
+ clr a
+ mov dps, a
+
+ mov dptr, IN4CS
+ movx a, @dptr
+ jb acc.1, dump_stat__done; busy: cannot dump, old one still pending
+ mov dptr, IN4BUF
+
+ mov a, tx_ring_in
+ movx @dptr, a
+ inc dptr
+ mov a, tx_ring_out
+ movx @dptr, a
+ inc dptr
+
+ mov a, rx_ring_in
+ movx @dptr, a
+ inc dptr
+ mov a, rx_ring_out
+ movx @dptr, a
+ inc dptr
+
+ clr a
+ jnb TX_RUNNING, dump_stat__no_tx_running
+ inc a
+dump_stat__no_tx_running:
+ movx @dptr, a
+ inc dptr
+ ;; tx_ring[0..15]
+ inc dps
+ mov dptr, #tx_ring ; DPTR1: source
+ mov r1, #16
+dump_stat__tx_ring_loop:
+ movx a, @dptr
+ inc dptr
+ inc dps
+ movx @dptr, a
+ inc dptr
+ inc dps
+ djnz r1, dump_stat__tx_ring_loop
+ inc dps
+
+ mov a, #0xfc
+ movx @dptr, a
+ inc dptr
+
+ ;; rx_ring[0..15]
+ inc dps
+ mov dptr, #rx_ring ; DPTR1: source
+ mov r1, #16
+dump_stat__rx_ring_loop:
+ movx a, @dptr
+ inc dptr
+ inc dps
+ movx @dptr, a
+ inc dptr
+ inc dps
+ djnz r1, dump_stat__rx_ring_loop
+
+ ;; now send it
+ clr a
+ mov dps, a
+ mov dptr, IN4BC
+ mov a, #38
+ movx @dptr, a
+dump_stat__done:
+ ret
+
+;;; ============================================================
+
+maybe_start_tx:
+ ;; make sure the tx process is running.
+ jb TX_RUNNING, start_tx_done
+start_tx:
+ ;; is there work to be done?
+ mov a, tx_ring_in
+ cjne a,tx_ring_out, start_tx__work
+ ret ; no work
+start_tx__work:
+ ;; tx was not running. send the first character, setup the TI int
+ inc tx_ring_out ; [++tx_ring_out]
+ mov dph, #HIGH(tx_ring)
+ mov dpl, tx_ring_out
+ movx a, @dptr
+ mov sbuf, a
+ setb TX_RUNNING
+start_tx_done:
+ ;; can we unthrottle the host tx process?
+ ;; step 1: do we care?
+ mov a, #0
+ cjne a, tx_unthrottle_threshold, start_tx__maybe_unthrottle_tx
+ ;; nope
+start_tx_really_done:
+ ret
+start_tx__maybe_unthrottle_tx:
+ ;; step 2: is there now room?
+ mov a, tx_ring_out
+ setb c
+ subb a, tx_ring_in
+ ;; a is now write_room. If thresh >= a, we can unthrottle
+ clr c
+ subb a, tx_unthrottle_threshold
+ jc start_tx_really_done ; nope
+ ;; yes, we can unthrottle. remove the threshold and mark a request
+ mov tx_unthrottle_threshold, #0
+ setb DO_TX_UNTHROTTLE
+ ;; prod rx, which will actually send the message when in2 becomes free
+ ljmp start_in
+
+
+serial_int:
+ push dps
+ push dpl
+ push dph
+ push dpl1
+ push dph1
+ push acc
+ jnb TI, serial_int__not_tx
+ ;; tx finished. send another character if we have one
+ clr TI ; clear int
+ clr TX_RUNNING
+ lcall start_tx
+serial_int__not_tx:
+ jnb RI, serial_int__not_rx
+ lcall get_rx_char
+ clr RI ; clear int
+serial_int__not_rx:
+ ;; return
+ pop acc
+ pop dph1
+ pop dpl1
+ pop dph
+ pop dpl
+ pop dps
+ reti
+
+get_rx_char:
+ mov dph, #HIGH(rx_ring)
+ mov dpl, rx_ring_in
+ inc dpl ; target = rx_ring_in+1
+ mov a, sbuf
+ movx @dptr, a
+ ;; check for overflow before incrementing rx_ring_in
+ mov a, dpl
+ cjne a, rx_ring_out, get_rx_char__no_overflow
+ ;; signal overflow
+ ret
+get_rx_char__no_overflow:
+ inc rx_ring_in
+ ;; kick off USB INpipe
+ acall start_in
+ ret
+
+start_in:
+ ;; check if the inpipe is already running.
+ mov dptr, IN2CS
+ movx a, @dptr
+ jb acc.1, start_in__done; int will handle it
+ jb DO_TX_UNTHROTTLE, start_in__do_tx_unthrottle
+ ;; see if there is any work to do. a serial interrupt might occur
+ ;; during this sequence?
+ mov a, rx_ring_in
+ cjne a, rx_ring_out, start_in__have_work
+ ret ; nope
+start_in__have_work:
+ ;; now copy as much data as possible into the pipe. 63 bytes max.
+ clr a
+ mov dps, a
+ mov dph, #HIGH(rx_ring) ; load DPTR0 with source
+ inc dps
+ mov dptr, IN2BUF ; load DPTR1 with target
+ movx @dptr, a ; in[0] signals that rest of IN is rx data
+ inc dptr
+ inc dps
+ ;; loop until we run out of data, or we have copied 64 bytes
+ mov r1, #1 ; INbuf size counter
+start_in__loop:
+ mov a, rx_ring_in
+ cjne a, rx_ring_out, start_in__still_copying
+ sjmp start_in__kick
+start_in__still_copying:
+ inc rx_ring_out
+ mov dpl, rx_ring_out
+ movx a, @dptr
+ inc dps
+ movx @dptr, a ; write into IN buffer
+ inc dptr
+ inc dps
+ inc r1
+ cjne r1, #64, start_in__loop; loop
+start_in__kick:
+ ;; either we ran out of data, or we copied 64 bytes. r1 has byte count
+ ;; kick off IN
+ mov dptr, IN2BC
+ mov a, r1
+ jz start_in__done
+ movx @dptr, a
+ ;; done
+start_in__done:
+ ;acall dump_stat
+ ret
+start_in__do_tx_unthrottle:
+ ;; special sequence: send a tx unthrottle message
+ clr DO_TX_UNTHROTTLE
+ clr a
+ mov dps, a
+ mov dptr, IN2BUF
+ mov a, #1
+ movx @dptr, a
+ inc dptr
+ mov a, #2
+ movx @dptr, a
+ mov dptr, IN2BC
+ movx @dptr, a
+ ret
+
+putchar:
+ clr TI
+ mov SBUF, a
+putchar_wait:
+ jnb TI, putchar_wait
+ clr TI
+ ret
+
+
+baud_table: ; baud_high, then baud_low
+ ;; baud[0]: 110
+ .byte BAUD_HIGH(110)
+ .byte BAUD_LOW(110)
+ ;; baud[1]: 300
+ .byte BAUD_HIGH(300)
+ .byte BAUD_LOW(300)
+ ;; baud[2]: 1200
+ .byte BAUD_HIGH(1200)
+ .byte BAUD_LOW(1200)
+ ;; baud[3]: 2400
+ .byte BAUD_HIGH(2400)
+ .byte BAUD_LOW(2400)
+ ;; baud[4]: 4800
+ .byte BAUD_HIGH(4800)
+ .byte BAUD_LOW(4800)
+ ;; baud[5]: 9600
+ .byte BAUD_HIGH(9600)
+ .byte BAUD_LOW(9600)
+ ;; baud[6]: 19200
+ .byte BAUD_HIGH(19200)
+ .byte BAUD_LOW(19200)
+ ;; baud[7]: 38400
+ .byte BAUD_HIGH(38400)
+ .byte BAUD_LOW(38400)
+ ;; baud[8]: 57600
+ .byte BAUD_HIGH(57600)
+ .byte BAUD_LOW(57600)
+ ;; baud[9]: 115200
+ .byte BAUD_HIGH(115200)
+ .byte BAUD_LOW(115200)
+
+desc_device:
+ .byte 0x12, 0x01, 0x00, 0x01, 0xff, 0xff, 0xff, 0x40
+ .byte 0xcd, 0x06, 0x04, 0x01, 0x89, 0xab, 1, 2, 3, 0x01
+;;; The "real" device id, which must match the host driver, is that
+;;; "0xcd 0x06 0x04 0x01" sequence, which is 0x06cd, 0x0104
+
+desc_config1:
+ .byte 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0x80, 0x32
+ .byte 0x09, 0x04, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0x00
+ .byte 0x07, 0x05, 0x82, 0x03, 0x40, 0x00, 0x01
+ .byte 0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00
+
+desc_strings:
+ .word string_langids, string_mfg, string_product, string_serial
+desc_strings_end:
+
+string_langids: .byte string_langids_end-string_langids
+ .byte 3
+ .word 0
+string_langids_end:
+
+ ;; sigh. These strings are Unicode, meaning UTF16? 2 bytes each. Now
+ ;; *that* is a pain in the ass to encode. And they are little-endian
+ ;; too. Use this perl snippet to get the bytecodes:
+ /* while (<>) {
+ @c = split(//);
+ foreach $c (@c) {
+ printf("0x%02x, 0x00, ", ord($c));
+ }
+ }
+ */
+
+string_mfg: .byte string_mfg_end-string_mfg
+ .byte 3
+; .byte "ACME usb widgets"
+ .byte 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x20, 0x00, 0x75, 0x00, 0x73, 0x00, 0x62, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x67, 0x00, 0x65, 0x00, 0x74, 0x00, 0x73, 0x00
+string_mfg_end:
+
+string_product: .byte string_product_end-string_product
+ .byte 3
+; .byte "ACME USB serial widget"
+ .byte 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x20, 0x00, 0x55, 0x00, 0x53, 0x00, 0x42, 0x00, 0x20, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x67, 0x00, 0x65, 0x00, 0x74, 0x00
+string_product_end:
+
+string_serial: .byte string_serial_end-string_serial
+ .byte 3
+; .byte "47"
+ .byte 0x34, 0x00, 0x37, 0x00
+string_serial_end:
+
+;;; ring buffer memory
+ ;; tx_ring_in+1 is where the next input byte will go
+ ;; [tx_ring_out] has been sent
+ ;; if tx_ring_in == tx_ring_out, theres no work to do
+ ;; there are (tx_ring_in - tx_ring_out) chars to be written
+ ;; dont let _in lap _out
+ ;; cannot inc if tx_ring_in+1 == tx_ring_out
+ ;; write [tx_ring_in+1] then tx_ring_in++
+ ;; if (tx_ring_in+1 == tx_ring_out), overflow
+ ;; else tx_ring_in++
+ ;; read/send [tx_ring_out+1], then tx_ring_out++
+
+ ;; rx_ring_in works the same way
+
+ .org 0x1000
+tx_ring:
+ .skip 0x100 ; 256 bytes
+rx_ring:
+ .skip 0x100 ; 256 bytes
+
+
+ .END
+
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 6effcc048..021754994 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -14,6 +14,16 @@
*
* See Documentation/usb/usb-serial.txt for more information on using this driver
*
+ * (03/19/2000) gkh
+ * Fixed oops that could happen when device was removed while a program
+ * was talking to the device.
+ * Removed the static urbs and now all urbs are created and destroyed
+ * dynamically.
+ * Reworked the internal interface. Now everything is based on the
+ * usb_serial_port structure instead of the larger usb_serial structure.
+ * This fixes the bug that a multiport device could not have more than
+ * one port open at one time.
+ *
* (03/17/2000) gkh
* Added config option for debugging messages.
* Added patch for keyspan pda from Brian Warner.
@@ -220,10 +230,9 @@ static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum);
static void usb_serial_disconnect(struct usb_device *dev, void *ptr);
static struct usb_driver usb_serial_driver = {
- "serial",
- usb_serial_probe,
- usb_serial_disconnect,
- { NULL, NULL }
+ name: "serial",
+ probe: usb_serial_probe,
+ disconnect: usb_serial_disconnect,
};
static int serial_refcount;
@@ -233,11 +242,50 @@ static struct termios * serial_termios_locked[SERIAL_TTY_MINORS];
static struct usb_serial *serial_table[SERIAL_TTY_MINORS] = {NULL, };
+static inline int serial_paranoia_check (struct usb_serial *serial, const char *function)
+{
+ if (!serial) {
+ dbg("%s - serial == NULL", function);
+ return -1;
+ }
+ if (serial->magic != USB_SERIAL_MAGIC) {
+ dbg("%s - bad magic number for serial", function);
+ return -1;
+ }
+ if (!serial->type) {
+ dbg("%s - serial->type == NULL!", function);
+ return -1;
+ }
+
+ return 0;
+}
-static struct usb_serial *get_serial_by_minor (int minor)
+
+static inline int port_paranoia_check (struct usb_serial_port *port, const char *function)
{
- dbg("get_serial_by_minor %d", minor);
+ if (!port) {
+ dbg("%s - port == NULL", function);
+ return -1;
+ }
+ if (port->magic != USB_SERIAL_PORT_MAGIC) {
+ dbg("%s - bad magic number for port", function);
+ return -1;
+ }
+ if (!port->serial) {
+ dbg("%s - port->serial == NULL", function);
+ return -1;
+ }
+ if (!port->tty) {
+ dbg("%s - port->tty == NULL", function);
+ return -1;
+ }
+ return 0;
+}
+
+
+static struct usb_serial *get_serial_by_minor (int minor)
+{
return serial_table[minor];
}
@@ -267,6 +315,7 @@ static struct usb_serial *get_free_serial (int num_ports, int *minor)
return NULL;
}
memset(serial, 0, sizeof(struct usb_serial));
+ serial->magic = USB_SERIAL_MAGIC;
serial_table[i] = serial;
*minor = i;
dbg("minor base = %d", *minor);
@@ -337,6 +386,8 @@ static int ezusb_set_reset (struct usb_serial *serial, unsigned char reset_bit)
static int serial_open (struct tty_struct *tty, struct file * filp)
{
struct usb_serial *serial;
+ struct usb_serial_port *port;
+ int portNumber;
dbg("serial_open");
@@ -346,180 +397,182 @@ static int serial_open (struct tty_struct *tty, struct file * filp)
/* get the serial object associated with this tty pointer */
serial = get_serial_by_minor (MINOR(tty->device));
- /* do some sanity checking that we really have a device present */
- if (!serial) {
- dbg("serial == NULL!");
- return (-ENODEV);
- }
- if (!serial->type) {
- dbg("serial->type == NULL!");
- return (-ENODEV);
+ if (serial_paranoia_check (serial, "serial_open")) {
+ return -ENODEV;
}
- /* make the tty driver remember our serial object, and us it */
- tty->driver_data = serial;
- serial->tty = tty;
+ /* set up our port structure */
+ portNumber = MINOR(tty->device) - serial->minor;
+ port = &serial->port[portNumber];
+ port->number = portNumber;
+ port->serial = serial;
+ port->magic = USB_SERIAL_PORT_MAGIC;
+
+ /* make the tty driver remember our port object, and us it */
+ tty->driver_data = port;
+ port->tty = tty;
/* pass on to the driver specific version of this function if it is available */
if (serial->type->open) {
- return (serial->type->open(tty, filp));
+ return (serial->type->open(port, filp));
} else {
- return (generic_serial_open(tty, filp));
+ return (generic_open(port, filp));
}
}
static void serial_close(struct tty_struct *tty, struct file * filp)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int port;
+ struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+ struct usb_serial *serial;
dbg("serial_close");
- if (!serial) {
- dbg("serial == NULL!");
+ if (port_paranoia_check (port, "serial_close")) {
return;
}
- port = MINOR(tty->device) - serial->minor;
-
- dbg("serial_close port %d", port);
-
- /* do some sanity checking that we really have a device present */
- if (!serial->type) {
- dbg("serial->type == NULL!");
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "serial_close")) {
return;
}
- if (!serial->port[port].active) {
- dbg ("device not opened");
+
+ dbg("serial_close port %d", port->number);
+
+ if (!port->active) {
+ dbg ("port not opened");
return;
}
/* pass on to the driver specific version of this function if it is available */
if (serial->type->close) {
- serial->type->close(tty, filp);
+ serial->type->close(port, filp);
} else {
- generic_serial_close(tty, filp);
+ generic_close(port, filp);
}
}
static int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int port = MINOR(tty->device) - serial->minor;
+ struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+ struct usb_serial *serial;
- dbg("serial_write port %d, %d byte(s)", port, count);
-
- /* do some sanity checking that we really have a device present */
- if (!serial) {
- dbg("serial == NULL!");
- return (-ENODEV);
+ dbg("serial_write");
+
+ if (port_paranoia_check (port, "serial_write")) {
+ return -ENODEV;
}
- if (!serial->type) {
- dbg("serial->type == NULL!");
- return (-ENODEV);
+
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "serial_write")) {
+ return -ENODEV;
}
- if (!serial->port[port].active) {
- dbg ("device not opened");
- return (-EINVAL);
+
+ dbg("serial_write port %d, %d byte(s)", port->number, count);
+
+ if (!port->active) {
+ dbg ("port not opened");
+ return -EINVAL;
}
/* pass on to the driver specific version of this function if it is available */
if (serial->type->write) {
- return (serial->type->write(tty, from_user, buf, count));
+ return (serial->type->write(port, from_user, buf, count));
} else {
- return (generic_serial_write(tty, from_user, buf, count));
+ return (generic_write(port, from_user, buf, count));
}
}
static int serial_write_room (struct tty_struct *tty)
{
- struct usb_serial *serial = (struct usb_serial *)tty->driver_data;
- int port = MINOR(tty->device) - serial->minor;
-
- dbg("serial_write_room port %d", port);
+ struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+ struct usb_serial *serial;
- /* do some sanity checking that we really have a device present */
- if (!serial) {
- dbg("serial == NULL!");
- return (-ENODEV);
+ dbg("serial_write_room");
+
+ if (port_paranoia_check (port, "serial_write")) {
+ return -ENODEV;
}
- if (!serial->type) {
- dbg("serial->type == NULL!");
- return (-ENODEV);
+
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "serial_write")) {
+ return -ENODEV;
}
- if (!serial->port[port].active) {
- dbg ("device not open");
- return (-EINVAL);
+
+ dbg("serial_write_room port %d", port->number);
+
+ if (!port->active) {
+ dbg ("port not open");
+ return -EINVAL;
}
/* pass on to the driver specific version of this function if it is available */
if (serial->type->write_room) {
- return (serial->type->write_room(tty));
+ return (serial->type->write_room(port));
} else {
- return (generic_write_room(tty));
+ return (generic_write_room(port));
}
}
static int serial_chars_in_buffer (struct tty_struct *tty)
{
- struct usb_serial *serial = (struct usb_serial *)tty->driver_data;
- int port = MINOR(tty->device) - serial->minor;
-
- dbg("serial_chars_in_buffer port %d", port);
+ struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+ struct usb_serial *serial;
- /* do some sanity checking that we really have a device present */
- if (!serial) {
- dbg("serial == NULL!");
- return (-ENODEV);
+ dbg("serial_chars_in_buffer");
+
+ if (port_paranoia_check (port, "serial_chars_in_buffer")) {
+ return -ENODEV;
}
- if (!serial->type) {
- dbg("serial->type == NULL!");
- return (-ENODEV);
+
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "serial_chars_in_buffer")) {
+ return -ENODEV;
}
- if (!serial->port[port].active) {
- dbg ("device not open");
- return (-EINVAL);
+
+ if (!port->active) {
+ dbg ("port not open");
+ return -EINVAL;
}
/* pass on to the driver specific version of this function if it is available */
if (serial->type->chars_in_buffer) {
- return (serial->type->chars_in_buffer(tty));
+ return (serial->type->chars_in_buffer(port));
} else {
- return (generic_chars_in_buffer(tty));
+ return (generic_chars_in_buffer(port));
}
}
static void serial_throttle (struct tty_struct * tty)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int port = MINOR(tty->device) - serial->minor;
-
- dbg("serial_throttle port %d", port);
+ struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+ struct usb_serial *serial;
- /* do some sanity checking that we really have a device present */
- if (!serial) {
- dbg("serial == NULL!");
+ dbg("serial_throttle");
+
+ if (port_paranoia_check (port, "serial_throttle")) {
return;
}
- if (!serial->type) {
- dbg("serial->type == NULL!");
+
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "serial_throttle")) {
return;
}
- if (!serial->port[port].active) {
- dbg ("device not open");
+
+ dbg("serial_throttle port %d", port->number);
+
+ if (!port->active) {
+ dbg ("port not open");
return;
}
/* pass on to the driver specific version of this function */
if (serial->type->throttle) {
- serial->type->throttle(tty);
- } else {
- generic_throttle(tty);
+ serial->type->throttle(port);
}
return;
@@ -528,30 +581,30 @@ static void serial_throttle (struct tty_struct * tty)
static void serial_unthrottle (struct tty_struct * tty)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int port = MINOR(tty->device) - serial->minor;
-
- dbg("serial_unthrottle port %d", port);
+ struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+ struct usb_serial *serial;
- /* do some sanity checking that we really have a device present */
- if (!serial) {
- dbg("serial == NULL!");
+ dbg("serial_unthrottle");
+
+ if (port_paranoia_check (port, "serial_unthrottle")) {
return;
}
- if (!serial->type) {
- dbg("serial->type == NULL!");
+
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "serial_unthrottle")) {
return;
}
- if (!serial->port[port].active) {
- dbg ("device not open");
+
+ dbg("serial_unthrottle port %d", port->number);
+
+ if (!port->active) {
+ dbg ("port not open");
return;
}
/* pass on to the driver specific version of this function */
if (serial->type->unthrottle) {
- serial->type->unthrottle(tty);
- } else {
- generic_unthrottle(tty);
+ serial->type->unthrottle(port);
}
return;
@@ -560,70 +613,62 @@ static void serial_unthrottle (struct tty_struct * tty)
static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int port;
-
+ struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+ struct usb_serial *serial;
+
dbg("serial_ioctl");
-
- if (!serial) {
- dbg("serial == NULL!");
+
+ if (port_paranoia_check (port, "serial_ioctl")) {
return -ENODEV;
}
- port = MINOR(tty->device) - serial->minor;
-
- dbg("serial_ioctl port %d", port);
-
- /* do some sanity checking that we really have a device present */
- if (!serial->type) {
- dbg("serial->type == NULL!");
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "serial_ioctl")) {
return -ENODEV;
}
- if (!serial->port[port].active) {
- dbg ("device not open");
+
+ dbg("serial_ioctl port %d", port->number);
+
+ if (!port->active) {
+ dbg ("port not open");
return -ENODEV;
}
/* pass on to the driver specific version of this function if it is available */
if (serial->type->ioctl) {
- return (serial->type->ioctl(tty, file, cmd, arg));
+ return (serial->type->ioctl(port, file, cmd, arg));
} else {
- return (generic_ioctl (tty, file, cmd, arg));
+ return -ENOIOCTLCMD;
}
}
static void serial_set_termios (struct tty_struct *tty, struct termios * old)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int port;
-
+ struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+ struct usb_serial *serial;
+
dbg("serial_set_termios");
-
- if (!serial) {
- dbg("serial == NULL!");
+
+ if (port_paranoia_check (port, "serial_set_termios")) {
return;
}
- port = MINOR(tty->device) - serial->minor;
-
- dbg("serial_set_termios port %d", port);
-
- /* do some sanity checking that we really have a device present */
- if (!serial->type) {
- dbg("serial->type == NULL!");
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "serial_set_termios")) {
return;
}
- if (!serial->port[port].active) {
- dbg ("device not open");
+
+ dbg("serial_set_termios port %d", port->number);
+
+ if (!port->active) {
+ dbg ("port not open");
return;
}
/* pass on to the driver specific version of this function if it is available */
if (serial->type->set_termios) {
- serial->type->set_termios(tty, old);
- } else {
- generic_set_termios (tty, old);
+ serial->type->set_termios(port, old);
}
return;
@@ -632,32 +677,31 @@ static void serial_set_termios (struct tty_struct *tty, struct termios * old)
static void serial_break (struct tty_struct *tty, int break_state)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int port;
-
- if (!serial) {
- dbg("serial == NULL!");
+ struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+ struct usb_serial *serial;
+
+ dbg("serial_break");
+
+ if (port_paranoia_check (port, "serial_break")) {
return;
}
- port = MINOR(tty->device) - serial->minor;
-
- dbg("serial_break port %d", port);
-
- /* do some sanity checking that we really have a device present */
- if (!serial->type) {
- dbg("serial->type == NULL!");
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "serial_break")) {
return;
}
- if (!serial->port[port].active) {
- dbg ("device not open");
+
+ dbg("serial_break port %d", port->number);
+
+ if (!port->active) {
+ dbg ("port not open");
return;
}
/* pass on to the driver specific version of this function if it is
available */
if (serial->type->break_ctl) {
- serial->type->break_ctl(tty, break_state);
+ serial->type->break_ctl(port, break_state);
}
}
@@ -666,13 +710,9 @@ static void serial_break (struct tty_struct *tty, int break_state)
/*****************************************************************************
* Connect Tech's White Heat specific driver functions
*****************************************************************************/
-static int whiteheat_serial_open (struct tty_struct *tty, struct file *filp)
+static int whiteheat_open (struct usb_serial_port *port, struct file *filp)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int portNumber = MINOR(tty->device) - serial->minor;
- struct usb_serial_port *port = &serial->port[portNumber];
-
- dbg("whiteheat_serial_open port %d", portNumber);
+ dbg("whiteheat_open port %d", port->number);
if (port->active) {
dbg ("device already open");
@@ -681,7 +721,7 @@ static int whiteheat_serial_open (struct tty_struct *tty, struct file *filp)
port->active = 1;
/*Start reading from the device*/
- if (usb_submit_urb(&port->read_urb))
+ if (usb_submit_urb(port->read_urb))
dbg("usb_submit_urb(read bulk) failed");
/* Need to do device specific setup here (control lines, baud rate, etc.) */
@@ -691,36 +731,30 @@ static int whiteheat_serial_open (struct tty_struct *tty, struct file *filp)
}
-static void whiteheat_serial_close(struct tty_struct *tty, struct file * filp)
+static void whiteheat_close(struct usb_serial_port *port, struct file * filp)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int portNumber = MINOR(tty->device) - serial->minor;
- struct usb_serial_port *port = &serial->port[portNumber];
-
- dbg("whiteheat_serial_close port %d", portNumber);
+ dbg("whiteheat_close port %d", port->number);
/* Need to change the control lines here */
/* FIXME */
/* shutdown our bulk reads and writes */
- usb_unlink_urb (&port->write_urb);
- usb_unlink_urb (&port->read_urb);
+ usb_unlink_urb (port->write_urb);
+ usb_unlink_urb (port->read_urb);
port->active = 0;
}
-static void whiteheat_set_termios (struct tty_struct *tty, struct termios *old_termios)
+static void whiteheat_set_termios (struct usb_serial_port *port, struct termios *old_termios)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int port = MINOR(tty->device) - serial->minor;
- unsigned int cflag = tty->termios->c_cflag;
+ unsigned int cflag = port->tty->termios->c_cflag;
- dbg("whiteheat_set_termios port %d", port);
+ dbg("whiteheat_set_termios port %d", port->number);
/* check that they really want us to change something */
if (old_termios) {
if ((cflag == old_termios->c_cflag) &&
- (RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) {
+ (RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) {
dbg("nothing to change...");
return;
}
@@ -732,12 +766,9 @@ static void whiteheat_set_termios (struct tty_struct *tty, struct termios *old_t
return;
}
-static void whiteheat_throttle (struct tty_struct * tty)
+static void whiteheat_throttle (struct usb_serial_port *port)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int port = MINOR(tty->device) - serial->minor;
-
- dbg("whiteheat_throttle port %d", port);
+ dbg("whiteheat_throttle port %d", port->number);
/* Change the control signals */
/* FIXME!!! */
@@ -746,12 +777,9 @@ static void whiteheat_throttle (struct tty_struct * tty)
}
-static void whiteheat_unthrottle (struct tty_struct * tty)
+static void whiteheat_unthrottle (struct usb_serial_port *port)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int port = MINOR(tty->device) - serial->minor;
-
- dbg("whiteheat_unthrottle port %d", port);
+ dbg("whiteheat_unthrottle port %d", port->number);
/* Change the control signals */
/* FIXME!!! */
@@ -837,13 +865,9 @@ static int whiteheat_startup (struct usb_serial *serial)
/******************************************************************************
* Handspring Visor specific driver functions
******************************************************************************/
-static int visor_serial_open (struct tty_struct *tty, struct file *filp)
+static int visor_open (struct usb_serial_port *port, struct file *filp)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int portNumber = MINOR(tty->device) - serial->minor;
- struct usb_serial_port *port = &serial->port[portNumber];
-
- dbg("visor_serial_open port %d", portNumber);
+ dbg("visor_open port %d", port->number);
if (port->active) {
dbg ("device already open");
@@ -853,23 +877,22 @@ static int visor_serial_open (struct tty_struct *tty, struct file *filp)
port->active = 1;
/*Start reading from the device*/
- if (usb_submit_urb(&port->read_urb))
+ if (usb_submit_urb(port->read_urb))
dbg("usb_submit_urb(read bulk) failed");
return (0);
}
-static void visor_serial_close(struct tty_struct *tty, struct file * filp)
+
+static void visor_close (struct usb_serial_port *port, struct file * filp)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int portNumber = MINOR(tty->device) - serial->minor;
- struct usb_serial_port *port = &serial->port[portNumber];
+ struct usb_serial *serial = port->serial;
unsigned char *transfer_buffer = kmalloc (0x12, GFP_KERNEL);
- dbg("visor_serial_close port %d", portNumber);
+ dbg("visor_close port %d", port->number);
if (!transfer_buffer) {
- err("visor_serial_close: kmalloc(%d) failed.", 0x12);
+ err("visor_close: kmalloc(%d) failed.", 0x12);
} else {
/* send a shutdown message to the device */
usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_CLOSE_NOTIFICATION,
@@ -877,33 +900,27 @@ static void visor_serial_close(struct tty_struct *tty, struct file * filp)
}
/* shutdown our bulk reads and writes */
- usb_unlink_urb (&port->write_urb);
- usb_unlink_urb (&port->read_urb);
+ usb_unlink_urb (port->write_urb);
+ usb_unlink_urb (port->read_urb);
port->active = 0;
}
-static void visor_throttle (struct tty_struct * tty)
+static void visor_throttle (struct usb_serial_port *port)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int port = MINOR(tty->device) - serial->minor;
-
- dbg("visor_throttle port %d", port);
+ dbg("visor_throttle port %d", port->number);
- usb_unlink_urb (&serial->port[port].read_urb);
+ usb_unlink_urb (port->read_urb);
return;
}
-static void visor_unthrottle (struct tty_struct * tty)
+static void visor_unthrottle (struct usb_serial_port *port)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int port = MINOR(tty->device) - serial->minor;
+ dbg("visor_unthrottle port %d", port->number);
- dbg("visor_unthrottle port %d", port);
-
- if (usb_unlink_urb (&serial->port[port].read_urb))
+ if (usb_unlink_urb (port->read_urb))
dbg("usb_submit_urb(read bulk) failed");
return;
@@ -988,17 +1005,15 @@ static int visor_startup (struct usb_serial *serial)
#include "ftdi_sio.h"
-static int ftdi_sio_serial_open (struct tty_struct *tty, struct file *filp)
+static int ftdi_sio_open (struct usb_serial_port *port, struct file *filp)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int portNumber = MINOR(tty->device) - serial->minor;
- struct usb_serial_port *port = &serial->port[portNumber];
+ struct usb_serial *serial = port->serial;
char buf[1]; /* Needed for the usb_control_msg I think */
- dbg("ftdi_sio_serial_open port %d", portNumber);
+ dbg("ftdi_sio_open port %d", port->number);
if (port->active) {
- dbg ("device already open");
+ dbg ("port already open");
return -EINVAL;
}
port->active = 1;
@@ -1069,21 +1084,19 @@ static int ftdi_sio_serial_open (struct tty_struct *tty, struct file *filp)
}
/*Start reading from the device*/
- if (usb_submit_urb(&port->read_urb))
+ if (usb_submit_urb(port->read_urb))
dbg("usb_submit_urb(read bulk) failed");
return (0);
}
-static void ftdi_sio_serial_close (struct tty_struct *tty, struct file *filp)
+static void ftdi_sio_close (struct usb_serial_port *port, struct file *filp)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int portNumber = MINOR(tty->device) - serial->minor;
- struct usb_serial_port *port = &serial->port[portNumber];
+ struct usb_serial *serial = port->serial;
char buf[1];
- dbg("ftdi_sio_serial_close port %d", portNumber);
+ dbg("ftdi_sio_close port %d", port->number);
/* FIXME - might be able to do both simultaneously */
if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
@@ -1104,8 +1117,8 @@ static void ftdi_sio_serial_close (struct tty_struct *tty, struct file *filp)
/* FIXME Should I flush the device here? - not doing it for now */
/* shutdown our bulk reads and writes */
- usb_unlink_urb (&port->write_urb);
- usb_unlink_urb (&port->read_urb);
+ usb_unlink_urb (port->write_urb);
+ usb_unlink_urb (port->read_urb);
port->active = 0;
}
@@ -1116,15 +1129,13 @@ static void ftdi_sio_serial_close (struct tty_struct *tty, struct file *filp)
B1 0
B2..7 length of message excluding byte 0
*/
-static int ftdi_sio_serial_write (struct tty_struct * tty, int from_user,
- const unsigned char *buf, int count)
+static int ftdi_sio_write (struct usb_serial_port *port, int from_user,
+ const unsigned char *buf, int count)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int portNumber = MINOR(tty->device) - serial->minor;
- struct usb_serial_port *port = &serial->port[portNumber];
+ struct usb_serial *serial = port->serial;
const int data_offset = 1;
- dbg("ftdi_sio_serial_write port %d, %d bytes", portNumber, count);
+ dbg("ftdi_sio_serial_write port %d, %d bytes", port->number, count);
if (count == 0) {
dbg("write request of 0 bytes");
@@ -1133,9 +1144,9 @@ static int ftdi_sio_serial_write (struct tty_struct * tty, int from_user,
/* only do something if we have a bulk out endpoint */
if (serial->num_bulk_out) {
- unsigned char *first_byte = port->write_urb.transfer_buffer;
+ unsigned char *first_byte = port->write_urb->transfer_buffer;
- if (port->write_urb.status == -EINPROGRESS) {
+ if (port->write_urb->status == -EINPROGRESS) {
dbg ("already writing");
return 0;
}
@@ -1148,16 +1159,16 @@ static int ftdi_sio_serial_write (struct tty_struct * tty, int from_user,
/* Copy in the data to send */
if (from_user) {
- copy_from_user(port->write_urb.transfer_buffer + data_offset ,
+ copy_from_user(port->write_urb->transfer_buffer + data_offset ,
buf, count - data_offset );
}
else {
- memcpy(port->write_urb.transfer_buffer + data_offset,
+ memcpy(port->write_urb->transfer_buffer + data_offset,
buf, count - data_offset );
}
/* Write the control byte at the front of the packet*/
- first_byte = port->write_urb.transfer_buffer;
+ first_byte = port->write_urb->transfer_buffer;
*first_byte = 1 | ((count-data_offset) << 2) ;
#ifdef DEBUG
@@ -1181,9 +1192,9 @@ static int ftdi_sio_serial_write (struct tty_struct * tty, int from_user,
#endif
/* send the data out the bulk port */
- port->write_urb.transfer_buffer_length = count;
+ port->write_urb->transfer_buffer_length = count;
- if (usb_submit_urb(&port->write_urb))
+ if (usb_submit_urb(port->write_urb))
dbg("usb_submit_urb(write bulk) failed");
dbg("write returning: %d", count - data_offset);
@@ -1197,14 +1208,24 @@ static int ftdi_sio_serial_write (struct tty_struct * tty, int from_user,
static void ftdi_sio_read_bulk_callback (struct urb *urb)
{ /* ftdi_sio_serial_buld_callback */
- struct usb_serial *serial = (struct usb_serial *)urb->context;
- struct tty_struct *tty = serial->tty;
+ struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+ struct usb_serial *serial;
+ struct tty_struct *tty;
unsigned char *data = urb->transfer_buffer;
const int data_offset = 2;
int i;
dbg("ftdi_sio_read_bulk_callback");
+ if (port_paranoia_check (port, "ftdi_sio_read_bulk_callback")) {
+ return;
+ }
+
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "ftdi_sio_read_bulk_callback")) {
+ return;
+ }
+
if (urb->status) {
dbg("nonzero read bulk status received: %d", urb->status);
return;
@@ -1227,6 +1248,7 @@ static void ftdi_sio_read_bulk_callback (struct urb *urb)
if (urb->actual_length > data_offset) {
+ tty = port->tty;
for (i = data_offset ; i < urb->actual_length ; ++i) {
tty_insert_flip_char(tty, data[i], 0);
}
@@ -1241,14 +1263,14 @@ static void ftdi_sio_read_bulk_callback (struct urb *urb)
} /* ftdi_sio_serial_read_bulk_callback */
-static void ftdi_sio_set_termios (struct tty_struct *tty, struct termios *old_termios)
+static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios *old_termios)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int port = MINOR(tty->device) - serial->minor;
- unsigned int cflag = tty->termios->c_cflag;
+ struct usb_serial *serial = port->serial;
+ unsigned int cflag = port->tty->termios->c_cflag;
__u16 urb_value; /* Will hold the new flags */
char buf[1]; /* Perhaps I should dynamically alloc this? */
- dbg("ftdi_sio_set_termios port %d", port);
+
+ dbg("ftdi_sio_set_termios port %d", port->number);
/* FIXME - we should keep the old termios really */
/* FIXME -For this cut I don't care if the line is really changing or
@@ -1312,14 +1334,14 @@ static void ftdi_sio_set_termios (struct tty_struct *tty, struct termios *old_te
/*FIXME - the beginnings of this implementation - not even hooked into the driver yet */
-static int ftdi_sio_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
+static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int port = MINOR(tty->device) - serial->minor;
+ struct usb_serial *serial = port->serial;
__u16 urb_value=0; /* Will hold the new flags */
char buf[1];
int ret, mask;
- dbg("ftdi_sio_ioctl port %d", port);
+
+ dbg("ftdi_sio_ioctl port %d", port->number);
/* Based on code from acm.c */
switch (cmd) {
@@ -1408,8 +1430,9 @@ static int ftdi_sio_ioctl (struct tty_struct *tty, struct file * file, unsigned
static void keyspan_pda_rx_interrupt (struct urb *urb)
{
- struct usb_serial *serial = (struct usb_serial *) urb->context;
- struct tty_struct *tty = serial->tty;
+ struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+ struct usb_serial *serial;
+ struct tty_struct *tty;
unsigned char *data = urb->transfer_buffer;
int i;
@@ -1417,11 +1440,21 @@ static void keyspan_pda_rx_interrupt (struct urb *urb)
if (urb->status)
return;
- /* see if the message is data or a status interrupt */
+ if (port_paranoia_check (port, "keyspan_pda_rx_interrupt")) {
+ return;
+ }
+
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "keyspan_pda_rx_interrupt")) {
+ return;
+ }
+
+ /* see if the message is data or a status interrupt */
switch (data[0]) {
case 0:
/* rest of message is rx data */
if (urb->actual_length) {
+ tty = serial->port[0].tty;
for (i = 1; i < urb->actual_length ; ++i) {
tty_insert_flip_char(tty, data[i], 0);
}
@@ -1435,6 +1468,7 @@ static void keyspan_pda_rx_interrupt (struct urb *urb)
case 1: /* modemline change */
break;
case 2: /* tx unthrottle interrupt */
+ tty = serial->port[0].tty;
serial->tx_throttled = 0;
wake_up(&serial->write_wait); /* wake up writer */
wake_up(&tty->write_wait); /* them too */
@@ -1451,11 +1485,8 @@ static void keyspan_pda_rx_interrupt (struct urb *urb)
}
-static void keyspan_pda_rx_throttle (struct tty_struct *tty)
+static void keyspan_pda_rx_throttle (struct usb_serial_port *port)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int port = MINOR(tty->device) - serial->minor;
-
/* stop receiving characters. We just turn off the URB request, and
let chars pile up in the device. If we're doing hardware
flowcontrol, the device will signal the other end when its buffer
@@ -1463,19 +1494,16 @@ static void keyspan_pda_rx_throttle (struct tty_struct *tty)
send an XOFF, although it might make sense to foist that off
upon the device too. */
- dbg("keyspan_pda_rx_throttle port %d", port);
- usb_unlink_urb(&serial->port[port].read_urb);
+ dbg("keyspan_pda_rx_throttle port %d", port->number);
+ usb_unlink_urb(port->read_urb);
}
-static void keyspan_pda_rx_unthrottle (struct tty_struct *tty)
+static void keyspan_pda_rx_unthrottle (struct usb_serial_port *port)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int port = MINOR(tty->device) - serial->minor;
-
/* just restart the receive interrupt URB */
- dbg("keyspan_pda_rx_unthrottle port %d", port);
- if (usb_submit_urb(&serial->port[port].read_urb))
+ dbg("keyspan_pda_rx_unthrottle port %d", port->number);
+ if (usb_submit_urb(port->read_urb))
dbg(" usb_submit_urb(read urb) failed");
return;
}
@@ -1516,9 +1544,9 @@ static int keyspan_pda_setbaud (struct usb_serial *serial, int baud)
}
-static void keyspan_pda_break_ctl (struct tty_struct *tty, int break_state)
+static void keyspan_pda_break_ctl (struct usb_serial_port *port, int break_state)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
+ struct usb_serial *serial = port->serial;
int value;
if (break_state == -1)
value = 1; /* start break */
@@ -1535,11 +1563,11 @@ static void keyspan_pda_break_ctl (struct tty_struct *tty, int break_state)
}
-static void keyspan_pda_set_termios (struct tty_struct *tty,
+static void keyspan_pda_set_termios (struct usb_serial_port *port,
struct termios *old_termios)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- unsigned int cflag = tty->termios->c_cflag;
+ struct usb_serial *serial = port->serial;
+ unsigned int cflag = port->tty->termios->c_cflag;
/* cflag specifies lots of stuff: number of stop bits, parity, number
of data bits, baud. What can the device actually handle?:
@@ -1611,10 +1639,10 @@ static int keyspan_pda_set_modem_info(struct usb_serial *serial,
}
-static int keyspan_pda_ioctl(struct tty_struct *tty, struct file *file,
+static int keyspan_pda_ioctl(struct usb_serial_port *port, struct file *file,
unsigned int cmd, unsigned long arg)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
+ struct usb_serial *serial = port->serial;
int rc;
unsigned int value;
unsigned char status, mask;
@@ -1677,11 +1705,10 @@ static int keyspan_pda_ioctl(struct tty_struct *tty, struct file *file,
return -ENOIOCTLCMD;
}
-static int keyspan_pda_write(struct tty_struct * tty, int from_user,
+static int keyspan_pda_write(struct usb_serial_port *port, int from_user,
const unsigned char *buf, int count)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int port = MINOR(tty->device) - serial->minor;
+ struct usb_serial *serial = port->serial;
int request_unthrottle = 0;
int rc = 0;
DECLARE_WAITQUEUE(wait, current);
@@ -1704,7 +1731,7 @@ static int keyspan_pda_write(struct tty_struct * tty, int from_user,
the TX urb is in-flight (wait until it completes)
the device is full (wait until it says there is room)
*/
- while (serial->port[port].write_urb.status == -EINPROGRESS) {
+ while (port->write_urb->status == -EINPROGRESS) {
if (0 /* file->f_flags & O_NONBLOCK */) {
rc = -EAGAIN;
goto err;
@@ -1746,8 +1773,7 @@ static int keyspan_pda_write(struct tty_struct * tty, int from_user,
remove_wait_queue(&serial->write_wait, &wait);
set_current_state(TASK_RUNNING);
- count = (count > serial->port[port].bulk_out_size) ?
- serial->port[port].bulk_out_size : count;
+ count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
if (count > serial->tx_room) {
unsigned char room;
/* Looks like we might overrun the Tx buffer. Ask the device
@@ -1784,17 +1810,15 @@ static int keyspan_pda_write(struct tty_struct * tty, int from_user,
if (count) {
/* now transfer data */
if (from_user) {
- copy_from_user(serial->port[port].write_urb.transfer_buffer,
- buf, count);
+ copy_from_user(port->write_urb->transfer_buffer, buf, count);
}
else {
- memcpy (serial->port[port].write_urb.transfer_buffer,
- buf, count);
+ memcpy (port->write_urb->transfer_buffer, buf, count);
}
/* send the data out the bulk port */
- serial->port[port].write_urb.transfer_buffer_length = count;
+ port->write_urb->transfer_buffer_length = count;
- if (usb_submit_urb(&serial->port[port].write_urb))
+ if (usb_submit_urb(port->write_urb))
dbg(" usb_submit_urb(write bulk) failed");
}
else {
@@ -1828,11 +1852,22 @@ static int keyspan_pda_write(struct tty_struct * tty, int from_user,
static void keyspan_pda_write_bulk_callback (struct urb *urb)
{
- struct usb_serial *serial = (struct usb_serial *) urb->context;
- struct tty_struct *tty = serial->tty;
+ struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+ struct usb_serial *serial;
+ struct tty_struct *tty;
+ if (port_paranoia_check (port, "keyspan_pda_rx_interrupt")) {
+ return;
+ }
+
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "keyspan_pda_rx_interrupt")) {
+ return;
+ }
+
wake_up_interruptible(&serial->write_wait);
+ tty = port->tty;
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
tty->ldisc.write_wakeup)
(tty->ldisc.write_wakeup)(tty);
@@ -1841,9 +1876,9 @@ static void keyspan_pda_write_bulk_callback (struct urb *urb)
}
-static int keyspan_pda_write_room (struct tty_struct *tty)
+static int keyspan_pda_write_room (struct usb_serial_port *port)
{
- struct usb_serial *serial = (struct usb_serial *)tty->driver_data;
+ struct usb_serial *serial = port->serial;
/* used by n_tty.c for processing of tabs and such. Giving it our
conservative guess is probably good enough, but needs testing by
@@ -1853,9 +1888,9 @@ static int keyspan_pda_write_room (struct tty_struct *tty)
}
-static int keyspan_pda_chars_in_buffer (struct tty_struct *tty)
+static int keyspan_pda_chars_in_buffer (struct usb_serial_port *port)
{
- struct usb_serial *serial = (struct usb_serial *)tty->driver_data;
+ struct usb_serial *serial = port->serial;
/* when throttled, return at least WAKEUP_CHARS to tell select() (via
n_tty.c:normal_poll() ) that we're not writeable. */
@@ -1865,11 +1900,9 @@ static int keyspan_pda_chars_in_buffer (struct tty_struct *tty)
}
-static int keyspan_pda_serial_open (struct tty_struct *tty, struct file *filp)
+static int keyspan_pda_open (struct usb_serial_port *port, struct file *filp)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int portNumber = MINOR(tty->device) - serial->minor;
- struct usb_serial_port *port = &serial->port[portNumber];
+ struct usb_serial *serial = port->serial;
unsigned char room;
int rc;
@@ -1901,33 +1934,30 @@ static int keyspan_pda_serial_open (struct tty_struct *tty, struct file *filp)
/* the normal serial device seems to always turn on DTR and RTS here,
so do the same */
- if (tty->termios->c_cflag & CBAUD)
+ if (port->tty->termios->c_cflag & CBAUD)
keyspan_pda_set_modem_info(serial, (1<<7) | (1<<2) );
else
keyspan_pda_set_modem_info(serial, 0);
/*Start reading from the device*/
- if (usb_submit_urb(&port->read_urb))
+ if (usb_submit_urb(port->read_urb))
dbg(" usb_submit_urb(read int) failed");
return (0);
}
-static void keyspan_pda_serial_close(struct tty_struct *tty,
- struct file *filp)
+static void keyspan_pda_close(struct usb_serial_port *port, struct file *filp)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int portNumber = MINOR(tty->device) - serial->minor;
- struct usb_serial_port *port = &serial->port[portNumber];
+ struct usb_serial *serial = port->serial;
/* the normal serial device seems to always shut off DTR and RTS now */
- if (tty->termios->c_cflag & HUPCL)
+ if (port->tty->termios->c_cflag & HUPCL)
keyspan_pda_set_modem_info(serial, 0);
/* shutdown our bulk reads and writes */
- usb_unlink_urb (&port->write_urb);
- usb_unlink_urb (&port->read_urb);
+ usb_unlink_urb (port->write_urb);
+ usb_unlink_urb (port->read_urb);
port->active = 0;
}
@@ -1971,7 +2001,7 @@ static int keyspan_pda_startup (struct usb_serial *serial)
intin = serial->port[0].interrupt_in_endpoint;
/* set up the receive interrupt urb */
- FILL_INT_URB(&serial->port[0].read_urb, serial->dev,
+ FILL_INT_URB(serial->port[0].read_urb, serial->dev,
usb_rcvintpipe(serial->dev, intin->bEndpointAddress),
serial->port[0].interrupt_in_buffer,
intin->wMaxPacketSize,
@@ -1990,13 +2020,11 @@ static int keyspan_pda_startup (struct usb_serial *serial)
/*****************************************************************************
* generic devices specific driver functions
*****************************************************************************/
-static int generic_serial_open (struct tty_struct *tty, struct file *filp)
+static int generic_open (struct usb_serial_port *port, struct file *filp)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int portNumber = MINOR(tty->device) - serial->minor;
- struct usb_serial_port *port = &serial->port[portNumber];
+ struct usb_serial *serial = port->serial;
- dbg("generic_serial_open port %d", portNumber);
+ dbg("generic_open port %d", port->number);
if (port->active) {
dbg ("device already open");
@@ -2007,7 +2035,7 @@ static int generic_serial_open (struct tty_struct *tty, struct file *filp)
/* if we have a bulk interrupt, start reading from it */
if (serial->num_bulk_in) {
/*Start reading from the device*/
- if (usb_submit_urb(&port->read_urb))
+ if (usb_submit_urb(port->read_urb))
dbg("usb_submit_urb(read bulk) failed");
}
@@ -2015,33 +2043,29 @@ static int generic_serial_open (struct tty_struct *tty, struct file *filp)
}
-static void generic_serial_close(struct tty_struct *tty, struct file * filp)
+static void generic_close (struct usb_serial_port *port, struct file * filp)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int portNumber = MINOR(tty->device) - serial->minor;
- struct usb_serial_port *port = &serial->port[portNumber];
+ struct usb_serial *serial = port->serial;
- dbg("generic_serial_close port %d", portNumber);
+ dbg("generic_close port %d", port->number);
/* shutdown any bulk reads that might be going on */
if (serial->num_bulk_out) {
- usb_unlink_urb (&port->write_urb);
+ usb_unlink_urb (port->write_urb);
}
if (serial->num_bulk_in) {
- usb_unlink_urb (&port->read_urb);
+ usb_unlink_urb (port->read_urb);
}
port->active = 0;
}
-static int generic_serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count)
+static int generic_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count)
{
- struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
- int portNumber = MINOR(tty->device) - serial->minor;
- struct usb_serial_port *port = &serial->port[portNumber];
+ struct usb_serial *serial = port->serial;
- dbg("generic_serial_write port %d", portNumber);
+ dbg("generic_serial_write port %d", port->number);
if (count == 0) {
dbg("write request of 0 bytes");
@@ -2050,7 +2074,7 @@ static int generic_serial_write (struct tty_struct * tty, int from_user, const u
/* only do something if we have a bulk out endpoint */
if (serial->num_bulk_out) {
- if (port->write_urb.status == -EINPROGRESS) {
+ if (port->write_urb->status == -EINPROGRESS) {
dbg ("already writing");
return (0);
}
@@ -2058,16 +2082,16 @@ static int generic_serial_write (struct tty_struct * tty, int from_user, const u
count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
if (from_user) {
- copy_from_user(port->write_urb.transfer_buffer, buf, count);
+ copy_from_user(port->write_urb->transfer_buffer, buf, count);
}
else {
- memcpy (port->write_urb.transfer_buffer, buf, count);
+ memcpy (port->write_urb->transfer_buffer, buf, count);
}
/* send the data out the bulk port */
- port->write_urb.transfer_buffer_length = count;
+ port->write_urb->transfer_buffer_length = count;
- if (usb_submit_urb(&port->write_urb))
+ if (usb_submit_urb(port->write_urb))
dbg("usb_submit_urb(write bulk) failed");
return (count);
@@ -2078,17 +2102,15 @@ static int generic_serial_write (struct tty_struct * tty, int from_user, const u
}
-static int generic_write_room (struct tty_struct *tty)
+static int generic_write_room (struct usb_serial_port *port)
{
- struct usb_serial *serial = (struct usb_serial *)tty->driver_data;
- int portNumber = MINOR(tty->device) - serial->minor;
- struct usb_serial_port *port = &serial->port[portNumber];
+ struct usb_serial *serial = port->serial;
int room;
- dbg("generic_write_room port %d", portNumber);
+ dbg("generic_write_room port %d", port->number);
if (serial->num_bulk_out) {
- if (port->write_urb.status == -EINPROGRESS)
+ if (port->write_urb->status == -EINPROGRESS)
room = 0;
else
room = port->bulk_out_size;
@@ -2100,16 +2122,14 @@ static int generic_write_room (struct tty_struct *tty)
}
-static int generic_chars_in_buffer (struct tty_struct *tty)
+static int generic_chars_in_buffer (struct usb_serial_port *port)
{
- struct usb_serial *serial = (struct usb_serial *)tty->driver_data;
- int portNumber = MINOR(tty->device) - serial->minor;
- struct usb_serial_port *port = &serial->port[portNumber];
+ struct usb_serial *serial = port->serial;
- dbg("generic_chars_in_buffer port %d", portNumber);
+ dbg("generic_chars_in_buffer port %d", port->number);
if (serial->num_bulk_out) {
- if (port->write_urb.status == -EINPROGRESS) {
+ if (port->write_urb->status == -EINPROGRESS) {
return (port->bulk_out_size);
}
}
@@ -2118,43 +2138,25 @@ static int generic_chars_in_buffer (struct tty_struct *tty)
}
-static void generic_throttle (struct tty_struct *tty)
-{
- /* do nothing for the generic device */
- return;
-}
-
-
-static void generic_unthrottle (struct tty_struct *tty)
-{
- /* do nothing for the generic device */
- return;
-}
-
-
-static int generic_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
-{
- /* generic driver doesn't support any ioctls yet */
- return -ENOIOCTLCMD;
-}
-
-
-static void generic_set_termios (struct tty_struct *tty, struct termios * old)
-{
- /* generic driver doesn't really care about setting any line settings */
- return;
-}
-
-
static void generic_read_bulk_callback (struct urb *urb)
{
- struct usb_serial *serial = (struct usb_serial *)urb->context;
- struct tty_struct *tty = serial->tty;
+ struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+ struct usb_serial *serial;
+ struct tty_struct *tty;
unsigned char *data = urb->transfer_buffer;
int i;
dbg("generic_read_bulk_callback");
+ if (port_paranoia_check (port, "generic_read_bulk_callback")) {
+ return;
+ }
+
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "generic_read_bulk_callback")) {
+ return;
+ }
+
if (urb->status) {
dbg("nonzero read bulk status received: %d", urb->status);
return;
@@ -2169,7 +2171,8 @@ static void generic_read_bulk_callback (struct urb *urb)
printk ("\n");
}
#endif
-
+
+ tty = port->tty;
if (urb->actual_length) {
for (i = 0; i < urb->actual_length ; ++i) {
tty_insert_flip_char(tty, data[i], 0);
@@ -2187,16 +2190,27 @@ static void generic_read_bulk_callback (struct urb *urb)
static void generic_write_bulk_callback (struct urb *urb)
{
- struct usb_serial *serial = (struct usb_serial *) urb->context;
- struct tty_struct *tty = serial->tty;
+ struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+ struct usb_serial *serial;
+ struct tty_struct *tty;
dbg("generic_write_bulk_callback");
+ if (port_paranoia_check (port, "generic_write_bulk_callback")) {
+ return;
+ }
+
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "generic_write_bulk_callback")) {
+ return;
+ }
+
if (urb->status) {
dbg("nonzero write bulk status received: %d", urb->status);
return;
}
+ tty = port->tty;
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
(tty->ldisc.write_wakeup)(tty);
@@ -2206,7 +2220,6 @@ static void generic_write_bulk_callback (struct urb *urb)
}
-
static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum)
{
struct usb_serial *serial = NULL;
@@ -2331,6 +2344,11 @@ static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum)
/* set up the endpoint information */
for (i = 0; i < num_bulk_in; ++i) {
port = &serial->port[i];
+ port->read_urb = usb_alloc_urb (0);
+ if (!port->read_urb) {
+ err("No free urbs available");
+ goto probe_error;
+ }
buffer_size = bulk_in_endpoint[i]->wMaxPacketSize;
port->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
if (!port->bulk_in_buffer) {
@@ -2338,16 +2356,21 @@ static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum)
goto probe_error;
}
if (serial->type->read_bulk_callback) {
- FILL_BULK_URB(&port->read_urb, dev, usb_rcvbulkpipe (dev, bulk_in_endpoint[i]->bEndpointAddress),
- port->bulk_in_buffer, buffer_size, serial->type->read_bulk_callback, serial);
+ FILL_BULK_URB(port->read_urb, dev, usb_rcvbulkpipe (dev, bulk_in_endpoint[i]->bEndpointAddress),
+ port->bulk_in_buffer, buffer_size, serial->type->read_bulk_callback, port);
} else {
- FILL_BULK_URB(&port->read_urb, dev, usb_rcvbulkpipe (dev, bulk_in_endpoint[i]->bEndpointAddress),
- port->bulk_in_buffer, buffer_size, generic_read_bulk_callback, serial);
+ FILL_BULK_URB(port->read_urb, dev, usb_rcvbulkpipe (dev, bulk_in_endpoint[i]->bEndpointAddress),
+ port->bulk_in_buffer, buffer_size, generic_read_bulk_callback, port);
}
}
for (i = 0; i < num_bulk_out; ++i) {
port = &serial->port[i];
+ port->write_urb = usb_alloc_urb(0);
+ if (!port->write_urb) {
+ err("No free urbs available");
+ goto probe_error;
+ }
port->bulk_out_size = bulk_out_endpoint[i]->wMaxPacketSize;
port->bulk_out_buffer = kmalloc (port->bulk_out_size, GFP_KERNEL);
if (!port->bulk_out_buffer) {
@@ -2355,25 +2378,31 @@ static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum)
goto probe_error;
}
if (serial->type->write_bulk_callback) {
- FILL_BULK_URB(&port->write_urb, dev, usb_sndbulkpipe (dev, bulk_out_endpoint[i]->bEndpointAddress),
- port->bulk_out_buffer, port->bulk_out_size, serial->type->write_bulk_callback, serial);
+ FILL_BULK_URB(port->write_urb, dev, usb_sndbulkpipe (dev, bulk_out_endpoint[i]->bEndpointAddress),
+ port->bulk_out_buffer, port->bulk_out_size, serial->type->write_bulk_callback, port);
} else {
- FILL_BULK_URB(&port->write_urb, dev, usb_sndbulkpipe (dev, bulk_out_endpoint[i]->bEndpointAddress),
- port->bulk_out_buffer, port->bulk_out_size, generic_write_bulk_callback, serial);
+ FILL_BULK_URB(port->write_urb, dev, usb_sndbulkpipe (dev, bulk_out_endpoint[i]->bEndpointAddress),
+ port->bulk_out_buffer, port->bulk_out_size, generic_write_bulk_callback, port);
}
}
#if 0 /* use this code when WhiteHEAT is up and running */
for (i = 0; i < num_interrupt_in; ++i) {
+ port = &serial->port[i];
+ port->control_urb = usb_alloc_urb(0);
+ if (!port->control_urb) {
+ err("No free urbs available");
+ goto probe_error;
+ }
buffer_size = interrupt_in_endpoint[i]->wMaxPacketSize;
- serial->interrupt_in_buffer[i] = kmalloc (buffer_size, GFP_KERNEL);
- if (!serial->interrupt_in_buffer[i]) {
+ port->interrupt_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
+ if (!port->interrupt_in_buffer) {
err("Couldn't allocate interrupt_in_buffer");
goto probe_error;
}
- FILL_INT_URB(&serial->control_urb[i], dev, usb_rcvintpipe (dev, interrupt_in_endpoint[i]->bEndpointAddress),
- serial->interrupt_in_buffer[i], buffer_size, serial_control_irq,
- serial, interrupt_in_endpoint[i]->bInterval);
+ FILL_INT_URB(port->control_urb, dev, usb_rcvintpipe (dev, interrupt_in_endpoint[i]->bEndpointAddress),
+ port->interrupt_in_buffer, buffer_size, serial_control_irq,
+ port, interrupt_in_endpoint[i]->bInterval);
}
#endif
@@ -2395,15 +2424,27 @@ static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum)
probe_error:
if (serial) {
- for (i = 0; i < num_bulk_in; ++i)
+ for (i = 0; i < num_bulk_in; ++i) {
+ port = &serial->port[i];
+ if (port->read_urb)
+ usb_free_urb (port->read_urb);
if (serial->port[i].bulk_in_buffer[i])
kfree (serial->port[i].bulk_in_buffer);
- for (i = 0; i < num_bulk_out; ++i)
+ }
+ for (i = 0; i < num_bulk_out; ++i) {
+ port = &serial->port[i];
+ if (port->write_urb)
+ usb_free_urb (port->write_urb);
if (serial->port[i].bulk_out_buffer)
kfree (serial->port[i].bulk_out_buffer);
- for (i = 0; i < num_interrupt_in; ++i)
+ }
+ for (i = 0; i < num_interrupt_in; ++i) {
+ port = &serial->port[i];
+ if (port->control_urb)
+ usb_free_urb (port->control_urb);
if (serial->port[i].interrupt_in_buffer)
kfree (serial->port[i].interrupt_in_buffer);
+ }
/* return the minor range that this device had */
return_serial (serial);
@@ -2422,27 +2463,33 @@ static void usb_serial_disconnect(struct usb_device *dev, void *ptr)
int i;
if (serial) {
- /* need to stop any transfers...*/
- for (i = 0; i < serial->num_ports; ++i) {
- port = &serial->port[i];
- usb_unlink_urb (&port->write_urb);
- usb_unlink_urb (&port->read_urb);
- port->active = 0;
- }
+ for (i = 0; i < serial->num_ports; ++i)
+ serial->port[i].active = 0;
- /* free up any memory that we allocated */
for (i = 0; i < serial->num_bulk_in; ++i) {
port = &serial->port[i];
+ if (port->read_urb) {
+ usb_unlink_urb (port->read_urb);
+ usb_free_urb (port->read_urb);
+ }
if (port->bulk_in_buffer)
kfree (port->bulk_in_buffer);
}
for (i = 0; i < serial->num_bulk_out; ++i) {
port = &serial->port[i];
+ if (port->write_urb) {
+ usb_unlink_urb (port->write_urb);
+ usb_free_urb (port->write_urb);
+ }
if (port->bulk_out_buffer)
kfree (port->bulk_out_buffer);
}
for (i = 0; i < serial->num_interrupt_in; ++i) {
port = &serial->port[i];
+ if (port->control_urb) {
+ usb_unlink_urb (port->control_urb);
+ usb_free_urb (port->control_urb);
+ }
if (port->interrupt_in_buffer)
kfree (port->interrupt_in_buffer);
}
@@ -2468,7 +2515,7 @@ static void usb_serial_disconnect(struct usb_device *dev, void *ptr)
static struct tty_driver serial_tty_driver = {
magic: TTY_DRIVER_MAGIC,
driver_name: "usb",
- name: "ttyUSB",
+ name: "ttyUSB%d",
major: SERIAL_TTY_MAJOR,
minor_start: 0,
num: SERIAL_TTY_MINORS,
diff --git a/drivers/usb/serial/usb-serial.h b/drivers/usb/serial/usb-serial.h
index 71fecb0ec..48082101a 100644
--- a/drivers/usb/serial/usb-serial.h
+++ b/drivers/usb/serial/usb-serial.h
@@ -52,8 +52,12 @@ MODULE_PARM_DESC(product, "User specified USB idProduct");
#define MAX_NUM_PORTS 8 /* The maximum number of ports one device can grab at once */
+#define USB_SERIAL_MAGIC 0x6702 /* magic number for usb_serial struct */
+#define USB_SERIAL_PORT_MAGIC 0x7301 /* magic number for usb_serial_port struct */
+
struct usb_serial_port {
+ int magic;
struct usb_serial *serial; /* pointer back to the owner of this port */
struct tty_struct * tty; /* the coresponding tty for this device */
unsigned char minor;
@@ -63,21 +67,21 @@ struct usb_serial_port {
struct usb_endpoint_descriptor * interrupt_in_endpoint;
__u8 interrupt_in_interval;
unsigned char * interrupt_in_buffer;
- struct urb control_urb;
+ struct urb * control_urb;
unsigned char * bulk_in_buffer;
- struct urb read_urb;
+ struct urb * read_urb;
unsigned char * bulk_out_buffer;
int bulk_out_size;
- struct urb write_urb;
+ struct urb * write_urb;
void * private; /* data private to the specific driver */
};
struct usb_serial {
+ int magic;
struct usb_device * dev;
struct usb_serial_device_type * type;
- struct tty_struct * tty; /* the coresponding tty for this device */
unsigned char minor;
unsigned char num_ports; /* the number of ports this device has */
char num_interrupt_in; /* number of interrupt in endpoints we have */
@@ -121,16 +125,16 @@ struct usb_serial_device_type {
int (*startup) (struct usb_serial *serial); /* return 0 to continue initialization, anything else to abort */
/* serial function calls */
- int (*open)(struct tty_struct * tty, struct file * filp);
- void (*close)(struct tty_struct * tty, struct file * filp);
- int (*write)(struct tty_struct * tty, int from_user,const unsigned char *buf, int count);
- int (*write_room)(struct tty_struct *tty);
- int (*ioctl)(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg);
- void (*set_termios)(struct tty_struct *tty, struct termios * old);
- void (*break_ctl)(struct tty_struct *tty, int break_state);
- int (*chars_in_buffer)(struct tty_struct *tty);
- void (*throttle)(struct tty_struct * tty);
- void (*unthrottle)(struct tty_struct * tty);
+ int (*open) (struct usb_serial_port *port, struct file * filp);
+ void (*close) (struct usb_serial_port *port, struct file * filp);
+ int (*write) (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
+ int (*write_room) (struct usb_serial_port *port);
+ int (*ioctl) (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg);
+ void (*set_termios) (struct usb_serial_port *port, struct termios * old);
+ void (*break_ctl) (struct usb_serial_port *port, int break_state);
+ int (*chars_in_buffer) (struct usb_serial_port *port);
+ void (*throttle) (struct usb_serial_port *port);
+ void (*unthrottle) (struct usb_serial_port *port);
void (*read_bulk_callback)(struct urb *urb);
void (*write_bulk_callback)(struct urb *urb);
@@ -140,15 +144,11 @@ struct usb_serial_device_type {
/* function prototypes for a "generic" type serial converter (no flow control, not all endpoints needed) */
/* need to always compile these in, as some of the other devices use these functions as their own. */
/* if a driver does not provide a function pointer, the generic function will be called. */
-static int generic_serial_open (struct tty_struct *tty, struct file *filp);
-static void generic_serial_close (struct tty_struct *tty, struct file *filp);
-static int generic_serial_write (struct tty_struct *tty, int from_user, const unsigned char *buf, int count);
-static int generic_write_room (struct tty_struct *tty);
-static int generic_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg);
-static void generic_set_termios (struct tty_struct *tty, struct termios * old);
-static int generic_chars_in_buffer (struct tty_struct *tty);
-static void generic_throttle (struct tty_struct *tty);
-static void generic_unthrottle (struct tty_struct *tty);
+static int generic_open (struct usb_serial_port *port, struct file *filp);
+static void generic_close (struct usb_serial_port *port, struct file *filp);
+static int generic_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
+static int generic_write_room (struct usb_serial_port *port);
+static int generic_chars_in_buffer (struct usb_serial_port *port);
static void generic_read_bulk_callback (struct urb *urb);
static void generic_write_bulk_callback (struct urb *urb);
@@ -172,11 +172,11 @@ static struct usb_serial_device_type generic_device = {
#ifdef CONFIG_USB_SERIAL_WHITEHEAT
/* function prototypes for the Connect Tech WhiteHEAT serial converter */
-static int whiteheat_serial_open (struct tty_struct *tty, struct file *filp);
-static void whiteheat_serial_close (struct tty_struct *tty, struct file *filp);
-static void whiteheat_set_termios (struct tty_struct *tty, struct termios * old);
-static void whiteheat_throttle (struct tty_struct *tty);
-static void whiteheat_unthrottle (struct tty_struct *tty);
+static int whiteheat_open (struct usb_serial_port *port, struct file *filp);
+static void whiteheat_close (struct usb_serial_port *port, struct file *filp);
+static void whiteheat_set_termios (struct usb_serial_port *port, struct termios * old);
+static void whiteheat_throttle (struct usb_serial_port *port);
+static void whiteheat_unthrottle (struct usb_serial_port *port);
static int whiteheat_startup (struct usb_serial *serial);
/* All of the device info needed for the Connect Tech WhiteHEAT */
@@ -193,6 +193,7 @@ static struct usb_serial_device_type whiteheat_fake_device = {
num_interrupt_in: NUM_DONT_CARE,
num_bulk_in: NUM_DONT_CARE,
num_bulk_out: NUM_DONT_CARE,
+ num_ports: 1,
startup: whiteheat_startup
};
static struct usb_serial_device_type whiteheat_device = {
@@ -206,8 +207,8 @@ static struct usb_serial_device_type whiteheat_device = {
num_bulk_in: NUM_DONT_CARE,
num_bulk_out: NUM_DONT_CARE,
num_ports: 4,
- open: whiteheat_serial_open,
- close: whiteheat_serial_close,
+ open: whiteheat_open,
+ close: whiteheat_close,
throttle: whiteheat_throttle,
unthrottle: whiteheat_unthrottle,
set_termios: whiteheat_set_termios,
@@ -269,11 +270,11 @@ struct visor_connection_info {
/* function prototypes for a handspring visor */
-static int visor_serial_open (struct tty_struct *tty, struct file *filp);
-static void visor_serial_close (struct tty_struct *tty, struct file *filp);
-static void visor_throttle (struct tty_struct *tty);
-static void visor_unthrottle (struct tty_struct *tty);
-static int visor_startup (struct usb_serial *serial);
+static int visor_open (struct usb_serial_port *port, struct file *filp);
+static void visor_close (struct usb_serial_port *port, struct file *filp);
+static void visor_throttle (struct usb_serial_port *port);
+static void visor_unthrottle (struct usb_serial_port *port);
+static int visor_startup (struct usb_serial *serial);
/* All of the device info needed for the Handspring Visor */
static __u16 handspring_vendor_id = HANDSPRING_VENDOR_ID;
@@ -289,8 +290,8 @@ static struct usb_serial_device_type handspring_device = {
num_bulk_in: 2,
num_bulk_out: 2,
num_ports: 2,
- open: visor_serial_open,
- close: visor_serial_close,
+ open: visor_open,
+ close: visor_close,
throttle: visor_throttle,
unthrottle: visor_unthrottle,
startup: visor_startup,
@@ -300,12 +301,12 @@ static struct usb_serial_device_type handspring_device = {
#ifdef CONFIG_USB_SERIAL_FTDI_SIO
/* function prototypes for a FTDI serial converter */
-static int ftdi_sio_serial_open (struct tty_struct *tty, struct file *filp);
-static void ftdi_sio_serial_close (struct tty_struct *tty, struct file *filp);
-static int ftdi_sio_serial_write (struct tty_struct *tty, int from_user, const unsigned char *buf, int count);
+static int ftdi_sio_open (struct usb_serial_port *port, struct file *filp);
+static void ftdi_sio_close (struct usb_serial_port *port, struct file *filp);
+static int ftdi_sio_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
static void ftdi_sio_read_bulk_callback (struct urb *urb);
-static void ftdi_sio_set_termios (struct tty_struct *tty, struct termios * old);
-static int ftdi_sio_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg);
+static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios * old);
+static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg);
/* All of the device info needed for the FTDI SIO serial converter */
static __u16 ftdi_vendor_id = FTDI_VENDOR_ID;
@@ -321,9 +322,10 @@ static struct usb_serial_device_type ftdi_sio_device = {
num_bulk_in: 1,
num_bulk_out: 1,
num_ports: 1,
- open: ftdi_sio_serial_open,
- close: ftdi_sio_serial_close,
- write: ftdi_sio_serial_write,
+ open: ftdi_sio_open,
+ close: ftdi_sio_close,
+ write: ftdi_sio_write,
+ ioctl: ftdi_sio_ioctl,
read_bulk_callback: ftdi_sio_read_bulk_callback,
set_termios: ftdi_sio_set_termios
};
@@ -332,28 +334,28 @@ static struct usb_serial_device_type ftdi_sio_device = {
#ifdef CONFIG_USB_SERIAL_KEYSPAN_PDA
/* function prototypes for a Keyspan PDA serial converter */
-static int keyspan_pda_serial_open (struct tty_struct *tty,
+static int keyspan_pda_open (struct usb_serial_port *port,
struct file *filp);
-static void keyspan_pda_serial_close (struct tty_struct *tty,
+static void keyspan_pda_close (struct usb_serial_port *port,
struct file *filp);
static int keyspan_pda_startup (struct usb_serial *serial);
-static void keyspan_pda_rx_throttle (struct tty_struct *tty);
-static void keyspan_pda_rx_unthrottle (struct tty_struct *tty);
+static void keyspan_pda_rx_throttle (struct usb_serial_port *port);
+static void keyspan_pda_rx_unthrottle (struct usb_serial_port *port);
static int keyspan_pda_setbaud (struct usb_serial *serial, int baud);
-static int keyspan_pda_write_room (struct tty_struct *tty);
-static int keyspan_pda_write (struct tty_struct *tty,
+static int keyspan_pda_write_room (struct usb_serial_port *port);
+static int keyspan_pda_write (struct usb_serial_port *port,
int from_user,
const unsigned char *buf,
int count);
static void keyspan_pda_write_bulk_callback (struct urb *urb);
-static int keyspan_pda_chars_in_buffer (struct tty_struct *tty);
-static int keyspan_pda_ioctl (struct tty_struct *tty,
+static int keyspan_pda_chars_in_buffer (struct usb_serial_port *port);
+static int keyspan_pda_ioctl (struct usb_serial_port *port,
struct file *file,
unsigned int cmd,
unsigned long arg);
-static void keyspan_pda_set_termios (struct tty_struct *tty,
+static void keyspan_pda_set_termios (struct usb_serial_port *port,
struct termios *old);
-static void keyspan_pda_break_ctl (struct tty_struct *tty,
+static void keyspan_pda_break_ctl (struct usb_serial_port *port,
int break_state);
static int keyspan_pda_fake_startup (struct usb_serial *serial);
@@ -385,8 +387,8 @@ static struct usb_serial_device_type keyspan_pda_device = {
num_bulk_in: 0,
num_bulk_out: 1,
num_ports: 1,
- open: keyspan_pda_serial_open,
- close: keyspan_pda_serial_close,
+ open: keyspan_pda_open,
+ close: keyspan_pda_close,
write: keyspan_pda_write,
write_room: keyspan_pda_write_room,
write_bulk_callback: keyspan_pda_write_bulk_callback,
diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c
index f5e67acec..9927b5382 100644
--- a/drivers/usb/uhci.c
+++ b/drivers/usb/uhci.c
@@ -1315,8 +1315,8 @@ static __u8 root_hub_dev_des[] =
0x00, /* __u16 bcdDevice; */
0x00,
0x00, /* __u8 iManufacturer; */
- 0x00, /* __u8 iProduct; */
- 0x00, /* __u8 iSerialNumber; */
+ 0x02, /* __u8 iProduct; */
+ 0x01, /* __u8 iSerialNumber; */
0x01 /* __u8 bNumConfigurations; */
};
@@ -1617,7 +1617,13 @@ static int rh_submit_urb(urb_t *urb)
memcpy (data, root_hub_config_des, len);
OK(len);
case 0x03: /* string descriptors */
- stat = -EPIPE;
+ len = usb_root_hub_string (wValue & 0xff,
+ uhci->io_addr, "UHCI",
+ data, wLength);
+ if (len > 0) {
+ OK (min (leni, len));
+ } else
+ stat = -EPIPE;
}
break;
case RH_GET_DESCRIPTOR | RH_CLASS:
@@ -1985,6 +1991,15 @@ static int setup_uhci(struct pci_dev *dev, int irq, unsigned int io_addr, unsign
{
int retval;
struct uhci *uhci;
+ char buf[8], *bufp = buf;
+
+#ifndef __sparc__
+ sprintf(buf, "%d", irq);
+#else
+ bufp = __irq_itoa(irq);
+#endif
+ printk(KERN_INFO __FILE__ ": USB UHCI at I/O 0x%x, IRQ %s\n",
+ io_addr, bufp);
uhci = alloc_uhci(io_addr, io_size);
if (!uhci)
diff --git a/drivers/usb/usb-ohci.c b/drivers/usb/usb-ohci.c
index 78ae0a0ea..b134107da 100644
--- a/drivers/usb/usb-ohci.c
+++ b/drivers/usb/usb-ohci.c
@@ -262,8 +262,14 @@ static int sohci_submit_urb (urb_t * urb)
urb_print (urb, "SUB", usb_pipein (pipe));
#endif
+ /* a request to the virtual root hub */
if (usb_pipedevice (pipe) == ohci->rh.devnum)
- return rh_submit_urb (urb); /* a request to the virtual root hub */
+ return rh_submit_urb (urb);
+
+ /* when controller's hung, permit only hub cleanup attempts
+ * such as powering down ports */
+ if (ohci->disabled)
+ return -ESHUTDOWN;
/* every endpoint has a ed, locate and fill it */
if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1))) {
@@ -333,10 +339,11 @@ static int sohci_submit_urb (urb_t * urb)
(le16_to_cpu (ohci->hcca.frame_no) + 10)) & 0xffff;
}
- td_submit_urb (urb); /* fill the TDs and link it to the ed */
-
if (ed->state != ED_OPER) /* link the ed into a chain if is not already */
ep_link (ohci, ed);
+
+ td_submit_urb (urb); /* fill the TDs and link it to the ed */
+
spin_unlock_irqrestore (&usb_ed_lock, flags);
urb->status = USB_ST_URB_PENDING;
@@ -1122,8 +1129,8 @@ static __u8 root_hub_dev_des[] =
0x00, /* __u16 bcdDevice; */
0x00,
0x00, /* __u8 iManufacturer; */
- 0x00, /* __u8 iProduct; */
- 0x00, /* __u8 iSerialNumber; */
+ 0x02, /* __u8 iProduct; */
+ 0x01, /* __u8 iSerialNumber; */
0x01 /* __u8 bNumConfigurations; */
};
@@ -1222,13 +1229,16 @@ static void rh_int_timer_do (unsigned long ptr)
urb_t * urb = (urb_t *) ptr;
ohci_t * ohci = urb->dev->bus->hcpriv;
+
+ if (ohci->disabled)
+ return;
if(ohci->rh.send) {
len = rh_send_irq (ohci, urb->transfer_buffer, urb->transfer_buffer_length);
if (len > 0) {
urb->actual_length = len;
#ifdef DEBUG
- urb_print (urb, "RET(rh)", usb_pipeout (urb->pipe));
+ urb_print (urb, "RET-t(rh)", usb_pipeout (urb->pipe));
#endif
if (urb->complete) urb->complete (urb);
}
@@ -1379,27 +1389,49 @@ static int rh_submit_urb (urb_t * urb)
len = min (leni, min (sizeof (root_hub_config_des), wLength));
data_buf = root_hub_config_des; OK(len);
case (0x03): /* string descriptors */
+ len = usb_root_hub_string (wValue & 0xff,
+ (int) ohci->regs, "OHCI",
+ data, wLength);
+ if (len > 0) {
+ data_buf = data;
+ OK (min (leni, len));
+ }
+ // else fallthrough
default:
status = TD_CC_STALL;
}
break;
case RH_GET_DESCRIPTOR | RH_CLASS:
- *(__u8 *) (data_buf+1) = 0x29;
- put_unaligned(cpu_to_le32 (readl (&ohci->regs->roothub.a)),
- (__u32 *) (data_buf + 2));
- *(__u8 *) data_buf = (*(__u8 *) (data_buf + 2) / 8) * 2 + 9; /* length of descriptor */
-
- len = min (leni, min(*(__u8 *) data_buf, wLength));
- *(__u8 *) (data_buf+6) = 0; /* Root Hub needs no current from bus */
- if (*(__u8 *) (data_buf+2) < 8) { /* less than 8 Ports */
- *(__u8 *) (data_buf+7) = readl (&ohci->regs->roothub.b) & 0xff;
- *(__u8 *) (data_buf+8) = (readl (&ohci->regs->roothub.b) & 0xff0000) >> 16;
- } else {
- put_unaligned(cpu_to_le32 (readl(&ohci->regs->roothub.b)),
- (__u32 *) (data_buf + 7));
+ {
+ __u32 temp = readl (&ohci->regs->roothub.a);
+
+ data_buf [0] = 9; // min length;
+ data_buf [1] = 0x29;
+ data_buf [2] = temp & RH_A_NDP;
+ data_buf [3] = 0;
+ if (temp & RH_A_PSM) /* per-port power switching? */
+ data_buf [3] |= 0x1;
+ if (temp & RH_A_NOCP) /* no overcurrent reporting? */
+ data_buf [3] |= 0x10;
+ else if (temp & RH_A_OCPM) /* per-port overcurrent reporting? */
+ data_buf [3] |= 0x8;
+
+ datab [1] = 0;
+ data_buf [5] = (temp & RH_A_POTPGT) >> 24;
+ temp = readl (&ohci->regs->roothub.b);
+ data_buf [7] = temp & RH_B_DR;
+ if (data_buf [2] < 7) {
+ data_buf [8] = 0xff;
+ } else {
+ data_buf [0] += 2;
+ data_buf [8] = (temp & RH_B_DR) >> 8;
+ data_buf [10] = data_buf [9] = 0xff;
+ }
+
+ len = min (leni, min (data_buf [0], wLength));
+ OK (len);
}
- OK (len);
case RH_GET_CONFIGURATION: *(__u8 *) data_buf = 0x01; OK (1);
@@ -1413,7 +1445,8 @@ static int rh_submit_urb (urb_t * urb)
dbg("USB HC roothubstat2: %x", readl ( &(ohci->regs->roothub.portstatus[1]) ));
len = min(len, leni);
- memcpy (data, data_buf, len);
+ if (data != data_buf)
+ memcpy (data, data_buf, len);
urb->actual_length = len;
urb->status = cc_to_error [status];
@@ -1472,6 +1505,7 @@ static void hc_reset (ohci_t * ohci)
}
udelay (1);
}
+ ohci->disabled = 0;
}
/*-------------------------------------------------------------------------*/
@@ -1502,7 +1536,7 @@ static int hc_start (ohci_t * ohci)
writel (0x628, &ohci->regs->lsthresh);
/* Choose the interrupts we care about now, others later on demand */
- mask = OHCI_INTR_MIE | OHCI_INTR_WDH | OHCI_INTR_SO;
+ mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO;
writel (ohci->hc_control = 0xBF, &ohci->regs->control); /* USB Operational */
writel (mask, &ohci->regs->intrenable);
@@ -1550,6 +1584,11 @@ static void hc_interrupt (int irq, void * __ohci, struct pt_regs * r)
dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca.frame_no));
+ if (ints & OHCI_INTR_UE) {
+ ohci->disabled++;
+ err ("OHCI Unrecoverable Error, controller disabled");
+ }
+
if (ints & OHCI_INTR_WDH) {
writel (OHCI_INTR_WDH, &regs->intrdisable);
dl_done_list (ohci, dl_reverse_done_list (ohci));
@@ -1650,16 +1689,24 @@ static void hc_release_ohci (ohci_t * ohci)
static int hc_found_ohci (struct pci_dev *dev, int irq, void * mem_base)
{
ohci_t * ohci;
- dbg("USB HC found: irq= %d membase= %lx", irq, (unsigned long) mem_base);
+ char buf[8], *bufp = buf;
+
+#ifndef __sparc__
+ sprintf(buf, "%d", irq);
+#else
+ bufp = __irq_itoa(irq);
+#endif
+ printk(KERN_INFO __FILE__ ": USB OHCI at membase 0x%lx, IRQ %s\n",
+ (unsigned long) mem_base, bufp);
ohci = hc_alloc_ohci (mem_base);
if (!ohci) {
return -ENOMEM;
}
-
+
INIT_LIST_HEAD (&ohci->ohci_hcd_list);
list_add (&ohci->ohci_hcd_list, &ohci_hcd_list);
-
+
hc_reset (ohci);
writel (ohci->hc_control = OHCI_USB_RESET, &ohci->regs->control);
wait_ms (10);
diff --git a/drivers/usb/usb-ohci.h b/drivers/usb/usb-ohci.h
index 6098f0b39..e854e0c1d 100644
--- a/drivers/usb/usb-ohci.h
+++ b/drivers/usb/usb-ohci.h
@@ -212,31 +212,53 @@ struct ohci_regs {
} roothub;
} __attribute((aligned(32)));
+
+/* OHCI CONTROL AND STATUS REGISTER MASKS */
+
/*
- * cmdstatus register */
-#define OHCI_CLF 0x02
-#define OHCI_BLF 0x04
+ * HcControl (control) register masks
+ */
+#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */
+#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */
+#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */
+#define OHCI_CTRL_CLE (1 << 4) /* control list enable */
+#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */
+#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */
+#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */
+#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */
+#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */
+
+/* pre-shifted values for HCFS */
+# define OHCI_USB_RESET (0 << 6)
+# define OHCI_USB_RESUME (1 << 6)
+# define OHCI_USB_OPER (2 << 6)
+# define OHCI_USB_SUSPEND (3 << 6)
/*
- * Interrupt register masks
+ * HcCommandStatus (cmdstatus) register masks
*/
-#define OHCI_INTR_SO (1)
-#define OHCI_INTR_WDH (1 << 1)
-#define OHCI_INTR_SF (1 << 2)
-#define OHCI_INTR_RD (1 << 3)
-#define OHCI_INTR_UE (1 << 4)
-#define OHCI_INTR_FNO (1 << 5)
-#define OHCI_INTR_RHSC (1 << 6)
-#define OHCI_INTR_OC (1 << 30)
-#define OHCI_INTR_MIE (1 << 31)
+#define OHCI_HCR (1 << 0) /* host controller reset */
+#define OHCI_CLF (1 << 1) /* control list filled */
+#define OHCI_BLF (1 << 2) /* bulk list filled */
+#define OHCI_OCR (1 << 3) /* ownership change request */
+#define OHCI_SOC (3 << 16) /* scheduling overrun count */
/*
- * Control register masks
+ * masks used with interrupt registers:
+ * HcInterruptStatus (intrstatus)
+ * HcInterruptEnable (intrenable)
+ * HcInterruptDisable (intrdisable)
*/
-#define OHCI_USB_RESET 0
-#define OHCI_USB_RESUME (1 << 6)
-#define OHCI_USB_OPER (2 << 6)
-#define OHCI_USB_SUSPEND (3 << 6)
+#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */
+#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */
+#define OHCI_INTR_SF (1 << 2) /* start frame */
+#define OHCI_INTR_RD (1 << 3) /* resume detect */
+#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */
+#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */
+#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */
+#define OHCI_INTR_OC (1 << 30) /* ownership change */
+#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */
+
/* Virtual Root HUB */
@@ -248,6 +270,9 @@ struct virt_root_hub {
int interval;
struct timer_list rh_int_timer;
};
+
+
+/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */
/* destination of request */
#define RH_INTERFACE 0x01
@@ -261,8 +286,8 @@ struct virt_root_hub {
#define RH_GET_STATUS 0x0080
#define RH_CLEAR_FEATURE 0x0100
#define RH_SET_FEATURE 0x0300
-#define RH_SET_ADDRESS 0x0500
-#define RH_GET_DESCRIPTOR 0x0680
+#define RH_SET_ADDRESS 0x0500
+#define RH_GET_DESCRIPTOR 0x0680
#define RH_SET_DESCRIPTOR 0x0700
#define RH_GET_CONFIGURATION 0x0880
#define RH_SET_CONFIGURATION 0x0900
@@ -282,6 +307,7 @@ struct virt_root_hub {
#define RH_PORT_RESET 0x04
#define RH_PORT_POWER 0x08
#define RH_PORT_LOW_SPEED 0x09
+
#define RH_C_PORT_CONNECTION 0x10
#define RH_C_PORT_ENABLE 0x11
#define RH_C_PORT_SUSPEND 0x12
@@ -298,29 +324,44 @@ struct virt_root_hub {
#define RH_ACK 0x01
#define RH_REQ_ERR -1
#define RH_NACK 0x00
+
+
+/* OHCI ROOT HUB REGISTER MASKS */
-/* Root-Hub Register info */
-
-#define RH_PS_CCS 0x00000001
-#define RH_PS_PES 0x00000002
-#define RH_PS_PSS 0x00000004
-#define RH_PS_POCI 0x00000008
-#define RH_PS_PRS 0x00000010
-#define RH_PS_PPS 0x00000100
-#define RH_PS_LSDA 0x00000200
-#define RH_PS_CSC 0x00010000
-#define RH_PS_PESC 0x00020000
-#define RH_PS_PSSC 0x00040000
-#define RH_PS_OCIC 0x00080000
-#define RH_PS_PRSC 0x00100000
-
-/* Root hub status bits */
-#define RH_HS_LPS 0x00000001
-#define RH_HS_OCI 0x00000002
-#define RH_HS_DRWE 0x00008000
-#define RH_HS_LPSC 0x00010000
-#define RH_HS_OCIC 0x00020000
-#define RH_HS_CRWE 0x80000000
+/* roothub.portstatus [i] bits */
+#define RH_PS_CCS 0x00000001 /* current connect status */
+#define RH_PS_PES 0x00000002 /* port enable status*/
+#define RH_PS_PSS 0x00000004 /* port suspend status */
+#define RH_PS_POCI 0x00000008 /* port over current indicator */
+#define RH_PS_PRS 0x00000010 /* port reset status */
+#define RH_PS_PPS 0x00000100 /* port power status */
+#define RH_PS_LSDA 0x00000200 /* low speed device attached */
+#define RH_PS_CSC 0x00010000 /* connect status change */
+#define RH_PS_PESC 0x00020000 /* port enable status change */
+#define RH_PS_PSSC 0x00040000 /* port suspend status change */
+#define RH_PS_OCIC 0x00080000 /* over current indicator change */
+#define RH_PS_PRSC 0x00100000 /* port reset status change */
+
+/* roothub.status bits */
+#define RH_HS_LPS 0x00000001 /* local power status */
+#define RH_HS_OCI 0x00000002 /* over current indicator */
+#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */
+#define RH_HS_LPSC 0x00010000 /* local power status change */
+#define RH_HS_OCIC 0x00020000 /* over current indicator change */
+#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */
+
+/* roothub.b masks */
+#define RH_B_DR 0x0000ffff /* device removable flags */
+#define RH_B_PPCM 0xffff0000 /* port power control mask */
+
+/* roothub.a masks */
+#define RH_A_NDP (0xff << 0) /* number of downstream ports */
+#define RH_A_PSM (1 << 8) /* power switching mode */
+#define RH_A_NPS (1 << 9) /* no power switching */
+#define RH_A_DT (1 << 10) /* device type (mbz) */
+#define RH_A_OCPM (1 << 11) /* over current protection mode */
+#define RH_A_NOCP (1 << 12) /* no over current protection */
+#define RH_A_POTPGT (0xff << 24) /* power on to power good time */
#define min(a,b) (((a)<(b))?(a):(b))
@@ -347,23 +388,25 @@ typedef struct
typedef struct ohci {
- struct ohci_hcca hcca; /* hcca */
+ struct ohci_hcca hcca; /* hcca */
int irq;
- struct ohci_regs * regs; /* OHCI controller's memory */
- struct list_head ohci_hcd_list; /* list of all ohci_hcd */
+ int disabled; /* e.g. got a UE, we're hung */
+
+ struct ohci_regs * regs; /* OHCI controller's memory */
+ struct list_head ohci_hcd_list; /* list of all ohci_hcd */
struct ohci * next; // chain of uhci device contexts
struct list_head urb_list; // list of all pending urbs
spinlock_t urb_list_lock; // lock to keep consistency
- int ohci_int_load[32]; /* load of the 32 Interrupt Chains (for load ballancing)*/
+ int ohci_int_load[32]; /* load of the 32 Interrupt Chains (for load balancing)*/
ed_t * ed_rm_list[2]; /* lists of all endpoints to be removed */
ed_t * ed_bulktail; /* last endpoint of bulk list */
ed_t * ed_controltail; /* last endpoint of control list */
ed_t * ed_isotail; /* last endpoint of iso list */
int intrstatus;
- __u32 hc_control; /* copy of the hc control reg */
+ __u32 hc_control; /* copy of the hc control reg */
struct usb_bus * bus;
struct usb_device * dev[128];
struct virt_root_hub rh;
diff --git a/drivers/usb/usb-storage.c b/drivers/usb/usb-storage.c
index 0cac2ef13..d1b77c8f5 100644
--- a/drivers/usb/usb-storage.c
+++ b/drivers/usb/usb-storage.c
@@ -3,20 +3,19 @@
* (c) 1999 Michael Gee (michael@linuxspecific.com)
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
*
- * Further reference:
- * This driver is based on the 'USB Mass Storage Class' document. This
- * describes in detail the protocol used to communicate with such
- * devices. Clearly, the designers had SCSI and ATAPI commands in mind
- * when they created this document. The commands are all very similar
- * to commands in the SCSI-II and ATAPI specifications.
+ * This driver is based on the 'USB Mass Storage Class' document. This
+ * describes in detail the protocol used to communicate with such
+ * devices. Clearly, the designers had SCSI and ATAPI commands in
+ * mind when they created this document. The commands are all very
+ * similar to commands in the SCSI-II and ATAPI specifications.
*
- * It is important to note that in a number of cases this class exhibits
- * class-specific exemptions from the USB specification. Notably the
- * usage of NAK, STALL and ACK differs from the norm, in that they are
- * used to communicate wait, failed and OK on commands.
- * Also, for certain devices, the interrupt endpoint is used to convey
- * status of a command.
+ * It is important to note that in a number of cases this class
+ * exhibits class-specific exemptions from the USB specification.
+ * Notably the usage of NAK, STALL and ACK differs from the norm, in
+ * that they are used to communicate wait, failed and OK on commands.
*
+ * Also, for certain devices, the interrupt endpoint is used to convey
+ * status of a command.
*/
#include <linux/module.h>
@@ -40,7 +39,6 @@
#include "usb-storage.h"
#include "usb-storage-debug.h"
-
/*
* This is the size of the structure Scsi_Host_Template. We create
* an instance of this structure in this file and this is a check
@@ -75,34 +73,47 @@ typedef int (*trans_cmnd)(Scsi_Cmnd*, struct us_data*);
typedef int (*trans_reset)(struct us_data*);
typedef void (*proto_cmnd)(Scsi_Cmnd*, struct us_data*);
+/* we allocate one of these for every device that we remember */
struct us_data {
struct us_data *next; /* next device */
struct usb_device *pusb_dev; /* this usb_device */
unsigned int flags; /* from filter initially */
+
+ /* information about the device -- only good if device is attached */
__u8 ifnum; /* interface number */
__u8 ep_in; /* in endpoint */
__u8 ep_out; /* out ....... */
__u8 ep_int; /* interrupt . */
- __u8 subclass; /* as in overview */
- __u8 protocol; /* .............. */
- trans_cmnd transport; /* protocol specific do cmd */
- trans_reset transport_reset; /* .......... device reset */
+ __u8 subclass;
+ __u8 protocol;
+
+ /* function pointers for this device */
+ trans_cmnd transport; /* transport function */
+ trans_reset transport_reset; /* transport device reset */
proto_cmnd proto_handler; /* protocol handler */
+
+ /* SCSI interfaces */
GUID(guid); /* unique dev id */
struct Scsi_Host *host; /* our dummy host data */
Scsi_Host_Template htmplt; /* own host template */
int host_number; /* to find us */
int host_no; /* allocated by scsi */
Scsi_Cmnd *srb; /* current srb */
+
+ /* thread information */
Scsi_Cmnd *queue_srb; /* the single queue slot */
int action; /* what to do */
+ int pid; /* control thread */
+
+ /* interrupt info for CBI devices */
struct semaphore ip_waitq; /* for CBI interrupts */
__u16 ip_data; /* interrupt data */
int ip_wanted; /* needed */
- int pid; /* control thread */
- struct semaphore *notify; /* wait for thread to begin */
void *irq_handle; /* for USB int requests */
unsigned int irqpipe; /* pipe for release_irq */
+
+ /* mutual exclusion structures */
+ struct semaphore notify; /* wait for thread to begin */
struct semaphore sleeper; /* to sleep on */
struct semaphore queue_exclusion; /* to protect data structs */
};
@@ -116,6 +127,7 @@ struct us_data {
#define US_ACT_DEVICE_RESET 3
#define US_ACT_BUS_RESET 4
#define US_ACT_HOST_RESET 5
+#define US_ACT_EXIT 6
/* The list of structures and the protective lock for them */
static struct us_data *us_list;
@@ -254,11 +266,6 @@ static unsigned int us_transfer_length(Scsi_Cmnd *srb)
case MODE_SENSE:
return srb->cmnd[4];
- /* FIXME: this needs to come out when the other
- * fix is in place */
- case READ_CAPACITY:
- return 8;
-
/* FIXME: these should be removed and tested */
case LOG_SENSE:
case MODE_SENSE_10:
@@ -791,9 +798,8 @@ static int CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
us->ifnum, srb->cmnd, srb->cmd_len, HZ*5);
/* check the return code for the command */
+ US_DEBUGP("Call to usb_control_msg() returned %d\n", result);
if (result < 0) {
- US_DEBUGP("Call to usb_control_msg() returned %d\n", result);
-
/* a stall is a fatal condition from the device */
if (result == -EPIPE) {
US_DEBUGP("-- Stall on control pipe. Clearing\n");
@@ -820,15 +826,10 @@ static int CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
/* STATUS STAGE */
- /* go to sleep until we get this interrup */
- /* FIXME: this should be changed to use a timeout -- or let the
- * device reset routine up() this for us to unjam us
- */
+ /* go to sleep until we get this interrupt */
down(&(us->ip_waitq));
- /* FIXME: currently this code is unreachable, but the idea is
- * necessary. See above comment.
- */
+ /* if we were woken up by a reset instead of the actual interrupt */
if (us->ip_wanted) {
US_DEBUGP("Did not get interrupt on CBI\n");
us->ip_wanted = 0;
@@ -837,8 +838,9 @@ static int CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
US_DEBUGP("Got interrupt data 0x%x\n", us->ip_data);
- /* UFI gives us ASC and ASCQ, like a request sense */
- /* REQUEST_SENSE and INQUIRY don't affect the sense data, so we
+ /* UFI gives us ASC and ASCQ, like a request sense
+ *
+ * REQUEST_SENSE and INQUIRY don't affect the sense data, so we
* ignore the information for those commands
*/
if (us->subclass == US_SC_UFI) {
@@ -1114,29 +1116,27 @@ static int us_detect(struct SHT *sht)
static int us_release(struct Scsi_Host *psh)
{
struct us_data *us = (struct us_data *)psh->hostdata[0];
- struct us_data *prev;
unsigned long flags;
+ int result;
+ /* lock the data structures */
+ spin_lock_irqsave(&us_list_spinlock, flags);
+
+ US_DEBUGP("us_release() called for host %s\n", us->htmplt.name);
+
+ /* release the interrupt handler, if necessary */
if (us->irq_handle) {
- usb_release_irq(us->pusb_dev, us->irq_handle, us->irqpipe);
+ US_DEBUGP("-- releasing irq\n");
+ result = usb_release_irq(us->pusb_dev, us->irq_handle,
+ us->irqpipe);
+ US_DEBUGP("-- usb_release_irq() returned %d\n", result);
us->irq_handle = NULL;
}
- /* FIXME: release the interface claim here? */
-
- /* FIXME: we need to move this elsewhere --
- * the remove function only gets called to remove the module
- */
- spin_lock_irqsave(&us_list_spinlock, flags);
- if (us_list == us)
- us_list = us->next;
- else {
- prev = us_list;
- while (prev->next != us)
- prev = prev->next;
- prev->next = us->next;
- }
+ /* lock the data structures */
spin_unlock_irqrestore(&us_list_spinlock, flags);
+
+ /* we always have a successful release */
return 0;
}
@@ -1153,7 +1153,7 @@ static int us_queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *))
{
struct us_data *us = (struct us_data *)srb->host->hostdata[0];
- US_DEBUGP("Command wakeup\n");
+ US_DEBUGP("us_queuecommand() called\n");
srb->host_scribble = (unsigned char *)us;
/* get exclusive access to the structures we want */
@@ -1180,9 +1180,11 @@ static int us_abort( Scsi_Cmnd *srb )
/* FIXME: this doesn't do anything right now */
static int us_bus_reset( Scsi_Cmnd *srb )
{
- // struct us_data *us = (struct us_data *)srb->host->hostdata[0];
+ struct us_data *us = (struct us_data *)srb->host->hostdata[0];
US_DEBUGP("Bus reset requested\n");
+ if (us->ip_wanted)
+ up(&(us->ip_waitq));
// us->transport_reset(us);
return SUCCESS;
}
@@ -1364,12 +1366,9 @@ static int usb_stor_control_thread(void * __us)
unlock_kernel();
- up(us->notify);
+ up(&(us->notify));
for(;;) {
- siginfo_t info;
- int unsigned long signr;
-
US_DEBUGP("*** thread sleeping.\n");
down(&(us->sleeper));
down(&(us->queue_exclusion));
@@ -1378,7 +1377,7 @@ static int usb_stor_control_thread(void * __us)
/* take the command off the queue */
action = us->action;
us->action = 0;
- us->srb = us-> queue_srb;
+ us->srb = us->queue_srb;
/* release the queue lock as fast as possible */
up(&(us->queue_exclusion));
@@ -1433,7 +1432,8 @@ static int usb_stor_control_thread(void * __us)
us->proto_handler(us->srb, us);
}
- US_DEBUGP("scsi cmd done, result=0x%x\n", us->srb->result);
+ US_DEBUGP("scsi cmd done, result=0x%x\n",
+ us->srb->result);
us->srb->scsi_done(us->srb);
us->srb = NULL;
break;
@@ -1452,19 +1452,12 @@ static int usb_stor_control_thread(void * __us)
} /* end switch on action */
- /* FIXME: we ignore TERM and KILL... is this right? */
- if (signal_pending(current)) {
- /* sending SIGUSR1 makes us print out some info */
- spin_lock_irq(&current->sigmask_lock);
- signr = dequeue_signal(&current->blocked, &info);
- spin_unlock_irq(&current->sigmask_lock);
- } /* if (singal_pending(current)) */
+ /* exit if we get a signal to exit */
+ if (action == US_ACT_EXIT)
+ break;
} /* for (;;) */
- // MOD_DEC_USE_COUNT;
-
printk("usb_stor_control_thread exiting\n");
-
return 0;
}
@@ -1495,14 +1488,12 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
/* FIXME: this isn't quite right... */
/* We make an exception for the shuttle E-USB */
- if (dev->descriptor.idVendor == 0x04e6 &&
- dev->descriptor.idProduct == 0x0001) {
- protocol = US_PR_CB;
- subclass = US_SC_8070; /* an assumption */
- } else if (dev->descriptor.bDeviceClass != 0 ||
- altsetting->bInterfaceClass != USB_CLASS_MASS_STORAGE ||
- altsetting->bInterfaceSubClass < US_SC_MIN ||
- altsetting->bInterfaceSubClass > US_SC_MAX) {
+ if (!(dev->descriptor.idVendor == 0x04e6 &&
+ dev->descriptor.idProduct == 0x0001) &&
+ !(dev->descriptor.bDeviceClass == 0 &&
+ altsetting->bInterfaceClass == USB_CLASS_MASS_STORAGE &&
+ altsetting->bInterfaceSubClass >= US_SC_MIN &&
+ altsetting->bInterfaceSubClass <= US_SC_MAX)) {
/* if it's not a mass storage, we go no further */
return NULL;
}
@@ -1510,7 +1501,43 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
/* At this point, we know we've got a live one */
US_DEBUGP("USB Mass Storage device detected\n");
+ /* Determine subclass and protocol, or copy from the interface */
+ /* FIXME: this isn't quite right */
+ if (dev->descriptor.idVendor == 0x04e6 &&
+ dev->descriptor.idProduct == 0x0001) {
+ protocol = US_PR_CB;
+ subclass = US_SC_8070; /* an assumption */
+ } else {
+ subclass = altsetting->bInterfaceSubClass;
+ protocol = altsetting->bInterfaceProtocol;
+ }
+
+ /* shuttle E-USB */
+ /* FIXME: all we should need to do here is determine the protocol */
+ if (dev->descriptor.idVendor == 0x04e6 &&
+ dev->descriptor.idProduct == 0x0001) {
+ __u8 qstat[2];
+
+ result = usb_control_msg(ss->pusb_dev,
+ usb_rcvctrlpipe(dev,0),
+ 1, 0xC0,
+ 0, ss->ifnum,
+ qstat, 2, HZ*5);
+ US_DEBUGP("C0 status 0x%x 0x%x\n", qstat[0], qstat[1]);
+ init_MUTEX_LOCKED(&(ss->ip_waitq));
+ ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
+ result = usb_request_irq(ss->pusb_dev, ss->irqpipe,
+ CBI_irq, 255, (void *)ss,
+ &ss->irq_handle);
+ if (result < 0)
+ return NULL;
+
+ /* FIXME: what is this?? */
+ down(&(ss->ip_waitq));
+ }
+
/*
+ * Find the endpoints we need
* We are expecting a minimum of 2 endpoints - in and out (bulk).
* An optional interrupt is OK (necessary for CBI protocol).
* We will ignore any others.
@@ -1519,6 +1546,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
/* is it an BULK endpoint? */
if ((altsetting->endpoint[i].bmAttributes &
USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
+ /* BULK in or out? */
if (altsetting->endpoint[i].bEndpointAddress &
USB_DIR_IN)
ep_in = altsetting->endpoint[i].bEndpointAddress &
@@ -1542,36 +1570,14 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
result = usb_set_interface(dev, altsetting->bInterfaceNumber, 0);
US_DEBUGP("Result from usb_set_interface is %d\n", result);
if (result == -EPIPE) {
+ US_DEBUGP("-- clearing stall on control interface\n");
usb_clear_halt(dev, usb_sndctrlpipe(dev, 0));
} else if (result != 0) {
/* it's not a stall, but another error -- time to bail */
+ US_DEBUGP("-- unknown error. rejecting device\n");
return NULL;
}
- /* shuttle E-USB */
- /* FIXME: all we should need to do here is determine the protocol */
- if (dev->descriptor.idVendor == 0x04e6 &&
- dev->descriptor.idProduct == 0x0001) {
- __u8 qstat[2];
-
- result = usb_control_msg(ss->pusb_dev,
- usb_rcvctrlpipe(dev,0),
- 1, 0xC0,
- 0, ss->ifnum,
- qstat, 2, HZ*5);
- US_DEBUGP("C0 status 0x%x 0x%x\n", qstat[0], qstat[1]);
- init_MUTEX_LOCKED(&(ss->ip_waitq));
- ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
- result = usb_request_irq(ss->pusb_dev, ss->irqpipe,
- CBI_irq, 255, (void *)ss,
- &ss->irq_handle);
- if (result < 0)
- return NULL;
-
- /* FIXME: what is this?? */
- down(&(ss->ip_waitq));
- }
-
/* Do some basic sanity checks, and bail if we find a problem */
if (!ep_in || !ep_out || (protocol == US_PR_CBI && !ep_int)) {
US_DEBUGP("Problems with device\n");
@@ -1638,8 +1644,8 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
US_DEBUGP("Allocating IRQ for CBI transport\n");
ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
result = usb_request_irq(ss->pusb_dev, ss->irqpipe,
- CBI_irq, 255,
- (void *)ss, &ss->irq_handle);
+ CBI_irq, 255, (void *)ss,
+ &(ss->irq_handle));
US_DEBUGP("usb_request_irq returned %d\n", result);
}
} else {
@@ -1656,23 +1662,12 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
/* Initialize the mutexes only when the struct is new */
init_MUTEX_LOCKED(&(ss->sleeper));
+ init_MUTEX_LOCKED(&(ss->notify));
init_MUTEX(&(ss->queue_exclusion));
- /*
- * If we've allready determined the subclass and protocol,
- * use that. Otherwise, use the interface ones. This
- * allows us to support devices which are compliant but
- * don't announce it. Note that this information is
- * maintained in the us_data struct so we only have to do
- * this for new devices.
- */
- if (subclass) {
- ss->subclass = subclass;
- ss->protocol = protocol;
- } else {
- ss->subclass = altsetting->bInterfaceSubClass;
- ss->protocol = altsetting->bInterfaceProtocol;
- }
+ /* copy over the subclass and protocol data */
+ ss->subclass = subclass;
+ ss->protocol = protocol;
/* copy over the endpoint data */
ss->ep_in = ep_in;
@@ -1762,9 +1757,9 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
US_DEBUGP("Allocating IRQ for CBI transport\n");
ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
result = usb_request_irq(ss->pusb_dev, ss->irqpipe,
- CBI_irq, 255,
- (void *)ss, &ss->irq_handle);
- US_DEBUGP("usb_request_irq returned %d", result);
+ CBI_irq, 255, (void *)ss,
+ &(ss->irq_handle));
+ US_DEBUGP("usb_request_irq returned %d\n", result);
}
/*
@@ -1786,23 +1781,18 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
(struct us_data *)ss->htmplt.proc_dir = ss;
/* start up our thread */
- {
- DECLARE_MUTEX_LOCKED(sem);
-
- ss->notify = &sem;
- ss->pid = kernel_thread(usb_stor_control_thread, ss,
- CLONE_FS | CLONE_FILES |
- CLONE_SIGHAND);
- if (ss->pid < 0) {
- printk(KERN_WARNING USB_STORAGE
- "Unable to start control thread\n");
- kfree(ss);
- return NULL;
- }
-
- /* wait for it to start */
- down(&sem);
+ ss->pid = kernel_thread(usb_stor_control_thread, ss,
+ CLONE_FS | CLONE_FILES |
+ CLONE_SIGHAND);
+ if (ss->pid < 0) {
+ printk(KERN_WARNING USB_STORAGE
+ "Unable to start control thread\n");
+ kfree(ss);
+ return NULL;
}
+
+ /* wait for it to start */
+ down(&(ss->notify));
/* now register - our detect function will be called */
ss->htmplt.module = &__this_module;
@@ -1829,18 +1819,26 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
static void storage_disconnect(struct usb_device *dev, void *ptr)
{
struct us_data *ss = ptr;
+ int result;
- if (!ss)
+ US_DEBUGP("storage_disconnect() called\n");
+
+ /* this is the odd case -- we disconnected but weren't using it */
+ if (!ss) {
+ US_DEBUGP("-- device was not in use\n");
return;
-
- /* FIXME: we need mututal exclusion and resource freeing here */
+ }
/* release the IRQ, if we have one */
if (ss->irq_handle) {
- usb_release_irq(ss->pusb_dev, ss->irq_handle, ss->irqpipe);
+ US_DEBUGP("-- releasing irq handle\n");
+ result = usb_release_irq(ss->pusb_dev, ss->irq_handle,
+ ss->irqpipe);
+ US_DEBUGP("-- usb_release_irq() returned %d\n", result);
ss->irq_handle = NULL;
}
+ /* mark the device as gone */
ss->pusb_dev = NULL;
}
@@ -1851,11 +1849,16 @@ static void storage_disconnect(struct usb_device *dev, void *ptr)
int __init usb_stor_init(void)
{
+ /*
+ * Check to see if the host template is a different size from
+ * what we're expected -- people have updated this in the past
+ * and forgotten about this driver.
+ */
if (sizeof(my_host_template) != SCSI_HOST_TEMPLATE_SIZE) {
- printk(KERN_ERR "usb-storage: SCSI_HOST_TEMPLATE_SIZE does not match\n") ;
- printk(KERN_ERR "usb-storage: expected %d bytes, got %d bytes\n",
+ printk(KERN_ERR "usb-storage: SCSI_HOST_TEMPLATE_SIZE bad\n");
+ printk(KERN_ERR
+ "usb-storage: expected %d bytes, got %d bytes\n",
SCSI_HOST_TEMPLATE_SIZE, sizeof(my_host_template)) ;
-
return -1 ;
}
@@ -1863,6 +1866,7 @@ int __init usb_stor_init(void)
if (usb_register(&storage_driver) < 0)
return -1;
+ /* we're all set */
printk(KERN_INFO "USB Mass Storage support registered.\n");
return 0;
}
@@ -1870,17 +1874,35 @@ int __init usb_stor_init(void)
void __exit usb_stor_exit(void)
{
static struct us_data *ptr;
-
+ static struct us_data *next;
+ unsigned long flags;
+
+ /*
+ * deregister the driver -- this eliminates races with probes and
+ * disconnects
+ */
+ usb_deregister(&storage_driver) ;
+
+ /* lock access to the data structures */
+ spin_lock_irqsave(&us_list_spinlock, flags);
+
/* unregister all the virtual hosts */
for (ptr = us_list; ptr != NULL; ptr = ptr->next)
- scsi_unregister_module(MODULE_SCSI_HA, &(ptr->htmplt));
-
- /* free up the data structures */
-
+ scsi_unregister_module(MODULE_SCSI_HA, &(ptr->htmplt));
+
/* kill the threads */
+ /* FIXME: we can do this by sending them a signal to die */
- /* deregister the driver */
- usb_deregister(&storage_driver) ;
+ /* free up the data structures */
+ /* FIXME: we need to eliminate the host structure also */
+ while (ptr) {
+ next = ptr->next;
+ kfree(ptr);
+ ptr = next;
+ }
+
+ /* unlock the data structures */
+ spin_unlock_irqrestore(&us_list_spinlock, flags);
}
module_init(usb_stor_init) ;
diff --git a/drivers/usb/usb-uhci.c b/drivers/usb/usb-uhci.c
index 5dbde2959..f77296d7f 100644
--- a/drivers/usb/usb-uhci.c
+++ b/drivers/usb/usb-uhci.c
@@ -1002,8 +1002,11 @@ _static int uhci_unlink_urb_sync (uhci_t *s, urb_t *urb)
urb_priv = urb->hcpriv;
switch (usb_pipetype (urb->pipe)) {
- case PIPE_ISOCHRONOUS:
+
case PIPE_INTERRUPT:
+ usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
+
+ case PIPE_ISOCHRONOUS:
uhci_clean_iso_step1(s, urb_priv);
uhci_wait_ms(1);
uhci_clean_iso_step2(s, urb_priv);
@@ -1131,8 +1134,10 @@ _static int uhci_unlink_urb_async (uhci_t *s,urb_t *urb)
urb_priv = (urb_priv_t*)urb->hcpriv;
switch (usb_pipetype (urb->pipe)) {
- case PIPE_ISOCHRONOUS:
case PIPE_INTERRUPT:
+ usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
+
+ case PIPE_ISOCHRONOUS:
uhci_clean_iso_step1 (s, urb_priv);
break;
@@ -1617,8 +1622,8 @@ _static __u8 root_hub_dev_des[] =
0x00, /* __u16 bcdDevice; */
0x00,
0x00, /* __u8 iManufacturer; */
- 0x00, /* __u8 iProduct; */
- 0x00, /* __u8 iSerialNumber; */
+ 0x02, /* __u8 iProduct; */
+ 0x01, /* __u8 iSerialNumber; */
0x01 /* __u8 bNumConfigurations; */
};
@@ -1915,8 +1920,14 @@ _static int rh_submit_urb (urb_t *urb)
len = min (leni, min (sizeof (root_hub_config_des), wLength));
memcpy (data, root_hub_config_des, len);
OK (len);
- case (0x03): /*string descriptors */
- stat = -EPIPE;
+ case (0x03): /* string descriptors */
+ len = usb_root_hub_string (wValue & 0xff,
+ uhci->io_addr, "UHCI",
+ data, wLength);
+ if (len > 0) {
+ OK (min (leni, len));
+ } else
+ stat = -EPIPE;
}
break;
@@ -2597,6 +2608,15 @@ _static int __init alloc_uhci (struct pci_dev *dev, int irq, unsigned int io_add
uhci_t *s;
struct usb_bus *bus;
struct pm_dev *pmdev;
+ char buf[8], *bufp = buf;
+
+#ifndef __sparc__
+ sprintf(buf, "%d", irq);
+#else
+ bufp = __irq_itoa(irq);
+#endif
+ printk(KERN_INFO __FILE__ ": USB UHCI at I/O 0x%x, IRQ %s\n",
+ io_addr, bufp);
s = kmalloc (sizeof (uhci_t), GFP_KERNEL);
if (!s)
diff --git a/drivers/usb/usb.c b/drivers/usb/usb.c
index df8fe9b38..9b2d38b6f 100644
--- a/drivers/usb/usb.c
+++ b/drivers/usb/usb.c
@@ -1184,6 +1184,54 @@ void usb_init_root_hub(struct usb_device *dev)
dev->actconfig = NULL;
}
+/* for returning string descriptors in UTF-16LE */
+static int ascii2utf (char *ascii, __u8 *utf, int utfmax)
+{
+ int retval;
+
+ for (retval = 0; *ascii && utfmax > 1; utfmax -= 2, retval += 2) {
+ *utf++ = *ascii++ & 0x7f;
+ *utf++ = 0;
+ }
+ return retval;
+}
+
+/*
+ * root_hub_string is used by each host controller's root hub code,
+ * so that they're identified consistently throughout the system.
+ */
+int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len)
+{
+ char buf [20];
+
+ // assert (len > (2 * (sizeof (buf) + 1)));
+ // assert (strlen (type) ~== 4);
+
+ // language ids
+ if (id == 0) {
+ *data++ = 4; *data++ = 3; /* 4 bytes data */
+ *data++ = 0; *data++ = 0; /* some language id */
+ return 4;
+
+ // serial number
+ } else if (id == 1) {
+ sprintf (buf, "%x", serial);
+
+ // product description
+ } else if (id == 2) {
+ sprintf (buf, "USB %s Root Hub", type);
+
+ // id 3 == vendor description
+
+ // unsupported IDs --> "stall"
+ } else
+ return 0;
+
+ data [0] = 2 + ascii2utf (buf, data + 2, len - 2);
+ data [1] = 3;
+ return data [0];
+}
+
/*
* __usb_get_extra_descriptor() finds a descriptor of specific type in the
* extra field of the interface and endpoint descriptor structs.
@@ -1824,6 +1872,7 @@ EXPORT_SYMBOL(usb_interface_claimed);
EXPORT_SYMBOL(usb_driver_release_interface);
EXPORT_SYMBOL(usb_init_root_hub);
+EXPORT_SYMBOL(usb_root_hub_string);
EXPORT_SYMBOL(usb_new_device);
EXPORT_SYMBOL(usb_connect);
EXPORT_SYMBOL(usb_disconnect);
diff --git a/drivers/usb/wacom.c b/drivers/usb/wacom.c
index 6dc9ab480..cb77f0717 100644
--- a/drivers/usb/wacom.c
+++ b/drivers/usb/wacom.c
@@ -1,5 +1,5 @@
/*
- * wacom.c Version 0.4
+ * wacom.c Version 0.5
*
* Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2000 Andreas Bach Aaen <abach@stofanet.dk>
@@ -14,8 +14,10 @@
* v0.1 (vp) - Initial release
* v0.2 (aba) - Support for all buttons / combinations
* v0.3 (vp) - Support for Intuos added
- * v0.4 (sm) - Support for more Intuos models, menustrip,
- * relative mode, proximity.
+ * v0.4 (sm) - Support for more Intuos models, menustrip
+ * relative mode, proximity.
+ * v0.5 (vp) - Big cleanup, nifty features removed,
+ * they belong in userspace
*/
/*
@@ -50,16 +52,14 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
/*
* Wacom Graphire packet:
*
- * The input report:
- *
* byte 0: report ID (2)
- * byte 1: bit7 mouse/pen/rubber near
- * bit5-6 0 - pen, 1 - rubber, 2 - mouse
- * bit4 1 ?
- * bit3 0 ?
- * bit2 mouse middle button / pen button2
- * bit1 mouse right button / pen button1
- * bit0 mouse left button / pen tip / rubber
+ * byte 1: bit7 pointer in range
+ * bit5-6 pointer type 0 - pen, 1 - rubber, 2 - mouse
+ * bit4 1 ?
+ * bit3 0 ?
+ * bit2 mouse middle button / pen button2
+ * bit1 mouse right button / pen button1
+ * bit0 mouse left button / touch
* byte 2: X low bits
* byte 3: X high bits
* byte 4: Y low bits
@@ -69,115 +69,58 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
*
* There are also two single-byte feature reports (2 and 3).
*
- * Resolution:
- * X: 0 - 10206
- * Y: 0 - 7422
- *
- * (0,0) is upper left corner
- *
- * Wacom Intuos Status packet:
+ * Wacom Intuos status packet:
*
- * byte 0: report ID (2)
- * byte 1: bit7 1 (Sync Byte)
- * bit6 Pointer Near
- * bit5 0 - first proximity report
- * bit4 0 ?
- * bit3 0 ?
- * bit2 pen button2
- * bit1 pen button1
- * bit0 0 ?
- * byte 2: X high bits
- * byte 3: X low bits
- * byte 4: Y high bits
- * byte 5: Y low bits
- * byte 6: bits 0-7: pressure (bits 2-9)
- * byte 7: bits 6-7: pressure (bits 0-1)
- * byte 7: bits 0-5: X tilt (bits 1-6)
- * byte 8: bit 7: X tilt (bit 0)
- * byte 8: bits 0-6: Y tilt (bits 0-6)
- * byte 9: bits 4-7: Proximity
+ * byte 0: report ID (2)
+ * byte 1: bit7 1 - sync bit
+ * bit6 pointer in range
+ * bit5 pointer type report
+ * bit4 0 ?
+ * bit3 0 ?
+ * bit2 pen button2
+ * bit1 pen button1
+ * bit0 0 ?
+ * byte 2: X high bits
+ * byte 3: X low bits
+ * byte 4: Y high bits
+ * byte 5: Y low bits
+ * byte 6: bits 0-7: pressure (bits 2-9)
+ * byte 7: bits 6-7: pressure (bits 0-1)
+ * byte 7: bits 0-5: X tilt (bits 1-6)
+ * byte 8: bit 7: X tilt (bit 0)
+ * byte 8: bits 0-6: Y tilt (bits 0-6)
+ * byte 9: bits 4-7: distance
*/
-#define USB_VENDOR_ID_WACOM 0x056a
-#define USB_DEVICE_ID_WACOM_GRAPHIRE 0x0010
-#define USB_DEVICE_ID_WACOM_INTUOS45 0x0020 /* Guess */
-#define USB_DEVICE_ID_WACOM_INTUOS68 0x0021
-#define USB_DEVICE_ID_WACOM_INTUOS912 0x0022 /* Guess */
-#define USB_DEVICE_ID_WACOM_INTUOS1212 0x0023
-#define USB_DEVICE_ID_WACOM_INTUOS1218 0x0024 /* Guess */
-
-#define USB_TOOL_ID_WACOM_PEN 0x0022
-#define USB_TOOL_ID_WACOM_ERASER 0x00fa
-#define USB_TOOL_ID_WACOM_STROKE_PEN 0x0032
-#define USB_TOOL_ID_WACOM_INKING_PEN 0x0012
-#define USB_TOOL_ID_WACOM_AIRBRUSH 0x0112
-#define USB_TOOL_ID_WACOM_MOUSE4D 0x0094
-#define USB_TOOL_ID_WACOM_LENS_CURSOR 0x0096
-
-#define INTUOS_PEN_MODE_ABS 0x00
-#define INTUOS_PEN_MODE_REL 0x01
-#define INTUOS_PEN_MODE_QUICKPOINT 0x02
-
-#define INTUOS_PRESSURE_MODE_SOFT 0x00
-#define INTUOS_PRESSURE_MODE_MED 0x01
-#define INTUOS_PRESSURE_MODE_FIRM 0x02
-
-#define INTUOS_MENUSTRIP_Y_ZONE 1400
-#define INTUOS_MENUSTRIP_BTN_YMIN 270
-#define INTUOS_MENUSTRIP_BTN_YMAX 1070
-#define INTUOS_MENUSTRIP_F1_XMIN 40
-#define INTUOS_MENUSTRIP_F7_XMIN 8340
-#define INTUOS_MENUSTRIP_F12_XMIN 15300
-
-#define INTUOS_MENUSTRIP_BTN_WIDTH 1300
-
-#define INTUOS_MENUSTRIP_F7_IX_OFFSET 6 /* offset into wacom_fkeys */
-#define INTUOS_MENUSTRIP_F12_IX_OFFSET 11
+#define USB_VENDOR_ID_WACOM 0x056a
+
+struct wacom_features {
+ char *name;
+ int idProduct;
+ int pktlen;
+ int x_max;
+ int y_max;
+ int pressure_max;
+ int distance_max;
+ void (*irq)(struct urb *urb);
+ unsigned long evbit;
+ unsigned long relbit;
+ unsigned long absbit;
+ unsigned long btnbit;
+ unsigned long digibit;
+};
struct wacom {
- signed char data[10];
+ signed char data[10];
struct input_dev dev;
struct urb irq;
-
- int last_x, last_y;
- unsigned int tool, device;
- unsigned int ymax, menustrip_touch;
- unsigned int pen_mode;
- unsigned int pressure_mode;
+ struct wacom_features *features;
+ int tool;
};
-static int wacom_fkeys[16] = { KEY_F1, KEY_F2, KEY_F3, KEY_F4,
- KEY_F5, KEY_F6, KEY_F7, KEY_F8,
- KEY_F9, KEY_F10, KEY_F11, KEY_F12,
- KEY_F13, KEY_F14, KEY_F15, KEY_F16};
-
-#define INTUOS_EXTENTS_MAX_X 0x00
-#define INTUOS_EXTENTS_MAX_Y 0x01
-#define INTUOS_EXTENTS_HAS_F7 0x02
-#define INTUOS_EXTENTS_HAS_F12 0x03
-#define INTUOS_EXTENTS_PEN_MODE 0x04
-#define INTUOS_EXTENTS_HAS_QUICKPOINT 0x05
-#define INTUOS_EXTENTS_HAS_PRESSURE_MODE 0x06
-#define INTUOS_EXTENTS_PRESSURE_MODE 0x07
-
-#define WACOM_TRUE 1
-#define WACOM_FALSE 0
-
-static int intuos_extents[5][8] = {
- { 12700, 10360, WACOM_FALSE, WACOM_FALSE, 8340, WACOM_FALSE, WACOM_FALSE, 0}, /* Intuos 4x5 */
- { 20320, 15040, WACOM_TRUE, WACOM_FALSE, 15300, WACOM_FALSE, WACOM_TRUE, 18360}, /* Intuos 6x8 */
- { 30480, 23060, WACOM_TRUE, WACOM_TRUE, 22280, WACOM_TRUE, WACOM_TRUE, 26640}, /* Intuos 9x12 */
- { 30480, 30480, WACOM_TRUE, WACOM_TRUE, 22280, WACOM_TRUE, WACOM_TRUE, 26640}, /* Intuos 12x12 */
- { 47720, 30480, WACOM_TRUE, WACOM_TRUE, 29260, WACOM_TRUE, WACOM_TRUE, 33620}}; /* Intuos 12x18 */
-
-static char intuos_names[5][12] = {
- {"Intuos 4x5 "}, {"Intuos 6x8 "},
- {"Intuos 9x12 "}, {"Intuos 12x12"},
- {"Intuos 12x18"}};
-
static void wacom_graphire_irq(struct urb *urb)
{
- struct wacom *wacom = urb->context;
+ struct wacom *wacom = urb->context;
unsigned char *data = wacom->data;
struct input_dev *dev = &wacom->dev;
@@ -186,285 +129,135 @@ static void wacom_graphire_irq(struct urb *urb)
if (data[0] != 2)
dbg("received unknown report #%d", data[0]);
- if ( data[1] & 0x80 ) {
- input_report_abs(dev, ABS_X, data[2] | ((__u32)data[3] << 8));
- input_report_abs(dev, ABS_Y, 7422 - (data[4] | ((__u32)data[5] << 8)));
+ if ( data[1] & 0x80 ) {
+ input_report_abs(dev, ABS_X, data[2] | ((__u32)data[3] << 8));
+ input_report_abs(dev, ABS_Y, data[4] | ((__u32)data[5] << 8));
}
switch ((data[1] >> 5) & 3) {
- case 0: /* Pen */
- input_report_btn(dev, BTN_TOOL_PEN, data[1] & 0x80);
- input_report_btn(dev, BTN_TOUCH, data[1] & 0x01);
- input_report_btn(dev, BTN_STYLUS, data[1] & 0x02);
- input_report_btn(dev, BTN_STYLUS2, data[1] & 0x04);
- input_report_abs(dev, ABS_PRESSURE, data[6] | ((__u32)data[7] << 8));
- break;
-
- case 1: /* Rubber */
- input_report_btn(dev, BTN_TOOL_RUBBER, data[1] & 0x80);
- input_report_btn(dev, BTN_TOUCH, data[1] & 0x01);
- input_report_btn(dev, BTN_STYLUS, data[1] & 0x02);
- input_report_btn(dev, BTN_STYLUS2, data[1] & 0x04);
- input_report_abs(dev, ABS_PRESSURE, data[6] | ((__u32)data[7] << 8));
- break;
-
- case 2: /* Mouse */
- input_report_btn(dev, BTN_TOOL_MOUSE, data[7] > 24);
- input_report_btn(dev, BTN_LEFT, data[1] & 0x01);
- input_report_btn(dev, BTN_RIGHT, data[1] & 0x02);
- input_report_btn(dev, BTN_MIDDLE, data[1] & 0x04);
- input_report_abs(dev, ABS_DISTANCE, data[7]);
- input_report_rel(dev, REL_WHEEL, (signed char) data[6]);
- break;
- }
-}
+ case 0: /* Pen */
+ input_report_btn(dev, BTN_TOOL_PEN, data[1] & 0x80);
+ break;
-static void intuos_menustrip( unsigned int x, unsigned int y, struct wacom *wacom )
-{
- struct input_dev *dev = &wacom->dev;
- unsigned int local_x = x;
- unsigned int local_y = y - ( wacom->ymax - INTUOS_MENUSTRIP_Y_ZONE );
- unsigned int fkey_index ;
-
- /* Ensure we are in the vertical strip for the buttons */
- if ( (local_y > INTUOS_MENUSTRIP_BTN_YMIN) && (local_y < INTUOS_MENUSTRIP_BTN_YMAX) ) {
-
- /* Handle Pressure Mode */
- if ( intuos_extents[wacom->device][INTUOS_EXTENTS_HAS_PRESSURE_MODE] ) {
- int pressure_mode = ( (local_x - intuos_extents[wacom->device][INTUOS_EXTENTS_PRESSURE_MODE])
- / INTUOS_MENUSTRIP_BTN_WIDTH );
- if ( ( pressure_mode >= INTUOS_PRESSURE_MODE_SOFT ) &&
- ( pressure_mode <= INTUOS_PRESSURE_MODE_FIRM ) ) {
- wacom->pressure_mode = pressure_mode;
- return;
- }
- }
-
- /* Handle Pen Mode */
- {
- int pen_mode = ( (local_x - intuos_extents[wacom->device][INTUOS_EXTENTS_PEN_MODE])
- / INTUOS_MENUSTRIP_BTN_WIDTH );
- if ( ( pen_mode == INTUOS_PEN_MODE_ABS ) ||
- ( pen_mode == INTUOS_PEN_MODE_REL ) ||
- ( ( pen_mode == INTUOS_PEN_MODE_QUICKPOINT ) &&
- ( intuos_extents[wacom->device][INTUOS_EXTENTS_HAS_QUICKPOINT] ) ) ) {
- wacom->pen_mode = pen_mode;
- return;
- }
- }
-
- /* Handle Function Keys */
- if ( local_x > INTUOS_MENUSTRIP_F12_XMIN ) {
- fkey_index = INTUOS_MENUSTRIP_F12_IX_OFFSET
- + ( (local_x - INTUOS_MENUSTRIP_F12_XMIN) / INTUOS_MENUSTRIP_BTN_WIDTH );
- fkey_index = ( fkey_index > 16 ) ? 16 : fkey_index; /* Ensure in range */
- }
- else if ( local_x > INTUOS_MENUSTRIP_F7_XMIN ) {
- fkey_index = INTUOS_MENUSTRIP_F7_IX_OFFSET
- + ( (local_x - INTUOS_MENUSTRIP_F7_XMIN) / INTUOS_MENUSTRIP_BTN_WIDTH );
- fkey_index = ( fkey_index > 11 ) ? 11 : fkey_index;
- }
- else {
- fkey_index = ( (local_x - INTUOS_MENUSTRIP_F1_XMIN) / INTUOS_MENUSTRIP_BTN_WIDTH );
- fkey_index = ( fkey_index > 6 ) ? 6 : fkey_index;
- }
- input_report_key(dev, wacom_fkeys[fkey_index], 1);
- input_report_key(dev, wacom_fkeys[fkey_index], 0);
+ case 1: /* Rubber */
+ input_report_btn(dev, BTN_TOOL_RUBBER, data[1] & 0x80);
+ break;
- return;
+ case 2: /* Mouse */
+ input_report_btn(dev, BTN_TOOL_MOUSE, data[7] > 24);
+ input_report_btn(dev, BTN_LEFT, data[1] & 0x01);
+ input_report_btn(dev, BTN_RIGHT, data[1] & 0x02);
+ input_report_btn(dev, BTN_MIDDLE, data[1] & 0x04);
+ input_report_abs(dev, ABS_DISTANCE, data[7]);
+ input_report_rel(dev, REL_WHEEL, (signed char) data[6]);
+ return;
}
+
+ input_report_abs(dev, ABS_PRESSURE, data[6] | ((__u32)data[7] << 8));
+
+ input_report_btn(dev, BTN_TOUCH, data[1] & 0x01);
+ input_report_btn(dev, BTN_STYLUS, data[1] & 0x02);
+ input_report_btn(dev, BTN_STYLUS2, data[1] & 0x04);
}
-
+
static void wacom_intuos_irq(struct urb *urb)
{
- struct wacom *wacom = urb->context;
+ struct wacom *wacom = urb->context;
unsigned char *data = wacom->data;
struct input_dev *dev = &wacom->dev;
unsigned int t;
- int x, y;
if (urb->status) return;
if (data[0] != 2)
dbg("received unknown report #%d", data[0]);
- if ( ((data[1] >> 5) & 0x3) == 0x2 ) /* First record, feature report */
- {
- wacom->tool = (((char)data[2] << 4) | (char)data[3] >> 4) & 0xff ;
- /* Report tool type */
- switch ( wacom->tool ) {
- case USB_TOOL_ID_WACOM_PEN:
- input_report_btn(dev, BTN_TOOL_PEN, 1);
- break;
- case USB_TOOL_ID_WACOM_ERASER:
- input_report_btn(dev, BTN_TOOL_RUBBER, 1);
- break;
- case USB_TOOL_ID_WACOM_STROKE_PEN:
- input_report_btn(dev, BTN_TOOL_BRUSH, 1);
- break;
- case USB_TOOL_ID_WACOM_INKING_PEN:
- input_report_btn(dev, BTN_TOOL_PENCIL, 1);
- break;
- case USB_TOOL_ID_WACOM_AIRBRUSH:
- input_report_btn(dev, BTN_TOOL_AIRBRUSH, 1);
- break;
- case USB_TOOL_ID_WACOM_MOUSE4D:
- case USB_TOOL_ID_WACOM_LENS_CURSOR:
- input_report_btn(dev, BTN_TOOL_MOUSE, 1);
- break;
- default:
- break;
- }
- return;
- }
-
- if ( ( data[1] | data[2] | data[3] | data[4] | data[5] | data[6]
- | data[7] | data[8] | data[9] ) == 0x80 ) { /* exit report */
- switch ( wacom->tool ) {
- case USB_TOOL_ID_WACOM_PEN:
- input_report_btn(dev, BTN_TOOL_PEN, 0);
- break;
- case USB_TOOL_ID_WACOM_ERASER:
- input_report_btn(dev, BTN_TOOL_RUBBER, 0);
- break;
- case USB_TOOL_ID_WACOM_STROKE_PEN:
- input_report_btn(dev, BTN_TOOL_BRUSH, 0);
- break;
- case USB_TOOL_ID_WACOM_INKING_PEN:
- input_report_btn(dev, BTN_TOOL_PENCIL, 0);
- break;
- case USB_TOOL_ID_WACOM_AIRBRUSH:
- input_report_btn(dev, BTN_TOOL_AIRBRUSH, 0);
- break;
- case USB_TOOL_ID_WACOM_MOUSE4D:
- case USB_TOOL_ID_WACOM_LENS_CURSOR:
- input_report_btn(dev, BTN_TOOL_MOUSE, 0);
- break;
- default:
- break;
+ if (((data[1] >> 5) & 0x3) == 0x2) { /* Enter report */
+
+ switch (((__u32)data[2] << 4) | (data[3] >> 4)) {
+ case 0x012: wacom->tool = BTN_TOOL_PENCIL; break; /* Inking pen */
+ case 0x022: wacom->tool = BTN_TOOL_PEN; break; /* Pen */
+ case 0x032: wacom->tool = BTN_TOOL_BRUSH; break; /* Stroke pen */
+ case 0x094: wacom->tool = BTN_TOOL_MOUSE; break; /* Mouse 4D */
+ case 0x096: wacom->tool = BTN_TOOL_LENS; break; /* Lens cursor */
+ case 0x0fa: wacom->tool = BTN_TOOL_RUBBER; break; /* Eraser */
+ case 0x112: wacom->tool = BTN_TOOL_AIRBRUSH; break; /* Airbrush */
+ default: wacom->tool = BTN_TOOL_PEN; break; /* Unknown tool */
}
+ input_report_btn(dev, wacom->tool, 1);
return;
}
- x = (((unsigned int) data[2]) << 8) | data[3] ;
- y = wacom->dev.absmax[ABS_Y] - ((((unsigned int) data[4]) << 8) | data[5]);
-
- t = (((unsigned int) data[6]) << 2) | ((data[7] & 0xC0) >> 6);
-
- /* Handle touch near menustrip */
- if ( y > ( wacom->dev.absmax[ABS_Y] - INTUOS_MENUSTRIP_Y_ZONE ) ) {
- if ( t > 10 ) /* Touch */
- wacom->menustrip_touch = 1;
- if ( (wacom->menustrip_touch) && (t <= 10) ) { /* Pen Up */
- intuos_menustrip( x, y, wacom );
- wacom->menustrip_touch = 0;
- }
+ if ((data[1] | data[2] | data[3] | data[4] | data[5] |
+ data[6] | data[7] | data[8] | data[9]) == 0x80) { /* Exit report */
+ input_report_btn(dev, wacom->tool, 0);
return;
}
- else
- wacom->menustrip_touch = 0;
-
- switch ( wacom->pen_mode ) {
- case INTUOS_PEN_MODE_ABS:
- input_report_abs(dev, ABS_X, x);
- input_report_abs(dev, ABS_Y, y);
- break;
- case INTUOS_PEN_MODE_REL:
- input_report_rel(dev, REL_X, ( x - wacom->last_x) / 8 );
- input_report_rel(dev, REL_Y, - ( y - wacom->last_y) / 8 );
- break;
- default: break;
- }
-
- wacom->last_x = x;
- wacom->last_y = y;
- input_report_btn(dev, BTN_STYLUS, data[1] & 0x02);
- input_report_btn(dev, BTN_STYLUS2, data[1] & 0x04);
-
- input_report_btn(dev, BTN_TOUCH, t > 10);
- input_report_abs(dev, ABS_PRESSURE, t);
-
- input_report_abs(dev, ABS_DISTANCE, ( data[9] & 0xf0 ) >> 4 );
-
- input_report_abs(dev, ABS_TILT_X, ((((unsigned int) data[7]) & 0x3f) << 1) | ((data[8] & 0x80) >> 7));
+ input_report_abs(dev, ABS_X, ((__u32)data[2] << 8) | data[3]);
+ input_report_abs(dev, ABS_Y, ((__u32)data[4] << 8) | data[5]);
+ input_report_abs(dev, ABS_PRESSURE, t = ((__u32)data[6] << 2) | ((data[7] >> 6) & 3));
+ input_report_abs(dev, ABS_DISTANCE, data[9] >> 4);
+ input_report_abs(dev, ABS_TILT_X, ((data[7] << 1) & 0x7e) | (data[8] >> 7));
input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f);
+ input_report_btn(dev, BTN_STYLUS, data[1] & 2);
+ input_report_btn(dev, BTN_STYLUS2, data[1] & 4);
+ input_report_btn(dev, BTN_TOUCH, t > 10);
}
+#define WACOM_INTUOS_TOOLS (BIT(BTN_TOOL_BRUSH) | BIT(BTN_TOOL_PENCIL) | BIT(BTN_TOOL_AIRBRUSH) | BIT(BTN_TOOL_LENS))
+
+struct wacom_features wacom_features[] = {
+ { "Graphire", 0x10, 8, 10206, 7422, 511, 32, wacom_graphire_irq,
+ BIT(EV_REL), 0, BIT(REL_WHEEL), BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE), 0 },
+ { "Intuos 4x5", 0x20, 10, 12700, 10360, 1023, 15, wacom_intuos_irq,
+ 0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS },
+ { "Intuos 6x8", 0x21, 10, 20320, 15040, 1023, 15, wacom_intuos_irq,
+ 0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS },
+ { "Intuos 9x12", 0x22, 10, 30480, 23060, 1023, 15, wacom_intuos_irq,
+ 0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS },
+ { "Intuos 12x12", 0x23, 10, 30480, 30480, 1023, 15, wacom_intuos_irq,
+ 0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS },
+ { "Intuos 12x18", 0x24, 10, 47720, 30480, 1023, 15, wacom_intuos_irq,
+ 0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS },
+ { NULL , 0 }
+};
+
static void *wacom_probe(struct usb_device *dev, unsigned int ifnum)
{
struct usb_endpoint_descriptor *endpoint;
struct wacom *wacom;
- char *name;
+ int i;
- if (dev->descriptor.idVendor != USB_VENDOR_ID_WACOM ||
- (dev->descriptor.idProduct != USB_DEVICE_ID_WACOM_GRAPHIRE &&
- dev->descriptor.idProduct != USB_DEVICE_ID_WACOM_INTUOS45 &&
- dev->descriptor.idProduct != USB_DEVICE_ID_WACOM_INTUOS68 &&
- dev->descriptor.idProduct != USB_DEVICE_ID_WACOM_INTUOS912 &&
- dev->descriptor.idProduct != USB_DEVICE_ID_WACOM_INTUOS1212 &&
- dev->descriptor.idProduct != USB_DEVICE_ID_WACOM_INTUOS1218))
- return NULL;
+ if (dev->descriptor.idVendor != USB_VENDOR_ID_WACOM) return NULL;
+ for (i = 0; wacom_features[i].idProduct && wacom_features[i].idProduct != dev->descriptor.idProduct; i++);
+ if (!wacom_features[i].idProduct) return NULL;
endpoint = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0;
if (!(wacom = kmalloc(sizeof(struct wacom), GFP_KERNEL))) return NULL;
memset(wacom, 0, sizeof(struct wacom));
- if ( dev->descriptor.idProduct == USB_DEVICE_ID_WACOM_GRAPHIRE ) {
- wacom->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS);
- wacom->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
- wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE);
- wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2);
- wacom->dev.relbit[0] |= BIT(REL_WHEEL);
- wacom->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | BIT(ABS_DISTANCE);
-
- wacom->dev.absmax[ABS_X] = 10206;
- wacom->dev.absmax[ABS_Y] = 7422;
- wacom->dev.absmax[ABS_PRESSURE] = 511;
- wacom->dev.absmax[ABS_DISTANCE] = 32;
-
- FILL_INT_URB(&wacom->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress),
- wacom->data, 8, wacom_graphire_irq, wacom, endpoint->bInterval);
-
- name = "Graphire";
- }
- else { /* Intuos */
-
- wacom->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_REL);
- wacom->dev.keybit[LONG(KEY_F1)] |= BIT(KEY_F1) | BIT(KEY_F2) | BIT(KEY_F3) | BIT(KEY_F4) | BIT(KEY_F5);
- wacom->dev.keybit[LONG(KEY_F6)] |= BIT(KEY_F6) | BIT(KEY_F7) | BIT(KEY_F8);
- wacom->dev.keybit[LONG(KEY_F9)] |= BIT(KEY_F9) | BIT(KEY_F10) | BIT(KEY_F11) | BIT(KEY_F12);
- wacom->dev.keybit[LONG(KEY_F13)] |= BIT(KEY_F13) | BIT(KEY_F14) | BIT(KEY_F15) | BIT(KEY_F16);
- wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2);
- wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_BRUSH);
- wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PENCIL) | BIT(BTN_TOOL_AIRBRUSH) | BIT(BTN_TOOL_MOUSE);
- wacom->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | BIT(ABS_DISTANCE);
- wacom->dev.absbit[0] |= BIT(ABS_TILT_X) | BIT(ABS_TILT_Y);
- wacom->dev.relbit[0] |= BIT(REL_X) | BIT(REL_Y);
-
- wacom->dev.absmax[ABS_PRESSURE] = 1023;
- wacom->dev.absmax[ABS_DISTANCE] = 15;
- wacom->dev.absmax[ABS_TILT_X] = 127;
- wacom->dev.absmax[ABS_TILT_Y] = 127;
-
- wacom->device = dev->descriptor.idProduct - USB_DEVICE_ID_WACOM_INTUOS45;
+ wacom->features = wacom_features + i;
- wacom->dev.absmax[ABS_X] = intuos_extents[wacom->device][INTUOS_EXTENTS_MAX_X];
- wacom->dev.absmax[ABS_Y] = intuos_extents[wacom->device][INTUOS_EXTENTS_MAX_Y];
+ wacom->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | wacom->features->evbit;
+ wacom->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | BIT(ABS_DISTANCE) | wacom->features->absbit;
+ wacom->dev.relbit[0] |= wacom->features->relbit;
+ wacom->dev.keybit[LONG(BTN_LEFT)] |= wacom->features->btnbit;
+ wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) |
+ BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2) | wacom->features->digibit;
- wacom->ymax = intuos_extents[wacom->device][INTUOS_EXTENTS_MAX_Y];
- wacom->pen_mode = INTUOS_PEN_MODE_ABS;
- wacom->pressure_mode = INTUOS_PRESSURE_MODE_SOFT;
+ wacom->dev.absmax[ABS_X] = wacom->features->x_max;
+ wacom->dev.absmax[ABS_Y] = wacom->features->y_max;
+ wacom->dev.absmax[ABS_PRESSURE] = wacom->features->pressure_max;
+ wacom->dev.absmax[ABS_DISTANCE] = wacom->features->distance_max;
+ wacom->dev.absmax[ABS_TILT_X] = 127;
+ wacom->dev.absmax[ABS_TILT_Y] = 127;
- name = intuos_names[wacom->device];
-
- FILL_INT_URB(&wacom->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress),
- wacom->data, 10, wacom_intuos_irq, wacom, endpoint->bInterval);
-
- }
+ FILL_INT_URB(&wacom->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+ wacom->data, wacom->features->pktlen, wacom->features->irq, wacom, endpoint->bInterval);
if (usb_submit_urb(&wacom->irq)) {
kfree(wacom);
@@ -473,7 +266,7 @@ static void *wacom_probe(struct usb_device *dev, unsigned int ifnum)
input_register_device(&wacom->dev);
- printk(KERN_INFO "input%d: Wacom %s\n", wacom->dev.number, name);
+ printk(KERN_INFO "input%d: Wacom %s on usb%d\n", wacom->dev.number, wacom->features->name, dev->devnum);
return wacom;
}