From cea26f6e9f1a58f28a4b790756a2f95dcf0eaa9d Mon Sep 17 00:00:00 2001 From: Bert Vermeulen Date: Thu, 20 Jun 2013 14:35:34 +0200 Subject: [PATCH] cem-dt-885x: Support for retrieving stored samples --- hardware/cem-dt-885x/api.c | 2 + hardware/cem-dt-885x/protocol.c | 126 +++++++++++++++++++++++++++++++- hardware/cem-dt-885x/protocol.h | 21 +++++- 3 files changed, 144 insertions(+), 5 deletions(-) diff --git a/hardware/cem-dt-885x/api.c b/hardware/cem-dt-885x/api.c index ca680501..8a9d69c4 100644 --- a/hardware/cem-dt-885x/api.c +++ b/hardware/cem-dt-885x/api.c @@ -118,6 +118,7 @@ static GSList *scan(GSList *options) devc->recording = -1; devc->cur_meas_range = 0; devc->cur_data_source = DATA_SOURCE_LIVE; + devc->enable_data_source_memory = FALSE; if (!(sdi->conn = sr_serial_dev_inst_new(conn, SERIALCOMM))) return NULL; @@ -333,6 +334,7 @@ static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi) devc->cur_data_source = DATA_SOURCE_MEMORY; else return SR_ERR; + devc->enable_data_source_memory = devc->cur_data_source == DATA_SOURCE_MEMORY; break; default: ret = SR_ERR_NA; diff --git a/hardware/cem-dt-885x/protocol.c b/hardware/cem-dt-885x/protocol.c index 5f2972f6..74531b1e 100644 --- a/hardware/cem-dt-885x/protocol.c +++ b/hardware/cem-dt-885x/protocol.c @@ -172,10 +172,49 @@ static void process_mset(const struct sr_dev_inst *sdi) } +static void send_data(const struct sr_dev_inst *sdi, unsigned char *data, + uint64_t num_samples) +{ + struct dev_context *devc; + struct sr_datafeed_packet packet; + struct sr_datafeed_analog analog; + float fbuf[SAMPLES_PER_PACKET]; + unsigned int i; + + devc = sdi->priv; + + for (i = 0; i < num_samples; i ++) { + fbuf[i] = ((data[i * 2] & 0xf0) >> 4) * 100; + fbuf[i] += (data[i * 2] & 0x0f) * 10; + fbuf[i] += ((data[i * 2 + 1] & 0xf0) >> 4); + fbuf[i] += (data[i * 2 + 1] & 0x0f) / 10.0; + } + memset(&analog, 0, sizeof(struct sr_datafeed_analog)); + analog.mq = SR_MQ_SOUND_PRESSURE_LEVEL; + analog.mqflags = devc->cur_mqflags; + analog.unit = SR_UNIT_DECIBEL_SPL; + analog.probes = sdi->probes; + analog.num_samples = num_samples; + analog.data = fbuf; + packet.type = SR_DF_ANALOG; + packet.payload = &analog; + sr_session_send(devc->cb_data, &packet); + + devc->num_samples += analog.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); + + return; +} + static void process_byte(const struct sr_dev_inst *sdi, const unsigned char c, int handle_packets) { struct dev_context *devc; + struct sr_datafeed_packet packet; + struct sr_datafeed_meta meta; + struct sr_config *src; gint64 cur_time; int len; @@ -216,7 +255,8 @@ static void process_byte(const struct sr_dev_inst *sdi, const unsigned char c, } else if (c == 0xbb) { devc->cmd = c; devc->buf_len = 0; - devc->state = ST_GET_LOG; + devc->state = ST_GET_LOG_HEADER; + sr_dbg("got command 0xbb"); } } else if (devc->state == ST_GET_TOKEN) { devc->token = c; @@ -258,7 +298,74 @@ static void process_byte(const struct sr_dev_inst *sdi, const unsigned char c, devc->state = ST_INIT; } } - } else if (devc->state == ST_GET_LOG) { + } else if (devc->state == ST_GET_LOG_HEADER) { + sr_dbg("log header: 0x%.2x", c); + if (devc->buf_len < 2) + devc->buf[devc->buf_len++] = c; + if (devc->buf_len == 2) { + sr_dbg("Device says it has %d bytes stored.", + ((devc->buf[0] << 8) + devc->buf[1]) - 100); + devc->buf_len = 0; + devc->state = ST_GET_LOG_RECORD_META; + } + } else if (devc->state == ST_GET_LOG_RECORD_META) { + sr_dbg("log meta: 0x%.2x", c); + if (c == RECORD_END) { + devc->state = ST_INIT; + /* Stop acquisition after transferring all stored + * records. Otherwise the frontend would have no + * way to tell where stored data ends and live + * measurements begin. */ + sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi, + devc->cb_data); + } else if (c == RECORD_DATA) { + devc->buf_len = 0; + devc->state = ST_GET_LOG_RECORD_DATA; + } else { + /* RECORD_DBA/RECORD_DBC + 7 bytes of metadata */ + devc->buf[devc->buf_len++] = c; + if (devc->buf_len < 8) + /* Keep filling up the record header. */ + return; + if (devc->buf[0] == RECORD_DBA) + devc->cur_mqflags = SR_MQFLAG_SPL_FREQ_WEIGHT_A; + else if (devc->buf[0] == RECORD_DBC) + devc->cur_mqflags = SR_MQFLAG_SPL_FREQ_WEIGHT_C; + else { + /* Shouldn't happen. */ + sr_dbg("Unknown record token 0x%.2x", c); + return; + } + packet.type = SR_DF_META; + packet.payload = &meta; + src = sr_config_new(SR_CONF_SAMPLE_INTERVAL, + g_variant_new_uint64(devc->buf[7] * 1000)); + meta.config = g_slist_append(NULL, src); + sr_session_send(devc->cb_data, &packet); + g_free(src); + devc->buf_len = 0; + } + } else if (devc->state == ST_GET_LOG_RECORD_DATA) { + sr_dbg("log data: 0x%.2x", c); + if (c == RECORD_DBA || c == RECORD_DBC || c == RECORD_DATA || c == RECORD_END) { + /* Work around off-by-one bug in device firmware. This + * happens only on the last record, i.e. before RECORD_END */ + if (devc->buf_len & 1) + devc->buf_len--; + /* Done with this set of samples */ + send_data(sdi, devc->buf, devc->buf_len / 2); + devc->buf_len = 0; + + /* Process this meta marker in the right state. */ + devc->state = ST_GET_LOG_RECORD_META; + process_byte(sdi, c, handle_packets); + } else { + devc->buf[devc->buf_len++] = c; + if (devc->buf_len == SAMPLES_PER_PACKET * 2) { + send_data(sdi, devc->buf, devc->buf_len / 2); + devc->buf_len = 0; + } + } } } @@ -266,19 +373,32 @@ static void process_byte(const struct sr_dev_inst *sdi, const unsigned char c, SR_PRIV int cem_dt_885x_receive_data(int fd, int revents, void *cb_data) { const struct sr_dev_inst *sdi; + struct dev_context *devc; struct sr_serial_dev_inst *serial; - unsigned char c; + unsigned char c, cmd; (void)fd; if (!(sdi = cb_data)) return TRUE; + devc = sdi->priv; serial = sdi->conn; if (revents == G_IO_IN) { if (serial_read(serial, &c, 1) != 1) return TRUE; process_byte(sdi, c, TRUE); + + if (devc->enable_data_source_memory) { + if (devc->state == ST_GET_LOG_HEADER) { + /* Memory transfer started. */ + devc->enable_data_source_memory = FALSE; + } else { + /* Tell device to start transferring from memory. */ + cmd = CMD_TRANSFER_MEMORY; + serial_write(serial, &cmd, 1); + } + } } return TRUE; diff --git a/hardware/cem-dt-885x/protocol.h b/hardware/cem-dt-885x/protocol.h index fa9474d6..85a0ea77 100644 --- a/hardware/cem-dt-885x/protocol.h +++ b/hardware/cem-dt-885x/protocol.h @@ -34,7 +34,13 @@ #define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args) #define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args) -#define BUF_SIZE 32 +/* When retrieving samples from device memory, group this many + * together into a sigrok packet. */ +#define SAMPLES_PER_PACKET 50 + +/* Various temporary storage, at least 8 bytes. */ +#define BUF_SIZE SAMPLES_PER_PACKET * 2 + /* When in hold mode, force the last measurement out at this interval. * We're using 50ms, which duplicates the non-hold 20Hz update rate. */ #define HOLD_REPEAT_INTERVAL 50 * 1000 @@ -73,6 +79,14 @@ enum { CMD_TOGGLE_HOLD_MAX_MIN = 0x11, CMD_TOGGLE_MEAS_RANGE = 0x88, CMD_TOGGLE_POWER_OFF = 0x33, + CMD_TRANSFER_MEMORY = 0xac, +}; + +enum { + RECORD_DBA = 0xaa, + RECORD_DBC = 0xcc, + RECORD_DATA = 0xac, + RECORD_END = 0xdd, }; enum { @@ -94,6 +108,7 @@ struct dev_context { /* Operational state */ int state; uint64_t num_samples; + gboolean enable_data_source_memory; /* Temporary state across callbacks */ void *cb_data; @@ -110,7 +125,9 @@ enum { ST_INIT, ST_GET_TOKEN, ST_GET_DATA, - ST_GET_LOG, + ST_GET_LOG_HEADER, + ST_GET_LOG_RECORD_META, + ST_GET_LOG_RECORD_DATA, }; SR_PRIV int cem_dt_885x_receive_data(int fd, int revents, void *cb_data);