Hi,

Here's a patch against 2.3.50-pre2 (with your previous usb-serial
change) that adds support for the Keyspan PDA USB to Serial device.

This code was done by Brian Warner <[EMAIL PROTECTED]>

This driver has firmware written also by Brian, which he has generously
released under the GPL. I have not included it in this patch, as I have
a question for the list about it. If anyone wants the code, just drop me
a message.

It also has some cleanups that I have started to do, in anticipation of
multiple port serial devices working correctly (there's a bug that keeps
multiport devices from having more than one port active at a time, I'm
working on the proper fix, and this is the first step.)

Thanks,

greg k-h

diff -Naur -X dontdiff linux-2.3.50-pre2/Documentation/usb/usb-serial.txt 
linux-2.3.50-pre2-greg/Documentation/usb/usb-serial.txt
--- linux-2.3.50-pre2/Documentation/usb/usb-serial.txt  Sun Mar  5 21:53:02 2000
+++ linux-2.3.50-pre2-greg/Documentation/usb/usb-serial.txt     Mon Mar  6 22:09:56 
+2000
@@ -67,6 +67,32 @@
   http://usbvisor.sourceforge.net/
 
 
+Keyspan PDA Serial Adapter
+
+  Single port DB-9 serial adapter, pushed as a PDA adapter for iMacs (mostly
+  sold in Macintosh catalogs, comes in a translucent white/green dongle).
+  Fairly simple device. Firmware is homebrew.
+
+Current status:
+ Things that work:
+   basic input/output (tested with 'cu')
+   blocking write when serial line can't keep up
+   changing baud rates (up to 115200)
+   getting/setting modem control pins (TIOCM{GET,SET,BIS,BIC})
+   sending break (although duration looks suspect)
+ Things that don't:
+   device strings (as logged by kernel) have trailing binary garbage
+   device ID isn't right, might collide with other Keyspan products
+   changing baud rates ought to flush tx/rx to avoid mangled half characters
+ Big Things on the todo list:
+   parity, 7 vs 8 bits per char, 1 or 2 stop bits
+   HW flow control
+   not all of the standard USB descriptors are handled: Get_Status, Set_Feature
+   O_NONBLOCK, select()
+
+ The device usually appears at /dev/ttyUSB1 .
+
+
 Generic Serial driver
 
   If your device is not one of the above listed devices, compatible with
diff -Naur -X dontdiff linux-2.3.50-pre2/drivers/usb/keyspan_pda_fw.h 
linux-2.3.50-pre2-greg/drivers/usb/keyspan_pda_fw.h
--- linux-2.3.50-pre2/drivers/usb/keyspan_pda_fw.h      Wed Dec 31 16:00:00 1969
+++ linux-2.3.50-pre2-greg/drivers/usb/keyspan_pda_fw.h Mon Mar  6 22:11:26 2000
@@ -0,0 +1,93 @@
+/*
+ * keyspan_pda_fw.h
+ *
+ * Generated from keyspan_pda.s by ezusb_convert.pl
+ * This file is presumed to be under the same copyright as the source file
+ * from which it was derived.
+ */
+
+static const struct ezusb_hex_record keyspan_pda_firmware[] = {
+{ 0x0000,      3,      {0x02, 0x02, 0x00} },
+{ 0x0023,      4,      {0x02, 0x05, 0x5f, 0x00} },
+{ 0x0043,      4,      {0x02, 0x01, 0x00, 0x00} },
+{ 0x0030,      5,      {0x00, 0x00, 0x00, 0x00, 0x00} },
+{ 0x0100,      16,     {0x02, 0x02, 0x96, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 
+0x00, 0x00, 0x02, 0x00, 0x00, 0x00} },
+{ 0x0110,      16,     {0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 
+0x00, 0x00, 0x02, 0x00, 0x00, 0x00} },
+{ 0x0120,      16,     {0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x04, 
+0x61, 0x00, 0x02, 0x04, 0x89, 0x00} },
+{ 0x0200,      16,     {0x75, 0x81, 0x5e, 0xe4, 0xf5, 0x32, 0xf5, 0x33, 0xf5, 0x30, 
+0xf5, 0x31, 0xf5, 0x34, 0xc2, 0x00} },
+{ 0x0210,      16,     {0xc2, 0x01, 0xa9, 0x00, 0x74, 0xfe, 0x90, 0x10, 0x00, 0xf0, 
+0xa3, 0xd9, 0xfc, 0x74, 0xfd, 0x90} },
+{ 0x0220,      16,     {0x11, 0x00, 0xf0, 0xa3, 0xd9, 0xfc, 0x74, 0x02, 0x90, 0x7f, 
+0x9d, 0xf0, 0x74, 0x00, 0x90, 0x7f} },
+{ 0x0230,      16,     {0x97, 0xf0, 0x74, 0x86, 0x90, 0x7f, 0x9e, 0xf0, 0x90, 0x7f, 
+0x95, 0x74, 0x03, 0xf0, 0x90, 0x7f} },
+{ 0x0240,      16,     {0xaf, 0xe0, 0xd2, 0xe0, 0xf0, 0x74, 0x01, 0x90, 0x7f, 0xab, 
+0xf0, 0x90, 0x7f, 0xae, 0xf0, 0x90} },
+{ 0x0250,      16,     {0x7f, 0xac, 0x74, 0x04, 0xf0, 0x90, 0x7f, 0xad, 0x74, 0x04, 
+0xf0, 0x90, 0x7f, 0xc9, 0xf0, 0x74} },
+{ 0x0260,      16,     {0x84, 0x90, 0x7f, 0x98, 0xf0, 0x74, 0x00, 0xf5, 0x98, 0x75, 
+0xc8, 0x30, 0x7b, 0x05, 0x91, 0x20} },
+{ 0x0270,      16,     {0xd2, 0xca, 0x75, 0x98, 0x50, 0xd2, 0xe8, 0xd2, 0xaf, 0xd2, 
+0xac, 0x74, 0x00, 0xf5, 0x86, 0x90} },
+{ 0x0280,      16,     {0x7f, 0xd6, 0x74, 0x02, 0xf0, 0x79, 0x2e, 0x7a, 0x00, 0x7b, 
+0x00, 0xdb, 0xfe, 0xda, 0xfa, 0xd9} },
+{ 0x0290,      16,     {0xf6, 0x74, 0x06, 0xf0, 0x80, 0xfe, 0xc0, 0x86, 0xc0, 0x82, 
+0xc0, 0x83, 0xc0, 0x84, 0xc0, 0x85} },
+{ 0x02a0,      16,     {0xc0, 0xe0, 0xe5, 0x91, 0xc2, 0xe4, 0xf5, 0x91, 0x90, 0x7f, 
+0xab, 0x74, 0x01, 0xf0, 0x90, 0x7f} },
+{ 0x02b0,      16,     {0xe8, 0xe0, 0xf9, 0xa3, 0xe0, 0xfa, 0xa3, 0xe0, 0xfb, 0xa3, 
+0xe0, 0xfc, 0xe9, 0x54, 0x60, 0xb4} },
+{ 0x02c0,      16,     {0x00, 0x03, 0x02, 0x03, 0x39, 0xb4, 0x40, 0x6e, 0xba, 0x00, 
+0x0b, 0x12, 0x04, 0x20, 0x40, 0x03} },
+{ 0x02d0,      16,     {0x02, 0x04, 0x02, 0x02, 0x04, 0x0a, 0xba, 0x01, 0x03, 0x02, 
+0x04, 0x0a, 0xba, 0x02, 0x03, 0x02} },
+{ 0x02e0,      16,     {0x04, 0x0a, 0xba, 0x03, 0x03, 0x02, 0x04, 0x44, 0xba, 0x04, 
+0x1e, 0xbb, 0x00, 0x0a, 0x90, 0x7f} },
+{ 0x02f0,      16,     {0x95, 0xe0, 0x44, 0x02, 0xf0, 0x02, 0x04, 0x02, 0x90, 0x7f, 
+0x98, 0xe0, 0x54, 0xfd, 0xf0, 0x90} },
+{ 0x0300,      16,     {0x7f, 0x95, 0xe0, 0x54, 0xfd, 0xf0, 0x02, 0x04, 0x02, 0xba, 
+0x05, 0x03, 0x02, 0x04, 0x0a, 0xba} },
+{ 0x0310,      16,     {0x06, 0x19, 0xbb, 0x00, 0x08, 0xe5, 0x33, 0xd3, 0x95, 0x32, 
+0x02, 0x03, 0xde, 0xbb, 0x01, 0x08} },
+{ 0x0320,      16,     {0xe5, 0x32, 0xc3, 0x95, 0x33, 0x02, 0x03, 0xde, 0x02, 0x04, 
+0x0a, 0xba, 0x07, 0x05, 0x8b, 0x34} },
+{ 0x0330,      16,     {0x02, 0x04, 0x02, 0x02, 0x04, 0x0a, 0x02, 0x04, 0x0a, 0xba, 
+0x00, 0x20, 0xb9, 0x80, 0x10, 0x90} },
+{ 0x0340,      16,     {0x7f, 0x00, 0xe4, 0xf0, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 
+0x02, 0xf0, 0x02, 0x04, 0x02, 0xb9} },
+{ 0x0350,      16,     {0x82, 0x02, 0x80, 0xeb, 0xb9, 0x81, 0x02, 0x80, 0xe6, 0x02, 
+0x04, 0x0a, 0xba, 0x01, 0x0f, 0xbb} },
+{ 0x0360,      16,     {0x00, 0x03, 0x02, 0x04, 0x0a, 0xbb, 0x01, 0x03, 0x02, 0x04, 
+0x02, 0x02, 0x04, 0x0a, 0xba, 0x03} },
+{ 0x0370,      16,     {0x0f, 0xbb, 0x00, 0x03, 0x02, 0x04, 0x0a, 0xbb, 0x01, 0x03, 
+0x02, 0x04, 0x02, 0x02, 0x04, 0x0a} },
+{ 0x0380,      16,     {0xba, 0x06, 0x56, 0xbc, 0x01, 0x0f, 0x90, 0x7f, 0xd4, 0x74, 
+0x06, 0xf0, 0x90, 0x7f, 0xd5, 0x74} },
+{ 0x0390,      16,     {0x12, 0xf0, 0x02, 0x04, 0x02, 0xbc, 0x02, 0x12, 0xbb, 0x00, 
+0x6f, 0x90, 0x7f, 0xd4, 0x74, 0x06} },
+{ 0x03a0,      16,     {0xf0, 0x90, 0x7f, 0xd5, 0x74, 0x24, 0xf0, 0x02, 0x04, 0x02, 
+0xbc, 0x03, 0x29, 0x74, 0x04, 0xc3} },
+{ 0x03b0,      16,     {0x9b, 0x40, 0x57, 0x60, 0x55, 0xeb, 0x2b, 0x90, 0x06, 0x44, 
+0x25, 0x82, 0xf5, 0x82, 0x74, 0x00} },
+{ 0x03c0,      16,     {0x35, 0x83, 0xf5, 0x83, 0xe0, 0xf9, 0xa3, 0xe0, 0xfa, 0x90, 
+0x7f, 0xd4, 0xe9, 0xf0, 0x90, 0x7f} },
+{ 0x03d0,      16,     {0xd5, 0xea, 0xf0, 0x02, 0x04, 0x02, 0x02, 0x04, 0x0a, 0xba, 
+0x08, 0x0f, 0x74, 0x01, 0x90, 0x7f} },
+{ 0x03e0,      16,     {0x00, 0xf0, 0x74, 0x01, 0x90, 0x7f, 0xb5, 0xf0, 0x02, 0x04, 
+0x02, 0xba, 0x09, 0x03, 0x02, 0x04} },
+{ 0x03f0,      16,     {0x02, 0xba, 0x0a, 0x05, 0x74, 0x00, 0x02, 0x03, 0xde, 0xba, 
+0x0b, 0x03, 0x02, 0x04, 0x02, 0x02} },
+{ 0x0400,      16,     {0x04, 0x0a, 0x90, 0x7f, 0xb4, 0x74, 0x02, 0xf0, 0x80, 0x09, 
+0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01} },
+{ 0x0410,      16,     {0xf0, 0x80, 0x00, 0xd0, 0xe0, 0xd0, 0x85, 0xd0, 0x84, 0xd0, 
+0x83, 0xd0, 0x82, 0xd0, 0x86, 0x32} },
+{ 0x0420,      16,     {0xeb, 0x20, 0xe7, 0x1e, 0xc3, 0x94, 0x0a, 0x50, 0x19, 0xeb, 
+0x23, 0x24, 0xfe, 0xf5, 0x82, 0x74} },
+{ 0x0430,      16,     {0x05, 0x34, 0x00, 0xf5, 0x83, 0xe0, 0xf5, 0xcb, 0xf5, 0xcd, 
+0xa3, 0xe0, 0xf5, 0xca, 0xf5, 0xcc} },
+{ 0x0440,      16,     {0xc3, 0x22, 0xd3, 0x22, 0xb9, 0x41, 0x11, 0xeb, 0x64, 0xff, 
+0x54, 0x84, 0xfb, 0x90, 0x7f, 0x98} },
+{ 0x0450,      16,     {0xe0, 0x54, 0x7b, 0x4b, 0xf0, 0x02, 0x04, 0x02, 0x90, 0x7f, 
+0x9b, 0xe0, 0x64, 0xff, 0x02, 0x03} },
+{ 0x0460,      16,     {0xde, 0xc0, 0x86, 0xc0, 0x82, 0xc0, 0x83, 0xc0, 0x84, 0xc0, 
+0x85, 0xc0, 0xe0, 0xe5, 0x91, 0xc2} },
+{ 0x0470,      16,     {0xe4, 0xf5, 0x91, 0x90, 0x7f, 0xa9, 0x74, 0x04, 0xf0, 0x12, 
+0x05, 0xa0, 0xd0, 0xe0, 0xd0, 0x85} },
+{ 0x0480,      16,     {0xd0, 0x84, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0x86, 0x32, 0xc0, 
+0x86, 0xc0, 0x82, 0xc0, 0x83, 0xc0} },
+{ 0x0490,      16,     {0x84, 0xc0, 0x85, 0xc0, 0xe0, 0xe5, 0x91, 0xc2, 0xe4, 0xf5, 
+0x91, 0x90, 0x7f, 0xaa, 0x74, 0x04} },
+{ 0x04a0,      16,     {0xf0, 0x90, 0x7f, 0xc9, 0xe0, 0xf9, 0xe4, 0xf5, 0x86, 0x90, 
+0x7d, 0xc0, 0x75, 0x85, 0x10, 0x85} },
+{ 0x04b0,      16,     {0x32, 0x84, 0xe0, 0x05, 0x86, 0x05, 0x84, 0xf0, 0xe5, 0x84, 
+0xb5, 0x33, 0x02, 0x80, 0x09, 0x05} },
+{ 0x04c0,      16,     {0x32, 0x05, 0x86, 0xa3, 0xd9, 0xec, 0x80, 0x00, 0x90, 0x7f, 
+0xc9, 0xf0, 0xb1, 0x31, 0xd0, 0xe0} },
+{ 0x04d0,      16,     {0xd0, 0x85, 0xd0, 0x84, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0x86, 
+0x32, 0xe4, 0xf5, 0x86, 0x90, 0x7f} },
+{ 0x04e0,      16,     {0xbc, 0xe0, 0x20, 0xe1, 0x4b, 0x90, 0x7d, 0x00, 0xe5, 0x32, 
+0xf0, 0xa3, 0xe5, 0x33, 0xf0, 0xa3} },
+{ 0x04f0,      16,     {0xe5, 0x30, 0xf0, 0xa3, 0xe5, 0x31, 0xf0, 0xa3, 0xe4, 0x30, 
+0x00, 0x01, 0x04, 0xf0, 0xa3, 0x05} },
+{ 0x0500,      16,     {0x86, 0x90, 0x10, 0x00, 0x79, 0x10, 0xe0, 0xa3, 0x05, 0x86, 
+0xf0, 0xa3, 0x05, 0x86, 0xd9, 0xf6} },
+{ 0x0510,      16,     {0x05, 0x86, 0x74, 0xfc, 0xf0, 0xa3, 0x05, 0x86, 0x90, 0x11, 
+0x00, 0x79, 0x10, 0xe0, 0xa3, 0x05} },
+{ 0x0520,      16,     {0x86, 0xf0, 0xa3, 0x05, 0x86, 0xd9, 0xf6, 0xe4, 0xf5, 0x86, 
+0x90, 0x7f, 0xbd, 0x74, 0x26, 0xf0} },
+{ 0x0530,      16,     {0x22, 0x20, 0x00, 0x13, 0xe5, 0x32, 0xb5, 0x33, 0x01, 0x22, 
+0x05, 0x33, 0x75, 0x83, 0x10, 0x85} },
+{ 0x0540,      16,     {0x33, 0x82, 0xe0, 0xf5, 0x99, 0xd2, 0x00, 0x74, 0x00, 0xb5, 
+0x34, 0x01, 0x22, 0xe5, 0x33, 0xd3} },
+{ 0x0550,      16,     {0x95, 0x32, 0xc3, 0x95, 0x34, 0x40, 0xf5, 0x75, 0x34, 0x00, 
+0xd2, 0x01, 0x02, 0x05, 0xa0, 0xc0} },
+{ 0x0560,      16,     {0x86, 0xc0, 0x82, 0xc0, 0x83, 0xc0, 0x84, 0xc0, 0x85, 0xc0, 
+0xe0, 0x30, 0x99, 0x07, 0xc2, 0x99} },
+{ 0x0570,      16,     {0xc2, 0x00, 0x12, 0x05, 0x34, 0x30, 0x98, 0x05, 0x12, 0x05, 
+0x8a, 0xc2, 0x98, 0xd0, 0xe0, 0xd0} },
+{ 0x0580,      16,     {0x85, 0xd0, 0x84, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0x86, 0x32, 
+0x75, 0x83, 0x11, 0x85, 0x30, 0x82} },
+{ 0x0590,      16,     {0x05, 0x82, 0xe5, 0x99, 0xf0, 0xe5, 0x82, 0xb5, 0x31, 0x01, 
+0x22, 0x05, 0x30, 0xb1, 0xa0, 0x22} },
+{ 0x05a0,      16,     {0x90, 0x7f, 0xb8, 0xe0, 0x20, 0xe1, 0x38, 0x20, 0x01, 0x36, 
+0xe5, 0x30, 0xb5, 0x31, 0x01, 0x22} },
+{ 0x05b0,      16,     {0xe4, 0xf5, 0x86, 0x75, 0x83, 0x11, 0x05, 0x86, 0x90, 0x7e, 
+0x00, 0xf0, 0xa3, 0x05, 0x86, 0x79} },
+{ 0x05c0,      16,     {0x01, 0xe5, 0x30, 0xb5, 0x31, 0x02, 0x80, 0x10, 0x05, 0x31, 
+0x85, 0x31, 0x82, 0xe0, 0x05, 0x86} },
+{ 0x05d0,      16,     {0xf0, 0xa3, 0x05, 0x86, 0x09, 0xb9, 0x40, 0xe9, 0x90, 0x7f, 
+0xb9, 0xe9, 0x60, 0x01, 0xf0, 0x22} },
+{ 0x05e0,      16,     {0xc2, 0x01, 0xe4, 0xf5, 0x86, 0x90, 0x7e, 0x00, 0x74, 0x01, 
+0xf0, 0xa3, 0x74, 0x02, 0xf0, 0x90} },
+{ 0x05f0,      16,     {0x7f, 0xb9, 0xf0, 0x22, 0xc2, 0x99, 0xf5, 0x99, 0x30, 0x99, 
+0xfd, 0xc2, 0x99, 0x22, 0xe5, 0x5e} },
+{ 0x0600,      16,     {0xf6, 0x3c, 0xfd, 0x8f, 0xfe, 0xc8, 0xff, 0x64, 0xff, 0xb2, 
+0xff, 0xd9, 0xff, 0xed, 0xff, 0xf3} },
+{ 0x0610,      16,     {0xff, 0xfa, 0x12, 0x01, 0x00, 0x01, 0xff, 0xff, 0xff, 0x40, 
+0xcd, 0x06, 0x04, 0x01, 0x89, 0xab} },
+{ 0x0620,      16,     {0x01, 0x02, 0x03, 0x01, 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 
+0x00, 0x80, 0x32, 0x09, 0x04, 0x00} },
+{ 0x0630,      16,     {0x00, 0x02, 0xff, 0xff, 0xff, 0x00, 0x07, 0x05, 0x82, 0x03, 
+0x40, 0x00, 0x01, 0x07, 0x05, 0x02} },
+{ 0x0640,      16,     {0x02, 0x40, 0x00, 0x00, 0x06, 0x4c, 0x06, 0x50, 0x06, 0x72, 
+0x06, 0xa0, 0x04, 0x03, 0x00, 0x00} },
+{ 0x0650,      16,     {0x22, 0x03, 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x45, 0x00, 
+0x20, 0x00, 0x75, 0x00, 0x73, 0x00} },
+{ 0x0660,      16,     {0x62, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 
+0x67, 0x00, 0x65, 0x00, 0x74, 0x00} },
+{ 0x0670,      16,     {0x73, 0x00, 0x2e, 0x03, 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 
+0x45, 0x00, 0x20, 0x00, 0x55, 0x00} },
+{ 0x0680,      16,     {0x53, 0x00, 0x42, 0x00, 0x20, 0x00, 0x73, 0x00, 0x65, 0x00, 
+0x72, 0x00, 0x69, 0x00, 0x61, 0x00} },
+{ 0x0690,      16,     {0x6c, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 
+0x67, 0x00, 0x65, 0x00, 0x74, 0x00} },
+{ 0x06a0,      6,      {0x06, 0x03, 0x34, 0x00, 0x37, 0x00} },
+{ 0xffff,      0,      {0x00} }
+};
diff -Naur -X dontdiff linux-2.3.50-pre2/drivers/usb/usb-serial.c 
linux-2.3.50-pre2-greg/drivers/usb/usb-serial.c
--- linux-2.3.50-pre2/drivers/usb/usb-serial.c  Sun Mar  5 21:53:24 2000
+++ linux-2.3.50-pre2-greg/drivers/usb/usb-serial.c     Mon Mar  6 23:22:21 2000
@@ -14,6 +14,12 @@
  *
  * See Documentation/usb/usb-serial.txt for more information on using this driver
  * 
+ * (03/06/2000) gkh
+ *     Added the keyspan pda code from Brian Warner <[EMAIL PROTECTED]>
+ *     Moved a bunch of the port specific stuff into its own structure. This
+ *     is in anticipation of the true multiport devices (there's a bug if you
+ *     try to access more than one port of any multiport device right now)
+ *
  * (02/21/2000) gkh
  *     Made it so that any serial devices only have to specify which functions
  *     they want to overload from the generic function calls (great, 
@@ -178,6 +184,15 @@
 #include "whiteheat.h"         /* firmware for the ConnectTech WhiteHEAT device */
 #endif
 
+#ifdef CONFIG_USB_SERIAL_KEYSPAN_PDA
+struct ezusb_hex_record {
+       __u16 address;
+       __u8 data_size;
+       __u8 data[16];
+};
+#include "keyspan_pda_fw.h"
+#endif
+
 #include "usb-serial.h"
 
 /* parity check flag */
@@ -214,23 +229,9 @@
 
 static struct usb_serial *get_serial_by_minor (int minor)
 {
-       int i;
-
        dbg("get_serial_by_minor %d", minor);
 
-       if (serial_table[minor] == NULL)
-               return (NULL);
-
-       if (serial_table[minor] != SERIAL_PTR_EMPTY)
-               return (serial_table[minor]);
-
-       i = minor;
-       while (serial_table[i] == SERIAL_PTR_EMPTY) {
-               if (i == 0)
-                       return (NULL);
-               --i;
-               }
-       return (serial_table[i]);
+       return serial_table[minor];
 }
 
 
@@ -263,10 +264,10 @@
                *minor = i;
                dbg("minor base = %d", *minor);
                for (i = *minor+1; (i < (*minor + num_ports)) && (i < 
SERIAL_TTY_MINORS); ++i)
-                       serial_table[i] = SERIAL_PTR_EMPTY;
-               return (serial);
+                       serial_table[i] = serial;
+               return serial;
                }
-       return (NULL);
+       return NULL;
 }
 
 
@@ -317,7 +318,7 @@
        if (response < 0) {
                err("ezusb_set_reset %d failed", reset_bit);
        }
-       return (response);
+       return response;
 }
 
 #endif /* USES_EZUSB_FUNCTIONS */
@@ -382,8 +383,8 @@
                dbg("serial->type == NULL!");
                return;
        }
-       if (!serial->active[port]) {
-               dbg ("device already open");
+       if (!serial->port[port].active) {
+               dbg ("device not opened");
                return;
        }
 
@@ -412,7 +413,7 @@
                dbg("serial->type == NULL!");
                return (-ENODEV);
        }
-       if (!serial->active[port]) {
+       if (!serial->port[port].active) {
                dbg ("device not opened");
                return (-EINVAL);
        }
@@ -442,7 +443,7 @@
                dbg("serial->type == NULL!");
                return (-ENODEV);
        }
-       if (!serial->active[port]) {
+       if (!serial->port[port].active) {
                dbg ("device not open");
                return (-EINVAL);
        }
@@ -472,7 +473,7 @@
                dbg("serial->type == NULL!");
                return (-ENODEV);
        }
-       if (!serial->active[port]) {
+       if (!serial->port[port].active) {
                dbg ("device not open");
                return (-EINVAL);
        }
@@ -502,7 +503,7 @@
                dbg("serial->type == NULL!");
                return;
        }
-       if (!serial->active[port]) {
+       if (!serial->port[port].active) {
                dbg ("device not open");
                return;
        }
@@ -534,7 +535,7 @@
                dbg("serial->type == NULL!");
                return;
        }
-       if (!serial->active[port]) {
+       if (!serial->port[port].active) {
                dbg ("device not open");
                return;
        }
@@ -571,7 +572,7 @@
                dbg("serial->type == NULL!");
                return -ENODEV;
        }
-       if (!serial->active[port]) {
+       if (!serial->port[port].active) {
                dbg ("device not open");
                return -ENODEV;
        }
@@ -606,7 +607,7 @@
                dbg("serial->type == NULL!");
                return;
        }
-       if (!serial->active[port]) {
+       if (!serial->port[port].active) {
                dbg ("device not open");
                return;
        }
@@ -622,6 +623,38 @@
 }
 
 
+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!");
+               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!");
+               return;
+       }
+       if (!serial->port[port].active) {
+               dbg ("device 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);
+       }
+}
+
+
 #ifdef CONFIG_USB_SERIAL_WHITEHEAT
 /*****************************************************************************
  * Connect Tech's White Heat specific driver functions
@@ -629,18 +662,19 @@
 static int whiteheat_serial_open (struct tty_struct *tty, struct file *filp)
 {
        struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-       int port = MINOR(tty->device) - serial->minor;
+       int portNumber = MINOR(tty->device) - serial->minor;
+       struct usb_serial_port *port = &serial->port[portNumber];
 
-       dbg("whiteheat_serial_open port %d", port);
+       dbg("whiteheat_serial_open port %d", portNumber);
 
-       if (serial->active[port]) {
+       if (port->active) {
                dbg ("device already open");
                return -EINVAL;
        }
-       serial->active[port] = 1;
+       port->active = 1;
  
        /*Start reading from the device*/
-       if (usb_submit_urb(&serial->read_urb[port]))
+       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.) */
@@ -653,17 +687,18 @@
 static void whiteheat_serial_close(struct tty_struct *tty, struct file * filp)
 {
        struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-       int port = MINOR(tty->device) - serial->minor;
+       int portNumber = MINOR(tty->device) - serial->minor;
+       struct usb_serial_port *port = &serial->port[portNumber];
 
-       dbg("whiteheat_serial_close port %d", port);
+       dbg("whiteheat_serial_close port %d", portNumber);
        
        /* Need to change the control lines here */
        /* FIXME */
        
        /* shutdown our bulk reads and writes */
-       usb_unlink_urb (&serial->write_urb[port]);
-       usb_unlink_urb (&serial->read_urb[port]);
-       serial->active[port] = 0;
+       usb_unlink_urb (&port->write_urb);
+       usb_unlink_urb (&port->read_urb);
+       port->active = 0;
 }
 
 
@@ -798,19 +833,20 @@
 static int visor_serial_open (struct tty_struct *tty, struct file *filp)
 {
        struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
-       int port = MINOR(tty->device) - serial->minor;
+       int portNumber = MINOR(tty->device) - serial->minor;
+       struct usb_serial_port *port = &serial->port[portNumber];
 
-       dbg("visor_serial_open port %d", port);
+       dbg("visor_serial_open port %d", portNumber);
 
-       if (serial->active[port]) {
+       if (port->active) {
                dbg ("device already open");
                return -EINVAL;
        }
 
-       serial->active[port] = 1;
+       port->active = 1;
 
        /*Start reading from the device*/
-       if (usb_submit_urb(&serial->read_urb[port]))
+       if (usb_submit_urb(&port->read_urb))
                dbg("usb_submit_urb(read bulk) failed");
 
        return (0);
@@ -819,10 +855,11 @@
 static void visor_serial_close(struct tty_struct *tty, struct file * filp)
 {
        struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
-       int port = MINOR(tty->device) - serial->minor;
+       int portNumber = MINOR(tty->device) - serial->minor;
+       struct usb_serial_port *port = &serial->port[portNumber];
        unsigned char *transfer_buffer =  kmalloc (0x12, GFP_KERNEL);
        
-       dbg("visor_serial_close port %d", port);
+       dbg("visor_serial_close port %d", portNumber);
                         
        if (!transfer_buffer) {
                err("visor_serial_close: kmalloc(%d) failed.", 0x12);
@@ -833,9 +870,9 @@
        }
 
        /* shutdown our bulk reads and writes */
-       usb_unlink_urb (&serial->write_urb[port]);
-       usb_unlink_urb (&serial->read_urb[port]);
-       serial->active[port] = 0;
+       usb_unlink_urb (&port->write_urb);
+       usb_unlink_urb (&port->read_urb);
+       port->active = 0;
 }
 
 
@@ -846,7 +883,7 @@
 
        dbg("visor_throttle port %d", port);
 
-       usb_unlink_urb (&serial->read_urb[port]);
+       usb_unlink_urb (&serial->port[port].read_urb);
 
        return;
 }
@@ -859,7 +896,7 @@
 
        dbg("visor_unthrottle port %d", port);
 
-       if (usb_unlink_urb (&serial->read_urb[port]))
+       if (usb_unlink_urb (&serial->port[port].read_urb))
                dbg("usb_submit_urb(read bulk) failed");
 
        return;
@@ -947,16 +984,17 @@
 static int  ftdi_sio_serial_open (struct tty_struct *tty, 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];
        char buf[1]; /* Needed for the usb_control_msg I think */
-       int port = MINOR(tty->device) - serial->minor;
 
-       dbg("ftdi_sio_serial_open port %d", port);
+       dbg("ftdi_sio_serial_open port %d", portNumber);
 
-       if (serial->active[port]) {
+       if (port->active) {
                dbg ("device already open");
                return -EINVAL;
        }
-       serial->active[port] = 1;
+       port->active = 1;
 
        usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
                        FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE, 
@@ -1024,10 +1062,9 @@
        }
        
        /*Start reading from the device*/
-       if (usb_submit_urb(&serial->read_urb[port]))
+       if (usb_submit_urb(&port->read_urb))
                dbg("usb_submit_urb(read bulk) failed");
 
-
        return (0);
 }
 
@@ -1035,10 +1072,11 @@
 static void ftdi_sio_serial_close (struct tty_struct *tty, 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];
        char buf[1];
-       int port = MINOR(tty->device) - serial->minor;
 
-       dbg("ftdi_sio_serial_close port %d", port);
+       dbg("ftdi_sio_serial_close port %d", portNumber);
        
        /* FIXME - might be able to do both simultaneously */
        if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
@@ -1059,9 +1097,9 @@
        /* FIXME Should I flush the device here? - not doing it for now */
 
        /* shutdown our bulk reads and writes */
-       usb_unlink_urb (&serial->write_urb[port]);
-       usb_unlink_urb (&serial->read_urb[port]);
-       serial->active[port] = 0;
+       usb_unlink_urb (&port->write_urb);
+       usb_unlink_urb (&port->read_urb);
+       port->active = 0;
 }
 
 
@@ -1075,42 +1113,44 @@
                                  const unsigned char *buf, int count)
 {
        struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-       int port = MINOR(tty->device) - serial->minor;
+       int portNumber = MINOR(tty->device) - serial->minor;
+       struct usb_serial_port *port = &serial->port[portNumber];
        const int data_offset = 1;
 
-       dbg("ftdi_sio_serial_write port %d, %d bytes", port, count);
+       dbg("ftdi_sio_serial_write port %d, %d bytes", portNumber, count);
 
        if (count == 0) {
                dbg("write request of 0 bytes");
-               return (0);
+               return 0;
        }
 
        /* only do something if we have a bulk out endpoint */
        if (serial->num_bulk_out) {
-               unsigned char *first_byte = serial->write_urb[port].transfer_buffer;
+               unsigned char *first_byte = port->write_urb.transfer_buffer;
 
-               if (serial->write_urb[port].status == -EINPROGRESS) {
+               if (port->write_urb.status == -EINPROGRESS) {
                        dbg ("already writing");
-                       return (0);
+                       return 0;
                }
 
                count += data_offset;
-               count = (count > serial->bulk_out_size[port]) ? 
-                       serial->bulk_out_size[port] : count;
-
+               count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
+               if (count == 0) {
+                       return 0;
+               }
 
                /* Copy in the data to send */
                if (from_user) {
-                       copy_from_user(serial->write_urb[port].transfer_buffer + 
data_offset , 
+                       copy_from_user(port->write_urb.transfer_buffer + data_offset , 
                                       buf, count - data_offset );
                }
                else {
-                       memcpy(serial->write_urb[port].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 = serial->write_urb[port].transfer_buffer;
+               first_byte = port->write_urb.transfer_buffer;
                *first_byte = 1 | ((count-data_offset) << 2) ; 
 
 #ifdef DEBUG
@@ -1133,20 +1173,18 @@
                }
 
 #endif
-
-
                /* send the data out the bulk port */
-               serial->write_urb[port].transfer_buffer_length = count;
+               port->write_urb.transfer_buffer_length = count;
 
-               if (usb_submit_urb(&serial->write_urb[port]))
+               if (usb_submit_urb(&port->write_urb))
                        dbg("usb_submit_urb(write bulk) failed");
 
-               dbg("write returning: %d",count - data_offset);
+               dbg("write returning: %d", count - data_offset);
                return (count - data_offset);
        }
        
        /* no bulk out, so return 0 bytes written */
-       return (0);
+       return 0;
 } 
 
 
@@ -1195,6 +1233,7 @@
        return;
 } /* ftdi_sio_serial_read_bulk_callback */
 
+
 static void ftdi_sio_set_termios (struct tty_struct *tty, struct termios *old_termios)
 {
        struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
@@ -1264,6 +1303,7 @@
        return;
 }
 
+
 /*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)
 {
@@ -1358,57 +1398,597 @@
 /*****************************************************************************
  * Keyspan PDA specific driver functions
  *****************************************************************************/
-static int keyspan_pda_serial_open (struct tty_struct *tty, struct file *filp)
+
+static void keyspan_pda_rx_interrupt (struct urb *urb)
+{
+       struct usb_serial *serial = (struct usb_serial *) urb->context;
+       struct tty_struct *tty = serial->tty;
+       unsigned char *data = urb->transfer_buffer;
+       int i;
+
+       /* the urb might have been killed. */
+       if (urb->status)
+               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) {
+                       for (i = 1; i < urb->actual_length ; ++i) {
+                               tty_insert_flip_char(tty, data[i], 0);
+                       }
+                       tty_flip_buffer_push(tty);
+               }
+               break;
+       case 1:
+               /* status interrupt */
+               dbg(" rx int, d1=%d, d2=%d", data[1], data[2]);
+               switch (data[1]) {
+               case 1: /* modemline change */
+                       break;
+               case 2: /* tx unthrottle interrupt */
+                       serial->tx_throttled = 0;
+                       wake_up(&serial->write_wait); /* wake up writer */
+                       break;
+               default:
+                       break;
+               }
+               break;
+       default:
+               break;
+       }
+
+       /* INT urbs are automatically re-submitted */
+}
+
+
+static void keyspan_pda_rx_throttle (struct tty_struct *tty)
 {
        struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
        int port = MINOR(tty->device) - serial->minor;
 
-       dbg("keyspan_pda_serial_open port %d", port);
+       /* 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
+          fills up. If we're doing XON/XOFF, this would be a good time to
+          send an XOFF, although it might make sense to foist that off
+          upon the device too. */
 
-       if (serial->active[port]) {
-               dbg ("device already open");
+       dbg("keyspan_pda_rx_throttle port %d", port);
+       usb_unlink_urb(&serial->port[port].read_urb);
+}
+
+
+static void keyspan_pda_rx_unthrottle (struct tty_struct *tty)
+{
+       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(" usb_submit_urb(read urb) failed");
+       return;
+}
+
+
+static int keyspan_pda_setbaud (struct usb_serial *serial, int baud)
+{
+       int rc;
+       int bindex;
+
+       switch(baud) {
+               case 110: bindex = 0; break;
+               case 300: bindex = 1; break;
+               case 1200: bindex = 2; break;
+               case 2400: bindex = 3; break;
+               case 4800: bindex = 4; break;
+               case 9600: bindex = 5; break;
+               case 19200: bindex = 6; break;
+               case 38400: bindex = 7; break;
+               case 57600: bindex = 8; break;
+               case 115200: bindex = 9; break;
+               default: return -EINVAL;
+       }
+
+       /* rather than figure out how to sleep while waiting for this
+          to complete, I just use the "legacy" API. */
+       rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+                            0, /* set baud */
+                            USB_TYPE_VENDOR 
+                            | USB_RECIP_INTERFACE
+                            | USB_DIR_OUT, /* type */
+                            bindex, /* value */
+                            0, /* index */
+                            NULL, /* &data */
+                            0, /* size */
+                            2*HZ); /* timeout */
+       return(rc);
+}
+
+
+static void keyspan_pda_break_ctl (struct tty_struct *tty, int break_state)
+{
+       struct usb_serial *serial = (struct usb_serial *) tty->driver_data;
+       int value;
+       if (break_state == -1)
+               value = 1; /* start break */
+       else
+               value = 0; /* clear break */
+       usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+                       4, /* set break */
+                       USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+                       value, 0, NULL, 0, 2*HZ);
+       /* there is something funky about this.. the TCSBRK that 'cu' performs
+          ought to translate into a break_ctl(-1),break_ctl(0) pair HZ/4
+          seconds apart, but it feels like the break sent isn't as long as it
+          is on /dev/ttyS0 */
+}
+
+
+static void keyspan_pda_set_termios (struct tty_struct *tty,
+                                    struct termios *old_termios)
+{
+       struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
+       unsigned int cflag = 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?:
+          CSTOPB (1 stop bit or 2)
+          PARENB (parity)
+          CSIZE (5bit .. 8bit)
+          There is minimal hw support for parity (a PSW bit seems to hold the
+          parity of whatever is in the accumulator). The UART either deals
+          with 10 bits (start, 8 data, stop) or 11 bits (start, 8 data,
+          1 special, stop). So, with firmware changes, we could do:
+          8N1: 10 bit
+          8N2: 11 bit, extra bit always (mark?)
+          8[EOMS]1: 11 bit, extra bit is parity
+          7[EOMS]1: 10 bit, b0/b7 is parity
+          7[EOMS]2: 11 bit, b0/b7 is parity, extra bit always (mark?)
+
+          HW flow control is dictated by the tty->termios->c_cflags & CRTSCTS
+          bit.
+
+          For now, just do baud. */
+
+       switch (cflag & CBAUD) {
+               /* we could support more values here, just need to calculate
+                  the necessary divisors in the firmware. <asm/termbits.h>
+                  has the Bnnn constants. */
+               case B110: keyspan_pda_setbaud(serial, 110); break;
+               case B300: keyspan_pda_setbaud(serial, 300); break;
+               case B1200: keyspan_pda_setbaud(serial, 1200); break;
+               case B2400: keyspan_pda_setbaud(serial, 2400); break;
+               case B4800: keyspan_pda_setbaud(serial, 4800); break;
+               case B9600: keyspan_pda_setbaud(serial, 9600); break;
+               case B19200: keyspan_pda_setbaud(serial, 19200); break;
+               case B38400: keyspan_pda_setbaud(serial, 38400); break;
+               case B57600: keyspan_pda_setbaud(serial, 57600); break;
+               case B115200: keyspan_pda_setbaud(serial, 115200); break;
+               default: dbg("can't handle requested baud rate"); break;
+       }
+}
+
+
+/* modem control pins: DTR and RTS are outputs and can be controlled.
+   DCD, RI, DSR, CTS are inputs and can be read. All outputs can also be
+   read. The byte passed is: DTR(b7) DCD RI DSR CTS RTS(b2) unused unused */
+
+static int keyspan_pda_get_modem_info(struct usb_serial *serial,
+                                     unsigned char *value)
+{
+       int rc;
+       unsigned char data;
+       rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+                            3, /* get pins */
+                            USB_TYPE_VENDOR|USB_RECIP_INTERFACE|USB_DIR_IN,
+                            0, 0, &data, 1, 2*HZ);
+       if (rc > 0)
+               *value = data;
+       return rc;
+}
+
+
+static int keyspan_pda_set_modem_info(struct usb_serial *serial,
+                                     unsigned char value)
+{
+       int rc;
+       rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+                            3, /* set pins */
+                            USB_TYPE_VENDOR|USB_RECIP_INTERFACE|USB_DIR_OUT,
+                            value, 0, NULL, 0, 2*HZ);
+       return rc;
+}
+
+
+static int keyspan_pda_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 rc;
+       unsigned int value;
+       unsigned char status, mask;
+
+       switch (cmd) {
+       case TIOCMGET: /* get modem pins state */
+               rc = keyspan_pda_get_modem_info(serial, &status);
+               if (rc < 0)
+                       return rc;
+               value =
+                       ((status & (1<<7)) ? TIOCM_DTR : 0) |
+                       ((status & (1<<6)) ? TIOCM_CAR : 0) |
+                       ((status & (1<<5)) ? TIOCM_RNG : 0) |
+                       ((status & (1<<4)) ? TIOCM_DSR : 0) |
+                       ((status & (1<<3)) ? TIOCM_CTS : 0) |
+                       ((status & (1<<2)) ? TIOCM_RTS : 0);
+               if (copy_to_user((unsigned int *)arg, &value, sizeof(int)))
+                       return -EFAULT;
+               return 0;
+       case TIOCMSET: /* set a state as returned by MGET */
+               if (copy_from_user(&value, (unsigned int *)arg, sizeof(int)))
+                       return -EFAULT;
+               status =
+                       ((value & TIOCM_DTR) ? (1<<7) : 0) |
+                       ((value & TIOCM_CAR) ? (1<<6) : 0) |
+                       ((value & TIOCM_RNG) ? (1<<5) : 0) |
+                       ((value & TIOCM_DSR) ? (1<<4) : 0) |
+                       ((value & TIOCM_CTS) ? (1<<3) : 0) |
+                       ((value & TIOCM_RTS) ? (1<<2) : 0);
+               rc = keyspan_pda_set_modem_info(serial, status);
+               if (rc < 0)
+                       return rc;
+               return 0;
+       case TIOCMBIS: /* set bits in bitmask <arg> */
+       case TIOCMBIC: /* clear bits from bitmask <arg> */
+               if (copy_from_user(&value, (unsigned int *)arg, sizeof(int)))
+                       return -EFAULT;
+               rc = keyspan_pda_get_modem_info(serial, &status);
+               if (rc < 0)
+                       return rc;
+               mask =
+                       ((value & TIOCM_RTS) ? (1<<2) : 0) |
+                       ((value & TIOCM_DTR) ? (1<<7) : 0);
+               if (cmd == TIOCMBIS)
+                       status |= mask;
+               else
+                       status &= ~mask;
+               rc = keyspan_pda_set_modem_info(serial, status);
+               if (rc < 0)
+                       return rc;
+               return 0;
+       case TIOCMIWAIT:
+               /* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/
+               /* TODO */
+       case TIOCGICOUNT:
+               /* return count of modemline transitions */
+               return 0; /* TODO */
+       }
+       
+       return -ENOIOCTLCMD;
+}
+
+static int keyspan_pda_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;
+       int request_unthrottle = 0;
+       int rc = 0;
+       DECLARE_WAITQUEUE(wait, current);
+
+       /* guess how much room is left in the device's ring buffer, and if we
+          want to send more than that, check first, updating our notion of
+          what is left. If our write will result in no room left, ask the
+          device to give us an interrupt when the room available rises above
+          a threshold, and hold off all writers (eventually, those using
+          select() or poll() too) until we receive that unthrottle interrupt.
+          Block if we can't write anything at all, otherwise write as much as
+          we can. */
+
+       if (count == 0) {
+               dbg(" write request of 0 bytes");
+               return (0);
+       }
+
+       /* we might block because of:
+          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) {
+               if (0 /* file->f_flags & O_NONBLOCK */) {
+                       rc = -EAGAIN;
+                       goto err;
+               }
+               interruptible_sleep_on(&serial->write_wait);
+               if (signal_pending(current)) {
+                       rc = -ERESTARTSYS;
+                       goto err;
+               }
+       }
+
+       /* at this point the URB is in our control, nobody else can submit it
+          again (the only sudden transition was the one from EINPROGRESS to
+          finished) */
+
+       /* the next potential block is that our TX process might be throttled.
+          The transition from throttled->not happens because of an Rx
+          interrupt, and the wake_up occurs during the same interrupt, so we
+          have to be careful to avoid a race that would cause us to sleep
+          forever. */
+
+       add_wait_queue(&serial->write_wait, &wait);
+       set_current_state(TASK_INTERRUPTIBLE);
+       while (serial->tx_throttled) {
+               /* device can't accomodate any more characters. Sleep until it
+                  can. Woken up by an Rx interrupt message, which clears
+                  tx_throttled first. */
+               dbg(" tx_throttled, going to sleep");
+               if (signal_pending(current)) {
+                       current->state = TASK_RUNNING;
+                       remove_wait_queue(&serial->write_wait, &wait);
+                       dbg(" woke up because of signal");
+                       rc = -ERESTARTSYS;
+                       goto err;
+               }
+               schedule();
+               dbg(" woke up");
+       }
+       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;
+       if (count > serial->tx_room) {
+               unsigned char room;
+               /* Looks like we might overrun the Tx buffer. Ask the device
+                  how much room it really has */
+               rc = usb_control_msg(serial->dev, 
+                                    usb_rcvctrlpipe(serial->dev, 0),
+                                    6, /* write_room */
+                                    USB_TYPE_VENDOR | USB_RECIP_INTERFACE
+                                    | USB_DIR_IN,
+                                    0, /* value: 0 means "remaining room" */
+                                    0, /* index */
+                                    &room,
+                                    1,
+                                    2*HZ);
+               if (rc < 0) {
+                       dbg(" roomquery failed");
+                       return rc; /* failed */
+               }
+               if (rc == 0) {
+                       dbg(" roomquery returned 0 bytes");
+                       return -EIO; /* device didn't return any data */
+               }
+               dbg(" roomquery says %d", room);
+               serial->tx_room = room;
+               if (count > serial->tx_room) {
+                       /* we're about to completely fill the Tx buffer, so
+                          we'll be throttled afterwards. */
+                       count = serial->tx_room;
+                       request_unthrottle = 1;
+               }
+       }
+       serial->tx_room -= count;
+
+       if (count) {
+               /* now transfer data */
+               if (from_user) {
+                       copy_from_user(serial->port[port].write_urb.transfer_buffer,
+                                      buf, count);
+               }
+               else {
+                       memcpy (serial->port[port].write_urb.transfer_buffer, 
+                               buf, count);
+               }  
+               /* send the data out the bulk port */
+               serial->port[port].write_urb.transfer_buffer_length = count;
+               
+               if (usb_submit_urb(&serial->port[port].write_urb))
+                       dbg(" usb_submit_urb(write bulk) failed");
+       }
+       else {
+               /* There wasn't any room left, so we are throttled until
+                  the buffer empties a bit */
+               request_unthrottle = 1;
+       }
+
+       if (request_unthrottle) {
+               dbg(" request_unthrottle");
+               /* ask the device to tell us when the tx buffer becomes
+                  sufficiently empty */
+               serial->tx_throttled = 1; /* block writers */
+               rc = usb_control_msg(serial->dev, 
+                                    usb_sndctrlpipe(serial->dev, 0),
+                                    7, /* request_unthrottle */
+                                    USB_TYPE_VENDOR | USB_RECIP_INTERFACE
+                                    | USB_DIR_OUT,
+                                    16, /* value: threshold */
+                                    0, /* index */
+                                    NULL,
+                                    0,
+                                    2*HZ);
+       }
+
+       return (count);
+ err:
+       return (rc);
+}
+
+
+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; 
+
+       wake_up_interruptible(&serial->write_wait);
+
+       if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && 
+           tty->ldisc.write_wakeup)
+               (tty->ldisc.write_wakeup)(tty);
+
+       wake_up_interruptible(&tty->write_wait);
+}
+
+
+static int keyspan_pda_write_room (struct tty_struct *tty)
+{
+       struct usb_serial *serial = (struct usb_serial *)tty->driver_data; 
+
+       /* used by n_tty.c for processing of tabs and such. Giving it our
+          conservative guess is probably good enough, but needs testing by
+          running a console through the device. */
+
+       return (serial->tx_room);
+}
+
+
+static int keyspan_pda_chars_in_buffer (struct tty_struct *tty) 
+{
+       struct usb_serial *serial = (struct usb_serial *)tty->driver_data; 
+       unsigned char count;
+       int rc;
+
+       /* used by tty stuff to wait for output to drain. Go ask the
+          device how much is still queued in the tx ring */
+       rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+                            6, /* write_room */
+                            USB_TYPE_VENDOR | USB_RECIP_INTERFACE
+                            | USB_DIR_IN,
+                            1, /* value: 1 means chars_in_buffer */
+                            0, /* index */
+                            &count,
+                            1,
+                            2*HZ);
+       if (rc < 0)
+               return rc; /* failed */
+       if (rc == 0)
+               return -EIO; /* device didn't return any data */
+       return (count);
+}
+
+
+static int keyspan_pda_serial_open (struct tty_struct *tty, 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];
+       unsigned char room;
+       int rc;
+
+       if (port->active) {
                return -EINVAL;
        }
-       serial->active[port] = 1;
+       port->active = 1;
  
-       /*Start reading from the device*/
-       if (usb_submit_urb(&serial->read_urb[port]))
-               dbg("usb_submit_urb(read bulk) failed");
+       /* find out how much room is in the Tx ring */
+       rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+                            6, /* write_room */
+                            USB_TYPE_VENDOR | USB_RECIP_INTERFACE
+                            | USB_DIR_IN,
+                            0, /* value */
+                            0, /* index */
+                            &room,
+                            1,
+                            2*HZ);
+       if (rc < 0) {
+               dbg(" roomquery failed");
+               return rc; /* failed */
+       }
+       if (rc == 0) {
+               dbg(" roomquery returned 0 bytes");
+               return -EIO; /* device didn't return any data */
+       }
+       serial->tx_room = room;
+       serial->tx_throttled = room ? 0 : 1;
+
+       /* the normal serial device seems to always turn on DTR and RTS here,
+          so do the same */
+       if (tty->termios->c_cflag & CBAUD)
+               keyspan_pda_set_modem_info(serial, (1<<7) | (1<<2) );
+       else
+               keyspan_pda_set_modem_info(serial, 0);
 
-       /* Need to do device specific setup here (control lines, baud rate, etc.) */
-       /* FIXME */
+       /*Start reading from the device*/
+       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_serial_close(struct tty_struct *tty, 
+                                    struct file *filp)
 {
        struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-       int port = MINOR(tty->device) - serial->minor;
+       int portNumber = MINOR(tty->device) - serial->minor;
+       struct usb_serial_port *port = &serial->port[portNumber];
+
+       /* the normal serial device seems to always shut off DTR and RTS now */
+       if (tty->termios->c_cflag & HUPCL)
+               keyspan_pda_set_modem_info(serial, 0);
 
-       dbg("keyspan_pda_serial_close port %d", port);
-       
-       /* Need to change the control lines here */
-       /* FIXME */
-       
        /* shutdown our bulk reads and writes */
-       usb_unlink_urb (&serial->write_urb[port]);
-       usb_unlink_urb (&serial->read_urb[port]);
-       serial->active[port] = 0;
+       usb_unlink_urb (&port->write_urb);
+       usb_unlink_urb (&port->read_urb);
+       port->active = 0;
 }
 
 
-static int  keyspan_pda_startup (struct usb_serial *serial)
+/* download the firmware to a "fake" device (pre-renumeration) */
+static int keyspan_pda_fake_startup (struct usb_serial *serial)
 {
-       dbg("keyspan_pda_startup");
+       int response;
+       const struct ezusb_hex_record *record;
 
        /* download the firmware here ... */
-       /* FIXME */
+       response = ezusb_set_reset(serial, 1);
+
+       record = &keyspan_pda_firmware[0];
+       while(record->address != 0xffff) {
+               response = ezusb_writememory(serial, record->address,
+                                            (unsigned char *)record->data,
+                                            record->data_size, 0xa0);
+               if (response < 0) {
+                       err("ezusb_writememory failed for Keyspan PDA "
+                           "firmware (%d %04X %p %d)",
+                           response, 
+                           record->address, record->data, record->data_size);
+                       break;
+               }
+               record++;
+       }
+       /* bring device out of reset. Renumeration will occur in a moment
+          and the new device will bind to the real driver */
+       response = ezusb_set_reset(serial, 0);
 
        /* we want this device to fail to have a driver assigned to it. */
        return (1);
 }
+
+
+/* do some startup allocations not currently performed by usb_serial_probe() */
+static int keyspan_pda_startup (struct usb_serial *serial)
+{
+       struct usb_endpoint_descriptor *intin;
+       intin = serial->port[0].interrupt_in_endpoint;
+
+       /* set up the receive interrupt urb */
+       FILL_INT_URB(&serial->port[0].read_urb, serial->dev,
+                    usb_rcvintpipe(serial->dev, intin->bEndpointAddress),
+                    serial->port[0].interrupt_in_buffer,
+                    intin->wMaxPacketSize,
+                    keyspan_pda_rx_interrupt,
+                    serial,
+                    intin->bInterval);
+
+       init_waitqueue_head(&serial->write_wait);
+       
+       return (0);
+}
+
 #endif /* CONFIG_USB_SERIAL_KEYSPAN_PDA */
 
 
@@ -1418,20 +1998,21 @@
 static int generic_serial_open (struct tty_struct *tty, struct file *filp)
 {
        struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-       int port = MINOR(tty->device) - serial->minor;
+       int portNumber = MINOR(tty->device) - serial->minor;
+       struct usb_serial_port *port = &serial->port[portNumber];
 
-       dbg("generic_serial_open port %d", port);
+       dbg("generic_serial_open port %d", portNumber);
 
-       if (serial->active[port]) {
+       if (port->active) {
                dbg ("device already open");
                return -EINVAL;
        }
-       serial->active[port] = 1;
+       port->active = 1;
  
        /* if we have a bulk interrupt, start reading from it */
        if (serial->num_bulk_in) {
                /*Start reading from the device*/
-               if (usb_submit_urb(&serial->read_urb[port]))
+               if (usb_submit_urb(&port->read_urb))
                        dbg("usb_submit_urb(read bulk) failed");
        }
 
@@ -1442,28 +2023,30 @@
 static void generic_serial_close(struct tty_struct *tty, struct file * filp)
 {
        struct usb_serial *serial = (struct usb_serial *) tty->driver_data; 
-       int port = MINOR(tty->device) - serial->minor;
+       int portNumber = MINOR(tty->device) - serial->minor;
+       struct usb_serial_port *port = &serial->port[portNumber];
 
-       dbg("generic_serial_close port %d", port);
+       dbg("generic_serial_close port %d", portNumber);
        
        /* shutdown any bulk reads that might be going on */
        if (serial->num_bulk_out) {
-               usb_unlink_urb (&serial->write_urb[port]);
+               usb_unlink_urb (&port->write_urb);
        }
        if (serial->num_bulk_in) {
-               usb_unlink_urb (&serial->read_urb[port]);
+               usb_unlink_urb (&port->read_urb);
        }
 
-       serial->active[port] = 0;
+       port->active = 0;
 }
 
 
 static int generic_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;
+       int portNumber = MINOR(tty->device) - serial->minor;
+       struct usb_serial_port *port = &serial->port[portNumber];
 
-       dbg("generic_serial_write port %d", port);
+       dbg("generic_serial_write port %d", portNumber);
 
        if (count == 0) {
                dbg("write request of 0 bytes");
@@ -1472,24 +2055,24 @@
 
        /* only do something if we have a bulk out endpoint */
        if (serial->num_bulk_out) {
-               if (serial->write_urb[port].status == -EINPROGRESS) {
+               if (port->write_urb.status == -EINPROGRESS) {
                        dbg ("already writing");
                        return (0);
                }
 
-               count = (count > serial->bulk_out_size[port]) ? 
serial->bulk_out_size[port] : count;
+               count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
 
                if (from_user) {
-                       copy_from_user(serial->write_urb[port].transfer_buffer, buf, 
count);
+                       copy_from_user(port->write_urb.transfer_buffer, buf, count);
                }
                else {
-                       memcpy (serial->write_urb[port].transfer_buffer, buf, count);
+                       memcpy (port->write_urb.transfer_buffer, buf, count);
                }  
 
                /* send the data out the bulk port */
-               serial->write_urb[port].transfer_buffer_length = count;
+               port->write_urb.transfer_buffer_length = count;
 
-               if (usb_submit_urb(&serial->write_urb[port]))
+               if (usb_submit_urb(&port->write_urb))
                        dbg("usb_submit_urb(write bulk) failed");
 
                return (count);
@@ -1503,16 +2086,17 @@
 static int generic_write_room (struct tty_struct *tty) 
 {
        struct usb_serial *serial = (struct usb_serial *)tty->driver_data; 
-       int port = MINOR(tty->device) - serial->minor;
+       int portNumber = MINOR(tty->device) - serial->minor;
+       struct usb_serial_port *port = &serial->port[portNumber];
        int room;
 
-       dbg("generic_write_room port %d", port);
+       dbg("generic_write_room port %d", portNumber);
        
        if (serial->num_bulk_out) {
-               if (serial->write_urb[port].status == -EINPROGRESS)
+               if (port->write_urb.status == -EINPROGRESS)
                        room = 0;
                else
-                       room = serial->bulk_out_size[port];
+                       room = port->bulk_out_size;
                dbg("generic_write_room returns %d", room);
                return (room);
        }
@@ -1524,13 +2108,14 @@
 static int generic_chars_in_buffer (struct tty_struct *tty) 
 {
        struct usb_serial *serial = (struct usb_serial *)tty->driver_data; 
-       int port = MINOR(tty->device) - serial->minor;
+       int portNumber = MINOR(tty->device) - serial->minor;
+       struct usb_serial_port *port = &serial->port[portNumber];
 
-       dbg("generic_chars_in_buffer port %d", port);
+       dbg("generic_chars_in_buffer port %d", portNumber);
        
        if (serial->num_bulk_out) {
-               if (serial->write_urb[port].status == -EINPROGRESS) {
-                       return (serial->bulk_out_size[port]);
+               if (port->write_urb.status == -EINPROGRESS) {
+                       return (port->bulk_out_size);
                }
        }
 
@@ -1630,6 +2215,7 @@
 static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum)
 {
        struct usb_serial *serial = NULL;
+       struct usb_serial_port *port;
        struct usb_interface_descriptor *interface;
        struct usb_endpoint_descriptor *endpoint;
        struct usb_endpoint_descriptor *interrupt_in_endpoint[MAX_NUM_PORTS];
@@ -1725,6 +2311,20 @@
                                serial->num_bulk_out = num_bulk_out;
                                serial->num_interrupt_in = num_interrupt_in;
 
+                               /* collect interrupt_in endpoints now, because
+                                  the keyspan_pda startup function needs
+                                  to know about them */
+                               for (i = 0; i < num_interrupt_in; ++i) {
+                                       port = &serial->port[i];
+                                       buffer_size = 
+interrupt_in_endpoint[i]->wMaxPacketSize;
+                                       port->interrupt_in_buffer = kmalloc 
+(buffer_size, GFP_KERNEL);
+                                       if (!port->interrupt_in_buffer) {
+                                               err("Couldn't allocate 
+interrupt_in_buffer");
+                                               goto probe_error;
+                                       }
+                                       port->interrupt_in_endpoint = 
+interrupt_in_endpoint[i];
+                               }
+
                                /* if this device type has a startup function, call it 
*/
                                if (type->startup) {
                                        if (type->startup (serial)) {
@@ -1735,34 +2335,36 @@
 
                                /* set up the endpoint information */
                                for (i = 0; i < num_bulk_in; ++i) {
+                                       port = &serial->port[i];
                                        buffer_size = 
bulk_in_endpoint[i]->wMaxPacketSize;
-                                       serial->bulk_in_buffer[i] = kmalloc 
(buffer_size, GFP_KERNEL);
-                                       if (!serial->bulk_in_buffer[i]) {
+                                       port->bulk_in_buffer = kmalloc (buffer_size, 
+GFP_KERNEL);
+                                       if (!port->bulk_in_buffer) {
                                                err("Couldn't allocate 
bulk_in_buffer");
                                                goto probe_error;
                                        }
                                        if (serial->type->read_bulk_callback) {
-                                               FILL_BULK_URB(&serial->read_urb[i], 
dev, usb_rcvbulkpipe (dev, bulk_in_endpoint[i]->bEndpointAddress),
-                                                               
serial->bulk_in_buffer[i], 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, serial);
                                        } else {
-                                               FILL_BULK_URB(&serial->read_urb[i], 
dev, usb_rcvbulkpipe (dev, bulk_in_endpoint[i]->bEndpointAddress),
-                                                               
serial->bulk_in_buffer[i], 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, serial);
                                        }
                                }
 
                                for (i = 0; i < num_bulk_out; ++i) {
-                                       serial->bulk_out_size[i] = 
bulk_out_endpoint[i]->wMaxPacketSize;
-                                       serial->bulk_out_buffer[i] = kmalloc 
(serial->bulk_out_size[i], GFP_KERNEL);
-                                       if (!serial->bulk_out_buffer[i]) {
+                                       port = &serial->port[i];
+                                       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) {
                                                err("Couldn't allocate 
bulk_out_buffer");
                                                goto probe_error;
                                        }
                                        if (serial->type->write_bulk_callback) {
-                                               FILL_BULK_URB(&serial->write_urb[i], 
dev, usb_sndbulkpipe (dev, bulk_out_endpoint[i]->bEndpointAddress),
-                                                               
serial->bulk_out_buffer[i], serial->bulk_out_size[i], 
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, serial);
                                        } else {
-                                               FILL_BULK_URB(&serial->write_urb[i], 
dev, usb_sndbulkpipe (dev, bulk_out_endpoint[i]->bEndpointAddress),
-                                                               
serial->bulk_out_buffer[i], serial->bulk_out_size[i], 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, serial);
                                        }
                                }
 
@@ -1799,14 +2401,20 @@
 probe_error:
        if (serial) {
                for (i = 0; i < num_bulk_in; ++i)
-                       if (serial->bulk_in_buffer[i])
-                               kfree (serial->bulk_in_buffer[i]);
+                       if (serial->port[i].bulk_in_buffer[i])
+                               kfree (serial->port[i].bulk_in_buffer);
                for (i = 0; i < num_bulk_out; ++i)
-                       if (serial->bulk_out_buffer[i])
-                               kfree (serial->bulk_out_buffer[i]);
+                       if (serial->port[i].bulk_out_buffer)
+                               kfree (serial->port[i].bulk_out_buffer);
                for (i = 0; i < num_interrupt_in; ++i)
-                       if (serial->interrupt_in_buffer[i])
-                               kfree (serial->interrupt_in_buffer[i]);
+                       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);
+
+               /* free up any memory that we allocated */
+               kfree (serial);
        }
        return NULL;
 }
@@ -1815,26 +2423,34 @@
 static void usb_serial_disconnect(struct usb_device *dev, void *ptr)
 {
        struct usb_serial *serial = (struct usb_serial *) ptr;
+       struct usb_serial_port *port;
        int i;
 
        if (serial) {
                /* need to stop any transfers...*/
                for (i = 0; i < serial->num_ports; ++i) {
-                       usb_unlink_urb (&serial->write_urb[i]);
-                       usb_unlink_urb (&serial->read_urb[i]);
-                       serial->active[i] = 0;
+                       port = &serial->port[i];
+                       usb_unlink_urb (&port->write_urb);
+                       usb_unlink_urb (&port->read_urb);
+                       port->active = 0;
                }
 
                /* free up any memory that we allocated */
-               for (i = 0; i < serial->num_bulk_in; ++i)
-                       if (serial->bulk_in_buffer[i])
-                               kfree (serial->bulk_in_buffer[i]);
-               for (i = 0; i < serial->num_bulk_out; ++i)
-                       if (serial->bulk_out_buffer[i])
-                               kfree (serial->bulk_out_buffer[i]);
-               for (i = 0; i < serial->num_interrupt_in; ++i)
-                       if (serial->interrupt_in_buffer[i])
-                               kfree (serial->interrupt_in_buffer[i]);
+               for (i = 0; i < serial->num_bulk_in; ++i) {
+                       port = &serial->port[i];
+                       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->bulk_out_buffer)
+                               kfree (port->bulk_out_buffer);
+               }
+               for (i = 0; i < serial->num_interrupt_in; ++i) {
+                       port = &serial->port[i];
+                       if (port->interrupt_in_buffer)
+                               kfree (port->interrupt_in_buffer);
+               }
 
                for (i = 0; i < serial->num_ports; ++i) {
                        info("%s converter now disconnected from ttyUSB%d", 
serial->type->name, serial->minor + i);
@@ -1885,7 +2501,7 @@
        stop:                   NULL,
        start:                  NULL,
        hangup:                 NULL,
-       break_ctl:              NULL,
+       break_ctl:              serial_break,
        wait_until_sent:        NULL,
        send_xchar:             NULL,
        read_proc:              NULL,
diff -Naur -X dontdiff linux-2.3.50-pre2/drivers/usb/usb-serial.h 
linux-2.3.50-pre2-greg/drivers/usb/usb-serial.h
--- linux-2.3.50-pre2/drivers/usb/usb-serial.h  Sun Mar  5 21:53:13 2000
+++ linux-2.3.50-pre2-greg/drivers/usb/usb-serial.h     Mon Mar  6 23:19:17 2000
@@ -44,7 +44,7 @@
 #define FTDI_SIO_SERIAL_CONVERTER_ID   0x8372
 #define KEYSPAN_VENDOR_ID              0x06cd
 #define KEYSPAN_PDA_FAKE_ID            0x0103
-#define KEYSPAN_PDA_ID                 0x0103
+#define KEYSPAN_PDA_ID                 0x0104 /* no clue */
 
 #define SERIAL_TTY_MAJOR       188     /* Nice legal number now */
 #define SERIAL_TTY_MINORS      16      /* Actually we are allowed 255, but this is 
good for now */
@@ -52,29 +52,45 @@
 
 #define MAX_NUM_PORTS  8       /* The maximum number of ports one device can grab at 
once */
 
+
+struct usb_serial_port {
+       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;
+       unsigned char           number;
+       char                    active;         /* someone has this device open */
+
+       struct usb_endpoint_descriptor * interrupt_in_endpoint;
+       __u8                    interrupt_in_interval;
+       unsigned char *         interrupt_in_buffer;
+       struct urb              control_urb;
+
+       unsigned char *         bulk_in_buffer;
+       struct urb              read_urb;
+
+       unsigned char *         bulk_out_buffer;
+       int                     bulk_out_size;
+       struct urb              write_urb;
+       void *                  private;        /* data private to the specific driver 
+*/
+};
+
 struct usb_serial {
        struct usb_device *             dev;
        struct usb_serial_device_type * type;
-       void *                          irq_handle;
-       unsigned int                    irqpipe;
        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                            active[MAX_NUM_PORTS];  /* someone has this 
device open */
+       char                            num_interrupt_in;       /* number of interrupt 
+in endpoints we have */
+       char                            num_bulk_in;            /* number of bulk in 
+endpoints we have */
+       char                            num_bulk_out;           /* number of bulk out 
+endpoints we have */
+       struct usb_serial_port          port[MAX_NUM_PORTS];
+
+       /* FIXME! These should move to the private area of the keyspan driver */
+       int                     tx_room;
+       int                     tx_throttled;
+       wait_queue_head_t       write_wait;
 
-       char                    num_interrupt_in;               /* number of interrupt 
in endpoints we have */
-       __u8                    interrupt_in_interval[MAX_NUM_PORTS];
-       unsigned char *         interrupt_in_buffer[MAX_NUM_PORTS];
-       struct urb              control_urb[MAX_NUM_PORTS];
-
-       char                    num_bulk_in;                    /* number of bulk in 
endpoints we have */
-       unsigned char *         bulk_in_buffer[MAX_NUM_PORTS];
-       struct urb              read_urb[MAX_NUM_PORTS];
-
-       char                    num_bulk_out;                   /* number of bulk out 
endpoints we have */
-       unsigned char *         bulk_out_buffer[MAX_NUM_PORTS];
-       int                     bulk_out_size[MAX_NUM_PORTS];
-       struct urb              write_urb[MAX_NUM_PORTS];
+       void *                  private;                /* data private to the 
+specific driver */
 };
 
 
@@ -101,8 +117,6 @@
        char    num_bulk_out;
        char    num_ports;              /* number of serial ports this device has */
 
-       void    *private;               /* data private to the specific driver */
-       
        /* function call to make before accepting driver */
        int (*startup) (struct usb_serial *serial);     /* return 0 to continue 
initialization, anything else to abort */
        
@@ -113,13 +127,13 @@
        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);
        
        void (*read_bulk_callback)(struct urb *urb);
        void (*write_bulk_callback)(struct urb *urb);
-
 };
 
 
@@ -317,10 +331,31 @@
 
 
 #ifdef CONFIG_USB_SERIAL_KEYSPAN_PDA
-/* function prototypes for a FTDI serial converter */
-static int  keyspan_pda_serial_open    (struct tty_struct *tty, struct file *filp);
-static void keyspan_pda_serial_close   (struct tty_struct *tty, struct file *filp);
+/* function prototypes for a Keyspan PDA serial converter */
+static int  keyspan_pda_serial_open    (struct tty_struct *tty, 
+                                        struct file *filp);
+static void keyspan_pda_serial_close   (struct tty_struct *tty, 
+                                        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 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,
+                                        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,
+                                        struct file *file,
+                                        unsigned int cmd,
+                                        unsigned long arg);
+static void keyspan_pda_set_termios    (struct tty_struct *tty,
+                                        struct termios *old);
+static void keyspan_pda_break_ctl      (struct tty_struct *tty,
+                                        int break_state);
+static int  keyspan_pda_fake_startup   (struct usb_serial *serial);
 
 /* All of the device info needed for the Keyspan PDA serial converter */
 static __u16   keyspan_vendor_id               = KEYSPAN_VENDOR_ID;
@@ -336,24 +371,35 @@
        num_interrupt_in:       NUM_DONT_CARE,
        num_bulk_in:            NUM_DONT_CARE,
        num_bulk_out:           NUM_DONT_CARE,
-       startup:                keyspan_pda_startup     
+       startup:                keyspan_pda_fake_startup
 };
 static struct usb_serial_device_type keyspan_pda_device = {
        name:                   "Keyspan PDA",
        idVendor:               &keyspan_vendor_id,             /* the Keyspan PDA 
vendor ID */
        idProduct:              &keyspan_pda_product_id,        /* the Keyspan PDA 
product id */
-       needs_interrupt_in:     MUST_HAVE_NOT,                  /* this device must 
not have an interrupt in endpoint */
-       needs_bulk_in:          MUST_HAVE,                      /* this device must 
have a bulk in endpoint */
-       needs_bulk_out:         MUST_HAVE,                      /* this device must 
have a bulk out endpoint */
-       num_interrupt_in:       0,
-       num_bulk_in:            1,
+       needs_interrupt_in:     MUST_HAVE,
+       needs_bulk_in:          DONT_CARE,
+       needs_bulk_out:         MUST_HAVE,
+       num_interrupt_in:       1,
+       num_bulk_in:            0,
        num_bulk_out:           1,
        num_ports:              1,
        open:                   keyspan_pda_serial_open,
        close:                  keyspan_pda_serial_close,
+       write:                  keyspan_pda_write,
+       write_room:             keyspan_pda_write_room,
+       write_bulk_callback:    keyspan_pda_write_bulk_callback,
+       chars_in_buffer:        keyspan_pda_chars_in_buffer,
+       throttle:               keyspan_pda_rx_throttle,
+       unthrottle:             keyspan_pda_rx_unthrottle,
+       startup:                keyspan_pda_startup,
+       ioctl:                  keyspan_pda_ioctl,
+       set_termios:            keyspan_pda_set_termios,
+       break_ctl:              keyspan_pda_break_ctl,
 };
 #endif
 
+
 /* To add support for another serial converter, create a usb_serial_device_type
    structure for that device, and add it to this list, making sure that the
    last  entry is NULL. */
@@ -385,11 +431,6 @@
 #else
        #undef  USES_EZUSB_FUNCTIONS
 #endif
-
-
-/* used to mark that a pointer is empty (and not NULL) */
-#define SERIAL_PTR_EMPTY ((void *)(-1))
-
 
 #endif /* ifdef __LINUX_USB_SERIAL_H */
 

Reply via email to