nicer formatting
[tridge/junkcode.git] / tty.c
1 #define TTY_BUFSIZE     1024            /* size of one serial buffer    */
2
3
4 static struct {
5   char  *speed;
6   int   code;
7 } tty_speeds[] = {                      /* table of usable baud rates   */
8   { "50",       B50     }, { "75",      B75     },      
9   { "110",      B110    }, { "300",     B300    },
10   { "600",      B600    }, { "1200",    B1200   },
11   { "2400",     B2400   }, { "4800",    B4800   },
12   { "9600",     B9600   },
13 #ifdef B14400
14   { "14400",    B14400  },
15 #endif
16 #ifdef B19200
17   { "19200",    B19200  },
18 #endif
19 #ifdef B38400
20   { "38400",    B38400  },
21 #endif
22 #ifdef B57600
23   { "57600",    B57600  },
24 #endif
25 #ifdef B115200
26   { "115200",   B115200 },
27 #endif
28   { NULL,       0       }
29 };
30 static struct termios   tty_saved,      /* saved TTY device state       */
31                         tty_current;    /* current TTY device state     */
32 static char             *in_buff,       /* line input/output buffers    */
33                         *out_buff,
34                         *in_ptr,
35                         *out_ptr;
36 static int              in_size,        /* buffer sizes and counters    */
37                         out_size,
38                         in_cnt,
39                         out_cnt,
40                         tty_sdisc,      /* saved TTY line discipline    */
41                         tty_ldisc,      /* current TTY line discipline  */
42                         tty_fd = -1;    /* TTY file descriptor          */
43
44
45 /* Find a serial speed code in the table. */
46 static int
47 tty_find_speed(char *speed)
48 {
49   int i;
50
51   i = 0;
52   while (tty_speeds[i].speed != NULL) {
53         if (!strcmp(tty_speeds[i].speed, speed)) return(tty_speeds[i].code);
54         i++;
55   }
56   return(-EINVAL);
57 }
58
59
60 /* Set the number of stop bits. */
61 static int
62 tty_set_stopbits(struct termios *tty, char *stopbits)
63 {
64   switch(*stopbits) {
65         case '1':
66                 tty->c_cflag &= ~CSTOPB;
67                 break;
68
69         case '2':
70                 tty->c_cflag |= CSTOPB;
71                 break;
72
73         default:
74                 return(-EINVAL);
75   }
76   if (opt_v) printf("STOPBITS = %c\n", *stopbits);
77   return(0);
78 }
79
80
81 /* Set the number of data bits. */
82 static int
83 tty_set_databits(struct termios *tty, char *databits)
84 {
85   tty->c_cflag &= ~CSIZE;
86   switch(*databits) {
87         case '5':
88                 tty->c_cflag |= CS5;
89                 break;
90
91         case '6':
92                 tty->c_cflag |= CS6;
93                 break;
94
95         case '7':
96                 tty->c_cflag |= CS7;
97                 break;
98
99         case '8':
100                 tty->c_cflag |= CS8;
101                 break;
102
103         default:
104                 return(-EINVAL);
105   }
106   if (opt_v) printf("DATABITS = %c\n", *databits);
107   return(0);
108 }
109
110
111 /* Set the type of parity encoding. */
112 static int
113 tty_set_parity(struct termios *tty, char *parity)
114 {
115   switch(toupper(*parity)) {
116         case 'N':
117                 tty->c_cflag &= ~(PARENB | PARODD);
118                 break;  
119
120         case 'O':
121                 tty->c_cflag &= ~(PARENB | PARODD);
122                 tty->c_cflag |= (PARENB | PARODD);
123                 break;
124
125         case 'E':
126                 tty->c_cflag &= ~(PARENB | PARODD);
127                 tty->c_cflag |= (PARENB);
128                 break;
129
130         default:
131                 return(-EINVAL);
132   }
133   if (opt_v) printf("PARITY = %c\n", *parity);
134   return(0);
135 }
136
137
138 /* Set the line speed of a terminal line. */
139 static int
140 tty_set_speed(struct termios *tty, char *speed)
141 {
142   int code;
143
144   if ((code = tty_find_speed(speed)) < 0) return(code);
145   tty->c_cflag &= ~CBAUD;
146   tty->c_cflag |= code;
147   return(0);
148 }
149
150
151 /* Put a terminal line in a transparent state. */
152 static int
153 tty_set_raw(struct termios *tty)
154 {
155   int i;
156   int speed;
157
158   for(i = 0; i < NCCS; i++)
159                 tty->c_cc[i] = '\0';            /* no spec chr          */
160   tty->c_cc[VMIN] = 1;
161   tty->c_cc[VTIME] = 0;
162   tty->c_iflag = (IGNBRK | IGNPAR);             /* input flags          */
163   tty->c_oflag = (0);                           /* output flags         */
164   tty->c_lflag = (0);                           /* local flags          */
165   speed = (tty->c_cflag & CBAUD);               /* save current speed   */
166   tty->c_cflag = (CRTSCTS | HUPCL | CREAD);     /* UART flags           */
167 #if 0
168   tty->c_cflag |= CLOCAL;                       /* not good!            */
169 #endif
170   tty->c_cflag |= speed;                        /* restore speed        */
171   return(0);
172 }
173
174
175 /* Fetch the state of a terminal. */
176 static int
177 tty_get_state(struct termios *tty)
178 {
179   if (ioctl(tty_fd, TCGETS, tty) < 0) {
180         fprintf(stderr, "dip: tty_get_state: %s\n", strerror(errno));
181         return(-errno);
182   }
183   return(0);
184 }
185
186
187 /* Set the state of a terminal. */
188 static int
189 tty_set_state(struct termios *tty)
190 {
191   if (ioctl(tty_fd, TCSETS, tty) < 0) {
192         fprintf(stderr, "dip: tty_set_state: %s\n", strerror(errno));
193         return(-errno);
194   }
195   return(0);
196 }
197
198
199 /* Get the line discipline of a terminal line. */
200 int
201 tty_get_disc(int *disc)
202 {
203   if (ioctl(tty_fd, TIOCGETD, disc) < 0) {
204         fprintf(stderr, "dip: tty_get_disc: %s\n", strerror(errno));
205         return(-errno);
206   }
207   return(0);
208 }
209
210
211 /* Set the line discipline of a terminal line. */
212 int
213 tty_set_disc(int disc)
214 {
215   if (disc == -1) disc = tty_sdisc;
216
217   if (ioctl(tty_fd, TIOCSETD, &disc) < 0) {
218         fprintf(stderr, "dip: tty_set_disc(%d): %s\n", disc, strerror(errno));
219         return(-errno);
220   }
221   return(0);
222 }
223
224
225 /* Get the encapsulation type of a terminal line. */
226 int
227 tty_get_encap(int *encap)
228 {
229   if (ioctl(tty_fd, SIOCGIFENCAP, encap) < 0) {
230         fprintf(stderr, "dip: tty_get_encap: %s\n", strerror(errno));
231         return(-errno);
232   }
233   return(0);
234 }
235
236
237 /* Set the encapsulation type of a terminal line. */
238 int
239 tty_set_encap(int encap)
240 {
241   if (ioctl(tty_fd, SIOCSIFENCAP, &encap) < 0) {
242         fprintf(stderr, "dip: tty_set_encap(%d): %s\n", encap, strerror(errno));
243         return(-errno);
244   }
245   return(0);
246 }
247
248
249 /* Fetch the name of the network interface attached to this terminal. */
250 int
251 tty_get_name(char *name)
252 {
253   if (ioctl(tty_fd, SIOCGIFNAME, name) < 0) {
254         fprintf(stderr, "dip: tty_get_name: %s\n", strerror(errno));
255         return(-errno);
256   }
257   return(0);
258 }
259
260
261 /* Read one character (byte) from the TTY link. */
262 int
263 tty_getc(void)
264 {
265   int s;
266
267   if (in_cnt == 0) {
268         s = read(tty_fd, in_buff, in_size);
269         if (s < 0) return(-1);
270         in_cnt = s;
271         in_ptr = in_buff;
272   }
273
274   if (in_cnt < 0) {
275         if (opt_v == 1) printf("dip: tty_getc: I/O error.\n");
276         return(-1);
277   }
278
279   s = (int) *in_ptr;
280   s &= 0xFF;
281   in_ptr++;
282   in_cnt--;
283   return(s);
284 }
285
286
287 /* Write one character (byte) to the TTY link. */
288 int
289 tty_putc(int c)
290 {
291   int s;
292
293   if ((out_cnt == out_size) || (c == -1)) {
294         s = write(tty_fd, out_buff, out_cnt);
295         out_cnt = 0;
296         out_ptr = out_buff;
297         if (s < 0) return(-1);
298   }
299
300   if (c != -1) {
301         *out_ptr = (char) c;
302         out_ptr++;
303         out_cnt++;
304   }
305
306   return(0);
307 }
308
309
310 /* Output a string of characters to the TTY link. */
311 void
312 tty_puts(char *s)
313 {
314   while(*s != '\0') tty_putc((int) *s++);
315   tty_putc(-1); /* flush */
316 }
317
318
319 /* Return the TTY link's file descriptor. */
320 int
321 tty_askfd(void)
322 {
323   return(tty_fd);
324 }
325
326
327 /* Set the number of databits a terminal line. */
328 int
329 tty_databits(char *bits)
330 {
331   if (tty_set_databits(&tty_current, bits) < 0) return(-1);
332   return(tty_set_state(&tty_current));
333 }
334
335
336 /* Set the number of stopbits of a terminal line. */
337 int
338 tty_stopbits(char *bits)
339 {
340   if (tty_set_stopbits(&tty_current, bits) < 0) return(-1);
341   return(tty_set_state(&tty_current));
342 }
343
344
345 /* Set the type of parity of a terminal line. */
346 int
347 tty_parity(char *type)
348 {
349   if (tty_set_parity(&tty_current, type) < 0) return(-1);
350   return(tty_set_state(&tty_current));
351 }
352
353
354 /* Set the line speed of a terminal line. */
355 int
356 tty_speed(char *speed)
357 {
358   if (tty_set_speed(&tty_current, speed) < 0) return(-1);
359   return(tty_set_state(&tty_current));
360 }
361
362
363 /* Hangup the line. */
364 int
365 tty_hangup(void)
366 {
367   struct termios tty;
368
369   tty = tty_current;
370   (void) tty_set_speed(&tty, "0");
371   if (tty_set_state(&tty) < 0) {
372         fprintf(stderr, "dip: tty_hangup(DROP): %s\n", strerror(errno));
373         return(-errno);
374   }
375
376   (void) sleep(1);
377
378   if (tty_set_state(&tty_current) < 0) {
379         fprintf(stderr, "dip: tty_hangup(RAISE): %s\n", strerror(errno));
380         return(-errno);
381   }
382   return(0);
383 }
384
385
386 /* Close down a terminal line. */
387 int
388 tty_close(void)
389 {
390   (void) tty_hangup();
391   (void) tty_set_disc(tty_sdisc);
392   return(0);
393 }
394
395
396 /* Open and initialize a terminal line. */
397 int
398 tty_open(char *name)
399 {
400   char path[PATH_MAX];
401   register char *sp;
402   int fd;
403
404   /* Try opening the TTY device. */
405   if (name != NULL) {
406         if ((sp = strrchr(name, '/')) != (char *)NULL) *sp++ = '\0';
407           else sp = name;
408         sprintf(path, "/dev/%s", sp);
409         if ((fd = open(path, O_RDWR)) < 0) {
410                 fprintf(stderr, "dip: tty_open(%s, RW): %s\n",
411                                                 path, strerror(errno));
412                 return(-errno);
413         }
414         tty_fd = fd;
415         if (opt_v) printf("TTY = %s (%d) ", path, fd);
416   } else tty_fd = 0;
417
418   /* Size and allocate the I/O buffers. */
419   in_size = TTY_BUFSIZE;
420   out_size = in_size;
421   in_buff = (char *) malloc(in_size);
422   out_buff = (char *) malloc(out_size);
423   if (in_buff == (char *)NULL || out_buff == (char *)NULL) {
424         fprintf(stderr, "dip: tty_open: cannot allocate(%d, %d) buffers (%d)\n",
425                                                 in_size, out_size, errno);
426         return(-ENOMEM);
427   }
428   in_cnt = 0;
429   out_cnt = 0;
430   in_ptr = in_buff;
431   out_ptr = out_buff;
432   out_size -= 4; /* safety */
433   if (opt_v) printf("IBUF=%d OBUF=%d\n", in_size, out_size);
434
435   /* Fetch the current state of the terminal. */
436   if (tty_get_state(&tty_saved) < 0) {
437         fprintf(stderr, "dip: tty_open: cannot get current state!\n");
438         return(-errno);
439   }
440   tty_current = tty_saved;
441
442   /* Fetch the current line discipline of this terminal. */
443   if (tty_get_disc(&tty_sdisc) < 0) {
444         fprintf(stderr, "dip: tty_open: cannot get current line disc!\n");
445         return(-errno);
446   } 
447   tty_ldisc = tty_sdisc;
448
449   /* Put this terminal line in a 8-bit transparent mode. */
450   if (tty_set_raw(&tty_current) < 0) {
451         fprintf(stderr, "dip: tty_open: cannot set RAW mode!\n");
452         return(-errno);
453   }
454
455   /* If we are running in MASTER mode, set the default speed. */
456   if ((name != NULL) && (tty_set_speed(&tty_current, "9600") != 0)) {
457         fprintf(stderr, "dip: tty_open: cannot set 9600 bps!\n");
458         return(-errno);
459   }
460
461   /* Set up a completely 8-bit clean line. */
462   if (tty_set_databits(&tty_current, "8") ||
463       tty_set_stopbits(&tty_current, "1") ||
464       tty_set_parity(&tty_current, "N")) {
465         fprintf(stderr, "dip: tty_open: cannot set 8N1 mode!\n");
466         return(-errno);
467   }
468   return(tty_set_state(&tty_current));
469 }