diff options
author | phk <phk@FreeBSD.org> | 2004-06-01 22:53:00 +0000 |
---|---|---|
committer | phk <phk@FreeBSD.org> | 2004-06-01 22:53:00 +0000 |
commit | 1a00e2ae6b335c12f51dcca8f457c6b064c686f6 (patch) | |
tree | a529f4b6b46afede731abf9a8af3652b539cabc3 /sys/dev/nmdm | |
parent | b5aa4ee7064bba450be12b4a92673eb114377fc1 (diff) | |
download | FreeBSD-src-1a00e2ae6b335c12f51dcca8f457c6b064c686f6.zip FreeBSD-src-1a00e2ae6b335c12f51dcca8f457c6b064c686f6.tar.gz |
A major overhaul of the nmdm(4) driver:
It was based on the pty(4) driver which as a tty side an a non-tty side.
Nmdm(4) seems to have inherited two symmetric sides from pty but
unfortunately they are not quite ttys. Running a getty one one
side and tip on the other failed to produce NL->CRNL mapping for
instance.
Rip out the basically bogus cdevsw->{read,write} functions and rely
on ttyread() and ttywrite() which does the same thing.
Use taskqueue_swi_giant to run a task for either side to do what
needs to be done. (Direct calling is not an option as it leads to
recursion.) Trigger the task from the t_oproc and t_stop methods.
Default the ports to not ECHO. Since we neither rate limiting nor
emulation, two ports echoing each other is a really bad idea, which
can only be properly mitigated by rate limiting, rate emulation or
intelligent detection. Rate emulation would be a neat feature.
Ditch the modem-line emulation, if needed for some app, it needs
to be thought much more about how it interacts with the open/close
logic.
Diffstat (limited to 'sys/dev/nmdm')
-rw-r--r-- | sys/dev/nmdm/nmdm.c | 456 |
1 files changed, 81 insertions, 375 deletions
diff --git a/sys/dev/nmdm/nmdm.c b/sys/dev/nmdm/nmdm.c index 79a5476..8855a27 100644 --- a/sys/dev/nmdm/nmdm.c +++ b/sys/dev/nmdm/nmdm.c @@ -41,9 +41,6 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> -#if defined(COMPAT_43) || defined(COMPAT_SUNOS) -#include <sys/ioctl_compat.h> -#endif #include <sys/proc.h> #include <sys/tty.h> #include <sys/conf.h> @@ -54,27 +51,21 @@ __FBSDID("$FreeBSD$"); #include <sys/vnode.h> #include <sys/signalvar.h> #include <sys/malloc.h> +#include <sys/taskqueue.h> MALLOC_DEFINE(M_NLMDM, "nullmodem", "nullmodem data structures"); static void nmdmstart(struct tty *tp); static void nmdmstop(struct tty *tp, int rw); -static void wakeup_other(struct tty *tp, int flag); static void nmdminit(dev_t dev); static d_open_t nmdmopen; static d_close_t nmdmclose; -static d_read_t nmdmread; -static d_write_t nmdmwrite; -static d_ioctl_t nmdmioctl; static struct cdevsw nmdm_cdevsw = { .d_version = D_VERSION, .d_open = nmdmopen, .d_close = nmdmclose, - .d_read = nmdmread, - .d_write = nmdmwrite, - .d_ioctl = nmdmioctl, .d_name = "nmdn", .d_flags = D_TTY | D_PSEUDO | D_NEEDGIANT, }; @@ -85,10 +76,11 @@ static struct cdevsw nmdm_cdevsw = { #define BFLAG CLONE_FLAG0 struct softpart { - struct tty nm_tty; + struct tty *nm_tty; dev_t dev; - int modemsignals; /* bits defined in sys/ttycom.h */ - int gotbreak; + int dcd; + struct task pt_task; + struct softpart *other; }; struct nm_softc { @@ -148,21 +140,40 @@ nmdm_clone(void *arg, char *name, int nameen, dev_t *dev) } static void -nmdm_crossover(struct nm_softc *pti, - struct softpart *ourpart, - struct softpart *otherpart); - -#define GETPARTS(tp, ourpart, otherpart) \ -do { \ - struct nm_softc *pti = tp->t_dev->si_drv1; \ - if (tp == &pti->part1.nm_tty) { \ - ourpart = &pti->part1; \ - otherpart = &pti->part2; \ - } else { \ - ourpart = &pti->part2; \ - otherpart = &pti->part1; \ - } \ -} while (0) +nmdm_task_tty(void *arg, int pending __unused) +{ + struct tty *tp, *otp; + struct softpart *sp; + int c; + + tp = arg; + sp = tp->t_sc; + otp = sp->other->nm_tty; + KASSERT(otp != NULL, ("NULL otp in nmdmstart")); + KASSERT(otp != tp, ("NULL otp == tp nmdmstart")); + if (sp->other->dcd) { + if (!(tp->t_state & TS_ISOPEN)) { + sp->other->dcd = 0; + (void)(*linesw[otp->t_line].l_modem)(otp, 0); + } + } else { + if (tp->t_state & TS_ISOPEN) { + sp->other->dcd = 1; + (void)(*linesw[otp->t_line].l_modem)(otp, 1); + } + } + if (tp->t_state & TS_TTSTOP) + return; + while (tp->t_outq.c_cc != 0) { + if (otp->t_state & TS_TBLOCK) + return; + c = getc(&tp->t_outq); + if (otp->t_state & TS_ISOPEN) + (*linesw[otp->t_line].l_rint)(c, otp); + } + if (tp->t_outq.c_cc == 0) + ttwwakeup(tp); +} /* * This function creates and initializes a pair of ttys. @@ -180,20 +191,34 @@ nmdminit(dev_t dev1) pt = malloc(sizeof(*pt), M_NLMDM, M_WAITOK | M_ZERO); TAILQ_INSERT_TAIL(&nmdmhead, pt, pt_list); + dev1->si_drv1 = dev2->si_drv1 = pt; pt->part1.dev = dev1; pt->part2.dev = dev2; - dev1->si_tty = &pt->part1.nm_tty; - dev2->si_tty = &pt->part2.nm_tty; - ttyregister(&pt->part1.nm_tty); - ttyregister(&pt->part2.nm_tty); - pt->part1.nm_tty.t_oproc = nmdmstart; - pt->part2.nm_tty.t_oproc = nmdmstart; - pt->part1.nm_tty.t_stop = nmdmstop; - pt->part2.nm_tty.t_stop = nmdmstop; - pt->part2.nm_tty.t_dev = dev1; - pt->part1.nm_tty.t_dev = dev2; + + pt->part1.nm_tty = ttymalloc(pt->part1.nm_tty); + pt->part1.nm_tty->t_oproc = nmdmstart; + pt->part1.nm_tty->t_stop = nmdmstop; + pt->part1.nm_tty->t_dev = dev1; + pt->part1.nm_tty->t_sc = &pt->part1; + TASK_INIT(&pt->part1.pt_task, 0, nmdm_task_tty, pt->part1.nm_tty); + + pt->part2.nm_tty = ttymalloc(pt->part2.nm_tty); + pt->part2.nm_tty->t_oproc = nmdmstart; + pt->part2.nm_tty->t_stop = nmdmstop; + pt->part2.nm_tty->t_dev = dev2; + pt->part2.nm_tty->t_sc = &pt->part2; + TASK_INIT(&pt->part2.pt_task, 0, nmdm_task_tty, pt->part2.nm_tty); + + pt->part1.other = &pt->part2; + pt->part2.other = &pt->part1; + + dev1->si_tty = pt->part1.nm_tty; + dev1->si_drv1 = pt; + + dev2->si_tty = pt->part2.nm_tty; + dev2->si_drv1 = pt; } /* @@ -202,380 +227,61 @@ nmdminit(dev_t dev1) static int nmdmopen(dev_t dev, int flag, int devtype, struct thread *td) { - register struct tty *tp, *tp2; + struct tty *tp, *tp2; int error; struct nm_softc *pti; - struct softpart *ourpart, *otherpart; + struct softpart *sp; if (dev->si_drv1 == NULL) nmdminit(dev); pti = dev->si_drv1; + if (pti->pt_prison != td->td_ucred->cr_prison) + return (EBUSY); - if (minor(dev) & BFLAG) - tp = &pti->part2.nm_tty; - else - tp = &pti->part1.nm_tty; - GETPARTS(tp, ourpart, otherpart); - - tp2 = &otherpart->nm_tty; - ourpart->modemsignals |= TIOCM_LE; + tp = dev->si_tty; + sp = tp->t_sc; + tp2 = sp->other->nm_tty; if ((tp->t_state & TS_ISOPEN) == 0) { ttychars(tp); /* Set up default chars */ tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_lflag = TTYDEF_LFLAG; + tp->t_lflag = 0; tp->t_cflag = TTYDEF_CFLAG; tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; + ttsetwater(tp); /* XXX ? */ } else if (tp->t_state & TS_XCLUDE && suser(td)) { return (EBUSY); - } else if (pti->pt_prison != td->td_ucred->cr_prison) { - return (EBUSY); } - /* - * If the other side is open we have carrier - */ - if (tp2->t_state & TS_ISOPEN) { - (void)(*linesw[tp->t_line].l_modem)(tp, 1); - } - - /* - * And the other side gets carrier as we are now open. - */ - (void)(*linesw[tp2->t_line].l_modem)(tp2, 1); - - /* External processing makes no sense here */ - tp->t_lflag &= ~EXTPROC; - - /* - * Wait here if we don't have carrier. - */ -#if 0 - while ((tp->t_state & TS_CARR_ON) == 0) { - if (flag & FNONBLOCK) - break; - error = ttysleep(tp, TSA_CARR_ON(tp), TTIPRI | PCATCH, - "nmdopn", 0); - if (error) - return (error); - } -#endif - - /* - * Give the line disciplin a chance to set this end up. - */ error = (*linesw[tp->t_line].l_open)(dev, tp); - - /* - * Wake up the other side. - * Theoretically not needed. - */ - ourpart->modemsignals |= TIOCM_DTR; - nmdm_crossover(pti, ourpart, otherpart); - if (error == 0) - wakeup_other(tp, FREAD|FWRITE); /* XXX */ return (error); } -/* - * Device closed again - */ -static int +static int nmdmclose(dev_t dev, int flag, int mode, struct thread *td) { - register struct tty *tp, *tp2; - int err; - struct softpart *ourpart, *otherpart; - - /* - * let the other end know that the game is up - */ - tp = dev->si_tty; - GETPARTS(tp, ourpart, otherpart); - tp2 = &otherpart->nm_tty; - (void)(*linesw[tp2->t_line].l_modem)(tp2, 0); - - /* - * XXX MDMBUF makes no sense for nmdms but would inhibit the above - * l_modem(). CLOCAL makes sense but isn't supported. Special - * l_modem()s that ignore carrier drop make no sense for nmdms but - * may be in use because other parts of the line discipline make - * sense for nmdms. Recover by doing everything that a normal - * ttymodem() would have done except for sending a SIGHUP. - */ - if (tp2->t_state & TS_ISOPEN) { - tp2->t_state &= ~(TS_CARR_ON | TS_CONNECTED); - tp2->t_state |= TS_ZOMBIE; - ttyflush(tp2, FREAD | FWRITE); - } - err = (*linesw[tp->t_line].l_close)(tp, flag); - ourpart->modemsignals &= ~TIOCM_DTR; - nmdm_crossover(dev->si_drv1, ourpart, otherpart); - (void) ttyclose(tp); - return (err); + return (ttyclose(dev->si_tty)); } -/* - * handle read(2) request from userland - */ -static int -nmdmread(dev_t dev, struct uio *uio, int flag) -{ - int error = 0; - struct tty *tp, *tp2; - struct softpart *ourpart, *otherpart; - - tp = dev->si_tty; - GETPARTS(tp, ourpart, otherpart); - tp2 = &otherpart->nm_tty; - -#if 0 - if (tp2->t_state & TS_ISOPEN) { - error = (*linesw[tp->t_line].l_read)(tp, uio, flag); - wakeup_other(tp, FWRITE); - } else { - if (flag & IO_NDELAY) { - return (EWOULDBLOCK); - } - error = tsleep(TSA_PTC_READ(tp), - TTIPRI | PCATCH, "nmdout", 0); - } - } -#else - if ((error = (*linesw[tp->t_line].l_read)(tp, uio, flag)) == 0) - wakeup_other(tp, FWRITE); -#endif - return (error); -} - -/* - * Write to pseudo-tty. - * Wakeups of controlling tty will happen - * indirectly, when tty driver calls nmdmstart. - */ -static int -nmdmwrite(dev_t dev, struct uio *uio, int flag) -{ - register u_char *cp = 0; - register int cc = 0; - u_char locbuf[BUFSIZ]; - int cnt = 0; - int error = 0; - struct tty *tp1, *tp; - struct softpart *ourpart, *otherpart; - - tp1 = dev->si_tty; - /* - * Get the other tty struct. - * basically we are writing into the INPUT side of the other device. - */ - GETPARTS(tp1, ourpart, otherpart); - tp = &otherpart->nm_tty; - -again: - if ((tp->t_state & TS_ISOPEN) == 0) - return (EIO); - while (uio->uio_resid > 0 || cc > 0) { - /* - * Fill up the buffer if it's empty - */ - if (cc == 0) { - cc = min(uio->uio_resid, BUFSIZ); - cp = locbuf; - error = uiomove((caddr_t)cp, cc, uio); - if (error) - return (error); - /* check again for safety */ - if ((tp->t_state & TS_ISOPEN) == 0) { - /* adjust for data copied in but not written */ - uio->uio_resid += cc; - return (EIO); - } - } - while (cc > 0) { - if (((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= (TTYHOG-2)) - && ((tp->t_canq.c_cc > 0) || !(tp->t_iflag&ICANON))) { - /* - * Come here to wait for space in outq, - * or space in rawq, or an empty canq. - */ - wakeup(TSA_HUP_OR_INPUT(tp)); - if ((tp->t_state & TS_CONNECTED) == 0) { - /* - * Data piled up because not connected. - * Adjust for data copied in but - * not written. - */ - uio->uio_resid += cc; - return (EIO); - } - if (flag & IO_NDELAY) { - /* - * Don't wait if asked not to. - * Adjust for data copied in but - * not written. - */ - uio->uio_resid += cc; - if (cnt == 0) - return (EWOULDBLOCK); - return (0); - } - error = tsleep(TSA_PTC_WRITE(tp), - TTOPRI | PCATCH, "nmdout", 0); - if (error) { - /* - * Tsleep returned (signal?). - * Go find out what the user wants. - * adjust for data copied in but - * not written - */ - uio->uio_resid += cc; - return (error); - } - goto again; - } - (*linesw[tp->t_line].l_rint)(*cp++, tp); - cnt++; - cc--; - } - cc = 0; - } - return (0); -} - -/* - * Start output on pseudo-tty. - * Wake up process selecting or sleeping for input from controlling tty. - */ static void nmdmstart(struct tty *tp) { - register struct nm_softc *pti = tp->t_dev->si_drv1; + struct softpart *pt; - if (tp->t_state & TS_TTSTOP) - return; - pti->pt_flags &= ~PF_STOPPED; - wakeup_other(tp, FREAD); + pt = tp->t_sc; + taskqueue_enqueue(taskqueue_swi_giant, &pt->pt_task); } -/* Wakes up the OTHER tty;*/ static void -wakeup_other(struct tty *tp, int flag) +nmdmstop(struct tty *tp, int flush) { - struct softpart *ourpart, *otherpart; + struct softpart *pt; - GETPARTS(tp, ourpart, otherpart); - if (flag & FREAD) { - selwakeuppri(&otherpart->nm_tty.t_rsel, TTIPRI); - wakeup(TSA_PTC_READ((&otherpart->nm_tty))); - } - if (flag & FWRITE) { - selwakeuppri(&otherpart->nm_tty.t_wsel, TTOPRI); - wakeup(TSA_PTC_WRITE((&otherpart->nm_tty))); - } -} - -/* - * stopped output on tty, called when device is closed - */ -static void -nmdmstop(register struct tty *tp, int flush) -{ - struct nm_softc *pti = tp->t_dev->si_drv1; - int flag; - - /* note: FLUSHREAD and FLUSHWRITE already ok */ - if (flush == 0) { - flush = TIOCPKT_STOP; - pti->pt_flags |= PF_STOPPED; - } else - pti->pt_flags &= ~PF_STOPPED; - /* change of perspective */ - flag = 0; - if (flush & FREAD) - flag |= FWRITE; - if (flush & FWRITE) - flag |= FREAD; - wakeup_other(tp, flag); -} - -/* - * handle ioctl(2) request from userland - */ -static int -nmdmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td) -{ - register struct tty *tp = dev->si_tty; - struct nm_softc *pti = dev->si_drv1; - int error, s; - register struct tty *tp2; - struct softpart *ourpart, *otherpart; - - s = spltty(); - GETPARTS(tp, ourpart, otherpart); - tp2 = &otherpart->nm_tty; - - error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, td); - if (error == ENOIOCTL) - error = ttioctl(tp, cmd, data, flag); - if (error == ENOIOCTL) { - switch (cmd) { - case TIOCSBRK: - otherpart->gotbreak = 1; - break; - case TIOCCBRK: - break; - case TIOCSDTR: - ourpart->modemsignals |= TIOCM_DTR; - break; - case TIOCCDTR: - ourpart->modemsignals &= TIOCM_DTR; - break; - case TIOCMSET: - ourpart->modemsignals = *(int *)data; - otherpart->modemsignals = *(int *)data; - break; - case TIOCMBIS: - ourpart->modemsignals |= *(int *)data; - break; - case TIOCMBIC: - ourpart->modemsignals &= ~(*(int *)data); - otherpart->modemsignals &= ~(*(int *)data); - break; - case TIOCMGET: - *(int *)data = ourpart->modemsignals; - break; - case TIOCMSDTRWAIT: - break; - case TIOCMGDTRWAIT: - *(int *)data = 0; - break; - case TIOCTIMESTAMP: - /* FALLTHROUGH */ - case TIOCDCDTIMESTAMP: - default: - splx(s); - error = ENOTTY; - return (error); - } - error = 0; - nmdm_crossover(pti, ourpart, otherpart); - } - splx(s); - return (error); -} - -static void -nmdm_crossover(struct nm_softc *pti, struct softpart *ourpart, - struct softpart *otherpart) -{ - otherpart->modemsignals &= ~(TIOCM_CTS|TIOCM_CAR); - if (ourpart->modemsignals & TIOCM_RTS) - otherpart->modemsignals |= TIOCM_CTS; - if (ourpart->modemsignals & TIOCM_DTR) - otherpart->modemsignals |= TIOCM_CAR; + pt = tp->t_sc; + taskqueue_enqueue(taskqueue_swi_giant, &pt->pt_task); } /* |