xref: /inferno-os/emu/port/deveia-posix.c (revision a60fa48ce2f27a689f276bea9538b5db2b74ff86)
1 /*
2  * Driver for POSIX serial ports
3  */
4 #include	"dat.h"
5 #include	"fns.h"
6 #include	"error.h"
7 #undef _POSIX_C_SOURCE       /* for deveia-bsd.c */
8 #include <sys/stat.h>
9 #include	<termios.h>
10 
11 enum
12 {
13 	Devchar = 't',
14 
15 	Ndataqid = 1,
16 	Nctlqid,
17 	Nstatqid,
18 	Nqid = 3,		/* number of QIDs */
19 
20 	CTLS=	023,
21 	CTLQ=	021,
22 
23 	Maxctl = 128,
24 	Maxfield = 32
25 };
26 
27 /*
28  *  Macros to manage QIDs
29  */
30 #define NETTYPE(x)	((x)&0x0F)
31 #define NETID(x)	((x)>>4)
32 #define NETQID(i,t)	(((i)<<4)|(t))
33 
34 static Dirtab *eiadir;
35 static int ndir;
36 
37 static char Devname[] = "eia";
38 
39 typedef struct Eia Eia;
40 struct Eia {
41 	Ref		r;
42 	int		fd;
43 	int		overrun;
44 	int		frame;
45 	int		restore;       /* flag to restore prev. states */
46 	struct termios 	ts;
47 	int		dtr;
48 	int		rts;
49 	int		cts;
50 };
51 
52 static Eia *eia;
53 
54 struct tcdef_t {
55 	int	val;
56 	tcflag_t	flag;
57 };
58 
59 struct flagmap {
60 	char*	s;
61 	tcflag_t	flag;
62 };
63 
64 static struct tcdef_t bps[];
65 
66 static struct tcdef_t size[] = {
67 	{5,	CS5},
68 	{6,	CS6},
69 	{7,	CS7},
70 	{8,	CS8},
71 	{-1,	-1}
72 };
73 
74 static char *
75 ftos(char *buf, struct tcdef_t *tbl, tcflag_t flag)
76 {
77 	for(; tbl->val >= 0; tbl++)
78 		if(tbl->flag == flag){
79 			sprint(buf, "%d", tbl->val);
80 			return buf;
81 		}
82 	return "unknown";
83 }
84 
85 static tcflag_t
86 stof(struct tcdef_t *tbl, int val)
87 {
88 	for(; tbl->val >= 0 && tbl->val != val; tbl++)
89 		{}
90 	return tbl->flag;
91 }
92 
93 static char *
94 rdxtra(int port, struct termios *ts, char *str);	/* non-POSIX extensions */
95 
96 static long
97 rdstat(int port, void *buf, long n, ulong offset)
98 {
99 	int  fd = eia[port].fd;
100 	struct termios ts;
101 	char str[Maxctl];
102 	char sbuf[20];
103 	char *s;
104 
105 	if(tcgetattr(fd, &ts) < 0)
106 		oserror();
107 
108 	s = str;
109 	s += sprint(s, "opens %d ferr %d oerr %d baud %s",
110 		    eia[port].r.ref-1, eia[port].frame, eia[port].overrun,
111 		    ftos(sbuf, bps, (tcflag_t)cfgetospeed(&ts)));
112 	s = rdxtra(port, &ts, s);
113 	sprint(s, "\n");
114 
115 	return readstr(offset, buf, n, str);
116 }
117 
118 static char *
119 wrxtra(int port, struct termios *ts, char *cmd);  /* non-POSIX extensions */
120 
121 static void
122 wrctl(int port, char *cmd)
123 {
124 	struct termios ts;
125 	char *xerr;
126 	int r, nf, n, i;
127 	char *f[Maxfield];
128 	int fd = eia[port].fd;
129 	tcflag_t flag;
130 
131 	if(tcgetattr(fd, &ts) < 0) {
132 Error:
133 		oserror();
134 	}
135 
136 	nf = tokenize(cmd, f, nelem(f));
137 	for(i = 0; i < nf; i++){
138 		if(strncmp(f[i], "break", 5) == 0){
139 			tcsendbreak(fd, 0);
140 			continue;
141 		}
142 		n = atoi(f[i]+1);
143 		switch(*f[i]) {
144 		case 'F':
145 		case 'f':
146 			if(tcflush(fd, TCOFLUSH) < 0)
147 				goto Error;
148 			break;
149 		case 'K':
150 		case 'k':
151 			if(tcsendbreak(fd, 0) < 0)
152 				;	/* ignore it */
153 			break;
154 		case 'H':
155 		case 'h':
156 			cfsetospeed(&ts, B0);
157 			break;
158 		case 'B':
159 		case 'b':
160 			flag = stof(bps, n);
161 			if((int)flag == -1)
162 				error(Ebadarg);
163 			cfsetispeed(&ts, (speed_t)flag);
164 			cfsetospeed(&ts, (speed_t)flag);
165 			break;
166 		case 'L':
167 		case 'l':
168 			flag = stof(size, n);
169 			if((int)flag == -1)
170 				error(Ebadarg);
171 			ts.c_cflag &= ~CSIZE;
172 			ts.c_cflag |= flag;
173 			break;
174 		case 'S':
175 		case 's':
176 			if(n == 1)
177 				ts.c_cflag &= ~CSTOPB;
178 			else if(n ==2)
179 				ts.c_cflag |= CSTOPB;
180 			else
181 				error(Ebadarg);
182 			break;
183 		case 'P':
184 		case 'p':
185 			if(*(f[i]+1) == 'o')
186 				ts.c_cflag |= PARENB|PARODD;
187 			else if(*(f[i]+1) == 'e') {
188 				ts.c_cflag |= PARENB;
189 				ts.c_cflag &= ~PARODD;
190 			}
191 			else
192 				ts.c_cflag &= ~PARENB;
193 			break;
194 		case 'X':
195 		case 'x':
196 			if(n == 0)
197 			        ts.c_iflag &= ~(IXON|IXOFF);
198 			else
199 			        ts.c_iflag |= (IXON|IXOFF);
200 			break;
201 		case 'i':
202 		case 'I':
203 			/* enable fifo; ignore */
204 			break;
205 		default:
206 		        if((xerr = wrxtra(port, &ts, f[i])) != nil)
207 			        error(xerr);
208 		}
209 	}
210 
211 	osenter();
212 	r = tcsetattr(fd, TCSADRAIN, &ts);
213 	osleave();
214 	if(r < 0)
215 		goto Error;
216 	eia[port].restore = 1;
217 	eia[port].ts      = ts;
218 }
219 
220 static void
221 eiainit(void)
222 {
223 	int i, nports;
224 	Dirtab *dp;
225 	struct stat sb;
226 
227 #ifdef buildsysdev
228 	buildsysdev();
229 #endif
230 
231 	/* check to see which ports exist by trying to stat them */
232 	nports = 0;
233 	for (i=0; i < nelem(sysdev); i++) {
234 		if(stat(sysdev[i], &sb) < 0)
235 			break;
236 
237 		nports++;
238 	}
239 
240 	if (!nports)
241 		return;
242 
243 	ndir = Nqid*nports+1;
244 	dp = eiadir = malloc(ndir*sizeof(Dirtab));
245 	if(dp == 0)
246 		panic("eiainit");
247 	strcpy(dp->name, ".");
248 	dp->qid.path = 0;
249 	dp->qid.type = QTDIR;
250 	dp->perm = DMDIR|0555;
251 	dp++;
252 	eia = malloc(nports*sizeof(Eia));
253 	if(eia == 0)
254 		panic("eiainit");
255 	for(i = 0; i < nports; i++) {
256 		sprint(dp->name, "%s%d", Devname, i);
257 		dp->qid.path = NETQID(i, Ndataqid);
258 		dp->perm = 0660;
259 		dp++;
260 		sprint(dp->name, "%s%dctl", Devname, i);
261 		dp->qid.path = NETQID(i, Nctlqid);
262 		dp->perm = 0660;
263 		dp++;
264 		sprint(dp->name, "%s%dstatus", Devname, i);
265 		dp->qid.path = NETQID(i, Nstatqid);
266 		dp->perm = 0660;
267 		dp++;
268 		eia[i].frame = eia[i].overrun = 0;
269 		eia[i].restore = eia[i].dtr = eia[i].rts = eia[i].cts = 0;
270 	}
271 }
272 
273 static Chan*
274 eiaattach(char *spec)
275 {
276 	if(eiadir == nil)
277 		error(Enodev);
278 
279 	return devattach(Devchar, spec);
280 }
281 
282 Walkqid*
283 eiawalk(Chan *c, Chan *nc, char **name, int nname)
284 {
285 	return devwalk(c, nc, name, nname, eiadir, ndir, devgen);
286 }
287 
288 int
289 eiastat(Chan *c, uchar *db, int n)
290 {
291 	return devstat(c, db, n, eiadir, ndir, devgen);
292 }
293 
294 static void
295 resxtra(int port, struct termios *ts);	/* non-POSIX extensions */
296 
297 static Chan*
298 eiaopen(Chan *c, int mode)
299 {
300 	int port = NETID(c->qid.path);
301 	struct termios ts;
302 	int r;
303 
304 	c = devopen(c, mode, eiadir, ndir, devgen);
305 
306 	switch(NETTYPE(c->qid.path)) {
307 	case Nctlqid:
308 	case Ndataqid:
309 	case Nstatqid:
310 		if(incref(&eia[port].r) != 1)
311 			break;
312 
313 		osenter();
314 		eia[port].fd = open(sysdev[port], O_RDWR);
315 		osleave();
316 		if(eia[port].fd < 0)
317 			oserror();
318 
319 		/* make port settings sane */
320 		if(tcgetattr(eia[port].fd, &ts) < 0)
321 			oserror();
322 		ts.c_iflag = ts.c_oflag = ts.c_lflag = 0;
323 		if(eia[port].restore)
324 		        ts = eia[port].ts;
325 		else {
326 			cfsetispeed(&ts, B9600);
327 			cfsetospeed(&ts, B9600);
328 			ts.c_iflag |= IGNPAR;
329 			ts.c_cflag &= ~CSIZE;
330 			ts.c_cflag |= CS8|CREAD;
331 			ts.c_cflag &= ~(PARENB|PARODD);
332 			ts.c_cc[VMIN] = 1;
333 			ts.c_cc[VTIME] = 0;
334 		}
335 		osenter();
336 		r = tcsetattr(eia[port].fd, TCSANOW, &ts);
337 		osleave();
338 		if(r < 0)
339 			oserror();
340 
341 		if(eia[port].restore)
342 		        resxtra(port, &ts);
343 		break;
344 	}
345 	return c;
346 }
347 
348 static void
349 eiaclose(Chan *c)
350 {
351 	int port = NETID(c->qid.path);
352 
353 	if((c->flag & COPEN) == 0)
354 		return;
355 
356 	switch(NETTYPE(c->qid.path)) {
357 	case Nctlqid:
358 	case Ndataqid:
359 	case Nstatqid:
360 		if(decref(&eia[port].r) != 0)
361 			break;
362 		if(eia[port].fd >= 0) {
363 			osenter();
364 			close(eia[port].fd);
365 			osleave();
366 		}
367 		break;
368 	}
369 
370 }
371 
372 static long
373 eiaread(Chan *c, void *buf, long n, vlong offset)
374 {
375 	ssize_t cnt;
376 	int port = NETID(c->qid.path);
377 
378 	if(c->qid.type & QTDIR)
379 		return devdirread(c, buf, n, eiadir, ndir, devgen);
380 
381 	switch(NETTYPE(c->qid.path)) {
382 	case Ndataqid:
383 	  	osenter();
384 		cnt = read(eia[port].fd, buf, n);
385 		osleave();
386 		if(cnt == -1)
387 			oserror();
388 		return cnt;
389 	case Nctlqid:
390 		return readnum(offset, buf, n, port, NUMSIZE);
391 	case Nstatqid:
392 		return rdstat(port, buf, n, offset);
393 	}
394 
395 	return 0;
396 }
397 
398 static long
399 eiawrite(Chan *c, void *buf, long n, vlong offset)
400 {
401 	ssize_t cnt;
402 	char cmd[Maxctl];
403 	int port = NETID(c->qid.path);
404 
405 	USED(offset);
406 
407 	if(c->qid.type & QTDIR)
408 		error(Eperm);
409 
410 	switch(NETTYPE(c->qid.path)) {
411 	case Ndataqid:
412 	  	osenter();
413 		cnt = write(eia[port].fd, buf, n);
414 		osleave();
415 		if(cnt == -1)
416 			oserror();
417 		return cnt;
418 	case Nctlqid:
419 		if(n >= (long)sizeof(cmd))
420 			n = sizeof(cmd)-1;
421 		memmove(cmd, buf, n);
422 		cmd[n] = 0;
423 		wrctl(port, cmd);
424 		return n;
425 	}
426 	return 0;
427 }
428 
429 int
430 eiawstat(Chan *c, uchar *dp, int n)
431 {
432 	Dir d;
433 	int i;
434 
435 	if(strcmp(up->env->user, eve) != 0)
436 		error(Eperm);
437 	if(c->qid.type & QTDIR)
438 		error(Eperm);
439 
440 	n = convM2D(dp, n, &d, nil);
441 	i = Nqid*NETID(c->qid.path)+NETTYPE(c->qid.path)-Ndataqid;
442 	eiadir[i+1].perm = d.mode&0666;
443 	return n;
444 }
445 
446 Dev eiadevtab = {
447         Devchar,
448         Devname,
449 
450         eiainit,
451         eiaattach,
452         eiawalk,
453         eiastat,
454         eiaopen,
455         devcreate,
456         eiaclose,
457         eiaread,
458         devbread,
459         eiawrite,
460         devbwrite,
461         devremove,
462         eiawstat
463 };
464