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 */