xref: /freebsd/sys/dev/hid/hidbus.c (revision a05a680469a7ac77b195021fed74e3aa58152dd7)
12b4464b0SVladimir Kondratyev /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
32b4464b0SVladimir Kondratyev  *
42b4464b0SVladimir Kondratyev  * Copyright (c) 2019-2020 Vladimir Kondratyev <wulf@FreeBSD.org>
52b4464b0SVladimir Kondratyev  *
62b4464b0SVladimir Kondratyev  * Redistribution and use in source and binary forms, with or without
72b4464b0SVladimir Kondratyev  * modification, are permitted provided that the following conditions
82b4464b0SVladimir Kondratyev  * are met:
92b4464b0SVladimir Kondratyev  * 1. Redistributions of source code must retain the above copyright
102b4464b0SVladimir Kondratyev  *    notice, this list of conditions and the following disclaimer.
112b4464b0SVladimir Kondratyev  * 2. Redistributions in binary form must reproduce the above copyright
122b4464b0SVladimir Kondratyev  *    notice, this list of conditions and the following disclaimer in the
132b4464b0SVladimir Kondratyev  *    documentation and/or other materials provided with the distribution.
142b4464b0SVladimir Kondratyev  *
152b4464b0SVladimir Kondratyev  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
162b4464b0SVladimir Kondratyev  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
172b4464b0SVladimir Kondratyev  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
182b4464b0SVladimir Kondratyev  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
192b4464b0SVladimir Kondratyev  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
202b4464b0SVladimir Kondratyev  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
212b4464b0SVladimir Kondratyev  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
222b4464b0SVladimir Kondratyev  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
232b4464b0SVladimir Kondratyev  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
242b4464b0SVladimir Kondratyev  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
252b4464b0SVladimir Kondratyev  * SUCH DAMAGE.
262b4464b0SVladimir Kondratyev  */
272b4464b0SVladimir Kondratyev 
282b4464b0SVladimir Kondratyev #include <sys/param.h>
292b4464b0SVladimir Kondratyev #include <sys/bus.h>
302b4464b0SVladimir Kondratyev #include <sys/ck.h>
312b4464b0SVladimir Kondratyev #include <sys/epoch.h>
322b4464b0SVladimir Kondratyev #include <sys/kdb.h>
332b4464b0SVladimir Kondratyev #include <sys/kernel.h>
342b4464b0SVladimir Kondratyev #include <sys/libkern.h>
352b4464b0SVladimir Kondratyev #include <sys/lock.h>
362b4464b0SVladimir Kondratyev #include <sys/malloc.h>
372b4464b0SVladimir Kondratyev #include <sys/module.h>
382b4464b0SVladimir Kondratyev #include <sys/mutex.h>
392b4464b0SVladimir Kondratyev #include <sys/proc.h>
40ddfc9c4cSWarner Losh #include <sys/sbuf.h>
412b4464b0SVladimir Kondratyev #include <sys/sx.h>
42ddfc9c4cSWarner Losh #include <sys/systm.h>
432b4464b0SVladimir Kondratyev 
442b4464b0SVladimir Kondratyev #define	HID_DEBUG_VAR	hid_debug
452b4464b0SVladimir Kondratyev #include <dev/hid/hid.h>
462b4464b0SVladimir Kondratyev #include <dev/hid/hidbus.h>
472b4464b0SVladimir Kondratyev #include <dev/hid/hidquirk.h>
482b4464b0SVladimir Kondratyev 
492b4464b0SVladimir Kondratyev #include "hid_if.h"
502b4464b0SVladimir Kondratyev 
512b4464b0SVladimir Kondratyev #define	INPUT_EPOCH	global_epoch_preempt
522b4464b0SVladimir Kondratyev #define	HID_RSIZE_MAX	1024
532b4464b0SVladimir Kondratyev 
542b4464b0SVladimir Kondratyev static hid_intr_t	hidbus_intr;
552b4464b0SVladimir Kondratyev 
562b4464b0SVladimir Kondratyev static device_probe_t	hidbus_probe;
572b4464b0SVladimir Kondratyev static device_attach_t	hidbus_attach;
582b4464b0SVladimir Kondratyev static device_detach_t	hidbus_detach;
592b4464b0SVladimir Kondratyev 
602b4464b0SVladimir Kondratyev struct hidbus_ivars {
612b4464b0SVladimir Kondratyev 	int32_t				usage;
622b4464b0SVladimir Kondratyev 	uint8_t				index;
632b4464b0SVladimir Kondratyev 	uint32_t			flags;
642b4464b0SVladimir Kondratyev 	uintptr_t			driver_info;    /* for internal use */
652b4464b0SVladimir Kondratyev 	struct mtx			*mtx;		/* child intr mtx */
662b4464b0SVladimir Kondratyev 	hid_intr_t			*intr_handler;	/* executed under mtx*/
672b4464b0SVladimir Kondratyev 	void				*intr_ctx;
682b4464b0SVladimir Kondratyev 	unsigned int			refcnt;		/* protected by mtx */
692b4464b0SVladimir Kondratyev 	struct epoch_context		epoch_ctx;
702b4464b0SVladimir Kondratyev 	CK_STAILQ_ENTRY(hidbus_ivars)	link;
712b4464b0SVladimir Kondratyev };
722b4464b0SVladimir Kondratyev 
732b4464b0SVladimir Kondratyev struct hidbus_softc {
742b4464b0SVladimir Kondratyev 	device_t			dev;
752b4464b0SVladimir Kondratyev 	struct sx			sx;
762b4464b0SVladimir Kondratyev 	struct mtx			mtx;
772b4464b0SVladimir Kondratyev 
782b4464b0SVladimir Kondratyev 	bool				nowrite;
792b4464b0SVladimir Kondratyev 
802b4464b0SVladimir Kondratyev 	struct hid_rdesc_info		rdesc;
812b4464b0SVladimir Kondratyev 	bool				overloaded;
822b4464b0SVladimir Kondratyev 	int				nest;	/* Child attach nesting lvl */
832b4464b0SVladimir Kondratyev 	int				nauto;	/* Number of autochildren */
842b4464b0SVladimir Kondratyev 
852b4464b0SVladimir Kondratyev 	CK_STAILQ_HEAD(, hidbus_ivars)	tlcs;
862b4464b0SVladimir Kondratyev };
872b4464b0SVladimir Kondratyev 
882b4464b0SVladimir Kondratyev static int
892b4464b0SVladimir Kondratyev hidbus_fill_rdesc_info(struct hid_rdesc_info *hri, const void *data,
902b4464b0SVladimir Kondratyev     hid_size_t len)
912b4464b0SVladimir Kondratyev {
922b4464b0SVladimir Kondratyev 	int error = 0;
932b4464b0SVladimir Kondratyev 
942b4464b0SVladimir Kondratyev 	hri->data = __DECONST(void *, data);
952b4464b0SVladimir Kondratyev 	hri->len = len;
962b4464b0SVladimir Kondratyev 
972b4464b0SVladimir Kondratyev 	/*
982b4464b0SVladimir Kondratyev 	 * If report descriptor is not available yet, set maximal
992b4464b0SVladimir Kondratyev 	 * report sizes high enough to allow hidraw to work.
1002b4464b0SVladimir Kondratyev 	 */
1012b4464b0SVladimir Kondratyev 	hri->isize = len == 0 ? HID_RSIZE_MAX :
1022b4464b0SVladimir Kondratyev 	    hid_report_size_max(data, len, hid_input, &hri->iid);
1032b4464b0SVladimir Kondratyev 	hri->osize = len == 0 ? HID_RSIZE_MAX :
1042b4464b0SVladimir Kondratyev 	    hid_report_size_max(data, len, hid_output, &hri->oid);
1052b4464b0SVladimir Kondratyev 	hri->fsize = len == 0 ? HID_RSIZE_MAX :
1062b4464b0SVladimir Kondratyev 	    hid_report_size_max(data, len, hid_feature, &hri->fid);
1072b4464b0SVladimir Kondratyev 
1082b4464b0SVladimir Kondratyev 	if (hri->isize > HID_RSIZE_MAX) {
1092b4464b0SVladimir Kondratyev 		DPRINTF("input size is too large, %u bytes (truncating)\n",
1102b4464b0SVladimir Kondratyev 		    hri->isize);
1112b4464b0SVladimir Kondratyev 		hri->isize = HID_RSIZE_MAX;
1122b4464b0SVladimir Kondratyev 		error = EOVERFLOW;
1132b4464b0SVladimir Kondratyev 	}
1142b4464b0SVladimir Kondratyev 	if (hri->osize > HID_RSIZE_MAX) {
1152b4464b0SVladimir Kondratyev 		DPRINTF("output size is too large, %u bytes (truncating)\n",
1162b4464b0SVladimir Kondratyev 		    hri->osize);
1172b4464b0SVladimir Kondratyev 		hri->osize = HID_RSIZE_MAX;
1182b4464b0SVladimir Kondratyev 		error = EOVERFLOW;
1192b4464b0SVladimir Kondratyev 	}
1202b4464b0SVladimir Kondratyev 	if (hri->fsize > HID_RSIZE_MAX) {
1212b4464b0SVladimir Kondratyev 		DPRINTF("feature size is too large, %u bytes (truncating)\n",
1222b4464b0SVladimir Kondratyev 		    hri->fsize);
1232b4464b0SVladimir Kondratyev 		hri->fsize = HID_RSIZE_MAX;
1242b4464b0SVladimir Kondratyev 		error = EOVERFLOW;
1252b4464b0SVladimir Kondratyev 	}
1262b4464b0SVladimir Kondratyev 
1272b4464b0SVladimir Kondratyev 	return (error);
1282b4464b0SVladimir Kondratyev }
1292b4464b0SVladimir Kondratyev 
1302b4464b0SVladimir Kondratyev int
1312b4464b0SVladimir Kondratyev hidbus_locate(const void *desc, hid_size_t size, int32_t u, enum hid_kind k,
1322b4464b0SVladimir Kondratyev     uint8_t tlc_index, uint8_t index, struct hid_location *loc,
1332b4464b0SVladimir Kondratyev     uint32_t *flags, uint8_t *id, struct hid_absinfo *ai)
1342b4464b0SVladimir Kondratyev {
1352b4464b0SVladimir Kondratyev 	struct hid_data *d;
1362b4464b0SVladimir Kondratyev 	struct hid_item h;
1372b4464b0SVladimir Kondratyev 	int i;
1382b4464b0SVladimir Kondratyev 
1392b4464b0SVladimir Kondratyev 	d = hid_start_parse(desc, size, 1 << k);
1402b4464b0SVladimir Kondratyev 	HIDBUS_FOREACH_ITEM(d, &h, tlc_index) {
1412b4464b0SVladimir Kondratyev 		for (i = 0; i < h.nusages; i++) {
1422b4464b0SVladimir Kondratyev 			if (h.kind == k && h.usages[i] == u) {
1432b4464b0SVladimir Kondratyev 				if (index--)
1442b4464b0SVladimir Kondratyev 					break;
1452b4464b0SVladimir Kondratyev 				if (loc != NULL)
1462b4464b0SVladimir Kondratyev 					*loc = h.loc;
1472b4464b0SVladimir Kondratyev 				if (flags != NULL)
1482b4464b0SVladimir Kondratyev 					*flags = h.flags;
1492b4464b0SVladimir Kondratyev 				if (id != NULL)
1502b4464b0SVladimir Kondratyev 					*id = h.report_ID;
1512b4464b0SVladimir Kondratyev 				if (ai != NULL && (h.flags&HIO_RELATIVE) == 0)
1522b4464b0SVladimir Kondratyev 					*ai = (struct hid_absinfo) {
1532b4464b0SVladimir Kondratyev 					    .max = h.logical_maximum,
1542b4464b0SVladimir Kondratyev 					    .min = h.logical_minimum,
1552b4464b0SVladimir Kondratyev 					    .res = hid_item_resolution(&h),
1562b4464b0SVladimir Kondratyev 					};
1572b4464b0SVladimir Kondratyev 				hid_end_parse(d);
1582b4464b0SVladimir Kondratyev 				return (1);
1592b4464b0SVladimir Kondratyev 			}
1602b4464b0SVladimir Kondratyev 		}
1612b4464b0SVladimir Kondratyev 	}
1622b4464b0SVladimir Kondratyev 	if (loc != NULL)
1632b4464b0SVladimir Kondratyev 		loc->size = 0;
1642b4464b0SVladimir Kondratyev 	if (flags != NULL)
1652b4464b0SVladimir Kondratyev 		*flags = 0;
1662b4464b0SVladimir Kondratyev 	if (id != NULL)
1672b4464b0SVladimir Kondratyev 		*id = 0;
1682b4464b0SVladimir Kondratyev 	hid_end_parse(d);
1692b4464b0SVladimir Kondratyev 	return (0);
1702b4464b0SVladimir Kondratyev }
1712b4464b0SVladimir Kondratyev 
172d51e4376SVladimir Kondratyev bool
173d51e4376SVladimir Kondratyev hidbus_is_collection(const void *desc, hid_size_t size, int32_t usage,
174d51e4376SVladimir Kondratyev     uint8_t tlc_index)
175d51e4376SVladimir Kondratyev {
176d51e4376SVladimir Kondratyev 	struct hid_data *d;
177d51e4376SVladimir Kondratyev 	struct hid_item h;
178d51e4376SVladimir Kondratyev 	bool ret = false;
179d51e4376SVladimir Kondratyev 
180d51e4376SVladimir Kondratyev 	d = hid_start_parse(desc, size, 0);
181d51e4376SVladimir Kondratyev 	HIDBUS_FOREACH_ITEM(d, &h, tlc_index) {
182d51e4376SVladimir Kondratyev 		if (h.kind == hid_collection && h.usage == usage) {
183d51e4376SVladimir Kondratyev 			ret = true;
184d51e4376SVladimir Kondratyev 			break;
185d51e4376SVladimir Kondratyev 		}
186d51e4376SVladimir Kondratyev 	}
187d51e4376SVladimir Kondratyev 	hid_end_parse(d);
188d51e4376SVladimir Kondratyev 	return (ret);
189d51e4376SVladimir Kondratyev }
190d51e4376SVladimir Kondratyev 
1912b4464b0SVladimir Kondratyev static device_t
1922b4464b0SVladimir Kondratyev hidbus_add_child(device_t dev, u_int order, const char *name, int unit)
1932b4464b0SVladimir Kondratyev {
1942b4464b0SVladimir Kondratyev 	struct hidbus_softc *sc = device_get_softc(dev);
1952b4464b0SVladimir Kondratyev 	struct hidbus_ivars *tlc;
1962b4464b0SVladimir Kondratyev 	device_t child;
1972b4464b0SVladimir Kondratyev 
1982b4464b0SVladimir Kondratyev 	child = device_add_child_ordered(dev, order, name, unit);
1992b4464b0SVladimir Kondratyev 	if (child == NULL)
2002b4464b0SVladimir Kondratyev 			return (child);
2012b4464b0SVladimir Kondratyev 
2022b4464b0SVladimir Kondratyev 	tlc = malloc(sizeof(struct hidbus_ivars), M_DEVBUF, M_WAITOK | M_ZERO);
2032b4464b0SVladimir Kondratyev 	tlc->mtx = &sc->mtx;
2042b4464b0SVladimir Kondratyev 	device_set_ivars(child, tlc);
2052b4464b0SVladimir Kondratyev 	sx_xlock(&sc->sx);
2062b4464b0SVladimir Kondratyev 	CK_STAILQ_INSERT_TAIL(&sc->tlcs, tlc, link);
2072b4464b0SVladimir Kondratyev 	sx_unlock(&sc->sx);
2082b4464b0SVladimir Kondratyev 
2092b4464b0SVladimir Kondratyev 	return (child);
2102b4464b0SVladimir Kondratyev }
2112b4464b0SVladimir Kondratyev 
2122b4464b0SVladimir Kondratyev static int
2132b4464b0SVladimir Kondratyev hidbus_enumerate_children(device_t dev, const void* data, hid_size_t len)
2142b4464b0SVladimir Kondratyev {
2152b4464b0SVladimir Kondratyev 	struct hidbus_softc *sc = device_get_softc(dev);
2162b4464b0SVladimir Kondratyev 	struct hid_data *hd;
2172b4464b0SVladimir Kondratyev 	struct hid_item hi;
2182b4464b0SVladimir Kondratyev 	device_t child;
2192b4464b0SVladimir Kondratyev 	uint8_t index = 0;
2202b4464b0SVladimir Kondratyev 
2212b4464b0SVladimir Kondratyev 	if (data == NULL || len == 0)
2222b4464b0SVladimir Kondratyev 		return (ENXIO);
2232b4464b0SVladimir Kondratyev 
2242b4464b0SVladimir Kondratyev 	/* Add a child for each top level collection */
2252b4464b0SVladimir Kondratyev 	hd = hid_start_parse(data, len, 1 << hid_input);
2262b4464b0SVladimir Kondratyev 	while (hid_get_item(hd, &hi)) {
2272b4464b0SVladimir Kondratyev 		if (hi.kind != hid_collection || hi.collevel != 1)
2282b4464b0SVladimir Kondratyev 			continue;
229*a05a6804SWarner Losh 		child = BUS_ADD_CHILD(dev, 0, NULL, DEVICE_UNIT_ANY);
2302b4464b0SVladimir Kondratyev 		if (child == NULL) {
2312b4464b0SVladimir Kondratyev 			device_printf(dev, "Could not add HID device\n");
2322b4464b0SVladimir Kondratyev 			continue;
2332b4464b0SVladimir Kondratyev 		}
2342b4464b0SVladimir Kondratyev 		hidbus_set_index(child, index);
2352b4464b0SVladimir Kondratyev 		hidbus_set_usage(child, hi.usage);
2362b4464b0SVladimir Kondratyev 		hidbus_set_flags(child, HIDBUS_FLAG_AUTOCHILD);
2372b4464b0SVladimir Kondratyev 		index++;
2382b4464b0SVladimir Kondratyev 		DPRINTF("Add child TLC: 0x%04x:0x%04x\n",
2392b4464b0SVladimir Kondratyev 		    HID_GET_USAGE_PAGE(hi.usage), HID_GET_USAGE(hi.usage));
2402b4464b0SVladimir Kondratyev 	}
2412b4464b0SVladimir Kondratyev 	hid_end_parse(hd);
2422b4464b0SVladimir Kondratyev 
2432b4464b0SVladimir Kondratyev 	if (index == 0)
2442b4464b0SVladimir Kondratyev 		return (ENXIO);
2452b4464b0SVladimir Kondratyev 
2462b4464b0SVladimir Kondratyev 	sc->nauto = index;
2472b4464b0SVladimir Kondratyev 
2482b4464b0SVladimir Kondratyev 	return (0);
2492b4464b0SVladimir Kondratyev }
2502b4464b0SVladimir Kondratyev 
2512b4464b0SVladimir Kondratyev static int
2522b4464b0SVladimir Kondratyev hidbus_attach_children(device_t dev)
2532b4464b0SVladimir Kondratyev {
2542b4464b0SVladimir Kondratyev 	struct hidbus_softc *sc = device_get_softc(dev);
2552b4464b0SVladimir Kondratyev 	int error;
2562b4464b0SVladimir Kondratyev 
2574b171281SVladimir Kondratyev 	HID_INTR_SETUP(device_get_parent(dev), dev, hidbus_intr, sc,
2584b171281SVladimir Kondratyev 	    &sc->rdesc);
2592b4464b0SVladimir Kondratyev 
2602b4464b0SVladimir Kondratyev 	error = hidbus_enumerate_children(dev, sc->rdesc.data, sc->rdesc.len);
2612b4464b0SVladimir Kondratyev 	if (error != 0)
2622b4464b0SVladimir Kondratyev 		DPRINTF("failed to enumerate children: error %d\n", error);
2632b4464b0SVladimir Kondratyev 
2642b4464b0SVladimir Kondratyev 	/*
2652b4464b0SVladimir Kondratyev 	 * hidbus_attach_children() can recurse through device_identify->
2662b4464b0SVladimir Kondratyev 	 * hid_set_report_descr() call sequence. Do not perform children
2672b4464b0SVladimir Kondratyev 	 * attach twice in that case.
2682b4464b0SVladimir Kondratyev 	 */
2692b4464b0SVladimir Kondratyev 	sc->nest++;
2702b4464b0SVladimir Kondratyev 	bus_generic_probe(dev);
2712b4464b0SVladimir Kondratyev 	sc->nest--;
2722b4464b0SVladimir Kondratyev 	if (sc->nest != 0)
2732b4464b0SVladimir Kondratyev 		return (0);
2742b4464b0SVladimir Kondratyev 
2752b4464b0SVladimir Kondratyev 	if (hid_is_keyboard(sc->rdesc.data, sc->rdesc.len) != 0)
2762b4464b0SVladimir Kondratyev 		error = bus_generic_attach(dev);
2772b4464b0SVladimir Kondratyev 	else
2782b4464b0SVladimir Kondratyev 		error = bus_delayed_attach_children(dev);
2792b4464b0SVladimir Kondratyev 	if (error != 0)
2802b4464b0SVladimir Kondratyev 		device_printf(dev, "failed to attach child: error %d\n", error);
2812b4464b0SVladimir Kondratyev 
2822b4464b0SVladimir Kondratyev 	return (error);
2832b4464b0SVladimir Kondratyev }
2842b4464b0SVladimir Kondratyev 
2852b4464b0SVladimir Kondratyev static int
2862b4464b0SVladimir Kondratyev hidbus_detach_children(device_t dev)
2872b4464b0SVladimir Kondratyev {
2882b4464b0SVladimir Kondratyev 	device_t *children, bus;
2892b4464b0SVladimir Kondratyev 	bool is_bus;
2902b4464b0SVladimir Kondratyev 	int i, error;
2912b4464b0SVladimir Kondratyev 
2922b4464b0SVladimir Kondratyev 	error = 0;
2932b4464b0SVladimir Kondratyev 
294e9d92100SJohn Baldwin 	is_bus = device_get_devclass(dev) == devclass_find("hidbus");
2952b4464b0SVladimir Kondratyev 	bus = is_bus ? dev : device_get_parent(dev);
2962b4464b0SVladimir Kondratyev 
297e9d92100SJohn Baldwin 	KASSERT(device_get_devclass(bus) == devclass_find("hidbus"),
2982b4464b0SVladimir Kondratyev 	    ("Device is not hidbus or it's child"));
2992b4464b0SVladimir Kondratyev 
3002b4464b0SVladimir Kondratyev 	if (is_bus) {
3012b4464b0SVladimir Kondratyev 		/* If hidbus is passed, delete all children. */
3022b4464b0SVladimir Kondratyev 		bus_generic_detach(bus);
3032b4464b0SVladimir Kondratyev 		device_delete_children(bus);
3042b4464b0SVladimir Kondratyev 	} else {
3052b4464b0SVladimir Kondratyev 		/*
3062b4464b0SVladimir Kondratyev 		 * If hidbus child is passed, delete all hidbus children
3072b4464b0SVladimir Kondratyev 		 * except caller. Deleting the caller may result in deadlock.
3082b4464b0SVladimir Kondratyev 		 */
3092b4464b0SVladimir Kondratyev 		error = device_get_children(bus, &children, &i);
3102b4464b0SVladimir Kondratyev 		if (error != 0)
3112b4464b0SVladimir Kondratyev 			return (error);
3122b4464b0SVladimir Kondratyev 		while (i-- > 0) {
3132b4464b0SVladimir Kondratyev 			if (children[i] == dev)
3142b4464b0SVladimir Kondratyev 				continue;
3152b4464b0SVladimir Kondratyev 			DPRINTF("Delete child. index=%d (%s)\n",
3162b4464b0SVladimir Kondratyev 			    hidbus_get_index(children[i]),
3172b4464b0SVladimir Kondratyev 			    device_get_nameunit(children[i]));
3182b4464b0SVladimir Kondratyev 			error = device_delete_child(bus, children[i]);
3192b4464b0SVladimir Kondratyev 			if (error) {
3202b4464b0SVladimir Kondratyev 				DPRINTF("Failed deleting %s\n",
3212b4464b0SVladimir Kondratyev 				    device_get_nameunit(children[i]));
3222b4464b0SVladimir Kondratyev 				break;
3232b4464b0SVladimir Kondratyev 			}
3242b4464b0SVladimir Kondratyev 		}
3252b4464b0SVladimir Kondratyev 		free(children, M_TEMP);
3262b4464b0SVladimir Kondratyev 	}
3272b4464b0SVladimir Kondratyev 
3284b171281SVladimir Kondratyev 	HID_INTR_UNSETUP(device_get_parent(bus), bus);
3292b4464b0SVladimir Kondratyev 
3302b4464b0SVladimir Kondratyev 	return (error);
3312b4464b0SVladimir Kondratyev }
3322b4464b0SVladimir Kondratyev 
3332b4464b0SVladimir Kondratyev static int
3342b4464b0SVladimir Kondratyev hidbus_probe(device_t dev)
3352b4464b0SVladimir Kondratyev {
3362b4464b0SVladimir Kondratyev 
3372b4464b0SVladimir Kondratyev 	device_set_desc(dev, "HID bus");
3382b4464b0SVladimir Kondratyev 
3392b4464b0SVladimir Kondratyev 	/* Allow other subclasses to override this driver. */
3402b4464b0SVladimir Kondratyev 	return (BUS_PROBE_GENERIC);
3412b4464b0SVladimir Kondratyev }
3422b4464b0SVladimir Kondratyev 
3432b4464b0SVladimir Kondratyev static int
3442b4464b0SVladimir Kondratyev hidbus_attach(device_t dev)
3452b4464b0SVladimir Kondratyev {
3462b4464b0SVladimir Kondratyev 	struct hidbus_softc *sc = device_get_softc(dev);
3472b4464b0SVladimir Kondratyev 	struct hid_device_info *devinfo = device_get_ivars(dev);
3482b4464b0SVladimir Kondratyev 	void *d_ptr = NULL;
3492b4464b0SVladimir Kondratyev 	hid_size_t d_len;
3502b4464b0SVladimir Kondratyev 	int error;
3512b4464b0SVladimir Kondratyev 
3522b4464b0SVladimir Kondratyev 	sc->dev = dev;
3532b4464b0SVladimir Kondratyev 	CK_STAILQ_INIT(&sc->tlcs);
3542b4464b0SVladimir Kondratyev 	mtx_init(&sc->mtx, "hidbus ivar lock", NULL, MTX_DEF);
3552b4464b0SVladimir Kondratyev 	sx_init(&sc->sx, "hidbus ivar list lock");
3562b4464b0SVladimir Kondratyev 
3572b4464b0SVladimir Kondratyev 	/*
3582b4464b0SVladimir Kondratyev 	 * Ignore error. It is possible for non-HID device e.g. XBox360 gamepad
3592b4464b0SVladimir Kondratyev 	 * to emulate HID through overloading of report descriptor.
3602b4464b0SVladimir Kondratyev 	 */
3612b4464b0SVladimir Kondratyev 	d_len = devinfo->rdescsize;
3622b4464b0SVladimir Kondratyev 	if (d_len != 0) {
3632b4464b0SVladimir Kondratyev 		d_ptr = malloc(d_len, M_DEVBUF, M_ZERO | M_WAITOK);
3642b4464b0SVladimir Kondratyev 		error = hid_get_rdesc(dev, d_ptr, d_len);
3652b4464b0SVladimir Kondratyev 		if (error != 0) {
3662b4464b0SVladimir Kondratyev 			free(d_ptr, M_DEVBUF);
3672b4464b0SVladimir Kondratyev 			d_len = 0;
3682b4464b0SVladimir Kondratyev 			d_ptr = NULL;
3692b4464b0SVladimir Kondratyev 		}
3702b4464b0SVladimir Kondratyev 	}
3712b4464b0SVladimir Kondratyev 
3722b4464b0SVladimir Kondratyev 	hidbus_fill_rdesc_info(&sc->rdesc, d_ptr, d_len);
3732b4464b0SVladimir Kondratyev 
3742b4464b0SVladimir Kondratyev 	sc->nowrite = hid_test_quirk(devinfo, HQ_NOWRITE);
3752b4464b0SVladimir Kondratyev 
3762b4464b0SVladimir Kondratyev 	error = hidbus_attach_children(dev);
3772b4464b0SVladimir Kondratyev 	if (error != 0) {
3782b4464b0SVladimir Kondratyev 		hidbus_detach(dev);
3792b4464b0SVladimir Kondratyev 		return (ENXIO);
3802b4464b0SVladimir Kondratyev 	}
3812b4464b0SVladimir Kondratyev 
3822b4464b0SVladimir Kondratyev 	return (0);
3832b4464b0SVladimir Kondratyev }
3842b4464b0SVladimir Kondratyev 
3852b4464b0SVladimir Kondratyev static int
3862b4464b0SVladimir Kondratyev hidbus_detach(device_t dev)
3872b4464b0SVladimir Kondratyev {
3882b4464b0SVladimir Kondratyev 	struct hidbus_softc *sc = device_get_softc(dev);
3892b4464b0SVladimir Kondratyev 
3902b4464b0SVladimir Kondratyev 	hidbus_detach_children(dev);
3912b4464b0SVladimir Kondratyev 	sx_destroy(&sc->sx);
3922b4464b0SVladimir Kondratyev 	mtx_destroy(&sc->mtx);
3932b4464b0SVladimir Kondratyev 	free(sc->rdesc.data, M_DEVBUF);
3942b4464b0SVladimir Kondratyev 
3952b4464b0SVladimir Kondratyev 	return (0);
3962b4464b0SVladimir Kondratyev }
3972b4464b0SVladimir Kondratyev 
3982b4464b0SVladimir Kondratyev static void
3992b4464b0SVladimir Kondratyev hidbus_child_detached(device_t bus, device_t child)
4002b4464b0SVladimir Kondratyev {
4012b4464b0SVladimir Kondratyev 	struct hidbus_softc *sc = device_get_softc(bus);
4022b4464b0SVladimir Kondratyev 	struct hidbus_ivars *tlc = device_get_ivars(child);
4032b4464b0SVladimir Kondratyev 
4042b4464b0SVladimir Kondratyev 	KASSERT(tlc->refcnt == 0, ("Child device is running"));
4052b4464b0SVladimir Kondratyev 	tlc->mtx = &sc->mtx;
4062b4464b0SVladimir Kondratyev 	tlc->intr_handler = NULL;
4072b4464b0SVladimir Kondratyev 	tlc->flags &= ~HIDBUS_FLAG_CAN_POLL;
4082b4464b0SVladimir Kondratyev }
4092b4464b0SVladimir Kondratyev 
4102b4464b0SVladimir Kondratyev /*
4112b4464b0SVladimir Kondratyev  * Epoch callback indicating tlc is safe to destroy
4122b4464b0SVladimir Kondratyev  */
4132b4464b0SVladimir Kondratyev static void
4142b4464b0SVladimir Kondratyev hidbus_ivar_dtor(epoch_context_t ctx)
4152b4464b0SVladimir Kondratyev {
4162b4464b0SVladimir Kondratyev 	struct hidbus_ivars *tlc;
4172b4464b0SVladimir Kondratyev 
4182b4464b0SVladimir Kondratyev 	tlc = __containerof(ctx, struct hidbus_ivars, epoch_ctx);
4192b4464b0SVladimir Kondratyev 	free(tlc, M_DEVBUF);
4202b4464b0SVladimir Kondratyev }
4212b4464b0SVladimir Kondratyev 
4222b4464b0SVladimir Kondratyev static void
4232b4464b0SVladimir Kondratyev hidbus_child_deleted(device_t bus, device_t child)
4242b4464b0SVladimir Kondratyev {
4252b4464b0SVladimir Kondratyev 	struct hidbus_softc *sc = device_get_softc(bus);
4262b4464b0SVladimir Kondratyev 	struct hidbus_ivars *tlc = device_get_ivars(child);
4272b4464b0SVladimir Kondratyev 
4282b4464b0SVladimir Kondratyev 	sx_xlock(&sc->sx);
4292b4464b0SVladimir Kondratyev 	KASSERT(tlc->refcnt == 0, ("Child device is running"));
4302b4464b0SVladimir Kondratyev 	CK_STAILQ_REMOVE(&sc->tlcs, tlc, hidbus_ivars, link);
4312b4464b0SVladimir Kondratyev 	sx_unlock(&sc->sx);
4322b4464b0SVladimir Kondratyev 	epoch_call(INPUT_EPOCH, hidbus_ivar_dtor, &tlc->epoch_ctx);
4332b4464b0SVladimir Kondratyev }
4342b4464b0SVladimir Kondratyev 
4352b4464b0SVladimir Kondratyev static int
4362b4464b0SVladimir Kondratyev hidbus_read_ivar(device_t bus, device_t child, int which, uintptr_t *result)
4372b4464b0SVladimir Kondratyev {
4382b4464b0SVladimir Kondratyev 	struct hidbus_softc *sc = device_get_softc(bus);
4392b4464b0SVladimir Kondratyev 	struct hidbus_ivars *tlc = device_get_ivars(child);
4402b4464b0SVladimir Kondratyev 
4412b4464b0SVladimir Kondratyev 	switch (which) {
4422b4464b0SVladimir Kondratyev 	case HIDBUS_IVAR_INDEX:
4432b4464b0SVladimir Kondratyev 		*result = tlc->index;
4442b4464b0SVladimir Kondratyev 		break;
4452b4464b0SVladimir Kondratyev 	case HIDBUS_IVAR_USAGE:
4462b4464b0SVladimir Kondratyev 		*result = tlc->usage;
4472b4464b0SVladimir Kondratyev 		break;
4482b4464b0SVladimir Kondratyev 	case HIDBUS_IVAR_FLAGS:
4492b4464b0SVladimir Kondratyev 		*result = tlc->flags;
4502b4464b0SVladimir Kondratyev 		break;
4512b4464b0SVladimir Kondratyev 	case HIDBUS_IVAR_DRIVER_INFO:
4522b4464b0SVladimir Kondratyev 		*result = tlc->driver_info;
4532b4464b0SVladimir Kondratyev 		break;
4542b4464b0SVladimir Kondratyev 	case HIDBUS_IVAR_LOCK:
4552b4464b0SVladimir Kondratyev 		*result = (uintptr_t)(tlc->mtx == &sc->mtx ? NULL : tlc->mtx);
4562b4464b0SVladimir Kondratyev 		break;
4572b4464b0SVladimir Kondratyev 	default:
4582b4464b0SVladimir Kondratyev 		return (EINVAL);
4592b4464b0SVladimir Kondratyev 	}
4602b4464b0SVladimir Kondratyev 	return (0);
4612b4464b0SVladimir Kondratyev }
4622b4464b0SVladimir Kondratyev 
4632b4464b0SVladimir Kondratyev static int
4642b4464b0SVladimir Kondratyev hidbus_write_ivar(device_t bus, device_t child, int which, uintptr_t value)
4652b4464b0SVladimir Kondratyev {
4662b4464b0SVladimir Kondratyev 	struct hidbus_softc *sc = device_get_softc(bus);
4672b4464b0SVladimir Kondratyev 	struct hidbus_ivars *tlc = device_get_ivars(child);
4682b4464b0SVladimir Kondratyev 
4692b4464b0SVladimir Kondratyev 	switch (which) {
4702b4464b0SVladimir Kondratyev 	case HIDBUS_IVAR_INDEX:
4712b4464b0SVladimir Kondratyev 		tlc->index = value;
4722b4464b0SVladimir Kondratyev 		break;
4732b4464b0SVladimir Kondratyev 	case HIDBUS_IVAR_USAGE:
4742b4464b0SVladimir Kondratyev 		tlc->usage = value;
4752b4464b0SVladimir Kondratyev 		break;
4762b4464b0SVladimir Kondratyev 	case HIDBUS_IVAR_FLAGS:
4772b4464b0SVladimir Kondratyev 		tlc->flags = value;
4789aa0e5afSVladimir Kondratyev 		if ((value & HIDBUS_FLAG_CAN_POLL) != 0)
4799aa0e5afSVladimir Kondratyev 			HID_INTR_SETUP(
4804b171281SVladimir Kondratyev 			    device_get_parent(bus), bus, NULL, NULL, NULL);
4812b4464b0SVladimir Kondratyev 		break;
4822b4464b0SVladimir Kondratyev 	case HIDBUS_IVAR_DRIVER_INFO:
4832b4464b0SVladimir Kondratyev 		tlc->driver_info = value;
4842b4464b0SVladimir Kondratyev 		break;
4852b4464b0SVladimir Kondratyev 	case HIDBUS_IVAR_LOCK:
4862b4464b0SVladimir Kondratyev 		tlc->mtx = (struct mtx *)value == NULL ?
4872b4464b0SVladimir Kondratyev 		    &sc->mtx : (struct mtx *)value;
4882b4464b0SVladimir Kondratyev 		break;
4892b4464b0SVladimir Kondratyev 	default:
4902b4464b0SVladimir Kondratyev 		return (EINVAL);
4912b4464b0SVladimir Kondratyev 	}
4922b4464b0SVladimir Kondratyev 	return (0);
4932b4464b0SVladimir Kondratyev }
4942b4464b0SVladimir Kondratyev 
4952b4464b0SVladimir Kondratyev /* Location hint for devctl(8) */
4962b4464b0SVladimir Kondratyev static int
497ddfc9c4cSWarner Losh hidbus_child_location(device_t bus, device_t child, struct sbuf *sb)
4982b4464b0SVladimir Kondratyev {
4992b4464b0SVladimir Kondratyev 	struct hidbus_ivars *tlc = device_get_ivars(child);
5002b4464b0SVladimir Kondratyev 
501ddfc9c4cSWarner Losh 	sbuf_printf(sb, "index=%hhu", tlc->index);
5022b4464b0SVladimir Kondratyev         return (0);
5032b4464b0SVladimir Kondratyev }
5042b4464b0SVladimir Kondratyev 
5052b4464b0SVladimir Kondratyev /* PnP information for devctl(8) */
5062b4464b0SVladimir Kondratyev static int
507ddfc9c4cSWarner Losh hidbus_child_pnpinfo(device_t bus, device_t child, struct sbuf *sb)
5082b4464b0SVladimir Kondratyev {
5092b4464b0SVladimir Kondratyev 	struct hidbus_ivars *tlc = device_get_ivars(child);
5102b4464b0SVladimir Kondratyev 	struct hid_device_info *devinfo = device_get_ivars(bus);
5112b4464b0SVladimir Kondratyev 
512ddfc9c4cSWarner Losh 	sbuf_printf(sb, "page=0x%04x usage=0x%04x bus=0x%02hx "
5132b4464b0SVladimir Kondratyev 	    "vendor=0x%04hx product=0x%04hx version=0x%04hx%s%s",
5142b4464b0SVladimir Kondratyev 	    HID_GET_USAGE_PAGE(tlc->usage), HID_GET_USAGE(tlc->usage),
5152b4464b0SVladimir Kondratyev 	    devinfo->idBus, devinfo->idVendor, devinfo->idProduct,
5162b4464b0SVladimir Kondratyev 	    devinfo->idVersion, devinfo->idPnP[0] == '\0' ? "" : " _HID=",
5172b4464b0SVladimir Kondratyev 	    devinfo->idPnP[0] == '\0' ? "" : devinfo->idPnP);
5182b4464b0SVladimir Kondratyev 	return (0);
5192b4464b0SVladimir Kondratyev }
5202b4464b0SVladimir Kondratyev 
5212b4464b0SVladimir Kondratyev void
5222b4464b0SVladimir Kondratyev hidbus_set_desc(device_t child, const char *suffix)
5232b4464b0SVladimir Kondratyev {
5242b4464b0SVladimir Kondratyev 	device_t bus = device_get_parent(child);
5252b4464b0SVladimir Kondratyev 	struct hidbus_softc *sc = device_get_softc(bus);
5262b4464b0SVladimir Kondratyev 	struct hid_device_info *devinfo = device_get_ivars(bus);
5272b4464b0SVladimir Kondratyev 	struct hidbus_ivars *tlc = device_get_ivars(child);
5282b4464b0SVladimir Kondratyev 
5292b4464b0SVladimir Kondratyev 	/* Do not add NULL suffix or if device name already contains it. */
5302b4464b0SVladimir Kondratyev 	if (suffix != NULL && strcasestr(devinfo->name, suffix) == NULL &&
5313f795763SMark Johnston 	    (sc->nauto > 1 || (tlc->flags & HIDBUS_FLAG_AUTOCHILD) == 0))
5323f795763SMark Johnston 		device_set_descf(child, "%s %s", devinfo->name, suffix);
5333f795763SMark Johnston 	else
5342b4464b0SVladimir Kondratyev 		device_set_desc(child, devinfo->name);
5352b4464b0SVladimir Kondratyev }
5362b4464b0SVladimir Kondratyev 
5372b4464b0SVladimir Kondratyev device_t
5382b4464b0SVladimir Kondratyev hidbus_find_child(device_t bus, int32_t usage)
5392b4464b0SVladimir Kondratyev {
5402b4464b0SVladimir Kondratyev 	device_t *children, child;
5412b4464b0SVladimir Kondratyev 	int ccount, i;
5422b4464b0SVladimir Kondratyev 
543d14bc723SWarner Losh 	bus_topo_assert();
5442b4464b0SVladimir Kondratyev 
5452b4464b0SVladimir Kondratyev 	/* Get a list of all hidbus children */
5462b4464b0SVladimir Kondratyev 	if (device_get_children(bus, &children, &ccount) != 0)
5472b4464b0SVladimir Kondratyev 		return (NULL);
5482b4464b0SVladimir Kondratyev 
5492b4464b0SVladimir Kondratyev 	/* Scan through to find required TLC */
5502b4464b0SVladimir Kondratyev 	for (i = 0, child = NULL; i < ccount; i++) {
5512b4464b0SVladimir Kondratyev 		if (hidbus_get_usage(children[i]) == usage) {
5522b4464b0SVladimir Kondratyev 			child = children[i];
5532b4464b0SVladimir Kondratyev 			break;
5542b4464b0SVladimir Kondratyev 		}
5552b4464b0SVladimir Kondratyev 	}
5562b4464b0SVladimir Kondratyev 	free(children, M_TEMP);
5572b4464b0SVladimir Kondratyev 
5582b4464b0SVladimir Kondratyev 	return (child);
5592b4464b0SVladimir Kondratyev }
5602b4464b0SVladimir Kondratyev 
5612b4464b0SVladimir Kondratyev void
5622b4464b0SVladimir Kondratyev hidbus_intr(void *context, void *buf, hid_size_t len)
5632b4464b0SVladimir Kondratyev {
5642b4464b0SVladimir Kondratyev 	struct hidbus_softc *sc = context;
5652b4464b0SVladimir Kondratyev 	struct hidbus_ivars *tlc;
5662b4464b0SVladimir Kondratyev 	struct epoch_tracker et;
5672b4464b0SVladimir Kondratyev 
5682b4464b0SVladimir Kondratyev 	/*
5692b4464b0SVladimir Kondratyev 	 * Broadcast input report to all subscribers.
5702b4464b0SVladimir Kondratyev 	 * TODO: Add check for input report ID.
5712b4464b0SVladimir Kondratyev 	 *
5722b4464b0SVladimir Kondratyev 	 * Relock mutex on every TLC item as we can't hold any locks over whole
5732b4464b0SVladimir Kondratyev 	 * TLC list here due to LOR with open()/close() handlers.
5742b4464b0SVladimir Kondratyev 	 */
5752b4464b0SVladimir Kondratyev 	if (!HID_IN_POLLING_MODE())
5762b4464b0SVladimir Kondratyev 		epoch_enter_preempt(INPUT_EPOCH, &et);
5772b4464b0SVladimir Kondratyev 	CK_STAILQ_FOREACH(tlc, &sc->tlcs, link) {
5782b4464b0SVladimir Kondratyev 		if (tlc->refcnt == 0 || tlc->intr_handler == NULL)
5792b4464b0SVladimir Kondratyev 			continue;
5802b4464b0SVladimir Kondratyev 		if (HID_IN_POLLING_MODE()) {
5812b4464b0SVladimir Kondratyev 			if ((tlc->flags & HIDBUS_FLAG_CAN_POLL) != 0)
5822b4464b0SVladimir Kondratyev 				tlc->intr_handler(tlc->intr_ctx, buf, len);
5832b4464b0SVladimir Kondratyev 		} else {
5842b4464b0SVladimir Kondratyev 			mtx_lock(tlc->mtx);
5852b4464b0SVladimir Kondratyev 			tlc->intr_handler(tlc->intr_ctx, buf, len);
5862b4464b0SVladimir Kondratyev 			mtx_unlock(tlc->mtx);
5872b4464b0SVladimir Kondratyev 		}
5882b4464b0SVladimir Kondratyev 	}
5892b4464b0SVladimir Kondratyev 	if (!HID_IN_POLLING_MODE())
5902b4464b0SVladimir Kondratyev 		epoch_exit_preempt(INPUT_EPOCH, &et);
5912b4464b0SVladimir Kondratyev }
5922b4464b0SVladimir Kondratyev 
5932b4464b0SVladimir Kondratyev void
5942b4464b0SVladimir Kondratyev hidbus_set_intr(device_t child, hid_intr_t *handler, void *context)
5952b4464b0SVladimir Kondratyev {
5962b4464b0SVladimir Kondratyev 	struct hidbus_ivars *tlc = device_get_ivars(child);
5972b4464b0SVladimir Kondratyev 
5982b4464b0SVladimir Kondratyev 	tlc->intr_handler = handler;
5992b4464b0SVladimir Kondratyev 	tlc->intr_ctx = context;
6002b4464b0SVladimir Kondratyev }
6012b4464b0SVladimir Kondratyev 
6024151ac9fSVladimir Kondratyev static int
6034151ac9fSVladimir Kondratyev hidbus_intr_start(device_t bus, device_t child)
6042b4464b0SVladimir Kondratyev {
6054eb82e65SZhenlei Huang 	MPASS(bus == device_get_parent(child));
6062b4464b0SVladimir Kondratyev 	struct hidbus_softc *sc = device_get_softc(bus);
6072b4464b0SVladimir Kondratyev 	struct hidbus_ivars *ivar = device_get_ivars(child);
6082b4464b0SVladimir Kondratyev 	struct hidbus_ivars *tlc;
609c019a169SHans Petter Selasky 	bool refcnted = false;
6102b4464b0SVladimir Kondratyev 	int error;
6112b4464b0SVladimir Kondratyev 
6122b4464b0SVladimir Kondratyev 	if (sx_xlock_sig(&sc->sx) != 0)
6132b4464b0SVladimir Kondratyev 		return (EINTR);
6142b4464b0SVladimir Kondratyev 	CK_STAILQ_FOREACH(tlc, &sc->tlcs, link) {
615c019a169SHans Petter Selasky 		refcnted |= (tlc->refcnt != 0);
6162b4464b0SVladimir Kondratyev 		if (tlc == ivar) {
6172b4464b0SVladimir Kondratyev 			mtx_lock(tlc->mtx);
6182b4464b0SVladimir Kondratyev 			++tlc->refcnt;
6192b4464b0SVladimir Kondratyev 			mtx_unlock(tlc->mtx);
6202b4464b0SVladimir Kondratyev 		}
6212b4464b0SVladimir Kondratyev 	}
6224151ac9fSVladimir Kondratyev 	error = refcnted ? 0 : hid_intr_start(bus);
6232b4464b0SVladimir Kondratyev 	sx_unlock(&sc->sx);
6242b4464b0SVladimir Kondratyev 
6252b4464b0SVladimir Kondratyev 	return (error);
6262b4464b0SVladimir Kondratyev }
6272b4464b0SVladimir Kondratyev 
6284151ac9fSVladimir Kondratyev static int
6294151ac9fSVladimir Kondratyev hidbus_intr_stop(device_t bus, device_t child)
6302b4464b0SVladimir Kondratyev {
6314eb82e65SZhenlei Huang 	MPASS(bus == device_get_parent(child));
6322b4464b0SVladimir Kondratyev 	struct hidbus_softc *sc = device_get_softc(bus);
6332b4464b0SVladimir Kondratyev 	struct hidbus_ivars *ivar = device_get_ivars(child);
6342b4464b0SVladimir Kondratyev 	struct hidbus_ivars *tlc;
635c019a169SHans Petter Selasky 	bool refcnted = false;
6362b4464b0SVladimir Kondratyev 	int error;
6372b4464b0SVladimir Kondratyev 
6382b4464b0SVladimir Kondratyev 	if (sx_xlock_sig(&sc->sx) != 0)
6392b4464b0SVladimir Kondratyev 		return (EINTR);
6402b4464b0SVladimir Kondratyev 	CK_STAILQ_FOREACH(tlc, &sc->tlcs, link) {
6412b4464b0SVladimir Kondratyev 		if (tlc == ivar) {
6422b4464b0SVladimir Kondratyev 			mtx_lock(tlc->mtx);
6432b4464b0SVladimir Kondratyev 			MPASS(tlc->refcnt != 0);
6442b4464b0SVladimir Kondratyev 			--tlc->refcnt;
6452b4464b0SVladimir Kondratyev 			mtx_unlock(tlc->mtx);
6462b4464b0SVladimir Kondratyev 		}
647c019a169SHans Petter Selasky 		refcnted |= (tlc->refcnt != 0);
6482b4464b0SVladimir Kondratyev 	}
6494151ac9fSVladimir Kondratyev 	error = refcnted ? 0 : hid_intr_stop(bus);
6502b4464b0SVladimir Kondratyev 	sx_unlock(&sc->sx);
6512b4464b0SVladimir Kondratyev 
6522b4464b0SVladimir Kondratyev 	return (error);
6532b4464b0SVladimir Kondratyev }
6542b4464b0SVladimir Kondratyev 
6554151ac9fSVladimir Kondratyev static void
6564151ac9fSVladimir Kondratyev hidbus_intr_poll(device_t bus, device_t child __unused)
6572b4464b0SVladimir Kondratyev {
6584151ac9fSVladimir Kondratyev 	hid_intr_poll(bus);
6592b4464b0SVladimir Kondratyev }
6602b4464b0SVladimir Kondratyev 
6612b4464b0SVladimir Kondratyev struct hid_rdesc_info *
6622b4464b0SVladimir Kondratyev hidbus_get_rdesc_info(device_t child)
6632b4464b0SVladimir Kondratyev {
6642b4464b0SVladimir Kondratyev 	device_t bus = device_get_parent(child);
6652b4464b0SVladimir Kondratyev 	struct hidbus_softc *sc = device_get_softc(bus);
6662b4464b0SVladimir Kondratyev 
6672b4464b0SVladimir Kondratyev 	return (&sc->rdesc);
6682b4464b0SVladimir Kondratyev }
6692b4464b0SVladimir Kondratyev 
6702b4464b0SVladimir Kondratyev /*
6712b4464b0SVladimir Kondratyev  * HID interface.
6722b4464b0SVladimir Kondratyev  *
6732b4464b0SVladimir Kondratyev  * Hidbus as well as any hidbus child can be passed as first arg.
6742b4464b0SVladimir Kondratyev  */
6752b4464b0SVladimir Kondratyev 
6762b4464b0SVladimir Kondratyev /* Read cached report descriptor */
6772b4464b0SVladimir Kondratyev int
6782b4464b0SVladimir Kondratyev hid_get_report_descr(device_t dev, void **data, hid_size_t *len)
6792b4464b0SVladimir Kondratyev {
6802b4464b0SVladimir Kondratyev 	device_t bus;
6812b4464b0SVladimir Kondratyev 	struct hidbus_softc *sc;
6822b4464b0SVladimir Kondratyev 
683e9d92100SJohn Baldwin 	bus = device_get_devclass(dev) == devclass_find("hidbus") ?
6842b4464b0SVladimir Kondratyev 	    dev : device_get_parent(dev);
6852b4464b0SVladimir Kondratyev 	sc = device_get_softc(bus);
6862b4464b0SVladimir Kondratyev 
6872b4464b0SVladimir Kondratyev 	/*
6882b4464b0SVladimir Kondratyev 	 * Do not send request to a transport backend.
6892b4464b0SVladimir Kondratyev 	 * Use cached report descriptor instead of it.
6902b4464b0SVladimir Kondratyev 	 */
6912b4464b0SVladimir Kondratyev 	if (sc->rdesc.data == NULL || sc->rdesc.len == 0)
6922b4464b0SVladimir Kondratyev 		return (ENXIO);
6932b4464b0SVladimir Kondratyev 
6942b4464b0SVladimir Kondratyev 	if (data != NULL)
6952b4464b0SVladimir Kondratyev 		*data = sc->rdesc.data;
6962b4464b0SVladimir Kondratyev 	if (len != NULL)
6972b4464b0SVladimir Kondratyev 		*len = sc->rdesc.len;
6982b4464b0SVladimir Kondratyev 
6992b4464b0SVladimir Kondratyev 	return (0);
7002b4464b0SVladimir Kondratyev }
7012b4464b0SVladimir Kondratyev 
7022b4464b0SVladimir Kondratyev /*
7032b4464b0SVladimir Kondratyev  * Replace cached report descriptor with top level driver provided one.
7042b4464b0SVladimir Kondratyev  *
7052b4464b0SVladimir Kondratyev  * It deletes all hidbus children except caller and enumerates them again after
7062b4464b0SVladimir Kondratyev  * new descriptor has been registered. Currently it can not be called from
7072b4464b0SVladimir Kondratyev  * autoenumerated (by report's TLC) child device context as it results in child
7082b4464b0SVladimir Kondratyev  * duplication. To overcome this limitation hid_set_report_descr() should be
7092b4464b0SVladimir Kondratyev  * called from device_identify driver's handler with hidbus itself passed as
7102b4464b0SVladimir Kondratyev  * 'device_t dev' parameter.
7112b4464b0SVladimir Kondratyev  */
7122b4464b0SVladimir Kondratyev int
7132b4464b0SVladimir Kondratyev hid_set_report_descr(device_t dev, const void *data, hid_size_t len)
7142b4464b0SVladimir Kondratyev {
7152b4464b0SVladimir Kondratyev 	struct hid_rdesc_info rdesc;
7162b4464b0SVladimir Kondratyev 	device_t bus;
7172b4464b0SVladimir Kondratyev 	struct hidbus_softc *sc;
7182b4464b0SVladimir Kondratyev 	bool is_bus;
7192b4464b0SVladimir Kondratyev 	int error;
7202b4464b0SVladimir Kondratyev 
721d14bc723SWarner Losh 	bus_topo_assert();
7222b4464b0SVladimir Kondratyev 
723e9d92100SJohn Baldwin 	is_bus = device_get_devclass(dev) == devclass_find("hidbus");
7242b4464b0SVladimir Kondratyev 	bus = is_bus ? dev : device_get_parent(dev);
7252b4464b0SVladimir Kondratyev 	sc = device_get_softc(bus);
7262b4464b0SVladimir Kondratyev 
7272b4464b0SVladimir Kondratyev 	/*
7282b4464b0SVladimir Kondratyev 	 * Do not overload already overloaded report descriptor in
7292b4464b0SVladimir Kondratyev 	 * device_identify handler. It causes infinite recursion loop.
7302b4464b0SVladimir Kondratyev 	 */
7312b4464b0SVladimir Kondratyev 	if (is_bus && sc->overloaded)
7322b4464b0SVladimir Kondratyev 		return(0);
7332b4464b0SVladimir Kondratyev 
7342b4464b0SVladimir Kondratyev 	DPRINTFN(5, "len=%d\n", len);
7352b4464b0SVladimir Kondratyev 	DPRINTFN(5, "data = %*D\n", len, data, " ");
7362b4464b0SVladimir Kondratyev 
7372b4464b0SVladimir Kondratyev 	error = hidbus_fill_rdesc_info(&rdesc, data, len);
7382b4464b0SVladimir Kondratyev 	if (error != 0)
7392b4464b0SVladimir Kondratyev 		return (error);
7402b4464b0SVladimir Kondratyev 
7412b4464b0SVladimir Kondratyev 	error = hidbus_detach_children(dev);
7422b4464b0SVladimir Kondratyev 	if (error != 0)
7432b4464b0SVladimir Kondratyev 		return(error);
7442b4464b0SVladimir Kondratyev 
7452b4464b0SVladimir Kondratyev 	/* Make private copy to handle a case of dynamicaly allocated data. */
7462b4464b0SVladimir Kondratyev 	rdesc.data = malloc(len, M_DEVBUF, M_ZERO | M_WAITOK);
7472b4464b0SVladimir Kondratyev 	bcopy(data, rdesc.data, len);
7482b4464b0SVladimir Kondratyev 	sc->overloaded = true;
7492b4464b0SVladimir Kondratyev 	free(sc->rdesc.data, M_DEVBUF);
7502b4464b0SVladimir Kondratyev 	bcopy(&rdesc, &sc->rdesc, sizeof(struct hid_rdesc_info));
7512b4464b0SVladimir Kondratyev 
7522b4464b0SVladimir Kondratyev 	error = hidbus_attach_children(bus);
7532b4464b0SVladimir Kondratyev 
7542b4464b0SVladimir Kondratyev 	return (error);
7552b4464b0SVladimir Kondratyev }
7562b4464b0SVladimir Kondratyev 
7572b4464b0SVladimir Kondratyev static int
7584b171281SVladimir Kondratyev hidbus_get_rdesc(device_t dev, device_t child __unused, void *data,
7594b171281SVladimir Kondratyev     hid_size_t len)
7604b171281SVladimir Kondratyev {
7614b171281SVladimir Kondratyev 	return (hid_get_rdesc(dev, data, len));
7624b171281SVladimir Kondratyev }
7634b171281SVladimir Kondratyev 
7644b171281SVladimir Kondratyev static int
7654b171281SVladimir Kondratyev hidbus_read(device_t dev, device_t child __unused, void *data,
7664b171281SVladimir Kondratyev     hid_size_t maxlen, hid_size_t *actlen)
7674b171281SVladimir Kondratyev {
7684b171281SVladimir Kondratyev 	return (hid_read(dev, data, maxlen, actlen));
7694b171281SVladimir Kondratyev }
7704b171281SVladimir Kondratyev 
7714b171281SVladimir Kondratyev static int
7724b171281SVladimir Kondratyev hidbus_write(device_t dev, device_t child __unused, const void *data,
7734b171281SVladimir Kondratyev     hid_size_t len)
7742b4464b0SVladimir Kondratyev {
7752b4464b0SVladimir Kondratyev 	struct hidbus_softc *sc;
7762b4464b0SVladimir Kondratyev 	uint8_t id;
7772b4464b0SVladimir Kondratyev 
7782b4464b0SVladimir Kondratyev 	sc = device_get_softc(dev);
7792b4464b0SVladimir Kondratyev 	/*
7802b4464b0SVladimir Kondratyev 	 * Output interrupt endpoint is often optional. If HID device
7812b4464b0SVladimir Kondratyev 	 * does not provide it, send reports via control pipe.
7822b4464b0SVladimir Kondratyev 	 */
7832b4464b0SVladimir Kondratyev 	if (sc->nowrite) {
7842b4464b0SVladimir Kondratyev 		/* try to extract the ID byte */
7852b4464b0SVladimir Kondratyev 		id = (sc->rdesc.oid & (len > 0)) ? *(const uint8_t*)data : 0;
7862b4464b0SVladimir Kondratyev 		return (hid_set_report(dev, data, len, HID_OUTPUT_REPORT, id));
7872b4464b0SVladimir Kondratyev 	}
7882b4464b0SVladimir Kondratyev 
7892b4464b0SVladimir Kondratyev 	return (hid_write(dev, data, len));
7902b4464b0SVladimir Kondratyev }
7912b4464b0SVladimir Kondratyev 
7924b171281SVladimir Kondratyev static int
7934b171281SVladimir Kondratyev hidbus_get_report(device_t dev, device_t child __unused, void *data,
7944b171281SVladimir Kondratyev     hid_size_t maxlen, hid_size_t *actlen, uint8_t type, uint8_t id)
7954b171281SVladimir Kondratyev {
7964b171281SVladimir Kondratyev 	return (hid_get_report(dev, data, maxlen, actlen, type, id));
7974b171281SVladimir Kondratyev }
7984b171281SVladimir Kondratyev 
7994b171281SVladimir Kondratyev static int
8004b171281SVladimir Kondratyev hidbus_set_report(device_t dev, device_t child __unused, const void *data,
8014b171281SVladimir Kondratyev     hid_size_t len, uint8_t type, uint8_t id)
8024b171281SVladimir Kondratyev {
8034b171281SVladimir Kondratyev 	return (hid_set_report(dev, data, len, type, id));
8044b171281SVladimir Kondratyev }
8054b171281SVladimir Kondratyev 
8064b171281SVladimir Kondratyev static int
8074b171281SVladimir Kondratyev hidbus_set_idle(device_t dev, device_t child __unused, uint16_t duration,
8084b171281SVladimir Kondratyev     uint8_t id)
8094b171281SVladimir Kondratyev {
8104b171281SVladimir Kondratyev 	return (hid_set_idle(dev, duration, id));
8114b171281SVladimir Kondratyev }
8124b171281SVladimir Kondratyev 
8134b171281SVladimir Kondratyev static int
8144b171281SVladimir Kondratyev hidbus_set_protocol(device_t dev, device_t child __unused, uint16_t protocol)
8154b171281SVladimir Kondratyev {
8164b171281SVladimir Kondratyev 	return (hid_set_protocol(dev, protocol));
8174b171281SVladimir Kondratyev }
8184b171281SVladimir Kondratyev 
8194b171281SVladimir Kondratyev static int
8204b171281SVladimir Kondratyev hidbus_ioctl(device_t dev, device_t child __unused, unsigned long cmd,
8214b171281SVladimir Kondratyev     uintptr_t data)
8224b171281SVladimir Kondratyev {
8234b171281SVladimir Kondratyev 	return (hid_ioctl(dev, cmd, data));
8244b171281SVladimir Kondratyev }
8254b171281SVladimir Kondratyev 
8262b4464b0SVladimir Kondratyev /*------------------------------------------------------------------------*
8272b4464b0SVladimir Kondratyev  *	hidbus_lookup_id
8282b4464b0SVladimir Kondratyev  *
8292b4464b0SVladimir Kondratyev  * This functions takes an array of "struct hid_device_id" and tries
8302b4464b0SVladimir Kondratyev  * to match the entries with the information in "struct hid_device_info".
8312b4464b0SVladimir Kondratyev  *
8322b4464b0SVladimir Kondratyev  * Return values:
8332b4464b0SVladimir Kondratyev  * NULL: No match found.
8342b4464b0SVladimir Kondratyev  * Else: Pointer to matching entry.
8352b4464b0SVladimir Kondratyev  *------------------------------------------------------------------------*/
8362b4464b0SVladimir Kondratyev const struct hid_device_id *
8372b4464b0SVladimir Kondratyev hidbus_lookup_id(device_t dev, const struct hid_device_id *id, int nitems_id)
8382b4464b0SVladimir Kondratyev {
8392b4464b0SVladimir Kondratyev 	const struct hid_device_id *id_end;
8402b4464b0SVladimir Kondratyev 	const struct hid_device_info *info;
8412b4464b0SVladimir Kondratyev 	int32_t usage;
8422b4464b0SVladimir Kondratyev 	bool is_child;
8432b4464b0SVladimir Kondratyev 
8442b4464b0SVladimir Kondratyev 	if (id == NULL) {
8452b4464b0SVladimir Kondratyev 		goto done;
8462b4464b0SVladimir Kondratyev 	}
8472b4464b0SVladimir Kondratyev 
8482b4464b0SVladimir Kondratyev 	id_end = id + nitems_id;
8492b4464b0SVladimir Kondratyev 	info = hid_get_device_info(dev);
850e9d92100SJohn Baldwin 	is_child = device_get_devclass(dev) != devclass_find("hidbus");
8512b4464b0SVladimir Kondratyev 	if (is_child)
8522b4464b0SVladimir Kondratyev 		usage = hidbus_get_usage(dev);
8532b4464b0SVladimir Kondratyev 
8542b4464b0SVladimir Kondratyev 	/*
8552b4464b0SVladimir Kondratyev 	 * Keep on matching array entries until we find a match or
8562b4464b0SVladimir Kondratyev 	 * until we reach the end of the matching array:
8572b4464b0SVladimir Kondratyev 	 */
8582b4464b0SVladimir Kondratyev 	for (; id != id_end; id++) {
8592b4464b0SVladimir Kondratyev 
8602b4464b0SVladimir Kondratyev 		if (is_child && (id->match_flag_page) &&
8612b4464b0SVladimir Kondratyev 		    (id->page != HID_GET_USAGE_PAGE(usage))) {
8622b4464b0SVladimir Kondratyev 			continue;
8632b4464b0SVladimir Kondratyev 		}
8642b4464b0SVladimir Kondratyev 		if (is_child && (id->match_flag_usage) &&
8652b4464b0SVladimir Kondratyev 		    (id->usage != HID_GET_USAGE(usage))) {
8662b4464b0SVladimir Kondratyev 			continue;
8672b4464b0SVladimir Kondratyev 		}
8682b4464b0SVladimir Kondratyev 		if ((id->match_flag_bus) &&
8692b4464b0SVladimir Kondratyev 		    (id->idBus != info->idBus)) {
8702b4464b0SVladimir Kondratyev 			continue;
8712b4464b0SVladimir Kondratyev 		}
8722b4464b0SVladimir Kondratyev 		if ((id->match_flag_vendor) &&
8732b4464b0SVladimir Kondratyev 		    (id->idVendor != info->idVendor)) {
8742b4464b0SVladimir Kondratyev 			continue;
8752b4464b0SVladimir Kondratyev 		}
8762b4464b0SVladimir Kondratyev 		if ((id->match_flag_product) &&
8772b4464b0SVladimir Kondratyev 		    (id->idProduct != info->idProduct)) {
8782b4464b0SVladimir Kondratyev 			continue;
8792b4464b0SVladimir Kondratyev 		}
8802b4464b0SVladimir Kondratyev 		if ((id->match_flag_ver_lo) &&
8812b4464b0SVladimir Kondratyev 		    (id->idVersion_lo > info->idVersion)) {
8822b4464b0SVladimir Kondratyev 			continue;
8832b4464b0SVladimir Kondratyev 		}
8842b4464b0SVladimir Kondratyev 		if ((id->match_flag_ver_hi) &&
8852b4464b0SVladimir Kondratyev 		    (id->idVersion_hi < info->idVersion)) {
8862b4464b0SVladimir Kondratyev 			continue;
8872b4464b0SVladimir Kondratyev 		}
8882b4464b0SVladimir Kondratyev 		if (id->match_flag_pnp &&
8892b4464b0SVladimir Kondratyev 		    strncmp(id->idPnP, info->idPnP, HID_PNP_ID_SIZE) != 0) {
8902b4464b0SVladimir Kondratyev 			continue;
8912b4464b0SVladimir Kondratyev 		}
8922b4464b0SVladimir Kondratyev 		/* We found a match! */
8932b4464b0SVladimir Kondratyev 		return (id);
8942b4464b0SVladimir Kondratyev 	}
8952b4464b0SVladimir Kondratyev 
8962b4464b0SVladimir Kondratyev done:
8972b4464b0SVladimir Kondratyev 	return (NULL);
8982b4464b0SVladimir Kondratyev }
8992b4464b0SVladimir Kondratyev 
9002b4464b0SVladimir Kondratyev /*------------------------------------------------------------------------*
9012b4464b0SVladimir Kondratyev  *	hidbus_lookup_driver_info - factored out code
9022b4464b0SVladimir Kondratyev  *
9032b4464b0SVladimir Kondratyev  * Return values:
9042b4464b0SVladimir Kondratyev  *    0: Success
9052b4464b0SVladimir Kondratyev  * Else: Failure
9062b4464b0SVladimir Kondratyev  *------------------------------------------------------------------------*/
9072b4464b0SVladimir Kondratyev int
9082b4464b0SVladimir Kondratyev hidbus_lookup_driver_info(device_t child, const struct hid_device_id *id,
9092b4464b0SVladimir Kondratyev     int nitems_id)
9102b4464b0SVladimir Kondratyev {
9112b4464b0SVladimir Kondratyev 
9122b4464b0SVladimir Kondratyev 	id = hidbus_lookup_id(child, id, nitems_id);
9132b4464b0SVladimir Kondratyev 	if (id) {
9142b4464b0SVladimir Kondratyev 		/* copy driver info */
9152b4464b0SVladimir Kondratyev 		hidbus_set_driver_info(child, id->driver_info);
9162b4464b0SVladimir Kondratyev 		return (0);
9172b4464b0SVladimir Kondratyev 	}
9182b4464b0SVladimir Kondratyev 	return (ENXIO);
9192b4464b0SVladimir Kondratyev }
9202b4464b0SVladimir Kondratyev 
9212b4464b0SVladimir Kondratyev const struct hid_device_info *
9222b4464b0SVladimir Kondratyev hid_get_device_info(device_t dev)
9232b4464b0SVladimir Kondratyev {
9242b4464b0SVladimir Kondratyev 	device_t bus;
9252b4464b0SVladimir Kondratyev 
926e9d92100SJohn Baldwin 	bus = device_get_devclass(dev) == devclass_find("hidbus") ?
9272b4464b0SVladimir Kondratyev 	    dev : device_get_parent(dev);
9282b4464b0SVladimir Kondratyev 
9292b4464b0SVladimir Kondratyev 	return (device_get_ivars(bus));
9302b4464b0SVladimir Kondratyev }
9312b4464b0SVladimir Kondratyev 
9322b4464b0SVladimir Kondratyev static device_method_t hidbus_methods[] = {
9332b4464b0SVladimir Kondratyev 	/* device interface */
9342b4464b0SVladimir Kondratyev 	DEVMETHOD(device_probe,		hidbus_probe),
9352b4464b0SVladimir Kondratyev 	DEVMETHOD(device_attach,	hidbus_attach),
9362b4464b0SVladimir Kondratyev 	DEVMETHOD(device_detach,	hidbus_detach),
9372b4464b0SVladimir Kondratyev 	DEVMETHOD(device_suspend,	bus_generic_suspend),
9382b4464b0SVladimir Kondratyev 	DEVMETHOD(device_resume,	bus_generic_resume),
9392b4464b0SVladimir Kondratyev 
9402b4464b0SVladimir Kondratyev 	/* bus interface */
9412b4464b0SVladimir Kondratyev 	DEVMETHOD(bus_add_child,	hidbus_add_child),
9422b4464b0SVladimir Kondratyev 	DEVMETHOD(bus_child_detached,	hidbus_child_detached),
9432b4464b0SVladimir Kondratyev 	DEVMETHOD(bus_child_deleted,	hidbus_child_deleted),
9442b4464b0SVladimir Kondratyev 	DEVMETHOD(bus_read_ivar,	hidbus_read_ivar),
9452b4464b0SVladimir Kondratyev 	DEVMETHOD(bus_write_ivar,	hidbus_write_ivar),
946ddfc9c4cSWarner Losh 	DEVMETHOD(bus_child_pnpinfo,	hidbus_child_pnpinfo),
947ddfc9c4cSWarner Losh 	DEVMETHOD(bus_child_location,	hidbus_child_location),
9482b4464b0SVladimir Kondratyev 
9492b4464b0SVladimir Kondratyev 	/* hid interface */
9504151ac9fSVladimir Kondratyev 	DEVMETHOD(hid_intr_start,	hidbus_intr_start),
9514151ac9fSVladimir Kondratyev 	DEVMETHOD(hid_intr_stop,	hidbus_intr_stop),
9524151ac9fSVladimir Kondratyev 	DEVMETHOD(hid_intr_poll,	hidbus_intr_poll),
9534b171281SVladimir Kondratyev 	DEVMETHOD(hid_get_rdesc,	hidbus_get_rdesc),
9544b171281SVladimir Kondratyev 	DEVMETHOD(hid_read,		hidbus_read),
9552b4464b0SVladimir Kondratyev 	DEVMETHOD(hid_write,		hidbus_write),
9564b171281SVladimir Kondratyev 	DEVMETHOD(hid_get_report,	hidbus_get_report),
9574b171281SVladimir Kondratyev 	DEVMETHOD(hid_set_report,	hidbus_set_report),
9584b171281SVladimir Kondratyev 	DEVMETHOD(hid_set_idle,		hidbus_set_idle),
9594b171281SVladimir Kondratyev 	DEVMETHOD(hid_set_protocol,	hidbus_set_protocol),
9604b171281SVladimir Kondratyev 	DEVMETHOD(hid_ioctl,		hidbus_ioctl),
9612b4464b0SVladimir Kondratyev 
9622b4464b0SVladimir Kondratyev 	DEVMETHOD_END
9632b4464b0SVladimir Kondratyev };
9642b4464b0SVladimir Kondratyev 
9652b4464b0SVladimir Kondratyev driver_t hidbus_driver = {
9662b4464b0SVladimir Kondratyev 	"hidbus",
9672b4464b0SVladimir Kondratyev 	hidbus_methods,
9682b4464b0SVladimir Kondratyev 	sizeof(struct hidbus_softc),
9692b4464b0SVladimir Kondratyev };
9702b4464b0SVladimir Kondratyev 
9712b4464b0SVladimir Kondratyev MODULE_DEPEND(hidbus, hid, 1, 1, 1);
9722b4464b0SVladimir Kondratyev MODULE_VERSION(hidbus, 1);
97364fbda90SVal Packett DRIVER_MODULE(hidbus, atopcase, hidbus_driver, 0, 0);
974e4d3f1e4SYuri DRIVER_MODULE(hidbus, hvhid, hidbus_driver, 0, 0);
9757eeede15SJohn Baldwin DRIVER_MODULE(hidbus, iichid, hidbus_driver, 0, 0);
9767eeede15SJohn Baldwin DRIVER_MODULE(hidbus, usbhid, hidbus_driver, 0, 0);
977