12b4464b0SVladimir Kondratyev /*- 22b4464b0SVladimir Kondratyev * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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/cdefs.h> 292b4464b0SVladimir Kondratyev __FBSDID("$FreeBSD$"); 302b4464b0SVladimir Kondratyev 312b4464b0SVladimir Kondratyev #include <sys/param.h> 322b4464b0SVladimir Kondratyev #include <sys/bus.h> 332b4464b0SVladimir Kondratyev #include <sys/ck.h> 342b4464b0SVladimir Kondratyev #include <sys/epoch.h> 352b4464b0SVladimir Kondratyev #include <sys/kdb.h> 362b4464b0SVladimir Kondratyev #include <sys/kernel.h> 372b4464b0SVladimir Kondratyev #include <sys/libkern.h> 382b4464b0SVladimir Kondratyev #include <sys/lock.h> 392b4464b0SVladimir Kondratyev #include <sys/malloc.h> 402b4464b0SVladimir Kondratyev #include <sys/module.h> 412b4464b0SVladimir Kondratyev #include <sys/mutex.h> 422b4464b0SVladimir Kondratyev #include <sys/proc.h> 43*ddfc9c4cSWarner Losh #include <sys/sbuf.h> 442b4464b0SVladimir Kondratyev #include <sys/sx.h> 45*ddfc9c4cSWarner Losh #include <sys/systm.h> 462b4464b0SVladimir Kondratyev 472b4464b0SVladimir Kondratyev #define HID_DEBUG_VAR hid_debug 482b4464b0SVladimir Kondratyev #include <dev/hid/hid.h> 492b4464b0SVladimir Kondratyev #include <dev/hid/hidbus.h> 502b4464b0SVladimir Kondratyev #include <dev/hid/hidquirk.h> 512b4464b0SVladimir Kondratyev 522b4464b0SVladimir Kondratyev #include "hid_if.h" 532b4464b0SVladimir Kondratyev 542b4464b0SVladimir Kondratyev #define INPUT_EPOCH global_epoch_preempt 552b4464b0SVladimir Kondratyev #define HID_RSIZE_MAX 1024 562b4464b0SVladimir Kondratyev 572b4464b0SVladimir Kondratyev static hid_intr_t hidbus_intr; 582b4464b0SVladimir Kondratyev 592b4464b0SVladimir Kondratyev static device_probe_t hidbus_probe; 602b4464b0SVladimir Kondratyev static device_attach_t hidbus_attach; 612b4464b0SVladimir Kondratyev static device_detach_t hidbus_detach; 622b4464b0SVladimir Kondratyev 632b4464b0SVladimir Kondratyev struct hidbus_ivars { 642b4464b0SVladimir Kondratyev int32_t usage; 652b4464b0SVladimir Kondratyev uint8_t index; 662b4464b0SVladimir Kondratyev uint32_t flags; 672b4464b0SVladimir Kondratyev uintptr_t driver_info; /* for internal use */ 682b4464b0SVladimir Kondratyev struct mtx *mtx; /* child intr mtx */ 692b4464b0SVladimir Kondratyev hid_intr_t *intr_handler; /* executed under mtx*/ 702b4464b0SVladimir Kondratyev void *intr_ctx; 712b4464b0SVladimir Kondratyev unsigned int refcnt; /* protected by mtx */ 722b4464b0SVladimir Kondratyev struct epoch_context epoch_ctx; 732b4464b0SVladimir Kondratyev CK_STAILQ_ENTRY(hidbus_ivars) link; 742b4464b0SVladimir Kondratyev }; 752b4464b0SVladimir Kondratyev 762b4464b0SVladimir Kondratyev struct hidbus_softc { 772b4464b0SVladimir Kondratyev device_t dev; 782b4464b0SVladimir Kondratyev struct sx sx; 792b4464b0SVladimir Kondratyev struct mtx mtx; 802b4464b0SVladimir Kondratyev 812b4464b0SVladimir Kondratyev bool nowrite; 822b4464b0SVladimir Kondratyev 832b4464b0SVladimir Kondratyev struct hid_rdesc_info rdesc; 842b4464b0SVladimir Kondratyev bool overloaded; 852b4464b0SVladimir Kondratyev int nest; /* Child attach nesting lvl */ 862b4464b0SVladimir Kondratyev int nauto; /* Number of autochildren */ 872b4464b0SVladimir Kondratyev 882b4464b0SVladimir Kondratyev CK_STAILQ_HEAD(, hidbus_ivars) tlcs; 892b4464b0SVladimir Kondratyev }; 902b4464b0SVladimir Kondratyev 912b4464b0SVladimir Kondratyev static int 922b4464b0SVladimir Kondratyev hidbus_fill_rdesc_info(struct hid_rdesc_info *hri, const void *data, 932b4464b0SVladimir Kondratyev hid_size_t len) 942b4464b0SVladimir Kondratyev { 952b4464b0SVladimir Kondratyev int error = 0; 962b4464b0SVladimir Kondratyev 972b4464b0SVladimir Kondratyev hri->data = __DECONST(void *, data); 982b4464b0SVladimir Kondratyev hri->len = len; 992b4464b0SVladimir Kondratyev 1002b4464b0SVladimir Kondratyev /* 1012b4464b0SVladimir Kondratyev * If report descriptor is not available yet, set maximal 1022b4464b0SVladimir Kondratyev * report sizes high enough to allow hidraw to work. 1032b4464b0SVladimir Kondratyev */ 1042b4464b0SVladimir Kondratyev hri->isize = len == 0 ? HID_RSIZE_MAX : 1052b4464b0SVladimir Kondratyev hid_report_size_max(data, len, hid_input, &hri->iid); 1062b4464b0SVladimir Kondratyev hri->osize = len == 0 ? HID_RSIZE_MAX : 1072b4464b0SVladimir Kondratyev hid_report_size_max(data, len, hid_output, &hri->oid); 1082b4464b0SVladimir Kondratyev hri->fsize = len == 0 ? HID_RSIZE_MAX : 1092b4464b0SVladimir Kondratyev hid_report_size_max(data, len, hid_feature, &hri->fid); 1102b4464b0SVladimir Kondratyev 1112b4464b0SVladimir Kondratyev if (hri->isize > HID_RSIZE_MAX) { 1122b4464b0SVladimir Kondratyev DPRINTF("input size is too large, %u bytes (truncating)\n", 1132b4464b0SVladimir Kondratyev hri->isize); 1142b4464b0SVladimir Kondratyev hri->isize = HID_RSIZE_MAX; 1152b4464b0SVladimir Kondratyev error = EOVERFLOW; 1162b4464b0SVladimir Kondratyev } 1172b4464b0SVladimir Kondratyev if (hri->osize > HID_RSIZE_MAX) { 1182b4464b0SVladimir Kondratyev DPRINTF("output size is too large, %u bytes (truncating)\n", 1192b4464b0SVladimir Kondratyev hri->osize); 1202b4464b0SVladimir Kondratyev hri->osize = HID_RSIZE_MAX; 1212b4464b0SVladimir Kondratyev error = EOVERFLOW; 1222b4464b0SVladimir Kondratyev } 1232b4464b0SVladimir Kondratyev if (hri->fsize > HID_RSIZE_MAX) { 1242b4464b0SVladimir Kondratyev DPRINTF("feature size is too large, %u bytes (truncating)\n", 1252b4464b0SVladimir Kondratyev hri->fsize); 1262b4464b0SVladimir Kondratyev hri->fsize = HID_RSIZE_MAX; 1272b4464b0SVladimir Kondratyev error = EOVERFLOW; 1282b4464b0SVladimir Kondratyev } 1292b4464b0SVladimir Kondratyev 1302b4464b0SVladimir Kondratyev return (error); 1312b4464b0SVladimir Kondratyev } 1322b4464b0SVladimir Kondratyev 1332b4464b0SVladimir Kondratyev int 1342b4464b0SVladimir Kondratyev hidbus_locate(const void *desc, hid_size_t size, int32_t u, enum hid_kind k, 1352b4464b0SVladimir Kondratyev uint8_t tlc_index, uint8_t index, struct hid_location *loc, 1362b4464b0SVladimir Kondratyev uint32_t *flags, uint8_t *id, struct hid_absinfo *ai) 1372b4464b0SVladimir Kondratyev { 1382b4464b0SVladimir Kondratyev struct hid_data *d; 1392b4464b0SVladimir Kondratyev struct hid_item h; 1402b4464b0SVladimir Kondratyev int i; 1412b4464b0SVladimir Kondratyev 1422b4464b0SVladimir Kondratyev d = hid_start_parse(desc, size, 1 << k); 1432b4464b0SVladimir Kondratyev HIDBUS_FOREACH_ITEM(d, &h, tlc_index) { 1442b4464b0SVladimir Kondratyev for (i = 0; i < h.nusages; i++) { 1452b4464b0SVladimir Kondratyev if (h.kind == k && h.usages[i] == u) { 1462b4464b0SVladimir Kondratyev if (index--) 1472b4464b0SVladimir Kondratyev break; 1482b4464b0SVladimir Kondratyev if (loc != NULL) 1492b4464b0SVladimir Kondratyev *loc = h.loc; 1502b4464b0SVladimir Kondratyev if (flags != NULL) 1512b4464b0SVladimir Kondratyev *flags = h.flags; 1522b4464b0SVladimir Kondratyev if (id != NULL) 1532b4464b0SVladimir Kondratyev *id = h.report_ID; 1542b4464b0SVladimir Kondratyev if (ai != NULL && (h.flags&HIO_RELATIVE) == 0) 1552b4464b0SVladimir Kondratyev *ai = (struct hid_absinfo) { 1562b4464b0SVladimir Kondratyev .max = h.logical_maximum, 1572b4464b0SVladimir Kondratyev .min = h.logical_minimum, 1582b4464b0SVladimir Kondratyev .res = hid_item_resolution(&h), 1592b4464b0SVladimir Kondratyev }; 1602b4464b0SVladimir Kondratyev hid_end_parse(d); 1612b4464b0SVladimir Kondratyev return (1); 1622b4464b0SVladimir Kondratyev } 1632b4464b0SVladimir Kondratyev } 1642b4464b0SVladimir Kondratyev } 1652b4464b0SVladimir Kondratyev if (loc != NULL) 1662b4464b0SVladimir Kondratyev loc->size = 0; 1672b4464b0SVladimir Kondratyev if (flags != NULL) 1682b4464b0SVladimir Kondratyev *flags = 0; 1692b4464b0SVladimir Kondratyev if (id != NULL) 1702b4464b0SVladimir Kondratyev *id = 0; 1712b4464b0SVladimir Kondratyev hid_end_parse(d); 1722b4464b0SVladimir Kondratyev return (0); 1732b4464b0SVladimir Kondratyev } 1742b4464b0SVladimir Kondratyev 1752b4464b0SVladimir Kondratyev static device_t 1762b4464b0SVladimir Kondratyev hidbus_add_child(device_t dev, u_int order, const char *name, int unit) 1772b4464b0SVladimir Kondratyev { 1782b4464b0SVladimir Kondratyev struct hidbus_softc *sc = device_get_softc(dev); 1792b4464b0SVladimir Kondratyev struct hidbus_ivars *tlc; 1802b4464b0SVladimir Kondratyev device_t child; 1812b4464b0SVladimir Kondratyev 1822b4464b0SVladimir Kondratyev child = device_add_child_ordered(dev, order, name, unit); 1832b4464b0SVladimir Kondratyev if (child == NULL) 1842b4464b0SVladimir Kondratyev return (child); 1852b4464b0SVladimir Kondratyev 1862b4464b0SVladimir Kondratyev tlc = malloc(sizeof(struct hidbus_ivars), M_DEVBUF, M_WAITOK | M_ZERO); 1872b4464b0SVladimir Kondratyev tlc->mtx = &sc->mtx; 1882b4464b0SVladimir Kondratyev device_set_ivars(child, tlc); 1892b4464b0SVladimir Kondratyev sx_xlock(&sc->sx); 1902b4464b0SVladimir Kondratyev CK_STAILQ_INSERT_TAIL(&sc->tlcs, tlc, link); 1912b4464b0SVladimir Kondratyev sx_unlock(&sc->sx); 1922b4464b0SVladimir Kondratyev 1932b4464b0SVladimir Kondratyev return (child); 1942b4464b0SVladimir Kondratyev } 1952b4464b0SVladimir Kondratyev 1962b4464b0SVladimir Kondratyev static int 1972b4464b0SVladimir Kondratyev hidbus_enumerate_children(device_t dev, const void* data, hid_size_t len) 1982b4464b0SVladimir Kondratyev { 1992b4464b0SVladimir Kondratyev struct hidbus_softc *sc = device_get_softc(dev); 2002b4464b0SVladimir Kondratyev struct hid_data *hd; 2012b4464b0SVladimir Kondratyev struct hid_item hi; 2022b4464b0SVladimir Kondratyev device_t child; 2032b4464b0SVladimir Kondratyev uint8_t index = 0; 2042b4464b0SVladimir Kondratyev 2052b4464b0SVladimir Kondratyev if (data == NULL || len == 0) 2062b4464b0SVladimir Kondratyev return (ENXIO); 2072b4464b0SVladimir Kondratyev 2082b4464b0SVladimir Kondratyev /* Add a child for each top level collection */ 2092b4464b0SVladimir Kondratyev hd = hid_start_parse(data, len, 1 << hid_input); 2102b4464b0SVladimir Kondratyev while (hid_get_item(hd, &hi)) { 2112b4464b0SVladimir Kondratyev if (hi.kind != hid_collection || hi.collevel != 1) 2122b4464b0SVladimir Kondratyev continue; 2132b4464b0SVladimir Kondratyev child = BUS_ADD_CHILD(dev, 0, NULL, -1); 2142b4464b0SVladimir Kondratyev if (child == NULL) { 2152b4464b0SVladimir Kondratyev device_printf(dev, "Could not add HID device\n"); 2162b4464b0SVladimir Kondratyev continue; 2172b4464b0SVladimir Kondratyev } 2182b4464b0SVladimir Kondratyev hidbus_set_index(child, index); 2192b4464b0SVladimir Kondratyev hidbus_set_usage(child, hi.usage); 2202b4464b0SVladimir Kondratyev hidbus_set_flags(child, HIDBUS_FLAG_AUTOCHILD); 2212b4464b0SVladimir Kondratyev index++; 2222b4464b0SVladimir Kondratyev DPRINTF("Add child TLC: 0x%04x:0x%04x\n", 2232b4464b0SVladimir Kondratyev HID_GET_USAGE_PAGE(hi.usage), HID_GET_USAGE(hi.usage)); 2242b4464b0SVladimir Kondratyev } 2252b4464b0SVladimir Kondratyev hid_end_parse(hd); 2262b4464b0SVladimir Kondratyev 2272b4464b0SVladimir Kondratyev if (index == 0) 2282b4464b0SVladimir Kondratyev return (ENXIO); 2292b4464b0SVladimir Kondratyev 2302b4464b0SVladimir Kondratyev sc->nauto = index; 2312b4464b0SVladimir Kondratyev 2322b4464b0SVladimir Kondratyev return (0); 2332b4464b0SVladimir Kondratyev } 2342b4464b0SVladimir Kondratyev 2352b4464b0SVladimir Kondratyev static int 2362b4464b0SVladimir Kondratyev hidbus_attach_children(device_t dev) 2372b4464b0SVladimir Kondratyev { 2382b4464b0SVladimir Kondratyev struct hidbus_softc *sc = device_get_softc(dev); 2392b4464b0SVladimir Kondratyev int error; 2402b4464b0SVladimir Kondratyev 2412b4464b0SVladimir Kondratyev HID_INTR_SETUP(device_get_parent(dev), hidbus_intr, sc, &sc->rdesc); 2422b4464b0SVladimir Kondratyev 2432b4464b0SVladimir Kondratyev error = hidbus_enumerate_children(dev, sc->rdesc.data, sc->rdesc.len); 2442b4464b0SVladimir Kondratyev if (error != 0) 2452b4464b0SVladimir Kondratyev DPRINTF("failed to enumerate children: error %d\n", error); 2462b4464b0SVladimir Kondratyev 2472b4464b0SVladimir Kondratyev /* 2482b4464b0SVladimir Kondratyev * hidbus_attach_children() can recurse through device_identify-> 2492b4464b0SVladimir Kondratyev * hid_set_report_descr() call sequence. Do not perform children 2502b4464b0SVladimir Kondratyev * attach twice in that case. 2512b4464b0SVladimir Kondratyev */ 2522b4464b0SVladimir Kondratyev sc->nest++; 2532b4464b0SVladimir Kondratyev bus_generic_probe(dev); 2542b4464b0SVladimir Kondratyev sc->nest--; 2552b4464b0SVladimir Kondratyev if (sc->nest != 0) 2562b4464b0SVladimir Kondratyev return (0); 2572b4464b0SVladimir Kondratyev 2582b4464b0SVladimir Kondratyev if (hid_is_keyboard(sc->rdesc.data, sc->rdesc.len) != 0) 2592b4464b0SVladimir Kondratyev error = bus_generic_attach(dev); 2602b4464b0SVladimir Kondratyev else 2612b4464b0SVladimir Kondratyev error = bus_delayed_attach_children(dev); 2622b4464b0SVladimir Kondratyev if (error != 0) 2632b4464b0SVladimir Kondratyev device_printf(dev, "failed to attach child: error %d\n", error); 2642b4464b0SVladimir Kondratyev 2652b4464b0SVladimir Kondratyev return (error); 2662b4464b0SVladimir Kondratyev } 2672b4464b0SVladimir Kondratyev 2682b4464b0SVladimir Kondratyev static int 2692b4464b0SVladimir Kondratyev hidbus_detach_children(device_t dev) 2702b4464b0SVladimir Kondratyev { 2712b4464b0SVladimir Kondratyev device_t *children, bus; 2722b4464b0SVladimir Kondratyev bool is_bus; 2732b4464b0SVladimir Kondratyev int i, error; 2742b4464b0SVladimir Kondratyev 2752b4464b0SVladimir Kondratyev error = 0; 2762b4464b0SVladimir Kondratyev 2772b4464b0SVladimir Kondratyev is_bus = device_get_devclass(dev) == hidbus_devclass; 2782b4464b0SVladimir Kondratyev bus = is_bus ? dev : device_get_parent(dev); 2792b4464b0SVladimir Kondratyev 2802b4464b0SVladimir Kondratyev KASSERT(device_get_devclass(bus) == hidbus_devclass, 2812b4464b0SVladimir Kondratyev ("Device is not hidbus or it's child")); 2822b4464b0SVladimir Kondratyev 2832b4464b0SVladimir Kondratyev if (is_bus) { 2842b4464b0SVladimir Kondratyev /* If hidbus is passed, delete all children. */ 2852b4464b0SVladimir Kondratyev bus_generic_detach(bus); 2862b4464b0SVladimir Kondratyev device_delete_children(bus); 2872b4464b0SVladimir Kondratyev } else { 2882b4464b0SVladimir Kondratyev /* 2892b4464b0SVladimir Kondratyev * If hidbus child is passed, delete all hidbus children 2902b4464b0SVladimir Kondratyev * except caller. Deleting the caller may result in deadlock. 2912b4464b0SVladimir Kondratyev */ 2922b4464b0SVladimir Kondratyev error = device_get_children(bus, &children, &i); 2932b4464b0SVladimir Kondratyev if (error != 0) 2942b4464b0SVladimir Kondratyev return (error); 2952b4464b0SVladimir Kondratyev while (i-- > 0) { 2962b4464b0SVladimir Kondratyev if (children[i] == dev) 2972b4464b0SVladimir Kondratyev continue; 2982b4464b0SVladimir Kondratyev DPRINTF("Delete child. index=%d (%s)\n", 2992b4464b0SVladimir Kondratyev hidbus_get_index(children[i]), 3002b4464b0SVladimir Kondratyev device_get_nameunit(children[i])); 3012b4464b0SVladimir Kondratyev error = device_delete_child(bus, children[i]); 3022b4464b0SVladimir Kondratyev if (error) { 3032b4464b0SVladimir Kondratyev DPRINTF("Failed deleting %s\n", 3042b4464b0SVladimir Kondratyev device_get_nameunit(children[i])); 3052b4464b0SVladimir Kondratyev break; 3062b4464b0SVladimir Kondratyev } 3072b4464b0SVladimir Kondratyev } 3082b4464b0SVladimir Kondratyev free(children, M_TEMP); 3092b4464b0SVladimir Kondratyev } 3102b4464b0SVladimir Kondratyev 3112b4464b0SVladimir Kondratyev HID_INTR_UNSETUP(device_get_parent(bus)); 3122b4464b0SVladimir Kondratyev 3132b4464b0SVladimir Kondratyev return (error); 3142b4464b0SVladimir Kondratyev } 3152b4464b0SVladimir Kondratyev 3162b4464b0SVladimir Kondratyev static int 3172b4464b0SVladimir Kondratyev hidbus_probe(device_t dev) 3182b4464b0SVladimir Kondratyev { 3192b4464b0SVladimir Kondratyev 3202b4464b0SVladimir Kondratyev device_set_desc(dev, "HID bus"); 3212b4464b0SVladimir Kondratyev 3222b4464b0SVladimir Kondratyev /* Allow other subclasses to override this driver. */ 3232b4464b0SVladimir Kondratyev return (BUS_PROBE_GENERIC); 3242b4464b0SVladimir Kondratyev } 3252b4464b0SVladimir Kondratyev 3262b4464b0SVladimir Kondratyev static int 3272b4464b0SVladimir Kondratyev hidbus_attach(device_t dev) 3282b4464b0SVladimir Kondratyev { 3292b4464b0SVladimir Kondratyev struct hidbus_softc *sc = device_get_softc(dev); 3302b4464b0SVladimir Kondratyev struct hid_device_info *devinfo = device_get_ivars(dev); 3312b4464b0SVladimir Kondratyev void *d_ptr = NULL; 3322b4464b0SVladimir Kondratyev hid_size_t d_len; 3332b4464b0SVladimir Kondratyev int error; 3342b4464b0SVladimir Kondratyev 3352b4464b0SVladimir Kondratyev sc->dev = dev; 3362b4464b0SVladimir Kondratyev CK_STAILQ_INIT(&sc->tlcs); 3372b4464b0SVladimir Kondratyev mtx_init(&sc->mtx, "hidbus ivar lock", NULL, MTX_DEF); 3382b4464b0SVladimir Kondratyev sx_init(&sc->sx, "hidbus ivar list lock"); 3392b4464b0SVladimir Kondratyev 3402b4464b0SVladimir Kondratyev /* 3412b4464b0SVladimir Kondratyev * Ignore error. It is possible for non-HID device e.g. XBox360 gamepad 3422b4464b0SVladimir Kondratyev * to emulate HID through overloading of report descriptor. 3432b4464b0SVladimir Kondratyev */ 3442b4464b0SVladimir Kondratyev d_len = devinfo->rdescsize; 3452b4464b0SVladimir Kondratyev if (d_len != 0) { 3462b4464b0SVladimir Kondratyev d_ptr = malloc(d_len, M_DEVBUF, M_ZERO | M_WAITOK); 3472b4464b0SVladimir Kondratyev error = hid_get_rdesc(dev, d_ptr, d_len); 3482b4464b0SVladimir Kondratyev if (error != 0) { 3492b4464b0SVladimir Kondratyev free(d_ptr, M_DEVBUF); 3502b4464b0SVladimir Kondratyev d_len = 0; 3512b4464b0SVladimir Kondratyev d_ptr = NULL; 3522b4464b0SVladimir Kondratyev } 3532b4464b0SVladimir Kondratyev } 3542b4464b0SVladimir Kondratyev 3552b4464b0SVladimir Kondratyev hidbus_fill_rdesc_info(&sc->rdesc, d_ptr, d_len); 3562b4464b0SVladimir Kondratyev 3572b4464b0SVladimir Kondratyev sc->nowrite = hid_test_quirk(devinfo, HQ_NOWRITE); 3582b4464b0SVladimir Kondratyev 3592b4464b0SVladimir Kondratyev error = hidbus_attach_children(dev); 3602b4464b0SVladimir Kondratyev if (error != 0) { 3612b4464b0SVladimir Kondratyev hidbus_detach(dev); 3622b4464b0SVladimir Kondratyev return (ENXIO); 3632b4464b0SVladimir Kondratyev } 3642b4464b0SVladimir Kondratyev 3652b4464b0SVladimir Kondratyev return (0); 3662b4464b0SVladimir Kondratyev } 3672b4464b0SVladimir Kondratyev 3682b4464b0SVladimir Kondratyev static int 3692b4464b0SVladimir Kondratyev hidbus_detach(device_t dev) 3702b4464b0SVladimir Kondratyev { 3712b4464b0SVladimir Kondratyev struct hidbus_softc *sc = device_get_softc(dev); 3722b4464b0SVladimir Kondratyev 3732b4464b0SVladimir Kondratyev hidbus_detach_children(dev); 3742b4464b0SVladimir Kondratyev sx_destroy(&sc->sx); 3752b4464b0SVladimir Kondratyev mtx_destroy(&sc->mtx); 3762b4464b0SVladimir Kondratyev free(sc->rdesc.data, M_DEVBUF); 3772b4464b0SVladimir Kondratyev 3782b4464b0SVladimir Kondratyev return (0); 3792b4464b0SVladimir Kondratyev } 3802b4464b0SVladimir Kondratyev 3812b4464b0SVladimir Kondratyev static void 3822b4464b0SVladimir Kondratyev hidbus_child_detached(device_t bus, device_t child) 3832b4464b0SVladimir Kondratyev { 3842b4464b0SVladimir Kondratyev struct hidbus_softc *sc = device_get_softc(bus); 3852b4464b0SVladimir Kondratyev struct hidbus_ivars *tlc = device_get_ivars(child); 3862b4464b0SVladimir Kondratyev 3872b4464b0SVladimir Kondratyev KASSERT(tlc->refcnt == 0, ("Child device is running")); 3882b4464b0SVladimir Kondratyev tlc->mtx = &sc->mtx; 3892b4464b0SVladimir Kondratyev tlc->intr_handler = NULL; 3902b4464b0SVladimir Kondratyev tlc->flags &= ~HIDBUS_FLAG_CAN_POLL; 3912b4464b0SVladimir Kondratyev } 3922b4464b0SVladimir Kondratyev 3932b4464b0SVladimir Kondratyev /* 3942b4464b0SVladimir Kondratyev * Epoch callback indicating tlc is safe to destroy 3952b4464b0SVladimir Kondratyev */ 3962b4464b0SVladimir Kondratyev static void 3972b4464b0SVladimir Kondratyev hidbus_ivar_dtor(epoch_context_t ctx) 3982b4464b0SVladimir Kondratyev { 3992b4464b0SVladimir Kondratyev struct hidbus_ivars *tlc; 4002b4464b0SVladimir Kondratyev 4012b4464b0SVladimir Kondratyev tlc = __containerof(ctx, struct hidbus_ivars, epoch_ctx); 4022b4464b0SVladimir Kondratyev free(tlc, M_DEVBUF); 4032b4464b0SVladimir Kondratyev } 4042b4464b0SVladimir Kondratyev 4052b4464b0SVladimir Kondratyev static void 4062b4464b0SVladimir Kondratyev hidbus_child_deleted(device_t bus, device_t child) 4072b4464b0SVladimir Kondratyev { 4082b4464b0SVladimir Kondratyev struct hidbus_softc *sc = device_get_softc(bus); 4092b4464b0SVladimir Kondratyev struct hidbus_ivars *tlc = device_get_ivars(child); 4102b4464b0SVladimir Kondratyev 4112b4464b0SVladimir Kondratyev sx_xlock(&sc->sx); 4122b4464b0SVladimir Kondratyev KASSERT(tlc->refcnt == 0, ("Child device is running")); 4132b4464b0SVladimir Kondratyev CK_STAILQ_REMOVE(&sc->tlcs, tlc, hidbus_ivars, link); 4142b4464b0SVladimir Kondratyev sx_unlock(&sc->sx); 4152b4464b0SVladimir Kondratyev epoch_call(INPUT_EPOCH, hidbus_ivar_dtor, &tlc->epoch_ctx); 4162b4464b0SVladimir Kondratyev } 4172b4464b0SVladimir Kondratyev 4182b4464b0SVladimir Kondratyev static int 4192b4464b0SVladimir Kondratyev hidbus_read_ivar(device_t bus, device_t child, int which, uintptr_t *result) 4202b4464b0SVladimir Kondratyev { 4212b4464b0SVladimir Kondratyev struct hidbus_softc *sc = device_get_softc(bus); 4222b4464b0SVladimir Kondratyev struct hidbus_ivars *tlc = device_get_ivars(child); 4232b4464b0SVladimir Kondratyev 4242b4464b0SVladimir Kondratyev switch (which) { 4252b4464b0SVladimir Kondratyev case HIDBUS_IVAR_INDEX: 4262b4464b0SVladimir Kondratyev *result = tlc->index; 4272b4464b0SVladimir Kondratyev break; 4282b4464b0SVladimir Kondratyev case HIDBUS_IVAR_USAGE: 4292b4464b0SVladimir Kondratyev *result = tlc->usage; 4302b4464b0SVladimir Kondratyev break; 4312b4464b0SVladimir Kondratyev case HIDBUS_IVAR_FLAGS: 4322b4464b0SVladimir Kondratyev *result = tlc->flags; 4332b4464b0SVladimir Kondratyev break; 4342b4464b0SVladimir Kondratyev case HIDBUS_IVAR_DRIVER_INFO: 4352b4464b0SVladimir Kondratyev *result = tlc->driver_info; 4362b4464b0SVladimir Kondratyev break; 4372b4464b0SVladimir Kondratyev case HIDBUS_IVAR_LOCK: 4382b4464b0SVladimir Kondratyev *result = (uintptr_t)(tlc->mtx == &sc->mtx ? NULL : tlc->mtx); 4392b4464b0SVladimir Kondratyev break; 4402b4464b0SVladimir Kondratyev default: 4412b4464b0SVladimir Kondratyev return (EINVAL); 4422b4464b0SVladimir Kondratyev } 4432b4464b0SVladimir Kondratyev return (0); 4442b4464b0SVladimir Kondratyev } 4452b4464b0SVladimir Kondratyev 4462b4464b0SVladimir Kondratyev static int 4472b4464b0SVladimir Kondratyev hidbus_write_ivar(device_t bus, device_t child, int which, uintptr_t value) 4482b4464b0SVladimir Kondratyev { 4492b4464b0SVladimir Kondratyev struct hidbus_softc *sc = device_get_softc(bus); 4502b4464b0SVladimir Kondratyev struct hidbus_ivars *tlc = device_get_ivars(child); 4512b4464b0SVladimir Kondratyev 4522b4464b0SVladimir Kondratyev switch (which) { 4532b4464b0SVladimir Kondratyev case HIDBUS_IVAR_INDEX: 4542b4464b0SVladimir Kondratyev tlc->index = value; 4552b4464b0SVladimir Kondratyev break; 4562b4464b0SVladimir Kondratyev case HIDBUS_IVAR_USAGE: 4572b4464b0SVladimir Kondratyev tlc->usage = value; 4582b4464b0SVladimir Kondratyev break; 4592b4464b0SVladimir Kondratyev case HIDBUS_IVAR_FLAGS: 4602b4464b0SVladimir Kondratyev tlc->flags = value; 4619aa0e5afSVladimir Kondratyev if ((value & HIDBUS_FLAG_CAN_POLL) != 0) 4629aa0e5afSVladimir Kondratyev HID_INTR_SETUP( 4639aa0e5afSVladimir Kondratyev device_get_parent(bus), NULL, NULL, NULL); 4642b4464b0SVladimir Kondratyev break; 4652b4464b0SVladimir Kondratyev case HIDBUS_IVAR_DRIVER_INFO: 4662b4464b0SVladimir Kondratyev tlc->driver_info = value; 4672b4464b0SVladimir Kondratyev break; 4682b4464b0SVladimir Kondratyev case HIDBUS_IVAR_LOCK: 4692b4464b0SVladimir Kondratyev tlc->mtx = (struct mtx *)value == NULL ? 4702b4464b0SVladimir Kondratyev &sc->mtx : (struct mtx *)value; 4712b4464b0SVladimir Kondratyev break; 4722b4464b0SVladimir Kondratyev default: 4732b4464b0SVladimir Kondratyev return (EINVAL); 4742b4464b0SVladimir Kondratyev } 4752b4464b0SVladimir Kondratyev return (0); 4762b4464b0SVladimir Kondratyev } 4772b4464b0SVladimir Kondratyev 4782b4464b0SVladimir Kondratyev /* Location hint for devctl(8) */ 4792b4464b0SVladimir Kondratyev static int 480*ddfc9c4cSWarner Losh hidbus_child_location(device_t bus, device_t child, struct sbuf *sb) 4812b4464b0SVladimir Kondratyev { 4822b4464b0SVladimir Kondratyev struct hidbus_ivars *tlc = device_get_ivars(child); 4832b4464b0SVladimir Kondratyev 484*ddfc9c4cSWarner Losh sbuf_printf(sb, "index=%hhu", tlc->index); 4852b4464b0SVladimir Kondratyev return (0); 4862b4464b0SVladimir Kondratyev } 4872b4464b0SVladimir Kondratyev 4882b4464b0SVladimir Kondratyev /* PnP information for devctl(8) */ 4892b4464b0SVladimir Kondratyev static int 490*ddfc9c4cSWarner Losh hidbus_child_pnpinfo(device_t bus, device_t child, struct sbuf *sb) 4912b4464b0SVladimir Kondratyev { 4922b4464b0SVladimir Kondratyev struct hidbus_ivars *tlc = device_get_ivars(child); 4932b4464b0SVladimir Kondratyev struct hid_device_info *devinfo = device_get_ivars(bus); 4942b4464b0SVladimir Kondratyev 495*ddfc9c4cSWarner Losh sbuf_printf(sb, "page=0x%04x usage=0x%04x bus=0x%02hx " 4962b4464b0SVladimir Kondratyev "vendor=0x%04hx product=0x%04hx version=0x%04hx%s%s", 4972b4464b0SVladimir Kondratyev HID_GET_USAGE_PAGE(tlc->usage), HID_GET_USAGE(tlc->usage), 4982b4464b0SVladimir Kondratyev devinfo->idBus, devinfo->idVendor, devinfo->idProduct, 4992b4464b0SVladimir Kondratyev devinfo->idVersion, devinfo->idPnP[0] == '\0' ? "" : " _HID=", 5002b4464b0SVladimir Kondratyev devinfo->idPnP[0] == '\0' ? "" : devinfo->idPnP); 5012b4464b0SVladimir Kondratyev return (0); 5022b4464b0SVladimir Kondratyev } 5032b4464b0SVladimir Kondratyev 5042b4464b0SVladimir Kondratyev void 5052b4464b0SVladimir Kondratyev hidbus_set_desc(device_t child, const char *suffix) 5062b4464b0SVladimir Kondratyev { 5072b4464b0SVladimir Kondratyev device_t bus = device_get_parent(child); 5082b4464b0SVladimir Kondratyev struct hidbus_softc *sc = device_get_softc(bus); 5092b4464b0SVladimir Kondratyev struct hid_device_info *devinfo = device_get_ivars(bus); 5102b4464b0SVladimir Kondratyev struct hidbus_ivars *tlc = device_get_ivars(child); 5112b4464b0SVladimir Kondratyev char buf[80]; 5122b4464b0SVladimir Kondratyev 5132b4464b0SVladimir Kondratyev /* Do not add NULL suffix or if device name already contains it. */ 5142b4464b0SVladimir Kondratyev if (suffix != NULL && strcasestr(devinfo->name, suffix) == NULL && 5152b4464b0SVladimir Kondratyev (sc->nauto > 1 || (tlc->flags & HIDBUS_FLAG_AUTOCHILD) == 0)) { 5162b4464b0SVladimir Kondratyev snprintf(buf, sizeof(buf), "%s %s", devinfo->name, suffix); 5172b4464b0SVladimir Kondratyev device_set_desc_copy(child, buf); 5182b4464b0SVladimir Kondratyev } else 5192b4464b0SVladimir Kondratyev device_set_desc(child, devinfo->name); 5202b4464b0SVladimir Kondratyev } 5212b4464b0SVladimir Kondratyev 5222b4464b0SVladimir Kondratyev device_t 5232b4464b0SVladimir Kondratyev hidbus_find_child(device_t bus, int32_t usage) 5242b4464b0SVladimir Kondratyev { 5252b4464b0SVladimir Kondratyev device_t *children, child; 5262b4464b0SVladimir Kondratyev int ccount, i; 5272b4464b0SVladimir Kondratyev 5282b4464b0SVladimir Kondratyev GIANT_REQUIRED; 5292b4464b0SVladimir Kondratyev 5302b4464b0SVladimir Kondratyev /* Get a list of all hidbus children */ 5312b4464b0SVladimir Kondratyev if (device_get_children(bus, &children, &ccount) != 0) 5322b4464b0SVladimir Kondratyev return (NULL); 5332b4464b0SVladimir Kondratyev 5342b4464b0SVladimir Kondratyev /* Scan through to find required TLC */ 5352b4464b0SVladimir Kondratyev for (i = 0, child = NULL; i < ccount; i++) { 5362b4464b0SVladimir Kondratyev if (hidbus_get_usage(children[i]) == usage) { 5372b4464b0SVladimir Kondratyev child = children[i]; 5382b4464b0SVladimir Kondratyev break; 5392b4464b0SVladimir Kondratyev } 5402b4464b0SVladimir Kondratyev } 5412b4464b0SVladimir Kondratyev free(children, M_TEMP); 5422b4464b0SVladimir Kondratyev 5432b4464b0SVladimir Kondratyev return (child); 5442b4464b0SVladimir Kondratyev } 5452b4464b0SVladimir Kondratyev 5462b4464b0SVladimir Kondratyev void 5472b4464b0SVladimir Kondratyev hidbus_intr(void *context, void *buf, hid_size_t len) 5482b4464b0SVladimir Kondratyev { 5492b4464b0SVladimir Kondratyev struct hidbus_softc *sc = context; 5502b4464b0SVladimir Kondratyev struct hidbus_ivars *tlc; 5512b4464b0SVladimir Kondratyev struct epoch_tracker et; 5522b4464b0SVladimir Kondratyev 5532b4464b0SVladimir Kondratyev /* 5542b4464b0SVladimir Kondratyev * Broadcast input report to all subscribers. 5552b4464b0SVladimir Kondratyev * TODO: Add check for input report ID. 5562b4464b0SVladimir Kondratyev * 5572b4464b0SVladimir Kondratyev * Relock mutex on every TLC item as we can't hold any locks over whole 5582b4464b0SVladimir Kondratyev * TLC list here due to LOR with open()/close() handlers. 5592b4464b0SVladimir Kondratyev */ 5602b4464b0SVladimir Kondratyev if (!HID_IN_POLLING_MODE()) 5612b4464b0SVladimir Kondratyev epoch_enter_preempt(INPUT_EPOCH, &et); 5622b4464b0SVladimir Kondratyev CK_STAILQ_FOREACH(tlc, &sc->tlcs, link) { 5632b4464b0SVladimir Kondratyev if (tlc->refcnt == 0 || tlc->intr_handler == NULL) 5642b4464b0SVladimir Kondratyev continue; 5652b4464b0SVladimir Kondratyev if (HID_IN_POLLING_MODE()) { 5662b4464b0SVladimir Kondratyev if ((tlc->flags & HIDBUS_FLAG_CAN_POLL) != 0) 5672b4464b0SVladimir Kondratyev tlc->intr_handler(tlc->intr_ctx, buf, len); 5682b4464b0SVladimir Kondratyev } else { 5692b4464b0SVladimir Kondratyev mtx_lock(tlc->mtx); 5702b4464b0SVladimir Kondratyev tlc->intr_handler(tlc->intr_ctx, buf, len); 5712b4464b0SVladimir Kondratyev mtx_unlock(tlc->mtx); 5722b4464b0SVladimir Kondratyev } 5732b4464b0SVladimir Kondratyev } 5742b4464b0SVladimir Kondratyev if (!HID_IN_POLLING_MODE()) 5752b4464b0SVladimir Kondratyev epoch_exit_preempt(INPUT_EPOCH, &et); 5762b4464b0SVladimir Kondratyev } 5772b4464b0SVladimir Kondratyev 5782b4464b0SVladimir Kondratyev void 5792b4464b0SVladimir Kondratyev hidbus_set_intr(device_t child, hid_intr_t *handler, void *context) 5802b4464b0SVladimir Kondratyev { 5812b4464b0SVladimir Kondratyev struct hidbus_ivars *tlc = device_get_ivars(child); 5822b4464b0SVladimir Kondratyev 5832b4464b0SVladimir Kondratyev tlc->intr_handler = handler; 5842b4464b0SVladimir Kondratyev tlc->intr_ctx = context; 5852b4464b0SVladimir Kondratyev } 5862b4464b0SVladimir Kondratyev 5872b4464b0SVladimir Kondratyev int 5882b4464b0SVladimir Kondratyev hidbus_intr_start(device_t child) 5892b4464b0SVladimir Kondratyev { 5902b4464b0SVladimir Kondratyev device_t bus = device_get_parent(child); 5912b4464b0SVladimir Kondratyev struct hidbus_softc *sc = device_get_softc(bus); 5922b4464b0SVladimir Kondratyev struct hidbus_ivars *ivar = device_get_ivars(child); 5932b4464b0SVladimir Kondratyev struct hidbus_ivars *tlc; 5942b4464b0SVladimir Kondratyev int refcnt = 0; 5952b4464b0SVladimir Kondratyev int error; 5962b4464b0SVladimir Kondratyev 5972b4464b0SVladimir Kondratyev if (sx_xlock_sig(&sc->sx) != 0) 5982b4464b0SVladimir Kondratyev return (EINTR); 5992b4464b0SVladimir Kondratyev CK_STAILQ_FOREACH(tlc, &sc->tlcs, link) { 6002b4464b0SVladimir Kondratyev refcnt += tlc->refcnt; 6012b4464b0SVladimir Kondratyev if (tlc == ivar) { 6022b4464b0SVladimir Kondratyev mtx_lock(tlc->mtx); 6032b4464b0SVladimir Kondratyev ++tlc->refcnt; 6042b4464b0SVladimir Kondratyev mtx_unlock(tlc->mtx); 6052b4464b0SVladimir Kondratyev } 6062b4464b0SVladimir Kondratyev } 6072b4464b0SVladimir Kondratyev error = refcnt != 0 ? 0 : HID_INTR_START(device_get_parent(bus)); 6082b4464b0SVladimir Kondratyev sx_unlock(&sc->sx); 6092b4464b0SVladimir Kondratyev 6102b4464b0SVladimir Kondratyev return (error); 6112b4464b0SVladimir Kondratyev } 6122b4464b0SVladimir Kondratyev 6132b4464b0SVladimir Kondratyev int 6142b4464b0SVladimir Kondratyev hidbus_intr_stop(device_t child) 6152b4464b0SVladimir Kondratyev { 6162b4464b0SVladimir Kondratyev device_t bus = device_get_parent(child); 6172b4464b0SVladimir Kondratyev struct hidbus_softc *sc = device_get_softc(bus); 6182b4464b0SVladimir Kondratyev struct hidbus_ivars *ivar = device_get_ivars(child); 6192b4464b0SVladimir Kondratyev struct hidbus_ivars *tlc; 6202b4464b0SVladimir Kondratyev bool refcnt = 0; 6212b4464b0SVladimir Kondratyev int error; 6222b4464b0SVladimir Kondratyev 6232b4464b0SVladimir Kondratyev if (sx_xlock_sig(&sc->sx) != 0) 6242b4464b0SVladimir Kondratyev return (EINTR); 6252b4464b0SVladimir Kondratyev CK_STAILQ_FOREACH(tlc, &sc->tlcs, link) { 6262b4464b0SVladimir Kondratyev if (tlc == ivar) { 6272b4464b0SVladimir Kondratyev mtx_lock(tlc->mtx); 6282b4464b0SVladimir Kondratyev MPASS(tlc->refcnt != 0); 6292b4464b0SVladimir Kondratyev --tlc->refcnt; 6302b4464b0SVladimir Kondratyev mtx_unlock(tlc->mtx); 6312b4464b0SVladimir Kondratyev } 6322b4464b0SVladimir Kondratyev refcnt += tlc->refcnt; 6332b4464b0SVladimir Kondratyev } 6342b4464b0SVladimir Kondratyev error = refcnt != 0 ? 0 : HID_INTR_STOP(device_get_parent(bus)); 6352b4464b0SVladimir Kondratyev sx_unlock(&sc->sx); 6362b4464b0SVladimir Kondratyev 6372b4464b0SVladimir Kondratyev return (error); 6382b4464b0SVladimir Kondratyev } 6392b4464b0SVladimir Kondratyev 6402b4464b0SVladimir Kondratyev void 6412b4464b0SVladimir Kondratyev hidbus_intr_poll(device_t child) 6422b4464b0SVladimir Kondratyev { 6432b4464b0SVladimir Kondratyev device_t bus = device_get_parent(child); 6442b4464b0SVladimir Kondratyev 6452b4464b0SVladimir Kondratyev HID_INTR_POLL(device_get_parent(bus)); 6462b4464b0SVladimir Kondratyev } 6472b4464b0SVladimir Kondratyev 6482b4464b0SVladimir Kondratyev struct hid_rdesc_info * 6492b4464b0SVladimir Kondratyev hidbus_get_rdesc_info(device_t child) 6502b4464b0SVladimir Kondratyev { 6512b4464b0SVladimir Kondratyev device_t bus = device_get_parent(child); 6522b4464b0SVladimir Kondratyev struct hidbus_softc *sc = device_get_softc(bus); 6532b4464b0SVladimir Kondratyev 6542b4464b0SVladimir Kondratyev return (&sc->rdesc); 6552b4464b0SVladimir Kondratyev } 6562b4464b0SVladimir Kondratyev 6572b4464b0SVladimir Kondratyev /* 6582b4464b0SVladimir Kondratyev * HID interface. 6592b4464b0SVladimir Kondratyev * 6602b4464b0SVladimir Kondratyev * Hidbus as well as any hidbus child can be passed as first arg. 6612b4464b0SVladimir Kondratyev */ 6622b4464b0SVladimir Kondratyev 6632b4464b0SVladimir Kondratyev /* Read cached report descriptor */ 6642b4464b0SVladimir Kondratyev int 6652b4464b0SVladimir Kondratyev hid_get_report_descr(device_t dev, void **data, hid_size_t *len) 6662b4464b0SVladimir Kondratyev { 6672b4464b0SVladimir Kondratyev device_t bus; 6682b4464b0SVladimir Kondratyev struct hidbus_softc *sc; 6692b4464b0SVladimir Kondratyev 6702b4464b0SVladimir Kondratyev bus = device_get_devclass(dev) == hidbus_devclass ? 6712b4464b0SVladimir Kondratyev dev : device_get_parent(dev); 6722b4464b0SVladimir Kondratyev sc = device_get_softc(bus); 6732b4464b0SVladimir Kondratyev 6742b4464b0SVladimir Kondratyev /* 6752b4464b0SVladimir Kondratyev * Do not send request to a transport backend. 6762b4464b0SVladimir Kondratyev * Use cached report descriptor instead of it. 6772b4464b0SVladimir Kondratyev */ 6782b4464b0SVladimir Kondratyev if (sc->rdesc.data == NULL || sc->rdesc.len == 0) 6792b4464b0SVladimir Kondratyev return (ENXIO); 6802b4464b0SVladimir Kondratyev 6812b4464b0SVladimir Kondratyev if (data != NULL) 6822b4464b0SVladimir Kondratyev *data = sc->rdesc.data; 6832b4464b0SVladimir Kondratyev if (len != NULL) 6842b4464b0SVladimir Kondratyev *len = sc->rdesc.len; 6852b4464b0SVladimir Kondratyev 6862b4464b0SVladimir Kondratyev return (0); 6872b4464b0SVladimir Kondratyev } 6882b4464b0SVladimir Kondratyev 6892b4464b0SVladimir Kondratyev /* 6902b4464b0SVladimir Kondratyev * Replace cached report descriptor with top level driver provided one. 6912b4464b0SVladimir Kondratyev * 6922b4464b0SVladimir Kondratyev * It deletes all hidbus children except caller and enumerates them again after 6932b4464b0SVladimir Kondratyev * new descriptor has been registered. Currently it can not be called from 6942b4464b0SVladimir Kondratyev * autoenumerated (by report's TLC) child device context as it results in child 6952b4464b0SVladimir Kondratyev * duplication. To overcome this limitation hid_set_report_descr() should be 6962b4464b0SVladimir Kondratyev * called from device_identify driver's handler with hidbus itself passed as 6972b4464b0SVladimir Kondratyev * 'device_t dev' parameter. 6982b4464b0SVladimir Kondratyev */ 6992b4464b0SVladimir Kondratyev int 7002b4464b0SVladimir Kondratyev hid_set_report_descr(device_t dev, const void *data, hid_size_t len) 7012b4464b0SVladimir Kondratyev { 7022b4464b0SVladimir Kondratyev struct hid_rdesc_info rdesc; 7032b4464b0SVladimir Kondratyev device_t bus; 7042b4464b0SVladimir Kondratyev struct hidbus_softc *sc; 7052b4464b0SVladimir Kondratyev bool is_bus; 7062b4464b0SVladimir Kondratyev int error; 7072b4464b0SVladimir Kondratyev 7082b4464b0SVladimir Kondratyev GIANT_REQUIRED; 7092b4464b0SVladimir Kondratyev 7102b4464b0SVladimir Kondratyev is_bus = device_get_devclass(dev) == hidbus_devclass; 7112b4464b0SVladimir Kondratyev bus = is_bus ? dev : device_get_parent(dev); 7122b4464b0SVladimir Kondratyev sc = device_get_softc(bus); 7132b4464b0SVladimir Kondratyev 7142b4464b0SVladimir Kondratyev /* 7152b4464b0SVladimir Kondratyev * Do not overload already overloaded report descriptor in 7162b4464b0SVladimir Kondratyev * device_identify handler. It causes infinite recursion loop. 7172b4464b0SVladimir Kondratyev */ 7182b4464b0SVladimir Kondratyev if (is_bus && sc->overloaded) 7192b4464b0SVladimir Kondratyev return(0); 7202b4464b0SVladimir Kondratyev 7212b4464b0SVladimir Kondratyev DPRINTFN(5, "len=%d\n", len); 7222b4464b0SVladimir Kondratyev DPRINTFN(5, "data = %*D\n", len, data, " "); 7232b4464b0SVladimir Kondratyev 7242b4464b0SVladimir Kondratyev error = hidbus_fill_rdesc_info(&rdesc, data, len); 7252b4464b0SVladimir Kondratyev if (error != 0) 7262b4464b0SVladimir Kondratyev return (error); 7272b4464b0SVladimir Kondratyev 7282b4464b0SVladimir Kondratyev error = hidbus_detach_children(dev); 7292b4464b0SVladimir Kondratyev if (error != 0) 7302b4464b0SVladimir Kondratyev return(error); 7312b4464b0SVladimir Kondratyev 7322b4464b0SVladimir Kondratyev /* Make private copy to handle a case of dynamicaly allocated data. */ 7332b4464b0SVladimir Kondratyev rdesc.data = malloc(len, M_DEVBUF, M_ZERO | M_WAITOK); 7342b4464b0SVladimir Kondratyev bcopy(data, rdesc.data, len); 7352b4464b0SVladimir Kondratyev sc->overloaded = true; 7362b4464b0SVladimir Kondratyev free(sc->rdesc.data, M_DEVBUF); 7372b4464b0SVladimir Kondratyev bcopy(&rdesc, &sc->rdesc, sizeof(struct hid_rdesc_info)); 7382b4464b0SVladimir Kondratyev 7392b4464b0SVladimir Kondratyev error = hidbus_attach_children(bus); 7402b4464b0SVladimir Kondratyev 7412b4464b0SVladimir Kondratyev return (error); 7422b4464b0SVladimir Kondratyev } 7432b4464b0SVladimir Kondratyev 7442b4464b0SVladimir Kondratyev static int 7452b4464b0SVladimir Kondratyev hidbus_write(device_t dev, const void *data, hid_size_t len) 7462b4464b0SVladimir Kondratyev { 7472b4464b0SVladimir Kondratyev struct hidbus_softc *sc; 7482b4464b0SVladimir Kondratyev uint8_t id; 7492b4464b0SVladimir Kondratyev 7502b4464b0SVladimir Kondratyev sc = device_get_softc(dev); 7512b4464b0SVladimir Kondratyev /* 7522b4464b0SVladimir Kondratyev * Output interrupt endpoint is often optional. If HID device 7532b4464b0SVladimir Kondratyev * does not provide it, send reports via control pipe. 7542b4464b0SVladimir Kondratyev */ 7552b4464b0SVladimir Kondratyev if (sc->nowrite) { 7562b4464b0SVladimir Kondratyev /* try to extract the ID byte */ 7572b4464b0SVladimir Kondratyev id = (sc->rdesc.oid & (len > 0)) ? *(const uint8_t*)data : 0; 7582b4464b0SVladimir Kondratyev return (hid_set_report(dev, data, len, HID_OUTPUT_REPORT, id)); 7592b4464b0SVladimir Kondratyev } 7602b4464b0SVladimir Kondratyev 7612b4464b0SVladimir Kondratyev return (hid_write(dev, data, len)); 7622b4464b0SVladimir Kondratyev } 7632b4464b0SVladimir Kondratyev 7642b4464b0SVladimir Kondratyev /*------------------------------------------------------------------------* 7652b4464b0SVladimir Kondratyev * hidbus_lookup_id 7662b4464b0SVladimir Kondratyev * 7672b4464b0SVladimir Kondratyev * This functions takes an array of "struct hid_device_id" and tries 7682b4464b0SVladimir Kondratyev * to match the entries with the information in "struct hid_device_info". 7692b4464b0SVladimir Kondratyev * 7702b4464b0SVladimir Kondratyev * Return values: 7712b4464b0SVladimir Kondratyev * NULL: No match found. 7722b4464b0SVladimir Kondratyev * Else: Pointer to matching entry. 7732b4464b0SVladimir Kondratyev *------------------------------------------------------------------------*/ 7742b4464b0SVladimir Kondratyev const struct hid_device_id * 7752b4464b0SVladimir Kondratyev hidbus_lookup_id(device_t dev, const struct hid_device_id *id, int nitems_id) 7762b4464b0SVladimir Kondratyev { 7772b4464b0SVladimir Kondratyev const struct hid_device_id *id_end; 7782b4464b0SVladimir Kondratyev const struct hid_device_info *info; 7792b4464b0SVladimir Kondratyev int32_t usage; 7802b4464b0SVladimir Kondratyev bool is_child; 7812b4464b0SVladimir Kondratyev 7822b4464b0SVladimir Kondratyev if (id == NULL) { 7832b4464b0SVladimir Kondratyev goto done; 7842b4464b0SVladimir Kondratyev } 7852b4464b0SVladimir Kondratyev 7862b4464b0SVladimir Kondratyev id_end = id + nitems_id; 7872b4464b0SVladimir Kondratyev info = hid_get_device_info(dev); 7882b4464b0SVladimir Kondratyev is_child = device_get_devclass(dev) != hidbus_devclass; 7892b4464b0SVladimir Kondratyev if (is_child) 7902b4464b0SVladimir Kondratyev usage = hidbus_get_usage(dev); 7912b4464b0SVladimir Kondratyev 7922b4464b0SVladimir Kondratyev /* 7932b4464b0SVladimir Kondratyev * Keep on matching array entries until we find a match or 7942b4464b0SVladimir Kondratyev * until we reach the end of the matching array: 7952b4464b0SVladimir Kondratyev */ 7962b4464b0SVladimir Kondratyev for (; id != id_end; id++) { 7972b4464b0SVladimir Kondratyev 7982b4464b0SVladimir Kondratyev if (is_child && (id->match_flag_page) && 7992b4464b0SVladimir Kondratyev (id->page != HID_GET_USAGE_PAGE(usage))) { 8002b4464b0SVladimir Kondratyev continue; 8012b4464b0SVladimir Kondratyev } 8022b4464b0SVladimir Kondratyev if (is_child && (id->match_flag_usage) && 8032b4464b0SVladimir Kondratyev (id->usage != HID_GET_USAGE(usage))) { 8042b4464b0SVladimir Kondratyev continue; 8052b4464b0SVladimir Kondratyev } 8062b4464b0SVladimir Kondratyev if ((id->match_flag_bus) && 8072b4464b0SVladimir Kondratyev (id->idBus != info->idBus)) { 8082b4464b0SVladimir Kondratyev continue; 8092b4464b0SVladimir Kondratyev } 8102b4464b0SVladimir Kondratyev if ((id->match_flag_vendor) && 8112b4464b0SVladimir Kondratyev (id->idVendor != info->idVendor)) { 8122b4464b0SVladimir Kondratyev continue; 8132b4464b0SVladimir Kondratyev } 8142b4464b0SVladimir Kondratyev if ((id->match_flag_product) && 8152b4464b0SVladimir Kondratyev (id->idProduct != info->idProduct)) { 8162b4464b0SVladimir Kondratyev continue; 8172b4464b0SVladimir Kondratyev } 8182b4464b0SVladimir Kondratyev if ((id->match_flag_ver_lo) && 8192b4464b0SVladimir Kondratyev (id->idVersion_lo > info->idVersion)) { 8202b4464b0SVladimir Kondratyev continue; 8212b4464b0SVladimir Kondratyev } 8222b4464b0SVladimir Kondratyev if ((id->match_flag_ver_hi) && 8232b4464b0SVladimir Kondratyev (id->idVersion_hi < info->idVersion)) { 8242b4464b0SVladimir Kondratyev continue; 8252b4464b0SVladimir Kondratyev } 8262b4464b0SVladimir Kondratyev if (id->match_flag_pnp && 8272b4464b0SVladimir Kondratyev strncmp(id->idPnP, info->idPnP, HID_PNP_ID_SIZE) != 0) { 8282b4464b0SVladimir Kondratyev continue; 8292b4464b0SVladimir Kondratyev } 8302b4464b0SVladimir Kondratyev /* We found a match! */ 8312b4464b0SVladimir Kondratyev return (id); 8322b4464b0SVladimir Kondratyev } 8332b4464b0SVladimir Kondratyev 8342b4464b0SVladimir Kondratyev done: 8352b4464b0SVladimir Kondratyev return (NULL); 8362b4464b0SVladimir Kondratyev } 8372b4464b0SVladimir Kondratyev 8382b4464b0SVladimir Kondratyev /*------------------------------------------------------------------------* 8392b4464b0SVladimir Kondratyev * hidbus_lookup_driver_info - factored out code 8402b4464b0SVladimir Kondratyev * 8412b4464b0SVladimir Kondratyev * Return values: 8422b4464b0SVladimir Kondratyev * 0: Success 8432b4464b0SVladimir Kondratyev * Else: Failure 8442b4464b0SVladimir Kondratyev *------------------------------------------------------------------------*/ 8452b4464b0SVladimir Kondratyev int 8462b4464b0SVladimir Kondratyev hidbus_lookup_driver_info(device_t child, const struct hid_device_id *id, 8472b4464b0SVladimir Kondratyev int nitems_id) 8482b4464b0SVladimir Kondratyev { 8492b4464b0SVladimir Kondratyev 8502b4464b0SVladimir Kondratyev id = hidbus_lookup_id(child, id, nitems_id); 8512b4464b0SVladimir Kondratyev if (id) { 8522b4464b0SVladimir Kondratyev /* copy driver info */ 8532b4464b0SVladimir Kondratyev hidbus_set_driver_info(child, id->driver_info); 8542b4464b0SVladimir Kondratyev return (0); 8552b4464b0SVladimir Kondratyev } 8562b4464b0SVladimir Kondratyev return (ENXIO); 8572b4464b0SVladimir Kondratyev } 8582b4464b0SVladimir Kondratyev 8592b4464b0SVladimir Kondratyev const struct hid_device_info * 8602b4464b0SVladimir Kondratyev hid_get_device_info(device_t dev) 8612b4464b0SVladimir Kondratyev { 8622b4464b0SVladimir Kondratyev device_t bus; 8632b4464b0SVladimir Kondratyev 8642b4464b0SVladimir Kondratyev bus = device_get_devclass(dev) == hidbus_devclass ? 8652b4464b0SVladimir Kondratyev dev : device_get_parent(dev); 8662b4464b0SVladimir Kondratyev 8672b4464b0SVladimir Kondratyev return (device_get_ivars(bus)); 8682b4464b0SVladimir Kondratyev } 8692b4464b0SVladimir Kondratyev 8702b4464b0SVladimir Kondratyev static device_method_t hidbus_methods[] = { 8712b4464b0SVladimir Kondratyev /* device interface */ 8722b4464b0SVladimir Kondratyev DEVMETHOD(device_probe, hidbus_probe), 8732b4464b0SVladimir Kondratyev DEVMETHOD(device_attach, hidbus_attach), 8742b4464b0SVladimir Kondratyev DEVMETHOD(device_detach, hidbus_detach), 8752b4464b0SVladimir Kondratyev DEVMETHOD(device_suspend, bus_generic_suspend), 8762b4464b0SVladimir Kondratyev DEVMETHOD(device_resume, bus_generic_resume), 8772b4464b0SVladimir Kondratyev 8782b4464b0SVladimir Kondratyev /* bus interface */ 8792b4464b0SVladimir Kondratyev DEVMETHOD(bus_add_child, hidbus_add_child), 8802b4464b0SVladimir Kondratyev DEVMETHOD(bus_child_detached, hidbus_child_detached), 8812b4464b0SVladimir Kondratyev DEVMETHOD(bus_child_deleted, hidbus_child_deleted), 8822b4464b0SVladimir Kondratyev DEVMETHOD(bus_read_ivar, hidbus_read_ivar), 8832b4464b0SVladimir Kondratyev DEVMETHOD(bus_write_ivar, hidbus_write_ivar), 884*ddfc9c4cSWarner Losh DEVMETHOD(bus_child_pnpinfo, hidbus_child_pnpinfo), 885*ddfc9c4cSWarner Losh DEVMETHOD(bus_child_location, hidbus_child_location), 8862b4464b0SVladimir Kondratyev 8872b4464b0SVladimir Kondratyev /* hid interface */ 8882b4464b0SVladimir Kondratyev DEVMETHOD(hid_get_rdesc, hid_get_rdesc), 8892b4464b0SVladimir Kondratyev DEVMETHOD(hid_read, hid_read), 8902b4464b0SVladimir Kondratyev DEVMETHOD(hid_write, hidbus_write), 8912b4464b0SVladimir Kondratyev DEVMETHOD(hid_get_report, hid_get_report), 8922b4464b0SVladimir Kondratyev DEVMETHOD(hid_set_report, hid_set_report), 8932b4464b0SVladimir Kondratyev DEVMETHOD(hid_set_idle, hid_set_idle), 8942b4464b0SVladimir Kondratyev DEVMETHOD(hid_set_protocol, hid_set_protocol), 8952b4464b0SVladimir Kondratyev 8962b4464b0SVladimir Kondratyev DEVMETHOD_END 8972b4464b0SVladimir Kondratyev }; 8982b4464b0SVladimir Kondratyev 8992b4464b0SVladimir Kondratyev devclass_t hidbus_devclass; 9002b4464b0SVladimir Kondratyev driver_t hidbus_driver = { 9012b4464b0SVladimir Kondratyev "hidbus", 9022b4464b0SVladimir Kondratyev hidbus_methods, 9032b4464b0SVladimir Kondratyev sizeof(struct hidbus_softc), 9042b4464b0SVladimir Kondratyev }; 9052b4464b0SVladimir Kondratyev 9062b4464b0SVladimir Kondratyev MODULE_DEPEND(hidbus, hid, 1, 1, 1); 9072b4464b0SVladimir Kondratyev MODULE_VERSION(hidbus, 1); 908b1f1b07fSVladimir Kondratyev DRIVER_MODULE(hidbus, iichid, hidbus_driver, hidbus_devclass, 0, 0); 90901f2e864SVladimir Kondratyev DRIVER_MODULE(hidbus, usbhid, hidbus_driver, hidbus_devclass, 0, 0); 910