4 * Copyright (c) 1982, 1986, 1989, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * $FreeBSD: src/sys/dev/nmdm/nmdm.c,v 1.5.2.1 2001/08/11 00:54:14 mp Exp $
35 * MPSAFE NOTE: This file acquires the tty_token mainly for linesw access and
36 * tp (struct tty) access.
40 * Pseudo-nulmodem Driver
42 #include <sys/param.h>
43 #include <sys/systm.h>
46 #include <sys/thread2.h>
49 #include <sys/fcntl.h>
50 #include <sys/kernel.h>
51 #include <sys/vnode.h>
52 #include <sys/signalvar.h>
53 #include <sys/malloc.h>
55 MALLOC_DEFINE(M_NLMDM
, "nullmodem", "nullmodem data structures");
57 static void nmdmstart (struct tty
*tp
);
58 static void nmdmstop (struct tty
*tp
, int rw
);
59 static void wakeup_other (struct tty
*tp
, int flag
);
60 static void nmdminit (int n
);
62 static d_open_t nmdmopen
;
63 static d_close_t nmdmclose
;
64 static d_read_t nmdmread
;
65 static d_write_t nmdmwrite
;
66 static d_ioctl_t nmdmioctl
;
69 static struct dev_ops nmdm_ops
= {
76 .d_kqfilter
= ttykqfilter
,
80 #define BUFSIZ 100 /* Chunk size iomoved to/from user */
85 int modemsignals
; /* bits defined in sys/ttycom.h */
91 struct softpart part1
, part2
;
92 struct prison
*pt_prison
;
95 #define PF_STOPPED 0x10 /* user told stopped */
98 nmdm_crossover(struct nm_softc
*pti
,
99 struct softpart
*ourpart
,
100 struct softpart
*otherpart
);
102 #define GETPARTS(tp, ourpart, otherpart) \
104 struct nm_softc *pti = tp->t_dev->si_drv1; \
105 if (tp == &pti->part1.nm_tty) { \
106 ourpart = &pti->part1; \
107 otherpart = &pti->part2; \
109 ourpart = &pti->part2; \
110 otherpart = &pti->part1; \
115 * This function creates and initializes a pair of ttys.
117 * NOTE: Must be called with tty_token held
126 * Simplified unit number, use low 8 bits of minor number
127 * (remember, the minor number mask is 0xffff00ff).
132 ASSERT_LWKT_TOKEN_HELD(&tty_token
);
134 pt
= kmalloc(sizeof(*pt
), M_NLMDM
, M_WAITOK
| M_ZERO
);
135 pt
->part1
.dev
= dev1
= make_dev(&nmdm_ops
, n
<< 1,
136 0, 0, 0666, "nmdm%dA", n
);
137 pt
->part2
.dev
= dev2
= make_dev(&nmdm_ops
, (n
<< 1) + 1,
138 0, 0, 0666, "nmdm%dB", n
);
140 dev1
->si_drv1
= dev2
->si_drv1
= pt
;
141 dev1
->si_tty
= &pt
->part1
.nm_tty
;
142 dev2
->si_tty
= &pt
->part2
.nm_tty
;
143 ttyregister(&pt
->part1
.nm_tty
);
144 ttyregister(&pt
->part2
.nm_tty
);
145 pt
->part1
.nm_tty
.t_oproc
= nmdmstart
;
146 pt
->part2
.nm_tty
.t_oproc
= nmdmstart
;
147 pt
->part1
.nm_tty
.t_stop
= nmdmstop
;
148 pt
->part2
.nm_tty
.t_dev
= dev1
;
149 pt
->part1
.nm_tty
.t_dev
= dev2
;
150 pt
->part2
.nm_tty
.t_stop
= nmdmstop
;
155 nmdmopen(struct dev_open_args
*ap
)
157 cdev_t dev
= ap
->a_head
.a_dev
;
158 struct tty
*tp
, *tp2
;
164 struct nm_softc
*pti
;
167 struct softpart
*ourpart
, *otherpart
;
175 * XXX: Gross hack for DEVFS:
176 * If we openned this device, ensure we have the
177 * next one too, so people can open it.
180 nextdev
= makedev(major(dev
), (pair
+pair
) + 1);
181 if (!nextdev
->si_drv1
) {
192 lwkt_gettoken(&tty_token
);
195 tp
= &pti
->part2
.nm_tty
;
197 tp
= &pti
->part1
.nm_tty
;
198 GETPARTS(tp
, ourpart
, otherpart
);
199 tp2
= &otherpart
->nm_tty
;
200 ourpart
->modemsignals
|= TIOCM_LE
;
202 if ((tp
->t_state
& TS_ISOPEN
) == 0) {
203 ttychars(tp
); /* Set up default chars */
204 tp
->t_iflag
= TTYDEF_IFLAG
;
205 tp
->t_oflag
= TTYDEF_OFLAG
;
206 tp
->t_lflag
= TTYDEF_LFLAG
;
207 tp
->t_cflag
= TTYDEF_CFLAG
;
208 tp
->t_ispeed
= tp
->t_ospeed
= TTYDEF_SPEED
;
209 } else if (tp
->t_state
& TS_XCLUDE
&& priv_check_cred(ap
->a_cred
, PRIV_ROOT
, 0)) {
210 lwkt_reltoken(&tty_token
);
212 } else if (pti
->pt_prison
!= ap
->a_cred
->cr_prison
) {
213 lwkt_reltoken(&tty_token
);
218 * If the other side is open we have carrier
220 if (tp2
->t_state
& TS_ISOPEN
) {
221 (void)(*linesw
[tp
->t_line
].l_modem
)(tp
, 1);
225 * And the other side gets carrier as we are now open.
227 (void)(*linesw
[tp2
->t_line
].l_modem
)(tp2
, 1);
229 /* External processing makes no sense here */
230 tp
->t_lflag
&= ~EXTPROC
;
233 * Wait here if we don't have carrier.
236 while ((tp
->t_state
& TS_CARR_ON
) == 0) {
237 if (flag
& FNONBLOCK
)
239 error
= ttysleep(tp
, TSA_CARR_ON(tp
), PCATCH
, "nmdopn", 0);
241 lwkt_reltoken(&tty_token
);
248 * Give the line disciplin a chance to set this end up.
250 error
= (*linesw
[tp
->t_line
].l_open
)(dev
, tp
);
253 * Wake up the other side.
254 * Theoretically not needed.
256 ourpart
->modemsignals
|= TIOCM_DTR
;
257 nmdm_crossover(pti
, ourpart
, otherpart
);
259 wakeup_other(tp
, FREAD
|FWRITE
); /* XXX */
260 lwkt_reltoken(&tty_token
);
265 nmdmclose(struct dev_close_args
*ap
)
267 cdev_t dev
= ap
->a_head
.a_dev
;
268 struct tty
*tp
, *tp2
;
270 struct softpart
*ourpart
, *otherpart
;
272 lwkt_gettoken(&tty_token
);
274 * let the other end know that the game is up
277 GETPARTS(tp
, ourpart
, otherpart
);
278 tp2
= &otherpart
->nm_tty
;
279 (void)(*linesw
[tp2
->t_line
].l_modem
)(tp2
, 0);
282 * XXX MDMBUF makes no sense for nmdms but would inhibit the above
283 * l_modem(). CLOCAL makes sense but isn't supported. Special
284 * l_modem()s that ignore carrier drop make no sense for nmdms but
285 * may be in use because other parts of the line discipline make
286 * sense for nmdms. Recover by doing everything that a normal
287 * ttymodem() would have done except for sending a SIGHUP.
289 if (tp2
->t_state
& TS_ISOPEN
) {
290 tp2
->t_state
&= ~(TS_CARR_ON
| TS_CONNECTED
);
291 tp2
->t_state
|= TS_ZOMBIE
;
292 ttyflush(tp2
, FREAD
| FWRITE
);
295 err
= (*linesw
[tp
->t_line
].l_close
)(tp
, ap
->a_fflag
);
296 ourpart
->modemsignals
&= ~TIOCM_DTR
;
297 nmdm_crossover(dev
->si_drv1
, ourpart
, otherpart
);
298 nmdmstop(tp
, FREAD
|FWRITE
);
300 lwkt_reltoken(&tty_token
);
305 nmdmread(struct dev_read_args
*ap
)
307 cdev_t dev
= ap
->a_head
.a_dev
;
312 struct softpart
*ourpart
, *otherpart
;
315 lwkt_gettoken(&tty_token
);
318 GETPARTS(tp
, ourpart
, otherpart
);
319 tp2
= &otherpart
->nm_tty
;
321 if (tp2
->t_state
& TS_ISOPEN
) {
322 error
= (*linesw
[tp
->t_line
].l_read
)(tp
, ap
->a_uio
, flag
);
323 wakeup_other(tp
, FWRITE
);
325 if (flag
& IO_NDELAY
) {
326 lwkt_reltoken(&tty_token
);
327 return (EWOULDBLOCK
);
329 error
= tsleep(TSA_PTC_READ(tp
), PCATCH
, "nmdout", 0);
333 if ((error
= (*linesw
[tp
->t_line
].l_read
)(tp
, ap
->a_uio
, ap
->a_ioflag
)) == 0)
334 wakeup_other(tp
, FWRITE
);
336 lwkt_reltoken(&tty_token
);
341 * Write to pseudo-tty.
342 * Wakeups of controlling tty will happen
343 * indirectly, when tty driver calls nmdmstart.
346 nmdmwrite(struct dev_write_args
*ap
)
348 cdev_t dev
= ap
->a_head
.a_dev
;
349 struct uio
*uio
= ap
->a_uio
;
352 u_char locbuf
[BUFSIZ
];
355 struct tty
*tp1
, *tp
;
356 struct softpart
*ourpart
, *otherpart
;
358 lwkt_gettoken(&tty_token
);
361 * Get the other tty struct.
362 * basically we are writing into the INPUT side of the other device.
364 GETPARTS(tp1
, ourpart
, otherpart
);
365 tp
= &otherpart
->nm_tty
;
368 if ((tp
->t_state
& TS_ISOPEN
) == 0) {
369 lwkt_reltoken(&tty_token
);
372 while (uio
->uio_resid
> 0 || cc
> 0) {
374 * Fill up the buffer if it's empty
377 cc
= szmin(uio
->uio_resid
, BUFSIZ
);
379 error
= uiomove((caddr_t
)cp
, cc
, uio
);
381 lwkt_reltoken(&tty_token
);
384 /* check again for safety */
385 if ((tp
->t_state
& TS_ISOPEN
) == 0) {
386 /* adjust for data copied in but not written */
387 uio
->uio_resid
+= cc
;
388 lwkt_reltoken(&tty_token
);
393 if (((tp
->t_rawq
.c_cc
+ tp
->t_canq
.c_cc
) >= (TTYHOG
-2))
394 && ((tp
->t_canq
.c_cc
> 0) || !(tp
->t_iflag
&ICANON
))) {
396 * Come here to wait for space in outq,
397 * or space in rawq, or an empty canq.
399 wakeup(TSA_HUP_OR_INPUT(tp
));
400 if ((tp
->t_state
& TS_CONNECTED
) == 0) {
402 * Data piled up because not connected.
403 * Adjust for data copied in but
406 uio
->uio_resid
+= cc
;
407 lwkt_reltoken(&tty_token
);
410 if (ap
->a_ioflag
& IO_NDELAY
) {
412 * Don't wait if asked not to.
413 * Adjust for data copied in but
416 uio
->uio_resid
+= cc
;
418 lwkt_reltoken(&tty_token
);
419 return (EWOULDBLOCK
);
421 lwkt_reltoken(&tty_token
);
424 error
= tsleep(TSA_PTC_WRITE(tp
),
425 PCATCH
, "nmdout", 0);
428 * Tsleep returned (signal?).
429 * Go find out what the user wants.
430 * adjust for data copied in but
433 uio
->uio_resid
+= cc
;
434 lwkt_reltoken(&tty_token
);
439 (*linesw
[tp
->t_line
].l_rint
)(*cp
++, tp
);
445 lwkt_reltoken(&tty_token
);
450 * Start output on pseudo-tty.
451 * Wake up process selecting or sleeping for input from controlling tty.
454 nmdmstart(struct tty
*tp
)
456 struct nm_softc
*pti
= tp
->t_dev
->si_drv1
;
458 lwkt_gettoken(&tty_token
);
459 if (tp
->t_state
& TS_TTSTOP
) {
460 lwkt_reltoken(&tty_token
);
463 pti
->pt_flags
&= ~PF_STOPPED
;
464 wakeup_other(tp
, FREAD
);
465 lwkt_reltoken(&tty_token
);
468 /* Wakes up the OTHER tty;*/
470 wakeup_other(struct tty
*tp
, int flag
)
472 struct softpart
*ourpart
, *otherpart
;
474 lwkt_gettoken(&tty_token
);
475 GETPARTS(tp
, ourpart
, otherpart
);
477 wakeup(TSA_PTC_READ((&otherpart
->nm_tty
)));
478 KNOTE(&otherpart
->nm_tty
.t_rkq
.ki_note
, 0);
481 wakeup(TSA_PTC_WRITE((&otherpart
->nm_tty
)));
482 KNOTE(&otherpart
->nm_tty
.t_wkq
.ki_note
, 0);
484 lwkt_reltoken(&tty_token
);
488 nmdmstop(struct tty
*tp
, int flush
)
490 struct nm_softc
*pti
= tp
->t_dev
->si_drv1
;
493 lwkt_gettoken(&tty_token
);
494 /* note: FLUSHREAD and FLUSHWRITE already ok */
496 flush
= TIOCPKT_STOP
;
497 pti
->pt_flags
|= PF_STOPPED
;
499 pti
->pt_flags
&= ~PF_STOPPED
;
500 /* change of perspective */
506 wakeup_other(tp
, flag
);
507 lwkt_reltoken(&tty_token
);
512 nmdmioctl(struct dev_ioctl_args
*ap
)
514 cdev_t dev
= ap
->a_head
.a_dev
;
515 struct tty
*tp
= dev
->si_tty
;
516 struct nm_softc
*pti
= dev
->si_drv1
;
518 struct softpart
*ourpart
, *otherpart
;
521 lwkt_gettoken(&tty_token
);
522 GETPARTS(tp
, ourpart
, otherpart
);
524 error
= (*linesw
[tp
->t_line
].l_ioctl
)(tp
, ap
->a_cmd
, ap
->a_data
,
525 ap
->a_fflag
, ap
->a_cred
);
526 if (error
== ENOIOCTL
)
527 error
= ttioctl(tp
, ap
->a_cmd
, ap
->a_data
, ap
->a_fflag
);
528 if (error
== ENOIOCTL
) {
531 otherpart
->gotbreak
= 1;
536 ourpart
->modemsignals
|= TIOCM_DTR
;
539 ourpart
->modemsignals
&= TIOCM_DTR
;
542 ourpart
->modemsignals
= *(int *)ap
->a_data
;
543 otherpart
->modemsignals
= *(int *)ap
->a_data
;
546 ourpart
->modemsignals
|= *(int *)ap
->a_data
;
549 ourpart
->modemsignals
&= ~(*(int *)ap
->a_data
);
550 otherpart
->modemsignals
&= ~(*(int *)ap
->a_data
);
553 *(int *)ap
->a_data
= ourpart
->modemsignals
;
558 *(int *)ap
->a_data
= 0;
561 case TIOCDCDTIMESTAMP
:
563 lwkt_reltoken(&tty_token
);
569 nmdm_crossover(pti
, ourpart
, otherpart
);
571 lwkt_reltoken(&tty_token
);
577 nmdm_crossover(struct nm_softc
*pti
,
578 struct softpart
*ourpart
,
579 struct softpart
*otherpart
)
581 lwkt_gettoken(&tty_token
);
582 otherpart
->modemsignals
&= ~(TIOCM_CTS
|TIOCM_CAR
);
583 if (ourpart
->modemsignals
& TIOCM_RTS
)
584 otherpart
->modemsignals
|= TIOCM_CTS
;
585 if (ourpart
->modemsignals
& TIOCM_DTR
)
586 otherpart
->modemsignals
|= TIOCM_CAR
;
587 lwkt_reltoken(&tty_token
);
592 static void nmdm_drvinit (void *unused
);
595 nmdm_drvinit(void *unused
)
597 /* XXX: Gross hack for DEVFS */
598 lwkt_gettoken(&tty_token
);
600 lwkt_reltoken(&tty_token
);
603 SYSINIT(nmdmdev
, SI_SUB_DRIVERS
, SI_ORDER_MIDDLE
+ CDEV_MAJOR
, nmdm_drvinit
,