root/dev/wscons/wsmux.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. wsmuxattach
  2. wsmux_getmux
  3. wsmuxopen
  4. wsmux_mux_open
  5. wsmux_do_open
  6. wsmuxclose
  7. wsmux_mux_close
  8. wsmux_do_close
  9. wsmuxread
  10. wsmuxioctl
  11. wsmux_do_ioctl
  12. wsmuxpoll
  13. wsmux_add_mux
  14. wsmux_create
  15. wsmux_attach_sc
  16. wsmux_detach_sc
  17. wsmux_do_displayioctl
  18. wsmux_evsrc_set_display
  19. wsmux_set_display

    1 /*      $OpenBSD: wsmux.c,v 1.20 2007/05/14 09:03:34 tedu Exp $ */
    2 /*      $NetBSD: wsmux.c,v 1.37 2005/04/30 03:47:12 augustss Exp $      */
    3 
    4 /*
    5  * Copyright (c) 1998, 2005 The NetBSD Foundation, Inc.
    6  * All rights reserved.
    7  *
    8  * Author: Lennart Augustsson <augustss@carlstedt.se>
    9  *         Carlstedt Research & Technology
   10  *
   11  * Redistribution and use in source and binary forms, with or without
   12  * modification, are permitted provided that the following conditions
   13  * are met:
   14  * 1. Redistributions of source code must retain the above copyright
   15  *    notice, this list of conditions and the following disclaimer.
   16  * 2. Redistributions in binary form must reproduce the above copyright
   17  *    notice, this list of conditions and the following disclaimer in the
   18  *    documentation and/or other materials provided with the distribution.
   19  * 3. All advertising materials mentioning features or use of this software
   20  *    must display the following acknowledgement:
   21  *        This product includes software developed by the NetBSD
   22  *        Foundation, Inc. and its contributors.
   23  * 4. Neither the name of The NetBSD Foundation nor the names of its
   24  *    contributors may be used to endorse or promote products derived
   25  *    from this software without specific prior written permission.
   26  *
   27  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   28  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   29  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   30  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   31  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   37  * POSSIBILITY OF SUCH DAMAGE.
   38  */
   39 
   40 #include "wsmux.h"
   41 #include "wsdisplay.h"
   42 #include "wskbd.h"
   43 #include "wsmouse.h"
   44 
   45 /*
   46  * wscons mux device.
   47  *
   48  * The mux device is a collection of real mice and keyboards and acts as 
   49  * a merge point for all the events from the different real devices.
   50  */
   51 
   52 #include <sys/param.h>
   53 #include <sys/conf.h>
   54 #include <sys/ioctl.h>
   55 #include <sys/fcntl.h>
   56 #include <sys/kernel.h>
   57 #include <sys/malloc.h>
   58 #include <sys/proc.h>
   59 #include <sys/queue.h>
   60 #include <sys/syslog.h>
   61 #include <sys/systm.h>
   62 #include <sys/tty.h>
   63 #include <sys/signalvar.h>
   64 #include <sys/device.h>
   65 #include <sys/poll.h>
   66 
   67 #include <dev/wscons/wsconsio.h>
   68 #include <dev/wscons/wsksymdef.h>
   69 #include <dev/wscons/wseventvar.h>
   70 #include <dev/wscons/wscons_callbacks.h>
   71 #include <dev/wscons/wsmuxvar.h>
   72 
   73 #ifdef WSMUX_DEBUG
   74 #define DPRINTF(x)      if (wsmuxdebug) printf x
   75 #define DPRINTFN(n,x)   if (wsmuxdebug > (n)) printf x
   76 int     wsmuxdebug = 0;
   77 #else
   78 #define DPRINTF(x)
   79 #define DPRINTFN(n,x)
   80 #endif
   81 
   82 /*
   83  * The wsmux pseudo device is used to multiplex events from several wsmouse,
   84  * wskbd, and/or wsmux devices together.
   85  * The devices connected together form a tree with muxes in the interior
   86  * and real devices (mouse and kbd) at the leaves.  The special case of
   87  * a tree with one node (mux or other) is supported as well.
   88  * Only the device at the root of the tree can be opened (if a non-root
   89  * device is opened the subtree rooted at that point is severed from the
   90  * containing tree).  When the root is opened it allocates a wseventvar
   91  * struct which all the nodes in the tree will send their events too.
   92  * An ioctl() performed on the root is propagated to all the nodes.
   93  * There are also ioctl() operations to add and remove nodes from a tree.
   94  */
   95 
   96 int     wsmux_mux_open(struct wsevsrc *, struct wseventvar *);
   97 int     wsmux_mux_close(struct wsevsrc *);
   98 
   99 void    wsmux_do_open(struct wsmux_softc *, struct wseventvar *);
  100 
  101 void    wsmux_do_close(struct wsmux_softc *);
  102 #if NWSDISPLAY > 0
  103 int     wsmux_evsrc_set_display(struct device *, struct device *);
  104 #else
  105 #define wsmux_evsrc_set_display NULL
  106 #endif
  107 
  108 int     wsmux_do_displayioctl(struct device *dev, u_long cmd, caddr_t data,
  109             int flag, struct proc *p);
  110 int     wsmux_do_ioctl(struct device *, u_long, caddr_t,int,struct proc *);
  111 
  112 int     wsmux_add_mux(int, struct wsmux_softc *);
  113 
  114 void    wsmuxattach(int);
  115 
  116 struct wssrcops wsmux_srcops = {
  117         WSMUX_MUX,
  118         wsmux_mux_open, wsmux_mux_close, wsmux_do_ioctl, wsmux_do_displayioctl,
  119         wsmux_evsrc_set_display
  120 };
  121 
  122 /* From upper level */
  123 void
  124 wsmuxattach(int n)
  125 {
  126 }
  127 
  128 /* Keep track of all muxes that have been allocated */
  129 int nwsmux = 0;
  130 struct wsmux_softc **wsmuxdevs = NULL;
  131 
  132 /* Return mux n, create if necessary */
  133 struct wsmux_softc *
  134 wsmux_getmux(int n)
  135 {
  136         struct wsmux_softc *sc;
  137         struct wsmux_softc **new, **old;
  138         int i;
  139 
  140         /* Make sure there is room for mux n in the table */
  141         if (n >= nwsmux) {
  142                 old = wsmuxdevs;
  143                 new = (struct wsmux_softc **)
  144                     malloc((n + 1) * sizeof (*wsmuxdevs), M_DEVBUF, M_NOWAIT);
  145                 if (new == NULL) {
  146                         printf("wsmux_getmux: no memory for mux %d\n", n);
  147                         return (NULL);
  148                 }
  149                 if (old != NULL)
  150                         bcopy(old, new, nwsmux * sizeof(*wsmuxdevs));
  151                 for (i = nwsmux; i < (n + 1); i++)
  152                         new[i] = NULL;
  153                 wsmuxdevs = new;
  154                 nwsmux = n + 1;
  155                 if (old != NULL)
  156                         free(old, M_DEVBUF);
  157         }
  158 
  159         sc = wsmuxdevs[n];
  160         if (sc == NULL) {
  161                 sc = wsmux_create("wsmux", n);
  162                 if (sc == NULL)
  163                         printf("wsmux: attach out of memory\n");
  164                 wsmuxdevs[n] = sc;
  165         }
  166         return (sc);
  167 }
  168 
  169 /*
  170  * open() of the pseudo device from device table.
  171  */
  172 int
  173 wsmuxopen(dev_t dev, int flags, int mode, struct proc *p)
  174 {
  175         struct wsmux_softc *sc;
  176         struct wseventvar *evar;
  177         int unit;
  178 
  179         unit = minor(dev);
  180         sc = wsmux_getmux(unit);
  181         if (sc == NULL)
  182                 return (ENXIO);
  183 
  184         DPRINTF(("wsmuxopen: %s: sc=%p p=%p\n", sc->sc_base.me_dv.dv_xname, sc, p));
  185         
  186         if ((flags & (FREAD | FWRITE)) == FWRITE) {
  187                 /* Not opening for read, only ioctl is available. */
  188                 return (0);
  189         }
  190 
  191         if (sc->sc_base.me_parent != NULL) {
  192                 /* Grab the mux out of the greedy hands of the parent mux. */
  193                 DPRINTF(("wsmuxopen: detach\n"));
  194                 wsmux_detach_sc(&sc->sc_base);
  195         }
  196 
  197         if (sc->sc_base.me_evp != NULL)
  198                 /* Already open. */
  199                 return (EBUSY);
  200 
  201         evar = &sc->sc_base.me_evar;
  202         wsevent_init(evar);
  203         evar->io = p;
  204 #ifdef WSDISPLAY_COMPAT_RAWKBD
  205         sc->sc_rawkbd = 0;
  206 #endif
  207 
  208         wsmux_do_open(sc, evar);
  209 
  210         return (0);
  211 }
  212 
  213 /*
  214  * Open of a mux via the parent mux.
  215  */
  216 int
  217 wsmux_mux_open(struct wsevsrc *me, struct wseventvar *evar)
  218 {
  219         struct wsmux_softc *sc = (struct wsmux_softc *)me;
  220 
  221 #ifdef DIAGNOSTIC
  222         if (sc->sc_base.me_evp != NULL) {
  223                 printf("wsmux_mux_open: busy\n");
  224                 return (EBUSY);
  225         }
  226         if (sc->sc_base.me_parent == NULL) {
  227                 printf("wsmux_mux_open: no parent\n");
  228                 return (EINVAL);
  229         }
  230 #endif
  231 
  232         wsmux_do_open(sc, evar);
  233 
  234         return (0);
  235 }
  236 
  237 /* Common part of opening a mux. */
  238 void
  239 wsmux_do_open(struct wsmux_softc *sc, struct wseventvar *evar)
  240 {
  241         struct wsevsrc *me;
  242 #ifdef DIAGNOSTIC
  243         int error;
  244 #endif
  245 
  246         sc->sc_base.me_evp = evar; /* remember event variable, mark as open */
  247 
  248         /* Open all children. */
  249         CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
  250                 DPRINTF(("wsmuxopen: %s: m=%p dev=%s\n",
  251                          sc->sc_base.me_dv.dv_xname, me, me->me_dv.dv_xname));
  252 #ifdef DIAGNOSTIC
  253                 if (me->me_evp != NULL) {
  254                         printf("wsmuxopen: dev already in use\n");
  255                         continue;
  256                 }
  257                 if (me->me_parent != sc) {
  258                         printf("wsmux_do_open: bad child=%p\n", me);
  259                         continue;
  260                 }
  261                 error = wsevsrc_open(me, evar);
  262                 if (error) {
  263                         DPRINTF(("wsmuxopen: open failed %d\n", error));
  264                 }
  265 #else
  266                 /* ignore errors, failing children will not be marked open */
  267                 (void)wsevsrc_open(me, evar);
  268 #endif
  269         }
  270 }
  271 
  272 /*
  273  * close() of the pseudo device from device table.
  274  */
  275 int
  276 wsmuxclose(dev_t dev, int flags, int mode, struct proc *p)
  277 {
  278         struct wsmux_softc *sc =
  279             (struct wsmux_softc *)wsmuxdevs[minor(dev)];
  280         struct wseventvar *evar = sc->sc_base.me_evp;
  281 
  282         if (evar == NULL)
  283                 /* Not open for read */
  284                 return (0);
  285 
  286         wsmux_do_close(sc);
  287         sc->sc_base.me_evp = NULL;
  288         wsevent_fini(evar);
  289         return (0);
  290 }
  291 
  292 /*
  293  * Close of a mux via the parent mux.
  294  */
  295 int
  296 wsmux_mux_close(struct wsevsrc *me)
  297 {
  298         me->me_evp = NULL;
  299         wsmux_do_close((struct wsmux_softc *)me);
  300         return (0);
  301 }
  302 
  303 /* Common part of closing a mux. */
  304 void
  305 wsmux_do_close(struct wsmux_softc *sc)
  306 {
  307         struct wsevsrc *me;
  308 
  309         DPRINTF(("wsmuxclose: %s: sc=%p\n", sc->sc_base.me_dv.dv_xname, sc));
  310 
  311         /* Close all the children. */
  312         CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
  313                 DPRINTF(("wsmuxclose %s: m=%p dev=%s\n",
  314                          sc->sc_base.me_dv.dv_xname, me, me->me_dv.dv_xname));
  315 #ifdef DIAGNOSTIC
  316                 if (me->me_parent != sc) {
  317                         printf("wsmuxclose: bad child=%p\n", me);
  318                         continue;
  319                 }
  320 #endif
  321                 (void)wsevsrc_close(me);
  322                 me->me_evp = NULL;
  323         }
  324 }
  325 
  326 /*
  327  * read() of the pseudo device from device table.
  328  */
  329 int
  330 wsmuxread(dev_t dev, struct uio *uio, int flags)
  331 {
  332         struct wsmux_softc *sc = wsmuxdevs[minor(dev)];
  333         struct wseventvar *evar;
  334         int error;
  335 
  336         evar = sc->sc_base.me_evp;
  337         if (evar == NULL) {
  338 #ifdef DIAGNOSTIC
  339                 /* XXX can we get here? */
  340                 printf("wsmuxread: not open\n");
  341 #endif
  342                 return (EINVAL);
  343         }
  344 
  345         DPRINTFN(5,("wsmuxread: %s event read evar=%p\n",
  346                     sc->sc_base.me_dv.dv_xname, evar));
  347         error = wsevent_read(evar, uio, flags);
  348         DPRINTFN(5,("wsmuxread: %s event read ==> error=%d\n",
  349                     sc->sc_base.me_dv.dv_xname, error));
  350         return (error);
  351 }
  352 
  353 /*
  354  * ioctl of the pseudo device from device table.
  355  */
  356 int
  357 wsmuxioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
  358 {
  359         return wsmux_do_ioctl(&wsmuxdevs[minor(dev)]->sc_base.me_dv, cmd, data, flag, p);
  360 }
  361 
  362 /*
  363  * ioctl of a mux via the parent mux, continuation of wsmuxioctl().
  364  */
  365 int
  366 wsmux_do_ioctl(struct device *dv, u_long cmd, caddr_t data, int flag,
  367     struct proc *p)
  368 {
  369         struct wsmux_softc *sc = (struct wsmux_softc *)dv;
  370         struct wsevsrc *me;
  371         int error, ok;
  372         int s, put, get, n;
  373         struct wseventvar *evar;
  374         struct wscons_event *ev;
  375         struct wsmux_device_list *l;
  376 
  377         DPRINTF(("wsmux_do_ioctl: %s: enter sc=%p, cmd=%08lx\n",
  378                  sc->sc_base.me_dv.dv_xname, sc, cmd));
  379 
  380         switch (cmd) {
  381         case WSMUXIO_INJECTEVENT:
  382         case WSMUXIO_ADD_DEVICE:
  383         case WSMUXIO_REMOVE_DEVICE:
  384 #ifdef WSDISPLAY_COMPAT_RAWKBD
  385         case WSKBDIO_SETMODE:
  386 #endif
  387                 if ((flag & FWRITE) == 0)
  388                         return (EACCES);
  389         }
  390 
  391         switch (cmd) {
  392         case WSMUXIO_INJECTEVENT:
  393                 /* Inject an event, e.g., from moused. */
  394                 DPRINTF(("%s: inject\n", sc->sc_base.me_dv.dv_xname));
  395                 evar = sc->sc_base.me_evp;
  396                 if (evar == NULL) {
  397                         /* No event sink, so ignore it. */
  398                         DPRINTF(("wsmux_do_ioctl: event ignored\n"));
  399                         return (0);
  400                 }
  401 
  402                 s = spltty();
  403                 get = evar->get;
  404                 put = evar->put;
  405                 ev = &evar->q[put];
  406                 if (++put % WSEVENT_QSIZE == get) {
  407                         put--;
  408                         splx(s);
  409                         return (ENOSPC);
  410                 }
  411                 if (put >= WSEVENT_QSIZE)
  412                         put = 0;
  413                 *ev = *(struct wscons_event *)data;
  414                 nanotime(&ev->time);
  415                 evar->put = put;
  416                 WSEVENT_WAKEUP(evar);
  417                 splx(s);
  418                 return (0);
  419         case WSMUXIO_ADD_DEVICE:
  420 #define d ((struct wsmux_device *)data)
  421                 DPRINTF(("%s: add type=%d, no=%d\n", sc->sc_base.me_dv.dv_xname,
  422                          d->type, d->idx));
  423                 switch (d->type) {
  424 #if NWSMOUSE > 0
  425                 case WSMUX_MOUSE:
  426                         return (wsmouse_add_mux(d->idx, sc));
  427 #endif
  428 #if NWSKBD > 0
  429                 case WSMUX_KBD:
  430                         return (wskbd_add_mux(d->idx, sc));
  431 #endif
  432                 case WSMUX_MUX:
  433                         return (wsmux_add_mux(d->idx, sc));
  434                 default:
  435                         return (EINVAL);
  436                 }
  437         case WSMUXIO_REMOVE_DEVICE:
  438                 DPRINTF(("%s: rem type=%d, no=%d\n", sc->sc_base.me_dv.dv_xname,
  439                          d->type, d->idx));
  440                 /* Locate the device */
  441                 CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
  442                         if (me->me_ops->type == d->type &&
  443                             me->me_dv.dv_unit == d->idx) {
  444                                 DPRINTF(("wsmux_do_ioctl: detach\n"));
  445                                 wsmux_detach_sc(me);
  446                                 return (0);
  447                         }
  448                 }
  449                 return (EINVAL);
  450 #undef d
  451 
  452         case WSMUXIO_LIST_DEVICES:
  453                 DPRINTF(("%s: list\n", sc->sc_base.me_dv.dv_xname));
  454                 l = (struct wsmux_device_list *)data;
  455                 n = 0;
  456                 CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
  457                         if (n >= WSMUX_MAXDEV)
  458                                 break;
  459                         l->devices[n].type = me->me_ops->type;
  460                         l->devices[n].idx = me->me_dv.dv_unit;
  461                         n++;
  462                 }
  463                 l->ndevices = n;
  464                 return (0);
  465 #ifdef WSDISPLAY_COMPAT_RAWKBD
  466         case WSKBDIO_SETMODE:
  467                 sc->sc_rawkbd = *(int *)data;
  468                 DPRINTF(("wsmux_do_ioctl: save rawkbd = %d\n", sc->sc_rawkbd));
  469                 break;
  470 #endif
  471         case FIONBIO:
  472                 DPRINTF(("%s: FIONBIO\n", sc->sc_base.me_dv.dv_xname));
  473                 return (0);
  474 
  475         case FIOASYNC:
  476                 DPRINTF(("%s: FIOASYNC\n", sc->sc_base.me_dv.dv_xname));
  477                 evar = sc->sc_base.me_evp;
  478                 if (evar == NULL)
  479                         return (EINVAL);
  480                 evar->async = *(int *)data != 0;
  481                 return (0);
  482         case FIOSETOWN:
  483                 DPRINTF(("%s: FIOSETOWN\n", sc->sc_base.me_dv.dv_xname));
  484                 evar = sc->sc_base.me_evp;
  485                 if (evar == NULL)
  486                         return (EINVAL);
  487                 if (-*(int *)data != evar->io->p_pgid
  488                     && *(int *)data != evar->io->p_pid)
  489                         return (EPERM);
  490                 return (0);
  491         case TIOCSPGRP:
  492                 DPRINTF(("%s: TIOCSPGRP\n", sc->sc_base.me_dv.dv_xname));
  493                 evar = sc->sc_base.me_evp;
  494                 if (evar == NULL)
  495                         return (EINVAL);
  496                 if (*(int *)data != evar->io->p_pgid)
  497                         return (EPERM);
  498                 return (0);
  499         default:
  500                 DPRINTF(("%s: unknown\n", sc->sc_base.me_dv.dv_xname));
  501                 break;
  502         }
  503 
  504         if (sc->sc_base.me_evp == NULL
  505 #if NWSDISPLAY > 0
  506             && sc->sc_displaydv == NULL
  507 #endif
  508             )
  509                 return (EACCES);
  510 
  511         /* Return 0 if any of the ioctl() succeeds, otherwise the last error */
  512         error = 0;
  513         ok = 0;
  514         CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
  515 #ifdef DIAGNOSTIC
  516                 /* XXX check evp? */
  517                 if (me->me_parent != sc) {
  518                         printf("wsmux_do_ioctl: bad child %p\n", me);
  519                         continue;
  520                 }
  521 #endif
  522                 error = wsevsrc_ioctl(me, cmd, data, flag, p);
  523                 DPRINTF(("wsmux_do_ioctl: %s: me=%p dev=%s ==> %d\n",
  524                          sc->sc_base.me_dv.dv_xname, me, me->me_dv.dv_xname,
  525                          error));
  526                 if (!error)
  527                         ok = 1;
  528         }
  529         if (ok) {
  530                 error = 0;
  531                 if (cmd == WSKBDIO_SETENCODING) {
  532                         sc->sc_kbd_layout = *((kbd_t *)data);
  533                 }
  534 
  535         }
  536 
  537         return (error);
  538 }
  539 
  540 /*
  541  * poll() of the pseudo device from device table.
  542  */
  543 int
  544 wsmuxpoll(dev_t dev, int events, struct proc *p)
  545 {
  546         struct wsmux_softc *sc = wsmuxdevs[minor(dev)];
  547 
  548         if (sc->sc_base.me_evp == NULL) {
  549 #ifdef DIAGNOSTIC
  550                 printf("wsmuxpoll: not open\n");
  551 #endif
  552                 return (POLLERR);
  553         }
  554 
  555         return (wsevent_poll(sc->sc_base.me_evp, events, p));
  556 }
  557 
  558 /*
  559  * Add mux unit as a child to muxsc.
  560  */
  561 int
  562 wsmux_add_mux(int unit, struct wsmux_softc *muxsc)
  563 {
  564         struct wsmux_softc *sc, *m;
  565 
  566         sc = wsmux_getmux(unit);
  567         if (sc == NULL)
  568                 return (ENXIO);
  569 
  570         DPRINTF(("wsmux_add_mux: %s(%p) to %s(%p)\n",
  571                  sc->sc_base.me_dv.dv_xname, sc, muxsc->sc_base.me_dv.dv_xname,
  572                  muxsc));
  573 
  574         if (sc->sc_base.me_parent != NULL || sc->sc_base.me_evp != NULL)
  575                 return (EBUSY);
  576 
  577         /* The mux we are adding must not be an ancestor of itself. */
  578         for (m = muxsc; m != NULL ; m = m->sc_base.me_parent)
  579                 if (m == sc)
  580                         return (EINVAL);
  581 
  582         return (wsmux_attach_sc(muxsc, &sc->sc_base));
  583 }
  584 
  585 /* Create a new mux softc. */
  586 struct wsmux_softc *
  587 wsmux_create(const char *name, int unit)
  588 {
  589         struct wsmux_softc *sc;
  590 
  591         DPRINTF(("wsmux_create: allocating\n"));
  592         sc = malloc(sizeof *sc, M_DEVBUF, M_NOWAIT);
  593         if (sc == NULL)
  594                 return (NULL);
  595         bzero(sc, sizeof *sc);
  596         CIRCLEQ_INIT(&sc->sc_cld);
  597         snprintf(sc->sc_base.me_dv.dv_xname, sizeof sc->sc_base.me_dv.dv_xname,
  598                  "%s%d", name, unit);
  599         sc->sc_base.me_dv.dv_unit = unit;
  600         sc->sc_base.me_ops = &wsmux_srcops;
  601         sc->sc_kbd_layout = KB_NONE;
  602         return (sc);
  603 }
  604 
  605 /* Attach me as a child to sc. */
  606 int
  607 wsmux_attach_sc(struct wsmux_softc *sc, struct wsevsrc *me)
  608 {
  609         int error;
  610 
  611         if (sc == NULL)
  612                 return (EINVAL);
  613 
  614         DPRINTF(("wsmux_attach_sc: %s(%p): type=%d\n",
  615                  sc->sc_base.me_dv.dv_xname, sc, me->me_ops->type));
  616 
  617 #ifdef DIAGNOSTIC
  618         if (me->me_parent != NULL) {
  619                 printf("wsmux_attach_sc: busy\n");
  620                 return (EBUSY);
  621         }
  622 #endif
  623         me->me_parent = sc;
  624         CIRCLEQ_INSERT_TAIL(&sc->sc_cld, me, me_next);
  625 
  626         error = 0;
  627 #if NWSDISPLAY > 0
  628         if (sc->sc_displaydv != NULL) {
  629                 /* This is a display mux, so attach the new device to it. */
  630                 DPRINTF(("wsmux_attach_sc: %s: set display %p\n",
  631                          sc->sc_base.me_dv.dv_xname, sc->sc_displaydv));
  632                 if (me->me_ops->dsetdisplay != NULL) {
  633                         error = wsevsrc_set_display(me, sc->sc_displaydv);
  634                         /* Ignore that the console already has a display. */
  635                         if (error == EBUSY)
  636                                 error = 0;
  637                         if (!error) {
  638 #ifdef WSDISPLAY_COMPAT_RAWKBD
  639                                 DPRINTF(("wsmux_attach_sc: %s set rawkbd=%d\n",
  640                                          me->me_dv.dv_xname, sc->sc_rawkbd));
  641                                 (void)wsevsrc_ioctl(me, WSKBDIO_SETMODE,
  642                                                     &sc->sc_rawkbd, FWRITE, 0);
  643 #endif
  644                                 if (sc->sc_kbd_layout != KB_NONE)
  645                                         (void)wsevsrc_ioctl(me,
  646                                             WSKBDIO_SETENCODING,
  647                                             &sc->sc_kbd_layout, FWRITE, 0);
  648                         }
  649                 }
  650         }
  651 #endif
  652         if (sc->sc_base.me_evp != NULL) {
  653                 /* Mux is open, so open the new subdevice */
  654                 DPRINTF(("wsmux_attach_sc: %s: calling open of %s\n",
  655                          sc->sc_base.me_dv.dv_xname, me->me_dv.dv_xname));
  656                 error = wsevsrc_open(me, sc->sc_base.me_evp);
  657         } else {
  658                 DPRINTF(("wsmux_attach_sc: %s not open\n",
  659                          sc->sc_base.me_dv.dv_xname));
  660         }
  661 
  662         if (error) {
  663                 me->me_parent = NULL;
  664                 CIRCLEQ_REMOVE(&sc->sc_cld, me, me_next);
  665         }
  666 
  667         DPRINTF(("wsmux_attach_sc: %s(%p) done, error=%d\n",
  668                  sc->sc_base.me_dv.dv_xname, sc, error));
  669         return (error);
  670 }
  671 
  672 /* Remove me from the parent. */
  673 void
  674 wsmux_detach_sc(struct wsevsrc *me)
  675 {
  676         struct wsmux_softc *sc = me->me_parent;
  677 
  678         DPRINTF(("wsmux_detach_sc: %s(%p) parent=%p\n",
  679                  me->me_dv.dv_xname, me, sc));
  680 
  681 #ifdef DIAGNOSTIC
  682         if (sc == NULL) {
  683                 printf("wsmux_detach_sc: %s has no parent\n",
  684                        me->me_dv.dv_xname);
  685                 return;
  686         }
  687 #endif
  688 
  689 #if NWSDISPLAY > 0
  690         if (sc->sc_displaydv != NULL) {
  691                 if (me->me_ops->dsetdisplay != NULL)
  692                         /* ignore error, there's nothing we can do */
  693                         (void)wsevsrc_set_display(me, NULL);
  694         } else
  695 #endif
  696                 if (me->me_evp != NULL) {
  697                 DPRINTF(("wsmux_detach_sc: close\n"));
  698                 /* mux device is open, so close multiplexee */
  699                 (void)wsevsrc_close(me);
  700         }
  701 
  702         CIRCLEQ_REMOVE(&sc->sc_cld, me, me_next);
  703         me->me_parent = NULL;
  704 
  705         DPRINTF(("wsmux_detach_sc: done sc=%p\n", sc));
  706 }
  707 
  708 /*
  709  * Display ioctl() of a mux via the parent mux.
  710  */
  711 int
  712 wsmux_do_displayioctl(struct device *dv, u_long cmd, caddr_t data, int flag,
  713     struct proc *p)
  714 {
  715         struct wsmux_softc *sc = (struct wsmux_softc *)dv;
  716         struct wsevsrc *me;
  717         int error, ok;
  718 
  719         DPRINTF(("wsmux_displayioctl: %s: sc=%p, cmd=%08lx\n",
  720                  sc->sc_base.me_dv.dv_xname, sc, cmd));
  721 
  722 #ifdef WSDISPLAY_COMPAT_RAWKBD
  723         if (cmd == WSKBDIO_SETMODE) {
  724                 sc->sc_rawkbd = *(int *)data;
  725                 DPRINTF(("wsmux_displayioctl: rawkbd = %d\n", sc->sc_rawkbd));
  726         }
  727 #endif
  728 
  729         /*
  730          * Return 0 if any of the ioctl() succeeds, otherwise the last error.
  731          * Return -1 if no mux component accepts the ioctl.
  732          */
  733         error = -1;
  734         ok = 0;
  735         CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
  736                 DPRINTF(("wsmux_displayioctl: me=%p\n", me));
  737 #ifdef DIAGNOSTIC
  738                 if (me->me_parent != sc) {
  739                         printf("wsmux_displayioctl: bad child %p\n", me);
  740                         continue;
  741                 }
  742 #endif
  743                 if (me->me_ops->ddispioctl != NULL) {
  744                         error = wsevsrc_display_ioctl(me, cmd, data, flag, p);
  745                         DPRINTF(("wsmux_displayioctl: me=%p dev=%s ==> %d\n",
  746                                  me, me->me_dv.dv_xname, error));
  747                         if (!error)
  748                                 ok = 1;
  749                 }
  750         }
  751         if (ok)
  752                 error = 0;
  753 
  754         return (error);
  755 }
  756 
  757 #if NWSDISPLAY > 0
  758 /*
  759  * Set display of a mux via the parent mux.
  760  */
  761 int
  762 wsmux_evsrc_set_display(struct device *dv, struct device *displaydv)
  763 {
  764         struct wsmux_softc *sc = (struct wsmux_softc *)dv;
  765 
  766         DPRINTF(("wsmux_set_display: %s: displaydv=%p\n",
  767                  sc->sc_base.me_dv.dv_xname, displaydv));
  768 
  769         if (displaydv != NULL) {
  770                 if (sc->sc_displaydv != NULL)
  771                         return (EBUSY);
  772         } else {
  773                 if (sc->sc_displaydv == NULL)
  774                         return (ENXIO);
  775         }
  776 
  777         return wsmux_set_display(sc, displaydv);
  778 }
  779 
  780 int
  781 wsmux_set_display(struct wsmux_softc *sc, struct device *displaydv)
  782 {
  783         struct device *odisplaydv;
  784         struct wsevsrc *me;
  785         struct wsmux_softc *nsc = displaydv ? sc : NULL;
  786         int error, ok;
  787 
  788         odisplaydv = sc->sc_displaydv;
  789         sc->sc_displaydv = displaydv;
  790 
  791         if (displaydv) {
  792                 DPRINTF(("%s: connecting to %s\n",
  793                        sc->sc_base.me_dv.dv_xname, displaydv->dv_xname));
  794         }
  795         ok = 0;
  796         error = 0;
  797         CIRCLEQ_FOREACH(me, &sc->sc_cld,me_next) {
  798 #ifdef DIAGNOSTIC
  799                 if (me->me_parent != sc) {
  800                         printf("wsmux_set_display: bad child parent %p\n", me);
  801                         continue;
  802                 }
  803 #endif
  804                 if (me->me_ops->dsetdisplay != NULL) {
  805                         error = wsevsrc_set_display(me, nsc->sc_displaydv);
  806                         DPRINTF(("wsmux_set_display: m=%p dev=%s error=%d\n",
  807                                  me, me->me_dv.dv_xname, error));
  808                         if (!error) {
  809                                 ok = 1;
  810 #ifdef WSDISPLAY_COMPAT_RAWKBD
  811                                 DPRINTF(("wsmux_set_display: %s set rawkbd=%d\n"
  812 ,
  813                                          me->me_dv.dv_xname, sc->sc_rawkbd));
  814                                 (void)wsevsrc_ioctl(me, WSKBDIO_SETMODE,
  815                                                     &sc->sc_rawkbd, FWRITE, 0);
  816 #endif
  817                         }
  818                 }
  819         }
  820         if (ok)
  821                 error = 0;
  822 
  823         if (displaydv == NULL) {
  824                 DPRINTF(("%s: disconnecting from %s\n",
  825                        sc->sc_base.me_dv.dv_xname, odisplaydv->dv_xname));
  826         }
  827 
  828         return (error);
  829 }
  830 #endif /* NWSDISPLAY > 0 */

/* [<][>][^][v][top][bottom][index][help] */