/* -*- mode: c; mode: fold -*- */ /*{{{ includes */ # include "config.h" # include # include # include # include # include # include # include # include # include # include # include # include # include # if HAVE_SYS_SELECT_H # include # endif /* HAVE_SYS_SELECT_H */ # if HAVE_SYS_SYSMACROS_H # include # elif HAVE_SYS_MKDEV_H # include # else /* ! HAVE_SYS_SYSMACROS_H && ! HAVE_SYS_MKDEV_H */ # define major(xx) (((xx) >> 8) & 0xff) # define minor(xx) ((xx) & 0xff) # endif /* HAVE_SYS_SYSMACROS_H || HAVE_SYS_MKDEV_H */ # include "pager.h" /*}}}*/ /*{{{ statics and typedefs */ static struct { int speed; speed_t tok; } stab[] = { { 300, B300 }, { 1200, B1200 }, { 2400, B2400 }, { 4800, B4800 }, { 9600, B9600 }, { 19200, B19200 }, { 38400, B38400 }, # ifdef B57600 { 57600, B57600 }, # endif /* B57600 */ # ifdef B115200 { 115200, B115200 }, # endif /* B115200 */ # ifdef B230400 { 230400, B230400 }, # endif /* B230400 */ # ifdef B460800 { 460800, B460800 }, # endif /* B460800 */ { -1, B9600 } }; static char *lckmth[] = { "ascii", "binary", "lower", "upper", "sysv4", "timeout", NULL }; typedef struct { # ifndef NDEBUG # define MAGIC MKMAGIC ('t', 't', 'y', '\0') long magic; # endif /* NDEBUG */ char *lck; char *device; struct termios tty, sav; int fd; string_t *line; char *sep; void (*callback) (void *, string_t *, char_t, void *); void *data; Bool suspend; /* CAPI */ int is_capi; int controller; char *msn; int connected; } serial; typedef struct _expect { int idx; char *str; int pos; int len; struct _expect *next; } expect; static int data_ready (serial *s, int fd, int *msec); /*}}}*/ /*{{{ support routines */ static char * mkprint (char *str, int len) { static char *buf = NULL; static int size = 0; int n; char ch; char extra; char *ptr; if (len >= size) { size = len + 128; if (! (buf = Realloc (buf, size + 2))) return NULL; } extra = '\0'; for (n = 0; len > 0; ++str, --len) { if (n + 8 >= size) { size += 128; if (! (buf = Realloc (buf, size + 2))) break; } ch = *str; if (ch & 0x80) { buf[n++] = '<'; buf[n++] = 'M'; buf[n++] = '-'; ch &= 0x7f; extra = '>'; } switch (ch) { case '\x00': ptr = ""; break; case '\x01': ptr = ""; break; case '\x02': ptr = ""; break; case '\x03': ptr = ""; break; case '\x04': ptr = ""; break; case '\x05': ptr = ""; break; case '\x06': ptr = ""; break; case '\x07': ptr = ""; break; case '\x08': ptr = ""; break; case '\x09': ptr = ""; break; case '\x0a': ptr = ""; break; case '\x0b': ptr = ""; break; case '\x0c': ptr = ""; break; case '\x0d': ptr = ""; break; case '\x0e': ptr = ""; break; case '\x0f': ptr = ""; break; case '\x10': ptr = ""; break; case '\x11': ptr = ""; break; case '\x12': ptr = ""; break; case '\x13': ptr = ""; break; case '\x14': ptr = ""; break; case '\x15': ptr = ""; break; case '\x16': ptr = ""; break; case '\x17': ptr = ""; break; case '\x18': ptr = ""; break; case '\x19': ptr = ""; break; case '\x1a': ptr = ""; break; case '\x1b': ptr = ""; break; case '\x1c': ptr = ""; break; case '\x1d': ptr = ""; break; case '\x1e': ptr = ""; break; case '\x1f': ptr = ""; break; case '\x7f': ptr = ""; break; default: ptr = NULL; buf[n++] = ch; break; } if (ptr) while (*ptr) buf[n++] = *ptr++; if (extra) { buf[n++] = extra; extra = '\0'; } } if (buf) buf[n] = '\0'; return buf; } static inline void msleep (int msec) { struct timeval tv; if (msec > 0) { do { tv.tv_sec = msec / 1000; tv.tv_usec = (msec % 1000) * 1000; errno = 0; } while ((select (0, NULL, NULL, NULL, & tv) < 0) && (errno == EINTR)); } } static Bool do_lock (serial *s, char *dev, char *prefix, char *method) { Bool binary; Bool lower, upper; Bool sysv4; int tout; struct stat st; char *ptr, *sav, *val; int len; char *bdev; int fd; char buf[32]; int n, m; pid_t pid; s -> lck = NULL; if (prefix) { binary = False; lower = False; upper = False; sysv4 = False; tout = 0; if (method && (method = strdup (method))) { for (ptr = method; *ptr; ) { sav = ptr; ptr = skipch (ptr, ','); val = skipch (sav, '='); len = strlen (sav); for (n = 0; lckmth[n]; ++n) if (! strncmp (lckmth[n], sav, len)) break; switch (n) { case 0: /* ascii */ binary = False; break; case 1: /* binary */ binary = True; break; case 2: /* lower */ lower = True; upper = False; break; case 3: /* upper */ lower = False; upper = True; break; case 4: /* sysv4 */ sysv4 = True; break; case 5: /* timeout */ tout = atoi (val); break; } } free (method); } if (sysv4) { bdev = NULL; if ((stat (dev, & st) != -1) && S_ISCHR (st.st_mode) && (bdev = malloc (96))) sprintf (bdev, "%03d.%03d.%03d", major (st.st_dev), major (st.st_rdev), minor (st.st_rdev)); } else { if (bdev = strrchr (dev, '/')) ++bdev; else bdev = dev; bdev = strdup (bdev); } len = strlen (prefix); if (bdev && (s -> lck = malloc (len + strlen (bdev) + 4))) { sprintf (s -> lck, "%s%s", prefix, bdev); free (bdev); if (upper || lower) { ptr = s -> lck + len; while (*ptr) { if (lower) *ptr = tolower (*ptr); else if (upper) *ptr = toupper (*ptr); ++ptr; } } do { for (n = 0; n < 2; ++n) { if ((fd = open (s -> lck, O_CREAT | O_EXCL | O_WRONLY, 0600)) != -1) break; if ((! n) && ((fd = open (s -> lck, O_RDONLY)) != -1)) { pid = 0; if (binary) { if (read (fd, & pid, sizeof (pid)) != sizeof (pid)) pid = 0; } else { if ((m = read (fd, buf, sizeof (buf) - 1)) > 1) { buf[m - 1] = '\0'; pid = (int) atoi (buf); } } close (fd); fd = -1; if ((pid > 0) && (kill (pid, 0) < 0) && (errno == ESRCH)) unlink (s -> lck); else break; } } if ((fd < 0) && (tout > 0)) sleep (1); } while ((fd < 0) && (tout-- > 0)); if (fd != -1) { pid = getpid (); if (binary) write (fd, & pid, sizeof (pid)); else { sprintf (buf, "%10d\n", (int) pid); write (fd, buf, strlen (buf)); } # if HAVE_FCHMOD fchmod (fd, 0644); # else /* HAVE_FCHMOD */ chmod (s -> lck, 0644); # endif /* HAVE_FCHMOD */ # if HAVE_FCHOWN fchown (fd, geteuid (), getegid ()); # else /* HAVE_FCHOWN */ chown (s -> lck, geteuid (), getegid ()); # endif /* HAVE_FCHOWN */ close (fd); } else { free (s -> lck); s -> lck = NULL; return False; } } else { if (bdev) free (bdev); return False; } } return True; } static void do_unlock (serial *s) { if (s -> lck) { unlink (s -> lck); free (s -> lck); s -> lck = NULL; } } /*}}}*/ /*{{{ open/close/reopen */ void * tty_open (char *dev, char *lckprefix, char *lckmethod) { serial *s; int n; if (s = (serial *) malloc (sizeof (serial))) { # ifndef NDEBUG s -> magic = MAGIC; # endif /* NDEBUG */ s -> is_capi = 0; if (do_lock (s, dev, lckprefix, lckmethod)) { if ((s -> fd = open (dev, O_RDWR)) != -1) { n = tcgetattr (s -> fd, & s -> sav); if ((n < 0) || (! (s -> device = strdup (dev)))) { close (s -> fd); do_unlock (s); free (s); s = NULL; } else { s -> tty = s -> sav; s -> line = NULL; s -> sep = NULL; s -> callback = NULL; s -> data = NULL; s -> suspend = False; } } else { do_unlock (s); free (s); s = NULL; } } else { free (s); s = NULL; } } return s; } void * tty_close (void *sp) { serial *s = (serial *) sp; MCHK (s); if (s -> is_capi) { return(hyla_capi_close(sp)); } if (s) { if (s -> fd != -1) { tcsetattr (s -> fd, TCSANOW, & s -> sav); close (s -> fd); } do_unlock (s); if (s -> device) free (s -> device); if (s -> sep) free (s -> sep); if (s -> line) sfree (s -> line); free (s); } return NULL; } Bool tty_reopen (void *sp, int msec) { serial *s = (serial *) sp; MCHK (s); if (s -> is_capi) { return((s -> connected) ? True : False); } if (s -> fd != -1) { close (s -> fd); if (msec > 0) msleep (msec); s -> fd = -1; } if (s -> device && ((s -> fd = open (s -> device, O_RDWR)) != -1)) tcsetattr (s -> fd, TCSANOW, & s -> tty); return s -> fd != -1 ? True : False; } /*}}}*/ /*{{{ hangup, get fd */ void tty_hangup (void *sp, int msec) { serial *s = (serial *) sp; struct termios tmp; MCHK (s); V (2, ("[Hangup] ")); if (s && (s -> fd != -1)) { if (s -> is_capi) { hyla_capi_disconnect(sp); return; } else { tmp = s -> tty; cfsetispeed (& tmp, B0); cfsetospeed (& tmp, B0); if (tcsetattr (s -> fd, TCSANOW, & tmp) != -1) { msleep (msec); tcsetattr (s -> fd, TCSANOW, & s -> tty); } tty_reopen (s, msec); } } V (2, ("\n")); } int tty_fd (void *sp) { serial *s = (serial *) sp; MCHK (s); return s ? s -> fd : -1; } /*}}}*/ /*{{{ configuration */ int tty_setup (void *sp, Bool raw, Bool modem, int speed, int bpb, int sb, char par) { serial *s = (serial *) sp; int n; MCHK (s); if ((! s) || (s -> fd < 0)) return -1; if ((bpb < 5) || (bpb > 8) || ((sb != 1) && (sb != 2)) || ((par != 'n') && (par != 'e') && (par != 'o'))) return -1; for (n = 0; stab[n].speed > 0; ++n) if (stab[n].speed == speed) break; if (stab[n].speed < 0) return -1; if (raw) { s -> tty.c_iflag &= ~(IGNCR | ICRNL | INLCR | ISTRIP | IXON | IXOFF); s -> tty.c_iflag |= IGNBRK; s -> tty.c_oflag = 0; s -> tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ISIG); # ifdef CRTSCTS # ifdef sun /* SunOS 4.x needs RTSCTS off if carrier detect is on */ /* but there is no carrier present (fkk) */ s -> tty.c_cflag &= ~CRTSCTS; # else /* sun */ s -> tty.c_cflag |= CRTSCTS; # endif /* sun */ # endif /* CRTSCTS */ s -> tty.c_cc[VMIN] = 0; s -> tty.c_cc[VTIME] = 0; } else { s -> tty = s -> sav; s -> tty.c_lflag |= ICANON | ECHO | ECHOE | ECHOK | ECHONL | ISIG; } if (modem) { # if 0 s -> tty.c_cflag &= ~CLOCAL; # else s -> tty.c_cflag |= CLOCAL; # endif s -> tty.c_cflag |= HUPCL; } else { s -> tty.c_cflag |= CLOCAL; s -> tty.c_cflag &= ~HUPCL; } cfsetispeed (& s -> tty, stab[n].tok); cfsetospeed (& s -> tty, stab[n].tok); s -> tty.c_cflag &= ~(CSIZE); s -> tty.c_cflag |= CREAD; switch (sb) { default: case 1: s -> tty.c_cflag &= ~CSTOPB; break; case 2: s -> tty.c_cflag |= CSTOPB; break; } switch (bpb) { case 5: s -> tty.c_cflag |= CS5; break; case 6: s -> tty.c_cflag |= CS6; break; case 7: s -> tty.c_cflag |= CS7; break; default: case 8: s -> tty.c_cflag |= CS8; break; } switch (par) { default: case 'n': s -> tty.c_cflag &= ~PARENB; break; case 'e': s -> tty.c_cflag &= ~PARODD; s -> tty.c_cflag |= PARENB; break; case 'o': s -> tty.c_cflag |= PARENB | PARODD; break; } return tcsetattr (s -> fd, TCSANOW, & s -> tty) < 0 ? -1 : 0; } /*}}}*/ /*{{{ callback */ void tty_set_line_callback (void *sp, void (*func) (void *, string_t *, char_t, void *), char *sep, void *data) { serial *s = (serial *) sp; MCHK (s); if (s) { if (! (s -> callback = func)) { if (s -> line) s -> line = sfree (s -> line); if (s -> sep) { free (s -> sep); s -> sep = NULL; } s -> data = NULL; } else { if (s -> sep) free (s -> sep); s -> sep = sep ? strdup (sep) : NULL; s -> data = data; } s -> suspend = False; } } void tty_suspend_callback (void *sp, Bool susp) { serial *s = (serial *) sp; MCHK (s); if (s) if (s -> suspend = susp) if (s -> line) s -> line -> len = 0; } /*}}}*/ /*{{{ sending */ int tty_send (void *sp, char *str, int len) { serial *s = (serial *) sp; int n, sent; MCHK (s); if ((! s) || (s -> fd < 0) || (! str)) return -1; V (2, ("[Send] %s", mkprint (str, len))); sent = 0; while (len > 0) { if (s -> is_capi) { if (hyla_capi_send(sp, str, len)) { sent = len; len = 0; } break; } if ((n = write (s -> fd, str, len)) > 0) { str += n; sent += n; len -= n; } else if (! n) break; else if (errno == EIO) { if (! tty_reopen (s, 0)) break; } else if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) msleep (200); else if (errno != EINTR) break; } V (2, ("\n")); return sent; } int tty_send_string (void *sp, char *str) { return str ? tty_send (sp, str, strlen (str)) : -1; } /*}}}*/ /*{{{ expecting */ static void addline (serial *s, char ch) { if (s -> callback && s -> sep && (! s -> suspend) && ch) { if (! s -> line) s -> line = snew (NULL, 32); else if (s -> line -> len + 1 >= s -> line -> size) if (! sexpand (s -> line, s -> line -> size * 2)) return; if (strchr (s -> sep, ch)) { (*s -> callback) ((void *) s, s -> line, (char_t) ch, s -> data); s -> line -> len = 0; } else s -> line -> str[s -> line -> len++] = (char_t) ch; } } static size_t dev_read(serial *s, int fd, void *buf, size_t count) { if (s -> is_capi) return(hyla_capi_read(s, buf, count)); return(read(fd, buf, count)); } static int do_expect (serial *s, int tout, expect *base) { int ret; int msec; int n; expect *run; char ch; if ((! s) || (s -> fd < 0)) return -1; V (2, ("[Expect] ")); for (run = base; run; run = run -> next) run -> pos = 0; msec = (tout > 0) ? tout * 1000 : 0; ret = 0; while (! ret) if ((n = data_ready (s, s -> fd, & msec)) > 0) { while ((n = dev_read (s, s -> fd, & ch, 1)) == 1) { addline (s, ch); V (3, ("%s", mkprint (& ch, 1))); for (run = base; run; run = run -> next) if (run -> str[run -> pos] == ch) { run -> pos++; if (run -> pos == run -> len) { ret = run -> idx; break; } } else run -> pos = 0; if (ret) break; } if (n < 0) if (errno == EIO) tty_reopen (s, 0); } else if (! n) break; if (verbose > 1) if (run) printf (" got %s", mkprint (run -> str, run -> len)); else printf (" timeout"); V (2, ("\n")); return ret; } int tty_expect (void *sp, int tout, ...) { va_list par; int ret; char *ptr; expect *base, *prev, *tmp; int idx; va_start (par, tout); MCHK ((serial *) sp); ret = 0; base = NULL; prev = NULL; idx = 1; while (ptr = va_arg (par, char *)) if (tmp = (expect *) malloc (sizeof (expect))) { tmp -> idx = idx++; tmp -> str = ptr; tmp -> pos = 0; tmp -> len = va_arg (par, int); tmp -> next = NULL; if (prev) prev -> next = tmp; else base = tmp; prev = tmp; } else break; if (! ptr) ret = do_expect ((serial *) sp, tout, base); else ret = -1; while (base) { tmp = base; base = base -> next; free (tmp); } va_end (par); return ret; } int tty_expect_list (void *sp, int tout, char **strs, int *lens) { int n; int ret; expect *base, *prev, *tmp; MCHK ((serial *) sp); base = NULL; prev = NULL; for (n = 0; strs[n]; ++n) if (tmp = (expect *) malloc (sizeof (expect))) { tmp -> idx = n + 1; tmp -> str = strs[n]; tmp -> pos = 0; tmp -> len = lens[n]; tmp -> next = NULL; if (prev) prev -> next = tmp; else base = tmp; prev = tmp; } else break; if (strs[n]) ret = -1; else ret = do_expect ((serial *) sp, tout, base); while (base) { tmp = base; base = base -> next; free (tmp); } return ret; } int tty_expect_string (void *sp, int tout, char *str) { expect tmp; MCHK ((serial *) sp); if (! str) return -1; tmp.idx = 1; tmp.str = str; tmp.pos = 0; tmp.len = strlen (str); tmp.next = NULL; return do_expect ((serial *) sp, tout, & tmp); } /*}}}*/ /*{{{ send/expect */ static int tonum (char ch) { switch (ch) { default: case '0': return 0; case '1': return 1; case '2': return 2; case '3': return 3; case '4': return 4; case '5': return 5; case '6': return 6; case '7': return 7; case '8': return 8; case '9': return 9; } } static char * expand (char *str, char **opts, int ocnt) { char *ret; int len, siz; int idx, olen; ret = NULL; len = 0; siz = 0; while (*str) { if (len >= siz) { siz += (siz ? siz : 32); if (! (ret = Realloc (ret, siz + 2))) break; } if (*str == '\\') { ++str; if (*str) { switch (*str) { case 'a': ret[len++] = '\a'; break; case 'b': ret[len++] = '\b'; break; case 'f': ret[len++] = '\f'; break; case 'l': ret[len++] = '\012'; break; case 'n': ret[len++] = '\n'; break; case 'r': ret[len++] = '\r'; break; case 's': ret[len++] = ' '; break; case 't': ret[len++] = '\t'; break; default: ret[len++] = *str; break; } ++str; } } else if (*str == '^') { ++str; if (*str) { if (*str == '?') ret[len++] = '\x7f'; else ret[len++] = *str & 0x1f; ++str; } } else if (*str == '%') { ++str; idx = 0; while (isdigit (*str)) { idx *= 10; idx += tonum (*str++); } if ((idx >= 0) && (idx < ocnt)) { olen = strlen (opts[idx]); if (len + olen >= siz) { siz = len + olen + 64; if (! (ret = Realloc (ret, siz + 2))) break; } strcpy (ret + len, opts[idx]); len += olen; } } else if ((*str == '\'') || (*str == '"')) ++str; else ret[len++] = *str++; } if (ret) ret[len] = '\0'; return ret; } static int handle_command (void *sp, char *str) { serial *s = (serial *) sp; char *p1; int mult; int ret; ret = 0; V (2, ("[Cmd")); for (p1 = str; *p1; ) { if (isdigit (*p1)) { mult = 0; while (isdigit (*p1)) { mult *= 10; mult += tonum (*p1++); } } else mult = 1; if (*p1) switch (*p1++) { case 'd': V (2, (" Dzz %d", mult)); sleep (mult); break; case 'D': V (2, (" Mdzz %d", mult)); msleep (mult); break; case 'b': if (s && (s -> fd != -1)) { V (2, (" Brk %d", mult)); if (s -> is_capi) hyla_capi_sendbreak(s, mult); else tcsendbreak (s -> fd, mult); } break; case 'h': if (s && (s -> fd != -1)) { V (2, (" Hup")); tty_hangup (sp, mult * 500); } break; case 'o': if (s && (s -> fd != -1)) { V (2, (" Drain")); if (s -> is_capi) hyla_capi_drain(s); else tcdrain (s -> fd); } break; case '<': if (s && (s -> fd != -1)) { V (2, (" Iflush")); if (s -> is_capi) hyla_capi_iflush(s); else tcflush (s -> fd, TCIFLUSH); } break; case '>': if (s && (s -> fd != -1)) { V (2, (" Oflush")); if (s -> is_capi) hyla_capi_oflush(s); else tcflush (s -> fd, TCOFLUSH); } break; case 'f': V (2, (" fail")); ret = -1; break; case 's': V (2, (" success")); ret = 1; break; } } V (2, ("]\n")); return ret; } static int expect_expr (void *sp, int deftout, char *line) { int ret; char **ex; int cnt, siz; int n, m, tout, slen; char *ptr; char *p1, *p2; char **list; int *len; MCHK ((serial *) sp); ex = NULL; cnt = 0; siz = 0; for (ptr = line; *ptr; ) { if (cnt >= siz) { siz += 4; if (! (ex = (char **) Realloc (ex, (siz + 1) * sizeof (char *)))) break; } ex[cnt++] = ptr; while (*ptr && (*ptr != '-')) ++ptr; if (*ptr) *ptr++ = '\0'; } if (! ex) return -1; ret = 0; for (n = 0; n < cnt; ++n) { ptr = ex[n]; if (isdigit (*ptr)) { tout = 0; while (isdigit (*ptr)) { tout *= 10; tout += tonum (*ptr++); } } else tout = deftout; p1 = ptr; for (siz = 1, p2 = p1; p2; p2 = strchr (p2, '|')) ++siz, ++p2; if ((list = (char **) malloc ((siz + 1) * sizeof (char *))) && (len = (int *) malloc ((siz + 1) * sizeof (int)))) { for (n = 0, p1 = ptr; p1; ++n) { p2 = p1; if (p1 = strchr (p1, '|')) *p1++ = '\0'; list[n] = p2; len[n] = strlen (p2); } list[n] = NULL; len[n] = 0; if (tty_expect_list (sp, tout, list, len) != 1) ret = -1; free (list); free (len); } else { ret = -1; break; } if ((ret < 0) && (n + 1 < cnt)) { ++n; ptr = ex[n]; ret = 0; if (*ptr == '!') { if (m = handle_command (sp, ptr + 1)) { if (m < 0) ret = -1; break; } } else { slen = strlen (ptr); if (tty_send (sp, ptr, slen) != slen) { ret = -1; break; } } } else n = cnt; } if (ex) free (ex); return ret; } int tty_send_expect (void *sp, int deftout, char *str, char **opts) { serial *s = (serial *) sp; int ret; int ocnt; char *sav, *ptr, *line; int n; char quote; Bool esc; MCHK (s); ret = -1; if (opts) for (ocnt = 0; opts[ocnt]; ++ocnt) ; else ocnt = 0; if (str = strdup (str)) { ret = 0; for (ptr = str; *ptr && (! ret); ) { sav = ptr; esc = False; quote = '\0'; while (*ptr) { if (esc) esc = False; else if (*ptr == '\\') esc = True; else if (quote) { if (*ptr == quote) quote = '\0'; } else if ((*ptr == '\'') || (*ptr == '"')) quote = *ptr; else if (isspace (*ptr)) break; ++ptr; } if (*ptr) { *ptr++ = '\0'; while (isspace (*ptr)) ++ptr; } if (line = expand (sav, opts, ocnt)) { if (line[0] == '<') { if (expect_expr (sp, deftout, line + 1) < 0) ret = -1; } else if (line[0] == '!') { if ((n = handle_command (sp, line + 1)) < 0) ret = -1; else if (n > 0) while (*ptr) ++ptr; } else { n = strlen (line); if (tty_send (sp, line, n) != n) ret = -1; } free (line); } else ret = -1; } free (str); } return ret; } /*}}}*/ /*{{{ draining */ void tty_mdrain (void *sp, int msecs) { serial *s = (serial *) sp; int n, m; char ch; MCHK (s); if (s && (s -> fd != -1)) { V (2, ("[Drain] ")); if (msecs < 0) msecs = 0; do { if ((n = data_ready (s, s -> fd, & msecs)) > 0) { while ((m = dev_read (s, s -> fd, & ch, 1)) == 1) { addline (s, ch); V (2, ("%s", mkprint (& ch, 1))); } if (m < 0) if (errno == EIO) tty_reopen (s, 0); } } while (n != 0); V (2, ("\n")); } } void tty_drain (void *sp, int secs) { tty_mdrain (sp, secs * 1000); } /*}}}*/ /* * CAPI */ #include "capiconn.h" static capiconn_context *ctx; static unsigned applid; static capi_contrinfo cinfo = { 0 , 0, 0 }; static struct capi_connection *capi_conn = NULL; static serial *capi_serial; static int conn_in_progress = 0; static int capi_data_sent; static unsigned char capi_data[4096]; static int capi_data_len = 0; static void handle_messages(void) { unsigned char *msg = 0; struct timeval tv; tv.tv_sec = 1; tv.tv_usec = 0; again: if (capi20_waitformessage(applid, &tv) == 0) { if (capi20_get_message(applid, &msg) == 0) capiconn_inject(applid, msg); tv.tv_sec = 0; tv.tv_usec = 0; goto again; } } int device_is_capi(char *dev) { if (!dev) return(0); if (strlen(dev) < 4) return(0); if (strncasecmp(dev, "capi", 4)) return(0); return(1); } int hyla_is_capi(void *sp) { serial *s = (serial *) sp; return(s->is_capi); } static void capi_log(const char *format, ...) { char buffer[2048]; va_list ap; va_start(ap, format); vsprintf(buffer, format, ap); strcat(buffer, "\n"); va_end(ap); V (3, (buffer)); } static void chargeinfo(capi_connection *cp, unsigned long charge, int inunits) { if (inunits) { V (3, ("charge in units: %d\n", charge)); } else { V (3, ("charge in currency: %d\n", charge)); } } static void put_message(unsigned appid, unsigned char *msg) { unsigned err; err = capi20_put_message (appid, msg); if (err) V (2, ("capi putmessage %s 0x%x\n", capi_info2str(err), err)); } static void connected(capi_connection *cp, _cstruct NCPI) { capi_serial -> connected = 1; conn_in_progress = 0; V (3, ("CAPI connected\n")); } static void disconnected(capi_connection *cp, int localdisconnect, unsigned reason, unsigned reason_b3) { capi_serial -> connected = 0; conn_in_progress = 0; capi_conn = NULL; V (3, ("CAPI disconnected\n")); } static void handle_capi_sent(capi_connection *con, unsigned char* data) { capi_data_sent = 1; } static void handle_capi_data(capi_connection *con, unsigned char* data, unsigned int len) { int mlen = ((sizeof(capi_data) - capi_data_len) < len) ? (sizeof(capi_data) - capi_data_len) : len; if (mlen) { memcpy(&capi_data[capi_data_len], data, mlen); capi_data_len += mlen; } else { V (3, ("\ncapi data buffer overflow\n")); } } capiconn_callbacks callbacks = { malloc: malloc, free: free, disconnected: disconnected, incoming: 0, connected: connected, received: handle_capi_data, datasent: handle_capi_sent, chargeinfo: chargeinfo, capi_put_message: put_message, debugmsg: (void (*)(const char *, ...))capi_log, infomsg: (void (*)(const char *, ...))capi_log, errmsg: (void (*)(const char *, ...))capi_log }; void *hyla_capi_init(char *dev) { serial *s; char *p, *p1, *p2; if (s = (serial *) malloc (sizeof (serial))) { if ((capi20_register (1, 8, 2048, &applid)) != 0) { free (s); V (3, ("Error CAPI register\n")); return(NULL); } # ifndef NDEBUG s -> magic = MAGIC; # endif /* NDEBUG */ s -> device = strdup (dev); p = skipch(s -> device, '/'); p1 = p; p2 = skipch(p, '/'); if (*p1) s -> controller = strtol(p1, NULL, 10); else s -> controller = 1; if (*p2) s -> msn = strdup(p2); else s -> msn = NULL; V (3, ("CAPI controller=%d MSN=%s\n", s -> controller, s -> msn)); if ((ctx = capiconn_getcontext(applid, &callbacks)) != 0) { if (capiconn_addcontr(ctx, s -> controller, &cinfo) != CAPICONN_OK) { (void)capiconn_freecontext(ctx); (void)capi20_release(applid); if (s -> msn) free (s -> msn); if (s -> device) free (s -> device); free (s); s = NULL; } s -> fd = capi20_fileno(applid); s -> is_capi = 1; s -> connected = 0; s -> line = NULL; s -> sep = NULL; s -> callback = NULL; s -> data = NULL; s -> suspend = False; capi_serial = s; } else { (void)capi20_release(applid); V (3, ("Error CAPI-conn register\n")); if (s -> msn) free (s -> msn); if (s -> device) free (s -> device); free (s); s = NULL; } } return s; } void hyla_capi_disconnect(void *sp) { serial *s = (serial *) sp; time_t t; if (!s || !s -> connected) return; (void)capiconn_disconnect(capi_conn, 0); t = time(NULL) + 3; /* 3 seconds */ do { handle_messages(); } while ((time(NULL) < t) && (s -> connected)); } void *hyla_capi_close(void *sp) { serial *s = (serial *) sp; MCHK (s); if (s) { hyla_capi_disconnect(sp); (void)capi20_release(applid); (void)capiconn_freecontext(ctx); if (s -> device) free (s -> device); if (s -> msn) free (s -> msn); if (s -> sep) free (s -> sep); if (s -> line) sfree (s -> line); free (s); capi_serial = NULL; capi_conn = NULL; } return(NULL); } int hyla_capi_connect(void *sp, char *dial, char *number) { serial *s = (serial *) sp; struct capi_connection *cp; time_t t; char num[128]; conn_in_progress = 1; if (!dial) dial = ""; sprintf(num, "%s%s", dial, number); /* X.75 connect */ cp = capiconn_connect(ctx, s -> controller, 2, /* cip */ num, s -> msn, 0, 0, 0, 0, 0, 0, 0, 0); capi_conn = cp; t = time(NULL) + 10; /* 10 seconds */ do { handle_messages(); } while ((time(NULL) < t) && (conn_in_progress)); if ((!s -> connected) && (capi_conn)) (void)capiconn_disconnect(capi_conn, 0); return(s -> connected); } int hyla_capi_send(void *sp, char *str, int len) { serial *s = (serial *) sp; time_t t; if (!s || !s -> connected) return(0); capi_data_sent = 0; capiconn_send(capi_conn, str, len); t = time(NULL) + 5; /* 5 seconds */ do { handle_messages(); } while ((time(NULL) < t) && (!capi_data_sent)); return(capi_data_sent); } size_t hyla_capi_read(void *sp, void *buf, size_t count) { serial *s = (serial *) sp; size_t ret=0; if (!capi_data_len) handle_messages(); if (!s || !s -> connected) return(-1); ret = (count < capi_data_len) ? count : capi_data_len; memcpy(buf, capi_data, ret); capi_data_len -= ret; memmove(capi_data, capi_data+ret, capi_data_len); return(ret); } void hyla_capi_iflush(void *sp) { capi_data_len = 0; } void hyla_capi_oflush(void *sp) { /* not necessary */ } void hyla_capi_drain(void *sp) { /* not necessary */ } void hyla_capi_sendbreak(void *sp, int duration) { /* ... */ } static int data_ready (serial *s, int fd, int *msec) { int ret = 0; struct timeval tv; fd_set fset; if (s -> is_capi) { if (capi_data_len) return(1); if (!s -> connected) return(0); } FD_ZERO (& fset); FD_SET (fd, & fset); tv.tv_sec = *msec / 1000; tv.tv_usec = (*msec % 1000) * 1000; if (((ret = select (fd + 1, & fset, NULL, NULL, & tv)) > 0) && FD_ISSET (fd, & fset)) { *msec = tv.tv_sec * 1000 + (tv.tv_usec / 1000); return 1; } return ret; }