1098ca2bdSWarner Losh /*- 27282444bSPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 37282444bSPedro F. Giffuni * 4737a1286SJulian Elischer * Copyright (c) 1982, 1986, 1989, 1993 5737a1286SJulian Elischer * The Regents of the University of California. All rights reserved. 6737a1286SJulian Elischer * 7737a1286SJulian Elischer * Redistribution and use in source and binary forms, with or without 8737a1286SJulian Elischer * modification, are permitted provided that the following conditions 9737a1286SJulian Elischer * are met: 10737a1286SJulian Elischer * 1. Redistributions of source code must retain the above copyright 11737a1286SJulian Elischer * notice, this list of conditions and the following disclaimer. 12737a1286SJulian Elischer * 2. Redistributions in binary form must reproduce the above copyright 13737a1286SJulian Elischer * notice, this list of conditions and the following disclaimer in the 14737a1286SJulian Elischer * documentation and/or other materials provided with the distribution. 15fbbd9655SWarner Losh * 3. Neither the name of the University nor the names of its contributors 16737a1286SJulian Elischer * may be used to endorse or promote products derived from this software 17737a1286SJulian Elischer * without specific prior written permission. 18737a1286SJulian Elischer * 19737a1286SJulian Elischer * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20737a1286SJulian Elischer * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21737a1286SJulian Elischer * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22737a1286SJulian Elischer * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23737a1286SJulian Elischer * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24737a1286SJulian Elischer * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25737a1286SJulian Elischer * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26737a1286SJulian Elischer * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27737a1286SJulian Elischer * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28737a1286SJulian Elischer * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29737a1286SJulian Elischer * SUCH DAMAGE. 30737a1286SJulian Elischer * 31737a1286SJulian Elischer */ 32737a1286SJulian Elischer 33aad970f1SDavid E. O'Brien #include <sys/cdefs.h> 34aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$"); 35aad970f1SDavid E. O'Brien 36737a1286SJulian Elischer /* 379308b700SJulian Elischer * Pseudo-nulmodem driver 389308b700SJulian Elischer * Mighty handy for use with serial console in Vmware 39737a1286SJulian Elischer */ 409308b700SJulian Elischer 41737a1286SJulian Elischer #include <sys/param.h> 42737a1286SJulian Elischer #include <sys/systm.h> 43acd3428bSRobert Watson #include <sys/priv.h> 44737a1286SJulian Elischer #include <sys/proc.h> 45737a1286SJulian Elischer #include <sys/tty.h> 46737a1286SJulian Elischer #include <sys/conf.h> 47e2e050c8SConrad Meyer #include <sys/eventhandler.h> 48737a1286SJulian Elischer #include <sys/fcntl.h> 49737a1286SJulian Elischer #include <sys/poll.h> 50737a1286SJulian Elischer #include <sys/kernel.h> 51bc093719SEd Schouten #include <sys/limits.h> 52fe12f24bSPoul-Henning Kamp #include <sys/module.h> 53c24097e4SPoul-Henning Kamp #include <sys/serial.h> 54737a1286SJulian Elischer #include <sys/signalvar.h> 55737a1286SJulian Elischer #include <sys/malloc.h> 569c01a318SPoul-Henning Kamp #include <sys/taskqueue.h> 57737a1286SJulian Elischer 58d745c852SEd Schouten static MALLOC_DEFINE(M_NMDM, "nullmodem", "nullmodem data structures"); 59737a1286SJulian Elischer 60bc093719SEd Schouten static tsw_inwakeup_t nmdm_outwakeup; 61bc093719SEd Schouten static tsw_outwakeup_t nmdm_inwakeup; 62bc093719SEd Schouten static tsw_param_t nmdm_param; 63bc093719SEd Schouten static tsw_modem_t nmdm_modem; 64a95ecdf0SGleb Smirnoff static tsw_close_t nmdm_close; 65a95ecdf0SGleb Smirnoff static tsw_free_t nmdm_free; 66017a4322SPoul-Henning Kamp 67bc093719SEd Schouten static struct ttydevsw nmdm_class = { 68bc093719SEd Schouten .tsw_flags = TF_NOPREFIX, 69bc093719SEd Schouten .tsw_inwakeup = nmdm_inwakeup, 70bc093719SEd Schouten .tsw_outwakeup = nmdm_outwakeup, 71bc093719SEd Schouten .tsw_param = nmdm_param, 72bc093719SEd Schouten .tsw_modem = nmdm_modem, 73a95ecdf0SGleb Smirnoff .tsw_close = nmdm_close, 74a95ecdf0SGleb Smirnoff .tsw_free = nmdm_free, 75737a1286SJulian Elischer }; 76737a1286SJulian Elischer 77bc093719SEd Schouten static void nmdm_task_tty(void *, int); 78737a1286SJulian Elischer 79bc093719SEd Schouten struct nmdmsoftc; 80bc093719SEd Schouten 81bc093719SEd Schouten struct nmdmpart { 82bc093719SEd Schouten struct tty *np_tty; 83bc093719SEd Schouten int np_dcd; 84bc093719SEd Schouten struct task np_task; 85bc093719SEd Schouten struct nmdmpart *np_other; 86bc093719SEd Schouten struct nmdmsoftc *np_pair; 87bc093719SEd Schouten struct callout np_callout; 88bc093719SEd Schouten u_long np_quota; 89bc093719SEd Schouten u_long np_accumulator; 90bc093719SEd Schouten int np_rate; 91bc093719SEd Schouten int np_credits; 92017a4322SPoul-Henning Kamp 93017a4322SPoul-Henning Kamp #define QS 8 /* Quota shift */ 94737a1286SJulian Elischer }; 95737a1286SJulian Elischer 96bc093719SEd Schouten struct nmdmsoftc { 97bc093719SEd Schouten struct nmdmpart ns_part1; 98bc093719SEd Schouten struct nmdmpart ns_part2; 99bc093719SEd Schouten struct mtx ns_mtx; 100737a1286SJulian Elischer }; 101737a1286SJulian Elischer 102bc093719SEd Schouten static int nmdm_count = 0; 103bc093719SEd Schouten 104a95ecdf0SGleb Smirnoff static void 105a95ecdf0SGleb Smirnoff nmdm_close(struct tty *tp) 106a95ecdf0SGleb Smirnoff { 107a95ecdf0SGleb Smirnoff struct nmdmpart *np; 108a95ecdf0SGleb Smirnoff struct nmdmpart *onp; 109a95ecdf0SGleb Smirnoff struct tty *otp; 110a95ecdf0SGleb Smirnoff 111a95ecdf0SGleb Smirnoff np = tty_softc(tp); 112a95ecdf0SGleb Smirnoff onp = np->np_other; 113a95ecdf0SGleb Smirnoff otp = onp->np_tty; 114a95ecdf0SGleb Smirnoff 115a95ecdf0SGleb Smirnoff /* If second part is opened, do not destroy ourselves. */ 116a95ecdf0SGleb Smirnoff if (tty_opened(otp)) 117a95ecdf0SGleb Smirnoff return; 118a95ecdf0SGleb Smirnoff 119a95ecdf0SGleb Smirnoff /* Shut down self. */ 120a95ecdf0SGleb Smirnoff tty_rel_gone(tp); 121a95ecdf0SGleb Smirnoff 122a95ecdf0SGleb Smirnoff /* Shut down second part. */ 123a95ecdf0SGleb Smirnoff tty_lock(tp); 124a95ecdf0SGleb Smirnoff onp = np->np_other; 125a95ecdf0SGleb Smirnoff if (onp == NULL) 126a95ecdf0SGleb Smirnoff return; 127a95ecdf0SGleb Smirnoff otp = onp->np_tty; 128a95ecdf0SGleb Smirnoff tty_rel_gone(otp); 129a95ecdf0SGleb Smirnoff tty_lock(tp); 130a95ecdf0SGleb Smirnoff } 131a95ecdf0SGleb Smirnoff 132a95ecdf0SGleb Smirnoff static void 133a95ecdf0SGleb Smirnoff nmdm_free(void *softc) 134a95ecdf0SGleb Smirnoff { 135a95ecdf0SGleb Smirnoff struct nmdmpart *np = (struct nmdmpart *)softc; 136a95ecdf0SGleb Smirnoff struct nmdmsoftc *ns = np->np_pair; 137a95ecdf0SGleb Smirnoff 138a95ecdf0SGleb Smirnoff callout_drain(&np->np_callout); 139a95ecdf0SGleb Smirnoff taskqueue_drain(taskqueue_swi, &np->np_task); 140a95ecdf0SGleb Smirnoff 141a95ecdf0SGleb Smirnoff /* 142a95ecdf0SGleb Smirnoff * The function is called on both parts simultaneously. We serialize 143a95ecdf0SGleb Smirnoff * with help of ns_mtx. The first invocation should return and 144a95ecdf0SGleb Smirnoff * delegate freeing of resources to the second. 145a95ecdf0SGleb Smirnoff */ 146a95ecdf0SGleb Smirnoff mtx_lock(&ns->ns_mtx); 147a95ecdf0SGleb Smirnoff if (np->np_other != NULL) { 148a95ecdf0SGleb Smirnoff np->np_other->np_other = NULL; 149a95ecdf0SGleb Smirnoff mtx_unlock(&ns->ns_mtx); 150a95ecdf0SGleb Smirnoff return; 151a95ecdf0SGleb Smirnoff } 152a95ecdf0SGleb Smirnoff mtx_destroy(&ns->ns_mtx); 153a95ecdf0SGleb Smirnoff free(ns, M_NMDM); 154a95ecdf0SGleb Smirnoff atomic_subtract_int(&nmdm_count, 1); 155a95ecdf0SGleb Smirnoff } 156a95ecdf0SGleb Smirnoff 157a95ecdf0SGleb Smirnoff static void 158a95ecdf0SGleb Smirnoff nmdm_clone(void *arg, struct ucred *cred, char *name, int nameen, 159a95ecdf0SGleb Smirnoff struct cdev **dev) 160bc093719SEd Schouten { 161bc093719SEd Schouten struct nmdmsoftc *ns; 162bc093719SEd Schouten struct tty *tp; 163a95ecdf0SGleb Smirnoff char *end; 164a95ecdf0SGleb Smirnoff int error; 165b3f7f76bSPeter Grehan char endc; 166bc093719SEd Schouten 167a95ecdf0SGleb Smirnoff if (*dev != NULL) 168a95ecdf0SGleb Smirnoff return; 169a95ecdf0SGleb Smirnoff if (strncmp(name, "nmdm", 4) != 0) 170a95ecdf0SGleb Smirnoff return; 171b3f7f76bSPeter Grehan if (strlen(name) <= strlen("nmdmX")) 172a95ecdf0SGleb Smirnoff return; 173b3f7f76bSPeter Grehan 174b3f7f76bSPeter Grehan /* Device name must be "nmdm%s%c", where %c is 'A' or 'B'. */ 175b3f7f76bSPeter Grehan end = name + strlen(name) - 1; 176b3f7f76bSPeter Grehan endc = *end; 177b3f7f76bSPeter Grehan if (endc != 'A' && endc != 'B') 178a95ecdf0SGleb Smirnoff return; 179bc093719SEd Schouten 180bc093719SEd Schouten ns = malloc(sizeof(*ns), M_NMDM, M_WAITOK | M_ZERO); 181bc093719SEd Schouten mtx_init(&ns->ns_mtx, "nmdm", NULL, MTX_DEF); 182bc093719SEd Schouten 183bc093719SEd Schouten /* Hook the pairs together. */ 184bc093719SEd Schouten ns->ns_part1.np_pair = ns; 185bc093719SEd Schouten ns->ns_part1.np_other = &ns->ns_part2; 186bc093719SEd Schouten TASK_INIT(&ns->ns_part1.np_task, 0, nmdm_task_tty, &ns->ns_part1); 1873e7865bcSJohn Baldwin callout_init_mtx(&ns->ns_part1.np_callout, &ns->ns_mtx, 0); 188bc093719SEd Schouten 189bc093719SEd Schouten ns->ns_part2.np_pair = ns; 190bc093719SEd Schouten ns->ns_part2.np_other = &ns->ns_part1; 191bc093719SEd Schouten TASK_INIT(&ns->ns_part2.np_task, 0, nmdm_task_tty, &ns->ns_part2); 1923e7865bcSJohn Baldwin callout_init_mtx(&ns->ns_part2.np_callout, &ns->ns_mtx, 0); 193bc093719SEd Schouten 194bc093719SEd Schouten /* Create device nodes. */ 195c5e30cc0SEd Schouten tp = ns->ns_part1.np_tty = tty_alloc_mutex(&nmdm_class, &ns->ns_part1, 196bc093719SEd Schouten &ns->ns_mtx); 197b3f7f76bSPeter Grehan *end = 'A'; 198*ea70ab23SPoul-Henning Kamp error = tty_makedevf(tp, cred, endc == 'A' ? TTYMK_CLONING : 0, 199b3f7f76bSPeter Grehan "%s", name); 200a95ecdf0SGleb Smirnoff if (error) { 201b3f7f76bSPeter Grehan *end = endc; 202a95ecdf0SGleb Smirnoff mtx_destroy(&ns->ns_mtx); 203a95ecdf0SGleb Smirnoff free(ns, M_NMDM); 204a95ecdf0SGleb Smirnoff return; 205a95ecdf0SGleb Smirnoff } 206bc093719SEd Schouten 207c5e30cc0SEd Schouten tp = ns->ns_part2.np_tty = tty_alloc_mutex(&nmdm_class, &ns->ns_part2, 208bc093719SEd Schouten &ns->ns_mtx); 209b3f7f76bSPeter Grehan *end = 'B'; 210*ea70ab23SPoul-Henning Kamp error = tty_makedevf(tp, cred, endc == 'B' ? TTYMK_CLONING : 0, 211b3f7f76bSPeter Grehan "%s", name); 212a95ecdf0SGleb Smirnoff if (error) { 213b3f7f76bSPeter Grehan *end = endc; 214a95ecdf0SGleb Smirnoff mtx_lock(&ns->ns_mtx); 215a95ecdf0SGleb Smirnoff /* see nmdm_free() */ 216a95ecdf0SGleb Smirnoff ns->ns_part1.np_other = NULL; 217a95ecdf0SGleb Smirnoff atomic_add_int(&nmdm_count, 1); 218a95ecdf0SGleb Smirnoff tty_rel_gone(ns->ns_part1.np_tty); 219a95ecdf0SGleb Smirnoff return; 220bc093719SEd Schouten } 221b0b03348SPoul-Henning Kamp 222b3f7f76bSPeter Grehan if (endc == 'A') 223bc093719SEd Schouten *dev = ns->ns_part1.np_tty->t_dev; 224b0b03348SPoul-Henning Kamp else 225bc093719SEd Schouten *dev = ns->ns_part2.np_tty->t_dev; 226a95ecdf0SGleb Smirnoff 227b3f7f76bSPeter Grehan *end = endc; 228a95ecdf0SGleb Smirnoff atomic_add_int(&nmdm_count, 1); 229b0b03348SPoul-Henning Kamp } 230b0b03348SPoul-Henning Kamp 231737a1286SJulian Elischer static void 232017a4322SPoul-Henning Kamp nmdm_timeout(void *arg) 233017a4322SPoul-Henning Kamp { 234bc093719SEd Schouten struct nmdmpart *np = arg; 235017a4322SPoul-Henning Kamp 236bc093719SEd Schouten if (np->np_rate == 0) 237017a4322SPoul-Henning Kamp return; 238017a4322SPoul-Henning Kamp 239017a4322SPoul-Henning Kamp /* 240017a4322SPoul-Henning Kamp * Do a simple Floyd-Steinberg dither here to avoid FP math. 241017a4322SPoul-Henning Kamp * Wipe out unused quota from last tick. 242017a4322SPoul-Henning Kamp */ 243bc093719SEd Schouten np->np_accumulator += np->np_credits; 244bc093719SEd Schouten np->np_quota = np->np_accumulator >> QS; 245bc093719SEd Schouten np->np_accumulator &= ((1 << QS) - 1); 246017a4322SPoul-Henning Kamp 247bc093719SEd Schouten taskqueue_enqueue(taskqueue_swi, &np->np_task); 248bc093719SEd Schouten callout_reset(&np->np_callout, np->np_rate, nmdm_timeout, np); 249017a4322SPoul-Henning Kamp } 250017a4322SPoul-Henning Kamp 251017a4322SPoul-Henning Kamp static void 2529c01a318SPoul-Henning Kamp nmdm_task_tty(void *arg, int pending __unused) 2539c01a318SPoul-Henning Kamp { 2549c01a318SPoul-Henning Kamp struct tty *tp, *otp; 255bc093719SEd Schouten struct nmdmpart *np = arg; 256bc093719SEd Schouten char c; 257737a1286SJulian Elischer 258bc093719SEd Schouten tp = np->np_tty; 259bc093719SEd Schouten tty_lock(tp); 260a95ecdf0SGleb Smirnoff if (tty_gone(tp)) { 261a95ecdf0SGleb Smirnoff tty_unlock(tp); 262a95ecdf0SGleb Smirnoff return; 263a95ecdf0SGleb Smirnoff } 264bc093719SEd Schouten 265bc093719SEd Schouten otp = np->np_other->np_tty; 2669c01a318SPoul-Henning Kamp KASSERT(otp != NULL, ("NULL otp in nmdmstart")); 2679c01a318SPoul-Henning Kamp KASSERT(otp != tp, ("NULL otp == tp nmdmstart")); 268bc093719SEd Schouten if (np->np_other->np_dcd) { 269bc093719SEd Schouten if (!tty_opened(tp)) { 270bc093719SEd Schouten np->np_other->np_dcd = 0; 271bc093719SEd Schouten ttydisc_modem(otp, 0); 2729c01a318SPoul-Henning Kamp } 2739c01a318SPoul-Henning Kamp } else { 274bc093719SEd Schouten if (tty_opened(tp)) { 275bc093719SEd Schouten np->np_other->np_dcd = 1; 276bc093719SEd Schouten ttydisc_modem(otp, 1); 2779c01a318SPoul-Henning Kamp } 2789c01a318SPoul-Henning Kamp } 279017a4322SPoul-Henning Kamp 280a95ecdf0SGleb Smirnoff /* This may happen when we are in detach process. */ 281a95ecdf0SGleb Smirnoff if (tty_gone(otp)) { 282a95ecdf0SGleb Smirnoff tty_unlock(otp); 283a95ecdf0SGleb Smirnoff return; 284a95ecdf0SGleb Smirnoff } 285a95ecdf0SGleb Smirnoff 286bc093719SEd Schouten while (ttydisc_rint_poll(otp) > 0) { 287bc093719SEd Schouten if (np->np_rate && !np->np_quota) 288bc093719SEd Schouten break; 289bc093719SEd Schouten if (ttydisc_getc(tp, &c, 1) != 1) 290bc093719SEd Schouten break; 291bc093719SEd Schouten np->np_quota--; 292bc093719SEd Schouten ttydisc_rint(otp, c, 0); 2939c01a318SPoul-Henning Kamp } 294737a1286SJulian Elischer 295bc093719SEd Schouten ttydisc_rint_done(otp); 296737a1286SJulian Elischer 297bc093719SEd Schouten tty_unlock(tp); 298737a1286SJulian Elischer } 299737a1286SJulian Elischer 300737a1286SJulian Elischer static int 301017a4322SPoul-Henning Kamp bits_per_char(struct termios *t) 302017a4322SPoul-Henning Kamp { 303017a4322SPoul-Henning Kamp int bits; 304017a4322SPoul-Henning Kamp 305017a4322SPoul-Henning Kamp bits = 1; /* start bit */ 306017a4322SPoul-Henning Kamp switch (t->c_cflag & CSIZE) { 307017a4322SPoul-Henning Kamp case CS5: bits += 5; break; 308017a4322SPoul-Henning Kamp case CS6: bits += 6; break; 309017a4322SPoul-Henning Kamp case CS7: bits += 7; break; 310017a4322SPoul-Henning Kamp case CS8: bits += 8; break; 311017a4322SPoul-Henning Kamp } 312017a4322SPoul-Henning Kamp bits++; /* stop bit */ 313017a4322SPoul-Henning Kamp if (t->c_cflag & PARENB) 314017a4322SPoul-Henning Kamp bits++; 315017a4322SPoul-Henning Kamp if (t->c_cflag & CSTOPB) 316017a4322SPoul-Henning Kamp bits++; 317017a4322SPoul-Henning Kamp return (bits); 318017a4322SPoul-Henning Kamp } 319017a4322SPoul-Henning Kamp 320017a4322SPoul-Henning Kamp static int 321bc093719SEd Schouten nmdm_param(struct tty *tp, struct termios *t) 322017a4322SPoul-Henning Kamp { 323bc093719SEd Schouten struct nmdmpart *np = tty_softc(tp); 324017a4322SPoul-Henning Kamp struct tty *tp2; 325017a4322SPoul-Henning Kamp int bpc, rate, speed, i; 326017a4322SPoul-Henning Kamp 327bc093719SEd Schouten tp2 = np->np_other->np_tty; 328017a4322SPoul-Henning Kamp 329bc093719SEd Schouten if (!((t->c_cflag | tp2->t_termios.c_cflag) & CDSR_OFLOW)) { 330bc093719SEd Schouten np->np_rate = 0; 331bc093719SEd Schouten np->np_other->np_rate = 0; 332017a4322SPoul-Henning Kamp return (0); 333017a4322SPoul-Henning Kamp } 334017a4322SPoul-Henning Kamp 335017a4322SPoul-Henning Kamp /* 336017a4322SPoul-Henning Kamp * DSRFLOW one either side enables rate-simulation for both 337017a4322SPoul-Henning Kamp * directions. 338017a4322SPoul-Henning Kamp * NB: the two directions may run at different rates. 339017a4322SPoul-Henning Kamp */ 340017a4322SPoul-Henning Kamp 341017a4322SPoul-Henning Kamp /* Find the larger of the number of bits transmitted */ 342017a4322SPoul-Henning Kamp bpc = imax(bits_per_char(t), bits_per_char(&tp2->t_termios)); 343017a4322SPoul-Henning Kamp 344017a4322SPoul-Henning Kamp for (i = 0; i < 2; i++) { 345017a4322SPoul-Henning Kamp /* Use the slower of our receive and their transmit rate */ 346bc093719SEd Schouten speed = imin(tp2->t_termios.c_ospeed, t->c_ispeed); 347017a4322SPoul-Henning Kamp if (speed == 0) { 348bc093719SEd Schouten np->np_rate = 0; 349bc093719SEd Schouten np->np_other->np_rate = 0; 350017a4322SPoul-Henning Kamp return (0); 351017a4322SPoul-Henning Kamp } 352017a4322SPoul-Henning Kamp 353017a4322SPoul-Henning Kamp speed <<= QS; /* [bit/sec, scaled] */ 354017a4322SPoul-Henning Kamp speed /= bpc; /* [char/sec, scaled] */ 355017a4322SPoul-Henning Kamp rate = (hz << QS) / speed; /* [hz per callout] */ 356017a4322SPoul-Henning Kamp if (rate == 0) 357017a4322SPoul-Henning Kamp rate = 1; 358017a4322SPoul-Henning Kamp 359017a4322SPoul-Henning Kamp speed *= rate; 360017a4322SPoul-Henning Kamp speed /= hz; /* [(char/sec)/tick, scaled */ 361017a4322SPoul-Henning Kamp 362bc093719SEd Schouten np->np_credits = speed; 363bc093719SEd Schouten np->np_rate = rate; 364bc093719SEd Schouten callout_reset(&np->np_callout, rate, nmdm_timeout, np); 365017a4322SPoul-Henning Kamp 366017a4322SPoul-Henning Kamp /* 367017a4322SPoul-Henning Kamp * swap pointers for second pass so the other end gets 368017a4322SPoul-Henning Kamp * updated as well. 369017a4322SPoul-Henning Kamp */ 370bc093719SEd Schouten np = np->np_other; 371017a4322SPoul-Henning Kamp t = &tp2->t_termios; 372017a4322SPoul-Henning Kamp tp2 = tp; 373017a4322SPoul-Henning Kamp } 374bc093719SEd Schouten 375017a4322SPoul-Henning Kamp return (0); 376017a4322SPoul-Henning Kamp } 377017a4322SPoul-Henning Kamp 378017a4322SPoul-Henning Kamp static int 379bc093719SEd Schouten nmdm_modem(struct tty *tp, int sigon, int sigoff) 380c24097e4SPoul-Henning Kamp { 381bc093719SEd Schouten struct nmdmpart *np = tty_softc(tp); 382bc093719SEd Schouten int i = 0; 383c24097e4SPoul-Henning Kamp 384c24097e4SPoul-Henning Kamp if (sigon || sigoff) { 385017a4322SPoul-Henning Kamp if (sigon & SER_DTR) 386bc093719SEd Schouten np->np_other->np_dcd = 1; 387017a4322SPoul-Henning Kamp if (sigoff & SER_DTR) 388bc093719SEd Schouten np->np_other->np_dcd = 0; 389bc093719SEd Schouten 390bc093719SEd Schouten ttydisc_modem(np->np_other->np_tty, np->np_other->np_dcd); 391bc093719SEd Schouten 392c24097e4SPoul-Henning Kamp return (0); 393c24097e4SPoul-Henning Kamp } else { 394bc093719SEd Schouten if (np->np_dcd) 395c24097e4SPoul-Henning Kamp i |= SER_DCD; 396bc093719SEd Schouten if (np->np_other->np_dcd) 397c24097e4SPoul-Henning Kamp i |= SER_DTR; 398bc093719SEd Schouten 399c24097e4SPoul-Henning Kamp return (i); 400c24097e4SPoul-Henning Kamp } 401c24097e4SPoul-Henning Kamp } 402c24097e4SPoul-Henning Kamp 403bc093719SEd Schouten static void 404bc093719SEd Schouten nmdm_inwakeup(struct tty *tp) 405737a1286SJulian Elischer { 406bc093719SEd Schouten struct nmdmpart *np = tty_softc(tp); 407737a1286SJulian Elischer 408bc093719SEd Schouten /* We can receive again, so wake up the other side. */ 409bc093719SEd Schouten taskqueue_enqueue(taskqueue_swi, &np->np_other->np_task); 410737a1286SJulian Elischer } 411737a1286SJulian Elischer 412737a1286SJulian Elischer static void 413bc093719SEd Schouten nmdm_outwakeup(struct tty *tp) 414737a1286SJulian Elischer { 415bc093719SEd Schouten struct nmdmpart *np = tty_softc(tp); 416737a1286SJulian Elischer 417bc093719SEd Schouten /* We can transmit again, so wake up our side. */ 418bc093719SEd Schouten taskqueue_enqueue(taskqueue_swi, &np->np_task); 419737a1286SJulian Elischer } 420737a1286SJulian Elischer 4219308b700SJulian Elischer /* 4229308b700SJulian Elischer * Module handling 4239308b700SJulian Elischer */ 4249308b700SJulian Elischer static int 4259308b700SJulian Elischer nmdm_modevent(module_t mod, int type, void *data) 426737a1286SJulian Elischer { 427b0b03348SPoul-Henning Kamp static eventhandler_tag tag; 4289308b700SJulian Elischer 4299308b700SJulian Elischer switch(type) { 430b0b03348SPoul-Henning Kamp case MOD_LOAD: 431b0b03348SPoul-Henning Kamp tag = EVENTHANDLER_REGISTER(dev_clone, nmdm_clone, 0, 1000); 432b0b03348SPoul-Henning Kamp if (tag == NULL) 433b0b03348SPoul-Henning Kamp return (ENOMEM); 4349308b700SJulian Elischer break; 4359308b700SJulian Elischer 4369308b700SJulian Elischer case MOD_SHUTDOWN: 4379308b700SJulian Elischer break; 438bc093719SEd Schouten 439bc093719SEd Schouten case MOD_UNLOAD: 440bc093719SEd Schouten if (nmdm_count != 0) 441bc093719SEd Schouten return (EBUSY); 442bc093719SEd Schouten EVENTHANDLER_DEREGISTER(dev_clone, tag); 443bc093719SEd Schouten break; 444bc093719SEd Schouten 4459308b700SJulian Elischer default: 446bc093719SEd Schouten return (EOPNOTSUPP); 4479308b700SJulian Elischer } 448bc093719SEd Schouten 449bc093719SEd Schouten return (0); 450737a1286SJulian Elischer } 451737a1286SJulian Elischer 4529308b700SJulian Elischer DEV_MODULE(nmdm, nmdm_modevent, NULL); 453