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