ref: 9f4184892cb4d95c39064906d2c7630f30352215
parent: 2ba1b4c476c986ae788e3b0869fc799e5516a2c2
author: aiju <[email protected]>
date: Sat Jul 30 15:14:18 EDT 2011
added nusb/serial
--- a/sys/src/cmd/nusb/mkfile
+++ b/sys/src/cmd/nusb/mkfile
@@ -5,6 +5,8 @@
kb\
audio\
usbd\
+ disk\
+ serial\
UPDATE=\
mkfile\
--- /dev/null
+++ b/sys/src/cmd/nusb/serial/ftdi.c
@@ -1,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,
+};
--- /dev/null
+++ b/sys/src/cmd/nusb/serial/ftdi.h
@@ -1,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);
--- /dev/null
+++ b/sys/src/cmd/nusb/serial/mkfile
@@ -1,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
--- /dev/null
+++ b/sys/src/cmd/nusb/serial/prolific.c
@@ -1,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,
+};
--- /dev/null
+++ b/sys/src/cmd/nusb/serial/prolific.h
@@ -1,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);
--- /dev/null
+++ b/sys/src/cmd/nusb/serial/serial.c
@@ -1,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);
+ }
+}
--- /dev/null
+++ b/sys/src/cmd/nusb/serial/serial.h
@@ -1,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);
--- /dev/null
+++ b/sys/src/cmd/nusb/serial/ucons.c
@@ -1,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,
+};
--- /dev/null
+++ b/sys/src/cmd/nusb/serial/ucons.h
@@ -1,0 +1,9 @@
+
+
+enum {
+ Net20DCVid = 0x0525, /* Ajays usb debug cable */
+ Net20DCDid = 0x127a,
+};
+
+int uconsmatch(char *info);
+extern Serialops uconsops;