diff options
Diffstat (limited to 'sys/dev/i2c/ihidev.c')
-rw-r--r-- | sys/dev/i2c/ihidev.c | 99 |
1 files changed, 85 insertions, 14 deletions
diff --git a/sys/dev/i2c/ihidev.c b/sys/dev/i2c/ihidev.c index af5e90cebd5..099f271a637 100644 --- a/sys/dev/i2c/ihidev.c +++ b/sys/dev/i2c/ihidev.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ihidev.c,v 1.35 2025/01/06 02:13:55 kirill Exp $ */ +/* $OpenBSD: ihidev.c,v 1.39 2025/01/13 15:33:34 kirill Exp $ */ /* * HID-over-i2c driver * @@ -76,6 +76,17 @@ int ihidev_maxrepid(void *buf, int len); int ihidev_print(void *aux, const char *pnp); int ihidev_submatch(struct device *parent, void *cf, void *aux); +#define IHIDEV_QUIRK_RE_POWER_ON 0x1 + +const struct ihidev_quirks { + uint16_t ihq_vid; + uint16_t ihq_pid; + int ihq_quirks; +} ihidev_devs[] = { + /* HONOR MagicBook Art 14 Touchpad (QTEC0002) */ + { 0x35cc, 0x0104, IHIDEV_QUIRK_RE_POWER_ON }, +}; + const struct cfattach ihidev_ca = { sizeof(struct ihidev_softc), ihidev_match, @@ -99,6 +110,25 @@ ihidev_match(struct device *parent, void *match, void *aux) return (0); } +int +ihidev_quirks(struct ihidev_softc *sc) +{ + const struct ihidev_quirks *q; + uint16_t vid, pid; + int i, nent; + + nent = nitems(ihidev_devs); + + vid = letoh16(sc->hid_desc.wVendorID); + pid = letoh16(sc->hid_desc.wProductID); + + for (i = 0, q = ihidev_devs; i < nent; i++, q++) + if (vid == q->ihq_vid && pid == q->ihq_pid) + return (q->ihq_quirks); + + return (0); +} + void ihidev_attach(struct device *parent, struct device *self, void *aux) { @@ -447,17 +477,8 @@ ihidev_hid_command(struct ihidev_softc *sc, int hidcmd, void *arg) cmd[2] = report_id | rreq->type << 4; - if (rreq->type == I2C_HID_REPORT_TYPE_FEATURE) { - cmd[dataoff++] = htole16(sc->hid_desc.wDataRegister) - & 0xff; - cmd[dataoff++] = htole16(sc->hid_desc.wDataRegister) - >> 8; - } else { - cmd[dataoff++] = htole16(sc->hid_desc.wOutputRegister) - & 0xff; - cmd[dataoff++] = htole16(sc->hid_desc.wOutputRegister) - >> 8; - } + cmd[dataoff++] = htole16(sc->hid_desc.wDataRegister) & 0xff; + cmd[dataoff++] = htole16(sc->hid_desc.wDataRegister) >> 8; cmd[dataoff++] = report_len & 0xff; cmd[dataoff++] = report_len >> 8; @@ -556,8 +577,6 @@ ihidev_hid_command(struct ihidev_softc *sc, int hidcmd, void *arg) int ihidev_poweron(struct ihidev_softc *sc) { - DPRINTF(("%s: resetting\n", sc->sc_dev.dv_xname)); - if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_ON)) { printf("%s: failed to power on\n", sc->sc_dev.dv_xname); return (1); @@ -572,6 +591,8 @@ ihidev_poweron(struct ihidev_softc *sc) int ihidev_reset(struct ihidev_softc *sc) { + DPRINTF(("%s: resetting\n", sc->sc_dev.dv_xname)); + if (ihidev_poweron(sc)) return (1); @@ -602,6 +623,8 @@ ihidev_reset(struct ihidev_softc *sc) int ihidev_hid_desc_parse(struct ihidev_softc *sc) { + sc->sc_quirks = ihidev_quirks(sc); + /* must be v01.00 */ if (letoh16(sc->hid_desc.bcdVersion) != 0x0100) { printf("%s: bad HID descriptor bcdVersion (0x%x)\n", @@ -639,6 +662,23 @@ ihidev_hid_desc_parse(struct ihidev_softc *sc) return (1); } + if (sc->sc_quirks & IHIDEV_QUIRK_RE_POWER_ON) { + if (ihidev_poweron(sc)) + return (1); + + /* + * 7.2.8 states that a device shall not respond back + * after receiving the power on command, and must ensure + * that it transitions to power on state in less than 1 + * second. The ihidev_poweron function uses a shorter + * sleep, sufficient for the ON-RESET sequence. Here, + * however, it sleeps for the full second to accommodate + * cold boot scenarios on affected devices. + */ + + ihidev_sleep(sc, 1000); + } + return (0); } @@ -920,3 +960,34 @@ ihidev_set_report(struct device *dev, int type, int id, void *data, int len) return 0; } + +int +ihidev_send_report(struct device *dev, int repid, void *data, int data_len) +{ + struct ihidev_softc *sc = (struct ihidev_softc *)dev; + uint8_t *finalcmd, cmd[5]; + int cmd_len, report_len, res; + + cmd_len = sizeof(cmd); + report_len = 2 + 1 + data_len; + + cmd[0] = htole16(sc->hid_desc.wOutputRegister) & 0xff; + cmd[1] = htole16(sc->hid_desc.wOutputRegister) >> 8; + cmd[2] = report_len & 0xff; + cmd[3] = report_len >> 8; + cmd[4] = repid; + + finalcmd = malloc(cmd_len + data_len, M_DEVBUF, M_NOWAIT | M_ZERO); + if (finalcmd == NULL) + return ENOMEM; + + memcpy(finalcmd, cmd, cmd_len); + memcpy(finalcmd + cmd_len, data, data_len); + + res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, + finalcmd, cmd_len + data_len, NULL, 0, 0); + + free(finalcmd, M_DEVBUF, cmd_len + data_len); + + return res; +} |