xref: /plan9/sys/src/cmd/usb/serial/prolific.c (revision 5e4f5c7857c9a6aaee7e5b3fa086afa0917883d8)
1*5e4f5c78SDavid du Colombier #include <u.h>
2*5e4f5c78SDavid du Colombier #include <libc.h>
3*5e4f5c78SDavid du Colombier #include <thread.h>
4*5e4f5c78SDavid du Colombier #include "usb.h"
5*5e4f5c78SDavid du Colombier #include "usbfs.h"
6*5e4f5c78SDavid du Colombier #include "serial.h"
7*5e4f5c78SDavid du Colombier #include "prolific.h"
8*5e4f5c78SDavid du Colombier 
9*5e4f5c78SDavid du Colombier static void	statusreader(void *u);
10*5e4f5c78SDavid du Colombier 
11*5e4f5c78SDavid du Colombier static void
12*5e4f5c78SDavid du Colombier dumpbuf(uchar *buf, int bufsz)
13*5e4f5c78SDavid du Colombier {
14*5e4f5c78SDavid du Colombier 	int i;
15*5e4f5c78SDavid du Colombier 
16*5e4f5c78SDavid du Colombier 	for(i=0; i<bufsz; i++)
17*5e4f5c78SDavid du Colombier 		print("buf[%d]=%#ux ", i, buf[i]);
18*5e4f5c78SDavid du Colombier 	print("\n");
19*5e4f5c78SDavid du Colombier }
20*5e4f5c78SDavid du Colombier 
21*5e4f5c78SDavid du Colombier static int
22*5e4f5c78SDavid du Colombier vendorread(Serial *ser, int val, int index, uchar *buf)
23*5e4f5c78SDavid du Colombier {
24*5e4f5c78SDavid du Colombier 	int res;
25*5e4f5c78SDavid du Colombier 
26*5e4f5c78SDavid du Colombier 	dsprint(2, "serial: vendorread val: 0x%x idx:%d buf:%p\n",
27*5e4f5c78SDavid du Colombier 		val, index, buf);
28*5e4f5c78SDavid du Colombier 	res = usbcmd(ser->dev,  Rd2h | Rvendor | Rdev, VendorReadReq,
29*5e4f5c78SDavid du Colombier 		val, index, buf, 1);
30*5e4f5c78SDavid du Colombier 	dsprint(2, "serial: vendorread res:%d\n", res);
31*5e4f5c78SDavid du Colombier 	return res;
32*5e4f5c78SDavid du Colombier }
33*5e4f5c78SDavid du Colombier 
34*5e4f5c78SDavid du Colombier static int
35*5e4f5c78SDavid du Colombier vendorwrite(Serial *ser, int val, int index)
36*5e4f5c78SDavid du Colombier {
37*5e4f5c78SDavid du Colombier 	int res;
38*5e4f5c78SDavid du Colombier 
39*5e4f5c78SDavid du Colombier 	dsprint(2, "serial: vendorwrite val: 0x%x idx:%d\n", val, index);
40*5e4f5c78SDavid du Colombier 	res = usbcmd(ser->dev, Rh2d | Rvendor | Rdev, VendorWriteReq,
41*5e4f5c78SDavid du Colombier 		val, index, nil, 0);
42*5e4f5c78SDavid du Colombier 	dsprint(2, "serial: vendorwrite res:%d\n", res);
43*5e4f5c78SDavid du Colombier 	return res;
44*5e4f5c78SDavid du Colombier }
45*5e4f5c78SDavid du Colombier 
46*5e4f5c78SDavid du Colombier static int
47*5e4f5c78SDavid du Colombier plgetparam(Serial *ser)
48*5e4f5c78SDavid du Colombier {
49*5e4f5c78SDavid du Colombier 	uchar buf[7];
50*5e4f5c78SDavid du Colombier 	int res;
51*5e4f5c78SDavid du Colombier 
52*5e4f5c78SDavid du Colombier 	res = usbcmd(ser->dev, Rd2h | Rclass | Riface, GetLineReq,
53*5e4f5c78SDavid du Colombier 		0, 0, buf, sizeof buf);
54*5e4f5c78SDavid du Colombier 	ser->baud = GET4(buf);
55*5e4f5c78SDavid du Colombier 
56*5e4f5c78SDavid du Colombier 	/*
57*5e4f5c78SDavid du Colombier 	 * with the Pl9 interface it is not possible to set `1.5' as stop bits
58*5e4f5c78SDavid du Colombier 	 * for the prologic:
59*5e4f5c78SDavid du Colombier 	 *	0 is 1 stop bit
60*5e4f5c78SDavid du Colombier 	 *	1 is 1.5 stop bits
61*5e4f5c78SDavid du Colombier 	 *	2 is 2 stop bits
62*5e4f5c78SDavid du Colombier 	 */
63*5e4f5c78SDavid du Colombier 	if(buf[4] == 1)
64*5e4f5c78SDavid du Colombier 		fprint(2, "warning, stop bit set to 1.5 unsupported");
65*5e4f5c78SDavid du Colombier 	else if(buf[4] == 0)
66*5e4f5c78SDavid du Colombier 		ser->stop = 1;
67*5e4f5c78SDavid du Colombier 	else if(buf[4] == 2)
68*5e4f5c78SDavid du Colombier 		ser->stop = 2;
69*5e4f5c78SDavid du Colombier 	ser->parity = buf[5];
70*5e4f5c78SDavid du Colombier 	ser->bits = buf[6];
71*5e4f5c78SDavid du Colombier 
72*5e4f5c78SDavid du Colombier 	dsprint(2, "serial: getparam: ");
73*5e4f5c78SDavid du Colombier 	if(serialdebug)
74*5e4f5c78SDavid du Colombier 		dumpbuf(buf, sizeof buf);
75*5e4f5c78SDavid du Colombier 	dsprint(2, "serial: getparam res: %d\n", res);
76*5e4f5c78SDavid du Colombier 	return res;
77*5e4f5c78SDavid du Colombier }
78*5e4f5c78SDavid du Colombier 
79*5e4f5c78SDavid du Colombier static void
80*5e4f5c78SDavid du Colombier plmodemctl(Serial *ser, int set)
81*5e4f5c78SDavid du Colombier {
82*5e4f5c78SDavid du Colombier 	if(set == 0){
83*5e4f5c78SDavid du Colombier 		ser->mctl = 0;
84*5e4f5c78SDavid du Colombier 		vendorwrite(ser, 0x0, 0x0);
85*5e4f5c78SDavid du Colombier 		return;
86*5e4f5c78SDavid du Colombier 	}
87*5e4f5c78SDavid du Colombier 
88*5e4f5c78SDavid du Colombier 	ser->mctl = 1;
89*5e4f5c78SDavid du Colombier 	if(ser->type == TypeHX)
90*5e4f5c78SDavid du Colombier 		vendorwrite(ser, 0x0, Dcr0InitX);
91*5e4f5c78SDavid du Colombier 	else
92*5e4f5c78SDavid du Colombier 		vendorwrite(ser, 0x0, Dcr0InitH);
93*5e4f5c78SDavid du Colombier }
94*5e4f5c78SDavid du Colombier 
95*5e4f5c78SDavid du Colombier static int
96*5e4f5c78SDavid du Colombier plsetparam(Serial *ser)
97*5e4f5c78SDavid du Colombier {
98*5e4f5c78SDavid du Colombier 	uchar buf[7];
99*5e4f5c78SDavid du Colombier 	int res;
100*5e4f5c78SDavid du Colombier 
101*5e4f5c78SDavid du Colombier 	PUT4(buf, ser->baud);
102*5e4f5c78SDavid du Colombier 
103*5e4f5c78SDavid du Colombier 	if(ser->stop == 1)
104*5e4f5c78SDavid du Colombier 		buf[4] = 0;
105*5e4f5c78SDavid du Colombier 	else if(ser->stop == 2)
106*5e4f5c78SDavid du Colombier 		buf[4] = 2; 			/* see comment in getparam */
107*5e4f5c78SDavid du Colombier 	buf[5] = ser->parity;
108*5e4f5c78SDavid du Colombier 	buf[6] = ser->bits;
109*5e4f5c78SDavid du Colombier 
110*5e4f5c78SDavid du Colombier 	dsprint(2, "serial: setparam: ");
111*5e4f5c78SDavid du Colombier 	if(serialdebug)
112*5e4f5c78SDavid du Colombier 		dumpbuf(buf, sizeof buf);
113*5e4f5c78SDavid du Colombier 	res = usbcmd(ser->dev, Rh2d | Rclass | Riface, SetLineReq,
114*5e4f5c78SDavid du Colombier 		0, 0, buf, sizeof buf);
115*5e4f5c78SDavid du Colombier 	plmodemctl(ser, ser->mctl);
116*5e4f5c78SDavid du Colombier 	plgetparam(ser);		/* make sure our state corresponds */
117*5e4f5c78SDavid du Colombier 
118*5e4f5c78SDavid du Colombier 	dsprint(2, "serial: setparam res: %d\n", res);
119*5e4f5c78SDavid du Colombier 	return res;
120*5e4f5c78SDavid du Colombier }
121*5e4f5c78SDavid du Colombier 
122*5e4f5c78SDavid du Colombier static int
123*5e4f5c78SDavid du Colombier plinit(Serial *ser)
124*5e4f5c78SDavid du Colombier {
125*5e4f5c78SDavid du Colombier 	ulong csp;
126*5e4f5c78SDavid du Colombier 	char *st;
127*5e4f5c78SDavid du Colombier 	uchar *buf;
128*5e4f5c78SDavid du Colombier 
129*5e4f5c78SDavid du Colombier 	buf = emallocz(VendorReqSize, 1);
130*5e4f5c78SDavid du Colombier 	qlock(ser);
131*5e4f5c78SDavid du Colombier 	serialreset(ser);
132*5e4f5c78SDavid du Colombier 	csp = ser->dev->usb->csp;
133*5e4f5c78SDavid du Colombier 
134*5e4f5c78SDavid du Colombier 	if(Class(csp) == 0x02)
135*5e4f5c78SDavid du Colombier 		ser->type = Type0;
136*5e4f5c78SDavid du Colombier 	else if(ser->dev->maxpkt == 0x40)
137*5e4f5c78SDavid du Colombier 		ser->type = TypeHX;
138*5e4f5c78SDavid du Colombier 	else if(Class(csp) == 0x00 || Class(csp) == 0xFF)
139*5e4f5c78SDavid du Colombier 		ser->type = Type1;
140*5e4f5c78SDavid du Colombier 
141*5e4f5c78SDavid du Colombier 	if(ser->type != ser->dev->usb->psid)
142*5e4f5c78SDavid du Colombier 		fprint(2, "serial: warning, heuristics: %#ux and psid: "
143*5e4f5c78SDavid du Colombier 			"%#ux, not a match\n", ser->type, ser->dev->usb->psid);
144*5e4f5c78SDavid du Colombier 	dsprint(2, "serial: type %d\n", ser->type);
145*5e4f5c78SDavid du Colombier 
146*5e4f5c78SDavid du Colombier 	vendorread(ser, 0x8484, 0, buf);
147*5e4f5c78SDavid du Colombier 	vendorwrite(ser, 0x0404, 0);
148*5e4f5c78SDavid du Colombier 	vendorread(ser, 0x8484, 0, buf);
149*5e4f5c78SDavid du Colombier 	vendorread(ser, 0x8383, 0, buf);
150*5e4f5c78SDavid du Colombier 	vendorread(ser, 0x8484, 0, buf);
151*5e4f5c78SDavid du Colombier 	vendorwrite(ser, 0x0404, 1);
152*5e4f5c78SDavid du Colombier 	vendorread(ser, 0x8484, 0, buf);
153*5e4f5c78SDavid du Colombier 	vendorread(ser, 0x8383, 0, buf);
154*5e4f5c78SDavid du Colombier 	vendorwrite(ser, 0, 1);
155*5e4f5c78SDavid du Colombier 	vendorwrite(ser, 1, 0);
156*5e4f5c78SDavid du Colombier 
157*5e4f5c78SDavid du Colombier 	if(ser->type == TypeHX)
158*5e4f5c78SDavid du Colombier 		vendorwrite(ser, 2, Dcr2InitX);
159*5e4f5c78SDavid du Colombier 	else
160*5e4f5c78SDavid du Colombier 		vendorwrite(ser, 2, Dcr2InitH);
161*5e4f5c78SDavid du Colombier 
162*5e4f5c78SDavid du Colombier 	plgetparam(ser);
163*5e4f5c78SDavid du Colombier 	qunlock(ser);
164*5e4f5c78SDavid du Colombier 	free(buf);
165*5e4f5c78SDavid du Colombier 	st = emallocz(255, 1);
166*5e4f5c78SDavid du Colombier 	qlock(ser);
167*5e4f5c78SDavid du Colombier 	if(serialdebug)
168*5e4f5c78SDavid du Colombier 		dumpstatus(ser, st, 255);
169*5e4f5c78SDavid du Colombier 	dsprint(2, st);
170*5e4f5c78SDavid du Colombier 	qunlock(ser);
171*5e4f5c78SDavid du Colombier 	free(st);
172*5e4f5c78SDavid du Colombier 	/* ser gets freed by closedev, the process has a reference */
173*5e4f5c78SDavid du Colombier 	incref(ser->dev);
174*5e4f5c78SDavid du Colombier 	proccreate(statusreader, ser, 8*1024);
175*5e4f5c78SDavid du Colombier 	return 0;
176*5e4f5c78SDavid du Colombier }
177*5e4f5c78SDavid du Colombier 
178*5e4f5c78SDavid du Colombier static int
179*5e4f5c78SDavid du Colombier plsetbreak(Serial *ser, int val)
180*5e4f5c78SDavid du Colombier {
181*5e4f5c78SDavid du Colombier 	return usbcmd(ser->dev, Rh2d | Rclass | Riface,
182*5e4f5c78SDavid du Colombier 		(val != 0? BreakOn: BreakOff), val, 0, nil, 0);
183*5e4f5c78SDavid du Colombier }
184*5e4f5c78SDavid du Colombier 
185*5e4f5c78SDavid du Colombier static void
186*5e4f5c78SDavid du Colombier plclearpipes(Serial *ser)
187*5e4f5c78SDavid du Colombier {
188*5e4f5c78SDavid du Colombier 	if(ser->type == TypeHX){
189*5e4f5c78SDavid du Colombier 		vendorwrite(ser, 8, 0x0);
190*5e4f5c78SDavid du Colombier 		vendorwrite(ser, 9, 0x0);
191*5e4f5c78SDavid du Colombier 	}else{
192*5e4f5c78SDavid du Colombier 		if(unstall(ser->dev, ser->epout, Eout) < 0)
193*5e4f5c78SDavid du Colombier 			dprint(2, "disk: unstall epout: %r\n");
194*5e4f5c78SDavid du Colombier 		if(unstall(ser->dev, ser->epin, Ein) < 0)
195*5e4f5c78SDavid du Colombier 			dprint(2, "disk: unstall epin: %r\n");
196*5e4f5c78SDavid du Colombier 		if(unstall(ser->dev, ser->epintr, Ein) < 0)
197*5e4f5c78SDavid du Colombier 			dprint(2, "disk: unstall epintr: %r\n");
198*5e4f5c78SDavid du Colombier 	}
199*5e4f5c78SDavid du Colombier }
200*5e4f5c78SDavid du Colombier 
201*5e4f5c78SDavid du Colombier static int
202*5e4f5c78SDavid du Colombier setctlline(Serial *ser, uchar val)
203*5e4f5c78SDavid du Colombier {
204*5e4f5c78SDavid du Colombier 	return usbcmd(ser->dev, Rh2d | Rclass | Riface, SetCtlReq,
205*5e4f5c78SDavid du Colombier 		val, 0, nil, 0);
206*5e4f5c78SDavid du Colombier }
207*5e4f5c78SDavid du Colombier 
208*5e4f5c78SDavid du Colombier static void
209*5e4f5c78SDavid du Colombier composectl(Serial *ser)
210*5e4f5c78SDavid du Colombier {
211*5e4f5c78SDavid du Colombier 	if(ser->rts)
212*5e4f5c78SDavid du Colombier 		ser->ctlstate |= CtlRTS;
213*5e4f5c78SDavid du Colombier 	else
214*5e4f5c78SDavid du Colombier 		ser->ctlstate &= ~CtlRTS;
215*5e4f5c78SDavid du Colombier 	if(ser->dtr)
216*5e4f5c78SDavid du Colombier 		ser->ctlstate |= CtlDTR;
217*5e4f5c78SDavid du Colombier 	else
218*5e4f5c78SDavid du Colombier 		ser->ctlstate &= ~CtlDTR;
219*5e4f5c78SDavid du Colombier }
220*5e4f5c78SDavid du Colombier 
221*5e4f5c78SDavid du Colombier void
222*5e4f5c78SDavid du Colombier plsendlines(Serial *ser)
223*5e4f5c78SDavid du Colombier {
224*5e4f5c78SDavid du Colombier 	int res;
225*5e4f5c78SDavid du Colombier 
226*5e4f5c78SDavid du Colombier 	dsprint(2, "serial: sendlines: %#2.2x\n", ser->ctlstate);
227*5e4f5c78SDavid du Colombier 	composectl(ser);
228*5e4f5c78SDavid du Colombier 	res = setctlline(ser, ser->ctlstate);
229*5e4f5c78SDavid du Colombier 	dsprint(2, "serial: getparam res: %d\n", res);
230*5e4f5c78SDavid du Colombier }
231*5e4f5c78SDavid du Colombier 
232*5e4f5c78SDavid du Colombier static int
233*5e4f5c78SDavid du Colombier plreadstatus(Serial *ser)
234*5e4f5c78SDavid du Colombier {
235*5e4f5c78SDavid du Colombier 	int nr, dfd;
236*5e4f5c78SDavid du Colombier 	char err[40];
237*5e4f5c78SDavid du Colombier 	uchar buf[10];
238*5e4f5c78SDavid du Colombier 
239*5e4f5c78SDavid du Colombier 	qlock(ser);
240*5e4f5c78SDavid du Colombier 	dsprint(2, "serial: reading from interrupt\n");
241*5e4f5c78SDavid du Colombier 	dfd = ser->epintr->dfd;
242*5e4f5c78SDavid du Colombier 
243*5e4f5c78SDavid du Colombier 	qunlock(ser);
244*5e4f5c78SDavid du Colombier 	nr = read(dfd, buf, sizeof buf);
245*5e4f5c78SDavid du Colombier 	qlock(ser);
246*5e4f5c78SDavid du Colombier 	snprint(err, sizeof err, "%r");
247*5e4f5c78SDavid du Colombier 	dsprint(2, "serial: interrupt read %d %r\n", nr);
248*5e4f5c78SDavid du Colombier 
249*5e4f5c78SDavid du Colombier 	if(nr < 0 && strstr(err, "timed out") != nil){
250*5e4f5c78SDavid du Colombier 		dsprint(2, "serial: need to recover, status read %d %r\n", nr);
251*5e4f5c78SDavid du Colombier 		if(serialrecover(ser, err) < 0){
252*5e4f5c78SDavid du Colombier 			qunlock(ser);
253*5e4f5c78SDavid du Colombier 			return -1;
254*5e4f5c78SDavid du Colombier 		}
255*5e4f5c78SDavid du Colombier 	}
256*5e4f5c78SDavid du Colombier 	if(nr < 0)
257*5e4f5c78SDavid du Colombier 		dsprint(2, "serial: reading status: %r");
258*5e4f5c78SDavid du Colombier 	else if(nr >= sizeof buf - 1){
259*5e4f5c78SDavid du Colombier 		ser->dcd = buf[8] & DcdStatus;
260*5e4f5c78SDavid du Colombier 		ser->dsr = buf[8] & DsrStatus;
261*5e4f5c78SDavid du Colombier 		ser->cts = buf[8] & BreakerrStatus;
262*5e4f5c78SDavid du Colombier 		ser->ring = buf[8] & RingStatus;
263*5e4f5c78SDavid du Colombier 		ser->cts = buf[8] & CtsStatus;
264*5e4f5c78SDavid du Colombier 		if (buf[8] & FrerrStatus)
265*5e4f5c78SDavid du Colombier 			ser->nframeerr++;
266*5e4f5c78SDavid du Colombier 		if (buf[8] & ParerrStatus)
267*5e4f5c78SDavid du Colombier 			ser->nparityerr++;
268*5e4f5c78SDavid du Colombier 		if (buf[8] & OvererrStatus)
269*5e4f5c78SDavid du Colombier 			ser->novererr++;
270*5e4f5c78SDavid du Colombier 	} else
271*5e4f5c78SDavid du Colombier 		dsprint(2, "serial: bad status read %d\n", nr);
272*5e4f5c78SDavid du Colombier 	dsprint(2, "serial: finished read from interrupt %d\n", nr);
273*5e4f5c78SDavid du Colombier 	qunlock(ser);
274*5e4f5c78SDavid du Colombier 	return 0;
275*5e4f5c78SDavid du Colombier }
276*5e4f5c78SDavid du Colombier 
277*5e4f5c78SDavid du Colombier static void
278*5e4f5c78SDavid du Colombier statusreader(void *u)
279*5e4f5c78SDavid du Colombier {
280*5e4f5c78SDavid du Colombier 	Serial *ser;
281*5e4f5c78SDavid du Colombier 
282*5e4f5c78SDavid du Colombier 	ser = u;
283*5e4f5c78SDavid du Colombier 	threadsetname("statusreaderproc");
284*5e4f5c78SDavid du Colombier 
285*5e4f5c78SDavid du Colombier 	while(plreadstatus(ser) > 0)
286*5e4f5c78SDavid du Colombier 		;
287*5e4f5c78SDavid du Colombier 	closedev(ser->dev);
288*5e4f5c78SDavid du Colombier }
289*5e4f5c78SDavid du Colombier 
290*5e4f5c78SDavid du Colombier Serialops plops = {
291*5e4f5c78SDavid du Colombier 	.init =		plinit,
292*5e4f5c78SDavid du Colombier 	.getparam =	plgetparam,
293*5e4f5c78SDavid du Colombier 	.setparam =	plsetparam,
294*5e4f5c78SDavid du Colombier 	.clearpipes =	plclearpipes,
295*5e4f5c78SDavid du Colombier 	.sendlines =	plsendlines,
296*5e4f5c78SDavid du Colombier 	.modemctl =	plmodemctl,
297*5e4f5c78SDavid du Colombier 	.setbreak =	plsetbreak,
298*5e4f5c78SDavid du Colombier };
299