From 372bf416d215ee3087b611c177258d4d422eb547 Mon Sep 17 00:00:00 2001 From: jcs Date: Wed, 29 Nov 2017 02:48:16 +0000 Subject: [PATCH] add an adaptive polling mode when no interrupt handler is available, such as on PCI dwiic(4) devices. this is a temporary workaround until the underlying interrupt problem is fixed. tested by various --- sys/dev/i2c/ihidev.c | 133 ++++++++++++++++++++++++++++++++----------- sys/dev/i2c/ihidev.h | 8 ++- 2 files changed, 108 insertions(+), 33 deletions(-) diff --git a/sys/dev/i2c/ihidev.c b/sys/dev/i2c/ihidev.c index 269fe74628c..b0a80e93278 100644 --- a/sys/dev/i2c/ihidev.c +++ b/sys/dev/i2c/ihidev.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ihidev.c,v 1.13 2017/04/08 02:57:23 deraadt Exp $ */ +/* $OpenBSD: ihidev.c,v 1.14 2017/11/29 02:48:16 jcs Exp $ */ /* * HID-over-i2c driver * @@ -38,6 +38,9 @@ #define DPRINTF(x) #endif +#define SLOW_POLL_MS 200 +#define FAST_POLL_MS 10 + /* 7.2 */ enum { I2C_HID_CMD_DESCR = 0x0, @@ -70,6 +73,8 @@ 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); +extern int hz; + struct cfattach ihidev_ca = { sizeof(struct ihidev_softc), ihidev_match, @@ -108,15 +113,27 @@ ihidev_attach(struct device *parent, struct device *self, void *aux) sc->sc_addr = ia->ia_addr; sc->sc_hid_desc_addr = ia->ia_size; - if (ia->ia_intr) - printf(" %s", iic_intr_string(sc->sc_tag, ia->ia_intr)); - if (ihidev_hid_command(sc, I2C_HID_CMD_DESCR, NULL) || ihidev_hid_desc_parse(sc)) { printf(", failed fetching initial HID descriptor\n"); return; } + if (ia->ia_intr) { + printf(" %s", iic_intr_string(sc->sc_tag, ia->ia_intr)); + + sc->sc_ih = iic_intr_establish(sc->sc_tag, ia->ia_intr, + IPL_TTY, ihidev_intr, sc, sc->sc_dev.dv_xname); + if (sc->sc_ih == NULL) + printf(", can't establish interrupt"); + } + + if (sc->sc_ih == NULL) { + printf(" (polling)"); + sc->sc_poll = 1; + sc->sc_fastpoll = 1; + } + printf(", vendor 0x%x product 0x%x, %s\n", letoh16(sc->hid_desc.wVendorID), letoh16(sc->hid_desc.wProductID), (char *)ia->ia_cookie); @@ -148,21 +165,12 @@ ihidev_attach(struct device *parent, struct device *self, void *aux) if (isize > sc->sc_isize) sc->sc_isize = isize; - DPRINTF(("%s: repid %d size %d\n", sc->sc_dev.dv_xname, repid, - repsz)); + if (repsz != 0) + DPRINTF(("%s: repid %d size %d\n", sc->sc_dev.dv_xname, + repid, repsz)); } sc->sc_ibuf = malloc(sc->sc_isize, M_DEVBUF, M_NOWAIT | M_ZERO); - /* register interrupt with system */ - if (ia->ia_intr) { - sc->sc_ih = iic_intr_establish(sc->sc_tag, ia->ia_intr, - IPL_TTY, ihidev_intr, sc, sc->sc_dev.dv_xname); - if (sc->sc_ih == NULL) { - printf(", can't establish interrupt\n"); - return; - } - } - iha.iaa = ia; iha.parent = sc; @@ -219,6 +227,20 @@ ihidev_detach(struct device *self, int flags) return (0); } +void +ihidev_sleep(struct ihidev_softc *sc, int ms) +{ + int to = ms * hz / 1000; + + if (cold) + delay(ms * 1000); + else { + if (to <= 0) + to = 1; + tsleep(&sc, PWAIT, "ihidev", to); + } +} + int ihidev_hid_command(struct ihidev_softc *sc, int hidcmd, void *arg) { @@ -363,7 +385,7 @@ ihidev_hid_command(struct ihidev_softc *sc, int hidcmd, void *arg) I2C_HID_CMD_SET_REPORT, 0, 0, 0, 0, 0, 0, }; - int cmdlen = 10; + int cmdlen = sizeof(cmd); int report_id = rreq->id; int report_len = 2 + (report_id ? 1 : 0) + rreq->len; int dataoff; @@ -377,7 +399,7 @@ ihidev_hid_command(struct ihidev_softc *sc, int hidcmd, void *arg) DPRINTF(("\n")); /* - * 7.2.2.4 - "The protocol is optimized for Report < 15. If a + * 7.2.3.4 - "The protocol is optimized for Report < 15. If a * report ID >= 15 is necessary, then the Report ID in the Low * Byte must be set to 1111 and a Third Byte is appended to the * protocol. This Third Byte contains the entire/actual report @@ -482,7 +504,7 @@ ihidev_reset(struct ihidev_softc *sc) return (1); } - DELAY(1000); + ihidev_sleep(sc, 100); if (ihidev_hid_command(sc, I2C_HID_CMD_RESET, 0)) { printf("%s: failed to reset hardware\n", sc->sc_dev.dv_xname); @@ -493,7 +515,7 @@ ihidev_reset(struct ihidev_softc *sc) return (1); } - DELAY(1000); + ihidev_sleep(sc, 100); return (0); } @@ -539,7 +561,7 @@ ihidev_hid_desc_parse(struct ihidev_softc *sc) if (retries == 0) return(1); - DELAY(1000); + ihidev_sleep(sc, 10); } else break; @@ -563,7 +585,7 @@ ihidev_intr(void *arg) struct ihidev_softc *sc = arg; struct ihidev *scd; u_int psize; - int res, i; + int res, i, fast = 0; u_char *p; u_int rep = 0; @@ -573,10 +595,8 @@ ihidev_intr(void *arg) */ iic_acquire_bus(sc->sc_tag, I2C_F_POLL); - res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, NULL, 0, sc->sc_ibuf, sc->sc_isize, I2C_F_POLL); - iic_release_bus(sc->sc_tag, I2C_F_POLL); /* @@ -585,8 +605,17 @@ ihidev_intr(void *arg) */ psize = sc->sc_ibuf[0] | sc->sc_ibuf[1] << 8; if (!psize || psize > sc->sc_isize) { - DPRINTF(("%s: %s: invalid packet size (%d vs. %d)\n", - sc->sc_dev.dv_xname, __func__, psize, sc->sc_isize)); + if (sc->sc_poll) { + /* + * TODO: all fingers are up, should we pass to hid + * layer? + */ + sc->sc_fastpoll = 0; + goto more_polling; + } else + DPRINTF(("%s: %s: invalid packet size (%d vs. %d)\n", + sc->sc_dev.dv_xname, __func__, psize, + sc->sc_isize)); return (1); } @@ -599,21 +628,50 @@ ihidev_intr(void *arg) if (rep >= sc->sc_nrepid) { printf("%s: %s: bad report id %d\n", sc->sc_dev.dv_xname, __func__, rep); + if (sc->sc_poll) { + sc->sc_fastpoll = 0; + goto more_polling; + } return (1); } - DPRINTF(("%s: ihidev_intr: hid input (rep %d):", sc->sc_dev.dv_xname, + DPRINTF(("%s: %s: hid input (rep %d):", sc->sc_dev.dv_xname, __func__, rep)); - for (i = 0; i < sc->sc_isize; i++) - DPRINTF((" %.2x", sc->sc_ibuf[i])); + for (i = 0; i < psize; i++) { + if (i > 0 && p[i] != 0 && p[i] != 0xff) { + fast = 1; + } + DPRINTF((" %.2x", p[i])); + } DPRINTF(("\n")); scd = sc->sc_subdevs[rep]; - if (scd == NULL || !(scd->sc_state & IHIDEV_OPEN)) + if (scd == NULL || !(scd->sc_state & IHIDEV_OPEN)) { + if (sc->sc_poll) { + if (sc->sc_fastpoll) { + DPRINTF(("%s: fast->slow polling\n", + sc->sc_dev.dv_xname)); + sc->sc_fastpoll = 0; + } + goto more_polling; + } return (1); + } scd->sc_intr(scd, p, psize); + if (sc->sc_poll && fast != sc->sc_fastpoll) { + DPRINTF(("%s: %s->%s polling\n", sc->sc_dev.dv_xname, + sc->sc_fastpoll ? "fast" : "slow", + fast ? "fast" : "slow")); + sc->sc_fastpoll = fast; + } + +more_polling: + if (sc->sc_poll && sc->sc_refcnt && !timeout_pending(&sc->sc_timer)) + timeout_add_msec(&sc->sc_timer, + sc->sc_fastpoll ? FAST_POLL_MS : SLOW_POLL_MS); + return (1); } @@ -680,6 +738,13 @@ ihidev_open(struct ihidev *scd) /* power on */ ihidev_reset(sc); + if (sc->sc_poll) { + if (!timeout_initialized(&sc->sc_timer)) + timeout_set(&sc->sc_timer, (void *)ihidev_intr, sc); + if (!timeout_pending(&sc->sc_timer)) + timeout_add(&sc->sc_timer, FAST_POLL_MS); + } + return (0); } @@ -699,6 +764,11 @@ ihidev_close(struct ihidev *scd) if (--sc->sc_refcnt) return; + /* no sub-devices open, conserve power */ + + if (sc->sc_poll) + timeout_del(&sc->sc_timer); + if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_OFF)) printf("%s: failed to power down\n", sc->sc_dev.dv_xname); } @@ -757,8 +827,7 @@ ihidev_get_report(struct device *dev, int type, int id, void *data, int len) } int -ihidev_set_report(struct device *dev, int type, int id, void *data, - int len) +ihidev_set_report(struct device *dev, int type, int id, void *data, int len) { struct ihidev_softc *sc = (struct ihidev_softc *)dev; struct i2c_hid_report_request rreq; diff --git a/sys/dev/i2c/ihidev.h b/sys/dev/i2c/ihidev.h index ba9e0d64863..7cd6dbc1cf5 100644 --- a/sys/dev/i2c/ihidev.h +++ b/sys/dev/i2c/ihidev.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ihidev.h,v 1.4 2016/01/31 18:24:35 jcs Exp $ */ +/* $OpenBSD: ihidev.h,v 1.5 2017/11/29 02:48:16 jcs Exp $ */ /* * HID-over-i2c driver * @@ -17,6 +17,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include + /* from usbdi.h: Match codes. */ /* First five codes is for a whole device. */ #define IMATCH_VENDOR_PRODUCT_REV 14 @@ -85,6 +87,10 @@ struct ihidev_softc { u_char *sc_ibuf; int sc_refcnt; + + int sc_poll; + int sc_fastpoll; + struct timeout sc_timer; }; struct ihidev { -- 2.20.1