146686Sbrian/*-
246686Sbrian * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
346686Sbrian * All rights reserved.
446686Sbrian *
546686Sbrian * Redistribution and use in source and binary forms, with or without
646686Sbrian * modification, are permitted provided that the following conditions
746686Sbrian * are met:
846686Sbrian * 1. Redistributions of source code must retain the above copyright
946686Sbrian *    notice, this list of conditions and the following disclaimer.
1046686Sbrian * 2. Redistributions in binary form must reproduce the above copyright
1146686Sbrian *    notice, this list of conditions and the following disclaimer in the
1246686Sbrian *    documentation and/or other materials provided with the distribution.
1346686Sbrian *
1446686Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1546686Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1646686Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1746686Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1846686Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1946686Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2046686Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2146686Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2246686Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2346686Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2446686Sbrian * SUCH DAMAGE.
2546686Sbrian *
2650479Speter * $FreeBSD$
2746686Sbrian */
2846686Sbrian
2946686Sbrian#include <sys/param.h>
3046686Sbrian#include <sys/un.h>
3146686Sbrian#if defined(__OpenBSD__) || defined(__NetBSD__)
3246686Sbrian#include <sys/ioctl.h>
3346686Sbrian#endif
3446686Sbrian
3546686Sbrian#include <errno.h>
3646686Sbrian#include <fcntl.h>
3793418Sbrian#include <stdio.h>
3846686Sbrian#include <stdlib.h>
3946686Sbrian#include <string.h>
4047769Sbrian#include <sysexits.h>
4147061Sbrian#include <sys/uio.h>
4246686Sbrian#include <termios.h>
4396582Sbrian#include <ttyent.h>
4446686Sbrian#include <unistd.h>
4593464Sbrian#ifndef NONETGRAPH
4693418Sbrian#include <netgraph.h>
4793418Sbrian#include <netgraph/ng_async.h>
4893418Sbrian#include <netgraph/ng_message.h>
4993418Sbrian#include <netgraph/ng_ppp.h>
5093418Sbrian#include <netgraph/ng_tty.h>
5193464Sbrian#endif
5246686Sbrian
5346686Sbrian#include "layer.h"
5446686Sbrian#include "defs.h"
5546686Sbrian#include "mbuf.h"
5646686Sbrian#include "log.h"
5746686Sbrian#include "timer.h"
5846686Sbrian#include "lqr.h"
5946686Sbrian#include "hdlc.h"
6046686Sbrian#include "throughput.h"
6146686Sbrian#include "fsm.h"
6246686Sbrian#include "lcp.h"
6346686Sbrian#include "ccp.h"
6446686Sbrian#include "link.h"
6546686Sbrian#include "async.h"
6646686Sbrian#include "descriptor.h"
6746686Sbrian#include "physical.h"
6846686Sbrian#include "mp.h"
6946686Sbrian#include "chat.h"
7046686Sbrian#include "auth.h"
7146686Sbrian#include "chap.h"
7246686Sbrian#include "cbcp.h"
7346686Sbrian#include "datalink.h"
7447769Sbrian#include "main.h"
7593418Sbrian#include "id.h"
7646686Sbrian#include "tty.h"
7746686Sbrian
7852265Sbrian#if defined(__mac68k__) || defined(__macppc__)
7952265Sbrian#undef	CRTS_IFLOW
8052265Sbrian#undef	CCTS_OFLOW
8152265Sbrian#define	CRTS_IFLOW	CDTRCTS
8252265Sbrian#define	CCTS_OFLOW	CDTRCTS
8352265Sbrian#endif
8452265Sbrian
8547061Sbrian#define	Online(dev)	((dev)->mbits & TIOCM_CD)
8646686Sbrian
8747061Sbrianstruct ttydevice {
8847061Sbrian  struct device dev;		/* What struct physical knows about */
8947061Sbrian  struct pppTimer Timer;	/* CD checks */
9047061Sbrian  int mbits;			/* Current DCD status */
9149472Sbrian  int carrier_seconds;		/* seconds before CD is *required* */
9293418Sbrian#ifndef NONETGRAPH
9393418Sbrian  struct {
94134789Sbrian    unsigned speed;		/* Pre-line-discipline speed */
9593418Sbrian    int fd;			/* Pre-line-discipline fd */
9693418Sbrian    int disc;			/* Old line-discipline */
9793418Sbrian  } real;
9893418Sbrian  char hook[sizeof NG_ASYNC_HOOK_SYNC]; /* our ng_socket hook */
9993418Sbrian  int cs;			/* A netgraph control socket (maybe) */
10093418Sbrian#endif
10147061Sbrian  struct termios ios;		/* To be able to reset from raw mode */
10247061Sbrian};
10347061Sbrian
10447061Sbrian#define device2tty(d) ((d)->type == TTY_DEVICE ? (struct ttydevice *)d : NULL)
10547061Sbrian
106134789Sbrianunsigned
10747769Sbriantty_DeviceSize(void)
10847769Sbrian{
10947769Sbrian  return sizeof(struct ttydevice);
11047769Sbrian}
11147769Sbrian
11246686Sbrian/*
11346686Sbrian * tty_Timeout() watches the DCD signal and mentions it if it's status
11446686Sbrian * changes.
11546686Sbrian */
11646686Sbrianstatic void
11746686Sbriantty_Timeout(void *data)
11846686Sbrian{
11946686Sbrian  struct physical *p = data;
12047061Sbrian  struct ttydevice *dev = device2tty(p->handler);
12146686Sbrian  int ombits, change;
12246686Sbrian
12347061Sbrian  timer_Stop(&dev->Timer);
12447061Sbrian  dev->Timer.load = SECTICKS;		/* Once a second please */
12547061Sbrian  timer_Start(&dev->Timer);
12647061Sbrian  ombits = dev->mbits;
12746686Sbrian
12846686Sbrian  if (p->fd >= 0) {
12947061Sbrian    if (ioctl(p->fd, TIOCMGET, &dev->mbits) < 0) {
13049472Sbrian      /* we must be a pty ? */
13153733Sbrian      if (p->cfg.cd.necessity != CD_DEFAULT)
13253733Sbrian        log_Printf(LogWARN, "%s: Carrier ioctl not supported, "
13353733Sbrian                   "using ``set cd off''\n", p->link.name);
13447061Sbrian      timer_Stop(&dev->Timer);
13554569Sbrian      dev->mbits = TIOCM_CD;
13646686Sbrian      return;
13746686Sbrian    }
13846686Sbrian  } else
13947061Sbrian    dev->mbits = 0;
14046686Sbrian
14146686Sbrian  if (ombits == -1) {
14246686Sbrian    /* First time looking for carrier */
14347061Sbrian    if (Online(dev))
14449472Sbrian      log_Printf(LogPHASE, "%s: %s: CD detected\n", p->link.name, p->name.full);
14553733Sbrian    else if (++dev->carrier_seconds >= dev->dev.cd.delay) {
14653733Sbrian      if (dev->dev.cd.necessity == CD_REQUIRED)
14749472Sbrian        log_Printf(LogPHASE, "%s: %s: Required CD not detected\n",
14849472Sbrian                   p->link.name, p->name.full);
14949472Sbrian      else {
15049472Sbrian        log_Printf(LogPHASE, "%s: %s doesn't support CD\n",
15149472Sbrian                   p->link.name, p->name.full);
15249472Sbrian        dev->mbits = TIOCM_CD;		/* Dodgy null-modem cable ? */
15349472Sbrian      }
15449472Sbrian      timer_Stop(&dev->Timer);
15549472Sbrian      /* tty_AwaitCarrier() will notice */
15646686Sbrian    } else {
15749472Sbrian      /* Keep waiting */
15849472Sbrian      log_Printf(LogDEBUG, "%s: %s: Still no carrier (%d/%d)\n",
15949472Sbrian                 p->link.name, p->name.full, dev->carrier_seconds,
16053733Sbrian                 dev->dev.cd.delay);
16149472Sbrian      dev->mbits = -1;
16246686Sbrian    }
16346686Sbrian  } else {
16447061Sbrian    change = ombits ^ dev->mbits;
16546686Sbrian    if (change & TIOCM_CD) {
16647061Sbrian      if (dev->mbits & TIOCM_CD)
16746686Sbrian        log_Printf(LogDEBUG, "%s: offline -> online\n", p->link.name);
16846686Sbrian      else {
16946686Sbrian        log_Printf(LogDEBUG, "%s: online -> offline\n", p->link.name);
17046686Sbrian        log_Printf(LogPHASE, "%s: Carrier lost\n", p->link.name);
17146686Sbrian        datalink_Down(p->dl, CLOSE_NORMAL);
17247061Sbrian        timer_Stop(&dev->Timer);
17346686Sbrian      }
17446686Sbrian    } else
17546686Sbrian      log_Printf(LogDEBUG, "%s: Still %sline\n", p->link.name,
17647061Sbrian                 Online(dev) ? "on" : "off");
17746686Sbrian  }
17846686Sbrian}
17946686Sbrian
18046686Sbrianstatic void
18146686Sbriantty_StartTimer(struct physical *p)
18246686Sbrian{
18347061Sbrian  struct ttydevice *dev = device2tty(p->handler);
18447061Sbrian
18547061Sbrian  timer_Stop(&dev->Timer);
18649472Sbrian  dev->Timer.load = SECTICKS;
18747061Sbrian  dev->Timer.func = tty_Timeout;
18847061Sbrian  dev->Timer.name = "tty CD";
18947061Sbrian  dev->Timer.arg = p;
19046686Sbrian  log_Printf(LogDEBUG, "%s: Using tty_Timeout [%p]\n",
19146686Sbrian             p->link.name, tty_Timeout);
19247061Sbrian  timer_Start(&dev->Timer);
19346686Sbrian}
19446686Sbrian
19546686Sbrianstatic int
19649472Sbriantty_AwaitCarrier(struct physical *p)
19749472Sbrian{
19849472Sbrian  struct ttydevice *dev = device2tty(p->handler);
19949472Sbrian
20053733Sbrian  if (dev->dev.cd.necessity == CD_NOTREQUIRED || physical_IsSync(p))
20149472Sbrian    return CARRIER_OK;
20249472Sbrian
20349472Sbrian  if (dev->mbits == -1) {
20449472Sbrian    if (dev->Timer.state == TIMER_STOPPED) {
20549472Sbrian      dev->carrier_seconds = 0;
20649472Sbrian      tty_StartTimer(p);
20749472Sbrian    }
20849472Sbrian    return CARRIER_PENDING;			/* Not yet ! */
20949472Sbrian  }
21049472Sbrian
21154569Sbrian  return Online(dev) ? CARRIER_OK : CARRIER_LOST;
21249472Sbrian}
21349472Sbrian
21493418Sbrian#ifdef NONETGRAPH
21593418Sbrian#define tty_SetAsyncParams	NULL
21693418Sbrian#define tty_Write		NULL
21793418Sbrian#define tty_Read		NULL
21893418Sbrian#else
21993418Sbrian
22049472Sbrianstatic int
22193418Sbrianisngtty(struct ttydevice *dev)
22293418Sbrian{
22393418Sbrian  return dev->real.fd != -1;
22493418Sbrian}
22593418Sbrian
22693418Sbrianstatic void
22793418Sbriantty_SetAsyncParams(struct physical *p, u_int32_t mymap, u_int32_t hismap)
22893418Sbrian{
22993418Sbrian  struct ttydevice *dev = device2tty(p->handler);
230122758Sharti  char asyncpath[NG_PATHSIZ];
23193418Sbrian  struct ng_async_cfg cfg;
23293418Sbrian
23393418Sbrian  if (isngtty(dev)) {
23493418Sbrian    /* Configure the async converter node */
23593418Sbrian
23693418Sbrian    snprintf(asyncpath, sizeof asyncpath, ".:%s", dev->hook);
23793418Sbrian    memset(&cfg, 0, sizeof cfg);
23893418Sbrian    cfg.enabled = 1;
23993418Sbrian    cfg.accm = mymap | hismap;
24093418Sbrian    cfg.amru = MAX_MTU;
24193418Sbrian    cfg.smru = MAX_MRU;
24293418Sbrian    log_Printf(LogDEBUG, "Configure async node at %s\n", asyncpath);
24393418Sbrian    if (NgSendMsg(dev->cs, asyncpath, NGM_ASYNC_COOKIE,
24493418Sbrian                  NGM_ASYNC_CMD_SET_CONFIG, &cfg, sizeof cfg) < 0)
24593418Sbrian      log_Printf(LogWARN, "%s: Can't configure async node at %s\n",
24693418Sbrian                 p->link.name, asyncpath);
24793418Sbrian  } else
24893418Sbrian    /* No netgraph node, just config the async layer */
24993418Sbrian    async_SetLinkParams(&p->async, mymap, hismap);
25093418Sbrian}
25193418Sbrian
25293418Sbrianstatic int
25393418SbrianLoadLineDiscipline(struct physical *p)
25493418Sbrian{
25593418Sbrian  struct ttydevice *dev = device2tty(p->handler);
25693418Sbrian  u_char rbuf[sizeof(struct ng_mesg) + sizeof(struct nodeinfo)];
25793418Sbrian  struct ng_mesg *reply;
25893418Sbrian  struct nodeinfo *info;
259122758Sharti  char ttypath[NG_NODESIZ];
26093418Sbrian  struct ngm_mkpeer ngm;
26193418Sbrian  struct ngm_connect ngc;
262134789Sbrian  int ldisc, cs, ds, hot;
263134789Sbrian  unsigned speed;
26493418Sbrian
26595171Sbrian  /*
26695171Sbrian   * Don't use the netgraph line discipline for now.  Using it works, but
26795171Sbrian   * carrier cannot be detected via TIOCMGET and the device doesn't become
26895171Sbrian   * selectable with 0 bytes to read when carrier is lost :(
26995171Sbrian   */
27095171Sbrian  return 0;
27195171Sbrian
27293418Sbrian  reply = (struct ng_mesg *)rbuf;
27393418Sbrian  info = (struct nodeinfo *)reply->data;
27493418Sbrian
27593418Sbrian  loadmodules(LOAD_VERBOSLY, "netgraph", "ng_tty", "ng_async", "ng_socket",
27693418Sbrian              NULL);
27793418Sbrian
27893418Sbrian  /* Get the speed before loading the line discipline */
27993418Sbrian  speed = physical_GetSpeed(p);
28093418Sbrian
28193418Sbrian  if (ioctl(p->fd, TIOCGETD, &dev->real.disc) < 0) {
28293418Sbrian    log_Printf(LogDEBUG, "%s: Couldn't get tty line discipline\n",
28393418Sbrian               p->link.name);
28493418Sbrian    return 0;
28593418Sbrian  }
28693418Sbrian  ldisc = NETGRAPHDISC;
28793418Sbrian  if (ID0ioctl(p->fd, TIOCSETD, &ldisc) < 0) {
28893418Sbrian    log_Printf(LogDEBUG, "%s: Couldn't set NETGRAPHDISC line discipline\n",
28993418Sbrian               p->link.name);
29093418Sbrian    return 0;
29193418Sbrian  }
29293418Sbrian
29393418Sbrian  /* Get the name of the tty node */
29493418Sbrian  if (ioctl(p->fd, NGIOCGINFO, info) < 0) {
29593418Sbrian    log_Printf(LogWARN, "%s: ioctl(NGIOCGINFO): %s\n", p->link.name,
29693418Sbrian               strerror(errno));
29793418Sbrian    ID0ioctl(p->fd, TIOCSETD, &dev->real.disc);
29893418Sbrian    return 0;
29993418Sbrian  }
30093418Sbrian  snprintf(ttypath, sizeof ttypath, "%s:", info->name);
30193418Sbrian
30293418Sbrian  /* Create a socket node for our endpoint (and to send messages via) */
30393418Sbrian  if (ID0NgMkSockNode(NULL, &cs, &ds) == -1) {
30493418Sbrian    log_Printf(LogWARN, "%s: NgMkSockNode: %s\n", p->link.name,
30593418Sbrian               strerror(errno));
30693418Sbrian    ID0ioctl(p->fd, TIOCSETD, &dev->real.disc);
30793418Sbrian    return 0;
30893418Sbrian  }
30993418Sbrian
31093418Sbrian  /* Set the ``hot char'' on the TTY node */
31193418Sbrian  hot = HDLC_SYN;
31293418Sbrian  log_Printf(LogDEBUG, "%s: Set tty hotchar to 0x%02x\n", p->link.name, hot);
31393418Sbrian  if (NgSendMsg(cs, ttypath, NGM_TTY_COOKIE,
31493418Sbrian      NGM_TTY_SET_HOTCHAR, &hot, sizeof hot) < 0) {
31593418Sbrian    log_Printf(LogWARN, "%s: Can't set hot char\n", p->link.name);
31693418Sbrian    goto failed;
31793418Sbrian  }
31893418Sbrian
31993418Sbrian  /* Attach an async converter node */
32093418Sbrian  snprintf(ngm.type, sizeof ngm.type, "%s", NG_ASYNC_NODE_TYPE);
32193418Sbrian  snprintf(ngm.ourhook, sizeof ngm.ourhook, "%s", NG_TTY_HOOK);
32293418Sbrian  snprintf(ngm.peerhook, sizeof ngm.peerhook, "%s", NG_ASYNC_HOOK_ASYNC);
32393418Sbrian  log_Printf(LogDEBUG, "%s: Send mkpeer async:%s to %s:%s\n", p->link.name,
32493418Sbrian             ngm.peerhook, ttypath, ngm.ourhook);
32593418Sbrian  if (NgSendMsg(cs, ttypath, NGM_GENERIC_COOKIE,
32693418Sbrian      NGM_MKPEER, &ngm, sizeof ngm) < 0) {
32793418Sbrian    log_Printf(LogWARN, "%s: Can't create %s node\n", p->link.name,
32893418Sbrian               NG_ASYNC_NODE_TYPE);
32993418Sbrian    goto failed;
33093418Sbrian  }
33193418Sbrian
33293418Sbrian  /* Connect the async node to our socket */
33393418Sbrian  snprintf(ngc.path, sizeof ngc.path, "%s%s", ttypath, NG_TTY_HOOK);
33493418Sbrian  snprintf(ngc.peerhook, sizeof ngc.peerhook, "%s", NG_ASYNC_HOOK_SYNC);
33593418Sbrian  memcpy(ngc.ourhook, ngc.peerhook, sizeof ngc.ourhook);
33693418Sbrian  log_Printf(LogDEBUG, "%s: Send connect %s:%s to .:%s\n", p->link.name,
33793418Sbrian             ngc.path, ngc.peerhook, ngc.ourhook);
33893418Sbrian  if (NgSendMsg(cs, ".:", NGM_GENERIC_COOKIE, NGM_CONNECT,
33993418Sbrian      &ngc, sizeof ngc) < 0) {
34093418Sbrian    log_Printf(LogWARN, "%s: Can't connect .:%s -> %s.%s: %s\n",
34193418Sbrian               p->link.name, ngc.ourhook, ngc.path, ngc.peerhook,
34293418Sbrian               strerror(errno));
34393418Sbrian    goto failed;
34493418Sbrian  }
34593418Sbrian
34693418Sbrian  /* Get the async node id */
34793418Sbrian  if (NgSendMsg(cs, ngc.path, NGM_GENERIC_COOKIE, NGM_NODEINFO, NULL, 0) < 0) {
34893418Sbrian    log_Printf(LogWARN, "%s: Can't request async node info at %s: %s\n",
34993418Sbrian               p->link.name, ngc.path, strerror(errno));
35093418Sbrian    goto failed;
35193418Sbrian  }
35293418Sbrian  if (NgRecvMsg(cs, reply, sizeof rbuf, NULL) < 0) {
35393418Sbrian    log_Printf(LogWARN, "%s: Can't obtain async node info at %s: %s\n",
35493418Sbrian               p->link.name, ngc.path, strerror(errno));
35593418Sbrian    goto failed;
35693418Sbrian  }
35793418Sbrian
35893418Sbrian  /* All done, set up our device state */
35993418Sbrian  snprintf(dev->hook, sizeof dev->hook, "%s", ngc.ourhook);
36093418Sbrian  dev->cs = cs;
36193418Sbrian  dev->real.fd = p->fd;
36293418Sbrian  p->fd = ds;
36393418Sbrian  dev->real.speed = speed;
36493418Sbrian  physical_SetSync(p);
36593418Sbrian
36693418Sbrian  tty_SetAsyncParams(p, 0xffffffff, 0xffffffff);
36793418Sbrian  physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
36893418Sbrian  log_Printf(LogPHASE, "%s: Loaded netgraph tty line discipline\n",
36993418Sbrian             p->link.name);
37093418Sbrian
37193418Sbrian  return 1;
37293418Sbrian
37393418Sbrianfailed:
37493418Sbrian  ID0ioctl(p->fd, TIOCSETD, &dev->real.disc);
37593418Sbrian  close(ds);
37693418Sbrian  close(cs);
37793418Sbrian
37893418Sbrian  return 0;
37993418Sbrian}
38093418Sbrian
38193418Sbrianstatic void
38293418SbrianUnloadLineDiscipline(struct physical *p)
38393418Sbrian{
38493418Sbrian  struct ttydevice *dev = device2tty(p->handler);
38593418Sbrian
38693418Sbrian  if (isngtty(dev)) {
38793418Sbrian    if (!physical_SetSpeed(p, dev->real.speed))
38893418Sbrian      log_Printf(LogWARN, "Couldn't reset tty speed to %d\n", dev->real.speed);
38993418Sbrian    dev->real.speed = 0;
39093418Sbrian    close(p->fd);
39193418Sbrian    p->fd = dev->real.fd;
39293418Sbrian    dev->real.fd = -1;
39393418Sbrian    close(dev->cs);
39493418Sbrian    dev->cs = -1;
39593418Sbrian    *dev->hook = '\0';
39693418Sbrian    if (ID0ioctl(p->fd, TIOCSETD, &dev->real.disc) == 0) {
39793418Sbrian      physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
39893418Sbrian      log_Printf(LogPHASE, "%s: Unloaded netgraph tty line discipline\n",
39993418Sbrian                 p->link.name);
40093418Sbrian    } else
40193418Sbrian      log_Printf(LogWARN, "%s: Failed to unload netgraph tty line discipline\n",
40293418Sbrian                 p->link.name);
40393418Sbrian  }
40493418Sbrian}
40593418Sbrian
40693418Sbrianstatic ssize_t
40793418Sbriantty_Write(struct physical *p, const void *v, size_t n)
40893418Sbrian{
40993418Sbrian  struct ttydevice *dev = device2tty(p->handler);
41093418Sbrian
41193418Sbrian  if (isngtty(dev))
412134789Sbrian    return NgSendData(p->fd, dev->hook, v, n) == -1 ? -1 : (ssize_t)n;
41393418Sbrian  else
41493418Sbrian    return write(p->fd, v, n);
41593418Sbrian}
41693418Sbrian
41793418Sbrianstatic ssize_t
41893418Sbriantty_Read(struct physical *p, void *v, size_t n)
41993418Sbrian{
42093418Sbrian  struct ttydevice *dev = device2tty(p->handler);
42193418Sbrian  char hook[sizeof NG_ASYNC_HOOK_SYNC];
42293418Sbrian
42393418Sbrian  if (isngtty(dev))
42493418Sbrian    return NgRecvData(p->fd, v, n, hook);
42593418Sbrian  else
42693418Sbrian    return read(p->fd, v, n);
42793418Sbrian}
42893418Sbrian
42993418Sbrian#endif /* NETGRAPH */
43093418Sbrian
43193418Sbrianstatic int
43246686Sbriantty_Raw(struct physical *p)
43346686Sbrian{
43447061Sbrian  struct ttydevice *dev = device2tty(p->handler);
43547120Sbrian  struct termios ios;
43646686Sbrian  int oldflag;
43746686Sbrian
43849472Sbrian  log_Printf(LogDEBUG, "%s: Entering tty_Raw\n", p->link.name);
43946686Sbrian
44047061Sbrian  if (p->type != PHYS_DIRECT && p->fd >= 0 && !Online(dev))
44158038Sbrian    log_Printf(LogDEBUG, "%s: Raw: descriptor = %d, mbits = %x\n",
44247061Sbrian              p->link.name, p->fd, dev->mbits);
44346686Sbrian
44449472Sbrian  if (!physical_IsSync(p)) {
44593418Sbrian#ifndef NONETGRAPH
44693418Sbrian    if (!LoadLineDiscipline(p))
44793418Sbrian#endif
44893418Sbrian    {
44993418Sbrian      tcgetattr(p->fd, &ios);
45093418Sbrian      cfmakeraw(&ios);
45193418Sbrian      if (p->cfg.rts_cts)
45293418Sbrian        ios.c_cflag |= CLOCAL | CCTS_OFLOW | CRTS_IFLOW;
45393418Sbrian      else
45493418Sbrian        ios.c_cflag |= CLOCAL;
45546686Sbrian
45693418Sbrian      if (p->type != PHYS_DEDICATED)
45793418Sbrian        ios.c_cflag |= HUPCL;
45846686Sbrian
45993418Sbrian      if (tcsetattr(p->fd, TCSANOW, &ios) == -1)
46093418Sbrian        log_Printf(LogWARN, "%s: tcsetattr: Failed configuring device\n",
46193418Sbrian                   p->link.name);
46293418Sbrian    }
46349472Sbrian  }
46446686Sbrian
46546686Sbrian  oldflag = fcntl(p->fd, F_GETFL, 0);
46646686Sbrian  if (oldflag < 0)
46746686Sbrian    return 0;
46846686Sbrian  fcntl(p->fd, F_SETFL, oldflag | O_NONBLOCK);
46946686Sbrian
47046686Sbrian  return 1;
47146686Sbrian}
47246686Sbrian
47346686Sbrianstatic void
47446686Sbriantty_Offline(struct physical *p)
47546686Sbrian{
47647061Sbrian  struct ttydevice *dev = device2tty(p->handler);
47747061Sbrian
47846686Sbrian  if (p->fd >= 0) {
47947061Sbrian    timer_Stop(&dev->Timer);
48049472Sbrian    dev->mbits &= ~TIOCM_DTR;	/* XXX: Hmm, what's this supposed to do ? */
48147061Sbrian    if (Online(dev)) {
48246686Sbrian      struct termios tio;
48346686Sbrian
48446686Sbrian      tcgetattr(p->fd, &tio);
48568127Sbrian      if (cfsetspeed(&tio, B0) == -1 || tcsetattr(p->fd, TCSANOW, &tio) == -1)
48646686Sbrian        log_Printf(LogWARN, "%s: Unable to set physical to speed 0\n",
48746686Sbrian                   p->link.name);
48846686Sbrian    }
48946686Sbrian  }
49046686Sbrian}
49146686Sbrian
49246686Sbrianstatic void
49346686Sbriantty_Cooked(struct physical *p)
49446686Sbrian{
49547061Sbrian  struct ttydevice *dev = device2tty(p->handler);
49646686Sbrian  int oldflag;
49746686Sbrian
49847539Sbrian  tty_Offline(p);	/* In case of emergency close()s */
49947539Sbrian
50046686Sbrian  tcflush(p->fd, TCIOFLUSH);
50149472Sbrian
50268127Sbrian  if (!physical_IsSync(p) && tcsetattr(p->fd, TCSAFLUSH, &dev->ios) == -1)
50368127Sbrian    log_Printf(LogWARN, "%s: tcsetattr: Unable to restore device settings\n",
50468127Sbrian               p->link.name);
50549472Sbrian
50693418Sbrian#ifndef NONETGRAPH
50793418Sbrian  UnloadLineDiscipline(p);
50893418Sbrian#endif
50993418Sbrian
51049472Sbrian  if ((oldflag = fcntl(p->fd, F_GETFL, 0)) != -1)
51149472Sbrian    fcntl(p->fd, F_SETFL, oldflag & ~O_NONBLOCK);
51246686Sbrian}
51346686Sbrian
51446686Sbrianstatic void
51547061Sbriantty_StopTimer(struct physical *p)
51646686Sbrian{
51747061Sbrian  struct ttydevice *dev = device2tty(p->handler);
51847061Sbrian
51947061Sbrian  timer_Stop(&dev->Timer);
52046686Sbrian}
52146686Sbrian
52246686Sbrianstatic void
52347061Sbriantty_Free(struct physical *p)
52446686Sbrian{
52547061Sbrian  struct ttydevice *dev = device2tty(p->handler);
52647061Sbrian
52747539Sbrian  tty_Offline(p);	/* In case of emergency close()s */
52847061Sbrian  free(dev);
52946686Sbrian}
53046686Sbrian
531134789Sbrianstatic unsigned
53246686Sbriantty_Speed(struct physical *p)
53346686Sbrian{
53447120Sbrian  struct termios ios;
53546686Sbrian
53647120Sbrian  if (tcgetattr(p->fd, &ios) == -1)
53746686Sbrian    return 0;
53846686Sbrian
539134789Sbrian  return SpeedToUnsigned(cfgetispeed(&ios));
54046686Sbrian}
54146686Sbrian
54246686Sbrianstatic const char *
54346686Sbriantty_OpenInfo(struct physical *p)
54446686Sbrian{
54547061Sbrian  struct ttydevice *dev = device2tty(p->handler);
54646686Sbrian  static char buf[13];
54746686Sbrian
54847061Sbrian  if (Online(dev))
54946686Sbrian    strcpy(buf, "with");
55046686Sbrian  else
55146686Sbrian    strcpy(buf, "no");
55246686Sbrian  strcat(buf, " carrier");
55349472Sbrian
55446686Sbrian  return buf;
55546686Sbrian}
55646686Sbrian
55796582Sbrianstatic int
55896582Sbriantty_Slot(struct physical *p)
55996582Sbrian{
56096582Sbrian  struct ttyent *ttyp;
56196582Sbrian  int slot;
56296582Sbrian
56396582Sbrian  setttyent();
56496582Sbrian  for (slot = 1; (ttyp = getttyent()); ++slot)
56596582Sbrian    if (!strcmp(ttyp->ty_name, p->name.base)) {
56696582Sbrian      endttyent();
56796582Sbrian      return slot;
56896582Sbrian    }
56996582Sbrian
57096582Sbrian  endttyent();
57196582Sbrian  return -1;
57296582Sbrian}
57396582Sbrian
57447061Sbrianstatic void
57547769Sbriantty_device2iov(struct device *d, struct iovec *iov, int *niov,
576135166Sru               int maxiov __unused,
577135166Sru#ifndef NONETGRAPH
578135166Sru               int *auxfd, int *nauxfd
579135166Sru#else
580135166Sru               int *auxfd __unused, int *nauxfd __unused
581135166Sru#endif
582135166Sru               )
58347061Sbrian{
584196513Sbrian  struct ttydevice *dev;
58547769Sbrian  int sz = physical_MaxDeviceSize();
58647061Sbrian
587196513Sbrian  iov[*niov].iov_base = d = realloc(d, sz);
588196513Sbrian  if (d == NULL) {
58947769Sbrian    log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz);
59047769Sbrian    AbortProgram(EX_OSERR);
59147769Sbrian  }
59247769Sbrian  iov[*niov].iov_len = sz;
59347061Sbrian  (*niov)++;
59447061Sbrian
595196513Sbrian  dev = device2tty(d);
596196513Sbrian
59793418Sbrian#ifndef NONETGRAPH
59893418Sbrian  if (dev->cs >= 0) {
59993418Sbrian    *auxfd = dev->cs;
60093418Sbrian    (*nauxfd)++;
60193418Sbrian  }
60293418Sbrian#endif
60393418Sbrian
60447061Sbrian  if (dev->Timer.state != TIMER_STOPPED) {
60547061Sbrian    timer_Stop(&dev->Timer);
60647061Sbrian    dev->Timer.state = TIMER_RUNNING;
60747061Sbrian  }
60847061Sbrian}
60947061Sbrian
61047061Sbrianstatic struct device basettydevice = {
61146686Sbrian  TTY_DEVICE,
61246686Sbrian  "tty",
61378410Sbrian  0,
61453733Sbrian  { CD_VARIABLE, DEF_TTYCDDELAY },
61549472Sbrian  tty_AwaitCarrier,
61652942Sbrian  NULL,
61746686Sbrian  tty_Raw,
61846686Sbrian  tty_Offline,
61946686Sbrian  tty_Cooked,
62093418Sbrian  tty_SetAsyncParams,
62147061Sbrian  tty_StopTimer,
62247061Sbrian  tty_Free,
62393418Sbrian  tty_Read,
62493418Sbrian  tty_Write,
62547061Sbrian  tty_device2iov,
62646686Sbrian  tty_Speed,
62796582Sbrian  tty_OpenInfo,
62896582Sbrian  tty_Slot
62946686Sbrian};
63047061Sbrian
63147286Sbrianstruct device *
63247286Sbriantty_iov2device(int type, struct physical *p, struct iovec *iov, int *niov,
633135166Sru               int maxiov __unused,
634135166Sru#ifndef NONETGRAPH
635135166Sru               int *auxfd, int *nauxfd
636135166Sru#else
637135166Sru               int *auxfd __unused, int *nauxfd __unused
638135166Sru#endif
639135166Sru               )
64047124Sbrian{
64147286Sbrian  if (type == TTY_DEVICE) {
64247461Sbrian    struct ttydevice *dev = (struct ttydevice *)iov[(*niov)++].iov_base;
64347286Sbrian
64447769Sbrian    dev = realloc(dev, sizeof *dev);	/* Reduce to the correct size */
64547769Sbrian    if (dev == NULL) {
64647769Sbrian      log_Printf(LogALERT, "Failed to allocate memory: %d\n",
64747769Sbrian                 (int)(sizeof *dev));
64847769Sbrian      AbortProgram(EX_OSERR);
64947769Sbrian    }
65047769Sbrian
65193418Sbrian#ifndef NONETGRAPH
65293418Sbrian    if (*nauxfd) {
65393418Sbrian      dev->cs = *auxfd;
65493418Sbrian      (*nauxfd)--;
65593418Sbrian    } else
65693418Sbrian      dev->cs = -1;
65793418Sbrian#endif
65893418Sbrian
65947286Sbrian    /* Refresh function pointers etc */
66047286Sbrian    memcpy(&dev->dev, &basettydevice, sizeof dev->dev);
66147286Sbrian
66247461Sbrian    physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
66347286Sbrian    if (dev->Timer.state != TIMER_STOPPED) {
66447286Sbrian      dev->Timer.state = TIMER_STOPPED;
66547769Sbrian      p->handler = &dev->dev;		/* For the benefit of StartTimer */
66647286Sbrian      tty_StartTimer(p);
66747286Sbrian    }
66847286Sbrian    return &dev->dev;
66947286Sbrian  }
67047286Sbrian
67147286Sbrian  return NULL;
67247286Sbrian}
67347286Sbrian
67447286Sbrianstruct device *
67547286Sbriantty_Create(struct physical *p)
67647286Sbrian{
67747124Sbrian  struct ttydevice *dev;
67847124Sbrian  struct termios ios;
67947124Sbrian  int oldflag;
68047124Sbrian
68147286Sbrian  if (p->fd < 0 || !isatty(p->fd))
68247286Sbrian    /* Don't want this */
68347124Sbrian    return NULL;
68447124Sbrian
68547286Sbrian  if (*p->name.full == '\0') {
68647286Sbrian    physical_SetDevice(p, ttyname(p->fd));
68747286Sbrian    log_Printf(LogDEBUG, "%s: Input is a tty (%s)\n",
68847286Sbrian               p->link.name, p->name.full);
68947286Sbrian  } else
69047286Sbrian    log_Printf(LogDEBUG, "%s: Opened %s\n", p->link.name, p->name.full);
69147286Sbrian
69247286Sbrian  /* We're gonna return a ttydevice (unless something goes horribly wrong) */
69347286Sbrian
69447286Sbrian  if ((dev = malloc(sizeof *dev)) == NULL) {
69547286Sbrian    /* Complete failure - parent doesn't continue trying to ``create'' */
69647286Sbrian    close(p->fd);
69747286Sbrian    p->fd = -1;
69847286Sbrian    return NULL;
69947286Sbrian  }
70047286Sbrian
70147124Sbrian  memcpy(&dev->dev, &basettydevice, sizeof dev->dev);
70247250Sbrian  memset(&dev->Timer, '\0', sizeof dev->Timer);
70349472Sbrian  dev->mbits = -1;
70493418Sbrian#ifndef NONETGRAPH
70593418Sbrian  dev->real.speed = 0;
70693418Sbrian  dev->real.fd = -1;
70793418Sbrian  dev->real.disc = -1;
70893418Sbrian  *dev->hook = '\0';
70993418Sbrian#endif
71047124Sbrian  tcgetattr(p->fd, &ios);
71147124Sbrian  dev->ios = ios;
71247124Sbrian
71353733Sbrian  if (p->cfg.cd.necessity != CD_DEFAULT)
71453733Sbrian    /* Any override is ok for the tty device */
71553733Sbrian    dev->dev.cd = p->cfg.cd;
71653733Sbrian
71747286Sbrian  log_Printf(LogDEBUG, "%s: tty_Create: physical (get): fd = %d,"
71847124Sbrian             " iflag = %lx, oflag = %lx, cflag = %lx\n", p->link.name, p->fd,
71947124Sbrian             (u_long)ios.c_iflag, (u_long)ios.c_oflag, (u_long)ios.c_cflag);
72047124Sbrian
72147124Sbrian  cfmakeraw(&ios);
72247124Sbrian  if (p->cfg.rts_cts)
72347124Sbrian    ios.c_cflag |= CLOCAL | CCTS_OFLOW | CRTS_IFLOW;
72447124Sbrian  else {
72547124Sbrian    ios.c_cflag |= CLOCAL;
72647124Sbrian    ios.c_iflag |= IXOFF;
72747124Sbrian  }
72847124Sbrian  ios.c_iflag |= IXON;
72949588Sbrian  if (p->type != PHYS_DEDICATED)
73047124Sbrian    ios.c_cflag |= HUPCL;
73147124Sbrian
73247124Sbrian  if (p->type != PHYS_DIRECT) {
73347124Sbrian      /* Change tty speed when we're not in -direct mode */
73447124Sbrian      ios.c_cflag &= ~(CSIZE | PARODD | PARENB);
73547124Sbrian      ios.c_cflag |= p->cfg.parity;
736134789Sbrian      if (cfsetspeed(&ios, UnsignedToSpeed(p->cfg.speed)) == -1)
73747124Sbrian	log_Printf(LogWARN, "%s: %s: Unable to set speed to %d\n",
73847124Sbrian		  p->link.name, p->name.full, p->cfg.speed);
73947124Sbrian  }
74068127Sbrian
74168127Sbrian  if (tcsetattr(p->fd, TCSADRAIN, &ios) == -1) {
74268127Sbrian    log_Printf(LogWARN, "%s: tcsetattr: Failed configuring device\n",
74368127Sbrian               p->link.name);
74468127Sbrian    if (p->type != PHYS_DIRECT && p->cfg.speed > 115200)
74568127Sbrian      log_Printf(LogWARN, "%.*s             Perhaps the speed is unsupported\n",
74668127Sbrian                 (int)strlen(p->link.name), "");
74768127Sbrian  }
74868127Sbrian
74947124Sbrian  log_Printf(LogDEBUG, "%s: physical (put): iflag = %lx, oflag = %lx, "
75047124Sbrian            "cflag = %lx\n", p->link.name, (u_long)ios.c_iflag,
75147124Sbrian            (u_long)ios.c_oflag, (u_long)ios.c_cflag);
75247124Sbrian
75347124Sbrian  oldflag = fcntl(p->fd, F_GETFL, 0);
75447124Sbrian  if (oldflag < 0) {
75547286Sbrian    /* Complete failure - parent doesn't continue trying to ``create'' */
75647286Sbrian
75747124Sbrian    log_Printf(LogWARN, "%s: Open: Cannot get physical flags: %s\n",
75847124Sbrian               p->link.name, strerror(errno));
75947286Sbrian    tty_Cooked(p);
76047286Sbrian    close(p->fd);
76147286Sbrian    p->fd = -1;
76249472Sbrian    free(dev);
76347124Sbrian    return NULL;
76447124Sbrian  } else
76547124Sbrian    fcntl(p->fd, F_SETFL, oldflag & ~O_NONBLOCK);
76647124Sbrian
76747461Sbrian  physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
76847124Sbrian
76947124Sbrian  return &dev->dev;
77047124Sbrian}
771