# HG changeset patch # User Taylor R Campbell # Date 1780438380 0 # Tue Jun 02 22:13:00 2026 +0000 # Branch trunk # Node ID 19f84c00a20aebdc704283a733b2b36f5639a3d7 # Parent eae8bd032315361e524ccfee3ae72424743f062e # EXP-Topic riastradh-20260602-uhidevrefactor uhidev: Redo the uhidev(9) abstraction. This partially reverts jmcneill's December changes so there is no net ABI change when applied on top of them: drivers that attach at the uhidbus iattr and use uhidev_* continue to work unmodified. As a result, the net change is safe to pull up to netbsd-11. Net changes from netbsd-11: - Factor out the implementation of the _existing_ `struct uhidev' abstraction so non-USB buses can use exactly the same drivers written in terms of the abstraction. No need to invent a new `struct hidev_tag' for an abstraction that is already there to serve exactly this purpose. - Move the USB-specific ioctls out of uhid(4) and into the USB implementation of uhidev(9) via new kernel symbol uhidev_ioctl. The use of the name `uhidev' for non-USB buses is confusing, of course. So a subsequent change can rename uhidev(9) to hidev(9), in an ABI break that can't be pulled up to netbsd-11 (at least, not without some careful aliases). Gross changes from current: - Restore the original layout of `struct uhidev_attach_arg' with just one `struct uhidev *parent' abstraction in it, not two abstractions `struct uhidev *parent' and `struct hidev_tag *tag'. This undoes ABI breakage that was introduced by the original change. - Rename the kernel symbols hidev_* -> uhidev_*. This is in some sense a regression but it is ABI breakage that makes the change unpullable-up; we can reapply this rename again after pullup. - Delete unnecessary uhid_common.c. The original uhid.c driver serves just fine once the ioctl is factored out, just like read/write, into uhidev. - Delete unnecessary uhid.h. This doesn't need to be shared between multiple drivers any more because the one uhid.c serves just fine. - Delete unnecessary si.h with si_attach_args. The original uihdev_attach_arg (singularly named though it may be) served just fine. - Delete unnecessary uhid_si.c. The original uhid.c driver serves just fine once the USB ioctl is factored out. - Add missing write_async callback to si(4). diff -r eae8bd032315 -r 19f84c00a20a sys/arch/evbppc/nintendo/dev/files.dev --- a/sys/arch/evbppc/nintendo/dev/files.dev Tue Jun 02 21:43:41 2026 +0000 +++ b/sys/arch/evbppc/nintendo/dev/files.dev Tue Jun 02 22:13:00 2026 +0000 @@ -29,14 +29,10 @@ device gecko attach gecko at exi file arch/evbppc/nintendo/dev/gecko.c gecko needs-flag -define si { } -device si: si, hid +device si: uhidbus attach si at mainbus file arch/evbppc/nintendo/dev/si.c si -attach uhid at si with uhid_si -file arch/evbppc/nintendo/dev/uhid_si.c uhid_si - define ahb { [addr=-1], [irq=-1] } device ahb: ahb attach ahb at mainbus diff -r eae8bd032315 -r 19f84c00a20a sys/arch/evbppc/nintendo/dev/si.c --- a/sys/arch/evbppc/nintendo/dev/si.c Tue Jun 02 21:43:41 2026 +0000 +++ b/sys/arch/evbppc/nintendo/dev/si.c Tue Jun 02 22:13:00 2026 +0000 @@ -42,10 +42,10 @@ #include #include +#include #include "locators.h" #include "mainbus.h" -#include "si.h" #include "gcpad_rdesc.h" #define SI_NUM_CHAN 4 @@ -101,7 +101,7 @@ struct si_channel { struct si_softc *ch_sc; device_t ch_dev; unsigned ch_index; - struct hidev_tag ch_hidev; + struct uhidev ch_hidev; kmutex_t ch_lock; kcondvar_t ch_cv; uint8_t ch_state; @@ -137,13 +137,16 @@ static void si_softintr(void *); static int si_rescan(device_t, const char *, const int *); static int si_print(void *, const char *); -static void si_get_report_desc(void *, void **, int *); -static int si_open(void *, void (*)(void *, void *, unsigned), void *); -static void si_stop(void *); -static void si_close(void *); -static usbd_status si_set_report(void *, int, void *, int); -static usbd_status si_get_report(void *, int, void *, int); -static usbd_status si_write(void *, void *, int); +static void si_get_report_desc(void *, void **, int *); +static int si_open(void *, void (*)(void *, void *, unsigned), void *); +static void si_stop(void *); +static void si_close(void *); +static usbd_status si_set_report(void *, int, void *, int); +static usbd_status si_get_report(void *, int, void *, int); +static usbd_status si_write(void *, void *, int); +static usbd_status si_write_async(void *, void *, int, int, int, + usbd_callback, void *); +static int si_ioctl(void *, u_long, void *, int, struct lwp *); CFATTACH_DECL_NEW(si, sizeof(struct si_softc), si_match, si_attach, NULL, NULL); @@ -179,7 +182,7 @@ si_attach(device_t parent, device_t self for (chan = 0; chan < SI_NUM_CHAN; chan++) { struct si_channel *ch; - struct hidev_tag *t; + struct uhidev *t; ch = &sc->sc_chan[chan]; ch->ch_sc = sc; @@ -199,6 +202,8 @@ si_attach(device_t parent, device_t self t->_set_report = si_set_report; t->_get_report = si_get_report; t->_write = si_write; + t->_write_async = si_write_async; + t->_ioctl = si_ioctl; } WR4(sc, SIPOLL, @@ -217,17 +222,18 @@ static int si_rescan(device_t self, const char *ifattr, const int *locs) { struct si_softc * const sc = device_private(self); - struct si_attach_args saa; + struct uhidev_attach_arg uaa; unsigned chan; for (chan = 0; chan < SI_NUM_CHAN; chan++) { struct si_channel *ch = &sc->sc_chan[chan]; if (ch->ch_dev == NULL) { - saa.saa_hidev = &ch->ch_hidev; - saa.saa_index = ch->ch_index; + uaa.uiaa = NULL; + uaa.parent = &ch->ch_hidev; + uaa.reportid = ch->ch_index + 1; - ch->ch_dev = config_found(self, &saa, si_print, + ch->ch_dev = config_found(self, &uaa, si_print, CFARGS(.submatch = config_stdsubmatch, .locators = locs)); } @@ -239,7 +245,7 @@ si_rescan(device_t self, const char *ifa static int si_print(void *aux, const char *pnp) { - struct si_attach_args *saa = aux; + struct uhidev_attach_arg *uaa = aux; if (pnp != NULL) { aprint_normal("uhid at %s", pnp); @@ -249,7 +255,7 @@ si_print(void *aux, const char *pnp) * The Wii Operations Manual for RVL-001 refers to the controller * ports as "Nintendo GameCube Controller Sockets". */ - aprint_normal(" socket %d", saa->saa_index + 1); + aprint_normal(" socket %d", uaa->reportid); return UNCONF; } @@ -446,3 +452,32 @@ si_write(void *cookie, void *data, int l { return USBD_INVAL; } + +static usbd_status +si_write_async(void *cookie, void *data, int len, int flags, int timo, + usbd_callback writecallback, void *writecookie) +{ + return USBD_INVAL; +} + +#define UHID_SI_VENDOR 0x057e /* Nintendo */ +#define UHID_SI_PRODUCT 0x0337 /* GameCube adapter */ + +static int +si_ioctl(void *cookie, u_long cmd, void *addr, int flag, struct lwp *l) +{ + struct usb_device_info *udi; + + switch (cmd) { + case USB_GET_DEVICEINFO: + udi = addr; + udi->udi_vendorNo = UHID_SI_VENDOR; + udi->udi_productNo = UHID_SI_PRODUCT; + snprintf(udi->udi_vendor, sizeof(udi->udi_vendor), "Nintendo"); + snprintf(udi->udi_product, sizeof(udi->udi_product), + "GameCube Controller"); + return 0; + } + + return EINVAL; +} diff -r eae8bd032315 -r 19f84c00a20a sys/arch/evbppc/nintendo/dev/si.h --- a/sys/arch/evbppc/nintendo/dev/si.h Tue Jun 02 21:43:41 2026 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,39 +0,0 @@ -/* $NetBSD: si.h,v 1.1 2026/01/09 22:54:30 jmcneill Exp $ */ - -/*- - * Copyright (c) 2025 Jared McNeill - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef _WII_DEV_SI_H_ -#define _WII_DEV_SI_H_ - -#include - -struct si_attach_args { - struct hidev_tag *saa_hidev; - int saa_index; -}; - -#endif /* _WII_DEV_SI_H_ */ diff -r eae8bd032315 -r 19f84c00a20a sys/arch/evbppc/nintendo/dev/uhid_si.c --- a/sys/arch/evbppc/nintendo/dev/uhid_si.c Tue Jun 02 21:43:41 2026 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,94 +0,0 @@ -/* $NetBSD: uhid_si.c,v 1.1 2026/01/09 22:54:30 jmcneill Exp $ */ - -/*- - * Copyright (c) 2025 Jared McNeill - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -__KERNEL_RCSID(0, "$NetBSD: uhid_si.c,v 1.1 2026/01/09 22:54:30 jmcneill Exp $"); - -#include -#include -#include -#include -#include - -#include -#include - -#include "si.h" - -#define UHID_SI_VENDOR 0x057e /* Nintendo */ -#define UHID_SI_PRODUCT 0x0337 /* GameCube adapter */ - -static int uhid_si_match(device_t, cfdata_t, void *); -static void uhid_si_attach(device_t, device_t, void *); - -static int uhid_si_ioctl(struct uhid_softc *, u_long, void *, int, - struct lwp *); - -CFATTACH_DECL_NEW(uhid_si, sizeof(struct uhid_softc), - uhid_si_match, uhid_si_attach, NULL, NULL); - -static int -uhid_si_match(device_t parent, cfdata_t cf, void *aux) -{ - return 1; -} - -static void -uhid_si_attach(device_t parent, device_t self, void *aux) -{ - struct uhid_softc * const sc = device_private(self); - struct si_attach_args * const saa = aux; - - sc->sc_dev = self; - sc->sc_report_id = saa->saa_index + 1; - sc->sc_ioctl = uhid_si_ioctl; - sc->sc_hidev = saa->saa_hidev; - - uhid_attach_common(sc); -} - -static int -uhid_si_ioctl(struct uhid_softc *sc, u_long cmd, void *addr, int flag, - struct lwp *l) -{ - struct usb_device_info *udi; - - switch (cmd) { - case USB_GET_DEVICEINFO: - udi = addr; - udi->udi_vendorNo = UHID_SI_VENDOR; - udi->udi_productNo = UHID_SI_PRODUCT; - snprintf(udi->udi_vendor, sizeof(udi->udi_vendor), "Nintendo"); - snprintf(udi->udi_product, sizeof(udi->udi_product), - "GameCube Controller"); - return 0; - break; - } - - return EINVAL; -} diff -r eae8bd032315 -r 19f84c00a20a sys/dev/hid/files.hid --- a/sys/dev/hid/files.hid Tue Jun 02 21:43:41 2026 +0000 +++ b/sys/dev/hid/files.hid Tue Jun 02 22:13:00 2026 +0000 @@ -17,8 +17,3 @@ file dev/hid/hidkbdmap.c ukbd | btkbd | define hidev file dev/hid/hidev.c hidev - -# Generic userspace HID devices -defflag opt_hid.h UHID_DEBUG -device uhid: hid, hidev -file dev/hid/uhid_common.c uhid diff -r eae8bd032315 -r 19f84c00a20a sys/dev/hid/hidev.c --- a/sys/dev/hid/hidev.c Tue Jun 02 21:43:41 2026 +0000 +++ b/sys/dev/hid/hidev.c Tue Jun 02 22:13:00 2026 +0000 @@ -26,46 +26,61 @@ * SUCH DAMAGE. */ -#include +#include void -hidev_get_report_desc(struct hidev_tag *t, void **desc, int *size) +uhidev_get_report_desc(struct uhidev *t, void **desc, int *size) { t->_get_report_desc(t->_cookie, desc, size); } int -hidev_open(struct hidev_tag *t, void (*intr)(void *, void *, u_int), void *cookie) +uhidev_open(struct uhidev *t, void (*intr)(void *, void *, unsigned), + void *cookie) { return t->_open(t->_cookie, intr, cookie); } void -hidev_stop(struct hidev_tag *t) +uhidev_stop(struct uhidev *t) { t->_stop(t->_cookie); } void -hidev_close(struct hidev_tag *t) +uhidev_close(struct uhidev *t) { t->_close(t->_cookie); } usbd_status -hidev_set_report(struct hidev_tag *t, int type, void *data, int len) +uhidev_set_report(struct uhidev *t, int type, void *data, int len) { return t->_set_report(t->_cookie, type, data, len); } usbd_status -hidev_get_report(struct hidev_tag *t, int type, void *data, int len) +uhidev_get_report(struct uhidev *t, int type, void *data, int len) { return t->_get_report(t->_cookie, type, data, len); } usbd_status -hidev_write(struct hidev_tag *t, void *data, int len) +uhidev_write(struct uhidev *t, void *data, int len) { return t->_write(t->_cookie, data, len); } + +usbd_status +uhidev_write_async(struct uhidev *t, void *data, int len, int flags, int timo, + usbd_callback writecallback, void *writecookie) +{ + return t->_write_async(t->_cookie, data, len, flags, timo, + writecallback, writecookie); +} + +int +uhidev_ioctl(struct uhidev *t, u_long cmd, void *addr, int flag, struct lwp *l) +{ + return t->_ioctl(t->_cookie, cmd, addr, flag, l); +} diff -r eae8bd032315 -r 19f84c00a20a sys/dev/hid/hidev.h --- a/sys/dev/hid/hidev.h Tue Jun 02 21:43:41 2026 +0000 +++ b/sys/dev/hid/hidev.h Tue Jun 02 22:13:00 2026 +0000 @@ -29,11 +29,14 @@ #ifndef _DEV_HID_HIDEV_H_ #define _DEV_HID_HIDEV_H_ +#include + #include -struct hidev_tag { - /* Opaque cookie for use by back-end. */ - void *_cookie; +struct lwp; + +struct uhidev { + void *_cookie; /* HID device methods. */ void (*_get_report_desc)(void *, void **, int *); @@ -44,14 +47,9 @@ struct hidev_tag { usbd_status (*_set_report)(void *, int, void *, int); usbd_status (*_get_report)(void *, int, void *, int); usbd_status (*_write)(void *, void *, int); + usbd_status (*_write_async)(void *, void *, int, int, int, + usbd_callback, void *); + int (*_ioctl)(void *, u_long, void *, int, struct lwp *); }; -void hidev_get_report_desc(struct hidev_tag *, void **, int *); -int hidev_open(struct hidev_tag *, void (*)(void *, void *, u_int), void *); -void hidev_stop(struct hidev_tag *); -void hidev_close(struct hidev_tag *); -usbd_status hidev_set_report(struct hidev_tag *, int, void *, int); -usbd_status hidev_get_report(struct hidev_tag *, int, void *, int); -usbd_status hidev_write(struct hidev_tag *, void *, int); - #endif /* _DEV_HID_HIDEV_H_ */ diff -r eae8bd032315 -r 19f84c00a20a sys/dev/hid/uhid.h --- a/sys/dev/hid/uhid.h Tue Jun 02 21:43:41 2026 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,70 +0,0 @@ -/* $NetBSD: uhid.h,v 1.2 2025/12/07 19:59:51 jmcneill Exp $ */ - -/*- - * Copyright (c) 2025 Jared McNeill - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef _DEV_HID_UHID_H_ -#define _DEV_HID_UHID_H_ - -#include - -#include - -struct uhid_softc { - device_t sc_dev; - struct hidev_tag *sc_hidev; - uint8_t sc_report_id; - int (*sc_ioctl)(struct uhid_softc *, u_long, void *, int, struct lwp *); - - kmutex_t sc_lock; - kcondvar_t sc_cv; - - int sc_isize; - int sc_osize; - int sc_fsize; - - u_char *sc_obuf; - - struct clist sc_q; /* protected by sc_lock */ - struct selinfo sc_rsel; - proc_t *sc_async; /* process that wants SIGIO */ - void *sc_sih; - volatile uint32_t sc_state; /* driver state */ -#define UHID_IMMED 0x02 /* return read data immediately */ - - int sc_raw; - enum { - UHID_CLOSED, - UHID_OPENING, - UHID_OPEN, - } sc_open; - bool sc_closing; -}; - -void uhid_attach_common(struct uhid_softc *); -int uhid_detach_common(struct uhid_softc *); - -#endif /* _DEV_HID_UHID_H_ */ diff -r eae8bd032315 -r 19f84c00a20a sys/dev/hid/uhid_common.c --- a/sys/dev/hid/uhid_common.c Tue Jun 02 21:43:41 2026 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,676 +0,0 @@ -/* $NetBSD: uhid_common.c,v 1.2 2025/12/15 08:38:21 hannken Exp $ */ - -/* - * Copyright (c) 1998, 2004, 2008, 2012 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Lennart Augustsson (lennart@augustsson.net) at - * Carlstedt Research & Technology and Matthew R. Green (mrg@eterna23.net). - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf - */ - -#include -__KERNEL_RCSID(0, "$NetBSD: uhid_common.c,v 1.2 2025/12/15 08:38:21 hannken Exp $"); - -#ifdef _KERNEL_OPT -#include "opt_hid.h" -#endif - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "ioconf.h" - -#ifdef UHID_DEBUG -#define DPRINTF(x) if (uhiddebug) printf x -#define DPRINTFN(n,x) if (uhiddebug>(n)) printf x -extern int uhiddebug; -#else -#define DPRINTF(x) -#define DPRINTFN(n,x) -#endif - -#define UHIDUNIT(dev) (minor(dev)) -#define UHID_CHUNK 128 /* chunk size for read */ -#define UHID_BSIZE 1020 /* buffer size */ - -static dev_type_open(uhidopen); -static dev_type_cancel(uhidcancel); -static dev_type_close(uhidclose); -static dev_type_read(uhidread); -static dev_type_write(uhidwrite); -static dev_type_ioctl(uhidioctl); -static dev_type_poll(uhidpoll); -static dev_type_kqfilter(uhidkqfilter); - -const struct cdevsw uhid_cdevsw = { - .d_open = uhidopen, - .d_cancel = uhidcancel, - .d_close = uhidclose, - .d_read = uhidread, - .d_write = uhidwrite, - .d_ioctl = uhidioctl, - .d_stop = nostop, - .d_tty = notty, - .d_poll = uhidpoll, - .d_mmap = nommap, - .d_kqfilter = uhidkqfilter, - .d_discard = nodiscard, - .d_cfdriver = &uhid_cd, - .d_devtounit = dev_minor_unit, - .d_flag = D_OTHER -}; - -static void uhid_intr(void *, void *, u_int); - -void -uhid_attach_common(struct uhid_softc *sc) -{ - int size, repid; - void *desc; - - selinit(&sc->sc_rsel); - - hidev_get_report_desc(sc->sc_hidev, &desc, &size); - repid = sc->sc_report_id; - sc->sc_isize = hid_report_size(desc, size, hid_input, repid); - sc->sc_osize = hid_report_size(desc, size, hid_output, repid); - sc->sc_fsize = hid_report_size(desc, size, hid_feature, repid); - sc->sc_raw = hid_is_collection(desc, size, repid, - HID_USAGE2(HUP_FIDO, HUF_U2FHID)); - - aprint_naive("\n"); - aprint_normal(": input=%d, output=%d, feature=%d\n", - sc->sc_isize, sc->sc_osize, sc->sc_fsize); - - mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTUSB); - cv_init(&sc->sc_cv, "uhidrea"); - - if (!pmf_device_register(sc->sc_dev, NULL, NULL)) { - aprint_error_dev(sc->sc_dev, - "couldn't establish power handler\n"); - } -} - -int -uhid_detach_common(struct uhid_softc *sc) -{ - int maj, mn; - - DPRINTF(("uhid_detach: sc=%p\n", sc)); - - pmf_device_deregister(sc->sc_dev); - - /* locate the major number */ - maj = cdevsw_lookup_major(&uhid_cdevsw); - - /* Nuke the vnodes for any open instances (calls close). */ - mn = device_unit(sc->sc_dev); - vdevgone(maj, mn, mn, VCHR); - - KASSERTMSG(sc->sc_open == UHID_CLOSED, "open=%d", (int)sc->sc_open); - - cv_destroy(&sc->sc_cv); - mutex_destroy(&sc->sc_lock); - seldestroy(&sc->sc_rsel); - - return 0; -} - -static void -uhid_intr(void *cookie, void *data, u_int len) -{ - struct uhid_softc *sc = cookie; - -#ifdef UHID_DEBUG - if (uhiddebug > 5) { - uint32_t i; - - DPRINTF(("uhid_intr: data =")); - for (i = 0; i < len; i++) - DPRINTF((" %02x", ((u_char *)data)[i])); - DPRINTF(("\n")); - } -#endif - - mutex_enter(&sc->sc_lock); - (void)b_to_q(data, len, &sc->sc_q); - - DPRINTFN(5, ("uhid_intr: waking %p\n", &sc->sc_q)); - cv_broadcast(&sc->sc_cv); - selnotify(&sc->sc_rsel, 0, NOTE_SUBMIT); - if (atomic_load_relaxed(&sc->sc_async) != NULL) { - mutex_enter(&proc_lock); - if (sc->sc_async != NULL) { - DPRINTFN(3, ("uhid_intr: sending SIGIO to %jd\n", - (intmax_t)sc->sc_async->p_pid)); - psignal(sc->sc_async, SIGIO); - } - mutex_exit(&proc_lock); - } - mutex_exit(&sc->sc_lock); -} - -static int -uhidopen(dev_t dev, int flag, int mode, struct lwp *l) -{ - struct uhid_softc *sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); - int error; - - DPRINTF(("uhidopen: sc=%p\n", sc)); - if (sc == NULL) - return ENXIO; - - /* - * Try to open. If closing in progress, give up. If already - * open (or opening), fail -- opens are exclusive. - */ - mutex_enter(&sc->sc_lock); - if (sc->sc_open != UHID_CLOSED || sc->sc_closing) { - mutex_exit(&sc->sc_lock); - return EBUSY; - } - sc->sc_open = UHID_OPENING; - atomic_store_relaxed(&sc->sc_state, 0); - mutex_exit(&sc->sc_lock); - - /* uhid interrupts aren't enabled yet, so setup sc_q now */ - if (clalloc(&sc->sc_q, UHID_BSIZE, 0) == -1) { - error = ENOMEM; - goto fail0; - } - - /* Allocate an output buffer if needed. */ - if (sc->sc_osize > 0) - sc->sc_obuf = kmem_alloc(sc->sc_osize, KM_SLEEP); - else - sc->sc_obuf = NULL; - - /* Paranoia: reset SIGIO before enabling interrupts. */ - mutex_enter(&proc_lock); - atomic_store_relaxed(&sc->sc_async, NULL); - mutex_exit(&proc_lock); - - /* Open the hidev -- after this point we can get interrupts. */ - error = hidev_open(sc->sc_hidev, &uhid_intr, sc); - if (error) - goto fail1; - - /* We are open for business. */ - mutex_enter(&sc->sc_lock); - sc->sc_open = UHID_OPEN; - mutex_exit(&sc->sc_lock); - - return 0; - -fail1: selnotify(&sc->sc_rsel, POLLHUP, 0); - mutex_enter(&proc_lock); - atomic_store_relaxed(&sc->sc_async, NULL); - mutex_exit(&proc_lock); - if (sc->sc_osize > 0) { - kmem_free(sc->sc_obuf, sc->sc_osize); - sc->sc_obuf = NULL; - } - clfree(&sc->sc_q); -fail0: mutex_enter(&sc->sc_lock); - KASSERT(sc->sc_open == UHID_OPENING); - sc->sc_open = UHID_CLOSED; - atomic_store_relaxed(&sc->sc_state, 0); - mutex_exit(&sc->sc_lock); - return error; -} - -static int -uhidcancel(dev_t dev, int flag, int mode, struct lwp *l) -{ - struct uhid_softc *sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); - - DPRINTF(("uhidcancel: sc=%p\n", sc)); - if (sc == NULL) - return 0; - - /* - * Mark it closing, wake any waiters, and suspend output. - * After this point, no new xfers can be submitted. - */ - mutex_enter(&sc->sc_lock); - sc->sc_closing = true; - cv_broadcast(&sc->sc_cv); - mutex_exit(&sc->sc_lock); - - hidev_stop(sc->sc_hidev); - - return 0; -} - -static int -uhidclose(dev_t dev, int flag, int mode, struct lwp *l) -{ - struct uhid_softc *sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); - - DPRINTF(("uhidclose: sc=%p\n", sc)); - if (sc == NULL) - return 0; - - mutex_enter(&sc->sc_lock); - KASSERT(sc->sc_closing); - KASSERTMSG(sc->sc_open == UHID_OPEN || sc->sc_open == UHID_CLOSED, - "sc_open=%d", sc->sc_open); - if (sc->sc_open == UHID_CLOSED) - goto out; - - /* Release the lock while we free things. */ - mutex_exit(&sc->sc_lock); - - /* Prevent further interrupts. */ - hidev_close(sc->sc_hidev); - - /* Hang up all select/poll. */ - selnotify(&sc->sc_rsel, POLLHUP, 0); - - /* Reset SIGIO. */ - mutex_enter(&proc_lock); - atomic_store_relaxed(&sc->sc_async, NULL); - mutex_exit(&proc_lock); - - /* Free the buffer and queue. */ - if (sc->sc_osize > 0) { - kmem_free(sc->sc_obuf, sc->sc_osize); - sc->sc_obuf = NULL; - } - clfree(&sc->sc_q); - - mutex_enter(&sc->sc_lock); - - /* All set. We are now closed. */ - sc->sc_open = UHID_CLOSED; -out: KASSERT(sc->sc_open == UHID_CLOSED); - sc->sc_closing = false; - atomic_store_relaxed(&sc->sc_state, 0); - mutex_exit(&sc->sc_lock); - - return 0; -} - -static int -uhidread(dev_t dev, struct uio *uio, int flag) -{ - struct uhid_softc *sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); - int error = 0; - int extra; - size_t length; - u_char buffer[UHID_CHUNK]; - usbd_status err; - - DPRINTFN(1, ("uhidread\n")); - if (atomic_load_relaxed(&sc->sc_state) & UHID_IMMED) { - DPRINTFN(1, ("uhidread immed\n")); - extra = sc->sc_report_id != 0; - if (sc->sc_isize + extra > sizeof(buffer)) - return ENOBUFS; - err = hidev_get_report(sc->sc_hidev, UHID_INPUT_REPORT, - buffer, sc->sc_isize + extra); - if (err) - return EIO; - return uiomove(buffer+extra, sc->sc_isize, uio); - } - - mutex_enter(&sc->sc_lock); - while (sc->sc_q.c_cc == 0) { - if (flag & IO_NDELAY) { - mutex_exit(&sc->sc_lock); - return EWOULDBLOCK; - } - if (sc->sc_closing) { - mutex_exit(&sc->sc_lock); - return EIO; - } - DPRINTFN(5, ("uhidread: sleep on %p\n", &sc->sc_q)); - error = cv_wait_sig(&sc->sc_cv, &sc->sc_lock); - DPRINTFN(5, ("uhidread: woke, error=%d\n", error)); - if (error) { - break; - } - } - - /* Transfer as many chunks as possible. */ - while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0 && !error) { - length = uimin(sc->sc_q.c_cc, uio->uio_resid); - if (length > sizeof(buffer)) - length = sizeof(buffer); - - /* Remove a small chunk from the input queue. */ - (void) q_to_b(&sc->sc_q, buffer, length); - DPRINTFN(5, ("uhidread: got %lu chars\n", (u_long)length)); - - /* Copy the data to the user process. */ - mutex_exit(&sc->sc_lock); - if ((error = uiomove(buffer, length, uio)) != 0) - return error; - mutex_enter(&sc->sc_lock); - } - - mutex_exit(&sc->sc_lock); - return error; -} - -static int -uhidwrite(dev_t dev, struct uio *uio, int flag) -{ - struct uhid_softc *sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); - int error; - int size; - usbd_status err; - - DPRINTFN(1, ("uhidwrite\n")); - - size = sc->sc_osize; - if (uio->uio_resid != size || size == 0) - return EINVAL; - error = uiomove(sc->sc_obuf, size, uio); -#ifdef UHID_DEBUG - if (uhiddebug > 5) { - uint32_t i; - - DPRINTF(("%s: outdata[%d] =", device_xname(sc->sc_dev), - error)); - for (i = 0; i < size; i++) - DPRINTF((" %02x", sc->sc_obuf[i])); - DPRINTF(("\n")); - } -#endif - if (!error) { - if (sc->sc_raw) - err = hidev_write(sc->sc_hidev, sc->sc_obuf, size); - else - err = hidev_set_report(sc->sc_hidev, - UHID_OUTPUT_REPORT, sc->sc_obuf, size); - if (err) { - DPRINTF(("%s: err = %d\n", - device_xname(sc->sc_dev), err)); - error = EIO; - } - } - - return error; -} - -static int -uhidioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l) -{ - struct uhid_softc *sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); - struct usb_ctl_report_desc *rd; - struct usb_ctl_report *re; - u_char buffer[UHID_CHUNK]; - int size, extra; - usbd_status err; - void *desc; - - DPRINTFN(2, ("uhidioctl: cmd=%lx\n", cmd)); - - switch (cmd) { - case FIONBIO: - /* All handled in the upper FS layer. */ - break; - - case FIOASYNC: - mutex_enter(&proc_lock); - if (*(int *)addr) { - if (sc->sc_async != NULL) { - mutex_exit(&proc_lock); - return EBUSY; - } - atomic_store_relaxed(&sc->sc_async, l->l_proc); - DPRINTF(("uhid_do_ioctl: FIOASYNC %p\n", l->l_proc)); - } else - atomic_store_relaxed(&sc->sc_async, NULL); - mutex_exit(&proc_lock); - break; - - /* XXX this is not the most general solution. */ - case TIOCSPGRP: - mutex_enter(&proc_lock); - if (sc->sc_async == NULL) { - mutex_exit(&proc_lock); - return EINVAL; - } - if (*(int *)addr != sc->sc_async->p_pgid) { - mutex_exit(&proc_lock); - return EPERM; - } - mutex_exit(&proc_lock); - break; - - case FIOSETOWN: - mutex_enter(&proc_lock); - if (sc->sc_async == NULL) { - mutex_exit(&proc_lock); - return EINVAL; - } - if (-*(int *)addr != sc->sc_async->p_pgid - && *(int *)addr != sc->sc_async->p_pid) { - mutex_exit(&proc_lock); - return EPERM; - } - mutex_exit(&proc_lock); - break; - - case USB_HID_GET_RAW: - *(int *)addr = sc->sc_raw; - break; - - case USB_HID_SET_RAW: - sc->sc_raw = *(int *)addr; - break; - - case USB_GET_REPORT_DESC: - hidev_get_report_desc(sc->sc_hidev, &desc, &size); - rd = (struct usb_ctl_report_desc *)addr; - size = uimin(size, sizeof(rd->ucrd_data)); - rd->ucrd_size = size; - memcpy(rd->ucrd_data, desc, size); - break; - - case USB_SET_IMMED: - if (*(int *)addr) { - extra = sc->sc_report_id != 0; - if (sc->sc_isize + extra > sizeof(buffer)) - return ENOBUFS; - err = hidev_get_report(sc->sc_hidev, UHID_INPUT_REPORT, - buffer, sc->sc_isize + extra); - if (err) - return EOPNOTSUPP; - - atomic_or_32(&sc->sc_state, UHID_IMMED); - } else - atomic_and_32(&sc->sc_state, ~UHID_IMMED); - break; - - case USB_GET_REPORT: - re = (struct usb_ctl_report *)addr; - switch (re->ucr_report) { - case UHID_INPUT_REPORT: - size = sc->sc_isize; - break; - case UHID_OUTPUT_REPORT: - size = sc->sc_osize; - break; - case UHID_FEATURE_REPORT: - size = sc->sc_fsize; - break; - default: - return EINVAL; - } - extra = sc->sc_report_id != 0; - if (size + extra > sizeof(re->ucr_data)) - return ENOBUFS; - err = hidev_get_report(sc->sc_hidev, re->ucr_report, - re->ucr_data, size + extra); - if (extra) - memmove(re->ucr_data, re->ucr_data+1, size); - if (err) - return EIO; - break; - - case USB_SET_REPORT: - re = (struct usb_ctl_report *)addr; - switch (re->ucr_report) { - case UHID_INPUT_REPORT: - size = sc->sc_isize; - break; - case UHID_OUTPUT_REPORT: - size = sc->sc_osize; - break; - case UHID_FEATURE_REPORT: - size = sc->sc_fsize; - break; - default: - return EINVAL; - } - if (size > sizeof(re->ucr_data)) - return ENOBUFS; - err = hidev_set_report(sc->sc_hidev, re->ucr_report, - re->ucr_data, size); - if (err) - return EIO; - break; - - case USB_GET_REPORT_ID: - *(int *)addr = sc->sc_report_id; - break; - - default: - if (sc->sc_ioctl != NULL) { - return sc->sc_ioctl(sc, cmd, addr, flag, l); - } - return EINVAL; - } - return 0; -} - -static int -uhidpoll(dev_t dev, int events, struct lwp *l) -{ - struct uhid_softc *sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); - int revents = 0; - - mutex_enter(&sc->sc_lock); - if (events & (POLLOUT | POLLWRNORM)) - revents |= events & (POLLOUT | POLLWRNORM); - if (events & (POLLIN | POLLRDNORM)) { - if (sc->sc_q.c_cc > 0) - revents |= events & (POLLIN | POLLRDNORM); - else - selrecord(l, &sc->sc_rsel); - } - mutex_exit(&sc->sc_lock); - - return revents; -} - -static void -filt_uhidrdetach(struct knote *kn) -{ - struct uhid_softc *sc = kn->kn_hook; - - mutex_enter(&sc->sc_lock); - selremove_knote(&sc->sc_rsel, kn); - mutex_exit(&sc->sc_lock); -} - -static int -filt_uhidread(struct knote *kn, long hint) -{ - struct uhid_softc *sc = kn->kn_hook; - - if (hint == NOTE_SUBMIT) - KASSERT(mutex_owned(&sc->sc_lock)); - else - mutex_enter(&sc->sc_lock); - - kn->kn_data = sc->sc_q.c_cc; - - if (hint == NOTE_SUBMIT) - KASSERT(mutex_owned(&sc->sc_lock)); - else - mutex_exit(&sc->sc_lock); - - return kn->kn_data > 0; -} - -static const struct filterops uhidread_filtops = { - .f_flags = FILTEROP_ISFD, - .f_attach = NULL, - .f_detach = filt_uhidrdetach, - .f_event = filt_uhidread, -}; - -static int -uhidkqfilter(dev_t dev, struct knote *kn) -{ - struct uhid_softc *sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); - - switch (kn->kn_filter) { - case EVFILT_READ: - kn->kn_fop = &uhidread_filtops; - kn->kn_hook = sc; - mutex_enter(&sc->sc_lock); - selrecord_knote(&sc->sc_rsel, kn); - mutex_exit(&sc->sc_lock); - return 0; - - case EVFILT_WRITE: - kn->kn_fop = &seltrue_filtops; - return 0; - - default: - return EINVAL; - } -} diff -r eae8bd032315 -r 19f84c00a20a sys/dev/usb/files.usb --- a/sys/dev/usb/files.usb Tue Jun 02 21:43:41 2026 +0000 +++ b/sys/dev/usb/files.usb Tue Jun 02 22:13:00 2026 +0000 @@ -50,6 +50,7 @@ defflag opt_usb.h UGENSA_DEBUG: USB_DEBU defflag opt_usb.h UGEN_DEBUG: USB_DEBUG defparam opt_usb.h UGEN_DEBUG_DEFAULT: UGEN_DEBUG defflag opt_usb.h UHIDEV_DEBUG: USB_DEBUG +defflag opt_usb.h UHID_DEBUG: USB_DEBUG defflag opt_usb.h UHMODEM_DEBUG: USB_DEBUG defflag opt_usb.h UHSO_DEBUG: USB_DEBUG defflag opt_usb.h UIPAD_DEBUG: USB_DEBUG @@ -147,7 +148,7 @@ file dev/usb/ugen.c ugen | ugenif nee # HID # HID "bus" -define uhidbus {[ reportid = -1 ]} +define uhidbus {[ reportid = -1 ]}: hidev # HID root device for multiple report IDs device uhidev: hid, uhidbus @@ -155,8 +156,9 @@ attach uhidev at usbifif file dev/usb/uhidev.c uhidev # Generic HID devices -attach uhid at uhidbus with uhid_usb -file dev/usb/uhid.c uhid_usb needs-flag +device uhid: hid +attach uhid at uhidbus +file dev/usb/uhid.c uhid needs-flag # Keyboards defparam UKBD_LAYOUT diff -r eae8bd032315 -r 19f84c00a20a sys/dev/usb/uhid.c --- a/sys/dev/usb/uhid.c Tue Jun 02 21:43:41 2026 +0000 +++ b/sys/dev/usb/uhid.c Tue Jun 02 22:13:00 2026 +0000 @@ -1,4 +1,4 @@ -/* $NetBSD: uhid.c,v 1.131 2025/12/13 12:22:20 jmcneill Exp $ */ +/* $NetBSD: uhid.c,v 1.129 2024/02/04 05:43:06 mrg Exp $ */ /* * Copyright (c) 1998, 2004, 2008, 2012 The NetBSD Foundation, Inc. @@ -35,26 +35,30 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: uhid.c,v 1.131 2025/12/13 12:22:20 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: uhid.c,v 1.129 2024/02/04 05:43:06 mrg Exp $"); #ifdef _KERNEL_OPT -#include "opt_compat_netbsd.h" -#include "opt_hid.h" +#include "opt_usb.h" #endif #include #include -#include +#include #include #include +#include +#include #include #include #include +#include #include +#include +#include #include #include -#include +#include #include #include @@ -64,7 +68,6 @@ #include #include #include -#include #include @@ -79,21 +82,75 @@ int uhiddebug = 0; #define DPRINTFN(n,x) #endif -struct uhid_usb_softc { - struct uhid_softc sc_base; +struct uhid_softc { + device_t sc_dev; struct uhidev *sc_hdev; - struct usbd_device *sc_udev; + uint8_t sc_report_id; + + kmutex_t sc_lock; + kcondvar_t sc_cv; + + int sc_isize; + int sc_osize; + int sc_fsize; + + u_char *sc_obuf; + + struct clist sc_q; /* protected by sc_lock */ + struct selinfo sc_rsel; + proc_t *sc_async; /* process that wants SIGIO */ + void *sc_sih; + volatile uint32_t sc_state; /* driver state */ +#define UHID_IMMED 0x02 /* return read data immediately */ + + int sc_raw; + enum { + UHID_CLOSED, + UHID_OPENING, + UHID_OPEN, + } sc_open; + bool sc_closing; }; +#define UHIDUNIT(dev) (minor(dev)) +#define UHID_CHUNK 128 /* chunk size for read */ +#define UHID_BSIZE 1020 /* buffer size */ + +static dev_type_open(uhidopen); +static dev_type_cancel(uhidcancel); +static dev_type_close(uhidclose); +static dev_type_read(uhidread); +static dev_type_write(uhidwrite); +static dev_type_ioctl(uhidioctl); +static dev_type_poll(uhidpoll); +static dev_type_kqfilter(uhidkqfilter); + +const struct cdevsw uhid_cdevsw = { + .d_open = uhidopen, + .d_cancel = uhidcancel, + .d_close = uhidclose, + .d_read = uhidread, + .d_write = uhidwrite, + .d_ioctl = uhidioctl, + .d_stop = nostop, + .d_tty = notty, + .d_poll = uhidpoll, + .d_mmap = nommap, + .d_kqfilter = uhidkqfilter, + .d_discard = nodiscard, + .d_cfdriver = &uhid_cd, + .d_devtounit = dev_minor_unit, + .d_flag = D_OTHER +}; + +static void uhid_intr(void *, void *, u_int); + static int uhid_match(device_t, cfdata_t, void *); static void uhid_attach(device_t, device_t, void *); static int uhid_detach(device_t, int); -static int uhid_usb_ioctl(struct uhid_softc *, u_long, void *, int, - struct lwp *); - -CFATTACH_DECL_NEW(uhid_usb, sizeof(struct uhid_usb_softc), - uhid_match, uhid_attach, uhid_detach, NULL); +CFATTACH_DECL_NEW(uhid, sizeof(struct uhid_softc), uhid_match, uhid_attach, + uhid_detach, NULL); static int uhid_match(device_t parent, cfdata_t match, void *aux) @@ -113,72 +170,568 @@ uhid_match(device_t parent, cfdata_t mat static void uhid_attach(device_t parent, device_t self, void *aux) { - struct uhid_usb_softc *usc = device_private(self); - struct uhid_softc *sc = &usc->sc_base; + struct uhid_softc *sc = device_private(self); struct uhidev_attach_arg *uha = aux; + int size, repid; + void *desc; sc->sc_dev = self; + sc->sc_hdev = uha->parent; sc->sc_report_id = uha->reportid; - sc->sc_ioctl = uhid_usb_ioctl; - sc->sc_hidev = uha->hidev; - usc->sc_hdev = uha->parent; - usc->sc_udev = uha->uiaa->uiaa_device; + + selinit(&sc->sc_rsel); + + uhidev_get_report_desc(uha->parent, &desc, &size); + repid = uha->reportid; + sc->sc_isize = hid_report_size(desc, size, hid_input, repid); + sc->sc_osize = hid_report_size(desc, size, hid_output, repid); + sc->sc_fsize = hid_report_size(desc, size, hid_feature, repid); + sc->sc_raw = hid_is_collection(desc, size, uha->reportid, + HID_USAGE2(HUP_FIDO, HUF_U2FHID)); - uhid_attach_common(sc); + aprint_naive("\n"); + aprint_normal(": input=%d, output=%d, feature=%d\n", + sc->sc_isize, sc->sc_osize, sc->sc_fsize); + + mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTUSB); + cv_init(&sc->sc_cv, "uhidrea"); + + if (!pmf_device_register(self, NULL, NULL)) + aprint_error_dev(self, "couldn't establish power handler\n"); + + return; } static int uhid_detach(device_t self, int flags) { - struct uhid_usb_softc *usc = device_private(self); - struct uhid_softc *sc = &usc->sc_base; + struct uhid_softc *sc = device_private(self); + int maj, mn; + + DPRINTF(("uhid_detach: sc=%p flags=%d\n", sc, flags)); + + pmf_device_deregister(self); + + /* locate the major number */ + maj = cdevsw_lookup_major(&uhid_cdevsw); + + /* Nuke the vnodes for any open instances (calls close). */ + mn = device_unit(self); + vdevgone(maj, mn, mn, VCHR); + + KASSERTMSG(sc->sc_open == UHID_CLOSED, "open=%d", (int)sc->sc_open); + + cv_destroy(&sc->sc_cv); + mutex_destroy(&sc->sc_lock); + seldestroy(&sc->sc_rsel); + + return 0; +} + +static void +uhid_intr(void *cookie, void *data, u_int len) +{ + struct uhid_softc *sc = cookie; + +#ifdef UHID_DEBUG + if (uhiddebug > 5) { + uint32_t i; + + DPRINTF(("uhid_intr: data =")); + for (i = 0; i < len; i++) + DPRINTF((" %02x", ((u_char *)data)[i])); + DPRINTF(("\n")); + } +#endif + + mutex_enter(&sc->sc_lock); + (void)b_to_q(data, len, &sc->sc_q); + + DPRINTFN(5, ("uhid_intr: waking %p\n", &sc->sc_q)); + cv_broadcast(&sc->sc_cv); + selnotify(&sc->sc_rsel, 0, NOTE_SUBMIT); + if (atomic_load_relaxed(&sc->sc_async) != NULL) { + mutex_enter(&proc_lock); + if (sc->sc_async != NULL) { + DPRINTFN(3, ("uhid_intr: sending SIGIO to %jd\n", + (intmax_t)sc->sc_async->p_pid)); + psignal(sc->sc_async, SIGIO); + } + mutex_exit(&proc_lock); + } + mutex_exit(&sc->sc_lock); +} + +static int +uhidopen(dev_t dev, int flag, int mode, struct lwp *l) +{ + struct uhid_softc *sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); + int error; + + DPRINTF(("uhidopen: sc=%p\n", sc)); + if (sc == NULL) + return ENXIO; - return uhid_detach_common(sc); + /* + * Try to open. If closing in progress, give up. If already + * open (or opening), fail -- opens are exclusive. + */ + mutex_enter(&sc->sc_lock); + if (sc->sc_open != UHID_CLOSED || sc->sc_closing) { + mutex_exit(&sc->sc_lock); + return EBUSY; + } + sc->sc_open = UHID_OPENING; + atomic_store_relaxed(&sc->sc_state, 0); + mutex_exit(&sc->sc_lock); + + /* uhid interrupts aren't enabled yet, so setup sc_q now */ + if (clalloc(&sc->sc_q, UHID_BSIZE, 0) == -1) { + error = ENOMEM; + goto fail0; + } + + /* Allocate an output buffer if needed. */ + if (sc->sc_osize > 0) + sc->sc_obuf = kmem_alloc(sc->sc_osize, KM_SLEEP); + else + sc->sc_obuf = NULL; + + /* Paranoia: reset SIGIO before enabling interrupts. */ + mutex_enter(&proc_lock); + atomic_store_relaxed(&sc->sc_async, NULL); + mutex_exit(&proc_lock); + + /* Open the uhidev -- after this point we can get interrupts. */ + error = uhidev_open(sc->sc_hdev, &uhid_intr, sc); + if (error) + goto fail1; + + /* We are open for business. */ + mutex_enter(&sc->sc_lock); + sc->sc_open = UHID_OPEN; + mutex_exit(&sc->sc_lock); + + return 0; + +fail1: selnotify(&sc->sc_rsel, POLLHUP, 0); + mutex_enter(&proc_lock); + atomic_store_relaxed(&sc->sc_async, NULL); + mutex_exit(&proc_lock); + if (sc->sc_osize > 0) { + kmem_free(sc->sc_obuf, sc->sc_osize); + sc->sc_obuf = NULL; + } + clfree(&sc->sc_q); +fail0: mutex_enter(&sc->sc_lock); + KASSERT(sc->sc_open == UHID_OPENING); + sc->sc_open = UHID_CLOSED; + atomic_store_relaxed(&sc->sc_state, 0); + mutex_exit(&sc->sc_lock); + return error; +} + +static int +uhidcancel(dev_t dev, int flag, int mode, struct lwp *l) +{ + struct uhid_softc *sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); + + DPRINTF(("uhidcancel: sc=%p\n", sc)); + if (sc == NULL) + return 0; + + /* + * Mark it closing, wake any waiters, and suspend output. + * After this point, no new xfers can be submitted. + */ + mutex_enter(&sc->sc_lock); + sc->sc_closing = true; + cv_broadcast(&sc->sc_cv); + mutex_exit(&sc->sc_lock); + + uhidev_stop(sc->sc_hdev); + + return 0; } static int -uhid_usb_ioctl(struct uhid_softc *sc, u_long cmd, void *addr, int flag, - struct lwp *l) +uhidclose(dev_t dev, int flag, int mode, struct lwp *l) +{ + struct uhid_softc *sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); + + DPRINTF(("uhidclose: sc=%p\n", sc)); + if (sc == NULL) + return 0; + + mutex_enter(&sc->sc_lock); + KASSERT(sc->sc_closing); + KASSERTMSG(sc->sc_open == UHID_OPEN || sc->sc_open == UHID_CLOSED, + "sc_open=%d", sc->sc_open); + if (sc->sc_open == UHID_CLOSED) + goto out; + + /* Release the lock while we free things. */ + mutex_exit(&sc->sc_lock); + + /* Prevent further interrupts. */ + uhidev_close(sc->sc_hdev); + + /* Hang up all select/poll. */ + selnotify(&sc->sc_rsel, POLLHUP, 0); + + /* Reset SIGIO. */ + mutex_enter(&proc_lock); + atomic_store_relaxed(&sc->sc_async, NULL); + mutex_exit(&proc_lock); + + /* Free the buffer and queue. */ + if (sc->sc_osize > 0) { + kmem_free(sc->sc_obuf, sc->sc_osize); + sc->sc_obuf = NULL; + } + clfree(&sc->sc_q); + + mutex_enter(&sc->sc_lock); + + /* All set. We are now closed. */ + sc->sc_open = UHID_CLOSED; +out: KASSERT(sc->sc_open == UHID_CLOSED); + sc->sc_closing = false; + atomic_store_relaxed(&sc->sc_state, 0); + mutex_exit(&sc->sc_lock); + + return 0; +} + +static int +uhidread(dev_t dev, struct uio *uio, int flag) { - struct uhid_usb_softc *usc = - container_of(sc, struct uhid_usb_softc, sc_base); + struct uhid_softc *sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); + int error = 0; + int extra; + size_t length; + u_char buffer[UHID_CHUNK]; + usbd_status err; + + DPRINTFN(1, ("uhidread\n")); + if (atomic_load_relaxed(&sc->sc_state) & UHID_IMMED) { + DPRINTFN(1, ("uhidread immed\n")); + extra = sc->sc_report_id != 0; + if (sc->sc_isize + extra > sizeof(buffer)) + return ENOBUFS; + err = uhidev_get_report(sc->sc_hdev, UHID_INPUT_REPORT, + buffer, sc->sc_isize + extra); + if (err) + return EIO; + return uiomove(buffer+extra, sc->sc_isize, uio); + } + + mutex_enter(&sc->sc_lock); + while (sc->sc_q.c_cc == 0) { + if (flag & IO_NDELAY) { + mutex_exit(&sc->sc_lock); + return EWOULDBLOCK; + } + if (sc->sc_closing) { + mutex_exit(&sc->sc_lock); + return EIO; + } + DPRINTFN(5, ("uhidread: sleep on %p\n", &sc->sc_q)); + error = cv_wait_sig(&sc->sc_cv, &sc->sc_lock); + DPRINTFN(5, ("uhidread: woke, error=%d\n", error)); + if (error) { + break; + } + } + + /* Transfer as many chunks as possible. */ + while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0 && !error) { + length = uimin(sc->sc_q.c_cc, uio->uio_resid); + if (length > sizeof(buffer)) + length = sizeof(buffer); + + /* Remove a small chunk from the input queue. */ + (void) q_to_b(&sc->sc_q, buffer, length); + DPRINTFN(5, ("uhidread: got %lu chars\n", (u_long)length)); + + /* Copy the data to the user process. */ + mutex_exit(&sc->sc_lock); + if ((error = uiomove(buffer, length, uio)) != 0) + return error; + mutex_enter(&sc->sc_lock); + } + + mutex_exit(&sc->sc_lock); + return error; +} + +static int +uhidwrite(dev_t dev, struct uio *uio, int flag) +{ + struct uhid_softc *sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); + int error; int size; usbd_status err; + DPRINTFN(1, ("uhidwrite\n")); + + size = sc->sc_osize; + if (uio->uio_resid != size || size == 0) + return EINVAL; + error = uiomove(sc->sc_obuf, size, uio); +#ifdef UHID_DEBUG + if (uhiddebug > 5) { + uint32_t i; + + DPRINTF(("%s: outdata[%d] =", device_xname(sc->sc_dev), + error)); + for (i = 0; i < size; i++) + DPRINTF((" %02x", sc->sc_obuf[i])); + DPRINTF(("\n")); + } +#endif + if (!error) { + if (sc->sc_raw) + err = uhidev_write(sc->sc_hdev, sc->sc_obuf, size); + else + err = uhidev_set_report(sc->sc_hdev, + UHID_OUTPUT_REPORT, sc->sc_obuf, size); + if (err) { + DPRINTF(("%s: err = %d\n", + device_xname(sc->sc_dev), err)); + error = EIO; + } + } + + return error; +} + +static int +uhidioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l) +{ + struct uhid_softc *sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); + struct usb_ctl_report_desc *rd; + struct usb_ctl_report *re; + u_char buffer[UHID_CHUNK]; + int size, extra; + usbd_status err; + void *desc; + DPRINTFN(2, ("uhidioctl: cmd=%lx\n", cmd)); switch (cmd) { - case USB_GET_DEVICE_DESC: - *(usb_device_descriptor_t *)addr = - *usbd_get_device_descriptor(usc->sc_udev); + case FIONBIO: + /* All handled in the upper FS layer. */ + break; + + case FIOASYNC: + mutex_enter(&proc_lock); + if (*(int *)addr) { + if (sc->sc_async != NULL) { + mutex_exit(&proc_lock); + return EBUSY; + } + atomic_store_relaxed(&sc->sc_async, l->l_proc); + DPRINTF(("uhid_do_ioctl: FIOASYNC %p\n", l->l_proc)); + } else + atomic_store_relaxed(&sc->sc_async, NULL); + mutex_exit(&proc_lock); + break; + + /* XXX this is not the most general solution. */ + case TIOCSPGRP: + mutex_enter(&proc_lock); + if (sc->sc_async == NULL) { + mutex_exit(&proc_lock); + return EINVAL; + } + if (*(int *)addr != sc->sc_async->p_pgid) { + mutex_exit(&proc_lock); + return EPERM; + } + mutex_exit(&proc_lock); + break; + + case FIOSETOWN: + mutex_enter(&proc_lock); + if (sc->sc_async == NULL) { + mutex_exit(&proc_lock); + return EINVAL; + } + if (-*(int *)addr != sc->sc_async->p_pgid + && *(int *)addr != sc->sc_async->p_pid) { + mutex_exit(&proc_lock); + return EPERM; + } + mutex_exit(&proc_lock); + break; + + case USB_HID_GET_RAW: + *(int *)addr = sc->sc_raw; + break; + + case USB_HID_SET_RAW: + sc->sc_raw = *(int *)addr; + break; + + case USB_GET_REPORT_DESC: + uhidev_get_report_desc(sc->sc_hdev, &desc, &size); + rd = (struct usb_ctl_report_desc *)addr; + size = uimin(size, sizeof(rd->ucrd_data)); + rd->ucrd_size = size; + memcpy(rd->ucrd_data, desc, size); + break; + + case USB_SET_IMMED: + if (*(int *)addr) { + extra = sc->sc_report_id != 0; + if (sc->sc_isize + extra > sizeof(buffer)) + return ENOBUFS; + err = uhidev_get_report(sc->sc_hdev, UHID_INPUT_REPORT, + buffer, sc->sc_isize + extra); + if (err) + return EOPNOTSUPP; + + atomic_or_32(&sc->sc_state, UHID_IMMED); + } else + atomic_and_32(&sc->sc_state, ~UHID_IMMED); + break; + + case USB_GET_REPORT: + re = (struct usb_ctl_report *)addr; + switch (re->ucr_report) { + case UHID_INPUT_REPORT: + size = sc->sc_isize; + break; + case UHID_OUTPUT_REPORT: + size = sc->sc_osize; + break; + case UHID_FEATURE_REPORT: + size = sc->sc_fsize; + break; + default: + return EINVAL; + } + extra = sc->sc_report_id != 0; + if (size + extra > sizeof(re->ucr_data)) + return ENOBUFS; + err = uhidev_get_report(sc->sc_hdev, re->ucr_report, + re->ucr_data, size + extra); + if (extra) + memmove(re->ucr_data, re->ucr_data+1, size); + if (err) + return EIO; break; - case USB_GET_DEVICEINFO: - usbd_fill_deviceinfo(usc->sc_udev, - (struct usb_device_info *)addr, 0); + case USB_SET_REPORT: + re = (struct usb_ctl_report *)addr; + switch (re->ucr_report) { + case UHID_INPUT_REPORT: + size = sc->sc_isize; + break; + case UHID_OUTPUT_REPORT: + size = sc->sc_osize; + break; + case UHID_FEATURE_REPORT: + size = sc->sc_fsize; + break; + default: + return EINVAL; + } + if (size > sizeof(re->ucr_data)) + return ENOBUFS; + err = uhidev_set_report(sc->sc_hdev, re->ucr_report, + re->ucr_data, size); + if (err) + return EIO; + break; + + case USB_GET_REPORT_ID: + *(int *)addr = sc->sc_report_id; break; - case USB_GET_DEVICEINFO_30: - MODULE_HOOK_CALL(usb_subr_fill_30_hook, - (usc->sc_udev, - (struct usb_device_info30 *)addr, 0, - usbd_devinfo_vp, usbd_printBCD), - enosys(), err); - if (err == 0) - return 0; - break; - case USB_GET_STRING_DESC: - { - struct usb_string_desc *si = (struct usb_string_desc *)addr; - err = usbd_get_string_desc(usc->sc_udev, - si->usd_string_index, - si->usd_language_id, &si->usd_desc, &size); - if (err) - return EINVAL; - break; - } + + default: + return uhidev_ioctl(sc->sc_hdev, cmd, addr, flag, l); + } + return 0; +} + +static int +uhidpoll(dev_t dev, int events, struct lwp *l) +{ + struct uhid_softc *sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); + int revents = 0; + + mutex_enter(&sc->sc_lock); + if (events & (POLLOUT | POLLWRNORM)) + revents |= events & (POLLOUT | POLLWRNORM); + if (events & (POLLIN | POLLRDNORM)) { + if (sc->sc_q.c_cc > 0) + revents |= events & (POLLIN | POLLRDNORM); + else + selrecord(l, &sc->sc_rsel); + } + mutex_exit(&sc->sc_lock); + + return revents; +} + +static void +filt_uhidrdetach(struct knote *kn) +{ + struct uhid_softc *sc = kn->kn_hook; + + mutex_enter(&sc->sc_lock); + selremove_knote(&sc->sc_rsel, kn); + mutex_exit(&sc->sc_lock); +} + +static int +filt_uhidread(struct knote *kn, long hint) +{ + struct uhid_softc *sc = kn->kn_hook; + + if (hint == NOTE_SUBMIT) + KASSERT(mutex_owned(&sc->sc_lock)); + else + mutex_enter(&sc->sc_lock); + + kn->kn_data = sc->sc_q.c_cc; + + if (hint == NOTE_SUBMIT) + KASSERT(mutex_owned(&sc->sc_lock)); + else + mutex_exit(&sc->sc_lock); + + return kn->kn_data > 0; +} + +static const struct filterops uhidread_filtops = { + .f_flags = FILTEROP_ISFD, + .f_attach = NULL, + .f_detach = filt_uhidrdetach, + .f_event = filt_uhidread, +}; + +static int +uhidkqfilter(dev_t dev, struct knote *kn) +{ + struct uhid_softc *sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); + + switch (kn->kn_filter) { + case EVFILT_READ: + kn->kn_fop = &uhidread_filtops; + kn->kn_hook = sc; + mutex_enter(&sc->sc_lock); + selrecord_knote(&sc->sc_rsel, kn); + mutex_exit(&sc->sc_lock); + return 0; + + case EVFILT_WRITE: + kn->kn_fop = &seltrue_filtops; + return 0; + default: return EINVAL; } - - return 0; } diff -r eae8bd032315 -r 19f84c00a20a sys/dev/usb/uhidev.c --- a/sys/dev/usb/uhidev.c Tue Jun 02 21:43:41 2026 +0000 +++ b/sys/dev/usb/uhidev.c Tue Jun 02 22:13:00 2026 +0000 @@ -38,6 +38,7 @@ __KERNEL_RCSID(0, "$NetBSD: uhidev.c,v 1.97 2025/12/07 19:59:51 jmcneill Exp $"); #ifdef _KERNEL_OPT +#include "opt_compat_netbsd.h" #include "opt_usb.h" #endif @@ -45,6 +46,7 @@ #include #include +#include #include #include #include @@ -89,7 +91,7 @@ struct uhidev_softc { void *sc_repdesc; u_int sc_nrepid; - struct uhidev { + struct uhidev_usb { struct uhidev_softc *sc_parent; device_t sc_dev; void (*sc_intr)(void *, void *, u_int); @@ -100,7 +102,7 @@ struct uhidev_softc { uint8_t sc_state; #define UHIDEV_OPEN 0x01 /* device is open */ #define UHIDEV_STOPPED 0x02 /* xfers are stopped */ - struct hidev_tag sc_hidev; + struct uhidev sc_uhidev; } *sc_subdevs; kmutex_t sc_lock; @@ -148,7 +150,7 @@ static void uhidev_attach(device_t, devi static void uhidev_childdet(device_t, device_t); static int uhidev_detach(device_t, int); -static void uhidev_init_tag(struct uhidev *); +static void uhidev_init_tag(struct uhidev_usb *, struct uhidev *); CFATTACH_DECL2_NEW(uhidev, sizeof(struct uhidev_softc), uhidev_match, uhidev_attach, uhidev_detach, NULL, NULL, uhidev_childdet); @@ -406,12 +408,12 @@ uhidev_attach(device_t parent, device_t DPRINTF(("uhidev_attach: isize=%d\n", sc->sc_isize)); for (repid = 0; repid < nrepid; repid++) { - struct uhidev *scd = &sc->sc_subdevs[repid]; + struct uhidev_usb *scd = &sc->sc_subdevs[repid]; scd->sc_parent = sc; scd->sc_report_id = repid; scd->sc_in_rep_size = repsizes[repid]; - uhidev_init_tag(scd); + uhidev_init_tag(scd, &scd->sc_uhidev); DPRINTF(("uhidev_match: try repid=%d\n", repid)); if (hid_report_size(desc, size, hid_input, repid) == 0 && @@ -419,9 +421,8 @@ uhidev_attach(device_t parent, device_t hid_report_size(desc, size, hid_feature, repid) == 0) { ; /* already NULL in sc->sc_subdevs[repid] */ } else { - uha.parent = scd; + uha.parent = &scd->sc_uhidev; uha.reportid = repid; - uha.hidev = &scd->sc_hidev; locs[UHIDBUSCF_REPORTID] = repid; dev = config_found(self, &uha, uhidevprint, @@ -544,7 +545,7 @@ static void uhidev_intr(struct usbd_xfer *xfer, void *addr, usbd_status status) { struct uhidev_softc *sc = addr; - struct uhidev *scd; + struct uhidev_usb *scd; u_char *p; u_int rep; uint32_t cc; @@ -602,9 +603,10 @@ uhidev_intr(struct usbd_xfer *xfer, void scd->sc_intr(scd->sc_cookie, p, cc); } -void -uhidev_get_report_desc(struct uhidev *scd, void **desc, int *size) +static void +uhidev_usb_get_report_desc(void *cookie, void **desc, int *size) { + struct uhidev_usb *scd = cookie; struct uhidev_softc *sc = scd->sc_parent; *desc = sc->sc_repdesc; @@ -857,10 +859,11 @@ uhidev_close_pipes(struct uhidev_softc * device_xname(sc->sc_dev), sc->sc_refcnt); } -int -uhidev_open(struct uhidev *scd, void (*intr)(void *, void *, u_int), - void *cookie) +static int +uhidev_usb_open(void *cookie, void (*intr)(void *, void *, u_int), + void *opencookie) { + struct uhidev_usb *scd = cookie; struct uhidev_softc *sc = scd->sc_parent; int error; @@ -879,7 +882,7 @@ uhidev_open(struct uhidev *scd, void (*i goto out; } scd->sc_intr = intr; - scd->sc_cookie = cookie; + scd->sc_cookie = opencookie; atomic_store_release(&scd->sc_state, scd->sc_state | UHIDEV_OPEN); /* Open the pipes which are shared by all report ids. */ @@ -914,9 +917,10 @@ out: if (error) { * May sleep but only for a short duration to wait for USB * transfer completion callbacks to run. */ -void -uhidev_stop(struct uhidev *scd) +static void +uhidev_usb_stop(void *cookie) { + struct uhidev_usb *scd = cookie; struct uhidev_softc *sc = scd->sc_parent; mutex_enter(&sc->sc_lock); @@ -961,15 +965,16 @@ out: mutex_exit(&sc->sc_lock); } /* - * uhidev_close(scd) + * uhidev_usb_close(scd) * * Close a uhidev previously opened with uhidev_open. If writes * had been stopped with uhidev_stop, allow writes at other report * ids again. */ -void -uhidev_close(struct uhidev *scd) +static void +uhidev_usb_close(void *cookie) { + struct uhidev_usb *scd = cookie; struct uhidev_softc *sc = scd->sc_parent; mutex_enter(&sc->sc_lock); @@ -1039,9 +1044,10 @@ uhidev_close(struct uhidev *scd) mutex_exit(&sc->sc_lock); } -usbd_status -uhidev_set_report(struct uhidev *scd, int type, void *data, int len) +static usbd_status +uhidev_usb_set_report(void *cookie, int type, void *data, int len) { + struct uhidev_usb *scd = cookie; char *buf; usbd_status retstat; @@ -1061,16 +1067,18 @@ uhidev_set_report(struct uhidev *scd, in return retstat; } -usbd_status -uhidev_get_report(struct uhidev *scd, int type, void *data, int len) +static usbd_status +uhidev_usb_get_report(void *cookie, int type, void *data, int len) { + struct uhidev_usb *scd = cookie; return usbd_get_report(scd->sc_parent->sc_iface, type, scd->sc_report_id, data, len); } -usbd_status -uhidev_write(struct uhidev *scd, void *data, int len) +static usbd_status +uhidev_usb_write(void *cookie, void *data, int len) { + struct uhidev_usb *scd = cookie; struct uhidev_softc *sc = scd->sc_parent; usbd_status err; @@ -1154,10 +1162,11 @@ uhidev_write_callback(struct usbd_xfer * (*writecallback)(xfer, writecookie, err); } -usbd_status -uhidev_write_async(struct uhidev *scd, void *data, int len, int flags, +static usbd_status +uhidev_usb_write_async(void *cookie, void *data, int len, int flags, int timo, usbd_callback writecallback, void *writecookie) { + struct uhidev_usb *scd = cookie; struct uhidev_softc *sc = scd->sc_parent; usbd_status err; @@ -1200,59 +1209,62 @@ out: mutex_exit(&sc->sc_lock); return err; } -static void -uhidev_hidev_get_report_desc(void *cookie, void **desc, int *size) +static int +uhidev_usb_ioctl(void *cookie, u_long cmd, void *addr, int flag, + struct lwp *l) { - uhidev_get_report_desc(cookie, desc, size); -} + struct uhidev_usb *scd = cookie; + struct uhidev_softc *sc = scd->sc_parent; + int size; + usbd_status err; + + switch (cmd) { + case USB_GET_DEVICE_DESC: + *(usb_device_descriptor_t *)addr = + *usbd_get_device_descriptor(sc->sc_udev); + break; -static int -uhidev_hidev_open(void *cookie, void (*intr)(void *, void *, u_int), void *arg) -{ - return uhidev_open(cookie, intr, arg); -} - -static void -uhidev_hidev_stop(void *cookie) -{ - uhidev_stop(cookie); + case USB_GET_DEVICEINFO: + usbd_fill_deviceinfo(sc->sc_udev, + (struct usb_device_info *)addr, 0); + break; + case USB_GET_DEVICEINFO_30: + MODULE_HOOK_CALL(usb_subr_fill_30_hook, + (sc->sc_udev, + (struct usb_device_info30 *)addr, 0, + usbd_devinfo_vp, usbd_printBCD), + enosys(), err); + if (err == 0) + return 0; + break; + case USB_GET_STRING_DESC: + { + struct usb_string_desc *si = (struct usb_string_desc *)addr; + err = usbd_get_string_desc(sc->sc_udev, + si->usd_string_index, + si->usd_language_id, &si->usd_desc, &size); + if (err) + return EINVAL; + break; + } + default: + return EINVAL; + } + return 0; } static void -uhidev_hidev_close(void *cookie) -{ - uhidev_close(cookie); -} - -static usbd_status -uhidev_hidev_set_report(void *cookie, int type, void *data, int len) -{ - return uhidev_set_report(cookie, type, data, len); -} - -static usbd_status -uhidev_hidev_get_report(void *cookie, int type, void *data, int len) +uhidev_init_tag(struct uhidev_usb *scd, struct uhidev *t) { - return uhidev_get_report(cookie, type, data, len); -} - -static usbd_status -uhidev_hidev_write(void *cookie, void *data, int len) -{ - return uhidev_write(cookie, data, len); -} - -static void -uhidev_init_tag(struct uhidev *scd) -{ - struct hidev_tag *t = &scd->sc_hidev; t->_cookie = scd; - t->_get_report_desc = uhidev_hidev_get_report_desc; - t->_open = uhidev_hidev_open; - t->_stop = uhidev_hidev_stop; - t->_close = uhidev_hidev_close; - t->_set_report = uhidev_hidev_set_report; - t->_get_report = uhidev_hidev_get_report; - t->_write = uhidev_hidev_write; + t->_get_report_desc = uhidev_usb_get_report_desc; + t->_open = uhidev_usb_open; + t->_stop = uhidev_usb_stop; + t->_close = uhidev_usb_close; + t->_set_report = uhidev_usb_set_report; + t->_get_report = uhidev_usb_get_report; + t->_write = uhidev_usb_write; + t->_write_async = uhidev_usb_write_async; + t->_ioctl = uhidev_usb_ioctl; } diff -r eae8bd032315 -r 19f84c00a20a sys/dev/usb/uhidev.h --- a/sys/dev/usb/uhidev.h Tue Jun 02 21:43:41 2026 +0000 +++ b/sys/dev/usb/uhidev.h Tue Jun 02 22:13:00 2026 +0000 @@ -33,15 +33,17 @@ #ifndef _DEV_USB_UHIDEV_H_ #define _DEV_USB_UHIDEV_H_ +#include + #include #include +struct lwp; struct uhidev; struct uhidev_attach_arg { struct usbif_attach_arg *uiaa; struct uhidev *parent; - struct hidev_tag *hidev; int reportid; }; @@ -54,6 +56,7 @@ usbd_status uhidev_get_report(struct uhi usbd_status uhidev_write(struct uhidev *, void *, int); usbd_status uhidev_write_async(struct uhidev *, void *, int, int, int, usbd_callback, void *); +int uhidev_ioctl(struct uhidev *, u_long, void *, int, struct lwp *); #define UHIDEV_OSIZE 64 #define UHIDEV_MAXREPID 255