1737a1286SJulian Elischer /* 2737a1286SJulian Elischer * Copyright (c) 1982, 1986, 1989, 1993 3737a1286SJulian Elischer * The Regents of the University of California. All rights reserved. 4737a1286SJulian Elischer * 5737a1286SJulian Elischer * Redistribution and use in source and binary forms, with or without 6737a1286SJulian Elischer * modification, are permitted provided that the following conditions 7737a1286SJulian Elischer * are met: 8737a1286SJulian Elischer * 1. Redistributions of source code must retain the above copyright 9737a1286SJulian Elischer * notice, this list of conditions and the following disclaimer. 10737a1286SJulian Elischer * 2. Redistributions in binary form must reproduce the above copyright 11737a1286SJulian Elischer * notice, this list of conditions and the following disclaimer in the 12737a1286SJulian Elischer * documentation and/or other materials provided with the distribution. 13737a1286SJulian Elischer * 4. Neither the name of the University nor the names of its contributors 14737a1286SJulian Elischer * may be used to endorse or promote products derived from this software 15737a1286SJulian Elischer * without specific prior written permission. 16737a1286SJulian Elischer * 17737a1286SJulian Elischer * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18737a1286SJulian Elischer * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19737a1286SJulian Elischer * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20737a1286SJulian Elischer * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21737a1286SJulian Elischer * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22737a1286SJulian Elischer * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23737a1286SJulian Elischer * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24737a1286SJulian Elischer * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25737a1286SJulian Elischer * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26737a1286SJulian Elischer * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27737a1286SJulian Elischer * SUCH DAMAGE. 28737a1286SJulian Elischer * 29737a1286SJulian Elischer */ 30737a1286SJulian Elischer 31aad970f1SDavid E. O'Brien #include <sys/cdefs.h> 32aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$"); 33aad970f1SDavid E. O'Brien 34737a1286SJulian Elischer /* 359308b700SJulian Elischer * Pseudo-nulmodem driver 369308b700SJulian Elischer * Mighty handy for use with serial console in Vmware 37737a1286SJulian Elischer */ 389308b700SJulian Elischer 39737a1286SJulian Elischer #include "opt_compat.h" 409c62b3eeSDavid Schultz #include "opt_tty.h" 419c62b3eeSDavid Schultz 42737a1286SJulian Elischer #include <sys/param.h> 43737a1286SJulian Elischer #include <sys/systm.h> 44737a1286SJulian Elischer #include <sys/proc.h> 45737a1286SJulian Elischer #include <sys/tty.h> 46737a1286SJulian Elischer #include <sys/conf.h> 47737a1286SJulian Elischer #include <sys/fcntl.h> 48737a1286SJulian Elischer #include <sys/poll.h> 49737a1286SJulian Elischer #include <sys/kernel.h> 50fe12f24bSPoul-Henning Kamp #include <sys/module.h> 51737a1286SJulian Elischer #include <sys/vnode.h> 52737a1286SJulian Elischer #include <sys/signalvar.h> 53737a1286SJulian Elischer #include <sys/malloc.h> 549c01a318SPoul-Henning Kamp #include <sys/taskqueue.h> 55737a1286SJulian Elischer 56737a1286SJulian Elischer MALLOC_DEFINE(M_NLMDM, "nullmodem", "nullmodem data structures"); 57737a1286SJulian Elischer 589308b700SJulian Elischer static void nmdmstart(struct tty *tp); 599308b700SJulian Elischer static void nmdmstop(struct tty *tp, int rw); 60b0b03348SPoul-Henning Kamp static void nmdminit(dev_t dev); 61737a1286SJulian Elischer 62737a1286SJulian Elischer static d_open_t nmdmopen; 63737a1286SJulian Elischer static d_close_t nmdmclose; 64737a1286SJulian Elischer 65737a1286SJulian Elischer static struct cdevsw nmdm_cdevsw = { 66dc08ffecSPoul-Henning Kamp .d_version = D_VERSION, 677ac40f5fSPoul-Henning Kamp .d_open = nmdmopen, 687ac40f5fSPoul-Henning Kamp .d_close = nmdmclose, 69dc08ffecSPoul-Henning Kamp .d_name = "nmdn", 70dc08ffecSPoul-Henning Kamp .d_flags = D_TTY | D_PSEUDO | D_NEEDGIANT, 71737a1286SJulian Elischer }; 72737a1286SJulian Elischer 73737a1286SJulian Elischer #define BUFSIZ 100 /* Chunk size iomoved to/from user */ 749308b700SJulian Elischer #define NMDM_MAX_NUM 128 /* Artificially limit # devices. */ 759308b700SJulian Elischer #define PF_STOPPED 0x10 /* user told stopped */ 76b0b03348SPoul-Henning Kamp #define BFLAG CLONE_FLAG0 77737a1286SJulian Elischer 78737a1286SJulian Elischer struct softpart { 799c01a318SPoul-Henning Kamp struct tty *nm_tty; 80737a1286SJulian Elischer dev_t dev; 819c01a318SPoul-Henning Kamp int dcd; 829c01a318SPoul-Henning Kamp struct task pt_task; 839c01a318SPoul-Henning Kamp struct softpart *other; 84737a1286SJulian Elischer }; 85737a1286SJulian Elischer 86737a1286SJulian Elischer struct nm_softc { 87b0b03348SPoul-Henning Kamp TAILQ_ENTRY(nm_softc) pt_list; 88737a1286SJulian Elischer int pt_flags; 89737a1286SJulian Elischer struct softpart part1, part2; 90737a1286SJulian Elischer struct prison *pt_prison; 91737a1286SJulian Elischer }; 92737a1286SJulian Elischer 93b0b03348SPoul-Henning Kamp static struct clonedevs *nmdmclones; 94b0b03348SPoul-Henning Kamp static TAILQ_HEAD(,nm_softc) nmdmhead = TAILQ_HEAD_INITIALIZER(nmdmhead); 95b0b03348SPoul-Henning Kamp 96b0b03348SPoul-Henning Kamp static void 97b0b03348SPoul-Henning Kamp nmdm_clone(void *arg, char *name, int nameen, dev_t *dev) 98b0b03348SPoul-Henning Kamp { 99b0b03348SPoul-Henning Kamp int i, unit; 100b0b03348SPoul-Henning Kamp char *p; 101b0b03348SPoul-Henning Kamp dev_t d1, d2; 102b0b03348SPoul-Henning Kamp 103b0b03348SPoul-Henning Kamp if (*dev != NODEV) 104b0b03348SPoul-Henning Kamp return; 105b0b03348SPoul-Henning Kamp if (strcmp(name, "nmdm") == 0) { 106b0b03348SPoul-Henning Kamp p = NULL; 107b0b03348SPoul-Henning Kamp unit = -1; 108b0b03348SPoul-Henning Kamp } else { 109b0b03348SPoul-Henning Kamp i = dev_stdclone(name, &p, "nmdm", &unit); 110b0b03348SPoul-Henning Kamp if (i == 0) 111b0b03348SPoul-Henning Kamp return; 112b0b03348SPoul-Henning Kamp if (p[0] != '\0' && p[0] != 'A' && p[0] != 'B') 113b0b03348SPoul-Henning Kamp return; 114b0b03348SPoul-Henning Kamp else if (p[0] != '\0' && p[1] != '\0') 115b0b03348SPoul-Henning Kamp return; 116b0b03348SPoul-Henning Kamp } 117b0b03348SPoul-Henning Kamp i = clone_create(&nmdmclones, &nmdm_cdevsw, &unit, &d1, 0); 118b0b03348SPoul-Henning Kamp if (i) { 119b0b03348SPoul-Henning Kamp d1 = make_dev(&nmdm_cdevsw, unit2minor(unit), 120b0b03348SPoul-Henning Kamp 0, 0, 0666, "nmdm%dA", unit); 121b0b03348SPoul-Henning Kamp if (d1 == NULL) 122b0b03348SPoul-Henning Kamp return; 123b0b03348SPoul-Henning Kamp d2 = make_dev(&nmdm_cdevsw, unit2minor(unit) | BFLAG, 124b0b03348SPoul-Henning Kamp 0, 0, 0666, "nmdm%dB", unit); 125b0b03348SPoul-Henning Kamp if (d2 == NULL) { 126b0b03348SPoul-Henning Kamp destroy_dev(d1); 127b0b03348SPoul-Henning Kamp return; 128b0b03348SPoul-Henning Kamp } 129b0b03348SPoul-Henning Kamp d2->si_drv2 = d1; 130b0b03348SPoul-Henning Kamp d1->si_drv2 = d2; 131b0b03348SPoul-Henning Kamp dev_depends(d1, d2); 132b0b03348SPoul-Henning Kamp dev_depends(d2, d1); 133b0b03348SPoul-Henning Kamp d1->si_flags |= SI_CHEAPCLONE; 134b0b03348SPoul-Henning Kamp d2->si_flags |= SI_CHEAPCLONE; 135b0b03348SPoul-Henning Kamp } 136b0b03348SPoul-Henning Kamp if (p != NULL && p[0] == 'B') 137b0b03348SPoul-Henning Kamp *dev = d1->si_drv2; 138b0b03348SPoul-Henning Kamp else 139b0b03348SPoul-Henning Kamp *dev = d1; 140b0b03348SPoul-Henning Kamp } 141b0b03348SPoul-Henning Kamp 142737a1286SJulian Elischer static void 1439c01a318SPoul-Henning Kamp nmdm_task_tty(void *arg, int pending __unused) 1449c01a318SPoul-Henning Kamp { 1459c01a318SPoul-Henning Kamp struct tty *tp, *otp; 1469c01a318SPoul-Henning Kamp struct softpart *sp; 1479c01a318SPoul-Henning Kamp int c; 148737a1286SJulian Elischer 1499c01a318SPoul-Henning Kamp tp = arg; 1509c01a318SPoul-Henning Kamp sp = tp->t_sc; 1519c01a318SPoul-Henning Kamp otp = sp->other->nm_tty; 1529c01a318SPoul-Henning Kamp KASSERT(otp != NULL, ("NULL otp in nmdmstart")); 1539c01a318SPoul-Henning Kamp KASSERT(otp != tp, ("NULL otp == tp nmdmstart")); 1549c01a318SPoul-Henning Kamp if (sp->other->dcd) { 1559c01a318SPoul-Henning Kamp if (!(tp->t_state & TS_ISOPEN)) { 1569c01a318SPoul-Henning Kamp sp->other->dcd = 0; 1579c01a318SPoul-Henning Kamp (void)(*linesw[otp->t_line].l_modem)(otp, 0); 1589c01a318SPoul-Henning Kamp } 1599c01a318SPoul-Henning Kamp } else { 1609c01a318SPoul-Henning Kamp if (tp->t_state & TS_ISOPEN) { 1619c01a318SPoul-Henning Kamp sp->other->dcd = 1; 1629c01a318SPoul-Henning Kamp (void)(*linesw[otp->t_line].l_modem)(otp, 1); 1639c01a318SPoul-Henning Kamp } 1649c01a318SPoul-Henning Kamp } 1659c01a318SPoul-Henning Kamp if (tp->t_state & TS_TTSTOP) 1669c01a318SPoul-Henning Kamp return; 1679c01a318SPoul-Henning Kamp while (tp->t_outq.c_cc != 0) { 1689c01a318SPoul-Henning Kamp if (otp->t_state & TS_TBLOCK) 1699c01a318SPoul-Henning Kamp return; 1709c01a318SPoul-Henning Kamp c = getc(&tp->t_outq); 1719c01a318SPoul-Henning Kamp if (otp->t_state & TS_ISOPEN) 1729c01a318SPoul-Henning Kamp (*linesw[otp->t_line].l_rint)(c, otp); 1739c01a318SPoul-Henning Kamp } 1749c01a318SPoul-Henning Kamp if (tp->t_outq.c_cc == 0) 1759c01a318SPoul-Henning Kamp ttwwakeup(tp); 1769c01a318SPoul-Henning Kamp } 177737a1286SJulian Elischer 178737a1286SJulian Elischer /* 179737a1286SJulian Elischer * This function creates and initializes a pair of ttys. 180737a1286SJulian Elischer */ 181737a1286SJulian Elischer static void 182b0b03348SPoul-Henning Kamp nmdminit(dev_t dev1) 183737a1286SJulian Elischer { 184b0b03348SPoul-Henning Kamp dev_t dev2; 185737a1286SJulian Elischer struct nm_softc *pt; 186737a1286SJulian Elischer 187b0b03348SPoul-Henning Kamp dev2 = dev1->si_drv2; 188737a1286SJulian Elischer 189b0b03348SPoul-Henning Kamp dev1->si_flags &= ~SI_CHEAPCLONE; 190b0b03348SPoul-Henning Kamp dev2->si_flags &= ~SI_CHEAPCLONE; 191737a1286SJulian Elischer 192b0b03348SPoul-Henning Kamp pt = malloc(sizeof(*pt), M_NLMDM, M_WAITOK | M_ZERO); 193b0b03348SPoul-Henning Kamp TAILQ_INSERT_TAIL(&nmdmhead, pt, pt_list); 1949c01a318SPoul-Henning Kamp 195737a1286SJulian Elischer dev1->si_drv1 = dev2->si_drv1 = pt; 196b0b03348SPoul-Henning Kamp 197b0b03348SPoul-Henning Kamp pt->part1.dev = dev1; 198b0b03348SPoul-Henning Kamp pt->part2.dev = dev2; 1999c01a318SPoul-Henning Kamp 2009c01a318SPoul-Henning Kamp pt->part1.nm_tty = ttymalloc(pt->part1.nm_tty); 2019c01a318SPoul-Henning Kamp pt->part1.nm_tty->t_oproc = nmdmstart; 2029c01a318SPoul-Henning Kamp pt->part1.nm_tty->t_stop = nmdmstop; 2039c01a318SPoul-Henning Kamp pt->part1.nm_tty->t_dev = dev1; 2049c01a318SPoul-Henning Kamp pt->part1.nm_tty->t_sc = &pt->part1; 2059c01a318SPoul-Henning Kamp TASK_INIT(&pt->part1.pt_task, 0, nmdm_task_tty, pt->part1.nm_tty); 2069c01a318SPoul-Henning Kamp 2079c01a318SPoul-Henning Kamp pt->part2.nm_tty = ttymalloc(pt->part2.nm_tty); 2089c01a318SPoul-Henning Kamp pt->part2.nm_tty->t_oproc = nmdmstart; 2099c01a318SPoul-Henning Kamp pt->part2.nm_tty->t_stop = nmdmstop; 2109c01a318SPoul-Henning Kamp pt->part2.nm_tty->t_dev = dev2; 2119c01a318SPoul-Henning Kamp pt->part2.nm_tty->t_sc = &pt->part2; 2129c01a318SPoul-Henning Kamp TASK_INIT(&pt->part2.pt_task, 0, nmdm_task_tty, pt->part2.nm_tty); 2139c01a318SPoul-Henning Kamp 2149c01a318SPoul-Henning Kamp pt->part1.other = &pt->part2; 2159c01a318SPoul-Henning Kamp pt->part2.other = &pt->part1; 2169c01a318SPoul-Henning Kamp 2179c01a318SPoul-Henning Kamp dev1->si_tty = pt->part1.nm_tty; 2189c01a318SPoul-Henning Kamp dev1->si_drv1 = pt; 2199c01a318SPoul-Henning Kamp 2209c01a318SPoul-Henning Kamp dev2->si_tty = pt->part2.nm_tty; 2219c01a318SPoul-Henning Kamp dev2->si_drv1 = pt; 222737a1286SJulian Elischer } 223737a1286SJulian Elischer 2249308b700SJulian Elischer /* 2259308b700SJulian Elischer * Device opened from userland 2269308b700SJulian Elischer */ 227737a1286SJulian Elischer static int 2289308b700SJulian Elischer nmdmopen(dev_t dev, int flag, int devtype, struct thread *td) 229737a1286SJulian Elischer { 2309c01a318SPoul-Henning Kamp struct tty *tp, *tp2; 231737a1286SJulian Elischer int error; 232737a1286SJulian Elischer struct nm_softc *pti; 2339c01a318SPoul-Henning Kamp struct softpart *sp; 234737a1286SJulian Elischer 235b0b03348SPoul-Henning Kamp if (dev->si_drv1 == NULL) 236b0b03348SPoul-Henning Kamp nmdminit(dev); 237737a1286SJulian Elischer pti = dev->si_drv1; 2389c01a318SPoul-Henning Kamp if (pti->pt_prison != td->td_ucred->cr_prison) 2399c01a318SPoul-Henning Kamp return (EBUSY); 240b0b03348SPoul-Henning Kamp 2419c01a318SPoul-Henning Kamp tp = dev->si_tty; 2429c01a318SPoul-Henning Kamp sp = tp->t_sc; 2439c01a318SPoul-Henning Kamp tp2 = sp->other->nm_tty; 244737a1286SJulian Elischer 245737a1286SJulian Elischer if ((tp->t_state & TS_ISOPEN) == 0) { 246737a1286SJulian Elischer ttychars(tp); /* Set up default chars */ 247737a1286SJulian Elischer tp->t_iflag = TTYDEF_IFLAG; 248737a1286SJulian Elischer tp->t_oflag = TTYDEF_OFLAG; 249737a1286SJulian Elischer tp->t_lflag = TTYDEF_LFLAG; 2509c01a318SPoul-Henning Kamp tp->t_lflag = 0; 251737a1286SJulian Elischer tp->t_cflag = TTYDEF_CFLAG; 252737a1286SJulian Elischer tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 2539c01a318SPoul-Henning Kamp ttsetwater(tp); /* XXX ? */ 25444731cabSJohn Baldwin } else if (tp->t_state & TS_XCLUDE && suser(td)) { 255737a1286SJulian Elischer return (EBUSY); 256737a1286SJulian Elischer } 257737a1286SJulian Elischer 258737a1286SJulian Elischer error = (*linesw[tp->t_line].l_open)(dev, tp); 259737a1286SJulian Elischer return (error); 260737a1286SJulian Elischer } 261737a1286SJulian Elischer 262737a1286SJulian Elischer static int 2639308b700SJulian Elischer nmdmclose(dev_t dev, int flag, int mode, struct thread *td) 264737a1286SJulian Elischer { 265737a1286SJulian Elischer 2669c01a318SPoul-Henning Kamp return (ttyclose(dev->si_tty)); 267737a1286SJulian Elischer } 268737a1286SJulian Elischer 269737a1286SJulian Elischer static void 2709308b700SJulian Elischer nmdmstart(struct tty *tp) 271737a1286SJulian Elischer { 2729c01a318SPoul-Henning Kamp struct softpart *pt; 273737a1286SJulian Elischer 2749c01a318SPoul-Henning Kamp pt = tp->t_sc; 2759c01a318SPoul-Henning Kamp taskqueue_enqueue(taskqueue_swi_giant, &pt->pt_task); 276737a1286SJulian Elischer } 277737a1286SJulian Elischer 278737a1286SJulian Elischer static void 2799c01a318SPoul-Henning Kamp nmdmstop(struct tty *tp, int flush) 280737a1286SJulian Elischer { 2819c01a318SPoul-Henning Kamp struct softpart *pt; 2829c01a318SPoul-Henning Kamp 2839c01a318SPoul-Henning Kamp pt = tp->t_sc; 2849c01a318SPoul-Henning Kamp taskqueue_enqueue(taskqueue_swi_giant, &pt->pt_task); 285737a1286SJulian Elischer } 286737a1286SJulian Elischer 2879308b700SJulian Elischer /* 2889308b700SJulian Elischer * Module handling 2899308b700SJulian Elischer */ 2909308b700SJulian Elischer static int 2919308b700SJulian Elischer nmdm_modevent(module_t mod, int type, void *data) 292737a1286SJulian Elischer { 293b0b03348SPoul-Henning Kamp static eventhandler_tag tag; 294b0b03348SPoul-Henning Kamp struct nm_softc *pt, *tpt; 2959308b700SJulian Elischer int error = 0; 2969308b700SJulian Elischer 2979308b700SJulian Elischer switch(type) { 298b0b03348SPoul-Henning Kamp case MOD_LOAD: 2999397290eSPoul-Henning Kamp clone_setup(&nmdmclones); 300b0b03348SPoul-Henning Kamp tag = EVENTHANDLER_REGISTER(dev_clone, nmdm_clone, 0, 1000); 301b0b03348SPoul-Henning Kamp if (tag == NULL) 302b0b03348SPoul-Henning Kamp return (ENOMEM); 3039308b700SJulian Elischer break; 3049308b700SJulian Elischer 3059308b700SJulian Elischer case MOD_SHUTDOWN: 3069308b700SJulian Elischer /* FALLTHROUGH */ 3079308b700SJulian Elischer case MOD_UNLOAD: 308b0b03348SPoul-Henning Kamp EVENTHANDLER_DEREGISTER(dev_clone, tag); 309b0b03348SPoul-Henning Kamp TAILQ_FOREACH_SAFE(pt, &nmdmhead, pt_list, tpt) { 310b0b03348SPoul-Henning Kamp destroy_dev(pt->part1.dev); 311b0b03348SPoul-Henning Kamp TAILQ_REMOVE(&nmdmhead, pt, pt_list); 312b0b03348SPoul-Henning Kamp free(pt, M_NLMDM); 313b0b03348SPoul-Henning Kamp } 314b0b03348SPoul-Henning Kamp clone_cleanup(&nmdmclones); 3159308b700SJulian Elischer break; 3169308b700SJulian Elischer default: 3179308b700SJulian Elischer error = EOPNOTSUPP; 3189308b700SJulian Elischer } 3199308b700SJulian Elischer return (error); 320737a1286SJulian Elischer } 321737a1286SJulian Elischer 3229308b700SJulian Elischer DEV_MODULE(nmdm, nmdm_modevent, NULL); 323