]> git.lizzy.rs Git - plan9front.git/commitdiff
added nusb/serial
authoraiju <aiju@phicode.de>
Sat, 30 Jul 2011 17:14:18 +0000 (19:14 +0200)
committeraiju <aiju@phicode.de>
Sat, 30 Jul 2011 17:14:18 +0000 (19:14 +0200)
sys/src/cmd/nusb/mkfile
sys/src/cmd/nusb/serial/ftdi.c [new file with mode: 0644]
sys/src/cmd/nusb/serial/ftdi.h [new file with mode: 0644]
sys/src/cmd/nusb/serial/mkfile [new file with mode: 0644]
sys/src/cmd/nusb/serial/prolific.c [new file with mode: 0644]
sys/src/cmd/nusb/serial/prolific.h [new file with mode: 0644]
sys/src/cmd/nusb/serial/serial.c [new file with mode: 0644]
sys/src/cmd/nusb/serial/serial.h [new file with mode: 0644]
sys/src/cmd/nusb/serial/ucons.c [new file with mode: 0644]
sys/src/cmd/nusb/serial/ucons.h [new file with mode: 0644]

index 9361b70c0071c71963789e9e8cb14e42224b9bf4..2ef11f154d642ed5550327d972c8989a92e11189 100644 (file)
@@ -5,6 +5,8 @@ DIRS=\
        kb\
        audio\
        usbd\
+       disk\
+       serial\
 
 UPDATE=\
        mkfile\
diff --git a/sys/src/cmd/nusb/serial/ftdi.c b/sys/src/cmd/nusb/serial/ftdi.c
new file mode 100644 (file)
index 0000000..ee88e54
--- /dev/null
@@ -0,0 +1,960 @@
+/* Future Technology Devices International serial ports */
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <fcall.h>
+#include <9p.h>
+#include "usb.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, FTOpenRDUltDid},
+       { 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 },
+       { FT8U232AMDid, FT4232HDid },
+       { 0,    0 },
+};
+
+enum {
+       Packsz          = 64,           /* default size */
+       Maxpacksz       = 512,
+       Bufsiz          = 4 * 1024,
+};
+
+static int
+ftdiread(Serialport *p, int index, int req, uchar *buf, int len)
+{
+       int res;
+       Serial *ser;
+
+       ser = p->s;
+
+       if(req != FTGETE2READ)
+               index |= p->interfc + 1;
+       dsprint(2, "serial: ftdiread %#p [%d] req: %#x val: %#x idx:%d buf:%p len:%d\n",
+               p, p->interfc, req, 0, index, buf, len);
+       res = usbcmd(ser->dev,  Rd2h | Rftdireq | Rdev, req, 0, index, buf, len);
+       dsprint(2, "serial: ftdiread res:%d\n", res);
+       return res;
+}
+
+static int
+ftdiwrite(Serialport *p, int val, int index, int req)
+{
+       int res;
+       Serial *ser;
+
+       ser = p->s;
+
+       if(req != FTGETE2READ || req != FTSETE2ERASE || req != FTSETBAUDRATE)
+               index |= p->interfc + 1;
+       dsprint(2, "serial: ftdiwrite %#p [%d] req: %#x val: %#x idx:%d\n",
+               p, p->interfc, 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(Serialport *p, int set)
+{
+       if(set == 0){
+               p->mctl = 0;
+               ftdiwrite(p, 0, 0, FTSETMODEMCTRL);
+               return 0;
+       }
+       p->mctl = 1;
+       ftdiwrite(p, 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:
+       case FTKINDR:
+       case FT2232H:
+       case FT4232H:
+               if(baud <= 3000000)
+                       divval = ft232bmbaud2div(baud);
+               else
+                       divval = ft232bmbaud2div(9600);
+               break;
+       default:
+               divval = ft232bmbaud2div(9600);
+               break;
+       }
+       return divval;
+}
+
+static int
+ftsetparam(Serialport *p)
+{
+       int res;
+       ushort val;
+       ulong bauddiv;
+
+       val = 0;
+       if(p->stop == 1)
+               val |= FTSETDATASTOPBITS1;
+       else if(p->stop == 2)
+               val |= FTSETDATASTOPBITS2;
+       else if(p->stop == 15)
+               val |= FTSETDATASTOPBITS15;
+       switch(p->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(p, val, 0, FTSETDATA);
+       if(res < 0)
+               return res;
+
+       res = ftmodemctl(p, p->mctl);
+       if(res < 0)
+               return res;
+
+       bauddiv = ftbaudcalcdiv(p->s, p->baud);
+       res = ftdiwrite(p, bauddiv, (bauddiv>>16) & 1, FTSETBAUDRATE);
+
+       dsprint(2, "serial: setparam res: %d\n", res);
+       return res;
+}
+
+static int
+hasjtag(Usbdev *udev)
+{
+       /* no string, for now, by default we detect no jtag */
+       if(udev->product != nil && cistrstr(udev->product, "jtag") != nil)
+               return 1;
+       return 0;
+}
+
+/* ser locked */
+static void
+ftgettype(Serial *ser)
+{
+       int i, outhdrsz, dno, pksz;
+       ulong baudbase;
+       Conf *cnf;
+
+       pksz = Packsz;
+       /* Assume it is not the original SIO device for now. */
+       baudbase = ClockNew / 2;
+       outhdrsz = 0;
+       dno = ser->dev->usb->dno;
+       cnf = ser->dev->usb->conf[0];
+       ser->nifcs = 0;
+       for(i = 0; i < Niface; i++)
+               if(cnf->iface[i] != nil)
+                       ser->nifcs++;
+       if(ser->nifcs > 1) {
+               /*
+                * Multiple interfaces.  default assume FT2232C,
+                */
+               if(dno == 0x500)
+                       ser->type = FT2232C;
+               else if(dno == 0x600)
+                       ser->type = FTKINDR;
+               else if(dno == 0x700){
+                       ser->type = FT2232H;
+                       pksz = Maxpacksz;
+               } else if(dno == 0x800){
+                       ser->type = FT4232H;
+                       pksz = Maxpacksz;
+               } else
+                       ser->type = FT2232C;
+
+               if(hasjtag(ser->dev->usb))
+                       ser->jtag = 0;
+
+               /*
+                * BM-type devices have a bug where dno gets set
+                * to 0x200 when serial is 0.
+                */
+               if(dno < 0x500)
+                       fprint(2, "serial: warning: dno %d too low for "
+                               "multi-interface device\n", dno);
+       } 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->maxrtrans = ser->maxwtrans = pksz;
+       ser->baudbase = baudbase;
+       ser->outhdrsz = outhdrsz;
+       ser->inhdrsz = 2;
+
+       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(Serialport *p, uchar *b)
+{
+       if(b[0] & FTICTS)
+               p->cts = 1;
+       else
+               p->cts = 0;
+       if(b[0] & FTIDSR)
+               p->dsr = 1;
+       else
+               p->dsr = 0;
+       if(b[0] & FTIRI)
+               p->ring = 1;
+       else
+               p->ring = 0;
+       if(b[0] & FTIRLSD)
+               p->rlsd = 1;
+       else
+               p->rlsd = 0;
+
+       if(b[1] & FTIOE)
+               p->novererr++;
+       if(b[1] & FTIPE)
+               p->nparityerr++;
+       if(b[1] & FTIFE)
+               p->nframeerr++;
+       if(b[1] & FTIBI)
+               p->nbreakerr++;
+       return 0;
+}
+
+static int
+ftsetouthdr(Serialport *p, uchar *b, int len)
+{
+       if(p->s->outhdrsz != 0)
+               b[0] = FTOPORT | (FTOLENMSK & len);
+       return p->s->outhdrsz;
+}
+
+static int
+wait4data(Serialport *p, uchar *data, int count)
+{
+       int d;
+       Serial *ser;
+
+       ser = p->s;
+
+       qunlock(ser);
+       d = sendul(p->w4data, 1);
+       qlock(ser);
+       if(d <= 0)
+               return -1;
+       if(p->ndata >= count)
+               p->ndata -= count;
+       else{
+               count = p->ndata;
+               p->ndata = 0;
+       }
+       assert(count >= 0);
+       assert(p->ndata >= 0);
+       memmove(data, p->data, count);
+       if(p->ndata != 0)
+               memmove(p->data, p->data+count, p->ndata);
+
+       recvul(p->gotdata);
+       return count;
+}
+
+static int
+wait4write(Serialport *p, uchar *data, int count)
+{
+       int off, fd;
+       uchar *b;
+       Serial *ser;
+
+       ser = p->s;
+
+       b = emallocz(count+ser->outhdrsz, 1);
+       off = ftsetouthdr(p, b, count);
+       memmove(b+off, data, count);
+
+       fd = p->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[Bufsiz];
+};
+
+typedef struct Areader Areader;
+struct Areader{
+       Serialport      *p;
+       Channel *c;
+};
+
+static void
+shutdownchan(Channel *c)
+{
+       Packser *bp;
+
+       while((bp=nbrecvp(c)) != nil)
+               free(bp);
+       chanfree(c);
+}
+
+int
+cpdata(Serial *ser, Serialport *port, uchar *out, uchar *in, int sz)
+{
+       int i, ncp, ntotcp, pksz;
+
+       pksz = ser->maxrtrans;
+       ntotcp = 0;
+
+       for(i = 0; i < sz; i+= pksz){
+               ftuseinhdr(port, in + i);
+               if(sz - i > pksz)
+                       ncp = pksz - ser->inhdrsz;
+               else
+                       ncp = sz - i - ser->inhdrsz;
+               memmove(out, in + i + ser->inhdrsz, ncp);
+               out += ncp;
+               ntotcp += ncp;
+       }
+       return ntotcp;
+}
+
+static void
+epreader(void *u)
+{
+       int dfd, rcount, cl, ntries, recov;
+       char err[40];
+       Areader *a;
+       Channel *c;
+       Packser *pk;
+       Serial *ser;
+       Serialport *p;
+
+       threadsetname("epreader proc");
+       a = u;
+       p = a->p;
+       ser = p->s;
+       c = a->c;
+       free(a);
+
+       qlock(ser);     /* this makes the reader wait end of initialization too */
+       dfd = p->epin->dfd;
+       qunlock(ser);
+
+       ntries = 0;
+       pk = nil;
+       do {
+               if (pk == nil)
+                       pk = emallocz(sizeof(Packser), 1);
+Eagain:
+               rcount = read(dfd, pk->b, sizeof pk->b);
+               if(serialdebug > 5)
+                       dsprint(2, "%d %#ux%#ux ", rcount, p->data[0],
+                               p->data[1]);
+
+               if(rcount < 0){
+                       if(ntries++ > 100)
+                               break;
+                       qlock(ser);
+                       recov = serialrecover(ser, p, nil, "epreader: bulkin error");
+                       qunlock(ser);
+                       if(recov >= 0)
+                               goto Eagain;
+               }
+               if(rcount == 0)
+                       continue;
+               if(rcount >= ser->inhdrsz){
+                       rcount = cpdata(ser, p, pk->b, pk->b, rcount);
+                       if(rcount != 0){
+                               pk->nb = rcount;
+                               cl = sendp(c, pk);
+                               if(cl < 0){
+                                       /*
+                                        * if it was a time-out, I don't want
+                                        * to give back an error.
+                                        */
+                                       rcount = 0;
+                                       break;
+                               }
+                       }else
+                               free(pk);
+                       qlock(ser);
+                       ser->recover = 0;
+                       qunlock(ser);
+                       ntries = 0;
+                       pk = nil;
+               }
+       } while(rcount >= 0 || (rcount < 0 && strstr(err, "timed out") != nil));
+
+       if(rcount < 0)
+               fprint(2, "%s: error reading %s: %r\n", argv0, p->name);
+       free(pk);
+       nbsendp(c, nil);
+       if(p->w4data != nil)
+               chanclose(p->w4data);
+       if(p->gotdata != nil)
+               chanclose(p->gotdata);
+       devctl(ser->dev, "detach");
+       closedev(ser->dev);
+}
+
+static void
+statusreader(void *u)
+{
+       Areader *a;
+       Channel *c;
+       Packser *pk;
+       Serialport *p;
+       Serial *ser;
+       int cl;
+
+       p = u;
+       ser = p->s;
+       threadsetname("statusreader thread");
+       /* big buffering, fewer bytes lost */
+       c = chancreate(sizeof(Packser *), 128);
+       a = emallocz(sizeof(Areader), 1);
+       a->p = p;
+       a->c = c;
+       incref(ser->dev);
+       proccreate(epreader, a, 16*1024);
+
+       while((pk = recvp(c)) != nil){
+               memmove(p->data, pk->b, pk->nb);
+               p->ndata = pk->nb;
+               free(pk);
+               dsprint(2, "serial %p: status reader %d \n", p, p->ndata);
+               /* consume it all */
+               while(p->ndata != 0){
+                       dsprint(2, "serial %p: status reader to consume: %d\n",
+                               p, p->ndata);
+                       cl = recvul(p->w4data);
+                       if(cl  < 0)
+                               break;
+                       cl = sendul(p->gotdata, 1);
+                       if(cl  < 0)
+                               break;
+               }
+       }
+
+       shutdownchan(c);
+       devctl(ser->dev, "detach");
+       closedev(ser->dev);
+}
+
+static int
+ftreset(Serial *ser, Serialport *p)
+{
+       int i;
+
+       if(p != nil){
+               ftdiwrite(p, FTRESETCTLVAL, 0, FTRESET);
+               return 0;
+       }
+       p = ser->p;
+       for(i = 0; i < Maxifc; i++)
+               if(p[i].s != nil)
+                       ftdiwrite(&p[i], FTRESETCTLVAL, 0, FTRESET);
+       return 0;
+}
+
+static int
+ftinit(Serialport *p)
+{
+       Serial *ser;
+       uint timerval;
+       int res;
+
+       ser = p->s;
+       if(p->isjtag){
+               res = ftdiwrite(p, FTSETFLOWCTRL, 0, FTDISABLEFLOWCTRL);
+               if(res < 0)
+                       return -1;
+               res = ftdiread(p, FTSETLATENCYTIMER, 0, (uchar *)&timerval,
+                       FTLATENCYTIMERSZ);
+               if(res < 0)
+                       return -1;
+               dsprint(2, "serial: jtag latency timer is %d\n", timerval);
+               timerval = 2;
+               ftdiwrite(p, FTLATENCYDEFAULT, 0, FTSETLATENCYTIMER);
+               res = ftdiread(p, FTSETLATENCYTIMER, 0, (uchar *)&timerval,
+                       FTLATENCYTIMERSZ);
+               if(res < 0)
+                       return -1;
+
+               dsprint(2, "serial: jtag latency timer set to %d\n", timerval);
+               /* may be unnecessary */
+               devctl(p->epin,  "timeout 5000");
+               devctl(p->epout, "timeout 5000");
+               /* 0xb is the mask for lines. plug dependant? */
+               ftdiwrite(p, BMMPSSE|0x0b, 0, FTSETBITMODE);
+       }
+       incref(ser->dev);
+       threadcreate(statusreader, p, 8*1024);
+       return 0;
+}
+
+static int
+ftsetbreak(Serialport *p, int val)
+{
+       return ftdiwrite(p, (val != 0? FTSETBREAK: 0), 0, FTSETDATA);
+}
+
+static int
+ftclearpipes(Serialport *p)
+{
+       /* maybe can be done in one... */
+       ftdiwrite(p, FTRESETCTLVALPURGETX, 0, FTRESET);
+       ftdiwrite(p, FTRESETCTLVALPURGERX, 0, FTRESET);
+       return 0;
+}
+
+static int
+setctlline(Serialport *p, uchar val)
+{
+       return ftdiwrite(p, val | (val << 8), 0, FTSETMODEMCTRL);
+}
+
+static void
+updatectlst(Serialport *p, int val)
+{
+       if(p->rts)
+               p->ctlstate |= val;
+       else
+               p->ctlstate &= ~val;
+}
+
+static int
+setctl(Serialport *p)
+{
+       int res;
+       Serial *ser;
+
+       ser = p->s;
+
+       if(ser->dev->usb->vid == FTVid && ser->dev->usb->did ==  FTHETIRA1Did){
+               fprint(2, "serial: cannot set lines for this device\n");
+               updatectlst(p, CtlRTS|CtlDTR);
+               p->rts = p->dtr = 1;
+               return -1;
+       }
+
+       /* NB: you can not set DTR and RTS with one control message */
+       updatectlst(p, CtlRTS);
+       res = setctlline(p, (CtlRTS<<8)|p->ctlstate);
+       if(res < 0)
+               return res;
+
+       updatectlst(p, CtlDTR);
+       res = setctlline(p, (CtlDTR<<8)|p->ctlstate);
+       if(res < 0)
+               return res;
+
+       return 0;
+}
+
+static int
+ftsendlines(Serialport *p)
+{
+       int res;
+
+       dsprint(2, "serial: sendlines: %#2.2x\n", p->ctlstate);
+       res = setctl(p);
+       dsprint(2, "serial: sendlines res: %d\n", res);
+       return 0;
+}
+
+static int
+ftseteps(Serialport *p)
+{
+       char *s;
+       Serial *ser;
+
+       ser = p->s;
+
+       s = smprint("maxpkt %d", ser->maxrtrans);
+       devctl(p->epin, s);
+       free(s);
+
+       s = smprint("maxpkt %d", ser->maxwtrans);
+       devctl(p->epout, s);
+       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,
+};
diff --git a/sys/src/cmd/nusb/serial/ftdi.h b/sys/src/cmd/nusb/serial/ftdi.h
new file mode 100644 (file)
index 0000000..7abb638
--- /dev/null
@@ -0,0 +1,632 @@
+enum {
+       /* used by devices which don't provide their own Vid */
+       FTVid           = 0x0403,
+
+       FTSheevaVid     = 0x9E88,
+       FTSheevaDid     = 0x9E8F,
+       FTOpenRDUltDid  = 0x9E90,
+
+       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) */
+       /*
+        * new high speed devices
+        */
+       FT4232HDid      = 0x6011,               /* FTDI FT4232H based device */
+
+};
+
+/* 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 */
+       FTUNKNOWN,
+       FTSETLATENCYTIMER,              /* Set the latency timer */
+       FTGETLATENCYTIMER,              /* Get the latency timer */
+       FTSETBITMODE,                   /* Set bit mode */
+       FTGETPINS,                      /* Read pins state */
+       FTGETE2READ     = 0x90,         /* Read address from 128-byte I2C EEPROM */
+       FTSETE2WRITE,                   /* Write to address on 128-byte I2C EEPROM */
+       FTSETE2ERASE,                   /* Erase address on 128-byte I2C EEPROM */
+};
+
+/* Port Identifier Table, index for interfaces */
+enum {
+       PITDEFAULT = 0,         /* SIOA */
+       PITA,                   /* SIOA jtag if there is one */
+};
+
+enum {
+       Rftdireq = 1<<6,                /* bit for type of request */
+};
+
+/*
+ * Commands Data size
+ * Sets have wLength = 0
+ * Gets have wValue = 0
+ */
+enum {
+       FTMODEMSTATUSSZ = 1,
+       FTLATENCYTIMERSZ= 1,
+       FTPINSSZ        = 1,
+       FTE2READSZ      = 2,
+};
+
+/*
+ * bRequest: FTGETE2READ
+ * wIndex: Address of word to read
+ * Data: Will return a word (2 bytes) of data from E2Address
+ * Results put in the I2C 128 byte EEPROM string eeprom+(2*index)
+ */
+
+/*
+ * bRequest: FTSETE2WRITE
+ * wIndex: Address of word to read
+ * wValue: Value of the word
+ * Data: Will return a word (2 bytes) of data from E2Address
+ */
+
+/*
+ * bRequest: FTSETE2ERASE
+ * Erases the EEPROM
+ * wIndex: 0
+ */
+
+/*
+ * 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,
+       FTKINDR         = 5,
+       FT2232H         = 6,
+       FT4232H         = 7,
+};
+
+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
+ *     bits 0-7 number of data bits
+ * 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 protocol; 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: FTSETBITMODE
+ * wIndex: Port
+ * either it is big bang mode, in which case
+ * wValue: 1 byte L is the big bang mode BIG*
+ *     or BM is
+ * wValue: 1 byte bitbang mode H, 1 byte bitmask for lines L
+ */
+enum {
+       BMSERIAL        = 0,            /* reset, turn off bit-bang mode */
+
+       BIGBMNORMAL     = 1,            /* normal bit-bang mode */
+       BIGBMSPI        = 2,            /* spi bit-bang mode */
+
+       BMABM           = 1<<8,         /* async mode */
+       BMMPSSE         = 2<<8,
+       BMSYNCBB        = 4<<8,         /* sync bit-bang -- 2232x and R-type */
+       BMMCU           = 8<<8,         /* MCU Host Bus -- 2232x */
+       BMOPTO          = 0x10<<8,      /* opto-isolated<<8, 2232x */
+       BMCBUS          = 0x20<<8,      /* CBUS pins of R-type chips */
+       BMSYNCFF        = 0x40<<8,      /* Single Channel Sync FIFO, 2232H only */
+};
+
+/*
+ * bRequest: FTSETLATENCYTIMER
+ * wValue: Latency (milliseconds 1-255)
+ * wIndex: Port
+ */
+enum {
+       FTLATENCYDEFAULT = 2,
+};
+
+/*
+ * BmRequestType: SET
+ * bRequest: FTSETEVENTCHAR
+ * wValue: EventChar
+ * wIndex: Port
+ * 0-7 lower bits event char
+ * 8 enable
+ */
+enum {
+       FTEVCHARENAB = 1<<8,
+};
+
+/*
+ * BmRequestType: SET
+ * bRequest: FTSETERRORCHAR
+ * wValue: Error Char
+ * wIndex: Port
+ * 0-7 lower bits event char
+ * 8 enable
+ */
+enum {
+       FTERRCHARENAB = 1<<8,
+};
+/*
+ * 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);
diff --git a/sys/src/cmd/nusb/serial/mkfile b/sys/src/cmd/nusb/serial/mkfile
new file mode 100644 (file)
index 0000000..a1df868
--- /dev/null
@@ -0,0 +1,23 @@
+</$objtype/mkfile
+
+TARG=serial
+OFILES=ftdi.$O serial.$O prolific.$O ucons.$O
+HFILES=\
+       ../lib/usb.h\
+       ftdi.h\
+       prolific.h\
+       serial.h\
+       ucons.h\
+
+LIB=../lib/usb.a$O
+
+BIN=/$objtype/bin/nusb
+
+UPDATE=\
+       mkfile\
+       $HFILES\
+       ${OFILES:%.$O=%.c}\
+
+</sys/src/cmd/mkone
+
+CFLAGS=-I../lib $CFLAGS
diff --git a/sys/src/cmd/nusb/serial/prolific.c b/sys/src/cmd/nusb/serial/prolific.c
new file mode 100644 (file)
index 0000000..5035c2f
--- /dev/null
@@ -0,0 +1,439 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <fcall.h>
+#include <9p.h>
+#include "usb.h"
+#include "serial.h"
+#include "prolific.h"
+
+Cinfo plinfo[] = {
+       { PL2303Vid,    PL2303Did },
+       { PL2303Vid,    PL2303DidRSAQ2 },
+       { PL2303Vid,    PL2303DidDCU11 },
+       { PL2303Vid,    PL2303DidRSAQ3 },
+       { PL2303Vid,    PL2303DidPHAROS },
+       { PL2303Vid,    PL2303DidALDIGA },
+       { PL2303Vid,    PL2303DidMMX },
+       { PL2303Vid,    PL2303DidGPRS },
+       { IODATAVid,    IODATADid },
+       { IODATAVid,    IODATADidRSAQ5 },
+       { ATENVid,      ATENDid },
+       { ATENVid2,     ATENDid },
+       { ELCOMVid,     ELCOMDid },
+       { ELCOMVid,     ELCOMDidUCSGT },
+       { ITEGNOVid,    ITEGNODid },
+       { ITEGNOVid,    ITEGNODid2080 },
+       { MA620Vid,     MA620Did },
+       { RATOCVid,     RATOCDid },
+       { TRIPPVid,     TRIPPDid },
+       { RADIOSHACKVid,RADIOSHACKDid },
+       { DCU10Vid,     DCU10Did },
+       { SITECOMVid,   SITECOMDid },
+       { ALCATELVid,   ALCATELDid },
+       { SAMSUNGVid,   SAMSUNGDid },
+       { SIEMENSVid,   SIEMENSDidSX1 },
+       { SIEMENSVid,   SIEMENSDidX65 },
+       { SIEMENSVid,   SIEMENSDidX75 },
+       { SIEMENSVid,   SIEMENSDidEF81 },
+       { SYNTECHVid,   SYNTECHDid },
+       { NOKIACA42Vid, NOKIACA42Did },
+       { CA42CA42Vid,  CA42CA42Did },
+       { SAGEMVid,     SAGEMDid },
+       { LEADTEKVid,   LEADTEK9531Did },
+       { SPEEDDRAGONVid,SPEEDDRAGONDid },
+       { DATAPILOTU2Vid,DATAPILOTU2Did },
+       { BELKINVid,    BELKINDid },
+       { ALCORVid,     ALCORDid },
+       { WS002INVid,   WS002INDid },
+       { COREGAVid,    COREGADid },
+       { YCCABLEVid,   YCCABLEDid },
+       { SUPERIALVid,  SUPERIALDid },
+       { HPVid,        HPLD220Did },
+       { 0,            0 },
+};
+
+int
+plmatch(char *info)
+{
+       Cinfo *ip;
+       char buf[50];
+
+       for(ip = plinfo; 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)
+                       return 0;
+       }
+       return -1;
+}
+
+static void    statusreader(void *u);
+
+static void
+dumpbuf(uchar *buf, int bufsz)
+{
+       int i;
+
+       for(i=0; i<bufsz; i++)
+               print("buf[%d]=%#ux ", i, buf[i]);
+       print("\n");
+}
+
+static int
+vendorread(Serialport *p, int val, int index, uchar *buf)
+{
+       int res;
+       Serial *ser;
+
+       ser = p->s;
+
+       dsprint(2, "serial: vendorread val: 0x%x idx:%d buf:%p\n",
+               val, index, buf);
+       res = usbcmd(ser->dev,  Rd2h | Rvendor | Rdev, VendorReadReq,
+               val, index, buf, 1);
+       dsprint(2, "serial: vendorread res:%d\n", res);
+       return res;
+}
+
+static int
+vendorwrite(Serialport *p, int val, int index)
+{
+       int res;
+       Serial *ser;
+
+       ser = p->s;
+
+       dsprint(2, "serial: vendorwrite val: 0x%x idx:%d\n", val, index);
+       res = usbcmd(ser->dev, Rh2d | Rvendor | Rdev, VendorWriteReq,
+               val, index, nil, 0);
+       dsprint(2, "serial: vendorwrite res:%d\n", res);
+       return res;
+}
+
+/* BUG: I could probably read Dcr0 and set only the bits */
+static int
+plmodemctl(Serialport *p, int set)
+{
+       Serial *ser;
+
+       ser = p->s;
+
+       if(set == 0){
+               p->mctl = 0;
+               vendorwrite(p, Dcr0Idx|DcrSet, Dcr0Init);
+               return 0;
+       }
+
+       p->mctl = 1;
+       if(ser->type == TypeHX)
+               vendorwrite(p, Dcr0Idx|DcrSet, Dcr0Init|Dcr0HwFcX);
+       else
+               vendorwrite(p, Dcr0Idx|DcrSet, Dcr0Init|Dcr0HwFcH);
+       return 0;
+}
+
+static int
+plgetparam(Serialport *p)
+{
+       uchar buf[ParamReqSz];
+       int res;
+       Serial *ser;
+
+       ser = p->s;
+
+
+       res = usbcmd(ser->dev, Rd2h | Rclass | Riface, GetLineReq,
+               0, 0, buf, sizeof buf);
+       p->baud = GET4(buf);
+
+       /*
+        * with the Pl9 interface it is not possible to set `1.5' as stop bits
+        * for the prologic:
+        *      0 is 1 stop bit
+        *      1 is 1.5 stop bits
+        *      2 is 2 stop bits
+        */
+       if(buf[4] == 1)
+               fprint(2, "warning, stop bit set to 1.5 unsupported");
+       else if(buf[4] == 0)
+               p->stop = 1;
+       else if(buf[4] == 2)
+               p->stop = 2;
+       p->parity = buf[5];
+       p->bits = buf[6];
+
+       dsprint(2, "serial: getparam: ");
+       if(serialdebug)
+               dumpbuf(buf, sizeof buf);
+       dsprint(2, "serial: getparam res: %d\n", res);
+       return res;
+}
+
+static int
+plsetparam(Serialport *p)
+{
+       uchar buf[ParamReqSz];
+       int res;
+       Serial *ser;
+
+       ser = p->s;
+
+       PUT4(buf, p->baud);
+
+       if(p->stop == 1)
+               buf[4] = 0;
+       else if(p->stop == 2)
+               buf[4] = 2;                     /* see comment in getparam */
+       buf[5] = p->parity;
+       buf[6] = p->bits;
+
+       dsprint(2, "serial: setparam: ");
+       if(serialdebug)
+               dumpbuf(buf, sizeof buf);
+       res = usbcmd(ser->dev, Rh2d | Rclass | Riface, SetLineReq,
+               0, 0, buf, sizeof buf);
+       plmodemctl(p, p->mctl);
+       plgetparam(p);          /* make sure our state corresponds */
+
+       dsprint(2, "serial: setparam res: %d\n", res);
+       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(Serialport *p)
+{
+       char *st;
+       uchar *buf;
+       ulong csp, maxpkt, dno;
+       Serial *ser;
+
+       ser = p->s;
+       buf = emallocz(VendorReqSz, 1);
+       dsprint(2, "plinit\n");
+
+       csp = ser->dev->usb->csp;
+       maxpkt = ser->dev->maxpkt;
+       dno = ser->dev->usb->dno;
+
+       if((ser->type = revid(dno)) == TypeUnk)
+               ser->type = heuristicid(csp, maxpkt);
+
+       dsprint(2, "serial: type %d\n", ser->type);
+
+       vendorread(p, 0x8484, 0, buf);
+       vendorwrite(p, 0x0404, 0);
+       vendorread(p, 0x8484, 0, buf);
+       vendorread(p, 0x8383, 0, buf);
+       vendorread(p, 0x8484, 0, buf);
+       vendorwrite(p, 0x0404, 1);
+       vendorread(p, 0x8484, 0, buf);
+       vendorread(p, 0x8383, 0, buf);
+
+       vendorwrite(p, Dcr0Idx|DcrSet, Dcr0Init);
+       vendorwrite(p, Dcr1Idx|DcrSet, Dcr1Init);
+
+       if(ser->type == TypeHX)
+               vendorwrite(p, Dcr2Idx|DcrSet, Dcr2InitX);
+       else
+               vendorwrite(p, Dcr2Idx|DcrSet, Dcr2InitH);
+
+       plgetparam(p);
+       qunlock(ser);
+       free(buf);
+       st = emallocz(255, 1);
+       qlock(ser);
+       if(serialdebug)
+               serdumpst(p, st, 255);
+       dsprint(2, st);
+       free(st);
+       /* p gets freed by closedev, the process has a reference */
+       incref(ser->dev);
+       proccreate(statusreader, p, 8*1024);
+       return 0;
+}
+
+static int
+plsetbreak(Serialport *p, int val)
+{
+       Serial *ser;
+
+       ser = p->s;
+       return usbcmd(ser->dev, Rh2d | Rclass | Riface,
+               (val != 0? BreakOn: BreakOff), val, 0, nil, 0);
+}
+
+static int
+plclearpipes(Serialport *p)
+{
+       Serial *ser;
+
+       ser = p->s;
+
+       if(ser->type == TypeHX){
+               vendorwrite(p, PipeDSRst, 0);
+               vendorwrite(p, PipeUSRst, 0);
+       }else{
+               if(unstall(ser->dev, p->epout, Eout) < 0)
+                       dprint(2, "disk: unstall epout: %r\n");
+               if(unstall(ser->dev, p->epin, Ein) < 0)
+                       dprint(2, "disk: unstall epin: %r\n");
+               if(unstall(ser->dev, p->epintr, Ein) < 0)
+                       dprint(2, "disk: unstall epintr: %r\n");
+       }
+       return 0;
+}
+
+static int
+setctlline(Serialport *p, uchar val)
+{
+       Serial *ser;
+
+       ser = p->s;
+       return usbcmd(ser->dev, Rh2d | Rclass | Riface, SetCtlReq,
+               val, 0, nil, 0);
+}
+
+static void
+composectl(Serialport *p)
+{
+       if(p->rts)
+               p->ctlstate |= CtlRTS;
+       else
+               p->ctlstate &= ~CtlRTS;
+       if(p->dtr)
+               p->ctlstate |= CtlDTR;
+       else
+               p->ctlstate &= ~CtlDTR;
+}
+
+static int
+plsendlines(Serialport *p)
+{
+       int res;
+
+       dsprint(2, "serial: sendlines: %#2.2x\n", p->ctlstate);
+       composectl(p);
+       res = setctlline(p, p->ctlstate);
+       dsprint(2, "serial: sendlines res: %d\n", res);
+       return 0;
+}
+
+static int
+plreadstatus(Serialport *p)
+{
+       int nr, dfd;
+       char err[40];
+       uchar buf[VendorReqSz];
+       Serial *ser;
+
+       ser = p->s;
+
+       qlock(ser);
+       dsprint(2, "serial: reading from interrupt\n");
+       dfd = p->epintr->dfd;
+
+       qunlock(ser);
+       nr = read(dfd, buf, sizeof buf);
+       qlock(ser);
+       snprint(err, sizeof err, "%r");
+       dsprint(2, "serial: interrupt read %d %r\n", nr);
+
+       if(nr < 0 && strstr(err, "timed out") == nil){
+               dsprint(2, "serial: need to recover, status read %d %r\n", nr);
+               if(serialrecover(ser, nil, nil, err) < 0){
+                       qunlock(ser);
+                       return -1;
+               }
+       }
+       if(nr < 0)
+               dsprint(2, "serial: reading status: %r");
+       else if(nr >= sizeof buf - 1){
+               p->dcd = buf[8] & DcdStatus;
+               p->dsr = buf[8] & DsrStatus;
+               p->cts = buf[8] & BreakerrStatus;
+               p->ring = buf[8] & RingStatus;
+               p->cts = buf[8] & CtsStatus;
+               if(buf[8] & FrerrStatus)
+                       p->nframeerr++;
+               if(buf[8] & ParerrStatus)
+                       p->nparityerr++;
+               if(buf[8] & OvererrStatus)
+                       p->novererr++;
+       } else
+               dsprint(2, "serial: bad status read %d\n", nr);
+       dsprint(2, "serial: finished read from interrupt %d\n", nr);
+       qunlock(ser);
+       return 0;
+}
+
+static void
+statusreader(void *u)
+{
+       Serialport *p;
+       Serial *ser;
+
+       p = u;
+       ser = p->s;
+       threadsetname("statusreaderproc");
+       while(plreadstatus(p) >= 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(Serialport *p)
+{
+       devctl(p->epin,  "maxpkt 256");
+       devctl(p->epout, "maxpkt 256");
+       return 0;
+}
+
+Serialops plops = {
+       .init           = plinit,
+       .getparam       = plgetparam,
+       .setparam       = plsetparam,
+       .clearpipes     = plclearpipes,
+       .sendlines      = plsendlines,
+       .modemctl       = plmodemctl,
+       .setbreak       = plsetbreak,
+       .seteps         = plseteps,
+};
diff --git a/sys/src/cmd/nusb/serial/prolific.h b/sys/src/cmd/nusb/serial/prolific.h
new file mode 100644 (file)
index 0000000..d072793
--- /dev/null
@@ -0,0 +1,178 @@
+enum {
+       /* flavours of the device */
+       TypeH,
+       TypeHX,
+       TypeUnk,
+
+       RevH            = 0x0202,
+       RevX            = 0x0300,
+       RevHX           = 0x0400,
+       Rev1            = 0x0001,
+
+       /* usbcmd parameters */
+       SetLineReq      = 0x20,
+
+       SetCtlReq       = 0x22,
+
+       BreakReq        = 0x23,
+       BreakOn         = 0xffff,
+       BreakOff        = 0x0000,
+
+       GetLineReq      = 0x21,
+
+       VendorWriteReq  = 0x01,         /* BUG: is this a standard request? */
+       VendorReadReq   = 0x01,
+
+       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,
+
+       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,
+
+       ATENVid         = 0x0557,
+       ATENVid2        = 0x0547,
+       ATENDid         = 0x2008,
+
+       IODATAVid       = 0x04bb,
+       IODATADid       = 0x0a03,
+       IODATADidRSAQ5  = 0x0a0e,
+
+       ELCOMVid        = 0x056e,
+       ELCOMDid        = 0x5003,
+       ELCOMDidUCSGT   = 0x5004,
+
+       ITEGNOVid       = 0x0eba,
+       ITEGNODid       = 0x1080,
+       ITEGNODid2080   = 0x2080,
+
+       MA620Vid        = 0x0df7,
+       MA620Did        = 0x0620,
+
+       RATOCVid        = 0x0584,
+       RATOCDid        = 0xb000,
+
+       TRIPPVid        = 0x2478,
+       TRIPPDid        = 0x2008,
+
+       RADIOSHACKVid   = 0x1453,
+       RADIOSHACKDid   = 0x4026,
+
+       DCU10Vid        = 0x0731,
+       DCU10Did        = 0x0528,
+
+       SITECOMVid      = 0x6189,
+       SITECOMDid      = 0x2068,
+
+        /* Alcatel OT535/735 USB cable */
+       ALCATELVid      = 0x11f7,
+       ALCATELDid      = 0x02df,
+
+       /* Samsung I330 phone cradle */
+       SAMSUNGVid      = 0x04e8,
+       SAMSUNGDid      = 0x8001,
+
+       SIEMENSVid      = 0x11f5,
+       SIEMENSDidSX1   = 0x0001,
+       SIEMENSDidX65   = 0x0003,
+       SIEMENSDidX75   = 0x0004,
+       SIEMENSDidEF81  = 0x0005,
+
+       SYNTECHVid      = 0x0745,
+       SYNTECHDid      = 0x0001,
+
+       /* Nokia CA-42 Cable */
+       NOKIACA42Vid    = 0x078b,
+       NOKIACA42Did    = 0x1234,
+
+       /* CA-42 CLONE Cable www.ca-42.com chipset: Prolific Technology Inc */
+       CA42CA42Vid     = 0x10b5,
+       CA42CA42Did     = 0xac70,
+
+       SAGEMVid        = 0x079b,
+       SAGEMDid        = 0x0027,
+
+       /* Leadtek GPS 9531 (ID 0413:2101) */
+       LEADTEKVid      = 0x0413,
+       LEADTEK9531Did  = 0x2101,
+
+        /* USB GSM cable from Speed Dragon Multimedia, Ltd */
+       SPEEDDRAGONVid  = 0x0e55,
+       SPEEDDRAGONDid  = 0x110b,
+
+       /* DATAPILOT Universal-2 Phone Cable */
+       BELKINVid       = 0x050d,
+       BELKINDid       = 0x0257,
+
+       /* Belkin "F5U257" Serial Adapter */
+       DATAPILOTU2Vid  = 0x0731,
+       DATAPILOTU2Did  = 0x2003,
+
+       ALCORVid        = 0x058F,
+       ALCORDid        = 0x9720,
+
+       /* Willcom WS002IN Data Driver (by NetIndex Inc.) */,
+       WS002INVid      = 0x11f6,
+       WS002INDid      = 0x2001,
+
+       /* Corega CG-USBRS232R Serial Adapter */,
+       COREGAVid       = 0x07aa,
+       COREGADid       = 0x002a,
+
+       /* Y.C. Cable U.S.A., Inc - USB to RS-232 */,
+       YCCABLEVid      = 0x05ad,
+       YCCABLEDid      = 0x0fba,
+
+       /* "Superial" USB - Serial */,
+       SUPERIALVid     = 0x5372,
+       SUPERIALDid     = 0x2303,
+
+       /* Hewlett-Packard LD220-HP POS Pole Display */,
+       HPVid           = 0x03f0,
+       HPLD220Did      = 0x3524,
+};
+
+extern Serialops plops;
+int    plmatch(char *info);
diff --git a/sys/src/cmd/nusb/serial/serial.c b/sys/src/cmd/nusb/serial/serial.c
new file mode 100644 (file)
index 0000000..4a00032
--- /dev/null
@@ -0,0 +1,876 @@
+/*
+ * This part takes care of locking except for initialization and
+ * other threads created by the hw dep. drivers.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <thread.h>
+#include <fcall.h>
+#include <9p.h>
+#include "usb.h"
+#include "serial.h"
+#include "prolific.h"
+#include "ucons.h"
+#include "ftdi.h"
+
+int serialdebug;
+static int sdebug;
+
+Serialport **ports;
+int nports;
+
+static void
+serialfatal(Serial *ser)
+{
+       Serialport *p;
+       int i;
+
+       dsprint(2, "serial: fatal error, detaching\n");
+       devctl(ser->dev, "detach");
+
+       for(i = 0; i < ser->nifcs; i++){
+               p = &ser->p[i];
+               if(p->w4data != nil)
+                       chanclose(p->w4data);
+               if(p->gotdata != nil)
+                       chanclose(p->gotdata);
+               if(p->readc)
+                       chanclose(p->readc);
+       }
+}
+
+/* I sleep with the lock... only way to drain in general */
+static void
+serialdrain(Serialport *p)
+{
+       Serial *ser;
+       uint baud, pipesize;
+
+       ser = p->s;
+       baud = p->baud;
+
+       if(p->baud == ~0)
+               return;
+       if(ser->maxwtrans < 256)
+               pipesize = 256;
+       else
+               pipesize = ser->maxwtrans;
+       /* wait for the at least 256-byte pipe to clear */
+       sleep(10 + pipesize/((1 + baud)*1000));
+       if(ser->clearpipes != nil)
+               ser->clearpipes(p);
+}
+
+int
+serialreset(Serial *ser)
+{
+       Serialport *p;
+       int i, res;
+
+       res = 0;
+       /* cmd for reset */
+       for(i = 0; i < ser->nifcs; i++){
+               p = &ser->p[i];
+               serialdrain(p);
+       }
+       if(ser->reset != nil)
+               res = ser->reset(ser, nil);
+       return res;
+}
+
+/* call this if something goes wrong, must be qlocked */
+int
+serialrecover(Serial *ser, Serialport *p, Dev *ep, char *err)
+{
+       if(p != nil)
+               dprint(2, "serial[%d], %s: %s, level %d\n", p->interfc,
+                       p->name, err, ser->recover);
+       else
+               dprint(2, "serial[%s], global error, level %d\n",
+                       ser->p[0].name, ser->recover);
+       ser->recover++;
+       if(strstr(err, "detached") != nil)
+               return -1;
+       if(ser->recover < 3){
+               if(p != nil){   
+                       if(ep != nil){
+                               if(ep == p->epintr)
+                                       unstall(ser->dev, p->epintr, Ein);
+                               if(ep == p->epin)
+                                       unstall(ser->dev, p->epin, Ein);
+                               if(ep == p->epout)
+                                       unstall(ser->dev, p->epout, Eout);
+                               return 0;
+                       }
+
+                       if(p->epintr != nil)
+                               unstall(ser->dev, p->epintr, Ein);
+                       if(p->epin != nil)
+                               unstall(ser->dev, p->epin, Ein);
+                       if(p->epout != nil)
+                               unstall(ser->dev, p->epout, Eout);
+               }
+               return 0;
+       }
+       if(ser->recover > 4 && ser->recover < 8)
+               serialfatal(ser);
+       if(ser->recover > 8){
+               ser->reset(ser, p);
+               return 0;
+       }
+       if(serialreset(ser) < 0)
+               return -1;
+       return 0;
+}
+
+static int
+serialctl(Serialport *p, char *cmd)
+{
+       Serial *ser;
+       int c, i, n, nf, nop, nw, par, drain, set, lines;
+       char *f[16];
+       uchar x;
+
+       ser = p->s;
+       drain = set = lines = 0;
+       nf = tokenize(cmd, f, nelem(f));
+       for(i = 0; i < nf; i++){
+               if(strncmp(f[i], "break", 5) == 0){
+                       if(ser->setbreak != nil)
+                               ser->setbreak(p, 1);
+                       continue;
+               }
+
+               nop = 0;
+               n = atoi(f[i]+1);
+               c = *f[i];
+               if (isascii(c) && isupper(c))
+                       c = tolower(c);
+               switch(c){
+               case 'b':
+                       drain++;
+                       p->baud = n;
+                       set++;
+                       break;
+               case 'c':
+                       p->dcd = n;
+                       // lines++;
+                       ++nop;
+                       break;
+               case 'd':
+                       p->dtr = n;
+                       lines++;
+                       break;
+               case 'e':
+                       p->dsr = n;
+                       // lines++;
+                       ++nop;
+                       break;
+               case 'f':               /* flush the pipes */
+                       drain++;
+                       break;
+               case 'h':               /* hangup?? */
+                       p->rts = p->dtr = 0;
+                       lines++;
+                       fprint(2, "serial: %c, unsure ctl\n", c);
+                       break;
+               case 'i':
+                       ++nop;
+                       break;
+               case 'k':
+                       drain++;
+                       ser->setbreak(p, 1);
+                       sleep(n);
+                       ser->setbreak(p, 0);
+                       break;
+               case 'l':
+                       drain++;
+                       p->bits = n;
+                       set++;
+                       break;
+               case 'm':
+                       drain++;
+                       if(ser->modemctl != nil)
+                               ser->modemctl(p, n);
+                       if(n == 0)
+                               p->cts = 0;
+                       break;
+               case 'n':
+                       p->blocked = n;
+                       ++nop;
+                       break;
+               case 'p':               /* extended... */
+                       if(strlen(f[i]) != 2)
+                               return -1;
+                       drain++;
+                       par = f[i][1];
+                       if(par == 'n')
+                               p->parity = 0;
+                       else if(par == 'o')
+                               p->parity = 1;
+                       else if(par == 'e')
+                               p->parity = 2;
+                       else if(par == 'm')     /* mark parity */
+                               p->parity = 3;
+                       else if(par == 's')     /* space parity */
+                               p->parity = 4;
+                       else
+                               return -1;
+                       set++;
+                       break;
+               case 'q':
+                       // drain++;
+                       p->limit = n;
+                       ++nop;
+                       break;
+               case 'r':
+                       drain++;
+                       p->rts = n;
+                       lines++;
+                       break;
+               case 's':
+                       drain++;
+                       p->stop = n;
+                       set++;
+                       break;
+               case 'w':
+                       /* ?? how do I put this */
+                       p->timer = n * 100000LL;
+                       ++nop;
+                       break;
+               case 'x':
+                       if(n == 0)
+                               x = CTLS;
+                       else
+                               x = CTLQ;
+                       if(ser->wait4write != nil)
+                               nw = ser->wait4write(p, &x, 1);
+                       else
+                               nw = write(p->epout->dfd, &x, 1);
+                       if(nw != 1){
+                               serialrecover(ser, p, p->epout, "");
+                               return -1;
+                       }
+                       break;
+               }
+               /*
+                * don't print.  the condition is harmless and the print
+                * splatters all over the display.
+                */
+               USED(nop);
+               if (0 && nop)
+                       fprint(2, "serial: %c, unsupported nop ctl\n", c);
+       }
+       if(drain)
+               serialdrain(p);
+       if(lines && !set){
+               if(ser->sendlines != nil && ser->sendlines(p) < 0)
+                       return -1;
+       } else if(set){
+               if(ser->setparam != nil && ser->setparam(p) < 0)
+                       return -1;
+       }
+       ser->recover = 0;
+       return 0;
+}
+
+char *pformat = "noems";
+
+char *
+serdumpst(Serialport *p, char *buf, int bufsz)
+{
+       char *e, *s;
+       Serial *ser;
+
+       ser = p->s;
+
+       e = buf + bufsz;
+       s = seprint(buf, e, "b%d ", p->baud);
+       s = seprint(s, e, "c%d ", p->dcd);      /* unimplemented */
+       s = seprint(s, e, "d%d ", p->dtr);
+       s = seprint(s, e, "e%d ", p->dsr);      /* unimplemented */
+       s = seprint(s, e, "l%d ", p->bits);
+       s = seprint(s, e, "m%d ", p->mctl);
+       if(p->parity >= 0 || p->parity < strlen(pformat))
+               s = seprint(s, e, "p%c ", pformat[p->parity]);
+       else
+               s = seprint(s, e, "p%c ", '?');
+       s = seprint(s, e, "r%d ", p->rts);
+       s = seprint(s, e, "s%d ", p->stop);
+       s = seprint(s, e, "i%d ", p->fifo);
+       s = seprint(s, e, "\ndev(%d) ", 0);
+       s = seprint(s, e, "type(%d)  ", ser->type);
+       s = seprint(s, e, "framing(%d) ", p->nframeerr);
+       s = seprint(s, e, "overruns(%d) ", p->novererr);
+       s = seprint(s, e, "berr(%d) ", p->nbreakerr);
+       s = seprint(s, e, " serr(%d)\n", p->nparityerr);
+       return s;
+}
+
+static int
+serinit(Serialport *p)
+{
+       int res;
+       res = 0;
+       Serial *ser;
+
+       ser = p->s;
+
+       if(ser->init != nil)
+               res = ser->init(p);
+       if(ser->getparam != nil)
+               ser->getparam(p);
+       p->nframeerr = p->nparityerr = p->nbreakerr = p->novererr = 0;
+
+       return res;
+}
+
+static void
+dattach(Req *req)
+{
+       req->fid->qid = (Qid) {0, 0, QTDIR};
+       req->ofcall.qid = req->fid->qid;
+       respond(req, nil);
+}
+
+static int
+dirgen(int n, Dir *d, void *)
+{
+       if(n >= nports * 2)
+               return -1;
+       d->qid.path = n + 1;
+       d->qid.vers = 0;
+       if(n >= 0)
+               d->qid.type = 0;
+       else
+               d->qid.type = QTDIR;
+       d->uid = strdup("usb");
+       d->gid = strdup(d->uid);
+       d->muid = strdup(d->uid);
+       if(n >= 0){
+               d->name = smprint((n & 1) ? "%sctl" : "%s", ports[n/2]->name);
+               d->mode = ((n & 1) ? 0664 : 0660);
+       }else{
+               d->name = strdup("");
+               d->mode = 0555 | QTDIR;
+       }
+       d->atime = d->mtime = time(0);
+       d->length = 0;
+       return 0;
+}
+
+static char *
+dwalk(Fid *fid, char *name, Qid *qidp)
+{
+       int i;
+       int len;
+       Qid qid;
+       char *p;
+
+       qid = fid->qid;
+       if((qid.type & QTDIR) == 0){
+               return "walk in non-directory";
+       }
+
+       if(strcmp(name, "..") == 0){
+               fid->qid.path = 0;
+               fid->qid.vers = 0;
+               fid->qid.type = QTDIR;
+               *qidp = fid->qid;
+               return nil;
+       }
+
+       for(i = 0; i < nports; i++)
+               if(strncmp(name, ports[i]->name, len = strlen(ports[i]->name)) == 0){
+                       p = name + len;
+                       if(*p == 0)
+                               fid->qid.path = 2 * i + 1;
+                       else if(strcmp(p, "ctl") == 0)
+                               fid->qid.path = 2 * i + 2;
+                       else
+                               continue;
+                       fid->qid.vers = 0;
+                       fid->qid.type = 0;
+                       *qidp = fid->qid;
+                       return nil;
+               }
+       return "does not exist";
+}
+
+static void
+dstat(Req *req)
+{
+       if(dirgen(req->fid->qid.path - 1, &req->d, nil) < 0)
+               respond(req, "the front fell off");
+       else
+               respond(req, nil);
+}
+
+enum {
+       Serbufsize      = 255,
+};
+
+static void
+readproc(void *aux)
+{
+       int dfd;
+       Req *req;
+       long count, rcount;
+       void *data;
+       Serial *ser;
+       Serialport *p;
+       static int errrun, good;
+       char err[Serbufsize];
+
+       p = aux;
+       ser = p->s;
+       for(;;){
+               qlock(&p->readq);
+               while(p->readfirst == nil)
+                       rsleep(&p->readrend);
+               req = p->readfirst;
+               p->readfirst = req->aux;
+               if(p->readlast == req)
+                       p->readlast = nil;
+               req->aux = nil;
+               qunlock(&p->readq);
+
+               count = req->ifcall.count;
+               data = req->ofcall.data;
+               qlock(ser);
+               if(count > ser->maxread)
+                       count = ser->maxread;
+               dsprint(2, "serial: reading from data\n");
+               do {
+                       err[0] = 0;
+                       dfd = p->epin->dfd;
+                       if(usbdebug >= 3)
+                               dsprint(2, "serial: reading: %ld\n", count);
+       
+                       assert(count > 0);
+                       if(ser->wait4data != nil)
+                               rcount = ser->wait4data(p, data, count);
+                       else{
+                               qunlock(ser);
+                               rcount = read(dfd, data, count);
+                               qlock(ser);
+                       }
+                       /*
+                        * if we encounter a long run of continuous read
+                        * errors, do something drastic so that our caller
+                        * doesn't just spin its wheels forever.
+                        */
+                       if(rcount < 0) {
+                               snprint(err, Serbufsize, "%r");
+                               ++errrun;
+                               sleep(20);
+                               if (good > 0 && errrun > 10000) {
+                                       /* the line has been dropped; give up */
+                                       qunlock(ser);
+                                       fprint(2, "%s: line %s is gone: %r\n",
+                                               argv0, p->name);
+                                       threadexitsall("serial line gone");
+                               }
+                       } else {
+                               errrun = 0;
+                               good++;
+                       }
+                       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, p, p->epin, err);
+               }
+               dsprint(2, "serial: read from bulk %ld\n", rcount);
+               if(rcount >= 0){
+                       req->ofcall.count = rcount;
+                       respond(req, nil);
+               } else
+                       responderror(req);
+               qunlock(ser);
+       }
+}
+
+static void
+dread(Req *req)
+{
+       char *e;        /* change */
+       Qid q;
+       Serial *ser;
+       vlong offset;
+       Serialport *p;
+       static char buf[Serbufsize];
+       
+       q = req->fid->qid;
+       
+       if(q.path == 0){
+               dirread9p(req, dirgen, nil);
+               respond(req, nil);
+               return;
+       }
+
+       p = ports[(q.path - 1) / 2];
+       ser = p->s;
+       offset = req->ifcall.offset;
+
+       memset(buf, 0, sizeof buf);
+       qlock(ser);
+       switch((long)((q.path - 1) % 2)){
+       case 0:
+               qlock(&p->readq);
+               if(p->readfirst == nil)
+                       p->readfirst = req;
+               else
+                       p->readlast->aux = req;
+               p->readlast = req;
+               rwakeup(&p->readrend);
+               qunlock(&p->readq);
+               break;
+       case 1:
+               if(offset == 0) {
+                       if(!p->isjtag){
+                               e = serdumpst(p, buf, Serbufsize);
+                               readbuf(req, buf, e - buf);
+                       }
+               }
+               respond(req, nil);
+               break;
+       }
+       qunlock(ser);
+}
+
+static long
+altwrite(Serialport *p, uchar *buf, long count)
+{
+       int nw, dfd;
+       char err[128];
+       Serial *ser;
+
+       ser = p->s;
+       do{
+               dsprint(2, "serial: write to bulk %ld\n", count);
+
+               if(ser->wait4write != nil)
+                       /* unlocked inside later */
+                       nw = ser->wait4write(p, buf, count);
+               else{
+                       dfd = p->epout->dfd;
+                       qunlock(ser);
+                       nw = write(dfd, buf, count);
+                       qlock(ser);
+               }
+               rerrstr(err, sizeof err);
+               dsprint(2, "serial: written %s %d\n", err, nw);
+       } 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(p->s, p, p->epout, err);
+       }
+       return nw;
+}
+
+static void
+dwrite(Req *req)
+{
+       ulong path;
+       char *cmd;
+       Serial *ser;
+       long count;
+       void *buf;
+       Serialport *p;
+
+       path = req->fid->qid.path;
+       p = ports[(path-1)/2];
+       ser = p->s;
+       count = req->ifcall.count;
+       buf = req->ifcall.data;
+
+       qlock(ser);
+       switch((long)((path-1)%2)){
+       case 0:
+               count = altwrite(p, (uchar *)buf, count);
+               break;
+       case 1:
+               if(p->isjtag)
+                       break;
+               cmd = emallocz(count+1, 1);
+               memmove(cmd, buf, count);
+               cmd[count] = 0;
+               if(serialctl(p, cmd) < 0){
+                       qunlock(ser);
+                       free(cmd);
+                       respond(req, "bad control request");
+                       return;
+               }
+               free(cmd);
+               break;
+       }
+       if(count >= 0)
+               ser->recover = 0;
+       else
+               serialrecover(ser, p, p->epout, "writing");
+       qunlock(ser);
+       if(count >= 0){
+               req->ofcall.count = count;
+               respond(req, nil);
+       } else
+               responderror(req);
+}
+
+static int
+openeps(Serialport *p, int epin, int epout, int epintr)
+{
+       Serial *ser;
+
+       ser = p->s;
+       p->epin = openep(ser->dev, epin);
+       if(p->epin == nil){
+               fprint(2, "serial: openep %d: %r\n", epin);
+               return -1;
+       }
+       p->epout = openep(ser->dev, epout);
+       if(p->epout == nil){
+               fprint(2, "serial: openep %d: %r\n", epout);
+               closedev(p->epin);
+               return -1;
+       }
+
+       if(!p->isjtag){
+               devctl(p->epin,  "timeout 1000");
+               devctl(p->epout, "timeout 1000");
+       }
+
+       if(ser->hasepintr){
+               p->epintr = openep(ser->dev, epintr);
+               if(p->epintr == nil){
+                       fprint(2, "serial: openep %d: %r\n", epintr);
+                       closedev(p->epin);
+                       closedev(p->epout);
+                       return -1;
+               }
+               opendevdata(p->epintr, OREAD);
+               devctl(p->epintr, "timeout 1000");
+       }
+
+       if(ser->seteps!= nil)
+               ser->seteps(p);
+       opendevdata(p->epin, OREAD);
+       opendevdata(p->epout, OWRITE);
+       if(p->epin->dfd < 0 ||p->epout->dfd < 0 ||
+           (ser->hasepintr && p->epintr->dfd < 0)){
+               fprint(2, "serial: open i/o ep data: %r\n");
+               closedev(p->epin);
+               closedev(p->epout);
+               if(ser->hasepintr)
+                       closedev(p->epintr);
+               return -1;
+       }
+       return 0;
+}
+
+static int
+findendpoints(Serial *ser, int ifc)
+{
+       int i, epin, epout, epintr;
+       Ep *ep, **eps;
+
+       epintr = epin = epout = -1;
+
+       /*
+        * interfc 0 means start from the start which is equiv to
+        * iterate through endpoints probably, could be done better
+        */
+       eps = ser->dev->usb->conf[0]->iface[ifc]->ep;
+
+       for(i = 0; i < Nep; i++){
+               if((ep = eps[i]) == nil)
+                       continue;
+               if(ser->hasepintr && ep->type == Eintr &&
+                   ep->dir == Ein && epintr == -1)
+                       epintr = ep->id;
+               if(ep->type == Ebulk){
+                       if(ep->dir == Ein && epin == -1)
+                               epin = ep->id;
+                       if(ep->dir == Eout && epout == -1)
+                               epout = ep->id;
+               }
+       }
+       dprint(2, "serial[%d]: ep ids: in %d out %d intr %d\n", ifc, epin, epout, epintr);
+       if(epin == -1 || epout == -1 || (ser->hasepintr && epintr == -1))
+               return -1;
+
+       if(openeps(&ser->p[ifc], epin, epout, epintr) < 0)
+               return -1;
+
+       dprint(2, "serial: ep in %s out %s\n", ser->p[ifc].epin->dir, ser->p[ifc].epout->dir);
+       if(ser->hasepintr)
+               dprint(2, "serial: ep intr %s\n", ser->p[ifc].epintr->dir);
+
+       if(usbdebug > 1 || serialdebug > 2){
+               devctl(ser->p[ifc].epin,  "debug 1");
+               devctl(ser->p[ifc].epout, "debug 1");
+               if(ser->hasepintr)
+                       devctl(ser->p[ifc].epintr, "debug 1");
+               devctl(ser->dev, "debug 1");
+       }
+       return 0;
+}
+
+/* keep in sync with main.c */
+static void
+usage(void)
+{
+       fprint(2, "usage: usb/serial [-dD] [-m mtpt] [-s srv] devid\n");
+       threadexitsall("usage");
+}
+
+static void
+serdevfree(void *a)
+{
+       Serial *ser = a;
+       Serialport *p;
+       int i;
+
+       if(ser == nil)
+               return;
+
+       for(i = 0; i < ser->nifcs; i++){
+               p = &ser->p[i];
+
+               if(ser->hasepintr)
+                       closedev(p->epintr);
+               closedev(p->epin);
+               closedev(p->epout);
+               p->epintr = p->epin = p->epout = nil;
+               if(p->w4data != nil)
+                       chanfree(p->w4data);
+               if(p->gotdata != nil)
+                       chanfree(p->gotdata);
+               if(p->readc)
+                       chanfree(p->readc);
+
+       }
+       free(ser);
+}
+
+static Srv serialfs = {
+       .attach = dattach,
+       .walk1 =        dwalk,
+       .read = dread,
+       .write= dwrite,
+       .stat = dstat,
+};
+
+/*
+static void
+serialfsend(void)
+{
+       if(p->w4data != nil)
+               chanclose(p->w4data);
+       if(p->gotdata != nil)
+               chanclose(p->gotdata);
+       if(p->readc)
+               chanclose(p->readc);
+}
+*/
+
+void
+threadmain(int argc, char* argv[])
+{
+       Serial *ser;
+       Dev *dev;
+       char buf[50];
+       int i, devid;
+       Serialport *p;
+
+       ARGBEGIN{
+       case 'd':
+               serialdebug++;
+               break;
+       default:
+               usage();
+       }ARGEND
+       if(argc != 1)
+               usage();
+       devid = atoi(*argv);
+       dev = getdev(devid);
+       if(dev == nil)
+               sysfatal("getdev: %r");
+
+       ser = dev->aux = emallocz(sizeof(Serial), 1);
+       ser->maxrtrans = ser->maxwtrans = sizeof ser->p[0].data;
+       ser->maxread = ser->maxwrite = sizeof ser->p[0].data;
+       ser->dev = dev;
+       dev->free = serdevfree;
+       ser->jtag = -1;
+       ser->nifcs = 1;
+
+       snprint(buf, sizeof buf, "vid %#06x did %#06x",
+               dev->usb->vid, dev->usb->did);
+       if(plmatch(buf) == 0){
+               ser->hasepintr = 1;
+               ser->Serialops = plops;
+       } else if(uconsmatch(buf) == 0)
+               ser->Serialops = uconsops;
+       else if(ftmatch(ser, buf) == 0)
+               ser->Serialops = ftops;
+       else {
+               sysfatal("no serial devices found");
+       }
+       for(i = 0; i < ser->nifcs; i++){
+               p = &ser->p[i];
+               p->interfc = i;
+               p->s = ser;
+               if(i == ser->jtag){
+                       p->isjtag++;
+               }
+               if(findendpoints(ser, i) < 0)
+                       sysfatal("no endpoints found for ifc %d", i);
+               p->w4data  = chancreate(sizeof(ulong), 0);
+               p->gotdata = chancreate(sizeof(ulong), 0);
+       }
+
+       qlock(ser);
+       serialreset(ser);
+       for(i = 0; i < ser->nifcs; i++){
+               p = &ser->p[i];
+               dprint(2, "serial: valid interface, calling serinit\n");
+               if(serinit(p) < 0){
+                       sysfatal("wserinit: %r");
+               }
+
+               dsprint(2, "serial: adding interface %d, %p\n", p->interfc, p);
+               if(p->isjtag){
+                       snprint(p->name, sizeof p->name, "jtag");
+                       dsprint(2, "serial: JTAG interface %d %p\n", i, p);
+                       snprint(p->name, sizeof p->name, "jtag%d.%d", devid, i);
+               } else {
+                       snprint(p->name, sizeof p->name, "eiaU");
+                       if(i == 0)
+                               snprint(p->name, sizeof p->name, "eiaU%d", devid);
+                       else
+                               snprint(p->name, sizeof p->name, "eiaU%d.%d", devid, i);
+               }
+               fprint(2, "%s...", p->name);
+               incref(dev);
+               p->readrend.l = &p->readq;
+               p->readpid = proccreate(readproc, p, mainstacksize);
+               ports = realloc(ports, (nports + 1) * sizeof(Serialport*));
+               ports[nports++] = p;
+       }
+
+       qunlock(ser);
+       if(nports > 0){
+               snprint(buf, sizeof buf, "serial-%d", devid);
+               threadpostsharesrv(&serialfs, nil, "usb", buf);
+       }
+}
diff --git a/sys/src/cmd/nusb/serial/serial.h b/sys/src/cmd/nusb/serial/serial.h
new file mode 100644 (file)
index 0000000..2677381
--- /dev/null
@@ -0,0 +1,130 @@
+typedef struct Serial Serial;
+typedef struct Serialops Serialops;
+typedef struct Serialport Serialport;
+
+struct Serialops {
+       int     (*seteps)(Serialport*);
+       int     (*init)(Serialport*);
+       int     (*getparam)(Serialport*);
+       int     (*setparam)(Serialport*);
+       int     (*clearpipes)(Serialport*);
+       int     (*reset)(Serial*, Serialport*);
+       int     (*sendlines)(Serialport*);
+       int     (*modemctl)(Serialport*, int);
+       int     (*setbreak)(Serialport*, int);
+       int     (*readstatus)(Serialport*);
+       int     (*wait4data)(Serialport*, uchar *, int);
+       int     (*wait4write)(Serialport*, uchar *, int);
+};
+
+enum {
+       DataBufSz = 8*1024,
+       Maxifc = 16,
+};
+
+
+struct Serialport {
+       char name[32];
+       Serial  *s;             /* device we belong to */
+       int     isjtag;
+
+       Dev     *epintr;        /* may not exist */
+
+       Dev     *epin;
+       Dev     *epout;
+
+       uchar   ctlstate;
+
+       /* serial parameters */
+       uint    baud;
+       int     stop;
+       int     mctl;
+       int     parity;
+       int     bits;
+       int     fifo;
+       int     limit;
+       int     rts;
+       int     cts;
+       int     dsr;
+       int     dcd;
+       int     dtr;
+       int     rlsd;
+
+       vlong   timer;
+       int     blocked;        /* for sw flow ctl. BUG: not implemented yet */
+       int     nbreakerr;
+       int     ring;
+       int     nframeerr;
+       int     nparityerr;
+       int     novererr;
+       int     enabled;
+
+       int     interfc;        /* interfc on the device for ftdi */
+
+       Channel *w4data;
+       Channel *gotdata;
+       Channel *readc;         /* to uncouple reads, only used in ftdi... */
+       int     ndata;
+       uchar   data[DataBufSz];
+       
+       QLock readq;
+       Req *readfirst, *readlast; /* read request queue */
+       int readpid;
+       Rendez readrend;
+};
+
+struct Serial {
+       QLock;
+       Dev     *dev;           /* usb device*/
+
+       int     type;           /* serial model subtype */
+       int     recover;        /* # of non-fatal recovery tries */
+       Serialops;
+
+       int     hasepintr;
+
+       int     jtag;           /* index of jtag interface, -1 none */
+       int     nifcs;          /* # of serial interfaces, including JTAG */
+       Serialport p[Maxifc];
+       int     maxrtrans;
+       int     maxwtrans;
+
+       int     maxread;
+       int     maxwrite;
+
+       int     inhdrsz;
+       int     outhdrsz;
+       int     baudbase;       /* for special baud base settings, see ftdi */
+};
+
+enum {
+       /* soft flow control chars */
+       CTLS    = 023,
+       CTLQ    = 021,
+       CtlDTR  = 1,
+       CtlRTS  = 2,
+};
+
+/*
+ * !hget http://lxr.linux.no/source/drivers/usb/serial/pl2303.h|htmlfmt
+ * !hget http://lxr.linux.no/source/drivers/usb/serial/pl2303.c|htmlfmt
+ */
+
+int serialmain(Dev *d, int argc, char *argv[]);
+
+typedef struct Cinfo Cinfo;
+struct Cinfo {
+       int     vid;            /* usb vendor id */
+       int     did;            /* usb device/product id */
+       int     cid;            /* controller id assigned by us */
+};
+
+extern Cinfo plinfo[];
+extern Cinfo uconsinfo[];
+extern int serialdebug;
+
+#define        dsprint if(serialdebug)fprint
+
+int    serialrecover(Serial *ser, Serialport *p, Dev *ep, char *err);
+int    serialreset(Serial *ser);
+char   *serdumpst(Serialport *p, char *buf, int bufsz);
diff --git a/sys/src/cmd/nusb/serial/ucons.c b/sys/src/cmd/nusb/serial/ucons.c
new file mode 100644 (file)
index 0000000..8dcf637
--- /dev/null
@@ -0,0 +1,48 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <fcall.h>
+#include <9p.h>
+#include "usb.h"
+#include "serial.h"
+#include "ucons.h"
+
+Cinfo uconsinfo[] = {
+       { Net20DCVid,   Net20DCDid },
+       { 0,            0 },
+};
+
+int
+uconsmatch(char *info)
+{
+       Cinfo *ip;
+       char buf[50];
+
+       for(ip = uconsinfo; 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)
+                       return 0;
+       }
+       return -1;
+}
+
+static int
+ucseteps(Serialport *p)
+{
+       Serial *ser;
+
+       ser = p->s;
+
+       p->baud = ~0;   /* not real port */
+       ser->maxrtrans = ser->maxwtrans = 8;
+       devctl(p->epin,  "maxpkt 8");
+       devctl(p->epout, "maxpkt 8");
+       return 0;
+}
+
+/* all nops */
+Serialops uconsops = {
+       .seteps = ucseteps,
+};
diff --git a/sys/src/cmd/nusb/serial/ucons.h b/sys/src/cmd/nusb/serial/ucons.h
new file mode 100644 (file)
index 0000000..421526a
--- /dev/null
@@ -0,0 +1,9 @@
+
+
+enum {
+       Net20DCVid =    0x0525, /* Ajays usb debug cable */
+       Net20DCDid =    0x127a,
+};
+
+int    uconsmatch(char *info);
+extern Serialops uconsops;