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>
49 #include <netgraph7/async/ng_async.h>
50 #include <netgraph7/ng_message.h>
51 #include <netgraph7/ppp/ng_ppp.h>
52 #include <netgraph7/tty/ng_tty.h>
54 #include <netgraph/async/ng_async.h>
55 #include <netgraph/ng_message.h>
56 #include <netgraph/ppp/ng_ppp.h>
57 #include <netgraph/tty/ng_tty.h>
68 #include "throughput.h"
74 #include "descriptor.h"
86 #if defined(__mac68k__) || defined(__macppc__)
89 #define CRTS_IFLOW CDTRCTS
90 #define CCTS_OFLOW CDTRCTS
93 #define Online(dev) ((dev)->mbits & TIOCM_CD)
96 struct device dev
; /* What struct physical knows about */
97 struct pppTimer Timer
; /* CD checks */
98 int mbits
; /* Current DCD status */
99 int carrier_seconds
; /* seconds before CD is *required* */
102 unsigned speed
; /* Pre-line-discipline speed */
103 int fd
; /* Pre-line-discipline fd */
104 int disc
; /* Old line-discipline */
106 char hook
[sizeof NG_ASYNC_HOOK_SYNC
]; /* our ng_socket hook */
107 int cs
; /* A netgraph control socket (maybe) */
109 struct termios ios
; /* To be able to reset from raw mode */
112 #define device2tty(d) ((d)->type == TTY_DEVICE ? (struct ttydevice *)d : NULL)
117 return sizeof(struct ttydevice
);
121 * tty_Timeout() watches the DCD signal and mentions it if it's status
125 tty_Timeout(void *data
)
127 struct physical
*p
= data
;
128 struct ttydevice
*dev
= device2tty(p
->handler
);
131 timer_Stop(&dev
->Timer
);
132 dev
->Timer
.load
= SECTICKS
; /* Once a second please */
133 timer_Start(&dev
->Timer
);
137 if (ioctl(p
->fd
, TIOCMGET
, &dev
->mbits
) < 0) {
138 /* we must be a pty ? */
139 if (p
->cfg
.cd
.necessity
!= CD_DEFAULT
)
140 log_Printf(LogWARN
, "%s: Carrier ioctl not supported, "
141 "using ``set cd off''\n", p
->link
.name
);
142 timer_Stop(&dev
->Timer
);
143 dev
->mbits
= TIOCM_CD
;
150 /* First time looking for carrier */
152 log_Printf(LogPHASE
, "%s: %s: CD detected\n", p
->link
.name
, p
->name
.full
);
153 else if (++dev
->carrier_seconds
>= dev
->dev
.cd
.delay
) {
154 if (dev
->dev
.cd
.necessity
== CD_REQUIRED
)
155 log_Printf(LogPHASE
, "%s: %s: Required CD not detected\n",
156 p
->link
.name
, p
->name
.full
);
158 log_Printf(LogPHASE
, "%s: %s doesn't support CD\n",
159 p
->link
.name
, p
->name
.full
);
160 dev
->mbits
= TIOCM_CD
; /* Dodgy null-modem cable ? */
162 timer_Stop(&dev
->Timer
);
163 /* tty_AwaitCarrier() will notice */
166 log_Printf(LogDEBUG
, "%s: %s: Still no carrier (%d/%d)\n",
167 p
->link
.name
, p
->name
.full
, dev
->carrier_seconds
,
172 change
= ombits
^ dev
->mbits
;
173 if (change
& TIOCM_CD
) {
174 if (dev
->mbits
& TIOCM_CD
)
175 log_Printf(LogDEBUG
, "%s: offline -> online\n", p
->link
.name
);
177 log_Printf(LogDEBUG
, "%s: online -> offline\n", p
->link
.name
);
178 log_Printf(LogPHASE
, "%s: Carrier lost\n", p
->link
.name
);
179 datalink_Down(p
->dl
, CLOSE_NORMAL
);
180 timer_Stop(&dev
->Timer
);
183 log_Printf(LogDEBUG
, "%s: Still %sline\n", p
->link
.name
,
184 Online(dev
) ? "on" : "off");
189 tty_StartTimer(struct physical
*p
)
191 struct ttydevice
*dev
= device2tty(p
->handler
);
193 timer_Stop(&dev
->Timer
);
194 dev
->Timer
.load
= SECTICKS
;
195 dev
->Timer
.func
= tty_Timeout
;
196 dev
->Timer
.name
= "tty CD";
198 log_Printf(LogDEBUG
, "%s: Using tty_Timeout [%p]\n",
199 p
->link
.name
, tty_Timeout
);
200 timer_Start(&dev
->Timer
);
204 tty_AwaitCarrier(struct physical
*p
)
206 struct ttydevice
*dev
= device2tty(p
->handler
);
208 if (dev
->dev
.cd
.necessity
== CD_NOTREQUIRED
|| physical_IsSync(p
))
211 if (dev
->mbits
== -1) {
212 if (dev
->Timer
.state
== TIMER_STOPPED
) {
213 dev
->carrier_seconds
= 0;
216 return CARRIER_PENDING
; /* Not yet ! */
219 return Online(dev
) ? CARRIER_OK
: CARRIER_LOST
;
223 #define tty_SetAsyncParams NULL
224 #define tty_Write NULL
225 #define tty_Read NULL
229 isngtty(struct ttydevice
*dev
)
231 return dev
->real
.fd
!= -1;
235 tty_SetAsyncParams(struct physical
*p
, u_int32_t mymap
, u_int32_t hismap
)
237 struct ttydevice
*dev
= device2tty(p
->handler
);
238 char asyncpath
[NG_PATHSIZ
];
239 struct ng_async_cfg cfg
;
242 /* Configure the async converter node */
244 snprintf(asyncpath
, sizeof asyncpath
, ".:%s", dev
->hook
);
245 memset(&cfg
, 0, sizeof cfg
);
247 cfg
.accm
= mymap
| hismap
;
250 log_Printf(LogDEBUG
, "Configure async node at %s\n", asyncpath
);
251 if (NgSendMsg(dev
->cs
, asyncpath
, NGM_ASYNC_COOKIE
,
252 NGM_ASYNC_CMD_SET_CONFIG
, &cfg
, sizeof cfg
) < 0)
253 log_Printf(LogWARN
, "%s: Can't configure async node at %s\n",
254 p
->link
.name
, asyncpath
);
256 /* No netgraph node, just config the async layer */
257 async_SetLinkParams(&p
->async
, mymap
, hismap
);
261 LoadLineDiscipline(struct physical
*p
)
263 struct ttydevice
*dev
= device2tty(p
->handler
);
264 u_char rbuf
[sizeof(struct ng_mesg
) + sizeof(struct nodeinfo
)];
265 struct ng_mesg
*reply
;
266 struct nodeinfo
*info
;
267 char ttypath
[NG_NODESIZ
];
268 struct ngm_mkpeer ngm
;
269 struct ngm_connect ngc
;
270 int ldisc
, cs
, ds
, hot
, speed
;
273 * Don't use the netgraph line discipline for now. Using it works, but
274 * carrier cannot be detected via TIOCMGET and the device doesn't become
275 * selectable with 0 bytes to read when carrier is lost :(
279 reply
= (struct ng_mesg
*)rbuf
;
280 info
= (struct nodeinfo
*)reply
->data
;
282 loadmodules(LOAD_VERBOSLY
, "netgraph", "ng_tty", "ng_async", "ng_socket",
285 /* Get the speed before loading the line discipline */
286 speed
= physical_GetSpeed(p
);
288 if (ioctl(p
->fd
, TIOCGETD
, &dev
->real
.disc
) < 0) {
289 log_Printf(LogDEBUG
, "%s: Couldn't get tty line discipline\n",
293 ldisc
= NETGRAPHDISC
;
294 if (ID0ioctl(p
->fd
, TIOCSETD
, &ldisc
) < 0) {
295 log_Printf(LogDEBUG
, "%s: Couldn't set NETGRAPHDISC line discipline\n",
300 /* Get the name of the tty node */
301 if (ioctl(p
->fd
, NGIOCGINFO
, info
) < 0) {
302 log_Printf(LogWARN
, "%s: ioctl(NGIOCGINFO): %s\n", p
->link
.name
,
304 ID0ioctl(p
->fd
, TIOCSETD
, &dev
->real
.disc
);
307 snprintf(ttypath
, sizeof ttypath
, "%s:", info
->name
);
309 /* Create a socket node for our endpoint (and to send messages via) */
310 if (ID0NgMkSockNode(NULL
, &cs
, &ds
) == -1) {
311 log_Printf(LogWARN
, "%s: NgMkSockNode: %s\n", p
->link
.name
,
313 ID0ioctl(p
->fd
, TIOCSETD
, &dev
->real
.disc
);
317 /* Set the ``hot char'' on the TTY node */
319 log_Printf(LogDEBUG
, "%s: Set tty hotchar to 0x%02x\n", p
->link
.name
, hot
);
320 if (NgSendMsg(cs
, ttypath
, NGM_TTY_COOKIE
,
321 NGM_TTY_SET_HOTCHAR
, &hot
, sizeof hot
) < 0) {
322 log_Printf(LogWARN
, "%s: Can't set hot char\n", p
->link
.name
);
326 /* Attach an async converter node */
327 snprintf(ngm
.type
, sizeof ngm
.type
, "%s", NG_ASYNC_NODE_TYPE
);
328 snprintf(ngm
.ourhook
, sizeof ngm
.ourhook
, "%s", NG_TTY_HOOK
);
329 snprintf(ngm
.peerhook
, sizeof ngm
.peerhook
, "%s", NG_ASYNC_HOOK_ASYNC
);
330 log_Printf(LogDEBUG
, "%s: Send mkpeer async:%s to %s:%s\n", p
->link
.name
,
331 ngm
.peerhook
, ttypath
, ngm
.ourhook
);
332 if (NgSendMsg(cs
, ttypath
, NGM_GENERIC_COOKIE
,
333 NGM_MKPEER
, &ngm
, sizeof ngm
) < 0) {
334 log_Printf(LogWARN
, "%s: Can't create %s node\n", p
->link
.name
,
339 /* Connect the async node to our socket */
340 snprintf(ngc
.path
, sizeof ngc
.path
, "%s%s", ttypath
, NG_TTY_HOOK
);
341 snprintf(ngc
.peerhook
, sizeof ngc
.peerhook
, "%s", NG_ASYNC_HOOK_SYNC
);
342 memcpy(ngc
.ourhook
, ngc
.peerhook
, sizeof ngc
.ourhook
);
343 log_Printf(LogDEBUG
, "%s: Send connect %s:%s to .:%s\n", p
->link
.name
,
344 ngc
.path
, ngc
.peerhook
, ngc
.ourhook
);
345 if (NgSendMsg(cs
, ".:", NGM_GENERIC_COOKIE
, NGM_CONNECT
,
346 &ngc
, sizeof ngc
) < 0) {
347 log_Printf(LogWARN
, "%s: Can't connect .:%s -> %s.%s: %s\n",
348 p
->link
.name
, ngc
.ourhook
, ngc
.path
, ngc
.peerhook
,
353 /* Get the async node id */
354 if (NgSendMsg(cs
, ngc
.path
, NGM_GENERIC_COOKIE
, NGM_NODEINFO
, NULL
, 0) < 0) {
355 log_Printf(LogWARN
, "%s: Can't request async node info at %s: %s\n",
356 p
->link
.name
, ngc
.path
, strerror(errno
));
359 if (NgRecvMsg(cs
, reply
, sizeof rbuf
, NULL
) < 0) {
360 log_Printf(LogWARN
, "%s: Can't obtain async node info at %s: %s\n",
361 p
->link
.name
, ngc
.path
, strerror(errno
));
365 /* All done, set up our device state */
366 snprintf(dev
->hook
, sizeof dev
->hook
, "%s", ngc
.ourhook
);
368 dev
->real
.fd
= p
->fd
;
370 dev
->real
.speed
= speed
;
373 tty_SetAsyncParams(p
, 0xffffffff, 0xffffffff);
374 physical_SetupStack(p
, dev
->dev
.name
, PHYSICAL_NOFORCE
);
375 log_Printf(LogPHASE
, "%s: Loaded netgraph tty line discipline\n",
381 ID0ioctl(p
->fd
, TIOCSETD
, &dev
->real
.disc
);
389 UnloadLineDiscipline(struct physical
*p
)
391 struct ttydevice
*dev
= device2tty(p
->handler
);
394 log_Printf(LogPHASE
, "back to speed %d\n", dev
->real
.speed
);
395 if (!physical_SetSpeed(p
, dev
->real
.speed
))
396 log_Printf(LogWARN
, "Couldn't reset tty speed to %d\n", dev
->real
.speed
);
399 p
->fd
= dev
->real
.fd
;
404 if (ID0ioctl(p
->fd
, TIOCSETD
, &dev
->real
.disc
) == 0) {
405 physical_SetupStack(p
, dev
->dev
.name
, PHYSICAL_NOFORCE
);
406 log_Printf(LogPHASE
, "%s: Unloaded netgraph tty line discipline\n",
409 log_Printf(LogWARN
, "%s: Failed to unload netgraph tty line discipline\n",
415 tty_Write(struct physical
*p
, const void *v
, size_t n
)
417 struct ttydevice
*dev
= device2tty(p
->handler
);
420 return NgSendData(p
->fd
, dev
->hook
, v
, n
) == -1 ? -1 : (ssize_t
)n
;
422 return write(p
->fd
, v
, n
);
426 tty_Read(struct physical
*p
, void *v
, size_t n
)
428 struct ttydevice
*dev
= device2tty(p
->handler
);
429 char hook
[sizeof NG_ASYNC_HOOK_SYNC
];
432 return NgRecvData(p
->fd
, v
, n
, hook
);
434 return read(p
->fd
, v
, n
);
437 #endif /* NETGRAPH */
440 tty_Raw(struct physical
*p
)
442 struct ttydevice
*dev
= device2tty(p
->handler
);
446 log_Printf(LogDEBUG
, "%s: Entering tty_Raw\n", p
->link
.name
);
448 if (p
->type
!= PHYS_DIRECT
&& p
->fd
>= 0 && !Online(dev
))
449 log_Printf(LogDEBUG
, "%s: Raw: descriptor = %d, mbits = %x\n",
450 p
->link
.name
, p
->fd
, dev
->mbits
);
452 if (!physical_IsSync(p
)) {
454 if (!LoadLineDiscipline(p
))
457 tcgetattr(p
->fd
, &ios
);
460 ios
.c_cflag
|= CLOCAL
| CCTS_OFLOW
| CRTS_IFLOW
;
462 ios
.c_cflag
|= CLOCAL
;
464 if (p
->type
!= PHYS_DEDICATED
)
465 ios
.c_cflag
|= HUPCL
;
467 if (tcsetattr(p
->fd
, TCSANOW
, &ios
) == -1)
468 log_Printf(LogWARN
, "%s: tcsetattr: Failed configuring device\n",
473 oldflag
= fcntl(p
->fd
, F_GETFL
, 0);
476 fcntl(p
->fd
, F_SETFL
, oldflag
| O_NONBLOCK
);
482 tty_Offline(struct physical
*p
)
484 struct ttydevice
*dev
= device2tty(p
->handler
);
487 timer_Stop(&dev
->Timer
);
488 dev
->mbits
&= ~TIOCM_DTR
; /* XXX: Hmm, what's this supposed to do ? */
492 tcgetattr(p
->fd
, &tio
);
493 if (cfsetspeed(&tio
, B0
) == -1 || tcsetattr(p
->fd
, TCSANOW
, &tio
) == -1)
494 log_Printf(LogWARN
, "%s: Unable to set physical to speed 0\n",
501 tty_Cooked(struct physical
*p
)
503 struct ttydevice
*dev
= device2tty(p
->handler
);
506 tty_Offline(p
); /* In case of emergency close()s */
508 tcflush(p
->fd
, TCIOFLUSH
);
510 if (!physical_IsSync(p
) && tcsetattr(p
->fd
, TCSAFLUSH
, &dev
->ios
) == -1)
511 log_Printf(LogWARN
, "%s: tcsetattr: Unable to restore device settings\n",
515 UnloadLineDiscipline(p
);
518 if ((oldflag
= fcntl(p
->fd
, F_GETFL
, 0)) != -1)
519 fcntl(p
->fd
, F_SETFL
, oldflag
& ~O_NONBLOCK
);
523 tty_StopTimer(struct physical
*p
)
525 struct ttydevice
*dev
= device2tty(p
->handler
);
527 timer_Stop(&dev
->Timer
);
531 tty_Free(struct physical
*p
)
533 struct ttydevice
*dev
= device2tty(p
->handler
);
535 tty_Offline(p
); /* In case of emergency close()s */
540 tty_Speed(struct physical
*p
)
544 if (tcgetattr(p
->fd
, &ios
) == -1)
547 return SpeedToUnsigned(cfgetispeed(&ios
));
551 tty_OpenInfo(struct physical
*p
)
553 struct ttydevice
*dev
= device2tty(p
->handler
);
560 strcat(buf
, " carrier");
566 tty_Slot(struct physical
*p
)
572 for (slot
= 1; (ttyp
= getttyent()); ++slot
)
573 if (!strcmp(ttyp
->ty_name
, p
->name
.base
)) {
583 tty_device2iov(struct device
*d
, struct iovec
*iov
, int *niov
,
584 int maxiov __unused
, int *auxfd
, int *nauxfd
)
586 struct ttydevice
*dev
= device2tty(d
);
587 int sz
= physical_MaxDeviceSize();
589 iov
[*niov
].iov_base
= realloc(d
, sz
);
590 if (iov
[*niov
].iov_base
== NULL
) {
591 log_Printf(LogALERT
, "Failed to allocate memory: %d\n", sz
);
592 AbortProgram(EX_OSERR
);
594 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
,
633 int maxiov __unused
, int *auxfd
, int *nauxfd
)
635 if (type
== TTY_DEVICE
) {
636 struct ttydevice
*dev
= (struct ttydevice
*)iov
[(*niov
)++].iov_base
;
638 dev
= realloc(dev
, sizeof *dev
); /* Reduce to the correct size */
640 log_Printf(LogALERT
, "Failed to allocate memory: %d\n",
642 AbortProgram(EX_OSERR
);
653 /* Refresh function pointers etc */
654 memcpy(&dev
->dev
, &basettydevice
, sizeof dev
->dev
);
656 physical_SetupStack(p
, dev
->dev
.name
, PHYSICAL_NOFORCE
);
657 if (dev
->Timer
.state
!= TIMER_STOPPED
) {
658 dev
->Timer
.state
= TIMER_STOPPED
;
659 p
->handler
= &dev
->dev
; /* For the benefit of StartTimer */
669 tty_Create(struct physical
*p
)
671 struct ttydevice
*dev
;
675 if (p
->fd
< 0 || !isatty(p
->fd
))
676 /* Don't want this */
679 if (*p
->name
.full
== '\0') {
680 physical_SetDevice(p
, ttyname(p
->fd
));
681 log_Printf(LogDEBUG
, "%s: Input is a tty (%s)\n",
682 p
->link
.name
, p
->name
.full
);
684 log_Printf(LogDEBUG
, "%s: Opened %s\n", p
->link
.name
, p
->name
.full
);
686 /* We're gonna return a ttydevice (unless something goes horribly wrong) */
688 if ((dev
= malloc(sizeof *dev
)) == NULL
) {
689 /* Complete failure - parent doesn't continue trying to ``create'' */
695 memcpy(&dev
->dev
, &basettydevice
, sizeof dev
->dev
);
696 memset(&dev
->Timer
, '\0', sizeof dev
->Timer
);
704 tcgetattr(p
->fd
, &ios
);
707 if (p
->cfg
.cd
.necessity
!= CD_DEFAULT
)
708 /* Any override is ok for the tty device */
709 dev
->dev
.cd
= p
->cfg
.cd
;
711 log_Printf(LogDEBUG
, "%s: tty_Create: physical (get): fd = %d,"
712 " iflag = %lx, oflag = %lx, cflag = %lx\n", p
->link
.name
, p
->fd
,
713 (u_long
)ios
.c_iflag
, (u_long
)ios
.c_oflag
, (u_long
)ios
.c_cflag
);
717 ios
.c_cflag
|= CLOCAL
| CCTS_OFLOW
| CRTS_IFLOW
;
719 ios
.c_cflag
|= CLOCAL
;
720 ios
.c_iflag
|= IXOFF
;
723 if (p
->type
!= PHYS_DEDICATED
)
724 ios
.c_cflag
|= HUPCL
;
726 if (p
->type
!= PHYS_DIRECT
) {
727 /* Change tty speed when we're not in -direct mode */
728 ios
.c_cflag
&= ~(CSIZE
| PARODD
| PARENB
);
729 ios
.c_cflag
|= p
->cfg
.parity
;
730 if (cfsetspeed(&ios
, UnsignedToSpeed(p
->cfg
.speed
)) == -1)
731 log_Printf(LogWARN
, "%s: %s: Unable to set speed to %d\n",
732 p
->link
.name
, p
->name
.full
, p
->cfg
.speed
);
735 if (tcsetattr(p
->fd
, TCSADRAIN
, &ios
) == -1) {
736 log_Printf(LogWARN
, "%s: tcsetattr: Failed configuring device\n",
738 if (p
->type
!= PHYS_DIRECT
&& p
->cfg
.speed
> 115200)
739 log_Printf(LogWARN
, "%.*s Perhaps the speed is unsupported\n",
740 (int)strlen(p
->link
.name
), "");
743 log_Printf(LogDEBUG
, "%s: physical (put): iflag = %lx, oflag = %lx, "
744 "cflag = %lx\n", p
->link
.name
, (u_long
)ios
.c_iflag
,
745 (u_long
)ios
.c_oflag
, (u_long
)ios
.c_cflag
);
747 oldflag
= fcntl(p
->fd
, F_GETFL
, 0);
749 /* Complete failure - parent doesn't continue trying to ``create'' */
751 log_Printf(LogWARN
, "%s: Open: Cannot get physical flags: %s\n",
752 p
->link
.name
, strerror(errno
));
759 fcntl(p
->fd
, F_SETFL
, oldflag
& ~O_NONBLOCK
);
761 physical_SetupStack(p
, dev
->dev
.name
, PHYSICAL_NOFORCE
);