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