From df03552894dc80e2de6f818622936663c5b05946 Mon Sep 17 00:00:00 2001 From: Bert Vermeulen Date: Tue, 25 Jun 2013 14:09:01 +0200 Subject: [PATCH] kecheng-kc-330b: Live SPL acquisition --- hardware/kecheng-kc-330b/api.c | 36 ++++++--- hardware/kecheng-kc-330b/protocol.c | 117 +++++++++++++++++++++++++++- hardware/kecheng-kc-330b/protocol.h | 13 +++- 3 files changed, 151 insertions(+), 15 deletions(-) diff --git a/hardware/kecheng-kc-330b/api.c b/hardware/kecheng-kc-330b/api.c index 9afc3979..1555ac48 100644 --- a/hardware/kecheng-kc-330b/api.c +++ b/hardware/kecheng-kc-330b/api.c @@ -34,7 +34,7 @@ static const int32_t hwcaps[] = { SR_CONF_DATA_SOURCE, }; -static const uint64_t sample_intervals[][2] = { +SR_PRIV const uint64_t kecheng_kc_330b_sample_intervals[][2] = { { 1, 8 }, { 1, 2 }, { 1, 1 }, @@ -260,7 +260,7 @@ static int config_get(int key, GVariant **data, const struct sr_dev_inst *sdi) *data = g_variant_new_uint64(devc->limit_samples); break; case SR_CONF_SAMPLE_INTERVAL: - si = sample_intervals[devc->sample_interval]; + si = kecheng_kc_330b_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); @@ -322,14 +322,14 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi) 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) + for (i = 0; i < ARRAY_SIZE(kecheng_kc_330b_sample_intervals); i++) { + if (kecheng_kc_330b_sample_intervals[i][0] != p || kecheng_kc_330b_sample_intervals[i][1] != q) continue; devc->sample_interval = i; kecheng_kc_330b_configure(sdi); break; } - if (i == ARRAY_SIZE(sample_intervals)) + if (i == ARRAY_SIZE(kecheng_kc_330b_sample_intervals)) ret = SR_ERR_ARG; break; case SR_CONF_SPL_WEIGHT_FREQ: @@ -387,9 +387,9 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi) 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]); + for (i = 0; i < ARRAY_SIZE(kecheng_kc_330b_sample_intervals); i++) { + rational[0] = g_variant_new_uint64(kecheng_kc_330b_sample_intervals[i][0]); + rational[1] = g_variant_new_uint64(kecheng_kc_330b_sample_intervals[i][1]); tuple = g_variant_new_tuple(rational, 2); g_variant_builder_add_value(&gvb, tuple); } @@ -431,13 +431,16 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, devc->cb_data = cb_data; devc->num_samples = 0; + if (!(devc->xfer = libusb_alloc_transfer(0))) + return SR_ERR; + /* 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, + /* Handle USB events every 10ms. */ + sr_source_add(pfd[i]->fd, pfd[i]->events, 10, kecheng_kc_330b_handle_events, (void *)sdi); /* We'll need to remove this fd later. */ devc->usbfd[i] = pfd[i]->fd; @@ -448,8 +451,18 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, 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)); + libusb_free_transfer(devc->xfer); return SR_ERR; } + devc->last_live_request = g_get_monotonic_time() / 1000; + + libusb_fill_bulk_transfer(devc->xfer, usb->devhdl, EP_IN, devc->buf, + 16, kecheng_kc_330b_receive_transfer, (void *)sdi, 15); + if (libusb_submit_transfer(devc->xfer) != 0) { + libusb_free_transfer(devc->xfer); + return SR_ERR; + } + devc->state = LIVE_SPL_WAIT; return SR_OK; } @@ -461,7 +474,8 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data) if (sdi->status != SR_ST_ACTIVE) return SR_ERR_DEV_CLOSED; - /* TODO: stop acquisition. */ + /* Signal USB transfer handler to clean up and stop. */ + sdi->status = SR_ST_STOPPING; return SR_OK; } diff --git a/hardware/kecheng-kc-330b/protocol.c b/hardware/kecheng-kc-330b/protocol.c index 04065295..5133783e 100644 --- a/hardware/kecheng-kc-330b/protocol.c +++ b/hardware/kecheng-kc-330b/protocol.c @@ -17,24 +17,139 @@ * along with this program. If not, see . */ +#include #include "protocol.h" +extern struct sr_dev_driver kecheng_kc_330b_driver_info; +static struct sr_dev_driver *di = &kecheng_kc_330b_driver_info; +extern const uint64_t kecheng_kc_330b_sample_intervals[][2]; + SR_PRIV int kecheng_kc_330b_handle_events(int fd, int revents, void *cb_data) { - const struct sr_dev_inst *sdi; + struct drv_context *drvc; struct dev_context *devc; + struct sr_datafeed_packet packet; + struct sr_dev_inst *sdi; + struct sr_usb_dev_inst *usb; struct timeval tv; + const uint64_t *intv_entry; + gint64 now, interval; + int len, ret, i; + unsigned char cmd; (void)fd; (void)revents; + drvc = di->priv; sdi = cb_data; devc = sdi->priv; + usb = sdi->conn; + memset(&tv, 0, sizeof(struct timeval)); + libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx, &tv, + NULL); + + if (sdi->status == SR_ST_STOPPING) { + libusb_free_transfer(devc->xfer); + for (i = 0; devc->usbfd[i] != -1; i++) + sr_source_remove(devc->usbfd[i]); + packet.type = SR_DF_END; + sr_session_send(cb_data, &packet); + sdi->status = SR_ST_ACTIVE; + return TRUE; + } + + if (devc->state == LIVE_SPL_IDLE) { + now = g_get_monotonic_time() / 1000; + intv_entry = kecheng_kc_330b_sample_intervals[devc->sample_interval]; + interval = intv_entry[0] * 1000 / intv_entry[1]; + if (now - devc->last_live_request > interval) { + 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 request new acquisition: %s", + libusb_error_name(ret)); + sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi, + devc->cb_data); + return TRUE; + } + libusb_submit_transfer(devc->xfer); + devc->last_live_request = now; + devc->state = LIVE_SPL_WAIT; + } + } return TRUE; } +static void send_data(const struct sr_dev_inst *sdi, void *buf, unsigned int buf_len) +{ + struct dev_context *devc; + struct sr_datafeed_packet packet; + struct sr_datafeed_analog analog; + + devc = sdi->priv; + + memset(&analog, 0, sizeof(struct sr_datafeed_analog)); + analog.mq = SR_MQ_SOUND_PRESSURE_LEVEL; + analog.mqflags = devc->mqflags; + analog.unit = SR_UNIT_DECIBEL_SPL; + analog.probes = sdi->probes; + analog.num_samples = buf_len; + analog.data = buf; + packet.type = SR_DF_ANALOG; + packet.payload = &analog; + sr_session_send(devc->cb_data, &packet); + +} + +SR_PRIV void kecheng_kc_330b_receive_transfer(struct libusb_transfer *transfer) +{ + struct dev_context *devc; + struct sr_dev_inst *sdi; + float fvalue; + int packet_has_error; + + sdi = transfer->user_data; + devc = sdi->priv; + + packet_has_error = FALSE; + switch (transfer->status) { + case LIBUSB_TRANSFER_NO_DEVICE: + /* USB device was unplugged. */ + sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi, + devc->cb_data); + return; + case LIBUSB_TRANSFER_COMPLETED: + case LIBUSB_TRANSFER_TIMED_OUT: /* We may have received some data though */ + break; + default: + packet_has_error = TRUE; + break; + } + + if (!packet_has_error) { + if (devc->state == LIVE_SPL_WAIT) { + if (transfer->actual_length != 3 || transfer->buffer[0] != 0x88) { + sr_dbg("Received invalid SPL packet."); + } else { + fvalue = ((transfer->buffer[1] << 8) + transfer->buffer[2]) / 10.0; + send_data(sdi, &fvalue, 1); + devc->num_samples++; + if (devc->limit_samples && devc->num_samples >= devc->limit_samples) + sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi, + devc->cb_data); + + /* let USB event handler fire off another + * request when the time is right. */ + devc->state = LIVE_SPL_IDLE; + } + } + } + + +} + SR_PRIV int kecheng_kc_330b_configure(const struct sr_dev_inst *sdi) { struct dev_context *devc; diff --git a/hardware/kecheng-kc-330b/protocol.h b/hardware/kecheng-kc-330b/protocol.h index 8541a68b..1ec64e3e 100644 --- a/hardware/kecheng-kc-330b/protocol.h +++ b/hardware/kecheng-kc-330b/protocol.h @@ -38,7 +38,7 @@ #define EP_OUT 2 /* 500ms */ -#define DEFAULT_SAMPLE_INTERVAL 1 +#define DEFAULT_SAMPLE_INTERVAL 0 #define DEFAULT_ALARM_LOW 40 #define DEFAULT_ALARM_HIGH 120 #define DEFAULT_WEIGHT_TIME SR_MQFLAG_SPL_TIME_WEIGHT_F @@ -46,6 +46,10 @@ /* Live */ #define DEFAULT_DATA_SOURCE DATA_SOURCE_LIVE +enum { + LIVE_SPL_IDLE, + LIVE_SPL_WAIT, +}; enum { CMD_CONFIGURE = 0x01, @@ -61,8 +65,6 @@ enum { /** Private, per-device-instance driver context. */ struct dev_context { - /* Model-specific information */ - /* Acquisition settings */ uint64_t limit_samples; int sample_interval; @@ -72,15 +74,20 @@ struct dev_context { int data_source; /* Operational state */ + int state; uint64_t num_samples; void *cb_data; int usbfd[10]; + struct libusb_transfer *xfer; + unsigned char buf[16]; /* Temporary state across callbacks */ + gint64 last_live_request; }; SR_PRIV int kecheng_kc_330b_handle_events(int fd, int revents, void *cb_data); +SR_PRIV void kecheng_kc_330b_receive_transfer(struct libusb_transfer *transfer); 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,