LCOV - code coverage report
Current view: top level - dev/i2c - ihidev.c (source / functions) Hit Total Coverage
Test: 6.4 Lines: 0 355 0.0 %
Date: 2018-10-19 03:25:38 Functions: 0 18 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* $OpenBSD: ihidev.c,v 1.17 2018/08/25 18:32:05 jcs Exp $ */
       2             : /*
       3             :  * HID-over-i2c driver
       4             :  *
       5             :  * https://msdn.microsoft.com/en-us/library/windows/hardware/dn642101%28v=vs.85%29.aspx
       6             :  *
       7             :  * Copyright (c) 2015, 2016 joshua stein <jcs@openbsd.org>
       8             :  *
       9             :  * Permission to use, copy, modify, and distribute this software for any
      10             :  * purpose with or without fee is hereby granted, provided that the above
      11             :  * copyright notice and this permission notice appear in all copies.
      12             :  *
      13             :  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      14             :  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      15             :  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
      16             :  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
      17             :  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
      18             :  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
      19             :  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
      20             :  */
      21             : 
      22             : #include <sys/param.h>
      23             : #include <sys/systm.h>
      24             : #include <sys/device.h>
      25             : #include <sys/malloc.h>
      26             : #include <sys/stdint.h>
      27             : 
      28             : #include <dev/i2c/i2cvar.h>
      29             : #include <dev/i2c/ihidev.h>
      30             : 
      31             : #include <dev/hid/hid.h>
      32             : 
      33             : /* #define IHIDEV_DEBUG */
      34             : 
      35             : #ifdef IHIDEV_DEBUG
      36             : #define DPRINTF(x) printf x
      37             : #else
      38             : #define DPRINTF(x)
      39             : #endif
      40             : 
      41             : #define SLOW_POLL_MS    200
      42             : #define FAST_POLL_MS    10
      43             : 
      44             : /* 7.2 */
      45             : enum {
      46             :         I2C_HID_CMD_DESCR       = 0x0,
      47             :         I2C_HID_CMD_RESET       = 0x1,
      48             :         I2C_HID_CMD_GET_REPORT  = 0x2,
      49             :         I2C_HID_CMD_SET_REPORT  = 0x3,
      50             :         I2C_HID_CMD_GET_IDLE    = 0x4,
      51             :         I2C_HID_CMD_SET_IDLE    = 0x5,
      52             :         I2C_HID_CMD_GET_PROTO   = 0x6,
      53             :         I2C_HID_CMD_SET_PROTO   = 0x7,
      54             :         I2C_HID_CMD_SET_POWER   = 0x8,
      55             : 
      56             :         /* pseudo commands */
      57             :         I2C_HID_REPORT_DESCR    = 0x100,
      58             : };
      59             : 
      60             : static int I2C_HID_POWER_ON     = 0x0;
      61             : static int I2C_HID_POWER_OFF    = 0x1;
      62             : 
      63             : int     ihidev_match(struct device *, void *, void *);
      64             : void    ihidev_attach(struct device *, struct device *, void *);
      65             : int     ihidev_detach(struct device *, int);
      66             : 
      67             : int     ihidev_hid_command(struct ihidev_softc *, int, void *);
      68             : int     ihidev_intr(void *);
      69             : int     ihidev_reset(struct ihidev_softc *);
      70             : int     ihidev_hid_desc_parse(struct ihidev_softc *);
      71             : 
      72             : int     ihidev_maxrepid(void *buf, int len);
      73             : int     ihidev_print(void *aux, const char *pnp);
      74             : int     ihidev_submatch(struct device *parent, void *cf, void *aux);
      75             : 
      76             : extern int hz;
      77             : 
      78             : struct cfattach ihidev_ca = {
      79             :         sizeof(struct ihidev_softc),
      80             :         ihidev_match,
      81             :         ihidev_attach,
      82             :         ihidev_detach,
      83             :         NULL
      84             : };
      85             : 
      86             : struct cfdriver ihidev_cd = {
      87             :         NULL, "ihidev", DV_DULL
      88             : };
      89             : 
      90             : int
      91           0 : ihidev_match(struct device *parent, void *match, void *aux)
      92             : {
      93           0 :         struct i2c_attach_args *ia = aux;
      94             : 
      95           0 :         if (strcmp(ia->ia_name, "ihidev") == 0)
      96           0 :                 return (1);
      97             : 
      98           0 :         return (0);
      99           0 : }
     100             : 
     101             : void
     102           0 : ihidev_attach(struct device *parent, struct device *self, void *aux)
     103             : {
     104           0 :         struct ihidev_softc *sc = (struct ihidev_softc *)self;
     105           0 :         struct i2c_attach_args *ia = aux;
     106           0 :         struct ihidev_attach_arg iha;
     107             :         struct device *dev;
     108             :         int repid, repsz;
     109           0 :         int repsizes[256];
     110             :         int isize;
     111             : 
     112           0 :         sc->sc_tag = ia->ia_tag;
     113           0 :         sc->sc_addr = ia->ia_addr;
     114           0 :         sc->sc_hid_desc_addr = ia->ia_size;
     115             : 
     116           0 :         if (ihidev_hid_command(sc, I2C_HID_CMD_DESCR, NULL) ||
     117           0 :             ihidev_hid_desc_parse(sc)) {
     118           0 :                 printf(", failed fetching initial HID descriptor\n");
     119           0 :                 return;
     120             :         }
     121             : 
     122           0 :         if (ia->ia_intr) {
     123           0 :                 printf(" %s", iic_intr_string(sc->sc_tag, ia->ia_intr));
     124             : 
     125           0 :                 sc->sc_ih = iic_intr_establish(sc->sc_tag, ia->ia_intr,
     126             :                     IPL_TTY, ihidev_intr, sc, sc->sc_dev.dv_xname);
     127           0 :                 if (sc->sc_ih == NULL)
     128           0 :                         printf(", can't establish interrupt");
     129             :         }
     130             : 
     131           0 :         if (sc->sc_ih == NULL) {
     132           0 :                 printf(" (polling)");
     133           0 :                 sc->sc_poll = 1;
     134           0 :                 sc->sc_fastpoll = 1;
     135           0 :         }
     136             : 
     137           0 :         printf(", vendor 0x%x product 0x%x, %s\n",
     138           0 :             letoh16(sc->hid_desc.wVendorID), letoh16(sc->hid_desc.wProductID),
     139           0 :             (char *)ia->ia_cookie);
     140             : 
     141           0 :         sc->sc_nrepid = ihidev_maxrepid(sc->sc_report, sc->sc_reportlen);
     142           0 :         if (sc->sc_nrepid < 0)
     143           0 :                 return;
     144             : 
     145           0 :         printf("%s: %d report id%s\n", sc->sc_dev.dv_xname, sc->sc_nrepid,
     146           0 :             sc->sc_nrepid > 1 ? "s" : "");
     147             : 
     148           0 :         sc->sc_nrepid++;
     149           0 :         sc->sc_subdevs = mallocarray(sc->sc_nrepid, sizeof(struct ihidev *),
     150             :             M_DEVBUF, M_NOWAIT | M_ZERO);
     151           0 :         if (sc->sc_subdevs == NULL) {
     152           0 :                 printf("%s: failed allocating memory\n", sc->sc_dev.dv_xname);
     153           0 :                 return;
     154             :         }
     155             : 
     156             :         /* find largest report size and allocate memory for input buffer */
     157           0 :         sc->sc_isize = letoh16(sc->hid_desc.wMaxInputLength);
     158           0 :         for (repid = 0; repid < sc->sc_nrepid; repid++) {
     159           0 :                 repsz = hid_report_size(sc->sc_report, sc->sc_reportlen,
     160           0 :                     hid_input, repid);
     161           0 :                 repsizes[repid] = repsz;
     162             : 
     163           0 :                 isize = repsz + 2; /* two bytes for the length */
     164           0 :                 isize += (sc->sc_nrepid != 1); /* one byte for the report ID */
     165           0 :                 if (isize > sc->sc_isize)
     166           0 :                         sc->sc_isize = isize;
     167             : 
     168             :                 if (repsz != 0)
     169             :                         DPRINTF(("%s: repid %d size %d\n", sc->sc_dev.dv_xname,
     170             :                             repid, repsz));
     171             :         }
     172           0 :         sc->sc_ibuf = malloc(sc->sc_isize, M_DEVBUF, M_NOWAIT | M_ZERO);
     173             : 
     174           0 :         iha.iaa = ia;
     175           0 :         iha.parent = sc;
     176             : 
     177             :         /* Look for a driver claiming all report IDs first. */
     178           0 :         iha.reportid = IHIDEV_CLAIM_ALLREPORTID;
     179           0 :         dev = config_found_sm((struct device *)sc, &iha, NULL,
     180             :             ihidev_submatch);
     181           0 :         if (dev != NULL) {
     182           0 :                 for (repid = 0; repid < sc->sc_nrepid; repid++)
     183           0 :                         sc->sc_subdevs[repid] = (struct ihidev *)dev;
     184           0 :                 return;
     185             :         }
     186             : 
     187           0 :         for (repid = 0; repid < sc->sc_nrepid; repid++) {
     188           0 :                 if (hid_report_size(sc->sc_report, sc->sc_reportlen, hid_input,
     189           0 :                     repid) == 0 &&
     190           0 :                     hid_report_size(sc->sc_report, sc->sc_reportlen,
     191           0 :                     hid_output, repid) == 0 &&
     192           0 :                     hid_report_size(sc->sc_report, sc->sc_reportlen,
     193           0 :                     hid_feature, repid) == 0)
     194             :                         continue;
     195             : 
     196           0 :                 iha.reportid = repid;
     197           0 :                 dev = config_found_sm(self, &iha, ihidev_print,
     198             :                     ihidev_submatch);
     199           0 :                 sc->sc_subdevs[repid] = (struct ihidev *)dev;
     200           0 :         }
     201             : 
     202             :         /* power down until we're opened */
     203           0 :         if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_OFF)) {
     204           0 :                 printf("%s: failed to power down\n", sc->sc_dev.dv_xname);
     205           0 :                 return;
     206             :         }
     207           0 : }
     208             : 
     209             : int
     210           0 : ihidev_detach(struct device *self, int flags)
     211             : {
     212           0 :         struct ihidev_softc *sc = (struct ihidev_softc *)self;
     213             : 
     214           0 :         if (sc->sc_ih != NULL) {
     215           0 :                 intr_disestablish(sc->sc_ih);
     216           0 :                 sc->sc_ih = NULL;
     217           0 :         }
     218             : 
     219           0 :         if (sc->sc_ibuf != NULL) {
     220           0 :                 free(sc->sc_ibuf, M_DEVBUF, sc->sc_isize);
     221           0 :                 sc->sc_ibuf = NULL;
     222           0 :         }
     223             : 
     224           0 :         if (sc->sc_report != NULL)
     225           0 :                 free(sc->sc_report, M_DEVBUF, sc->sc_reportlen);
     226             : 
     227           0 :         return (0);
     228             : }
     229             : 
     230             : void
     231           0 : ihidev_sleep(struct ihidev_softc *sc, int ms)
     232             : {
     233           0 :         int to = ms * hz / 1000;
     234             : 
     235           0 :         if (cold)
     236           0 :                 delay(ms * 1000);
     237             :         else {
     238           0 :                 if (to <= 0)
     239           0 :                         to = 1;
     240           0 :                 tsleep(&sc, PWAIT, "ihidev", to);
     241             :         }
     242           0 : }
     243             : 
     244             : int
     245           0 : ihidev_hid_command(struct ihidev_softc *sc, int hidcmd, void *arg)
     246             : {
     247             :         int i, res = 1;
     248             : 
     249           0 :         iic_acquire_bus(sc->sc_tag, 0);
     250             : 
     251           0 :         switch (hidcmd) {
     252             :         case I2C_HID_CMD_DESCR: {
     253             :                 /*
     254             :                  * 5.2.2 - HID Descriptor Retrieval
     255             :                  * register is passed from the controller
     256             :                  */
     257           0 :                 uint8_t cmd[] = {
     258           0 :                         htole16(sc->sc_hid_desc_addr) & 0xff,
     259           0 :                         htole16(sc->sc_hid_desc_addr) >> 8,
     260             :                 };
     261             : 
     262             :                 DPRINTF(("%s: HID command I2C_HID_CMD_DESCR at 0x%x\n",
     263             :                     sc->sc_dev.dv_xname, htole16(sc->sc_hid_desc_addr)));
     264             : 
     265             :                 /* 20 00 */
     266           0 :                 res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
     267           0 :                     &cmd, sizeof(cmd), &sc->hid_desc_buf,
     268             :                     sizeof(struct i2c_hid_desc), 0);
     269             : 
     270             :                 DPRINTF(("%s: HID descriptor:", sc->sc_dev.dv_xname));
     271           0 :                 for (i = 0; i < sizeof(struct i2c_hid_desc); i++)
     272             :                         DPRINTF((" %.2x", sc->hid_desc_buf[i]));
     273             :                 DPRINTF(("\n"));
     274             : 
     275             :                 break;
     276           0 :         }
     277             :         case I2C_HID_CMD_RESET: {
     278           0 :                 uint8_t cmd[] = {
     279           0 :                         htole16(sc->hid_desc.wCommandRegister) & 0xff,
     280           0 :                         htole16(sc->hid_desc.wCommandRegister) >> 8,
     281             :                         0,
     282             :                         I2C_HID_CMD_RESET,
     283             :                 };
     284             : 
     285             :                 DPRINTF(("%s: HID command I2C_HID_CMD_RESET\n",
     286             :                     sc->sc_dev.dv_xname));
     287             : 
     288             :                 /* 22 00 00 01 */
     289           0 :                 res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
     290             :                     &cmd, sizeof(cmd), NULL, 0, 0);
     291             : 
     292             :                 break;
     293           0 :         }
     294             :         case I2C_HID_CMD_GET_REPORT: {
     295             :                 struct i2c_hid_report_request *rreq =
     296           0 :                     (struct i2c_hid_report_request *)arg;
     297             : 
     298           0 :                 uint8_t cmd[] = {
     299           0 :                         htole16(sc->hid_desc.wCommandRegister) & 0xff,
     300           0 :                         htole16(sc->hid_desc.wCommandRegister) >> 8,
     301             :                         0,
     302             :                         I2C_HID_CMD_GET_REPORT,
     303             :                         0, 0, 0,
     304             :                 };
     305             :                 int cmdlen = 7;
     306             :                 int dataoff = 4;
     307           0 :                 int report_id = rreq->id;
     308             :                 int report_id_len = 1;
     309           0 :                 int report_len = rreq->len + 2;
     310             :                 int d;
     311             :                 uint8_t *tmprep;
     312             : 
     313             :                 DPRINTF(("%s: HID command I2C_HID_CMD_GET_REPORT %d "
     314             :                     "(type %d, len %d)\n", sc->sc_dev.dv_xname, report_id,
     315             :                     rreq->type, rreq->len));
     316             : 
     317             :                 /*
     318             :                  * 7.2.2.4 - "The protocol is optimized for Report < 15.  If a
     319             :                  * report ID >= 15 is necessary, then the Report ID in the Low
     320             :                  * Byte must be set to 1111 and a Third Byte is appended to the
     321             :                  * protocol.  This Third Byte contains the entire/actual report
     322             :                  * ID."
     323             :                  */
     324           0 :                 if (report_id >= 15) {
     325           0 :                         cmd[dataoff++] = report_id;
     326             :                         report_id = 15;
     327             :                         report_id_len = 2;
     328           0 :                 } else
     329             :                         cmdlen--;
     330             : 
     331           0 :                 cmd[2] = report_id | rreq->type << 4;
     332             : 
     333           0 :                 cmd[dataoff++] = sc->hid_desc.wDataRegister & 0xff;
     334           0 :                 cmd[dataoff] = sc->hid_desc.wDataRegister >> 8;
     335             : 
     336             :                 /*
     337             :                  * 7.2.2.2 - Response will be a 2-byte length value, the report
     338             :                  * id with length determined above, and then the report.
     339             :                  * Allocate rreq->len + 2 + 2 bytes, read into that temporary
     340             :                  * buffer, and then copy only the report back out to
     341             :                  * rreq->data.
     342             :                  */
     343           0 :                 report_len += report_id_len;
     344           0 :                 tmprep = malloc(report_len, M_DEVBUF, M_NOWAIT | M_ZERO);
     345             : 
     346             :                 /* type 3 id 8: 22 00 38 02 23 00 */
     347           0 :                 res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
     348           0 :                     &cmd, cmdlen, tmprep, report_len, 0);
     349             : 
     350           0 :                 d = tmprep[0] | tmprep[1] << 8;
     351             :                 if (d != report_len)
     352             :                         DPRINTF(("%s: response size %d != expected length %d\n",
     353             :                             sc->sc_dev.dv_xname, d, report_len));
     354             : 
     355           0 :                 if (report_id_len == 2)
     356           0 :                         d = tmprep[2] | tmprep[3] << 8;
     357             :                 else
     358             :                         d = tmprep[2];
     359             : 
     360           0 :                 if (d != rreq->id) {
     361             :                         DPRINTF(("%s: response report id %d != %d\n",
     362             :                             sc->sc_dev.dv_xname, d, rreq->id));
     363           0 :                         iic_release_bus(sc->sc_tag, 0);
     364           0 :                         return (1);
     365             :                 }
     366             : 
     367             :                 DPRINTF(("%s: response:", sc->sc_dev.dv_xname));
     368           0 :                 for (i = 0; i < report_len; i++)
     369             :                         DPRINTF((" %.2x", tmprep[i]));
     370             :                 DPRINTF(("\n"));
     371             : 
     372           0 :                 memcpy(rreq->data, tmprep + 2 + report_id_len, rreq->len);
     373           0 :                 free(tmprep, M_DEVBUF, report_len);
     374             : 
     375           0 :                 break;
     376           0 :         }
     377             :         case I2C_HID_CMD_SET_REPORT: {
     378             :                 struct i2c_hid_report_request *rreq =
     379           0 :                     (struct i2c_hid_report_request *)arg;
     380             : 
     381           0 :                 uint8_t cmd[] = {
     382           0 :                         htole16(sc->hid_desc.wCommandRegister) & 0xff,
     383           0 :                         htole16(sc->hid_desc.wCommandRegister) >> 8,
     384             :                         0,
     385             :                         I2C_HID_CMD_SET_REPORT,
     386             :                         0, 0, 0, 0, 0, 0,
     387             :                 };
     388             :                 int cmdlen = sizeof(cmd);
     389           0 :                 int report_id = rreq->id;
     390           0 :                 int report_len = 2 + (report_id ? 1 : 0) + rreq->len;
     391             :                 int dataoff;
     392             :                 uint8_t *finalcmd;
     393             : 
     394             :                 DPRINTF(("%s: HID command I2C_HID_CMD_SET_REPORT %d "
     395             :                     "(type %d, len %d):", sc->sc_dev.dv_xname, report_id,
     396             :                     rreq->type, rreq->len));
     397           0 :                 for (i = 0; i < rreq->len; i++)
     398             :                         DPRINTF((" %.2x", ((uint8_t *)rreq->data)[i]));
     399             :                 DPRINTF(("\n"));
     400             : 
     401             :                 /*
     402             :                  * 7.2.3.4 - "The protocol is optimized for Report < 15.  If a
     403             :                  * report ID >= 15 is necessary, then the Report ID in the Low
     404             :                  * Byte must be set to 1111 and a Third Byte is appended to the
     405             :                  * protocol.  This Third Byte contains the entire/actual report
     406             :                  * ID."
     407             :                  */
     408             :                 dataoff = 4;
     409           0 :                 if (report_id >= 15) {
     410           0 :                         cmd[dataoff++] = report_id;
     411             :                         report_id = 15;
     412           0 :                 } else
     413             :                         cmdlen--;
     414             : 
     415           0 :                 cmd[2] = report_id | rreq->type << 4;
     416             : 
     417           0 :                 if (rreq->type == I2C_HID_REPORT_TYPE_FEATURE) {
     418           0 :                         cmd[dataoff++] = htole16(sc->hid_desc.wDataRegister)
     419           0 :                             & 0xff;
     420           0 :                         cmd[dataoff++] = htole16(sc->hid_desc.wDataRegister)
     421           0 :                             >> 8;
     422           0 :                 } else {
     423           0 :                         cmd[dataoff++] = htole16(sc->hid_desc.wOutputRegister)
     424           0 :                             & 0xff;
     425           0 :                         cmd[dataoff++] = htole16(sc->hid_desc.wOutputRegister)
     426           0 :                             >> 8;
     427             :                 }
     428             : 
     429           0 :                 cmd[dataoff++] = report_len & 0xff;
     430           0 :                 cmd[dataoff++] = report_len >> 8;
     431           0 :                 cmd[dataoff] = rreq->id;
     432             : 
     433           0 :                 finalcmd = malloc(cmdlen + rreq->len, M_DEVBUF,
     434             :                     M_NOWAIT | M_ZERO);
     435             : 
     436           0 :                 memcpy(finalcmd, cmd, cmdlen);
     437           0 :                 memcpy(finalcmd + cmdlen, rreq->data, rreq->len);
     438             : 
     439             :                 /* type 3 id 4: 22 00 34 03 23 00 04 00 04 03 */
     440           0 :                 res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
     441           0 :                     finalcmd, cmdlen + rreq->len, NULL, 0, 0);
     442             : 
     443           0 :                 free(finalcmd, M_DEVBUF, cmdlen + rreq->len);
     444             : 
     445             :                 break;
     446           0 :         }
     447             : 
     448             :         case I2C_HID_CMD_SET_POWER: {
     449           0 :                 int power = *(int *)arg;
     450           0 :                 uint8_t cmd[] = {
     451           0 :                         htole16(sc->hid_desc.wCommandRegister) & 0xff,
     452           0 :                         htole16(sc->hid_desc.wCommandRegister) >> 8,
     453           0 :                         power,
     454             :                         I2C_HID_CMD_SET_POWER,
     455             :                 };
     456             : 
     457             :                 DPRINTF(("%s: HID command I2C_HID_CMD_SET_POWER(%d)\n",
     458             :                     sc->sc_dev.dv_xname, power));
     459             : 
     460             :                 /* 22 00 00 08 */
     461           0 :                 res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
     462             :                     &cmd, sizeof(cmd), NULL, 0, 0);
     463             : 
     464             :                 break;
     465           0 :         }
     466             :         case I2C_HID_REPORT_DESCR: {
     467           0 :                 uint8_t cmd[] = {
     468           0 :                         htole16(sc->hid_desc.wReportDescRegister) & 0xff,
     469           0 :                         htole16(sc->hid_desc.wReportDescRegister) >> 8,
     470             :                 };
     471             : 
     472             :                 DPRINTF(("%s: HID command I2C_HID_REPORT_DESCR at 0x%x with "
     473             :                     "size %d\n", sc->sc_dev.dv_xname, cmd[0],
     474             :                     sc->sc_reportlen));
     475             : 
     476             :                 /* 20 00 */
     477           0 :                 res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
     478           0 :                     &cmd, sizeof(cmd), sc->sc_report, sc->sc_reportlen, 0);
     479             : 
     480             :                 DPRINTF(("%s: HID report descriptor:", sc->sc_dev.dv_xname));
     481           0 :                 for (i = 0; i < sc->sc_reportlen; i++)
     482             :                         DPRINTF((" %.2x", sc->sc_report[i]));
     483             :                 DPRINTF(("\n"));
     484             : 
     485             :                 break;
     486           0 :         }
     487             :         default:
     488           0 :                 printf("%s: unknown command %d\n", sc->sc_dev.dv_xname,
     489             :                     hidcmd);
     490           0 :         }
     491             : 
     492           0 :         iic_release_bus(sc->sc_tag, 0);
     493             : 
     494           0 :         return (res);
     495           0 : }
     496             : 
     497             : int
     498           0 : ihidev_reset(struct ihidev_softc *sc)
     499             : {
     500             :         DPRINTF(("%s: resetting\n", sc->sc_dev.dv_xname));
     501             : 
     502           0 :         if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_ON)) {
     503           0 :                 printf("%s: failed to power on\n", sc->sc_dev.dv_xname);
     504           0 :                 return (1);
     505             :         }
     506             : 
     507           0 :         ihidev_sleep(sc, 100);
     508             : 
     509           0 :         if (ihidev_hid_command(sc, I2C_HID_CMD_RESET, 0)) {
     510           0 :                 printf("%s: failed to reset hardware\n", sc->sc_dev.dv_xname);
     511             : 
     512           0 :                 ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER,
     513             :                     &I2C_HID_POWER_OFF);
     514             : 
     515           0 :                 return (1);
     516             :         }
     517             : 
     518           0 :         ihidev_sleep(sc, 100);
     519             : 
     520           0 :         return (0);
     521           0 : }
     522             : 
     523             : /*
     524             :  * 5.2.2 - HID Descriptor Retrieval
     525             :  *
     526             :  * parse HID Descriptor that has already been read into hid_desc with
     527             :  * I2C_HID_CMD_DESCR
     528             :  */
     529             : int
     530           0 : ihidev_hid_desc_parse(struct ihidev_softc *sc)
     531             : {
     532             :         int retries = 3;
     533             : 
     534             :         /* must be v01.00 */
     535           0 :         if (letoh16(sc->hid_desc.bcdVersion) != 0x0100) {
     536           0 :                 printf("%s: bad HID descriptor bcdVersion (0x%x)\n",
     537           0 :                     sc->sc_dev.dv_xname,
     538             :                     letoh16(sc->hid_desc.bcdVersion));
     539           0 :                 return (1);
     540             :         }
     541             : 
     542             :         /* must be 30 bytes for v1.00 */
     543           0 :         if (letoh16(sc->hid_desc.wHIDDescLength !=
     544             :             sizeof(struct i2c_hid_desc))) {
     545           0 :                 printf("%s: bad HID descriptor size (%d != %zu)\n",
     546           0 :                     sc->sc_dev.dv_xname,
     547           0 :                     letoh16(sc->hid_desc.wHIDDescLength),
     548             :                     sizeof(struct i2c_hid_desc));
     549           0 :                 return (1);
     550             :         }
     551             : 
     552           0 :         if (letoh16(sc->hid_desc.wReportDescLength) <= 0) {
     553           0 :                 printf("%s: bad HID report descriptor size (%d)\n",
     554           0 :                     sc->sc_dev.dv_xname,
     555             :                     letoh16(sc->hid_desc.wReportDescLength));
     556           0 :                 return (1);
     557             :         }
     558             : 
     559           0 :         while (retries-- > 0) {
     560           0 :                 if (ihidev_reset(sc)) {
     561           0 :                         if (retries == 0)
     562           0 :                                 return(1);
     563             : 
     564           0 :                         ihidev_sleep(sc, 10);
     565             :                 }
     566             :                 else
     567             :                         break;
     568             :         }
     569             : 
     570           0 :         sc->sc_reportlen = letoh16(sc->hid_desc.wReportDescLength);
     571           0 :         sc->sc_report = malloc(sc->sc_reportlen, M_DEVBUF, M_NOWAIT | M_ZERO);
     572             : 
     573           0 :         if (ihidev_hid_command(sc, I2C_HID_REPORT_DESCR, 0)) {
     574           0 :                 printf("%s: failed fetching HID report\n",
     575           0 :                     sc->sc_dev.dv_xname);
     576           0 :                 return (1);
     577             :         }
     578             : 
     579           0 :         return (0);
     580           0 : }
     581             : 
     582             : int
     583           0 : ihidev_intr(void *arg)
     584             : {
     585           0 :         struct ihidev_softc *sc = arg;
     586             :         struct ihidev *scd;
     587             :         u_int psize;
     588             :         int res, i, fast = 0;
     589             :         u_char *p;
     590             :         u_int rep = 0;
     591             : 
     592             :         /*
     593             :          * XXX: force I2C_F_POLL for now to avoid dwiic interrupting
     594             :          * while we are interrupting
     595             :          */
     596             : 
     597           0 :         iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
     598           0 :         res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, NULL, 0,
     599           0 :             sc->sc_ibuf, sc->sc_isize, I2C_F_POLL);
     600           0 :         iic_release_bus(sc->sc_tag, I2C_F_POLL);
     601             : 
     602             :         /*
     603             :          * 6.1.1 - First two bytes are the packet length, which must be less
     604             :          * than or equal to wMaxInputLength
     605             :          */
     606           0 :         psize = sc->sc_ibuf[0] | sc->sc_ibuf[1] << 8;
     607           0 :         if (!psize || psize > sc->sc_isize) {
     608           0 :                 if (sc->sc_poll) {
     609             :                         /*
     610             :                          * TODO: all fingers are up, should we pass to hid
     611             :                          * layer?
     612             :                          */
     613           0 :                         sc->sc_fastpoll = 0;
     614           0 :                         goto more_polling;
     615             :                 } else
     616             :                         DPRINTF(("%s: %s: invalid packet size (%d vs. %d)\n",
     617             :                             sc->sc_dev.dv_xname, __func__, psize,
     618             :                             sc->sc_isize));
     619           0 :                 return (1);
     620             :         }
     621             : 
     622             :         /* 3rd byte is the report id */
     623           0 :         p = sc->sc_ibuf + 2;
     624           0 :         psize -= 2;
     625           0 :         if (sc->sc_nrepid != 1)
     626           0 :                 rep = *p++, psize--;
     627             : 
     628           0 :         if (rep >= sc->sc_nrepid) {
     629           0 :                 printf("%s: %s: bad report id %d\n", sc->sc_dev.dv_xname,
     630             :                     __func__, rep);
     631           0 :                 if (sc->sc_poll) {
     632           0 :                         sc->sc_fastpoll = 0;
     633           0 :                         goto more_polling;
     634             :                 }
     635           0 :                 return (1);
     636             :         }
     637             : 
     638             :         DPRINTF(("%s: %s: hid input (rep %d):", sc->sc_dev.dv_xname, __func__,
     639             :             rep));
     640           0 :         for (i = 0; i < psize; i++) {
     641           0 :                 if (i > 0 && p[i] != 0 && p[i] != 0xff) {
     642             :                         fast = 1;
     643           0 :                 }
     644             :                 DPRINTF((" %.2x", p[i]));
     645             :         }
     646             :         DPRINTF(("\n"));
     647             : 
     648           0 :         scd = sc->sc_subdevs[rep];
     649           0 :         if (scd == NULL || !(scd->sc_state & IHIDEV_OPEN)) {
     650           0 :                 if (sc->sc_poll) {
     651           0 :                         if (sc->sc_fastpoll) {
     652             :                                 DPRINTF(("%s: fast->slow polling\n",
     653             :                                     sc->sc_dev.dv_xname));
     654           0 :                                 sc->sc_fastpoll = 0;
     655           0 :                         }
     656             :                         goto more_polling;
     657             :                 }
     658           0 :                 return (1);
     659             :         }
     660             : 
     661           0 :         scd->sc_intr(scd, p, psize);
     662             : 
     663           0 :         if (sc->sc_poll && fast != sc->sc_fastpoll) {
     664             :                 DPRINTF(("%s: %s->%s polling\n", sc->sc_dev.dv_xname,
     665             :                     sc->sc_fastpoll ? "fast" : "slow",
     666             :                     fast ? "fast" : "slow"));
     667           0 :                 sc->sc_fastpoll = fast;
     668           0 :         }
     669             : 
     670             : more_polling:
     671           0 :         if (sc->sc_poll && sc->sc_refcnt && !timeout_pending(&sc->sc_timer))
     672           0 :                 timeout_add_msec(&sc->sc_timer,
     673           0 :                     sc->sc_fastpoll ? FAST_POLL_MS : SLOW_POLL_MS);
     674             : 
     675           0 :         return (1);
     676           0 : }
     677             : 
     678             : int
     679           0 : ihidev_maxrepid(void *buf, int len)
     680             : {
     681             :         struct hid_data *d;
     682           0 :         struct hid_item h;
     683             :         int maxid;
     684             : 
     685             :         maxid = -1;
     686           0 :         h.report_ID = 0;
     687           0 :         for (d = hid_start_parse(buf, len, hid_none); hid_get_item(d, &h); )
     688           0 :                 if (h.report_ID > maxid)
     689           0 :                         maxid = h.report_ID;
     690           0 :         hid_end_parse(d);
     691             : 
     692           0 :         return (maxid);
     693           0 : }
     694             : 
     695             : int
     696           0 : ihidev_print(void *aux, const char *pnp)
     697             : {
     698           0 :         struct ihidev_attach_arg *iha = aux;
     699             : 
     700           0 :         if (pnp)
     701           0 :                 printf("hid at %s", pnp);
     702             : 
     703           0 :         if (iha->reportid != 0 && iha->reportid != IHIDEV_CLAIM_ALLREPORTID)
     704           0 :                 printf(" reportid %d", iha->reportid);
     705             : 
     706           0 :         return (UNCONF);
     707             : }
     708             : 
     709             : int
     710           0 : ihidev_submatch(struct device *parent, void *match, void *aux)
     711             : {
     712           0 :         struct ihidev_attach_arg *iha = aux;
     713           0 :         struct cfdata *cf = match;
     714             : 
     715           0 :         if (cf->ihidevcf_reportid != IHIDEV_UNK_REPORTID &&
     716           0 :             cf->ihidevcf_reportid != iha->reportid)
     717           0 :                 return (0);
     718             : 
     719           0 :         return ((*cf->cf_attach->ca_match)(parent, cf, aux));
     720           0 : }
     721             : 
     722             : int
     723           0 : ihidev_open(struct ihidev *scd)
     724             : {
     725           0 :         struct ihidev_softc *sc = scd->sc_parent;
     726             : 
     727             :         DPRINTF(("%s: %s: state=%d refcnt=%d\n", sc->sc_dev.dv_xname,
     728             :             __func__, scd->sc_state, sc->sc_refcnt));
     729             : 
     730           0 :         if (scd->sc_state & IHIDEV_OPEN)
     731           0 :                 return (EBUSY);
     732             : 
     733           0 :         scd->sc_state |= IHIDEV_OPEN;
     734             : 
     735           0 :         if (sc->sc_refcnt++ || sc->sc_isize == 0)
     736           0 :                 return (0);
     737             : 
     738             :         /* power on */
     739           0 :         ihidev_reset(sc);
     740             : 
     741           0 :         if (sc->sc_poll) {
     742           0 :                 if (!timeout_initialized(&sc->sc_timer))
     743           0 :                         timeout_set(&sc->sc_timer, (void *)ihidev_intr, sc);
     744           0 :                 if (!timeout_pending(&sc->sc_timer))
     745           0 :                         timeout_add(&sc->sc_timer, FAST_POLL_MS);
     746             :         }
     747             : 
     748           0 :         return (0);
     749           0 : }
     750             : 
     751             : void
     752           0 : ihidev_close(struct ihidev *scd)
     753             : {
     754           0 :         struct ihidev_softc *sc = scd->sc_parent;
     755             : 
     756             :         DPRINTF(("%s: %s: state=%d refcnt=%d\n", sc->sc_dev.dv_xname,
     757             :             __func__, scd->sc_state, sc->sc_refcnt));
     758             : 
     759           0 :         if (!(scd->sc_state & IHIDEV_OPEN))
     760           0 :                 return;
     761             : 
     762           0 :         scd->sc_state &= ~IHIDEV_OPEN;
     763             : 
     764           0 :         if (--sc->sc_refcnt)
     765           0 :                 return;
     766             : 
     767             :         /* no sub-devices open, conserve power */
     768             : 
     769           0 :         if (sc->sc_poll)
     770           0 :                 timeout_del(&sc->sc_timer);
     771             : 
     772           0 :         if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_OFF))
     773           0 :                 printf("%s: failed to power down\n", sc->sc_dev.dv_xname);
     774           0 : }
     775             : 
     776             : int
     777           0 : ihidev_ioctl(struct ihidev *sc, u_long cmd, caddr_t addr, int flag,
     778             :     struct proc *p)
     779             : {
     780           0 :         return -1;
     781             : }
     782             : 
     783             : void
     784           0 : ihidev_get_report_desc(struct ihidev_softc *sc, void **desc, int *size)
     785             : {
     786           0 :         *desc = sc->sc_report;
     787           0 :         *size = sc->sc_reportlen;
     788           0 : }
     789             : 
     790             : int
     791           0 : ihidev_report_type_conv(int hid_type_id)
     792             : {
     793           0 :         switch (hid_type_id) {
     794             :         case hid_input:
     795           0 :                 return I2C_HID_REPORT_TYPE_INPUT;
     796             :         case hid_output:
     797           0 :                 return I2C_HID_REPORT_TYPE_OUTPUT;
     798             :         case hid_feature:
     799           0 :                 return I2C_HID_REPORT_TYPE_FEATURE;
     800             :         default:
     801           0 :                 return -1;
     802             :         }
     803           0 : }
     804             : 
     805             : int
     806           0 : ihidev_get_report(struct device *dev, int type, int id, void *data, int len)
     807             : {
     808           0 :         struct ihidev_softc *sc = (struct ihidev_softc *)dev;
     809           0 :         struct i2c_hid_report_request rreq;
     810             : 
     811           0 :         rreq.type = type;
     812           0 :         rreq.id = id;
     813           0 :         rreq.data = data;
     814           0 :         rreq.len = len;
     815             : 
     816           0 :         if (ihidev_hid_command(sc, I2C_HID_CMD_GET_REPORT, &rreq)) {
     817           0 :                 printf("%s: failed fetching report\n", sc->sc_dev.dv_xname);
     818           0 :                 return (1);
     819             :         }
     820             : 
     821           0 :         return 0;
     822           0 : }
     823             : 
     824             : int
     825           0 : ihidev_set_report(struct device *dev, int type, int id, void *data, int len)
     826             : {
     827           0 :         struct ihidev_softc *sc = (struct ihidev_softc *)dev;
     828           0 :         struct i2c_hid_report_request rreq;
     829             : 
     830           0 :         rreq.type = type;
     831           0 :         rreq.id = id;
     832           0 :         rreq.data = data;
     833           0 :         rreq.len = len;
     834             : 
     835           0 :         if (ihidev_hid_command(sc, I2C_HID_CMD_SET_REPORT, &rreq)) {
     836           0 :                 printf("%s: failed setting report\n", sc->sc_dev.dv_xname);
     837           0 :                 return (1);
     838             :         }
     839             : 
     840           0 :         return 0;
     841           0 : }

Generated by: LCOV version 1.13