Annotation of embedaddon/mpd/src/modem.c, revision 1.1

1.1     ! misho       1: 
        !             2: /*
        !             3:  * modem.c
        !             4:  *
        !             5:  * Written by Archie Cobbs <archie@freebsd.org>
        !             6:  * Copyright (c) 1995-1999 Whistle Communications, Inc. All rights reserved.
        !             7:  * See ``COPYRIGHT.whistle''
        !             8:  */
        !             9: 
        !            10: #include "ppp.h"
        !            11: #include <termios.h>
        !            12: #include "chat.h"
        !            13: #include "phys.h"
        !            14: #include "modem.h"
        !            15: #include "ngfunc.h"
        !            16: #include "lcp.h"
        !            17: #include "event.h"
        !            18: #include "util.h"
        !            19: #include "log.h"
        !            20: 
        !            21: #include <netgraph/ng_message.h>
        !            22: #include <netgraph/ng_socket.h>
        !            23: #include <netgraph/ng_async.h>
        !            24: #include <netgraph/ng_tty.h>
        !            25: #include <netgraph.h>
        !            26: 
        !            27: /*
        !            28:  * DEFINITIONS
        !            29:  */
        !            30: 
        !            31: #ifndef NETGRAPHDISC
        !            32:   #define NETGRAPHDISC                 7       /* XXX */
        !            33: #endif
        !            34: 
        !            35:   #define MODEM_MTU                    1600
        !            36:   #define MODEM_MRU                    1600
        !            37: 
        !            38:   #define MODEM_MIN_CLOSE_TIME         3
        !            39:   #define MODEM_CHECK_INTERVAL         1
        !            40:   #define MODEM_DEFAULT_SPEED          115200
        !            41:   #define MODEM_ERR_REPORT_INTERVAL    60
        !            42: 
        !            43:   #define MODEM_IDLE_RESULT_ANSWER     "answer"
        !            44:   #define MODEM_IDLE_RESULT_RINGBACK   "ringback"
        !            45: 
        !            46:   /* Special chat script variables we set/use */
        !            47:   #define CHAT_VAR_LOGIN               "$Login"
        !            48:   #define CHAT_VAR_PASSWORD            "$Password"
        !            49:   #define CHAT_VAR_DEVICE              "$modemDevice"
        !            50:   #define CHAT_VAR_IDLE_RESULT         "$IdleResult"
        !            51:   #define CHAT_VAR_CONNECT_SPEED       "$ConnectionSpeed"
        !            52:   #define CHAT_VAR_CALLING             "$CallingID"
        !            53:   #define CHAT_VAR_CALLED              "$CalledID"
        !            54: 
        !            55:   /* Nominal link parameters */
        !            56:   #define MODEM_DEFAULT_BANDWIDTH      28800   /* ~33.6 modem */
        !            57:   #define MODEM_DEFAULT_LATENCY                10000   /* 10ms */
        !            58: 
        !            59:   /* Modem device state */
        !            60:   struct modeminfo {
        !            61:     int                        fd;                     /* Device file desc, or -1 */
        !            62:     int                        csock;                  /* netgraph control socket */
        !            63:     int                        speed;                  /* Port speed */
        !            64:     u_int              watch;                  /* Signals to watch */
        !            65:     char               device[20];             /* Serial device name */
        !            66:     char               ttynode[NG_NODESIZ];    /* TTY node name */
        !            67:     char               connScript[CHAT_MAX_LABEL];     /* Connect script */
        !            68:     char               idleScript[CHAT_MAX_LABEL];     /* Idle script */
        !            69:     struct pppTimer    checkTimer;             /* Timer to check pins */
        !            70:     struct pppTimer    reportTimer;            /* Timer to report errs */
        !            71:     struct pppTimer    startTimer;             /* Timer for ModemStart() */
        !            72:     struct optinfo     options;                /* Binary options */
        !            73:     struct ng_async_cfg        acfg;                   /* ng_async node config */
        !            74:     ChatInfo           chat;                   /* Chat script state */
        !            75:     time_t             lastClosed;             /* Last time device closed */
        !            76:     u_char             opened:1;               /* We have been opened */
        !            77:     u_char             originated:1;           /* We originated current call */
        !            78:     u_char             answering:1;            /* $IdleResult was "answer" */
        !            79:   };
        !            80:   typedef struct modeminfo     *ModemInfo;
        !            81: 
        !            82:   /* Set menu options */
        !            83:   enum {
        !            84:     SET_DEVICE,
        !            85:     SET_SPEED,
        !            86:     SET_CSCRIPT,
        !            87:     SET_ISCRIPT,
        !            88:     SET_SCRIPT_VAR,
        !            89:     SET_WATCH
        !            90:   };
        !            91: 
        !            92: /*
        !            93:  * INTERNAL FUNCTIONS
        !            94:  */
        !            95: 
        !            96:   static int           ModemInit(Link l);
        !            97:   static void          ModemOpen(Link l);
        !            98:   static void          ModemClose(Link l);
        !            99:   static void          ModemUpdate(Link l);
        !           100:   static int           ModemSetAccm(Link l, u_int32_t xmit, u_int32_t recv);
        !           101:   static void          ModemStat(Context ctx);
        !           102:   static int           ModemOriginated(Link l);
        !           103:   static int           ModemIsSync(Link l);
        !           104:   static int           ModemSelfAddr(Link l, void *buf, size_t buf_len);
        !           105:   static int           ModemIface(Link l, void *buf, size_t buf_len);
        !           106:   static int           ModemCallingNum(Link l, void *buf, size_t buf_len);
        !           107:   static int           ModemCalledNum(Link l, void *buf, size_t buf_len);
        !           108: 
        !           109:   static void          ModemStart(void *arg);
        !           110:   static void          ModemDoClose(Link l, int opened);
        !           111: 
        !           112:   /* Chat callbacks */
        !           113:   static int           ModemChatSetBaudrate(void *arg, int baud);
        !           114:   static void          ModemChatConnectResult(void *arg,
        !           115:                                int rslt, const char *msg);
        !           116:   static void          ModemChatIdleResult(void *arg, int rslt,
        !           117:                                const char *msg);
        !           118: 
        !           119:   static int           ModemSetCommand(Context ctx, int ac, char *av[], void *arg);
        !           120:   static int           ModemInstallNodes(Link l);
        !           121:   static int           ModemGetNgStats(Link l, struct ng_async_stat *sp);
        !           122: 
        !           123:   static void          ModemCheck(void *arg);
        !           124:   static void          ModemErrorCheck(void *arg);
        !           125: 
        !           126: /*
        !           127:  * GLOBAL VARIABLES
        !           128:  */
        !           129: 
        !           130:   const struct phystype gModemPhysType = {
        !           131:     .name              = "modem",
        !           132:     .descr             = "Serial port modem",
        !           133:     .mtu               = MODEM_MTU,
        !           134:     .mru               = MODEM_MRU,
        !           135:     .tmpl              = 0,
        !           136:     .init              = ModemInit,
        !           137:     .open              = ModemOpen,
        !           138:     .close             = ModemClose,
        !           139:     .update            = ModemUpdate,
        !           140:     .showstat          = ModemStat,
        !           141:     .originate         = ModemOriginated,
        !           142:     .issync            = ModemIsSync,
        !           143:     .setaccm           = ModemSetAccm,
        !           144:     .selfaddr          = ModemSelfAddr,
        !           145:     .peeraddr          = ModemSelfAddr,
        !           146:     .peeriface         = ModemIface,
        !           147:     .callingnum                = ModemCallingNum,
        !           148:     .callednum         = ModemCalledNum,
        !           149:   };
        !           150: 
        !           151:   const struct cmdtab ModemSetCmds[] = {
        !           152:     { "device {name}",                 "Set modem device",
        !           153:       ModemSetCommand, NULL, 2, (void *) SET_DEVICE },
        !           154:     { "speed {port-speed}",            "Set modem speed",
        !           155:       ModemSetCommand, NULL, 2, (void *) SET_SPEED },
        !           156:     { "script [{label}]",              "Set connect script",
        !           157:       ModemSetCommand, NULL, 2, (void *) SET_CSCRIPT },
        !           158:     { "idle-script [{label}]",         "Set idle script",
        !           159:       ModemSetCommand, NULL, 2, (void *) SET_ISCRIPT },
        !           160:     { "var ${var} {string}",           "Set script variable",
        !           161:       ModemSetCommand, NULL, 2, (void *) SET_SCRIPT_VAR },
        !           162:     { "watch [+|-cd] [+|-dsr]",        "Set signals to monitor",
        !           163:       ModemSetCommand, NULL, 2, (void *) SET_WATCH },
        !           164:     { NULL },
        !           165:   };
        !           166: 
        !           167: /*
        !           168:  * INTERNAL VARIABLES
        !           169:  */
        !           170: 
        !           171:   static int   gSpeedList[] = {
        !           172:     50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 
        !           173:     38400, 7200, 14400, 28800, 57600, 76800, 115200, 230400, 460800, 614400,
        !           174:     921600, 1228800, 1843200, 2000000, 3000000, -1
        !           175:   };
        !           176: 
        !           177: /*
        !           178:  * ModemInit()
        !           179:  *
        !           180:  * Allocate and initialize device private info
        !           181:  */
        !           182: 
        !           183: static int
        !           184: ModemInit(Link l)
        !           185: {
        !           186:     char       defSpeed[32];
        !           187:     ModemInfo  m;
        !           188: 
        !           189:     m = (ModemInfo) (l->info = Malloc(MB_PHYS, sizeof(*m)));
        !           190:     m->watch = TIOCM_CAR;
        !           191:     m->chat = ChatInit(l, ModemChatSetBaudrate);
        !           192:     m->fd = -1;
        !           193:     m->opened = FALSE;
        !           194: 
        !           195:     /* Set nominal link speed and bandwith for a modem connection */
        !           196:     l->latency = MODEM_DEFAULT_LATENCY;
        !           197:     l->bandwidth = MODEM_DEFAULT_BANDWIDTH;
        !           198: 
        !           199:     /* Set default speed */
        !           200:     m->speed = MODEM_DEFAULT_SPEED;
        !           201:     snprintf(defSpeed, sizeof(defSpeed), "%d", m->speed);
        !           202:     ChatPresetVar(m->chat, CHAT_VAR_BAUDRATE, defSpeed);
        !           203:     return(0);
        !           204: }
        !           205: 
        !           206: /*
        !           207:  * ModemOpen()
        !           208:  */
        !           209: 
        !           210: static void
        !           211: ModemOpen(Link l)
        !           212: {
        !           213:     ModemInfo  const m = (ModemInfo) l->info;
        !           214: 
        !           215:     assert(!m->opened);
        !           216:     m->opened = TRUE;
        !           217:     if (m->fd >= 0) {                  /* Device is already open.. */
        !           218:        if (m->answering) {                     /* We just answered a call */
        !           219:            m->originated = FALSE;
        !           220:            m->answering = FALSE;
        !           221:            ModemChatConnectResult(l, TRUE, NULL);
        !           222:        } else {
        !           223:            Log(LG_PHYS2, ("[%s] MODEM: Stop idle script then dial back",
        !           224:              l->name));
        !           225:            ModemDoClose(l, TRUE);              /* Stop idle script then dial back */
        !           226:        }
        !           227:     } else
        !           228:        ModemStart(l);                  /* Open device and try to dial */
        !           229: }
        !           230: 
        !           231: /*
        !           232:  * ModemStart()
        !           233:  */
        !           234: 
        !           235: static void
        !           236: ModemStart(void *arg)
        !           237: {
        !           238:     Link               const l = (Link) arg;
        !           239:     ModemInfo          const m = (ModemInfo) l->info;
        !           240:     const time_t       now = time(NULL);
        !           241:     char               password[AUTH_MAX_PASSWORD];
        !           242:     FILE               *scriptfp;
        !           243: 
        !           244:     /* If we're idle, and there's no idle script, there's nothing to do */
        !           245:     assert(!m->answering);
        !           246:     TimerStop(&m->startTimer);
        !           247:     if (!m->opened &&
        !           248:       (!*m->idleScript || !Enabled(&l->conf.options, LINK_CONF_INCOMING) ||
        !           249:        gShutdownInProgress))
        !           250:        return;
        !           251: 
        !           252:     /* Avoid brief hang from kernel enforcing minimum DTR hold time */
        !           253:     if (now - m->lastClosed < MODEM_MIN_CLOSE_TIME) {
        !           254:        TimerInit(&m->startTimer, "ModemStart",
        !           255:          (MODEM_MIN_CLOSE_TIME - (now - m->lastClosed)) * SECONDS, ModemStart, l);
        !           256:        TimerStart(&m->startTimer);
        !           257:        return;
        !           258:     }
        !           259: 
        !           260:     /* Open and configure serial port */
        !           261:     if ((m->fd = OpenSerialDevice(l->name, m->device, m->speed)) < 0) {
        !           262:        Log(LG_ERR, ("[%s] MODEM: Fail to open serial port %s on speed %d",
        !           263:          l->name, m->device, m->speed));
        !           264:        goto fail;
        !           265:     }
        !           266:     /* If connecting, but no connect script, then skip chat altogether */
        !           267:     if (m->opened && !*m->connScript) {
        !           268:        Log(LG_PHYS2, ("[%s] MODEM: No connect script present", l->name));
        !           269:        ModemChatConnectResult(l, TRUE, NULL);
        !           270:        return;
        !           271:     }
        !           272: 
        !           273:     /* Open chat script file */
        !           274:     if ((scriptfp = OpenConfFile(SCRIPT_FILE, NULL)) == NULL) {
        !           275:        Log(LG_ERR, ("[%s] MODEM: can't open chat script file", l->name));
        !           276:        ExclusiveCloseDevice(l->name, m->fd, m->device);
        !           277:        m->fd = -1;
        !           278: fail:
        !           279:        m->opened = FALSE;
        !           280:        m->lastClosed = time(NULL);
        !           281:        l->state = PHYS_STATE_DOWN;
        !           282:        PhysDown(l, STR_ERROR, STR_DEV_NOT_READY);
        !           283:        return;
        !           284:     }
        !           285: 
        !           286:     /* Preset some special chat variables */
        !           287:     ChatPresetVar(m->chat, CHAT_VAR_DEVICE, m->device);
        !           288:     ChatPresetVar(m->chat, CHAT_VAR_LOGIN, l->lcp.auth.conf.authname);
        !           289:     if (l->lcp.auth.conf.password[0] != 0) {
        !           290:        ChatPresetVar(m->chat, CHAT_VAR_PASSWORD, l->lcp.auth.conf.password);
        !           291:     } else if (AuthGetData(l->lcp.auth.conf.authname,
        !           292:        password, sizeof(password), NULL, NULL) >= 0) {
        !           293:            ChatPresetVar(m->chat, CHAT_VAR_PASSWORD, password);
        !           294:     }
        !           295: 
        !           296:     /* Run connect or idle script as appropriate */
        !           297:     if (!m->opened) {
        !           298:        ChatPresetVar(m->chat, CHAT_VAR_IDLE_RESULT, "<unknown>");
        !           299:        ChatStart(m->chat, m->fd, scriptfp, m->idleScript, ModemChatIdleResult);
        !           300:     } else {
        !           301:        m->originated = TRUE;
        !           302:        l->state = PHYS_STATE_CONNECTING;
        !           303:        ChatStart(m->chat, m->fd, scriptfp, m->connScript, ModemChatConnectResult);
        !           304:     }
        !           305: }
        !           306: 
        !           307: /*
        !           308:  * ModemClose()
        !           309:  */
        !           310: 
        !           311: static void
        !           312: ModemClose(Link l)
        !           313: {
        !           314:     ModemInfo  const m = (ModemInfo) l->info;
        !           315: 
        !           316:     if (!m->opened)
        !           317:        return;
        !           318:     ModemDoClose(l, FALSE);
        !           319:     l->state = PHYS_STATE_DOWN;
        !           320:     PhysDown(l, STR_MANUALLY, NULL);
        !           321: }
        !           322: 
        !           323: static void
        !           324: ModemUpdate(Link l)
        !           325: {
        !           326:     ModemInfo  const m = (ModemInfo) l->info;
        !           327: 
        !           328:     if (m->opened || TimerRemain(&m->startTimer) >= 0)
        !           329:        return;         /* nothing needs to be done right now */
        !           330:     if (m->fd >= 0 &&
        !           331:       (!*m->idleScript || !Enabled(&l->conf.options, LINK_CONF_INCOMING)))
        !           332:        ModemDoClose(l, FALSE);
        !           333:     else if (m->fd < 0 &&
        !           334:       (*m->idleScript && Enabled(&l->conf.options, LINK_CONF_INCOMING)))
        !           335:        ModemStart(l);
        !           336: }
        !           337: 
        !           338: /*
        !           339:  * ModemDoClose()
        !           340:  */
        !           341: 
        !           342: static void
        !           343: ModemDoClose(Link l, int opened)
        !           344: {
        !           345:     ModemInfo  const m = (ModemInfo) l->info;
        !           346:     const char ch = ' ';
        !           347: 
        !           348:     /* Shutdown everything */
        !           349:     assert(m->fd >= 0);
        !           350:     ChatAbort(m->chat);
        !           351:     TimerStop(&m->checkTimer);
        !           352:     TimerStop(&m->startTimer);
        !           353:     TimerStop(&m->reportTimer);
        !           354:     if (*m->ttynode != '\0') {
        !           355:        char    path[NG_PATHSIZ];
        !           356: 
        !           357:        snprintf(path, sizeof(path), "%s:%s", m->ttynode, NG_TTY_HOOK);
        !           358:        NgFuncShutdownNode(m->csock, l->name, path);
        !           359:        snprintf(path, sizeof(path), "%s:", m->ttynode);
        !           360:        NgFuncShutdownNode(m->csock, l->name, path);
        !           361:        *m->ttynode = '\0';
        !           362:     }
        !           363:     (void) write(m->fd, &ch, 1);       /* USR kludge to prevent dial lockup */
        !           364:     if (m->csock > 0) {
        !           365:        close(m->csock);
        !           366:        m->csock = -1;
        !           367:     }
        !           368:     ExclusiveCloseDevice(l->name, m->fd, m->device);
        !           369:     m->lastClosed = time(NULL);
        !           370:     m->answering = FALSE;
        !           371:     m->fd = -1;
        !           372:     m->opened = opened;
        !           373:     ModemStart(l);
        !           374: }
        !           375: 
        !           376: /*
        !           377:  * ModemSetAccm()
        !           378:  */
        !           379: 
        !           380: static int
        !           381: ModemSetAccm(Link l, u_int32_t xmit, u_int32_t recv)
        !           382: {
        !           383:     ModemInfo  const m = (ModemInfo) l->info;
        !           384:     char       path[NG_PATHSIZ];
        !           385: 
        !           386:     /* Update async config */
        !           387:     m->acfg.accm = xmit|recv;
        !           388:     snprintf(path, sizeof(path), "%s:%s", m->ttynode, NG_TTY_HOOK);
        !           389:     if (NgSendMsg(m->csock, path, NGM_ASYNC_COOKIE,
        !           390:       NGM_ASYNC_CMD_SET_CONFIG, &m->acfg, sizeof(m->acfg)) < 0) {
        !           391:        Perror("[%s] MODEM: can't update config for %s", l->name, path);
        !           392:        return (-1);
        !           393:     }
        !           394:     return (0);
        !           395: }
        !           396: 
        !           397: /*
        !           398:  * ModemChatConnectResult()
        !           399:  *
        !           400:  * Connect chat script returns here when finished.
        !           401:  */
        !           402: 
        !           403: static void
        !           404: ModemChatConnectResult(void *arg, int result, const char *msg)
        !           405: {
        !           406:     Link       const l = (Link) arg;
        !           407:     ModemInfo  const m = (ModemInfo) l->info;
        !           408:     char       *cspeed;
        !           409:     int                bw;
        !           410: 
        !           411:     /* Was the connect script successful? */
        !           412:     Log(LG_PHYS, ("[%s] MODEM: chat script %s",
        !           413:        l->name, result ? "succeeded" : "failed"));
        !           414:     if (!result) {
        !           415: failed:
        !           416:        ModemDoClose(l, FALSE);
        !           417:        l->state = PHYS_STATE_DOWN;
        !           418:        PhysDown(l, STR_ERROR, msg);
        !           419:        return;
        !           420:     }
        !           421: 
        !           422:     /* Set modem's reported connection speed (if any) as the link bandwidth */
        !           423:     if ((cspeed = ChatGetVar(m->chat, CHAT_VAR_CONNECT_SPEED)) != NULL) {
        !           424:        if ((bw = (int) strtoul(cspeed, NULL, 10)) > 0)
        !           425:            l->bandwidth = bw;
        !           426:        Freee(cspeed);
        !           427:     }
        !           428: 
        !           429:     /* Do async <-> sync conversion via netgraph node */
        !           430:     if (ModemInstallNodes(l) < 0) {
        !           431:        msg = STR_DEV_NOT_READY;
        !           432:        goto failed;
        !           433:     }
        !           434: 
        !           435:     /* Start pin check and report timers */
        !           436:     TimerInit(&m->checkTimer, "ModemCheck",
        !           437:        MODEM_CHECK_INTERVAL * SECONDS, ModemCheck, l);
        !           438:     TimerStart(&m->checkTimer);
        !           439:     TimerStop(&m->reportTimer);
        !           440:     TimerInit(&m->reportTimer, "ModemReport",
        !           441:        MODEM_ERR_REPORT_INTERVAL * SECONDS, ModemErrorCheck, l);
        !           442:     TimerStart(&m->reportTimer);
        !           443: 
        !           444:     l->state = PHYS_STATE_UP;
        !           445:     PhysUp(l);
        !           446: }
        !           447: 
        !           448: /*
        !           449:  * ModemChatIdleResult()
        !           450:  *
        !           451:  * Idle chat script returns here when finished. If the script returned
        !           452:  * successfully, then one of two things happened: either we answered
        !           453:  * an incoming call, or else we got a ring and want to do ringback.
        !           454:  * We tell the difference by checking $IdleResult.
        !           455:  */
        !           456: 
        !           457: static void
        !           458: ModemChatIdleResult(void *arg, int result, const char *msg)
        !           459: {
        !           460:     Link       const l = (Link) arg;
        !           461:     ModemInfo  const m = (ModemInfo) l->info;
        !           462:     char       *idleResult;
        !           463: 
        !           464:     /* If script failed, then do nothing */
        !           465:     if (!result) {
        !           466:        ModemDoClose(l, FALSE);
        !           467:        return;
        !           468:     }
        !           469: 
        !           470:     /* See what script wants us to do now by checking variable $IdleResult */
        !           471:     if ((idleResult = ChatGetVar(m->chat, CHAT_VAR_IDLE_RESULT)) == NULL) {
        !           472:        Log(LG_ERR, ("[%s] MODEM: idle script succeeded, but %s not defined",
        !           473:            l->name, CHAT_VAR_IDLE_RESULT));
        !           474:        ModemDoClose(l, FALSE);
        !           475:        return;
        !           476:     }
        !           477: 
        !           478:     /* Do whatever */
        !           479:     Log(LG_PHYS, ("[%s] MODEM: idle script succeeded, action=%s",
        !           480:        l->name, idleResult));
        !           481: 
        !           482:     if (gShutdownInProgress) {
        !           483:        Log(LG_PHYS, ("Shutdown sequence in progress, ignoring"));
        !           484:        ModemDoClose(l, FALSE);
        !           485:     } else if (strcasecmp(idleResult, MODEM_IDLE_RESULT_ANSWER) == 0) {
        !           486:        Log(LG_PHYS, ("[%s] MODEM: opening link in %s mode", l->name, "answer"));
        !           487:        RecordLinkUpDownReason(NULL, l, 1, STR_INCOMING_CALL, msg ? "%s" : NULL, msg);
        !           488:        m->answering = TRUE;
        !           489:        l->state = PHYS_STATE_READY;
        !           490:        PhysIncoming(l);
        !           491:     } else if (strcasecmp(idleResult, MODEM_IDLE_RESULT_RINGBACK) == 0) {
        !           492:        Log(LG_PHYS, ("[%s] MODEM: opening link in %s mode", l->name, "ringback"));
        !           493:        RecordLinkUpDownReason(NULL, l, 1, STR_RINGBACK, msg ? "%s" : NULL, msg);
        !           494:        m->answering = FALSE;
        !           495:        PhysIncoming(l);
        !           496:     } else {
        !           497:        Log(LG_ERR, ("[%s] MODEM: idle script succeeded, but action \"%s\" unknown",
        !           498:          l->name, idleResult));
        !           499:        ModemDoClose(l, FALSE);
        !           500:     }
        !           501:     Freee(idleResult);
        !           502: }
        !           503: 
        !           504: /*
        !           505:  * ModemInstallNodes()
        !           506:  */
        !           507: 
        !           508: static int
        !           509: ModemInstallNodes(Link l)
        !           510: {
        !           511:     ModemInfo          m = (ModemInfo) l->info;
        !           512:     struct ngm_mkpeer  ngm;
        !           513:     struct ngm_connect cn;
        !           514:     char                       path[NG_PATHSIZ];
        !           515:     int                        hotchar = PPP_FLAG;
        !           516: #if NGM_TTY_COOKIE < 1226109660
        !           517:     struct nodeinfo    ngtty;
        !           518:     int                        ldisc = NETGRAPHDISC;
        !           519: #else
        !           520:     struct ngm_rmhook  rm;
        !           521:     union {
        !           522:        u_char buf[sizeof(struct ng_mesg) + sizeof(struct nodeinfo)];
        !           523:        struct ng_mesg reply;
        !           524:     } repbuf;
        !           525:     struct ng_mesg *const reply = &repbuf.reply;
        !           526:     struct nodeinfo *ninfo = (struct nodeinfo *)&reply->data;
        !           527:     int        tty[2];
        !           528: #endif
        !           529: 
        !           530:     /* Get a temporary netgraph socket node */
        !           531:     if (NgMkSockNode(NULL, &m->csock, NULL) < 0) {
        !           532:        Perror("[%s] MODEM: NgMkSockNode failed", l->name);
        !           533:        return(-1);
        !           534:     }
        !           535: 
        !           536: #if NGM_TTY_COOKIE < 1226109660
        !           537:     /* Install ng_tty line discipline */
        !           538:     if (ioctl(m->fd, TIOCSETD, &ldisc) < 0) {
        !           539: 
        !           540:        /* Installation of the tty node type should be automatic, but isn't yet.
        !           541:           The 'mkpeer' below will fail, because you can only create a ng_tty
        !           542:            node via TIOCSETD; however, this will force a load of the node type. */
        !           543:        if (errno == ENODEV) {
        !           544:            (void)NgSendAsciiMsg(m->csock, ".:",
        !           545:                "mkpeer { type=\"%s\" ourhook=\"dummy\" peerhook=\"%s\" }",
        !           546:                NG_TTY_NODE_TYPE, NG_TTY_HOOK);
        !           547:        }
        !           548:        if (ioctl(m->fd, TIOCSETD, &ldisc) < 0) {
        !           549:            Perror("[%s] ioctl(TIOCSETD, %d)", l->name, ldisc);
        !           550:            close(m->csock);
        !           551:            return(-1);
        !           552:        }
        !           553:     }
        !           554: 
        !           555:     /* Get the name of the ng_tty node */
        !           556:     if (ioctl(m->fd, NGIOCGINFO, &ngtty) < 0) {
        !           557:        Perror("[%s] MODEM: ioctl(NGIOCGINFO)", l->name);
        !           558:        close(m->csock);
        !           559:        return(-1);
        !           560:     }
        !           561:     strlcpy(m->ttynode, ngtty.name, sizeof(m->ttynode));
        !           562: #else
        !           563:     /* Attach a TTY node */
        !           564:     snprintf(ngm.type, sizeof(ngm.type), "%s", NG_TTY_NODE_TYPE);
        !           565:     snprintf(ngm.ourhook, sizeof(ngm.ourhook), "%s", NG_TTY_HOOK);
        !           566:     snprintf(ngm.peerhook, sizeof(ngm.peerhook), "%s", NG_TTY_HOOK);
        !           567:     if (NgSendMsg(m->csock, ".", NGM_GENERIC_COOKIE,
        !           568:            NGM_MKPEER, &ngm, sizeof(ngm)) < 0) {
        !           569:        Perror("[%s] MODEM: can't connect %s node on %s", l->name,
        !           570:            NG_TTY_NODE_TYPE, ".");
        !           571:        close(m->csock);
        !           572:        return(-1);
        !           573:     }
        !           574:     snprintf(path, sizeof(path), ".:%s", NG_TTY_HOOK);
        !           575:     if (NgSendMsg(m->csock, path,
        !           576:            NGM_GENERIC_COOKIE, NGM_NODEINFO, NULL, 0) != -1) {
        !           577:        if (NgRecvMsg(m->csock, reply, sizeof(repbuf), NULL) < 0) {
        !           578:            Perror("[%s] MODEM: can't locate %s node on %s (%d)", l->name,
        !           579:            NG_TTY_NODE_TYPE, path, errno);
        !           580:            close(m->csock);
        !           581:            return(-1);
        !           582:        }
        !           583:     }
        !           584:     snprintf(m->ttynode, sizeof(m->ttynode), "[%x]", ninfo->id);
        !           585:     /* Attach to the TTY */
        !           586:     tty[0] = gPid;
        !           587:     tty[1] = m->fd;
        !           588:     if (NgSendMsg(m->csock, path, NGM_TTY_COOKIE,
        !           589:           NGM_TTY_SET_TTY, &tty, sizeof(tty)) < 0) {
        !           590:        Perror("[%s] MODEM: can't hook tty to fd %d", l->name, m->fd);
        !           591:        close(m->csock);
        !           592:        return(-1);
        !           593:     }
        !           594:     /* Disconnect temporary hook. */
        !           595:     snprintf(rm.ourhook, sizeof(rm.ourhook), "%s", NG_TTY_HOOK);
        !           596:     if (NgSendMsg(m->csock, ".",
        !           597:            NGM_GENERIC_COOKIE, NGM_RMHOOK, &rm, sizeof(rm)) < 0) {
        !           598:        Perror("[%s] MODEM: can't remove hook %s", l->name, NG_TTY_HOOK);
        !           599:        close(m->csock);
        !           600:        return(-1);
        !           601:     }
        !           602: #endif
        !           603: 
        !           604:     /* Set the ``hot char'' on the TTY node */
        !           605:     snprintf(path, sizeof(path), "%s:", m->ttynode);
        !           606:     if (NgSendMsg(m->csock, path, NGM_TTY_COOKIE,
        !           607:       NGM_TTY_SET_HOTCHAR, &hotchar, sizeof(hotchar)) < 0) {
        !           608:        Perror("[%s] MODEM: can't set hotchar", l->name);
        !           609:        close(m->csock);
        !           610:        return(-1);
        !           611:     }
        !           612: 
        !           613:     /* Attach an async converter node */
        !           614:     strcpy(ngm.type, NG_ASYNC_NODE_TYPE);
        !           615:     strcpy(ngm.ourhook, NG_TTY_HOOK);
        !           616:     strcpy(ngm.peerhook, NG_ASYNC_HOOK_ASYNC);
        !           617:     if (NgSendMsg(m->csock, path, NGM_GENERIC_COOKIE,
        !           618:       NGM_MKPEER, &ngm, sizeof(ngm)) < 0) {
        !           619:        Perror("[%s] MODEM: can't connect %s node", l->name, NG_ASYNC_NODE_TYPE);
        !           620:        close(m->csock);
        !           621:        return(-1);
        !           622:     }
        !           623: 
        !           624:     /* Configure the async converter node */
        !           625:     snprintf(path, sizeof(path), "%s:%s", m->ttynode, NG_TTY_HOOK);
        !           626:     memset(&m->acfg, 0, sizeof(m->acfg));
        !           627:     m->acfg.enabled = TRUE;
        !           628:     m->acfg.accm = ~0;
        !           629:     m->acfg.amru = MODEM_MRU;
        !           630:     m->acfg.smru = MODEM_MTU;
        !           631:     if (NgSendMsg(m->csock, path, NGM_ASYNC_COOKIE,
        !           632:       NGM_ASYNC_CMD_SET_CONFIG, &m->acfg, sizeof(m->acfg)) < 0) {
        !           633:        Perror("[%s] MODEM: can't config %s", l->name, path);
        !           634:        close(m->csock);
        !           635:        return(-1);
        !           636:     }
        !           637: 
        !           638:     /* Attach async node to PPP node */
        !           639:     if (!PhysGetUpperHook(l, cn.path, cn.peerhook)) {
        !           640:        Log(LG_PHYS, ("[%s] MODEM: can't get upper hook", l->name));
        !           641:        close(m->csock);
        !           642:        return (-1);
        !           643:     }
        !           644:     snprintf(cn.ourhook, sizeof(cn.ourhook), NG_ASYNC_HOOK_SYNC);
        !           645:     if (NgSendMsg(m->csock, path, NGM_GENERIC_COOKIE, NGM_CONNECT, 
        !           646:         &cn, sizeof(cn)) < 0) {
        !           647:        Perror("[%s] MODEM: can't connect \"%s\"->\"%s\" and \"%s\"->\"%s\"",
        !           648:            l->name, path, cn.ourhook, cn.path, cn.peerhook);
        !           649:        close(m->csock);
        !           650:        return (-1);
        !           651:     }
        !           652: 
        !           653:     return(0);
        !           654: }
        !           655: 
        !           656: /*
        !           657:  * ModemChatSetBaudrate()
        !           658:  *
        !           659:  * This callback changes the actual baudrate of the serial port.
        !           660:  * Should only be called once the device is already open.
        !           661:  * Returns -1 on failure.
        !           662:  */
        !           663: 
        !           664: static int
        !           665: ModemChatSetBaudrate(void *arg, int baud)
        !           666: {
        !           667:     Link               const l = (Link) arg;
        !           668:     ModemInfo          const m = (ModemInfo) l->info;
        !           669:     struct termios     attr;
        !           670: 
        !           671:     /* Change baud rate */
        !           672:     if (tcgetattr(m->fd, &attr) < 0) {
        !           673:        Perror("[%s] MODEM: can't tcgetattr \"%s\"", l->name, m->device);
        !           674:        return(-1);
        !           675:     }
        !           676:     if (cfsetspeed(&attr, (speed_t) baud) < 0) {
        !           677:        Perror("[%s] MODEM: can't set speed %d", l->name, baud);
        !           678:        return(-1);
        !           679:     }
        !           680:     if (tcsetattr(m->fd, TCSANOW, &attr) < 0) {
        !           681:        Perror("[%s] MODEM: can't tcsetattr \"%s\"", l->name, m->device);
        !           682:        return(-1);
        !           683:     }
        !           684:     return(0);
        !           685: }
        !           686: 
        !           687: /*
        !           688:  * ModemGetVar()
        !           689:  */
        !           690: 
        !           691: char *
        !           692: ModemGetVar(Link l, const char *name)
        !           693: {
        !           694:     ModemInfo  const m = (ModemInfo) l->info;
        !           695: 
        !           696:     return ChatGetVar(m->chat, name);
        !           697: }
        !           698: 
        !           699: /*
        !           700:  * ModemCheck()
        !           701:  */
        !           702: 
        !           703: static void
        !           704: ModemCheck(void *arg)
        !           705: {
        !           706:     Link       const l = (Link)arg;
        !           707:     ModemInfo  const m = (ModemInfo) l->info;
        !           708:     int                state;
        !           709: 
        !           710:     if (ioctl(m->fd, TIOCMGET, &state) < 0) {
        !           711:        Perror("[%s] MODEM: can't ioctl(TIOCMGET) %s", l->name, m->device);
        !           712:        l->state = PHYS_STATE_DOWN;
        !           713:        ModemDoClose(l, FALSE);
        !           714:        PhysDown(l, STR_ERROR, strerror(errno));
        !           715:        return;
        !           716:     }
        !           717:     if ((m->watch & TIOCM_CAR) && !(state & TIOCM_CAR)) {
        !           718:        Log(LG_PHYS, ("[%s] MODEM: carrier detect (CD) signal lost", l->name));
        !           719:        l->state = PHYS_STATE_DOWN;
        !           720:        ModemDoClose(l, FALSE);
        !           721:        PhysDown(l, STR_DROPPED, STR_LOST_CD);
        !           722:        return;
        !           723:     }
        !           724:     if ((m->watch & TIOCM_DSR) && !(state & TIOCM_DSR)) {
        !           725:        Log(LG_PHYS, ("[%s] MODEM: data-set ready (DSR) signal lost", l->name));
        !           726:        l->state = PHYS_STATE_DOWN;
        !           727:        ModemDoClose(l, FALSE);
        !           728:        PhysDown(l, STR_DROPPED, STR_LOST_DSR);
        !           729:        return;
        !           730:     }
        !           731:     TimerStart(&m->checkTimer);
        !           732: }
        !           733: 
        !           734: /*
        !           735:  * ModemErrorCheck()
        !           736:  *
        !           737:  * Called every second to record errors to the log
        !           738:  */
        !           739: 
        !           740: static void
        !           741: ModemErrorCheck(void *arg)
        !           742: {
        !           743:     Link                       const l = (Link) arg;
        !           744:     ModemInfo                  const m = (ModemInfo) l->info;
        !           745:     char                       path[NG_PATHSIZ];
        !           746:     struct ng_async_stat       stats;
        !           747: 
        !           748:     /* Check for errors */
        !           749:     snprintf(path, sizeof(path), "%s:%s", m->ttynode, NG_TTY_HOOK);
        !           750:     if (ModemGetNgStats(l, &stats) >= 0
        !           751:       && (stats.asyncBadCheckSums
        !           752:       || stats.asyncRunts || stats.asyncOverflows)) {
        !           753:        Log(LG_PHYS, ("[%s] NEW FRAME ERRS: FCS %u RUNT %u OVFL %u", l->name,
        !           754:           stats.asyncBadCheckSums, stats.asyncRunts, stats.asyncOverflows));
        !           755:        (void) NgSendMsg(m->csock, path,
        !           756:            NGM_ASYNC_COOKIE, NGM_ASYNC_CMD_CLR_STATS, NULL, 0);
        !           757:     }
        !           758: 
        !           759:     /* Restart timer */
        !           760:     TimerStop(&m->reportTimer);
        !           761:     TimerStart(&m->reportTimer);
        !           762: }
        !           763: 
        !           764: /*
        !           765:  * ModemGetNgStats()
        !           766:  */
        !           767: 
        !           768: static int
        !           769: ModemGetNgStats(Link l, struct ng_async_stat *sp)
        !           770: {
        !           771:     ModemInfo          const m = (ModemInfo) l->info;
        !           772:     char               path[NG_PATHSIZ];
        !           773:     union {
        !           774:        u_char          buf[sizeof(struct ng_mesg) + sizeof(*sp)];
        !           775:        struct ng_mesg  resp;
        !           776:     } u;
        !           777: 
        !           778:     /* Get stats */
        !           779:     snprintf(path, sizeof(path), "%s:%s", m->ttynode, NG_TTY_HOOK);
        !           780:     if (NgFuncSendQuery(path, NGM_ASYNC_COOKIE, NGM_ASYNC_CMD_GET_STATS,
        !           781:       NULL, 0, &u.resp, sizeof(u), NULL) < 0) {
        !           782:        Perror("[%s] MODEM: can't get stats", l->name);
        !           783:        return(-1);
        !           784:     }
        !           785: 
        !           786:     memcpy(sp, u.resp.data, sizeof(*sp));
        !           787:     return(0);
        !           788: }
        !           789: 
        !           790: /*
        !           791:  * ModemSetCommand()
        !           792:  */
        !           793: 
        !           794: static int
        !           795: ModemSetCommand(Context ctx, int ac, char *av[], void *arg)
        !           796: {
        !           797:     Link       const l = ctx->lnk;
        !           798:     ModemInfo  const m = (ModemInfo) l->info;
        !           799: 
        !           800:     switch ((intptr_t)arg) {
        !           801:        case SET_DEVICE:
        !           802:            if (ac == 1)
        !           803:                strlcpy(m->device, av[0], sizeof(m->device));
        !           804:            break;
        !           805:        case SET_SPEED:
        !           806:            {
        !           807:                int     k, baud;
        !           808: 
        !           809:                if (ac != 1)
        !           810:                    return(-1);
        !           811:                baud = atoi(*av);
        !           812:                for (k = 0; gSpeedList[k] != -1 && baud != gSpeedList[k]; k++);
        !           813:                if (gSpeedList[k] == -1)
        !           814:                    Error("invalid speed \'%s\'", *av);
        !           815:                else {
        !           816:                    char        buf[32];
        !           817: 
        !           818:                    m->speed = baud;
        !           819:                    snprintf(buf, sizeof(buf), "%d", m->speed);
        !           820:                    ChatPresetVar(m->chat, CHAT_VAR_BAUDRATE, buf);
        !           821:                }
        !           822:            }
        !           823:            break;
        !           824:        case SET_CSCRIPT:
        !           825:            if (ac != 1)
        !           826:                return(-1);
        !           827:            *m->connScript = 0;
        !           828:            strlcpy(m->connScript, av[0], sizeof(m->connScript));
        !           829:            break;
        !           830:        case SET_ISCRIPT:
        !           831:            if (ac != 1)
        !           832:                return(-1);
        !           833:            *m->idleScript = 0;
        !           834:            strlcpy(m->idleScript, av[0], sizeof(m->idleScript));
        !           835:            if (m->opened || TimerRemain(&m->startTimer) >= 0)
        !           836:                break;          /* nothing needs to be done right now */
        !           837:            if (m->fd >= 0 && !*m->idleScript)
        !           838:                ModemDoClose(l, FALSE);
        !           839:            else if (m->fd < 0 && *m->idleScript)
        !           840:                ModemStart(l);
        !           841:            break;
        !           842:        case SET_SCRIPT_VAR:
        !           843:            if (ac != 2)
        !           844:                return(-1);
        !           845:            ChatPresetVar(m->chat, av[0], av[1]);
        !           846:            break;
        !           847:        case SET_WATCH:
        !           848:            {
        !           849:                int     bit, add;
        !           850: 
        !           851:                while (ac--) {
        !           852:                    switch (**av) {
        !           853:                        case '+':
        !           854:                            (*av)++;
        !           855:                        default:
        !           856:                            add = TRUE;
        !           857:                            break;
        !           858:                        case '-':
        !           859:                            add = FALSE;
        !           860:                            (*av)++;
        !           861:                            break;
        !           862:                    }
        !           863:                    if (!strcasecmp(*av, "cd"))
        !           864:                        bit = TIOCM_CAR;
        !           865:                    else if (!strcasecmp(*av, "dsr"))
        !           866:                        bit = TIOCM_DSR;
        !           867:                    else {
        !           868:                        Printf("[%s] modem signal \"%s\" is unknown\r\n", l->name, *av);
        !           869:                        bit = 0;
        !           870:                    }
        !           871:                    if (add)
        !           872:                        m->watch |= bit;
        !           873:                    else
        !           874:                        m->watch &= ~bit;
        !           875:                    av++;
        !           876:                }
        !           877:            }
        !           878:            break;
        !           879:        default:
        !           880:            assert(0);
        !           881:     }
        !           882:     return(0);
        !           883: }
        !           884: 
        !           885: /*
        !           886:  * ModemOriginated()
        !           887:  */
        !           888: 
        !           889: static int
        !           890: ModemOriginated(Link l)
        !           891: {
        !           892:     ModemInfo  const m = (ModemInfo) l->info;
        !           893: 
        !           894:     return(m->originated ? LINK_ORIGINATE_LOCAL : LINK_ORIGINATE_REMOTE);
        !           895: }
        !           896: 
        !           897: /*
        !           898:  * ModemIsSync()
        !           899:  */
        !           900: 
        !           901: static int
        !           902: ModemIsSync(Link l)
        !           903: {
        !           904:     return (0);
        !           905: }
        !           906: 
        !           907: static int
        !           908: ModemSelfAddr(Link l, void *buf, size_t buf_len)
        !           909: {
        !           910:     ModemInfo  const m = (ModemInfo) l->info;
        !           911: 
        !           912:     strlcpy(buf, m->ttynode, buf_len);
        !           913:     return(0);
        !           914: }
        !           915: 
        !           916: static int
        !           917: ModemIface(Link l, void *buf, size_t buf_len)
        !           918: {
        !           919:     ModemInfo  const m = (ModemInfo) l->info;
        !           920: 
        !           921:     strlcpy(buf, m->device, buf_len);
        !           922:     return(0);
        !           923: }
        !           924: 
        !           925: static int
        !           926: ModemCallingNum(Link l, void *buf, size_t buf_len)
        !           927: {
        !           928:     ModemInfo  const m = (ModemInfo) l->info;
        !           929:     char       *tmp;
        !           930: 
        !           931:     if ((tmp = ChatGetVar(m->chat, CHAT_VAR_CALLING)) == NULL) {
        !           932:        ((char *)buf)[0] = 0;
        !           933:        return (-1);
        !           934:     }
        !           935:     strlcpy((char*)buf, tmp, buf_len);
        !           936:     Freee(tmp);
        !           937:     return(0);
        !           938: }
        !           939: 
        !           940: static int
        !           941: ModemCalledNum(Link l, void *buf, size_t buf_len)
        !           942: {
        !           943:     ModemInfo  const m = (ModemInfo) l->info;
        !           944:     char       *tmp;
        !           945: 
        !           946:     if ((tmp = ChatGetVar(m->chat, CHAT_VAR_CALLED)) == NULL) {
        !           947:        ((char *)buf)[0] = 0;
        !           948:        return (-1);
        !           949:     }
        !           950:     strlcpy((char*)buf, tmp, buf_len);
        !           951:     Freee(tmp);
        !           952:     return(0);
        !           953: }
        !           954: 
        !           955: /*
        !           956:  * ModemStat()
        !           957:  */
        !           958: 
        !           959: void
        !           960: ModemStat(Context ctx)
        !           961: {
        !           962:     ModemInfo                  const m = (ModemInfo) ctx->lnk->info;
        !           963:     struct ng_async_stat       stats;
        !           964:     char                       *tmp;
        !           965: 
        !           966:     Printf("Modem info:\r\n");
        !           967:     Printf("\tDevice       : %s\r\n", m->device);
        !           968:     Printf("\tPort speed   : %d baud\r\n", m->speed);
        !           969:     Printf("\tConn. script : \"%s\"\r\n", m->connScript);
        !           970:     Printf("\tIdle script  : \"%s\"\r\n", m->idleScript);
        !           971:     Printf("\tPins to watch: %s%s\r\n",
        !           972:        (m->watch & TIOCM_CAR) ? "CD " : "",
        !           973:        (m->watch & TIOCM_DSR) ? "DSR" : "");
        !           974: 
        !           975:     Printf("Modem status:\r\n");
        !           976:     if (ctx->lnk->state != PHYS_STATE_DOWN) {
        !           977:        Printf("\tOpened       : %s\r\n", (m->opened?"YES":"NO"));
        !           978:        Printf("\tIncoming     : %s\r\n", (m->originated?"NO":"YES"));
        !           979: 
        !           980:        /* Set modem's reported connection speed (if any) as the link bandwidth */
        !           981:        if ((tmp = ChatGetVar(m->chat, CHAT_VAR_CONNECT_SPEED)) != NULL) {
        !           982:            Printf("\tConnect speed: %s baud\r\n", tmp);
        !           983:            Freee(tmp);
        !           984:        }
        !           985: 
        !           986:        if ((tmp = ChatGetVar(m->chat, CHAT_VAR_CALLING)) != NULL) {
        !           987:            Printf("\tCalling      : %s\r\n", tmp);
        !           988:            Freee(tmp);
        !           989:        }
        !           990:        if ((tmp = ChatGetVar(m->chat, CHAT_VAR_CALLED)) != NULL) {
        !           991:            Printf("\tCalled       : %s\r\n", tmp);
        !           992:            Freee(tmp);
        !           993:        }
        !           994: 
        !           995:        if (ctx->lnk->state == PHYS_STATE_UP && 
        !           996:                ModemGetNgStats(ctx->lnk, &stats) >= 0) {
        !           997:            Printf("Async stats:\r\n");
        !           998:            Printf("\t       syncOctets: %8u\r\n", stats.syncOctets);
        !           999:            Printf("\t       syncFrames: %8u\r\n", stats.syncFrames);
        !          1000:            Printf("\t    syncOverflows: %8u\r\n", stats.syncOverflows);
        !          1001:            Printf("\t      asyncOctets: %8u\r\n", stats.asyncOctets);
        !          1002:            Printf("\t      asyncFrames: %8u\r\n", stats.asyncFrames);
        !          1003:            Printf("\t       asyncRunts: %8u\r\n", stats.asyncRunts);
        !          1004:            Printf("\t   asyncOverflows: %8u\r\n", stats.asyncOverflows);
        !          1005:            Printf("\tasyncBadCheckSums: %8u\r\n", stats.asyncBadCheckSums);
        !          1006:        }
        !          1007:     }
        !          1008: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>