Browse Source

Plan 9 from Bell Labs 2010-01-09

David du Colombier 14 years ago
parent
commit
bc2639a77b

+ 1 - 0
sys/man/2/usb

@@ -41,6 +41,7 @@ struct Usbdev {
 	ulong	csp;		/* USB class/subclass/proto */
 	int	vid;		/* vendor id */
 	int	did;		/* product (device) id */
+	int	dno;		/* device release number */
 	char*	vendor;
 	char*	product;
 	char*	serial;

+ 17 - 15
sys/man/3/mouse

@@ -11,7 +11,7 @@ mouse, cursor \- kernel mouse interface
 .B /dev/cursor
 .fi
 .SH DESCRIPTION
-The 
+The
 .I mouse
 device provides an interface to the mouse.
 There is also a cursor associated with the screen;
@@ -21,7 +21,7 @@ Reading the
 .B mouse
 file returns the mouse status: its position and button state.
 The read blocks until the state has changed since the last read.
-The read returns 49 bytes: the letter 
+The read returns 49 bytes: the letter
 .B m
 followed by four decimal strings, each 11 characters
 wide followed by a blank:
@@ -52,7 +52,7 @@ and
 .I msec
 fields are ignored and may be omitted.
 .PP
-Writes to the 
+Writes to the
 .B mousein
 file are processed as if they were generated by the
 mouse hardware itself,
@@ -82,7 +82,7 @@ file configures and controls the mouse.
 The messages are:
 .TF ps2intellimouse
 .TP
-.B serial\fI n\fR
+.B "serial\fI n\fP"
 sets serial port
 .I n
 to be the mouse port.
@@ -100,17 +100,19 @@ is equivalent to a write of
 followed by a write of
 .BR intellimouse .
 .TP
-.B accelerated
+.B "accelerated\fI [n]\fP"
 turns on mouse acceleration.
+.I N
+is an optional acceleration factor.
 .TP
 .B linear
-turns off mouse acceleration
+turns off mouse acceleration.
 .TP
-.B res \fIn\fR
+.B "res\fI n\fR"
 sets mouse resolution to a setting between 0 and
 3 inclusive.
 .TP
-.B hwaccel \fIon/off\fR
+.B "hwaccel\fI on/off\fP"
 sets whether acceleration is done in hardware or
 software.
 By default, PS2 mice use hardware and serial mice use
@@ -121,7 +123,7 @@ implement hardware acceleration for external mice.
 .B swap
 swaps the left and right buttons on the mouse.
 .TP
-.B buttonmap \fIxyz\fR
+.B "buttonmap\fI xyz\fP"
 numbers the left, middle, and right mouse buttons
 .IR x ,
 .IR y ,
@@ -133,14 +135,14 @@ If
 is omitted, the default map, 123, is used.
 Thus in the default state writing
 .B "buttonmap 321
-swaps left and right buttons 
+swaps left and right buttons
 and writing
 .B "buttonmap 123
 or just
 .B buttonmap
 restores their usual meaning.
 Note that
-.B buttonmap 
+.B buttonmap
 messages are idempotent,
 unlike
 .BR swap .
@@ -153,7 +155,7 @@ to its default state.
 Not all mice interpret all messages; with some devices,
 some of the messages may be no-ops.
 .PP
-Cursors are described in 
+Cursors are described in
 .IR graphics (2).
 When read or written from or to the
 .B cursor
@@ -162,8 +164,8 @@ The first and second four bytes are little endian
 32-bit numbers specifying the
 .I x
 and
-.I y 
-coordinates of the cursor 
+.I y
+coordinates of the cursor
 .IR offset ;
 the next 32 bytes are the
 .B clr
@@ -175,7 +177,7 @@ bitmask.
 Reading from the
 .B cursor
 file returns the current cursor information.
-Writing to the 
+Writing to the
 .B cursor
 file sets the current cursor information.
 A write of fewer than 72 bytes sets the

+ 144 - 24
sys/man/4/usb

@@ -1,13 +1,15 @@
 .TH USB 4
 .SH NAME
 audio,
+ccid,
 disk,
 ether,
 kb,
 print,
 probe,
 serial,
-usbfat:
+usbfat:,
+usbeject
 \- Universal Serial Bus device drivers
 .SH SYNOPSIS
 .B usb/kb
@@ -23,16 +25,13 @@ usbfat:
 .B usb/disk
 [
 .B -Dd
-]
-[
+] [
 .B -m
 .I mnt
-]
-[
+] [
 .B -s
 .I srv
-]
-[
+] [
 .I dev ...
 ]
 .PP
@@ -41,6 +40,11 @@ usbfat:
 .I disk ...
 ]
 .PP
+.B usbeject
+[
+.I disk ...
+]
+.PP
 .B usb/audio
 [
 .B -dpV
@@ -60,43 +64,55 @@ usbfat:
 .B usb/ether
 [
 .B -Dd
-]
-[
+] [
 .B -m
 .I mnt
-]
-[
+] [
 .B -s
 .I srv
-]
-[
+] [
 .I dev ...
 ]
 .PP
 .B usb/serial
 [
 .B -Dd
-]
-[
+] [
 .B -m
 .I mnt
-]
-[
+] [
 .B -s
 .I srv
+] [
+.I dev ...
 ]
+.PP
+.B usb/print
 [
+.B -d
+] [
 .I dev ...
 ]
 .PP
-.B usb/print
+.B usb/ccid
 [
 .B -d
 ]
+.ig
+.PP
+.B usb/ibuddy
 [
+.B -Dd
+] [
+.B -m
+.I mnt
+] [
+.B -s
+.I srv
+] [
 .I dev ...
 ]
-.PP
+..
 .B usb/probe
 .SH DESCRIPTION
 These programs drive USB devices of specific classes via
@@ -204,12 +220,28 @@ The
 file supplies the device
 geometry when read.
 .PP
-The convenience script
+The script
 .B usbfat:
-mounts the FAT file system in the DOS partition of the named
+mounts the FAT file systems in the DOS partitions of the named
 .IR disk s;
 if none, it mounts those file systems found at
 .BR /dev/sdU*.*/data .
+When more than one partition is found, a suffix is appended to
+the disk name to identify the partition number.
+The script
+.B usbeject
+undoes the effect. If no argument is given, it unmounts all USB
+disks. An argument
+.BI sdU N
+unmounts all partitions from disk with USB target
+.IR N .
+.ig
+An argument
+.BI sdU N . M
+or
+.BI sdU N . M . P
+.\" TODO: fill in missing words
+..
 .SS Printers
 .I Print
 provides a single file can be written to print on a USB printer.
@@ -235,7 +267,7 @@ as is customary.
 .SS Serial ports
 .I Serial
 provides a file system (usually mounted at
-.BR /dev )
+.BR /n/serial )
 that includes one directory per USB serial port, named
 .BI eiaU N.
 In this directory there are two files,
@@ -360,6 +392,88 @@ samples ordered primarily by time and
 secondarily by channel.
 Samples occupy the minimum integral number of bytes.
 Read and write operations of arbitrary size are allowed.
+.SS Ccid
+.I Ccid
+discovers and configures SIM or SAM cards using the CCID standard.
+It provides a file system (usually seen at
+.BR /dev )
+that includes three files,
+.BI ctl ,
+.B raw
+and
+.BI rpc .
+Reading from
+.B ctl
+a description of the smartcard reader capabilities is printed.
+.B raw
+is just intended for debugging.
+Reads and writes to the
+raw file send and receive raw CCID packets.
+Smart cards identify themselves by giving out an ATR,
+an array of characters describing the card uniquely.
+Users of the driver write the ATR to the
+.B rpc
+file and are blocked until a card with that ATR is seen.
+From then on they can do ICC RPCs using whatever
+language the smart card speaks. A small write cancels
+an outstanding RPC.
+.PP
+The driver takes care of powering the card adequately, based
+on its ATR, and tunnelling the RPCs through the USB device.
+Only slot 0 is supported.
+.PP
+When the smartcard disappears,
+all reads and write fail until the file is reopened and
+a new ATR is written to it.
+.
+.ig
+.SS Ibuddy
+.PP
+Ibuddy supports a USB I-buddy toy, a little winged-demon.
+The driver provides one directory per attached toy with a 
+single 
+.BR ctl 
+file to control the device. Directories are named
+.BR ibuddyN ,
+being 
+.I N
+the corresponding usb device number.
+When read, the 
+.BR ctl 
+file provides the state of the device in this form:
+.IP
+.EX
+hips right|left
+wings open|close
+red on|off
+green on|off
+blue on|off
+heart on|off
+.EE
+.PP
+Each line describes the status of one feature. 
+.IR  Red , 
+.IR  blue ,
+and 
+.IR  green
+are the different leds in the head of
+the toy. 
+.IR  Heart 
+represents the red led in the chest of
+the toy. 
+.IR  Wings 
+represents the status of the wings, which
+can be closed or open.
+.IR  Hips 
+represents the orientation
+of the toy (left or right, from the figure's point of view).
+.PP
+Lines can be written to the 
+.BR ctl 
+file to command the device.
+Multiple lines (six at most) can be written
+at once, with one action per line.
+..
 .SH SOURCE
 .B /sys/src/cmd/usb
 .SH "SEE ALSO"
@@ -373,15 +487,21 @@ Read and write operations of arbitrary size are allowed.
 .SH BUGS
 The various device drivers are generic USB drivers and
 may work only for certain devices on each class.
-The Ethernet device works only for certain ASIX-based cards and for CDC devices.
+.PP
 ATA storage devices are not supported.
+.PP
+The Ethernet device works only for certain ASIX-based cards and for CDC devices.
 Both the Ethernet and printer drivers have not
 been tested and it is likely they will fail.
-The serial driver works only for the Prolific chip, and control of the
+.PP
+The serial driver works only for the Prolific chip and Ftdi,
+and control of the
 .B dcd
 and
 .B dsr
 signals and some of the extra features are unimplemented.
+For Ftdi, only the Sheevaplug has been tried.
+There is support for the EHCI debug port, but it loses bytes.
 .PP
 Not heavily exercised yet.
 The entire set of drivers is new and therefore potentially unreliable.

+ 1 - 0
sys/src/cmd/usb/lib/parse.c

@@ -35,6 +35,7 @@ parsedev(Dev *xd, uchar *b, int n)
 		dprint(2, "%s: %s: no configurations\n", argv0, xd->dir);
 	d->vid = GET2(dd->idVendor);
 	d->did = GET2(dd->idProduct);
+	d->dno = GET2(dd->bcdDev);
 	d->vsid = dd->iManufacturer;
 	d->psid = dd->iProduct;
 	d->ssid = dd->iSerialNumber;

+ 1 - 0
sys/src/cmd/usb/lib/usb.h

@@ -184,6 +184,7 @@ struct Usbdev
 	ulong	csp;		/* USB class/subclass/proto */
 	int	vid;		/* vendor id */
 	int	did;		/* product (device) id */
+	int	dno;		/* device release number */
 	char*	vendor;
 	char*	product;
 	char*	serial;

+ 806 - 0
sys/src/cmd/usb/serial/ftdi.c

@@ -0,0 +1,806 @@
+/* Future Technology Devices International serial ports */
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include "usb.h"
+#include "usbfs.h"
+#include "serial.h"
+#include "ftdi.h"
+
+/*
+ * BUG: This keeps growing, there has to be a better way, but without
+ * devices to try it...  We can probably simply look for FTDI in the
+ * string, or use regular expressions somehow.
+ */
+Cinfo ftinfo[] = {
+	{ FTVid, FTACTZWAVEDid },
+	{ FTSheevaVid, FTSheevaDid },
+	{ FTVid, FTIRTRANSDid },
+	{ FTVid, FTIPLUSDid },
+	{ FTVid, FTSIODid },
+	{ FTVid, FT8U232AMDid },
+	{ FTVid, FT8U232AMALTDid },
+	{ FTVid, FT8U2232CDid },
+	{ FTVid, FTRELAISDid },
+	{ INTERBIOMVid, INTERBIOMIOBRDDid },
+	{ INTERBIOMVid, INTERBIOMMINIIOBRDDid },
+	{ FTVid, FTXF632Did },
+	{ FTVid, FTXF634Did },
+	{ FTVid, FTXF547Did },
+	{ FTVid, FTXF633Did },
+	{ FTVid, FTXF631Did },
+	{ FTVid, FTXF635Did },
+	{ FTVid, FTXF640Did },
+	{ FTVid, FTXF642Did },
+	{ FTVid, FTDSS20Did },
+	{ FTNFRICVid, FTNFRICDid },
+	{ FTVid, FTVNHCPCUSBDDid },
+	{ FTVid, FTMTXORB0Did },
+	{ FTVid, FTMTXORB1Did },
+	{ FTVid, FTMTXORB2Did },
+	{ FTVid, FTMTXORB3Did },
+	{ FTVid, FTMTXORB4Did },
+	{ FTVid, FTMTXORB5Did },
+	{ FTVid, FTMTXORB6Did },
+	{ FTVid, FTPERLEULTRAPORTDid },
+	{ FTVid, FTPIEGROUPDid },
+	{ SEALEVELVid, SEALEVEL2101Did },
+	{ SEALEVELVid, SEALEVEL2102Did },
+	{ SEALEVELVid, SEALEVEL2103Did },
+	{ SEALEVELVid, SEALEVEL2104Did },
+	{ SEALEVELVid, SEALEVEL22011Did },
+	{ SEALEVELVid, SEALEVEL22012Did },
+	{ SEALEVELVid, SEALEVEL22021Did },
+	{ SEALEVELVid, SEALEVEL22022Did },
+	{ SEALEVELVid, SEALEVEL22031Did },
+	{ SEALEVELVid, SEALEVEL22032Did },
+	{ SEALEVELVid, SEALEVEL24011Did },
+	{ SEALEVELVid, SEALEVEL24012Did },
+	{ SEALEVELVid, SEALEVEL24013Did },
+	{ SEALEVELVid, SEALEVEL24014Did },
+	{ SEALEVELVid, SEALEVEL24021Did },
+	{ SEALEVELVid, SEALEVEL24022Did },
+	{ SEALEVELVid, SEALEVEL24023Did },
+	{ SEALEVELVid, SEALEVEL24024Did },
+	{ SEALEVELVid, SEALEVEL24031Did },
+	{ SEALEVELVid, SEALEVEL24032Did },
+	{ SEALEVELVid, SEALEVEL24033Did },
+	{ SEALEVELVid, SEALEVEL24034Did },
+	{ SEALEVELVid, SEALEVEL28011Did },
+	{ SEALEVELVid, SEALEVEL28012Did },
+	{ SEALEVELVid, SEALEVEL28013Did },
+	{ SEALEVELVid, SEALEVEL28014Did },
+	{ SEALEVELVid, SEALEVEL28015Did },
+	{ SEALEVELVid, SEALEVEL28016Did },
+	{ SEALEVELVid, SEALEVEL28017Did },
+	{ SEALEVELVid, SEALEVEL28018Did },
+	{ SEALEVELVid, SEALEVEL28021Did },
+	{ SEALEVELVid, SEALEVEL28022Did },
+	{ SEALEVELVid, SEALEVEL28023Did },
+	{ SEALEVELVid, SEALEVEL28024Did },
+	{ SEALEVELVid, SEALEVEL28025Did },
+	{ SEALEVELVid, SEALEVEL28026Did },
+	{ SEALEVELVid, SEALEVEL28027Did },
+	{ SEALEVELVid, SEALEVEL28028Did },
+	{ SEALEVELVid, SEALEVEL28031Did },
+	{ SEALEVELVid, SEALEVEL28032Did },
+	{ SEALEVELVid, SEALEVEL28033Did },
+	{ SEALEVELVid, SEALEVEL28034Did },
+	{ SEALEVELVid, SEALEVEL28035Did },
+	{ SEALEVELVid, SEALEVEL28036Did },
+	{ SEALEVELVid, SEALEVEL28037Did },
+	{ SEALEVELVid, SEALEVEL28038Did },
+	{ IDTECHVid, IDTECHIDT1221UDid },
+	{ OCTVid, OCTUS101Did },
+	{ FTVid, FTHETIRA1Did }, /* special quirk div = 240 baud = B38400 rtscts = 1 */
+	{ FTVid, FTUSBUIRTDid }, /* special quirk div = 77, baud = B38400 */
+	{ FTVid, PROTEGOSPECIAL1 },
+	{ FTVid, PROTEGOR2X0 },
+	{ FTVid, PROTEGOSPECIAL3 },
+	{ FTVid, PROTEGOSPECIAL4 },
+	{ FTVid, FTGUDEADSE808Did },
+	{ FTVid, FTGUDEADSE809Did },
+	{ FTVid, FTGUDEADSE80ADid },
+	{ FTVid, FTGUDEADSE80BDid },
+	{ FTVid, FTGUDEADSE80CDid },
+	{ FTVid, FTGUDEADSE80DDid },
+	{ FTVid, FTGUDEADSE80EDid },
+	{ FTVid, FTGUDEADSE80FDid },
+	{ FTVid, FTGUDEADSE888Did },
+	{ FTVid, FTGUDEADSE889Did },
+	{ FTVid, FTGUDEADSE88ADid },
+	{ FTVid, FTGUDEADSE88BDid },
+	{ FTVid, FTGUDEADSE88CDid },
+	{ FTVid, FTGUDEADSE88DDid },
+	{ FTVid, FTGUDEADSE88EDid },
+	{ FTVid, FTGUDEADSE88FDid },
+	{ FTVid, FTELVUO100Did },
+	{ FTVid, FTELVUM100Did },
+	{ FTVid, FTELVUR100Did },
+	{ FTVid, FTELVALC8500Did },
+	{ FTVid, FTPYRAMIDDid },
+	{ FTVid, FTELVFHZ1000PCDid },
+	{ FTVid, FTELVCLI7000Did },
+	{ FTVid, FTELVPPS7330Did },
+	{ FTVid, FTELVTFM100Did },
+	{ FTVid, FTELVUDF77Did },
+	{ FTVid, FTELVUIO88Did },
+	{ FTVid, FTELVUAD8Did },
+	{ FTVid, FTELVUDA7Did },
+	{ FTVid, FTELVUSI2Did },
+	{ FTVid, FTELVT1100Did },
+	{ FTVid, FTELVPCD200Did },
+	{ FTVid, FTELVULA200Did },
+	{ FTVid, FTELVCSI8Did },
+	{ FTVid, FTELVEM1000DLDid },
+	{ FTVid, FTELVPCK100Did },
+	{ FTVid, FTELVRFP500Did },
+	{ FTVid, FTELVFS20SIGDid },
+	{ FTVid, FTELVWS300PCDid },
+	{ FTVid, FTELVFHZ1300PCDid },
+	{ FTVid, FTELVWS500Did },
+	{ FTVid, LINXSDMUSBQSSDid },
+	{ FTVid, LINXMASTERDEVEL2Did },
+	{ FTVid, LINXFUTURE0Did },
+	{ FTVid, LINXFUTURE1Did },
+	{ FTVid, LINXFUTURE2Did },
+	{ FTVid, FTCCSICDU200Did },
+	{ FTVid, FTCCSICDU401Did },
+	{ FTVid, INSIDEACCESSO },
+	{ INTREDidVid, INTREDidVALUECANDid },
+	{ INTREDidVid, INTREDidNEOVIDid },
+	{ FALCOMVid, FALCOMTWISTDid },
+	{ FALCOMVid, FALCOMSAMBADid },
+	{ FTVid, FTSUUNTOSPORTSDid },
+	{ FTVid, FTRMCANVIEWDid },
+	{ BANDBVid, BANDBUSOTL4Did },
+	{ BANDBVid, BANDBUSTL4Did },
+	{ BANDBVid, BANDBUSO9ML2Did },
+	{ FTVid, EVERECOPROCDSDid },
+	{ FTVid, FT4NGALAXYDE0Did },
+	{ FTVid, FT4NGALAXYDE1Did },
+	{ FTVid, FT4NGALAXYDE2Did },
+	{ FTVid, XSENSCONVERTER0Did },
+	{ FTVid, XSENSCONVERTER1Did },
+	{ FTVid, XSENSCONVERTER2Did },
+	{ FTVid, XSENSCONVERTER3Did },
+	{ FTVid, XSENSCONVERTER4Did },
+	{ FTVid, XSENSCONVERTER5Did },
+	{ FTVid, XSENSCONVERTER6Did },
+	{ FTVid, XSENSCONVERTER7Did },
+	{ MOBILITYVid, MOBILITYUSBSERIALDid },
+	{ FTVid, FTACTIVEROBOTSDid },
+	{ FTVid, FTMHAMKWDid },
+	{ FTVid, FTMHAMYSDid },
+	{ FTVid, FTMHAMY6Did },
+	{ FTVid, FTMHAMY8Did },
+	{ FTVid, FTMHAMICDid },
+	{ FTVid, FTMHAMDB9Did },
+	{ FTVid, FTMHAMRS232Did },
+	{ FTVid, FTMHAMY9Did },
+	{ FTVid, FTTERATRONIKVCPDid },
+	{ FTVid, FTTERATRONIKD2XXDid },
+	{ EVOLUTIONVid, EVOLUTIONER1Did },
+	{ FTVid, FTARTEMISDid },
+	{ FTVid, FTATIKATK16Did },
+	{ FTVid, FTATIKATK16CDid },
+	{ FTVid, FTATIKATK16HRDid },
+	{ FTVid, FTATIKATK16HRCDid },
+	{ KOBILVid, KOBILCONVB1Did },
+	{ KOBILVid, KOBILCONVKAANDid },
+	{ POSIFLEXVid, POSIFLEXPP7000Did },
+	{ FTVid, FTTTUSBDid },
+	{ FTVid, FTECLOCOM1WIREDid },
+	{ FTVid, FTWESTREXMODEL777Did },
+	{ FTVid, FTWESTREXMODEL8900FDid },
+	{ FTVid, FTPCDJDAC2Did },
+	{ FTVid, FTRRCIRKITSLOCOBUFFERDid },
+	{ FTVid, FTASKRDR400Did },
+	{ ICOMID1Vid, ICOMID1Did },
+	{ PAPOUCHVid, PAPOUCHTMUDid },
+	{ FTVid, FTACGHFDUALDid },
+	{ 0,	0 },
+};
+
+enum {
+	Packsz = 1024,
+};
+
+static int
+ftdiread(Serial *ser, int val, int index, int req, uchar *buf)
+{
+	int res;
+
+	if(req != FTGETE2READ)
+		index |= ser->interfc + 1;
+	dsprint(2, "serial: ftdiread req: %#x val: %#x idx:%d buf:%p\n",
+		req, val, index, buf);
+	res = usbcmd(ser->dev,  Rd2h | Rftdireq | Rdev, req, val, index, buf, 1);
+	dsprint(2, "serial: ftdiread res:%d\n", res);
+	return res;
+}
+
+static int
+ftdiwrite(Serial *ser, int val, int index, int req)
+{
+	int res;
+
+	index |= ser->interfc + 1;
+	dsprint(2, "serial: ftdiwrite  req: %#x val: %#x idx:%d\n",
+		req, val, index);
+	res = usbcmd(ser->dev, Rh2d | Rftdireq | Rdev, req, val, index, nil, 0);
+	dsprint(2, "serial: ftdiwrite res:%d\n", res);
+	return res;
+}
+
+static int
+ftmodemctl(Serial *ser, int set)
+{
+	if(set == 0){
+		ser->mctl = 0;
+		ftdiwrite(ser, 0, 0, FTSETMODEMCTRL);
+		return 0;
+	}
+	ser->mctl = 1;
+	ftdiwrite(ser, 0, FTRTSCTSHS, FTSETFLOWCTRL);
+	return 0;
+}
+
+static ushort
+ft232ambaudbase2div(int baud, int base)
+{
+	int divisor3;
+	ushort divisor;
+
+	divisor3 = (base / 2) / baud;
+ 	if((divisor3 & 7) == 7)
+		divisor3++; 			/* round x.7/8 up to x+1 */
+ 	divisor = divisor3 >> 3;
+	divisor3 &= 7;
+
+	if(divisor3 == 1)
+		divisor |= 0xc000;		/*	0.125 */
+	else if(divisor3 >= 4)
+		divisor |= 0x4000;		/*	0.5	*/
+	else if(divisor3 != 0)
+		divisor |= 0x8000;		/*	0.25	*/
+ 	if( divisor == 1)
+		divisor = 0;		/* special case for maximum baud rate */
+	return divisor;
+}
+
+enum{
+	ClockNew	= 48000000,
+	ClockOld	= 12000000 / 16,
+	HetiraDiv	= 240,
+	UirtDiv		= 77,
+};
+
+static ushort
+ft232ambaud2div(int baud)
+{
+	return ft232ambaudbase2div(baud, ClockNew);
+}
+
+static ulong divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7};
+
+static ulong
+ft232bmbaudbase2div(int baud, int base)
+{
+	int divisor3;
+	u32int divisor;
+
+	divisor3 = (base / 2) / baud;
+	divisor = divisor3 >> 3 | divfrac[divisor3 & 7] << 14;
+
+ 	/* Deal with special cases for highest baud rates. */
+ 	if( divisor == 1)
+		divisor = 0; 			/* 1.0 */
+	else if( divisor == 0x4001)
+		divisor = 1;			/* 1.5 */
+	return divisor;
+}
+
+static ulong
+ft232bmbaud2div (int baud)
+{
+	return ft232bmbaudbase2div (baud, ClockNew);
+}
+
+static int
+customdiv(Serial *ser)
+{
+	if(ser->dev->usb->vid == FTVid && ser->dev->usb->did == FTHETIRA1Did)
+		return HetiraDiv;
+	else if(ser->dev->usb->vid == FTVid && ser->dev->usb->did == FTUSBUIRTDid)
+		return UirtDiv;
+
+	fprint(2, "serial: weird custom divisor\n");
+	return 0;		/* shouldn't happen, break as much as I can */
+}
+
+static ulong
+ftbaudcalcdiv(Serial *ser, int baud)
+{
+	int cusdiv;
+	ulong divval;
+
+	if(baud == 38400 && (cusdiv = customdiv(ser)) != 0)
+		baud = ser->baudbase / cusdiv;
+
+	if(baud == 0)
+		baud = 9600;
+
+ 	switch(ser->type) {
+ 	case SIO:
+		switch(baud) {
+		case 300:
+			divval = FTb300;
+			break;
+		case 600:
+			divval = FTb600;
+			break;
+		case 1200:
+			divval = FTb1200;
+			break;
+		case 2400:
+			divval = FTb2400;
+			break;
+		case 4800:
+			divval = FTb4800;
+			break;
+		case 9600:
+			divval = FTb9600;
+			break;
+		case 19200:
+			divval = FTb19200;
+			break;
+		case 38400:
+			divval = FTb38400;
+			break;
+		case 57600:
+			divval = FTb57600;
+			break;
+		case 115200:
+			divval = FTb115200;
+			break;
+		default:
+			divval = FTb9600;
+			break;
+		}
+		break;
+ 	case FT8U232AM:
+	 	if(baud <= 3000000)
+			divval = ft232ambaud2div(baud);
+		else
+			divval = ft232ambaud2div(9600);
+ 		break;
+	case FT232BM:
+	case FT2232C:
+		if(baud <= 3000000)
+			divval = ft232bmbaud2div(baud);
+		else
+			divval = ft232bmbaud2div(9600);
+		break;
+	default:
+		divval = ft232bmbaud2div(9600);
+		break;
+	}
+	return divval;
+}
+
+static int
+ftsetparam(Serial *ser)
+{
+	int res;
+	ushort val;
+	ulong bauddiv;
+
+	val = 0;
+	if(ser->stop == 1)
+		val |= FTSETDATASTOPBITS1;
+	else if(ser->stop == 2)
+		val |= FTSETDATASTOPBITS2;
+	else if(ser->stop == 15)
+		val |= FTSETDATASTOPBITS15;
+	switch(ser->parity){
+	case 0:
+		val |= FTSETDATAParNONE;
+		break;
+	case 1:
+		val |= FTSETDATAParODD;
+		break;
+	case 2:
+		val |= FTSETDATAParEVEN;
+		break;
+	case 3:
+		val |= FTSETDATAParMARK;
+		break;
+	case 4:
+		val |= FTSETDATAParSPACE;
+		break;
+	};
+
+	dsprint(2, "serial: setparam\n");
+
+	res = ftdiwrite(ser, val, 0, FTSETDATA);
+	if(res < 0)
+		return res;
+
+	res = ftmodemctl(ser, ser->mctl);
+	if(res < 0)
+		return res;
+
+	bauddiv = ftbaudcalcdiv(ser, ser->baud);
+	res = ftdiwrite(ser, bauddiv, 0x1&(bauddiv>>16), FTSETBaudRate);
+
+	dsprint(2, "serial: setparam res: %d\n", res);
+	return res;
+}
+
+/* ser locked */
+static void
+ftgettype(Serial *ser)
+{
+	int inter, nifcs, i, outhdrsz, dno;
+	ulong baudbase;
+	Conf *cnf;
+
+	inter = 0;
+ 	/* Assume it is not the original SIO device for now. */
+	baudbase = ClockNew / 2;
+	outhdrsz = 0;
+	nifcs = 0;
+	dno = ser->dev->usb->dno;
+	cnf = ser->dev->usb->conf[0];
+	for(i = 0; i < Niface; i++)
+		if(cnf->iface[i] != nil)
+			nifcs++;
+	if(nifcs> 1) {
+		/* Multiple interfaces.  Assume FT2232C. */
+		ser->type = FT2232C;
+		/*
+		 * BUG: If there is more than one interface, we use the second.
+		 * We only support 1 interface at the moment...
+		 * This works well for the sheeva/JTAG, but in other
+		 * cases we are ignoring nifcs-1 interfaces and using
+		 * the second (weird).  The problem is we have to rethink
+		 * the whole scheme to make it work with various ifaces.
+		 */
+		inter = PITA;
+
+		/*
+		 * BM-type devices have a bug where dno gets set
+		 * to 0x200 when serial is 0.
+		 */
+		if(dno < 0x500)
+			fprint(2, "serial: warning: dno too low for multi-interfacedevice\n");
+	} else if(dno < 0x200) {
+		/* Old device.  Assume it is the original SIO. */
+		ser->type = SIO;
+		baudbase = ClockOld/16;
+		outhdrsz = 1;
+	} else if(dno < 0x400)
+		/*
+		 * Assume its an FT8U232AM (or FT8U245AM)
+		 * (It might be a BM because of the iSerialNumber bug,
+		 * but it will still work as an AM device.)
+		 */
+		ser->type = FT8U232AM;
+	else			/* Assume it is an FT232BM (or FT245BM) */
+		ser->type = FT232BM;
+
+	ser->baudbase = baudbase;
+	ser->outhdrsz = outhdrsz;
+	ser->inhdrsz = 2;
+	ser->interfc = inter;
+	dsprint (2, "serial: detected type: %#x\n", ser->type);
+}
+
+int
+ftmatch(Serial *ser, char *info)
+{
+	Cinfo *ip;
+	char buf[50];
+
+	for(ip = ftinfo; ip->vid != 0; ip++){
+		snprint(buf, sizeof buf, "vid %#06x did %#06x", ip->vid, ip->did);
+		dsprint(2, "serial: %s %s\n", buf, info);
+		if(strstr(info, buf) != nil){
+			if(ser != nil){
+				qlock(ser);
+				ftgettype(ser);
+				qunlock(ser);
+			}
+			return 0;
+		}
+	}
+	return -1;
+}
+
+static int
+ftuseinhdr(Serial *ser, uchar *b)
+{
+	if(b[0] & FTICTS)
+		ser->cts = 1;
+	else
+		ser->cts = 0;
+	if(b[0] & FTIDSR)
+		ser->dsr = 1;
+	else
+		ser->dsr = 0;
+	if(b[0] & FTIRI)
+		ser->ring = 1;
+	else
+		ser->ring = 0;
+	if(b[0] & FTIRLSD)
+		ser->rlsd = 1;
+	else
+		ser->rlsd = 0;
+
+	if(b[1] & FTIOE)
+		ser->novererr++;
+	if(b[1] & FTIPE)
+		ser->nparityerr++;
+	if(b[1] & FTIFE)
+		ser->nframeerr++;
+	if(b[1] & FTIBI)
+		ser->nbreakerr++;
+	return 0;
+}
+
+static int
+ftsetouthdr(Serial *ser, uchar *b, int len)
+{
+	if(ser->outhdrsz != 0)
+		b[0] = FTOPORT | (FTOLENMSK & len);
+	return ser->outhdrsz;
+}
+
+static int
+wait4data(Serial *ser, uchar *data, int count)
+{
+	qunlock(ser);
+	recvul(ser->w4data);
+	qlock(ser);
+	if(ser->ndata >= count)
+		ser->ndata -= count;
+	else{
+		count = ser->ndata;
+		ser->ndata = 0;
+	}
+	memmove(data, ser->data, count);
+	if(ser->ndata != 0)
+		memmove(ser->data, ser->data+count, ser->ndata);
+
+	sendul(ser->gotdata, 1);
+	return count;
+}
+
+static int
+wait4write(Serial *ser, uchar *data, int count)
+{
+	int off, fd;
+	uchar *b;
+
+	// qunlock(ser);
+	// recvul(ser->w4empty);	should I really?
+	// qlock(ser);
+
+	b = emallocz(count+ser->outhdrsz, 1);
+	off = ftsetouthdr(ser, b, count);
+	memmove(b+off, data, count);
+
+	fd = ser->epout->dfd;
+	qunlock(ser);
+	count = write(fd, b, count+off);
+	qlock(ser);
+	free(b);
+	return count;
+}
+
+typedef struct Packser Packser;
+struct Packser{
+	int	nb;
+	uchar	b[Packsz];
+};
+
+typedef struct Areader Areader;
+struct Areader{
+	Serial	*s;
+	Channel	*c;
+};
+
+static void
+epreader(void *u)
+{
+	int dfd, rcount;
+	char err[40];
+	Areader *a;
+	Channel *c;
+	Packser *p;
+	Serial *ser;
+
+	threadsetname("epreader proc");
+	a = u;
+	ser = a->s;
+	c = a->c;
+	free(a);
+
+	qlock(ser);
+	dfd = ser->epin->dfd;
+	qunlock(ser);
+
+	do {
+		p = emallocz(sizeof(Packser), 1);
+		rcount = read(dfd, p->b, Packsz);
+		if(serialdebug > 5)
+			dsprint(2, "%d %#ux%#ux ", rcount, ser->data[0],
+				ser->data[1]);
+		if(rcount <= 0)
+			break;
+		if(rcount >= ser->inhdrsz){
+			ftuseinhdr(ser, p->b);
+			rcount -= ser->inhdrsz;
+			memmove(p->b, p->b+ser->inhdrsz, rcount);
+			if(rcount != 0){
+				p->nb = rcount;
+				sendp(c, p);
+			}
+		}
+	} while(rcount >= 0 || (rcount < 0 && strstr(err, "timed out") != nil));
+
+	if(rcount < 0)
+		fprint(2, "error reading: %r\n");
+	sendp(c, nil);
+	closedev(ser->dev);
+}
+
+static void
+statusreader(void *u)
+{
+	Areader *a;
+	Channel *c;
+	Packser *p;
+	Serial *ser;
+
+	ser = u;
+	threadsetname("statusreader thread");
+	/* big buffering, fewer bytes lost */
+	c = chancreate(sizeof(Packser *), 128);
+	a = emallocz(sizeof(Areader), 1);
+	a->s = ser;
+	a->c = c;
+	incref(ser->dev);
+	proccreate(epreader, a, 16*1024);
+
+	while((p = recvp(c)) != nil){
+		memmove(ser->data, p->b, p->nb);
+		ser->ndata = p->nb;
+		dsprint(2, "serial: status reader %d \n", p->nb);
+		/* consume it all */
+		while(ser->ndata != 0){
+			dsprint(2, "serial: status reader to consume: %d\n",
+				p->nb);
+			sendul(ser->w4data, 1);
+			recvul(ser->gotdata);
+		}
+		free(p);
+	}
+	free(a);
+	free(c);
+	closedev(ser->dev);
+}
+
+static int
+ftreset(Serial *ser)
+{
+	ftdiwrite(ser, FTRESETCTLVAL, 0, FTRESET);
+	return 0;
+}
+
+static int
+ftinit(Serial *ser)
+{
+	qlock(ser);
+	serialreset(ser);
+	qunlock(ser);
+
+	incref(ser->dev);
+	threadcreate(statusreader, ser, 8*1024);
+	return 0;
+}
+
+static int
+ftsetbreak(Serial *ser, int val)
+{
+	return ftdiwrite(ser, (val != 0? FTSETBREAK: 0), 0, FTSETDATA);
+}
+
+static int
+ftclearpipes(Serial *ser)
+{
+	/* maybe can be done in one... */
+	ftdiwrite(ser, FTRESETCTLVALPURGETX, 0, FTRESET);
+	ftdiwrite(ser, FTRESETCTLVALPURGERX, 0, FTRESET);
+	return 0;
+}
+
+static int
+setctlline(Serial *ser, uchar val)
+{
+	return ftdiwrite(ser, val | (val << 8), 0, FTSETMODEMCTRL);
+}
+
+static void
+updatectlst(Serial *ser, int val)
+{
+	if(ser->rts)
+		ser->ctlstate |= val;
+	else
+		ser->ctlstate &= ~val;
+}
+
+static int
+setctl(Serial *ser)
+{
+	int res;
+
+	if(ser->dev->usb->vid == FTVid && ser->dev->usb->did ==  FTHETIRA1Did){
+		fprint(2, "serial: cannot set lines for this device\n");
+		updatectlst(ser, CtlRTS|CtlDTR);
+		ser->rts = ser->dtr = 1;
+		return -1;
+	}
+
+	/* NB: you can not set DTR and RTS with one control message */
+	updatectlst(ser, CtlRTS);
+	res = setctlline(ser, (CtlRTS<<8)|ser->ctlstate);
+	if(res < 0)
+		return res;
+
+	updatectlst(ser, CtlDTR);
+	res = setctlline(ser, (CtlDTR<<8)|ser->ctlstate);
+	if(res < 0)
+		return res;
+
+	return 0;
+}
+
+static int
+ftsendlines(Serial *ser)
+{
+	int res;
+
+	dsprint(2, "serial: sendlines: %#2.2x\n", ser->ctlstate);
+	res = setctl(ser);
+	dsprint(2, "serial: sendlines res: %d\n", res);
+	return 0;
+}
+
+static int
+ftseteps(Serial *ser)
+{
+	char *s;
+
+	s = smprint("maxpkt %d", Packsz);
+	devctl(ser->epin, s);
+	ser->maxread = Packsz;
+	devctl(ser->epout, s);
+	ser->maxwrite = Packsz;
+	free(s);
+	return 0;
+}
+
+Serialops ftops = {
+	.init		= ftinit,
+	.seteps		= ftseteps,
+	.setparam	= ftsetparam,
+	.clearpipes	= ftclearpipes,
+	.reset		= ftreset,
+	.sendlines	= ftsendlines,
+	.modemctl	= ftmodemctl,
+	.setbreak	= ftsetbreak,
+	.wait4data	= wait4data,
+	.wait4write	= wait4write,
+};

+ 567 - 0
sys/src/cmd/usb/serial/ftdi.h

@@ -0,0 +1,567 @@
+enum {
+	/* used by devices which don't provide their own Vid */
+	FTVid = 0x0403,
+
+	FTSheevaVid = 0x9E88,
+	FTSheevaDid = 0x9E8F,
+
+	FTSIODid	= 0x8372,	/* Product Id SIO appl'n of 8U100AX */
+	FT8U232AMDid	= 0x6001, 	/* Similar device to SIO above */
+	FT8U232AMALTDid	= 0x6006,	/* FT's alternate Did for above*/
+	FT8U2232CDid	= 0x6010,	/* Dual channel device */
+	FTRELAISDid	= 0xFA10,	/* Relais device */
+
+	/* NF reader */
+	FTNFRICVid = 0x0DCD,
+	FTNFRICDid = 0x0001,
+
+	FTACTZWAVEDid = 0xF2D0,		/* www.irtrans.de device */
+
+	/*
+	 * ACT Solutions HomePro ZWave interface
+	 * http://www.act-solutions.com/HomePro.htm)
+	 */
+	FTIRTRANSDid = 0xFC60,
+
+	/*
+	 * www.thoughttechnology.com/ TT-USB
+	 */
+	FTTTUSBDid = 0xFF20,
+
+	/* iPlus device */
+	FTIPLUSDid = 0xD070,
+
+	/* www.crystalfontz.com devices */
+	FTXF632Did = 0xFC08,	/* 632: 16x2 Character Display */
+	FTXF634Did = 0xFC09,	/* 634: 20x4 Character Display */
+	FTXF547Did = 0xFC0A,	/* 547: Two line Display */
+	FTXF633Did = 0xFC0B,	/* 633: 16x2 Character Display with Keys */
+	FTXF631Did = 0xFC0C,	/* 631: 20x2 Character Display */
+	FTXF635Did = 0xFC0D,	/* 635: 20x4 Character Display */
+	FTXF640Did = 0xFC0E,	/* 640: Two line Display */
+	FTXF642Did = 0xFC0F,	/* 642: Two line Display */
+
+	/*
+	 * Video Networks Limited / Homechoice in the UK
+	 * use an ftdi-based device for their 1Mb broadband
+	 */
+	FTVNHCPCUSBDDid = 0xfe38,
+
+	/*
+	 * PCDJ use ftdi based dj-controllers
+	 * DAC-2 device http://www.pcdjhardware.com/DAC2.asp
+	 */
+	FTPCDJDAC2Did = 0xFA88,
+
+	/*
+	 * Matrix Orbital LCD displays,
+	 * which are the FT232BM (similar to the 8U232AM)
+	 */
+	FTMTXORB0Did = 0xFA00,
+	FTMTXORB1Did = 0xFA01,
+	FTMTXORB2Did = 0xFA02,
+	FTMTXORB3Did = 0xFA03,
+	FTMTXORB4Did = 0xFA04,
+	FTMTXORB5Did = 0xFA05,
+	FTMTXORB6Did = 0xFA06,
+
+	/* Interbiometrics USB I/O Board */
+	INTERBIOMVid		= 0x1209,
+	INTERBIOMIOBRDDid	= 0x1002,
+	INTERBIOMMINIIOBRDDid	= 0x1006,
+
+	/*
+	 * The following are the values for the Perle Systems
+	 * UltraPort USB serial converters
+	 */
+	FTPERLEULTRAPORTDid = 0xF0C0,
+
+	/*
+	 * Sealevel SeaLINK+ adapters.
+	 */
+
+	SEALEVELVid = 0x0c52,
+
+	SEALEVEL2101Did = 0x2101,	/* SeaLINK+232 (2101/2105) */
+	SEALEVEL2102Did = 0x2102,	/* SeaLINK+485 (2102) */
+	SEALEVEL2103Did = 0x2103,	/* SeaLINK+232I (2103) */
+	SEALEVEL2104Did = 0x2104,	/* SeaLINK+485I (2104) */
+	SEALEVEL22011Did = 0x2211,	/* SeaPORT+2/232 (2201) Port 1 */
+	SEALEVEL22012Did = 0x2221,	/* SeaPORT+2/232 (2201) Port 2 */
+	SEALEVEL22021Did = 0x2212,	/* SeaPORT+2/485 (2202) Port 1 */
+	SEALEVEL22022Did = 0x2222,	/* SeaPORT+2/485 (2202) Port 2 */
+	SEALEVEL22031Did = 0x2213,	/* SeaPORT+2 (2203) Port 1 */
+	SEALEVEL22032Did = 0x2223,	/* SeaPORT+2 (2203) Port 2 */
+	SEALEVEL24011Did = 0x2411,	/* SeaPORT+4/232 (2401) Port 1 */
+	SEALEVEL24012Did = 0x2421,	/* SeaPORT+4/232 (2401) Port 2 */
+	SEALEVEL24013Did = 0x2431,	/* SeaPORT+4/232 (2401) Port 3 */
+	SEALEVEL24014Did = 0x2441,	/* SeaPORT+4/232 (2401) Port 4 */
+	SEALEVEL24021Did = 0x2412,	/* SeaPORT+4/485 (2402) Port 1 */
+	SEALEVEL24022Did = 0x2422,	/* SeaPORT+4/485 (2402) Port 2 */
+	SEALEVEL24023Did = 0x2432,	/* SeaPORT+4/485 (2402) Port 3 */
+	SEALEVEL24024Did = 0x2442,	/* SeaPORT+4/485 (2402) Port 4 */
+	SEALEVEL24031Did = 0x2413,	/* SeaPORT+4 (2403) Port 1 */
+	SEALEVEL24032Did = 0x2423,	/* SeaPORT+4 (2403) Port 2 */
+	SEALEVEL24033Did = 0x2433,	/* SeaPORT+4 (2403) Port 3 */
+	SEALEVEL24034Did = 0x2443,	/* SeaPORT+4 (2403) Port 4 */
+	SEALEVEL28011Did = 0x2811,	/* SeaLINK+8/232 (2801) Port 1 */
+	SEALEVEL28012Did = 0x2821,	/* SeaLINK+8/232 (2801) Port 2 */
+	SEALEVEL28013Did = 0x2831,	/* SeaLINK+8/232 (2801) Port 3 */
+	SEALEVEL28014Did = 0x2841,	/* SeaLINK+8/232 (2801) Port 4 */
+	SEALEVEL28015Did = 0x2851,	/* SeaLINK+8/232 (2801) Port 5 */
+	SEALEVEL28016Did = 0x2861,	/* SeaLINK+8/232 (2801) Port 6 */
+	SEALEVEL28017Did = 0x2871,	/* SeaLINK+8/232 (2801) Port 7 */
+	SEALEVEL28018Did = 0x2881,	/* SeaLINK+8/232 (2801) Port 8 */
+	SEALEVEL28021Did = 0x2812,	/* SeaLINK+8/485 (2802) Port 1 */
+	SEALEVEL28022Did = 0x2822,	/* SeaLINK+8/485 (2802) Port 2 */
+	SEALEVEL28023Did = 0x2832,	/* SeaLINK+8/485 (2802) Port 3 */
+	SEALEVEL28024Did = 0x2842,	/* SeaLINK+8/485 (2802) Port 4 */
+	SEALEVEL28025Did = 0x2852,	/* SeaLINK+8/485 (2802) Port 5 */
+	SEALEVEL28026Did = 0x2862,	/* SeaLINK+8/485 (2802) Port 6 */
+	SEALEVEL28027Did = 0x2872,	/* SeaLINK+8/485 (2802) Port 7 */
+	SEALEVEL28028Did = 0x2882,	/* SeaLINK+8/485 (2802) Port 8 */
+	SEALEVEL28031Did = 0x2813,	/* SeaLINK+8 (2803) Port 1 */
+	SEALEVEL28032Did = 0x2823,	/* SeaLINK+8 (2803) Port 2 */
+	SEALEVEL28033Did = 0x2833,	/* SeaLINK+8 (2803) Port 3 */
+	SEALEVEL28034Did = 0x2843,	/* SeaLINK+8 (2803) Port 4 */
+	SEALEVEL28035Did = 0x2853,	/* SeaLINK+8 (2803) Port 5 */
+	SEALEVEL28036Did = 0x2863,	/* SeaLINK+8 (2803) Port 6 */
+	SEALEVEL28037Did = 0x2873,	/* SeaLINK+8 (2803) Port 7 */
+	SEALEVEL28038Did = 0x2883,	/* SeaLINK+8 (2803) Port 8 */
+
+	/* KOBIL Vendor ID chipcard terminals */
+	KOBILVid	= 0x0d46,
+	KOBILCONVB1Did	= 0x2020,	/* KOBIL Konverter for B1 */
+	KOBILCONVKAANDid = 0x2021,	/* KOBILKonverter for KAAN */
+
+	/* Icom ID-1 digital transceiver */
+	ICOMID1Vid	= 0x0C26,
+	ICOMID1Did	= 0x0004,
+
+	FTASKRDR400Did	= 0xC991,	/* ASK RDR 400 series card reader */
+	FTDSS20Did	= 0xFC82,	/* DSS-20 Sync Station for Sony Ericsson P800 */
+
+	/*
+	 * Home Electronics (www.home-electro.com) USB gadgets
+	 */
+	FTHETIRA1Did	= 0xFA78,	/* Tira-1 IR transceiver */
+
+	/*
+	 * An infrared receiver and transmitter using the 8U232AM chip
+	 * http://www.usbuirt.com
+	 */
+	FTUSBUIRTDid	= 0xF850,
+
+	FTELVUR100Did	= 0xFB58,	/* USB-RS232-Umsetzer (UR 100) */
+	FTELVUM100Did	= 0xFB5A,	/* USB-Modul UM 100 */
+	FTELVUO100Did	= 0xFB5B,	/* USB-Modul UO 100 */
+	FTELVALC8500Did	= 0xF06E,	/* ALC 8500 Expert */
+	FTELVCLI7000Did	= 0xFB59,	/* Computer-Light-Interface */
+	FTELVPPS7330Did	= 0xFB5C,	/* Processor-Power-Supply (PPS 7330) */
+	FTELVTFM100Did	= 0xFB5D,	/* Temperartur-Feuchte Messgeraet (TFM 100) */
+	FTELVUDF77Did	= 0xFB5E,	/* USB DCF Funkurh (UDF 77) */
+	FTELVUIO88Did	= 0xFB5F,	/* USB-I/O Interface (UIO 88) */
+	FTELVUAD8Did	= 0xF068,	/* USB-AD-Wandler (UAD 8) */
+	FTELVUDA7Did	= 0xF069,	/* USB-DA-Wandler (UDA 7) */
+	FTELVUSI2Did	= 0xF06A,	/* USB-Schrittmotoren-Interface (USI 2) */
+	FTELVT1100Did	= 0xF06B,	/* Thermometer (T 1100) */
+	FTELVPCD200Did	= 0xF06C,	/* PC-Datenlogger (PCD 200) */
+	FTELVULA200Did	= 0xF06D,	/* USB-LCD-Ansteuerung (ULA 200) */
+	FTELVFHZ1000PCDid= 0xF06F,	/* FHZ 1000 PC */
+	FTELVCSI8Did	= 0xE0F0,	/* Computer-Schalt-Interface (CSI 8) */
+	FTELVEM1000DLDid= 0xE0F1,	/* PC-Datenlogger fuer Energiemonitor (EM 1000 DL) */
+	FTELVPCK100Did	= 0xE0F2,	/* PC-Kabeltester (PCK 100) */
+	FTELVRFP500Did	= 0xE0F3,	/* HF-Leistungsmesser (RFP 500) */
+	FTELVFS20SIGDid	= 0xE0F4,	/* Signalgeber (FS 20 SIG) */
+	FTELVWS300PCDid	= 0xE0F6,	/* PC-Wetterstation (WS 300 PC) */
+	FTELVFHZ1300PCDid= 0xE0E8,	/* FHZ 1300 PC */
+	FTELVWS500Did	= 0xE0E9,	/* PC-Wetterstation (WS 500) */
+
+	/*
+	 * Definitions for ID TECH (http://www.idt-net.com) devices
+	 */
+	IDTECHVid	= 0x0ACD,	/* ID TECH Vendor ID */
+	IDTECHIDT1221UDid= 0x0300,	/* IDT1221U USB to RS-232 */
+
+	/*
+	 * Definitions for Omnidirectional Control Technology, Inc. devices
+	 */
+	OCTVid		= 0x0B39,	/* OCT vendor ID */
+
+	/*
+	 * Note: OCT US101 is also rebadged as Dick Smith Electronics
+	 * (NZ) XH6381, Dick Smith Electronics (Aus) XH6451, and SIIG
+	 * Inc. model US2308 hardware version 1.
+	 */
+	OCTUS101Did	= 0x0421,	/* OCT US101 USB to RS-232 */
+
+	/*
+	 *	infrared receiver for access control with IR tags
+	 */
+	FTPIEGROUPDid	= 0xF208,
+
+	/*
+	 * Definitions for Artemis astronomical USB based cameras
+	 * http://www.artemisccd.co.uk/
+	 */
+
+	FTARTEMISDid	= 0xDF28,	/* All Artemis Cameras */
+
+	FTATIKATK16Did	= 0xDF30,	/* ATIK ATK-16 Grayscale Camera */
+	FTATIKATK16CDid = 0xDF32,	/* ATIK ATK-16C Colour Camera */
+	FTATIKATK16HRDid= 0xDF31,	/* ATIK ATK-16HR Grayscale */
+	FTATIKATK16HRCDid= 0xDF33,	/* ATIK ATK-16HRC Colour Camera */
+
+	/*
+	 * Protego products
+	 */
+	PROTEGOSPECIAL1	= 0xFC70,	/* special/unknown device */
+	PROTEGOR2X0	= 0xFC71,	/* R200-USB TRNG unit (R210, R220, and R230) */
+	PROTEGOSPECIAL3	= 0xFC72,	/* special/unknown device */
+	PROTEGOSPECIAL4	= 0xFC73,	/* special/unknown device */
+
+	/*
+	 * Gude Analog- und Digitalsysteme GmbH
+	 */
+	FTGUDEADSE808Did = 0xE808,
+	FTGUDEADSE809Did = 0xE809,
+	FTGUDEADSE80ADid = 0xE80A,
+	FTGUDEADSE80BDid = 0xE80B,
+	FTGUDEADSE80CDid = 0xE80C,
+	FTGUDEADSE80DDid = 0xE80D,
+	FTGUDEADSE80EDid = 0xE80E,
+	FTGUDEADSE80FDid = 0xE80F,
+	FTGUDEADSE888Did = 0xE888,	/* Expert ISDN Control USB */
+	FTGUDEADSE889Did = 0xE889,	/* USB RS-232 OptoBridge */
+	FTGUDEADSE88ADid = 0xE88A,
+	FTGUDEADSE88BDid = 0xE88B,
+	FTGUDEADSE88CDid = 0xE88C,
+	FTGUDEADSE88DDid = 0xE88D,
+	FTGUDEADSE88EDid = 0xE88E,
+	FTGUDEADSE88FDid = 0xE88F,
+
+	/*
+	 * Linx Technologies
+	 */
+	LINXSDMUSBQSSDid= 0xF448,	/* Linx SDM-USB-QS-S */
+	LINXMASTERDEVEL2Did= 0xF449,	/* Linx Master Development.0 */
+	LINXFUTURE0Did	= 0xF44A,	/* Linx future device */
+	LINXFUTURE1Did	= 0xF44B,	/* Linx future device */
+	LINXFUTURE2Did	= 0xF44C,	/* Linx future device */
+
+	/*
+	 * CCS Inc. ICDU/ICDU40 - the FT232BM used in a in-circuit-debugger
+	 * unit for PIC16's/PIC18's
+	 */
+	FTCCSICDU200Did	= 0xF9D0,
+	FTCCSICDU401Did	= 0xF9D1,
+
+	/* Inside Accesso contactless reader (http://www.insidefr.com) */
+	INSIDEACCESSO	= 0xFAD0,
+
+	/*
+	 * Intrepid Control Systems (http://www.intrepidcs.com/)
+	 * ValueCAN and NeoVI
+	 */
+	INTREDidVid	= 0x093C,
+	INTREDidVALUECANDid= 0x0601,
+	INTREDidNEOVIDid= 0x0701,
+
+	/*
+	 * Falcom Wireless Communications GmbH
+	 */
+	FALCOMVid	= 0x0F94,
+	FALCOMTWISTDid	= 0x0001,	/* Falcom Twist USB GPRS modem */
+	FALCOMSAMBADid	= 0x0005,	/* Falcom Samba USB GPRS modem */
+
+	/*
+	 * SUUNTO
+	 */
+	FTSUUNTOSPORTSDid= 0xF680,	/* Suunto Sports instrument */
+
+	/*
+	 * B&B Electronics
+	 */
+	BANDBVid	= 0x0856,	/* B&B Electronics Vendor ID */
+	BANDBUSOTL4Did	= 0xAC01,	/* USOTL4 Isolated RS-485 */
+	BANDBUSTL4Did	= 0xAC02,	/* USTL4 RS-485 Converter */
+	BANDBUSO9ML2Did	= 0xAC03,	/* USO9ML2 Isolated RS-232 */
+
+	/*
+	 * RM Michaelides CANview USB (http://www.rmcan.com)
+	 * CAN fieldbus interface adapter
+	 */
+	FTRMCANVIEWDid	= 0xfd60,
+
+	/*
+	 * EVER Eco Pro UPS (http://www.ever.com.pl/)
+	 */
+	EVERECOPROCDSDid = 0xe520,	/* RS-232 converter */
+
+	/*
+	 * 4N-GALAXY.DE PIDs for CAN-USB, USB-RS232, USB-RS422, USB-RS485,
+	 * USB-TTY activ, USB-TTY passiv. Some PIDs are used by several devices
+	 */
+	FT4NGALAXYDE0Did = 0x8372,
+	FT4NGALAXYDE1Did = 0xF3C0,
+	FT4NGALAXYDE2Did = 0xF3C1,
+
+	/*
+	 * Mobility Electronics products.
+	 */
+	MOBILITYVid	= 0x1342,
+	MOBILITYUSBSERIALDid= 0x0202,	/* EasiDock USB 200 serial */
+
+	/*
+	 * microHAM product IDs (http://www.microham.com)
+	 */
+	FTMHAMKWDid	= 0xEEE8,	/* USB-KW interface */
+	FTMHAMYSDid	= 0xEEE9,	/* USB-YS interface */
+	FTMHAMY6Did	= 0xEEEA,	/* USB-Y6 interface */
+	FTMHAMY8Did	= 0xEEEB,	/* USB-Y8 interface */
+	FTMHAMICDid	= 0xEEEC,	/* USB-IC interface */
+	FTMHAMDB9Did	= 0xEEED,	/* USB-DB9 interface */
+	FTMHAMRS232Did	= 0xEEEE,	/* USB-RS232 interface */
+	FTMHAMY9Did	= 0xEEEF,	/* USB-Y9 interface */
+
+	/*
+	 * Active Robots product ids.
+	 */
+	FTACTIVEROBOTSDid	= 0xE548,	/* USB comms board */
+	XSENSCONVERTER0Did	= 0xD388,
+	XSENSCONVERTER1Did	= 0xD389,
+	XSENSCONVERTER2Did	= 0xD38A,
+	XSENSCONVERTER3Did	= 0xD38B,
+	XSENSCONVERTER4Did	= 0xD38C,
+	XSENSCONVERTER5Did	= 0xD38D,
+	XSENSCONVERTER6Did	= 0xD38E,
+	XSENSCONVERTER7Did	= 0xD38F,
+
+	/*
+	 * Xsens Technologies BV products (http://www.xsens.com).
+	 */
+	FTTERATRONIKVCPDid	= 0xEC88,	/* Teratronik device */
+	FTTERATRONIKD2XXDid	= 0xEC89,	/* Teratronik device */
+
+	/*
+	 * Evolution Robotics products (http://www.evolution.com/).
+	 */
+	EVOLUTIONVid	= 0xDEEE,
+	EVOLUTIONER1Did	= 0x0300,		/* ER1 Control Module */
+
+	/* Pyramid Computer GmbH */
+	FTPYRAMIDDid	= 0xE6C8,		/* Pyramid Appliance Display */
+
+	/*
+	 * Posiflex inc retail equipment (http://www.posiflex.com.tw)
+	 */
+	POSIFLEXVid	= 0x0d3a,
+	POSIFLEXPP7000Did= 0x0300,		/* PP-7000II thermal printer */
+
+	/*
+	 * Westrex International devices
+	 */
+	FTWESTREXMODEL777Did	= 0xDC00,	/* Model 777 */
+	FTWESTREXMODEL8900FDid	= 0xDC01,	/* Model 8900F */
+
+	/*
+	 * RR-CirKits LocoBuffer USB (http://www.rr-cirkits.com)
+	 */
+	FTRRCIRKITSLOCOBUFFERDid= 0xc7d0,	/* LocoBuffer USB */
+	FTECLOCOM1WIREDid	= 0xEA90,	/* COM to 1-Wire USB */
+
+	/*
+	 * Papouch products (http://www.papouch.com/)
+	 */
+	PAPOUCHVid	= 0x5050,
+	PAPOUCHTMUDid	= 0x0400,		/* TMU USB Thermometer */
+
+	/*
+	 * ACG Identification Technologies GmbH products http://www.acg.de/
+	 */
+	FTACGHFDUALDid	= 0xDD20,		/* HF Dual ISO Reader (RFID) */
+};
+
+/* Commands */
+enum {
+	FTRESET		= 0,		/* Reset the port */
+	FTSETMODEMCTRL,			/* Set the modem control register */
+	FTSETFLOWCTRL,			/* Set flow control register */
+	FTSETBaudRate,			/* Set baud rate */
+	FTSETDATA,			/* Set the parameters, parity */
+	FTGETMODEMSTATUS,		/* Retrieve current value of modem ctl */
+	FTSETEVENTCHAR,			/* Set the event character */
+	FTSETERRORCHAR,			/* Set the error character */
+	FTSETLATENCYTIMER,		/* Set the latency timer */
+	FTGETLATENCYTIMER,		/* Get the latency timer */
+	FTGETE2READ	= 0x90,		/* Read an address */
+};
+
+enum {
+	Rftdireq = 1<<6,		/* bit for type of request */
+};
+
+/*
+ * Commands Data size
+ * Sets have wLength = 0
+ * Gets have wValue = 0
+ */
+enum {
+	FTGETMODEMSTATUSSZ	= 1,
+	FTGETLATENCYTIMERSZ	= 0,		/* Get the latency timer */
+	FTGETE2READSZ		= 2,		/* Read an address */
+};
+
+/*
+ * bRequest: FTGETE2READ
+ * wIndex: Address of word to read
+ * Data: Will return a word of data from E2Address
+ */
+
+/* Port Identifier Table */
+enum {
+	PITDEFAULT = 0,		/* SIOA */
+	PITA,			/* SIOA */
+};
+
+/*
+ * bRequest: FTRESET
+ * wValue: Ctl Val
+ * wIndex: Port
+ */
+enum {
+	FTRESETCTLVAL		= 0,
+	FTRESETCTLVALPURGERX	= 1,
+	FTRESETCTLVALPURGETX	= 2,
+};
+
+/*
+ * BmRequestType: SET
+ * bRequest: FTSETBaudRate
+ * wValue: BaudDivisor value - see below
+ * Bits 15 to 0 of the 17-bit divisor are placed in the request value.
+ * Bit 16 is placed in bit 0 of the request index.
+ */
+
+/* chip type */
+enum {
+	SIO	= 1,
+	FT8U232AM= 2,
+	FT232BM	= 3,
+	FT2232C	= 4,
+};
+
+enum {
+	 FTb300		= 0,
+	 FTb600		= 1,
+	 FTb1200	= 2,
+	 FTb2400	= 3,
+	 FTb4800	= 4,
+	 FTb9600	= 5,
+	 FTb19200	= 6,
+	 FTb38400	= 7,
+	 FTb57600	= 8,
+	 FTb115200	= 9,
+};
+
+/*
+ * bRequest: FTSETDATA
+ * wValue: Data characteristics
+ * wIndex: Port
+ */
+enum {
+	FTSETDATAParNONE	= 0 << 8,
+	FTSETDATAParODD		= 1 << 8,
+	FTSETDATAParEVEN	= 2 << 8,
+	FTSETDATAParMARK	= 3 << 8,
+	FTSETDATAParSPACE	= 4 << 8,
+	FTSETDATASTOPBITS1	= 0 << 11,
+	FTSETDATASTOPBITS15	= 1 << 11,
+	FTSETDATASTOPBITS2	= 2 << 11,
+	FTSETBREAK		= 1 << 14,
+};
+
+/*
+ * bRequest: FTSETMODEMCTRL
+ * wValue: ControlValue (see below)
+ * wIndex: Port
+ */
+
+/*
+ * bRequest: FTSETFLOWCTRL
+ * wValue: Xoff/Xon
+ * wIndex: Protocol/Port - hIndex is protocl / lIndex is port
+ */
+enum {
+	FTDISABLEFLOWCTRL= 0,
+	FTRTSCTSHS	= 1 << 8,
+	FTDTRDSRHS	= 2 << 8,
+	FTXONXOFFHS	= 4 << 8,
+};
+
+/*
+ * bRequest: FTGETLATENCYTIMER
+ * wIndex: Port
+ * wLength: 0
+ * Data: latency (on return)
+ */
+
+/*
+ * bRequest: FTSETLATENCYTIMER
+ * wValue: Latency (milliseconds)
+ * wIndex: Port
+ */
+
+/*
+ * BmRequestType: SET
+ * bRequest: FTSETEVENTCHAR
+ * wValue: EventChar
+ * wIndex: Port
+ */
+
+/*
+ * BmRequestType: SET
+ * bRequest: FTSETERRORCHAR
+ * wValue: Error Char
+ * wIndex: Port
+ */
+
+/*
+ * BmRequestType: GET
+ * bRequest: FTGETMODEMSTATUS
+ * wIndex: Port
+ * wLength: 1
+ * Data: Status
+ */
+enum {
+	FTCTSMASK	= 0x10,
+	FTDSRMASK	= 0x20,
+	FTRIMASK	= 0x40,
+	FTRLSDMASK	= 0x80,
+};
+
+enum {
+	/* byte 0 of in data hdr */
+	FTICTS	= 1 << 4,
+	FTIDSR	= 1 << 5,
+	FTIRI	= 1 << 6,
+	FTIRLSD	= 1 << 7,	/* receive line signal detect */
+
+	/* byte 1 of in data hdr */
+	FTIDR	= 1<<0,		/* data ready */
+	FTIOE	= 1<<1,		/* overrun error */
+	FTIPE	= 1<<2,		/* parity error */
+	FTIFE	= 1<<3,		/* framing error */
+	FTIBI	= 1<<4,		/* break interrupt */
+	FTITHRE	= 1<<5,		/* xmitter holding register */
+	FTITEMT	= 1<<6,		/* xmitter empty */
+	FTIFIFO	= 1<<7,		/* error in rcv fifo */
+
+	/* byte 0 of out data hdr len does not include byte 0 */
+	FTOLENMSK= 0x3F,
+	FTOPORT	= 0x80,		/* must be set */
+};
+
+extern Serialops ftops;
+
+int	ftmatch(Serial *ser, char *info);

+ 7 - 5
sys/src/cmd/usb/serial/main.c

@@ -6,25 +6,27 @@
 #include "serial.h"
 #include "ucons.h"
 #include "prolific.h"
+#include "ftdi.h"
 
-typedef struct Parg Parg;
 enum {
 	Arglen = 80,
 };
 
+typedef struct Parg Parg;
 
-
+/* keep in sync with serial.c */
 static void
 usage(void)
 {
-	fprint(2, "usage: %s [-d] [-a n] [dev...]\n", argv0);
+	fprint(2, "usage: %s [-dD] [-m mtpt] [-s srv] [dev...]\n", argv0);
 	threadexitsall("usage");
 }
 
 static int
 matchserial(char *info, void*)
 {
-	if(uconsmatch(info) == 0 || plmatch(info) == 0)
+	if(uconsmatch(info) == 0 || plmatch(info) == 0 ||
+	    ftmatch(nil, info) == 0)
 		return 0;
 	return -1;
 }
@@ -35,7 +37,7 @@ threadmain(int argc, char **argv)
 	char *mnt, *srv, *as, *ae;
 	char args[Arglen];
 
-	mnt = "/dev";
+	mnt = "/n/serial";
 	srv = nil;
 
 	quotefmtinstall();

+ 3 - 3
sys/src/cmd/usb/serial/mkfile

@@ -2,9 +2,10 @@
 
 TARG=serial
 OFILES=main.$O
-LIBDOFILES=serial.$O prolific.$O ucons.$O
+LIBDOFILES=ftdi.$O serial.$O prolific.$O ucons.$O
 HFILES=\
 	../lib/usb.h\
+	ftdi.h\
 	prolific.h\
 	serial.h\
 	ucons.h\
@@ -17,13 +18,13 @@ LIB=\
 
 BIN=/$objtype/bin/usb
 
-
 UPDATE=\
 	mkfile\
 	$HFILES\
 	${OFILES:%.$O=%.c}\
 
 </sys/src/cmd/mkone
+
 CFLAGS=-I../lib $CFLAGS
 
 $LIBU:
@@ -34,4 +35,3 @@ $LIBU:
 $LIBD:V: $LIBDOFILES
 	ar vu $LIBD $newprereq
 	rm $newprereq
-

+ 94 - 51
sys/src/cmd/usb/serial/prolific.c

@@ -61,7 +61,7 @@ plmatch(char *info)
 	for(ip = plinfo; ip->vid != 0; ip++){
 		snprint(buf, sizeof buf, "vid %#06x did %#06x",
 			ip->vid, ip->did);
-		dsprint(2, "serial: %s %s", buf, info);
+		dsprint(2, "serial: %s %s\n", buf, info);
 		if(strstr(info, buf) != nil)
 			return 0;
 	}
@@ -105,10 +105,28 @@ vendorwrite(Serial *ser, int val, int index)
 	return res;
 }
 
+/* BUG: I could probably read Dcr0 and set only the bits */
+static int
+plmodemctl(Serial *ser, int set)
+{
+	if(set == 0){
+		ser->mctl = 0;
+		vendorwrite(ser, Dcr0Idx|DcrSet, Dcr0Init);
+		return 0;
+	}
+
+	ser->mctl = 1;
+	if(ser->type == TypeHX)
+		vendorwrite(ser, Dcr0Idx|DcrSet, Dcr0Init|Dcr0HwFcX);
+	else
+		vendorwrite(ser, Dcr0Idx|DcrSet, Dcr0Init|Dcr0HwFcH);
+	return 0;
+}
+
 static int
 plgetparam(Serial *ser)
 {
-	uchar buf[7];
+	uchar buf[ParamReqSz];
 	int res;
 
 	res = usbcmd(ser->dev, Rd2h | Rclass | Riface, GetLineReq,
@@ -138,27 +156,10 @@ plgetparam(Serial *ser)
 	return res;
 }
 
-static int
-plmodemctl(Serial *ser, int set)
-{
-	if(set == 0){
-		ser->mctl = 0;
-		vendorwrite(ser, 0x0, 0x0);
-		return 0;
-	}
-
-	ser->mctl = 1;
-	if(ser->type == TypeHX)
-		vendorwrite(ser, 0x0, Dcr0InitX);
-	else
-		vendorwrite(ser, 0x0, Dcr0InitH);
-	return 0;
-}
-
 static int
 plsetparam(Serial *ser)
 {
-	uchar buf[7];
+	uchar buf[ParamReqSz];
 	int res;
 
 	PUT4(buf, ser->baud);
@@ -182,28 +183,55 @@ plsetparam(Serial *ser)
 	return res;
 }
 
+static int
+revid(ulong devno)
+{
+	switch(devno){
+	case RevH:
+		return TypeH;
+	case RevX:
+	case RevHX:
+	case Rev1:
+		return TypeHX;
+	default:
+		return TypeUnk;
+	}
+}
+
+/* linux driver says the release id is not always right */
+static int
+heuristicid(ulong csp, ulong maxpkt)
+{
+	if(Class(csp) == 0x02)
+		return TypeH;
+	else if(maxpkt == 0x40)
+		return TypeHX;
+	else if(Class(csp) == 0x00 || Class(csp) == 0xFF)
+		return TypeH;
+	else{
+		fprint(2, "serial: chip unknown, setting to HX version\n");
+		return TypeHX;
+	}
+}
+
 static int
 plinit(Serial *ser)
 {
-	ulong csp;
 	char *st;
 	uchar *buf;
+	ulong csp, maxpkt, dno;
 
-	buf = emallocz(VendorReqSize, 1);
+	buf = emallocz(VendorReqSz, 1);
 	qlock(ser);
 	serialreset(ser);
+
 	csp = ser->dev->usb->csp;
+	maxpkt = ser->dev->maxpkt;
+	dno = ser->dev->usb->dno;
 
-	if(Class(csp) == 0x02)
-		ser->type = Type0;
-	else if(ser->dev->maxpkt == 0x40)
-		ser->type = TypeHX;
-	else if(Class(csp) == 0x00 || Class(csp) == 0xFF)
-		ser->type = Type1;
+	if((ser->type = revid(dno)) == TypeUnk)
+		ser->type = heuristicid(csp, maxpkt);
 
-	if(ser->type != ser->dev->usb->psid)
-		fprint(2, "serial: warning, heuristics: %#ux and psid: "
-			"%#ux, not a match\n", ser->type, ser->dev->usb->psid);
 	dsprint(2, "serial: type %d\n", ser->type);
 
 	vendorread(ser, 0x8484, 0, buf);
@@ -214,13 +242,14 @@ plinit(Serial *ser)
 	vendorwrite(ser, 0x0404, 1);
 	vendorread(ser, 0x8484, 0, buf);
 	vendorread(ser, 0x8383, 0, buf);
-	vendorwrite(ser, 0, 1);
-	vendorwrite(ser, 1, 0);
+
+	vendorwrite(ser, Dcr0Idx|DcrSet, Dcr0Init);
+	vendorwrite(ser, Dcr1Idx|DcrSet, Dcr1Init);
 
 	if(ser->type == TypeHX)
-		vendorwrite(ser, 2, Dcr2InitX);
+		vendorwrite(ser, Dcr2Idx|DcrSet, Dcr2InitX);
 	else
-		vendorwrite(ser, 2, Dcr2InitH);
+		vendorwrite(ser, Dcr2Idx|DcrSet, Dcr2InitH);
 
 	plgetparam(ser);
 	qunlock(ser);
@@ -249,8 +278,8 @@ static int
 plclearpipes(Serial *ser)
 {
 	if(ser->type == TypeHX){
-		vendorwrite(ser, 8, 0x0);
-		vendorwrite(ser, 9, 0x0);
+		vendorwrite(ser, PipeDSRst, 0);
+		vendorwrite(ser, PipeUSRst, 0);
 	}else{
 		if(unstall(ser->dev, ser->epout, Eout) < 0)
 			dprint(2, "disk: unstall epout: %r\n");
@@ -290,7 +319,7 @@ plsendlines(Serial *ser)
 	dsprint(2, "serial: sendlines: %#2.2x\n", ser->ctlstate);
 	composectl(ser);
 	res = setctlline(ser, ser->ctlstate);
-	dsprint(2, "serial: getparam res: %d\n", res);
+	dsprint(2, "serial: sendlines res: %d\n", res);
 	return 0;
 }
 
@@ -299,7 +328,7 @@ plreadstatus(Serial *ser)
 {
 	int nr, dfd;
 	char err[40];
-	uchar buf[10];
+	uchar buf[VendorReqSz];
 
 	qlock(ser);
 	dsprint(2, "serial: reading from interrupt\n");
@@ -326,11 +355,11 @@ plreadstatus(Serial *ser)
 		ser->cts = buf[8] & BreakerrStatus;
 		ser->ring = buf[8] & RingStatus;
 		ser->cts = buf[8] & CtsStatus;
-		if (buf[8] & FrerrStatus)
+		if(buf[8] & FrerrStatus)
 			ser->nframeerr++;
-		if (buf[8] & ParerrStatus)
+		if(buf[8] & ParerrStatus)
 			ser->nparityerr++;
-		if (buf[8] & OvererrStatus)
+		if(buf[8] & OvererrStatus)
 			ser->novererr++;
 	} else
 		dsprint(2, "serial: bad status read %d\n", nr);
@@ -346,18 +375,32 @@ statusreader(void *u)
 
 	ser = u;
 	threadsetname("statusreaderproc");
-
-	while(plreadstatus(ser) > 0)
+	while(plreadstatus(ser) >= 0)
 		;
+	fprint(2, "serial: statusreader exiting\n");
 	closedev(ser->dev);
 }
 
+/*
+ * Maximum number of bytes transferred per frame
+ * The output buffer size cannot be increased due to the size encoding
+ */
+
+static int
+plseteps(Serial *ser)
+{
+	devctl(ser->epin,  "maxpkt 256");
+	devctl(ser->epout, "maxpkt 256");
+	return 0;
+}
+
 Serialops plops = {
-	.init =		plinit,
-	.getparam =	plgetparam,
-	.setparam =	plsetparam,
-	.clearpipes =	plclearpipes,
-	.sendlines =	plsendlines,
-	.modemctl =	plmodemctl,
-	.setbreak =	plsetbreak,
+	.init		= plinit,
+	.getparam	= plgetparam,
+	.setparam	= plsetparam,
+	.clearpipes	= plclearpipes,
+	.sendlines	= plsendlines,
+	.modemctl	= plmodemctl,
+	.setbreak	= plsetbreak,
+	.seteps		= plseteps,
 };

+ 119 - 111
sys/src/cmd/usb/serial/prolific.h

@@ -1,169 +1,177 @@
 enum {
 	/* flavours of the device */
-	Type0,
-	Type1,
+	TypeH,
 	TypeHX,
+	TypeUnk,
+
+	RevH		= 0x0202,
+	RevX		= 0x0300,
+	RevHX		= 0x0400,
+	Rev1		= 0x0001,
 
 	/* usbcmd parameters */
-	SetLineReq = 0x20,
+	SetLineReq	= 0x20,
 
-	SetCtlReq = 0x22,
-	CtlDTR = 0x01,
-	CtlRTS = 0x02,
+	SetCtlReq	= 0x22,
 
-	BreakReq = 0x23,
-	BreakOn = 0xffff,
-	BreakOff = 0x0000,
+	BreakReq	= 0x23,
+	BreakOn		= 0xffff,
+	BreakOff	= 0x0000,
 
-	GetLineReq = 0x21,
+	GetLineReq	= 0x21,
 
-	VendorWriteReq = 0x01,	/* BUG: is this a standard request? */
-	VendorReadReq = 0x01,
+	VendorWriteReq	= 0x01,		/* BUG: is this a standard request? */
+	VendorReadReq	= 0x01,
 
-	VendorReqSize = 10,
+	ParamReqSz	= 7,
+	VendorReqSz	= 10,
 
 	/* status read from interrupt endpoint */
-	DcdStatus =	0x01,
-	DsrStatus =	0x02,
-	BreakerrStatus=	0x04,
-	RingStatus =	0x08,
-	FrerrStatus =	0x10,
-	ParerrStatus =	0x20,
-	OvererrStatus =	0x40,
-	CtsStatus =	0x80,
-
-	/*
-	 * flow control bits, Dcr0InitH
-	 * I think, composed this list from various drivers and specs.
-	 * FlowOutCts =	0x0001,
-	 * FlowOutDsr =	0x0002,
-	 * FlowInDsr =	0x0004,
-	 * FlowInDtr =	0x0008,
-	 * FlowInRts =	0x0010,
-	 * FlowOutRts =	0x0020,
-	 * FlowOutXon =	0x0080,
-	 * FlowInXon =	0x0100,
-	 */
-
-	Dcr0InitH = 0x0041,
-	Dcr0InitX = 0x0061,
-	Dcr1InitH = 0x0080,
-	Dcr1InitX = 0x0000,
-	Dcr2InitH = 0x0024,
-	Dcr2InitX = 0x0044,
+	DcdStatus	= 0x01,
+	DsrStatus	= 0x02,
+	BreakerrStatus	= 0x04,
+	RingStatus	= 0x08,
+	FrerrStatus	= 0x10,
+	ParerrStatus	= 0x20,
+	OvererrStatus	= 0x40,
+	CtsStatus	= 0x80,
+
+	DcrGet		= 0x80,
+	DcrSet		= 0x00,
+
+	Dcr0Idx		= 0x00,
+
+	Dcr0Init	= 0x0001,
+	Dcr0HwFcH	= 0x0040,
+	Dcr0HwFcX	= 0x0060,
+
+	Dcr1Idx		= 0x01,
+
+	Dcr1Init	= 0x0000,
+	Dcr1InitH	= 0x0080,
+	Dcr1InitX	= 0x0000,
+
+	Dcr2Idx		= 0x02,
+
+	Dcr2InitH	= 0x0024,
+	Dcr2InitX	= 0x0044,
+
+	PipeDSRst	= 0x08,
+	PipeUSRst	= 0x09,
+
 };
 
 enum {
-	PL2303Vid =	0x067b,
-	PL2303Did =	0x2303,
-	PL2303DidRSAQ2=	0x04bb,
-	PL2303DidDCU11=	0x1234,
-	PL2303DidPHAROS=0xaaa0,
-	PL2303DidRSAQ3=	0xaaa2,
-	PL2303DidALDIGA=0x0611,
-	PL2303DidMMX =	0x0612,
-	PL2303DidGPRS =	0x0609,
+	PL2303Vid	= 0x067b,
+	PL2303Did	= 0x2303,
+	PL2303DidRSAQ2	= 0x04bb,
+	PL2303DidDCU11	= 0x1234,
+	PL2303DidPHAROS	= 0xaaa0,
+	PL2303DidRSAQ3	= 0xaaa2,
+	PL2303DidALDIGA	= 0x0611,
+	PL2303DidMMX	= 0x0612,
+	PL2303DidGPRS	= 0x0609,
 
-	ATENVid =	0x0557,
-	ATENVid2 =	0x0547,
-	ATENDid =	0x2008,
+	ATENVid		= 0x0557,
+	ATENVid2	= 0x0547,
+	ATENDid		= 0x2008,
 
-	IODATAVid =	0x04bb,
-	IODATADid =	0x0a03,
-	IODATADidRSAQ5=	0x0a0e,
+	IODATAVid	= 0x04bb,
+	IODATADid	= 0x0a03,
+	IODATADidRSAQ5	= 0x0a0e,
 
-	ELCOMVid =	0x056e,
-	ELCOMDid =	0x5003,
-	ELCOMDidUCSGT =	0x5004,
+	ELCOMVid	= 0x056e,
+	ELCOMDid	= 0x5003,
+	ELCOMDidUCSGT	= 0x5004,
 
-	ITEGNOVid =	0x0eba,
-	ITEGNODid =	0x1080,
-	ITEGNODid2080 =	0x2080,
+	ITEGNOVid	= 0x0eba,
+	ITEGNODid	= 0x1080,
+	ITEGNODid2080	= 0x2080,
 
-	MA620Vid =	0x0df7,
-	MA620Did =	0x0620,
+	MA620Vid	= 0x0df7,
+	MA620Did	= 0x0620,
 
-	RATOCVid =	0x0584,
-	RATOCDid =	0xb000,
+	RATOCVid	= 0x0584,
+	RATOCDid	= 0xb000,
 
-	TRIPPVid =	0x2478,
-	TRIPPDid =	0x2008,
+	TRIPPVid	= 0x2478,
+	TRIPPDid	= 0x2008,
 
-	RADIOSHACKVid =	0x1453,
-	RADIOSHACKDid =	0x4026,
+	RADIOSHACKVid	= 0x1453,
+	RADIOSHACKDid	= 0x4026,
 
-	DCU10Vid =	0x0731,
-	DCU10Did =	0x0528,
+	DCU10Vid	= 0x0731,
+	DCU10Did	= 0x0528,
 
-	SITECOMVid =	0x6189,
-	SITECOMDid =	0x2068,
+	SITECOMVid	= 0x6189,
+	SITECOMDid	= 0x2068,
 
 	 /* Alcatel OT535/735 USB cable */
-	ALCATELVid =	0x11f7,
-	ALCATELDid =	0x02df,
+	ALCATELVid	= 0x11f7,
+	ALCATELDid	= 0x02df,
 
 	/* Samsung I330 phone cradle */
-	SAMSUNGVid =	0x04e8,
-	SAMSUNGDid =	0x8001,
+	SAMSUNGVid	= 0x04e8,
+	SAMSUNGDid	= 0x8001,
 
-	SIEMENSVid =	0x11f5,
-	SIEMENSDidSX1 =	0x0001,
-	SIEMENSDidX65 =	0x0003,
-	SIEMENSDidX75 =	0x0004,
-	SIEMENSDidEF81=	0x0005,
+	SIEMENSVid	= 0x11f5,
+	SIEMENSDidSX1	= 0x0001,
+	SIEMENSDidX65	= 0x0003,
+	SIEMENSDidX75	= 0x0004,
+	SIEMENSDidEF81	= 0x0005,
 
-	SYNTECHVid =	0x0745,
-	SYNTECHDid =	0x0001,
+	SYNTECHVid	= 0x0745,
+	SYNTECHDid	= 0x0001,
 
 	/* Nokia CA-42 Cable */
-	NOKIACA42Vid =	0x078b,
-	NOKIACA42Did =	0x1234,
+	NOKIACA42Vid	= 0x078b,
+	NOKIACA42Did	= 0x1234,
 
 	/* CA-42 CLONE Cable www.ca-42.com chipset: Prolific Technology Inc */
-	CA42CA42Vid =	0x10b5,
-	CA42CA42Did =	0xac70,
+	CA42CA42Vid	= 0x10b5,
+	CA42CA42Did	= 0xac70,
 
-	SAGEMVid =	0x079b,
-	SAGEMDid =	0x0027,
+	SAGEMVid	= 0x079b,
+	SAGEMDid	= 0x0027,
 
 	/* Leadtek GPS 9531 (ID 0413:2101) */
-	LEADTEKVid =	0x0413,
-	LEADTEK9531Did=	0x2101,
+	LEADTEKVid	= 0x0413,
+	LEADTEK9531Did	= 0x2101,
 
 	 /* USB GSM cable from Speed Dragon Multimedia, Ltd */
-	SPEEDDRAGONVid=	0x0e55,
-	SPEEDDRAGONDid=	0x110b,
+	SPEEDDRAGONVid	= 0x0e55,
+	SPEEDDRAGONDid	= 0x110b,
 
 	/* DATAPILOT Universal-2 Phone Cable */
-	BELKINVid =	0x050d,
-	BELKINDid =	0x0257,
+	BELKINVid	= 0x050d,
+	BELKINDid	= 0x0257,
 
 	/* Belkin "F5U257" Serial Adapter */
-	DATAPILOTU2Vid=	0x0731,
-	DATAPILOTU2Did=	0x2003,
+	DATAPILOTU2Vid	= 0x0731,
+	DATAPILOTU2Did	= 0x2003,
 
-	ALCORVid =	0x058F,
-	ALCORDid =	0x9720,
+	ALCORVid	= 0x058F,
+	ALCORDid	= 0x9720,
 
 	/* Willcom WS002IN Data Driver (by NetIndex Inc.) */,
-	WS002INVid =	0x11f6,
-	WS002INDid =	0x2001,
+	WS002INVid	= 0x11f6,
+	WS002INDid	= 0x2001,
 
 	/* Corega CG-USBRS232R Serial Adapter */,
-	COREGAVid =	0x07aa,
-	COREGADid =	0x002a,
+	COREGAVid	= 0x07aa,
+	COREGADid	= 0x002a,
 
 	/* Y.C. Cable U.S.A., Inc - USB to RS-232 */,
-	YCCABLEVid =	0x05ad,
-	YCCABLEDid =	0x0fba,
+	YCCABLEVid	= 0x05ad,
+	YCCABLEDid	= 0x0fba,
 
 	/* "Superial" USB - Serial */,
-	SUPERIALVid =	0x5372,
-	SUPERIALDid =	0x2303,
+	SUPERIALVid	= 0x5372,
+	SUPERIALDid	= 0x2303,
 
 	/* Hewlett-Packard LD220-HP POS Pole Display */,
-	HPVid =		0x03f0,
-	HPLD220Did =	0x3524,
+	HPVid		= 0x03f0,
+	HPLD220Did	= 0x3524,
 };
 
 extern Serialops plops;

+ 135 - 82
sys/src/cmd/usb/serial/serial.c

@@ -7,17 +7,16 @@
 #include "serial.h"
 #include "prolific.h"
 #include "ucons.h"
+#include "ftdi.h"
 
 /*
- * BUG: This device is not really prepared to use different
- * serial implementations. That part must be rewritten.
- *
+ * This part takes care of locking except for initialization and
+ * other threads created by the hw dep. drivers.
  * BUG: An error on the device does not make the driver exit.
  * It probably should.
  */
 
 int serialdebug;
-int debugport;
 
 enum {
 	/* Qids. Maintain order (relative to dirtabs structs) */
@@ -64,24 +63,30 @@ serialfatal(Serial *ser)
 	usbfsdel(&ser->fs);
 }
 
-/* I sleep with the lock... only way to drain */
+/* I sleep with the lock... only way to drain in general*/
 static void
 serialdrain(Serial *ser)
 {
-	uint baud;
+	uint baud, pipesize;
 
+	if(ser->maxwrite < 256)
+		pipesize = 256;
+	else
+		pipesize = ser->maxwrite;
 	baud = ser->baud;
-	/* wait for the 256-byte pipe to clear */
-	sleep(10 + 256/((1 + baud)*1000));
-	ser->clearpipes(ser);
+	/* wait for the at least 256-byte pipe to clear */
+	sleep(10 + pipesize/((1 + baud)*1000));
+	if(ser->clearpipes != nil)
+		ser->clearpipes(ser);
 }
 
 int
 serialreset(Serial *ser)
 {
 	/* cmd for reset */
-	fprint(2, "serial: error, resetting\n");
 	serialdrain(ser);
+	if(ser->reset != nil)
+		ser->reset(ser);
 	return 0;
 }
 
@@ -111,7 +116,8 @@ serialctl(Serial *p, char *cmd)
 	nf = tokenize(cmd, f, nelem(f));
 	for(i = 0; i < nf; i++){
 		if(strncmp(f[i], "break", 5) == 0){
-			p->setbreak(p, 1);
+			if(p->setbreak != nil)
+				p->setbreak(p, 1);
 			continue;
 		}
 
@@ -164,7 +170,8 @@ serialctl(Serial *p, char *cmd)
 			break;
 		case 'm':
 			drain++;
-			p->modemctl(p, n);
+			if(p->modemctl != nil)
+				p->modemctl(p, n);
 			if(n == 0)
 				p->cts = 0;
 			break;
@@ -176,7 +183,7 @@ serialctl(Serial *p, char *cmd)
 			if(strlen(f[i]) != 2)
 				return -1;
 			drain++;
-			par = f[i][2];
+			par = f[i][1];
 			if(par == 'n')
 				p->parity = 0;
 			else if(par == 'o')
@@ -216,8 +223,10 @@ serialctl(Serial *p, char *cmd)
 				x = CTLS;
 			else
 				x = CTLQ;
-
-			nw = write(p->epout->dfd, &x, 1);
+			if(p->wait4write != nil)
+				nw = p->wait4write(p, &x, 1);
+			else
+				nw = write(p->epout->dfd, &x, 1);
 			if(nw != 1){
 				serialrecover(p, "");
 				return -1;
@@ -229,10 +238,13 @@ serialctl(Serial *p, char *cmd)
 	}
 	if(drain)
 		serialdrain(p);
-	if(lines && !set)
-		p->sendlines(p);
-	else if(set && p->setparam(p) < 0)
-		return -1;
+	if(lines && !set){
+		if(p->sendlines != nil && p->sendlines(p) < 0)
+			return -1;
+	} else if(set){
+		if(p->setparam != nil && p->setparam(p) < 0)
+			return -1;
+	}
 	return 0;
 }
 
@@ -271,7 +283,11 @@ serinit(Serial *ser)
 {
 	int res;
 
-	res = ser->init(ser);
+	res = 0;
+	if(ser->init != nil)
+		res = ser->init(ser);
+	if(ser->getparam != nil)
+		ser->getparam(ser);
 	ser->nframeerr = ser->nparityerr = ser->nbreakerr = ser->novererr = 0;
 	return res;
 }
@@ -331,7 +347,6 @@ dostat(Usbfs *fs, int path, Dir *d)
 		d->name = t->name;
 	else
 		snprint(d->name, Namesz, t->name, ser->fs.name);
-
 	d->length = 0;
 }
 
@@ -381,26 +396,20 @@ filldir(Usbfs *fs, Dir *d, Dirtab *tab, int i)
 static int
 dirgen(Usbfs *fs, Qid, int i, Dir *d, void *)
 {
-	Dirtab *tab;
-
 	i++;				/* skip root */
-	if(i < nelem(dirtab))
-		tab = &dirtab[i];
-	else
+	if(i >= nelem(dirtab))
 		return -1;
-	filldir(fs, d, tab, i);
+	filldir(fs, d, &dirtab[i], i);
 	return 0;
 }
 
-
 static long
 dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
 {
 	int dfd;
 	long rcount;
 	ulong path;
-	char *buf, *err;	/* change */
-	char *e;
+	char *e, *buf, *err;	/* change */
 	Qid q;
 	Serial *ser;
 
@@ -416,34 +425,44 @@ dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
 		count = usbdirread(fs, q, data, count, offset, dirgen, nil);
 		break;
 	case Qdata:
-		if(debugport && count > 8)
-			count = 8;
-		if(0)dsprint(2, "serial: reading from data\n");
+		if(count > ser->maxread)
+			count = ser->maxread;
+		dsprint(2, "serial: reading from data\n");
 		do {
-			dfd = ser->epin->dfd;
-			qunlock(ser);
 			err[0] = 0;
-			rcount = read(dfd, data, count);
-			qlock(ser);
+			dfd = ser->epin->dfd;
+			if(usbdebug >= 3)
+				dsprint(2, "serial: reading: %ld\n", count);
+
+			if(ser->wait4data != nil)
+				rcount = ser->wait4data(ser, data, count);
+			else{
+				qunlock(ser);
+				rcount = read(dfd, data, count);
+				qlock(ser);
+			}
 			if(rcount < 0)
 				snprint(err, 255, "%r");
+			if(usbdebug >= 3)
+				dsprint(2, "serial: read: %s %ld\n", err, rcount);
 		} while(rcount < 0 && strstr(err, "timed out") != nil);
 
+		dsprint(2, "serial: read from bulk %ld, %10.10s\n", rcount, err);
 		if(rcount < 0){
 			dsprint(2, "serial: need to recover, data read %ld %r\n",
 				count);
 			serialrecover(ser, err);
 		}
-		if(0)dsprint(2, "serial: read from bulk %ld\n", rcount);
+		dsprint(2, "serial: read from bulk %ld\n", rcount);
 		count = rcount;
 		break;
 	case Qctl:
-		if(offset != 0){
+		if(offset != 0)
 			count = 0;
-			break;
+		else {
+			e = serdumpst(ser, buf, 255);
+			count = usbreadbuf(data, count, 0, buf, e - buf);
 		}
-		e = serdumpst(ser, buf, 255);
-		count = usbreadbuf(data, count, 0, buf, e - buf);
 		break;
 	}
 	qunlock(ser);
@@ -452,13 +471,39 @@ dread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
 	return count;
 }
 
+static long
+altwrite(Serial *ser, uchar *buf, long count)
+{
+	int nw, dfd;
+	char err[128];
+
+	do{
+		if(ser->wait4write != nil)
+			/* unlocked inside later */
+			nw = ser->wait4write(ser, buf, count);
+		else{
+			dfd = ser->epout->dfd;
+			qunlock(ser);
+			nw = write(dfd, buf, count);
+			qlock(ser);
+		}
+		rerrstr(err, sizeof err);
+	} while(nw < 0 && strstr(err, "timed out") != nil);
+
+	if(nw != count){
+		dsprint(2, "serial: need to recover, status in write %d %r\n",
+			nw);
+		snprint(err, sizeof err, "%r");
+		serialrecover(ser, err);
+	}
+	return nw;
+}
+
 static long
 dwrite(Usbfs *fs, Fid *fid, void *buf, long count, vlong)
 {
-	int nw;
 	ulong path;
 	char *cmd;
-	char err[40];
 	Serial *ser;
 
 	ser = fs->aux;
@@ -467,14 +512,7 @@ dwrite(Usbfs *fs, Fid *fid, void *buf, long count, vlong)
 	qlock(ser);
 	switch(path){
 	case Qdata:
-		nw = write(ser->epout->dfd, buf, count);
-		if(nw != count){
-			dsprint(2, "serial: need to recover, status read %d %r\n",
-				nw);
-			snprint(err, sizeof err, "%r");
-			serialrecover(ser, err);
-		}
-		count = nw;
+		count = altwrite(ser, (uchar *)buf, count);
 		break;
 	case Qctl:
 		cmd = emallocz(count+1, 1);
@@ -512,8 +550,8 @@ openeps(Serial *ser, int epin, int epout, int epintr)
 		return -1;
 	}
 
-	devctl(ser->epin, "timeout 2000");	/* probably a bug */
-	devctl(ser->epout, "timeout 2000");
+	devctl(ser->epin,  "timeout 1000");
+	devctl(ser->epout, "timeout 1000");
 
 	if(ser->hasepintr){
 		ser->epintr = openep(ser->dev, epintr);
@@ -524,20 +562,15 @@ openeps(Serial *ser, int epin, int epout, int epintr)
 			return -1;
 		}
 		opendevdata(ser->epintr, OREAD);
+		devctl(ser->epintr, "timeout 1000");
 	}
 
-	if(debugport){
-		/*
-		 * Debug port uses 8 as maxpkt.
-		 * There should be a Serialops op to
-		 * customize the device at this point.
-		 */
-		devctl(ser->epin, "maxpkt 8");
-		devctl(ser->epout, "maxpkt 8");
-	}
+	if(ser->seteps!= nil)
+		ser->seteps(ser);
 	opendevdata(ser->epin, OREAD);
 	opendevdata(ser->epout, OWRITE);
-	if(ser->epin->dfd < 0 || ser->epout->dfd < 0 || (ser->hasepintr && ser->epintr->dfd < 0)){
+	if(ser->epin->dfd < 0 || ser->epout->dfd < 0 ||
+	    (ser->hasepintr && ser->epintr->dfd < 0)){
 		fprint(2, "serial: open i/o ep data: %r\n");
 		closedev(ser->epin);
 		closedev(ser->epout);
@@ -552,15 +585,26 @@ static int
 findendpoints(Serial *ser)
 {
 	int i, epin, epout, epintr;
-	Ep *ep;
+	Ep *ep, **eps;
 	Usbdev *ud;
 
 	epintr = epin = epout = -1;
 	ud = ser->dev->usb;
-	for(i = 0; i < nelem(ud->ep); i++){
-		if((ep = ud->ep[i]) == nil)
+
+	/*
+	 * interfc 0 means start from the start which is equiv to
+	 * iterate through endpoints probably, could be done better
+	 */
+	if(ser->interfc == 0)
+		eps = ud->ep;
+	else
+		eps = ser->dev->usb->conf[0]->iface[ser->interfc]->ep;
+
+	for(i = 0; i < Niface; i++){
+		if((ep = eps[i]) == nil)
 			continue;
-		if(ser->hasepintr && ep->type == Eintr && ep->dir == Ein && epintr == -1)
+		if(ser->hasepintr && ep->type == Eintr &&
+		    ep->dir == Ein && epintr == -1)
 			epintr = ep->id;
 		if(ep->type == Ebulk){
 			if(ep->dir == Ein && epin == -1)
@@ -576,14 +620,12 @@ findendpoints(Serial *ser)
 	if(openeps(ser, epin, epout, epintr) < 0)
 		return -1;
 
-	dprint(2, "serial: ep in %s out %s\n",
-		ser->epin->dir, ser->epout->dir);
+	dprint(2, "serial: ep in %s out %s\n", ser->epin->dir, ser->epout->dir);
 	if(ser->hasepintr)
-		dprint(2, "disk: ep intr %s\n",
-		ser->epintr->dir);
+		dprint(2, "serial: ep intr %s\n", ser->epintr->dir);
 
 	if(usbdebug > 1 || serialdebug > 2){
-		devctl(ser->epin, "debug 1");
+		devctl(ser->epin,  "debug 1");
 		devctl(ser->epout, "debug 1");
 		if(ser->hasepintr)
 			devctl(ser->epintr, "debug 1");
@@ -592,10 +634,11 @@ findendpoints(Serial *ser)
 	return 0;
 }
 
+/* keep in sync with main.c */
 static int
 usage(void)
 {
-	werrstr("usage: usb/serial [-dkmn] [-a n]");
+	werrstr("usage: usb/serial [-dD] [-m mtpt] [-s srv]");
 	return -1;
 }
 
@@ -611,6 +654,9 @@ serdevfree(void *a)
 	closedev(ser->epin);
 	closedev(ser->epout);
 	ser->epintr = ser->epin = ser->epout = nil;
+	chanfree(ser->w4data);
+	chanfree(ser->gotdata);
+	chanfree(ser->w4empty);
 	free(ser);
 }
 
@@ -639,33 +685,40 @@ serialmain(Dev *dev, int argc, char* argv[])
 		return usage();
 
 	ser = dev->aux = emallocz(sizeof(Serial), 1);
+	/* BUG could this go wrong?,channel leaks? */
+	ser->w4data  = chancreate(sizeof(ulong), 0);
+	ser->gotdata = chancreate(sizeof(ulong), 0);
+	ser->w4empty = chancreate(sizeof(ulong), 0);
+	ser->maxread = ser->maxwrite = 8*1024;
 	ser->dev = dev;
 	dev->free = serdevfree;
 
 	snprint(buf, sizeof buf, "vid %#06x did %#06x",
-			dev->usb->vid, dev->usb->did);
+		dev->usb->vid, dev->usb->did);
 	ser->fs = serialfs;
-	debugport = 0;
 	if(plmatch(buf) == 0){
 		ser->hasepintr = 1;
 		ser->Serialops = plops;
-	}
-	else if(uconsmatch(buf) == 0){
+	} else if(uconsmatch(buf) == 0)
 		ser->Serialops = uconsops;
-		debugport = 1;
+	else if(ftmatch(ser, buf) == 0)
+		ser->Serialops = ftops;
+	else {
+		werrstr("serial: no serial devices found");
+		return -1;
 	}
 
 	if(findendpoints(ser) < 0){
-		werrstr("serial: endpoints not found");
+		werrstr("serial: no endpoints found");
 		return -1;
 	}
-
 	if(serinit(ser) < 0){
 		dprint(2, "serial: serinit: %r\n");
 		return -1;
 	}
 
-	snprint(ser->fs.name, sizeof(ser->fs.name), "eiaU%d", dev->id);
+	snprint(ser->fs.name, sizeof ser->fs.name, "eiaU%d", dev->id);
+	fprint(2, "%s\n", ser->fs.name);
 	ser->fs.dev = dev;
 	incref(dev);
 	ser->fs.aux = ser;

+ 32 - 2
sys/src/cmd/usb/serial/serial.h

@@ -2,16 +2,28 @@ typedef struct Serialops Serialops;
 typedef struct Serial Serial;
 
 struct Serialops {
+	int	(*seteps)(Serial*);
 	int	(*init)(Serial*);
 	int	(*getparam)(Serial*);
 	int	(*setparam)(Serial*);
 	int	(*clearpipes)(Serial*);
+	int	(*reset)(Serial*);
 	int	(*sendlines)(Serial*);
 	int	(*modemctl)(Serial*, int);
 	int	(*setbreak)(Serial*, int);
 	int	(*readstatus)(Serial*);
+	int	(*wait4data)(Serial*, uchar *, int);
+	int	(*wait4write)(Serial*, uchar *, int);
 };
 
+enum{
+	DataBufSz = 8*1024,
+};
+
+/*
+ * TODO: this should have an array of serial devices and
+ * have the common part separated.  Means rethinking locking though.
+ */
 struct Serial {
 	QLock;
 	Dev	*dev;		/* usb device*/
@@ -38,6 +50,8 @@ struct Serial {
 	int	dsr;
 	int	dcd;
 	int	dtr;
+	int	rlsd;
+
 	vlong	timer;
 	int	blocked;	/* for sw flow ctl. BUG: not implemented yet */
 	int	nbreakerr;
@@ -47,13 +61,29 @@ struct Serial {
 	int	novererr;
 	int	enabled;
 
+	int	maxread;
+	int	maxwrite;
+
+	int	inhdrsz;
+	int	outhdrsz;
 	Serialops;
+
+	int	baudbase;	/* for special baud base settings, see ftdi */
+	int	interfc;	/* interfc on the device for ftdi */
+
+	Channel *w4data;
+	Channel *gotdata;
+	Channel *w4empty;
+	int	ndata;
+	uchar	data[DataBufSz];
 };
 
 enum {
 	/* soft flow control chars */
-	CTLS = 023,
-	CTLQ = 021,
+	CTLS	= 023,
+	CTLQ	= 021,
+	CtlDTR	= 1,
+	CtlRTS	= 2,
 };
 
 /*

+ 11 - 9
sys/src/cmd/usb/serial/ucons.c

@@ -6,7 +6,6 @@
 #include "serial.h"
 #include "ucons.h"
 
-
 Cinfo uconsinfo[] = {
 	{ Net20DCVid,	Net20DCDid },
 	{ 0,		0 },
@@ -21,20 +20,23 @@ uconsmatch(char *info)
 	for(ip = uconsinfo; ip->vid != 0; ip++){
 		snprint(buf, sizeof buf, "vid %#06x did %#06x",
 			ip->vid, ip->did);
-		dsprint(2, "serial: %s %s", buf, info);
+		dsprint(2, "serial: %s %s\n", buf, info);
 		if(strstr(info, buf) != nil)
 			return 0;
 	}
 	return -1;
 }
 
+static int
+ucseteps(Serial *ser)
+{
+	ser->maxread = ser->maxwrite = 8;
+	devctl(ser->epin,  "maxpkt 8");
+	devctl(ser->epout, "maxpkt 8");
+	return 0;
+}
 
+/* all nops */
 Serialops uconsops = {
-	.init =		serialnop,
-	.getparam =	serialnop,
-	.setparam =	serialnop,
-	.clearpipes =	serialnop,
-	.sendlines =	serialnop,
-	.modemctl =	serialnopctl,
-	.setbreak =	serialnopctl,
+	.seteps = ucseteps,
 };