Bug Summary

File:dev/wscons/wsmux.c
Warning:line 861, column 2
Value stored to 'odisplaydv' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name wsmux.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model static -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -ffreestanding -mcmodel=kernel -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -target-feature -sse2 -target-feature -sse -target-feature -3dnow -target-feature -mmx -target-feature +save-args -disable-red-zone -no-implicit-float -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/sys/arch/amd64/compile/GENERIC.MP/obj -nostdsysteminc -nobuiltininc -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/sys -I /usr/src/sys/arch/amd64/compile/GENERIC.MP/obj -I /usr/src/sys/arch -I /usr/src/sys/dev/pci/drm/include -I /usr/src/sys/dev/pci/drm/include/uapi -I /usr/src/sys/dev/pci/drm/amd/include/asic_reg -I /usr/src/sys/dev/pci/drm/amd/include -I /usr/src/sys/dev/pci/drm/amd/amdgpu -I /usr/src/sys/dev/pci/drm/amd/display -I /usr/src/sys/dev/pci/drm/amd/display/include -I /usr/src/sys/dev/pci/drm/amd/display/dc -I /usr/src/sys/dev/pci/drm/amd/display/amdgpu_dm -I /usr/src/sys/dev/pci/drm/amd/pm/inc -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/smu11 -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/smu12 -I /usr/src/sys/dev/pci/drm/amd/pm/powerplay -I /usr/src/sys/dev/pci/drm/amd/pm/powerplay/hwmgr -I /usr/src/sys/dev/pci/drm/amd/pm/powerplay/smumgr -I /usr/src/sys/dev/pci/drm/amd/display/dc/inc -I /usr/src/sys/dev/pci/drm/amd/display/dc/inc/hw -I /usr/src/sys/dev/pci/drm/amd/display/dc/clk_mgr -I /usr/src/sys/dev/pci/drm/amd/display/modules/inc -I /usr/src/sys/dev/pci/drm/amd/display/modules/hdcp -I /usr/src/sys/dev/pci/drm/amd/display/dmub/inc -I /usr/src/sys/dev/pci/drm/i915 -D DDB -D DIAGNOSTIC -D KTRACE -D ACCOUNTING -D KMEMSTATS -D PTRACE -D POOL_DEBUG -D CRYPTO -D SYSVMSG -D SYSVSEM -D SYSVSHM -D UVM_SWAP_ENCRYPT -D FFS -D FFS2 -D FFS_SOFTUPDATES -D UFS_DIRHASH -D QUOTA -D EXT2FS -D MFS -D NFSCLIENT -D NFSSERVER -D CD9660 -D UDF -D MSDOSFS -D FIFO -D FUSE -D SOCKET_SPLICE -D TCP_ECN -D TCP_SIGNATURE -D INET6 -D IPSEC -D PPP_BSDCOMP -D PPP_DEFLATE -D PIPEX -D MROUTING -D MPLS -D BOOT_CONFIG -D USER_PCICONF -D APERTURE -D MTRR -D NTFS -D HIBERNATE -D PCIVERBOSE -D USBVERBOSE -D WSDISPLAY_COMPAT_USL -D WSDISPLAY_COMPAT_RAWKBD -D WSDISPLAY_DEFAULTSCREENS=6 -D X86EMU -D ONEWIREVERBOSE -D MULTIPROCESSOR -D MAXUSERS=80 -D _KERNEL -D CONFIG_DRM_AMD_DC_DCN3_0 -O2 -Wno-pointer-sign -Wno-address-of-packed-member -Wno-constant-conversion -Wno-unused-but-set-variable -Wno-gnu-folding-constant -fdebug-compilation-dir=/usr/src/sys/arch/amd64/compile/GENERIC.MP/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -o /usr/obj/sys/arch/amd64/compile/GENERIC.MP/scan-build/2022-01-12-131800-47421-1 -x c /usr/src/sys/dev/wscons/wsmux.c
1/* $OpenBSD: wsmux.c,v 1.55 2021/09/22 15:44:12 anton 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 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include "wsmux.h"
34#include "wsdisplay.h"
35#include "wskbd.h"
36#include "wsmouse.h"
37
38/*
39 * wscons mux device.
40 *
41 * The mux device is a collection of real mice and keyboards and acts as
42 * a merge point for all the events from the different real devices.
43 */
44
45#include <sys/param.h>
46#include <sys/conf.h>
47#include <sys/ioctl.h>
48#include <sys/fcntl.h>
49#include <sys/kernel.h>
50#include <sys/malloc.h>
51#include <sys/proc.h>
52#include <sys/queue.h>
53#include <sys/syslog.h>
54#include <sys/systm.h>
55#include <sys/tty.h>
56#include <sys/signalvar.h>
57#include <sys/device.h>
58#include <sys/poll.h>
59
60#include <dev/wscons/wsconsio.h>
61#include <dev/wscons/wsksymdef.h>
62#include <dev/wscons/wseventvar.h>
63#include <dev/wscons/wsmuxvar.h>
64
65#define WSMUX_MAXDEPTH8 8
66
67#ifdef WSMUX_DEBUG
68#define DPRINTF(x) if (wsmuxdebug) printf x
69#define DPRINTFN(n,x) if (wsmuxdebug > (n)) printf x
70int wsmuxdebug = 0;
71#else
72#define DPRINTF(x)
73#define DPRINTFN(n,x)
74#endif
75
76/*
77 * The wsmux pseudo device is used to multiplex events from several wsmouse,
78 * wskbd, and/or wsmux devices together.
79 * The devices connected together form a tree with muxes in the interior
80 * and real devices (mouse and kbd) at the leaves. The special case of
81 * a tree with one node (mux or other) is supported as well.
82 * Only the device at the root of the tree can be opened (if a non-root
83 * device is opened the subtree rooted at that point is severed from the
84 * containing tree). When the root is opened it allocates a wseventvar
85 * struct which all the nodes in the tree will send their events too.
86 * An ioctl() performed on the root is propagated to all the nodes.
87 * There are also ioctl() operations to add and remove nodes from a tree.
88 */
89
90int wsmux_mux_open(struct wsevsrc *, struct wseventvar *);
91int wsmux_mux_close(struct wsevsrc *);
92
93int wsmux_do_open(struct wsmux_softc *, struct wseventvar *);
94
95void wsmux_do_close(struct wsmux_softc *);
96#if NWSDISPLAY1 > 0
97int wsmux_evsrc_set_display(struct device *, struct device *);
98#else
99#define wsmux_evsrc_set_display NULL((void *)0)
100#endif
101
102int wsmux_do_displayioctl(struct device *dev, u_long cmd, caddr_t data,
103 int flag, struct proc *p);
104int wsmux_do_ioctl(struct device *, u_long, caddr_t,int,struct proc *);
105
106int wsmux_add_mux(int, struct wsmux_softc *);
107
108int wsmux_depth(struct wsmux_softc *);
109
110void wsmuxattach(int);
111
112void wsmux_detach_sc_locked(struct wsmux_softc *, struct wsevsrc *);
113
114struct wssrcops wsmux_srcops = {
115 .type = WSMUX_MUX3,
116 .dopen = wsmux_mux_open,
117 .dclose = wsmux_mux_close,
118 .dioctl = wsmux_do_ioctl,
119 .ddispioctl = wsmux_do_displayioctl,
120 .dsetdisplay = wsmux_evsrc_set_display,
121};
122
123/*
124 * Lock used by wsmux_add_mux() to grant exclusive access to the tree of
125 * stacked wsmux devices.
126 */
127struct rwlock wsmux_tree_lock = RWLOCK_INITIALIZER("wsmuxtreelk"){ 0, "wsmuxtreelk" };
128
129/* From upper level */
130void
131wsmuxattach(int n)
132{
133}
134
135/* Keep track of all muxes that have been allocated */
136int nwsmux = 0;
137struct wsmux_softc **wsmuxdevs = NULL((void *)0);
138
139/* Return mux n, create if necessary */
140struct wsmux_softc *
141wsmux_getmux(int n)
142{
143 struct wsmux_softc *sc;
144 struct wsmux_softc **new, **old;
145 int i;
146
147 if (n >= WSMUX_MAXDEV32)
148 return (NULL((void *)0));
149
150 /* Make sure there is room for mux n in the table */
151 if (n >= nwsmux) {
152 old = wsmuxdevs;
153 new = mallocarray(n + 1, sizeof (*wsmuxdevs),
154 M_DEVBUF2, M_NOWAIT0x0002);
155 if (new == NULL((void *)0)) {
156 printf("wsmux_getmux: no memory for mux %d\n", n);
157 return (NULL((void *)0));
158 }
159 if (old != NULL((void *)0))
160 bcopy(old, new, nwsmux * sizeof(*wsmuxdevs));
161 for (i = nwsmux; i < (n + 1); i++)
162 new[i] = NULL((void *)0);
163 if (old != NULL((void *)0))
164 free(old, M_DEVBUF2, nwsmux * sizeof(*wsmuxdevs));
165 wsmuxdevs = new;
166 nwsmux = n + 1;
167 }
168
169 sc = wsmuxdevs[n];
170 if (sc == NULL((void *)0)) {
171 sc = wsmux_create("wsmux", n);
172 if (sc == NULL((void *)0))
173 printf("wsmux: attach out of memory\n");
174 wsmuxdevs[n] = sc;
175 }
176 return (sc);
177}
178
179/*
180 * open() of the pseudo device from device table.
181 */
182int
183wsmuxopen(dev_t dev, int flags, int mode, struct proc *p)
184{
185 struct wsmux_softc *sc;
186 struct wseventvar *evar;
187 int error, unit;
188
189 unit = minor(dev)((unsigned)((dev) & 0xff) | (((dev) & 0xffff0000) >>
8))
;
190 sc = wsmux_getmux(unit);
191 if (sc == NULL((void *)0))
192 return (ENXIO6);
193
194 DPRINTF(("%s: %s: sc=%p\n", __func__, sc->sc_base.me_dv.dv_xname, sc));
195
196 if ((flags & (FREAD0x0001 | FWRITE0x0002)) == FWRITE0x0002) {
197 /* Not opening for read, only ioctl is available. */
198 return (0);
199 }
200
201 if (sc->sc_base.me_parent != NULL((void *)0)) {
202 /* Grab the mux out of the greedy hands of the parent mux. */
203 DPRINTF(("%s: detach\n", __func__));
204 wsmux_detach_sc(&sc->sc_base);
205 }
206
207 if (sc->sc_base.me_evp != NULL((void *)0))
208 /* Already open. */
209 return (EBUSY16);
210
211 evar = &sc->sc_base.me_evar;
212 if (wsevent_init(evar))
213 return (EBUSY16);
214#ifdef WSDISPLAY_COMPAT_RAWKBD1
215 sc->sc_rawkbd = 0;
216#endif
217
218 error = wsmux_do_open(sc, evar);
219 if (error)
220 wsevent_fini(evar);
221 return (error);
222}
223
224/*
225 * Open of a mux via the parent mux.
226 */
227int
228wsmux_mux_open(struct wsevsrc *me, struct wseventvar *evar)
229{
230 struct wsmux_softc *sc = (struct wsmux_softc *)me;
231
232#ifdef DIAGNOSTIC1
233 if (sc->sc_base.me_parent == NULL((void *)0)) {
234 printf("wsmux_mux_open: no parent\n");
235 return (EINVAL22);
236 }
237#endif
238
239 return (wsmux_do_open(sc, evar));
240}
241
242/* Common part of opening a mux. */
243int
244wsmux_do_open(struct wsmux_softc *sc, struct wseventvar *evar)
245{
246 struct wsevsrc *me;
247#ifdef DIAGNOSTIC1
248 int error;
249#endif
250
251 /* The device could already be attached to a mux. */
252 if (sc->sc_base.me_evp != NULL((void *)0))
253 return (EBUSY16);
254 sc->sc_base.me_evp = evar; /* remember event variable, mark as open */
255
256 /* Open all children. */
257 rw_enter_read(&sc->sc_lock);
258 TAILQ_FOREACH(me, &sc->sc_cld, me_next)for((me) = ((&sc->sc_cld)->tqh_first); (me) != ((void
*)0); (me) = ((me)->me_next.tqe_next))
{
259 DPRINTF(("%s: %s: m=%p dev=%s\n", __func__,
260 sc->sc_base.me_dv.dv_xname, me,
261 me->me_dv.dv_xname));
262#ifdef DIAGNOSTIC1
263 if (me->me_evp != NULL((void *)0)) {
264 printf("wsmuxopen: dev already in use\n");
265 continue;
266 }
267 if (me->me_parent != sc) {
268 printf("wsmux_do_open: bad child=%p\n", me);
269 continue;
270 }
271 error = wsevsrc_open(me, evar)((me)->me_ops->dopen((me), evar));
272 if (error) {
273 DPRINTF(("%s: open failed %d\n", __func__, error));
274 }
275#else
276 /* ignore errors, failing children will not be marked open */
277 (void)wsevsrc_open(me, evar)((me)->me_ops->dopen((me), evar));
278#endif
279 }
280 rw_exit_read(&sc->sc_lock);
281
282 return (0);
283}
284
285/*
286 * close() of the pseudo device from device table.
287 */
288int
289wsmuxclose(dev_t dev, int flags, int mode, struct proc *p)
290{
291 struct wsmux_softc *sc =
292 (struct wsmux_softc *)wsmuxdevs[minor(dev)((unsigned)((dev) & 0xff) | (((dev) & 0xffff0000) >>
8))
];
293 struct wseventvar *evar = sc->sc_base.me_evp;
294
295 if ((flags & (FREAD0x0001 | FWRITE0x0002)) == FWRITE0x0002)
296 /* Not open for read */
297 return (0);
298
299 wsmux_do_close(sc);
300 sc->sc_base.me_evp = NULL((void *)0);
301 wsevent_fini(evar);
302 return (0);
303}
304
305/*
306 * Close of a mux via the parent mux.
307 */
308int
309wsmux_mux_close(struct wsevsrc *me)
310{
311 struct wsmux_softc *sc = (struct wsmux_softc *)me;
312
313 wsmux_do_close(sc);
314 sc->sc_base.me_evp = NULL((void *)0);
315
316 return (0);
317}
318
319/* Common part of closing a mux. */
320void
321wsmux_do_close(struct wsmux_softc *sc)
322{
323 struct wsevsrc *me;
324
325 DPRINTF(("%s: %s: sc=%p\n", __func__, sc->sc_base.me_dv.dv_xname, sc));
326
327 /* Close all the children. */
328 rw_enter_read(&sc->sc_lock);
329 TAILQ_FOREACH(me, &sc->sc_cld, me_next)for((me) = ((&sc->sc_cld)->tqh_first); (me) != ((void
*)0); (me) = ((me)->me_next.tqe_next))
{
330 DPRINTF(("%s %s: m=%p dev=%s\n", __func__,
331 sc->sc_base.me_dv.dv_xname, me, me->me_dv.dv_xname));
332#ifdef DIAGNOSTIC1
333 if (me->me_parent != sc) {
334 printf("wsmuxclose: bad child=%p\n", me);
335 continue;
336 }
337#endif
338 (void)wsevsrc_close(me)((me)->me_ops->dclose((me)));
339 }
340 rw_exit_read(&sc->sc_lock);
341}
342
343/*
344 * read() of the pseudo device from device table.
345 */
346int
347wsmuxread(dev_t dev, struct uio *uio, int flags)
348{
349 struct wsmux_softc *sc = wsmuxdevs[minor(dev)((unsigned)((dev) & 0xff) | (((dev) & 0xffff0000) >>
8))
];
350 struct wseventvar *evar;
351 int error;
352
353 evar = sc->sc_base.me_evp;
354 if (evar == NULL((void *)0)) {
355#ifdef DIAGNOSTIC1
356 /* XXX can we get here? */
357 printf("wsmuxread: not open\n");
358#endif
359 return (EINVAL22);
360 }
361
362 DPRINTFN(5, ("%s: %s event read evar=%p\n", __func__,
363 sc->sc_base.me_dv.dv_xname, evar));
364 error = wsevent_read(evar, uio, flags);
365 DPRINTFN(5, ("%s: %s event read ==> error=%d\n", __func__,
366 sc->sc_base.me_dv.dv_xname, error));
367 return (error);
368}
369
370/*
371 * ioctl of the pseudo device from device table.
372 */
373int
374wsmuxioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
375{
376 return wsmux_do_ioctl(&wsmuxdevs[minor(dev)((unsigned)((dev) & 0xff) | (((dev) & 0xffff0000) >>
8))
]->sc_base.me_dv, cmd, data, flag, p);
377}
378
379/*
380 * ioctl of a mux via the parent mux, continuation of wsmuxioctl().
381 */
382int
383wsmux_do_ioctl(struct device *dv, u_long cmd, caddr_t data, int flag,
384 struct proc *p)
385{
386 struct wsmux_softc *sc = (struct wsmux_softc *)dv;
387 struct wsevsrc *me;
388 int error, ok;
389 int s, put, get, n;
390 struct wseventvar *evar;
391 struct wscons_event *ev;
392 struct wsmux_device_list *l;
393
394 DPRINTF(("%s: %s: enter sc=%p, cmd=%08lx\n", __func__,
395 sc->sc_base.me_dv.dv_xname, sc, cmd));
396
397 switch (cmd) {
398 case WSMUXIO_INJECTEVENT((unsigned long)0x80000000 | ((sizeof(struct wscons_event) &
0x1fff) << 16) | ((('W')) << 8) | ((96)))
:
399 case WSMUXIO_ADD_DEVICE((unsigned long)0x80000000 | ((sizeof(struct wsmux_device) &
0x1fff) << 16) | ((('W')) << 8) | ((97)))
:
400 case WSMUXIO_REMOVE_DEVICE((unsigned long)0x80000000 | ((sizeof(struct wsmux_device) &
0x1fff) << 16) | ((('W')) << 8) | ((98)))
:
401#ifdef WSDISPLAY_COMPAT_RAWKBD1
402 case WSKBDIO_SETMODE((unsigned long)0x80000000 | ((sizeof(int) & 0x1fff) <<
16) | ((('W')) << 8) | ((19)))
:
403#endif
404 if ((flag & FWRITE0x0002) == 0)
405 return (EACCES13);
406 }
407
408 switch (cmd) {
409 case WSMUXIO_INJECTEVENT((unsigned long)0x80000000 | ((sizeof(struct wscons_event) &
0x1fff) << 16) | ((('W')) << 8) | ((96)))
:
410 /* Inject an event, e.g., from moused. */
411 DPRINTF(("%s: inject\n", sc->sc_base.me_dv.dv_xname));
412 evar = sc->sc_base.me_evp;
413 if (evar == NULL((void *)0)) {
414 /* No event sink, so ignore it. */
415 DPRINTF(("%s: event ignored\n", __func__));
416 return (0);
417 }
418
419 s = spltty()splraise(0x9);
420 get = evar->get;
421 put = evar->put;
422 ev = &evar->q[put];
423 if (++put % WSEVENT_QSIZE256 == get) {
424 put--;
425 splx(s)spllower(s);
426 return (ENOSPC28);
427 }
428 if (put >= WSEVENT_QSIZE256)
429 put = 0;
430 *ev = *(struct wscons_event *)data;
431 nanotime(&ev->time);
432 evar->put = put;
433 WSEVENT_WAKEUP(evar){ selwakeup(&(evar)->sel); if ((evar)->wanted) { (evar
)->wanted = 0; wakeup((caddr_t)(evar)); } if ((evar)->async
) pgsigio(&(evar)->sigio, 23, 0); }
;
434 splx(s)spllower(s);
435 return (0);
436 case WSMUXIO_ADD_DEVICE((unsigned long)0x80000000 | ((sizeof(struct wsmux_device) &
0x1fff) << 16) | ((('W')) << 8) | ((97)))
:
437#define d ((struct wsmux_device *)data)
438 DPRINTF(("%s: add type=%d, no=%d\n", sc->sc_base.me_dv.dv_xname,
439 d->type, d->idx));
440 if (d->idx < 0)
441 return (ENXIO6);
442 switch (d->type) {
443#if NWSMOUSE1 > 0
444 case WSMUX_MOUSE1:
445 return (wsmouse_add_mux(d->idx, sc));
446#endif
447#if NWSKBD1 > 0
448 case WSMUX_KBD2:
449 return (wskbd_add_mux(d->idx, sc));
450#endif
451 case WSMUX_MUX3:
452 return (wsmux_add_mux(d->idx, sc));
453 default:
454 return (EINVAL22);
455 }
456 case WSMUXIO_REMOVE_DEVICE((unsigned long)0x80000000 | ((sizeof(struct wsmux_device) &
0x1fff) << 16) | ((('W')) << 8) | ((98)))
:
457 DPRINTF(("%s: rem type=%d, no=%d\n", sc->sc_base.me_dv.dv_xname,
458 d->type, d->idx));
459 /* Locate the device */
460 rw_enter_write(&sc->sc_lock);
461 TAILQ_FOREACH(me, &sc->sc_cld, me_next)for((me) = ((&sc->sc_cld)->tqh_first); (me) != ((void
*)0); (me) = ((me)->me_next.tqe_next))
{
462 if (me->me_ops->type == d->type &&
463 me->me_dv.dv_unit == d->idx) {
464 DPRINTF(("%s: detach\n", __func__));
465 wsmux_detach_sc_locked(sc, me);
466 rw_exit_write(&sc->sc_lock);
467 return (0);
468 }
469 }
470 rw_exit_write(&sc->sc_lock);
471 return (EINVAL22);
472#undef d
473
474 case WSMUXIO_LIST_DEVICES(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof
(struct wsmux_device_list) & 0x1fff) << 16) | ((('W'
)) << 8) | ((99)))
:
475 DPRINTF(("%s: list\n", sc->sc_base.me_dv.dv_xname));
476 l = (struct wsmux_device_list *)data;
477 n = 0;
478 rw_enter_read(&sc->sc_lock);
479 TAILQ_FOREACH(me, &sc->sc_cld, me_next)for((me) = ((&sc->sc_cld)->tqh_first); (me) != ((void
*)0); (me) = ((me)->me_next.tqe_next))
{
480 if (n >= WSMUX_MAXDEV32)
481 break;
482 l->devices[n].type = me->me_ops->type;
483 l->devices[n].idx = me->me_dv.dv_unit;
484 n++;
485 }
486 rw_exit_read(&sc->sc_lock);
487 l->ndevices = n;
488 return (0);
489#ifdef WSDISPLAY_COMPAT_RAWKBD1
490 case WSKBDIO_SETMODE((unsigned long)0x80000000 | ((sizeof(int) & 0x1fff) <<
16) | ((('W')) << 8) | ((19)))
:
491 sc->sc_rawkbd = *(int *)data;
492 DPRINTF(("%s: save rawkbd = %d\n", __func__, sc->sc_rawkbd));
493 break;
494#endif
495 case FIONBIO((unsigned long)0x80000000 | ((sizeof(int) & 0x1fff) <<
16) | ((('f')) << 8) | ((126)))
:
496 DPRINTF(("%s: FIONBIO\n", sc->sc_base.me_dv.dv_xname));
497 return (0);
498
499 case FIOASYNC((unsigned long)0x80000000 | ((sizeof(int) & 0x1fff) <<
16) | ((('f')) << 8) | ((125)))
:
500 DPRINTF(("%s: FIOASYNC\n", sc->sc_base.me_dv.dv_xname));
501 evar = sc->sc_base.me_evp;
502 if (evar == NULL((void *)0))
503 return (EINVAL22);
504 evar->async = *(int *)data != 0;
505 return (0);
506 case FIOGETOWN((unsigned long)0x40000000 | ((sizeof(int) & 0x1fff) <<
16) | ((('f')) << 8) | ((123)))
:
507 case TIOCGPGRP((unsigned long)0x40000000 | ((sizeof(int) & 0x1fff) <<
16) | ((('t')) << 8) | ((119)))
:
508 DPRINTF(("%s: getown (%lu)\n", sc->sc_base.me_dv.dv_xname,
509 cmd));
510 evar = sc->sc_base.me_evp;
511 if (evar == NULL((void *)0))
512 return (EINVAL22);
513 sigio_getown(&evar->sigio, cmd, data);
514 return (0);
515 case FIOSETOWN((unsigned long)0x80000000 | ((sizeof(int) & 0x1fff) <<
16) | ((('f')) << 8) | ((124)))
:
516 case TIOCSPGRP((unsigned long)0x80000000 | ((sizeof(int) & 0x1fff) <<
16) | ((('t')) << 8) | ((118)))
:
517 DPRINTF(("%s: setown (%lu)\n", sc->sc_base.me_dv.dv_xname,
518 cmd));
519 evar = sc->sc_base.me_evp;
520 if (evar == NULL((void *)0))
521 return (EINVAL22);
522 return (sigio_setown(&evar->sigio, cmd, data));
523 default:
524 DPRINTF(("%s: unknown\n", sc->sc_base.me_dv.dv_xname));
525 break;
526 }
527
528 if (sc->sc_base.me_evp == NULL((void *)0)
529#if NWSDISPLAY1 > 0
530 && sc->sc_displaydvsc_base.me_dispdv == NULL((void *)0)
531#endif
532 )
533 return (EACCES13);
534
535 /*
536 * If children are attached: return 0 if any of the ioctl() succeeds,
537 * otherwise the last error.
538 */
539 error = ENOTTY25;
540 ok = 0;
541 rw_enter_read(&sc->sc_lock);
542 TAILQ_FOREACH(me, &sc->sc_cld, me_next)for((me) = ((&sc->sc_cld)->tqh_first); (me) != ((void
*)0); (me) = ((me)->me_next.tqe_next))
{
543#ifdef DIAGNOSTIC1
544 /* XXX check evp? */
545 if (me->me_parent != sc) {
546 printf("wsmux_do_ioctl: bad child %p\n", me);
547 continue;
548 }
549#endif
550 error = wsevsrc_ioctl(me, cmd, data, flag, p)((me)->me_ops->dioctl(&(me)->me_dv, cmd, (caddr_t
)data, flag, p))
;
551 DPRINTF(("%s: %s: me=%p dev=%s ==> %d\n", __func__,
552 sc->sc_base.me_dv.dv_xname, me, me->me_dv.dv_xname,
553 error));
554 if (!error)
555 ok = 1;
556 }
557 rw_exit_read(&sc->sc_lock);
558 if (ok)
559 error = 0;
560
561 return (error);
562}
563
564/*
565 * poll() of the pseudo device from device table.
566 */
567int
568wsmuxpoll(dev_t dev, int events, struct proc *p)
569{
570 struct wsmux_softc *sc = wsmuxdevs[minor(dev)((unsigned)((dev) & 0xff) | (((dev) & 0xffff0000) >>
8))
];
571
572 if (sc->sc_base.me_evp == NULL((void *)0)) {
573#ifdef DIAGNOSTIC1
574 printf("wsmuxpoll: not open\n");
575#endif
576 return (POLLERR0x0008);
577 }
578
579 return (wsevent_poll(sc->sc_base.me_evp, events, p));
580}
581
582int
583wsmuxkqfilter(dev_t dev, struct knote *kn)
584{
585 struct wsmux_softc *sc = wsmuxdevs[minor(dev)((unsigned)((dev) & 0xff) | (((dev) & 0xffff0000) >>
8))
];
586
587 if (sc->sc_base.me_evp == NULL((void *)0))
588 return (ENXIO6);
589 return (wsevent_kqfilter(sc->sc_base.me_evp, kn));
590}
591
592/*
593 * Add mux unit as a child to muxsc.
594 */
595int
596wsmux_add_mux(int unit, struct wsmux_softc *muxsc)
597{
598 struct wsmux_softc *sc, *m;
599 int error;
600 int depth = 0;
601
602 sc = wsmux_getmux(unit);
603 if (sc == NULL((void *)0))
604 return (ENXIO6);
605
606 rw_enter_write(&wsmux_tree_lock);
607
608 DPRINTF(("%s: %s(%p) to %s(%p)\n", __func__,
609 sc->sc_base.me_dv.dv_xname, sc,
610 muxsc->sc_base.me_dv.dv_xname, muxsc));
611
612 if (sc->sc_base.me_parent != NULL((void *)0) || sc->sc_base.me_evp != NULL((void *)0)) {
613 error = EBUSY16;
614 goto out;
615 }
616
617 /* The mux we are adding must not be an ancestor of itself. */
618 for (m = muxsc; m != NULL((void *)0); m = m->sc_base.me_parent) {
619 if (m == sc) {
620 error = EINVAL22;
621 goto out;
622 }
623 depth++;
624 }
625
626 /*
627 * Limit the number of stacked wsmux devices to avoid exhausting
628 * the kernel stack during wsmux_do_open().
629 */
630 if (depth + wsmux_depth(sc) > WSMUX_MAXDEPTH8) {
631 error = EBUSY16;
632 goto out;
633 }
634
635 error = wsmux_attach_sc(muxsc, &sc->sc_base);
636out:
637 rw_exit_write(&wsmux_tree_lock);
638 return (error);
639}
640
641/* Create a new mux softc. */
642struct wsmux_softc *
643wsmux_create(const char *name, int unit)
644{
645 struct wsmux_softc *sc;
646
647 DPRINTF(("%s: allocating\n", __func__));
648 sc = malloc(sizeof *sc, M_DEVBUF2, M_NOWAIT0x0002 | M_ZERO0x0008);
649 if (sc == NULL((void *)0))
650 return (NULL((void *)0));
651 TAILQ_INIT(&sc->sc_cld)do { (&sc->sc_cld)->tqh_first = ((void *)0); (&
sc->sc_cld)->tqh_last = &(&sc->sc_cld)->tqh_first
; } while (0)
;
652 rw_init_flags(&sc->sc_lock, "wsmuxlk", RWL_DUPOK)_rw_init_flags(&sc->sc_lock, "wsmuxlk", 0x01, ((void *
)0))
;
653 snprintf(sc->sc_base.me_dv.dv_xname, sizeof sc->sc_base.me_dv.dv_xname,
654 "%s%d", name, unit);
655 sc->sc_base.me_dv.dv_unit = unit;
656 sc->sc_base.me_ops = &wsmux_srcops;
657 sc->sc_kbd_layout = KB_NONE0x0000;
658 return (sc);
659}
660
661/* Attach me as a child to sc. */
662int
663wsmux_attach_sc(struct wsmux_softc *sc, struct wsevsrc *me)
664{
665 int error;
666
667 if (sc == NULL((void *)0))
668 return (EINVAL22);
669
670 rw_enter_write(&sc->sc_lock);
671
672 DPRINTF(("%s: %s(%p): type=%d\n", __func__,
673 sc->sc_base.me_dv.dv_xname, sc, me->me_ops->type));
674
675#ifdef DIAGNOSTIC1
676 if (me->me_parent != NULL((void *)0)) {
677 rw_exit_write(&sc->sc_lock);
678 printf("wsmux_attach_sc: busy\n");
679 return (EBUSY16);
680 }
681#endif
682 me->me_parent = sc;
683 TAILQ_INSERT_TAIL(&sc->sc_cld, me, me_next)do { (me)->me_next.tqe_next = ((void *)0); (me)->me_next
.tqe_prev = (&sc->sc_cld)->tqh_last; *(&sc->
sc_cld)->tqh_last = (me); (&sc->sc_cld)->tqh_last
= &(me)->me_next.tqe_next; } while (0)
;
684
685 error = 0;
686#if NWSDISPLAY1 > 0
687 if (sc->sc_displaydvsc_base.me_dispdv != NULL((void *)0)) {
688 /* This is a display mux, so attach the new device to it. */
689 DPRINTF(("%s: %s: set display %p\n", __func__,
690 sc->sc_base.me_dv.dv_xname, sc->sc_displaydv));
691 if (me->me_ops->dsetdisplay != NULL((void *)0)) {
692 error = wsevsrc_set_display(me, sc->sc_displaydv)((me)->me_ops->dsetdisplay(&(me)->me_dv, sc->
sc_base.me_dispdv))
;
693 /* Ignore that the console already has a display. */
694 if (error == EBUSY16)
695 error = 0;
696 if (!error) {
697#ifdef WSDISPLAY_COMPAT_RAWKBD1
698 DPRINTF(("%s: %s set rawkbd=%d\n", __func__,
699 me->me_dv.dv_xname, sc->sc_rawkbd));
700 (void)wsevsrc_ioctl(me, WSKBDIO_SETMODE,((me)->me_ops->dioctl(&(me)->me_dv, ((unsigned long
)0x80000000 | ((sizeof(int) & 0x1fff) << 16) | ((('W'
)) << 8) | ((19))), (caddr_t)&sc->sc_rawkbd, 0x0002
, 0))
701 &sc->sc_rawkbd, FWRITE, 0)((me)->me_ops->dioctl(&(me)->me_dv, ((unsigned long
)0x80000000 | ((sizeof(int) & 0x1fff) << 16) | ((('W'
)) << 8) | ((19))), (caddr_t)&sc->sc_rawkbd, 0x0002
, 0))
;
702#endif
703 }
704 }
705 }
706#endif
707 if (sc->sc_base.me_evp != NULL((void *)0)) {
708 /* Mux is open, try to open the subdevice. */
709 error = wsevsrc_open(me, sc->sc_base.me_evp)((me)->me_ops->dopen((me), sc->sc_base.me_evp));
710 } else {
711 /* Mux is closed, ensure that the subdevice is also closed. */
712 if (me->me_evp != NULL((void *)0))
713 error = EBUSY16;
714 }
715
716 if (error) {
717 me->me_parent = NULL((void *)0);
718 TAILQ_REMOVE(&sc->sc_cld, me, me_next)do { if (((me)->me_next.tqe_next) != ((void *)0)) (me)->
me_next.tqe_next->me_next.tqe_prev = (me)->me_next.tqe_prev
; else (&sc->sc_cld)->tqh_last = (me)->me_next.tqe_prev
; *(me)->me_next.tqe_prev = (me)->me_next.tqe_next; ((me
)->me_next.tqe_prev) = ((void *)-1); ((me)->me_next.tqe_next
) = ((void *)-1); } while (0)
;
719 }
720
721 rw_exit_write(&sc->sc_lock);
722
723 DPRINTF(("%s: %s(%p) done, error=%d\n", __func__,
724 sc->sc_base.me_dv.dv_xname, sc, error));
725 return (error);
726}
727
728/* Remove me from the parent. */
729void
730wsmux_detach_sc(struct wsevsrc *me)
731{
732 struct wsmux_softc *sc = me->me_parent;
733
734 if (sc == NULL((void *)0)) {
735 printf("wsmux_detach_sc: %s has no parent\n",
736 me->me_dv.dv_xname);
737 return;
738 }
739
740 rw_enter_write(&sc->sc_lock);
741 wsmux_detach_sc_locked(sc, me);
742 rw_exit_write(&sc->sc_lock);
743}
744
745void
746wsmux_detach_sc_locked(struct wsmux_softc *sc, struct wsevsrc *me)
747{
748 rw_assert_wrlock(&sc->sc_lock);
749
750 DPRINTF(("%s: %s(%p) parent=%p\n", __func__,
751 me->me_dv.dv_xname, me, sc));
752
753 if (me->me_parent != sc) {
754 /* Device detached or attached to another mux while sleeping. */
755 return;
756 }
757
758#if NWSDISPLAY1 > 0
759 if (sc->sc_displaydvsc_base.me_dispdv != NULL((void *)0)) {
760 if (me->me_ops->dsetdisplay != NULL((void *)0))
761 /* ignore error, there's nothing we can do */
762 (void)wsevsrc_set_display(me, NULL)((me)->me_ops->dsetdisplay(&(me)->me_dv, ((void *
)0)))
;
763 } else
764#endif
765 if (me->me_evp != NULL((void *)0)) {
766 DPRINTF(("%s: close\n", __func__));
767 /* mux device is open, so close multiplexee */
768 (void)wsevsrc_close(me)((me)->me_ops->dclose((me)));
769 }
770
771 TAILQ_REMOVE(&sc->sc_cld, me, me_next)do { if (((me)->me_next.tqe_next) != ((void *)0)) (me)->
me_next.tqe_next->me_next.tqe_prev = (me)->me_next.tqe_prev
; else (&sc->sc_cld)->tqh_last = (me)->me_next.tqe_prev
; *(me)->me_next.tqe_prev = (me)->me_next.tqe_next; ((me
)->me_next.tqe_prev) = ((void *)-1); ((me)->me_next.tqe_next
) = ((void *)-1); } while (0)
;
772 me->me_parent = NULL((void *)0);
773
774 DPRINTF(("%s: done sc=%p\n", __func__, sc));
775}
776
777/*
778 * Display ioctl() of a mux via the parent mux.
779 */
780int
781wsmux_do_displayioctl(struct device *dv, u_long cmd, caddr_t data, int flag,
782 struct proc *p)
783{
784 struct wsmux_softc *sc = (struct wsmux_softc *)dv;
785 struct wsevsrc *me;
786 int error, ok;
787
788 DPRINTF(("%s: %s: sc=%p, cmd=%08lx\n", __func__,
789 sc->sc_base.me_dv.dv_xname, sc, cmd));
790
791#ifdef WSDISPLAY_COMPAT_RAWKBD1
792 if (cmd == WSKBDIO_SETMODE((unsigned long)0x80000000 | ((sizeof(int) & 0x1fff) <<
16) | ((('W')) << 8) | ((19)))
) {
793 sc->sc_rawkbd = *(int *)data;
794 DPRINTF(("%s: rawkbd = %d\n", __func__, sc->sc_rawkbd));
795 }
796#endif
797
798 /*
799 * Return 0 if any of the ioctl() succeeds, otherwise the last error.
800 * Return -1 if no mux component accepts the ioctl.
801 */
802 error = -1;
803 ok = 0;
804 rw_enter_read(&sc->sc_lock);
805 TAILQ_FOREACH(me, &sc->sc_cld, me_next)for((me) = ((&sc->sc_cld)->tqh_first); (me) != ((void
*)0); (me) = ((me)->me_next.tqe_next))
{
806 DPRINTF(("%s: me=%p\n", __func__, me));
807#ifdef DIAGNOSTIC1
808 if (me->me_parent != sc) {
809 printf("wsmux_displayioctl: bad child %p\n", me);
810 continue;
811 }
812#endif
813 if (me->me_ops->ddispioctl != NULL((void *)0)) {
814 error = wsevsrc_display_ioctl(me, cmd, data, flag, p)((me)->me_ops->ddispioctl(&(me)->me_dv, cmd, (caddr_t
)data, flag, p))
;
815 DPRINTF(("%s: me=%p dev=%s ==> %d\n", __func__,
816 me, me->me_dv.dv_xname, error));
817 if (!error)
818 ok = 1;
819 }
820 }
821 rw_exit_read(&sc->sc_lock);
822 if (ok)
823 error = 0;
824
825 return (error);
826}
827
828#if NWSDISPLAY1 > 0
829/*
830 * Set display of a mux via the parent mux.
831 */
832int
833wsmux_evsrc_set_display(struct device *dv, struct device *displaydv)
834{
835 struct wsmux_softc *sc = (struct wsmux_softc *)dv;
836
837 DPRINTF(("%s: %s: displaydv=%p\n", __func__,
838 sc->sc_base.me_dv.dv_xname, displaydv));
839
840 if (displaydv != NULL((void *)0)) {
841 if (sc->sc_displaydvsc_base.me_dispdv != NULL((void *)0))
842 return (EBUSY16);
843 } else {
844 if (sc->sc_displaydvsc_base.me_dispdv == NULL((void *)0))
845 return (ENXIO6);
846 }
847
848 return wsmux_set_display(sc, displaydv);
849}
850
851int
852wsmux_set_display(struct wsmux_softc *sc, struct device *displaydv)
853{
854 struct device *odisplaydv;
855 struct wsevsrc *me;
856 struct wsmux_softc *nsc = displaydv ? sc : NULL((void *)0);
857 int error, ok;
858
859 rw_enter_read(&sc->sc_lock);
860
861 odisplaydv = sc->sc_displaydvsc_base.me_dispdv;
Value stored to 'odisplaydv' is never read
862 sc->sc_displaydvsc_base.me_dispdv = displaydv;
863
864 if (displaydv) {
865 DPRINTF(("%s: connecting to %s\n",
866 sc->sc_base.me_dv.dv_xname, displaydv->dv_xname));
867 }
868 ok = 0;
869 error = 0;
870 TAILQ_FOREACH(me, &sc->sc_cld, me_next)for((me) = ((&sc->sc_cld)->tqh_first); (me) != ((void
*)0); (me) = ((me)->me_next.tqe_next))
{
871#ifdef DIAGNOSTIC1
872 if (me->me_parent != sc) {
873 printf("wsmux_set_display: bad child parent %p\n", me);
874 continue;
875 }
876#endif
877 if (me->me_ops->dsetdisplay != NULL((void *)0)) {
878 error = wsevsrc_set_display(me,((me)->me_ops->dsetdisplay(&(me)->me_dv, nsc ? nsc
->sc_base.me_dispdv : ((void *)0)))
879 nsc ? nsc->sc_displaydv : NULL)((me)->me_ops->dsetdisplay(&(me)->me_dv, nsc ? nsc
->sc_base.me_dispdv : ((void *)0)))
;
880 DPRINTF(("%s: m=%p dev=%s error=%d\n", __func__,
881 me, me->me_dv.dv_xname, error));
882 if (!error) {
883 ok = 1;
884#ifdef WSDISPLAY_COMPAT_RAWKBD1
885 DPRINTF(("%s: %s set rawkbd=%d\n", __func__,
886 me->me_dv.dv_xname, sc->sc_rawkbd));
887 (void)wsevsrc_ioctl(me, WSKBDIO_SETMODE,((me)->me_ops->dioctl(&(me)->me_dv, ((unsigned long
)0x80000000 | ((sizeof(int) & 0x1fff) << 16) | ((('W'
)) << 8) | ((19))), (caddr_t)&sc->sc_rawkbd, 0x0002
, 0))
888 &sc->sc_rawkbd, FWRITE, 0)((me)->me_ops->dioctl(&(me)->me_dv, ((unsigned long
)0x80000000 | ((sizeof(int) & 0x1fff) << 16) | ((('W'
)) << 8) | ((19))), (caddr_t)&sc->sc_rawkbd, 0x0002
, 0))
;
889#endif
890 }
891 }
892 }
893 if (ok)
894 error = 0;
895
896 if (displaydv == NULL((void *)0)) {
897 DPRINTF(("%s: disconnecting from %s\n",
898 sc->sc_base.me_dv.dv_xname, odisplaydv->dv_xname));
899 }
900
901 rw_exit_read(&sc->sc_lock);
902
903 return (error);
904}
905#endif /* NWSDISPLAY > 0 */
906
907uint32_t
908wsmux_get_layout(struct wsmux_softc *sc)
909{
910 return sc->sc_kbd_layout;
911}
912
913void
914wsmux_set_layout(struct wsmux_softc *sc, uint32_t layout)
915{
916 if ((layout & (KB_DEFAULT0x80000000 | KB_NOENCODING0x00000080)) == 0)
917 sc->sc_kbd_layout = layout;
918}
919
920/*
921 * Returns the depth of the longest chain of nested wsmux devices starting
922 * from sc.
923 */
924int
925wsmux_depth(struct wsmux_softc *sc)
926{
927 struct wsevsrc *me;
928 int depth;
929 int maxdepth = 0;
930
931 rw_assert_anylock(&wsmux_tree_lock);
932
933 rw_enter_read(&sc->sc_lock);
934 TAILQ_FOREACH(me, &sc->sc_cld, me_next)for((me) = ((&sc->sc_cld)->tqh_first); (me) != ((void
*)0); (me) = ((me)->me_next.tqe_next))
{
935 if (me->me_ops->type != WSMUX_MUX3)
936 continue;
937
938 depth = wsmux_depth((struct wsmux_softc *)me);
939 if (depth > maxdepth)
940 maxdepth = depth;
941 }
942 rw_exit_read(&sc->sc_lock);
943
944 return (maxdepth + 1);
945}