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  * Copyright (c) 1982, 1986, 1989, 1993
    3  *      The Regents of the University of California.  All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  * 3. All advertising materials mentioning features or use of this software
   14  *    must display the following acknowledgement:
   15  *      This product includes software developed by the University of
   16  *      California, Berkeley and its contributors.
   17  * 4. Neither the name of the University nor the names of its contributors
   18  *    may be used to endorse or promote products derived from this software
   19  *    without specific prior written permission.
   20  *
   21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   31  * SUCH DAMAGE.
   32  *
   33  * $FreeBSD$
   34  */
   35 
   36 /*
   37  * Pseudo-nulmodem Driver
   38  */
   39 #include "opt_compat.h"
   40 #include <sys/param.h>
   41 #include <sys/systm.h>
   42 #if defined(COMPAT_43) || defined(COMPAT_SUNOS)
   43 #include <sys/ioctl_compat.h>
   44 #endif
   45 #include <sys/proc.h>
   46 #include <sys/tty.h>
   47 #include <sys/conf.h>
   48 #include <sys/fcntl.h>
   49 #include <sys/poll.h>
   50 #include <sys/kernel.h>
   51 #include <sys/vnode.h>
   52 #include <sys/signalvar.h>
   53 #include <sys/malloc.h>
   54 
   55 MALLOC_DEFINE(M_NLMDM, "nullmodem", "nullmodem data structures");
   56 
   57 static void nmdmstart __P((struct tty *tp));
   58 static void nmdmstop __P((struct tty *tp, int rw));
   59 static void wakeup_other __P((struct tty *tp, int flag));
   60 static void nmdminit __P((int n));
   61 
   62 static  d_open_t        nmdmopen;
   63 static  d_close_t       nmdmclose;
   64 static  d_read_t        nmdmread;
   65 static  d_write_t       nmdmwrite;
   66 static  d_ioctl_t       nmdmioctl;
   67 
   68 #define CDEV_MAJOR      18
   69 static struct cdevsw nmdm_cdevsw = {
   70         /* open */      nmdmopen,
   71         /* close */     nmdmclose,
   72         /* read */      nmdmread,
   73         /* write */     nmdmwrite,
   74         /* ioctl */     nmdmioctl,
   75         /* poll */      ttypoll,
   76         /* mmap */      nommap,
   77         /* strategy */  nostrategy,
   78         /* name */      "pts",
   79         /* maj */       CDEV_MAJOR,
   80         /* dump */      nodump,
   81         /* psize */     nopsize,
   82         /* flags */     D_TTY,
   83         /* bmaj */      -1
   84 };
   85 
   86 #define BUFSIZ 100              /* Chunk size iomoved to/from user */
   87 
   88 struct softpart {
   89         struct tty nm_tty;
   90         dev_t   dev;
   91         int     modemsignals;   /* bits defined in sys/ttycom.h */
   92         int     gotbreak;
   93 };
   94 
   95 struct  nm_softc {
   96         int     pt_flags;
   97         struct softpart part1, part2;
   98         struct  prison *pt_prison;
   99 };
  100 
  101 #define PF_STOPPED      0x10            /* user told stopped */
  102 
  103 static void
  104 nmdm_crossover(struct nm_softc *pti,
  105                 struct softpart *ourpart,
  106                 struct softpart *otherpart);
  107 
  108 #define GETPARTS(tp, ourpart, otherpart) \
  109 do {    \
  110         struct nm_softc *pti = tp->t_dev->si_drv1; \
  111         if (tp == &pti->part1.nm_tty) { \
  112                 ourpart = &pti->part1; \
  113                 otherpart = &pti->part2; \
  114         } else { \
  115                 ourpart = &pti->part2; \
  116                 otherpart = &pti->part1; \
  117         }  \
  118 } while (0)
  119 
  120 /*
  121  * This function creates and initializes a pair of ttys.
  122  */
  123 static void
  124 nmdminit(n)
  125         int n;
  126 {
  127         dev_t dev1, dev2;
  128         struct nm_softc *pt;
  129 
  130         /* For now we only map the lower 8 bits of the minor */
  131         if (n & ~0xff)
  132                 return;
  133 
  134         pt = malloc(sizeof(*pt), M_NLMDM, M_WAITOK);
  135         bzero(pt, sizeof(*pt));
  136         pt->part1.dev = dev1 = make_dev(&nmdm_cdevsw, n+n,
  137             0, 0, 0666, "nmdm%dA", n);
  138         pt->part2.dev = dev2 = make_dev(&nmdm_cdevsw, n+n+1,
  139             0, 0, 0666, "nmdm%dB", n);
  140 
  141         dev1->si_drv1 = dev2->si_drv1 = pt;
  142         dev1->si_tty = &pt->part1.nm_tty;
  143         dev2->si_tty = &pt->part2.nm_tty;
  144         ttyregister(&pt->part1.nm_tty);
  145         ttyregister(&pt->part2.nm_tty);
  146         pt->part1.nm_tty.t_oproc = nmdmstart;
  147         pt->part2.nm_tty.t_oproc = nmdmstart;
  148         pt->part1.nm_tty.t_stop = nmdmstop;
  149         pt->part2.nm_tty.t_dev = dev1;
  150         pt->part1.nm_tty.t_dev = dev2;
  151         pt->part2.nm_tty.t_stop = nmdmstop;
  152 }
  153 
  154 /*ARGSUSED*/
  155 static  int
  156 nmdmopen(dev, flag, devtype, p)
  157         dev_t dev;
  158         int flag, devtype;
  159         struct proc *p;
  160 {
  161         register struct tty *tp, *tp2;
  162         int error;
  163         int minr;
  164         dev_t nextdev;
  165         struct nm_softc *pti;
  166         int is_b;
  167         int     pair;
  168         struct  softpart *ourpart, *otherpart;
  169 
  170         /*
  171          * XXX: Gross hack for DEVFS:
  172          * If we openned this device, ensure we have the
  173          * next one too, so people can open it.
  174          */
  175         minr = lminor(dev);
  176         pair = minr >> 1;
  177         is_b = minr & 1;
  178         
  179         if (pair < 127) {
  180                 nextdev = makedev(major(dev), (pair+pair) + 1);
  181                 if (!nextdev->si_drv1) {
  182                         nmdminit(pair + 1);
  183                 }
  184         }
  185         if (!dev->si_drv1)
  186                 nmdminit(pair);
  187 
  188         if (!dev->si_drv1)
  189                 return(ENXIO);  
  190 
  191         pti = dev->si_drv1;
  192         if (is_b) 
  193                 tp = &pti->part2.nm_tty;
  194         else 
  195                 tp = &pti->part1.nm_tty;
  196         GETPARTS(tp, ourpart, otherpart);
  197         tp2 = &otherpart->nm_tty;
  198         ourpart->modemsignals |= TIOCM_LE;
  199 
  200         if ((tp->t_state & TS_ISOPEN) == 0) {
  201                 ttychars(tp);           /* Set up default chars */
  202                 tp->t_iflag = TTYDEF_IFLAG;
  203                 tp->t_oflag = TTYDEF_OFLAG;
  204                 tp->t_lflag = TTYDEF_LFLAG;
  205                 tp->t_cflag = TTYDEF_CFLAG;
  206                 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
  207         } else if (tp->t_state & TS_XCLUDE && suser(p)) {
  208                 return (EBUSY);
  209         } else if (pti->pt_prison != p->p_prison) {
  210                 return (EBUSY);
  211         }
  212 
  213         /*
  214          * If the other side is open we have carrier
  215          */
  216         if (tp2->t_state & TS_ISOPEN) {
  217                 (void)(*linesw[tp->t_line].l_modem)(tp, 1);
  218         }
  219 
  220         /*
  221          * And the other side gets carrier as we are now open.
  222          */
  223         (void)(*linesw[tp2->t_line].l_modem)(tp2, 1);
  224 
  225         /* External processing makes no sense here */
  226         tp->t_lflag &= ~EXTPROC;
  227 
  228         /* 
  229          * Wait here if we don't have carrier.
  230          */
  231 #if 0
  232         while ((tp->t_state & TS_CARR_ON) == 0) {
  233                 if (flag & FNONBLOCK)
  234                         break;
  235                 error = ttysleep(tp, TSA_CARR_ON(tp), TTIPRI | PCATCH,
  236                                  "nmdopn", 0);
  237                 if (error)
  238                         return (error);
  239         }
  240 #endif
  241 
  242         /*
  243          * Give the line disciplin a chance to set this end up.
  244          */
  245         error = (*linesw[tp->t_line].l_open)(dev, tp);
  246 
  247         /*
  248          * Wake up the other side.
  249          * Theoretically not needed.
  250          */
  251         ourpart->modemsignals |= TIOCM_DTR;
  252         nmdm_crossover(pti, ourpart, otherpart);
  253         if (error == 0)
  254                 wakeup_other(tp, FREAD|FWRITE); /* XXX */
  255         return (error);
  256 }
  257 
  258 static  int
  259 nmdmclose(dev, flag, mode, p)
  260         dev_t dev;
  261         int flag, mode;
  262         struct proc *p;
  263 {
  264         register struct tty *tp, *tp2;
  265         int err;
  266         struct softpart *ourpart, *otherpart;
  267 
  268         /*
  269          * let the other end know that the game is up
  270          */
  271         tp = dev->si_tty;
  272         GETPARTS(tp, ourpart, otherpart);
  273         tp2 = &otherpart->nm_tty;
  274         (void)(*linesw[tp2->t_line].l_modem)(tp2, 0);
  275 
  276         /*
  277          * XXX MDMBUF makes no sense for nmdms but would inhibit the above
  278          * l_modem().  CLOCAL makes sense but isn't supported.   Special
  279          * l_modem()s that ignore carrier drop make no sense for nmdms but
  280          * may be in use because other parts of the line discipline make
  281          * sense for nmdms.  Recover by doing everything that a normal
  282          * ttymodem() would have done except for sending a SIGHUP.
  283          */
  284         if (tp2->t_state & TS_ISOPEN) {
  285                 tp2->t_state &= ~(TS_CARR_ON | TS_CONNECTED);
  286                 tp2->t_state |= TS_ZOMBIE;
  287                 ttyflush(tp2, FREAD | FWRITE);
  288         }
  289 
  290         err = (*linesw[tp->t_line].l_close)(tp, flag);
  291         ourpart->modemsignals &= ~TIOCM_DTR;
  292         nmdm_crossover(dev->si_drv1, ourpart, otherpart);
  293         nmdmstop(tp, FREAD|FWRITE);
  294         (void) ttyclose(tp);
  295         return (err);
  296 }
  297 
  298 static  int
  299 nmdmread(dev, uio, flag)
  300         dev_t dev;
  301         struct uio *uio;
  302         int flag;
  303 {
  304         int error = 0;
  305         struct tty *tp, *tp2;
  306         struct softpart *ourpart, *otherpart;
  307 
  308         tp = dev->si_tty;
  309         GETPARTS(tp, ourpart, otherpart);
  310         tp2 = &otherpart->nm_tty;
  311 
  312 #if 0
  313         if (tp2->t_state & TS_ISOPEN) {
  314                 error = (*linesw[tp->t_line].l_read)(tp, uio, flag);
  315                 wakeup_other(tp, FWRITE);
  316         } else {
  317                 if (flag & IO_NDELAY) {
  318                         return (EWOULDBLOCK);
  319                 }
  320                 error = tsleep(TSA_PTC_READ(tp),
  321                                 TTIPRI | PCATCH, "nmdout", 0);
  322                 }
  323         }
  324 #else
  325         if ((error = (*linesw[tp->t_line].l_read)(tp, uio, flag)) == 0)
  326                 wakeup_other(tp, FWRITE);
  327 #endif
  328         return (error);
  329 }
  330 
  331 /*
  332  * Write to pseudo-tty.
  333  * Wakeups of controlling tty will happen
  334  * indirectly, when tty driver calls nmdmstart.
  335  */
  336 static  int
  337 nmdmwrite(dev, uio, flag)
  338         dev_t dev;
  339         struct uio *uio;
  340         int flag;
  341 {
  342         register u_char *cp = 0;
  343         register int cc = 0;
  344         u_char locbuf[BUFSIZ];
  345         int cnt = 0;
  346         int error = 0;
  347         struct tty *tp1, *tp;
  348         struct softpart *ourpart, *otherpart;
  349 
  350         tp1 = dev->si_tty;
  351         /*
  352          * Get the other tty struct.
  353          * basically we are writing into the INPUT side of the other device.
  354          */
  355         GETPARTS(tp1, ourpart, otherpart);
  356         tp = &otherpart->nm_tty;
  357 
  358 again:
  359         if ((tp->t_state & TS_ISOPEN) == 0) 
  360                 return (EIO);
  361         while (uio->uio_resid > 0 || cc > 0) {
  362                 /*
  363                  * Fill up the buffer if it's empty
  364                  */
  365                 if (cc == 0) {
  366                         cc = min(uio->uio_resid, BUFSIZ);
  367                         cp = locbuf;
  368                         error = uiomove((caddr_t)cp, cc, uio);
  369                         if (error)
  370                                 return (error);
  371                         /* check again for safety */
  372                         if ((tp->t_state & TS_ISOPEN) == 0) {
  373                                 /* adjust for data copied in but not written */
  374                                 uio->uio_resid += cc;
  375                                 return (EIO);
  376                         }
  377                 }
  378                 while (cc > 0) {
  379                         if (((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= (TTYHOG-2))
  380                         && ((tp->t_canq.c_cc > 0) || !(tp->t_iflag&ICANON))) {
  381                                 /*
  382                                  * Come here to wait for space in outq,
  383                                  * or space in rawq, or an empty canq.
  384                                  */
  385                                 wakeup(TSA_HUP_OR_INPUT(tp));
  386                                 if ((tp->t_state & TS_CONNECTED) == 0) {
  387                                         /*
  388                                          * Data piled up because not connected.
  389                                          * Adjust for data copied in but
  390                                          * not written.
  391                                          */
  392                                         uio->uio_resid += cc;
  393                                         return (EIO);
  394                                 }
  395                                 if (flag & IO_NDELAY) {
  396                                         /*
  397                                          * Don't wait if asked not to.
  398                                          * Adjust for data copied in but
  399                                          * not written.
  400                                          */
  401                                         uio->uio_resid += cc;
  402                                         if (cnt == 0)
  403                                                 return (EWOULDBLOCK);
  404                                         return (0);
  405                                 }
  406                                 error = tsleep(TSA_PTC_WRITE(tp),
  407                                                 TTOPRI | PCATCH, "nmdout", 0);
  408                                 if (error) {
  409                                         /*
  410                                          * Tsleep returned (signal?).
  411                                          * Go find out what the user wants.
  412                                          * adjust for data copied in but
  413                                          * not written
  414                                          */
  415                                         uio->uio_resid += cc;
  416                                         return (error);
  417                                 }
  418                                 goto again;
  419                         }
  420                         (*linesw[tp->t_line].l_rint)(*cp++, tp);
  421                         cnt++;
  422                         cc--;
  423                 }
  424                 cc = 0;
  425         }
  426         return (0);
  427 }
  428 
  429 /*
  430  * Start output on pseudo-tty.
  431  * Wake up process selecting or sleeping for input from controlling tty.
  432  */
  433 static void
  434 nmdmstart(tp)
  435         struct tty *tp;
  436 {
  437         register struct nm_softc *pti = tp->t_dev->si_drv1;
  438 
  439         if (tp->t_state & TS_TTSTOP)
  440                 return;
  441         pti->pt_flags &= ~PF_STOPPED;
  442         wakeup_other(tp, FREAD);
  443 }
  444 
  445 /* Wakes up the OTHER tty;*/
  446 static void
  447 wakeup_other(tp, flag)
  448         struct tty *tp;
  449         int flag;
  450 {
  451         struct softpart *ourpart, *otherpart;
  452 
  453         GETPARTS(tp, ourpart, otherpart);
  454         if (flag & FREAD) {
  455                 selwakeup(&otherpart->nm_tty.t_rsel);
  456                 wakeup(TSA_PTC_READ((&otherpart->nm_tty)));
  457         }
  458         if (flag & FWRITE) {
  459                 selwakeup(&otherpart->nm_tty.t_wsel);
  460                 wakeup(TSA_PTC_WRITE((&otherpart->nm_tty)));
  461         }
  462 }
  463 
  464 static  void
  465 nmdmstop(tp, flush)
  466         register struct tty *tp;
  467         int flush;
  468 {
  469         struct nm_softc *pti = tp->t_dev->si_drv1;
  470         int flag;
  471 
  472         /* note: FLUSHREAD and FLUSHWRITE already ok */
  473         if (flush == 0) {
  474                 flush = TIOCPKT_STOP;
  475                 pti->pt_flags |= PF_STOPPED;
  476         } else
  477                 pti->pt_flags &= ~PF_STOPPED;
  478         /* change of perspective */
  479         flag = 0;
  480         if (flush & FREAD)
  481                 flag |= FWRITE;
  482         if (flush & FWRITE)
  483                 flag |= FREAD;
  484         wakeup_other(tp, flag);
  485 }
  486 
  487 /*ARGSUSED*/
  488 static  int
  489 nmdmioctl(dev, cmd, data, flag, p)
  490         dev_t dev;
  491         u_long cmd;
  492         caddr_t data;
  493         int flag;
  494         struct proc *p;
  495 {
  496         register struct tty *tp = dev->si_tty;
  497         struct nm_softc *pti = dev->si_drv1;
  498         int error, s;
  499         register struct tty *tp2;
  500         struct softpart *ourpart, *otherpart;
  501 
  502         s = spltty();
  503         GETPARTS(tp, ourpart, otherpart);
  504         tp2 = &otherpart->nm_tty;
  505 
  506         error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
  507         if (error == ENOIOCTL)
  508                  error = ttioctl(tp, cmd, data, flag);
  509         if (error == ENOIOCTL) {
  510                 switch (cmd) {
  511                 case TIOCSBRK:
  512                         otherpart->gotbreak = 1;
  513                         break;
  514                 case TIOCCBRK:
  515                         break;
  516                 case TIOCSDTR:
  517                         ourpart->modemsignals |= TIOCM_DTR;
  518                         break;
  519                 case TIOCCDTR:
  520                         ourpart->modemsignals &= TIOCM_DTR;
  521                         break;
  522                 case TIOCMSET:
  523                         ourpart->modemsignals = *(int *)data;
  524                         otherpart->modemsignals = *(int *)data;
  525                         break;
  526                 case TIOCMBIS:
  527                         ourpart->modemsignals |= *(int *)data;
  528                         break;
  529                 case TIOCMBIC:
  530                         ourpart->modemsignals &= ~(*(int *)data);
  531                         otherpart->modemsignals &= ~(*(int *)data);
  532                         break;
  533                 case TIOCMGET:
  534                         *(int *)data = ourpart->modemsignals;
  535                         break;
  536                 case TIOCMSDTRWAIT:
  537                         break;
  538                 case TIOCMGDTRWAIT:
  539                         *(int *)data = 0;
  540                         break;
  541                 case TIOCTIMESTAMP:
  542                 case TIOCDCDTIMESTAMP:
  543                 default:
  544                         splx(s);
  545                         error = ENOTTY;
  546                         return (error);
  547                 }
  548                 error = 0;
  549                 nmdm_crossover(pti, ourpart, otherpart);
  550         }
  551         splx(s);
  552         return (error);
  553 }
  554 
  555 static void
  556 nmdm_crossover(struct nm_softc *pti,
  557                 struct softpart *ourpart,
  558                 struct softpart *otherpart)
  559 {
  560         otherpart->modemsignals &= ~(TIOCM_CTS|TIOCM_CAR);
  561         if (ourpart->modemsignals & TIOCM_RTS)
  562                 otherpart->modemsignals |= TIOCM_CTS;
  563         if (ourpart->modemsignals & TIOCM_DTR)
  564                 otherpart->modemsignals |= TIOCM_CAR;
  565 }
  566 
  567 
  568 
  569 static void nmdm_drvinit __P((void *unused));
  570 
  571 static void
  572 nmdm_drvinit(unused)
  573         void *unused;
  574 {
  575         cdevsw_add(&nmdm_cdevsw);
  576         /* XXX: Gross hack for DEVFS */
  577         nmdminit(0);
  578 }
  579 
  580 SYSINIT(nmdmdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,nmdm_drvinit,NULL)

Cache object: a6f6c7b717c41e009fcafd02334d36d4


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