1*51cb4255Skirill /* $OpenBSD: ihidev.c,v 1.39 2025/01/13 15:33:34 kirill Exp $ */ 2b037e2e6Sjcs /* 3b037e2e6Sjcs * HID-over-i2c driver 4b037e2e6Sjcs * 5fc330ebdSjcs * https://msdn.microsoft.com/en-us/library/windows/hardware/dn642101%28v=vs.85%29.aspx 6b037e2e6Sjcs * 7b037e2e6Sjcs * Copyright (c) 2015, 2016 joshua stein <jcs@openbsd.org> 8b037e2e6Sjcs * 9b037e2e6Sjcs * Permission to use, copy, modify, and distribute this software for any 10b037e2e6Sjcs * purpose with or without fee is hereby granted, provided that the above 11b037e2e6Sjcs * copyright notice and this permission notice appear in all copies. 12b037e2e6Sjcs * 13b037e2e6Sjcs * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14b037e2e6Sjcs * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15b037e2e6Sjcs * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16b037e2e6Sjcs * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17b037e2e6Sjcs * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18b037e2e6Sjcs * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19b037e2e6Sjcs * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20b037e2e6Sjcs */ 21b037e2e6Sjcs 22b037e2e6Sjcs #include <sys/param.h> 23b037e2e6Sjcs #include <sys/systm.h> 24b037e2e6Sjcs #include <sys/device.h> 25b037e2e6Sjcs #include <sys/malloc.h> 26b037e2e6Sjcs #include <sys/stdint.h> 27b037e2e6Sjcs 28b037e2e6Sjcs #include <dev/i2c/i2cvar.h> 29b037e2e6Sjcs #include <dev/i2c/ihidev.h> 30b037e2e6Sjcs 31b037e2e6Sjcs #include <dev/hid/hid.h> 32b037e2e6Sjcs 33b037e2e6Sjcs /* #define IHIDEV_DEBUG */ 34b037e2e6Sjcs 35b037e2e6Sjcs #ifdef IHIDEV_DEBUG 36b037e2e6Sjcs #define DPRINTF(x) printf x 37b037e2e6Sjcs #else 38b037e2e6Sjcs #define DPRINTF(x) 39b037e2e6Sjcs #endif 40b037e2e6Sjcs 41372bf416Sjcs #define SLOW_POLL_MS 200 42372bf416Sjcs #define FAST_POLL_MS 10 43372bf416Sjcs 44b037e2e6Sjcs /* 7.2 */ 45b037e2e6Sjcs enum { 46b037e2e6Sjcs I2C_HID_CMD_DESCR = 0x0, 47b037e2e6Sjcs I2C_HID_CMD_RESET = 0x1, 48b037e2e6Sjcs I2C_HID_CMD_GET_REPORT = 0x2, 49b037e2e6Sjcs I2C_HID_CMD_SET_REPORT = 0x3, 50b037e2e6Sjcs I2C_HID_CMD_GET_IDLE = 0x4, 51b037e2e6Sjcs I2C_HID_CMD_SET_IDLE = 0x5, 52b037e2e6Sjcs I2C_HID_CMD_GET_PROTO = 0x6, 53b037e2e6Sjcs I2C_HID_CMD_SET_PROTO = 0x7, 54b037e2e6Sjcs I2C_HID_CMD_SET_POWER = 0x8, 55b037e2e6Sjcs 56b037e2e6Sjcs /* pseudo commands */ 57b037e2e6Sjcs I2C_HID_REPORT_DESCR = 0x100, 582e24d660Skirill I2C_HID_RESET_RESPONSE = 0x101, 59b037e2e6Sjcs }; 60b037e2e6Sjcs 61b037e2e6Sjcs static int I2C_HID_POWER_ON = 0x0; 62b037e2e6Sjcs static int I2C_HID_POWER_OFF = 0x1; 63b037e2e6Sjcs 64b037e2e6Sjcs int ihidev_match(struct device *, void *, void *); 65b037e2e6Sjcs void ihidev_attach(struct device *, struct device *, void *); 66b037e2e6Sjcs int ihidev_detach(struct device *, int); 67d03221a8Sjcs int ihidev_activate(struct device *, int); 68b037e2e6Sjcs 69b037e2e6Sjcs int ihidev_hid_command(struct ihidev_softc *, int, void *); 70b037e2e6Sjcs int ihidev_intr(void *); 71e773e7e3Stobhe int ihidev_poweron(struct ihidev_softc *); 72b037e2e6Sjcs int ihidev_reset(struct ihidev_softc *); 73b037e2e6Sjcs int ihidev_hid_desc_parse(struct ihidev_softc *); 74b037e2e6Sjcs 75b037e2e6Sjcs int ihidev_maxrepid(void *buf, int len); 76b037e2e6Sjcs int ihidev_print(void *aux, const char *pnp); 77b037e2e6Sjcs int ihidev_submatch(struct device *parent, void *cf, void *aux); 78b037e2e6Sjcs 79895a6fb3Skirill #define IHIDEV_QUIRK_RE_POWER_ON 0x1 80895a6fb3Skirill 81895a6fb3Skirill const struct ihidev_quirks { 82895a6fb3Skirill uint16_t ihq_vid; 83895a6fb3Skirill uint16_t ihq_pid; 84895a6fb3Skirill int ihq_quirks; 85895a6fb3Skirill } ihidev_devs[] = { 86895a6fb3Skirill /* HONOR MagicBook Art 14 Touchpad (QTEC0002) */ 87895a6fb3Skirill { 0x35cc, 0x0104, IHIDEV_QUIRK_RE_POWER_ON }, 88895a6fb3Skirill }; 89895a6fb3Skirill 90471aeecfSnaddy const struct cfattach ihidev_ca = { 91b037e2e6Sjcs sizeof(struct ihidev_softc), 92b037e2e6Sjcs ihidev_match, 93b037e2e6Sjcs ihidev_attach, 94b037e2e6Sjcs ihidev_detach, 95d03221a8Sjcs ihidev_activate, 96b037e2e6Sjcs }; 97b037e2e6Sjcs 98b037e2e6Sjcs struct cfdriver ihidev_cd = { 99b037e2e6Sjcs NULL, "ihidev", DV_DULL 100b037e2e6Sjcs }; 101b037e2e6Sjcs 102b037e2e6Sjcs int 103b037e2e6Sjcs ihidev_match(struct device *parent, void *match, void *aux) 104b037e2e6Sjcs { 105b037e2e6Sjcs struct i2c_attach_args *ia = aux; 106b037e2e6Sjcs 107b037e2e6Sjcs if (strcmp(ia->ia_name, "ihidev") == 0) 108b037e2e6Sjcs return (1); 109b037e2e6Sjcs 110b037e2e6Sjcs return (0); 111b037e2e6Sjcs } 112b037e2e6Sjcs 113895a6fb3Skirill int 114895a6fb3Skirill ihidev_quirks(struct ihidev_softc *sc) 115895a6fb3Skirill { 116895a6fb3Skirill const struct ihidev_quirks *q; 117895a6fb3Skirill uint16_t vid, pid; 118895a6fb3Skirill int i, nent; 119895a6fb3Skirill 120895a6fb3Skirill nent = nitems(ihidev_devs); 121895a6fb3Skirill 122895a6fb3Skirill vid = letoh16(sc->hid_desc.wVendorID); 123895a6fb3Skirill pid = letoh16(sc->hid_desc.wProductID); 124895a6fb3Skirill 125895a6fb3Skirill for (i = 0, q = ihidev_devs; i < nent; i++, q++) 126895a6fb3Skirill if (vid == q->ihq_vid && pid == q->ihq_pid) 127895a6fb3Skirill return (q->ihq_quirks); 128895a6fb3Skirill 129895a6fb3Skirill return (0); 130895a6fb3Skirill } 131895a6fb3Skirill 132b037e2e6Sjcs void 133b037e2e6Sjcs ihidev_attach(struct device *parent, struct device *self, void *aux) 134b037e2e6Sjcs { 135b037e2e6Sjcs struct ihidev_softc *sc = (struct ihidev_softc *)self; 136b037e2e6Sjcs struct i2c_attach_args *ia = aux; 137b037e2e6Sjcs struct ihidev_attach_arg iha; 138b037e2e6Sjcs struct device *dev; 139b037e2e6Sjcs int repid, repsz; 140b037e2e6Sjcs int repsizes[256]; 141b037e2e6Sjcs 142b037e2e6Sjcs sc->sc_tag = ia->ia_tag; 143b037e2e6Sjcs sc->sc_addr = ia->ia_addr; 144e08b9924Skettenis sc->sc_hid_desc_addr = ia->ia_size; 145b037e2e6Sjcs 146b037e2e6Sjcs if (ihidev_hid_command(sc, I2C_HID_CMD_DESCR, NULL) || 147b037e2e6Sjcs ihidev_hid_desc_parse(sc)) { 148b037e2e6Sjcs printf(", failed fetching initial HID descriptor\n"); 149b037e2e6Sjcs return; 150b037e2e6Sjcs } 151b037e2e6Sjcs 152b037e2e6Sjcs sc->sc_nrepid = ihidev_maxrepid(sc->sc_report, sc->sc_reportlen); 153b037e2e6Sjcs if (sc->sc_nrepid < 0) 154b037e2e6Sjcs return; 155b037e2e6Sjcs 156b037e2e6Sjcs sc->sc_nrepid++; 157b037e2e6Sjcs sc->sc_subdevs = mallocarray(sc->sc_nrepid, sizeof(struct ihidev *), 158a1868ea7Skettenis M_DEVBUF, M_WAITOK | M_ZERO); 159b037e2e6Sjcs 160fc330ebdSjcs /* find largest report size and allocate memory for input buffer */ 16159d1b13fSkettenis sc->sc_isize = letoh16(sc->hid_desc.wMaxInputLength); 162b037e2e6Sjcs for (repid = 0; repid < sc->sc_nrepid; repid++) { 163b037e2e6Sjcs repsz = hid_report_size(sc->sc_report, sc->sc_reportlen, 164b037e2e6Sjcs hid_input, repid); 165b037e2e6Sjcs repsizes[repid] = repsz; 166e8835b17Sjcs if (repsz > sc->sc_isize) 167e8835b17Sjcs sc->sc_isize = repsz; 168372bf416Sjcs if (repsz != 0) 169372bf416Sjcs DPRINTF(("%s: repid %d size %d\n", sc->sc_dev.dv_xname, 170372bf416Sjcs repid, repsz)); 171fc330ebdSjcs } 172a1868ea7Skettenis sc->sc_ibuf = malloc(sc->sc_isize, M_DEVBUF, M_WAITOK | M_ZERO); 173b037e2e6Sjcs 174a91662ffSmglocker if (ia->ia_intr) { 175a91662ffSmglocker printf(" %s", iic_intr_string(sc->sc_tag, ia->ia_intr)); 176a91662ffSmglocker 177a91662ffSmglocker sc->sc_ih = iic_intr_establish(sc->sc_tag, ia->ia_intr, 178a91662ffSmglocker IPL_TTY, ihidev_intr, sc, sc->sc_dev.dv_xname); 179a91662ffSmglocker if (sc->sc_ih == NULL) 180a91662ffSmglocker printf("%s: can't establish interrupt\n", 181a91662ffSmglocker sc->sc_dev.dv_xname); 182a91662ffSmglocker } 183a91662ffSmglocker 184a91662ffSmglocker if (ia->ia_poll || !sc->sc_ih) { 185a91662ffSmglocker printf(" (polling)"); 186a91662ffSmglocker sc->sc_poll = 1; 187a91662ffSmglocker sc->sc_fastpoll = 1; 188a91662ffSmglocker } 189a91662ffSmglocker 190a91662ffSmglocker printf(", vendor 0x%x product 0x%x, %s\n", 191a91662ffSmglocker letoh16(sc->hid_desc.wVendorID), letoh16(sc->hid_desc.wProductID), 192a91662ffSmglocker (char *)ia->ia_cookie); 193a91662ffSmglocker 194a91662ffSmglocker printf("%s: %d report id%s\n", sc->sc_dev.dv_xname, (sc->sc_nrepid - 1), 195a91662ffSmglocker (sc->sc_nrepid - 1) > 1 ? "s" : ""); 196a91662ffSmglocker 197fc330ebdSjcs iha.iaa = ia; 198fc330ebdSjcs iha.parent = sc; 199fc330ebdSjcs 2008fabbf99Sjcs /* Look for a driver claiming multiple report IDs first. */ 2018fabbf99Sjcs iha.reportid = IHIDEV_CLAIM_MULTIPLEID; 2028fabbf99Sjcs iha.nclaims = 0; 203fc330ebdSjcs dev = config_found_sm((struct device *)sc, &iha, NULL, 204fc330ebdSjcs ihidev_submatch); 205fc330ebdSjcs if (dev != NULL) { 2068fabbf99Sjcs for (repid = 0; repid < iha.nclaims; repid++) { 2078fabbf99Sjcs sc->sc_subdevs[iha.claims[repid]] = 2088fabbf99Sjcs (struct ihidev *)dev; 2098fabbf99Sjcs } 210fc330ebdSjcs } 211fc330ebdSjcs 212fc330ebdSjcs for (repid = 0; repid < sc->sc_nrepid; repid++) { 2138fabbf99Sjcs if (sc->sc_subdevs[repid] != NULL) 2148fabbf99Sjcs continue; 2158fabbf99Sjcs 216b037e2e6Sjcs if (hid_report_size(sc->sc_report, sc->sc_reportlen, hid_input, 217b037e2e6Sjcs repid) == 0 && 218b037e2e6Sjcs hid_report_size(sc->sc_report, sc->sc_reportlen, 219b037e2e6Sjcs hid_output, repid) == 0 && 220b037e2e6Sjcs hid_report_size(sc->sc_report, sc->sc_reportlen, 221b037e2e6Sjcs hid_feature, repid) == 0) 222b037e2e6Sjcs continue; 223b037e2e6Sjcs 224b037e2e6Sjcs iha.reportid = repid; 225b037e2e6Sjcs dev = config_found_sm(self, &iha, ihidev_print, 226b037e2e6Sjcs ihidev_submatch); 227b037e2e6Sjcs sc->sc_subdevs[repid] = (struct ihidev *)dev; 228b037e2e6Sjcs } 229b037e2e6Sjcs 230b9d12f9eSkettenis if (sc->sc_refcnt > 0) 231b9d12f9eSkettenis return; 232b9d12f9eSkettenis 233b037e2e6Sjcs /* power down until we're opened */ 234b037e2e6Sjcs if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_OFF)) { 235b037e2e6Sjcs printf("%s: failed to power down\n", sc->sc_dev.dv_xname); 236b037e2e6Sjcs return; 237b037e2e6Sjcs } 238b037e2e6Sjcs } 239b037e2e6Sjcs 240b037e2e6Sjcs int 241b037e2e6Sjcs ihidev_detach(struct device *self, int flags) 242b037e2e6Sjcs { 243b037e2e6Sjcs struct ihidev_softc *sc = (struct ihidev_softc *)self; 244b037e2e6Sjcs 245b037e2e6Sjcs if (sc->sc_ih != NULL) { 246d3689130Skettenis iic_intr_disestablish(sc->sc_tag, sc->sc_ih); 247b037e2e6Sjcs sc->sc_ih = NULL; 248b037e2e6Sjcs } 249b037e2e6Sjcs 250b037e2e6Sjcs if (sc->sc_ibuf != NULL) { 251234dfda1Sderaadt free(sc->sc_ibuf, M_DEVBUF, sc->sc_isize); 252b037e2e6Sjcs sc->sc_ibuf = NULL; 253b037e2e6Sjcs } 254b037e2e6Sjcs 255b037e2e6Sjcs if (sc->sc_report != NULL) 256b037e2e6Sjcs free(sc->sc_report, M_DEVBUF, sc->sc_reportlen); 257b037e2e6Sjcs 258b037e2e6Sjcs return (0); 259b037e2e6Sjcs } 260b037e2e6Sjcs 261d03221a8Sjcs int 262d03221a8Sjcs ihidev_activate(struct device *self, int act) 263d03221a8Sjcs { 264d03221a8Sjcs struct ihidev_softc *sc = (struct ihidev_softc *)self; 26507e9317cSderaadt int rv; 266d03221a8Sjcs 267d03221a8Sjcs DPRINTF(("%s(%d)\n", __func__, act)); 268d03221a8Sjcs 269d03221a8Sjcs switch (act) { 270d03221a8Sjcs case DVACT_QUIESCE: 27107e9317cSderaadt rv = config_activate_children(self, act); 272d03221a8Sjcs sc->sc_dying = 1; 273d03221a8Sjcs if (sc->sc_poll && timeout_initialized(&sc->sc_timer)) { 2744b1a56afSjsg DPRINTF(("%s: cancelling polling\n", 275d03221a8Sjcs sc->sc_dev.dv_xname)); 276d03221a8Sjcs timeout_del_barrier(&sc->sc_timer); 277d03221a8Sjcs } 278d03221a8Sjcs if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, 279d03221a8Sjcs &I2C_HID_POWER_OFF)) 280d03221a8Sjcs printf("%s: failed to power down\n", 281d03221a8Sjcs sc->sc_dev.dv_xname); 282d03221a8Sjcs break; 283d03221a8Sjcs case DVACT_WAKEUP: 284e773e7e3Stobhe ihidev_poweron(sc); 285d03221a8Sjcs sc->sc_dying = 0; 286d03221a8Sjcs if (sc->sc_poll && timeout_initialized(&sc->sc_timer)) 287d03221a8Sjcs timeout_add(&sc->sc_timer, 2000); 28807e9317cSderaadt rv = config_activate_children(self, act); 28907e9317cSderaadt break; 29007e9317cSderaadt default: 29107e9317cSderaadt rv = config_activate_children(self, act); 292d03221a8Sjcs break; 293d03221a8Sjcs } 29407e9317cSderaadt return rv; 295d03221a8Sjcs } 296d03221a8Sjcs 297372bf416Sjcs void 298372bf416Sjcs ihidev_sleep(struct ihidev_softc *sc, int ms) 299372bf416Sjcs { 300372bf416Sjcs if (cold) 301372bf416Sjcs delay(ms * 1000); 302deacac3eScheloha else 303deacac3eScheloha tsleep_nsec(&sc, PWAIT, "ihidev", MSEC_TO_NSEC(ms)); 304372bf416Sjcs } 305372bf416Sjcs 306b037e2e6Sjcs int 307b037e2e6Sjcs ihidev_hid_command(struct ihidev_softc *sc, int hidcmd, void *arg) 308b037e2e6Sjcs { 309b037e2e6Sjcs int i, res = 1; 310b037e2e6Sjcs 311b037e2e6Sjcs iic_acquire_bus(sc->sc_tag, 0); 312b037e2e6Sjcs 313b037e2e6Sjcs switch (hidcmd) { 314b037e2e6Sjcs case I2C_HID_CMD_DESCR: { 315b037e2e6Sjcs /* 316b037e2e6Sjcs * 5.2.2 - HID Descriptor Retrieval 317fc330ebdSjcs * register is passed from the controller 318b037e2e6Sjcs */ 319fc330ebdSjcs uint8_t cmd[] = { 320bb27db76Sjcs htole16(sc->sc_hid_desc_addr) & 0xff, 321bb27db76Sjcs htole16(sc->sc_hid_desc_addr) >> 8, 322fc330ebdSjcs }; 323b037e2e6Sjcs 324b037e2e6Sjcs DPRINTF(("%s: HID command I2C_HID_CMD_DESCR at 0x%x\n", 325e08b9924Skettenis sc->sc_dev.dv_xname, htole16(sc->sc_hid_desc_addr))); 326b037e2e6Sjcs 327b037e2e6Sjcs /* 20 00 */ 328146fcb02Skettenis res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 329fc330ebdSjcs &cmd, sizeof(cmd), &sc->hid_desc_buf, 330b037e2e6Sjcs sizeof(struct i2c_hid_desc), 0); 331b037e2e6Sjcs 332b037e2e6Sjcs DPRINTF(("%s: HID descriptor:", sc->sc_dev.dv_xname)); 333b037e2e6Sjcs for (i = 0; i < sizeof(struct i2c_hid_desc); i++) 334b037e2e6Sjcs DPRINTF((" %.2x", sc->hid_desc_buf[i])); 335b037e2e6Sjcs DPRINTF(("\n")); 336b037e2e6Sjcs 337b037e2e6Sjcs break; 338b037e2e6Sjcs } 339b037e2e6Sjcs case I2C_HID_CMD_RESET: { 340fc330ebdSjcs uint8_t cmd[] = { 341bb27db76Sjcs htole16(sc->hid_desc.wCommandRegister) & 0xff, 342bb27db76Sjcs htole16(sc->hid_desc.wCommandRegister) >> 8, 343fc330ebdSjcs 0, 344fc330ebdSjcs I2C_HID_CMD_RESET, 345fc330ebdSjcs }; 346b037e2e6Sjcs 347b037e2e6Sjcs DPRINTF(("%s: HID command I2C_HID_CMD_RESET\n", 348b037e2e6Sjcs sc->sc_dev.dv_xname)); 349b037e2e6Sjcs 350b037e2e6Sjcs /* 22 00 00 01 */ 351146fcb02Skettenis res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 352fc330ebdSjcs &cmd, sizeof(cmd), NULL, 0, 0); 353b037e2e6Sjcs 354b037e2e6Sjcs break; 355b037e2e6Sjcs } 356fc330ebdSjcs case I2C_HID_CMD_GET_REPORT: { 357fc330ebdSjcs struct i2c_hid_report_request *rreq = 358fc330ebdSjcs (struct i2c_hid_report_request *)arg; 359fc330ebdSjcs 360fc330ebdSjcs uint8_t cmd[] = { 361bb27db76Sjcs htole16(sc->hid_desc.wCommandRegister) & 0xff, 362bb27db76Sjcs htole16(sc->hid_desc.wCommandRegister) >> 8, 363fc330ebdSjcs 0, 364fc330ebdSjcs I2C_HID_CMD_GET_REPORT, 365fc330ebdSjcs 0, 0, 0, 366fc330ebdSjcs }; 367fc330ebdSjcs int cmdlen = 7; 368fc330ebdSjcs int dataoff = 4; 369fc330ebdSjcs int report_id = rreq->id; 370fc330ebdSjcs int report_id_len = 1; 371fc330ebdSjcs int report_len = rreq->len + 2; 372fc330ebdSjcs int d; 373fc330ebdSjcs uint8_t *tmprep; 374fc330ebdSjcs 375fc330ebdSjcs DPRINTF(("%s: HID command I2C_HID_CMD_GET_REPORT %d " 376fc330ebdSjcs "(type %d, len %d)\n", sc->sc_dev.dv_xname, report_id, 377fc330ebdSjcs rreq->type, rreq->len)); 378fc330ebdSjcs 379fc330ebdSjcs /* 380fc330ebdSjcs * 7.2.2.4 - "The protocol is optimized for Report < 15. If a 381fc330ebdSjcs * report ID >= 15 is necessary, then the Report ID in the Low 382fc330ebdSjcs * Byte must be set to 1111 and a Third Byte is appended to the 383fc330ebdSjcs * protocol. This Third Byte contains the entire/actual report 384fc330ebdSjcs * ID." 385fc330ebdSjcs */ 386fc330ebdSjcs if (report_id >= 15) { 387fc330ebdSjcs cmd[dataoff++] = report_id; 388fc330ebdSjcs report_id = 15; 389fc330ebdSjcs report_id_len = 2; 390fc330ebdSjcs } else 391fc330ebdSjcs cmdlen--; 392fc330ebdSjcs 393fc330ebdSjcs cmd[2] = report_id | rreq->type << 4; 394fc330ebdSjcs 395bb27db76Sjcs cmd[dataoff++] = sc->hid_desc.wDataRegister & 0xff; 396bb27db76Sjcs cmd[dataoff] = sc->hid_desc.wDataRegister >> 8; 397fc330ebdSjcs 398fc330ebdSjcs /* 399fc330ebdSjcs * 7.2.2.2 - Response will be a 2-byte length value, the report 400fc330ebdSjcs * id with length determined above, and then the report. 401fc330ebdSjcs * Allocate rreq->len + 2 + 2 bytes, read into that temporary 402fc330ebdSjcs * buffer, and then copy only the report back out to 403fc330ebdSjcs * rreq->data. 404fc330ebdSjcs */ 405fc330ebdSjcs report_len += report_id_len; 406a1868ea7Skettenis tmprep = malloc(report_len, M_DEVBUF, M_WAITOK | M_ZERO); 407fc330ebdSjcs 408fc330ebdSjcs /* type 3 id 8: 22 00 38 02 23 00 */ 409fc330ebdSjcs res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 410fc330ebdSjcs &cmd, cmdlen, tmprep, report_len, 0); 411fc330ebdSjcs 412fc330ebdSjcs d = tmprep[0] | tmprep[1] << 8; 413fc330ebdSjcs if (d != report_len) 414fc330ebdSjcs DPRINTF(("%s: response size %d != expected length %d\n", 415fc330ebdSjcs sc->sc_dev.dv_xname, d, report_len)); 416fc330ebdSjcs 417fc330ebdSjcs if (report_id_len == 2) 418fc330ebdSjcs d = tmprep[2] | tmprep[3] << 8; 419fc330ebdSjcs else 420fc330ebdSjcs d = tmprep[2]; 421fc330ebdSjcs 422fc330ebdSjcs if (d != rreq->id) { 423fc330ebdSjcs DPRINTF(("%s: response report id %d != %d\n", 424fc330ebdSjcs sc->sc_dev.dv_xname, d, rreq->id)); 425fc330ebdSjcs iic_release_bus(sc->sc_tag, 0); 4261937a0caSjsg free(tmprep, M_DEVBUF, report_len); 427fc330ebdSjcs return (1); 428fc330ebdSjcs } 429fc330ebdSjcs 430fc330ebdSjcs DPRINTF(("%s: response:", sc->sc_dev.dv_xname)); 431fc330ebdSjcs for (i = 0; i < report_len; i++) 432fc330ebdSjcs DPRINTF((" %.2x", tmprep[i])); 433fc330ebdSjcs DPRINTF(("\n")); 434fc330ebdSjcs 435fc330ebdSjcs memcpy(rreq->data, tmprep + 2 + report_id_len, rreq->len); 436fc330ebdSjcs free(tmprep, M_DEVBUF, report_len); 437fc330ebdSjcs 438fc330ebdSjcs break; 439fc330ebdSjcs } 440fc330ebdSjcs case I2C_HID_CMD_SET_REPORT: { 441fc330ebdSjcs struct i2c_hid_report_request *rreq = 442fc330ebdSjcs (struct i2c_hid_report_request *)arg; 443fc330ebdSjcs 444fc330ebdSjcs uint8_t cmd[] = { 445bb27db76Sjcs htole16(sc->hid_desc.wCommandRegister) & 0xff, 446bb27db76Sjcs htole16(sc->hid_desc.wCommandRegister) >> 8, 447fc330ebdSjcs 0, 448fc330ebdSjcs I2C_HID_CMD_SET_REPORT, 449fc330ebdSjcs 0, 0, 0, 0, 0, 0, 450fc330ebdSjcs }; 451372bf416Sjcs int cmdlen = sizeof(cmd); 452fc330ebdSjcs int report_id = rreq->id; 453fc330ebdSjcs int report_len = 2 + (report_id ? 1 : 0) + rreq->len; 454fc330ebdSjcs int dataoff; 455fc330ebdSjcs uint8_t *finalcmd; 456fc330ebdSjcs 457fc330ebdSjcs DPRINTF(("%s: HID command I2C_HID_CMD_SET_REPORT %d " 458fc330ebdSjcs "(type %d, len %d):", sc->sc_dev.dv_xname, report_id, 459fc330ebdSjcs rreq->type, rreq->len)); 460fc330ebdSjcs for (i = 0; i < rreq->len; i++) 461fc330ebdSjcs DPRINTF((" %.2x", ((uint8_t *)rreq->data)[i])); 462fc330ebdSjcs DPRINTF(("\n")); 463fc330ebdSjcs 464fc330ebdSjcs /* 465372bf416Sjcs * 7.2.3.4 - "The protocol is optimized for Report < 15. If a 466fc330ebdSjcs * report ID >= 15 is necessary, then the Report ID in the Low 467fc330ebdSjcs * Byte must be set to 1111 and a Third Byte is appended to the 468fc330ebdSjcs * protocol. This Third Byte contains the entire/actual report 469fc330ebdSjcs * ID." 470fc330ebdSjcs */ 471fc330ebdSjcs dataoff = 4; 472fc330ebdSjcs if (report_id >= 15) { 473fc330ebdSjcs cmd[dataoff++] = report_id; 474fc330ebdSjcs report_id = 15; 475fc330ebdSjcs } else 476fc330ebdSjcs cmdlen--; 477fc330ebdSjcs 478fc330ebdSjcs cmd[2] = report_id | rreq->type << 4; 479fc330ebdSjcs 480650700c9Smglocker cmd[dataoff++] = htole16(sc->hid_desc.wDataRegister) & 0xff; 481650700c9Smglocker cmd[dataoff++] = htole16(sc->hid_desc.wDataRegister) >> 8; 482fc330ebdSjcs 483fc330ebdSjcs cmd[dataoff++] = report_len & 0xff; 484fc330ebdSjcs cmd[dataoff++] = report_len >> 8; 485fc330ebdSjcs cmd[dataoff] = rreq->id; 486fc330ebdSjcs 487fc330ebdSjcs finalcmd = malloc(cmdlen + rreq->len, M_DEVBUF, 488a1868ea7Skettenis M_WAITOK | M_ZERO); 489fc330ebdSjcs 490fc330ebdSjcs memcpy(finalcmd, cmd, cmdlen); 491fc330ebdSjcs memcpy(finalcmd + cmdlen, rreq->data, rreq->len); 492fc330ebdSjcs 493fc330ebdSjcs /* type 3 id 4: 22 00 34 03 23 00 04 00 04 03 */ 494fc330ebdSjcs res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 495fc330ebdSjcs finalcmd, cmdlen + rreq->len, NULL, 0, 0); 496fc330ebdSjcs 497fc330ebdSjcs free(finalcmd, M_DEVBUF, cmdlen + rreq->len); 498fc330ebdSjcs 499fc330ebdSjcs break; 500fc330ebdSjcs } 501fc330ebdSjcs 502b037e2e6Sjcs case I2C_HID_CMD_SET_POWER: { 503b037e2e6Sjcs int power = *(int *)arg; 504fc330ebdSjcs uint8_t cmd[] = { 505bb27db76Sjcs htole16(sc->hid_desc.wCommandRegister) & 0xff, 506bb27db76Sjcs htole16(sc->hid_desc.wCommandRegister) >> 8, 507fc330ebdSjcs power, 508fc330ebdSjcs I2C_HID_CMD_SET_POWER, 509fc330ebdSjcs }; 510b037e2e6Sjcs 511b037e2e6Sjcs DPRINTF(("%s: HID command I2C_HID_CMD_SET_POWER(%d)\n", 512b037e2e6Sjcs sc->sc_dev.dv_xname, power)); 513b037e2e6Sjcs 514b037e2e6Sjcs /* 22 00 00 08 */ 515146fcb02Skettenis res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 516fc330ebdSjcs &cmd, sizeof(cmd), NULL, 0, 0); 517b037e2e6Sjcs 518b037e2e6Sjcs break; 519b037e2e6Sjcs } 520b037e2e6Sjcs case I2C_HID_REPORT_DESCR: { 521fc330ebdSjcs uint8_t cmd[] = { 522bb27db76Sjcs htole16(sc->hid_desc.wReportDescRegister) & 0xff, 523bb27db76Sjcs htole16(sc->hid_desc.wReportDescRegister) >> 8, 524fc330ebdSjcs }; 525b037e2e6Sjcs 526b037e2e6Sjcs DPRINTF(("%s: HID command I2C_HID_REPORT_DESCR at 0x%x with " 527fc330ebdSjcs "size %d\n", sc->sc_dev.dv_xname, cmd[0], 528b037e2e6Sjcs sc->sc_reportlen)); 529b037e2e6Sjcs 530b037e2e6Sjcs /* 20 00 */ 531146fcb02Skettenis res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 532fc330ebdSjcs &cmd, sizeof(cmd), sc->sc_report, sc->sc_reportlen, 0); 533b037e2e6Sjcs 534b037e2e6Sjcs DPRINTF(("%s: HID report descriptor:", sc->sc_dev.dv_xname)); 535b037e2e6Sjcs for (i = 0; i < sc->sc_reportlen; i++) 536b037e2e6Sjcs DPRINTF((" %.2x", sc->sc_report[i])); 537b037e2e6Sjcs DPRINTF(("\n")); 538b037e2e6Sjcs 539b037e2e6Sjcs break; 540b037e2e6Sjcs } 5412e24d660Skirill case I2C_HID_RESET_RESPONSE: { 5422e24d660Skirill int i; 5432e24d660Skirill uint8_t buf[2] = { 0xff, 0xff }; 5442e24d660Skirill 5452e24d660Skirill DPRINTF(("%s: HID command I2C_HID_RESET_RESPONSE\n", 5462e24d660Skirill sc->sc_dev.dv_xname)); 5472e24d660Skirill 5482e24d660Skirill /* 5492e24d660Skirill * 7.2.1 states that a device should response for RESET 5502e24d660Skirill * in less than 5 seconds. It uses poll instead of 5512e24d660Skirill * tsleep because interrupts are blocked during autoconf. 5522e24d660Skirill */ 5532e24d660Skirill for (i = 0; i < 50; i++) { 5542e24d660Skirill ihidev_sleep(sc, 100); 5552e24d660Skirill res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 5562e24d660Skirill sc->sc_addr, NULL, 0, buf, sizeof(buf), 0); 5572e24d660Skirill DPRINTF(("%s: read attempt %d: 0x%x, 0x%x, res: %d\n", 5582e24d660Skirill sc->sc_dev.dv_xname, i, buf[0], buf[1], res)); 5592e24d660Skirill if (!res) 5602e24d660Skirill res = (buf[0] != 0x00 || buf[1] != 0x00); 5612e24d660Skirill if (!res) 5622e24d660Skirill break; 5632e24d660Skirill } 5642e24d660Skirill 5652e24d660Skirill break; 5662e24d660Skirill } 567b037e2e6Sjcs default: 568b037e2e6Sjcs printf("%s: unknown command %d\n", sc->sc_dev.dv_xname, 569b037e2e6Sjcs hidcmd); 570b037e2e6Sjcs } 571b037e2e6Sjcs 572b037e2e6Sjcs iic_release_bus(sc->sc_tag, 0); 573b037e2e6Sjcs 574b037e2e6Sjcs return (res); 575b037e2e6Sjcs } 576b037e2e6Sjcs 577b037e2e6Sjcs int 578e773e7e3Stobhe ihidev_poweron(struct ihidev_softc *sc) 579b037e2e6Sjcs { 580b037e2e6Sjcs if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_ON)) { 581b037e2e6Sjcs printf("%s: failed to power on\n", sc->sc_dev.dv_xname); 582b037e2e6Sjcs return (1); 583b037e2e6Sjcs } 584b037e2e6Sjcs 585372bf416Sjcs ihidev_sleep(sc, 100); 586b037e2e6Sjcs 587e773e7e3Stobhe return 0; 588e773e7e3Stobhe } 589e773e7e3Stobhe 590e773e7e3Stobhe 591e773e7e3Stobhe int 592e773e7e3Stobhe ihidev_reset(struct ihidev_softc *sc) 593e773e7e3Stobhe { 594*51cb4255Skirill DPRINTF(("%s: resetting\n", sc->sc_dev.dv_xname)); 595*51cb4255Skirill 596e773e7e3Stobhe if (ihidev_poweron(sc)) 597e773e7e3Stobhe return (1); 598e773e7e3Stobhe 599b037e2e6Sjcs if (ihidev_hid_command(sc, I2C_HID_CMD_RESET, 0)) { 600b037e2e6Sjcs printf("%s: failed to reset hardware\n", sc->sc_dev.dv_xname); 601b037e2e6Sjcs 602b037e2e6Sjcs ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, 603b037e2e6Sjcs &I2C_HID_POWER_OFF); 604b037e2e6Sjcs 605b037e2e6Sjcs return (1); 606b037e2e6Sjcs } 607b037e2e6Sjcs 6082e24d660Skirill if (ihidev_hid_command(sc, I2C_HID_RESET_RESPONSE, 0)) { 6092e24d660Skirill printf("%s: unexpected reset response\n", 6102e24d660Skirill sc->sc_dev.dv_xname); 6112e24d660Skirill return (1); 6122e24d660Skirill } 613b037e2e6Sjcs 614b037e2e6Sjcs return (0); 615b037e2e6Sjcs } 616b037e2e6Sjcs 617b037e2e6Sjcs /* 618b037e2e6Sjcs * 5.2.2 - HID Descriptor Retrieval 619b037e2e6Sjcs * 620b037e2e6Sjcs * parse HID Descriptor that has already been read into hid_desc with 621b037e2e6Sjcs * I2C_HID_CMD_DESCR 622b037e2e6Sjcs */ 623b037e2e6Sjcs int 624b037e2e6Sjcs ihidev_hid_desc_parse(struct ihidev_softc *sc) 625b037e2e6Sjcs { 6263bd8062fSkirill sc->sc_quirks = ihidev_quirks(sc); 6273bd8062fSkirill 628b037e2e6Sjcs /* must be v01.00 */ 629b037e2e6Sjcs if (letoh16(sc->hid_desc.bcdVersion) != 0x0100) { 630b037e2e6Sjcs printf("%s: bad HID descriptor bcdVersion (0x%x)\n", 631b037e2e6Sjcs sc->sc_dev.dv_xname, 632b037e2e6Sjcs letoh16(sc->hid_desc.bcdVersion)); 633b037e2e6Sjcs return (1); 634b037e2e6Sjcs } 635b037e2e6Sjcs 636b037e2e6Sjcs /* must be 30 bytes for v1.00 */ 637b037e2e6Sjcs if (letoh16(sc->hid_desc.wHIDDescLength != 638b037e2e6Sjcs sizeof(struct i2c_hid_desc))) { 639b037e2e6Sjcs printf("%s: bad HID descriptor size (%d != %zu)\n", 640b037e2e6Sjcs sc->sc_dev.dv_xname, 641b037e2e6Sjcs letoh16(sc->hid_desc.wHIDDescLength), 642b037e2e6Sjcs sizeof(struct i2c_hid_desc)); 643b037e2e6Sjcs return (1); 644b037e2e6Sjcs } 645b037e2e6Sjcs 646b037e2e6Sjcs if (letoh16(sc->hid_desc.wReportDescLength) <= 0) { 647b037e2e6Sjcs printf("%s: bad HID report descriptor size (%d)\n", 648b037e2e6Sjcs sc->sc_dev.dv_xname, 649b037e2e6Sjcs letoh16(sc->hid_desc.wReportDescLength)); 650b037e2e6Sjcs return (1); 651b037e2e6Sjcs } 652b037e2e6Sjcs 6532e24d660Skirill if (ihidev_reset(sc)) 654b037e2e6Sjcs return (1); 655b037e2e6Sjcs 656b037e2e6Sjcs sc->sc_reportlen = letoh16(sc->hid_desc.wReportDescLength); 657a1868ea7Skettenis sc->sc_report = malloc(sc->sc_reportlen, M_DEVBUF, M_WAITOK | M_ZERO); 658b037e2e6Sjcs 659b037e2e6Sjcs if (ihidev_hid_command(sc, I2C_HID_REPORT_DESCR, 0)) { 660b037e2e6Sjcs printf("%s: failed fetching HID report\n", 661b037e2e6Sjcs sc->sc_dev.dv_xname); 662b037e2e6Sjcs return (1); 663b037e2e6Sjcs } 664b037e2e6Sjcs 665895a6fb3Skirill if (sc->sc_quirks & IHIDEV_QUIRK_RE_POWER_ON) { 666895a6fb3Skirill if (ihidev_poweron(sc)) 667895a6fb3Skirill return (1); 668895a6fb3Skirill 669895a6fb3Skirill /* 670895a6fb3Skirill * 7.2.8 states that a device shall not respond back 671895a6fb3Skirill * after receiving the power on command, and must ensure 672895a6fb3Skirill * that it transitions to power on state in less than 1 673895a6fb3Skirill * second. The ihidev_poweron function uses a shorter 674895a6fb3Skirill * sleep, sufficient for the ON-RESET sequence. Here, 675895a6fb3Skirill * however, it sleeps for the full second to accommodate 676895a6fb3Skirill * cold boot scenarios on affected devices. 677895a6fb3Skirill */ 678895a6fb3Skirill 679895a6fb3Skirill ihidev_sleep(sc, 1000); 680895a6fb3Skirill } 681895a6fb3Skirill 682b037e2e6Sjcs return (0); 683b037e2e6Sjcs } 684b037e2e6Sjcs 685d03221a8Sjcs void 686d03221a8Sjcs ihidev_poll(void *arg) 687d03221a8Sjcs { 688d03221a8Sjcs struct ihidev_softc *sc = arg; 689d03221a8Sjcs 690d03221a8Sjcs sc->sc_frompoll = 1; 691d03221a8Sjcs ihidev_intr(sc); 692d03221a8Sjcs sc->sc_frompoll = 0; 693d03221a8Sjcs } 694d03221a8Sjcs 695b037e2e6Sjcs int 696b037e2e6Sjcs ihidev_intr(void *arg) 697b037e2e6Sjcs { 698b037e2e6Sjcs struct ihidev_softc *sc = arg; 699b037e2e6Sjcs struct ihidev *scd; 700f6f15de3Sjcs int psize, res, i, fast = 0; 701b037e2e6Sjcs u_char *p; 702b037e2e6Sjcs u_int rep = 0; 703b037e2e6Sjcs 704d03221a8Sjcs if (sc->sc_poll && !sc->sc_frompoll) { 705d03221a8Sjcs DPRINTF(("%s: received interrupt while polling, disabling " 706d03221a8Sjcs "polling\n", sc->sc_dev.dv_xname)); 707d03221a8Sjcs sc->sc_poll = 0; 708d03221a8Sjcs timeout_del_barrier(&sc->sc_timer); 709d03221a8Sjcs } 710d03221a8Sjcs 711e6f4df9dSkettenis /* 712e6f4df9dSkettenis * XXX: force I2C_F_POLL for now to avoid dwiic interrupting 713e6f4df9dSkettenis * while we are interrupting 714e6f4df9dSkettenis */ 715b037e2e6Sjcs 716e6f4df9dSkettenis iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 717146fcb02Skettenis res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, NULL, 0, 718e8835b17Sjcs sc->sc_ibuf, letoh16(sc->hid_desc.wMaxInputLength), I2C_F_POLL); 719e6f4df9dSkettenis iic_release_bus(sc->sc_tag, I2C_F_POLL); 720b037e2e6Sjcs 721fc330ebdSjcs /* 722fc330ebdSjcs * 6.1.1 - First two bytes are the packet length, which must be less 723fc330ebdSjcs * than or equal to wMaxInputLength 724fc330ebdSjcs */ 725b037e2e6Sjcs psize = sc->sc_ibuf[0] | sc->sc_ibuf[1] << 8; 726f6f15de3Sjcs if (psize <= 2 || psize > sc->sc_isize) { 727372bf416Sjcs if (sc->sc_poll) { 728372bf416Sjcs /* 729372bf416Sjcs * TODO: all fingers are up, should we pass to hid 730372bf416Sjcs * layer? 731372bf416Sjcs */ 732372bf416Sjcs sc->sc_fastpoll = 0; 733372bf416Sjcs goto more_polling; 734372bf416Sjcs } else 735fc330ebdSjcs DPRINTF(("%s: %s: invalid packet size (%d vs. %d)\n", 736372bf416Sjcs sc->sc_dev.dv_xname, __func__, psize, 737372bf416Sjcs sc->sc_isize)); 738b037e2e6Sjcs return (1); 739b037e2e6Sjcs } 740b037e2e6Sjcs 741fc330ebdSjcs /* 3rd byte is the report id */ 742b037e2e6Sjcs p = sc->sc_ibuf + 2; 743bf610880Sjcs psize -= 2; 744b037e2e6Sjcs if (sc->sc_nrepid != 1) 745fc330ebdSjcs rep = *p++, psize--; 746b037e2e6Sjcs 747b037e2e6Sjcs if (rep >= sc->sc_nrepid) { 748fc330ebdSjcs printf("%s: %s: bad report id %d\n", sc->sc_dev.dv_xname, 749fc330ebdSjcs __func__, rep); 750372bf416Sjcs if (sc->sc_poll) { 751372bf416Sjcs sc->sc_fastpoll = 0; 752372bf416Sjcs goto more_polling; 753372bf416Sjcs } 754b037e2e6Sjcs return (1); 755b037e2e6Sjcs } 756b037e2e6Sjcs 757372bf416Sjcs DPRINTF(("%s: %s: hid input (rep %d):", sc->sc_dev.dv_xname, __func__, 758fc330ebdSjcs rep)); 759372bf416Sjcs for (i = 0; i < psize; i++) { 760372bf416Sjcs if (i > 0 && p[i] != 0 && p[i] != 0xff) { 761372bf416Sjcs fast = 1; 762372bf416Sjcs } 763372bf416Sjcs DPRINTF((" %.2x", p[i])); 764372bf416Sjcs } 765fc330ebdSjcs DPRINTF(("\n")); 766fc330ebdSjcs 767b037e2e6Sjcs scd = sc->sc_subdevs[rep]; 768372bf416Sjcs if (scd == NULL || !(scd->sc_state & IHIDEV_OPEN)) { 769372bf416Sjcs if (sc->sc_poll) { 770372bf416Sjcs if (sc->sc_fastpoll) { 771372bf416Sjcs DPRINTF(("%s: fast->slow polling\n", 772372bf416Sjcs sc->sc_dev.dv_xname)); 773372bf416Sjcs sc->sc_fastpoll = 0; 774372bf416Sjcs } 775372bf416Sjcs goto more_polling; 776372bf416Sjcs } 777b037e2e6Sjcs return (1); 778372bf416Sjcs } 779b037e2e6Sjcs 7805959e33bSkettenis if (!sc->sc_dying) 781bf610880Sjcs scd->sc_intr(scd, p, psize); 782b037e2e6Sjcs 783d03221a8Sjcs if (sc->sc_poll && (fast != sc->sc_fastpoll)) { 784372bf416Sjcs DPRINTF(("%s: %s->%s polling\n", sc->sc_dev.dv_xname, 785372bf416Sjcs sc->sc_fastpoll ? "fast" : "slow", 786372bf416Sjcs fast ? "fast" : "slow")); 787372bf416Sjcs sc->sc_fastpoll = fast; 788372bf416Sjcs } 789372bf416Sjcs 790372bf416Sjcs more_polling: 791d03221a8Sjcs if (sc->sc_poll && sc->sc_refcnt && !sc->sc_dying && 792d03221a8Sjcs !timeout_pending(&sc->sc_timer)) 793372bf416Sjcs timeout_add_msec(&sc->sc_timer, 794372bf416Sjcs sc->sc_fastpoll ? FAST_POLL_MS : SLOW_POLL_MS); 795372bf416Sjcs 796b037e2e6Sjcs return (1); 797b037e2e6Sjcs } 798b037e2e6Sjcs 799b037e2e6Sjcs int 800b037e2e6Sjcs ihidev_maxrepid(void *buf, int len) 801b037e2e6Sjcs { 802b037e2e6Sjcs struct hid_data *d; 803b037e2e6Sjcs struct hid_item h; 804b037e2e6Sjcs int maxid; 805b037e2e6Sjcs 806b037e2e6Sjcs maxid = -1; 807b037e2e6Sjcs h.report_ID = 0; 808fdc486edSanton for (d = hid_start_parse(buf, len, hid_all); hid_get_item(d, &h);) 809b037e2e6Sjcs if (h.report_ID > maxid) 810b037e2e6Sjcs maxid = h.report_ID; 811b037e2e6Sjcs hid_end_parse(d); 812b037e2e6Sjcs 813b037e2e6Sjcs return (maxid); 814b037e2e6Sjcs } 815b037e2e6Sjcs 816b037e2e6Sjcs int 817b037e2e6Sjcs ihidev_print(void *aux, const char *pnp) 818b037e2e6Sjcs { 819b037e2e6Sjcs struct ihidev_attach_arg *iha = aux; 820b037e2e6Sjcs 821b037e2e6Sjcs if (pnp) 822b037e2e6Sjcs printf("hid at %s", pnp); 823b037e2e6Sjcs 8248fabbf99Sjcs if (iha->reportid != 0) 825b037e2e6Sjcs printf(" reportid %d", iha->reportid); 826b037e2e6Sjcs 827b037e2e6Sjcs return (UNCONF); 828b037e2e6Sjcs } 829b037e2e6Sjcs 830b037e2e6Sjcs int 831b037e2e6Sjcs ihidev_submatch(struct device *parent, void *match, void *aux) 832b037e2e6Sjcs { 833b037e2e6Sjcs struct ihidev_attach_arg *iha = aux; 834b037e2e6Sjcs struct cfdata *cf = match; 835b037e2e6Sjcs 836b037e2e6Sjcs if (cf->ihidevcf_reportid != IHIDEV_UNK_REPORTID && 837b037e2e6Sjcs cf->ihidevcf_reportid != iha->reportid) 838b037e2e6Sjcs return (0); 839b037e2e6Sjcs 840b037e2e6Sjcs return ((*cf->cf_attach->ca_match)(parent, cf, aux)); 841b037e2e6Sjcs } 842b037e2e6Sjcs 843b037e2e6Sjcs int 844b037e2e6Sjcs ihidev_open(struct ihidev *scd) 845b037e2e6Sjcs { 846b037e2e6Sjcs struct ihidev_softc *sc = scd->sc_parent; 847b037e2e6Sjcs 848b037e2e6Sjcs DPRINTF(("%s: %s: state=%d refcnt=%d\n", sc->sc_dev.dv_xname, 849b037e2e6Sjcs __func__, scd->sc_state, sc->sc_refcnt)); 850b037e2e6Sjcs 851b037e2e6Sjcs if (scd->sc_state & IHIDEV_OPEN) 852b037e2e6Sjcs return (EBUSY); 853b037e2e6Sjcs 854b037e2e6Sjcs scd->sc_state |= IHIDEV_OPEN; 855b037e2e6Sjcs 856b037e2e6Sjcs if (sc->sc_refcnt++ || sc->sc_isize == 0) 857b037e2e6Sjcs return (0); 858b037e2e6Sjcs 859b037e2e6Sjcs /* power on */ 860e773e7e3Stobhe ihidev_poweron(sc); 861b037e2e6Sjcs 862372bf416Sjcs if (sc->sc_poll) { 863372bf416Sjcs if (!timeout_initialized(&sc->sc_timer)) 864d03221a8Sjcs timeout_set(&sc->sc_timer, (void *)ihidev_poll, sc); 865372bf416Sjcs if (!timeout_pending(&sc->sc_timer)) 866372bf416Sjcs timeout_add(&sc->sc_timer, FAST_POLL_MS); 867372bf416Sjcs } 868372bf416Sjcs 869b037e2e6Sjcs return (0); 870b037e2e6Sjcs } 871b037e2e6Sjcs 872b037e2e6Sjcs void 873b037e2e6Sjcs ihidev_close(struct ihidev *scd) 874b037e2e6Sjcs { 875b037e2e6Sjcs struct ihidev_softc *sc = scd->sc_parent; 876b037e2e6Sjcs 877b037e2e6Sjcs DPRINTF(("%s: %s: state=%d refcnt=%d\n", sc->sc_dev.dv_xname, 878b037e2e6Sjcs __func__, scd->sc_state, sc->sc_refcnt)); 879b037e2e6Sjcs 880b037e2e6Sjcs if (!(scd->sc_state & IHIDEV_OPEN)) 881b037e2e6Sjcs return; 882b037e2e6Sjcs 883b037e2e6Sjcs scd->sc_state &= ~IHIDEV_OPEN; 884b037e2e6Sjcs 885b037e2e6Sjcs if (--sc->sc_refcnt) 886b037e2e6Sjcs return; 887b037e2e6Sjcs 888372bf416Sjcs /* no sub-devices open, conserve power */ 889372bf416Sjcs 890d03221a8Sjcs if (sc->sc_poll && timeout_pending(&sc->sc_timer)) 891372bf416Sjcs timeout_del(&sc->sc_timer); 892372bf416Sjcs 893b037e2e6Sjcs if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_OFF)) 894b037e2e6Sjcs printf("%s: failed to power down\n", sc->sc_dev.dv_xname); 895b037e2e6Sjcs } 896b037e2e6Sjcs 897b037e2e6Sjcs int 898b037e2e6Sjcs ihidev_ioctl(struct ihidev *sc, u_long cmd, caddr_t addr, int flag, 899b037e2e6Sjcs struct proc *p) 900b037e2e6Sjcs { 901b037e2e6Sjcs return -1; 902b037e2e6Sjcs } 903b037e2e6Sjcs 904b037e2e6Sjcs void 905b037e2e6Sjcs ihidev_get_report_desc(struct ihidev_softc *sc, void **desc, int *size) 906b037e2e6Sjcs { 907b037e2e6Sjcs *desc = sc->sc_report; 908b037e2e6Sjcs *size = sc->sc_reportlen; 909b037e2e6Sjcs } 910fc330ebdSjcs 911fc330ebdSjcs int 912fc330ebdSjcs ihidev_report_type_conv(int hid_type_id) 913fc330ebdSjcs { 914fc330ebdSjcs switch (hid_type_id) { 915fc330ebdSjcs case hid_input: 916fc330ebdSjcs return I2C_HID_REPORT_TYPE_INPUT; 917fc330ebdSjcs case hid_output: 918fc330ebdSjcs return I2C_HID_REPORT_TYPE_OUTPUT; 919fc330ebdSjcs case hid_feature: 920fc330ebdSjcs return I2C_HID_REPORT_TYPE_FEATURE; 921fc330ebdSjcs default: 922fc330ebdSjcs return -1; 923fc330ebdSjcs } 924fc330ebdSjcs } 925fc330ebdSjcs 926fc330ebdSjcs int 927fc330ebdSjcs ihidev_get_report(struct device *dev, int type, int id, void *data, int len) 928fc330ebdSjcs { 929fc330ebdSjcs struct ihidev_softc *sc = (struct ihidev_softc *)dev; 930fc330ebdSjcs struct i2c_hid_report_request rreq; 931fc330ebdSjcs 932253b28d0Sjcs rreq.type = type; 933fc330ebdSjcs rreq.id = id; 934fc330ebdSjcs rreq.data = data; 935fc330ebdSjcs rreq.len = len; 936fc330ebdSjcs 937fc330ebdSjcs if (ihidev_hid_command(sc, I2C_HID_CMD_GET_REPORT, &rreq)) { 938fc330ebdSjcs printf("%s: failed fetching report\n", sc->sc_dev.dv_xname); 939fc330ebdSjcs return (1); 940fc330ebdSjcs } 941fc330ebdSjcs 942fc330ebdSjcs return 0; 943fc330ebdSjcs } 944fc330ebdSjcs 945fc330ebdSjcs int 946372bf416Sjcs ihidev_set_report(struct device *dev, int type, int id, void *data, int len) 947fc330ebdSjcs { 948fc330ebdSjcs struct ihidev_softc *sc = (struct ihidev_softc *)dev; 949fc330ebdSjcs struct i2c_hid_report_request rreq; 950fc330ebdSjcs 951253b28d0Sjcs rreq.type = type; 952fc330ebdSjcs rreq.id = id; 953fc330ebdSjcs rreq.data = data; 954fc330ebdSjcs rreq.len = len; 955fc330ebdSjcs 956fc330ebdSjcs if (ihidev_hid_command(sc, I2C_HID_CMD_SET_REPORT, &rreq)) { 957fc330ebdSjcs printf("%s: failed setting report\n", sc->sc_dev.dv_xname); 958fc330ebdSjcs return (1); 959fc330ebdSjcs } 960fc330ebdSjcs 961fc330ebdSjcs return 0; 962fc330ebdSjcs } 963650700c9Smglocker 964650700c9Smglocker int 965650700c9Smglocker ihidev_send_report(struct device *dev, int repid, void *data, int data_len) 966650700c9Smglocker { 967650700c9Smglocker struct ihidev_softc *sc = (struct ihidev_softc *)dev; 968650700c9Smglocker uint8_t *finalcmd, cmd[5]; 969650700c9Smglocker int cmd_len, report_len, res; 970650700c9Smglocker 971650700c9Smglocker cmd_len = sizeof(cmd); 972650700c9Smglocker report_len = 2 + 1 + data_len; 973650700c9Smglocker 974650700c9Smglocker cmd[0] = htole16(sc->hid_desc.wOutputRegister) & 0xff; 975650700c9Smglocker cmd[1] = htole16(sc->hid_desc.wOutputRegister) >> 8; 976650700c9Smglocker cmd[2] = report_len & 0xff; 977650700c9Smglocker cmd[3] = report_len >> 8; 978650700c9Smglocker cmd[4] = repid; 979650700c9Smglocker 980650700c9Smglocker finalcmd = malloc(cmd_len + data_len, M_DEVBUF, M_NOWAIT | M_ZERO); 981650700c9Smglocker if (finalcmd == NULL) 982650700c9Smglocker return ENOMEM; 983650700c9Smglocker 984650700c9Smglocker memcpy(finalcmd, cmd, cmd_len); 985650700c9Smglocker memcpy(finalcmd + cmd_len, data, data_len); 986650700c9Smglocker 987650700c9Smglocker res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 988650700c9Smglocker finalcmd, cmd_len + data_len, NULL, 0, 0); 989650700c9Smglocker 990650700c9Smglocker free(finalcmd, M_DEVBUF, cmd_len + data_len); 991650700c9Smglocker 992650700c9Smglocker return res; 993650700c9Smglocker } 994