File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / mpd / src / modem.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Jul 22 08:44:29 2013 UTC (10 years, 11 months ago) by misho
Branches: mpd, MAIN
CVS tags: v5_8p7, v5_8p1_cross, v5_8p1, v5_8, v5_7p0, v5_7, v5_6, HEAD
5.7

    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>