summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorYojiro Uo <yuo@cvs.openbsd.org>2012-09-20 13:52:12 +0000
committerYojiro Uo <yuo@cvs.openbsd.org>2012-09-20 13:52:12 +0000
commit06e2955cdc9dc864884658fa3502f0887b1feb94 (patch)
tree2a8ce216449c5044baf38e86239bb612d333e612 /sys/dev
parentee967bb279f78037ebc496177469bc4f5a82b2e3 (diff)
add device driver for Toradex OAK usb sensors:
uoaklux(4): Toradex OAK LUX, USB illuminance sensor uoakrh(4) : Toradex OAK RH, USB temperature and relative humidity sensor uoakv(4) : Toradex OAK V, USB +/- 10V 8channel ADC interface ok deraadet@
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/usb/files.usb21
-rw-r--r--sys/dev/usb/uoak.h171
-rw-r--r--sys/dev/usb/uoak_subr.c362
-rw-r--r--sys/dev/usb/uoaklux.c329
-rw-r--r--sys/dev/usb/uoakrh.c352
-rw-r--r--sys/dev/usb/uoakv.c360
6 files changed, 1594 insertions, 1 deletions
diff --git a/sys/dev/usb/files.usb b/sys/dev/usb/files.usb
index 1e4000a5232..9f73c825e54 100644
--- a/sys/dev/usb/files.usb
+++ b/sys/dev/usb/files.usb
@@ -1,4 +1,4 @@
-# $OpenBSD: files.usb,v 1.101 2012/09/19 16:33:04 jsg Exp $
+# $OpenBSD: files.usb,v 1.102 2012/09/20 13:52:11 yuo 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.
@@ -138,6 +138,25 @@ device usps
attach usps at uhub
file dev/usb/usps.c usps
+# Toradex OAK common
+define uoak
+file dev/usb/uoak_subr.c
+
+# Toradex OAK series USB RH sensor
+device uoakrh: hid, uoak
+attach uoakrh at uhidbus
+file dev/usb/uoakrh.c uoakrh
+
+# Toradex OAK series USB LUX sensor
+device uoaklux: hid, uoak
+attach uoaklux at uhidbus
+file dev/usb/uoaklux.c uoaklux
+
+# Toradex OAK series USB 10V sensor
+device uoakv: hid, uoak
+attach uoakv at uhidbus
+file dev/usb/uoakv.c uoakv
+
# Misc
# Gude Expert mouseCLOCK DCF77 time signal station receiver
device udcf
diff --git a/sys/dev/usb/uoak.h b/sys/dev/usb/uoak.h
new file mode 100644
index 00000000000..615e11ab72a
--- /dev/null
+++ b/sys/dev/usb/uoak.h
@@ -0,0 +1,171 @@
+/* $OpenBSD: uoak.h,v 1.1 2012/09/20 13:52:11 yuo Exp $ */
+
+/*
+ * Copyright (c) 2012 Yojiro UO <yuo@nui.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.
+ */
+
+/* TORADEX OAK seriese sensors */
+/* http://developer.toradex.com/files/toradex-dev/uploads/media/Oak/Oak_ProgrammingGuide.pdf */
+
+/* feture request direction */
+#define OAK_SET 0x0
+#define OAK_GET 0x1
+
+/* specification */
+#define OAK_V_MAXSENSORS 8
+
+/* OAK sensor command */
+/* 1 byte commands */
+#define OAK_CMD_REPORTMODE 0x0000
+#define OAK_REPORTMODE_AFTERSAMPING 0x0 /* default */
+#define OAK_REPORTMODE_AFTERCHANGE 0x1
+#define OAK_REPORTMODE_FIXEDRATE 0x2
+#define OAK_CMD_LEDMODE 0x0001
+#define OAK_CMD_SENSORSETTING 0x0002
+/* RH */
+#define OAK_RH_SENSOR_HEATER_MASK (0x1 << 3)
+#define OAK_RH_SENSOR_RES_MASK (0x1 << 0)
+#define OAK_RH_SENSOR_HEATER_OFF 0x0 /* default */
+#define OAK_RH_SENSOR_HEATER_ON 0x1
+#define OAK_RH_SENSOR_HIGHRES 0x0 /* default */
+#define OAK_RH_SENSOR_LOWRES 0x1
+/* LUX */
+#define OAK_LUX_SENSOR_GAIN_MASK (0x1 << 4)
+#define OAK_LUX_SENSOR_LOWGAIN 0x0 /* default */
+#define OAK_LUX_SENSOR_HIGHGAIN 0x1
+#define OAK_LUX_SENSOR_INTTIME_MASK 0x3
+#define OAK_LUX_SENSOR_INTTIME_13_7ms 0x0 /* 13.7ms */
+#define OAK_LUX_SENSOR_INTTIME_101ms 0x1 /* 101 ms */
+#define OAK_LUX_SENSOR_INTTIME_402ms 0x2 /* 402 ms (default) */
+/* 10V */
+#define OAK_V_SENSOR_INPUTMODEMASK (0x1 << 0)
+#define OAK_V_SENSOR_SINGLEENDED 0x0 /* default */
+#define OAK_V_SENSOR_DIFFERENTIAL 0x1
+
+/* 2 bytes commands */
+#define OAK_CMD_REPORTRATE 0x0000
+#define OAK_CMD_SAMPLERATE 0x0001
+
+/* 21 bytes (0x15) commands */
+#define OAK_CMD_DEVNAME 0x0000
+#define OAK_CMD_CHANNAME0 0x0001
+#define OAK_CMD_CHANNAME1 0x0002
+#define OAK_CMD_CHANNAME2 0x0003
+#define OAK_CMD_CHANNAME3 0x0004
+#define OAK_CMD_CHANNAME4 0x0005
+#define OAK_CMD_CHANNAME5 0x0006
+#define OAK_CMD_CHANNAME6 0x0007
+#define OAK_CMD_CHANNAME7 0x0008
+#define OAK_CMD_CHANNAME8 0x0009
+
+/* OAK LED command */
+#define OAK_LED_OFF 0x0
+#define OAK_LED_ON 0x1
+#define OAK_LED_BLINK_SLOW 0x2
+#define OAK_LED_BLINK_FAST 0x3
+#define OAK_LED_BLINK_PULSES 0x4
+
+/* OAK config storage targets */
+enum uoak_target {
+ OAK_TARGET_RAM,
+ OAK_TARGET_FLASH,
+ OAK_TARGET_CPU,
+ OAK_TARGET_SENSOR,
+ OAK_TARGET_OTHER,
+ OAK_TARGET_MAXTYPES
+};
+
+#define OAK_RH_TARGET_MAX 2
+#define OAK_V_TARGET_MAX 2
+#define OAK_LUX_TARGET_MAX 2
+
+struct uoak_rcmd {
+ uint8_t dir;
+ uint8_t target;
+ uint8_t datasize;
+ uint16_t cmd;
+ uint8_t val[26];
+} __packed;
+
+struct uoak_config {
+ char devname[24];
+ int report_mode;
+ int report_rate;
+ int sample_rate;
+};
+
+struct uoak_methods {
+ void (*dev_print)(void *parent, enum uoak_target target);
+ void (*dev_setting)(void *parent, enum uoak_target target);
+};
+
+struct uoak_softc {
+ struct uhidev *sc_hdev;
+ void *sc_parent;
+ struct ksensordev *sc_sensordev;
+ usbd_device_handle sc_udev;
+ uint16_t sc_flag;
+ struct usb_device_info sc_udi;
+
+ /* uhidev parameters */
+ size_t sc_flen; /* feature report length */
+ size_t sc_ilen; /* input report length */
+ size_t sc_olen; /* output report length */
+ uint8_t *sc_ibuf;
+
+ /* buffers */
+ struct uoak_rcmd sc_rcmd;
+ uint8_t sc_buf[32];
+
+ /* configurations */
+ struct uoak_config sc_config[OAK_TARGET_MAXTYPES];
+
+ /* device specific methods */
+ struct uoak_methods *sc_methods;
+};
+
+
+struct uoak_sensor {
+ struct ksensor ave;
+ struct ksensor max;
+ struct ksensor min;
+ int64_t vave, vmax, vmin;
+ unsigned int count;
+};
+
+
+int uoak_check_device_ready(struct uoak_softc *);
+int uoak_set_cmd(struct uoak_softc *);
+int uoak_get_cmd(struct uoak_softc *);
+
+int uoak_get_device_name(struct uoak_softc *, enum uoak_target);
+int uoak_get_report_mode(struct uoak_softc *, enum uoak_target);
+int uoak_get_report_rate(struct uoak_softc *, enum uoak_target);
+int uoak_get_sample_rate(struct uoak_softc *, enum uoak_target);
+int uoak_set_sample_rate(struct uoak_softc *, enum uoak_target, int);
+
+int uoak_led_ctrl(struct uoak_softc *, enum uoak_target, uint8_t);
+int uoak_led_status(struct uoak_softc *, enum uoak_target, uint8_t *);
+
+void uoak_get_devinfo(struct uoak_softc *);
+void uoak_get_setting(struct uoak_softc *, enum uoak_target);
+void uoak_print_devinfo(struct uoak_softc *);
+void uoak_print_setting(struct uoak_softc *, enum uoak_target);
+
+void uoak_sensor_attach(struct uoak_softc *, struct uoak_sensor *,
+ enum sensor_type);
+void uoak_sensor_detach(struct uoak_softc *, struct uoak_sensor *);
+void uoak_sensor_update(struct uoak_sensor *, int);
+void uoak_sensor_refresh(struct uoak_sensor *, int, int);
diff --git a/sys/dev/usb/uoak_subr.c b/sys/dev/usb/uoak_subr.c
new file mode 100644
index 00000000000..7e4cc653157
--- /dev/null
+++ b/sys/dev/usb/uoak_subr.c
@@ -0,0 +1,362 @@
+/* $OpenBSD: uoak_subr.c,v 1.1 2012/09/20 13:52:11 yuo Exp $ */
+
+/*
+ * Copyright (c) 2012 Yojiro UO <yuo@nui.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.
+ */
+
+/* TORADEX OAK seriese sensors: common functions */
+/* http://developer.toradex.com/files/toradex-dev/uploads/media/Oak/Oak_ProgrammingGuide.pdf */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+#include <sys/conf.h>
+#include <sys/sensors.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/uhidev.h>
+#include <dev/usb/hid.h>
+#include "uoak.h"
+
+#define UOAK_RETRY_DELAY 100 /* 100ms, XXX too long? */
+#define UOAK_RESPONSE_DELAY 10 /* 10ms, XXX too short? */
+/*
+ * basic procedure to issue command to the OAK device.
+ * 1) check the device is ready to accept command.
+ * if a report of a FEATURE_REPORT request is not start 0xff,
+ * wait for a while, and retry till the reponse start with 0xff.
+ * 2) issue command. (set or get)
+ * 3) if the command will response, wait for a while, and issue
+ * FEATURE_REPORT. leading 0xff indicate the response is valid.
+ * if the first byte is not 0xff, retry.
+ */
+int
+uoak_check_device_ready(struct uoak_softc *sc)
+{
+ if (uhidev_get_report(sc->sc_hdev, UHID_FEATURE_REPORT,
+ &sc->sc_buf, sc->sc_flen))
+ return EIO;
+
+ if (sc->sc_buf[0] != 0xff)
+ return -1;
+
+ return 0;
+}
+
+int
+uoak_set_cmd(struct uoak_softc *sc)
+{
+ sc->sc_rcmd.dir = OAK_SET;
+
+ while (uoak_check_device_ready(sc) < 0)
+ usbd_delay_ms(sc->sc_udev, UOAK_RETRY_DELAY);
+
+ if (uhidev_set_report(sc->sc_hdev, UHID_FEATURE_REPORT,
+ &sc->sc_rcmd, sc->sc_flen))
+ return EIO;
+
+ return 0;
+}
+
+int
+uoak_get_cmd(struct uoak_softc *sc)
+{
+ sc->sc_rcmd.dir = OAK_GET;
+
+ /* check the device is ready to request */
+ while (uoak_check_device_ready(sc) < 0)
+ usbd_delay_ms(sc->sc_udev, UOAK_RETRY_DELAY);
+
+ /* issue request */
+ if (uhidev_set_report(sc->sc_hdev, UHID_FEATURE_REPORT,
+ &sc->sc_rcmd, sc->sc_flen))
+ return EIO;
+
+ /* wait till the device ready to return the request */
+ while (uoak_check_device_ready(sc) < 0)
+ usbd_delay_ms(sc->sc_udev, UOAK_RESPONSE_DELAY);
+
+ return 0;
+}
+
+/*
+ * Functions to access device configurations.
+ * OAK sensor have some storages to store its configuration.
+ * (RAM, FLASH and others)
+ */
+int
+uoak_get_device_name(struct uoak_softc *sc, enum uoak_target target)
+{
+ memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
+ sc->sc_rcmd.target = target;
+ sc->sc_rcmd.datasize = 0x15;
+ USETW(&sc->sc_rcmd.cmd, OAK_CMD_DEVNAME);
+
+ if (uoak_get_cmd(sc) < 0)
+ return EIO;
+
+ strlcpy(sc->sc_config[target].devname, sc->sc_buf+1,
+ sizeof(sc->sc_config[target].devname));
+ return 0;
+}
+
+int
+uoak_get_report_mode(struct uoak_softc *sc, enum uoak_target target)
+{
+ memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
+ sc->sc_rcmd.target = target;
+ sc->sc_rcmd.datasize = 0x1;
+ USETW(&sc->sc_rcmd.cmd, OAK_CMD_REPORTMODE);
+
+ if (uoak_get_cmd(sc) < 0)
+ return EIO;
+
+ sc->sc_config[target].report_mode = sc->sc_buf[1];
+ return 0;
+}
+
+int
+uoak_get_report_rate(struct uoak_softc *sc, enum uoak_target target)
+{
+ uint16_t result;
+ memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
+ sc->sc_rcmd.target = target;
+ sc->sc_rcmd.datasize = 0x2;
+ USETW(&sc->sc_rcmd.cmd, OAK_CMD_REPORTRATE);
+
+ if (uoak_get_cmd(sc) < 0)
+ return EIO;
+
+ result = (sc->sc_buf[2] << 8) + sc->sc_buf[1];
+ sc->sc_config[target].report_rate = result;
+
+ return 0;
+}
+
+int
+uoak_get_sample_rate(struct uoak_softc *sc, enum uoak_target target)
+{
+ uint16_t result;
+ memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
+ sc->sc_rcmd.target = target;
+ sc->sc_rcmd.datasize = 0x2;
+ USETW(&sc->sc_rcmd.cmd, OAK_CMD_SAMPLERATE);
+
+ if (uoak_get_cmd(sc) < 0)
+ return EIO;
+
+ result = (sc->sc_buf[2] << 8) + sc->sc_buf[1];
+ sc->sc_config[target].sample_rate = result;
+
+ return 0;
+}
+
+int
+uoak_set_sample_rate(struct uoak_softc *sc, enum uoak_target target, int rate)
+{
+ memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
+ sc->sc_rcmd.target = target;
+ sc->sc_rcmd.datasize = 0x2;
+ USETW(&sc->sc_rcmd.cmd, OAK_CMD_SAMPLERATE);
+
+#if 0
+ sc->sc_rcmd.val[0] = (uint8_t)(rate & 0xff);
+ sc->sc_rcmd.val[1] = (uint8_t)((rate >> 8) & 0xff)
+#else
+ USETW(&sc->sc_rcmd.val, rate);
+#endif
+
+ if (uoak_set_cmd(sc) < 0)
+ return EIO;
+
+ return 0;
+}
+
+/*
+ * LED I/O
+ */
+int
+uoak_led_status(struct uoak_softc *sc, enum uoak_target target, uint8_t *mode)
+{
+ memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
+ sc->sc_rcmd.target = target;
+ sc->sc_rcmd.datasize = 0x1;
+ USETW(&sc->sc_rcmd.cmd, OAK_CMD_LEDMODE);
+
+ if (uoak_get_cmd(sc) < 0)
+ return EIO;
+
+ *mode = sc->sc_buf[1];
+ return 0;
+}
+
+int
+uoak_led_ctrl(struct uoak_softc *sc, enum uoak_target target, uint8_t mode)
+{
+ memset(&sc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
+
+ sc->sc_rcmd.target = target;
+ sc->sc_rcmd.datasize = 0x1;
+ USETW(&sc->sc_rcmd.cmd, OAK_CMD_LEDMODE);
+ sc->sc_rcmd.val[0] = mode;
+
+ return uoak_set_cmd(sc);
+}
+
+/* device setting: query and pretty print */
+void
+uoak_get_devinfo(struct uoak_softc *sc)
+{
+ /* get device serial# */
+ usbd_fill_deviceinfo(sc->sc_udev, &sc->sc_udi, 1);
+}
+
+void
+uoak_get_setting(struct uoak_softc *sc, enum uoak_target target)
+{
+ /* get device lavel */
+ (void)uoak_get_device_name(sc, target);
+
+ /* get global sensor configuration */
+ (void)uoak_get_report_mode(sc, target);
+ (void)uoak_get_sample_rate(sc, target);
+ (void)uoak_get_report_rate(sc, target);
+
+ /* get device spcecific information */
+ if (sc->sc_methods->dev_setting != NULL)
+ sc->sc_methods->dev_setting(sc->sc_parent, target);
+}
+
+void
+uoak_print_devinfo(struct uoak_softc *sc)
+{
+ printf(", serial#:%s", sc->sc_udi.udi_serial);
+ printf("\n");
+}
+
+void
+uoak_print_setting(struct uoak_softc *sc, enum uoak_target target)
+{
+ printf(" device label:%s", sc->sc_config[target].devname);
+ printf(", report mode:");
+ switch(sc->sc_config[target].report_mode) {
+ case OAK_REPORTMODE_AFTERSAMPING:
+ printf("after sampling (rate:%d ms).",
+ sc->sc_config[target].sample_rate);
+ break;
+ case OAK_REPORTMODE_AFTERCHANGE:
+ printf("after change.");
+ break;
+ case OAK_REPORTMODE_FIXEDRATE:
+ printf("fixed rate (rate:%d ms).",
+ sc->sc_config[target].report_rate);
+ break;
+ default:
+ printf("unknown.");
+ break;
+ }
+ printf("\n");
+
+ /* print device spcecific information */
+ if (sc->sc_methods->dev_print != NULL)
+ sc->sc_methods->dev_print(sc->sc_parent, target);
+}
+
+void
+uoak_sensor_attach(struct uoak_softc *sc, struct uoak_sensor *s,
+ enum sensor_type type)
+{
+ if (s == NULL)
+ return;
+
+ s->ave.type = type;
+ s->max.type = type;
+ s->min.type = type;
+ s->ave.flags |= SENSOR_FINVALID;
+ s->max.flags |= SENSOR_FINVALID;
+ s->min.flags |= SENSOR_FINVALID;
+
+ (void)snprintf(s->ave.desc, sizeof(s->ave.desc),
+ "ave(#%s)", sc->sc_udi.udi_serial);
+ (void)snprintf(s->max.desc, sizeof(s->max.desc),
+ "max(#%s)", sc->sc_udi.udi_serial);
+ (void)snprintf(s->min.desc, sizeof(s->min.desc),
+ "min(#%s)", sc->sc_udi.udi_serial);
+
+ sensor_attach(sc->sc_sensordev, &s->ave);
+ sensor_attach(sc->sc_sensordev, &s->max);
+ sensor_attach(sc->sc_sensordev, &s->min);
+}
+
+void
+uoak_sensor_detach(struct uoak_softc *sc, struct uoak_sensor *s)
+{
+ if (s == NULL)
+ return;
+
+ sensor_attach(sc->sc_sensordev, &s->ave);
+ sensor_attach(sc->sc_sensordev, &s->max);
+ sensor_attach(sc->sc_sensordev, &s->min);
+}
+
+void
+uoak_sensor_update(struct uoak_sensor *s, int val)
+{
+ if (s == NULL)
+ return;
+
+ /* reset */
+ if (s->count == 0) {
+ s->vmax = s->vmin = s->vave = val;
+ s->count++;
+ return;
+ }
+
+ /* update min/max */
+ if (val > s->vmax)
+ s->vmax = val;
+ else if (val < s->vmin)
+ s->vmin = val;
+
+ /* calc avelage */
+ s->vave = (s->vave * s->count + val) / (s->count + 1);
+
+ s->count++;
+}
+
+void
+uoak_sensor_refresh(struct uoak_sensor *s, int mag, int offset)
+{
+ if (s == NULL)
+ return;
+ /* update value */
+ s->ave.value = s->vave * mag + offset;
+ s->max.value = s->vmax * mag + offset;
+ s->min.value = s->vmin * mag + offset;
+
+ /* update flag */
+ s->ave.flags &= ~SENSOR_FINVALID;
+ s->max.flags &= ~SENSOR_FINVALID;
+ s->min.flags &= ~SENSOR_FINVALID;
+ s->count = 0;
+}
+
diff --git a/sys/dev/usb/uoaklux.c b/sys/dev/usb/uoaklux.c
new file mode 100644
index 00000000000..58c4a460ff7
--- /dev/null
+++ b/sys/dev/usb/uoaklux.c
@@ -0,0 +1,329 @@
+/* $OpenBSD: uoaklux.c,v 1.1 2012/09/20 13:52:11 yuo Exp $ */
+
+/*
+ * Copyright (c) 2012 Yojiro UO <yuo@nui.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.
+ */
+
+/* TORADEX OAK seriese sensors: lux sensor driver */
+/* http://developer.toradex.com/files/toradex-dev/uploads/media/Oak/Oak_ProgrammingGuide.pdf */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+#include <sys/conf.h>
+#include <sys/sensors.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/uhidev.h>
+#include <dev/usb/hid.h>
+#include "uoak.h"
+
+#ifdef USB_DEBUG
+#define UOAKLUX_DEBUG
+#endif
+
+#ifdef UOAKLUX_DEBUG
+int uoakluxdebug = 0;
+#define DPRINTFN(n, x) do { if (uoakluxdebug > (n)) printf x; } while (0)
+#else
+#define DPRINTFN(n, x)
+#endif
+
+#define DPRINTF(x) DPRINTFN(0, x)
+
+#define UOAKLUX_SAMPLE_RATE 200 /* ms */
+#define UOAKLUX_REFRESH_PERIOD 5 /* 5 sec : 0.2Hz */
+
+struct uoaklux_sensor {
+ struct uoak_sensor lux;
+ /* lux sensor setting */
+ uint8_t gain;
+ int inttime;
+
+};
+
+struct uoaklux_softc {
+ struct uhidev sc_hdev;
+ u_char sc_dying;
+
+ /* uoak common */
+ struct uoak_softc sc_uoak_softc;
+
+ /* sensor framework */
+ struct uoaklux_sensor sc_sensor;
+ struct ksensordev sc_sensordev;
+ struct sensor_task *sc_sensortask;
+};
+
+const struct usb_devno uoaklux_devs[] = {
+ { USB_VENDOR_TORADEX, USB_PRODUCT_TORADEX_LUX},
+};
+#define uoaklux_lookup(v, p) usb_lookup(uoaklux_devs, v, p)
+
+int uoaklux_match(struct device *, void *, void *);
+void uoaklux_attach(struct device *, struct device *, void *);
+int uoaklux_detach(struct device *, int);
+int uoaklux_activate(struct device *, int);
+
+void uoaklux_intr(struct uhidev *, void *, u_int);
+void uoaklux_refresh(void *);
+
+int uoaklux_get_sensor_setting(struct uoaklux_softc *, enum uoak_target);
+
+void uoaklux_dev_setting(void *, enum uoak_target);
+void uoaklux_dev_print(void *, enum uoak_target);
+
+
+struct cfdriver uoaklux_cd = {
+ NULL, "uoaklux", DV_DULL
+};
+
+const struct cfattach uoaklux_ca = {
+ sizeof(struct uoaklux_softc),
+ uoaklux_match,
+ uoaklux_attach,
+ uoaklux_detach,
+ uoaklux_activate,
+};
+
+struct uoak_methods uoaklux_methods = {
+ uoaklux_dev_print,
+ uoaklux_dev_setting
+};
+
+
+int
+uoaklux_match(struct device *parent, void *match, void *aux)
+{
+ struct usb_attach_arg *uaa = aux;
+ struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
+
+ if (uoaklux_lookup(uha->uaa->vendor, uha->uaa->product) == NULL)
+ return UMATCH_NONE;
+
+ return (UMATCH_VENDOR_PRODUCT);
+}
+
+void
+uoaklux_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct uoaklux_softc *sc = (struct uoaklux_softc *)self;
+ struct usb_attach_arg *uaa = aux;
+ struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
+ usbd_device_handle dev = uha->parent->sc_udev;
+
+ struct uoak_softc *scc = &sc->sc_uoak_softc;
+ int err, size, repid;
+ void *desc;
+
+ sc->sc_hdev.sc_intr = uoaklux_intr;
+ sc->sc_hdev.sc_parent = uha->parent;
+ sc->sc_hdev.sc_report_id = uha->reportid;
+
+ scc->sc_parent = sc;
+ scc->sc_udev = dev;
+ scc->sc_hdev = &sc->sc_hdev;
+ scc->sc_methods = &uoaklux_methods;
+ scc->sc_sensordev = &sc->sc_sensordev;
+
+ uhidev_get_report_desc(uha->parent, &desc, &size);
+ repid = uha->reportid;
+ scc->sc_ilen = hid_report_size(desc, size, hid_input, repid);
+ scc->sc_olen = hid_report_size(desc, size, hid_output, repid);
+ scc->sc_flen = hid_report_size(desc, size, hid_feature, repid);
+
+ /*device initialize */
+ (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_ON);
+ err = uoak_set_sample_rate(scc, OAK_TARGET_RAM, UOAKLUX_SAMPLE_RATE);
+ if (err) {
+ printf("%s: could not set sampling rate. exit\n",
+ sc->sc_hdev.sc_dev.dv_xname);
+ return;
+ }
+
+ /* query and print device setting */
+ uoak_get_devinfo(scc);
+ uoak_print_devinfo(scc);
+
+ DPRINTF((" config in RAM\n"));
+ uoak_get_setting(scc, OAK_TARGET_RAM);
+ uoak_print_setting(scc, OAK_TARGET_RAM);
+#ifdef UOAKLUX_DEBUG
+ DPRINTF((" config in FLASh\n"));
+ uoak_get_setting(scc, OAK_TARGET_FLASH);
+ uoak_print_setting(scc, OAK_TARGET_FLASH);
+#endif
+
+ /* attach sensor */
+ strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname,
+ sizeof(sc->sc_sensordev.xname));
+ uoak_sensor_attach(scc, &sc->sc_sensor.lux, SENSOR_LUX);
+
+ /* start sensor */
+ sc->sc_sensortask = sensor_task_register(sc, uoaklux_refresh,
+ UOAKLUX_REFRESH_PERIOD);
+ if (sc->sc_sensortask == NULL) {
+ printf(", unable to register update task\n");
+ return;
+ }
+ sensordev_install(&sc->sc_sensordev);
+
+ err = uhidev_open(&sc->sc_hdev);
+ if (err) {
+ printf("%s: could not open interrupt pipe, quit\n",
+ sc->sc_hdev.sc_dev.dv_xname);
+ return;
+ }
+ scc->sc_ibuf = malloc(scc->sc_ilen, M_USBDEV, M_WAITOK);
+
+ DPRINTF(("uoaklux_attach: complete\n"));
+}
+
+
+int
+uoaklux_detach(struct device *self, int flags)
+{
+ struct uoaklux_softc *sc = (struct uoaklux_softc *)self;
+ struct uoak_softc *scc = &sc->sc_uoak_softc;
+ int rv = 0;
+
+ wakeup(&sc->sc_sensortask);
+ sensordev_deinstall(&sc->sc_sensordev);
+
+ uoak_sensor_detach(scc, &sc->sc_sensor.lux);
+
+ if (sc->sc_sensortask != NULL)
+ sensor_task_unregister(sc->sc_sensortask);
+
+ if (scc->sc_ibuf != NULL) {
+ free(scc->sc_ibuf, M_USBDEV);
+ scc->sc_ibuf = NULL;
+ }
+
+ return (rv);
+}
+
+int
+uoaklux_activate(struct device *self, int act)
+{
+ struct uoaklux_softc *sc = (struct uoaklux_softc *)self;
+
+ switch (act) {
+ case DVACT_DEACTIVATE:
+ sc->sc_dying = 1;
+ break;
+ }
+ return (0);
+}
+
+void
+uoaklux_intr(struct uhidev *addr, void *ibuf, u_int len)
+{
+ struct uoaklux_softc *sc = (struct uoaklux_softc *)addr;
+ struct uoak_softc *scc = &sc->sc_uoak_softc;
+ int frame, val;
+
+ if (sc->sc_dying)
+ return;
+ if (scc->sc_ibuf == NULL)
+ return;
+
+ memcpy(scc->sc_ibuf, ibuf, len);
+ frame = (scc->sc_ibuf[1] << 8) + (scc->sc_ibuf[0]);
+ val = (scc->sc_ibuf[3] << 8) + (scc->sc_ibuf[2]);
+ uoak_sensor_update(&sc->sc_sensor.lux, val);
+}
+
+void
+uoaklux_refresh(void *arg)
+{
+ struct uoaklux_softc *sc = arg;
+ struct uoak_softc *scc = &sc->sc_uoak_softc;
+ uint8_t led;
+
+ /* blink LED for each cycle */
+ if (uoak_led_status(scc, OAK_TARGET_RAM, &led) < 0)
+ DPRINTF(("status query error\n"));
+ if (led == OAK_LED_OFF)
+ (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_ON);
+ else
+ (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_OFF);
+
+ uoak_sensor_refresh(&sc->sc_sensor.lux, 1000000, 0);
+}
+
+int
+uoaklux_get_sensor_setting(struct uoaklux_softc *sc, enum uoak_target target)
+{
+ struct uoak_softc *scc = &sc->sc_uoak_softc;
+ uint8_t result;
+
+ memset(&scc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
+ scc->sc_rcmd.target = target;
+ scc->sc_rcmd.datasize = 0x1;
+ USETW(&scc->sc_rcmd.cmd, OAK_CMD_SENSORSETTING);
+
+ if (uoak_get_cmd(scc) < 0)
+ return EIO;
+
+ result = scc->sc_buf[1];
+
+ sc->sc_sensor.gain = ((result & OAK_LUX_SENSOR_GAIN_MASK) >> 3);
+ sc->sc_sensor.inttime = (result & OAK_LUX_SENSOR_INTTIME_MASK);
+
+ return 0;
+}
+
+/* device specific functions */
+void
+uoaklux_dev_setting(void *parent, enum uoak_target target)
+{
+ struct uoaklux_softc *sc = (struct uoaklux_softc *)parent;
+
+ /* get device specific configuration */
+ (void)uoaklux_get_sensor_setting(sc, target);
+}
+
+void
+uoaklux_dev_print(void *parent, enum uoak_target target)
+{
+ struct uoaklux_softc *sc = (struct uoaklux_softc *)parent;
+
+ printf(" sensor gain:%s", (sc->sc_sensor.gain ? "HIGH" : "LOW"));
+ printf(", integration time:");
+ switch(sc->sc_sensor.inttime) {
+ case OAK_LUX_SENSOR_INTTIME_13_7ms:
+ printf("13.7ms");
+ break;
+ case OAK_LUX_SENSOR_INTTIME_101ms:
+ printf("101ms");
+ break;
+ case OAK_LUX_SENSOR_INTTIME_402ms:
+ printf("402ms");
+ break;
+ default:
+ printf("unknown");
+ break;
+ }
+
+ printf("\n");
+}
diff --git a/sys/dev/usb/uoakrh.c b/sys/dev/usb/uoakrh.c
new file mode 100644
index 00000000000..f10d71eb74b
--- /dev/null
+++ b/sys/dev/usb/uoakrh.c
@@ -0,0 +1,352 @@
+/* $OpenBSD: uoakrh.c,v 1.1 2012/09/20 13:52:11 yuo Exp $ */
+
+/*
+ * Copyright (c) 2012 Yojiro UO <yuo@nui.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.
+ */
+
+/* TORADEX OAK seriese sensors: Temperature/Humidity sensor driver */
+/* http://developer.toradex.com/files/toradex-dev/uploads/media/Oak/Oak_ProgrammingGuide.pdf */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+#include <sys/conf.h>
+#include <sys/sensors.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/uhidev.h>
+#include <dev/usb/hid.h>
+#include "uoak.h"
+
+#ifdef USB_DEBUG
+#define OARKRH_DEBUG
+#endif
+
+#ifdef OARKRH_DEBUG
+int uoakrhdebug = 0;
+#define DPRINTFN(n, x) do { if (uoakrhdebug > (n)) printf x; } while (0)
+#else
+#define DPRINTFN(n, x)
+#endif
+
+#define DPRINTF(x) DPRINTFN(0, x)
+
+#define UOAKRH_SAMPLE_RATE 200 /* ms */
+#define UOAKRH_REFRESH_PERIOD 10 /* 10 sec : 0.1Hz */
+
+struct uoakrh_sensor {
+ struct ksensor temp;
+ struct ksensor humi;
+ int count;
+ int tempval, humival;
+ int resolution;
+};
+
+struct uoakrh_softc {
+ struct uhidev sc_hdev;
+ u_char sc_dying;
+
+ /* uoak common */
+ struct uoak_softc sc_uoak_softc;
+
+ /* sensor framework */
+ struct uoakrh_sensor sc_sensor;
+ struct ksensordev sc_sensordev;
+ struct sensor_task *sc_sensortask;
+
+ /* sensor setting */
+ int sc_rh_heater;
+};
+
+const struct usb_devno uoakrh_devs[] = {
+ { USB_VENDOR_TORADEX, USB_PRODUCT_TORADEX_RH},
+};
+#define uoakrh_lookup(v, p) usb_lookup(uoakrh_devs, v, p)
+
+int uoakrh_match(struct device *, void *, void *);
+void uoakrh_attach(struct device *, struct device *, void *);
+int uoakrh_detach(struct device *, int);
+int uoakrh_activate(struct device *, int);
+
+void uoakrh_intr(struct uhidev *, void *, u_int);
+void uoakrh_refresh(void *);
+
+int uoakrh_get_sensor_setting(struct uoakrh_softc *, enum uoak_target);
+
+void uoakrh_dev_setting(void *, enum uoak_target);
+void uoakrh_dev_print(void *, enum uoak_target);
+
+
+struct cfdriver uoakrh_cd = {
+ NULL, "uoakrh", DV_DULL
+};
+
+const struct cfattach uoakrh_ca = {
+ sizeof(struct uoakrh_softc),
+ uoakrh_match,
+ uoakrh_attach,
+ uoakrh_detach,
+ uoakrh_activate,
+};
+
+struct uoak_methods uoakrh_methods = {
+ uoakrh_dev_print,
+ uoakrh_dev_setting
+};
+
+
+int
+uoakrh_match(struct device *parent, void *match, void *aux)
+{
+ struct usb_attach_arg *uaa = aux;
+ struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
+
+ if (uoakrh_lookup(uha->uaa->vendor, uha->uaa->product) == NULL)
+ return UMATCH_NONE;
+
+ return (UMATCH_VENDOR_PRODUCT);
+}
+
+void
+uoakrh_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct uoakrh_softc *sc = (struct uoakrh_softc *)self;
+ struct usb_attach_arg *uaa = aux;
+ struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
+ usbd_device_handle dev = uha->parent->sc_udev;
+
+ struct uoak_softc *scc = &sc->sc_uoak_softc;
+ int err, size, repid;
+ void *desc;
+
+ sc->sc_hdev.sc_intr = uoakrh_intr;
+ sc->sc_hdev.sc_parent = uha->parent;
+ sc->sc_hdev.sc_report_id = uha->reportid;
+
+ scc->sc_parent = sc;
+ scc->sc_udev = dev;
+ scc->sc_hdev = &sc->sc_hdev;
+ scc->sc_methods = &uoakrh_methods;
+ scc->sc_sensordev = &sc->sc_sensordev;
+
+ uhidev_get_report_desc(uha->parent, &desc, &size);
+ repid = uha->reportid;
+ scc->sc_ilen = hid_report_size(desc, size, hid_input, repid);
+ scc->sc_olen = hid_report_size(desc, size, hid_output, repid);
+ scc->sc_flen = hid_report_size(desc, size, hid_feature, repid);
+
+ /* device initialize */
+ (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_ON);
+ err = uoak_set_sample_rate(scc, OAK_TARGET_RAM, UOAKRH_SAMPLE_RATE);
+ if (err) {
+ printf("%s: could not set sampling rate. exit\n",
+ sc->sc_hdev.sc_dev.dv_xname);
+ return;
+ }
+
+ /* query and print device setting */
+ uoak_get_devinfo(scc);
+ uoak_print_devinfo(scc);
+
+ DPRINTF((" config in RAM\n"));
+ uoak_get_setting(scc, OAK_TARGET_RAM);
+ uoak_print_setting(scc, OAK_TARGET_RAM);
+#ifdef UOAKV_DEBUG
+ DPRINTF((" config in FLASH\n"));
+ uoak_get_setting(scc, OAK_TARGET_FLASH);
+ uoak_print_setting(scc, OAK_TARGET_FLASH);
+#endif
+
+ /* attach sensor */
+ strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname,
+ sizeof(sc->sc_sensordev.xname));
+ sc->sc_sensor.temp.type = SENSOR_TEMP;
+ sc->sc_sensor.humi.type = SENSOR_HUMIDITY;
+ sc->sc_sensor.temp.flags |= SENSOR_FINVALID;
+ sc->sc_sensor.humi.flags |= SENSOR_FINVALID;
+
+ /* add label with sensor serial# */
+ (void)snprintf(sc->sc_sensor.temp.desc, sizeof(sc->sc_sensor.temp.desc),
+ "Temp.(#%s)", scc->sc_udi.udi_serial);
+ (void)snprintf(sc->sc_sensor.humi.desc, sizeof(sc->sc_sensor.humi.desc),
+ "\%RH(#%s)", scc->sc_udi.udi_serial);
+ sensor_attach(&sc->sc_sensordev, &sc->sc_sensor.temp);
+ sensor_attach(&sc->sc_sensordev, &sc->sc_sensor.humi);
+
+ /* start sensor */
+ sc->sc_sensortask = sensor_task_register(sc, uoakrh_refresh,
+ UOAKRH_REFRESH_PERIOD);
+ if (sc->sc_sensortask == NULL) {
+ printf(", unable to register update task\n");
+ return;
+ }
+ sensordev_install(&sc->sc_sensordev);
+
+ err = uhidev_open(&sc->sc_hdev);
+ if (err) {
+ printf("%s: could not open interrupt pipe, quit\n",
+ sc->sc_hdev.sc_dev.dv_xname);
+ return;
+ }
+ scc->sc_ibuf = malloc(scc->sc_ilen, M_USBDEV, M_WAITOK);
+
+ DPRINTF(("uoakrh_attach: complete\n"));
+}
+
+int
+uoakrh_detach(struct device *self, int flags)
+{
+ struct uoakrh_softc *sc = (struct uoakrh_softc *)self;
+ struct uoak_softc *scc = &sc->sc_uoak_softc;
+ int rv = 0;
+
+ wakeup(&sc->sc_sensortask);
+ sensordev_deinstall(&sc->sc_sensordev);
+
+ if (&sc->sc_sensordev != NULL) {
+ sensor_detach(&sc->sc_sensordev, &sc->sc_sensor.temp);
+ sensor_detach(&sc->sc_sensordev, &sc->sc_sensor.humi);
+ }
+
+ if (sc->sc_sensortask != NULL)
+ sensor_task_unregister(sc->sc_sensortask);
+
+ if (scc->sc_ibuf != NULL) {
+ free(scc->sc_ibuf, M_USBDEV);
+ scc->sc_ibuf = NULL;
+ }
+
+ return (rv);
+}
+
+int
+uoakrh_activate(struct device *self, int act)
+{
+ struct uoakrh_softc *sc = (struct uoakrh_softc *)self;
+
+ switch (act) {
+ case DVACT_DEACTIVATE:
+ sc->sc_dying = 1;
+ break;
+ }
+ return (0);
+}
+
+void
+uoakrh_intr(struct uhidev *addr, void *ibuf, u_int len)
+{
+ struct uoakrh_softc *sc = (struct uoakrh_softc *)addr;
+ struct uoakrh_sensor *s = &sc->sc_sensor;
+ struct uoak_softc *scc = &sc->sc_uoak_softc;
+ int frame, temp, humi;
+
+ if (sc->sc_dying)
+ return;
+ if (scc->sc_ibuf == NULL)
+ return;
+
+ memcpy(scc->sc_ibuf, ibuf, len);
+ frame = (scc->sc_ibuf[1] << 8) + (scc->sc_ibuf[0]);
+ humi = (scc->sc_ibuf[3] << 8) + (scc->sc_ibuf[2]);
+ temp = (scc->sc_ibuf[5] << 8) + (scc->sc_ibuf[4]);
+
+ if (s->count == 0) {
+ s->tempval = temp;
+ s->humival = humi;
+ };
+
+ /* calculate average value */
+ s->tempval = (s->tempval * s->count + temp) / (s->count + 1);
+ s->humival = (s->humival * s->count + humi) / (s->count + 1);
+
+ s->count++;
+}
+
+void
+uoakrh_refresh(void *arg)
+{
+ struct uoakrh_softc *sc = arg;
+ struct uoakrh_sensor *s = &sc->sc_sensor;
+ struct uoak_softc *scc = &sc->sc_uoak_softc;
+ uint8_t led;
+
+ /* blink LED for each cycle */
+ if (uoak_led_status(scc, OAK_TARGET_RAM, &led) < 0)
+ DPRINTF(("status query error\n"));
+ if (led == OAK_LED_OFF)
+ (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_ON);
+ else
+ (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_OFF);
+
+ /* update sensor value */
+ s->temp.value = (uint64_t)(s->tempval) * 10000;
+ s->humi.value = (uint64_t)(s->humival) * 10;
+ s->temp.flags &= ~SENSOR_FINVALID;
+ s->humi.flags &= ~SENSOR_FINVALID;
+ s->count = 0;
+}
+
+
+int
+uoakrh_get_sensor_setting(struct uoakrh_softc *sc, enum uoak_target target)
+{
+ uint8_t result;
+ struct uoak_softc *scc = &sc->sc_uoak_softc;
+
+ memset(&scc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
+ scc->sc_rcmd.target = target;
+ scc->sc_rcmd.datasize = 0x1;
+ USETW(&scc->sc_rcmd.cmd, OAK_CMD_SENSORSETTING);
+
+ if (uoak_get_cmd(scc) < 0)
+ return EIO;
+
+ result = scc->sc_buf[1];
+ sc->sc_sensor.resolution = (result & OAK_RH_SENSOR_RES_MASK);
+ sc->sc_rh_heater = (result & OAK_RH_SENSOR_HEATER_MASK) >> 2;
+
+ return 0;
+}
+
+/* device specific functions */
+void
+uoakrh_dev_setting(void *parent, enum uoak_target target)
+{
+ struct uoakrh_softc *sc = (struct uoakrh_softc *)parent;
+
+ /* get device specific configuration */
+ (void)uoakrh_get_sensor_setting(sc, target);
+}
+
+void
+uoakrh_dev_print(void *parent, enum uoak_target target)
+{
+ struct uoakrh_softc *sc = (struct uoakrh_softc *)parent;
+
+
+ printf(" Sensor resolution: %s",
+ (sc->sc_sensor.resolution ? "8bit RH/12 bit" : "12bit RH/14bit"));
+ printf(", Heater: %s", (sc->sc_rh_heater ? "ON" : "OFF"));
+ printf("\n");
+}
+
+
diff --git a/sys/dev/usb/uoakv.c b/sys/dev/usb/uoakv.c
new file mode 100644
index 00000000000..4dccd5e9dec
--- /dev/null
+++ b/sys/dev/usb/uoakv.c
@@ -0,0 +1,360 @@
+/* $OpenBSD: uoakv.c,v 1.1 2012/09/20 13:52:11 yuo Exp $ */
+
+/*
+ * Copyright (c) 2012 Yojiro UO <yuo@nui.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.
+ */
+
+/* TORADEX OAK seriese sensors: 8channel +/-10V ADC driver */
+/* http://developer.toradex.com/files/toradex-dev/uploads/media/Oak/Oak_ProgrammingGuide.pdf */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+#include <sys/conf.h>
+#include <sys/sensors.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/uhidev.h>
+#include <dev/usb/hid.h>
+#include "uoak.h"
+
+#ifdef USB_DEBUG
+#define UOAKV_DEBUG
+#endif
+
+#ifdef UOAKV_DEBUG
+int uoakvdebug = 0;
+#define DPRINTFN(n, x) do { if (uoakvdebug > (n)) printf x; } while (0)
+#else
+#define DPRINTFN(n, x)
+#endif
+
+#define DPRINTF(x) DPRINTFN(0, x)
+
+#define UOAKV_SAMPLE_RATE 100 /* ms */
+#define UOAKV_REFRESH_PERIOD 1 /* 1 sec : 1Hz */
+
+struct uoakv_sensor {
+ struct uoak_sensor v;
+ /* ADC setting */
+ unsigned int offset[OAK_V_TARGET_MAX]; /* absolute offset (mV) */
+};
+
+struct uoakv_softc {
+ struct uhidev sc_hdev;
+ u_char sc_dying;
+
+ /* uoak common */
+ struct uoak_softc sc_uoak_softc;
+
+ /* sensor framework */
+ struct uoakv_sensor sc_sensor[OAK_V_MAXSENSORS];
+ struct ksensordev sc_sensordev;
+ struct sensor_task *sc_sensortask;
+
+ /* sensor setting */
+ int sc_inputmode[OAK_V_TARGET_MAX];
+
+};
+
+const struct usb_devno uoakv_devs[] = {
+ { USB_VENDOR_TORADEX, USB_PRODUCT_TORADEX_10V},
+};
+#define uoakv_lookup(v, p) usb_lookup(uoakv_devs, v, p)
+
+int uoakv_match(struct device *, void *, void *);
+void uoakv_attach(struct device *, struct device *, void *);
+int uoakv_detach(struct device *, int);
+int uoakv_activate(struct device *, int);
+
+void uoakv_intr(struct uhidev *, void *, u_int);
+void uoakv_refresh(void *);
+
+int uoakv_get_channel_setting(struct uoakv_softc *, enum uoak_target, int);
+int uoakv_get_sensor_setting(struct uoakv_softc *, enum uoak_target);
+
+void uoakv_dev_setting(void *, enum uoak_target);
+void uoakv_dev_print(void *, enum uoak_target);
+
+
+struct cfdriver uoakv_cd = {
+ NULL, "uoakv", DV_DULL
+};
+
+const struct cfattach uoakv_ca = {
+ sizeof(struct uoakv_softc),
+ uoakv_match,
+ uoakv_attach,
+ uoakv_detach,
+ uoakv_activate,
+
+};
+
+struct uoak_methods uoakv_methods = {
+ uoakv_dev_print,
+ uoakv_dev_setting
+};
+
+int
+uoakv_match(struct device *parent, void *match, void *aux)
+{
+ struct usb_attach_arg *uaa = aux;
+ struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
+
+ if (uoakv_lookup(uha->uaa->vendor, uha->uaa->product) == NULL)
+ return UMATCH_NONE;
+
+ return (UMATCH_VENDOR_PRODUCT);
+}
+
+void
+uoakv_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct uoakv_softc *sc = (struct uoakv_softc *)self;
+ struct usb_attach_arg *uaa = aux;
+ struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
+ usbd_device_handle dev = uha->parent->sc_udev;
+
+ struct uoak_softc *scc = &sc->sc_uoak_softc;
+ int i, err, size, repid;
+ void *desc;
+
+ sc->sc_hdev.sc_intr = uoakv_intr;
+ sc->sc_hdev.sc_parent = uha->parent;
+ sc->sc_hdev.sc_report_id = uha->reportid;
+
+ scc->sc_parent = sc;
+ scc->sc_udev = dev;
+ scc->sc_hdev = &sc->sc_hdev;
+ scc->sc_methods = &uoakv_methods;
+ scc->sc_sensordev = &sc->sc_sensordev;
+
+ uhidev_get_report_desc(uha->parent, &desc, &size);
+ repid = uha->reportid;
+ scc->sc_ilen = hid_report_size(desc, size, hid_input, repid);
+ scc->sc_olen = hid_report_size(desc, size, hid_output, repid);
+ scc->sc_flen = hid_report_size(desc, size, hid_feature, repid);
+
+ /* device initialize */
+ (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_ON);
+ err = uoak_set_sample_rate(scc, OAK_TARGET_RAM, UOAKV_SAMPLE_RATE);
+ if (err) {
+ printf("%s: could not set sampling rate. exit\n",
+ sc->sc_hdev.sc_dev.dv_xname);
+ return;
+ }
+
+ /* query and print device setting */
+ uoak_get_devinfo(scc);
+ uoak_print_devinfo(scc);
+
+ DPRINTF((" config in RAM\n"));
+ uoak_get_setting(scc, OAK_TARGET_RAM);
+ uoak_print_setting(scc, OAK_TARGET_RAM);
+#ifdef UOAKV_DEBUG
+ DPRINTF((" config in FRASH\n"));
+ uoak_get_setting(scc, OAK_TARGET_FLASH);
+ uoak_print_setting(scc, OAK_TARGET_FLASH);
+#endif
+
+ /* attach sensor */
+ strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname,
+ sizeof(sc->sc_sensordev.xname));
+ for (i = 0; i < OAK_V_MAXSENSORS; i++)
+ uoak_sensor_attach(scc, &sc->sc_sensor[i].v, SENSOR_VOLTS_DC);
+
+ /* start sensor */
+ sc->sc_sensortask = sensor_task_register(sc, uoakv_refresh,
+ UOAKV_REFRESH_PERIOD);
+ if (sc->sc_sensortask == NULL) {
+ printf(", unable to register update task\n");
+ return;
+ }
+ sensordev_install(&sc->sc_sensordev);
+
+ err = uhidev_open(&sc->sc_hdev);
+ if (err) {
+ printf("%s: could not open interrupt pipe, quit\n",
+ sc->sc_hdev.sc_dev.dv_xname);
+ return;
+ }
+ scc->sc_ibuf = malloc(scc->sc_ilen, M_USBDEV, M_WAITOK);
+
+ DPRINTF(("uoakv_attach: complete\n"));
+}
+
+int
+uoakv_detach(struct device *self, int flags)
+{
+ struct uoakv_softc *sc = (struct uoakv_softc *)self;
+ struct uoak_softc *scc = &sc->sc_uoak_softc;
+ int i, rv = 0;
+
+ wakeup(&sc->sc_sensortask);
+ sensordev_deinstall(&sc->sc_sensordev);
+
+ for (i = 0; i < OAK_V_MAXSENSORS; i++)
+ uoak_sensor_detach(scc, &sc->sc_sensor[i].v);
+
+ if (sc->sc_sensortask != NULL)
+ sensor_task_unregister(sc->sc_sensortask);
+
+ if (scc->sc_ibuf != NULL) {
+ free(scc->sc_ibuf, M_USBDEV);
+ scc->sc_ibuf = NULL;
+ }
+
+ return (rv);
+}
+
+int
+uoakv_activate(struct device *self, int act)
+{
+ struct uoakv_softc *sc = (struct uoakv_softc *)self;
+
+ switch (act) {
+ case DVACT_DEACTIVATE:
+ sc->sc_dying = 1;
+ break;
+ }
+ return (0);
+}
+
+void
+uoakv_intr(struct uhidev *addr, void *ibuf, u_int len)
+{
+ struct uoakv_softc *sc = (struct uoakv_softc *)addr;
+ struct uoak_softc *scc = &sc->sc_uoak_softc;
+ int i, idx, frame;
+ int16_t val;
+
+ if (sc->sc_dying)
+ return;
+ if (scc->sc_ibuf == NULL)
+ return;
+
+ memcpy(scc->sc_ibuf, ibuf, len);
+ frame = (scc->sc_ibuf[1] << 8) + scc->sc_ibuf[0];
+
+ for (i = 0; i < OAK_V_MAXSENSORS; i++) {
+ idx = (i + 1) * 2;
+ val = (int16_t)((scc->sc_ibuf[idx+1] << 8) | scc->sc_ibuf[idx]);
+ uoak_sensor_update(&sc->sc_sensor[i].v, val);
+ }
+}
+
+void
+uoakv_refresh(void *arg)
+{
+ struct uoakv_softc *sc = arg;
+ struct uoak_softc *scc = &sc->sc_uoak_softc;
+ uint8_t led;
+ int i;
+
+ /* blink LED for each cycle */
+ if (uoak_led_status(scc, OAK_TARGET_RAM, &led) < 0)
+ DPRINTF(("status query error\n"));
+ if (led == OAK_LED_OFF)
+ (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_ON);
+ else
+ (void)uoak_led_ctrl(scc, OAK_TARGET_RAM, OAK_LED_OFF);
+
+ for (i = 0; i < OAK_V_MAXSENSORS; i++)
+ uoak_sensor_refresh(&sc->sc_sensor[i].v, 1000, 0);
+}
+
+int
+uoakv_get_channel_setting(struct uoakv_softc *sc, enum uoak_target target,
+ int ch)
+{
+ struct uoak_softc *scc = &sc->sc_uoak_softc;
+ uint16_t cmd, result;
+
+ memset(&scc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
+ scc->sc_rcmd.target = target;
+ scc->sc_rcmd.datasize = 0x2;
+
+#define OAK_V_CHANNEL_IDX_OFFSET 3
+ cmd = (ch + OAK_V_CHANNEL_IDX_OFFSET);
+ USETW(&scc->sc_rcmd.cmd, cmd);
+
+ if (uoak_get_cmd(scc) < 0)
+ return EIO;
+
+ result = (scc->sc_buf[2] << 8) + scc->sc_buf[1];
+ sc->sc_sensor[ch].offset[target] = result;
+
+ return 0;
+}
+
+int
+uoakv_get_sensor_setting(struct uoakv_softc *sc, enum uoak_target target)
+{
+ struct uoak_softc *scc = &sc->sc_uoak_softc;
+ uint8_t result;
+
+ memset(&scc->sc_rcmd, 0, sizeof(struct uoak_rcmd));
+ scc->sc_rcmd.target = target;
+ scc->sc_rcmd.datasize = 0x1;
+ USETW(&scc->sc_rcmd.cmd, OAK_CMD_SENSORSETTING);
+
+ if (uoak_get_cmd(scc) < 0)
+ return EIO;
+
+ result = scc->sc_buf[1];
+ sc->sc_inputmode[target] = (result & OAK_V_SENSOR_INPUTMODEMASK);
+
+ return 0;
+}
+
+/* device specific functions */
+void
+uoakv_dev_setting(void *parent, enum uoak_target target)
+{
+ struct uoakv_softc *sc = (struct uoakv_softc *)parent;
+ int i;
+
+ /* get device specific configuration */
+ (void)uoakv_get_sensor_setting(sc, target);
+ for (i = 0; i < OAK_V_MAXSENSORS; i++)
+ (void)uoakv_get_channel_setting(sc, target, i);
+}
+
+void
+uoakv_dev_print(void *parent, enum uoak_target target)
+{
+ struct uoakv_softc *sc = (struct uoakv_softc *)parent;
+ int i;
+
+ printf(" input mode:%s\n",
+ (sc->sc_inputmode[target] ? "Psuedo-Diffential" : "Single-Ended"));
+
+ printf(" ADC offset(mV): ");
+ for (i = 0; i < OAK_V_MAXSENSORS; i++)
+ printf("ch%02d ",i);
+ printf("\n ");
+ for (i = 0; i < OAK_V_MAXSENSORS; i++)
+ printf("%2d.%02d ",
+ sc->sc_sensor[i].offset[target] / 100,
+ sc->sc_sensor[i].offset[target] % 100);
+ printf("\n");
+}