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 <sys/param.h> 40737a1286SJulian Elischer #include <sys/systm.h> 41acd3428bSRobert Watson #include <sys/priv.h> 42737a1286SJulian Elischer #include <sys/proc.h> 43737a1286SJulian Elischer #include <sys/tty.h> 44737a1286SJulian Elischer #include <sys/conf.h> 45737a1286SJulian Elischer #include <sys/fcntl.h> 46737a1286SJulian Elischer #include <sys/poll.h> 47737a1286SJulian Elischer #include <sys/kernel.h> 48bc093719SEd Schouten #include <sys/limits.h> 49fe12f24bSPoul-Henning Kamp #include <sys/module.h> 50c24097e4SPoul-Henning Kamp #include <sys/serial.h> 51737a1286SJulian Elischer #include <sys/signalvar.h> 52737a1286SJulian Elischer #include <sys/malloc.h> 539c01a318SPoul-Henning Kamp #include <sys/taskqueue.h> 54737a1286SJulian Elischer 55d745c852SEd Schouten static MALLOC_DEFINE(M_NMDM, "nullmodem", "nullmodem data structures"); 56737a1286SJulian Elischer 57bc093719SEd Schouten static tsw_inwakeup_t nmdm_outwakeup; 58bc093719SEd Schouten static tsw_outwakeup_t nmdm_inwakeup; 59bc093719SEd Schouten static tsw_param_t nmdm_param; 60bc093719SEd Schouten static tsw_modem_t nmdm_modem; 61a95ecdf0SGleb Smirnoff static tsw_close_t nmdm_close; 62a95ecdf0SGleb Smirnoff static tsw_free_t nmdm_free; 63017a4322SPoul-Henning Kamp 64bc093719SEd Schouten static struct ttydevsw nmdm_class = { 65bc093719SEd Schouten .tsw_flags = TF_NOPREFIX, 66bc093719SEd Schouten .tsw_inwakeup = nmdm_inwakeup, 67bc093719SEd Schouten .tsw_outwakeup = nmdm_outwakeup, 68bc093719SEd Schouten .tsw_param = nmdm_param, 69bc093719SEd Schouten .tsw_modem = nmdm_modem, 70a95ecdf0SGleb Smirnoff .tsw_close = nmdm_close, 71a95ecdf0SGleb Smirnoff .tsw_free = nmdm_free, 72737a1286SJulian Elischer }; 73737a1286SJulian Elischer 74bc093719SEd Schouten static void nmdm_task_tty(void *, int); 75737a1286SJulian Elischer 76bc093719SEd Schouten struct nmdmsoftc; 77bc093719SEd Schouten 78bc093719SEd Schouten struct nmdmpart { 79bc093719SEd Schouten struct tty *np_tty; 80bc093719SEd Schouten int np_dcd; 81bc093719SEd Schouten struct task np_task; 82bc093719SEd Schouten struct nmdmpart *np_other; 83bc093719SEd Schouten struct nmdmsoftc *np_pair; 84bc093719SEd Schouten struct callout np_callout; 85bc093719SEd Schouten u_long np_quota; 86bc093719SEd Schouten u_long np_accumulator; 87bc093719SEd Schouten int np_rate; 88bc093719SEd Schouten int np_credits; 89017a4322SPoul-Henning Kamp 90017a4322SPoul-Henning Kamp #define QS 8 /* Quota shift */ 91737a1286SJulian Elischer }; 92737a1286SJulian Elischer 93bc093719SEd Schouten struct nmdmsoftc { 94bc093719SEd Schouten struct nmdmpart ns_part1; 95bc093719SEd Schouten struct nmdmpart ns_part2; 96bc093719SEd Schouten struct mtx ns_mtx; 97737a1286SJulian Elischer }; 98737a1286SJulian Elischer 99bc093719SEd Schouten static int nmdm_count = 0; 100bc093719SEd Schouten 101a95ecdf0SGleb Smirnoff static void 102a95ecdf0SGleb Smirnoff nmdm_close(struct tty *tp) 103a95ecdf0SGleb Smirnoff { 104a95ecdf0SGleb Smirnoff struct nmdmpart *np; 105a95ecdf0SGleb Smirnoff struct nmdmpart *onp; 106a95ecdf0SGleb Smirnoff struct tty *otp; 107a95ecdf0SGleb Smirnoff 108a95ecdf0SGleb Smirnoff np = tty_softc(tp); 109a95ecdf0SGleb Smirnoff onp = np->np_other; 110a95ecdf0SGleb Smirnoff otp = onp->np_tty; 111a95ecdf0SGleb Smirnoff 112a95ecdf0SGleb Smirnoff /* If second part is opened, do not destroy ourselves. */ 113a95ecdf0SGleb Smirnoff if (tty_opened(otp)) 114a95ecdf0SGleb Smirnoff return; 115a95ecdf0SGleb Smirnoff 116a95ecdf0SGleb Smirnoff /* Shut down self. */ 117a95ecdf0SGleb Smirnoff tty_rel_gone(tp); 118a95ecdf0SGleb Smirnoff 119a95ecdf0SGleb Smirnoff /* Shut down second part. */ 120a95ecdf0SGleb Smirnoff tty_lock(tp); 121a95ecdf0SGleb Smirnoff onp = np->np_other; 122a95ecdf0SGleb Smirnoff if (onp == NULL) 123a95ecdf0SGleb Smirnoff return; 124a95ecdf0SGleb Smirnoff otp = onp->np_tty; 125a95ecdf0SGleb Smirnoff tty_rel_gone(otp); 126a95ecdf0SGleb Smirnoff tty_lock(tp); 127a95ecdf0SGleb Smirnoff } 128a95ecdf0SGleb Smirnoff 129a95ecdf0SGleb Smirnoff static void 130a95ecdf0SGleb Smirnoff nmdm_free(void *softc) 131a95ecdf0SGleb Smirnoff { 132a95ecdf0SGleb Smirnoff struct nmdmpart *np = (struct nmdmpart *)softc; 133a95ecdf0SGleb Smirnoff struct nmdmsoftc *ns = np->np_pair; 134a95ecdf0SGleb Smirnoff 135a95ecdf0SGleb Smirnoff callout_drain(&np->np_callout); 136a95ecdf0SGleb Smirnoff taskqueue_drain(taskqueue_swi, &np->np_task); 137a95ecdf0SGleb Smirnoff 138a95ecdf0SGleb Smirnoff /* 139a95ecdf0SGleb Smirnoff * The function is called on both parts simultaneously. We serialize 140a95ecdf0SGleb Smirnoff * with help of ns_mtx. The first invocation should return and 141a95ecdf0SGleb Smirnoff * delegate freeing of resources to the second. 142a95ecdf0SGleb Smirnoff */ 143a95ecdf0SGleb Smirnoff mtx_lock(&ns->ns_mtx); 144a95ecdf0SGleb Smirnoff if (np->np_other != NULL) { 145a95ecdf0SGleb Smirnoff np->np_other->np_other = NULL; 146a95ecdf0SGleb Smirnoff mtx_unlock(&ns->ns_mtx); 147a95ecdf0SGleb Smirnoff return; 148a95ecdf0SGleb Smirnoff } 149a95ecdf0SGleb Smirnoff mtx_destroy(&ns->ns_mtx); 150a95ecdf0SGleb Smirnoff free(ns, M_NMDM); 151a95ecdf0SGleb Smirnoff atomic_subtract_int(&nmdm_count, 1); 152a95ecdf0SGleb Smirnoff } 153a95ecdf0SGleb Smirnoff 154a95ecdf0SGleb Smirnoff static void 155a95ecdf0SGleb Smirnoff nmdm_clone(void *arg, struct ucred *cred, char *name, int nameen, 156a95ecdf0SGleb Smirnoff struct cdev **dev) 157bc093719SEd Schouten { 158bc093719SEd Schouten struct nmdmsoftc *ns; 159bc093719SEd Schouten struct tty *tp; 160a95ecdf0SGleb Smirnoff char *end; 161a95ecdf0SGleb Smirnoff int error; 162*b3f7f76bSPeter Grehan char endc; 163bc093719SEd Schouten 164a95ecdf0SGleb Smirnoff if (*dev != NULL) 165a95ecdf0SGleb Smirnoff return; 166a95ecdf0SGleb Smirnoff if (strncmp(name, "nmdm", 4) != 0) 167a95ecdf0SGleb Smirnoff return; 168*b3f7f76bSPeter Grehan if (strlen(name) <= strlen("nmdmX")) 169a95ecdf0SGleb Smirnoff return; 170*b3f7f76bSPeter Grehan 171*b3f7f76bSPeter Grehan /* Device name must be "nmdm%s%c", where %c is 'A' or 'B'. */ 172*b3f7f76bSPeter Grehan end = name + strlen(name) - 1; 173*b3f7f76bSPeter Grehan endc = *end; 174*b3f7f76bSPeter Grehan if (endc != 'A' && endc != 'B') 175a95ecdf0SGleb Smirnoff return; 176bc093719SEd Schouten 177bc093719SEd Schouten ns = malloc(sizeof(*ns), M_NMDM, M_WAITOK | M_ZERO); 178bc093719SEd Schouten mtx_init(&ns->ns_mtx, "nmdm", NULL, MTX_DEF); 179bc093719SEd Schouten 180bc093719SEd Schouten /* Hook the pairs together. */ 181bc093719SEd Schouten ns->ns_part1.np_pair = ns; 182bc093719SEd Schouten ns->ns_part1.np_other = &ns->ns_part2; 183bc093719SEd Schouten TASK_INIT(&ns->ns_part1.np_task, 0, nmdm_task_tty, &ns->ns_part1); 1843e7865bcSJohn Baldwin callout_init_mtx(&ns->ns_part1.np_callout, &ns->ns_mtx, 0); 185bc093719SEd Schouten 186bc093719SEd Schouten ns->ns_part2.np_pair = ns; 187bc093719SEd Schouten ns->ns_part2.np_other = &ns->ns_part1; 188bc093719SEd Schouten TASK_INIT(&ns->ns_part2.np_task, 0, nmdm_task_tty, &ns->ns_part2); 1893e7865bcSJohn Baldwin callout_init_mtx(&ns->ns_part2.np_callout, &ns->ns_mtx, 0); 190bc093719SEd Schouten 191bc093719SEd Schouten /* Create device nodes. */ 192c5e30cc0SEd Schouten tp = ns->ns_part1.np_tty = tty_alloc_mutex(&nmdm_class, &ns->ns_part1, 193bc093719SEd Schouten &ns->ns_mtx); 194*b3f7f76bSPeter Grehan *end = 'A'; 195*b3f7f76bSPeter Grehan error = tty_makedevf(tp, NULL, endc == 'A' ? TTYMK_CLONING : 0, 196*b3f7f76bSPeter Grehan "%s", name); 197a95ecdf0SGleb Smirnoff if (error) { 198*b3f7f76bSPeter Grehan *end = endc; 199a95ecdf0SGleb Smirnoff mtx_destroy(&ns->ns_mtx); 200a95ecdf0SGleb Smirnoff free(ns, M_NMDM); 201a95ecdf0SGleb Smirnoff return; 202a95ecdf0SGleb Smirnoff } 203bc093719SEd Schouten 204c5e30cc0SEd Schouten tp = ns->ns_part2.np_tty = tty_alloc_mutex(&nmdm_class, &ns->ns_part2, 205bc093719SEd Schouten &ns->ns_mtx); 206*b3f7f76bSPeter Grehan *end = 'B'; 207*b3f7f76bSPeter Grehan error = tty_makedevf(tp, NULL, endc == 'B' ? TTYMK_CLONING : 0, 208*b3f7f76bSPeter Grehan "%s", name); 209a95ecdf0SGleb Smirnoff if (error) { 210*b3f7f76bSPeter Grehan *end = endc; 211a95ecdf0SGleb Smirnoff mtx_lock(&ns->ns_mtx); 212a95ecdf0SGleb Smirnoff /* see nmdm_free() */ 213a95ecdf0SGleb Smirnoff ns->ns_part1.np_other = NULL; 214a95ecdf0SGleb Smirnoff atomic_add_int(&nmdm_count, 1); 215a95ecdf0SGleb Smirnoff tty_rel_gone(ns->ns_part1.np_tty); 216a95ecdf0SGleb Smirnoff return; 217bc093719SEd Schouten } 218b0b03348SPoul-Henning Kamp 219*b3f7f76bSPeter Grehan if (endc == 'A') 220bc093719SEd Schouten *dev = ns->ns_part1.np_tty->t_dev; 221b0b03348SPoul-Henning Kamp else 222bc093719SEd Schouten *dev = ns->ns_part2.np_tty->t_dev; 223a95ecdf0SGleb Smirnoff 224*b3f7f76bSPeter Grehan *end = endc; 225a95ecdf0SGleb Smirnoff atomic_add_int(&nmdm_count, 1); 226b0b03348SPoul-Henning Kamp } 227b0b03348SPoul-Henning Kamp 228737a1286SJulian Elischer static void 229017a4322SPoul-Henning Kamp nmdm_timeout(void *arg) 230017a4322SPoul-Henning Kamp { 231bc093719SEd Schouten struct nmdmpart *np = arg; 232017a4322SPoul-Henning Kamp 233bc093719SEd Schouten if (np->np_rate == 0) 234017a4322SPoul-Henning Kamp return; 235017a4322SPoul-Henning Kamp 236017a4322SPoul-Henning Kamp /* 237017a4322SPoul-Henning Kamp * Do a simple Floyd-Steinberg dither here to avoid FP math. 238017a4322SPoul-Henning Kamp * Wipe out unused quota from last tick. 239017a4322SPoul-Henning Kamp */ 240bc093719SEd Schouten np->np_accumulator += np->np_credits; 241bc093719SEd Schouten np->np_quota = np->np_accumulator >> QS; 242bc093719SEd Schouten np->np_accumulator &= ((1 << QS) - 1); 243017a4322SPoul-Henning Kamp 244bc093719SEd Schouten taskqueue_enqueue(taskqueue_swi, &np->np_task); 245bc093719SEd Schouten callout_reset(&np->np_callout, np->np_rate, nmdm_timeout, np); 246017a4322SPoul-Henning Kamp } 247017a4322SPoul-Henning Kamp 248017a4322SPoul-Henning Kamp static void 2499c01a318SPoul-Henning Kamp nmdm_task_tty(void *arg, int pending __unused) 2509c01a318SPoul-Henning Kamp { 2519c01a318SPoul-Henning Kamp struct tty *tp, *otp; 252bc093719SEd Schouten struct nmdmpart *np = arg; 253bc093719SEd Schouten char c; 254737a1286SJulian Elischer 255bc093719SEd Schouten tp = np->np_tty; 256bc093719SEd Schouten tty_lock(tp); 257a95ecdf0SGleb Smirnoff if (tty_gone(tp)) { 258a95ecdf0SGleb Smirnoff tty_unlock(tp); 259a95ecdf0SGleb Smirnoff return; 260a95ecdf0SGleb Smirnoff } 261bc093719SEd Schouten 262bc093719SEd Schouten otp = np->np_other->np_tty; 2639c01a318SPoul-Henning Kamp KASSERT(otp != NULL, ("NULL otp in nmdmstart")); 2649c01a318SPoul-Henning Kamp KASSERT(otp != tp, ("NULL otp == tp nmdmstart")); 265bc093719SEd Schouten if (np->np_other->np_dcd) { 266bc093719SEd Schouten if (!tty_opened(tp)) { 267bc093719SEd Schouten np->np_other->np_dcd = 0; 268bc093719SEd Schouten ttydisc_modem(otp, 0); 2699c01a318SPoul-Henning Kamp } 2709c01a318SPoul-Henning Kamp } else { 271bc093719SEd Schouten if (tty_opened(tp)) { 272bc093719SEd Schouten np->np_other->np_dcd = 1; 273bc093719SEd Schouten ttydisc_modem(otp, 1); 2749c01a318SPoul-Henning Kamp } 2759c01a318SPoul-Henning Kamp } 276017a4322SPoul-Henning Kamp 277a95ecdf0SGleb Smirnoff /* This may happen when we are in detach process. */ 278a95ecdf0SGleb Smirnoff if (tty_gone(otp)) { 279a95ecdf0SGleb Smirnoff tty_unlock(otp); 280a95ecdf0SGleb Smirnoff return; 281a95ecdf0SGleb Smirnoff } 282a95ecdf0SGleb Smirnoff 283bc093719SEd Schouten while (ttydisc_rint_poll(otp) > 0) { 284bc093719SEd Schouten if (np->np_rate && !np->np_quota) 285bc093719SEd Schouten break; 286bc093719SEd Schouten if (ttydisc_getc(tp, &c, 1) != 1) 287bc093719SEd Schouten break; 288bc093719SEd Schouten np->np_quota--; 289bc093719SEd Schouten ttydisc_rint(otp, c, 0); 2909c01a318SPoul-Henning Kamp } 291737a1286SJulian Elischer 292bc093719SEd Schouten ttydisc_rint_done(otp); 293737a1286SJulian Elischer 294bc093719SEd Schouten tty_unlock(tp); 295737a1286SJulian Elischer } 296737a1286SJulian Elischer 297737a1286SJulian Elischer static int 298017a4322SPoul-Henning Kamp bits_per_char(struct termios *t) 299017a4322SPoul-Henning Kamp { 300017a4322SPoul-Henning Kamp int bits; 301017a4322SPoul-Henning Kamp 302017a4322SPoul-Henning Kamp bits = 1; /* start bit */ 303017a4322SPoul-Henning Kamp switch (t->c_cflag & CSIZE) { 304017a4322SPoul-Henning Kamp case CS5: bits += 5; break; 305017a4322SPoul-Henning Kamp case CS6: bits += 6; break; 306017a4322SPoul-Henning Kamp case CS7: bits += 7; break; 307017a4322SPoul-Henning Kamp case CS8: bits += 8; break; 308017a4322SPoul-Henning Kamp } 309017a4322SPoul-Henning Kamp bits++; /* stop bit */ 310017a4322SPoul-Henning Kamp if (t->c_cflag & PARENB) 311017a4322SPoul-Henning Kamp bits++; 312017a4322SPoul-Henning Kamp if (t->c_cflag & CSTOPB) 313017a4322SPoul-Henning Kamp bits++; 314017a4322SPoul-Henning Kamp return (bits); 315017a4322SPoul-Henning Kamp } 316017a4322SPoul-Henning Kamp 317017a4322SPoul-Henning Kamp static int 318bc093719SEd Schouten nmdm_param(struct tty *tp, struct termios *t) 319017a4322SPoul-Henning Kamp { 320bc093719SEd Schouten struct nmdmpart *np = tty_softc(tp); 321017a4322SPoul-Henning Kamp struct tty *tp2; 322017a4322SPoul-Henning Kamp int bpc, rate, speed, i; 323017a4322SPoul-Henning Kamp 324bc093719SEd Schouten tp2 = np->np_other->np_tty; 325017a4322SPoul-Henning Kamp 326bc093719SEd Schouten if (!((t->c_cflag | tp2->t_termios.c_cflag) & CDSR_OFLOW)) { 327bc093719SEd Schouten np->np_rate = 0; 328bc093719SEd Schouten np->np_other->np_rate = 0; 329017a4322SPoul-Henning Kamp return (0); 330017a4322SPoul-Henning Kamp } 331017a4322SPoul-Henning Kamp 332017a4322SPoul-Henning Kamp /* 333017a4322SPoul-Henning Kamp * DSRFLOW one either side enables rate-simulation for both 334017a4322SPoul-Henning Kamp * directions. 335017a4322SPoul-Henning Kamp * NB: the two directions may run at different rates. 336017a4322SPoul-Henning Kamp */ 337017a4322SPoul-Henning Kamp 338017a4322SPoul-Henning Kamp /* Find the larger of the number of bits transmitted */ 339017a4322SPoul-Henning Kamp bpc = imax(bits_per_char(t), bits_per_char(&tp2->t_termios)); 340017a4322SPoul-Henning Kamp 341017a4322SPoul-Henning Kamp for (i = 0; i < 2; i++) { 342017a4322SPoul-Henning Kamp /* Use the slower of our receive and their transmit rate */ 343bc093719SEd Schouten speed = imin(tp2->t_termios.c_ospeed, t->c_ispeed); 344017a4322SPoul-Henning Kamp if (speed == 0) { 345bc093719SEd Schouten np->np_rate = 0; 346bc093719SEd Schouten np->np_other->np_rate = 0; 347017a4322SPoul-Henning Kamp return (0); 348017a4322SPoul-Henning Kamp } 349017a4322SPoul-Henning Kamp 350017a4322SPoul-Henning Kamp speed <<= QS; /* [bit/sec, scaled] */ 351017a4322SPoul-Henning Kamp speed /= bpc; /* [char/sec, scaled] */ 352017a4322SPoul-Henning Kamp rate = (hz << QS) / speed; /* [hz per callout] */ 353017a4322SPoul-Henning Kamp if (rate == 0) 354017a4322SPoul-Henning Kamp rate = 1; 355017a4322SPoul-Henning Kamp 356017a4322SPoul-Henning Kamp speed *= rate; 357017a4322SPoul-Henning Kamp speed /= hz; /* [(char/sec)/tick, scaled */ 358017a4322SPoul-Henning Kamp 359bc093719SEd Schouten np->np_credits = speed; 360bc093719SEd Schouten np->np_rate = rate; 361bc093719SEd Schouten callout_reset(&np->np_callout, rate, nmdm_timeout, np); 362017a4322SPoul-Henning Kamp 363017a4322SPoul-Henning Kamp /* 364017a4322SPoul-Henning Kamp * swap pointers for second pass so the other end gets 365017a4322SPoul-Henning Kamp * updated as well. 366017a4322SPoul-Henning Kamp */ 367bc093719SEd Schouten np = np->np_other; 368017a4322SPoul-Henning Kamp t = &tp2->t_termios; 369017a4322SPoul-Henning Kamp tp2 = tp; 370017a4322SPoul-Henning Kamp } 371bc093719SEd Schouten 372017a4322SPoul-Henning Kamp return (0); 373017a4322SPoul-Henning Kamp } 374017a4322SPoul-Henning Kamp 375017a4322SPoul-Henning Kamp static int 376bc093719SEd Schouten nmdm_modem(struct tty *tp, int sigon, int sigoff) 377c24097e4SPoul-Henning Kamp { 378bc093719SEd Schouten struct nmdmpart *np = tty_softc(tp); 379bc093719SEd Schouten int i = 0; 380c24097e4SPoul-Henning Kamp 381c24097e4SPoul-Henning Kamp if (sigon || sigoff) { 382017a4322SPoul-Henning Kamp if (sigon & SER_DTR) 383bc093719SEd Schouten np->np_other->np_dcd = 1; 384017a4322SPoul-Henning Kamp if (sigoff & SER_DTR) 385bc093719SEd Schouten np->np_other->np_dcd = 0; 386bc093719SEd Schouten 387bc093719SEd Schouten ttydisc_modem(np->np_other->np_tty, np->np_other->np_dcd); 388bc093719SEd Schouten 389c24097e4SPoul-Henning Kamp return (0); 390c24097e4SPoul-Henning Kamp } else { 391bc093719SEd Schouten if (np->np_dcd) 392c24097e4SPoul-Henning Kamp i |= SER_DCD; 393bc093719SEd Schouten if (np->np_other->np_dcd) 394c24097e4SPoul-Henning Kamp i |= SER_DTR; 395bc093719SEd Schouten 396c24097e4SPoul-Henning Kamp return (i); 397c24097e4SPoul-Henning Kamp } 398c24097e4SPoul-Henning Kamp } 399c24097e4SPoul-Henning Kamp 400bc093719SEd Schouten static void 401bc093719SEd Schouten nmdm_inwakeup(struct tty *tp) 402737a1286SJulian Elischer { 403bc093719SEd Schouten struct nmdmpart *np = tty_softc(tp); 404737a1286SJulian Elischer 405bc093719SEd Schouten /* We can receive again, so wake up the other side. */ 406bc093719SEd Schouten taskqueue_enqueue(taskqueue_swi, &np->np_other->np_task); 407737a1286SJulian Elischer } 408737a1286SJulian Elischer 409737a1286SJulian Elischer static void 410bc093719SEd Schouten nmdm_outwakeup(struct tty *tp) 411737a1286SJulian Elischer { 412bc093719SEd Schouten struct nmdmpart *np = tty_softc(tp); 413737a1286SJulian Elischer 414bc093719SEd Schouten /* We can transmit again, so wake up our side. */ 415bc093719SEd Schouten taskqueue_enqueue(taskqueue_swi, &np->np_task); 416737a1286SJulian Elischer } 417737a1286SJulian Elischer 4189308b700SJulian Elischer /* 4199308b700SJulian Elischer * Module handling 4209308b700SJulian Elischer */ 4219308b700SJulian Elischer static int 4229308b700SJulian Elischer nmdm_modevent(module_t mod, int type, void *data) 423737a1286SJulian Elischer { 424b0b03348SPoul-Henning Kamp static eventhandler_tag tag; 4259308b700SJulian Elischer 4269308b700SJulian Elischer switch(type) { 427b0b03348SPoul-Henning Kamp case MOD_LOAD: 428b0b03348SPoul-Henning Kamp tag = EVENTHANDLER_REGISTER(dev_clone, nmdm_clone, 0, 1000); 429b0b03348SPoul-Henning Kamp if (tag == NULL) 430b0b03348SPoul-Henning Kamp return (ENOMEM); 4319308b700SJulian Elischer break; 4329308b700SJulian Elischer 4339308b700SJulian Elischer case MOD_SHUTDOWN: 4349308b700SJulian Elischer break; 435bc093719SEd Schouten 436bc093719SEd Schouten case MOD_UNLOAD: 437bc093719SEd Schouten if (nmdm_count != 0) 438bc093719SEd Schouten return (EBUSY); 439bc093719SEd Schouten EVENTHANDLER_DEREGISTER(dev_clone, tag); 440bc093719SEd Schouten break; 441bc093719SEd Schouten 4429308b700SJulian Elischer default: 443bc093719SEd Schouten return (EOPNOTSUPP); 4449308b700SJulian Elischer } 445bc093719SEd Schouten 446bc093719SEd Schouten return (0); 447737a1286SJulian Elischer } 448737a1286SJulian Elischer 4499308b700SJulian Elischer DEV_MODULE(nmdm, nmdm_modevent, NULL); 450