180e9508eSDavid du Colombier /* Future Technology Devices International serial ports */ 280e9508eSDavid du Colombier #include <u.h> 380e9508eSDavid du Colombier #include <libc.h> 480e9508eSDavid du Colombier #include <thread.h> 580e9508eSDavid du Colombier #include "usb.h" 680e9508eSDavid du Colombier #include "usbfs.h" 780e9508eSDavid du Colombier #include "serial.h" 880e9508eSDavid du Colombier #include "ftdi.h" 980e9508eSDavid du Colombier 1080e9508eSDavid du Colombier /* 1180e9508eSDavid du Colombier * BUG: This keeps growing, there has to be a better way, but without 1280e9508eSDavid du Colombier * devices to try it... We can probably simply look for FTDI in the 1380e9508eSDavid du Colombier * string, or use regular expressions somehow. 1480e9508eSDavid du Colombier */ 1580e9508eSDavid du Colombier Cinfo ftinfo[] = { 1680e9508eSDavid du Colombier { FTVid, FTACTZWAVEDid }, 1780e9508eSDavid du Colombier { FTSheevaVid, FTSheevaDid }, 183e5d0078SDavid du Colombier { FTVid, FTOpenRDUltDid}, 1980e9508eSDavid du Colombier { FTVid, FTIRTRANSDid }, 2080e9508eSDavid du Colombier { FTVid, FTIPLUSDid }, 2180e9508eSDavid du Colombier { FTVid, FTSIODid }, 2280e9508eSDavid du Colombier { FTVid, FT8U232AMDid }, 2380e9508eSDavid du Colombier { FTVid, FT8U232AMALTDid }, 2480e9508eSDavid du Colombier { FTVid, FT8U2232CDid }, 2580e9508eSDavid du Colombier { FTVid, FTRELAISDid }, 2680e9508eSDavid du Colombier { INTERBIOMVid, INTERBIOMIOBRDDid }, 2780e9508eSDavid du Colombier { INTERBIOMVid, INTERBIOMMINIIOBRDDid }, 2880e9508eSDavid du Colombier { FTVid, FTXF632Did }, 2980e9508eSDavid du Colombier { FTVid, FTXF634Did }, 3080e9508eSDavid du Colombier { FTVid, FTXF547Did }, 3180e9508eSDavid du Colombier { FTVid, FTXF633Did }, 3280e9508eSDavid du Colombier { FTVid, FTXF631Did }, 3380e9508eSDavid du Colombier { FTVid, FTXF635Did }, 3480e9508eSDavid du Colombier { FTVid, FTXF640Did }, 3580e9508eSDavid du Colombier { FTVid, FTXF642Did }, 3680e9508eSDavid du Colombier { FTVid, FTDSS20Did }, 3780e9508eSDavid du Colombier { FTNFRICVid, FTNFRICDid }, 3880e9508eSDavid du Colombier { FTVid, FTVNHCPCUSBDDid }, 3980e9508eSDavid du Colombier { FTVid, FTMTXORB0Did }, 4080e9508eSDavid du Colombier { FTVid, FTMTXORB1Did }, 4180e9508eSDavid du Colombier { FTVid, FTMTXORB2Did }, 4280e9508eSDavid du Colombier { FTVid, FTMTXORB3Did }, 4380e9508eSDavid du Colombier { FTVid, FTMTXORB4Did }, 4480e9508eSDavid du Colombier { FTVid, FTMTXORB5Did }, 4580e9508eSDavid du Colombier { FTVid, FTMTXORB6Did }, 4680e9508eSDavid du Colombier { FTVid, FTPERLEULTRAPORTDid }, 4780e9508eSDavid du Colombier { FTVid, FTPIEGROUPDid }, 4880e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL2101Did }, 4980e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL2102Did }, 5080e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL2103Did }, 5180e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL2104Did }, 5280e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL22011Did }, 5380e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL22012Did }, 5480e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL22021Did }, 5580e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL22022Did }, 5680e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL22031Did }, 5780e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL22032Did }, 5880e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL24011Did }, 5980e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL24012Did }, 6080e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL24013Did }, 6180e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL24014Did }, 6280e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL24021Did }, 6380e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL24022Did }, 6480e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL24023Did }, 6580e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL24024Did }, 6680e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL24031Did }, 6780e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL24032Did }, 6880e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL24033Did }, 6980e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL24034Did }, 7080e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL28011Did }, 7180e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL28012Did }, 7280e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL28013Did }, 7380e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL28014Did }, 7480e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL28015Did }, 7580e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL28016Did }, 7680e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL28017Did }, 7780e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL28018Did }, 7880e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL28021Did }, 7980e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL28022Did }, 8080e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL28023Did }, 8180e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL28024Did }, 8280e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL28025Did }, 8380e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL28026Did }, 8480e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL28027Did }, 8580e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL28028Did }, 8680e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL28031Did }, 8780e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL28032Did }, 8880e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL28033Did }, 8980e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL28034Did }, 9080e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL28035Did }, 9180e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL28036Did }, 9280e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL28037Did }, 9380e9508eSDavid du Colombier { SEALEVELVid, SEALEVEL28038Did }, 9480e9508eSDavid du Colombier { IDTECHVid, IDTECHIDT1221UDid }, 9580e9508eSDavid du Colombier { OCTVid, OCTUS101Did }, 9680e9508eSDavid du Colombier { FTVid, FTHETIRA1Did }, /* special quirk div = 240 baud = B38400 rtscts = 1 */ 9780e9508eSDavid du Colombier { FTVid, FTUSBUIRTDid }, /* special quirk div = 77, baud = B38400 */ 9880e9508eSDavid du Colombier { FTVid, PROTEGOSPECIAL1 }, 9980e9508eSDavid du Colombier { FTVid, PROTEGOR2X0 }, 10080e9508eSDavid du Colombier { FTVid, PROTEGOSPECIAL3 }, 10180e9508eSDavid du Colombier { FTVid, PROTEGOSPECIAL4 }, 10280e9508eSDavid du Colombier { FTVid, FTGUDEADSE808Did }, 10380e9508eSDavid du Colombier { FTVid, FTGUDEADSE809Did }, 10480e9508eSDavid du Colombier { FTVid, FTGUDEADSE80ADid }, 10580e9508eSDavid du Colombier { FTVid, FTGUDEADSE80BDid }, 10680e9508eSDavid du Colombier { FTVid, FTGUDEADSE80CDid }, 10780e9508eSDavid du Colombier { FTVid, FTGUDEADSE80DDid }, 10880e9508eSDavid du Colombier { FTVid, FTGUDEADSE80EDid }, 10980e9508eSDavid du Colombier { FTVid, FTGUDEADSE80FDid }, 11080e9508eSDavid du Colombier { FTVid, FTGUDEADSE888Did }, 11180e9508eSDavid du Colombier { FTVid, FTGUDEADSE889Did }, 11280e9508eSDavid du Colombier { FTVid, FTGUDEADSE88ADid }, 11380e9508eSDavid du Colombier { FTVid, FTGUDEADSE88BDid }, 11480e9508eSDavid du Colombier { FTVid, FTGUDEADSE88CDid }, 11580e9508eSDavid du Colombier { FTVid, FTGUDEADSE88DDid }, 11680e9508eSDavid du Colombier { FTVid, FTGUDEADSE88EDid }, 11780e9508eSDavid du Colombier { FTVid, FTGUDEADSE88FDid }, 11880e9508eSDavid du Colombier { FTVid, FTELVUO100Did }, 11980e9508eSDavid du Colombier { FTVid, FTELVUM100Did }, 12080e9508eSDavid du Colombier { FTVid, FTELVUR100Did }, 12180e9508eSDavid du Colombier { FTVid, FTELVALC8500Did }, 12280e9508eSDavid du Colombier { FTVid, FTPYRAMIDDid }, 12380e9508eSDavid du Colombier { FTVid, FTELVFHZ1000PCDid }, 12480e9508eSDavid du Colombier { FTVid, FTELVCLI7000Did }, 12580e9508eSDavid du Colombier { FTVid, FTELVPPS7330Did }, 12680e9508eSDavid du Colombier { FTVid, FTELVTFM100Did }, 12780e9508eSDavid du Colombier { FTVid, FTELVUDF77Did }, 12880e9508eSDavid du Colombier { FTVid, FTELVUIO88Did }, 12980e9508eSDavid du Colombier { FTVid, FTELVUAD8Did }, 13080e9508eSDavid du Colombier { FTVid, FTELVUDA7Did }, 13180e9508eSDavid du Colombier { FTVid, FTELVUSI2Did }, 13280e9508eSDavid du Colombier { FTVid, FTELVT1100Did }, 13380e9508eSDavid du Colombier { FTVid, FTELVPCD200Did }, 13480e9508eSDavid du Colombier { FTVid, FTELVULA200Did }, 13580e9508eSDavid du Colombier { FTVid, FTELVCSI8Did }, 13680e9508eSDavid du Colombier { FTVid, FTELVEM1000DLDid }, 13780e9508eSDavid du Colombier { FTVid, FTELVPCK100Did }, 13880e9508eSDavid du Colombier { FTVid, FTELVRFP500Did }, 13980e9508eSDavid du Colombier { FTVid, FTELVFS20SIGDid }, 14080e9508eSDavid du Colombier { FTVid, FTELVWS300PCDid }, 14180e9508eSDavid du Colombier { FTVid, FTELVFHZ1300PCDid }, 14280e9508eSDavid du Colombier { FTVid, FTELVWS500Did }, 14380e9508eSDavid du Colombier { FTVid, LINXSDMUSBQSSDid }, 14480e9508eSDavid du Colombier { FTVid, LINXMASTERDEVEL2Did }, 14580e9508eSDavid du Colombier { FTVid, LINXFUTURE0Did }, 14680e9508eSDavid du Colombier { FTVid, LINXFUTURE1Did }, 14780e9508eSDavid du Colombier { FTVid, LINXFUTURE2Did }, 14880e9508eSDavid du Colombier { FTVid, FTCCSICDU200Did }, 14980e9508eSDavid du Colombier { FTVid, FTCCSICDU401Did }, 15080e9508eSDavid du Colombier { FTVid, INSIDEACCESSO }, 15180e9508eSDavid du Colombier { INTREDidVid, INTREDidVALUECANDid }, 15280e9508eSDavid du Colombier { INTREDidVid, INTREDidNEOVIDid }, 15380e9508eSDavid du Colombier { FALCOMVid, FALCOMTWISTDid }, 15480e9508eSDavid du Colombier { FALCOMVid, FALCOMSAMBADid }, 15580e9508eSDavid du Colombier { FTVid, FTSUUNTOSPORTSDid }, 15680e9508eSDavid du Colombier { FTVid, FTRMCANVIEWDid }, 15780e9508eSDavid du Colombier { BANDBVid, BANDBUSOTL4Did }, 15880e9508eSDavid du Colombier { BANDBVid, BANDBUSTL4Did }, 15980e9508eSDavid du Colombier { BANDBVid, BANDBUSO9ML2Did }, 16080e9508eSDavid du Colombier { FTVid, EVERECOPROCDSDid }, 16180e9508eSDavid du Colombier { FTVid, FT4NGALAXYDE0Did }, 16280e9508eSDavid du Colombier { FTVid, FT4NGALAXYDE1Did }, 16380e9508eSDavid du Colombier { FTVid, FT4NGALAXYDE2Did }, 16480e9508eSDavid du Colombier { FTVid, XSENSCONVERTER0Did }, 16580e9508eSDavid du Colombier { FTVid, XSENSCONVERTER1Did }, 16680e9508eSDavid du Colombier { FTVid, XSENSCONVERTER2Did }, 16780e9508eSDavid du Colombier { FTVid, XSENSCONVERTER3Did }, 16880e9508eSDavid du Colombier { FTVid, XSENSCONVERTER4Did }, 16980e9508eSDavid du Colombier { FTVid, XSENSCONVERTER5Did }, 17080e9508eSDavid du Colombier { FTVid, XSENSCONVERTER6Did }, 17180e9508eSDavid du Colombier { FTVid, XSENSCONVERTER7Did }, 17280e9508eSDavid du Colombier { MOBILITYVid, MOBILITYUSBSERIALDid }, 17380e9508eSDavid du Colombier { FTVid, FTACTIVEROBOTSDid }, 17480e9508eSDavid du Colombier { FTVid, FTMHAMKWDid }, 17580e9508eSDavid du Colombier { FTVid, FTMHAMYSDid }, 17680e9508eSDavid du Colombier { FTVid, FTMHAMY6Did }, 17780e9508eSDavid du Colombier { FTVid, FTMHAMY8Did }, 17880e9508eSDavid du Colombier { FTVid, FTMHAMICDid }, 17980e9508eSDavid du Colombier { FTVid, FTMHAMDB9Did }, 18080e9508eSDavid du Colombier { FTVid, FTMHAMRS232Did }, 18180e9508eSDavid du Colombier { FTVid, FTMHAMY9Did }, 18280e9508eSDavid du Colombier { FTVid, FTTERATRONIKVCPDid }, 18380e9508eSDavid du Colombier { FTVid, FTTERATRONIKD2XXDid }, 18480e9508eSDavid du Colombier { EVOLUTIONVid, EVOLUTIONER1Did }, 18580e9508eSDavid du Colombier { FTVid, FTARTEMISDid }, 18680e9508eSDavid du Colombier { FTVid, FTATIKATK16Did }, 18780e9508eSDavid du Colombier { FTVid, FTATIKATK16CDid }, 18880e9508eSDavid du Colombier { FTVid, FTATIKATK16HRDid }, 18980e9508eSDavid du Colombier { FTVid, FTATIKATK16HRCDid }, 19080e9508eSDavid du Colombier { KOBILVid, KOBILCONVB1Did }, 19180e9508eSDavid du Colombier { KOBILVid, KOBILCONVKAANDid }, 19280e9508eSDavid du Colombier { POSIFLEXVid, POSIFLEXPP7000Did }, 19380e9508eSDavid du Colombier { FTVid, FTTTUSBDid }, 19480e9508eSDavid du Colombier { FTVid, FTECLOCOM1WIREDid }, 19580e9508eSDavid du Colombier { FTVid, FTWESTREXMODEL777Did }, 19680e9508eSDavid du Colombier { FTVid, FTWESTREXMODEL8900FDid }, 19780e9508eSDavid du Colombier { FTVid, FTPCDJDAC2Did }, 19880e9508eSDavid du Colombier { FTVid, FTRRCIRKITSLOCOBUFFERDid }, 19980e9508eSDavid du Colombier { FTVid, FTASKRDR400Did }, 20080e9508eSDavid du Colombier { ICOMID1Vid, ICOMID1Did }, 20180e9508eSDavid du Colombier { PAPOUCHVid, PAPOUCHTMUDid }, 20280e9508eSDavid du Colombier { FTVid, FTACGHFDUALDid }, 203d5789509SDavid du Colombier { FT8U232AMDid, FT4232HDid }, 20480e9508eSDavid du Colombier { 0, 0 }, 20580e9508eSDavid du Colombier }; 20680e9508eSDavid du Colombier 20780e9508eSDavid du Colombier enum { 208d5789509SDavid du Colombier Packsz = 64, /* default size */ 209d5789509SDavid du Colombier Maxpacksz = 512, 210d5789509SDavid du Colombier Bufsiz = 4 * 1024, 21180e9508eSDavid du Colombier }; 21280e9508eSDavid du Colombier 21380e9508eSDavid du Colombier static int 214d584e620SDavid du Colombier ftdiread(Serialport *p, int index, int req, uchar *buf, int len) 21580e9508eSDavid du Colombier { 21680e9508eSDavid du Colombier int res; 217d5789509SDavid du Colombier Serial *ser; 218d5789509SDavid du Colombier 219d5789509SDavid du Colombier ser = p->s; 22080e9508eSDavid du Colombier 22180e9508eSDavid du Colombier if(req != FTGETE2READ) 222d5789509SDavid du Colombier index |= p->interfc + 1; 223d584e620SDavid du Colombier dsprint(2, "serial: ftdiread %#p [%d] req: %#x val: %#x idx:%d buf:%p len:%d\n", 224d584e620SDavid du Colombier p, p->interfc, req, 0, index, buf, len); 225d584e620SDavid du Colombier res = usbcmd(ser->dev, Rd2h | Rftdireq | Rdev, req, 0, index, buf, len); 22680e9508eSDavid du Colombier dsprint(2, "serial: ftdiread res:%d\n", res); 22780e9508eSDavid du Colombier return res; 22880e9508eSDavid du Colombier } 22980e9508eSDavid du Colombier 23080e9508eSDavid du Colombier static int 231d5789509SDavid du Colombier ftdiwrite(Serialport *p, int val, int index, int req) 23280e9508eSDavid du Colombier { 23380e9508eSDavid du Colombier int res; 234d5789509SDavid du Colombier Serial *ser; 23580e9508eSDavid du Colombier 236d5789509SDavid du Colombier ser = p->s; 237d5789509SDavid du Colombier 238d584e620SDavid du Colombier if(req != FTGETE2READ || req != FTSETE2ERASE || req != FTSETBAUDRATE) 239d5789509SDavid du Colombier index |= p->interfc + 1; 240d5789509SDavid du Colombier dsprint(2, "serial: ftdiwrite %#p [%d] req: %#x val: %#x idx:%d\n", 241d5789509SDavid du Colombier p, p->interfc, req, val, index); 24280e9508eSDavid du Colombier res = usbcmd(ser->dev, Rh2d | Rftdireq | Rdev, req, val, index, nil, 0); 24380e9508eSDavid du Colombier dsprint(2, "serial: ftdiwrite res:%d\n", res); 24480e9508eSDavid du Colombier return res; 24580e9508eSDavid du Colombier } 24680e9508eSDavid du Colombier 24780e9508eSDavid du Colombier static int 248d5789509SDavid du Colombier ftmodemctl(Serialport *p, int set) 24980e9508eSDavid du Colombier { 25080e9508eSDavid du Colombier if(set == 0){ 251d5789509SDavid du Colombier p->mctl = 0; 252d5789509SDavid du Colombier ftdiwrite(p, 0, 0, FTSETMODEMCTRL); 25380e9508eSDavid du Colombier return 0; 25480e9508eSDavid du Colombier } 255d5789509SDavid du Colombier p->mctl = 1; 256d5789509SDavid du Colombier ftdiwrite(p, 0, FTRTSCTSHS, FTSETFLOWCTRL); 25780e9508eSDavid du Colombier return 0; 25880e9508eSDavid du Colombier } 25980e9508eSDavid du Colombier 26080e9508eSDavid du Colombier static ushort 26180e9508eSDavid du Colombier ft232ambaudbase2div(int baud, int base) 26280e9508eSDavid du Colombier { 26380e9508eSDavid du Colombier int divisor3; 26480e9508eSDavid du Colombier ushort divisor; 26580e9508eSDavid du Colombier 26680e9508eSDavid du Colombier divisor3 = (base / 2) / baud; 26780e9508eSDavid du Colombier if((divisor3 & 7) == 7) 26880e9508eSDavid du Colombier divisor3++; /* round x.7/8 up to x+1 */ 26980e9508eSDavid du Colombier divisor = divisor3 >> 3; 27080e9508eSDavid du Colombier divisor3 &= 7; 27180e9508eSDavid du Colombier 27280e9508eSDavid du Colombier if(divisor3 == 1) 27380e9508eSDavid du Colombier divisor |= 0xc000; /* 0.125 */ 27480e9508eSDavid du Colombier else if(divisor3 >= 4) 27580e9508eSDavid du Colombier divisor |= 0x4000; /* 0.5 */ 27680e9508eSDavid du Colombier else if(divisor3 != 0) 27780e9508eSDavid du Colombier divisor |= 0x8000; /* 0.25 */ 27880e9508eSDavid du Colombier if( divisor == 1) 27980e9508eSDavid du Colombier divisor = 0; /* special case for maximum baud rate */ 28080e9508eSDavid du Colombier return divisor; 28180e9508eSDavid du Colombier } 28280e9508eSDavid du Colombier 28380e9508eSDavid du Colombier enum{ 28480e9508eSDavid du Colombier ClockNew = 48000000, 28580e9508eSDavid du Colombier ClockOld = 12000000 / 16, 28680e9508eSDavid du Colombier HetiraDiv = 240, 28780e9508eSDavid du Colombier UirtDiv = 77, 28880e9508eSDavid du Colombier }; 28980e9508eSDavid du Colombier 29080e9508eSDavid du Colombier static ushort 29180e9508eSDavid du Colombier ft232ambaud2div(int baud) 29280e9508eSDavid du Colombier { 29380e9508eSDavid du Colombier return ft232ambaudbase2div(baud, ClockNew); 29480e9508eSDavid du Colombier } 29580e9508eSDavid du Colombier 29680e9508eSDavid du Colombier static ulong divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7}; 29780e9508eSDavid du Colombier 29880e9508eSDavid du Colombier static ulong 29980e9508eSDavid du Colombier ft232bmbaudbase2div(int baud, int base) 30080e9508eSDavid du Colombier { 30180e9508eSDavid du Colombier int divisor3; 30280e9508eSDavid du Colombier u32int divisor; 30380e9508eSDavid du Colombier 30480e9508eSDavid du Colombier divisor3 = (base / 2) / baud; 30580e9508eSDavid du Colombier divisor = divisor3 >> 3 | divfrac[divisor3 & 7] << 14; 30680e9508eSDavid du Colombier 30780e9508eSDavid du Colombier /* Deal with special cases for highest baud rates. */ 30880e9508eSDavid du Colombier if( divisor == 1) 30980e9508eSDavid du Colombier divisor = 0; /* 1.0 */ 31080e9508eSDavid du Colombier else if( divisor == 0x4001) 31180e9508eSDavid du Colombier divisor = 1; /* 1.5 */ 31280e9508eSDavid du Colombier return divisor; 31380e9508eSDavid du Colombier } 31480e9508eSDavid du Colombier 31580e9508eSDavid du Colombier static ulong 31680e9508eSDavid du Colombier ft232bmbaud2div (int baud) 31780e9508eSDavid du Colombier { 31880e9508eSDavid du Colombier return ft232bmbaudbase2div (baud, ClockNew); 31980e9508eSDavid du Colombier } 32080e9508eSDavid du Colombier 32180e9508eSDavid du Colombier static int 32280e9508eSDavid du Colombier customdiv(Serial *ser) 32380e9508eSDavid du Colombier { 32480e9508eSDavid du Colombier if(ser->dev->usb->vid == FTVid && ser->dev->usb->did == FTHETIRA1Did) 32580e9508eSDavid du Colombier return HetiraDiv; 32680e9508eSDavid du Colombier else if(ser->dev->usb->vid == FTVid && ser->dev->usb->did == FTUSBUIRTDid) 32780e9508eSDavid du Colombier return UirtDiv; 32880e9508eSDavid du Colombier 32980e9508eSDavid du Colombier fprint(2, "serial: weird custom divisor\n"); 33080e9508eSDavid du Colombier return 0; /* shouldn't happen, break as much as I can */ 33180e9508eSDavid du Colombier } 33280e9508eSDavid du Colombier 33380e9508eSDavid du Colombier static ulong 33480e9508eSDavid du Colombier ftbaudcalcdiv(Serial *ser, int baud) 33580e9508eSDavid du Colombier { 33680e9508eSDavid du Colombier int cusdiv; 33780e9508eSDavid du Colombier ulong divval; 33880e9508eSDavid du Colombier 33980e9508eSDavid du Colombier if(baud == 38400 && (cusdiv = customdiv(ser)) != 0) 34080e9508eSDavid du Colombier baud = ser->baudbase / cusdiv; 34180e9508eSDavid du Colombier 34280e9508eSDavid du Colombier if(baud == 0) 34380e9508eSDavid du Colombier baud = 9600; 34480e9508eSDavid du Colombier 34580e9508eSDavid du Colombier switch(ser->type) { 34680e9508eSDavid du Colombier case SIO: 34780e9508eSDavid du Colombier switch(baud) { 34880e9508eSDavid du Colombier case 300: 34980e9508eSDavid du Colombier divval = FTb300; 35080e9508eSDavid du Colombier break; 35180e9508eSDavid du Colombier case 600: 35280e9508eSDavid du Colombier divval = FTb600; 35380e9508eSDavid du Colombier break; 35480e9508eSDavid du Colombier case 1200: 35580e9508eSDavid du Colombier divval = FTb1200; 35680e9508eSDavid du Colombier break; 35780e9508eSDavid du Colombier case 2400: 35880e9508eSDavid du Colombier divval = FTb2400; 35980e9508eSDavid du Colombier break; 36080e9508eSDavid du Colombier case 4800: 36180e9508eSDavid du Colombier divval = FTb4800; 36280e9508eSDavid du Colombier break; 36380e9508eSDavid du Colombier case 9600: 36480e9508eSDavid du Colombier divval = FTb9600; 36580e9508eSDavid du Colombier break; 36680e9508eSDavid du Colombier case 19200: 36780e9508eSDavid du Colombier divval = FTb19200; 36880e9508eSDavid du Colombier break; 36980e9508eSDavid du Colombier case 38400: 37080e9508eSDavid du Colombier divval = FTb38400; 37180e9508eSDavid du Colombier break; 37280e9508eSDavid du Colombier case 57600: 37380e9508eSDavid du Colombier divval = FTb57600; 37480e9508eSDavid du Colombier break; 37580e9508eSDavid du Colombier case 115200: 37680e9508eSDavid du Colombier divval = FTb115200; 37780e9508eSDavid du Colombier break; 37880e9508eSDavid du Colombier default: 37980e9508eSDavid du Colombier divval = FTb9600; 38080e9508eSDavid du Colombier break; 38180e9508eSDavid du Colombier } 38280e9508eSDavid du Colombier break; 38380e9508eSDavid du Colombier case FT8U232AM: 38480e9508eSDavid du Colombier if(baud <= 3000000) 38580e9508eSDavid du Colombier divval = ft232ambaud2div(baud); 38680e9508eSDavid du Colombier else 38780e9508eSDavid du Colombier divval = ft232ambaud2div(9600); 38880e9508eSDavid du Colombier break; 38980e9508eSDavid du Colombier case FT232BM: 39080e9508eSDavid du Colombier case FT2232C: 391d5789509SDavid du Colombier case FTKINDR: 392d5789509SDavid du Colombier case FT2232H: 393d5789509SDavid du Colombier case FT4232H: 39480e9508eSDavid du Colombier if(baud <= 3000000) 39580e9508eSDavid du Colombier divval = ft232bmbaud2div(baud); 39680e9508eSDavid du Colombier else 39780e9508eSDavid du Colombier divval = ft232bmbaud2div(9600); 39880e9508eSDavid du Colombier break; 39980e9508eSDavid du Colombier default: 40080e9508eSDavid du Colombier divval = ft232bmbaud2div(9600); 40180e9508eSDavid du Colombier break; 40280e9508eSDavid du Colombier } 40380e9508eSDavid du Colombier return divval; 40480e9508eSDavid du Colombier } 40580e9508eSDavid du Colombier 40680e9508eSDavid du Colombier static int 407d5789509SDavid du Colombier ftsetparam(Serialport *p) 40880e9508eSDavid du Colombier { 40980e9508eSDavid du Colombier int res; 41080e9508eSDavid du Colombier ushort val; 41180e9508eSDavid du Colombier ulong bauddiv; 41280e9508eSDavid du Colombier 41380e9508eSDavid du Colombier val = 0; 414d5789509SDavid du Colombier if(p->stop == 1) 41580e9508eSDavid du Colombier val |= FTSETDATASTOPBITS1; 416d5789509SDavid du Colombier else if(p->stop == 2) 41780e9508eSDavid du Colombier val |= FTSETDATASTOPBITS2; 418d5789509SDavid du Colombier else if(p->stop == 15) 41980e9508eSDavid du Colombier val |= FTSETDATASTOPBITS15; 420d5789509SDavid du Colombier switch(p->parity){ 42180e9508eSDavid du Colombier case 0: 42280e9508eSDavid du Colombier val |= FTSETDATAParNONE; 42380e9508eSDavid du Colombier break; 42480e9508eSDavid du Colombier case 1: 42580e9508eSDavid du Colombier val |= FTSETDATAParODD; 42680e9508eSDavid du Colombier break; 42780e9508eSDavid du Colombier case 2: 42880e9508eSDavid du Colombier val |= FTSETDATAParEVEN; 42980e9508eSDavid du Colombier break; 43080e9508eSDavid du Colombier case 3: 43180e9508eSDavid du Colombier val |= FTSETDATAParMARK; 43280e9508eSDavid du Colombier break; 43380e9508eSDavid du Colombier case 4: 43480e9508eSDavid du Colombier val |= FTSETDATAParSPACE; 43580e9508eSDavid du Colombier break; 43680e9508eSDavid du Colombier }; 43780e9508eSDavid du Colombier 43880e9508eSDavid du Colombier dsprint(2, "serial: setparam\n"); 43980e9508eSDavid du Colombier 440d5789509SDavid du Colombier res = ftdiwrite(p, val, 0, FTSETDATA); 44180e9508eSDavid du Colombier if(res < 0) 44280e9508eSDavid du Colombier return res; 44380e9508eSDavid du Colombier 444d5789509SDavid du Colombier res = ftmodemctl(p, p->mctl); 44580e9508eSDavid du Colombier if(res < 0) 44680e9508eSDavid du Colombier return res; 44780e9508eSDavid du Colombier 448d5789509SDavid du Colombier bauddiv = ftbaudcalcdiv(p->s, p->baud); 449d584e620SDavid du Colombier res = ftdiwrite(p, bauddiv, (bauddiv>>16) & 1, FTSETBAUDRATE); 45080e9508eSDavid du Colombier 45180e9508eSDavid du Colombier dsprint(2, "serial: setparam res: %d\n", res); 45280e9508eSDavid du Colombier return res; 45380e9508eSDavid du Colombier } 45480e9508eSDavid du Colombier 455*6f756d42SDavid du Colombier static int 456*6f756d42SDavid du Colombier hasjtag(Usbdev *udev) 457*6f756d42SDavid du Colombier { 458*6f756d42SDavid du Colombier /* no string, for now, by default we detect no jtag */ 459*6f756d42SDavid du Colombier if(udev->product != nil && cistrstr(udev->product, "jtag") != nil) 460*6f756d42SDavid du Colombier return 1; 461*6f756d42SDavid du Colombier return 0; 462*6f756d42SDavid du Colombier } 463*6f756d42SDavid du Colombier 46480e9508eSDavid du Colombier /* ser locked */ 46580e9508eSDavid du Colombier static void 46680e9508eSDavid du Colombier ftgettype(Serial *ser) 46780e9508eSDavid du Colombier { 468d5789509SDavid du Colombier int i, outhdrsz, dno, pksz; 46980e9508eSDavid du Colombier ulong baudbase; 47080e9508eSDavid du Colombier Conf *cnf; 47180e9508eSDavid du Colombier 472d5789509SDavid du Colombier pksz = Packsz; 47380e9508eSDavid du Colombier /* Assume it is not the original SIO device for now. */ 47480e9508eSDavid du Colombier baudbase = ClockNew / 2; 47580e9508eSDavid du Colombier outhdrsz = 0; 47680e9508eSDavid du Colombier dno = ser->dev->usb->dno; 47780e9508eSDavid du Colombier cnf = ser->dev->usb->conf[0]; 478d5789509SDavid du Colombier ser->nifcs = 0; 47980e9508eSDavid du Colombier for(i = 0; i < Niface; i++) 48080e9508eSDavid du Colombier if(cnf->iface[i] != nil) 481d5789509SDavid du Colombier ser->nifcs++; 482d5789509SDavid du Colombier if(ser->nifcs > 1) { 48380e9508eSDavid du Colombier /* 484d5789509SDavid du Colombier * Multiple interfaces. default assume FT2232C, 48580e9508eSDavid du Colombier */ 486d5789509SDavid du Colombier if(dno == 0x500) 487d5789509SDavid du Colombier ser->type = FT2232C; 488d5789509SDavid du Colombier else if(dno == 0x600) 489d5789509SDavid du Colombier ser->type = FTKINDR; 490d5789509SDavid du Colombier else if(dno == 0x700){ 491d5789509SDavid du Colombier ser->type = FT2232H; 492d5789509SDavid du Colombier pksz = Maxpacksz; 493d5789509SDavid du Colombier } else if(dno == 0x800){ 494d5789509SDavid du Colombier ser->type = FT4232H; 495d5789509SDavid du Colombier pksz = Maxpacksz; 496d5789509SDavid du Colombier } else 497d5789509SDavid du Colombier ser->type = FT2232C; 498d5789509SDavid du Colombier 499*6f756d42SDavid du Colombier if(hasjtag(ser->dev->usb)) 500d5789509SDavid du Colombier ser->jtag = 0; 50180e9508eSDavid du Colombier 50280e9508eSDavid du Colombier /* 50380e9508eSDavid du Colombier * BM-type devices have a bug where dno gets set 50480e9508eSDavid du Colombier * to 0x200 when serial is 0. 50580e9508eSDavid du Colombier */ 50680e9508eSDavid du Colombier if(dno < 0x500) 507d5789509SDavid du Colombier fprint(2, "serial: warning: dno %d too low for " 508d5789509SDavid du Colombier "multi-interface device\n", dno); 50980e9508eSDavid du Colombier } else if(dno < 0x200) { 51080e9508eSDavid du Colombier /* Old device. Assume it is the original SIO. */ 51180e9508eSDavid du Colombier ser->type = SIO; 51280e9508eSDavid du Colombier baudbase = ClockOld/16; 51380e9508eSDavid du Colombier outhdrsz = 1; 51480e9508eSDavid du Colombier } else if(dno < 0x400) 51580e9508eSDavid du Colombier /* 51680e9508eSDavid du Colombier * Assume its an FT8U232AM (or FT8U245AM) 51780e9508eSDavid du Colombier * (It might be a BM because of the iSerialNumber bug, 51880e9508eSDavid du Colombier * but it will still work as an AM device.) 51980e9508eSDavid du Colombier */ 52080e9508eSDavid du Colombier ser->type = FT8U232AM; 52180e9508eSDavid du Colombier else /* Assume it is an FT232BM (or FT245BM) */ 52280e9508eSDavid du Colombier ser->type = FT232BM; 52380e9508eSDavid du Colombier 524d5789509SDavid du Colombier ser->maxrtrans = ser->maxwtrans = pksz; 52580e9508eSDavid du Colombier ser->baudbase = baudbase; 52680e9508eSDavid du Colombier ser->outhdrsz = outhdrsz; 52780e9508eSDavid du Colombier ser->inhdrsz = 2; 528d5789509SDavid du Colombier 52980e9508eSDavid du Colombier dsprint (2, "serial: detected type: %#x\n", ser->type); 53080e9508eSDavid du Colombier } 53180e9508eSDavid du Colombier 53280e9508eSDavid du Colombier int 53380e9508eSDavid du Colombier ftmatch(Serial *ser, char *info) 53480e9508eSDavid du Colombier { 53580e9508eSDavid du Colombier Cinfo *ip; 53680e9508eSDavid du Colombier char buf[50]; 53780e9508eSDavid du Colombier 53880e9508eSDavid du Colombier for(ip = ftinfo; ip->vid != 0; ip++){ 53980e9508eSDavid du Colombier snprint(buf, sizeof buf, "vid %#06x did %#06x", ip->vid, ip->did); 54080e9508eSDavid du Colombier dsprint(2, "serial: %s %s\n", buf, info); 54180e9508eSDavid du Colombier if(strstr(info, buf) != nil){ 54280e9508eSDavid du Colombier if(ser != nil){ 54380e9508eSDavid du Colombier qlock(ser); 54480e9508eSDavid du Colombier ftgettype(ser); 54580e9508eSDavid du Colombier qunlock(ser); 54680e9508eSDavid du Colombier } 54780e9508eSDavid du Colombier return 0; 54880e9508eSDavid du Colombier } 54980e9508eSDavid du Colombier } 55080e9508eSDavid du Colombier return -1; 55180e9508eSDavid du Colombier } 55280e9508eSDavid du Colombier 55380e9508eSDavid du Colombier static int 554d5789509SDavid du Colombier ftuseinhdr(Serialport *p, uchar *b) 55580e9508eSDavid du Colombier { 55680e9508eSDavid du Colombier if(b[0] & FTICTS) 557d5789509SDavid du Colombier p->cts = 1; 55880e9508eSDavid du Colombier else 559d5789509SDavid du Colombier p->cts = 0; 56080e9508eSDavid du Colombier if(b[0] & FTIDSR) 561d5789509SDavid du Colombier p->dsr = 1; 56280e9508eSDavid du Colombier else 563d5789509SDavid du Colombier p->dsr = 0; 56480e9508eSDavid du Colombier if(b[0] & FTIRI) 565d5789509SDavid du Colombier p->ring = 1; 56680e9508eSDavid du Colombier else 567d5789509SDavid du Colombier p->ring = 0; 56880e9508eSDavid du Colombier if(b[0] & FTIRLSD) 569d5789509SDavid du Colombier p->rlsd = 1; 57080e9508eSDavid du Colombier else 571d5789509SDavid du Colombier p->rlsd = 0; 57280e9508eSDavid du Colombier 57380e9508eSDavid du Colombier if(b[1] & FTIOE) 574d5789509SDavid du Colombier p->novererr++; 57580e9508eSDavid du Colombier if(b[1] & FTIPE) 576d5789509SDavid du Colombier p->nparityerr++; 57780e9508eSDavid du Colombier if(b[1] & FTIFE) 578d5789509SDavid du Colombier p->nframeerr++; 57980e9508eSDavid du Colombier if(b[1] & FTIBI) 580d5789509SDavid du Colombier p->nbreakerr++; 58180e9508eSDavid du Colombier return 0; 58280e9508eSDavid du Colombier } 58380e9508eSDavid du Colombier 58480e9508eSDavid du Colombier static int 585d5789509SDavid du Colombier ftsetouthdr(Serialport *p, uchar *b, int len) 58680e9508eSDavid du Colombier { 587d5789509SDavid du Colombier if(p->s->outhdrsz != 0) 58880e9508eSDavid du Colombier b[0] = FTOPORT | (FTOLENMSK & len); 589d5789509SDavid du Colombier return p->s->outhdrsz; 59080e9508eSDavid du Colombier } 59180e9508eSDavid du Colombier 59280e9508eSDavid du Colombier static int 593d5789509SDavid du Colombier wait4data(Serialport *p, uchar *data, int count) 59480e9508eSDavid du Colombier { 595d5789509SDavid du Colombier int d; 596d5789509SDavid du Colombier Serial *ser; 597d5789509SDavid du Colombier 598d5789509SDavid du Colombier ser = p->s; 599d5789509SDavid du Colombier 60080e9508eSDavid du Colombier qunlock(ser); 601d5789509SDavid du Colombier d = sendul(p->w4data, 1); 602ed868a7cSDavid du Colombier qlock(ser); 603d5789509SDavid du Colombier if(d <= 0) 604d5789509SDavid du Colombier return -1; 605d5789509SDavid du Colombier if(p->ndata >= count) 606d5789509SDavid du Colombier p->ndata -= count; 60780e9508eSDavid du Colombier else{ 608d5789509SDavid du Colombier count = p->ndata; 609d5789509SDavid du Colombier p->ndata = 0; 61080e9508eSDavid du Colombier } 611ecc2a7c8SDavid du Colombier assert(count >= 0); 612d5789509SDavid du Colombier assert(p->ndata >= 0); 613d5789509SDavid du Colombier memmove(data, p->data, count); 614d5789509SDavid du Colombier if(p->ndata != 0) 615d5789509SDavid du Colombier memmove(p->data, p->data+count, p->ndata); 61680e9508eSDavid du Colombier 617d5789509SDavid du Colombier recvul(p->gotdata); 61880e9508eSDavid du Colombier return count; 61980e9508eSDavid du Colombier } 62080e9508eSDavid du Colombier 62180e9508eSDavid du Colombier static int 622d5789509SDavid du Colombier wait4write(Serialport *p, uchar *data, int count) 62380e9508eSDavid du Colombier { 62480e9508eSDavid du Colombier int off, fd; 62580e9508eSDavid du Colombier uchar *b; 626d5789509SDavid du Colombier Serial *ser; 62780e9508eSDavid du Colombier 628d5789509SDavid du Colombier ser = p->s; 62980e9508eSDavid du Colombier 63080e9508eSDavid du Colombier b = emallocz(count+ser->outhdrsz, 1); 631d5789509SDavid du Colombier off = ftsetouthdr(p, b, count); 63280e9508eSDavid du Colombier memmove(b+off, data, count); 63380e9508eSDavid du Colombier 634d5789509SDavid du Colombier fd = p->epout->dfd; 63580e9508eSDavid du Colombier qunlock(ser); 63680e9508eSDavid du Colombier count = write(fd, b, count+off); 63780e9508eSDavid du Colombier qlock(ser); 63880e9508eSDavid du Colombier free(b); 63980e9508eSDavid du Colombier return count; 64080e9508eSDavid du Colombier } 64180e9508eSDavid du Colombier 64280e9508eSDavid du Colombier typedef struct Packser Packser; 64380e9508eSDavid du Colombier struct Packser{ 64480e9508eSDavid du Colombier int nb; 645d5789509SDavid du Colombier uchar b[Bufsiz]; 64680e9508eSDavid du Colombier }; 64780e9508eSDavid du Colombier 64880e9508eSDavid du Colombier typedef struct Areader Areader; 64980e9508eSDavid du Colombier struct Areader{ 650d5789509SDavid du Colombier Serialport *p; 65180e9508eSDavid du Colombier Channel *c; 65280e9508eSDavid du Colombier }; 65380e9508eSDavid du Colombier 65480e9508eSDavid du Colombier static void 655d5789509SDavid du Colombier shutdownchan(Channel *c) 656d5789509SDavid du Colombier { 657d5789509SDavid du Colombier Packser *bp; 658d5789509SDavid du Colombier 659d5789509SDavid du Colombier while((bp=nbrecvp(c)) != nil) 660d5789509SDavid du Colombier free(bp); 661d5789509SDavid du Colombier chanfree(c); 662d5789509SDavid du Colombier } 663d5789509SDavid du Colombier 664d5789509SDavid du Colombier int 665d5789509SDavid du Colombier cpdata(Serial *ser, Serialport *port, uchar *out, uchar *in, int sz) 666d5789509SDavid du Colombier { 667d5789509SDavid du Colombier int i, ncp, ntotcp, pksz; 668d5789509SDavid du Colombier 669d5789509SDavid du Colombier pksz = ser->maxrtrans; 670d5789509SDavid du Colombier ntotcp = 0; 671d5789509SDavid du Colombier 672d5789509SDavid du Colombier for(i = 0; i < sz; i+= pksz){ 673d5789509SDavid du Colombier ftuseinhdr(port, in + i); 674d5789509SDavid du Colombier if(sz - i > pksz) 675d5789509SDavid du Colombier ncp = pksz - ser->inhdrsz; 676d5789509SDavid du Colombier else 677d5789509SDavid du Colombier ncp = sz - i - ser->inhdrsz; 678d5789509SDavid du Colombier memmove(out, in + i + ser->inhdrsz, ncp); 679d5789509SDavid du Colombier out += ncp; 680d5789509SDavid du Colombier ntotcp += ncp; 681d5789509SDavid du Colombier } 682d5789509SDavid du Colombier return ntotcp; 683d5789509SDavid du Colombier } 684d5789509SDavid du Colombier 685d5789509SDavid du Colombier static void 68680e9508eSDavid du Colombier epreader(void *u) 68780e9508eSDavid du Colombier { 688d5789509SDavid du Colombier int dfd, rcount, cl; 68980e9508eSDavid du Colombier char err[40]; 69080e9508eSDavid du Colombier Areader *a; 69180e9508eSDavid du Colombier Channel *c; 692d5789509SDavid du Colombier Packser *pk; 69380e9508eSDavid du Colombier Serial *ser; 694d5789509SDavid du Colombier Serialport *p; 69580e9508eSDavid du Colombier 69680e9508eSDavid du Colombier threadsetname("epreader proc"); 69780e9508eSDavid du Colombier a = u; 698d5789509SDavid du Colombier p = a->p; 699d5789509SDavid du Colombier ser = p->s; 70080e9508eSDavid du Colombier c = a->c; 70180e9508eSDavid du Colombier free(a); 70280e9508eSDavid du Colombier 70380e9508eSDavid du Colombier qlock(ser); 704d5789509SDavid du Colombier dfd = p->epin->dfd; 70580e9508eSDavid du Colombier qunlock(ser); 70680e9508eSDavid du Colombier 707d5789509SDavid du Colombier pk = nil; 70880e9508eSDavid du Colombier do { 709d5789509SDavid du Colombier if (pk == nil) 710d5789509SDavid du Colombier pk = emallocz(sizeof(Packser), 1); 711d5789509SDavid du Colombier rcount = read(dfd, pk->b, sizeof pk->b); 71280e9508eSDavid du Colombier if(serialdebug > 5) 713d5789509SDavid du Colombier dsprint(2, "%d %#ux%#ux ", rcount, p->data[0], 714d5789509SDavid du Colombier p->data[1]); 715d5789509SDavid du Colombier if(rcount < 0) 71680e9508eSDavid du Colombier break; 717d5789509SDavid du Colombier if(rcount == 0) 718d5789509SDavid du Colombier continue; 71980e9508eSDavid du Colombier if(rcount >= ser->inhdrsz){ 720d5789509SDavid du Colombier rcount = cpdata(ser, p, pk->b, pk->b, rcount); 72180e9508eSDavid du Colombier if(rcount != 0){ 722d5789509SDavid du Colombier pk->nb = rcount; 723d5789509SDavid du Colombier cl = sendp(c, pk); 724d5789509SDavid du Colombier if(cl < 0){ 725d5789509SDavid du Colombier /* 726d5789509SDavid du Colombier * if it was a time-out, I don't want 727d5789509SDavid du Colombier * to give back an error. 728d5789509SDavid du Colombier */ 729d5789509SDavid du Colombier rcount = 0; 730d5789509SDavid du Colombier break; 73180e9508eSDavid du Colombier } 732d5789509SDavid du Colombier }else 733d5789509SDavid du Colombier free(pk); 734d5789509SDavid du Colombier pk = nil; 73580e9508eSDavid du Colombier } 73680e9508eSDavid du Colombier } while(rcount >= 0 || (rcount < 0 && strstr(err, "timed out") != nil)); 73780e9508eSDavid du Colombier 73880e9508eSDavid du Colombier if(rcount < 0) 739d5789509SDavid du Colombier fprint(2, "%s: error reading %s: %r\n", argv0, p->fs.name); 740d5789509SDavid du Colombier free(pk); 741d5789509SDavid du Colombier nbsendp(c, nil); 742d5789509SDavid du Colombier if(p->w4data != nil) 743d5789509SDavid du Colombier chanclose(p->w4data); 744d5789509SDavid du Colombier if(p->gotdata != nil) 745d5789509SDavid du Colombier chanclose(p->gotdata); 746d5789509SDavid du Colombier devctl(ser->dev, "detach"); 74780e9508eSDavid du Colombier closedev(ser->dev); 748d5789509SDavid du Colombier usbfsdel(&p->fs); 74980e9508eSDavid du Colombier } 75080e9508eSDavid du Colombier 75180e9508eSDavid du Colombier static void 75280e9508eSDavid du Colombier statusreader(void *u) 75380e9508eSDavid du Colombier { 75480e9508eSDavid du Colombier Areader *a; 75580e9508eSDavid du Colombier Channel *c; 756d5789509SDavid du Colombier Packser *pk; 757d5789509SDavid du Colombier Serialport *p; 75880e9508eSDavid du Colombier Serial *ser; 759d5789509SDavid du Colombier int cl; 76080e9508eSDavid du Colombier 761d5789509SDavid du Colombier p = u; 762d5789509SDavid du Colombier ser = p->s; 76380e9508eSDavid du Colombier threadsetname("statusreader thread"); 76480e9508eSDavid du Colombier /* big buffering, fewer bytes lost */ 76580e9508eSDavid du Colombier c = chancreate(sizeof(Packser *), 128); 76680e9508eSDavid du Colombier a = emallocz(sizeof(Areader), 1); 767d5789509SDavid du Colombier a->p = p; 76880e9508eSDavid du Colombier a->c = c; 76980e9508eSDavid du Colombier incref(ser->dev); 77080e9508eSDavid du Colombier proccreate(epreader, a, 16*1024); 77180e9508eSDavid du Colombier 772d5789509SDavid du Colombier while((pk = recvp(c)) != nil){ 773d5789509SDavid du Colombier memmove(p->data, pk->b, pk->nb); 774d5789509SDavid du Colombier p->ndata = pk->nb; 775d5789509SDavid du Colombier free(pk); 776d584e620SDavid du Colombier dsprint(2, "serial %p: status reader %d \n", p, p->ndata); 77780e9508eSDavid du Colombier /* consume it all */ 778d5789509SDavid du Colombier while(p->ndata != 0){ 779d584e620SDavid du Colombier dsprint(2, "serial %p: status reader to consume: %d\n", 780d584e620SDavid du Colombier p, p->ndata); 781d5789509SDavid du Colombier cl = recvul(p->w4data); 782d5789509SDavid du Colombier if(cl < 0) 783d5789509SDavid du Colombier break; 784d5789509SDavid du Colombier cl = sendul(p->gotdata, 1); 785d5789509SDavid du Colombier if(cl < 0) 786d5789509SDavid du Colombier break; 78780e9508eSDavid du Colombier } 78880e9508eSDavid du Colombier } 789d5789509SDavid du Colombier 790d5789509SDavid du Colombier shutdownchan(c); 791d5789509SDavid du Colombier devctl(ser->dev, "detach"); 79280e9508eSDavid du Colombier closedev(ser->dev); 793d5789509SDavid du Colombier usbfsdel(&p->fs); 79480e9508eSDavid du Colombier } 79580e9508eSDavid du Colombier 79680e9508eSDavid du Colombier static int 79780e9508eSDavid du Colombier ftreset(Serial *ser) 79880e9508eSDavid du Colombier { 799d5789509SDavid du Colombier Serialport *p; 800d5789509SDavid du Colombier int i; 801d5789509SDavid du Colombier 802d5789509SDavid du Colombier p = ser->p; 803d5789509SDavid du Colombier for(i = 0; i < Maxifc; i++) 804d584e620SDavid du Colombier if(p[i].s != nil) 805d5789509SDavid du Colombier ftdiwrite(&p[i], FTRESETCTLVAL, 0, FTRESET); 80680e9508eSDavid du Colombier return 0; 80780e9508eSDavid du Colombier } 80880e9508eSDavid du Colombier 80980e9508eSDavid du Colombier static int 810d5789509SDavid du Colombier ftinit(Serialport *p) 81180e9508eSDavid du Colombier { 812d5789509SDavid du Colombier Serial *ser; 813d584e620SDavid du Colombier uint timerval; 814d584e620SDavid du Colombier int res; 81580e9508eSDavid du Colombier 816d5789509SDavid du Colombier ser = p->s; 817d584e620SDavid du Colombier if(p->isjtag){ 818d584e620SDavid du Colombier res = ftdiwrite(p, FTSETFLOWCTRL, 0, FTDISABLEFLOWCTRL); 819d584e620SDavid du Colombier if(res < 0) 820d584e620SDavid du Colombier return -1; 821d584e620SDavid du Colombier res = ftdiread(p, FTSETLATENCYTIMER, 0, (uchar *)&timerval, 822d584e620SDavid du Colombier FTLATENCYTIMERSZ); 823d584e620SDavid du Colombier if(res < 0) 824d584e620SDavid du Colombier return -1; 825d584e620SDavid du Colombier dsprint(2, "serial: jtag latency timer is %d\n", timerval); 826d584e620SDavid du Colombier timerval = 2; 827d584e620SDavid du Colombier ftdiwrite(p, FTLATENCYDEFAULT, 0, FTSETLATENCYTIMER); 828d584e620SDavid du Colombier res = ftdiread(p, FTSETLATENCYTIMER, 0, (uchar *)&timerval, 829d584e620SDavid du Colombier FTLATENCYTIMERSZ); 830d584e620SDavid du Colombier if(res < 0) 831d584e620SDavid du Colombier return -1; 832d584e620SDavid du Colombier 833d584e620SDavid du Colombier dsprint(2, "serial: jtag latency timer set to %d\n", timerval); 834d584e620SDavid du Colombier /* may be unnecessary */ 835d584e620SDavid du Colombier devctl(p->epin, "timeout 5000"); 836d584e620SDavid du Colombier devctl(p->epout, "timeout 5000"); 837d584e620SDavid du Colombier /* 0xb is the mask for lines. plug dependant? */ 838d584e620SDavid du Colombier ftdiwrite(p, BMMPSSE|0x0b, 0, FTSETBITMODE); 839d584e620SDavid du Colombier } 84080e9508eSDavid du Colombier incref(ser->dev); 841d5789509SDavid du Colombier threadcreate(statusreader, p, 8*1024); 84280e9508eSDavid du Colombier return 0; 84380e9508eSDavid du Colombier } 84480e9508eSDavid du Colombier 84580e9508eSDavid du Colombier static int 846d5789509SDavid du Colombier ftsetbreak(Serialport *p, int val) 84780e9508eSDavid du Colombier { 848d5789509SDavid du Colombier return ftdiwrite(p, (val != 0? FTSETBREAK: 0), 0, FTSETDATA); 84980e9508eSDavid du Colombier } 85080e9508eSDavid du Colombier 85180e9508eSDavid du Colombier static int 852d5789509SDavid du Colombier ftclearpipes(Serialport *p) 85380e9508eSDavid du Colombier { 85480e9508eSDavid du Colombier /* maybe can be done in one... */ 855d5789509SDavid du Colombier ftdiwrite(p, FTRESETCTLVALPURGETX, 0, FTRESET); 856d5789509SDavid du Colombier ftdiwrite(p, FTRESETCTLVALPURGERX, 0, FTRESET); 85780e9508eSDavid du Colombier return 0; 85880e9508eSDavid du Colombier } 85980e9508eSDavid du Colombier 86080e9508eSDavid du Colombier static int 861d5789509SDavid du Colombier setctlline(Serialport *p, uchar val) 86280e9508eSDavid du Colombier { 863d5789509SDavid du Colombier return ftdiwrite(p, val | (val << 8), 0, FTSETMODEMCTRL); 86480e9508eSDavid du Colombier } 86580e9508eSDavid du Colombier 86680e9508eSDavid du Colombier static void 867d5789509SDavid du Colombier updatectlst(Serialport *p, int val) 86880e9508eSDavid du Colombier { 869d5789509SDavid du Colombier if(p->rts) 870d5789509SDavid du Colombier p->ctlstate |= val; 87180e9508eSDavid du Colombier else 872d5789509SDavid du Colombier p->ctlstate &= ~val; 87380e9508eSDavid du Colombier } 87480e9508eSDavid du Colombier 87580e9508eSDavid du Colombier static int 876d5789509SDavid du Colombier setctl(Serialport *p) 87780e9508eSDavid du Colombier { 87880e9508eSDavid du Colombier int res; 879d5789509SDavid du Colombier Serial *ser; 880d5789509SDavid du Colombier 881d5789509SDavid du Colombier ser = p->s; 88280e9508eSDavid du Colombier 88380e9508eSDavid du Colombier if(ser->dev->usb->vid == FTVid && ser->dev->usb->did == FTHETIRA1Did){ 88480e9508eSDavid du Colombier fprint(2, "serial: cannot set lines for this device\n"); 885d5789509SDavid du Colombier updatectlst(p, CtlRTS|CtlDTR); 886d5789509SDavid du Colombier p->rts = p->dtr = 1; 88780e9508eSDavid du Colombier return -1; 88880e9508eSDavid du Colombier } 88980e9508eSDavid du Colombier 89080e9508eSDavid du Colombier /* NB: you can not set DTR and RTS with one control message */ 891d5789509SDavid du Colombier updatectlst(p, CtlRTS); 892d5789509SDavid du Colombier res = setctlline(p, (CtlRTS<<8)|p->ctlstate); 89380e9508eSDavid du Colombier if(res < 0) 89480e9508eSDavid du Colombier return res; 89580e9508eSDavid du Colombier 896d5789509SDavid du Colombier updatectlst(p, CtlDTR); 897d5789509SDavid du Colombier res = setctlline(p, (CtlDTR<<8)|p->ctlstate); 89880e9508eSDavid du Colombier if(res < 0) 89980e9508eSDavid du Colombier return res; 90080e9508eSDavid du Colombier 90180e9508eSDavid du Colombier return 0; 90280e9508eSDavid du Colombier } 90380e9508eSDavid du Colombier 90480e9508eSDavid du Colombier static int 905d5789509SDavid du Colombier ftsendlines(Serialport *p) 90680e9508eSDavid du Colombier { 90780e9508eSDavid du Colombier int res; 90880e9508eSDavid du Colombier 909d5789509SDavid du Colombier dsprint(2, "serial: sendlines: %#2.2x\n", p->ctlstate); 910d5789509SDavid du Colombier res = setctl(p); 91180e9508eSDavid du Colombier dsprint(2, "serial: sendlines res: %d\n", res); 91280e9508eSDavid du Colombier return 0; 91380e9508eSDavid du Colombier } 91480e9508eSDavid du Colombier 91580e9508eSDavid du Colombier static int 916d5789509SDavid du Colombier ftseteps(Serialport *p) 91780e9508eSDavid du Colombier { 91880e9508eSDavid du Colombier char *s; 919d5789509SDavid du Colombier Serial *ser; 92080e9508eSDavid du Colombier 921d5789509SDavid du Colombier ser = p->s; 922d5789509SDavid du Colombier 923d5789509SDavid du Colombier s = smprint("maxpkt %d", ser->maxrtrans); 924d5789509SDavid du Colombier devctl(p->epin, s); 925d5789509SDavid du Colombier free(s); 926d5789509SDavid du Colombier 927d5789509SDavid du Colombier s = smprint("maxpkt %d", ser->maxwtrans); 928d5789509SDavid du Colombier devctl(p->epout, s); 92980e9508eSDavid du Colombier free(s); 93080e9508eSDavid du Colombier return 0; 93180e9508eSDavid du Colombier } 93280e9508eSDavid du Colombier 93380e9508eSDavid du Colombier Serialops ftops = { 93480e9508eSDavid du Colombier .init = ftinit, 93580e9508eSDavid du Colombier .seteps = ftseteps, 93680e9508eSDavid du Colombier .setparam = ftsetparam, 93780e9508eSDavid du Colombier .clearpipes = ftclearpipes, 93880e9508eSDavid du Colombier .reset = ftreset, 93980e9508eSDavid du Colombier .sendlines = ftsendlines, 94080e9508eSDavid du Colombier .modemctl = ftmodemctl, 94180e9508eSDavid du Colombier .setbreak = ftsetbreak, 94280e9508eSDavid du Colombier .wait4data = wait4data, 94380e9508eSDavid du Colombier .wait4write = wait4write, 94480e9508eSDavid du Colombier }; 945