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; 61*a95ecdf0SGleb Smirnoff static tsw_close_t nmdm_close; 62*a95ecdf0SGleb 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, 70*a95ecdf0SGleb Smirnoff .tsw_close = nmdm_close, 71*a95ecdf0SGleb 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 101*a95ecdf0SGleb Smirnoff static void 102*a95ecdf0SGleb Smirnoff nmdm_close(struct tty *tp) 103*a95ecdf0SGleb Smirnoff { 104*a95ecdf0SGleb Smirnoff struct nmdmpart *np; 105*a95ecdf0SGleb Smirnoff struct nmdmpart *onp; 106*a95ecdf0SGleb Smirnoff struct tty *otp; 107*a95ecdf0SGleb Smirnoff 108*a95ecdf0SGleb Smirnoff np = tty_softc(tp); 109*a95ecdf0SGleb Smirnoff onp = np->np_other; 110*a95ecdf0SGleb Smirnoff otp = onp->np_tty; 111*a95ecdf0SGleb Smirnoff 112*a95ecdf0SGleb Smirnoff /* If second part is opened, do not destroy ourselves. */ 113*a95ecdf0SGleb Smirnoff if (tty_opened(otp)) 114*a95ecdf0SGleb Smirnoff return; 115*a95ecdf0SGleb Smirnoff 116*a95ecdf0SGleb Smirnoff /* Shut down self. */ 117*a95ecdf0SGleb Smirnoff tty_rel_gone(tp); 118*a95ecdf0SGleb Smirnoff 119*a95ecdf0SGleb Smirnoff /* Shut down second part. */ 120*a95ecdf0SGleb Smirnoff tty_lock(tp); 121*a95ecdf0SGleb Smirnoff onp = np->np_other; 122*a95ecdf0SGleb Smirnoff if (onp == NULL) 123*a95ecdf0SGleb Smirnoff return; 124*a95ecdf0SGleb Smirnoff otp = onp->np_tty; 125*a95ecdf0SGleb Smirnoff tty_rel_gone(otp); 126*a95ecdf0SGleb Smirnoff tty_lock(tp); 127*a95ecdf0SGleb Smirnoff } 128*a95ecdf0SGleb Smirnoff 129*a95ecdf0SGleb Smirnoff static void 130*a95ecdf0SGleb Smirnoff nmdm_free(void *softc) 131*a95ecdf0SGleb Smirnoff { 132*a95ecdf0SGleb Smirnoff struct nmdmpart *np = (struct nmdmpart *)softc; 133*a95ecdf0SGleb Smirnoff struct nmdmsoftc *ns = np->np_pair; 134*a95ecdf0SGleb Smirnoff 135*a95ecdf0SGleb Smirnoff callout_drain(&np->np_callout); 136*a95ecdf0SGleb Smirnoff taskqueue_drain(taskqueue_swi, &np->np_task); 137*a95ecdf0SGleb Smirnoff 138*a95ecdf0SGleb Smirnoff /* 139*a95ecdf0SGleb Smirnoff * The function is called on both parts simultaneously. We serialize 140*a95ecdf0SGleb Smirnoff * with help of ns_mtx. The first invocation should return and 141*a95ecdf0SGleb Smirnoff * delegate freeing of resources to the second. 142*a95ecdf0SGleb Smirnoff */ 143*a95ecdf0SGleb Smirnoff mtx_lock(&ns->ns_mtx); 144*a95ecdf0SGleb Smirnoff if (np->np_other != NULL) { 145*a95ecdf0SGleb Smirnoff np->np_other->np_other = NULL; 146*a95ecdf0SGleb Smirnoff mtx_unlock(&ns->ns_mtx); 147*a95ecdf0SGleb Smirnoff return; 148*a95ecdf0SGleb Smirnoff } 149*a95ecdf0SGleb Smirnoff mtx_destroy(&ns->ns_mtx); 150*a95ecdf0SGleb Smirnoff free(ns, M_NMDM); 151*a95ecdf0SGleb Smirnoff atomic_subtract_int(&nmdm_count, 1); 152*a95ecdf0SGleb Smirnoff } 153*a95ecdf0SGleb Smirnoff 154*a95ecdf0SGleb Smirnoff static void 155*a95ecdf0SGleb Smirnoff nmdm_clone(void *arg, struct ucred *cred, char *name, int nameen, 156*a95ecdf0SGleb Smirnoff struct cdev **dev) 157bc093719SEd Schouten { 158bc093719SEd Schouten struct nmdmsoftc *ns; 159bc093719SEd Schouten struct tty *tp; 160*a95ecdf0SGleb Smirnoff unsigned long unit; 161*a95ecdf0SGleb Smirnoff char *end; 162*a95ecdf0SGleb Smirnoff int error; 163bc093719SEd Schouten 164*a95ecdf0SGleb Smirnoff if (*dev != NULL) 165*a95ecdf0SGleb Smirnoff return; 166*a95ecdf0SGleb Smirnoff if (strncmp(name, "nmdm", 4) != 0) 167*a95ecdf0SGleb Smirnoff return; 168*a95ecdf0SGleb Smirnoff 169*a95ecdf0SGleb Smirnoff /* Device name must be "nmdm%lu%c", where %c is 'A' or 'B'. */ 170*a95ecdf0SGleb Smirnoff name += 4; 171*a95ecdf0SGleb Smirnoff unit = strtoul(name, &end, 10); 172*a95ecdf0SGleb Smirnoff if (unit == ULONG_MAX || name == end) 173*a95ecdf0SGleb Smirnoff return; 174*a95ecdf0SGleb Smirnoff if ((end[0] != 'A' && end[0] != 'B') || end[1] != '\0') 175*a95ecdf0SGleb 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*a95ecdf0SGleb Smirnoff error = tty_makedevf(tp, NULL, end[0] == 'A' ? TTYMK_CLONING : 0, 195*a95ecdf0SGleb Smirnoff "nmdm%luA", unit); 196*a95ecdf0SGleb Smirnoff if (error) { 197*a95ecdf0SGleb Smirnoff mtx_destroy(&ns->ns_mtx); 198*a95ecdf0SGleb Smirnoff free(ns, M_NMDM); 199*a95ecdf0SGleb Smirnoff return; 200*a95ecdf0SGleb Smirnoff } 201bc093719SEd Schouten 202c5e30cc0SEd Schouten tp = ns->ns_part2.np_tty = tty_alloc_mutex(&nmdm_class, &ns->ns_part2, 203bc093719SEd Schouten &ns->ns_mtx); 204*a95ecdf0SGleb Smirnoff error = tty_makedevf(tp, NULL, end[0] == 'B' ? TTYMK_CLONING : 0, 205*a95ecdf0SGleb Smirnoff "nmdm%luB", unit); 206*a95ecdf0SGleb Smirnoff if (error) { 207*a95ecdf0SGleb Smirnoff mtx_lock(&ns->ns_mtx); 208*a95ecdf0SGleb Smirnoff /* see nmdm_free() */ 209*a95ecdf0SGleb Smirnoff ns->ns_part1.np_other = NULL; 210*a95ecdf0SGleb Smirnoff atomic_add_int(&nmdm_count, 1); 211*a95ecdf0SGleb Smirnoff tty_rel_gone(ns->ns_part1.np_tty); 212*a95ecdf0SGleb Smirnoff return; 213bc093719SEd Schouten } 214b0b03348SPoul-Henning Kamp 215bc093719SEd Schouten if (end[0] == 'A') 216bc093719SEd Schouten *dev = ns->ns_part1.np_tty->t_dev; 217b0b03348SPoul-Henning Kamp else 218bc093719SEd Schouten *dev = ns->ns_part2.np_tty->t_dev; 219*a95ecdf0SGleb Smirnoff 220*a95ecdf0SGleb Smirnoff atomic_add_int(&nmdm_count, 1); 221b0b03348SPoul-Henning Kamp } 222b0b03348SPoul-Henning Kamp 223737a1286SJulian Elischer static void 224017a4322SPoul-Henning Kamp nmdm_timeout(void *arg) 225017a4322SPoul-Henning Kamp { 226bc093719SEd Schouten struct nmdmpart *np = arg; 227017a4322SPoul-Henning Kamp 228bc093719SEd Schouten if (np->np_rate == 0) 229017a4322SPoul-Henning Kamp return; 230017a4322SPoul-Henning Kamp 231017a4322SPoul-Henning Kamp /* 232017a4322SPoul-Henning Kamp * Do a simple Floyd-Steinberg dither here to avoid FP math. 233017a4322SPoul-Henning Kamp * Wipe out unused quota from last tick. 234017a4322SPoul-Henning Kamp */ 235bc093719SEd Schouten np->np_accumulator += np->np_credits; 236bc093719SEd Schouten np->np_quota = np->np_accumulator >> QS; 237bc093719SEd Schouten np->np_accumulator &= ((1 << QS) - 1); 238017a4322SPoul-Henning Kamp 239bc093719SEd Schouten taskqueue_enqueue(taskqueue_swi, &np->np_task); 240bc093719SEd Schouten callout_reset(&np->np_callout, np->np_rate, nmdm_timeout, np); 241017a4322SPoul-Henning Kamp } 242017a4322SPoul-Henning Kamp 243017a4322SPoul-Henning Kamp static void 2449c01a318SPoul-Henning Kamp nmdm_task_tty(void *arg, int pending __unused) 2459c01a318SPoul-Henning Kamp { 2469c01a318SPoul-Henning Kamp struct tty *tp, *otp; 247bc093719SEd Schouten struct nmdmpart *np = arg; 248bc093719SEd Schouten char c; 249737a1286SJulian Elischer 250bc093719SEd Schouten tp = np->np_tty; 251bc093719SEd Schouten tty_lock(tp); 252*a95ecdf0SGleb Smirnoff if (tty_gone(tp)) { 253*a95ecdf0SGleb Smirnoff tty_unlock(tp); 254*a95ecdf0SGleb Smirnoff return; 255*a95ecdf0SGleb Smirnoff } 256bc093719SEd Schouten 257bc093719SEd Schouten otp = np->np_other->np_tty; 2589c01a318SPoul-Henning Kamp KASSERT(otp != NULL, ("NULL otp in nmdmstart")); 2599c01a318SPoul-Henning Kamp KASSERT(otp != tp, ("NULL otp == tp nmdmstart")); 260bc093719SEd Schouten if (np->np_other->np_dcd) { 261bc093719SEd Schouten if (!tty_opened(tp)) { 262bc093719SEd Schouten np->np_other->np_dcd = 0; 263bc093719SEd Schouten ttydisc_modem(otp, 0); 2649c01a318SPoul-Henning Kamp } 2659c01a318SPoul-Henning Kamp } else { 266bc093719SEd Schouten if (tty_opened(tp)) { 267bc093719SEd Schouten np->np_other->np_dcd = 1; 268bc093719SEd Schouten ttydisc_modem(otp, 1); 2699c01a318SPoul-Henning Kamp } 2709c01a318SPoul-Henning Kamp } 271017a4322SPoul-Henning Kamp 272*a95ecdf0SGleb Smirnoff /* This may happen when we are in detach process. */ 273*a95ecdf0SGleb Smirnoff if (tty_gone(otp)) { 274*a95ecdf0SGleb Smirnoff tty_unlock(otp); 275*a95ecdf0SGleb Smirnoff return; 276*a95ecdf0SGleb Smirnoff } 277*a95ecdf0SGleb Smirnoff 278bc093719SEd Schouten while (ttydisc_rint_poll(otp) > 0) { 279bc093719SEd Schouten if (np->np_rate && !np->np_quota) 280bc093719SEd Schouten break; 281bc093719SEd Schouten if (ttydisc_getc(tp, &c, 1) != 1) 282bc093719SEd Schouten break; 283bc093719SEd Schouten np->np_quota--; 284bc093719SEd Schouten ttydisc_rint(otp, c, 0); 2859c01a318SPoul-Henning Kamp } 286737a1286SJulian Elischer 287bc093719SEd Schouten ttydisc_rint_done(otp); 288737a1286SJulian Elischer 289bc093719SEd Schouten tty_unlock(tp); 290737a1286SJulian Elischer } 291737a1286SJulian Elischer 292737a1286SJulian Elischer static int 293017a4322SPoul-Henning Kamp bits_per_char(struct termios *t) 294017a4322SPoul-Henning Kamp { 295017a4322SPoul-Henning Kamp int bits; 296017a4322SPoul-Henning Kamp 297017a4322SPoul-Henning Kamp bits = 1; /* start bit */ 298017a4322SPoul-Henning Kamp switch (t->c_cflag & CSIZE) { 299017a4322SPoul-Henning Kamp case CS5: bits += 5; break; 300017a4322SPoul-Henning Kamp case CS6: bits += 6; break; 301017a4322SPoul-Henning Kamp case CS7: bits += 7; break; 302017a4322SPoul-Henning Kamp case CS8: bits += 8; break; 303017a4322SPoul-Henning Kamp } 304017a4322SPoul-Henning Kamp bits++; /* stop bit */ 305017a4322SPoul-Henning Kamp if (t->c_cflag & PARENB) 306017a4322SPoul-Henning Kamp bits++; 307017a4322SPoul-Henning Kamp if (t->c_cflag & CSTOPB) 308017a4322SPoul-Henning Kamp bits++; 309017a4322SPoul-Henning Kamp return (bits); 310017a4322SPoul-Henning Kamp } 311017a4322SPoul-Henning Kamp 312017a4322SPoul-Henning Kamp static int 313bc093719SEd Schouten nmdm_param(struct tty *tp, struct termios *t) 314017a4322SPoul-Henning Kamp { 315bc093719SEd Schouten struct nmdmpart *np = tty_softc(tp); 316017a4322SPoul-Henning Kamp struct tty *tp2; 317017a4322SPoul-Henning Kamp int bpc, rate, speed, i; 318017a4322SPoul-Henning Kamp 319bc093719SEd Schouten tp2 = np->np_other->np_tty; 320017a4322SPoul-Henning Kamp 321bc093719SEd Schouten if (!((t->c_cflag | tp2->t_termios.c_cflag) & CDSR_OFLOW)) { 322bc093719SEd Schouten np->np_rate = 0; 323bc093719SEd Schouten np->np_other->np_rate = 0; 324017a4322SPoul-Henning Kamp return (0); 325017a4322SPoul-Henning Kamp } 326017a4322SPoul-Henning Kamp 327017a4322SPoul-Henning Kamp /* 328017a4322SPoul-Henning Kamp * DSRFLOW one either side enables rate-simulation for both 329017a4322SPoul-Henning Kamp * directions. 330017a4322SPoul-Henning Kamp * NB: the two directions may run at different rates. 331017a4322SPoul-Henning Kamp */ 332017a4322SPoul-Henning Kamp 333017a4322SPoul-Henning Kamp /* Find the larger of the number of bits transmitted */ 334017a4322SPoul-Henning Kamp bpc = imax(bits_per_char(t), bits_per_char(&tp2->t_termios)); 335017a4322SPoul-Henning Kamp 336017a4322SPoul-Henning Kamp for (i = 0; i < 2; i++) { 337017a4322SPoul-Henning Kamp /* Use the slower of our receive and their transmit rate */ 338bc093719SEd Schouten speed = imin(tp2->t_termios.c_ospeed, t->c_ispeed); 339017a4322SPoul-Henning Kamp if (speed == 0) { 340bc093719SEd Schouten np->np_rate = 0; 341bc093719SEd Schouten np->np_other->np_rate = 0; 342017a4322SPoul-Henning Kamp return (0); 343017a4322SPoul-Henning Kamp } 344017a4322SPoul-Henning Kamp 345017a4322SPoul-Henning Kamp speed <<= QS; /* [bit/sec, scaled] */ 346017a4322SPoul-Henning Kamp speed /= bpc; /* [char/sec, scaled] */ 347017a4322SPoul-Henning Kamp rate = (hz << QS) / speed; /* [hz per callout] */ 348017a4322SPoul-Henning Kamp if (rate == 0) 349017a4322SPoul-Henning Kamp rate = 1; 350017a4322SPoul-Henning Kamp 351017a4322SPoul-Henning Kamp speed *= rate; 352017a4322SPoul-Henning Kamp speed /= hz; /* [(char/sec)/tick, scaled */ 353017a4322SPoul-Henning Kamp 354bc093719SEd Schouten np->np_credits = speed; 355bc093719SEd Schouten np->np_rate = rate; 356bc093719SEd Schouten callout_reset(&np->np_callout, rate, nmdm_timeout, np); 357017a4322SPoul-Henning Kamp 358017a4322SPoul-Henning Kamp /* 359017a4322SPoul-Henning Kamp * swap pointers for second pass so the other end gets 360017a4322SPoul-Henning Kamp * updated as well. 361017a4322SPoul-Henning Kamp */ 362bc093719SEd Schouten np = np->np_other; 363017a4322SPoul-Henning Kamp t = &tp2->t_termios; 364017a4322SPoul-Henning Kamp tp2 = tp; 365017a4322SPoul-Henning Kamp } 366bc093719SEd Schouten 367017a4322SPoul-Henning Kamp return (0); 368017a4322SPoul-Henning Kamp } 369017a4322SPoul-Henning Kamp 370017a4322SPoul-Henning Kamp static int 371bc093719SEd Schouten nmdm_modem(struct tty *tp, int sigon, int sigoff) 372c24097e4SPoul-Henning Kamp { 373bc093719SEd Schouten struct nmdmpart *np = tty_softc(tp); 374bc093719SEd Schouten int i = 0; 375c24097e4SPoul-Henning Kamp 376c24097e4SPoul-Henning Kamp if (sigon || sigoff) { 377017a4322SPoul-Henning Kamp if (sigon & SER_DTR) 378bc093719SEd Schouten np->np_other->np_dcd = 1; 379017a4322SPoul-Henning Kamp if (sigoff & SER_DTR) 380bc093719SEd Schouten np->np_other->np_dcd = 0; 381bc093719SEd Schouten 382bc093719SEd Schouten ttydisc_modem(np->np_other->np_tty, np->np_other->np_dcd); 383bc093719SEd Schouten 384c24097e4SPoul-Henning Kamp return (0); 385c24097e4SPoul-Henning Kamp } else { 386bc093719SEd Schouten if (np->np_dcd) 387c24097e4SPoul-Henning Kamp i |= SER_DCD; 388bc093719SEd Schouten if (np->np_other->np_dcd) 389c24097e4SPoul-Henning Kamp i |= SER_DTR; 390bc093719SEd Schouten 391c24097e4SPoul-Henning Kamp return (i); 392c24097e4SPoul-Henning Kamp } 393c24097e4SPoul-Henning Kamp } 394c24097e4SPoul-Henning Kamp 395bc093719SEd Schouten static void 396bc093719SEd Schouten nmdm_inwakeup(struct tty *tp) 397737a1286SJulian Elischer { 398bc093719SEd Schouten struct nmdmpart *np = tty_softc(tp); 399737a1286SJulian Elischer 400bc093719SEd Schouten /* We can receive again, so wake up the other side. */ 401bc093719SEd Schouten taskqueue_enqueue(taskqueue_swi, &np->np_other->np_task); 402737a1286SJulian Elischer } 403737a1286SJulian Elischer 404737a1286SJulian Elischer static void 405bc093719SEd Schouten nmdm_outwakeup(struct tty *tp) 406737a1286SJulian Elischer { 407bc093719SEd Schouten struct nmdmpart *np = tty_softc(tp); 408737a1286SJulian Elischer 409bc093719SEd Schouten /* We can transmit again, so wake up our side. */ 410bc093719SEd Schouten taskqueue_enqueue(taskqueue_swi, &np->np_task); 411737a1286SJulian Elischer } 412737a1286SJulian Elischer 4139308b700SJulian Elischer /* 4149308b700SJulian Elischer * Module handling 4159308b700SJulian Elischer */ 4169308b700SJulian Elischer static int 4179308b700SJulian Elischer nmdm_modevent(module_t mod, int type, void *data) 418737a1286SJulian Elischer { 419b0b03348SPoul-Henning Kamp static eventhandler_tag tag; 4209308b700SJulian Elischer 4219308b700SJulian Elischer switch(type) { 422b0b03348SPoul-Henning Kamp case MOD_LOAD: 423b0b03348SPoul-Henning Kamp tag = EVENTHANDLER_REGISTER(dev_clone, nmdm_clone, 0, 1000); 424b0b03348SPoul-Henning Kamp if (tag == NULL) 425b0b03348SPoul-Henning Kamp return (ENOMEM); 4269308b700SJulian Elischer break; 4279308b700SJulian Elischer 4289308b700SJulian Elischer case MOD_SHUTDOWN: 4299308b700SJulian Elischer break; 430bc093719SEd Schouten 431bc093719SEd Schouten case MOD_UNLOAD: 432bc093719SEd Schouten if (nmdm_count != 0) 433bc093719SEd Schouten return (EBUSY); 434bc093719SEd Schouten EVENTHANDLER_DEREGISTER(dev_clone, tag); 435bc093719SEd Schouten break; 436bc093719SEd Schouten 4379308b700SJulian Elischer default: 438bc093719SEd Schouten return (EOPNOTSUPP); 4399308b700SJulian Elischer } 440bc093719SEd Schouten 441bc093719SEd Schouten return (0); 442737a1286SJulian Elischer } 443737a1286SJulian Elischer 4449308b700SJulian Elischer DEV_MODULE(nmdm, nmdm_modevent, NULL); 445