xref: /plan9/sys/src/cmd/usb/serial/ftdi.c (revision 6d2133e0d4be943e9ce2e416f00a3b046838334e)
1 /* Future Technology Devices International serial ports */
2 #include <u.h>
3 #include <libc.h>
4 #include <thread.h>
5 #include "usb.h"
6 #include "usbfs.h"
7 #include "serial.h"
8 #include "ftdi.h"
9 
10 /*
11  * BUG: This keeps growing, there has to be a better way, but without
12  * devices to try it...  We can probably simply look for FTDI in the
13  * string, or use regular expressions somehow.
14  */
15 Cinfo ftinfo[] = {
16 	{ FTVid, FTACTZWAVEDid },
17 	{ FTSheevaVid, FTSheevaDid },
18 	{ FTVid, FTOpenRDUltDid},
19 	{ FTVid, FTIRTRANSDid },
20 	{ FTVid, FTIPLUSDid },
21 	{ FTVid, FTSIODid },
22 	{ FTVid, FT8U232AMDid },
23 	{ FTVid, FT8U232AMALTDid },
24 	{ FTVid, FT8U2232CDid },
25 	{ FTVid, FTRELAISDid },
26 	{ INTERBIOMVid, INTERBIOMIOBRDDid },
27 	{ INTERBIOMVid, INTERBIOMMINIIOBRDDid },
28 	{ FTVid, FTXF632Did },
29 	{ FTVid, FTXF634Did },
30 	{ FTVid, FTXF547Did },
31 	{ FTVid, FTXF633Did },
32 	{ FTVid, FTXF631Did },
33 	{ FTVid, FTXF635Did },
34 	{ FTVid, FTXF640Did },
35 	{ FTVid, FTXF642Did },
36 	{ FTVid, FTDSS20Did },
37 	{ FTNFRICVid, FTNFRICDid },
38 	{ FTVid, FTVNHCPCUSBDDid },
39 	{ FTVid, FTMTXORB0Did },
40 	{ FTVid, FTMTXORB1Did },
41 	{ FTVid, FTMTXORB2Did },
42 	{ FTVid, FTMTXORB3Did },
43 	{ FTVid, FTMTXORB4Did },
44 	{ FTVid, FTMTXORB5Did },
45 	{ FTVid, FTMTXORB6Did },
46 	{ FTVid, FTPERLEULTRAPORTDid },
47 	{ FTVid, FTPIEGROUPDid },
48 	{ SEALEVELVid, SEALEVEL2101Did },
49 	{ SEALEVELVid, SEALEVEL2102Did },
50 	{ SEALEVELVid, SEALEVEL2103Did },
51 	{ SEALEVELVid, SEALEVEL2104Did },
52 	{ SEALEVELVid, SEALEVEL22011Did },
53 	{ SEALEVELVid, SEALEVEL22012Did },
54 	{ SEALEVELVid, SEALEVEL22021Did },
55 	{ SEALEVELVid, SEALEVEL22022Did },
56 	{ SEALEVELVid, SEALEVEL22031Did },
57 	{ SEALEVELVid, SEALEVEL22032Did },
58 	{ SEALEVELVid, SEALEVEL24011Did },
59 	{ SEALEVELVid, SEALEVEL24012Did },
60 	{ SEALEVELVid, SEALEVEL24013Did },
61 	{ SEALEVELVid, SEALEVEL24014Did },
62 	{ SEALEVELVid, SEALEVEL24021Did },
63 	{ SEALEVELVid, SEALEVEL24022Did },
64 	{ SEALEVELVid, SEALEVEL24023Did },
65 	{ SEALEVELVid, SEALEVEL24024Did },
66 	{ SEALEVELVid, SEALEVEL24031Did },
67 	{ SEALEVELVid, SEALEVEL24032Did },
68 	{ SEALEVELVid, SEALEVEL24033Did },
69 	{ SEALEVELVid, SEALEVEL24034Did },
70 	{ SEALEVELVid, SEALEVEL28011Did },
71 	{ SEALEVELVid, SEALEVEL28012Did },
72 	{ SEALEVELVid, SEALEVEL28013Did },
73 	{ SEALEVELVid, SEALEVEL28014Did },
74 	{ SEALEVELVid, SEALEVEL28015Did },
75 	{ SEALEVELVid, SEALEVEL28016Did },
76 	{ SEALEVELVid, SEALEVEL28017Did },
77 	{ SEALEVELVid, SEALEVEL28018Did },
78 	{ SEALEVELVid, SEALEVEL28021Did },
79 	{ SEALEVELVid, SEALEVEL28022Did },
80 	{ SEALEVELVid, SEALEVEL28023Did },
81 	{ SEALEVELVid, SEALEVEL28024Did },
82 	{ SEALEVELVid, SEALEVEL28025Did },
83 	{ SEALEVELVid, SEALEVEL28026Did },
84 	{ SEALEVELVid, SEALEVEL28027Did },
85 	{ SEALEVELVid, SEALEVEL28028Did },
86 	{ SEALEVELVid, SEALEVEL28031Did },
87 	{ SEALEVELVid, SEALEVEL28032Did },
88 	{ SEALEVELVid, SEALEVEL28033Did },
89 	{ SEALEVELVid, SEALEVEL28034Did },
90 	{ SEALEVELVid, SEALEVEL28035Did },
91 	{ SEALEVELVid, SEALEVEL28036Did },
92 	{ SEALEVELVid, SEALEVEL28037Did },
93 	{ SEALEVELVid, SEALEVEL28038Did },
94 	{ IDTECHVid, IDTECHIDT1221UDid },
95 	{ OCTVid, OCTUS101Did },
96 	{ FTVid, FTHETIRA1Did }, /* special quirk div = 240 baud = B38400 rtscts = 1 */
97 	{ FTVid, FTUSBUIRTDid }, /* special quirk div = 77, baud = B38400 */
98 	{ FTVid, PROTEGOSPECIAL1 },
99 	{ FTVid, PROTEGOR2X0 },
100 	{ FTVid, PROTEGOSPECIAL3 },
101 	{ FTVid, PROTEGOSPECIAL4 },
102 	{ FTVid, FTGUDEADSE808Did },
103 	{ FTVid, FTGUDEADSE809Did },
104 	{ FTVid, FTGUDEADSE80ADid },
105 	{ FTVid, FTGUDEADSE80BDid },
106 	{ FTVid, FTGUDEADSE80CDid },
107 	{ FTVid, FTGUDEADSE80DDid },
108 	{ FTVid, FTGUDEADSE80EDid },
109 	{ FTVid, FTGUDEADSE80FDid },
110 	{ FTVid, FTGUDEADSE888Did },
111 	{ FTVid, FTGUDEADSE889Did },
112 	{ FTVid, FTGUDEADSE88ADid },
113 	{ FTVid, FTGUDEADSE88BDid },
114 	{ FTVid, FTGUDEADSE88CDid },
115 	{ FTVid, FTGUDEADSE88DDid },
116 	{ FTVid, FTGUDEADSE88EDid },
117 	{ FTVid, FTGUDEADSE88FDid },
118 	{ FTVid, FTELVUO100Did },
119 	{ FTVid, FTELVUM100Did },
120 	{ FTVid, FTELVUR100Did },
121 	{ FTVid, FTELVALC8500Did },
122 	{ FTVid, FTPYRAMIDDid },
123 	{ FTVid, FTELVFHZ1000PCDid },
124 	{ FTVid, FTELVCLI7000Did },
125 	{ FTVid, FTELVPPS7330Did },
126 	{ FTVid, FTELVTFM100Did },
127 	{ FTVid, FTELVUDF77Did },
128 	{ FTVid, FTELVUIO88Did },
129 	{ FTVid, FTELVUAD8Did },
130 	{ FTVid, FTELVUDA7Did },
131 	{ FTVid, FTELVUSI2Did },
132 	{ FTVid, FTELVT1100Did },
133 	{ FTVid, FTELVPCD200Did },
134 	{ FTVid, FTELVULA200Did },
135 	{ FTVid, FTELVCSI8Did },
136 	{ FTVid, FTELVEM1000DLDid },
137 	{ FTVid, FTELVPCK100Did },
138 	{ FTVid, FTELVRFP500Did },
139 	{ FTVid, FTELVFS20SIGDid },
140 	{ FTVid, FTELVWS300PCDid },
141 	{ FTVid, FTELVFHZ1300PCDid },
142 	{ FTVid, FTELVWS500Did },
143 	{ FTVid, LINXSDMUSBQSSDid },
144 	{ FTVid, LINXMASTERDEVEL2Did },
145 	{ FTVid, LINXFUTURE0Did },
146 	{ FTVid, LINXFUTURE1Did },
147 	{ FTVid, LINXFUTURE2Did },
148 	{ FTVid, FTCCSICDU200Did },
149 	{ FTVid, FTCCSICDU401Did },
150 	{ FTVid, INSIDEACCESSO },
151 	{ INTREDidVid, INTREDidVALUECANDid },
152 	{ INTREDidVid, INTREDidNEOVIDid },
153 	{ FALCOMVid, FALCOMTWISTDid },
154 	{ FALCOMVid, FALCOMSAMBADid },
155 	{ FTVid, FTSUUNTOSPORTSDid },
156 	{ FTVid, FTRMCANVIEWDid },
157 	{ BANDBVid, BANDBUSOTL4Did },
158 	{ BANDBVid, BANDBUSTL4Did },
159 	{ BANDBVid, BANDBUSO9ML2Did },
160 	{ FTVid, EVERECOPROCDSDid },
161 	{ FTVid, FT4NGALAXYDE0Did },
162 	{ FTVid, FT4NGALAXYDE1Did },
163 	{ FTVid, FT4NGALAXYDE2Did },
164 	{ FTVid, XSENSCONVERTER0Did },
165 	{ FTVid, XSENSCONVERTER1Did },
166 	{ FTVid, XSENSCONVERTER2Did },
167 	{ FTVid, XSENSCONVERTER3Did },
168 	{ FTVid, XSENSCONVERTER4Did },
169 	{ FTVid, XSENSCONVERTER5Did },
170 	{ FTVid, XSENSCONVERTER6Did },
171 	{ FTVid, XSENSCONVERTER7Did },
172 	{ MOBILITYVid, MOBILITYUSBSERIALDid },
173 	{ FTVid, FTACTIVEROBOTSDid },
174 	{ FTVid, FTMHAMKWDid },
175 	{ FTVid, FTMHAMYSDid },
176 	{ FTVid, FTMHAMY6Did },
177 	{ FTVid, FTMHAMY8Did },
178 	{ FTVid, FTMHAMICDid },
179 	{ FTVid, FTMHAMDB9Did },
180 	{ FTVid, FTMHAMRS232Did },
181 	{ FTVid, FTMHAMY9Did },
182 	{ FTVid, FTTERATRONIKVCPDid },
183 	{ FTVid, FTTERATRONIKD2XXDid },
184 	{ EVOLUTIONVid, EVOLUTIONER1Did },
185 	{ FTVid, FTARTEMISDid },
186 	{ FTVid, FTATIKATK16Did },
187 	{ FTVid, FTATIKATK16CDid },
188 	{ FTVid, FTATIKATK16HRDid },
189 	{ FTVid, FTATIKATK16HRCDid },
190 	{ KOBILVid, KOBILCONVB1Did },
191 	{ KOBILVid, KOBILCONVKAANDid },
192 	{ POSIFLEXVid, POSIFLEXPP7000Did },
193 	{ FTVid, FTTTUSBDid },
194 	{ FTVid, FTECLOCOM1WIREDid },
195 	{ FTVid, FTWESTREXMODEL777Did },
196 	{ FTVid, FTWESTREXMODEL8900FDid },
197 	{ FTVid, FTPCDJDAC2Did },
198 	{ FTVid, FTRRCIRKITSLOCOBUFFERDid },
199 	{ FTVid, FTASKRDR400Did },
200 	{ ICOMID1Vid, ICOMID1Did },
201 	{ PAPOUCHVid, PAPOUCHTMUDid },
202 	{ FTVid, FTACGHFDUALDid },
203 	{ FT8U232AMDid, FT4232HDid },
204 	{ 0,	0 },
205 };
206 
207 enum {
208 	Packsz		= 64,		/* default size */
209 	Maxpacksz	= 512,
210 	Bufsiz		= 4 * 1024,
211 };
212 
213 static int
214 ftdiread(Serialport *p, int val, int index, int req, uchar *buf)
215 {
216 	int res;
217 	Serial *ser;
218 
219 	ser = p->s;
220 
221 	if(req != FTGETE2READ)
222 		index |= p->interfc + 1;
223 	dsprint(2, "serial: ftdiread %#p [%d] req: %#x val: %#x idx:%d buf:%p\n",
224 		p, p->interfc, req, val, index, buf);
225 	res = usbcmd(ser->dev,  Rd2h | Rftdireq | Rdev, req, val, index, buf, 1);
226 	dsprint(2, "serial: ftdiread res:%d\n", res);
227 	return res;
228 }
229 
230 static int
231 ftdiwrite(Serialport *p, int val, int index, int req)
232 {
233 	int res;
234 	Serial *ser;
235 
236 	ser = p->s;
237 
238 	index |= p->interfc + 1;
239 	dsprint(2, "serial: ftdiwrite %#p [%d] req: %#x val: %#x idx:%d\n",
240 		p, p->interfc, req, val, index);
241 	res = usbcmd(ser->dev, Rh2d | Rftdireq | Rdev, req, val, index, nil, 0);
242 	dsprint(2, "serial: ftdiwrite res:%d\n", res);
243 	return res;
244 }
245 
246 static int
247 ftmodemctl(Serialport *p, int set)
248 {
249 	if(set == 0){
250 		p->mctl = 0;
251 		ftdiwrite(p, 0, 0, FTSETMODEMCTRL);
252 		return 0;
253 	}
254 	p->mctl = 1;
255 	ftdiwrite(p, 0, FTRTSCTSHS, FTSETFLOWCTRL);
256 	return 0;
257 }
258 
259 static ushort
260 ft232ambaudbase2div(int baud, int base)
261 {
262 	int divisor3;
263 	ushort divisor;
264 
265 	divisor3 = (base / 2) / baud;
266  	if((divisor3 & 7) == 7)
267 		divisor3++; 			/* round x.7/8 up to x+1 */
268  	divisor = divisor3 >> 3;
269 	divisor3 &= 7;
270 
271 	if(divisor3 == 1)
272 		divisor |= 0xc000;		/*	0.125 */
273 	else if(divisor3 >= 4)
274 		divisor |= 0x4000;		/*	0.5	*/
275 	else if(divisor3 != 0)
276 		divisor |= 0x8000;		/*	0.25	*/
277  	if( divisor == 1)
278 		divisor = 0;		/* special case for maximum baud rate */
279 	return divisor;
280 }
281 
282 enum{
283 	ClockNew	= 48000000,
284 	ClockOld	= 12000000 / 16,
285 	HetiraDiv	= 240,
286 	UirtDiv		= 77,
287 };
288 
289 static ushort
290 ft232ambaud2div(int baud)
291 {
292 	return ft232ambaudbase2div(baud, ClockNew);
293 }
294 
295 static ulong divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7};
296 
297 static ulong
298 ft232bmbaudbase2div(int baud, int base)
299 {
300 	int divisor3;
301 	u32int divisor;
302 
303 	divisor3 = (base / 2) / baud;
304 	divisor = divisor3 >> 3 | divfrac[divisor3 & 7] << 14;
305 
306  	/* Deal with special cases for highest baud rates. */
307  	if( divisor == 1)
308 		divisor = 0; 			/* 1.0 */
309 	else if( divisor == 0x4001)
310 		divisor = 1;			/* 1.5 */
311 	return divisor;
312 }
313 
314 static ulong
315 ft232bmbaud2div (int baud)
316 {
317 	return ft232bmbaudbase2div (baud, ClockNew);
318 }
319 
320 static int
321 customdiv(Serial *ser)
322 {
323 	if(ser->dev->usb->vid == FTVid && ser->dev->usb->did == FTHETIRA1Did)
324 		return HetiraDiv;
325 	else if(ser->dev->usb->vid == FTVid && ser->dev->usb->did == FTUSBUIRTDid)
326 		return UirtDiv;
327 
328 	fprint(2, "serial: weird custom divisor\n");
329 	return 0;		/* shouldn't happen, break as much as I can */
330 }
331 
332 static ulong
333 ftbaudcalcdiv(Serial *ser, int baud)
334 {
335 	int cusdiv;
336 	ulong divval;
337 
338 	if(baud == 38400 && (cusdiv = customdiv(ser)) != 0)
339 		baud = ser->baudbase / cusdiv;
340 
341 	if(baud == 0)
342 		baud = 9600;
343 
344  	switch(ser->type) {
345  	case SIO:
346 		switch(baud) {
347 		case 300:
348 			divval = FTb300;
349 			break;
350 		case 600:
351 			divval = FTb600;
352 			break;
353 		case 1200:
354 			divval = FTb1200;
355 			break;
356 		case 2400:
357 			divval = FTb2400;
358 			break;
359 		case 4800:
360 			divval = FTb4800;
361 			break;
362 		case 9600:
363 			divval = FTb9600;
364 			break;
365 		case 19200:
366 			divval = FTb19200;
367 			break;
368 		case 38400:
369 			divval = FTb38400;
370 			break;
371 		case 57600:
372 			divval = FTb57600;
373 			break;
374 		case 115200:
375 			divval = FTb115200;
376 			break;
377 		default:
378 			divval = FTb9600;
379 			break;
380 		}
381 		break;
382  	case FT8U232AM:
383 	 	if(baud <= 3000000)
384 			divval = ft232ambaud2div(baud);
385 		else
386 			divval = ft232ambaud2div(9600);
387  		break;
388 	case FT232BM:
389 	case FT2232C:
390 	case FTKINDR:
391 	case FT2232H:
392 	case FT4232H:
393 		if(baud <= 3000000)
394 			divval = ft232bmbaud2div(baud);
395 		else
396 			divval = ft232bmbaud2div(9600);
397 		break;
398 	default:
399 		divval = ft232bmbaud2div(9600);
400 		break;
401 	}
402 	return divval;
403 }
404 
405 static int
406 ftsetparam(Serialport *p)
407 {
408 	int res;
409 	ushort val;
410 	ulong bauddiv;
411 
412 	val = 0;
413 	if(p->stop == 1)
414 		val |= FTSETDATASTOPBITS1;
415 	else if(p->stop == 2)
416 		val |= FTSETDATASTOPBITS2;
417 	else if(p->stop == 15)
418 		val |= FTSETDATASTOPBITS15;
419 	switch(p->parity){
420 	case 0:
421 		val |= FTSETDATAParNONE;
422 		break;
423 	case 1:
424 		val |= FTSETDATAParODD;
425 		break;
426 	case 2:
427 		val |= FTSETDATAParEVEN;
428 		break;
429 	case 3:
430 		val |= FTSETDATAParMARK;
431 		break;
432 	case 4:
433 		val |= FTSETDATAParSPACE;
434 		break;
435 	};
436 
437 	dsprint(2, "serial: setparam\n");
438 
439 	res = ftdiwrite(p, val, 0, FTSETDATA);
440 	if(res < 0)
441 		return res;
442 
443 	res = ftmodemctl(p, p->mctl);
444 	if(res < 0)
445 		return res;
446 
447 	bauddiv = ftbaudcalcdiv(p->s, p->baud);
448 	res = ftdiwrite(p, bauddiv, (bauddiv>>16) & 1, FTSETBaudRate);
449 
450 	dsprint(2, "serial: setparam res: %d\n", res);
451 	return res;
452 }
453 
454 /* ser locked */
455 static void
456 ftgettype(Serial *ser)
457 {
458 	int i, outhdrsz, dno, pksz;
459 	ulong baudbase;
460 	Conf *cnf;
461 
462 	pksz = Packsz;
463  	/* Assume it is not the original SIO device for now. */
464 	baudbase = ClockNew / 2;
465 	outhdrsz = 0;
466 	dno = ser->dev->usb->dno;
467 	cnf = ser->dev->usb->conf[0];
468 	ser->nifcs = 0;
469 	for(i = 0; i < Niface; i++)
470 		if(cnf->iface[i] != nil)
471 			ser->nifcs++;
472 	if(ser->nifcs > 1) {
473 		/*
474 		 * Multiple interfaces.  default assume FT2232C,
475 		 */
476 		if(dno == 0x500)
477 			ser->type = FT2232C;
478 		else if(dno == 0x600)
479 			ser->type = FTKINDR;
480 		else if(dno == 0x700){
481 			ser->type = FT2232H;
482 			pksz = Maxpacksz;
483 		} else if(dno == 0x800){
484 			ser->type = FT4232H;
485 			pksz = Maxpacksz;
486 		} else
487 			ser->type = FT2232C;
488 
489 		ser->jtag = 0;
490 
491 		/*
492 		 * BM-type devices have a bug where dno gets set
493 		 * to 0x200 when serial is 0.
494 		 */
495 		if(dno < 0x500)
496 			fprint(2, "serial: warning: dno %d too low for "
497 				"multi-interface device\n", dno);
498 	} else if(dno < 0x200) {
499 		/* Old device.  Assume it is the original SIO. */
500 		ser->type = SIO;
501 		baudbase = ClockOld/16;
502 		outhdrsz = 1;
503 	} else if(dno < 0x400)
504 		/*
505 		 * Assume its an FT8U232AM (or FT8U245AM)
506 		 * (It might be a BM because of the iSerialNumber bug,
507 		 * but it will still work as an AM device.)
508 		 */
509 		ser->type = FT8U232AM;
510 	else			/* Assume it is an FT232BM (or FT245BM) */
511 		ser->type = FT232BM;
512 
513 	ser->maxrtrans = ser->maxwtrans = pksz;
514 	ser->baudbase = baudbase;
515 	ser->outhdrsz = outhdrsz;
516 	ser->inhdrsz = 2;
517 
518 	dsprint (2, "serial: detected type: %#x\n", ser->type);
519 }
520 
521 int
522 ftmatch(Serial *ser, char *info)
523 {
524 	Cinfo *ip;
525 	char buf[50];
526 
527 	for(ip = ftinfo; ip->vid != 0; ip++){
528 		snprint(buf, sizeof buf, "vid %#06x did %#06x", ip->vid, ip->did);
529 		dsprint(2, "serial: %s %s\n", buf, info);
530 		if(strstr(info, buf) != nil){
531 			if(ser != nil){
532 				qlock(ser);
533 				ftgettype(ser);
534 				qunlock(ser);
535 			}
536 			return 0;
537 		}
538 	}
539 	return -1;
540 }
541 
542 static int
543 ftuseinhdr(Serialport *p, uchar *b)
544 {
545 	if(b[0] & FTICTS)
546 		p->cts = 1;
547 	else
548 		p->cts = 0;
549 	if(b[0] & FTIDSR)
550 		p->dsr = 1;
551 	else
552 		p->dsr = 0;
553 	if(b[0] & FTIRI)
554 		p->ring = 1;
555 	else
556 		p->ring = 0;
557 	if(b[0] & FTIRLSD)
558 		p->rlsd = 1;
559 	else
560 		p->rlsd = 0;
561 
562 	if(b[1] & FTIOE)
563 		p->novererr++;
564 	if(b[1] & FTIPE)
565 		p->nparityerr++;
566 	if(b[1] & FTIFE)
567 		p->nframeerr++;
568 	if(b[1] & FTIBI)
569 		p->nbreakerr++;
570 	return 0;
571 }
572 
573 static int
574 ftsetouthdr(Serialport *p, uchar *b, int len)
575 {
576 	if(p->s->outhdrsz != 0)
577 		b[0] = FTOPORT | (FTOLENMSK & len);
578 	return p->s->outhdrsz;
579 }
580 
581 static int
582 wait4data(Serialport *p, uchar *data, int count)
583 {
584 	int d;
585 	Serial *ser;
586 
587 	ser = p->s;
588 
589 	qunlock(ser);
590 	d = sendul(p->w4data, 1);
591 	qlock(ser);
592 	if(d <= 0)
593 		return -1;
594 	if(p->ndata >= count)
595 		p->ndata -= count;
596 	else{
597 		count = p->ndata;
598 		p->ndata = 0;
599 	}
600 	assert(count >= 0);
601 	assert(p->ndata >= 0);
602 	memmove(data, p->data, count);
603 	if(p->ndata != 0)
604 		memmove(p->data, p->data+count, p->ndata);
605 
606 	recvul(p->gotdata);
607 	return count;
608 }
609 
610 static int
611 wait4write(Serialport *p, uchar *data, int count)
612 {
613 	int off, fd;
614 	uchar *b;
615 	Serial *ser;
616 
617 	ser = p->s;
618 
619 	b = emallocz(count+ser->outhdrsz, 1);
620 	off = ftsetouthdr(p, b, count);
621 	memmove(b+off, data, count);
622 
623 	fd = p->epout->dfd;
624 	qunlock(ser);
625 	count = write(fd, b, count+off);
626 	qlock(ser);
627 	free(b);
628 	return count;
629 }
630 
631 typedef struct Packser Packser;
632 struct Packser{
633 	int	nb;
634 	uchar	b[Bufsiz];
635 };
636 
637 typedef struct Areader Areader;
638 struct Areader{
639 	Serialport	*p;
640 	Channel	*c;
641 };
642 
643 static void
644 shutdownchan(Channel *c)
645 {
646 	Packser *bp;
647 
648 	while((bp=nbrecvp(c)) != nil)
649 		free(bp);
650 	chanfree(c);
651 }
652 
653 int
654 cpdata(Serial *ser, Serialport *port, uchar *out, uchar *in, int sz)
655 {
656 	int i, ncp, ntotcp, pksz;
657 
658 	pksz = ser->maxrtrans;
659 	ntotcp = 0;
660 
661 	for(i = 0; i < sz; i+= pksz){
662 		ftuseinhdr(port, in + i);
663 		if(sz - i > pksz)
664 			ncp = pksz - ser->inhdrsz;
665 		else
666 			ncp = sz - i - ser->inhdrsz;
667 		memmove(out, in + i + ser->inhdrsz, ncp);
668 		out += ncp;
669 		ntotcp += ncp;
670 	}
671 	return ntotcp;
672 }
673 
674 static void
675 epreader(void *u)
676 {
677 	int dfd, rcount, cl;
678 	char err[40];
679 	Areader *a;
680 	Channel *c;
681 	Packser *pk;
682 	Serial *ser;
683 	Serialport *p;
684 
685 	threadsetname("epreader proc");
686 	a = u;
687 	p = a->p;
688 	ser = p->s;
689 	c = a->c;
690 	free(a);
691 
692 	qlock(ser);
693 	dfd = p->epin->dfd;
694 	qunlock(ser);
695 
696 	pk = nil;
697 	do {
698 		if (pk == nil)
699 			pk = emallocz(sizeof(Packser), 1);
700 		rcount = read(dfd, pk->b, sizeof pk->b);
701 		if(serialdebug > 5)
702 			dsprint(2, "%d %#ux%#ux ", rcount, p->data[0],
703 				p->data[1]);
704 		if(rcount < 0)
705 			break;
706 		if(rcount == 0)
707 			continue;
708 		if(rcount >= ser->inhdrsz){
709 			rcount = cpdata(ser, p, pk->b, pk->b, rcount);
710 			if(rcount != 0){
711 				pk->nb = rcount;
712 				cl = sendp(c, pk);
713 				if(cl < 0){
714 					/*
715 					 * if it was a time-out, I don't want
716 					 * to give back an error.
717 					 */
718 					rcount = 0;
719 					break;
720 				}
721 			}else
722 				free(pk);
723 			pk = nil;
724 		}
725 	} while(rcount >= 0 || (rcount < 0 && strstr(err, "timed out") != nil));
726 
727 	if(rcount < 0)
728 		fprint(2, "%s: error reading %s: %r\n", argv0, p->fs.name);
729 	free(pk);
730 	nbsendp(c, nil);
731 	if(p->w4data != nil)
732 		chanclose(p->w4data);
733 	if(p->gotdata != nil)
734 		chanclose(p->gotdata);
735 	devctl(ser->dev, "detach");
736 	closedev(ser->dev);
737 	usbfsdel(&p->fs);
738 }
739 
740 static void
741 statusreader(void *u)
742 {
743 	Areader *a;
744 	Channel *c;
745 	Packser *pk;
746 	Serialport *p;
747 	Serial *ser;
748 	int cl;
749 
750 	p = u;
751 	ser = p->s;
752 	threadsetname("statusreader thread");
753 	/* big buffering, fewer bytes lost */
754 	c = chancreate(sizeof(Packser *), 128);
755 	a = emallocz(sizeof(Areader), 1);
756 	a->p = p;
757 	a->c = c;
758 	incref(ser->dev);
759 	proccreate(epreader, a, 16*1024);
760 
761 	while((pk = recvp(c)) != nil){
762 		memmove(p->data, pk->b, pk->nb);
763 		p->ndata = pk->nb;
764 		free(pk);
765 		dsprint(2, "serial: status reader %d \n", p->ndata);
766 		/* consume it all */
767 		while(p->ndata != 0){
768 			dsprint(2, "serial: status reader to consume: %d\n",
769 				p->ndata);
770 			cl = recvul(p->w4data);
771 			if(cl  < 0)
772 				break;
773 			cl = sendul(p->gotdata, 1);
774 			if(cl  < 0)
775 				break;
776 		}
777 	}
778 
779 	shutdownchan(c);
780 	devctl(ser->dev, "detach");
781 	closedev(ser->dev);
782 	usbfsdel(&p->fs);
783 }
784 
785 static int
786 ftreset(Serial *ser)
787 {
788 	Serialport *p;
789 	int i;
790 
791 	p = ser->p;
792 	for(i = 0; i < Maxifc; i++)
793 		if(!p[i].isjtag && p[i].s != nil)
794 			ftdiwrite(&p[i], FTRESETCTLVAL, 0, FTRESET);
795 	return 0;
796 }
797 
798 static int
799 ftinit(Serialport *p)
800 {
801 	Serial *ser;
802 
803 	ser = p->s;
804 	incref(ser->dev);
805 	threadcreate(statusreader, p, 8*1024);
806 	return 0;
807 }
808 
809 static int
810 ftsetbreak(Serialport *p, int val)
811 {
812 	return ftdiwrite(p, (val != 0? FTSETBREAK: 0), 0, FTSETDATA);
813 }
814 
815 static int
816 ftclearpipes(Serialport *p)
817 {
818 	/* maybe can be done in one... */
819 	ftdiwrite(p, FTRESETCTLVALPURGETX, 0, FTRESET);
820 	ftdiwrite(p, FTRESETCTLVALPURGERX, 0, FTRESET);
821 	return 0;
822 }
823 
824 static int
825 setctlline(Serialport *p, uchar val)
826 {
827 	return ftdiwrite(p, val | (val << 8), 0, FTSETMODEMCTRL);
828 }
829 
830 static void
831 updatectlst(Serialport *p, int val)
832 {
833 	if(p->rts)
834 		p->ctlstate |= val;
835 	else
836 		p->ctlstate &= ~val;
837 }
838 
839 static int
840 setctl(Serialport *p)
841 {
842 	int res;
843 	Serial *ser;
844 
845 	ser = p->s;
846 
847 	if(ser->dev->usb->vid == FTVid && ser->dev->usb->did ==  FTHETIRA1Did){
848 		fprint(2, "serial: cannot set lines for this device\n");
849 		updatectlst(p, CtlRTS|CtlDTR);
850 		p->rts = p->dtr = 1;
851 		return -1;
852 	}
853 
854 	/* NB: you can not set DTR and RTS with one control message */
855 	updatectlst(p, CtlRTS);
856 	res = setctlline(p, (CtlRTS<<8)|p->ctlstate);
857 	if(res < 0)
858 		return res;
859 
860 	updatectlst(p, CtlDTR);
861 	res = setctlline(p, (CtlDTR<<8)|p->ctlstate);
862 	if(res < 0)
863 		return res;
864 
865 	return 0;
866 }
867 
868 static int
869 ftsendlines(Serialport *p)
870 {
871 	int res;
872 
873 	dsprint(2, "serial: sendlines: %#2.2x\n", p->ctlstate);
874 	res = setctl(p);
875 	dsprint(2, "serial: sendlines res: %d\n", res);
876 	return 0;
877 }
878 
879 static int
880 ftseteps(Serialport *p)
881 {
882 	char *s;
883 	Serial *ser;
884 
885 	ser = p->s;
886 
887 	s = smprint("maxpkt %d", ser->maxrtrans);
888 	devctl(p->epin, s);
889 	free(s);
890 
891 	s = smprint("maxpkt %d", ser->maxwtrans);
892 	devctl(p->epout, s);
893 	free(s);
894 	return 0;
895 }
896 
897 Serialops ftops = {
898 	.init		= ftinit,
899 	.seteps		= ftseteps,
900 	.setparam	= ftsetparam,
901 	.clearpipes	= ftclearpipes,
902 	.reset		= ftreset,
903 	.sendlines	= ftsendlines,
904 	.modemctl	= ftmodemctl,
905 	.setbreak	= ftsetbreak,
906 	.wait4data	= wait4data,
907 	.wait4write	= wait4write,
908 };
909