1098ca2bdSWarner Losh /*- 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> 51c24097e4SPoul-Henning Kamp #include <sys/serial.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); 6089c9c53dSPoul-Henning Kamp static void nmdminit(struct cdev *dev); 61c24097e4SPoul-Henning Kamp static t_modem_t nmdmmodem; 62737a1286SJulian Elischer 63737a1286SJulian Elischer static d_open_t nmdmopen; 64737a1286SJulian Elischer static d_close_t nmdmclose; 65737a1286SJulian Elischer 66737a1286SJulian Elischer static struct cdevsw nmdm_cdevsw = { 67dc08ffecSPoul-Henning Kamp .d_version = D_VERSION, 687ac40f5fSPoul-Henning Kamp .d_open = nmdmopen, 697ac40f5fSPoul-Henning Kamp .d_close = nmdmclose, 70dc08ffecSPoul-Henning Kamp .d_name = "nmdn", 71dc08ffecSPoul-Henning Kamp .d_flags = D_TTY | D_PSEUDO | D_NEEDGIANT, 72737a1286SJulian Elischer }; 73737a1286SJulian Elischer 74737a1286SJulian Elischer #define BUFSIZ 100 /* Chunk size iomoved to/from user */ 759308b700SJulian Elischer #define NMDM_MAX_NUM 128 /* Artificially limit # devices. */ 769308b700SJulian Elischer #define PF_STOPPED 0x10 /* user told stopped */ 77b0b03348SPoul-Henning Kamp #define BFLAG CLONE_FLAG0 78737a1286SJulian Elischer 79737a1286SJulian Elischer struct softpart { 809c01a318SPoul-Henning Kamp struct tty *nm_tty; 8189c9c53dSPoul-Henning Kamp struct cdev *dev; 82c24097e4SPoul-Henning Kamp int nm_dcd; 839c01a318SPoul-Henning Kamp struct task pt_task; 849c01a318SPoul-Henning Kamp struct softpart *other; 85737a1286SJulian Elischer }; 86737a1286SJulian Elischer 87737a1286SJulian Elischer struct nm_softc { 88b0b03348SPoul-Henning Kamp TAILQ_ENTRY(nm_softc) pt_list; 89737a1286SJulian Elischer int pt_flags; 90737a1286SJulian Elischer struct softpart part1, part2; 91737a1286SJulian Elischer struct prison *pt_prison; 92737a1286SJulian Elischer }; 93737a1286SJulian Elischer 94b0b03348SPoul-Henning Kamp static struct clonedevs *nmdmclones; 95b0b03348SPoul-Henning Kamp static TAILQ_HEAD(,nm_softc) nmdmhead = TAILQ_HEAD_INITIALIZER(nmdmhead); 96b0b03348SPoul-Henning Kamp 97b0b03348SPoul-Henning Kamp static void 9889c9c53dSPoul-Henning Kamp nmdm_clone(void *arg, char *name, int nameen, struct cdev **dev) 99b0b03348SPoul-Henning Kamp { 100b0b03348SPoul-Henning Kamp int i, unit; 101b0b03348SPoul-Henning Kamp char *p; 10289c9c53dSPoul-Henning Kamp struct cdev *d1, *d2; 103b0b03348SPoul-Henning Kamp 104f3732fd1SPoul-Henning Kamp if (*dev != NULL) 105b0b03348SPoul-Henning Kamp return; 106b0b03348SPoul-Henning Kamp if (strcmp(name, "nmdm") == 0) { 107b0b03348SPoul-Henning Kamp p = NULL; 108b0b03348SPoul-Henning Kamp unit = -1; 109b0b03348SPoul-Henning Kamp } else { 110b0b03348SPoul-Henning Kamp i = dev_stdclone(name, &p, "nmdm", &unit); 111b0b03348SPoul-Henning Kamp if (i == 0) 112b0b03348SPoul-Henning Kamp return; 113b0b03348SPoul-Henning Kamp if (p[0] != '\0' && p[0] != 'A' && p[0] != 'B') 114b0b03348SPoul-Henning Kamp return; 115b0b03348SPoul-Henning Kamp else if (p[0] != '\0' && p[1] != '\0') 116b0b03348SPoul-Henning Kamp return; 117b0b03348SPoul-Henning Kamp } 118b0b03348SPoul-Henning Kamp i = clone_create(&nmdmclones, &nmdm_cdevsw, &unit, &d1, 0); 119b0b03348SPoul-Henning Kamp if (i) { 120b0b03348SPoul-Henning Kamp d1 = make_dev(&nmdm_cdevsw, unit2minor(unit), 121b0b03348SPoul-Henning Kamp 0, 0, 0666, "nmdm%dA", unit); 122b0b03348SPoul-Henning Kamp if (d1 == NULL) 123b0b03348SPoul-Henning Kamp return; 124b0b03348SPoul-Henning Kamp d2 = make_dev(&nmdm_cdevsw, unit2minor(unit) | BFLAG, 125b0b03348SPoul-Henning Kamp 0, 0, 0666, "nmdm%dB", unit); 126b0b03348SPoul-Henning Kamp if (d2 == NULL) { 127b0b03348SPoul-Henning Kamp destroy_dev(d1); 128b0b03348SPoul-Henning Kamp return; 129b0b03348SPoul-Henning Kamp } 130b0b03348SPoul-Henning Kamp d2->si_drv2 = d1; 131b0b03348SPoul-Henning Kamp d1->si_drv2 = d2; 132b0b03348SPoul-Henning Kamp dev_depends(d1, d2); 133b0b03348SPoul-Henning Kamp dev_depends(d2, d1); 134b0b03348SPoul-Henning Kamp d1->si_flags |= SI_CHEAPCLONE; 135b0b03348SPoul-Henning Kamp d2->si_flags |= SI_CHEAPCLONE; 136b0b03348SPoul-Henning Kamp } 137b0b03348SPoul-Henning Kamp if (p != NULL && p[0] == 'B') 138b0b03348SPoul-Henning Kamp *dev = d1->si_drv2; 139b0b03348SPoul-Henning Kamp else 140b0b03348SPoul-Henning Kamp *dev = d1; 141b0b03348SPoul-Henning Kamp } 142b0b03348SPoul-Henning Kamp 143737a1286SJulian Elischer static void 1449c01a318SPoul-Henning Kamp nmdm_task_tty(void *arg, int pending __unused) 1459c01a318SPoul-Henning Kamp { 1469c01a318SPoul-Henning Kamp struct tty *tp, *otp; 1479c01a318SPoul-Henning Kamp struct softpart *sp; 1489c01a318SPoul-Henning Kamp int c; 149737a1286SJulian Elischer 1509c01a318SPoul-Henning Kamp tp = arg; 1519c01a318SPoul-Henning Kamp sp = tp->t_sc; 1529c01a318SPoul-Henning Kamp otp = sp->other->nm_tty; 1539c01a318SPoul-Henning Kamp KASSERT(otp != NULL, ("NULL otp in nmdmstart")); 1549c01a318SPoul-Henning Kamp KASSERT(otp != tp, ("NULL otp == tp nmdmstart")); 155c24097e4SPoul-Henning Kamp if (sp->other->nm_dcd) { 1569c01a318SPoul-Henning Kamp if (!(tp->t_state & TS_ISOPEN)) { 157c24097e4SPoul-Henning Kamp sp->other->nm_dcd = 0; 1582140d01bSPoul-Henning Kamp (void)ttyld_modem(otp, 0); 1599c01a318SPoul-Henning Kamp } 1609c01a318SPoul-Henning Kamp } else { 1619c01a318SPoul-Henning Kamp if (tp->t_state & TS_ISOPEN) { 162c24097e4SPoul-Henning Kamp sp->other->nm_dcd = 1; 1632140d01bSPoul-Henning Kamp (void)ttyld_modem(otp, 1); 1649c01a318SPoul-Henning Kamp } 1659c01a318SPoul-Henning Kamp } 1669c01a318SPoul-Henning Kamp if (tp->t_state & TS_TTSTOP) 1679c01a318SPoul-Henning Kamp return; 1689c01a318SPoul-Henning Kamp while (tp->t_outq.c_cc != 0) { 1699c01a318SPoul-Henning Kamp if (otp->t_state & TS_TBLOCK) 1709c01a318SPoul-Henning Kamp return; 1719c01a318SPoul-Henning Kamp c = getc(&tp->t_outq); 1729c01a318SPoul-Henning Kamp if (otp->t_state & TS_ISOPEN) 1732140d01bSPoul-Henning Kamp ttyld_rint(otp, c); 1749c01a318SPoul-Henning Kamp } 1759c01a318SPoul-Henning Kamp if (tp->t_outq.c_cc == 0) 1769c01a318SPoul-Henning Kamp ttwwakeup(tp); 1779c01a318SPoul-Henning Kamp } 178737a1286SJulian Elischer 179737a1286SJulian Elischer /* 180737a1286SJulian Elischer * This function creates and initializes a pair of ttys. 181737a1286SJulian Elischer */ 182737a1286SJulian Elischer static void 18389c9c53dSPoul-Henning Kamp nmdminit(struct cdev *dev1) 184737a1286SJulian Elischer { 18589c9c53dSPoul-Henning Kamp struct cdev *dev2; 186737a1286SJulian Elischer struct nm_softc *pt; 187737a1286SJulian Elischer 188b0b03348SPoul-Henning Kamp dev2 = dev1->si_drv2; 189737a1286SJulian Elischer 190b0b03348SPoul-Henning Kamp dev1->si_flags &= ~SI_CHEAPCLONE; 191b0b03348SPoul-Henning Kamp dev2->si_flags &= ~SI_CHEAPCLONE; 192737a1286SJulian Elischer 193b0b03348SPoul-Henning Kamp pt = malloc(sizeof(*pt), M_NLMDM, M_WAITOK | M_ZERO); 194b0b03348SPoul-Henning Kamp TAILQ_INSERT_TAIL(&nmdmhead, pt, pt_list); 1959c01a318SPoul-Henning Kamp 196737a1286SJulian Elischer dev1->si_drv1 = dev2->si_drv1 = pt; 197b0b03348SPoul-Henning Kamp 198b0b03348SPoul-Henning Kamp pt->part1.dev = dev1; 199b0b03348SPoul-Henning Kamp pt->part2.dev = dev2; 2009c01a318SPoul-Henning Kamp 2019c01a318SPoul-Henning Kamp pt->part1.nm_tty = ttymalloc(pt->part1.nm_tty); 2029c01a318SPoul-Henning Kamp pt->part1.nm_tty->t_oproc = nmdmstart; 2039c01a318SPoul-Henning Kamp pt->part1.nm_tty->t_stop = nmdmstop; 204c24097e4SPoul-Henning Kamp pt->part1.nm_tty->t_modem = nmdmmodem; 2059c01a318SPoul-Henning Kamp pt->part1.nm_tty->t_dev = dev1; 2069c01a318SPoul-Henning Kamp pt->part1.nm_tty->t_sc = &pt->part1; 2079c01a318SPoul-Henning Kamp TASK_INIT(&pt->part1.pt_task, 0, nmdm_task_tty, pt->part1.nm_tty); 2089c01a318SPoul-Henning Kamp 2099c01a318SPoul-Henning Kamp pt->part2.nm_tty = ttymalloc(pt->part2.nm_tty); 2109c01a318SPoul-Henning Kamp pt->part2.nm_tty->t_oproc = nmdmstart; 2119c01a318SPoul-Henning Kamp pt->part2.nm_tty->t_stop = nmdmstop; 212c24097e4SPoul-Henning Kamp pt->part2.nm_tty->t_modem = nmdmmodem; 2139c01a318SPoul-Henning Kamp pt->part2.nm_tty->t_dev = dev2; 2149c01a318SPoul-Henning Kamp pt->part2.nm_tty->t_sc = &pt->part2; 2159c01a318SPoul-Henning Kamp TASK_INIT(&pt->part2.pt_task, 0, nmdm_task_tty, pt->part2.nm_tty); 2169c01a318SPoul-Henning Kamp 2179c01a318SPoul-Henning Kamp pt->part1.other = &pt->part2; 2189c01a318SPoul-Henning Kamp pt->part2.other = &pt->part1; 2199c01a318SPoul-Henning Kamp 2209c01a318SPoul-Henning Kamp dev1->si_tty = pt->part1.nm_tty; 2219c01a318SPoul-Henning Kamp dev1->si_drv1 = pt; 2229c01a318SPoul-Henning Kamp 2239c01a318SPoul-Henning Kamp dev2->si_tty = pt->part2.nm_tty; 2249c01a318SPoul-Henning Kamp dev2->si_drv1 = pt; 225737a1286SJulian Elischer } 226737a1286SJulian Elischer 2279308b700SJulian Elischer /* 2289308b700SJulian Elischer * Device opened from userland 2299308b700SJulian Elischer */ 230737a1286SJulian Elischer static int 23189c9c53dSPoul-Henning Kamp nmdmopen(struct cdev *dev, int flag, int devtype, struct thread *td) 232737a1286SJulian Elischer { 2339c01a318SPoul-Henning Kamp struct tty *tp, *tp2; 234737a1286SJulian Elischer int error; 235737a1286SJulian Elischer struct nm_softc *pti; 2369c01a318SPoul-Henning Kamp struct softpart *sp; 237737a1286SJulian Elischer 238b0b03348SPoul-Henning Kamp if (dev->si_drv1 == NULL) 239b0b03348SPoul-Henning Kamp nmdminit(dev); 240737a1286SJulian Elischer pti = dev->si_drv1; 2419c01a318SPoul-Henning Kamp if (pti->pt_prison != td->td_ucred->cr_prison) 2429c01a318SPoul-Henning Kamp return (EBUSY); 243b0b03348SPoul-Henning Kamp 2449c01a318SPoul-Henning Kamp tp = dev->si_tty; 2459c01a318SPoul-Henning Kamp sp = tp->t_sc; 2469c01a318SPoul-Henning Kamp tp2 = sp->other->nm_tty; 247737a1286SJulian Elischer 248737a1286SJulian Elischer if ((tp->t_state & TS_ISOPEN) == 0) { 24995bc5689SPoul-Henning Kamp ttyinitmode(tp, 0, 0); 2509c01a318SPoul-Henning Kamp ttsetwater(tp); /* XXX ? */ 25144731cabSJohn Baldwin } else if (tp->t_state & TS_XCLUDE && suser(td)) { 252737a1286SJulian Elischer return (EBUSY); 253737a1286SJulian Elischer } 254737a1286SJulian Elischer 2552140d01bSPoul-Henning Kamp error = ttyld_open(tp, dev); 256737a1286SJulian Elischer return (error); 257737a1286SJulian Elischer } 258737a1286SJulian Elischer 259737a1286SJulian Elischer static int 260c24097e4SPoul-Henning Kamp nmdmmodem(struct tty *tp, int sigon, int sigoff) 261c24097e4SPoul-Henning Kamp { 262c24097e4SPoul-Henning Kamp struct softpart *sp; 263c24097e4SPoul-Henning Kamp int i; 264c24097e4SPoul-Henning Kamp 265c24097e4SPoul-Henning Kamp sp = tp->t_sc; 266c24097e4SPoul-Henning Kamp if (sigon || sigoff) { 267c24097e4SPoul-Henning Kamp if (sigon & SER_DTR) { 268c24097e4SPoul-Henning Kamp sp->other->nm_dcd = 1; 269c24097e4SPoul-Henning Kamp ttyld_modem(sp->other->nm_tty, sp->other->nm_dcd); 270c24097e4SPoul-Henning Kamp } 271c24097e4SPoul-Henning Kamp if (sigoff & SER_DTR) { 272c24097e4SPoul-Henning Kamp sp->other->nm_dcd = 0; 273c24097e4SPoul-Henning Kamp ttyld_modem(sp->other->nm_tty, sp->other->nm_dcd); 274c24097e4SPoul-Henning Kamp } 275c24097e4SPoul-Henning Kamp return (0); 276c24097e4SPoul-Henning Kamp } else { 277c24097e4SPoul-Henning Kamp i = 0; 278c24097e4SPoul-Henning Kamp if (sp->nm_dcd) 279c24097e4SPoul-Henning Kamp i |= SER_DCD; 280c24097e4SPoul-Henning Kamp if (sp->other->nm_dcd) 281c24097e4SPoul-Henning Kamp i |= SER_DTR; 282c24097e4SPoul-Henning Kamp return (i); 283c24097e4SPoul-Henning Kamp } 284c24097e4SPoul-Henning Kamp } 285c24097e4SPoul-Henning Kamp 286c24097e4SPoul-Henning Kamp static int 28789c9c53dSPoul-Henning Kamp nmdmclose(struct cdev *dev, int flag, int mode, struct thread *td) 288737a1286SJulian Elischer { 289737a1286SJulian Elischer 290672c05d4SPoul-Henning Kamp return (tty_close(dev->si_tty)); 291737a1286SJulian Elischer } 292737a1286SJulian Elischer 293737a1286SJulian Elischer static void 2949308b700SJulian Elischer nmdmstart(struct tty *tp) 295737a1286SJulian Elischer { 2969c01a318SPoul-Henning Kamp struct softpart *pt; 297737a1286SJulian Elischer 2989c01a318SPoul-Henning Kamp pt = tp->t_sc; 2999c01a318SPoul-Henning Kamp taskqueue_enqueue(taskqueue_swi_giant, &pt->pt_task); 300737a1286SJulian Elischer } 301737a1286SJulian Elischer 302737a1286SJulian Elischer static void 3039c01a318SPoul-Henning Kamp nmdmstop(struct tty *tp, int flush) 304737a1286SJulian Elischer { 3059c01a318SPoul-Henning Kamp struct softpart *pt; 3069c01a318SPoul-Henning Kamp 3079c01a318SPoul-Henning Kamp pt = tp->t_sc; 3089c01a318SPoul-Henning Kamp taskqueue_enqueue(taskqueue_swi_giant, &pt->pt_task); 309737a1286SJulian Elischer } 310737a1286SJulian Elischer 3119308b700SJulian Elischer /* 3129308b700SJulian Elischer * Module handling 3139308b700SJulian Elischer */ 3149308b700SJulian Elischer static int 3159308b700SJulian Elischer nmdm_modevent(module_t mod, int type, void *data) 316737a1286SJulian Elischer { 317b0b03348SPoul-Henning Kamp static eventhandler_tag tag; 318b0b03348SPoul-Henning Kamp struct nm_softc *pt, *tpt; 3199308b700SJulian Elischer int error = 0; 3209308b700SJulian Elischer 3219308b700SJulian Elischer switch(type) { 322b0b03348SPoul-Henning Kamp case MOD_LOAD: 3239397290eSPoul-Henning Kamp clone_setup(&nmdmclones); 324b0b03348SPoul-Henning Kamp tag = EVENTHANDLER_REGISTER(dev_clone, nmdm_clone, 0, 1000); 325b0b03348SPoul-Henning Kamp if (tag == NULL) 326b0b03348SPoul-Henning Kamp return (ENOMEM); 3279308b700SJulian Elischer break; 3289308b700SJulian Elischer 3299308b700SJulian Elischer case MOD_SHUTDOWN: 3309308b700SJulian Elischer /* FALLTHROUGH */ 3319308b700SJulian Elischer case MOD_UNLOAD: 332b0b03348SPoul-Henning Kamp EVENTHANDLER_DEREGISTER(dev_clone, tag); 333b0b03348SPoul-Henning Kamp TAILQ_FOREACH_SAFE(pt, &nmdmhead, pt_list, tpt) { 334b0b03348SPoul-Henning Kamp destroy_dev(pt->part1.dev); 335b0b03348SPoul-Henning Kamp TAILQ_REMOVE(&nmdmhead, pt, pt_list); 336b0b03348SPoul-Henning Kamp free(pt, M_NLMDM); 337b0b03348SPoul-Henning Kamp } 338b0b03348SPoul-Henning Kamp clone_cleanup(&nmdmclones); 3399308b700SJulian Elischer break; 3409308b700SJulian Elischer default: 3419308b700SJulian Elischer error = EOPNOTSUPP; 3429308b700SJulian Elischer } 3439308b700SJulian Elischer return (error); 344737a1286SJulian Elischer } 345737a1286SJulian Elischer 3469308b700SJulian Elischer DEV_MODULE(nmdm, nmdm_modevent, NULL); 347