aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/nmdm/nmdm.c
diff options
context:
space:
mode:
authorPoul-Henning Kamp <phk@FreeBSD.org>2004-06-01 22:53:00 +0000
committerPoul-Henning Kamp <phk@FreeBSD.org>2004-06-01 22:53:00 +0000
commit9c01a318d2573f667f09d55a69f249a18c7219f8 (patch)
treea529f4b6b46afede731abf9a8af3652b539cabc3 /sys/dev/nmdm/nmdm.c
parentc23b00b7ca8be830f5d4306d39d07f3c046d4a34 (diff)
downloadsrc-9c01a318d2573f667f09d55a69f249a18c7219f8.tar.gz
src-9c01a318d2573f667f09d55a69f249a18c7219f8.zip
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.
Notes
Notes: svn path=/head/; revision=129968
Diffstat (limited to 'sys/dev/nmdm/nmdm.c')
-rw-r--r--sys/dev/nmdm/nmdm.c456
1 files changed, 81 insertions, 375 deletions
diff --git a/sys/dev/nmdm/nmdm.c b/sys/dev/nmdm/nmdm.c
index 79a547687ef8..8855a27ff006 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);
}
/*