diff options
author | aiju <aiju@phicode.de> | 2011-07-30 19:14:18 +0200 |
---|---|---|
committer | aiju <aiju@phicode.de> | 2011-07-30 19:14:18 +0200 |
commit | 9f4184892cb4d95c39064906d2c7630f30352215 (patch) | |
tree | 0f4ab2102f86d3f93bdd684c5cd0fcf116673ef2 /sys/src/cmd/nusb/serial/ftdi.c | |
parent | 2ba1b4c476c986ae788e3b0869fc799e5516a2c2 (diff) |
added nusb/serial
Diffstat (limited to 'sys/src/cmd/nusb/serial/ftdi.c')
-rw-r--r-- | sys/src/cmd/nusb/serial/ftdi.c | 960 |
1 files changed, 960 insertions, 0 deletions
diff --git a/sys/src/cmd/nusb/serial/ftdi.c b/sys/src/cmd/nusb/serial/ftdi.c new file mode 100644 index 000000000..ee88e5433 --- /dev/null +++ b/sys/src/cmd/nusb/serial/ftdi.c @@ -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, +}; |