#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.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/uhidev.h>
#include "uoak.h"
#define UOAK_RETRY_DELAY 100
#define UOAK_RESPONSE_DELAY 10
int
uoak_check_device_ready(struct uoak_softc *sc)
{
int actlen;
actlen = uhidev_get_report(sc->sc_hdev->sc_parent, UHID_FEATURE_REPORT,
sc->sc_hdev->sc_report_id, &sc->sc_buf, sc->sc_flen);
if (actlen != sc->sc_flen)
return EIO;
if (sc->sc_buf[0] != 0xff)
return -1;
return 0;
}
int
uoak_set_cmd(struct uoak_softc *sc)
{
int actlen;
sc->sc_rcmd.dir = OAK_SET;
while (uoak_check_device_ready(sc) < 0)
usbd_delay_ms(sc->sc_udev, UOAK_RETRY_DELAY);
actlen = uhidev_set_report(sc->sc_hdev->sc_parent, UHID_FEATURE_REPORT,
sc->sc_hdev->sc_report_id, &sc->sc_rcmd, sc->sc_flen);
if (actlen != sc->sc_flen)
return EIO;
return 0;
}
int
uoak_get_cmd(struct uoak_softc *sc)
{
int actlen;
sc->sc_rcmd.dir = OAK_GET;
while (uoak_check_device_ready(sc) < 0)
usbd_delay_ms(sc->sc_udev, UOAK_RETRY_DELAY);
actlen = uhidev_set_report(sc->sc_hdev->sc_parent, UHID_FEATURE_REPORT,
sc->sc_hdev->sc_report_id, &sc->sc_rcmd, sc->sc_flen);
if (actlen != sc->sc_flen)
return EIO;
while (uoak_check_device_ready(sc) < 0)
usbd_delay_ms(sc->sc_udev, UOAK_RESPONSE_DELAY);
return 0;
}
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;
}
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);
}
void
uoak_get_devinfo(struct uoak_softc *sc)
{
usbd_fill_deviceinfo(sc->sc_udev, &sc->sc_udi);
}
void
uoak_get_setting(struct uoak_softc *sc, enum uoak_target target)
{
(void)uoak_get_device_name(sc, target);
(void)uoak_get_report_mode(sc, target);
(void)uoak_get_sample_rate(sc, target);
(void)uoak_get_report_rate(sc, target);
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);
}
void
uoak_print_setting(struct uoak_softc *sc, enum uoak_target target)
{
switch (sc->sc_config[target].report_mode) {
case OAK_REPORTMODE_AFTERSAMPLING:
printf(" sampling %dms",
sc->sc_config[target].sample_rate);
break;
case OAK_REPORTMODE_AFTERCHANGE:
printf(" reports changes");
break;
case OAK_REPORTMODE_FIXEDRATE:
printf(" rate %dms",
sc->sc_config[target].report_rate);
break;
default:
printf(" unknown sampling");
break;
}
if (sc->sc_methods->dev_print != NULL)
sc->sc_methods->dev_print(sc->sc_parent, target);
printf("\n");
}
void
uoak_sensor_attach(struct uoak_softc *sc, struct uoak_sensor *s,
enum sensor_type type)
{
if (s == NULL)
return;
s->avg.type = type;
s->max.type = type;
s->min.type = type;
s->avg.flags |= SENSOR_FINVALID;
s->max.flags |= SENSOR_FINVALID;
s->min.flags |= SENSOR_FINVALID;
(void)snprintf(s->avg.desc, sizeof(s->avg.desc),
"avg(#%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->avg);
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->avg);
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;
if (s->count == 0) {
s->vmax = s->vmin = s->vavg = val;
s->count++;
return;
}
if (val > s->vmax)
s->vmax = val;
else if (val < s->vmin)
s->vmin = val;
s->vavg = (s->vavg * s->count + val) / (s->count + 1);
s->count++;
}
void
uoak_sensor_refresh(struct uoak_sensor *s, int mag, int offset)
{
if (s == NULL)
return;
s->avg.value = s->vavg * mag + offset;
s->max.value = s->vmax * mag + offset;
s->min.value = s->vmin * mag + offset;
s->avg.flags &= ~SENSOR_FINVALID;
s->max.flags &= ~SENSOR_FINVALID;
s->min.flags &= ~SENSOR_FINVALID;
s->count = 0;
}