diff options
author | Yojiro Uo <yuo@cvs.openbsd.org> | 2012-09-20 13:52:12 +0000 |
---|---|---|
committer | Yojiro Uo <yuo@cvs.openbsd.org> | 2012-09-20 13:52:12 +0000 |
commit | 06e2955cdc9dc864884658fa3502f0887b1feb94 (patch) | |
tree | 2a8ce216449c5044baf38e86239bb612d333e612 /sys/dev | |
parent | ee967bb279f78037ebc496177469bc4f5a82b2e3 (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.usb | 21 | ||||
-rw-r--r-- | sys/dev/usb/uoak.h | 171 | ||||
-rw-r--r-- | sys/dev/usb/uoak_subr.c | 362 | ||||
-rw-r--r-- | sys/dev/usb/uoaklux.c | 329 | ||||
-rw-r--r-- | sys/dev/usb/uoakrh.c | 352 | ||||
-rw-r--r-- | sys/dev/usb/uoakv.c | 360 |
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"); +} |