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
26 * $FreeBSD: src/usr.sbin/ppp/tty.c,v 1.21.2.3 2002/09/01 02:12:32 brian Exp $
27 * $DragonFly: src/usr.sbin/ppp/tty.c,v 1.4 2007/06/04 00:40:32 swildner Exp $
30 #include <sys/param.h>
32 #if defined(__OpenBSD__) || defined(__NetBSD__)
33 #include <sys/ioctl.h>
48 #include <netgraph/async/ng_async.h>
49 #include <netgraph/ng_message.h>
50 #include <netgraph/ppp/ng_ppp.h>
51 #include <netgraph/tty/ng_tty.h>
61 #include "throughput.h"
67 #include "descriptor.h"
79 #if defined(__mac68k__) || defined(__macppc__)
82 #define CRTS_IFLOW CDTRCTS
83 #define CCTS_OFLOW CDTRCTS
86 #define Online(dev) ((dev)->mbits & TIOCM_CD)
89 struct device dev
; /* What struct physical knows about */
90 struct pppTimer Timer
; /* CD checks */
91 int mbits
; /* Current DCD status */
92 int carrier_seconds
; /* seconds before CD is *required* */
95 int speed
; /* Pre-line-discipline speed */
96 int fd
; /* Pre-line-discipline fd */
97 int disc
; /* Old line-discipline */
99 char hook
[sizeof NG_ASYNC_HOOK_SYNC
]; /* our ng_socket hook */
100 int cs
; /* A netgraph control socket (maybe) */
102 struct termios ios
; /* To be able to reset from raw mode */
105 #define device2tty(d) ((d)->type == TTY_DEVICE ? (struct ttydevice *)d : NULL)
110 return sizeof(struct ttydevice
);
114 * tty_Timeout() watches the DCD signal and mentions it if it's status
118 tty_Timeout(void *data
)
120 struct physical
*p
= data
;
121 struct ttydevice
*dev
= device2tty(p
->handler
);
124 timer_Stop(&dev
->Timer
);
125 dev
->Timer
.load
= SECTICKS
; /* Once a second please */
126 timer_Start(&dev
->Timer
);
130 if (ioctl(p
->fd
, TIOCMGET
, &dev
->mbits
) < 0) {
131 /* we must be a pty ? */
132 if (p
->cfg
.cd
.necessity
!= CD_DEFAULT
)
133 log_Printf(LogWARN
, "%s: Carrier ioctl not supported, "
134 "using ``set cd off''\n", p
->link
.name
);
135 timer_Stop(&dev
->Timer
);
136 dev
->mbits
= TIOCM_CD
;
143 /* First time looking for carrier */
145 log_Printf(LogPHASE
, "%s: %s: CD detected\n", p
->link
.name
, p
->name
.full
);
146 else if (++dev
->carrier_seconds
>= dev
->dev
.cd
.delay
) {
147 if (dev
->dev
.cd
.necessity
== CD_REQUIRED
)
148 log_Printf(LogPHASE
, "%s: %s: Required CD not detected\n",
149 p
->link
.name
, p
->name
.full
);
151 log_Printf(LogPHASE
, "%s: %s doesn't support CD\n",
152 p
->link
.name
, p
->name
.full
);
153 dev
->mbits
= TIOCM_CD
; /* Dodgy null-modem cable ? */
155 timer_Stop(&dev
->Timer
);
156 /* tty_AwaitCarrier() will notice */
159 log_Printf(LogDEBUG
, "%s: %s: Still no carrier (%d/%d)\n",
160 p
->link
.name
, p
->name
.full
, dev
->carrier_seconds
,
165 change
= ombits
^ dev
->mbits
;
166 if (change
& TIOCM_CD
) {
167 if (dev
->mbits
& TIOCM_CD
)
168 log_Printf(LogDEBUG
, "%s: offline -> online\n", p
->link
.name
);
170 log_Printf(LogDEBUG
, "%s: online -> offline\n", p
->link
.name
);
171 log_Printf(LogPHASE
, "%s: Carrier lost\n", p
->link
.name
);
172 datalink_Down(p
->dl
, CLOSE_NORMAL
);
173 timer_Stop(&dev
->Timer
);
176 log_Printf(LogDEBUG
, "%s: Still %sline\n", p
->link
.name
,
177 Online(dev
) ? "on" : "off");
182 tty_StartTimer(struct physical
*p
)
184 struct ttydevice
*dev
= device2tty(p
->handler
);
186 timer_Stop(&dev
->Timer
);
187 dev
->Timer
.load
= SECTICKS
;
188 dev
->Timer
.func
= tty_Timeout
;
189 dev
->Timer
.name
= "tty CD";
191 log_Printf(LogDEBUG
, "%s: Using tty_Timeout [%p]\n",
192 p
->link
.name
, tty_Timeout
);
193 timer_Start(&dev
->Timer
);
197 tty_AwaitCarrier(struct physical
*p
)
199 struct ttydevice
*dev
= device2tty(p
->handler
);
201 if (dev
->dev
.cd
.necessity
== CD_NOTREQUIRED
|| physical_IsSync(p
))
204 if (dev
->mbits
== -1) {
205 if (dev
->Timer
.state
== TIMER_STOPPED
) {
206 dev
->carrier_seconds
= 0;
209 return CARRIER_PENDING
; /* Not yet ! */
212 return Online(dev
) ? CARRIER_OK
: CARRIER_LOST
;
216 #define tty_SetAsyncParams NULL
217 #define tty_Write NULL
218 #define tty_Read NULL
222 isngtty(struct ttydevice
*dev
)
224 return dev
->real
.fd
!= -1;
228 tty_SetAsyncParams(struct physical
*p
, u_int32_t mymap
, u_int32_t hismap
)
230 struct ttydevice
*dev
= device2tty(p
->handler
);
231 char asyncpath
[NG_PATHSIZ
];
232 struct ng_async_cfg cfg
;
235 /* Configure the async converter node */
237 snprintf(asyncpath
, sizeof asyncpath
, ".:%s", dev
->hook
);
238 memset(&cfg
, 0, sizeof cfg
);
240 cfg
.accm
= mymap
| hismap
;
243 log_Printf(LogDEBUG
, "Configure async node at %s\n", asyncpath
);
244 if (NgSendMsg(dev
->cs
, asyncpath
, NGM_ASYNC_COOKIE
,
245 NGM_ASYNC_CMD_SET_CONFIG
, &cfg
, sizeof cfg
) < 0)
246 log_Printf(LogWARN
, "%s: Can't configure async node at %s\n",
247 p
->link
.name
, asyncpath
);
249 /* No netgraph node, just config the async layer */
250 async_SetLinkParams(&p
->async
, mymap
, hismap
);
254 LoadLineDiscipline(struct physical
*p
)
256 struct ttydevice
*dev
= device2tty(p
->handler
);
257 u_char rbuf
[sizeof(struct ng_mesg
) + sizeof(struct nodeinfo
)];
258 struct ng_mesg
*reply
;
259 struct nodeinfo
*info
;
260 char ttypath
[NG_NODESIZ
];
261 struct ngm_mkpeer ngm
;
262 struct ngm_connect ngc
;
263 int ldisc
, cs
, ds
, hot
, speed
;
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 log_Printf(LogPHASE
, "back to speed %d\n", dev
->real
.speed
);
388 if (!physical_SetSpeed(p
, dev
->real
.speed
))
389 log_Printf(LogWARN
, "Couldn't reset tty speed to %d\n", dev
->real
.speed
);
392 p
->fd
= dev
->real
.fd
;
397 if (ID0ioctl(p
->fd
, TIOCSETD
, &dev
->real
.disc
) == 0) {
398 physical_SetupStack(p
, dev
->dev
.name
, PHYSICAL_NOFORCE
);
399 log_Printf(LogPHASE
, "%s: Unloaded netgraph tty line discipline\n",
402 log_Printf(LogWARN
, "%s: Failed to unload netgraph tty line discipline\n",
408 tty_Write(struct physical
*p
, const void *v
, size_t n
)
410 struct ttydevice
*dev
= device2tty(p
->handler
);
413 return NgSendData(p
->fd
, dev
->hook
, v
, n
) == -1 ? -1 : n
;
415 return write(p
->fd
, v
, n
);
419 tty_Read(struct physical
*p
, void *v
, size_t n
)
421 struct ttydevice
*dev
= device2tty(p
->handler
);
422 char hook
[sizeof NG_ASYNC_HOOK_SYNC
];
425 return NgRecvData(p
->fd
, v
, n
, hook
);
427 return read(p
->fd
, v
, n
);
430 #endif /* NETGRAPH */
433 tty_Raw(struct physical
*p
)
435 struct ttydevice
*dev
= device2tty(p
->handler
);
439 log_Printf(LogDEBUG
, "%s: Entering tty_Raw\n", p
->link
.name
);
441 if (p
->type
!= PHYS_DIRECT
&& p
->fd
>= 0 && !Online(dev
))
442 log_Printf(LogDEBUG
, "%s: Raw: descriptor = %d, mbits = %x\n",
443 p
->link
.name
, p
->fd
, dev
->mbits
);
445 if (!physical_IsSync(p
)) {
447 if (!LoadLineDiscipline(p
))
450 tcgetattr(p
->fd
, &ios
);
453 ios
.c_cflag
|= CLOCAL
| CCTS_OFLOW
| CRTS_IFLOW
;
455 ios
.c_cflag
|= CLOCAL
;
457 if (p
->type
!= PHYS_DEDICATED
)
458 ios
.c_cflag
|= HUPCL
;
460 if (tcsetattr(p
->fd
, TCSANOW
, &ios
) == -1)
461 log_Printf(LogWARN
, "%s: tcsetattr: Failed configuring device\n",
466 oldflag
= fcntl(p
->fd
, F_GETFL
, 0);
469 fcntl(p
->fd
, F_SETFL
, oldflag
| O_NONBLOCK
);
475 tty_Offline(struct physical
*p
)
477 struct ttydevice
*dev
= device2tty(p
->handler
);
480 timer_Stop(&dev
->Timer
);
481 dev
->mbits
&= ~TIOCM_DTR
; /* XXX: Hmm, what's this supposed to do ? */
485 tcgetattr(p
->fd
, &tio
);
486 if (cfsetspeed(&tio
, B0
) == -1 || tcsetattr(p
->fd
, TCSANOW
, &tio
) == -1)
487 log_Printf(LogWARN
, "%s: Unable to set physical to speed 0\n",
494 tty_Cooked(struct physical
*p
)
496 struct ttydevice
*dev
= device2tty(p
->handler
);
499 tty_Offline(p
); /* In case of emergency close()s */
501 tcflush(p
->fd
, TCIOFLUSH
);
503 if (!physical_IsSync(p
) && tcsetattr(p
->fd
, TCSAFLUSH
, &dev
->ios
) == -1)
504 log_Printf(LogWARN
, "%s: tcsetattr: Unable to restore device settings\n",
508 UnloadLineDiscipline(p
);
511 if ((oldflag
= fcntl(p
->fd
, F_GETFL
, 0)) != -1)
512 fcntl(p
->fd
, F_SETFL
, oldflag
& ~O_NONBLOCK
);
516 tty_StopTimer(struct physical
*p
)
518 struct ttydevice
*dev
= device2tty(p
->handler
);
520 timer_Stop(&dev
->Timer
);
524 tty_Free(struct physical
*p
)
526 struct ttydevice
*dev
= device2tty(p
->handler
);
528 tty_Offline(p
); /* In case of emergency close()s */
533 tty_Speed(struct physical
*p
)
537 if (tcgetattr(p
->fd
, &ios
) == -1)
540 return SpeedToInt(cfgetispeed(&ios
));
544 tty_OpenInfo(struct physical
*p
)
546 struct ttydevice
*dev
= device2tty(p
->handler
);
553 strcat(buf
, " carrier");
559 tty_Slot(struct physical
*p
)
565 for (slot
= 1; (ttyp
= getttyent()); ++slot
)
566 if (!strcmp(ttyp
->ty_name
, p
->name
.base
)) {
576 tty_device2iov(struct device
*d
, struct iovec
*iov
, int *niov
,
577 int maxiov
, int *auxfd
, int *nauxfd
)
579 struct ttydevice
*dev
= device2tty(d
);
580 int sz
= physical_MaxDeviceSize();
582 iov
[*niov
].iov_base
= realloc(d
, sz
);
583 if (iov
[*niov
].iov_base
== NULL
) {
584 log_Printf(LogALERT
, "Failed to allocate memory: %d\n", sz
);
585 AbortProgram(EX_OSERR
);
587 iov
[*niov
].iov_len
= sz
;
597 if (dev
->Timer
.state
!= TIMER_STOPPED
) {
598 timer_Stop(&dev
->Timer
);
599 dev
->Timer
.state
= TIMER_RUNNING
;
603 static struct device basettydevice
= {
607 { CD_VARIABLE
, DEF_TTYCDDELAY
},
625 tty_iov2device(int type
, struct physical
*p
, struct iovec
*iov
, int *niov
,
626 int maxiov
, int *auxfd
, int *nauxfd
)
628 if (type
== TTY_DEVICE
) {
629 struct ttydevice
*dev
= (struct ttydevice
*)iov
[(*niov
)++].iov_base
;
631 dev
= realloc(dev
, sizeof *dev
); /* Reduce to the correct size */
633 log_Printf(LogALERT
, "Failed to allocate memory: %d\n",
635 AbortProgram(EX_OSERR
);
646 /* Refresh function pointers etc */
647 memcpy(&dev
->dev
, &basettydevice
, sizeof dev
->dev
);
649 physical_SetupStack(p
, dev
->dev
.name
, PHYSICAL_NOFORCE
);
650 if (dev
->Timer
.state
!= TIMER_STOPPED
) {
651 dev
->Timer
.state
= TIMER_STOPPED
;
652 p
->handler
= &dev
->dev
; /* For the benefit of StartTimer */
662 tty_Create(struct physical
*p
)
664 struct ttydevice
*dev
;
668 if (p
->fd
< 0 || !isatty(p
->fd
))
669 /* Don't want this */
672 if (*p
->name
.full
== '\0') {
673 physical_SetDevice(p
, ttyname(p
->fd
));
674 log_Printf(LogDEBUG
, "%s: Input is a tty (%s)\n",
675 p
->link
.name
, p
->name
.full
);
677 log_Printf(LogDEBUG
, "%s: Opened %s\n", p
->link
.name
, p
->name
.full
);
679 /* We're gonna return a ttydevice (unless something goes horribly wrong) */
681 if ((dev
= malloc(sizeof *dev
)) == NULL
) {
682 /* Complete failure - parent doesn't continue trying to ``create'' */
688 memcpy(&dev
->dev
, &basettydevice
, sizeof dev
->dev
);
689 memset(&dev
->Timer
, '\0', sizeof dev
->Timer
);
697 tcgetattr(p
->fd
, &ios
);
700 if (p
->cfg
.cd
.necessity
!= CD_DEFAULT
)
701 /* Any override is ok for the tty device */
702 dev
->dev
.cd
= p
->cfg
.cd
;
704 log_Printf(LogDEBUG
, "%s: tty_Create: physical (get): fd = %d,"
705 " iflag = %lx, oflag = %lx, cflag = %lx\n", p
->link
.name
, p
->fd
,
706 (u_long
)ios
.c_iflag
, (u_long
)ios
.c_oflag
, (u_long
)ios
.c_cflag
);
710 ios
.c_cflag
|= CLOCAL
| CCTS_OFLOW
| CRTS_IFLOW
;
712 ios
.c_cflag
|= CLOCAL
;
713 ios
.c_iflag
|= IXOFF
;
716 if (p
->type
!= PHYS_DEDICATED
)
717 ios
.c_cflag
|= HUPCL
;
719 if (p
->type
!= PHYS_DIRECT
) {
720 /* Change tty speed when we're not in -direct mode */
721 ios
.c_cflag
&= ~(CSIZE
| PARODD
| PARENB
);
722 ios
.c_cflag
|= p
->cfg
.parity
;
723 if (cfsetspeed(&ios
, IntToSpeed(p
->cfg
.speed
)) == -1)
724 log_Printf(LogWARN
, "%s: %s: Unable to set speed to %d\n",
725 p
->link
.name
, p
->name
.full
, p
->cfg
.speed
);
728 if (tcsetattr(p
->fd
, TCSADRAIN
, &ios
) == -1) {
729 log_Printf(LogWARN
, "%s: tcsetattr: Failed configuring device\n",
731 if (p
->type
!= PHYS_DIRECT
&& p
->cfg
.speed
> 115200)
732 log_Printf(LogWARN
, "%.*s Perhaps the speed is unsupported\n",
733 (int)strlen(p
->link
.name
), "");
736 log_Printf(LogDEBUG
, "%s: physical (put): iflag = %lx, oflag = %lx, "
737 "cflag = %lx\n", p
->link
.name
, (u_long
)ios
.c_iflag
,
738 (u_long
)ios
.c_oflag
, (u_long
)ios
.c_cflag
);
740 oldflag
= fcntl(p
->fd
, F_GETFL
, 0);
742 /* Complete failure - parent doesn't continue trying to ``create'' */
744 log_Printf(LogWARN
, "%s: Open: Cannot get physical flags: %s\n",
745 p
->link
.name
, strerror(errno
));
752 fcntl(p
->fd
, F_SETFL
, oldflag
& ~O_NONBLOCK
);
754 physical_SetupStack(p
, dev
->dev
.name
, PHYSICAL_NOFORCE
);