X-Git-Url: https://sigrok.org/gitweb/?a=blobdiff_plain;f=hardware%2Fcem-dt-885x%2Fprotocol.c;h=78b02f6a0728213207209b095fb6c3fabbeaf782;hb=bbabdaf1e2802aeef7d48fab4b0dc3507a69ffe9;hp=3ae43a7042a51f322b115e4db1ecb76e16b34fec;hpb=f157b2eebb8523792bd085eeb8fb3632b5d4c172;p=libsigrok.git diff --git a/hardware/cem-dt-885x/protocol.c b/hardware/cem-dt-885x/protocol.c index 3ae43a70..78b02f6a 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,26 +373,39 @@ 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; } -static int wait_for_token(const struct sr_dev_inst *sdi, char *tokens, int timeout) +static int wait_for_token(const struct sr_dev_inst *sdi, int8_t *tokens, int timeout) { struct dev_context *devc; struct sr_serial_dev_inst *serial; @@ -320,8 +440,8 @@ static int wait_for_token(const struct sr_dev_inst *sdi, char *tokens, int timeo /* cmd is the command to send, tokens are the tokens that denote the state * which the command affects. The first token is the desired state. */ -SR_PRIV int cem_dt_885x_toggle(const struct sr_dev_inst *sdi, uint8_t cmd, - char *tokens, int timeout) +static int cem_dt_885x_toggle(const struct sr_dev_inst *sdi, uint8_t cmd, + int8_t *tokens, int timeout) { struct dev_context *devc; struct sr_serial_dev_inst *serial; @@ -349,7 +469,7 @@ SR_PRIV gboolean cem_dt_885x_recording_get(const struct sr_dev_inst *sdi, int *state) { struct dev_context *devc; - char tokens[5]; + int8_t tokens[5]; devc = sdi->priv; if (devc->recording == -1) { @@ -370,7 +490,7 @@ SR_PRIV int cem_dt_885x_recording_set(const struct sr_dev_inst *sdi, { struct dev_context *devc; int ret; - char tokens[5]; + int8_t tokens[5]; devc = sdi->priv; @@ -406,7 +526,7 @@ SR_PRIV int cem_dt_885x_weight_freq_get(const struct sr_dev_inst *sdi) { struct dev_context *devc; int cur_setting; - char tokens[5]; + int8_t tokens[5]; devc = sdi->priv; @@ -424,14 +544,13 @@ SR_PRIV int cem_dt_885x_weight_freq_get(const struct sr_dev_inst *sdi) return SR_MQFLAG_SPL_FREQ_WEIGHT_C; } else return cur_setting; - } SR_PRIV int cem_dt_885x_weight_freq_set(const struct sr_dev_inst *sdi, int freqw) { struct dev_context *devc; int cur_setting, ret; - char tokens[5]; + int8_t tokens[5]; devc = sdi->priv; @@ -469,7 +588,7 @@ SR_PRIV int cem_dt_885x_weight_time_get(const struct sr_dev_inst *sdi) { struct dev_context *devc; int cur_setting; - char tokens[5]; + int8_t tokens[5]; devc = sdi->priv; @@ -487,14 +606,13 @@ SR_PRIV int cem_dt_885x_weight_time_get(const struct sr_dev_inst *sdi) return SR_MQFLAG_SPL_TIME_WEIGHT_S; } else return cur_setting; - } SR_PRIV int cem_dt_885x_weight_time_set(const struct sr_dev_inst *sdi, int timew) { struct dev_context *devc; int cur_setting, ret; - char tokens[5]; + int8_t tokens[5]; devc = sdi->priv; @@ -532,7 +650,7 @@ SR_PRIV int cem_dt_885x_holdmode_get(const struct sr_dev_inst *sdi, gboolean *holdmode) { struct dev_context *devc; - char tokens[5]; + int8_t tokens[5]; devc = sdi->priv; @@ -557,7 +675,7 @@ SR_PRIV int cem_dt_885x_holdmode_set(const struct sr_dev_inst *sdi, int holdmode { struct dev_context *devc; int cur_setting, ret; - char tokens[5]; + int8_t tokens[5]; devc = sdi->priv; @@ -601,7 +719,7 @@ SR_PRIV int cem_dt_885x_meas_range_get(const struct sr_dev_inst *sdi, uint64_t *low, uint64_t *high) { struct dev_context *devc; - char tokens[5]; + int8_t tokens[5]; devc = sdi->priv; if (devc->cur_meas_range == 0) { @@ -644,7 +762,7 @@ SR_PRIV int cem_dt_885x_meas_range_set(const struct sr_dev_inst *sdi, { struct dev_context *devc; int ret; - char token, tokens[6]; + int8_t token, tokens[6]; devc = sdi->priv; if (low == 30 && high == 130) @@ -685,3 +803,36 @@ SR_PRIV int cem_dt_885x_meas_range_set(const struct sr_dev_inst *sdi, return ret; } + +SR_PRIV int cem_dt_885x_power_off(const struct sr_dev_inst *sdi) +{ + struct sr_serial_dev_inst *serial; + char c, cmd; + + serial = sdi->conn; + + /* Reopen the port in non-blocking mode, so we can properly + * detect when the device stops communicating. */ + serial_close(serial); + if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK) + return SR_ERR; + + cmd = CMD_TOGGLE_POWER_OFF; + while (TRUE) { + serial_flush(serial); + if (serial_write(serial, (const void *)&cmd, 1) != 1) + return SR_ERR; + /* It never takes more than 23ms for the next token to arrive. */ + g_usleep(25 * 1000); + if (serial_read(serial, &c, 1) != 1) + /* Device is no longer responding. Good! */ + break; + } + + /* In case the user manually turns on the device again, reset + * the port back to blocking. */ + serial_close(serial); + serial_open(serial, SERIAL_RDWR); + + return SR_OK; +}