xref: /plan9/sys/src/cmd/usb/serial/ftdi.c (revision 6f756d421e301d3b6a3d9e3be3872a27b7fbdeea)
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