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

          Line data    Source code
       1             : /*      $OpenBSD: uhidev.c,v 1.76 2018/08/25 18:32:05 jcs Exp $ */
       2             : /*      $NetBSD: uhidev.c,v 1.14 2003/03/11 16:44:00 augustss Exp $     */
       3             : 
       4             : /*
       5             :  * Copyright (c) 2001 The NetBSD Foundation, Inc.
       6             :  * All rights reserved.
       7             :  *
       8             :  * This code is derived from software contributed to The NetBSD Foundation
       9             :  * by Lennart Augustsson (lennart@augustsson.net) at
      10             :  * Carlstedt Research & Technology.
      11             :  *
      12             :  * Redistribution and use in source and binary forms, with or without
      13             :  * modification, are permitted provided that the following conditions
      14             :  * are met:
      15             :  * 1. Redistributions of source code must retain the above copyright
      16             :  *    notice, this list of conditions and the following disclaimer.
      17             :  * 2. Redistributions in binary form must reproduce the above copyright
      18             :  *    notice, this list of conditions and the following disclaimer in the
      19             :  *    documentation and/or other materials provided with the distribution.
      20             :  *
      21             :  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
      22             :  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
      23             :  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
      24             :  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
      25             :  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      26             :  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
      27             :  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
      28             :  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
      29             :  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
      30             :  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      31             :  * POSSIBILITY OF SUCH DAMAGE.
      32             :  */
      33             : 
      34             : /*
      35             :  * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
      36             :  */
      37             : 
      38             : #include <sys/param.h>
      39             : #include <sys/systm.h>
      40             : #include <sys/kernel.h>
      41             : #include <sys/malloc.h>
      42             : #include <sys/signalvar.h>
      43             : #include <sys/device.h>
      44             : #include <sys/ioctl.h>
      45             : #include <sys/conf.h>
      46             : 
      47             : #include <machine/bus.h>
      48             : 
      49             : #include <dev/usb/usb.h>
      50             : #include <dev/usb/usbhid.h>
      51             : 
      52             : #include <dev/usb/usbdevs.h>
      53             : #include <dev/usb/usbdi.h>
      54             : #include <dev/usb/usbdi_util.h>
      55             : #include <dev/usb/usbdivar.h>
      56             : #include <dev/usb/usb_mem.h>
      57             : #include <dev/usb/usb_quirks.h>
      58             : 
      59             : #include <dev/usb/uhidev.h>
      60             : 
      61             : #ifndef SMALL_KERNEL
      62             : /* Replacement report descriptors for devices shipped with broken ones */
      63             : #include <dev/usb/uhid_rdesc.h>
      64             : int uhidev_use_rdesc(struct uhidev_softc *, usb_interface_descriptor_t *,
      65             :                 int, int, void **, int *);
      66             : #define UISUBCLASS_XBOX360_CONTROLLER 0x5d
      67             : #define UIPROTO_XBOX360_GAMEPAD 0x01
      68             : #endif /* !SMALL_KERNEL */
      69             : 
      70             : #define DEVNAME(sc)             ((sc)->sc_dev.dv_xname)
      71             : 
      72             : #ifdef UHIDEV_DEBUG
      73             : #define DPRINTF(x)      do { if (uhidevdebug) printf x; } while (0)
      74             : #define DPRINTFN(n,x)   do { if (uhidevdebug>(n)) printf x; } while (0)
      75             : int     uhidevdebug = 0;
      76             : #else
      77             : #define DPRINTF(x)
      78             : #define DPRINTFN(n,x)
      79             : #endif
      80             : 
      81             : struct uhidev_async_info {
      82             :         void (*callback)(void *priv, int id, void *data, int len);
      83             :         void *priv;
      84             :         void *data;
      85             :         int id;
      86             : };
      87             : 
      88             : void uhidev_intr(struct usbd_xfer *, void *, usbd_status);
      89             : 
      90             : int uhidev_maxrepid(void *buf, int len);
      91             : int uhidevprint(void *aux, const char *pnp);
      92             : int uhidevsubmatch(struct device *parent, void *cf, void *aux);
      93             : 
      94             : int uhidev_match(struct device *, void *, void *);
      95             : void uhidev_attach(struct device *, struct device *, void *);
      96             : int uhidev_detach(struct device *, int);
      97             : int uhidev_activate(struct device *, int);
      98             : 
      99             : void uhidev_get_report_async_cb(struct usbd_xfer *, void *, usbd_status);
     100             : 
     101             : struct cfdriver uhidev_cd = {
     102             :         NULL, "uhidev", DV_DULL
     103             : };
     104             : 
     105             : const struct cfattach uhidev_ca = {
     106             :         sizeof(struct uhidev_softc), uhidev_match, uhidev_attach,
     107             :         uhidev_detach, uhidev_activate,
     108             : };
     109             : 
     110             : int
     111           0 : uhidev_match(struct device *parent, void *match, void *aux)
     112             : {
     113           0 :         struct usb_attach_arg *uaa = aux;
     114             :         usb_interface_descriptor_t *id;
     115             : 
     116           0 :         if (uaa->iface == NULL)
     117           0 :                 return (UMATCH_NONE);
     118           0 :         id = usbd_get_interface_descriptor(uaa->iface);
     119           0 :         if (id == NULL)
     120           0 :                 return (UMATCH_NONE);
     121             : #ifndef SMALL_KERNEL
     122           0 :         if (id->bInterfaceClass == UICLASS_VENDOR &&
     123           0 :             id->bInterfaceSubClass == UISUBCLASS_XBOX360_CONTROLLER &&
     124           0 :             id->bInterfaceProtocol == UIPROTO_XBOX360_GAMEPAD)
     125           0 :                 return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO);
     126             : #endif /* !SMALL_KERNEL */
     127           0 :         if (id->bInterfaceClass != UICLASS_HID)
     128           0 :                 return (UMATCH_NONE);
     129           0 :         if (usbd_get_quirks(uaa->device)->uq_flags & UQ_BAD_HID)
     130           0 :                 return (UMATCH_NONE);
     131             : 
     132           0 :         return (UMATCH_IFACECLASS_GENERIC);
     133           0 : }
     134             : 
     135             : void
     136           0 : uhidev_attach(struct device *parent, struct device *self, void *aux)
     137             : {
     138           0 :         struct uhidev_softc *sc = (struct uhidev_softc *)self;
     139           0 :         struct usb_attach_arg *uaa = aux;
     140             :         usb_interface_descriptor_t *id;
     141             :         usb_endpoint_descriptor_t *ed;
     142           0 :         struct uhidev_attach_arg uha;
     143           0 :         int size, nrepid, repid, repsz;
     144           0 :         int i, repsizes[256];
     145           0 :         void *desc = NULL;
     146             :         struct device *dev;
     147             : 
     148           0 :         sc->sc_udev = uaa->device;
     149           0 :         sc->sc_iface = uaa->iface;
     150           0 :         sc->sc_ifaceno = uaa->ifaceno;
     151           0 :         id = usbd_get_interface_descriptor(sc->sc_iface);
     152             : 
     153           0 :         usbd_set_idle(sc->sc_udev, sc->sc_ifaceno, 0, 0);
     154             : 
     155           0 :         sc->sc_iep_addr = sc->sc_oep_addr = -1;
     156           0 :         for (i = 0; i < id->bNumEndpoints; i++) {
     157           0 :                 ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
     158           0 :                 if (ed == NULL) {
     159           0 :                         printf("%s: could not read endpoint descriptor\n",
     160           0 :                             DEVNAME(sc));
     161           0 :                         return;
     162             :                 }
     163             : 
     164             :                 DPRINTFN(10,("uhidev_attach: bLength=%d bDescriptorType=%d "
     165             :                     "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d"
     166             :                     " bInterval=%d\n",
     167             :                     ed->bLength, ed->bDescriptorType,
     168             :                     ed->bEndpointAddress & UE_ADDR,
     169             :                     UE_GET_DIR(ed->bEndpointAddress)==UE_DIR_IN? "in" : "out",
     170             :                     ed->bmAttributes & UE_XFERTYPE,
     171             :                     UGETW(ed->wMaxPacketSize), ed->bInterval));
     172             : 
     173           0 :                 if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
     174           0 :                     (ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT) {
     175           0 :                         sc->sc_iep_addr = ed->bEndpointAddress;
     176           0 :                 } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
     177           0 :                     (ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT) {
     178           0 :                         sc->sc_oep_addr = ed->bEndpointAddress;
     179             :                 } else {
     180           0 :                         printf("%s: unexpected endpoint\n", DEVNAME(sc));
     181           0 :                         return;
     182             :                 }
     183             :         }
     184             : 
     185             :         /*
     186             :          * Check that we found an input interrupt endpoint.
     187             :          * The output interrupt endpoint is optional
     188             :          */
     189           0 :         if (sc->sc_iep_addr == -1) {
     190           0 :                 printf("%s: no input interrupt endpoint\n", DEVNAME(sc));
     191           0 :                 return;
     192             :         }
     193             : 
     194             : #ifndef SMALL_KERNEL
     195           0 :         if (uhidev_use_rdesc(sc, id, uaa->vendor, uaa->product, &desc, &size))
     196           0 :                 return;
     197             : #endif /* !SMALL_KERNEL */
     198             : 
     199           0 :         if (desc == NULL) {
     200             :                 struct usb_hid_descriptor *hid;
     201             : 
     202           0 :                 hid = usbd_get_hid_descriptor(sc->sc_udev, id);
     203           0 :                 if (hid == NULL) {
     204           0 :                         printf("%s: no HID descriptor\n", DEVNAME(sc));
     205           0 :                         return;
     206             :                 }
     207           0 :                 size = UGETW(hid->descrs[0].wDescriptorLength);
     208           0 :                 desc = malloc(size, M_USBDEV, M_NOWAIT);
     209           0 :                 if (desc == NULL) {
     210           0 :                         printf("%s: no memory\n", DEVNAME(sc));
     211           0 :                         return;
     212             :                 }
     213           0 :                 if (usbd_get_report_descriptor(sc->sc_udev, sc->sc_ifaceno,
     214           0 :                     desc, size)) {
     215           0 :                         printf("%s: no report descriptor\n", DEVNAME(sc));
     216           0 :                         free(desc, M_USBDEV, size);
     217           0 :                         return;
     218             :                 }
     219           0 :         }
     220             : 
     221           0 :         sc->sc_repdesc = desc;
     222           0 :         sc->sc_repdesc_size = size;
     223             : 
     224           0 :         nrepid = uhidev_maxrepid(desc, size);
     225           0 :         if (nrepid < 0)
     226           0 :                 return;
     227           0 :         printf("%s: iclass %d/%d", DEVNAME(sc), id->bInterfaceClass,
     228           0 :             id->bInterfaceSubClass);
     229           0 :         if (nrepid > 0)
     230           0 :                 printf(", %d report id%s", nrepid, nrepid > 1 ? "s" : "");
     231           0 :         printf("\n");
     232           0 :         nrepid++;
     233           0 :         sc->sc_subdevs = mallocarray(nrepid, sizeof(struct uhidev *),
     234             :             M_USBDEV, M_NOWAIT | M_ZERO);
     235           0 :         if (sc->sc_subdevs == NULL) {
     236           0 :                 printf("%s: no memory\n", DEVNAME(sc));
     237           0 :                 return;
     238             :         }
     239           0 :         sc->sc_nrepid = nrepid;
     240           0 :         sc->sc_isize = 0;
     241             : 
     242           0 :         for (repid = 0; repid < nrepid; repid++) {
     243           0 :                 repsz = hid_report_size(desc, size, hid_input, repid);
     244             :                 DPRINTF(("uhidev_match: repid=%d, repsz=%d\n", repid, repsz));
     245           0 :                 repsizes[repid] = repsz;
     246           0 :                 if (repsz > sc->sc_isize)
     247           0 :                         sc->sc_isize = repsz;
     248             :         }
     249           0 :         sc->sc_isize += (nrepid != 1);       /* one byte for the report ID */
     250             :         DPRINTF(("uhidev_attach: isize=%d\n", sc->sc_isize));
     251             : 
     252           0 :         uha.uaa = uaa;
     253           0 :         uha.parent = sc;
     254           0 :         uha.reportid = UHIDEV_CLAIM_ALLREPORTID;
     255             : 
     256             :         /* Look for a driver claiming all report IDs first. */
     257           0 :         dev = config_found_sm(self, &uha, NULL, uhidevsubmatch);
     258           0 :         if (dev != NULL) {
     259           0 :                 for (repid = 0; repid < nrepid; repid++)
     260           0 :                         sc->sc_subdevs[repid] = (struct uhidev *)dev;
     261           0 :                 return;
     262             :         }
     263             : 
     264           0 :         for (repid = 0; repid < nrepid; repid++) {
     265             :                 DPRINTF(("%s: try repid=%d\n", __func__, repid));
     266           0 :                 if (hid_report_size(desc, size, hid_input, repid) == 0 &&
     267           0 :                     hid_report_size(desc, size, hid_output, repid) == 0 &&
     268           0 :                     hid_report_size(desc, size, hid_feature, repid) == 0)
     269             :                         continue;
     270             : 
     271           0 :                 uha.reportid = repid;
     272           0 :                 dev = config_found_sm(self, &uha, uhidevprint, uhidevsubmatch);
     273           0 :                 sc->sc_subdevs[repid] = (struct uhidev *)dev;
     274           0 :         }
     275           0 : }
     276             : 
     277             : #ifndef SMALL_KERNEL
     278             : int
     279           0 : uhidev_use_rdesc(struct uhidev_softc *sc, usb_interface_descriptor_t *id,
     280             :                 int vendor, int product, void **descp, int *sizep)
     281             : {
     282             :         static uByte reportbuf[] = {2, 2};
     283             :         const void *descptr = NULL;
     284             :         void *desc;
     285             :         int size;
     286             : 
     287           0 :         if (vendor == USB_VENDOR_WACOM) {
     288             :                 /* The report descriptor for the Wacom Graphire is broken. */
     289           0 :                 switch (product) {
     290             :                 case USB_PRODUCT_WACOM_GRAPHIRE:
     291             :                         size = sizeof(uhid_graphire_report_descr);
     292             :                         descptr = uhid_graphire_report_descr;
     293           0 :                         break;
     294             :                 case USB_PRODUCT_WACOM_GRAPHIRE3_4X5:
     295             :                 case USB_PRODUCT_WACOM_GRAPHIRE4_4X5:
     296           0 :                         uhidev_set_report(sc, UHID_FEATURE_REPORT,
     297             :                             2, &reportbuf, sizeof(reportbuf));
     298             :                         size = sizeof(uhid_graphire3_4x5_report_descr);
     299             :                         descptr = uhid_graphire3_4x5_report_descr;
     300           0 :                         break;
     301             :                 default:
     302             :                         break;
     303             :                 }
     304           0 :         } else if ((id->bInterfaceClass == UICLASS_VENDOR &&
     305           0 :                    id->bInterfaceSubClass == UISUBCLASS_XBOX360_CONTROLLER &&
     306           0 :                    id->bInterfaceProtocol == UIPROTO_XBOX360_GAMEPAD)) {
     307             :                 /* The Xbox 360 gamepad has no report descriptor. */
     308             :                 size = sizeof(uhid_xb360gp_report_descr);
     309             :                 descptr = uhid_xb360gp_report_descr;
     310           0 :         }
     311             : 
     312           0 :         if (descptr) {
     313           0 :                 desc = malloc(size, M_USBDEV, M_NOWAIT);
     314           0 :                 if (desc == NULL)
     315           0 :                         return (ENOMEM);
     316             : 
     317           0 :                 memcpy(desc, descptr, size);
     318             : 
     319           0 :                 *descp = desc;
     320           0 :                 *sizep = size;
     321           0 :         }
     322             : 
     323           0 :         return (0);
     324           0 : }
     325             : #endif /* !SMALL_KERNEL */
     326             : 
     327             : int
     328           0 : uhidev_maxrepid(void *buf, int len)
     329             : {
     330             :         struct hid_data *d;
     331           0 :         struct hid_item h;
     332             :         int maxid;
     333             : 
     334             :         maxid = -1;
     335           0 :         h.report_ID = 0;
     336           0 :         for (d = hid_start_parse(buf, len, hid_none); hid_get_item(d, &h); )
     337           0 :                 if (h.report_ID > maxid)
     338           0 :                         maxid = h.report_ID;
     339           0 :         hid_end_parse(d);
     340           0 :         return (maxid);
     341           0 : }
     342             : 
     343             : int
     344           0 : uhidevprint(void *aux, const char *pnp)
     345             : {
     346           0 :         struct uhidev_attach_arg *uha = aux;
     347             : 
     348           0 :         if (pnp)
     349           0 :                 printf("uhid at %s", pnp);
     350           0 :         if (uha->reportid != 0 && uha->reportid != UHIDEV_CLAIM_ALLREPORTID)
     351           0 :                 printf(" reportid %d", uha->reportid);
     352           0 :         return (UNCONF);
     353             : }
     354             : 
     355           0 : int uhidevsubmatch(struct device *parent, void *match, void *aux)
     356             : {
     357           0 :         struct uhidev_attach_arg *uha = aux;
     358           0 :         struct cfdata *cf = match;
     359             : 
     360           0 :         if (cf->uhidevcf_reportid != UHIDEV_UNK_REPORTID &&
     361           0 :             cf->uhidevcf_reportid != uha->reportid)
     362           0 :                 return (0);
     363           0 :         return ((*cf->cf_attach->ca_match)(parent, cf, aux));
     364           0 : }
     365             : 
     366             : int
     367           0 : uhidev_activate(struct device *self, int act)
     368             : {
     369           0 :         struct uhidev_softc *sc = (struct uhidev_softc *)self;
     370             :         int i, rv = 0, r;
     371             : 
     372           0 :         switch (act) {
     373             :         case DVACT_DEACTIVATE:
     374           0 :                 for (i = 0; i < sc->sc_nrepid; i++)
     375           0 :                         if (sc->sc_subdevs[i] != NULL) {
     376           0 :                                 r = config_deactivate(
     377           0 :                                     &sc->sc_subdevs[i]->sc_dev);
     378           0 :                                 if (r && r != EOPNOTSUPP)
     379           0 :                                         rv = r;
     380             :                         }
     381           0 :                 usbd_deactivate(sc->sc_udev);
     382           0 :                 break;
     383             :         }
     384           0 :         return (rv);
     385             : }
     386             : 
     387             : int
     388           0 : uhidev_detach(struct device *self, int flags)
     389             : {
     390           0 :         struct uhidev_softc *sc = (struct uhidev_softc *)self;
     391             :         int i, rv = 0;
     392             : 
     393             :         DPRINTF(("uhidev_detach: sc=%p flags=%d\n", sc, flags));
     394             : 
     395           0 :         if (sc->sc_opipe != NULL) {
     396           0 :                 usbd_abort_pipe(sc->sc_opipe);
     397           0 :                 usbd_close_pipe(sc->sc_opipe);
     398           0 :                 sc->sc_opipe = NULL;
     399           0 :         }
     400             : 
     401           0 :         if (sc->sc_ipipe != NULL) {
     402           0 :                 usbd_abort_pipe(sc->sc_ipipe);
     403           0 :                 usbd_close_pipe(sc->sc_ipipe);
     404           0 :                 sc->sc_ipipe = NULL;
     405           0 :         }
     406             : 
     407           0 :         if (sc->sc_repdesc != NULL)
     408           0 :                 free(sc->sc_repdesc, M_USBDEV, sc->sc_repdesc_size);
     409             : 
     410             :         /*
     411             :          * XXX Check if we have only one children claiming all the Report
     412             :          * IDs, this is a hack since we need a dev -> Report ID mapping
     413             :          * for uhidev_intr().
     414             :          */
     415           0 :         if (sc->sc_nrepid > 1 && sc->sc_subdevs[0] != NULL &&
     416           0 :             sc->sc_subdevs[0] == sc->sc_subdevs[1])
     417           0 :                 return (config_detach(&sc->sc_subdevs[0]->sc_dev, flags));
     418             : 
     419           0 :         for (i = 0; i < sc->sc_nrepid; i++) {
     420           0 :                 if (sc->sc_subdevs[i] != NULL) {
     421           0 :                         rv |= config_detach(&sc->sc_subdevs[i]->sc_dev, flags);
     422           0 :                         sc->sc_subdevs[i] = NULL;
     423           0 :                 }
     424             :         }
     425             : 
     426           0 :         return (rv);
     427           0 : }
     428             : 
     429             : void
     430           0 : uhidev_intr(struct usbd_xfer *xfer, void *addr, usbd_status status)
     431             : {
     432           0 :         struct uhidev_softc *sc = addr;
     433             :         struct uhidev *scd;
     434             :         u_char *p;
     435             :         u_int rep;
     436           0 :         u_int32_t cc;
     437             : 
     438           0 :         if (usbd_is_dying(sc->sc_udev))
     439           0 :                 return;
     440             : 
     441           0 :         usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL);
     442             : 
     443             : #ifdef UHIDEV_DEBUG
     444             :         if (uhidevdebug > 5) {
     445             :                 u_int32_t i;
     446             : 
     447             :                 DPRINTF(("uhidev_intr: status=%d cc=%d\n", status, cc));
     448             :                 DPRINTF(("uhidev_intr: data ="));
     449             :                 for (i = 0; i < cc; i++)
     450             :                         DPRINTF((" %02x", sc->sc_ibuf[i]));
     451             :                 DPRINTF(("\n"));
     452             :         }
     453             : #endif
     454             : 
     455           0 :         if (status == USBD_CANCELLED || status == USBD_IOERROR)
     456           0 :                 return;
     457             : 
     458           0 :         if (status != USBD_NORMAL_COMPLETION) {
     459             :                 DPRINTF(("%s: interrupt status=%d\n", DEVNAME(sc), status));
     460           0 :                 usbd_clear_endpoint_stall_async(sc->sc_ipipe);
     461           0 :                 return;
     462             :         }
     463             : 
     464           0 :         p = sc->sc_ibuf;
     465           0 :         if (sc->sc_nrepid != 1)
     466           0 :                 rep = *p++, cc--;
     467             :         else
     468             :                 rep = 0;
     469           0 :         if (rep >= sc->sc_nrepid) {
     470           0 :                 printf("uhidev_intr: bad repid %d\n", rep);
     471           0 :                 return;
     472             :         }
     473           0 :         scd = sc->sc_subdevs[rep];
     474             :         DPRINTFN(5,("uhidev_intr: rep=%d, scd=%p state=0x%x\n",
     475             :                     rep, scd, scd ? scd->sc_state : 0));
     476           0 :         if (scd == NULL || !(scd->sc_state & UHIDEV_OPEN))
     477           0 :                 return;
     478             : 
     479           0 :         scd->sc_intr(scd, p, cc);
     480           0 : }
     481             : 
     482             : void
     483           0 : uhidev_get_report_desc(struct uhidev_softc *sc, void **desc, int *size)
     484             : {
     485           0 :         *desc = sc->sc_repdesc;
     486           0 :         *size = sc->sc_repdesc_size;
     487           0 : }
     488             : 
     489             : int
     490           0 : uhidev_open(struct uhidev *scd)
     491             : {
     492           0 :         struct uhidev_softc *sc = scd->sc_parent;
     493             :         usbd_status err;
     494             :         int error;
     495             : 
     496             :         DPRINTF(("uhidev_open: open pipe, state=%d refcnt=%d\n",
     497             :                  scd->sc_state, sc->sc_refcnt));
     498             : 
     499           0 :         if (scd->sc_state & UHIDEV_OPEN)
     500           0 :                 return (EBUSY);
     501           0 :         scd->sc_state |= UHIDEV_OPEN;
     502           0 :         if (sc->sc_refcnt++)
     503           0 :                 return (0);
     504             : 
     505           0 :         if (sc->sc_isize == 0)
     506           0 :                 return (0);
     507             : 
     508           0 :         sc->sc_ibuf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
     509             : 
     510             :         /* Set up input interrupt pipe. */
     511             :         DPRINTF(("uhidev_open: isize=%d, ep=0x%02x\n", sc->sc_isize,
     512             :             sc->sc_iep_addr));
     513             : 
     514           0 :         err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_iep_addr,
     515           0 :                   USBD_SHORT_XFER_OK, &sc->sc_ipipe, sc, sc->sc_ibuf,
     516           0 :                   sc->sc_isize, uhidev_intr, USBD_DEFAULT_INTERVAL);
     517           0 :         if (err != USBD_NORMAL_COMPLETION) {
     518             :                 DPRINTF(("uhidopen: usbd_open_pipe_intr failed, "
     519             :                     "error=%d\n", err));
     520             :                 error = EIO;
     521           0 :                 goto out1;
     522             :         }
     523             : 
     524             :         DPRINTF(("uhidev_open: sc->sc_ipipe=%p\n", sc->sc_ipipe));
     525             : 
     526           0 :         sc->sc_ixfer = usbd_alloc_xfer(sc->sc_udev);
     527           0 :         if (sc->sc_ixfer == NULL) {
     528             :                 DPRINTF(("uhidev_open: couldn't allocate an xfer\n"));
     529             :                 error = ENOMEM;
     530           0 :                 goto out1; // xxxx
     531             :         }
     532             : 
     533             :         /*
     534             :          * Set up output interrupt pipe if an output interrupt endpoint
     535             :          * exists.
     536             :          */
     537           0 :         if (sc->sc_oep_addr != -1) {
     538             :                 DPRINTF(("uhidev_open: oep=0x%02x\n", sc->sc_oep_addr));
     539             : 
     540           0 :                 err = usbd_open_pipe(sc->sc_iface, sc->sc_oep_addr,
     541           0 :                     0, &sc->sc_opipe);
     542             : 
     543           0 :                 if (err != USBD_NORMAL_COMPLETION) {
     544             :                         DPRINTF(("uhidev_open: usbd_open_pipe failed, "
     545             :                             "error=%d\n", err));
     546             :                         error = EIO;
     547           0 :                         goto out2;
     548             :                 }
     549             :                 DPRINTF(("uhidev_open: sc->sc_opipe=%p\n", sc->sc_opipe));
     550             : 
     551           0 :                 sc->sc_oxfer = usbd_alloc_xfer(sc->sc_udev);
     552           0 :                 if (sc->sc_oxfer == NULL) {
     553             :                         DPRINTF(("uhidev_open: couldn't allocate an xfer\n"));
     554             :                         error = ENOMEM;
     555           0 :                         goto out3;
     556             :                 }
     557             : 
     558           0 :                 sc->sc_owxfer = usbd_alloc_xfer(sc->sc_udev);
     559           0 :                 if (sc->sc_owxfer == NULL) {
     560             :                         DPRINTF(("uhidev_open: couldn't allocate owxfer\n"));
     561             :                         error = ENOMEM;
     562           0 :                         goto out3;
     563             :                 }
     564             :         }
     565             : 
     566           0 :         return (0);
     567             : 
     568             : out3:
     569             :         /* Abort output pipe */
     570           0 :         usbd_close_pipe(sc->sc_opipe);
     571             : out2:
     572             :         /* Abort input pipe */
     573           0 :         usbd_close_pipe(sc->sc_ipipe);
     574             : out1:
     575             :         DPRINTF(("uhidev_open: failed in someway"));
     576           0 :         free(sc->sc_ibuf, M_USBDEV, sc->sc_isize);
     577           0 :         scd->sc_state &= ~UHIDEV_OPEN;
     578           0 :         sc->sc_refcnt = 0;
     579           0 :         sc->sc_ipipe = NULL;
     580           0 :         sc->sc_opipe = NULL;
     581           0 :         if (sc->sc_oxfer != NULL) {
     582           0 :                 usbd_free_xfer(sc->sc_oxfer);
     583           0 :                 sc->sc_oxfer = NULL;
     584           0 :         }
     585           0 :         if (sc->sc_owxfer != NULL) {
     586           0 :                 usbd_free_xfer(sc->sc_owxfer);
     587           0 :                 sc->sc_owxfer = NULL;
     588           0 :         }
     589           0 :         if (sc->sc_ixfer != NULL) {
     590           0 :                 usbd_free_xfer(sc->sc_ixfer);
     591           0 :                 sc->sc_ixfer = NULL;
     592           0 :         }
     593           0 :         return (error);
     594           0 : }
     595             : 
     596             : void
     597           0 : uhidev_close(struct uhidev *scd)
     598             : {
     599           0 :         struct uhidev_softc *sc = scd->sc_parent;
     600             : 
     601           0 :         if (!(scd->sc_state & UHIDEV_OPEN))
     602           0 :                 return;
     603           0 :         scd->sc_state &= ~UHIDEV_OPEN;
     604           0 :         if (--sc->sc_refcnt)
     605           0 :                 return;
     606             :         DPRINTF(("uhidev_close: close pipe\n"));
     607             : 
     608           0 :         if (sc->sc_oxfer != NULL) {
     609           0 :                 usbd_free_xfer(sc->sc_oxfer);
     610           0 :                 sc->sc_oxfer = NULL;
     611           0 :         }
     612             : 
     613           0 :         if (sc->sc_owxfer != NULL) {
     614           0 :                 usbd_free_xfer(sc->sc_owxfer);
     615           0 :                 sc->sc_owxfer = NULL;
     616           0 :         }
     617             : 
     618           0 :         if (sc->sc_ixfer != NULL) {
     619           0 :                 usbd_free_xfer(sc->sc_ixfer);
     620           0 :                 sc->sc_ixfer = NULL;
     621           0 :         }
     622             : 
     623             :         /* Disable interrupts. */
     624           0 :         if (sc->sc_opipe != NULL) {
     625           0 :                 usbd_abort_pipe(sc->sc_opipe);
     626           0 :                 usbd_close_pipe(sc->sc_opipe);
     627           0 :                 sc->sc_opipe = NULL;
     628           0 :         }
     629             : 
     630           0 :         if (sc->sc_ipipe != NULL) {
     631           0 :                 usbd_abort_pipe(sc->sc_ipipe);
     632           0 :                 usbd_close_pipe(sc->sc_ipipe);
     633           0 :                 sc->sc_ipipe = NULL;
     634           0 :         }
     635             : 
     636           0 :         if (sc->sc_ibuf != NULL) {
     637           0 :                 free(sc->sc_ibuf, M_USBDEV, sc->sc_isize);
     638           0 :                 sc->sc_ibuf = NULL;
     639           0 :         }
     640           0 : }
     641             : 
     642             : int
     643           0 : uhidev_report_type_conv(int hid_type_id)
     644             : {
     645           0 :         switch (hid_type_id) {
     646             :         case hid_input:
     647           0 :                 return UHID_INPUT_REPORT;
     648             :         case hid_output:
     649           0 :                 return UHID_OUTPUT_REPORT;
     650             :         case hid_feature:
     651           0 :                 return UHID_FEATURE_REPORT;
     652             :         default:
     653           0 :                 return -1;
     654             :         }
     655           0 : }
     656             : 
     657             : int
     658           0 : uhidev_set_report(struct uhidev_softc *sc, int type, int id, void *data,
     659             :     int len)
     660             : {
     661           0 :         usb_device_request_t req;
     662             :         char *buf = data;
     663             :         int actlen = len;
     664             : 
     665             :         /* Prepend the reportID. */
     666           0 :         if (id > 0) {
     667           0 :                 len++;
     668           0 :                 buf = malloc(len, M_TEMP, M_WAITOK);
     669           0 :                 buf[0] = id;
     670           0 :                 memcpy(buf + 1, data, len - 1);
     671           0 :         }
     672             : 
     673           0 :         req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
     674           0 :         req.bRequest = UR_SET_REPORT;
     675           0 :         USETW2(req.wValue, type, id);
     676           0 :         USETW(req.wIndex, sc->sc_ifaceno);
     677           0 :         USETW(req.wLength, len);
     678             : 
     679           0 :         if (usbd_do_request(sc->sc_udev, &req, buf))
     680           0 :                 actlen = -1;
     681             : 
     682           0 :         if (id > 0)
     683           0 :                 free(buf, M_TEMP, len);
     684             : 
     685           0 :         return (actlen);
     686           0 : }
     687             : 
     688             : int
     689           0 : uhidev_set_report_async(struct uhidev_softc *sc, int type, int id, void *data,
     690             :     int len)
     691             : {
     692             :         struct usbd_xfer *xfer;
     693           0 :         usb_device_request_t req;
     694             :         int actlen = len;
     695             :         char *buf;
     696             : 
     697           0 :         xfer = usbd_alloc_xfer(sc->sc_udev);
     698           0 :         if (xfer == NULL)
     699           0 :                 return (-1);
     700             : 
     701           0 :         if (id > 0)
     702           0 :                 len++;
     703             : 
     704           0 :         buf = usbd_alloc_buffer(xfer, len);
     705           0 :         if (buf == NULL) {
     706           0 :                 usbd_free_xfer(xfer);
     707           0 :                 return (-1);
     708             :         }
     709             : 
     710             :         /* Prepend the reportID. */
     711           0 :         if (id > 0) {
     712           0 :                 buf[0] = id;
     713           0 :                 memcpy(buf + 1, data, len - 1);
     714           0 :         } else {
     715           0 :                 memcpy(buf, data, len);
     716             :         }
     717             : 
     718           0 :         req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
     719           0 :         req.bRequest = UR_SET_REPORT;
     720           0 :         USETW2(req.wValue, type, id);
     721           0 :         USETW(req.wIndex, sc->sc_ifaceno);
     722           0 :         USETW(req.wLength, len);
     723             : 
     724           0 :         if (usbd_request_async(xfer, &req, NULL, NULL))
     725           0 :                 actlen = -1;
     726             : 
     727           0 :         return (actlen);
     728           0 : }
     729             : 
     730             : int
     731           0 : uhidev_get_report(struct uhidev_softc *sc, int type, int id, void *data,
     732             :     int len)
     733             : {
     734           0 :         usb_device_request_t req;
     735             :         char *buf = data;
     736             :         usbd_status err;
     737           0 :         int actlen;
     738             : 
     739           0 :         if (id > 0) {
     740           0 :                 len++;
     741           0 :                 buf = malloc(len, M_TEMP, M_WAITOK|M_ZERO);
     742           0 :         }
     743             : 
     744           0 :         req.bmRequestType = UT_READ_CLASS_INTERFACE;
     745           0 :         req.bRequest = UR_GET_REPORT;
     746           0 :         USETW2(req.wValue, type, id);
     747           0 :         USETW(req.wIndex, sc->sc_ifaceno);
     748           0 :         USETW(req.wLength, len);
     749             : 
     750           0 :         err = usbd_do_request_flags(sc->sc_udev, &req, buf, 0, &actlen,
     751             :             USBD_DEFAULT_TIMEOUT);
     752           0 :         if (err != USBD_NORMAL_COMPLETION && err != USBD_SHORT_XFER)
     753           0 :                 actlen = -1;
     754             : 
     755             :         /* Skip the reportID. */
     756           0 :         if (id > 0) {
     757           0 :                 memcpy(data, buf + 1, len - 1);
     758           0 :                 free(buf, M_TEMP, len);
     759           0 :         }
     760             : 
     761           0 :         return (actlen);
     762           0 : }
     763             : 
     764             : void
     765           0 : uhidev_get_report_async_cb(struct usbd_xfer *xfer, void *priv, usbd_status err)
     766             : {
     767           0 :         struct uhidev_async_info *info = priv;
     768             :         char *buf;
     769             :         int len = -1;
     770             : 
     771           0 :         if (!usbd_is_dying(xfer->pipe->device)) {
     772           0 :                 if (err == USBD_NORMAL_COMPLETION || err == USBD_SHORT_XFER) {
     773           0 :                         len = xfer->actlen;
     774           0 :                         buf = KERNADDR(&xfer->dmabuf, 0);
     775           0 :                         if (info->id > 0) {
     776           0 :                                 len--;
     777           0 :                                 memcpy(info->data, buf + 1, len);
     778           0 :                         } else {
     779           0 :                                 memcpy(info->data, buf, len);
     780             :                         }
     781             :                 }
     782           0 :                 info->callback(info->priv, info->id, info->data, len);
     783           0 :         }
     784           0 :         free(info, M_TEMP, sizeof(*info));
     785           0 :         usbd_free_xfer(xfer);
     786           0 : }
     787             : 
     788             : int
     789           0 : uhidev_get_report_async(struct uhidev_softc *sc, int type, int id, void *data,
     790             :     int len, void *priv, void (*callback)(void *, int, void *, int))
     791             : {
     792             :         struct usbd_xfer *xfer;
     793           0 :         usb_device_request_t req;
     794             :         struct uhidev_async_info *info;
     795             :         int actlen = len;
     796             :         char *buf;
     797             : 
     798           0 :         xfer = usbd_alloc_xfer(sc->sc_udev);
     799           0 :         if (xfer == NULL)
     800           0 :                 return (-1);
     801             : 
     802           0 :         if (id > 0)
     803           0 :                 len++;
     804             : 
     805           0 :         buf = usbd_alloc_buffer(xfer, len);
     806           0 :         if (buf == NULL) {
     807           0 :                 usbd_free_xfer(xfer);
     808           0 :                 return (-1);
     809             :         }
     810             : 
     811           0 :         info = malloc(sizeof(*info), M_TEMP, M_NOWAIT);
     812           0 :         if (info == NULL) {
     813           0 :                 usbd_free_xfer(xfer);
     814           0 :                 return (-1);
     815             :         }
     816             : 
     817           0 :         info->callback = callback;
     818           0 :         info->priv = priv;
     819           0 :         info->data = data;
     820           0 :         info->id = id;
     821             : 
     822           0 :         req.bmRequestType = UT_READ_CLASS_INTERFACE;
     823           0 :         req.bRequest = UR_GET_REPORT;
     824           0 :         USETW2(req.wValue, type, id);
     825           0 :         USETW(req.wIndex, sc->sc_ifaceno);
     826           0 :         USETW(req.wLength, len);
     827             : 
     828           0 :         if (usbd_request_async(xfer, &req, info, uhidev_get_report_async_cb)) {
     829           0 :                 free(info, M_TEMP, sizeof(*info));
     830             :                 actlen = -1;
     831           0 :         }
     832             : 
     833           0 :         return (actlen);
     834           0 : }
     835             : 
     836             : usbd_status
     837           0 : uhidev_write(struct uhidev_softc *sc, void *data, int len)
     838             : {
     839             :         usbd_status error;
     840             : 
     841             :         DPRINTF(("uhidev_write: data=%p, len=%d\n", data, len));
     842             : 
     843           0 :         if (sc->sc_opipe == NULL)
     844           0 :                 return USBD_INVAL;
     845             : 
     846             : #ifdef UHIDEV_DEBUG
     847             :         if (uhidevdebug > 50) {
     848             : 
     849             :                 u_int32_t i;
     850             :                 u_int8_t *d = data;
     851             : 
     852             :                 DPRINTF(("uhidev_write: data ="));
     853             :                 for (i = 0; i < len; i++)
     854             :                         DPRINTF((" %02x", d[i]));
     855             :                 DPRINTF(("\n"));
     856             :         }
     857             : #endif
     858           0 :         usbd_setup_xfer(sc->sc_owxfer, sc->sc_opipe, 0, data, len,
     859             :             USBD_SYNCHRONOUS | USBD_CATCH, 0, NULL);
     860           0 :         error = usbd_transfer(sc->sc_owxfer);
     861           0 :         if (error)
     862           0 :                 usbd_clear_endpoint_stall(sc->sc_opipe);
     863             : 
     864           0 :         return (error);
     865           0 : }
     866             : 
     867             : int
     868           0 : uhidev_ioctl(struct uhidev *sc, u_long cmd, caddr_t addr, int flag,
     869             :     struct proc *p)
     870             : {
     871             :         struct usb_ctl_report_desc *rd;
     872             :         struct usb_ctl_report *re;
     873           0 :         int size;
     874           0 :         void *desc;
     875             : 
     876           0 :         switch (cmd) {
     877             :         case USB_GET_REPORT_DESC:
     878           0 :                 uhidev_get_report_desc(sc->sc_parent, &desc, &size);
     879           0 :                 rd = (struct usb_ctl_report_desc *)addr;
     880           0 :                 size = min(size, sizeof rd->ucrd_data);
     881           0 :                 rd->ucrd_size = size;
     882           0 :                 memcpy(rd->ucrd_data, desc, size);
     883           0 :                 break;
     884             :         case USB_GET_REPORT:
     885           0 :                 re = (struct usb_ctl_report *)addr;
     886           0 :                 switch (re->ucr_report) {
     887             :                 case UHID_INPUT_REPORT:
     888           0 :                         size = sc->sc_isize;
     889           0 :                         break;
     890             :                 case UHID_OUTPUT_REPORT:
     891           0 :                         size = sc->sc_osize;
     892           0 :                         break;
     893             :                 case UHID_FEATURE_REPORT:
     894           0 :                         size = sc->sc_fsize;
     895           0 :                         break;
     896             :                 default:
     897           0 :                         return EINVAL;
     898             :                 }
     899           0 :                 if (uhidev_get_report(sc->sc_parent, re->ucr_report,
     900           0 :                     sc->sc_report_id, re->ucr_data, size) != size)
     901           0 :                         return EIO;
     902             :                 break;
     903             :         case USB_SET_REPORT:
     904           0 :                 re = (struct usb_ctl_report *)addr;
     905           0 :                 switch (re->ucr_report) {
     906             :                 case UHID_INPUT_REPORT:
     907           0 :                         size = sc->sc_isize;
     908           0 :                         break;
     909             :                 case UHID_OUTPUT_REPORT:
     910           0 :                         size = sc->sc_osize;
     911           0 :                         break;
     912             :                 case UHID_FEATURE_REPORT:
     913           0 :                         size = sc->sc_fsize;
     914           0 :                         break;
     915             :                 default:
     916           0 :                         return EINVAL;
     917             :                 }
     918           0 :                 if (uhidev_set_report(sc->sc_parent, re->ucr_report,
     919           0 :                     sc->sc_report_id, re->ucr_data, size) != size)
     920           0 :                         return EIO;
     921             :                 break;
     922             :         case USB_GET_REPORT_ID:
     923           0 :                 *(int *)addr = sc->sc_report_id;
     924           0 :                 break;
     925             :         default:
     926           0 :                 return -1;
     927             :         }
     928           0 :         return 0;
     929           0 : }

Generated by: LCOV version 1.13