summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--share/man/man4/upd.4115
-rw-r--r--sys/arch/amd64/conf/GENERIC3
-rw-r--r--sys/dev/usb/files.usb7
-rw-r--r--sys/dev/usb/upd.c317
4 files changed, 440 insertions, 2 deletions
diff --git a/share/man/man4/upd.4 b/share/man/man4/upd.4
new file mode 100644
index 00000000000..1c4a0c3e5d4
--- /dev/null
+++ b/share/man/man4/upd.4
@@ -0,0 +1,115 @@
+.\" $OpenBSD: upd.4,v 1.1 2014/03/17 18:40:46 andre Exp $
+.\"
+.\" Copyright (c) 2014 Andre de Oliveira <andre@openbsd.org>
+.\"
+.\" 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: March 17 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
+driver 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 -bullet -offset indent
+.It
+American Power Conversion Smart-UPS 750
+.It
+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 -bullet -offset indent
+.It
+RelativeStateOfCharge
+.It
+AbsoluteStateOfCharge
+.It
+RemainingCapacity
+.It
+FullChargeCapacity
+.It
+Charging
+.It
+Discharging
+.It
+BatteryPresent
+.It
+ShutdownImminent
+.It
+ACPresent
+.It
+AtRateTimeToFull
+.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%:
+.Bd -literal
+hw.sensors.upd0.percent0:low=70:command=/etc/sensorsd/lowbattwarn %l
+.Ed
+.Pp
+The contents of lowbattwarn could be:
+.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 5.6 .
+.Sh AUTHORS
+The
+.Nm
+driver was written by
+.An Andre de Oliveira Aq Mt andre@openbsd.org ,
+partially sponsored by Esdenera Networks GmbH.
+.Sh CAVEATS
+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
+cannot be trusted.
+.Pp
+Users of apcupsd port will notice either
+.Xr ugen 4 ,
+is missing, it is necessary to disable the
+.Nm
+driver
+in order to use apcupsd.
diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC
index 956e6d64b84..ed3678e241d 100644
--- a/sys/arch/amd64/conf/GENERIC
+++ b/sys/arch/amd64/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.362 2014/03/12 00:44:10 jmatthew Exp $
+# $OpenBSD: GENERIC,v 1.363 2014/03/17 18:40:46 andre Exp $
#
# For further information on compiling OpenBSD kernels, see the config(8)
# man page.
@@ -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 uhidev? # 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 9ad82c509e6..2043f88a1a2 100644
--- a/sys/dev/usb/files.usb
+++ b/sys/dev/usb/files.usb
@@ -1,4 +1,4 @@
-# $OpenBSD: files.usb,v 1.112 2014/01/20 18:27:46 jcs Exp $
+# $OpenBSD: files.usb,v 1.113 2014/03/17 18:40:46 andre Exp $
# $NetBSD: files.usb,v 1.16 2000/02/14 20:29:54 augustss Exp $
#
# Config file and device description for machine-independent USB code.
@@ -433,3 +433,8 @@ file dev/usb/uberry.c uberry
device udfu {}
attach udfu at uhub
file dev/usb/udfu.c udfu
+
+# USB Power Devices
+device upd: hid
+attach upd at uhidbus
+file dev/usb/upd.c upd
diff --git a/sys/dev/usb/upd.c b/sys/dev/usb/upd.c
new file mode 100644
index 00000000000..b6c751a7fab
--- /dev/null
+++ b/sys/dev/usb/upd.c
@@ -0,0 +1,317 @@
+/* $OpenBSD: upd.c,v 1.1 2014/03/17 18:40:46 andre Exp $ */
+
+/*
+ * Copyright (c) 2014 Andre de Oliveira <andre@openbsd.org>
+ *
+ * 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/hid.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/usbhid.h>
+#include <dev/usb/uhidev.h>
+#include <dev/usb/usbdi_util.h>
+
+#ifdef UPD_DEBUG
+#define DPRINTF(x) do { printf x; } while (0)
+#else
+#define DPRINTF(x)
+#endif
+
+enum upd_sensor_id {
+ UPD_SENSOR_UNKNOWN,
+ UPD_SENSOR_RELCHARGE,
+ UPD_SENSOR_ABSCHARGE,
+ UPD_SENSOR_REMCAPACI,
+ UPD_SENSOR_FULLCHARG,
+ UPD_SENSOR_CHARGING,
+ UPD_SENSOR_DISCHARG,
+ UPD_SENSOR_BATTPRESENT,
+ UPD_SENSOR_SHUTIMMINENT,
+ UPD_SENSOR_ACPRESENT,
+ UPD_SENSOR_TIMETOFULL,
+ UPD_SENSOR_NUM
+ /*
+ * TODO
+ * - atratetimetofull
+ * - atratetimetoempty
+ * - cyclecount
+ */
+};
+
+struct upd_usage_entry {
+ enum upd_sensor_id upd_sid;
+ uint8_t usage_pg;
+ uint8_t usage_id;
+ enum sensor_type senstype;
+ char *usage_name; /* sensor string */
+};
+
+static struct upd_usage_entry upd_usage_table[UPD_SENSOR_NUM] = {
+ { UPD_SENSOR_UNKNOWN, HUP_UNDEFINED,HUP_UNDEFINED,
+ -1, "unknown" },
+ { UPD_SENSOR_RELCHARGE, HUP_BATTERY, HUB_REL_STATEOF_CHARGE,
+ SENSOR_PERCENT, "RelativeStateOfCharge" },
+ { UPD_SENSOR_ABSCHARGE, HUP_BATTERY, HUB_ABS_STATEOF_CHARGE,
+ SENSOR_PERCENT, "AbsoluteStateOfCharge" },
+ { UPD_SENSOR_REMCAPACI, HUP_BATTERY, HUB_REM_CAPACITY,
+ SENSOR_PERCENT, "RemainingCapacity" },
+ { UPD_SENSOR_FULLCHARG, HUP_BATTERY, HUB_FULLCHARGE_CAPACITY,
+ SENSOR_PERCENT, "FullChargeCapacity" },
+ { UPD_SENSOR_CHARGING, HUP_BATTERY, HUB_CHARGING,
+ SENSOR_INDICATOR, "Charging" },
+ { UPD_SENSOR_DISCHARG, HUP_BATTERY, HUB_DISCHARGING,
+ SENSOR_INDICATOR, "Discharging" },
+ { UPD_SENSOR_BATTPRESENT, HUP_BATTERY, HUB_BATTERY_PRESENT,
+ SENSOR_INDICATOR, "BatteryPresent" },
+ { UPD_SENSOR_SHUTIMMINENT,HUP_POWER, HUP_SHUTDOWN_IMMINENT,
+ SENSOR_INDICATOR, "ShutdownImminent" },
+ { UPD_SENSOR_ACPRESENT, HUP_BATTERY, HUB_AC_PRESENT,
+ SENSOR_INDICATOR, "ACPresent" },
+ { UPD_SENSOR_TIMETOFULL, HUP_BATTERY, HUB_ATRATE_TIMETOFULL,
+ SENSOR_TIMEDELTA, "AtRateTimeToFull" }
+};
+
+struct upd_sensor {
+ int attached;
+ struct ksensor sensor;
+ struct hid_item item;
+ size_t flen;
+};
+
+struct upd_softc {
+ struct uhidev sc_hdev;
+ int sc_num_sensors;
+
+ /* sensor framework */
+ struct upd_sensor sc_sensors[UPD_SENSOR_NUM];
+ struct ksensordev sc_sensordev;
+ struct sensor_task *sc_sensortask;
+};
+
+static const struct usb_devno upd_devs[] = {
+ { USB_VENDOR_APC, USB_PRODUCT_APC_UPS },
+ { USB_VENDOR_APC, USB_PRODUCT_APC_UPS5G },
+ { USB_VENDOR_LIEBERT, USB_PRODUCT_LIEBERT_UPS }
+};
+#define upd_lookup(v, p) 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_add_sensor(struct upd_softc *, const struct hid_item *, void *, int);
+void upd_refresh(void *);
+void upd_intr(struct uhidev *, void *, uint);
+
+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 uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
+
+ if (uha->reportid != UHIDEV_CLAIM_ALLREPORTID)
+ return (UMATCH_NONE);
+
+ if (upd_lookup(uha->uaa->vendor, uha->uaa->product) == NULL)
+ return (UMATCH_NONE);
+
+ DPRINTF(("upd: vendor=0x%x, product=0x%x\n", uha->uaa->vendor,
+ uha->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 uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
+ struct hid_item item;
+ struct hid_data *hdata;
+ int size;
+ void *desc;
+
+ sc->sc_hdev.sc_intr = upd_intr;
+ sc->sc_hdev.sc_parent = uha->parent;
+ sc->sc_num_sensors = 0;
+
+ strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname,
+ sizeof(sc->sc_sensordev.xname));
+
+ DPRINTF(("upd: devname=%s sc_nrepid=%d\n", sc->sc_hdev.sc_dev.dv_xname,
+ uha->parent->sc_nrepid));
+
+ uhidev_get_report_desc(uha->parent, &desc, &size);
+ hdata = hid_start_parse(desc, size, hid_feature);
+ /* lookup for item in our sensors list */
+ while (hid_get_item(hdata, &item))
+ upd_add_sensor(sc, &item, desc, size);
+
+ hid_end_parse(hdata);
+ DPRINTF(("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);
+ }
+
+ printf("\n");
+
+ DPRINTF(("upd_attach: complete\n"));
+}
+
+int
+upd_detach(struct device *self, int flags)
+{
+ struct upd_softc *sc = (struct upd_softc *)self;
+ struct upd_sensor *sensor;
+ int i;
+
+ if (sc->sc_num_sensors <= 0)
+ goto finish;
+
+ if (sc->sc_sensortask != NULL) {
+ wakeup(&sc->sc_sensortask);
+ sensor_task_unregister(sc->sc_sensortask);
+ }
+
+ sensordev_deinstall(&sc->sc_sensordev);
+
+ for (i = 0; i < UPD_SENSOR_NUM; i++) {
+ sensor = &sc->sc_sensors[i];
+ if (!sensor->attached)
+ continue;
+
+ sensor_detach(&sc->sc_sensordev, &sensor->sensor);
+ DPRINTF(("upd_detach: %s\n", sensor->sensor.desc));
+ }
+
+finish:
+ DPRINTF(("upd_detach: complete\n"));
+ return (0);
+}
+
+void
+upd_refresh(void *arg)
+{
+ struct upd_softc *sc = (struct upd_softc *)arg;
+ struct hid_location *loc;
+ struct upd_sensor *sensor;
+ ulong hdata;
+ uint8_t buf[256];
+ int i, err;
+
+ for (i = 0; i < UPD_SENSOR_NUM; i++) {
+ sensor = &sc->sc_sensors[i];
+ if (sensor && ! sensor->attached)
+ continue;
+
+ loc = &sensor->item.loc;
+ sc->sc_hdev.sc_report_id = sensor->item.report_ID;
+ memset(buf, 0x0, sizeof(buf));
+ err = uhidev_get_report(&sc->sc_hdev, UHID_FEATURE_REPORT, buf,
+ sensor->flen);
+
+ if (err) {
+ DPRINTF(("read failure: sens=%02x reportid=%02x err=%d\n", i,
+ sc->sc_hdev.sc_report_id, err));
+ continue;
+ }
+
+ hdata = hid_get_data(buf + 1, loc);
+ switch (i) {
+ case UPD_SENSOR_RELCHARGE:
+ case UPD_SENSOR_ABSCHARGE:
+ case UPD_SENSOR_REMCAPACI:
+ case UPD_SENSOR_FULLCHARG:
+ if (sc->sc_sensors[UPD_SENSOR_BATTPRESENT].sensor.value)
+ hdata *= 1000; /* scale adjust */
+ else
+ hdata = 0;
+ break;
+ }
+
+ sensor->sensor.flags &= ~SENSOR_FINVALID;
+ sensor->sensor.value = hdata;
+ DPRINTF(("%s: %s: hidget data: %d\n",
+ sc->sc_sensordev.xname, upd_usage_table[i].usage_name,
+ hdata));
+ }
+}
+
+void
+upd_add_sensor(struct upd_softc *sc, const struct hid_item *item, void *desc,
+ int dsiz)
+{
+ struct upd_usage_entry *entry = NULL;
+ struct upd_sensor *sensor = NULL;
+ int i;
+
+ for (i = 0; i < UPD_SENSOR_NUM; i++) {
+ entry = &upd_usage_table[i];
+ if (entry->upd_sid == UPD_SENSOR_UNKNOWN ||
+ entry->usage_pg != HID_GET_USAGE_PAGE(item->usage) ||
+ entry->usage_id != HID_GET_USAGE(item->usage))
+ continue;
+
+ sensor = &sc->sc_sensors[i];
+ if (sensor && sensor->attached)
+ continue;
+
+ /* keep our copy of hid_item */
+ memset(&sensor->item, 0x0, sizeof(struct hid_item));
+ memcpy(&sensor->item, item, sizeof(struct hid_item));
+ sensor->flen = hid_report_size(desc, dsiz, hid_feature,
+ item->report_ID) + 1;
+ strlcpy(sensor->sensor.desc, entry->usage_name,
+ sizeof(sensor->sensor.desc));
+ sensor->sensor.type = entry->senstype;
+ sensor->sensor.flags |= SENSOR_FINVALID;
+ sensor->sensor.value = 0;
+ sensor_attach(&sc->sc_sensordev, &sensor->sensor);
+ sensor->attached = 1;
+ sc->sc_num_sensors++;
+ }
+}
+
+void
+upd_intr(struct uhidev *uh, void *p, uint len)
+{
+ /* noop */
+}