From: Bert Vermeulen Date: Mon, 24 Jun 2013 11:24:43 +0000 (+0200) Subject: kecheng-kc-330b: Implement all SR_CONF options X-Git-Tag: libsigrok-0.2.1~32 X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=bc7be4a9f4940dd9708208bddab87bd190ef816f;p=libsigrok.git kecheng-kc-330b: Implement all SR_CONF options --- diff --git a/hardware/kecheng-kc-330b/api.c b/hardware/kecheng-kc-330b/api.c index a817191a..9afc3979 100644 --- a/hardware/kecheng-kc-330b/api.c +++ b/hardware/kecheng-kc-330b/api.c @@ -17,18 +17,46 @@ * along with this program. If not, see . */ +#include #include "protocol.h" #define USB_CONN "1041.8101" #define VENDOR "Kecheng" #define USB_INTERFACE 0 -#define EP_IN 0x80 | 1 -#define EP_OUT 2 static const int32_t hwcaps[] = { SR_CONF_SOUNDLEVELMETER, SR_CONF_LIMIT_SAMPLES, SR_CONF_CONTINUOUS, + SR_CONF_DATALOG, + SR_CONF_SPL_WEIGHT_FREQ, + SR_CONF_SPL_WEIGHT_TIME, + SR_CONF_DATA_SOURCE, +}; + +static const uint64_t sample_intervals[][2] = { + { 1, 8 }, + { 1, 2 }, + { 1, 1 }, + { 2, 1 }, + { 5, 1 }, + { 10, 1 }, + { 60, 1 }, +}; + +static const char *weight_freq[] = { + "A", + "C", +}; + +static const char *weight_time[] = { + "F", + "S", +}; + +static const char *data_sources[] = { + "Live", + "Memory", }; SR_PRIV struct sr_dev_driver kecheng_kc_330b_driver_info; @@ -118,6 +146,15 @@ static GSList *scan(GSList *options) } sdi->priv = devc; devc->limit_samples = 0; + /* The protocol provides no way to read the current + * settings, so we'll enforce these. */ + devc->sample_interval = DEFAULT_SAMPLE_INTERVAL; + devc->alarm_low = DEFAULT_ALARM_LOW; + devc->alarm_high = DEFAULT_ALARM_HIGH; + devc->mqflags = DEFAULT_WEIGHT_TIME | DEFAULT_WEIGHT_FREQ; + devc->data_source = DEFAULT_DATA_SOURCE; + + /* TODO: Set date/time? */ drvc->instances = g_slist_append(drvc->instances, sdi); devices = g_slist_append(devices, sdi); @@ -165,6 +202,9 @@ static int dev_open(struct sr_dev_inst *sdi) } sdi->status = SR_ST_ACTIVE; + /* Force configuration. */ + ret = kecheng_kc_330b_configure(sdi); + return ret; } @@ -210,12 +250,45 @@ static int cleanup(void) static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi) { struct dev_context *devc; + GVariant *rational[2]; + const uint64_t *si; + int tmp, ret; devc = sdi->priv; switch (key) { case SR_CONF_LIMIT_SAMPLES: *data = g_variant_new_uint64(devc->limit_samples); break; + case SR_CONF_SAMPLE_INTERVAL: + si = sample_intervals[devc->sample_interval]; + rational[0] = g_variant_new_uint64(si[0]); + rational[1] = g_variant_new_uint64(si[1]); + *data = g_variant_new_tuple(rational, 2); + break; + case SR_CONF_DATALOG: + if ((ret = kecheng_kc_330b_recording_get(sdi, &tmp)) == SR_OK) + *data = g_variant_new_boolean(tmp); + else + return SR_ERR; + break; + case SR_CONF_SPL_WEIGHT_FREQ: + if (devc->mqflags & SR_MQFLAG_SPL_FREQ_WEIGHT_A) + *data = g_variant_new_string("A"); + else + *data = g_variant_new_string("C"); + break; + case SR_CONF_SPL_WEIGHT_TIME: + if (devc->mqflags & SR_MQFLAG_SPL_TIME_WEIGHT_F) + *data = g_variant_new_string("F"); + else + *data = g_variant_new_string("S"); + break; + case SR_CONF_DATA_SOURCE: + if (devc->data_source == DATA_SOURCE_LIVE) + *data = g_variant_new_string("Live"); + else + *data = g_variant_new_string("Memory"); + break; default: return SR_ERR_NA; } @@ -226,7 +299,10 @@ static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi) static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi) { struct dev_context *devc; - int ret; + uint64_t p, q; + unsigned int i; + int tmp, ret; + const char *tmp_str; if (sdi->status != SR_ST_ACTIVE) return SR_ERR_DEV_CLOSED; @@ -244,6 +320,51 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi) sr_dbg("Setting sample limit to %" PRIu64 ".", devc->limit_samples); break; + case SR_CONF_SAMPLE_INTERVAL: + g_variant_get(data, "(tt)", &p, &q); + for (i = 0; i < ARRAY_SIZE(sample_intervals); i++) { + if (sample_intervals[i][0] != p || sample_intervals[i][1] != q) + continue; + devc->sample_interval = i; + kecheng_kc_330b_configure(sdi); + break; + } + if (i == ARRAY_SIZE(sample_intervals)) + ret = SR_ERR_ARG; + break; + case SR_CONF_SPL_WEIGHT_FREQ: + tmp_str = g_variant_get_string(data, NULL); + if (!strcmp(tmp_str, "A")) + tmp = SR_MQFLAG_SPL_FREQ_WEIGHT_A; + else if (!strcmp(tmp_str, "C")) + tmp = SR_MQFLAG_SPL_FREQ_WEIGHT_C; + else + return SR_ERR_ARG; + devc->mqflags &= ~(SR_MQFLAG_SPL_FREQ_WEIGHT_A | SR_MQFLAG_SPL_FREQ_WEIGHT_C); + devc->mqflags |= tmp; + kecheng_kc_330b_configure(sdi); + break; + case SR_CONF_SPL_WEIGHT_TIME: + tmp_str = g_variant_get_string(data, NULL); + if (!strcmp(tmp_str, "F")) + tmp = SR_MQFLAG_SPL_TIME_WEIGHT_F; + else if (!strcmp(tmp_str, "S")) + tmp = SR_MQFLAG_SPL_TIME_WEIGHT_S; + else + return SR_ERR_ARG; + devc->mqflags &= ~(SR_MQFLAG_SPL_TIME_WEIGHT_F | SR_MQFLAG_SPL_TIME_WEIGHT_S); + devc->mqflags |= tmp; + kecheng_kc_330b_configure(sdi); + break; + case SR_CONF_DATA_SOURCE: + tmp_str = g_variant_get_string(data, NULL); + if (!strcmp(tmp_str, "Live")) + devc->data_source = DATA_SOURCE_LIVE; + else if (!strcmp(tmp_str, "Memory")) + devc->data_source = DATA_SOURCE_MEMORY; + else + return SR_ERR; + break; default: ret = SR_ERR_NA; } @@ -253,6 +374,9 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi) static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi) { + GVariant *tuple, *rational[2]; + GVariantBuilder gvb; + unsigned int i; (void)sdi; @@ -261,6 +385,25 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi) *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32, hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t)); break; + case SR_CONF_SAMPLE_INTERVAL: + g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY); + for (i = 0; i < ARRAY_SIZE(sample_intervals); i++) { + rational[0] = g_variant_new_uint64(sample_intervals[i][0]); + rational[1] = g_variant_new_uint64(sample_intervals[i][1]); + tuple = g_variant_new_tuple(rational, 2); + g_variant_builder_add_value(&gvb, tuple); + } + *data = g_variant_builder_end(&gvb); + break; + case SR_CONF_SPL_WEIGHT_FREQ: + *data = g_variant_new_strv(weight_freq, ARRAY_SIZE(weight_freq)); + break; + case SR_CONF_SPL_WEIGHT_TIME: + *data = g_variant_new_strv(weight_time, ARRAY_SIZE(weight_time)); + break; + case SR_CONF_DATA_SOURCE: + *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources)); + break; default: return SR_ERR_NA; } @@ -271,14 +414,42 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi) static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data) { - (void)sdi; - (void)cb_data; + struct drv_context *drvc; + struct dev_context *devc; + struct sr_usb_dev_inst *usb; + const struct libusb_pollfd **pfd; + int len, ret, i; + unsigned char cmd; if (sdi->status != SR_ST_ACTIVE) return SR_ERR_DEV_CLOSED; - /* TODO: configure hardware, reset acquisition state, set up - * callbacks and send header packet. */ + drvc = di->priv; + devc = sdi->priv; + usb = sdi->conn; + + devc->cb_data = cb_data; + devc->num_samples = 0; + + /* Send header packet to the session bus. */ + std_session_send_df_header(cb_data, LOG_PREFIX); + + pfd = libusb_get_pollfds(drvc->sr_ctx->libusb_ctx); + for (i = 0; pfd[i]; i++) { + /* Handle USB events every 100ms, for decent latency. */ + sr_source_add(pfd[i]->fd, pfd[i]->events, 100, + kecheng_kc_330b_handle_events, (void *)sdi); + /* We'll need to remove this fd later. */ + devc->usbfd[i] = pfd[i]->fd; + } + devc->usbfd[i] = -1; + + cmd = CMD_GET_LIVE_SPL; + ret = libusb_bulk_transfer(usb->devhdl, EP_OUT, &cmd, 1, &len, 5); + if (ret != 0 || len != 1) { + sr_dbg("Failed to start acquisition: %s", libusb_error_name(ret)); + return SR_ERR; + } return SR_OK; } diff --git a/hardware/kecheng-kc-330b/protocol.c b/hardware/kecheng-kc-330b/protocol.c index d519e6b3..04065295 100644 --- a/hardware/kecheng-kc-330b/protocol.c +++ b/hardware/kecheng-kc-330b/protocol.c @@ -19,22 +19,105 @@ #include "protocol.h" -SR_PRIV int kecheng_kc_330b_receive_data(int fd, int revents, void *cb_data) +SR_PRIV int kecheng_kc_330b_handle_events(int fd, int revents, void *cb_data) { + const struct sr_dev_inst *sdi; + struct dev_context *devc; + struct timeval tv; + (void)fd; + (void)revents; - const struct sr_dev_inst *sdi; + sdi = cb_data; + devc = sdi->priv; + + + return TRUE; +} + +SR_PRIV int kecheng_kc_330b_configure(const struct sr_dev_inst *sdi) +{ struct dev_context *devc; + struct sr_usb_dev_inst *usb; + int len, ret; + unsigned char buf[7]; - if (!(sdi = cb_data)) - return TRUE; + sr_dbg("Configuring device."); - if (!(devc = sdi->priv)) - return TRUE; + usb = sdi->conn; + devc = sdi->priv; - if (revents == G_IO_IN) { - /* TODO */ + buf[0] = CMD_CONFIGURE; + buf[1] = devc->sample_interval; + buf[2] = devc->alarm_low; + buf[3] = devc->alarm_high; + buf[4] = devc->mqflags & SR_MQFLAG_SPL_TIME_WEIGHT_F ? 0 : 1; + buf[5] = devc->mqflags & SR_MQFLAG_SPL_FREQ_WEIGHT_A ? 0 : 1; + buf[6] = devc->data_source; + ret = libusb_bulk_transfer(usb->devhdl, EP_OUT, buf, 7, &len, 5); + if (ret != 0 || len != 7) { + sr_dbg("Failed to configure device: %s", libusb_error_name(ret)); + return SR_ERR; } - return TRUE; + /* The configure command ack takes about 32ms to come in. */ + ret = libusb_bulk_transfer(usb->devhdl, EP_IN, buf, 1, &len, 40); + if (ret != 0 || len != 1) { + sr_dbg("Failed to configure device (no ack): %s", libusb_error_name(ret)); + return SR_ERR; + } + if (buf[0] != (CMD_CONFIGURE | 0x80)) { + sr_dbg("Failed to configure device: invalid response 0x%2.x", buf[0]); + return SR_ERR; + } + + return SR_OK; +} + +SR_PRIV int kecheng_kc_330b_set_date_time(struct sr_dev_inst *sdi) +{ + struct sr_usb_dev_inst *usb; + GDateTime *dt; + int len, ret; + unsigned char buf[7]; + + sr_dbg("Setting device date/time."); + + usb = sdi->conn; + + dt = g_date_time_new_now_local(); + buf[0] = CMD_SET_DATE_TIME; + buf[1] = g_date_time_get_year(dt) - 2000; + buf[2] = g_date_time_get_month(dt); + buf[3] = g_date_time_get_day_of_month(dt); + buf[4] = g_date_time_get_hour(dt); + buf[5] = g_date_time_get_minute(dt); + buf[6] = g_date_time_get_second(dt); + g_date_time_unref(dt); + ret = libusb_bulk_transfer(usb->devhdl, EP_OUT, buf, 7, &len, 5); + if (ret != 0 || len != 7) { + sr_dbg("Failed to set date/time: %s", libusb_error_name(ret)); + return SR_ERR; + } + + ret = libusb_bulk_transfer(usb->devhdl, EP_IN, buf, 1, &len, 10); + if (ret != 0 || len != 1) { + sr_dbg("Failed to set date/time (no ack): %s", libusb_error_name(ret)); + return SR_ERR; + } + if (buf[0] != (CMD_SET_DATE_TIME | 0x80)) { + sr_dbg("Failed to set date/time: invalid response 0x%2.x", buf[0]); + return SR_ERR; + } + + + + return SR_OK; +} + +SR_PRIV int kecheng_kc_330b_recording_get(const struct sr_dev_inst *sdi, + gboolean *tmp) +{ + + return SR_OK; } diff --git a/hardware/kecheng-kc-330b/protocol.h b/hardware/kecheng-kc-330b/protocol.h index 76e13891..8541a68b 100644 --- a/hardware/kecheng-kc-330b/protocol.h +++ b/hardware/kecheng-kc-330b/protocol.h @@ -34,8 +34,29 @@ #define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args) #define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args) -SR_PRIV enum { +#define EP_IN 0x80 | 1 +#define EP_OUT 2 + +/* 500ms */ +#define DEFAULT_SAMPLE_INTERVAL 1 +#define DEFAULT_ALARM_LOW 40 +#define DEFAULT_ALARM_HIGH 120 +#define DEFAULT_WEIGHT_TIME SR_MQFLAG_SPL_TIME_WEIGHT_F +#define DEFAULT_WEIGHT_FREQ SR_MQFLAG_SPL_FREQ_WEIGHT_A +/* Live */ +#define DEFAULT_DATA_SOURCE DATA_SOURCE_LIVE + + +enum { + CMD_CONFIGURE = 0x01, CMD_IDENTIFY = 0x02, + CMD_SET_DATE_TIME = 0x03, + CMD_GET_LIVE_SPL = 0x08, +}; + +enum { + DATA_SOURCE_LIVE, + DATA_SOURCE_MEMORY, }; /** Private, per-device-instance driver context. */ @@ -44,13 +65,25 @@ struct dev_context { /* Acquisition settings */ uint64_t limit_samples; + int sample_interval; + int alarm_low; + int alarm_high; + uint64_t mqflags; + int data_source; /* Operational state */ + uint64_t num_samples; + void *cb_data; + int usbfd[10]; /* Temporary state across callbacks */ }; -SR_PRIV int kecheng_kc_330b_receive_data(int fd, int revents, void *cb_data); +SR_PRIV int kecheng_kc_330b_handle_events(int fd, int revents, void *cb_data); +SR_PRIV int kecheng_kc_330b_configure(const struct sr_dev_inst *sdi); +SR_PRIV int kecheng_kc_330b_set_date_time(struct sr_dev_inst *sdi); +SR_PRIV int kecheng_kc_330b_recording_get(const struct sr_dev_inst *sdi, + gboolean *tmp); #endif