chiark / gitweb /
Change James's email address
[sympathy.git] / src / tty.c
1 /* 
2  * tty.c:
3  *
4  * Copyright (c) 2008 James McKenzie <sympathy@madingley.org>,
5  * All rights reserved.
6  *
7  */
8
9 static char rcsid[] = "$Id: tty.c,v 1.27 2010/07/27 14:49:35 james Exp $";
10
11 /* 
12  * $Log: tty.c,v $
13  * Revision 1.27  2010/07/27 14:49:35  james
14  * add support for byte logging
15  *
16  * Revision 1.26  2008/03/10 11:49:33  james
17  * *** empty log message ***
18  *
19  * Revision 1.25  2008/03/07 13:16:02  james
20  * *** empty log message ***
21  *
22  * Revision 1.24  2008/03/07 12:37:04  james
23  * *** empty log message ***
24  *
25  * Revision 1.23  2008/03/06 16:49:39  james
26  * *** empty log message ***
27  *
28  * Revision 1.22  2008/03/06 16:49:05  james
29  * *** empty log message ***
30  *
31  * Revision 1.21  2008/03/03 06:04:42  james
32  * *** empty log message ***
33  *
34  * Revision 1.20  2008/03/02 10:37:56  james
35  * *** empty log message ***
36  *
37  * Revision 1.19  2008/02/28 16:57:52  james
38  * *** empty log message ***
39  *
40  * Revision 1.18  2008/02/28 16:37:16  james
41  * *** empty log message ***
42  *
43  * Revision 1.17  2008/02/28 15:37:06  james
44  * *** empty log message ***
45  *
46  * Revision 1.16  2008/02/28 12:12:25  james
47  * *** empty log message ***
48  *
49  * Revision 1.15  2008/02/28 00:10:44  james
50  * *** empty log message ***
51  *
52  * Revision 1.14  2008/02/23 13:05:58  staffcvs
53  * *** empty log message ***
54  *
55  * Revision 1.13  2008/02/23 11:48:37  james
56  * *** empty log message ***
57  *
58  * Revision 1.12  2008/02/22 23:39:27  james
59  * *** empty log message ***
60  *
61  * Revision 1.11  2008/02/20 18:31:53  james
62  * *** empty log message ***
63  *
64  * Revision 1.10  2008/02/15 23:52:12  james
65  * *** empty log message ***
66  *
67  * Revision 1.9  2008/02/15 03:32:07  james
68  * *** empty log message ***
69  *
70  * Revision 1.8  2008/02/14 10:36:18  james
71  * *** empty log message ***
72  *
73  * Revision 1.7  2008/02/14 10:34:30  james
74  * *** empty log message ***
75  *
76  * Revision 1.6  2008/02/13 16:59:34  james
77  * *** empty log message ***
78  *
79  * Revision 1.5  2008/02/13 16:57:29  james
80  * *** empty log message ***
81  *
82  * Revision 1.4  2008/02/12 22:36:46  james
83  * *** empty log message ***
84  *
85  * Revision 1.3  2008/02/09 15:47:28  james
86  * *** empty log message ***
87  *
88  */
89
90
91 #include "project.h"
92
93 static int
94 speed_t_to_baud (speed_t s)
95 {
96   switch (s) {
97 #ifdef B0
98   case B0:
99     return 0;
100 #endif
101 #ifdef B50
102   case B50:
103     return 50;
104 #endif
105 #ifdef B75
106   case B75:
107     return 75;
108 #endif
109 #ifdef B110
110   case B110:
111     return 110;
112 #endif
113 #ifdef B134
114   case B134:
115     return 134;
116 #endif
117 #ifdef B150
118   case B150:
119     return 150;
120 #endif
121 #ifdef B200
122   case B200:
123     return 200;
124 #endif
125 #ifdef B300
126   case B300:
127     return 300;
128 #endif
129 #ifdef B600
130   case B600:
131     return 600;
132 #endif
133 #ifdef B1200
134   case B1200:
135     return 1200;
136 #endif
137 #ifdef B1800
138   case B1800:
139     return 1800;
140 #endif
141 #ifdef B2400
142   case B2400:
143     return 2400;
144 #endif
145 #ifdef B4800
146   case B4800:
147     return 4800;
148 #endif
149 #ifdef B9600
150   case B9600:
151     return 9600;
152 #endif
153 #ifdef B19200
154   case B19200:
155     return 19200;
156 #endif
157 #ifdef B38400
158   case B38400:
159     return 38400;
160 #endif
161 #ifdef B57600
162   case B57600:
163     return 57600;
164 #endif
165 #ifdef B115200
166   case B115200:
167     return 115200;
168 #endif
169 #ifdef B230400
170   case B230400:
171     return 230400;
172 #endif
173   }
174
175   return -1;
176 }
177
178 static speed_t
179 baud_to_speed_t (int baud)
180 {
181   switch (baud) {
182 #ifdef B0
183   case 0:
184     return B0;
185 #endif
186 #ifdef B50
187   case 50:
188     return B50;
189 #endif
190 #ifdef B75
191   case 75:
192     return B75;
193 #endif
194 #ifdef B110
195   case 110:
196     return B110;
197 #endif
198 #ifdef B134
199   case 134:
200     return B134;
201 #endif
202 #ifdef B150
203   case 150:
204     return B150;
205 #endif
206 #ifdef B200
207   case 200:
208     return B200;
209 #endif
210 #ifdef B300
211   case 300:
212     return B300;
213 #endif
214 #ifdef B600
215   case 600:
216     return B600;
217 #endif
218 #ifdef B1200
219   case 1200:
220     return B1200;
221 #endif
222 #ifdef B1800
223   case 1800:
224     return B1800;
225 #endif
226 #ifdef B2400
227   case 2400:
228     return B2400;
229 #endif
230 #ifdef B4800
231   case 4800:
232     return B4800;
233 #endif
234 #ifdef B9600
235   case 9600:
236     return B9600;
237 #endif
238 #ifdef B19200
239   case 19200:
240     return B19200;
241 #endif
242 #ifdef B38400
243   case 38400:
244     return B38400;
245 #endif
246 #ifdef B57600
247   case 57600:
248     return B57600;
249 #endif
250 #ifdef B115200
251   case 115200:
252     return B115200;
253 #endif
254 #ifdef B230400
255   case 230400:
256     return B230400;
257 #endif
258   }
259   return -1;
260 }
261
262 void
263 tty_pre_select (TTY * t, fd_set * rfds, fd_set * wfds)
264 {
265   int line;
266   struct timeval now, dif;
267
268   if (t->hanging_up) {
269
270     gettimeofday (&now, NULL);
271     timersub (&now, &t->hangup_clock, &dif);
272     if (dif.tv_sec) {
273       line = TIOCM_DTR;
274       ioctl (t->rfd, TIOCMBIS, &line);
275       t->hanging_up = 0;
276     }
277   }
278
279
280   FD_SET (t->rfd, rfds);
281 }
282
283 int
284 tty_get_status (TTY * t, TTY_Status * s)
285 {
286
287   s->lines = 0;
288   ioctl (t->rfd, TIOCMGET, &s->lines);
289
290   if (tcgetattr (t->rfd, &s->termios))
291     return -1;
292
293   s->baud = speed_t_to_baud (cfgetispeed (&s->termios));
294   s->blocked = t->blocked;
295
296   return 0;
297 }
298
299 int
300 tty_get_baud (TTY * t)
301 {
302   struct termios tios = { 0 };
303
304   if (tcgetattr (t->rfd, &tios))
305     return;
306
307   return speed_t_to_baud (cfgetispeed (&tios));
308 }
309
310
311 void
312 tty_set_baud (TTY * t, int rate)
313 {
314   struct termios tios = { 0 };
315
316   speed_t s = baud_to_speed_t (rate);
317
318   if (s == (speed_t) - 1)
319     return;
320
321   if (tcgetattr (t->rfd, &tios))
322     return;
323
324   cfsetispeed (&tios, s);
325   cfsetospeed (&tios, s);
326
327   tcsetattr (t->rfd, TCSANOW, &tios);
328 }
329
330 void
331 tty_send_break (TTY * t)
332 {
333   tcsendbreak (t->wfd, 0);
334 }
335
336 void
337 tty_set_flow (TTY * t, int flow)
338 {
339   struct termios tios = { 0 };
340
341   if (tcgetattr (t->rfd, &tios))
342     return;
343
344   if (flow)
345     tios.c_cflag |= CRTSCTS;
346   else
347     tios.c_cflag &= ~CRTSCTS;
348
349   tcsetattr (t->rfd, TCSANOW, &tios);
350
351 }
352
353 void
354 tty_hangup (TTY * t)
355 {
356   int line;
357
358   line = TIOCM_DTR;
359   ioctl (t->rfd, TIOCMBIC, &line);
360
361   t->hanging_up = 1;
362   gettimeofday (&t->hangup_clock, NULL);
363
364 }
365
366 void
367 tty_length (TTY * t, int l)
368 {
369   t->displayed_length = l;
370 }
371
372 void
373 tty_winch (TTY * t, CRT_Pos size)
374 {
375   struct winsize sz = { 0 };
376
377   sz.ws_col = size.x;
378   sz.ws_row = size.y;
379
380   ioctl (t->wfd, TIOCSWINSZ, &sz);
381 }
382
383
384
385 #if 0
386 typedef struct {
387   int in_dle;
388   int in_errmark;
389
390   int bit_edge_frequency[8];
391   int errs;
392 }
393 #endif
394 #define DLE 0377
395 #define bit(p,b,z,o) \
396         do { \
397           if ((b && z)) { \
398             p->bitfreq[z]++; \
399             z = 0; \
400           } \
401           \
402           if ((!b && o)) \
403           { \
404             p->bitfreq[z]++; \
405             o = 0; \
406           } \
407           \
408           if (b) \
409             o++; \
410           else \
411             z++; \
412           } \
413         while (0)
414 static void
415 tty_bit_analyse (Context * c, int err, int ch)
416 {
417   int d;
418   int zc = 0, oc = 0;
419   TTY_Parser *p = c->tp;
420
421
422   bit (p, 0, zc, oc);
423
424   for (d = 1; d < 0x100; d <<= 1) {
425     bit (p, ch & d, zc, oc);
426   }
427   bit (p, 1, zc, oc);
428
429
430
431   if (err) {
432     p->biterrs++;
433     gettimeofday (&p->lasterr, NULL);
434   }
435
436   if (p->biterrs) {
437     log_f (c->l,
438            "<tty_bit_analyse: 0%d%d%d%d%d%d%d%d1  [%d,%d,%d,%d,%d,%d,%d,%d,%d,%d]>",
439            ch & 0x01 ? 1 : 0, ch & 0x02 ? 1 : 0, ch & 0x04 ? 1 : 0,
440            ch & 0x08 ? 1 : 0, ch & 0x10 ? 1 : 0, ch & 0x20 ? 1 : 0,
441            ch & 0x40 ? 1 : 0, ch & 0x80 ? 1 : 0, p->bitfreq[0],
442            p->bitfreq[1], p->bitfreq[2], p->bitfreq[3], p->bitfreq[4],
443            p->bitfreq[5], p->bitfreq[6], p->bitfreq[7], p->bitfreq[8],
444            p->bitfreq[9]);
445   }
446
447 }
448
449 void
450 tty_parse_reset (Context * c)
451 {
452   TTY_Parser *p = c->tp;
453   memset (p->bitfreq, 0, sizeof (p->bitfreq));
454   p->biterrs = 0;
455   p->guessed_baud = 0;
456 }
457
458 void
459 tty_analyse (Context * c)
460 {
461   TTY_Parser *p = c->tp;
462   struct timeval now, dif;
463   int i, j, max;
464
465   if (!p->biterrs) {
466     p->guessed_baud = 0;
467     return;
468   }
469
470   gettimeofday (&now, NULL);
471
472   timersub (&now, &p->lasterr, &dif);
473
474   if (dif.tv_sec > 10) {
475     tty_parse_reset (c);
476     return;
477   }
478
479
480   max = -1;
481   j = 0;
482   for (i = 0; i < TTY_BITFREQ_LEN; ++i) {
483     if (p->bitfreq[i] > max) {
484       max = p->bitfreq[i];
485       j = i;
486     }
487   }
488
489   if (c->t)
490     i = tty_get_baud (c->t);
491   else
492     i = -1;
493
494   if (j == 1) {
495     /* Closest bit edge is one bit, so the baud rate is too low */
496     p->guessed_baud = -1;
497
498   } else {
499     if (i > 0 && j > 0)
500       p->guessed_baud = i / j;
501     else
502       p->guessed_baud = 0;
503
504   }
505
506   if (p->guessed_baud == -1) {
507     log_f (c->l, "<tty_analyse: %6d errors, current rate %db is too low>",
508            p->biterrs, i);
509   } else {
510     log_f (c->l, "<tty_analyse: %6d errors, current rate %db, suggest %db>",
511            p->biterrs, i, p->guessed_baud);
512   }
513
514 }
515
516 TTY_Parser *
517 tty_parser_new (void)
518 {
519   TTY_Parser *p;
520
521   p = (TTY_Parser *) xmalloc (sizeof (TTY_Parser));
522
523   memset (p, 0, sizeof (TTY_Parser));
524
525   return p;
526 }
527
528 int
529 tty_parse (Context * c, uint8_t * buf, int len)
530 {
531   TTY_Parser *p;
532   int err = 0;
533
534   p = c->tp;
535
536   while (len--) {
537
538     if (p->in_dle) {
539       p->in_dle = 0;
540       switch (*buf) {
541       case DLE:
542         tty_bit_analyse (c, 0, *buf);
543         err += utf8_parse (c, *buf);
544         break;
545       case 0:
546         p->in_errmark = 1;
547         break;
548       default:
549         log_f (c->l, "%s:%d DLE parsing error: \\377 \\%03o", __FILE__,
550                __LINE__, *buf);
551       }
552     } else if (p->in_errmark) {
553       p->in_errmark = 0;
554
555       log_f (c->l, "<tty reports error: \\377 \\000 \\%03o>",
556              __FILE__, __LINE__, *buf);
557
558       tty_bit_analyse (c, 1, *buf);
559
560       tty_analyse (c);
561
562       err += utf8_parse (c, *buf);
563
564       err += utf8_parse (c, SYM_CHAR_RESET);
565
566     } else if (*buf == DLE) {
567       p->in_dle = 1;
568
569     } else {
570       tty_bit_analyse (c, 0, *buf);
571
572       tty_analyse (c);
573
574       err += utf8_parse (c, *buf);
575
576     }
577     buf++;
578   }
579   return err;
580 }