1 /* $Id: scio.c,v 1.12 2003/04/02 22:57:51 deraadt Exp $ */
2 
3 /*
4 copyright 1997
5 the regents of the university of michigan
6 all rights reserved
7 
8 permission is granted to use, copy, create derivative works
9 and redistribute this software and such derivative works
10 for any purpose, so long as the name of the university of
11 michigan is not used in any advertising or publicity
12 pertaining to the use or distribution of this software
13 without specific, written prior authorization.  if the
14 above copyright notice or any other identification of the
15 university of michigan is included in any copy of any
16 portion of this software, then the disclaimer below must
17 also be included.
18 
19 this software is provided as is, without representation
20 from the university of michigan as to its fitness for any
21 purpose, and without warranty by the university of
22 michigan of any kind, either express or implied, including
23 without limitation the implied warranties of
24 merchantability and fitness for a particular purpose. the
25 regents of the university of michigan shall not be liable
26 for any damages, including special, indirect, incidental, or
27 consequential damages, with respect to any claim arising
28 out of or in connection with the use of the software, even
29 if it has been or is hereafter advised of the possibility of
30 such damages.
31 */
32 
33 /*
34  * OS dependent part, Unix version
35  *
36  * Jim Rees, University of Michigan, October 1997
37  */
38 
39 #include <sys/ioctl.h>
40 #include <sys/file.h>
41 #include <sys/types.h>
42 #include <sys/time.h>
43 #ifdef _AIX
44 #include <sys/select.h>
45 #endif /* _AIX */
46 #include <unistd.h>
47 #include <stdlib.h>
48 #include <stdio.h>
49 #include <string.h>
50 #include <termios.h>
51 #include <signal.h>
52 #include <fcntl.h>
53 #include <errno.h>
54 
55 #include "sectok.h"
56 #include "sc7816.h"
57 #include "todos_scrw.h"
58 
59 #ifndef howmany
60 #define howmany(x, y) (((x) + ((y) - 1)) / (y))
61 #endif
62 
63 static int todos_scfdopen(int ttyn, int fd, int flags, int *ep);
64 static int todos_sccts(int ttyn);
65 
66 #ifdef __linux
67 static char ttynametmpl[] = "/dev/ttyS%01d";
68 #elif _AIX
69 static char ttynametmpl[] = "/dev/tty%01d";
70 #elif __OpenBSD__
71 static char ttynametmpl[] = "/dev/cua%02d";
72 #elif __sun
73 /*static char ttynametmpl[] = "/dev/cua/%c";*/
74 static char ttynametmpl[] = "/dev/tty%c";
75 #else
76 static char ttynametmpl[] = "/dev/tty%02d";
77 #endif
78 
79 static struct {
80     int fd, flags;
81     pid_t pid;
82     struct termios tio0, tio1;
83 } sc[4];
84 
85 /* global variable */
86 #ifdef BYTECOUNT
87 int num_getc, num_putc;
88 #endif /* BYTECOUNT */
89 
90 int
todos_scopen(int ttyn,int flags,int * ep)91 todos_scopen(int ttyn, int flags, int *ep)
92 {
93     char ttyname[32];
94     int fd, i, oflags;
95     pid_t pid;
96 
97 #ifdef BYTECOUNT
98     num_getc = 0;
99     num_putc = 0;
100 #endif /* BYTECOUNT */
101 
102 #ifdef __sun
103     snprintf(ttyname, sizeof ttyname, ttynametmpl, 'a' + ttyn);
104 #else
105     snprintf(ttyname, sizeof ttyname, ttynametmpl, ttyn);
106 #endif
107 
108 
109 #ifdef DEBUG
110     printf ("ttyname=%s\n", ttyname);
111 #endif /* DEBUG */
112     oflags = O_RDWR;
113     if (!(flags & SCODCD))
114 	oflags |= O_NONBLOCK;
115     if ((fd = open(ttyname, oflags, 0)) < 0) {
116 	if (ep)
117 	    *ep = SCENOTTY;
118 	return -1;
119     }
120 
121     if ((ttyn = todos_scfdopen(ttyn, fd, flags, ep)) < 0) {
122 	close(fd);
123 	return -1;
124     }
125 
126     /* Figure out which reader we have */
127     if (ioctl(fd, TIOCMGET, &i) < 0) {
128 	close(fd);
129 	if (ep)
130 	    *ep = SCENOTTY;
131 	return -1;
132     }
133 #ifndef __sun
134     /* Todos has RTS wired to RI, so set RTS and see if RI goes high */
135     i |= TIOCM_RTS;
136     ioctl(fd, TIOCMSET, &i);
137     ioctl(fd, TIOCMGET, &i);
138 #else
139     /* Sun serial port is broken, has no RI line */
140     i = TIOCM_RI;
141 #endif
142     if (i & TIOCM_RI) {
143 	/* Todos reader */
144 	scsleep(20);
145 	sc[ttyn].flags |= (SCOXCTS | SCOXDTR);
146     }
147 
148     if (flags & SCODSR) {
149 	/* Wait for card present */
150 	while (!todos_sccardpresent(ttyn)) {
151 	    errno = 0;
152 	    sleep(1);
153 	    if (errno == EINTR)
154 		return -1;
155 	}
156     }
157 
158     if (flags & SCOHUP) {
159 	/* spawn a process to wait for card removal */
160 	pid = fork();
161 	if (pid == 0) {
162 	    /* See if the card is still there */
163 	    while (todos_sccardpresent(ttyn))
164 		sleep(1);
165 	    kill(getppid(), SIGHUP);
166 	    exit(0);
167 	}
168 	sc[ttyn].pid = pid;
169     }
170 
171     return ttyn;
172 }
173 
174 int
todos_scsetflags(int ttyn,int flags,int mask)175 todos_scsetflags(int ttyn, int flags, int mask)
176 {
177     int oflags = sc[ttyn].flags;
178 
179     sc[ttyn].flags &= ~mask;
180     sc[ttyn].flags |= (flags & mask);
181 
182     if ((sc[ttyn].flags & SCOINVRT) != (oflags & SCOINVRT)) {
183 	if (sc[ttyn].flags & SCOINVRT)
184 	    sc[ttyn].tio1.c_cflag |= PARODD;
185 	else
186 	    sc[ttyn].tio1.c_cflag &= ~PARODD;
187 	tcsetattr(sc[ttyn].fd, TCSADRAIN, &sc[ttyn].tio1);
188     }
189 
190     return oflags;
191 }
192 
193 /* NI: for Linux */
194 #if (B9600 != 9600)
195 struct speed_trans {
196     speed_t t_val;
197     int val;
198     char *s;
199 } speed_trans[] = {
200     {0, 0, "a"},
201     {B9600, 9600, "a"},
202     {B19200, 19200, "a"},
203     {B38400, 38400, "a"},
204     {B57600, 55928, "a"},
205     {B57600, 57600, "a"},
206     {B115200, 115200, "a"},
207     {-1, -1, NULL}};
208 #endif /* (B9600 != 9600) */
209 
210 int
todos_scsetspeed(int ttyn,int speed)211 todos_scsetspeed(int ttyn, int speed)
212 {
213 
214 #if (B9600 == 9600)
215     /* On OpenBSD, B9600 == 9600, and we can use the input argument
216        "speed" of this function as an argument to cfset[io]speed(). */
217 #else
218     /* On Linux, B9600 != 9600, and we have to translate the input argument
219        to speed_t. */
220     int i;
221 
222     for (i = 0; speed_trans[i].s; i++)
223 	if (speed_trans[i].val == speed) break;
224     if (speed_trans[i].s == NULL) {
225 	fprintf (stderr, "scsetspeed() failed : speed %d not supported.  ignore ...\n",
226 		 speed);
227 	return 0;
228     }
229     speed = speed_trans[i].t_val;
230 #endif
231     cfsetispeed(&sc[ttyn].tio1, speed);
232     cfsetospeed(&sc[ttyn].tio1, speed);
233 
234     return tcsetattr(sc[ttyn].fd, TCSADRAIN, &sc[ttyn].tio1);
235 }
236 
237 static int
todos_scfdopen(int ttyn,int fd,int flags,int * ep)238 todos_scfdopen(int ttyn, int fd, int flags, int *ep)
239 {
240     struct termios t;
241 
242     /* Get and save the tty state */
243 
244     if (tcgetattr(fd, &t) < 0) {
245 	if (ep)
246 	    *ep = SCENOTTY;
247 	return -1;
248     }
249     sc[ttyn].fd = fd;
250     sc[ttyn].tio0 = t;
251     sc[ttyn].flags = flags;
252 
253     /* Now put the tty in a happy ISO state */
254 
255     /* 9600 bps */
256     cfsetispeed(&t, B9600);
257     cfsetospeed(&t, B9600);
258 
259     /* raw 8/E/2 */
260     t.c_iflag &= ~(ISTRIP|INLCR|IGNCR|ICRNL|IXON);
261     t.c_iflag |= (IGNBRK|IGNPAR);
262     t.c_oflag &= ~OPOST;
263     t.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
264 #ifdef CHWFLOW
265     t.c_cflag &= ~CHWFLOW;
266 #endif
267 #ifdef CRTSCTS
268     t.c_cflag &= ~CRTSCTS;
269 #endif
270 #ifdef CRTSXOFF
271     t.c_cflag &= ~CRTSXOFF;
272 #endif
273     t.c_cflag |= CLOCAL;
274 
275     /* 8/E/2 */
276     t.c_cflag &= ~(CSIZE | PARODD);
277     t.c_cflag |= (CS8 | PARENB | CSTOPB);
278 
279     t.c_cc[VMIN] = 1;
280     t.c_cc[VTIME] = 0;
281 
282     if (tcsetattr(fd, TCSANOW, &t) < 0) {
283 	if (ep)
284 	    *ep = SCENOTTY;
285 	sc[ttyn].fd = -1;
286 	return -1;
287     }
288     sc[ttyn].tio1 = t;
289 
290     /* The open may or may not have reset the card.  Wait a while then flush
291        anything that came in on the port. */
292     scsleep(250);
293     tcflush(sc[ttyn].fd, TCIFLUSH);
294 
295     return ttyn;
296 }
297 
298 /* find out if there is a card in the reader */
299 
300 int
todos_sccardpresent(int ttyn)301 todos_sccardpresent(int ttyn)
302 {
303     return (sc[ttyn].flags & SCOXCTS) ? !todos_sccts(ttyn) : todos_scdsr(ttyn);
304 }
305 
306 /* query dsr on the port (usually indicates whether the card is present) */
307 
308 int
todos_scdsr(int ttyn)309 todos_scdsr(int ttyn)
310 {
311     int fd = sc[ttyn].fd;
312     int i;
313 
314     if (fd < 0 || ioctl(fd, TIOCMGET, &i) < 0)
315 	return 0;
316 
317     return ((i & TIOCM_DSR) ? 1 : 0);
318 }
319 
320 static int
todos_sccts(int ttyn)321 todos_sccts(int ttyn)
322 {
323     int fd = sc[ttyn].fd;
324     int i;
325 
326     if (fd < 0 || ioctl(fd, TIOCMGET, &i) < 0)
327 	return 0;
328 
329     return ((i & TIOCM_CTS) ? 1 : 0);
330 }
331 
332 /* raise or lower dtr */
333 
334 int
todos_scdtr(int ttyn,int cmd)335 todos_scdtr(int ttyn, int cmd)
336 {
337     int fd = sc[ttyn].fd;
338     int i;
339 
340     if (!todos_sccardpresent(ttyn))
341 	return -1;
342 
343     if (ioctl(fd, TIOCMGET, &i) < 0)
344 	return -1;
345 
346     if (cmd)
347 	i |= TIOCM_DTR;
348     else
349 	i &= ~TIOCM_DTR;
350     i |= TIOCM_RTS;
351 
352     return ioctl(fd, TIOCMSET, &i);
353 }
354 
355 int
todos_scclose(int ttyn)356 todos_scclose(int ttyn)
357 {
358     int fd = sc[ttyn].fd;
359 
360     tcsetattr(fd, TCSANOW, &sc[ttyn].tio0);
361     close(fd);
362     sc[ttyn].fd = -1;
363     if (sc[ttyn].pid) {
364 	kill(sc[ttyn].pid, SIGTERM);
365 	sc[ttyn].pid = 0;
366     }
367 
368 #ifdef BYTECOUNT
369     printf("#getc=%d, #putc=%d\n", num_getc - num_putc, num_putc);
370 #endif /* BYTECOUNT */
371     return 0;
372 }
373 
374 /*
375  * get one byte from the card.
376  * wait at most ms msec.  0 for poll, -1 for infinite.
377  * return byte in *cp.
378  * return 0 or error.
379  */
380 
381 int
scgetc(int ttyn,unsigned char * cp,int ms)382 scgetc(int ttyn, unsigned char *cp, int ms)
383 {
384     int fd = sc[ttyn].fd;
385     fd_set *fdset;
386     struct timeval tv, *tvp;
387 
388 #ifdef BYTECOUNT
389     num_getc++;
390 #endif /* BYTECOUNT */
391 
392     fdset = (fd_set *)calloc(howmany(fd + 1, NFDBITS), sizeof(fd_mask));
393     if (fdset == NULL)
394 	return SCENOMEM;
395     FD_SET(fd, fdset);
396 
397     if (ms == -1)
398 	tvp = NULL;
399     else {
400 	tv.tv_sec = (ms + 1) / 1000;
401 	tv.tv_usec = (ms % 1000) * 1000;
402 	tvp = &tv;
403     }
404 
405     if (select(fd + 1, fdset, NULL, NULL, tvp) != 1) {
406 	free(fdset);
407 	return SCTIMEO;
408     }
409 
410     if (read(fd, cp, 1) != 1) {
411 	free(fdset);
412 	return SCTIMEO;
413     }
414 
415     if (sc[ttyn].flags & SCOINVRT)
416 	*cp = todos_scinvert[*cp];
417 
418     free(fdset);
419     return SCEOK; /* 0 */
420 }
421 
422 /* write one byte to the card */
423 
424 int
scputc(int ttyn,int ic)425 scputc(int ttyn, int ic)
426 {
427     int fd = sc[ttyn].fd;
428     unsigned char c0, c1;
429     int code;
430 
431 #ifdef BYTECOUNT
432     num_putc++;
433 #endif /* BYTECOUNT */
434 
435     c0 = (sc[ttyn].flags & SCOINVRT) ? todos_scinvert[ic] : ic;
436     write(fd, &c0, 1);
437 
438     /* gobble up the echo */
439     code = scgetc(ttyn, &c1, 200);
440 #ifdef GOBBLEDEBUG
441     if (sc[ttyn].flags & SCOINVRT)
442 	c1 = todos_scinvert[c1];
443     if (code)
444 	printf("failed to gobble\n");
445     else if (c0 != c1)
446 	printf("misgobbled %x != %x\n", c0, c1);
447     else
448 	printf("gobble gobble %x\n", c0);
449 #endif
450     return code;
451 }
452 
453 int
scputblk(int ttyn,unsigned char * bp,int n)454 scputblk(int ttyn, unsigned char *bp, int n)
455 {
456     int fd = sc[ttyn].fd;
457     unsigned char c;
458 
459     write(fd, bp, n);
460     while (n--)
461 	scgetc(ttyn, &c, 30);
462 
463     return SCEOK;
464 }
465 
466 void
scsleep(int ms)467 scsleep(int ms)
468 {
469     struct timeval tv;
470 
471     if (!ms)
472 	return;
473     tv.tv_sec = (ms + 1) / 1000;
474     tv.tv_usec = (ms % 1000) * 1000;
475 
476     select(0, NULL, NULL, NULL, &tv);
477 }
478