On 2014/02/14 14:07, Andre de Oliveira wrote:
> hello.
> 
> blambert@ suggested me to write a driver for exposing usb-based
> uninterruptable power systems devices data as sysctl(8) sensors, thus
> enabling people to write "shutdown policies" using sensorsd(8), which
> can replaces apcupsd in some cases.
> 
> I received some feedback from reyk@ and blambert@, hope it's now ok for
> tech@ comments.
> 
> -a

Woohoo! This is really nice. Not only does this allow nice simple shutdowns
under the control of sensorsd, it has a bonus of exposing UPS status over
the network via snmpd.

I don't have much time now, but I'll try to take a closer look soon..


> 
> diff --git a/.gitignore b/.gitignore
> new file mode 100644
> index 0000000..a43f9f5
> --- /dev/null
> +++ b/.gitignore
> @@ -0,0 +1 @@
> +**.swp
> diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
> index beaffb8..76b20e7 100644
> --- a/share/man/man4/Makefile
> +++ b/share/man/man4/Makefile
> @@ -61,7 +61,7 @@ MAN=        aac.4 ac97.4 acphy.4 \
>       uk.4 ukbd.4 \
>       ukphy.4 ulpt.4 umass.4 umbg.4 umct.4 umidi.4 umodem.4 ums.4 umsm.4 \
>       unix.4 uow.4 uoaklux.4 uoakrh.4 uoakv.4 \
> -     upgt.4 upl.4 uplcom.4 ural.4 urio.4 url.4 urlphy.4 \
> +     upd.4 upgt.4 upl.4 uplcom.4 ural.4 urio.4 url.4 urlphy.4 \
>       urndis.4 urtw.4 urtwn.4 usb.4  usbf.4 uslcom.4 usps.4 \
>       uthum.4 uticom.4 utpms.4 utwitch.4 utrh.4 uts.4 uvideo.4 uvisor.4 \
>       uvscom.4 uyap.4 \
> diff --git a/share/man/man4/upd.4 b/share/man/man4/upd.4
> new file mode 100644
> index 0000000..5a9054d
> --- /dev/null
> +++ b/share/man/man4/upd.4
> @@ -0,0 +1,96 @@
> +.\"  $OpenBSD$
> +.\"
> +.\" Copyright (c) 2014 Andre de Oliveira <[email protected]>
> +.\"
> +.\" Permission to use, copy, modify, and distribute this software for any
> +.\" purpose with or without fee is hereby granted, provided that the above
> +.\" copyright notice and this permission notice appear in all copies.
> +.\"
> +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> +.\"
> +.Dd $Mdocdate: February 8 2014 $
> +.Dt UPD 4
> +.Os
> +.Sh NAME
> +.Nm upd
> +.Nd USB Power Devices battery voltage and load sensor
> +.Sh SYNOPSIS
> +.Cd "upd* at uhub?"
> +.Sh DESCRIPTION
> +The
> +.Nm
> +exposes data from USB Power Devices (such as an UPS), as hardware sensors
> +via
> +.Xr sysctl 3 .
> +The following devices are supported by the
> +.Nm
> +driver:
> +.Bl -column
> +.Pp
> +.It Li "American Power Conversion Smart-UPS 750"
> +.It Li "American Power Conversion Back-UPS CS 350"
> +.El
> +.Pp
> +The following sensors are provided by the
> +.Nm
> +driver, which can be monitored using
> +.Xr sensorsd 8 :
> +.Bl -column
> +.Pp
> +.It Battery Nominal Voltage
> +.It Battery Current Voltage
> +.It Battery Charging
> +.It Battery Discharging
> +.It Battery Present
> +.It Shutdown Imminent
> +.It AC Power Present
> +.It Battery Current Load
> +.El
> +.Sh EXAMPLES
> +In this example, the upd0 device is a regular UPS.
> +We use an entry on
> +.Xr sensorsd 8
> +to take an action when the battery level is below a 70%:
> +.Pp
> +.Bd -literal -offset indent
> +hw.sensors.upd0.percent0:low=70:command=/etc/sensorsd/lowbattwarn %l
> +.Ed
> +.Pp
> +The contents of lowbattwarn could be:
> +.Pp
> +.Bd -literal -offset indent
> +#!/bin/ksh
> +
> +if [ $# -lt 1 ]; then
> +        return;
> +fi
> +
> +if [ $1 -lt 70 ]; then
> +        logger "ups battery warning-level, halting"
> +        /sbin/halt -p
> +fi
> +.Ed
> +.Sh SEE ALSO
> +.Xr intro 4 ,
> +.Xr uhub 4 ,
> +.Xr sensorsd 8 ,
> +.Xr sysctl 8
> +.Sh HISTORY
> +The
> +.Nm
> +driver first appeared in .Ox
> +.Sh AUTHORS
> +The
> +.Nm
> +driver was written by Andre de Oliveira <[email protected]>,
> +sponsored by Esdenera Networks GmbH.
> +.Sh BUGS
> +When the battery is not present even with Battery Presence indicator set to 
> Off
> +the remaining battery sensors will still be present, besides their values can
> +not be trusted.
> diff --git a/share/man/man4/usb.4 b/share/man/man4/usb.4
> index 9009e62..f631fe2 100644
> --- a/share/man/man4/usb.4
> +++ b/share/man/man4/usb.4
> @@ -281,6 +281,8 @@ Maxim/Dallas DS2490 USB 1-Wire adapter
>  Prolific based host-to-host adapters
>  .It Xr usps 4
>  USPS composite AC power and temperature sensor
> +.It Xr upd 4
> +USB Power Devices battery voltage and load sensor
>  .It Xr uts 4
>  USB touchscreen support
>  .It Xr uyap 4
> diff --git a/sys/arch/.gitignore b/sys/arch/.gitignore
> new file mode 100644
> index 0000000..af7465e
> --- /dev/null
> +++ b/sys/arch/.gitignore
> @@ -0,0 +1 @@
> +*/compile
> diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC
> index 0a1bf36..77225c2 100644
> --- a/sys/arch/amd64/conf/GENERIC
> +++ b/sys/arch/amd64/conf/GENERIC
> @@ -236,6 +236,7 @@ udsbr*    at uhub?                # D-Link DSB-R100 radio
>  radio*       at udsbr?               # USB radio
>  uberry*      at uhub?                # Research In Motion Blackberry
>  ugen*        at uhub?                # USB Generic driver
> +upd* at uhub?                # USB Power Devices sensors
>  uath*        at uhub?                # Atheros AR5005UG/AR5005UX
>  ural*        at uhub?                # Ralink RT2500USB
>  rum* at uhub?                # Ralink RT2501USB/RT2601USB
> diff --git a/sys/dev/usb/files.usb b/sys/dev/usb/files.usb
> index 9ad82c5..42592f7 100644
> --- a/sys/dev/usb/files.usb
> +++ b/sys/dev/usb/files.usb
> @@ -60,6 +60,10 @@ device     ugen
>  attach       ugen at uhub
>  file dev/usb/ugen.c                  ugen                    needs-flag
>  
> +# USB Power Devices
> +device upd
> +attach upd at uhub
> +file   dev/usb/upd.c                 upd
>  
>  # HID
>  # HID "bus"
> diff --git a/sys/dev/usb/upd.c b/sys/dev/usb/upd.c
> new file mode 100644
> index 0000000..5a770b9
> --- /dev/null
> +++ b/sys/dev/usb/upd.c
> @@ -0,0 +1,413 @@
> +/*   $OpenBSD$ */
> +
> +/*
> + * Copyright (c) 2014 Andre de Oliveira <[email protected]>
> + *
> + * Permission to use, copy, modify, and distribute this software for any
> + * purpose with or without fee is hereby granted, provided that the above
> + * copyright notice and this permission notice appear in all copies.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCAIMS ALL WARRANTIES
> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +
> +/* Driver for USB Power Devices sensors */
> +
> +#include <sys/param.h>
> +#include <sys/systm.h>
> +#include <sys/kernel.h>
> +#include <sys/malloc.h>
> +#include <sys/device.h>
> +#include <sys/sensors.h>
> +
> +#include <dev/usb/usb.h>
> +#include <dev/usb/usbdi.h>
> +#include <dev/usb/usbdevs.h>
> +#include <dev/usb/usbhid.h>
> +#include <dev/usb/hid.h>
> +
> +#ifdef USB_DEBUG
> +#define UPD_DEBUG
> +#endif
> +
> +#ifdef UPD_DEBUG
> +int upddebug = 9;
> +#define DPRINTF(x)   do { if (upddebug) printf x; } while (0)
> +#define DPRINTFN(n, x)       do { if (upddebug >= (n)) printf x; } while (0)
> +#else
> +#define DPRINTF(x)
> +#define DPRINTFN(n, x)
> +#endif
> +
> +#define UPD_USB_TMOUT                1000
> +
> +/* protocol */
> +#define UPD_CMD_GET_BATTNOMV 0x0a
> +#define UPD_CMD_GET_BATTCURV 0x0b
> +#define UPD_CMD_GET_BATTLOAD 0x0c
> +#define UPD_CMD_GET_UPSMISC  0x07 /* misc flags from ups */
> +
> +#define UPD_CMD_NUM          4
> +
> +enum upd_sensor_id {
> +     UPD_SENSOR_UNKNOWN,
> +     UPD_SENSOR_BATTNOMV,
> +     UPD_SENSOR_BATTCURV,
> +     UPD_SENSOR_BATTLOAD,
> +     UPD_SENSOR_BATTCHARGING,
> +     UPD_SENSOR_BATTDISCHARG,
> +     UPD_SENSOR_BATTPRESENT,
> +     UPD_SENSOR_SHUTIMMINENT,
> +     UPD_SENSOR_ACPOWER,
> +     UPD_SENSOR_ACOFFTIME,
> +     UPD_SENSOR_NUM
> +};
> +
> +struct upd_report_param {
> +     int                      enabled;
> +     u_int8_t                 cmd;           /* usage id (issue command) */
> +     char                    *type_s;        /* sensor string */
> +     struct hid_location      loc;           /* size, count, pos */
> +};
> +
> +static struct upd_report_param upd_report_params[UPD_SENSOR_NUM] = {
> +     { 0, 0x0,                  "unknown",              { 0, 0, 0 } },
> +     { 1, UPD_CMD_GET_BATTNOMV, "battery nominal volt", { 0x10, 0x0, 0x0 } },
> +     { 1, UPD_CMD_GET_BATTCURV, "battery current volt", { 0x10, 0x0, 0x0 } },
> +     { 1, UPD_CMD_GET_BATTLOAD, "battery load",         { 0x08, 0x0, 0x0 } },
> +     { 1, UPD_CMD_GET_UPSMISC,  "battery charging",     { 0x01, 0x0, 0x0 } },
> +     { 1, UPD_CMD_GET_UPSMISC,  "battery discharging",  { 0x01, 0x0, 0x01 } 
> },
> +     { 1, UPD_CMD_GET_UPSMISC,  "battery present",      { 0x01, 0x0, 0x03 } 
> },
> +     { 1, UPD_CMD_GET_UPSMISC,  "shutdown imminent",    { 0x01, 0x0, 0x06 } 
> },
> +     { 1, UPD_CMD_GET_UPSMISC,  "acpower present",      { 0x0, 0x0, 0x0 } },
> +     { 0, UPD_CMD_GET_UPSMISC,  "acpower off time",     { 0x0, 0x0, 0x0 } }
> +};
> +
> +struct upd_udata {
> +     u_int8_t        cmd;
> +     u_int16_t       dlen;
> +     enum hid_kind   hkind;
> +     u_int8_t        data[8];
> +     int             rfail;
> +};
> +
> +struct upd_sensor {
> +     struct ksensor sensor;
> +     enum upd_sensor_id id;
> +     int attached;
> +};
> +
> +struct upd_softc {
> +     struct device            sc_hdev;
> +     struct usbd_device      *sc_udev;
> +     int                      sc_num_sensors;
> +
> +     /* sensor framework */
> +     struct upd_sensor        sc_sensor[UPD_SENSOR_NUM];
> +     struct upd_udata         sc_usbdata[UPD_CMD_NUM];
> +     struct ksensordev        sc_sensordev;
> +     struct sensor_task      *sc_sensortask;
> +};
> +
> +struct upd_type {
> +     struct usb_devno        upd_dev;
> +     u_int16_t               upd_flags;
> +};
> +
> +static const struct upd_type upd_devs[] = {
> +     { { USB_VENDOR_APC, USB_PRODUCT_APC_UPS }, 0 },
> +     { { USB_VENDOR_APC, USB_PRODUCT_APC_UPS5G }, 0 },
> +};
> +#define upd_lookup(v, p)  \
> +     ((const struct upd_type *)usb_lookup(upd_devs, v, p))
> +
> +int  upd_match(struct device *, void *, void *);
> +void upd_attach(struct device *, struct device *, void *);
> +int  upd_detach(struct device *, int);
> +
> +void upd_setup_sensors(struct upd_softc *);
> +void upd_refresh(void *);
> +void upd_refresh_data(struct upd_softc *);
> +int  upd_read_data(struct upd_softc *, struct upd_udata *, int);
> +struct upd_udata * upd_usbdata_lookup(struct upd_udata *, u_int8_t);
> +
> +struct cfdriver upd_cd = {
> +     NULL, "upd", DV_DULL
> +};
> +
> +const struct cfattach upd_ca = {
> +     sizeof(struct upd_softc),
> +     upd_match,
> +     upd_attach,
> +     upd_detach
> +};
> +
> +int
> +upd_match(struct device *parent, void *match, void *aux)
> +{
> +     struct usb_attach_arg *uaa = aux;
> +
> +     if (upd_lookup(uaa->vendor, uaa->product) == NULL)
> +                return (UMATCH_NONE);
> +
> +     DPRINTFN(3, ("upd: vendor=0x%x, product=0x%x\n", uaa->vendor,
> +         uaa->product));
> +
> +     return (UMATCH_VENDOR_PRODUCT);
> +}
> +
> +void
> +upd_attach(struct device *parent, struct device *self, void *aux)
> +{
> +     struct upd_softc *sc = (struct upd_softc *)self;
> +     struct usb_attach_arg *uaa = (struct usb_attach_arg *)aux;
> +     struct usbd_device *udev;
> +     int i;
> +
> +     sc->sc_udev = udev = uaa->device;
> +     sc->sc_num_sensors = 0;
> +
> +     strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.dv_xname,
> +         sizeof(sc->sc_sensordev.xname));
> +     upd_setup_sensors(sc);
> +
> +     /* attach sensors */
> +     for (i = 0; i < UPD_SENSOR_NUM; i++) {
> +             if (sc->sc_sensor[i].id == UPD_SENSOR_UNKNOWN)
> +                     continue;
> +             sc->sc_sensor[i].sensor.flags |= SENSOR_FINVALID;
> +             sc->sc_sensor[i].sensor.value = 0;
> +             sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i].sensor);
> +             sc->sc_sensor[i].attached = 1;
> +             sc->sc_num_sensors++;
> +     }
> +
> +     DPRINTFN(3, ("upd: sc_num_sensors=%d\n", sc->sc_num_sensors));
> +
> +     if (sc->sc_num_sensors > 0) {
> +             sc->sc_sensortask = sensor_task_register(sc, upd_refresh, 6);
> +             if (sc->sc_sensortask == NULL) {
> +                     printf(", unable to register update task\n");
> +                     return;
> +             }
> +             sensordev_install(&sc->sc_sensordev);
> +     }
> +
> +     /*
> +      * init usbdata
> +      * those commands width were identified from observing apcupsd/ugen(4)
> +      * data exchange
> +      */
> +     sc->sc_usbdata[0].cmd = UPD_CMD_GET_BATTNOMV;
> +     sc->sc_usbdata[0].dlen = 0x03;
> +     sc->sc_usbdata[0].hkind = hid_feature;
> +     sc->sc_usbdata[1].cmd = UPD_CMD_GET_BATTCURV;
> +     sc->sc_usbdata[1].dlen = 0x02;
> +     sc->sc_usbdata[1].hkind = hid_feature;
> +     sc->sc_usbdata[2].cmd = UPD_CMD_GET_BATTLOAD;
> +     sc->sc_usbdata[2].dlen = 0x03;
> +     sc->sc_usbdata[2].hkind = hid_feature;
> +     sc->sc_usbdata[3].cmd = UPD_CMD_GET_UPSMISC;
> +     sc->sc_usbdata[3].dlen = 0x03;
> +     sc->sc_usbdata[3].hkind = hid_feature;
> +
> +     DPRINTFN(3, ("upd_attach: complete\n"));
> +}
> +
> +int
> +upd_detach(struct device *self, int flags)
> +{
> +     struct upd_softc *sc = (struct upd_softc *)self;
> +     int i;
> +
> +     if (sc->sc_num_sensors > 0) {
> +             wakeup(&sc->sc_sensortask);
> +             sensordev_deinstall(&sc->sc_sensordev);
> +             for (i = 0; i < UPD_SENSOR_NUM; i++) {
> +                     if (sc->sc_sensor[i].attached) {
> +                             sensor_detach(&sc->sc_sensordev,
> +                                     &sc->sc_sensor[i].sensor);
> +                             DPRINTFN(5, ("upd_detach: %s\n",
> +                                 sc->sc_sensor[i].sensor.desc));
> +                     }
> +             }
> +             if (sc->sc_sensortask != NULL)
> +                     sensor_task_unregister(sc->sc_sensortask);
> +     }
> +
> +     DPRINTFN(3, ("upd_detach: complete\n"));
> +     return (0);
> +}
> +
> +int
> +upd_read_data(struct upd_softc *sc, struct upd_udata *udata, int tmout)
> +{
> +     usb_device_request_t req;
> +     int ucr_actlen = 0;
> +     int err = 0;
> +     enum hid_kind hkind;
> +     size_t buflen;
> +     u_int16_t dlen;
> +     u_int8_t cmd, *buf = NULL;
> +     void *ptr = NULL;
> +
> +     buf = udata->data;
> +     cmd = udata->cmd;
> +     dlen = udata->dlen;
> +     hkind = udata->hkind;
> +     buflen = sizeof(buf);
> +
> +     if (buf == NULL || buflen == 0)
> +             return (0);
> +
> +     ptr = malloc(buflen, M_TEMP, M_WAITOK);
> +
> +     req.bmRequestType = UT_READ_CLASS_INTERFACE;
> +     req.bRequest = UR_GET_REPORT;
> +
> +     USETW(req.wValue, ((hkind + 1) << 8) | cmd);
> +     USETW(req.wIndex, 0);
> +     USETW(req.wLength, dlen);
> +
> +     err = usbd_do_request_flags(sc->sc_udev, &req, ptr, USBD_SHORT_XFER_OK,
> +         &ucr_actlen, tmout);
> +
> +     if (err)
> +             goto ret;
> +
> +     memcpy(buf, ptr, buflen);
> +
> +     DPRINTFN(5, ("actlen=%02x ptr[0]=%02x ptr[1]=%02x ptr[2]=%02x\n",
> +         ucr_actlen, ((u_int8_t *)ptr)[0], ((u_int8_t *)ptr)[1],
> +         ((u_int8_t *)ptr)[2]));
> +
> +ret:
> +     if (ptr)
> +             free(ptr, M_TEMP);
> +
> +     return (err);
> +}
> +
> +void
> +upd_setup_sensors(struct upd_softc *sc)
> +{
> +     int i;
> +
> +     for (i = 0; i < UPD_SENSOR_NUM; i++) {
> +             if (upd_report_params[i].enabled != 1)
> +                     continue;
> +
> +             sc->sc_sensor[i].id = i;
> +             strlcpy(sc->sc_sensor[i].sensor.desc,
> +                 upd_report_params[i].type_s,
> +                 sizeof(sc->sc_sensor[i].sensor.desc));
> +             sc->sc_sensor[i].attached = 0;
> +
> +             switch (i) {
> +             case UPD_SENSOR_BATTNOMV:
> +             case UPD_SENSOR_BATTCURV:
> +                     sc->sc_sensor[i].sensor.type = SENSOR_VOLTS_DC;
> +                     break;
> +             case UPD_SENSOR_BATTLOAD:
> +                     sc->sc_sensor[i].sensor.type = SENSOR_PERCENT;
> +                     break;
> +             case UPD_SENSOR_SHUTIMMINENT:
> +             case UPD_SENSOR_BATTCHARGING:
> +             case UPD_SENSOR_BATTDISCHARG:
> +             case UPD_SENSOR_BATTPRESENT:
> +             case UPD_SENSOR_ACPOWER:
> +                     sc->sc_sensor[i].sensor.type = SENSOR_INDICATOR;
> +                     break;
> +             }
> +     }
> +}
> +
> +void
> +upd_refresh(void *arg)
> +{
> +     struct upd_softc *sc = (struct upd_softc *)arg;
> +     struct hid_location *loc;
> +     struct upd_udata *udata;
> +     u_long hdata;
> +     u_int8_t cmd;
> +     u_int8_t *buf;
> +     int i;
> +
> +     upd_refresh_data(sc);
> +
> +     for (i = 0; i < UPD_SENSOR_NUM; i++) {
> +             if (sc->sc_sensor[i].attached != 1 ||
> +                 upd_report_params[i].enabled != 1)
> +                     continue;
> +
> +             cmd = upd_report_params[i].cmd;
> +             udata = upd_usbdata_lookup(sc->sc_usbdata, cmd);
> +
> +             /* skip on eventual read failures or unsupported features */
> +             if (udata->rfail)
> +                     continue;
> +
> +             buf = udata->data;
> +             loc = &upd_report_params[i].loc;
> +             hdata = hid_get_data(buf + 1, loc);
> +
> +             switch (i) {
> +             case UPD_SENSOR_BATTNOMV:
> +             case UPD_SENSOR_BATTCURV:
> +             case UPD_SENSOR_BATTLOAD:
> +                     if (sc->sc_sensor[UPD_SENSOR_BATTPRESENT].sensor.value)
> +                             hdata *= 1000; /* scale adjust */
> +                     else
> +                             hdata = 0;
> +                     break;
> +             case UPD_SENSOR_ACPOWER:
> +                     /* use discharging flag as acpower indicator */
> +                     if (sc->sc_sensor[UPD_SENSOR_BATTDISCHARG].sensor.value)
> +                             hdata = 0;
> +                     else
> +                             hdata = 1;
> +                     break;
> +             }
> +
> +             sc->sc_sensor[i].sensor.flags &= ~SENSOR_FINVALID;
> +             sc->sc_sensor[i].sensor.value = hdata;
> +             DPRINTFN(3, ("%s: %s: hidget data: %d\n",
> +                          sc->sc_sensordev.xname,
> +                          upd_report_params[i].type_s, hdata));
> +     }
> +}
> +
> +void
> +upd_refresh_data(struct upd_softc *sc)
> +{
> +     struct upd_udata *udata;
> +     int i, ret;
> +
> +     for (i = 0; i < UPD_CMD_NUM; i++) {
> +             udata = &sc->sc_usbdata[i];
> +             bzero(udata->data, sizeof(udata->data));
> +             ret = upd_read_data(sc, udata, UPD_USB_TMOUT);
> +             if (ret != USBD_NORMAL_COMPLETION) {
> +                     DPRINTFN(5, ("upd: data read fail\n"));
> +                     udata->rfail = 1;
> +             }
> +             udata->rfail = 0;
> +     }
> +}
> +
> +struct upd_udata *
> +upd_usbdata_lookup(struct upd_udata *usbdata, u_int8_t cmd)
> +{
> +     int i;
> +
> +     for (i = 0; i < UPD_CMD_NUM; i++)
> +             if (usbdata[i].cmd == cmd)
> +                     return (&usbdata[i]);
> +
> +     return (NULL);
> +}
> 

Reply via email to