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 },
204*6bbfed0dSDavid du Colombier { FTVid, AMONKEYDid },
20580e9508eSDavid du Colombier { 0, 0 },
20680e9508eSDavid du Colombier };
20780e9508eSDavid du Colombier
20880e9508eSDavid du Colombier enum {
209d5789509SDavid du Colombier Packsz = 64, /* default size */
210d5789509SDavid du Colombier Maxpacksz = 512,
211d5789509SDavid du Colombier Bufsiz = 4 * 1024,
21280e9508eSDavid du Colombier };
21380e9508eSDavid du Colombier
21480e9508eSDavid du Colombier static int
ftdiread(Serialport * p,int index,int req,uchar * buf,int len)215d584e620SDavid du Colombier ftdiread(Serialport *p, int index, int req, uchar *buf, int len)
21680e9508eSDavid du Colombier {
21780e9508eSDavid du Colombier int res;
218d5789509SDavid du Colombier Serial *ser;
219d5789509SDavid du Colombier
220d5789509SDavid du Colombier ser = p->s;
22180e9508eSDavid du Colombier
22280e9508eSDavid du Colombier if(req != FTGETE2READ)
223d5789509SDavid du Colombier index |= p->interfc + 1;
224d584e620SDavid du Colombier dsprint(2, "serial: ftdiread %#p [%d] req: %#x val: %#x idx:%d buf:%p len:%d\n",
225d584e620SDavid du Colombier p, p->interfc, req, 0, index, buf, len);
226d584e620SDavid du Colombier res = usbcmd(ser->dev, Rd2h | Rftdireq | Rdev, req, 0, index, buf, len);
22780e9508eSDavid du Colombier dsprint(2, "serial: ftdiread res:%d\n", res);
22880e9508eSDavid du Colombier return res;
22980e9508eSDavid du Colombier }
23080e9508eSDavid du Colombier
23180e9508eSDavid du Colombier static int
ftdiwrite(Serialport * p,int val,int index,int req)232d5789509SDavid du Colombier ftdiwrite(Serialport *p, int val, int index, int req)
23380e9508eSDavid du Colombier {
23480e9508eSDavid du Colombier int res;
235d5789509SDavid du Colombier Serial *ser;
23680e9508eSDavid du Colombier
237d5789509SDavid du Colombier ser = p->s;
238d5789509SDavid du Colombier
239d584e620SDavid du Colombier if(req != FTGETE2READ || req != FTSETE2ERASE || req != FTSETBAUDRATE)
240d5789509SDavid du Colombier index |= p->interfc + 1;
241d5789509SDavid du Colombier dsprint(2, "serial: ftdiwrite %#p [%d] req: %#x val: %#x idx:%d\n",
242d5789509SDavid du Colombier p, p->interfc, req, val, index);
24380e9508eSDavid du Colombier res = usbcmd(ser->dev, Rh2d | Rftdireq | Rdev, req, val, index, nil, 0);
24480e9508eSDavid du Colombier dsprint(2, "serial: ftdiwrite res:%d\n", res);
24580e9508eSDavid du Colombier return res;
24680e9508eSDavid du Colombier }
24780e9508eSDavid du Colombier
24880e9508eSDavid du Colombier static int
ftmodemctl(Serialport * p,int set)249d5789509SDavid du Colombier ftmodemctl(Serialport *p, int set)
25080e9508eSDavid du Colombier {
25180e9508eSDavid du Colombier if(set == 0){
252d5789509SDavid du Colombier p->mctl = 0;
253d5789509SDavid du Colombier ftdiwrite(p, 0, 0, FTSETMODEMCTRL);
25480e9508eSDavid du Colombier return 0;
25580e9508eSDavid du Colombier }
256d5789509SDavid du Colombier p->mctl = 1;
257d5789509SDavid du Colombier ftdiwrite(p, 0, FTRTSCTSHS, FTSETFLOWCTRL);
25880e9508eSDavid du Colombier return 0;
25980e9508eSDavid du Colombier }
26080e9508eSDavid du Colombier
26180e9508eSDavid du Colombier static ushort
ft232ambaudbase2div(int baud,int base)26280e9508eSDavid du Colombier ft232ambaudbase2div(int baud, int base)
26380e9508eSDavid du Colombier {
26480e9508eSDavid du Colombier int divisor3;
26580e9508eSDavid du Colombier ushort divisor;
26680e9508eSDavid du Colombier
26780e9508eSDavid du Colombier divisor3 = (base / 2) / baud;
26880e9508eSDavid du Colombier if((divisor3 & 7) == 7)
26980e9508eSDavid du Colombier divisor3++; /* round x.7/8 up to x+1 */
27080e9508eSDavid du Colombier divisor = divisor3 >> 3;
27180e9508eSDavid du Colombier divisor3 &= 7;
27280e9508eSDavid du Colombier
27380e9508eSDavid du Colombier if(divisor3 == 1)
27480e9508eSDavid du Colombier divisor |= 0xc000; /* 0.125 */
27580e9508eSDavid du Colombier else if(divisor3 >= 4)
27680e9508eSDavid du Colombier divisor |= 0x4000; /* 0.5 */
27780e9508eSDavid du Colombier else if(divisor3 != 0)
27880e9508eSDavid du Colombier divisor |= 0x8000; /* 0.25 */
27980e9508eSDavid du Colombier if( divisor == 1)
28080e9508eSDavid du Colombier divisor = 0; /* special case for maximum baud rate */
28180e9508eSDavid du Colombier return divisor;
28280e9508eSDavid du Colombier }
28380e9508eSDavid du Colombier
28480e9508eSDavid du Colombier enum{
28580e9508eSDavid du Colombier ClockNew = 48000000,
28680e9508eSDavid du Colombier ClockOld = 12000000 / 16,
28780e9508eSDavid du Colombier HetiraDiv = 240,
28880e9508eSDavid du Colombier UirtDiv = 77,
28980e9508eSDavid du Colombier };
29080e9508eSDavid du Colombier
29180e9508eSDavid du Colombier static ushort
ft232ambaud2div(int baud)29280e9508eSDavid du Colombier ft232ambaud2div(int baud)
29380e9508eSDavid du Colombier {
29480e9508eSDavid du Colombier return ft232ambaudbase2div(baud, ClockNew);
29580e9508eSDavid du Colombier }
29680e9508eSDavid du Colombier
29780e9508eSDavid du Colombier static ulong divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7};
29880e9508eSDavid du Colombier
29980e9508eSDavid du Colombier static ulong
ft232bmbaudbase2div(int baud,int base)30080e9508eSDavid du Colombier ft232bmbaudbase2div(int baud, int base)
30180e9508eSDavid du Colombier {
30280e9508eSDavid du Colombier int divisor3;
30380e9508eSDavid du Colombier u32int divisor;
30480e9508eSDavid du Colombier
30580e9508eSDavid du Colombier divisor3 = (base / 2) / baud;
30680e9508eSDavid du Colombier divisor = divisor3 >> 3 | divfrac[divisor3 & 7] << 14;
30780e9508eSDavid du Colombier
30880e9508eSDavid du Colombier /* Deal with special cases for highest baud rates. */
30980e9508eSDavid du Colombier if( divisor == 1)
31080e9508eSDavid du Colombier divisor = 0; /* 1.0 */
31180e9508eSDavid du Colombier else if( divisor == 0x4001)
31280e9508eSDavid du Colombier divisor = 1; /* 1.5 */
31380e9508eSDavid du Colombier return divisor;
31480e9508eSDavid du Colombier }
31580e9508eSDavid du Colombier
31680e9508eSDavid du Colombier static ulong
ft232bmbaud2div(int baud)31780e9508eSDavid du Colombier ft232bmbaud2div (int baud)
31880e9508eSDavid du Colombier {
31980e9508eSDavid du Colombier return ft232bmbaudbase2div (baud, ClockNew);
32080e9508eSDavid du Colombier }
32180e9508eSDavid du Colombier
32280e9508eSDavid du Colombier static int
customdiv(Serial * ser)32380e9508eSDavid du Colombier customdiv(Serial *ser)
32480e9508eSDavid du Colombier {
32580e9508eSDavid du Colombier if(ser->dev->usb->vid == FTVid && ser->dev->usb->did == FTHETIRA1Did)
32680e9508eSDavid du Colombier return HetiraDiv;
32780e9508eSDavid du Colombier else if(ser->dev->usb->vid == FTVid && ser->dev->usb->did == FTUSBUIRTDid)
32880e9508eSDavid du Colombier return UirtDiv;
32980e9508eSDavid du Colombier
33080e9508eSDavid du Colombier fprint(2, "serial: weird custom divisor\n");
33180e9508eSDavid du Colombier return 0; /* shouldn't happen, break as much as I can */
33280e9508eSDavid du Colombier }
33380e9508eSDavid du Colombier
33480e9508eSDavid du Colombier static ulong
ftbaudcalcdiv(Serial * ser,int baud)33580e9508eSDavid du Colombier ftbaudcalcdiv(Serial *ser, int baud)
33680e9508eSDavid du Colombier {
33780e9508eSDavid du Colombier int cusdiv;
33880e9508eSDavid du Colombier ulong divval;
33980e9508eSDavid du Colombier
34080e9508eSDavid du Colombier if(baud == 38400 && (cusdiv = customdiv(ser)) != 0)
34180e9508eSDavid du Colombier baud = ser->baudbase / cusdiv;
34280e9508eSDavid du Colombier
34380e9508eSDavid du Colombier if(baud == 0)
34480e9508eSDavid du Colombier baud = 9600;
34580e9508eSDavid du Colombier
34680e9508eSDavid du Colombier switch(ser->type) {
34780e9508eSDavid du Colombier case SIO:
34880e9508eSDavid du Colombier switch(baud) {
34980e9508eSDavid du Colombier case 300:
35080e9508eSDavid du Colombier divval = FTb300;
35180e9508eSDavid du Colombier break;
35280e9508eSDavid du Colombier case 600:
35380e9508eSDavid du Colombier divval = FTb600;
35480e9508eSDavid du Colombier break;
35580e9508eSDavid du Colombier case 1200:
35680e9508eSDavid du Colombier divval = FTb1200;
35780e9508eSDavid du Colombier break;
35880e9508eSDavid du Colombier case 2400:
35980e9508eSDavid du Colombier divval = FTb2400;
36080e9508eSDavid du Colombier break;
36180e9508eSDavid du Colombier case 4800:
36280e9508eSDavid du Colombier divval = FTb4800;
36380e9508eSDavid du Colombier break;
36480e9508eSDavid du Colombier case 9600:
36580e9508eSDavid du Colombier divval = FTb9600;
36680e9508eSDavid du Colombier break;
36780e9508eSDavid du Colombier case 19200:
36880e9508eSDavid du Colombier divval = FTb19200;
36980e9508eSDavid du Colombier break;
37080e9508eSDavid du Colombier case 38400:
37180e9508eSDavid du Colombier divval = FTb38400;
37280e9508eSDavid du Colombier break;
37380e9508eSDavid du Colombier case 57600:
37480e9508eSDavid du Colombier divval = FTb57600;
37580e9508eSDavid du Colombier break;
37680e9508eSDavid du Colombier case 115200:
37780e9508eSDavid du Colombier divval = FTb115200;
37880e9508eSDavid du Colombier break;
37980e9508eSDavid du Colombier default:
38080e9508eSDavid du Colombier divval = FTb9600;
38180e9508eSDavid du Colombier break;
38280e9508eSDavid du Colombier }
38380e9508eSDavid du Colombier break;
38480e9508eSDavid du Colombier case FT8U232AM:
38580e9508eSDavid du Colombier if(baud <= 3000000)
38680e9508eSDavid du Colombier divval = ft232ambaud2div(baud);
38780e9508eSDavid du Colombier else
38880e9508eSDavid du Colombier divval = ft232ambaud2div(9600);
38980e9508eSDavid du Colombier break;
39080e9508eSDavid du Colombier case FT232BM:
39180e9508eSDavid du Colombier case FT2232C:
392d5789509SDavid du Colombier case FTKINDR:
393d5789509SDavid du Colombier case FT2232H:
394d5789509SDavid du Colombier case FT4232H:
39580e9508eSDavid du Colombier if(baud <= 3000000)
39680e9508eSDavid du Colombier divval = ft232bmbaud2div(baud);
39780e9508eSDavid du Colombier else
39880e9508eSDavid du Colombier divval = ft232bmbaud2div(9600);
39980e9508eSDavid du Colombier break;
40080e9508eSDavid du Colombier default:
40180e9508eSDavid du Colombier divval = ft232bmbaud2div(9600);
40280e9508eSDavid du Colombier break;
40380e9508eSDavid du Colombier }
40480e9508eSDavid du Colombier return divval;
40580e9508eSDavid du Colombier }
40680e9508eSDavid du Colombier
40780e9508eSDavid du Colombier static int
ftsetparam(Serialport * p)408d5789509SDavid du Colombier ftsetparam(Serialport *p)
40980e9508eSDavid du Colombier {
41080e9508eSDavid du Colombier int res;
41180e9508eSDavid du Colombier ushort val;
41280e9508eSDavid du Colombier ulong bauddiv;
41380e9508eSDavid du Colombier
41480e9508eSDavid du Colombier val = 0;
415d5789509SDavid du Colombier if(p->stop == 1)
41680e9508eSDavid du Colombier val |= FTSETDATASTOPBITS1;
417d5789509SDavid du Colombier else if(p->stop == 2)
41880e9508eSDavid du Colombier val |= FTSETDATASTOPBITS2;
419d5789509SDavid du Colombier else if(p->stop == 15)
42080e9508eSDavid du Colombier val |= FTSETDATASTOPBITS15;
421d5789509SDavid du Colombier switch(p->parity){
42280e9508eSDavid du Colombier case 0:
42380e9508eSDavid du Colombier val |= FTSETDATAParNONE;
42480e9508eSDavid du Colombier break;
42580e9508eSDavid du Colombier case 1:
42680e9508eSDavid du Colombier val |= FTSETDATAParODD;
42780e9508eSDavid du Colombier break;
42880e9508eSDavid du Colombier case 2:
42980e9508eSDavid du Colombier val |= FTSETDATAParEVEN;
43080e9508eSDavid du Colombier break;
43180e9508eSDavid du Colombier case 3:
43280e9508eSDavid du Colombier val |= FTSETDATAParMARK;
43380e9508eSDavid du Colombier break;
43480e9508eSDavid du Colombier case 4:
43580e9508eSDavid du Colombier val |= FTSETDATAParSPACE;
43680e9508eSDavid du Colombier break;
43780e9508eSDavid du Colombier };
43880e9508eSDavid du Colombier
43980e9508eSDavid du Colombier dsprint(2, "serial: setparam\n");
44080e9508eSDavid du Colombier
441d5789509SDavid du Colombier res = ftdiwrite(p, val, 0, FTSETDATA);
44280e9508eSDavid du Colombier if(res < 0)
44380e9508eSDavid du Colombier return res;
44480e9508eSDavid du Colombier
445d5789509SDavid du Colombier res = ftmodemctl(p, p->mctl);
44680e9508eSDavid du Colombier if(res < 0)
44780e9508eSDavid du Colombier return res;
44880e9508eSDavid du Colombier
449d5789509SDavid du Colombier bauddiv = ftbaudcalcdiv(p->s, p->baud);
450d584e620SDavid du Colombier res = ftdiwrite(p, bauddiv, (bauddiv>>16) & 1, FTSETBAUDRATE);
45180e9508eSDavid du Colombier
45280e9508eSDavid du Colombier dsprint(2, "serial: setparam res: %d\n", res);
45380e9508eSDavid du Colombier return res;
45480e9508eSDavid du Colombier }
45580e9508eSDavid du Colombier
4566f756d42SDavid du Colombier static int
hasjtag(Usbdev * udev)4576f756d42SDavid du Colombier hasjtag(Usbdev *udev)
4586f756d42SDavid du Colombier {
4596f756d42SDavid du Colombier /* no string, for now, by default we detect no jtag */
4606f756d42SDavid du Colombier if(udev->product != nil && cistrstr(udev->product, "jtag") != nil)
4616f756d42SDavid du Colombier return 1;
4626f756d42SDavid du Colombier return 0;
4636f756d42SDavid du Colombier }
4646f756d42SDavid du Colombier
46580e9508eSDavid du Colombier /* ser locked */
46680e9508eSDavid du Colombier static void
ftgettype(Serial * ser)46780e9508eSDavid du Colombier ftgettype(Serial *ser)
46880e9508eSDavid du Colombier {
469d5789509SDavid du Colombier int i, outhdrsz, dno, pksz;
47080e9508eSDavid du Colombier ulong baudbase;
47180e9508eSDavid du Colombier Conf *cnf;
47280e9508eSDavid du Colombier
473d5789509SDavid du Colombier pksz = Packsz;
47480e9508eSDavid du Colombier /* Assume it is not the original SIO device for now. */
47580e9508eSDavid du Colombier baudbase = ClockNew / 2;
47680e9508eSDavid du Colombier outhdrsz = 0;
47780e9508eSDavid du Colombier dno = ser->dev->usb->dno;
47880e9508eSDavid du Colombier cnf = ser->dev->usb->conf[0];
479d5789509SDavid du Colombier ser->nifcs = 0;
48080e9508eSDavid du Colombier for(i = 0; i < Niface; i++)
48180e9508eSDavid du Colombier if(cnf->iface[i] != nil)
482d5789509SDavid du Colombier ser->nifcs++;
483d5789509SDavid du Colombier if(ser->nifcs > 1) {
48480e9508eSDavid du Colombier /*
485d5789509SDavid du Colombier * Multiple interfaces. default assume FT2232C,
48680e9508eSDavid du Colombier */
487d5789509SDavid du Colombier if(dno == 0x500)
488d5789509SDavid du Colombier ser->type = FT2232C;
489d5789509SDavid du Colombier else if(dno == 0x600)
490d5789509SDavid du Colombier ser->type = FTKINDR;
491d5789509SDavid du Colombier else if(dno == 0x700){
492d5789509SDavid du Colombier ser->type = FT2232H;
493d5789509SDavid du Colombier pksz = Maxpacksz;
494d5789509SDavid du Colombier } else if(dno == 0x800){
495d5789509SDavid du Colombier ser->type = FT4232H;
496d5789509SDavid du Colombier pksz = Maxpacksz;
497d5789509SDavid du Colombier } else
498d5789509SDavid du Colombier ser->type = FT2232C;
499d5789509SDavid du Colombier
5006f756d42SDavid du Colombier if(hasjtag(ser->dev->usb))
501d5789509SDavid du Colombier ser->jtag = 0;
50280e9508eSDavid du Colombier
50380e9508eSDavid du Colombier /*
50480e9508eSDavid du Colombier * BM-type devices have a bug where dno gets set
50580e9508eSDavid du Colombier * to 0x200 when serial is 0.
50680e9508eSDavid du Colombier */
50780e9508eSDavid du Colombier if(dno < 0x500)
508d5789509SDavid du Colombier fprint(2, "serial: warning: dno %d too low for "
509d5789509SDavid du Colombier "multi-interface device\n", dno);
51080e9508eSDavid du Colombier } else if(dno < 0x200) {
51180e9508eSDavid du Colombier /* Old device. Assume it is the original SIO. */
51280e9508eSDavid du Colombier ser->type = SIO;
51380e9508eSDavid du Colombier baudbase = ClockOld/16;
51480e9508eSDavid du Colombier outhdrsz = 1;
51580e9508eSDavid du Colombier } else if(dno < 0x400)
51680e9508eSDavid du Colombier /*
51780e9508eSDavid du Colombier * Assume its an FT8U232AM (or FT8U245AM)
51880e9508eSDavid du Colombier * (It might be a BM because of the iSerialNumber bug,
51980e9508eSDavid du Colombier * but it will still work as an AM device.)
52080e9508eSDavid du Colombier */
52180e9508eSDavid du Colombier ser->type = FT8U232AM;
52280e9508eSDavid du Colombier else /* Assume it is an FT232BM (or FT245BM) */
52380e9508eSDavid du Colombier ser->type = FT232BM;
52480e9508eSDavid du Colombier
525d5789509SDavid du Colombier ser->maxrtrans = ser->maxwtrans = pksz;
52680e9508eSDavid du Colombier ser->baudbase = baudbase;
52780e9508eSDavid du Colombier ser->outhdrsz = outhdrsz;
52880e9508eSDavid du Colombier ser->inhdrsz = 2;
529d5789509SDavid du Colombier
53080e9508eSDavid du Colombier dsprint (2, "serial: detected type: %#x\n", ser->type);
53180e9508eSDavid du Colombier }
53280e9508eSDavid du Colombier
53380e9508eSDavid du Colombier int
ftmatch(Serial * ser,char * info)53480e9508eSDavid du Colombier ftmatch(Serial *ser, char *info)
53580e9508eSDavid du Colombier {
53680e9508eSDavid du Colombier Cinfo *ip;
53780e9508eSDavid du Colombier char buf[50];
53880e9508eSDavid du Colombier
53980e9508eSDavid du Colombier for(ip = ftinfo; ip->vid != 0; ip++){
54080e9508eSDavid du Colombier snprint(buf, sizeof buf, "vid %#06x did %#06x", ip->vid, ip->did);
54180e9508eSDavid du Colombier dsprint(2, "serial: %s %s\n", buf, info);
54280e9508eSDavid du Colombier if(strstr(info, buf) != nil){
54380e9508eSDavid du Colombier if(ser != nil){
54480e9508eSDavid du Colombier qlock(ser);
54580e9508eSDavid du Colombier ftgettype(ser);
54680e9508eSDavid du Colombier qunlock(ser);
54780e9508eSDavid du Colombier }
54880e9508eSDavid du Colombier return 0;
54980e9508eSDavid du Colombier }
55080e9508eSDavid du Colombier }
55180e9508eSDavid du Colombier return -1;
55280e9508eSDavid du Colombier }
55380e9508eSDavid du Colombier
55480e9508eSDavid du Colombier static int
ftuseinhdr(Serialport * p,uchar * b)555d5789509SDavid du Colombier ftuseinhdr(Serialport *p, uchar *b)
55680e9508eSDavid du Colombier {
55780e9508eSDavid du Colombier if(b[0] & FTICTS)
558d5789509SDavid du Colombier p->cts = 1;
55980e9508eSDavid du Colombier else
560d5789509SDavid du Colombier p->cts = 0;
56180e9508eSDavid du Colombier if(b[0] & FTIDSR)
562d5789509SDavid du Colombier p->dsr = 1;
56380e9508eSDavid du Colombier else
564d5789509SDavid du Colombier p->dsr = 0;
56580e9508eSDavid du Colombier if(b[0] & FTIRI)
566d5789509SDavid du Colombier p->ring = 1;
56780e9508eSDavid du Colombier else
568d5789509SDavid du Colombier p->ring = 0;
56980e9508eSDavid du Colombier if(b[0] & FTIRLSD)
570d5789509SDavid du Colombier p->rlsd = 1;
57180e9508eSDavid du Colombier else
572d5789509SDavid du Colombier p->rlsd = 0;
57380e9508eSDavid du Colombier
57480e9508eSDavid du Colombier if(b[1] & FTIOE)
575d5789509SDavid du Colombier p->novererr++;
57680e9508eSDavid du Colombier if(b[1] & FTIPE)
577d5789509SDavid du Colombier p->nparityerr++;
57880e9508eSDavid du Colombier if(b[1] & FTIFE)
579d5789509SDavid du Colombier p->nframeerr++;
58080e9508eSDavid du Colombier if(b[1] & FTIBI)
581d5789509SDavid du Colombier p->nbreakerr++;
58280e9508eSDavid du Colombier return 0;
58380e9508eSDavid du Colombier }
58480e9508eSDavid du Colombier
58580e9508eSDavid du Colombier static int
ftsetouthdr(Serialport * p,uchar * b,int len)586d5789509SDavid du Colombier ftsetouthdr(Serialport *p, uchar *b, int len)
58780e9508eSDavid du Colombier {
588d5789509SDavid du Colombier if(p->s->outhdrsz != 0)
58980e9508eSDavid du Colombier b[0] = FTOPORT | (FTOLENMSK & len);
590d5789509SDavid du Colombier return p->s->outhdrsz;
59180e9508eSDavid du Colombier }
59280e9508eSDavid du Colombier
59380e9508eSDavid du Colombier static int
wait4data(Serialport * p,uchar * data,int count)594d5789509SDavid du Colombier wait4data(Serialport *p, uchar *data, int count)
59580e9508eSDavid du Colombier {
596d5789509SDavid du Colombier int d;
597d5789509SDavid du Colombier Serial *ser;
598d5789509SDavid du Colombier
599d5789509SDavid du Colombier ser = p->s;
600d5789509SDavid du Colombier
60180e9508eSDavid du Colombier qunlock(ser);
602d5789509SDavid du Colombier d = sendul(p->w4data, 1);
603ed868a7cSDavid du Colombier qlock(ser);
604d5789509SDavid du Colombier if(d <= 0)
605d5789509SDavid du Colombier return -1;
606d5789509SDavid du Colombier if(p->ndata >= count)
607d5789509SDavid du Colombier p->ndata -= count;
60880e9508eSDavid du Colombier else{
609d5789509SDavid du Colombier count = p->ndata;
610d5789509SDavid du Colombier p->ndata = 0;
61180e9508eSDavid du Colombier }
612ecc2a7c8SDavid du Colombier assert(count >= 0);
613d5789509SDavid du Colombier assert(p->ndata >= 0);
614d5789509SDavid du Colombier memmove(data, p->data, count);
615d5789509SDavid du Colombier if(p->ndata != 0)
616d5789509SDavid du Colombier memmove(p->data, p->data+count, p->ndata);
61780e9508eSDavid du Colombier
618d5789509SDavid du Colombier recvul(p->gotdata);
61980e9508eSDavid du Colombier return count;
62080e9508eSDavid du Colombier }
62180e9508eSDavid du Colombier
62280e9508eSDavid du Colombier static int
wait4write(Serialport * p,uchar * data,int count)623d5789509SDavid du Colombier wait4write(Serialport *p, uchar *data, int count)
62480e9508eSDavid du Colombier {
62580e9508eSDavid du Colombier int off, fd;
62680e9508eSDavid du Colombier uchar *b;
627d5789509SDavid du Colombier Serial *ser;
62880e9508eSDavid du Colombier
629d5789509SDavid du Colombier ser = p->s;
63080e9508eSDavid du Colombier
63180e9508eSDavid du Colombier b = emallocz(count+ser->outhdrsz, 1);
632d5789509SDavid du Colombier off = ftsetouthdr(p, b, count);
63380e9508eSDavid du Colombier memmove(b+off, data, count);
63480e9508eSDavid du Colombier
635d5789509SDavid du Colombier fd = p->epout->dfd;
63680e9508eSDavid du Colombier qunlock(ser);
63780e9508eSDavid du Colombier count = write(fd, b, count+off);
63880e9508eSDavid du Colombier qlock(ser);
63980e9508eSDavid du Colombier free(b);
64080e9508eSDavid du Colombier return count;
64180e9508eSDavid du Colombier }
64280e9508eSDavid du Colombier
64380e9508eSDavid du Colombier typedef struct Packser Packser;
64480e9508eSDavid du Colombier struct Packser{
64580e9508eSDavid du Colombier int nb;
646d5789509SDavid du Colombier uchar b[Bufsiz];
64780e9508eSDavid du Colombier };
64880e9508eSDavid du Colombier
64980e9508eSDavid du Colombier typedef struct Areader Areader;
65080e9508eSDavid du Colombier struct Areader{
651d5789509SDavid du Colombier Serialport *p;
65280e9508eSDavid du Colombier Channel *c;
65380e9508eSDavid du Colombier };
65480e9508eSDavid du Colombier
65580e9508eSDavid du Colombier static void
shutdownchan(Channel * c)656d5789509SDavid du Colombier shutdownchan(Channel *c)
657d5789509SDavid du Colombier {
658d5789509SDavid du Colombier Packser *bp;
659d5789509SDavid du Colombier
660d5789509SDavid du Colombier while((bp=nbrecvp(c)) != nil)
661d5789509SDavid du Colombier free(bp);
662d5789509SDavid du Colombier chanfree(c);
663d5789509SDavid du Colombier }
664d5789509SDavid du Colombier
665d5789509SDavid du Colombier int
cpdata(Serial * ser,Serialport * port,uchar * out,uchar * in,int sz)666d5789509SDavid du Colombier cpdata(Serial *ser, Serialport *port, uchar *out, uchar *in, int sz)
667d5789509SDavid du Colombier {
668d5789509SDavid du Colombier int i, ncp, ntotcp, pksz;
669d5789509SDavid du Colombier
670d5789509SDavid du Colombier pksz = ser->maxrtrans;
671d5789509SDavid du Colombier ntotcp = 0;
672d5789509SDavid du Colombier
673d5789509SDavid du Colombier for(i = 0; i < sz; i+= pksz){
674d5789509SDavid du Colombier ftuseinhdr(port, in + i);
675d5789509SDavid du Colombier if(sz - i > pksz)
676d5789509SDavid du Colombier ncp = pksz - ser->inhdrsz;
677d5789509SDavid du Colombier else
678d5789509SDavid du Colombier ncp = sz - i - ser->inhdrsz;
679d5789509SDavid du Colombier memmove(out, in + i + ser->inhdrsz, ncp);
680d5789509SDavid du Colombier out += ncp;
681d5789509SDavid du Colombier ntotcp += ncp;
682d5789509SDavid du Colombier }
683d5789509SDavid du Colombier return ntotcp;
684d5789509SDavid du Colombier }
685d5789509SDavid du Colombier
686d5789509SDavid du Colombier static void
epreader(void * u)68780e9508eSDavid du Colombier epreader(void *u)
68880e9508eSDavid du Colombier {
689c8a340cdSDavid du Colombier int dfd, rcount, cl, ntries, recov;
69080e9508eSDavid du Colombier char err[40];
69180e9508eSDavid du Colombier Areader *a;
69280e9508eSDavid du Colombier Channel *c;
693d5789509SDavid du Colombier Packser *pk;
69480e9508eSDavid du Colombier Serial *ser;
695d5789509SDavid du Colombier Serialport *p;
69680e9508eSDavid du Colombier
69780e9508eSDavid du Colombier threadsetname("epreader proc");
69880e9508eSDavid du Colombier a = u;
699d5789509SDavid du Colombier p = a->p;
700d5789509SDavid du Colombier ser = p->s;
70180e9508eSDavid du Colombier c = a->c;
70280e9508eSDavid du Colombier free(a);
70380e9508eSDavid du Colombier
704c8a340cdSDavid du Colombier qlock(ser); /* this makes the reader wait end of initialization too */
705d5789509SDavid du Colombier dfd = p->epin->dfd;
70680e9508eSDavid du Colombier qunlock(ser);
70780e9508eSDavid du Colombier
708c8a340cdSDavid du Colombier ntries = 0;
709d5789509SDavid du Colombier pk = nil;
71080e9508eSDavid du Colombier do {
711d5789509SDavid du Colombier if (pk == nil)
712d5789509SDavid du Colombier pk = emallocz(sizeof(Packser), 1);
713c8a340cdSDavid du Colombier Eagain:
714d5789509SDavid du Colombier rcount = read(dfd, pk->b, sizeof pk->b);
71580e9508eSDavid du Colombier if(serialdebug > 5)
716d5789509SDavid du Colombier dsprint(2, "%d %#ux%#ux ", rcount, p->data[0],
717d5789509SDavid du Colombier p->data[1]);
718c8a340cdSDavid du Colombier
719c8a340cdSDavid du Colombier if(rcount < 0){
720c8a340cdSDavid du Colombier if(ntries++ > 100)
72180e9508eSDavid du Colombier break;
722c8a340cdSDavid du Colombier qlock(ser);
723c8a340cdSDavid du Colombier recov = serialrecover(ser, p, nil, "epreader: bulkin error");
724c8a340cdSDavid du Colombier qunlock(ser);
725c8a340cdSDavid du Colombier if(recov >= 0)
726c8a340cdSDavid du Colombier goto Eagain;
727c8a340cdSDavid du Colombier }
728d5789509SDavid du Colombier if(rcount == 0)
729d5789509SDavid du Colombier continue;
73080e9508eSDavid du Colombier if(rcount >= ser->inhdrsz){
731d5789509SDavid du Colombier rcount = cpdata(ser, p, pk->b, pk->b, rcount);
73280e9508eSDavid du Colombier if(rcount != 0){
733d5789509SDavid du Colombier pk->nb = rcount;
734d5789509SDavid du Colombier cl = sendp(c, pk);
735d5789509SDavid du Colombier if(cl < 0){
736d5789509SDavid du Colombier /*
737d5789509SDavid du Colombier * if it was a time-out, I don't want
738d5789509SDavid du Colombier * to give back an error.
739d5789509SDavid du Colombier */
740d5789509SDavid du Colombier rcount = 0;
741d5789509SDavid du Colombier break;
74280e9508eSDavid du Colombier }
743d5789509SDavid du Colombier }else
744d5789509SDavid du Colombier free(pk);
745c8a340cdSDavid du Colombier qlock(ser);
746c8a340cdSDavid du Colombier ser->recover = 0;
747c8a340cdSDavid du Colombier qunlock(ser);
748c8a340cdSDavid du Colombier ntries = 0;
749d5789509SDavid du Colombier pk = nil;
75080e9508eSDavid du Colombier }
75180e9508eSDavid du Colombier } while(rcount >= 0 || (rcount < 0 && strstr(err, "timed out") != nil));
75280e9508eSDavid du Colombier
75380e9508eSDavid du Colombier if(rcount < 0)
754d5789509SDavid du Colombier fprint(2, "%s: error reading %s: %r\n", argv0, p->fs.name);
755d5789509SDavid du Colombier free(pk);
756d5789509SDavid du Colombier nbsendp(c, nil);
757d5789509SDavid du Colombier if(p->w4data != nil)
758d5789509SDavid du Colombier chanclose(p->w4data);
759d5789509SDavid du Colombier if(p->gotdata != nil)
760d5789509SDavid du Colombier chanclose(p->gotdata);
761d5789509SDavid du Colombier devctl(ser->dev, "detach");
76280e9508eSDavid du Colombier closedev(ser->dev);
763d5789509SDavid du Colombier usbfsdel(&p->fs);
76480e9508eSDavid du Colombier }
76580e9508eSDavid du Colombier
76680e9508eSDavid du Colombier static void
statusreader(void * u)76780e9508eSDavid du Colombier statusreader(void *u)
76880e9508eSDavid du Colombier {
76980e9508eSDavid du Colombier Areader *a;
77080e9508eSDavid du Colombier Channel *c;
771d5789509SDavid du Colombier Packser *pk;
772d5789509SDavid du Colombier Serialport *p;
77380e9508eSDavid du Colombier Serial *ser;
774d5789509SDavid du Colombier int cl;
77580e9508eSDavid du Colombier
776d5789509SDavid du Colombier p = u;
777d5789509SDavid du Colombier ser = p->s;
77880e9508eSDavid du Colombier threadsetname("statusreader thread");
77980e9508eSDavid du Colombier /* big buffering, fewer bytes lost */
78080e9508eSDavid du Colombier c = chancreate(sizeof(Packser *), 128);
78180e9508eSDavid du Colombier a = emallocz(sizeof(Areader), 1);
782d5789509SDavid du Colombier a->p = p;
78380e9508eSDavid du Colombier a->c = c;
78480e9508eSDavid du Colombier incref(ser->dev);
78580e9508eSDavid du Colombier proccreate(epreader, a, 16*1024);
78680e9508eSDavid du Colombier
787d5789509SDavid du Colombier while((pk = recvp(c)) != nil){
788d5789509SDavid du Colombier memmove(p->data, pk->b, pk->nb);
789d5789509SDavid du Colombier p->ndata = pk->nb;
790d5789509SDavid du Colombier free(pk);
791d584e620SDavid du Colombier dsprint(2, "serial %p: status reader %d \n", p, p->ndata);
79280e9508eSDavid du Colombier /* consume it all */
793d5789509SDavid du Colombier while(p->ndata != 0){
794d584e620SDavid du Colombier dsprint(2, "serial %p: status reader to consume: %d\n",
795d584e620SDavid du Colombier p, p->ndata);
796d5789509SDavid du Colombier cl = recvul(p->w4data);
797d5789509SDavid du Colombier if(cl < 0)
798d5789509SDavid du Colombier break;
799d5789509SDavid du Colombier cl = sendul(p->gotdata, 1);
800d5789509SDavid du Colombier if(cl < 0)
801d5789509SDavid du Colombier break;
80280e9508eSDavid du Colombier }
80380e9508eSDavid du Colombier }
804d5789509SDavid du Colombier
805d5789509SDavid du Colombier shutdownchan(c);
806d5789509SDavid du Colombier devctl(ser->dev, "detach");
80780e9508eSDavid du Colombier closedev(ser->dev);
808d5789509SDavid du Colombier usbfsdel(&p->fs);
80980e9508eSDavid du Colombier }
81080e9508eSDavid du Colombier
81180e9508eSDavid du Colombier static int
ftreset(Serial * ser,Serialport * p)812c8a340cdSDavid du Colombier ftreset(Serial *ser, Serialport *p)
81380e9508eSDavid du Colombier {
814d5789509SDavid du Colombier int i;
815d5789509SDavid du Colombier
816c8a340cdSDavid du Colombier if(p != nil){
817c8a340cdSDavid du Colombier ftdiwrite(p, FTRESETCTLVAL, 0, FTRESET);
818c8a340cdSDavid du Colombier return 0;
819c8a340cdSDavid du Colombier }
820d5789509SDavid du Colombier p = ser->p;
821d5789509SDavid du Colombier for(i = 0; i < Maxifc; i++)
822d584e620SDavid du Colombier if(p[i].s != nil)
823d5789509SDavid du Colombier ftdiwrite(&p[i], FTRESETCTLVAL, 0, FTRESET);
82480e9508eSDavid du Colombier return 0;
82580e9508eSDavid du Colombier }
82680e9508eSDavid du Colombier
82780e9508eSDavid du Colombier static int
ftinit(Serialport * p)828d5789509SDavid du Colombier ftinit(Serialport *p)
82980e9508eSDavid du Colombier {
830d5789509SDavid du Colombier Serial *ser;
831d584e620SDavid du Colombier uint timerval;
832d584e620SDavid du Colombier int res;
83380e9508eSDavid du Colombier
834d5789509SDavid du Colombier ser = p->s;
835d584e620SDavid du Colombier if(p->isjtag){
836d584e620SDavid du Colombier res = ftdiwrite(p, FTSETFLOWCTRL, 0, FTDISABLEFLOWCTRL);
837d584e620SDavid du Colombier if(res < 0)
838d584e620SDavid du Colombier return -1;
839d584e620SDavid du Colombier res = ftdiread(p, FTSETLATENCYTIMER, 0, (uchar *)&timerval,
840d584e620SDavid du Colombier FTLATENCYTIMERSZ);
841d584e620SDavid du Colombier if(res < 0)
842d584e620SDavid du Colombier return -1;
843d584e620SDavid du Colombier dsprint(2, "serial: jtag latency timer is %d\n", timerval);
844d584e620SDavid du Colombier timerval = 2;
845d584e620SDavid du Colombier ftdiwrite(p, FTLATENCYDEFAULT, 0, FTSETLATENCYTIMER);
846d584e620SDavid du Colombier res = ftdiread(p, FTSETLATENCYTIMER, 0, (uchar *)&timerval,
847d584e620SDavid du Colombier FTLATENCYTIMERSZ);
848d584e620SDavid du Colombier if(res < 0)
849d584e620SDavid du Colombier return -1;
850d584e620SDavid du Colombier
851d584e620SDavid du Colombier dsprint(2, "serial: jtag latency timer set to %d\n", timerval);
852d584e620SDavid du Colombier /* may be unnecessary */
853d584e620SDavid du Colombier devctl(p->epin, "timeout 5000");
854d584e620SDavid du Colombier devctl(p->epout, "timeout 5000");
855d584e620SDavid du Colombier /* 0xb is the mask for lines. plug dependant? */
856d584e620SDavid du Colombier ftdiwrite(p, BMMPSSE|0x0b, 0, FTSETBITMODE);
857d584e620SDavid du Colombier }
85880e9508eSDavid du Colombier incref(ser->dev);
859d5789509SDavid du Colombier threadcreate(statusreader, p, 8*1024);
86080e9508eSDavid du Colombier return 0;
86180e9508eSDavid du Colombier }
86280e9508eSDavid du Colombier
86380e9508eSDavid du Colombier static int
ftsetbreak(Serialport * p,int val)864d5789509SDavid du Colombier ftsetbreak(Serialport *p, int val)
86580e9508eSDavid du Colombier {
866d5789509SDavid du Colombier return ftdiwrite(p, (val != 0? FTSETBREAK: 0), 0, FTSETDATA);
86780e9508eSDavid du Colombier }
86880e9508eSDavid du Colombier
86980e9508eSDavid du Colombier static int
ftclearpipes(Serialport * p)870d5789509SDavid du Colombier ftclearpipes(Serialport *p)
87180e9508eSDavid du Colombier {
87280e9508eSDavid du Colombier /* maybe can be done in one... */
873d5789509SDavid du Colombier ftdiwrite(p, FTRESETCTLVALPURGETX, 0, FTRESET);
874d5789509SDavid du Colombier ftdiwrite(p, FTRESETCTLVALPURGERX, 0, FTRESET);
87580e9508eSDavid du Colombier return 0;
87680e9508eSDavid du Colombier }
87780e9508eSDavid du Colombier
87880e9508eSDavid du Colombier static int
setctlline(Serialport * p,uchar val)879d5789509SDavid du Colombier setctlline(Serialport *p, uchar val)
88080e9508eSDavid du Colombier {
881d5789509SDavid du Colombier return ftdiwrite(p, val | (val << 8), 0, FTSETMODEMCTRL);
88280e9508eSDavid du Colombier }
88380e9508eSDavid du Colombier
88480e9508eSDavid du Colombier static void
updatectlst(Serialport * p,int val)885d5789509SDavid du Colombier updatectlst(Serialport *p, int val)
88680e9508eSDavid du Colombier {
887d5789509SDavid du Colombier if(p->rts)
888d5789509SDavid du Colombier p->ctlstate |= val;
88980e9508eSDavid du Colombier else
890d5789509SDavid du Colombier p->ctlstate &= ~val;
89180e9508eSDavid du Colombier }
89280e9508eSDavid du Colombier
89380e9508eSDavid du Colombier static int
setctl(Serialport * p)894d5789509SDavid du Colombier setctl(Serialport *p)
89580e9508eSDavid du Colombier {
89680e9508eSDavid du Colombier int res;
897d5789509SDavid du Colombier Serial *ser;
898d5789509SDavid du Colombier
899d5789509SDavid du Colombier ser = p->s;
90080e9508eSDavid du Colombier
90180e9508eSDavid du Colombier if(ser->dev->usb->vid == FTVid && ser->dev->usb->did == FTHETIRA1Did){
90280e9508eSDavid du Colombier fprint(2, "serial: cannot set lines for this device\n");
903d5789509SDavid du Colombier updatectlst(p, CtlRTS|CtlDTR);
904d5789509SDavid du Colombier p->rts = p->dtr = 1;
90580e9508eSDavid du Colombier return -1;
90680e9508eSDavid du Colombier }
90780e9508eSDavid du Colombier
90880e9508eSDavid du Colombier /* NB: you can not set DTR and RTS with one control message */
909d5789509SDavid du Colombier updatectlst(p, CtlRTS);
910d5789509SDavid du Colombier res = setctlline(p, (CtlRTS<<8)|p->ctlstate);
91180e9508eSDavid du Colombier if(res < 0)
91280e9508eSDavid du Colombier return res;
91380e9508eSDavid du Colombier
914d5789509SDavid du Colombier updatectlst(p, CtlDTR);
915d5789509SDavid du Colombier res = setctlline(p, (CtlDTR<<8)|p->ctlstate);
91680e9508eSDavid du Colombier if(res < 0)
91780e9508eSDavid du Colombier return res;
91880e9508eSDavid du Colombier
91980e9508eSDavid du Colombier return 0;
92080e9508eSDavid du Colombier }
92180e9508eSDavid du Colombier
92280e9508eSDavid du Colombier static int
ftsendlines(Serialport * p)923d5789509SDavid du Colombier ftsendlines(Serialport *p)
92480e9508eSDavid du Colombier {
92580e9508eSDavid du Colombier int res;
92680e9508eSDavid du Colombier
927d5789509SDavid du Colombier dsprint(2, "serial: sendlines: %#2.2x\n", p->ctlstate);
928d5789509SDavid du Colombier res = setctl(p);
92980e9508eSDavid du Colombier dsprint(2, "serial: sendlines res: %d\n", res);
93080e9508eSDavid du Colombier return 0;
93180e9508eSDavid du Colombier }
93280e9508eSDavid du Colombier
93380e9508eSDavid du Colombier static int
ftseteps(Serialport * p)934d5789509SDavid du Colombier ftseteps(Serialport *p)
93580e9508eSDavid du Colombier {
93680e9508eSDavid du Colombier char *s;
937d5789509SDavid du Colombier Serial *ser;
93880e9508eSDavid du Colombier
939d5789509SDavid du Colombier ser = p->s;
940d5789509SDavid du Colombier
941d5789509SDavid du Colombier s = smprint("maxpkt %d", ser->maxrtrans);
942d5789509SDavid du Colombier devctl(p->epin, s);
943d5789509SDavid du Colombier free(s);
944d5789509SDavid du Colombier
945d5789509SDavid du Colombier s = smprint("maxpkt %d", ser->maxwtrans);
946d5789509SDavid du Colombier devctl(p->epout, s);
94780e9508eSDavid du Colombier free(s);
94880e9508eSDavid du Colombier return 0;
94980e9508eSDavid du Colombier }
95080e9508eSDavid du Colombier
95180e9508eSDavid du Colombier Serialops ftops = {
95280e9508eSDavid du Colombier .init = ftinit,
95380e9508eSDavid du Colombier .seteps = ftseteps,
95480e9508eSDavid du Colombier .setparam = ftsetparam,
95580e9508eSDavid du Colombier .clearpipes = ftclearpipes,
95680e9508eSDavid du Colombier .reset = ftreset,
95780e9508eSDavid du Colombier .sendlines = ftsendlines,
95880e9508eSDavid du Colombier .modemctl = ftmodemctl,
95980e9508eSDavid du Colombier .setbreak = ftsetbreak,
96080e9508eSDavid du Colombier .wait4data = wait4data,
96180e9508eSDavid du Colombier .wait4write = wait4write,
96280e9508eSDavid du Colombier };
963