2 * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/param.h>
31 #if defined(__OpenBSD__) || defined(__NetBSD__)
32 #include <sys/ioctl.h>
47 #include <netgraph/ng_async.h>
48 #include <netgraph/ng_message.h>
49 #include <netgraph/ng_ppp.h>
50 #include <netgraph/ng_tty.h>
60 #include "throughput.h"
66 #include "descriptor.h"
78 #if defined(__mac68k__) || defined(__macppc__)
81 #define CRTS_IFLOW CDTRCTS
82 #define CCTS_OFLOW CDTRCTS
85 #define Online(dev) ((dev)->mbits & TIOCM_CD)
88 struct device dev
; /* What struct physical knows about */
89 struct pppTimer Timer
; /* CD checks */
90 int mbits
; /* Current DCD status */
91 int carrier_seconds
; /* seconds before CD is *required* */
94 unsigned speed
; /* Pre-line-discipline speed */
95 int fd
; /* Pre-line-discipline fd */
96 int disc
; /* Old line-discipline */
98 char hook
[sizeof NG_ASYNC_HOOK_SYNC
]; /* our ng_socket hook */
99 int cs
; /* A netgraph control socket (maybe) */
101 struct termios ios
; /* To be able to reset from raw mode */
104 #define device2tty(d) ((d)->type == TTY_DEVICE ? (struct ttydevice *)d : NULL)
109 return sizeof(struct ttydevice
);
113 * tty_Timeout() watches the DCD signal and mentions it if it's status
117 tty_Timeout(void *data
)
119 struct physical
*p
= data
;
120 struct ttydevice
*dev
= device2tty(p
->handler
);
123 timer_Stop(&dev
->Timer
);
124 dev
->Timer
.load
= SECTICKS
; /* Once a second please */
125 timer_Start(&dev
->Timer
);
129 if (ioctl(p
->fd
, TIOCMGET
, &dev
->mbits
) < 0) {
130 /* we must be a pty ? */
131 if (p
->cfg
.cd
.necessity
!= CD_DEFAULT
)
132 log_Printf(LogWARN
, "%s: Carrier ioctl not supported, "
133 "using ``set cd off''\n", p
->link
.name
);
134 timer_Stop(&dev
->Timer
);
135 dev
->mbits
= TIOCM_CD
;
142 /* First time looking for carrier */
144 log_Printf(LogPHASE
, "%s: %s: CD detected\n", p
->link
.name
, p
->name
.full
);
145 else if (++dev
->carrier_seconds
>= dev
->dev
.cd
.delay
) {
146 if (dev
->dev
.cd
.necessity
== CD_REQUIRED
)
147 log_Printf(LogPHASE
, "%s: %s: Required CD not detected\n",
148 p
->link
.name
, p
->name
.full
);
150 log_Printf(LogPHASE
, "%s: %s doesn't support CD\n",
151 p
->link
.name
, p
->name
.full
);
152 dev
->mbits
= TIOCM_CD
; /* Dodgy null-modem cable ? */
154 timer_Stop(&dev
->Timer
);
155 /* tty_AwaitCarrier() will notice */
158 log_Printf(LogDEBUG
, "%s: %s: Still no carrier (%d/%d)\n",
159 p
->link
.name
, p
->name
.full
, dev
->carrier_seconds
,
164 change
= ombits
^ dev
->mbits
;
165 if (change
& TIOCM_CD
) {
166 if (dev
->mbits
& TIOCM_CD
)
167 log_Printf(LogDEBUG
, "%s: offline -> online\n", p
->link
.name
);
169 log_Printf(LogDEBUG
, "%s: online -> offline\n", p
->link
.name
);
170 log_Printf(LogPHASE
, "%s: Carrier lost\n", p
->link
.name
);
171 datalink_Down(p
->dl
, CLOSE_NORMAL
);
172 timer_Stop(&dev
->Timer
);
175 log_Printf(LogDEBUG
, "%s: Still %sline\n", p
->link
.name
,
176 Online(dev
) ? "on" : "off");
181 tty_StartTimer(struct physical
*p
)
183 struct ttydevice
*dev
= device2tty(p
->handler
);
185 timer_Stop(&dev
->Timer
);
186 dev
->Timer
.load
= SECTICKS
;
187 dev
->Timer
.func
= tty_Timeout
;
188 dev
->Timer
.name
= "tty CD";
190 log_Printf(LogDEBUG
, "%s: Using tty_Timeout [%p]\n",
191 p
->link
.name
, tty_Timeout
);
192 timer_Start(&dev
->Timer
);
196 tty_AwaitCarrier(struct physical
*p
)
198 struct ttydevice
*dev
= device2tty(p
->handler
);
200 if (dev
->dev
.cd
.necessity
== CD_NOTREQUIRED
|| physical_IsSync(p
))
203 if (dev
->mbits
== -1) {
204 if (dev
->Timer
.state
== TIMER_STOPPED
) {
205 dev
->carrier_seconds
= 0;
208 return CARRIER_PENDING
; /* Not yet ! */
211 return Online(dev
) ? CARRIER_OK
: CARRIER_LOST
;
215 #define tty_SetAsyncParams NULL
216 #define tty_Write NULL
217 #define tty_Read NULL
221 isngtty(struct ttydevice
*dev
)
223 return dev
->real
.fd
!= -1;
227 tty_SetAsyncParams(struct physical
*p
, u_int32_t mymap
, u_int32_t hismap
)
229 struct ttydevice
*dev
= device2tty(p
->handler
);
230 char asyncpath
[NG_PATHSIZ
];
231 struct ng_async_cfg cfg
;
234 /* Configure the async converter node */
236 snprintf(asyncpath
, sizeof asyncpath
, ".:%s", dev
->hook
);
237 memset(&cfg
, 0, sizeof cfg
);
239 cfg
.accm
= mymap
| hismap
;
242 log_Printf(LogDEBUG
, "Configure async node at %s\n", asyncpath
);
243 if (NgSendMsg(dev
->cs
, asyncpath
, NGM_ASYNC_COOKIE
,
244 NGM_ASYNC_CMD_SET_CONFIG
, &cfg
, sizeof cfg
) < 0)
245 log_Printf(LogWARN
, "%s: Can't configure async node at %s\n",
246 p
->link
.name
, asyncpath
);
248 /* No netgraph node, just config the async layer */
249 async_SetLinkParams(&p
->async
, mymap
, hismap
);
253 LoadLineDiscipline(struct physical
*p
)
255 struct ttydevice
*dev
= device2tty(p
->handler
);
256 u_char rbuf
[sizeof(struct ng_mesg
) + sizeof(struct nodeinfo
)];
257 struct ng_mesg
*reply
;
258 struct nodeinfo
*info
;
259 char ttypath
[NG_NODESIZ
];
260 struct ngm_mkpeer ngm
;
261 struct ngm_connect ngc
;
262 int ldisc
, cs
, ds
, hot
;
266 * Don't use the netgraph line discipline for now. Using it works, but
267 * carrier cannot be detected via TIOCMGET and the device doesn't become
268 * selectable with 0 bytes to read when carrier is lost :(
272 reply
= (struct ng_mesg
*)rbuf
;
273 info
= (struct nodeinfo
*)reply
->data
;
275 loadmodules(LOAD_VERBOSLY
, "netgraph", "ng_tty", "ng_async", "ng_socket",
278 /* Get the speed before loading the line discipline */
279 speed
= physical_GetSpeed(p
);
281 if (ioctl(p
->fd
, TIOCGETD
, &dev
->real
.disc
) < 0) {
282 log_Printf(LogDEBUG
, "%s: Couldn't get tty line discipline\n",
286 ldisc
= NETGRAPHDISC
;
287 if (ID0ioctl(p
->fd
, TIOCSETD
, &ldisc
) < 0) {
288 log_Printf(LogDEBUG
, "%s: Couldn't set NETGRAPHDISC line discipline\n",
293 /* Get the name of the tty node */
294 if (ioctl(p
->fd
, NGIOCGINFO
, info
) < 0) {
295 log_Printf(LogWARN
, "%s: ioctl(NGIOCGINFO): %s\n", p
->link
.name
,
297 ID0ioctl(p
->fd
, TIOCSETD
, &dev
->real
.disc
);
300 snprintf(ttypath
, sizeof ttypath
, "%s:", info
->name
);
302 /* Create a socket node for our endpoint (and to send messages via) */
303 if (ID0NgMkSockNode(NULL
, &cs
, &ds
) == -1) {
304 log_Printf(LogWARN
, "%s: NgMkSockNode: %s\n", p
->link
.name
,
306 ID0ioctl(p
->fd
, TIOCSETD
, &dev
->real
.disc
);
310 /* Set the ``hot char'' on the TTY node */
312 log_Printf(LogDEBUG
, "%s: Set tty hotchar to 0x%02x\n", p
->link
.name
, hot
);
313 if (NgSendMsg(cs
, ttypath
, NGM_TTY_COOKIE
,
314 NGM_TTY_SET_HOTCHAR
, &hot
, sizeof hot
) < 0) {
315 log_Printf(LogWARN
, "%s: Can't set hot char\n", p
->link
.name
);
319 /* Attach an async converter node */
320 snprintf(ngm
.type
, sizeof ngm
.type
, "%s", NG_ASYNC_NODE_TYPE
);
321 snprintf(ngm
.ourhook
, sizeof ngm
.ourhook
, "%s", NG_TTY_HOOK
);
322 snprintf(ngm
.peerhook
, sizeof ngm
.peerhook
, "%s", NG_ASYNC_HOOK_ASYNC
);
323 log_Printf(LogDEBUG
, "%s: Send mkpeer async:%s to %s:%s\n", p
->link
.name
,
324 ngm
.peerhook
, ttypath
, ngm
.ourhook
);
325 if (NgSendMsg(cs
, ttypath
, NGM_GENERIC_COOKIE
,
326 NGM_MKPEER
, &ngm
, sizeof ngm
) < 0) {
327 log_Printf(LogWARN
, "%s: Can't create %s node\n", p
->link
.name
,
332 /* Connect the async node to our socket */
333 snprintf(ngc
.path
, sizeof ngc
.path
, "%s%s", ttypath
, NG_TTY_HOOK
);
334 snprintf(ngc
.peerhook
, sizeof ngc
.peerhook
, "%s", NG_ASYNC_HOOK_SYNC
);
335 memcpy(ngc
.ourhook
, ngc
.peerhook
, sizeof ngc
.ourhook
);
336 log_Printf(LogDEBUG
, "%s: Send connect %s:%s to .:%s\n", p
->link
.name
,
337 ngc
.path
, ngc
.peerhook
, ngc
.ourhook
);
338 if (NgSendMsg(cs
, ".:", NGM_GENERIC_COOKIE
, NGM_CONNECT
,
339 &ngc
, sizeof ngc
) < 0) {
340 log_Printf(LogWARN
, "%s: Can't connect .:%s -> %s.%s: %s\n",
341 p
->link
.name
, ngc
.ourhook
, ngc
.path
, ngc
.peerhook
,
346 /* Get the async node id */
347 if (NgSendMsg(cs
, ngc
.path
, NGM_GENERIC_COOKIE
, NGM_NODEINFO
, NULL
, 0) < 0) {
348 log_Printf(LogWARN
, "%s: Can't request async node info at %s: %s\n",
349 p
->link
.name
, ngc
.path
, strerror(errno
));
352 if (NgRecvMsg(cs
, reply
, sizeof rbuf
, NULL
) < 0) {
353 log_Printf(LogWARN
, "%s: Can't obtain async node info at %s: %s\n",
354 p
->link
.name
, ngc
.path
, strerror(errno
));
358 /* All done, set up our device state */
359 snprintf(dev
->hook
, sizeof dev
->hook
, "%s", ngc
.ourhook
);
361 dev
->real
.fd
= p
->fd
;
363 dev
->real
.speed
= speed
;
366 tty_SetAsyncParams(p
, 0xffffffff, 0xffffffff);
367 physical_SetupStack(p
, dev
->dev
.name
, PHYSICAL_NOFORCE
);
368 log_Printf(LogPHASE
, "%s: Loaded netgraph tty line discipline\n",
374 ID0ioctl(p
->fd
, TIOCSETD
, &dev
->real
.disc
);
382 UnloadLineDiscipline(struct physical
*p
)
384 struct ttydevice
*dev
= device2tty(p
->handler
);
387 if (!physical_SetSpeed(p
, dev
->real
.speed
))
388 log_Printf(LogWARN
, "Couldn't reset tty speed to %d\n", dev
->real
.speed
);
391 p
->fd
= dev
->real
.fd
;
396 if (ID0ioctl(p
->fd
, TIOCSETD
, &dev
->real
.disc
) == 0) {
397 physical_SetupStack(p
, dev
->dev
.name
, PHYSICAL_NOFORCE
);
398 log_Printf(LogPHASE
, "%s: Unloaded netgraph tty line discipline\n",
401 log_Printf(LogWARN
, "%s: Failed to unload netgraph tty line discipline\n",
407 tty_Write(struct physical
*p
, const void *v
, size_t n
)
409 struct ttydevice
*dev
= device2tty(p
->handler
);
412 return NgSendData(p
->fd
, dev
->hook
, v
, n
) == -1 ? -1 : (ssize_t
)n
;
414 return write(p
->fd
, v
, n
);
418 tty_Read(struct physical
*p
, void *v
, size_t n
)
420 struct ttydevice
*dev
= device2tty(p
->handler
);
421 char hook
[sizeof NG_ASYNC_HOOK_SYNC
];
424 return NgRecvData(p
->fd
, v
, n
, hook
);
426 return read(p
->fd
, v
, n
);
429 #endif /* NETGRAPH */
432 tty_Raw(struct physical
*p
)
434 struct ttydevice
*dev
= device2tty(p
->handler
);
438 log_Printf(LogDEBUG
, "%s: Entering tty_Raw\n", p
->link
.name
);
440 if (p
->type
!= PHYS_DIRECT
&& p
->fd
>= 0 && !Online(dev
))
441 log_Printf(LogDEBUG
, "%s: Raw: descriptor = %d, mbits = %x\n",
442 p
->link
.name
, p
->fd
, dev
->mbits
);
444 if (!physical_IsSync(p
)) {
446 if (!LoadLineDiscipline(p
))
449 tcgetattr(p
->fd
, &ios
);
452 ios
.c_cflag
|= CLOCAL
| CCTS_OFLOW
| CRTS_IFLOW
;
454 ios
.c_cflag
|= CLOCAL
;
456 if (p
->type
!= PHYS_DEDICATED
)
457 ios
.c_cflag
|= HUPCL
;
459 if (tcsetattr(p
->fd
, TCSANOW
, &ios
) == -1)
460 log_Printf(LogWARN
, "%s: tcsetattr: Failed configuring device\n",
465 oldflag
= fcntl(p
->fd
, F_GETFL
, 0);
468 fcntl(p
->fd
, F_SETFL
, oldflag
| O_NONBLOCK
);
474 tty_Offline(struct physical
*p
)
476 struct ttydevice
*dev
= device2tty(p
->handler
);
479 timer_Stop(&dev
->Timer
);
480 dev
->mbits
&= ~TIOCM_DTR
; /* XXX: Hmm, what's this supposed to do ? */
484 tcgetattr(p
->fd
, &tio
);
485 if (cfsetspeed(&tio
, B0
) == -1 || tcsetattr(p
->fd
, TCSANOW
, &tio
) == -1)
486 log_Printf(LogWARN
, "%s: Unable to set physical to speed 0\n",
493 tty_Cooked(struct physical
*p
)
495 struct ttydevice
*dev
= device2tty(p
->handler
);
498 tty_Offline(p
); /* In case of emergency close()s */
500 tcflush(p
->fd
, TCIOFLUSH
);
502 if (!physical_IsSync(p
) && tcsetattr(p
->fd
, TCSAFLUSH
, &dev
->ios
) == -1)
503 log_Printf(LogWARN
, "%s: tcsetattr: Unable to restore device settings\n",
507 UnloadLineDiscipline(p
);
510 if ((oldflag
= fcntl(p
->fd
, F_GETFL
, 0)) != -1)
511 fcntl(p
->fd
, F_SETFL
, oldflag
& ~O_NONBLOCK
);
515 tty_StopTimer(struct physical
*p
)
517 struct ttydevice
*dev
= device2tty(p
->handler
);
519 timer_Stop(&dev
->Timer
);
523 tty_Free(struct physical
*p
)
525 struct ttydevice
*dev
= device2tty(p
->handler
);
527 tty_Offline(p
); /* In case of emergency close()s */
532 tty_Speed(struct physical
*p
)
536 if (tcgetattr(p
->fd
, &ios
) == -1)
539 return SpeedToUnsigned(cfgetispeed(&ios
));
543 tty_OpenInfo(struct physical
*p
)
545 struct ttydevice
*dev
= device2tty(p
->handler
);
552 strcat(buf
, " carrier");
558 tty_Slot(struct physical
*p
)
564 for (slot
= 1; (ttyp
= getttyent()); ++slot
)
565 if (!strcmp(ttyp
->ty_name
, p
->name
.base
)) {
575 tty_device2iov(struct device
*d
, struct iovec
*iov
, int *niov
,
578 int *auxfd
, int *nauxfd
580 int *auxfd __unused
, int *nauxfd __unused
584 struct ttydevice
*dev
;
585 int sz
= physical_MaxDeviceSize();
587 iov
[*niov
].iov_base
= d
= realloc(d
, sz
);
589 log_Printf(LogALERT
, "Failed to allocate memory: %d\n", sz
);
590 AbortProgram(EX_OSERR
);
592 iov
[*niov
].iov_len
= sz
;
604 if (dev
->Timer
.state
!= TIMER_STOPPED
) {
605 timer_Stop(&dev
->Timer
);
606 dev
->Timer
.state
= TIMER_RUNNING
;
610 static struct device basettydevice
= {
614 { CD_VARIABLE
, DEF_TTYCDDELAY
},
632 tty_iov2device(int type
, struct physical
*p
, struct iovec
*iov
, int *niov
,
635 int *auxfd
, int *nauxfd
637 int *auxfd __unused
, int *nauxfd __unused
641 if (type
== TTY_DEVICE
) {
642 struct ttydevice
*dev
= (struct ttydevice
*)iov
[(*niov
)++].iov_base
;
644 dev
= realloc(dev
, sizeof *dev
); /* Reduce to the correct size */
646 log_Printf(LogALERT
, "Failed to allocate memory: %d\n",
648 AbortProgram(EX_OSERR
);
659 /* Refresh function pointers etc */
660 memcpy(&dev
->dev
, &basettydevice
, sizeof dev
->dev
);
662 physical_SetupStack(p
, dev
->dev
.name
, PHYSICAL_NOFORCE
);
663 if (dev
->Timer
.state
!= TIMER_STOPPED
) {
664 dev
->Timer
.state
= TIMER_STOPPED
;
665 p
->handler
= &dev
->dev
; /* For the benefit of StartTimer */
675 tty_Create(struct physical
*p
)
677 struct ttydevice
*dev
;
681 if (p
->fd
< 0 || !isatty(p
->fd
))
682 /* Don't want this */
685 if (*p
->name
.full
== '\0') {
686 physical_SetDevice(p
, ttyname(p
->fd
));
687 log_Printf(LogDEBUG
, "%s: Input is a tty (%s)\n",
688 p
->link
.name
, p
->name
.full
);
690 log_Printf(LogDEBUG
, "%s: Opened %s\n", p
->link
.name
, p
->name
.full
);
692 /* We're gonna return a ttydevice (unless something goes horribly wrong) */
694 if ((dev
= malloc(sizeof *dev
)) == NULL
) {
695 /* Complete failure - parent doesn't continue trying to ``create'' */
701 memcpy(&dev
->dev
, &basettydevice
, sizeof dev
->dev
);
702 memset(&dev
->Timer
, '\0', sizeof dev
->Timer
);
710 tcgetattr(p
->fd
, &ios
);
713 if (p
->cfg
.cd
.necessity
!= CD_DEFAULT
)
714 /* Any override is ok for the tty device */
715 dev
->dev
.cd
= p
->cfg
.cd
;
717 log_Printf(LogDEBUG
, "%s: tty_Create: physical (get): fd = %d,"
718 " iflag = %lx, oflag = %lx, cflag = %lx\n", p
->link
.name
, p
->fd
,
719 (u_long
)ios
.c_iflag
, (u_long
)ios
.c_oflag
, (u_long
)ios
.c_cflag
);
723 ios
.c_cflag
|= CLOCAL
| CCTS_OFLOW
| CRTS_IFLOW
;
725 ios
.c_cflag
|= CLOCAL
;
726 ios
.c_iflag
|= IXOFF
;
729 if (p
->type
!= PHYS_DEDICATED
)
730 ios
.c_cflag
|= HUPCL
;
732 if (p
->type
!= PHYS_DIRECT
) {
733 /* Change tty speed when we're not in -direct mode */
734 ios
.c_cflag
&= ~(CSIZE
| PARODD
| PARENB
);
735 ios
.c_cflag
|= p
->cfg
.parity
;
736 if (cfsetspeed(&ios
, UnsignedToSpeed(p
->cfg
.speed
)) == -1)
737 log_Printf(LogWARN
, "%s: %s: Unable to set speed to %d\n",
738 p
->link
.name
, p
->name
.full
, p
->cfg
.speed
);
741 if (tcsetattr(p
->fd
, TCSADRAIN
, &ios
) == -1) {
742 log_Printf(LogWARN
, "%s: tcsetattr: Failed configuring device\n",
744 if (p
->type
!= PHYS_DIRECT
&& p
->cfg
.speed
> 115200)
745 log_Printf(LogWARN
, "%.*s Perhaps the speed is unsupported\n",
746 (int)strlen(p
->link
.name
), "");
749 log_Printf(LogDEBUG
, "%s: physical (put): iflag = %lx, oflag = %lx, "
750 "cflag = %lx\n", p
->link
.name
, (u_long
)ios
.c_iflag
,
751 (u_long
)ios
.c_oflag
, (u_long
)ios
.c_cflag
);
753 oldflag
= fcntl(p
->fd
, F_GETFL
, 0);
755 /* Complete failure - parent doesn't continue trying to ``create'' */
757 log_Printf(LogWARN
, "%s: Open: Cannot get physical flags: %s\n",
758 p
->link
.name
, strerror(errno
));
765 fcntl(p
->fd
, F_SETFL
, oldflag
& ~O_NONBLOCK
);
767 physical_SetupStack(p
, dev
->dev
.name
, PHYSICAL_NOFORCE
);