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