The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/nmdm/nmdm.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-3-Clause
    3  *
    4  * Copyright (c) 1982, 1986, 1989, 1993
    5  *      The Regents of the University of California.  All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  * 3. Neither the name of the University nor the names of its contributors
   16  *    may be used to endorse or promote products derived from this software
   17  *    without specific prior written permission.
   18  *
   19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   29  * SUCH DAMAGE.
   30  *
   31  */
   32 
   33 #include <sys/cdefs.h>
   34 __FBSDID("$FreeBSD$");
   35 
   36 /*
   37  * Pseudo-nulmodem driver
   38  * Mighty handy for use with serial console in Vmware
   39  */
   40 
   41 #include <sys/param.h>
   42 #include <sys/systm.h>
   43 #include <sys/priv.h>
   44 #include <sys/proc.h>
   45 #include <sys/tty.h>
   46 #include <sys/conf.h>
   47 #include <sys/eventhandler.h>
   48 #include <sys/fcntl.h>
   49 #include <sys/poll.h>
   50 #include <sys/kernel.h>
   51 #include <sys/limits.h>
   52 #include <sys/module.h>
   53 #include <sys/serial.h>
   54 #include <sys/signalvar.h>
   55 #include <sys/malloc.h>
   56 #include <sys/taskqueue.h>
   57 
   58 static MALLOC_DEFINE(M_NMDM, "nullmodem", "nullmodem data structures");
   59 
   60 static tsw_inwakeup_t   nmdm_outwakeup;
   61 static tsw_outwakeup_t  nmdm_inwakeup;
   62 static tsw_param_t      nmdm_param;
   63 static tsw_modem_t      nmdm_modem;
   64 static tsw_close_t      nmdm_close;
   65 static tsw_free_t       nmdm_free;
   66 
   67 static struct ttydevsw nmdm_class = {
   68         .tsw_flags      = TF_NOPREFIX,
   69         .tsw_inwakeup   = nmdm_inwakeup,
   70         .tsw_outwakeup  = nmdm_outwakeup,
   71         .tsw_param      = nmdm_param,
   72         .tsw_modem      = nmdm_modem,
   73         .tsw_close      = nmdm_close,
   74         .tsw_free       = nmdm_free,
   75 };
   76 
   77 static void nmdm_task_tty(void *, int);
   78 
   79 struct nmdmsoftc;
   80 
   81 struct nmdmpart {
   82         struct tty              *np_tty;
   83         int                      np_dcd;
   84         struct task              np_task;
   85         struct nmdmpart         *np_other;
   86         struct nmdmsoftc        *np_pair;
   87         struct callout           np_callout;
   88         u_long                   np_quota;
   89         u_long                   np_accumulator;
   90         int                      np_rate;
   91         int                      np_credits;
   92 
   93 #define QS 8    /* Quota shift */
   94 };
   95 
   96 struct nmdmsoftc {
   97         struct nmdmpart ns_part1;
   98         struct nmdmpart ns_part2;
   99         struct mtx      ns_mtx;
  100 };
  101 
  102 static int nmdm_count = 0;
  103 
  104 static void
  105 nmdm_close(struct tty *tp)
  106 {
  107         struct nmdmpart *np;
  108         struct nmdmpart *onp;
  109         struct tty *otp;
  110 
  111         np = tty_softc(tp);
  112         onp = np->np_other;
  113         otp = onp->np_tty;
  114 
  115         /* If second part is opened, do not destroy ourselves. */
  116         if (tty_opened(otp))
  117                 return;
  118 
  119         /* Shut down self. */
  120         tty_rel_gone(tp);
  121 
  122         /* Shut down second part. */
  123         tty_lock(tp);
  124         onp = np->np_other;
  125         if (onp == NULL)
  126                 return;
  127         otp = onp->np_tty;
  128         tty_rel_gone(otp);
  129         tty_lock(tp);
  130 }
  131 
  132 static void
  133 nmdm_free(void *softc)
  134 {
  135         struct nmdmpart *np = (struct nmdmpart *)softc;
  136         struct nmdmsoftc *ns = np->np_pair;
  137 
  138         callout_drain(&np->np_callout);
  139         taskqueue_drain(taskqueue_swi, &np->np_task);
  140 
  141         /*
  142          * The function is called on both parts simultaneously.  We serialize
  143          * with help of ns_mtx.  The first invocation should return and
  144          * delegate freeing of resources to the second.
  145          */
  146         mtx_lock(&ns->ns_mtx);
  147         if (np->np_other != NULL) {
  148                 np->np_other->np_other = NULL;
  149                 mtx_unlock(&ns->ns_mtx);
  150                 return;
  151         }
  152         mtx_destroy(&ns->ns_mtx);
  153         free(ns, M_NMDM);
  154         atomic_subtract_int(&nmdm_count, 1);
  155 }
  156 
  157 static void
  158 nmdm_clone(void *arg, struct ucred *cred, char *name, int nameen,
  159     struct cdev **dev)
  160 {
  161         struct nmdmsoftc *ns;
  162         struct tty *tp;
  163         char *end;
  164         int error;
  165         char endc;
  166 
  167         if (*dev != NULL)
  168                 return;
  169         if (strncmp(name, "nmdm", 4) != 0)
  170                 return;
  171         if (strlen(name) <= strlen("nmdmX"))
  172                 return;
  173 
  174         /* Device name must be "nmdm%s%c", where %c is 'A' or 'B'. */
  175         end = name + strlen(name) - 1;
  176         endc = *end;
  177         if (endc != 'A' && endc != 'B')
  178                 return;
  179 
  180         ns = malloc(sizeof(*ns), M_NMDM, M_WAITOK | M_ZERO);
  181         mtx_init(&ns->ns_mtx, "nmdm", NULL, MTX_DEF);
  182 
  183         /* Hook the pairs together. */
  184         ns->ns_part1.np_pair = ns;
  185         ns->ns_part1.np_other = &ns->ns_part2;
  186         TASK_INIT(&ns->ns_part1.np_task, 0, nmdm_task_tty, &ns->ns_part1);
  187         callout_init_mtx(&ns->ns_part1.np_callout, &ns->ns_mtx, 0);
  188 
  189         ns->ns_part2.np_pair = ns;
  190         ns->ns_part2.np_other = &ns->ns_part1;
  191         TASK_INIT(&ns->ns_part2.np_task, 0, nmdm_task_tty, &ns->ns_part2);
  192         callout_init_mtx(&ns->ns_part2.np_callout, &ns->ns_mtx, 0);
  193 
  194         /* Create device nodes. */
  195         tp = ns->ns_part1.np_tty = tty_alloc_mutex(&nmdm_class, &ns->ns_part1,
  196             &ns->ns_mtx);
  197         *end = 'A';
  198         error = tty_makedevf(tp, cred, endc == 'A' ? TTYMK_CLONING : 0,
  199             "%s", name);
  200         if (error) {
  201                 *end = endc;
  202                 mtx_destroy(&ns->ns_mtx);
  203                 free(ns, M_NMDM);
  204                 return;
  205         }
  206 
  207         tp = ns->ns_part2.np_tty = tty_alloc_mutex(&nmdm_class, &ns->ns_part2,
  208             &ns->ns_mtx);
  209         *end = 'B';
  210         error = tty_makedevf(tp, cred, endc == 'B' ? TTYMK_CLONING : 0,
  211             "%s", name);
  212         if (error) {
  213                 *end = endc;
  214                 mtx_lock(&ns->ns_mtx);
  215                 /* see nmdm_free() */
  216                 ns->ns_part1.np_other = NULL;
  217                 atomic_add_int(&nmdm_count, 1);
  218                 tty_rel_gone(ns->ns_part1.np_tty);
  219                 return;
  220         }
  221 
  222         if (endc == 'A')
  223                 *dev = ns->ns_part1.np_tty->t_dev;
  224         else
  225                 *dev = ns->ns_part2.np_tty->t_dev;
  226 
  227         *end = endc;
  228         atomic_add_int(&nmdm_count, 1);
  229 }
  230 
  231 static void
  232 nmdm_timeout(void *arg)
  233 {
  234         struct nmdmpart *np = arg;
  235 
  236         if (np->np_rate == 0)
  237                 return;
  238 
  239         /*
  240          * Do a simple Floyd-Steinberg dither here to avoid FP math.
  241          * Wipe out unused quota from last tick.
  242          */
  243         np->np_accumulator += np->np_credits;
  244         np->np_quota = np->np_accumulator >> QS;
  245         np->np_accumulator &= ((1 << QS) - 1);
  246 
  247         taskqueue_enqueue(taskqueue_swi, &np->np_task);
  248         callout_reset(&np->np_callout, np->np_rate, nmdm_timeout, np);
  249 }
  250 
  251 static void
  252 nmdm_task_tty(void *arg, int pending __unused)
  253 {
  254         struct tty *tp, *otp;
  255         struct nmdmpart *np = arg;
  256         char c;
  257 
  258         tp = np->np_tty;
  259         tty_lock(tp);
  260         if (tty_gone(tp)) {
  261                 tty_unlock(tp);
  262                 return;
  263         }
  264 
  265         otp = np->np_other->np_tty;
  266         KASSERT(otp != NULL, ("NULL otp in nmdmstart"));
  267         KASSERT(otp != tp, ("NULL otp == tp nmdmstart"));
  268         if (np->np_other->np_dcd) {
  269                 if (!tty_opened(tp)) {
  270                         np->np_other->np_dcd = 0;
  271                         ttydisc_modem(otp, 0);
  272                 }
  273         } else {
  274                 if (tty_opened(tp)) {
  275                         np->np_other->np_dcd = 1;
  276                         ttydisc_modem(otp, 1);
  277                 }
  278         }
  279 
  280         /* This may happen when we are in detach process. */
  281         if (tty_gone(otp)) {
  282                 tty_unlock(otp);
  283                 return;
  284         }
  285 
  286         while (ttydisc_rint_poll(otp) > 0) {
  287                 if (np->np_rate && !np->np_quota)
  288                         break;
  289                 if (ttydisc_getc(tp, &c, 1) != 1)
  290                         break;
  291                 np->np_quota--;
  292                 ttydisc_rint(otp, c, 0);
  293         }
  294 
  295         ttydisc_rint_done(otp);
  296 
  297         tty_unlock(tp);
  298 }
  299 
  300 static int
  301 bits_per_char(struct termios *t)
  302 {
  303         int bits;
  304 
  305         bits = 1;               /* start bit */
  306         switch (t->c_cflag & CSIZE) {
  307         case CS5:       bits += 5;      break;
  308         case CS6:       bits += 6;      break;
  309         case CS7:       bits += 7;      break;
  310         case CS8:       bits += 8;      break;
  311         }
  312         bits++;                 /* stop bit */
  313         if (t->c_cflag & PARENB)
  314                 bits++;
  315         if (t->c_cflag & CSTOPB)
  316                 bits++;
  317         return (bits);
  318 }
  319 
  320 static int
  321 nmdm_param(struct tty *tp, struct termios *t)
  322 {
  323         struct nmdmpart *np = tty_softc(tp);
  324         struct tty *tp2;
  325         int bpc, rate, speed, i;
  326 
  327         tp2 = np->np_other->np_tty;
  328 
  329         if (!((t->c_cflag | tp2->t_termios.c_cflag) & CDSR_OFLOW)) {
  330                 np->np_rate = 0;
  331                 np->np_other->np_rate = 0;
  332                 return (0);
  333         }
  334 
  335         /*
  336          * DSRFLOW one either side enables rate-simulation for both
  337          * directions.
  338          * NB: the two directions may run at different rates.
  339          */
  340 
  341         /* Find the larger of the number of bits transmitted */
  342         bpc = imax(bits_per_char(t), bits_per_char(&tp2->t_termios));
  343 
  344         for (i = 0; i < 2; i++) {
  345                 /* Use the slower of our receive and their transmit rate */
  346                 speed = imin(tp2->t_termios.c_ospeed, t->c_ispeed);
  347                 if (speed == 0) {
  348                         np->np_rate = 0;
  349                         np->np_other->np_rate = 0;
  350                         return (0);
  351                 }
  352 
  353                 speed <<= QS;                   /* [bit/sec, scaled] */
  354                 speed /= bpc;                   /* [char/sec, scaled] */
  355                 rate = (hz << QS) / speed;      /* [hz per callout] */
  356                 if (rate == 0)
  357                         rate = 1;
  358 
  359                 speed *= rate;
  360                 speed /= hz;                    /* [(char/sec)/tick, scaled */
  361 
  362                 np->np_credits = speed;
  363                 np->np_rate = rate;
  364                 callout_reset(&np->np_callout, rate, nmdm_timeout, np);
  365 
  366                 /*
  367                  * swap pointers for second pass so the other end gets
  368                  * updated as well.
  369                  */
  370                 np = np->np_other;
  371                 t = &tp2->t_termios;
  372                 tp2 = tp;
  373         }
  374 
  375         return (0);
  376 }
  377 
  378 static int
  379 nmdm_modem(struct tty *tp, int sigon, int sigoff)
  380 {
  381         struct nmdmpart *np = tty_softc(tp);
  382         int i = 0;
  383 
  384         if (sigon || sigoff) {
  385                 if (sigon & SER_DTR)
  386                         np->np_other->np_dcd = 1;
  387                 if (sigoff & SER_DTR)
  388                         np->np_other->np_dcd = 0;
  389 
  390                 ttydisc_modem(np->np_other->np_tty, np->np_other->np_dcd);
  391 
  392                 return (0);
  393         } else {
  394                 if (np->np_dcd)
  395                         i |= SER_DCD;
  396                 if (np->np_other->np_dcd)
  397                         i |= SER_DTR;
  398 
  399                 return (i);
  400         }
  401 }
  402 
  403 static void
  404 nmdm_inwakeup(struct tty *tp)
  405 {
  406         struct nmdmpart *np = tty_softc(tp);
  407 
  408         /* We can receive again, so wake up the other side. */
  409         taskqueue_enqueue(taskqueue_swi, &np->np_other->np_task);
  410 }
  411 
  412 static void
  413 nmdm_outwakeup(struct tty *tp)
  414 {
  415         struct nmdmpart *np = tty_softc(tp);
  416 
  417         /* We can transmit again, so wake up our side. */
  418         taskqueue_enqueue(taskqueue_swi, &np->np_task);
  419 }
  420 
  421 /*
  422  * Module handling
  423  */
  424 static int
  425 nmdm_modevent(module_t mod, int type, void *data)
  426 {
  427         static eventhandler_tag tag;
  428 
  429         switch(type) {
  430         case MOD_LOAD: 
  431                 tag = EVENTHANDLER_REGISTER(dev_clone, nmdm_clone, 0, 1000);
  432                 if (tag == NULL)
  433                         return (ENOMEM);
  434                 break;
  435 
  436         case MOD_SHUTDOWN:
  437                 break;
  438 
  439         case MOD_UNLOAD:
  440                 if (nmdm_count != 0)
  441                         return (EBUSY);
  442                 EVENTHANDLER_DEREGISTER(dev_clone, tag);
  443                 break;
  444 
  445         default:
  446                 return (EOPNOTSUPP);
  447         }
  448 
  449         return (0);
  450 }
  451 
  452 DEV_MODULE(nmdm, nmdm_modevent, NULL);

Cache object: 9262986556d6d670f6d84775299e96ac


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.