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

          Line data    Source code
       1             : /*      $OpenBSD: upd.c,v 1.26 2017/04/08 02:57:25 deraadt Exp $ */
       2             : 
       3             : /*
       4             :  * Copyright (c) 2015 David Higgs <higgsd@gmail.com>
       5             :  * Copyright (c) 2014 Andre de Oliveira <andre@openbsd.org>
       6             :  *
       7             :  * Permission to use, copy, modify, and distribute this software for any
       8             :  * purpose with or without fee is hereby granted, provided that the above
       9             :  * copyright notice and this permission notice appear in all copies.
      10             :  *
      11             :  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCAIMS ALL WARRANTIES
      12             :  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      13             :  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
      14             :  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
      15             :  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
      16             :  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
      17             :  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
      18             :  */
      19             : 
      20             : /* Driver for USB Power Devices sensors */
      21             : 
      22             : #include <sys/param.h>
      23             : #include <sys/systm.h>
      24             : #include <sys/kernel.h>
      25             : #include <sys/malloc.h>
      26             : #include <sys/device.h>
      27             : #include <sys/queue.h>
      28             : #include <sys/sensors.h>
      29             : 
      30             : #include <dev/usb/usb.h>
      31             : #include <dev/usb/usbdi.h>
      32             : #include <dev/usb/usbdevs.h>
      33             : #include <dev/usb/usbhid.h>
      34             : #include <dev/usb/uhidev.h>
      35             : #include <dev/usb/usbdi_util.h>
      36             : 
      37             : #ifdef UPD_DEBUG
      38             : #define DPRINTF(x)      do { printf x; } while (0)
      39             : #else
      40             : #define DPRINTF(x)
      41             : #endif
      42             : 
      43             : #define DEVNAME(sc)     ((sc)->sc_hdev.sc_dev.dv_xname)
      44             : 
      45             : struct upd_usage_entry {
      46             :         uint8_t                 usage_pg;
      47             :         uint8_t                 usage_id;
      48             :         enum sensor_type        senstype;
      49             :         char                    *usage_name; /* sensor string */
      50             :         int                     nchildren;
      51             :         struct upd_usage_entry  *children;
      52             : };
      53             : 
      54             : static struct upd_usage_entry upd_usage_batdep[] = {
      55             :         { HUP_BATTERY,  HUB_REL_STATEOF_CHARGE,
      56             :             SENSOR_PERCENT,      "RelativeStateOfCharge" },
      57             :         { HUP_BATTERY,  HUB_ABS_STATEOF_CHARGE,
      58             :             SENSOR_PERCENT,      "AbsoluteStateOfCharge" },
      59             :         { HUP_BATTERY,  HUB_REM_CAPACITY,
      60             :             SENSOR_PERCENT,      "RemainingCapacity" },
      61             :         { HUP_BATTERY,  HUB_FULLCHARGE_CAPACITY,
      62             :             SENSOR_PERCENT,      "FullChargeCapacity" },
      63             :         { HUP_BATTERY,  HUB_CHARGING,
      64             :             SENSOR_INDICATOR,    "Charging" },
      65             :         { HUP_BATTERY,  HUB_DISCHARGING,
      66             :             SENSOR_INDICATOR,    "Discharging" },
      67             :         { HUP_BATTERY,  HUB_ATRATE_TIMETOFULL,
      68             :             SENSOR_TIMEDELTA,    "AtRateTimeToFull" },
      69             :         { HUP_BATTERY,  HUB_ATRATE_TIMETOEMPTY,
      70             :             SENSOR_TIMEDELTA,    "AtRateTimeToEmpty" },
      71             :         { HUP_BATTERY,  HUB_RUNTIMETO_EMPTY,
      72             :             SENSOR_TIMEDELTA,    "RunTimeToEmpty" },
      73             :         { HUP_BATTERY,  HUB_NEED_REPLACEMENT,
      74             :             SENSOR_INDICATOR,    "NeedReplacement" },
      75             : };
      76             : static struct upd_usage_entry upd_usage_roots[] = {
      77             :         { HUP_BATTERY,  HUB_BATTERY_PRESENT,
      78             :             SENSOR_INDICATOR,    "BatteryPresent",
      79             :             nitems(upd_usage_batdep),   upd_usage_batdep },
      80             :         { HUP_POWER,    HUP_SHUTDOWN_IMMINENT,
      81             :             SENSOR_INDICATOR,    "ShutdownImminent" },
      82             :         { HUP_BATTERY,  HUB_AC_PRESENT,
      83             :             SENSOR_INDICATOR,    "ACPresent" },
      84             :         { HUP_POWER,    HUP_OVERLOAD,
      85             :             SENSOR_INDICATOR,    "Overload" },
      86             : };
      87             : #define UPD_MAX_SENSORS (nitems(upd_usage_batdep) + nitems(upd_usage_roots))
      88             : 
      89             : SLIST_HEAD(upd_sensor_head, upd_sensor);
      90             : 
      91             : struct upd_report {
      92             :         size_t                  size;           /* Size of the report */
      93             :         struct upd_sensor_head  sensors;        /* List in dependency order */
      94             :         int                     pending;        /* Waiting for an answer */
      95             : };
      96             : 
      97             : struct upd_sensor {
      98             :         struct ksensor          ksensor;
      99             :         struct hid_item         hitem;
     100             :         int                     attached;       /* Is there a matching report */
     101             :         struct upd_sensor_head  children;       /* list of children sensors */
     102             :         SLIST_ENTRY(upd_sensor) dep_next;       /* next in the child list */
     103             :         SLIST_ENTRY(upd_sensor) rep_next;       /* next in the report list */
     104             : };
     105             : 
     106             : struct upd_softc {
     107             :         struct uhidev            sc_hdev;
     108             :         int                      sc_num_sensors;
     109             :         u_int                    sc_max_repid;
     110             :         char                     sc_buf[256];
     111             : 
     112             :         /* sensor framework */
     113             :         struct ksensordev        sc_sensordev;
     114             :         struct sensor_task      *sc_sensortask;
     115             :         struct upd_report       *sc_reports;
     116             :         struct upd_sensor       *sc_sensors;
     117             :         struct upd_sensor_head   sc_root_sensors;
     118             : };
     119             : 
     120             : int  upd_match(struct device *, void *, void *);
     121             : void upd_attach(struct device *, struct device *, void *);
     122             : void upd_attach_sensor_tree(struct upd_softc *, void *, int, int,
     123             :     struct upd_usage_entry *, struct upd_sensor_head *);
     124             : int  upd_detach(struct device *, int);
     125             : 
     126             : void upd_intr(struct uhidev *, void *, uint);
     127             : void upd_refresh(void *);
     128             : void upd_request_children(struct upd_softc *, struct upd_sensor_head *);
     129             : void upd_update_report_cb(void *, int, void *, int);
     130             : 
     131             : void upd_sensor_invalidate(struct upd_softc *, struct upd_sensor *);
     132             : void upd_sensor_update(struct upd_softc *, struct upd_sensor *, uint8_t *, int);
     133             : int upd_lookup_usage_entry(void *, int, struct upd_usage_entry *,
     134             :     struct hid_item *);
     135             : struct upd_sensor *upd_lookup_sensor(struct upd_softc *, int, int);
     136             : 
     137             : struct cfdriver upd_cd = {
     138             :         NULL, "upd", DV_DULL
     139             : };
     140             : 
     141             : const struct cfattach upd_ca = {
     142             :         sizeof(struct upd_softc), upd_match, upd_attach, upd_detach
     143             : };
     144             : 
     145             : int
     146           0 : upd_match(struct device *parent, void *match, void *aux)
     147             : {
     148           0 :         struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
     149           0 :         int                       size;
     150           0 :         void                     *desc;
     151           0 :         struct hid_item           item;
     152             :         int                       ret = UMATCH_NONE;
     153             :         int                       i;
     154             : 
     155           0 :         if (uha->reportid != UHIDEV_CLAIM_ALLREPORTID)
     156           0 :                 return (ret);
     157             : 
     158             :         DPRINTF(("upd: vendor=0x%04x, product=0x%04x\n", uha->uaa->vendor,
     159             :             uha->uaa->product));
     160             : 
     161             :         /* need at least one sensor from root of tree */
     162           0 :         uhidev_get_report_desc(uha->parent, &desc, &size);
     163           0 :         for (i = 0; i < nitems(upd_usage_roots); i++)
     164           0 :                 if (upd_lookup_usage_entry(desc, size,
     165           0 :                     upd_usage_roots + i, &item)) {
     166             :                         ret = UMATCH_VENDOR_PRODUCT;
     167           0 :                         break;
     168             :                 }
     169             : 
     170           0 :         return (ret);
     171           0 : }
     172             : 
     173             : void
     174           0 : upd_attach(struct device *parent, struct device *self, void *aux)
     175             : {
     176           0 :         struct upd_softc         *sc = (struct upd_softc *)self;
     177           0 :         struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
     178           0 :         int                       size;
     179             :         int                       i;
     180           0 :         void                     *desc;
     181             : 
     182           0 :         sc->sc_hdev.sc_intr = upd_intr;
     183           0 :         sc->sc_hdev.sc_parent = uha->parent;
     184           0 :         SLIST_INIT(&sc->sc_root_sensors);
     185             : 
     186           0 :         strlcpy(sc->sc_sensordev.xname, DEVNAME(sc),
     187             :             sizeof(sc->sc_sensordev.xname));
     188             : 
     189           0 :         sc->sc_max_repid = uha->parent->sc_nrepid;
     190             :         DPRINTF(("\nupd: devname=%s sc_max_repid=%d\n",
     191             :             DEVNAME(sc), sc->sc_max_repid));
     192             : 
     193           0 :         sc->sc_reports = mallocarray(sc->sc_max_repid,
     194             :             sizeof(struct upd_report), M_USBDEV, M_WAITOK | M_ZERO);
     195           0 :         for (i = 0; i < sc->sc_max_repid; i++)
     196           0 :                 SLIST_INIT(&sc->sc_reports[i].sensors);
     197           0 :         sc->sc_sensors = mallocarray(UPD_MAX_SENSORS,
     198             :             sizeof(struct upd_sensor), M_USBDEV, M_WAITOK | M_ZERO);
     199           0 :         for (i = 0; i < UPD_MAX_SENSORS; i++)
     200           0 :                 SLIST_INIT(&sc->sc_sensors[i].children);
     201             : 
     202           0 :         sc->sc_num_sensors = 0;
     203           0 :         uhidev_get_report_desc(uha->parent, &desc, &size);
     204           0 :         upd_attach_sensor_tree(sc, desc, size, nitems(upd_usage_roots),
     205             :             upd_usage_roots, &sc->sc_root_sensors);
     206             :         DPRINTF(("upd: sc_num_sensors=%d\n", sc->sc_num_sensors));
     207             : 
     208           0 :         sc->sc_sensortask = sensor_task_register(sc, upd_refresh, 6);
     209           0 :         if (sc->sc_sensortask == NULL) {
     210           0 :                 printf(", unable to register update task\n");
     211           0 :                 return;
     212             :         }
     213           0 :         sensordev_install(&sc->sc_sensordev);
     214             : 
     215           0 :         printf("\n");
     216             : 
     217             :         DPRINTF(("upd_attach: complete\n"));
     218           0 : }
     219             : 
     220             : void
     221           0 : upd_attach_sensor_tree(struct upd_softc *sc, void *desc, int size,
     222             :     int nentries, struct upd_usage_entry *entries,
     223             :     struct upd_sensor_head *queue)
     224             : {
     225           0 :         struct hid_item           item;
     226             :         struct upd_usage_entry   *entry;
     227             :         struct upd_sensor        *sensor;
     228             :         struct upd_report        *report;
     229             :         int                       i;
     230             : 
     231           0 :         for (i = 0; i < nentries; i++) {
     232           0 :                 entry = entries + i;
     233           0 :                 if (!upd_lookup_usage_entry(desc, size, entry, &item)) {
     234             :                         /* dependency missing, add children to parent */
     235           0 :                         upd_attach_sensor_tree(sc, desc, size,
     236           0 :                             entry->nchildren, entry->children, queue);
     237           0 :                         continue;
     238             :                 }
     239             : 
     240             :                 DPRINTF(("%s: found %s on repid=%d\n", DEVNAME(sc),
     241             :                     entry->usage_name, item.report_ID));
     242           0 :                 if (item.report_ID < 0 ||
     243           0 :                     item.report_ID >= sc->sc_max_repid)
     244             :                         continue;
     245             : 
     246           0 :                 sensor = &sc->sc_sensors[sc->sc_num_sensors];
     247           0 :                 memcpy(&sensor->hitem, &item, sizeof(struct hid_item));
     248           0 :                 strlcpy(sensor->ksensor.desc, entry->usage_name,
     249             :                     sizeof(sensor->ksensor.desc));
     250           0 :                 sensor->ksensor.type = entry->senstype;
     251           0 :                 sensor->ksensor.flags |= SENSOR_FINVALID;
     252           0 :                 sensor->ksensor.status = SENSOR_S_UNKNOWN;
     253           0 :                 sensor->ksensor.value = 0;
     254           0 :                 sensor_attach(&sc->sc_sensordev, &sensor->ksensor);
     255           0 :                 sensor->attached = 1;
     256           0 :                 SLIST_INSERT_HEAD(queue, sensor, dep_next);
     257           0 :                 sc->sc_num_sensors++;
     258             : 
     259           0 :                 upd_attach_sensor_tree(sc, desc, size, entry->nchildren,
     260           0 :                     entry->children, &sensor->children);
     261             : 
     262           0 :                 report = &sc->sc_reports[item.report_ID];
     263           0 :                 if (SLIST_EMPTY(&report->sensors))
     264           0 :                         report->size = hid_report_size(desc,
     265           0 :                             size, item.kind, item.report_ID);
     266           0 :                 SLIST_INSERT_HEAD(&report->sensors, sensor, rep_next);
     267           0 :         }
     268           0 : }
     269             : 
     270             : int
     271           0 : upd_detach(struct device *self, int flags)
     272             : {
     273           0 :         struct upd_softc        *sc = (struct upd_softc *)self;
     274             :         struct upd_sensor       *sensor;
     275             :         int                      i;
     276             : 
     277           0 :         if (sc->sc_sensortask != NULL)
     278           0 :                 sensor_task_unregister(sc->sc_sensortask);
     279             : 
     280           0 :         sensordev_deinstall(&sc->sc_sensordev);
     281             : 
     282           0 :         for (i = 0; i < sc->sc_num_sensors; i++) {
     283           0 :                 sensor = &sc->sc_sensors[i];
     284           0 :                 if (sensor->attached)
     285           0 :                         sensor_detach(&sc->sc_sensordev, &sensor->ksensor);
     286             :         }
     287             : 
     288           0 :         free(sc->sc_reports, M_USBDEV, sc->sc_max_repid * sizeof(struct upd_report));
     289           0 :         free(sc->sc_sensors, M_USBDEV, UPD_MAX_SENSORS * sizeof(struct upd_sensor));
     290           0 :         return (0);
     291             : }
     292             : 
     293             : void
     294           0 : upd_refresh(void *arg)
     295             : {
     296           0 :         struct upd_softc        *sc = arg;
     297             :         int                      s;
     298             : 
     299             :         /* request root sensors, do not let async handlers fire yet */
     300           0 :         s = splusb();
     301           0 :         upd_request_children(sc, &sc->sc_root_sensors);
     302           0 :         splx(s);
     303           0 : }
     304             : 
     305             : void
     306           0 : upd_request_children(struct upd_softc *sc, struct upd_sensor_head *queue)
     307             : {
     308             :         struct upd_sensor       *sensor;
     309             :         struct upd_report       *report;
     310             :         int                      len, repid;
     311             : 
     312           0 :         SLIST_FOREACH(sensor, queue, dep_next) {
     313           0 :                 repid = sensor->hitem.report_ID;
     314           0 :                 report = &sc->sc_reports[repid];
     315             : 
     316             :                 /* already requested */
     317           0 :                 if (report->pending)
     318             :                         continue;
     319           0 :                 report->pending = 1;
     320             : 
     321           0 :                 len = uhidev_get_report_async(sc->sc_hdev.sc_parent,
     322           0 :                     UHID_FEATURE_REPORT, repid, sc->sc_buf, report->size, sc,
     323             :                     upd_update_report_cb);
     324             : 
     325             :                 /* request failed, force-invalidate all sensors in report */
     326           0 :                 if (len < 0) {
     327           0 :                         upd_update_report_cb(sc, repid, NULL, -1);
     328           0 :                         report->pending = 0;
     329           0 :                 }
     330             :         }
     331           0 : }
     332             : 
     333             : int
     334           0 : upd_lookup_usage_entry(void *desc, int size, struct upd_usage_entry *entry,
     335             :     struct hid_item *item)
     336             : {
     337             :         struct hid_data *hdata;
     338             :         int              ret = 0;
     339             : 
     340           0 :         for (hdata = hid_start_parse(desc, size, hid_feature);
     341           0 :              hid_get_item(hdata, item); ) {
     342           0 :                 if (item->kind == hid_feature &&
     343           0 :                     entry->usage_pg == HID_GET_USAGE_PAGE(item->usage) &&
     344           0 :                     entry->usage_id == HID_GET_USAGE(item->usage)) {
     345             :                         ret = 1;
     346           0 :                         break;
     347             :                 }
     348             :         }
     349           0 :         hid_end_parse(hdata);
     350             : 
     351           0 :         return (ret);
     352             : }
     353             : 
     354             : struct upd_sensor *
     355           0 : upd_lookup_sensor(struct upd_softc *sc, int page, int usage)
     356             : {
     357             :         struct upd_sensor       *sensor = NULL;
     358             :         int                      i;
     359             : 
     360           0 :         for (i = 0; i < sc->sc_num_sensors; i++) {
     361           0 :                 sensor = &sc->sc_sensors[i];
     362           0 :                 if (page == HID_GET_USAGE_PAGE(sensor->hitem.usage) &&
     363           0 :                     usage == HID_GET_USAGE(sensor->hitem.usage))
     364           0 :                         return (sensor);
     365             :         }
     366           0 :         return (NULL);
     367           0 : }
     368             : 
     369             : void
     370           0 : upd_update_report_cb(void *priv, int repid, void *data, int len)
     371             : {
     372           0 :         struct upd_softc        *sc = priv;
     373           0 :         struct upd_report       *report = &sc->sc_reports[repid];
     374             :         struct upd_sensor       *sensor;
     375             : 
     376             :         /* handle buggy firmware */
     377           0 :         if (len > 0 && report->size != len)
     378           0 :                 report->size = len;
     379             : 
     380           0 :         if (data == NULL || len <= 0) {
     381           0 :                 SLIST_FOREACH(sensor, &report->sensors, rep_next)
     382           0 :                         upd_sensor_invalidate(sc, sensor);
     383             :         } else {
     384           0 :                 SLIST_FOREACH(sensor, &report->sensors, rep_next)
     385           0 :                         upd_sensor_update(sc, sensor, data, len);
     386             :         }
     387           0 :         report->pending = 0;
     388           0 : }
     389             : 
     390             : void
     391           0 : upd_sensor_invalidate(struct upd_softc *sc, struct upd_sensor *sensor)
     392             : {
     393             :         struct upd_sensor       *child;
     394             : 
     395           0 :         sensor->ksensor.status = SENSOR_S_UNKNOWN;
     396           0 :         sensor->ksensor.flags |= SENSOR_FINVALID;
     397             : 
     398           0 :         SLIST_FOREACH(child, &sensor->children, dep_next)
     399           0 :                 upd_sensor_invalidate(sc, child);
     400           0 : }
     401             : 
     402             : void
     403           0 : upd_sensor_update(struct upd_softc *sc, struct upd_sensor *sensor,
     404             :     uint8_t *buf, int len)
     405             : {
     406             :         struct upd_sensor       *child;
     407             :         int64_t                  hdata, adjust;
     408             : 
     409           0 :         switch (HID_GET_USAGE(sensor->hitem.usage)) {
     410             :         case HUB_REL_STATEOF_CHARGE:
     411             :         case HUB_ABS_STATEOF_CHARGE:
     412             :         case HUB_REM_CAPACITY:
     413             :         case HUB_FULLCHARGE_CAPACITY:
     414             :                 adjust = 1000; /* scale adjust */
     415           0 :                 break;
     416             :         case HUB_ATRATE_TIMETOFULL:
     417             :         case HUB_ATRATE_TIMETOEMPTY:
     418             :         case HUB_RUNTIMETO_EMPTY:
     419             :                 /* spec says minutes, not seconds */
     420             :                 adjust = 1000000000LL;
     421           0 :                 break;
     422             :         default:
     423             :                 adjust = 1; /* no scale adjust */
     424           0 :                 break;
     425             :         }
     426             : 
     427           0 :         hdata = hid_get_data(buf, len, &sensor->hitem.loc);
     428           0 :         sensor->ksensor.value = hdata * adjust;
     429           0 :         sensor->ksensor.status = SENSOR_S_OK;
     430           0 :         sensor->ksensor.flags &= ~SENSOR_FINVALID;
     431             : 
     432             :         /* if battery not present, invalidate children */
     433           0 :         if (HID_GET_USAGE_PAGE(sensor->hitem.usage) == HUP_BATTERY &&
     434           0 :             HID_GET_USAGE(sensor->hitem.usage) == HUB_BATTERY_PRESENT &&
     435           0 :             sensor->ksensor.value == 0) {
     436           0 :                 SLIST_FOREACH(child, &sensor->children, dep_next)
     437           0 :                         upd_sensor_invalidate(sc, child);
     438           0 :                 return;
     439             :         }
     440             : 
     441           0 :         upd_request_children(sc, &sensor->children);
     442           0 : }
     443             : 
     444             : void
     445           0 : upd_intr(struct uhidev *uh, void *p, uint len)
     446             : {
     447             :         /* noop */
     448           0 : }

Generated by: LCOV version 1.13