chiark / gitweb /
serialmgr: Copy from woking.cam.xci-test.com
[sympathy.git] / src / tty.c
index 053964672647b345bbc1bb423780cf0ec0f357ed..b0f1d29c2e188d24dc6ea137acaaf397b1b4830d 100644 (file)
--- a/src/tty.c
+++ b/src/tty.c
-/*
- * testtty.c:
+/* 
+ * tty.c:
  *
- * Copyright (c) 2008 James McKenzie <james@fishsoup.dhs.org>,
+ * Copyright (c) 2008 James McKenzie <sympathy@madingley.org>,
  * All rights reserved.
  *
  */
 
-static char rcsid[] = "$Id$";
+static char rcsid[] = "$Id: tty.c,v 1.27 2010/07/27 14:49:35 james Exp $";
 
-/*
- * $Log$
- * Revision 1.2  2008/02/07 11:11:14  staffcvs
+/* 
+ * $Log: tty.c,v $
+ * Revision 1.27  2010/07/27 14:49:35  james
+ * add support for byte logging
+ *
+ * Revision 1.26  2008/03/10 11:49:33  james
+ * *** empty log message ***
+ *
+ * Revision 1.25  2008/03/07 13:16:02  james
+ * *** empty log message ***
+ *
+ * Revision 1.24  2008/03/07 12:37:04  james
+ * *** empty log message ***
+ *
+ * Revision 1.23  2008/03/06 16:49:39  james
+ * *** empty log message ***
+ *
+ * Revision 1.22  2008/03/06 16:49:05  james
+ * *** empty log message ***
+ *
+ * Revision 1.21  2008/03/03 06:04:42  james
+ * *** empty log message ***
+ *
+ * Revision 1.20  2008/03/02 10:37:56  james
+ * *** empty log message ***
+ *
+ * Revision 1.19  2008/02/28 16:57:52  james
+ * *** empty log message ***
+ *
+ * Revision 1.18  2008/02/28 16:37:16  james
+ * *** empty log message ***
+ *
+ * Revision 1.17  2008/02/28 15:37:06  james
+ * *** empty log message ***
+ *
+ * Revision 1.16  2008/02/28 12:12:25  james
+ * *** empty log message ***
+ *
+ * Revision 1.15  2008/02/28 00:10:44  james
+ * *** empty log message ***
+ *
+ * Revision 1.14  2008/02/23 13:05:58  staffcvs
+ * *** empty log message ***
+ *
+ * Revision 1.13  2008/02/23 11:48:37  james
+ * *** empty log message ***
+ *
+ * Revision 1.12  2008/02/22 23:39:27  james
+ * *** empty log message ***
+ *
+ * Revision 1.11  2008/02/20 18:31:53  james
+ * *** empty log message ***
+ *
+ * Revision 1.10  2008/02/15 23:52:12  james
  * *** empty log message ***
  *
- * Revision 1.1  2008/02/07 01:02:52  james
+ * Revision 1.9  2008/02/15 03:32:07  james
  * *** empty log message ***
  *
- * Revision 1.3  2008/02/06 17:53:28  james
+ * Revision 1.8  2008/02/14 10:36:18  james
  * *** empty log message ***
  *
- * Revision 1.2  2008/02/04 02:05:06  james
+ * Revision 1.7  2008/02/14 10:34:30  james
  * *** empty log message ***
  *
- * Revision 1.1  2008/02/04 01:32:39  james
+ * Revision 1.6  2008/02/13 16:59:34  james
+ * *** empty log message ***
+ *
+ * Revision 1.5  2008/02/13 16:57:29  james
+ * *** empty log message ***
+ *
+ * Revision 1.4  2008/02/12 22:36:46  james
+ * *** empty log message ***
+ *
+ * Revision 1.3  2008/02/09 15:47:28  james
  * *** empty log message ***
  *
  */
 
+
 #include "project.h"
 
-static void
-set_nonblocking (int fd)
+static int
+speed_t_to_baud (speed_t s)
 {
-  long arg;
-  arg = fcntl (fd, F_GETFL, arg);
-  arg |= O_NONBLOCK;
-  fcntl (fd, F_SETFL, arg);
+  switch (s) {
+#ifdef B0
+  case B0:
+    return 0;
+#endif
+#ifdef B50
+  case B50:
+    return 50;
+#endif
+#ifdef B75
+  case B75:
+    return 75;
+#endif
+#ifdef B110
+  case B110:
+    return 110;
+#endif
+#ifdef B134
+  case B134:
+    return 134;
+#endif
+#ifdef B150
+  case B150:
+    return 150;
+#endif
+#ifdef B200
+  case B200:
+    return 200;
+#endif
+#ifdef B300
+  case B300:
+    return 300;
+#endif
+#ifdef B600
+  case B600:
+    return 600;
+#endif
+#ifdef B1200
+  case B1200:
+    return 1200;
+#endif
+#ifdef B1800
+  case B1800:
+    return 1800;
+#endif
+#ifdef B2400
+  case B2400:
+    return 2400;
+#endif
+#ifdef B4800
+  case B4800:
+    return 4800;
+#endif
+#ifdef B9600
+  case B9600:
+    return 9600;
+#endif
+#ifdef B19200
+  case B19200:
+    return 19200;
+#endif
+#ifdef B38400
+  case B38400:
+    return 38400;
+#endif
+#ifdef B57600
+  case B57600:
+    return 57600;
+#endif
+#ifdef B115200
+  case B115200:
+    return 115200;
+#endif
+#ifdef B230400
+  case B230400:
+    return 230400;
+#endif
+  }
+
+  return -1;
 }
 
-static void
-set_blocking (int fd)
+static speed_t
+baud_to_speed_t (int baud)
 {
-  long arg;
-  arg = fcntl (fd, F_GETFL, arg);
-  arg &= ~O_NONBLOCK;
-  fcntl (fd, F_SETFL, arg);
+  switch (baud) {
+#ifdef B0
+  case 0:
+    return B0;
+#endif
+#ifdef B50
+  case 50:
+    return B50;
+#endif
+#ifdef B75
+  case 75:
+    return B75;
+#endif
+#ifdef B110
+  case 110:
+    return B110;
+#endif
+#ifdef B134
+  case 134:
+    return B134;
+#endif
+#ifdef B150
+  case 150:
+    return B150;
+#endif
+#ifdef B200
+  case 200:
+    return B200;
+#endif
+#ifdef B300
+  case 300:
+    return B300;
+#endif
+#ifdef B600
+  case 600:
+    return B600;
+#endif
+#ifdef B1200
+  case 1200:
+    return B1200;
+#endif
+#ifdef B1800
+  case 1800:
+    return B1800;
+#endif
+#ifdef B2400
+  case 2400:
+    return B2400;
+#endif
+#ifdef B4800
+  case 4800:
+    return B4800;
+#endif
+#ifdef B9600
+  case 9600:
+    return B9600;
+#endif
+#ifdef B19200
+  case 19200:
+    return B19200;
+#endif
+#ifdef B38400
+  case 38400:
+    return B38400;
+#endif
+#ifdef B57600
+  case 57600:
+    return B57600;
+#endif
+#ifdef B115200
+  case 115200:
+    return B115200;
+#endif
+#ifdef B230400
+  case 230400:
+    return B230400;
+#endif
+  }
+  return -1;
 }
 
-static void
-default_termios (struct termios *termios)
+void
+tty_pre_select (TTY * t, fd_set * rfds, fd_set * wfds)
+{
+  int line;
+  struct timeval now, dif;
+
+  if (t->hanging_up) {
+
+    gettimeofday (&now, NULL);
+    timersub (&now, &t->hangup_clock, &dif);
+    if (dif.tv_sec) {
+      line = TIOCM_DTR;
+      ioctl (t->rfd, TIOCMBIS, &line);
+      t->hanging_up = 0;
+    }
+  }
+
+
+  FD_SET (t->rfd, rfds);
+}
+
+int
+tty_get_status (TTY * t, TTY_Status * s)
 {
 
-  memset (termios, 0, sizeof (termios));
+  s->lines = 0;
+  ioctl (t->rfd, TIOCMGET, &s->lines);
+
+  if (tcgetattr (t->rfd, &s->termios))
+    return -1;
+
+  s->baud = speed_t_to_baud (cfgetispeed (&s->termios));
+  s->blocked = t->blocked;
 
-  termios->c_iflag = ICRNL | IXON;
-  termios->c_oflag = OPOST | ONLCR | NL0 | CR0 | TAB0 | BS0 | VT0 | FF0;
-  termios->c_lflag =
-    ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE;
+  return 0;
+}
 
-  termios->c_cc[VINTR] = 003;
-  termios->c_cc[VQUIT] = 034;
-  termios->c_cc[VERASE] = 0177;
-  termios->c_cc[VKILL] = 025;
-  termios->c_cc[VEOF] = 004;
-  termios->c_cc[VEOL] = 0;
-  termios->c_cc[VEOL2] = 0;
-  termios->c_cc[VSTART] = 021;
-  termios->c_cc[VSTOP] = 023;
-  termios->c_cc[VSUSP] = 032;
-  termios->c_cc[VLNEXT] = 026;
-  termios->c_cc[VWERASE] = 027;
-  termios->c_cc[VREPRINT] = 022;
-  termios->c_cc[VDISCARD] = 017;
+int
+tty_get_baud (TTY * t)
+{
+  struct termios tios = { 0 };
 
-  termios->c_cflag = CS8 | CREAD | CLOCAL;
+  if (tcgetattr (t->rfd, &tios))
+    return;
 
-  cfsetispeed (termios, B9600);
-  cfsetospeed (termios, B9600);
+  return speed_t_to_baud (cfgetispeed (&tios));
 }
 
 
-static int
-open_fd_to_bash (void)          /*thump */
+void
+tty_set_baud (TTY * t, int rate)
 {
-  pid_t child;
-  int fd;
-  struct winsize winsize = { 0 };
-  struct termios termios;
+  struct termios tios = { 0 };
 
-  default_termios (&termios);
+  speed_t s = baud_to_speed_t (rate);
 
-  winsize.ws_row = VT102_ROWS;
-  winsize.ws_col = VT102_COLS;
+  if (s == (speed_t) - 1)
+    return;
 
-  child = forkpty (&fd, NULL, &termios, &winsize);
+  if (tcgetattr (t->rfd, &tios))
+    return;
 
-  switch (child)
-    {
-    case -1:                   /*boo hiss */
-      return -1;
-    case 0:                    /*waaah */
-      setenv ("TERM", "vt102", 1);
-      setenv ("LANG", "C", 1);
-      execl ("/bin/sh", "-", (char *) 0);
-      _exit (-1);
-    }
+  cfsetispeed (&tios, s);
+  cfsetospeed (&tios, s);
 
-  return fd;
+  tcsetattr (t->rfd, TCSANOW, &tios);
 }
 
+void
+tty_send_break (TTY * t)
+{
+  tcsendbreak (t->wfd, 0);
+}
 
-TTY *
-tty_new_test (void)
+void
+tty_set_flow (TTY * t, int flow)
 {
-  TTY *t;
-  t = (TTY *) malloc (sizeof (TTY));
-  t->fd = open_fd_to_bash ();
+  struct termios tios = { 0 };
 
-  set_nonblocking (t->fd);
+  if (tcgetattr (t->rfd, &tios))
+    return;
+
+  if (flow)
+    tios.c_cflag |= CRTSCTS;
+  else
+    tios.c_cflag &= ~CRTSCTS;
+
+  tcsetattr (t->rfd, TCSANOW, &tios);
 
-  return t;
 }
 
-static int
-wrap_read (int fd, void *buf, int len)
+void
+tty_hangup (TTY * t)
 {
-  int red;
+  int line;
 
-  red = read (fd, buf, len);
-  if (!red)
-    return -1;
+  line = TIOCM_DTR;
+  ioctl (t->rfd, TIOCMBIC, &line);
 
-  if ((red < 0) && (errno == EAGAIN))
-    red = 0;
+  t->hanging_up = 1;
+  gettimeofday (&t->hangup_clock, NULL);
 
-  return red;
 }
 
-static int
-wrap_write (int fd, void *buf, int len)
+void
+tty_length (TTY * t, int l)
 {
-  int writ;
+  t->displayed_length = l;
+}
 
-  writ = write (fd, buf, len);
-  if (!writ)
-    return -1;
+void
+tty_winch (TTY * t, CRT_Pos size)
+{
+  struct winsize sz = { 0 };
 
-  if ((writ < 0) && (errno == -EAGAIN))
-    writ = 0;
+  sz.ws_col = size.x;
+  sz.ws_row = size.y;
 
-  return writ;
+  ioctl (t->wfd, TIOCSWINSZ, &sz);
 }
 
-int
-tty_read (TTY * t, void *buf, int len)
+
+
+#if 0
+typedef struct {
+  int in_dle;
+  int in_errmark;
+
+  int bit_edge_frequency[8];
+  int errs;
+}
+#endif
+#define DLE 0377
+#define bit(p,b,z,o) \
+       do { \
+         if ((b && z)) { \
+           p->bitfreq[z]++; \
+           z = 0; \
+         } \
+         \
+         if ((!b && o)) \
+         { \
+           p->bitfreq[z]++; \
+           o = 0; \
+         } \
+         \
+         if (b) \
+           o++; \
+         else \
+           z++; \
+         } \
+       while (0)
+static void
+tty_bit_analyse (Context * c, int err, int ch)
 {
-  int red, done = 0;
+  int d;
+  int zc = 0, oc = 0;
+  TTY_Parser *p = c->tp;
 
 
-  do
-    {
+  bit (p, 0, zc, oc);
 
-      red = wrap_read (t->fd, buf, len);
-      if (red < 0)
-        return -1;
-      if (!red)
-        return done;
+  for (d = 1; d < 0x100; d <<= 1) {
+    bit (p, ch & d, zc, oc);
+  }
+  bit (p, 1, zc, oc);
 
-      buf += red;
-      len -= red;
-      done += red;
-    }
-  while (len);
 
 
-  return done;
+  if (err) {
+    p->biterrs++;
+    gettimeofday (&p->lasterr, NULL);
+  }
+
+  if (p->biterrs) {
+    log_f (c->l,
+           "<tty_bit_analyse: 0%d%d%d%d%d%d%d%d1  [%d,%d,%d,%d,%d,%d,%d,%d,%d,%d]>",
+           ch & 0x01 ? 1 : 0, ch & 0x02 ? 1 : 0, ch & 0x04 ? 1 : 0,
+           ch & 0x08 ? 1 : 0, ch & 0x10 ? 1 : 0, ch & 0x20 ? 1 : 0,
+           ch & 0x40 ? 1 : 0, ch & 0x80 ? 1 : 0, p->bitfreq[0],
+           p->bitfreq[1], p->bitfreq[2], p->bitfreq[3], p->bitfreq[4],
+           p->bitfreq[5], p->bitfreq[6], p->bitfreq[7], p->bitfreq[8],
+           p->bitfreq[9]);
+  }
+
 }
 
+void
+tty_parse_reset (Context * c)
+{
+  TTY_Parser *p = c->tp;
+  memset (p->bitfreq, 0, sizeof (p->bitfreq));
+  p->biterrs = 0;
+  p->guessed_baud = 0;
+}
 
-int
-tty_write (TTY * t, void *buf, int len)
+void
+tty_analyse (Context * c)
 {
-  int writ, done = 0;
+  TTY_Parser *p = c->tp;
+  struct timeval now, dif;
+  int i, j, max;
 
-#if 0
-  {
-    int i;
-    uint8_t *p = buf;
-    for (i = 0; i < len; ++i)
-      fprintf (stderr, "vw: %03o %c\n", p[i], p[i] > 31 ? p[i] : 32);
+  if (!p->biterrs) {
+    p->guessed_baud = 0;
+    return;
   }
-#endif
 
-  do
-    {
+  gettimeofday (&now, NULL);
+
+  timersub (&now, &p->lasterr, &dif);
+
+  if (dif.tv_sec > 10) {
+    tty_parse_reset (c);
+    return;
+  }
 
-      writ = wrap_write (t->fd, buf, len);
-      if (writ < 0)
-        return -1;
-      if (!writ)
-        sleep (1);
 
-      buf += writ;
-      len -= writ;
-      done += writ;
+  max = -1;
+  j = 0;
+  for (i = 0; i < TTY_BITFREQ_LEN; ++i) {
+    if (p->bitfreq[i] > max) {
+      max = p->bitfreq[i];
+      j = i;
     }
-  while (len);
+  }
 
+  if (c->t)
+    i = tty_get_baud (c->t);
+  else
+    i = -1;
+
+  if (j == 1) {
+    /* Closest bit edge is one bit, so the baud rate is too low */
+    p->guessed_baud = -1;
+
+  } else {
+    if (i > 0 && j > 0)
+      p->guessed_baud = i / j;
+    else
+      p->guessed_baud = 0;
+
+  }
+
+  if (p->guessed_baud == -1) {
+    log_f (c->l, "<tty_analyse: %6d errors, current rate %db is too low>",
+           p->biterrs, i);
+  } else {
+    log_f (c->l, "<tty_analyse: %6d errors, current rate %db, suggest %db>",
+           p->biterrs, i, p->guessed_baud);
+  }
 
-  return done;
 }
 
-void
-tty_free (TTY * t)
+TTY_Parser *
+tty_parser_new (void)
 {
-  close (t->fd);
-  free (t);
+  TTY_Parser *p;
+
+  p = (TTY_Parser *) xmalloc (sizeof (TTY_Parser));
+
+  memset (p, 0, sizeof (TTY_Parser));
+
+  return p;
+}
+
+int
+tty_parse (Context * c, uint8_t * buf, int len)
+{
+  TTY_Parser *p;
+  int err = 0;
+
+  p = c->tp;
+
+  while (len--) {
+
+    if (p->in_dle) {
+      p->in_dle = 0;
+      switch (*buf) {
+      case DLE:
+        tty_bit_analyse (c, 0, *buf);
+        err += utf8_parse (c, *buf);
+        break;
+      case 0:
+        p->in_errmark = 1;
+        break;
+      default:
+        log_f (c->l, "%s:%d DLE parsing error: \\377 \\%03o", __FILE__,
+               __LINE__, *buf);
+      }
+    } else if (p->in_errmark) {
+      p->in_errmark = 0;
+
+      log_f (c->l, "<tty reports error: \\377 \\000 \\%03o>",
+             __FILE__, __LINE__, *buf);
+
+      tty_bit_analyse (c, 1, *buf);
+
+      tty_analyse (c);
+
+      err += utf8_parse (c, *buf);
+
+      err += utf8_parse (c, SYM_CHAR_RESET);
+
+    } else if (*buf == DLE) {
+      p->in_dle = 1;
+
+    } else {
+      tty_bit_analyse (c, 0, *buf);
+
+      tty_analyse (c);
+
+      err += utf8_parse (c, *buf);
+
+    }
+    buf++;
+  }
+  return err;
 }