/* * modem.c * * Written by Archie Cobbs * Copyright (c) 1995-1999 Whistle Communications, Inc. All rights reserved. * See ``COPYRIGHT.whistle'' */ #include "ppp.h" #include #include "chat.h" #include "phys.h" #include "modem.h" #include "ngfunc.h" #include "lcp.h" #include "event.h" #include "util.h" #include "log.h" #include #include #include #include #include /* * DEFINITIONS */ #ifndef NETGRAPHDISC #define NETGRAPHDISC 7 /* XXX */ #endif #define MODEM_MTU 1600 #define MODEM_MRU 1600 #define MODEM_MIN_CLOSE_TIME 3 #define MODEM_CHECK_INTERVAL 1 #define MODEM_DEFAULT_SPEED 115200 #define MODEM_ERR_REPORT_INTERVAL 60 #define MODEM_IDLE_RESULT_ANSWER "answer" #define MODEM_IDLE_RESULT_RINGBACK "ringback" /* Special chat script variables we set/use */ #define CHAT_VAR_LOGIN "$Login" #define CHAT_VAR_PASSWORD "$Password" #define CHAT_VAR_DEVICE "$modemDevice" #define CHAT_VAR_IDLE_RESULT "$IdleResult" #define CHAT_VAR_CONNECT_SPEED "$ConnectionSpeed" #define CHAT_VAR_CALLING "$CallingID" #define CHAT_VAR_CALLED "$CalledID" /* Nominal link parameters */ #define MODEM_DEFAULT_BANDWIDTH 28800 /* ~33.6 modem */ #define MODEM_DEFAULT_LATENCY 10000 /* 10ms */ /* Modem device state */ struct modeminfo { int fd; /* Device file desc, or -1 */ int csock; /* netgraph control socket */ int speed; /* Port speed */ u_int watch; /* Signals to watch */ char device[20]; /* Serial device name */ char ttynode[NG_NODESIZ]; /* TTY node name */ char connScript[CHAT_MAX_LABEL]; /* Connect script */ char idleScript[CHAT_MAX_LABEL]; /* Idle script */ struct pppTimer checkTimer; /* Timer to check pins */ struct pppTimer reportTimer; /* Timer to report errs */ struct pppTimer startTimer; /* Timer for ModemStart() */ struct optinfo options; /* Binary options */ struct ng_async_cfg acfg; /* ng_async node config */ ChatInfo chat; /* Chat script state */ time_t lastClosed; /* Last time device closed */ u_char opened:1; /* We have been opened */ u_char originated:1; /* We originated current call */ u_char answering:1; /* $IdleResult was "answer" */ }; typedef struct modeminfo *ModemInfo; /* Set menu options */ enum { SET_DEVICE, SET_SPEED, SET_CSCRIPT, SET_ISCRIPT, SET_SCRIPT_VAR, SET_WATCH }; /* * INTERNAL FUNCTIONS */ static int ModemInit(Link l); static void ModemOpen(Link l); static void ModemClose(Link l); static void ModemUpdate(Link l); static int ModemSetAccm(Link l, u_int32_t xmit, u_int32_t recv); static void ModemStat(Context ctx); static int ModemOriginated(Link l); static int ModemIsSync(Link l); static int ModemSelfAddr(Link l, void *buf, size_t buf_len); static int ModemIface(Link l, void *buf, size_t buf_len); static int ModemCallingNum(Link l, void *buf, size_t buf_len); static int ModemCalledNum(Link l, void *buf, size_t buf_len); static void ModemStart(void *arg); static void ModemDoClose(Link l, int opened); /* Chat callbacks */ static int ModemChatSetBaudrate(void *arg, int baud); static void ModemChatConnectResult(void *arg, int rslt, const char *msg); static void ModemChatIdleResult(void *arg, int rslt, const char *msg); static int ModemSetCommand(Context ctx, int ac, char *av[], void *arg); static int ModemInstallNodes(Link l); static int ModemGetNgStats(Link l, struct ng_async_stat *sp); static void ModemCheck(void *arg); static void ModemErrorCheck(void *arg); /* * GLOBAL VARIABLES */ const struct phystype gModemPhysType = { .name = "modem", .descr = "Serial port modem", .mtu = MODEM_MTU, .mru = MODEM_MRU, .tmpl = 0, .init = ModemInit, .open = ModemOpen, .close = ModemClose, .update = ModemUpdate, .showstat = ModemStat, .originate = ModemOriginated, .issync = ModemIsSync, .setaccm = ModemSetAccm, .selfaddr = ModemSelfAddr, .peeraddr = ModemSelfAddr, .peeriface = ModemIface, .callingnum = ModemCallingNum, .callednum = ModemCalledNum, }; const struct cmdtab ModemSetCmds[] = { { "device {name}", "Set modem device", ModemSetCommand, NULL, 2, (void *) SET_DEVICE }, { "speed {port-speed}", "Set modem speed", ModemSetCommand, NULL, 2, (void *) SET_SPEED }, { "script [{label}]", "Set connect script", ModemSetCommand, NULL, 2, (void *) SET_CSCRIPT }, { "idle-script [{label}]", "Set idle script", ModemSetCommand, NULL, 2, (void *) SET_ISCRIPT }, { "var ${var} {string}", "Set script variable", ModemSetCommand, NULL, 2, (void *) SET_SCRIPT_VAR }, { "watch [+|-cd] [+|-dsr]", "Set signals to monitor", ModemSetCommand, NULL, 2, (void *) SET_WATCH }, { NULL }, }; /* * INTERNAL VARIABLES */ static int gSpeedList[] = { 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 7200, 14400, 28800, 57600, 76800, 115200, 230400, 460800, 614400, 921600, 1228800, 1843200, 2000000, 3000000, -1 }; /* * ModemInit() * * Allocate and initialize device private info */ static int ModemInit(Link l) { char defSpeed[32]; ModemInfo m; m = (ModemInfo) (l->info = Malloc(MB_PHYS, sizeof(*m))); m->watch = TIOCM_CAR; m->chat = ChatInit(l, ModemChatSetBaudrate); m->fd = -1; m->opened = FALSE; /* Set nominal link speed and bandwith for a modem connection */ l->latency = MODEM_DEFAULT_LATENCY; l->bandwidth = MODEM_DEFAULT_BANDWIDTH; /* Set default speed */ m->speed = MODEM_DEFAULT_SPEED; snprintf(defSpeed, sizeof(defSpeed), "%d", m->speed); ChatPresetVar(m->chat, CHAT_VAR_BAUDRATE, defSpeed); return(0); } /* * ModemOpen() */ static void ModemOpen(Link l) { ModemInfo const m = (ModemInfo) l->info; assert(!m->opened); m->opened = TRUE; if (m->fd >= 0) { /* Device is already open.. */ if (m->answering) { /* We just answered a call */ m->originated = FALSE; m->answering = FALSE; ModemChatConnectResult(l, TRUE, NULL); } else { Log(LG_PHYS2, ("[%s] MODEM: Stop idle script then dial back", l->name)); ModemDoClose(l, TRUE); /* Stop idle script then dial back */ } } else ModemStart(l); /* Open device and try to dial */ } /* * ModemStart() */ static void ModemStart(void *arg) { Link const l = (Link) arg; ModemInfo const m = (ModemInfo) l->info; const time_t now = time(NULL); char password[AUTH_MAX_PASSWORD]; FILE *scriptfp; /* If we're idle, and there's no idle script, there's nothing to do */ assert(!m->answering); TimerStop(&m->startTimer); if (!m->opened && (!*m->idleScript || !Enabled(&l->conf.options, LINK_CONF_INCOMING) || gShutdownInProgress)) return; /* Avoid brief hang from kernel enforcing minimum DTR hold time */ if (now - m->lastClosed < MODEM_MIN_CLOSE_TIME) { TimerInit(&m->startTimer, "ModemStart", (MODEM_MIN_CLOSE_TIME - (now - m->lastClosed)) * SECONDS, ModemStart, l); TimerStart(&m->startTimer); return; } /* Open and configure serial port */ if ((m->fd = OpenSerialDevice(l->name, m->device, m->speed)) < 0) { Log(LG_ERR, ("[%s] MODEM: Fail to open serial port %s on speed %d", l->name, m->device, m->speed)); goto fail; } /* If connecting, but no connect script, then skip chat altogether */ if (m->opened && !*m->connScript) { Log(LG_PHYS2, ("[%s] MODEM: No connect script present", l->name)); ModemChatConnectResult(l, TRUE, NULL); return; } /* Open chat script file */ if ((scriptfp = OpenConfFile(SCRIPT_FILE, NULL)) == NULL) { Log(LG_ERR, ("[%s] MODEM: can't open chat script file", l->name)); ExclusiveCloseDevice(l->name, m->fd, m->device); m->fd = -1; fail: m->opened = FALSE; m->lastClosed = time(NULL); l->state = PHYS_STATE_DOWN; PhysDown(l, STR_ERROR, STR_DEV_NOT_READY); return; } /* Preset some special chat variables */ ChatPresetVar(m->chat, CHAT_VAR_DEVICE, m->device); ChatPresetVar(m->chat, CHAT_VAR_LOGIN, l->lcp.auth.conf.authname); if (l->lcp.auth.conf.password[0] != 0) { ChatPresetVar(m->chat, CHAT_VAR_PASSWORD, l->lcp.auth.conf.password); } else if (AuthGetData(l->lcp.auth.conf.authname, password, sizeof(password), NULL, NULL) >= 0) { ChatPresetVar(m->chat, CHAT_VAR_PASSWORD, password); } /* Run connect or idle script as appropriate */ if (!m->opened) { ChatPresetVar(m->chat, CHAT_VAR_IDLE_RESULT, ""); ChatStart(m->chat, m->fd, scriptfp, m->idleScript, ModemChatIdleResult); } else { m->originated = TRUE; l->state = PHYS_STATE_CONNECTING; ChatStart(m->chat, m->fd, scriptfp, m->connScript, ModemChatConnectResult); } } /* * ModemClose() */ static void ModemClose(Link l) { ModemInfo const m = (ModemInfo) l->info; if (!m->opened) return; ModemDoClose(l, FALSE); l->state = PHYS_STATE_DOWN; PhysDown(l, STR_MANUALLY, NULL); } static void ModemUpdate(Link l) { ModemInfo const m = (ModemInfo) l->info; if (m->opened || TimerRemain(&m->startTimer) >= 0) return; /* nothing needs to be done right now */ if (m->fd >= 0 && (!*m->idleScript || !Enabled(&l->conf.options, LINK_CONF_INCOMING))) ModemDoClose(l, FALSE); else if (m->fd < 0 && (*m->idleScript && Enabled(&l->conf.options, LINK_CONF_INCOMING))) ModemStart(l); } /* * ModemDoClose() */ static void ModemDoClose(Link l, int opened) { ModemInfo const m = (ModemInfo) l->info; const char ch = ' '; /* Shutdown everything */ assert(m->fd >= 0); ChatAbort(m->chat); TimerStop(&m->checkTimer); TimerStop(&m->startTimer); TimerStop(&m->reportTimer); if (*m->ttynode != '\0') { char path[NG_PATHSIZ]; snprintf(path, sizeof(path), "%s:%s", m->ttynode, NG_TTY_HOOK); NgFuncShutdownNode(m->csock, l->name, path); snprintf(path, sizeof(path), "%s:", m->ttynode); NgFuncShutdownNode(m->csock, l->name, path); *m->ttynode = '\0'; } (void) write(m->fd, &ch, 1); /* USR kludge to prevent dial lockup */ if (m->csock > 0) { close(m->csock); m->csock = -1; } ExclusiveCloseDevice(l->name, m->fd, m->device); m->lastClosed = time(NULL); m->answering = FALSE; m->fd = -1; m->opened = opened; ModemStart(l); } /* * ModemSetAccm() */ static int ModemSetAccm(Link l, u_int32_t xmit, u_int32_t recv) { ModemInfo const m = (ModemInfo) l->info; char path[NG_PATHSIZ]; /* Update async config */ m->acfg.accm = xmit|recv; snprintf(path, sizeof(path), "%s:%s", m->ttynode, NG_TTY_HOOK); if (NgSendMsg(m->csock, path, NGM_ASYNC_COOKIE, NGM_ASYNC_CMD_SET_CONFIG, &m->acfg, sizeof(m->acfg)) < 0) { Perror("[%s] MODEM: can't update config for %s", l->name, path); return (-1); } return (0); } /* * ModemChatConnectResult() * * Connect chat script returns here when finished. */ static void ModemChatConnectResult(void *arg, int result, const char *msg) { Link const l = (Link) arg; ModemInfo const m = (ModemInfo) l->info; char *cspeed; int bw; /* Was the connect script successful? */ Log(LG_PHYS, ("[%s] MODEM: chat script %s", l->name, result ? "succeeded" : "failed")); if (!result) { failed: ModemDoClose(l, FALSE); l->state = PHYS_STATE_DOWN; PhysDown(l, STR_ERROR, msg); return; } /* Set modem's reported connection speed (if any) as the link bandwidth */ if ((cspeed = ChatGetVar(m->chat, CHAT_VAR_CONNECT_SPEED)) != NULL) { if ((bw = (int) strtoul(cspeed, NULL, 10)) > 0) l->bandwidth = bw; Freee(cspeed); } /* Do async <-> sync conversion via netgraph node */ if (ModemInstallNodes(l) < 0) { msg = STR_DEV_NOT_READY; goto failed; } /* Start pin check and report timers */ TimerInit(&m->checkTimer, "ModemCheck", MODEM_CHECK_INTERVAL * SECONDS, ModemCheck, l); TimerStart(&m->checkTimer); TimerStop(&m->reportTimer); TimerInit(&m->reportTimer, "ModemReport", MODEM_ERR_REPORT_INTERVAL * SECONDS, ModemErrorCheck, l); TimerStart(&m->reportTimer); l->state = PHYS_STATE_UP; PhysUp(l); } /* * ModemChatIdleResult() * * Idle chat script returns here when finished. If the script returned * successfully, then one of two things happened: either we answered * an incoming call, or else we got a ring and want to do ringback. * We tell the difference by checking $IdleResult. */ static void ModemChatIdleResult(void *arg, int result, const char *msg) { Link const l = (Link) arg; ModemInfo const m = (ModemInfo) l->info; char *idleResult; /* If script failed, then do nothing */ if (!result) { ModemDoClose(l, FALSE); return; } /* See what script wants us to do now by checking variable $IdleResult */ if ((idleResult = ChatGetVar(m->chat, CHAT_VAR_IDLE_RESULT)) == NULL) { Log(LG_ERR, ("[%s] MODEM: idle script succeeded, but %s not defined", l->name, CHAT_VAR_IDLE_RESULT)); ModemDoClose(l, FALSE); return; } /* Do whatever */ Log(LG_PHYS, ("[%s] MODEM: idle script succeeded, action=%s", l->name, idleResult)); if (gShutdownInProgress) { Log(LG_PHYS, ("Shutdown sequence in progress, ignoring")); ModemDoClose(l, FALSE); } else if (strcasecmp(idleResult, MODEM_IDLE_RESULT_ANSWER) == 0) { Log(LG_PHYS, ("[%s] MODEM: opening link in %s mode", l->name, "answer")); RecordLinkUpDownReason(NULL, l, 1, STR_INCOMING_CALL, msg ? "%s" : NULL, msg); m->answering = TRUE; l->state = PHYS_STATE_READY; PhysIncoming(l); } else if (strcasecmp(idleResult, MODEM_IDLE_RESULT_RINGBACK) == 0) { Log(LG_PHYS, ("[%s] MODEM: opening link in %s mode", l->name, "ringback")); RecordLinkUpDownReason(NULL, l, 1, STR_RINGBACK, msg ? "%s" : NULL, msg); m->answering = FALSE; PhysIncoming(l); } else { Log(LG_ERR, ("[%s] MODEM: idle script succeeded, but action \"%s\" unknown", l->name, idleResult)); ModemDoClose(l, FALSE); } Freee(idleResult); } /* * ModemInstallNodes() */ static int ModemInstallNodes(Link l) { ModemInfo m = (ModemInfo) l->info; struct ngm_mkpeer ngm; struct ngm_connect cn; char path[NG_PATHSIZ]; int hotchar = PPP_FLAG; #if NGM_TTY_COOKIE < 1226109660 struct nodeinfo ngtty; int ldisc = NETGRAPHDISC; #else struct ngm_rmhook rm; union { u_char buf[sizeof(struct ng_mesg) + sizeof(struct nodeinfo)]; struct ng_mesg reply; } repbuf; struct ng_mesg *const reply = &repbuf.reply; struct nodeinfo *ninfo = (struct nodeinfo *)&reply->data; int tty[2]; #endif /* Get a temporary netgraph socket node */ if (NgMkSockNode(NULL, &m->csock, NULL) < 0) { Perror("[%s] MODEM: NgMkSockNode failed", l->name); return(-1); } #if NGM_TTY_COOKIE < 1226109660 /* Install ng_tty line discipline */ if (ioctl(m->fd, TIOCSETD, &ldisc) < 0) { /* Installation of the tty node type should be automatic, but isn't yet. The 'mkpeer' below will fail, because you can only create a ng_tty node via TIOCSETD; however, this will force a load of the node type. */ if (errno == ENODEV) { (void)NgSendAsciiMsg(m->csock, ".:", "mkpeer { type=\"%s\" ourhook=\"dummy\" peerhook=\"%s\" }", NG_TTY_NODE_TYPE, NG_TTY_HOOK); } if (ioctl(m->fd, TIOCSETD, &ldisc) < 0) { Perror("[%s] ioctl(TIOCSETD, %d)", l->name, ldisc); close(m->csock); return(-1); } } /* Get the name of the ng_tty node */ if (ioctl(m->fd, NGIOCGINFO, &ngtty) < 0) { Perror("[%s] MODEM: ioctl(NGIOCGINFO)", l->name); close(m->csock); return(-1); } strlcpy(m->ttynode, ngtty.name, sizeof(m->ttynode)); #else /* Attach a TTY node */ snprintf(ngm.type, sizeof(ngm.type), "%s", NG_TTY_NODE_TYPE); snprintf(ngm.ourhook, sizeof(ngm.ourhook), "%s", NG_TTY_HOOK); snprintf(ngm.peerhook, sizeof(ngm.peerhook), "%s", NG_TTY_HOOK); if (NgSendMsg(m->csock, ".", NGM_GENERIC_COOKIE, NGM_MKPEER, &ngm, sizeof(ngm)) < 0) { Perror("[%s] MODEM: can't connect %s node on %s", l->name, NG_TTY_NODE_TYPE, "."); close(m->csock); return(-1); } snprintf(path, sizeof(path), ".:%s", NG_TTY_HOOK); if (NgSendMsg(m->csock, path, NGM_GENERIC_COOKIE, NGM_NODEINFO, NULL, 0) != -1) { if (NgRecvMsg(m->csock, reply, sizeof(repbuf), NULL) < 0) { Perror("[%s] MODEM: can't locate %s node on %s (%d)", l->name, NG_TTY_NODE_TYPE, path, errno); close(m->csock); return(-1); } } snprintf(m->ttynode, sizeof(m->ttynode), "[%x]", ninfo->id); /* Attach to the TTY */ tty[0] = gPid; tty[1] = m->fd; if (NgSendMsg(m->csock, path, NGM_TTY_COOKIE, NGM_TTY_SET_TTY, &tty, sizeof(tty)) < 0) { Perror("[%s] MODEM: can't hook tty to fd %d", l->name, m->fd); close(m->csock); return(-1); } /* Disconnect temporary hook. */ snprintf(rm.ourhook, sizeof(rm.ourhook), "%s", NG_TTY_HOOK); if (NgSendMsg(m->csock, ".", NGM_GENERIC_COOKIE, NGM_RMHOOK, &rm, sizeof(rm)) < 0) { Perror("[%s] MODEM: can't remove hook %s", l->name, NG_TTY_HOOK); close(m->csock); return(-1); } #endif /* Set the ``hot char'' on the TTY node */ snprintf(path, sizeof(path), "%s:", m->ttynode); if (NgSendMsg(m->csock, path, NGM_TTY_COOKIE, NGM_TTY_SET_HOTCHAR, &hotchar, sizeof(hotchar)) < 0) { Perror("[%s] MODEM: can't set hotchar", l->name); close(m->csock); return(-1); } /* Attach an async converter node */ strcpy(ngm.type, NG_ASYNC_NODE_TYPE); strcpy(ngm.ourhook, NG_TTY_HOOK); strcpy(ngm.peerhook, NG_ASYNC_HOOK_ASYNC); if (NgSendMsg(m->csock, path, NGM_GENERIC_COOKIE, NGM_MKPEER, &ngm, sizeof(ngm)) < 0) { Perror("[%s] MODEM: can't connect %s node", l->name, NG_ASYNC_NODE_TYPE); close(m->csock); return(-1); } /* Configure the async converter node */ snprintf(path, sizeof(path), "%s:%s", m->ttynode, NG_TTY_HOOK); memset(&m->acfg, 0, sizeof(m->acfg)); m->acfg.enabled = TRUE; m->acfg.accm = ~0; m->acfg.amru = MODEM_MRU; m->acfg.smru = MODEM_MTU; if (NgSendMsg(m->csock, path, NGM_ASYNC_COOKIE, NGM_ASYNC_CMD_SET_CONFIG, &m->acfg, sizeof(m->acfg)) < 0) { Perror("[%s] MODEM: can't config %s", l->name, path); close(m->csock); return(-1); } /* Attach async node to PPP node */ if (!PhysGetUpperHook(l, cn.path, cn.peerhook)) { Log(LG_PHYS, ("[%s] MODEM: can't get upper hook", l->name)); close(m->csock); return (-1); } snprintf(cn.ourhook, sizeof(cn.ourhook), NG_ASYNC_HOOK_SYNC); if (NgSendMsg(m->csock, path, NGM_GENERIC_COOKIE, NGM_CONNECT, &cn, sizeof(cn)) < 0) { Perror("[%s] MODEM: can't connect \"%s\"->\"%s\" and \"%s\"->\"%s\"", l->name, path, cn.ourhook, cn.path, cn.peerhook); close(m->csock); return (-1); } return(0); } /* * ModemChatSetBaudrate() * * This callback changes the actual baudrate of the serial port. * Should only be called once the device is already open. * Returns -1 on failure. */ static int ModemChatSetBaudrate(void *arg, int baud) { Link const l = (Link) arg; ModemInfo const m = (ModemInfo) l->info; struct termios attr; /* Change baud rate */ if (tcgetattr(m->fd, &attr) < 0) { Perror("[%s] MODEM: can't tcgetattr \"%s\"", l->name, m->device); return(-1); } if (cfsetspeed(&attr, (speed_t) baud) < 0) { Perror("[%s] MODEM: can't set speed %d", l->name, baud); return(-1); } if (tcsetattr(m->fd, TCSANOW, &attr) < 0) { Perror("[%s] MODEM: can't tcsetattr \"%s\"", l->name, m->device); return(-1); } return(0); } /* * ModemGetVar() */ char * ModemGetVar(Link l, const char *name) { ModemInfo const m = (ModemInfo) l->info; return ChatGetVar(m->chat, name); } /* * ModemCheck() */ static void ModemCheck(void *arg) { Link const l = (Link)arg; ModemInfo const m = (ModemInfo) l->info; int state; if (ioctl(m->fd, TIOCMGET, &state) < 0) { Perror("[%s] MODEM: can't ioctl(TIOCMGET) %s", l->name, m->device); l->state = PHYS_STATE_DOWN; ModemDoClose(l, FALSE); PhysDown(l, STR_ERROR, strerror(errno)); return; } if ((m->watch & TIOCM_CAR) && !(state & TIOCM_CAR)) { Log(LG_PHYS, ("[%s] MODEM: carrier detect (CD) signal lost", l->name)); l->state = PHYS_STATE_DOWN; ModemDoClose(l, FALSE); PhysDown(l, STR_DROPPED, STR_LOST_CD); return; } if ((m->watch & TIOCM_DSR) && !(state & TIOCM_DSR)) { Log(LG_PHYS, ("[%s] MODEM: data-set ready (DSR) signal lost", l->name)); l->state = PHYS_STATE_DOWN; ModemDoClose(l, FALSE); PhysDown(l, STR_DROPPED, STR_LOST_DSR); return; } TimerStart(&m->checkTimer); } /* * ModemErrorCheck() * * Called every second to record errors to the log */ static void ModemErrorCheck(void *arg) { Link const l = (Link) arg; ModemInfo const m = (ModemInfo) l->info; char path[NG_PATHSIZ]; struct ng_async_stat stats; /* Check for errors */ snprintf(path, sizeof(path), "%s:%s", m->ttynode, NG_TTY_HOOK); if (ModemGetNgStats(l, &stats) >= 0 && (stats.asyncBadCheckSums || stats.asyncRunts || stats.asyncOverflows)) { Log(LG_PHYS, ("[%s] NEW FRAME ERRS: FCS %u RUNT %u OVFL %u", l->name, stats.asyncBadCheckSums, stats.asyncRunts, stats.asyncOverflows)); (void) NgSendMsg(m->csock, path, NGM_ASYNC_COOKIE, NGM_ASYNC_CMD_CLR_STATS, NULL, 0); } /* Restart timer */ TimerStop(&m->reportTimer); TimerStart(&m->reportTimer); } /* * ModemGetNgStats() */ static int ModemGetNgStats(Link l, struct ng_async_stat *sp) { ModemInfo const m = (ModemInfo) l->info; char path[NG_PATHSIZ]; union { u_char buf[sizeof(struct ng_mesg) + sizeof(*sp)]; struct ng_mesg resp; } u; /* Get stats */ snprintf(path, sizeof(path), "%s:%s", m->ttynode, NG_TTY_HOOK); if (NgFuncSendQuery(path, NGM_ASYNC_COOKIE, NGM_ASYNC_CMD_GET_STATS, NULL, 0, &u.resp, sizeof(u), NULL) < 0) { Perror("[%s] MODEM: can't get stats", l->name); return(-1); } memcpy(sp, u.resp.data, sizeof(*sp)); return(0); } /* * ModemSetCommand() */ static int ModemSetCommand(Context ctx, int ac, char *av[], void *arg) { Link const l = ctx->lnk; ModemInfo const m = (ModemInfo) l->info; switch ((intptr_t)arg) { case SET_DEVICE: if (ac == 1) strlcpy(m->device, av[0], sizeof(m->device)); break; case SET_SPEED: { int k, baud; if (ac != 1) return(-1); baud = atoi(*av); for (k = 0; gSpeedList[k] != -1 && baud != gSpeedList[k]; k++); if (gSpeedList[k] == -1) Error("invalid speed \'%s\'", *av); else { char buf[32]; m->speed = baud; snprintf(buf, sizeof(buf), "%d", m->speed); ChatPresetVar(m->chat, CHAT_VAR_BAUDRATE, buf); } } break; case SET_CSCRIPT: if (ac != 1) return(-1); *m->connScript = 0; strlcpy(m->connScript, av[0], sizeof(m->connScript)); break; case SET_ISCRIPT: if (ac != 1) return(-1); *m->idleScript = 0; strlcpy(m->idleScript, av[0], sizeof(m->idleScript)); if (m->opened || TimerRemain(&m->startTimer) >= 0) break; /* nothing needs to be done right now */ if (m->fd >= 0 && !*m->idleScript) ModemDoClose(l, FALSE); else if (m->fd < 0 && *m->idleScript) ModemStart(l); break; case SET_SCRIPT_VAR: if (ac != 2) return(-1); ChatPresetVar(m->chat, av[0], av[1]); break; case SET_WATCH: { int bit, add; while (ac--) { switch (**av) { case '+': (*av)++; default: add = TRUE; break; case '-': add = FALSE; (*av)++; break; } if (!strcasecmp(*av, "cd")) bit = TIOCM_CAR; else if (!strcasecmp(*av, "dsr")) bit = TIOCM_DSR; else { Printf("[%s] modem signal \"%s\" is unknown\r\n", l->name, *av); bit = 0; } if (add) m->watch |= bit; else m->watch &= ~bit; av++; } } break; default: assert(0); } return(0); } /* * ModemOriginated() */ static int ModemOriginated(Link l) { ModemInfo const m = (ModemInfo) l->info; return(m->originated ? LINK_ORIGINATE_LOCAL : LINK_ORIGINATE_REMOTE); } /* * ModemIsSync() */ static int ModemIsSync(Link l) { return (0); } static int ModemSelfAddr(Link l, void *buf, size_t buf_len) { ModemInfo const m = (ModemInfo) l->info; strlcpy(buf, m->ttynode, buf_len); return(0); } static int ModemIface(Link l, void *buf, size_t buf_len) { ModemInfo const m = (ModemInfo) l->info; strlcpy(buf, m->device, buf_len); return(0); } static int ModemCallingNum(Link l, void *buf, size_t buf_len) { ModemInfo const m = (ModemInfo) l->info; char *tmp; if ((tmp = ChatGetVar(m->chat, CHAT_VAR_CALLING)) == NULL) { ((char *)buf)[0] = 0; return (-1); } strlcpy((char*)buf, tmp, buf_len); Freee(tmp); return(0); } static int ModemCalledNum(Link l, void *buf, size_t buf_len) { ModemInfo const m = (ModemInfo) l->info; char *tmp; if ((tmp = ChatGetVar(m->chat, CHAT_VAR_CALLED)) == NULL) { ((char *)buf)[0] = 0; return (-1); } strlcpy((char*)buf, tmp, buf_len); Freee(tmp); return(0); } /* * ModemStat() */ void ModemStat(Context ctx) { ModemInfo const m = (ModemInfo) ctx->lnk->info; struct ng_async_stat stats; char *tmp; Printf("Modem info:\r\n"); Printf("\tDevice : %s\r\n", m->device); Printf("\tPort speed : %d baud\r\n", m->speed); Printf("\tConn. script : \"%s\"\r\n", m->connScript); Printf("\tIdle script : \"%s\"\r\n", m->idleScript); Printf("\tPins to watch: %s%s\r\n", (m->watch & TIOCM_CAR) ? "CD " : "", (m->watch & TIOCM_DSR) ? "DSR" : ""); Printf("Modem status:\r\n"); if (ctx->lnk->state != PHYS_STATE_DOWN) { Printf("\tOpened : %s\r\n", (m->opened?"YES":"NO")); Printf("\tIncoming : %s\r\n", (m->originated?"NO":"YES")); /* Set modem's reported connection speed (if any) as the link bandwidth */ if ((tmp = ChatGetVar(m->chat, CHAT_VAR_CONNECT_SPEED)) != NULL) { Printf("\tConnect speed: %s baud\r\n", tmp); Freee(tmp); } if ((tmp = ChatGetVar(m->chat, CHAT_VAR_CALLING)) != NULL) { Printf("\tCalling : %s\r\n", tmp); Freee(tmp); } if ((tmp = ChatGetVar(m->chat, CHAT_VAR_CALLED)) != NULL) { Printf("\tCalled : %s\r\n", tmp); Freee(tmp); } if (ctx->lnk->state == PHYS_STATE_UP && ModemGetNgStats(ctx->lnk, &stats) >= 0) { Printf("Async stats:\r\n"); Printf("\t syncOctets: %8u\r\n", stats.syncOctets); Printf("\t syncFrames: %8u\r\n", stats.syncFrames); Printf("\t syncOverflows: %8u\r\n", stats.syncOverflows); Printf("\t asyncOctets: %8u\r\n", stats.asyncOctets); Printf("\t asyncFrames: %8u\r\n", stats.asyncFrames); Printf("\t asyncRunts: %8u\r\n", stats.asyncRunts); Printf("\t asyncOverflows: %8u\r\n", stats.asyncOverflows); Printf("\tasyncBadCheckSums: %8u\r\n", stats.asyncBadCheckSums); } } }