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