summaryrefslogtreecommitdiffstats
path: root/sys/dev/nmdm
diff options
context:
space:
mode:
authorjulian <julian@FreeBSD.org>2001-02-27 16:39:54 +0000
committerjulian <julian@FreeBSD.org>2001-02-27 16:39:54 +0000
commit9bfaef26091fb46a50aee15ca12d1f672153c574 (patch)
treeb160f1fceda762b0cb26b1f439e65f52269a9b4b /sys/dev/nmdm
parentd6232689cec5a8fbe56be256b29aa9ee9fc4331b (diff)
downloadFreeBSD-src-9bfaef26091fb46a50aee15ca12d1f672153c574.zip
FreeBSD-src-9bfaef26091fb46a50aee15ca12d1f672153c574.tar.gz
Do it right this time. Give it a better name and place
CVSrepo deletion of the previous attempt will be requested: --original message-- Add the 'virtual nulmodem driver' Particularly useful for debuging kernels using vmware. If your name is Bruce evans and you are a WIZ at tty interfaces, then you should probably rip this to shreds and offer lots of suggestions and patches. I've been using this since 4.0-CURRENT and it's never caused problems but I'm sure I got something wrong. This is similar to the pty/cty device driver except that both sides are ttys. Even minor numbers are side A and odd minor numbers are side B. Work needs to be done regarding what happens to the other side when you close a node. to use with vmware, configure vmware to redirect COM2 out to side A of one of these and boot a kernel with teh gdb remote port set to sio1. AFTER dropping into the gdb kernel debugger in your test kernel, fire up gdb with it's remote port pointing at the appropriate side B. To catch all console output, you can boot the vmware kernel with a serial console, (COM1) similarly redirected to a nulmodem, and use 'tip' to observe it. This is practically unaltered since pre 4.0 days except for changes made along the way needed to make it compile, so any suggestions or offers of total rewrites will be listenned to :-)
Diffstat (limited to 'sys/dev/nmdm')
-rw-r--r--sys/dev/nmdm/nmdm.c644
1 files changed, 644 insertions, 0 deletions
diff --git a/sys/dev/nmdm/nmdm.c b/sys/dev/nmdm/nmdm.c
new file mode 100644
index 0000000..55468ad
--- /dev/null
+++ b/sys/dev/nmdm/nmdm.c
@@ -0,0 +1,644 @@
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Pseudo-nulmodem Driver
+ */
+#include "opt_compat.h"
+#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>
+#include <sys/fcntl.h>
+#include <sys/poll.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <sys/signalvar.h>
+#include <sys/malloc.h>
+
+MALLOC_DEFINE(M_NLMDM, "nullmodem", "nullmodem data structures");
+
+static void nmdmstart __P((struct tty *tp));
+static void nmdmstop __P((struct tty *tp, int rw));
+static void wakeup_other __P((struct tty *tp, int flag));
+static void nmdminit __P((int n));
+
+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;
+
+#define CDEV_MAJOR 18
+static struct cdevsw nmdm_cdevsw = {
+ /* open */ nmdmopen,
+ /* close */ nmdmclose,
+ /* read */ nmdmread,
+ /* write */ nmdmwrite,
+ /* ioctl */ nmdmioctl,
+ /* poll */ ttypoll,
+ /* mmap */ nommap,
+ /* strategy */ nostrategy,
+ /* name */ "pts",
+ /* maj */ CDEV_MAJOR,
+ /* dump */ nodump,
+ /* psize */ nopsize,
+ /* flags */ D_TTY,
+ /* bmaj */ -1
+};
+
+#define BUFSIZ 100 /* Chunk size iomoved to/from user */
+
+struct softpart {
+ struct tty nm_tty;
+ dev_t dev;
+ int modemsignals; /* bits defined in sys/ttycom.h */
+ int gotbreak;
+};
+
+struct nm_softc {
+ int pt_flags;
+ struct softpart part1, part2;
+ struct prison *pt_prison;
+};
+
+#define PF_STOPPED 0x10 /* user told stopped */
+
+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)
+
+/*
+ * This function creates and initializes a pair of ttys.
+ */
+static void
+nmdminit(n)
+ int n;
+{
+ dev_t dev1, dev2;
+ struct nm_softc *pt;
+
+ /* For now we only map the lower 8 bits of the minor */
+ if (n & ~0xff)
+ return;
+
+ pt = malloc(sizeof(*pt), M_NLMDM, M_WAITOK);
+ bzero(pt, sizeof(*pt));
+ pt->part1.dev = dev1 = make_dev(&nmdm_cdevsw, n+n,
+ 0, 0, 0666, "nmdm%dA", n);
+ pt->part2.dev = dev2 = make_dev(&nmdm_cdevsw, n+n+1,
+ 0, 0, 0666, "nmdm%dB", n);
+
+ dev1->si_drv1 = dev2->si_drv1 = pt;
+ 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_dev = dev1;
+ pt->part1.nm_tty.t_dev = dev2;
+ pt->part2.nm_tty.t_stop = nmdmstop;
+}
+
+/*ARGSUSED*/
+static int
+nmdmopen(dev, flag, devtype, p)
+ dev_t dev;
+ int flag, devtype;
+ struct proc *p;
+{
+ register struct tty *tp, *tp2;
+ int error;
+ int minr;
+ dev_t nextdev;
+ struct nm_softc *pti;
+ int is_b;
+ int pair;
+ struct softpart *ourpart, *otherpart;
+
+ /*
+ * XXX: Gross hack for DEVFS:
+ * If we openned this device, ensure we have the
+ * next one too, so people can open it.
+ */
+ minr = dev2unit(dev);
+ pair = minr >> 1;
+ is_b = minr & 1;
+
+ if (pair < 127) {
+ nextdev = makedev(major(dev), (pair+pair) + 1);
+ if (!nextdev->si_drv1) {
+ nmdminit(pair + 1);
+ }
+ }
+ if (!dev->si_drv1)
+ nmdminit(pair);
+
+ if (!dev->si_drv1)
+ return(ENXIO);
+
+ pti = dev->si_drv1;
+ if (is_b)
+ tp = &pti->part2.nm_tty;
+ else
+ tp = &pti->part1.nm_tty;
+ GETPARTS(tp, ourpart, otherpart);
+ tp2 = &otherpart->nm_tty;
+ ourpart->modemsignals |= TIOCM_LE;
+
+ 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_cflag = TTYDEF_CFLAG;
+ tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
+ } else if (tp->t_state & TS_XCLUDE && suser(p)) {
+ return (EBUSY);
+ } else if (pti->pt_prison != p->p_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);
+}
+
+static int
+nmdmclose(dev, flag, mode, p)
+ dev_t dev;
+ int flag, mode;
+ struct proc *p;
+{
+ 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);
+ nmdmstop(tp, FREAD|FWRITE);
+ (void) ttyclose(tp);
+ return (err);
+}
+
+static int
+nmdmread(dev, uio, flag)
+ 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, uio, flag)
+ 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(tp)
+ struct tty *tp;
+{
+ register struct nm_softc *pti = tp->t_dev->si_drv1;
+
+ if (tp->t_state & TS_TTSTOP)
+ return;
+ pti->pt_flags &= ~PF_STOPPED;
+ wakeup_other(tp, FREAD);
+}
+
+/* Wakes up the OTHER tty;*/
+static void
+wakeup_other(tp, flag)
+ struct tty *tp;
+ int flag;
+{
+ struct softpart *ourpart, *otherpart;
+
+ GETPARTS(tp, ourpart, otherpart);
+ if (flag & FREAD) {
+ selwakeup(&otherpart->nm_tty.t_rsel);
+ wakeup(TSA_PTC_READ((&otherpart->nm_tty)));
+ }
+ if (flag & FWRITE) {
+ selwakeup(&otherpart->nm_tty.t_wsel);
+ wakeup(TSA_PTC_WRITE((&otherpart->nm_tty)));
+ }
+}
+
+static void
+nmdmstop(tp, flush)
+ 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);
+}
+
+static int
+nmdmpoll(dev, events, p)
+ dev_t dev;
+ int events;
+ struct proc *p;
+{
+ register struct tty *tp = dev->si_tty;
+ register struct tty *tp2;
+ int revents = 0;
+ int s;
+ struct softpart *ourpart, *otherpart;
+
+ GETPARTS(tp, ourpart, otherpart);
+ tp2 = &otherpart->nm_tty;
+
+ if ((tp->t_state & TS_CONNECTED) == 0)
+ return (seltrue(dev, events, p) | POLLHUP);
+
+ /*
+ * Need to block timeouts (ttrstart).
+ */
+ s = spltty();
+
+ /*
+ * First check if there is something to report immediatly.
+ */
+ if ((events & (POLLIN | POLLRDNORM))) {
+ if (tp->t_iflag & ICANON) {
+ if (tp->t_canq.c_cc)
+ revents |= events & (POLLIN | POLLRDNORM);
+ } else {
+ if (tp->t_rawq.c_cc + tp->t_canq.c_cc)
+ revents |= events & (POLLIN | POLLRDNORM);
+ }
+ }
+
+ /*
+ * check if there is room in the other tty's input buffers.
+ */
+ if ((events & (POLLOUT | POLLWRNORM))
+ && ((tp2->t_rawq.c_cc + tp2->t_canq.c_cc < TTYHOG - 2)
+ || (tp2->t_canq.c_cc == 0 && (tp2->t_iflag & ICANON)))) {
+ revents |= events & (POLLOUT | POLLWRNORM);
+ }
+
+ if (events & POLLHUP)
+ if ((tp->t_state & TS_CARR_ON) == 0)
+ revents |= POLLHUP;
+
+ /*
+ * If nothing immediate, set us to return when something IS found.
+ */
+ if (revents == 0) {
+ if (events & (POLLIN | POLLRDNORM))
+ selrecord(p, &tp->t_rsel);
+
+ if (events & (POLLOUT | POLLWRNORM))
+ selrecord(p, &tp->t_wsel);
+ }
+ splx(s);
+
+ return (revents);
+}
+
+/*ARGSUSED*/
+static int
+nmdmioctl(dev, cmd, data, flag, p)
+ dev_t dev;
+ u_long cmd;
+ caddr_t data;
+ int flag;
+ struct proc *p;
+{
+ 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, p);
+ 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:
+ 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;
+}
+
+
+
+static void nmdm_drvinit __P((void *unused));
+
+static void
+nmdm_drvinit(unused)
+ void *unused;
+{
+ cdevsw_add(&nmdm_cdevsw);
+ /* XXX: Gross hack for DEVFS */
+ nmdminit(0);
+}
+
+SYSINIT(nmdmdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,nmdm_drvinit,NULL)
OpenPOWER on IntegriCloud