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