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/fcntl.h>
   48 #include <sys/poll.h>
   49 #include <sys/kernel.h>
   50 #include <sys/limits.h>
   51 #include <sys/module.h>
   52 #include <sys/serial.h>
   53 #include <sys/signalvar.h>
   54 #include <sys/malloc.h>
   55 #include <sys/taskqueue.h>
   56 
   57 static MALLOC_DEFINE(M_NMDM, "nullmodem", "nullmodem data structures");
   58 
   59 static tsw_inwakeup_t   nmdm_outwakeup;
   60 static tsw_outwakeup_t  nmdm_inwakeup;
   61 static tsw_param_t      nmdm_param;
   62 static tsw_modem_t      nmdm_modem;
   63 static tsw_close_t      nmdm_close;
   64 static tsw_free_t       nmdm_free;
   65 
   66 static struct ttydevsw nmdm_class = {
   67         .tsw_flags      = TF_NOPREFIX,
   68         .tsw_inwakeup   = nmdm_inwakeup,
   69         .tsw_outwakeup  = nmdm_outwakeup,
   70         .tsw_param      = nmdm_param,
   71         .tsw_modem      = nmdm_modem,
   72         .tsw_close      = nmdm_close,
   73         .tsw_free       = nmdm_free,
   74 };
   75 
   76 static void nmdm_task_tty(void *, int);
   77 
   78 struct nmdmsoftc;
   79 
   80 struct nmdmpart {
   81         struct tty              *np_tty;
   82         int                      np_dcd;
   83         struct task              np_task;
   84         struct nmdmpart         *np_other;
   85         struct nmdmsoftc        *np_pair;
   86         struct callout           np_callout;
   87         u_long                   np_quota;
   88         u_long                   np_accumulator;
   89         int                      np_rate;
   90         int                      np_credits;
   91 
   92 #define QS 8    /* Quota shift */
   93 };
   94 
   95 struct nmdmsoftc {
   96         struct nmdmpart ns_part1;
   97         struct nmdmpart ns_part2;
   98         struct mtx      ns_mtx;
   99 };
  100 
  101 static int nmdm_count = 0;
  102 
  103 static void
  104 nmdm_close(struct tty *tp)
  105 {
  106         struct nmdmpart *np;
  107         struct nmdmpart *onp;
  108         struct tty *otp;
  109 
  110         np = tty_softc(tp);
  111         onp = np->np_other;
  112         otp = onp->np_tty;
  113 
  114         /* If second part is opened, do not destroy ourselves. */
  115         if (tty_opened(otp))
  116                 return;
  117 
  118         /* Shut down self. */
  119         tty_rel_gone(tp);
  120 
  121         /* Shut down second part. */
  122         tty_lock(tp);
  123         onp = np->np_other;
  124         if (onp == NULL)
  125                 return;
  126         otp = onp->np_tty;
  127         tty_rel_gone(otp);
  128         tty_lock(tp);
  129 }
  130 
  131 static void
  132 nmdm_free(void *softc)
  133 {
  134         struct nmdmpart *np = (struct nmdmpart *)softc;
  135         struct nmdmsoftc *ns = np->np_pair;
  136 
  137         callout_drain(&np->np_callout);
  138         taskqueue_drain(taskqueue_swi, &np->np_task);
  139 
  140         /*
  141          * The function is called on both parts simultaneously.  We serialize
  142          * with help of ns_mtx.  The first invocation should return and
  143          * delegate freeing of resources to the second.
  144          */
  145         mtx_lock(&ns->ns_mtx);
  146         if (np->np_other != NULL) {
  147                 np->np_other->np_other = NULL;
  148                 mtx_unlock(&ns->ns_mtx);
  149                 return;
  150         }
  151         mtx_destroy(&ns->ns_mtx);
  152         free(ns, M_NMDM);
  153         atomic_subtract_int(&nmdm_count, 1);
  154 }
  155 
  156 static void
  157 nmdm_clone(void *arg, struct ucred *cred, char *name, int nameen,
  158     struct cdev **dev)
  159 {
  160         struct nmdmsoftc *ns;
  161         struct tty *tp;
  162         char *end;
  163         int error;
  164         char endc;
  165 
  166         if (*dev != NULL)
  167                 return;
  168         if (strncmp(name, "nmdm", 4) != 0)
  169                 return;
  170         if (strlen(name) <= strlen("nmdmX"))
  171                 return;
  172 
  173         /* Device name must be "nmdm%s%c", where %c is 'A' or 'B'. */
  174         end = name + strlen(name) - 1;
  175         endc = *end;
  176         if (endc != 'A' && endc != 'B')
  177                 return;
  178 
  179         ns = malloc(sizeof(*ns), M_NMDM, M_WAITOK | M_ZERO);
  180         mtx_init(&ns->ns_mtx, "nmdm", NULL, MTX_DEF);
  181 
  182         /* Hook the pairs together. */
  183         ns->ns_part1.np_pair = ns;
  184         ns->ns_part1.np_other = &ns->ns_part2;
  185         TASK_INIT(&ns->ns_part1.np_task, 0, nmdm_task_tty, &ns->ns_part1);
  186         callout_init_mtx(&ns->ns_part1.np_callout, &ns->ns_mtx, 0);
  187 
  188         ns->ns_part2.np_pair = ns;
  189         ns->ns_part2.np_other = &ns->ns_part1;
  190         TASK_INIT(&ns->ns_part2.np_task, 0, nmdm_task_tty, &ns->ns_part2);
  191         callout_init_mtx(&ns->ns_part2.np_callout, &ns->ns_mtx, 0);
  192 
  193         /* Create device nodes. */
  194         tp = ns->ns_part1.np_tty = tty_alloc_mutex(&nmdm_class, &ns->ns_part1,
  195             &ns->ns_mtx);
  196         *end = 'A';
  197         error = tty_makedevf(tp, NULL, endc == 'A' ? TTYMK_CLONING : 0,
  198             "%s", name);
  199         if (error) {
  200                 *end = endc;
  201                 mtx_destroy(&ns->ns_mtx);
  202                 free(ns, M_NMDM);
  203                 return;
  204         }
  205 
  206         tp = ns->ns_part2.np_tty = tty_alloc_mutex(&nmdm_class, &ns->ns_part2,
  207             &ns->ns_mtx);
  208         *end = 'B';
  209         error = tty_makedevf(tp, NULL, endc == 'B' ? TTYMK_CLONING : 0,
  210             "%s", name);
  211         if (error) {
  212                 *end = endc;
  213                 mtx_lock(&ns->ns_mtx);
  214                 /* see nmdm_free() */
  215                 ns->ns_part1.np_other = NULL;
  216                 atomic_add_int(&nmdm_count, 1);
  217                 tty_rel_gone(ns->ns_part1.np_tty);
  218                 return;
  219         }
  220 
  221         if (endc == 'A')
  222                 *dev = ns->ns_part1.np_tty->t_dev;
  223         else
  224                 *dev = ns->ns_part2.np_tty->t_dev;
  225 
  226         *end = endc;
  227         atomic_add_int(&nmdm_count, 1);
  228 }
  229 
  230 static void
  231 nmdm_timeout(void *arg)
  232 {
  233         struct nmdmpart *np = arg;
  234 
  235         if (np->np_rate == 0)
  236                 return;
  237 
  238         /*
  239          * Do a simple Floyd-Steinberg dither here to avoid FP math.
  240          * Wipe out unused quota from last tick.
  241          */
  242         np->np_accumulator += np->np_credits;
  243         np->np_quota = np->np_accumulator >> QS;
  244         np->np_accumulator &= ((1 << QS) - 1);
  245 
  246         taskqueue_enqueue(taskqueue_swi, &np->np_task);
  247         callout_reset(&np->np_callout, np->np_rate, nmdm_timeout, np);
  248 }
  249 
  250 static void
  251 nmdm_task_tty(void *arg, int pending __unused)
  252 {
  253         struct tty *tp, *otp;
  254         struct nmdmpart *np = arg;
  255         char c;
  256 
  257         tp = np->np_tty;
  258         tty_lock(tp);
  259         if (tty_gone(tp)) {
  260                 tty_unlock(tp);
  261                 return;
  262         }
  263 
  264         otp = np->np_other->np_tty;
  265         KASSERT(otp != NULL, ("NULL otp in nmdmstart"));
  266         KASSERT(otp != tp, ("NULL otp == tp nmdmstart"));
  267         if (np->np_other->np_dcd) {
  268                 if (!tty_opened(tp)) {
  269                         np->np_other->np_dcd = 0;
  270                         ttydisc_modem(otp, 0);
  271                 }
  272         } else {
  273                 if (tty_opened(tp)) {
  274                         np->np_other->np_dcd = 1;
  275                         ttydisc_modem(otp, 1);
  276                 }
  277         }
  278 
  279         /* This may happen when we are in detach process. */
  280         if (tty_gone(otp)) {
  281                 tty_unlock(otp);
  282                 return;
  283         }
  284 
  285         while (ttydisc_rint_poll(otp) > 0) {
  286                 if (np->np_rate && !np->np_quota)
  287                         break;
  288                 if (ttydisc_getc(tp, &c, 1) != 1)
  289                         break;
  290                 np->np_quota--;
  291                 ttydisc_rint(otp, c, 0);
  292         }
  293 
  294         ttydisc_rint_done(otp);
  295 
  296         tty_unlock(tp);
  297 }
  298 
  299 static int
  300 bits_per_char(struct termios *t)
  301 {
  302         int bits;
  303 
  304         bits = 1;               /* start bit */
  305         switch (t->c_cflag & CSIZE) {
  306         case CS5:       bits += 5;      break;
  307         case CS6:       bits += 6;      break;
  308         case CS7:       bits += 7;      break;
  309         case CS8:       bits += 8;      break;
  310         }
  311         bits++;                 /* stop bit */
  312         if (t->c_cflag & PARENB)
  313                 bits++;
  314         if (t->c_cflag & CSTOPB)
  315                 bits++;
  316         return (bits);
  317 }
  318 
  319 static int
  320 nmdm_param(struct tty *tp, struct termios *t)
  321 {
  322         struct nmdmpart *np = tty_softc(tp);
  323         struct tty *tp2;
  324         int bpc, rate, speed, i;
  325 
  326         tp2 = np->np_other->np_tty;
  327 
  328         if (!((t->c_cflag | tp2->t_termios.c_cflag) & CDSR_OFLOW)) {
  329                 np->np_rate = 0;
  330                 np->np_other->np_rate = 0;
  331                 return (0);
  332         }
  333 
  334         /*
  335          * DSRFLOW one either side enables rate-simulation for both
  336          * directions.
  337          * NB: the two directions may run at different rates.
  338          */
  339 
  340         /* Find the larger of the number of bits transmitted */
  341         bpc = imax(bits_per_char(t), bits_per_char(&tp2->t_termios));
  342 
  343         for (i = 0; i < 2; i++) {
  344                 /* Use the slower of our receive and their transmit rate */
  345                 speed = imin(tp2->t_termios.c_ospeed, t->c_ispeed);
  346                 if (speed == 0) {
  347                         np->np_rate = 0;
  348                         np->np_other->np_rate = 0;
  349                         return (0);
  350                 }
  351 
  352                 speed <<= QS;                   /* [bit/sec, scaled] */
  353                 speed /= bpc;                   /* [char/sec, scaled] */
  354                 rate = (hz << QS) / speed;      /* [hz per callout] */
  355                 if (rate == 0)
  356                         rate = 1;
  357 
  358                 speed *= rate;
  359                 speed /= hz;                    /* [(char/sec)/tick, scaled */
  360 
  361                 np->np_credits = speed;
  362                 np->np_rate = rate;
  363                 callout_reset(&np->np_callout, rate, nmdm_timeout, np);
  364 
  365                 /*
  366                  * swap pointers for second pass so the other end gets
  367                  * updated as well.
  368                  */
  369                 np = np->np_other;
  370                 t = &tp2->t_termios;
  371                 tp2 = tp;
  372         }
  373 
  374         return (0);
  375 }
  376 
  377 static int
  378 nmdm_modem(struct tty *tp, int sigon, int sigoff)
  379 {
  380         struct nmdmpart *np = tty_softc(tp);
  381         int i = 0;
  382 
  383         if (sigon || sigoff) {
  384                 if (sigon & SER_DTR)
  385                         np->np_other->np_dcd = 1;
  386                 if (sigoff & SER_DTR)
  387                         np->np_other->np_dcd = 0;
  388 
  389                 ttydisc_modem(np->np_other->np_tty, np->np_other->np_dcd);
  390 
  391                 return (0);
  392         } else {
  393                 if (np->np_dcd)
  394                         i |= SER_DCD;
  395                 if (np->np_other->np_dcd)
  396                         i |= SER_DTR;
  397 
  398                 return (i);
  399         }
  400 }
  401 
  402 static void
  403 nmdm_inwakeup(struct tty *tp)
  404 {
  405         struct nmdmpart *np = tty_softc(tp);
  406 
  407         /* We can receive again, so wake up the other side. */
  408         taskqueue_enqueue(taskqueue_swi, &np->np_other->np_task);
  409 }
  410 
  411 static void
  412 nmdm_outwakeup(struct tty *tp)
  413 {
  414         struct nmdmpart *np = tty_softc(tp);
  415 
  416         /* We can transmit again, so wake up our side. */
  417         taskqueue_enqueue(taskqueue_swi, &np->np_task);
  418 }
  419 
  420 /*
  421  * Module handling
  422  */
  423 static int
  424 nmdm_modevent(module_t mod, int type, void *data)
  425 {
  426         static eventhandler_tag tag;
  427 
  428         switch(type) {
  429         case MOD_LOAD: 
  430                 tag = EVENTHANDLER_REGISTER(dev_clone, nmdm_clone, 0, 1000);
  431                 if (tag == NULL)
  432                         return (ENOMEM);
  433                 break;
  434 
  435         case MOD_SHUTDOWN:
  436                 break;
  437 
  438         case MOD_UNLOAD:
  439                 if (nmdm_count != 0)
  440                         return (EBUSY);
  441                 EVENTHANDLER_DEREGISTER(dev_clone, tag);
  442                 break;
  443 
  444         default:
  445                 return (EOPNOTSUPP);
  446         }
  447 
  448         return (0);
  449 }
  450 
  451 DEV_MODULE(nmdm, nmdm_modevent, NULL);

Cache object: 90bd070db984b6cbf23fc15c785fb1a0


[ 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.