X-Git-Url: https://sigrok.org/gitweb/?a=blobdiff_plain;f=src%2Fhardware%2Frdtech-tc%2Fprotocol.c;h=f2a439e4dacbcbbe0c1975ea562d1f6cdf9496ee;hb=8817bd6f5d9a92fcc582e3b51ea4cda3abc13b2a;hp=46d0e83e0510d301071707e4ce8c1e539436d076;hpb=5955b58c964f4db6f05d5f7110327fb722e963ba;p=libsigrok.git diff --git a/src/hardware/rdtech-tc/protocol.c b/src/hardware/rdtech-tc/protocol.c index 46d0e83e..f2a439e4 100644 --- a/src/hardware/rdtech-tc/protocol.c +++ b/src/hardware/rdtech-tc/protocol.c @@ -29,27 +29,32 @@ #include "libsigrok-internal.h" #include "protocol.h" -#define SERIAL_WRITE_TIMEOUT_MS 1 +#define PROBE_TO_MS 1000 +#define WRITE_TO_MS 1 +#define POLL_PERIOD_MS 100 -#define TC_POLL_LEN 192 -#define TC_POLL_PERIOD_MS 100 -#define TC_TIMEOUT_MS 1000 +static const char *poll_cmd = "getva"; -static const char POLL_CMD[] = "getva"; +/* + * Response data (raw sample data) consists of three adjacent chunks + * of 64 bytes each. These chunks start with their magic string, and + * end in a 32bit checksum field. Measurement values are scattered + * across these 192 bytes total size. All multi-byte integer values + * are represented in little endian format. Typical size is 32 bits. + */ -#define MAGIC_PAC1 0x31636170UL -#define MAGIC_PAC2 0x32636170UL -#define MAGIC_PAC3 0x33636170UL +#define MAGIC_PAC1 0x70616331 /* 'pac1' */ +#define MAGIC_PAC2 0x70616332 /* 'pac2' */ +#define MAGIC_PAC3 0x70616333 /* 'pac3' */ -/* Length of PAC block excluding CRC */ -#define PAC_DATA_LEN 60 -/* Length of PAC block including CRC */ #define PAC_LEN 64 +#define PAC_CRC_POS (PAC_LEN - sizeof(uint32_t)) /* Offset to PAC block from start of poll data */ #define OFF_PAC1 (0 * PAC_LEN) #define OFF_PAC2 (1 * PAC_LEN) #define OFF_PAC3 (2 * PAC_LEN) +#define TC_POLL_LEN (3 * PAC_LEN) #define OFF_MODEL 4 #define LEN_MODEL 4 @@ -76,42 +81,45 @@ static const struct binary_analog_channel rdtech_tc_channels[] = { ALL_ZERO, }; -static int check_pac_crc(uint8_t *data) +static gboolean check_pac_crc(uint8_t *data) { uint16_t crc; uint32_t crc_field; - crc = sr_crc16(SR_CRC16_DEFAULT_INIT, data, PAC_DATA_LEN); - crc_field = RL32(data + PAC_DATA_LEN); - + crc = sr_crc16(SR_CRC16_DEFAULT_INIT, data, PAC_CRC_POS); + crc_field = read_u32le(&data[PAC_CRC_POS]); if (crc != crc_field) { sr_spew("CRC error. Calculated: %0x" PRIx16 ", expected: %0x" PRIx32, crc, crc_field); - return 0; - } else { - return 1; + return FALSE; } + + return TRUE; } static int process_poll_pkt(struct dev_context *devc, uint8_t *dst) { struct aes256_ctx ctx; + gboolean ok; aes256_set_decrypt_key(&ctx, AES_KEY); aes256_decrypt(&ctx, TC_POLL_LEN, dst, devc->buf); - if (RL32(dst + OFF_PAC1) != MAGIC_PAC1 || - RL32(dst + OFF_PAC2) != MAGIC_PAC2 || - RL32(dst + OFF_PAC3) != MAGIC_PAC3) { - sr_err("Invalid poll packet magic values!"); - return SR_ERR; + ok = TRUE; + ok &= read_u32be(&dst[OFF_PAC1]) == MAGIC_PAC1; + ok &= read_u32be(&dst[OFF_PAC2]) == MAGIC_PAC2; + ok &= read_u32be(&dst[OFF_PAC3]) == MAGIC_PAC3; + if (!ok) { + sr_err("Invalid poll response packet (magic values)."); + return SR_ERR_DATA; } - if (!check_pac_crc(dst + OFF_PAC1) || - !check_pac_crc(dst + OFF_PAC2) || - !check_pac_crc(dst + OFF_PAC3)) { - sr_err("Invalid poll checksum!"); - return SR_ERR; + ok &= check_pac_crc(&dst[OFF_PAC1]); + ok &= check_pac_crc(&dst[OFF_PAC2]); + ok &= check_pac_crc(&dst[OFF_PAC3]); + if (!ok) { + sr_err("Invalid poll response packet (checksum)."); + return SR_ERR_DATA; } return SR_OK; @@ -122,13 +130,14 @@ SR_PRIV int rdtech_tc_probe(struct sr_serial_dev_inst *serial, struct dev_contex int len; uint8_t poll_pkt[TC_POLL_LEN]; - if (serial_write_blocking(serial, &POLL_CMD, sizeof(POLL_CMD) - 1, - SERIAL_WRITE_TIMEOUT_MS) < 0) { - sr_err("Unable to send probe request."); + len = serial_write_blocking(serial, + poll_cmd, strlen(poll_cmd), WRITE_TO_MS); + if (len < 0) { + sr_err("Failed to send probe request."); return SR_ERR; } - len = serial_read_blocking(serial, devc->buf, TC_POLL_LEN, TC_TIMEOUT_MS); + len = serial_read_blocking(serial, devc->buf, TC_POLL_LEN, PROBE_TO_MS); if (len != TC_POLL_LEN) { sr_err("Failed to read probe response."); return SR_ERR; @@ -140,73 +149,124 @@ SR_PRIV int rdtech_tc_probe(struct sr_serial_dev_inst *serial, struct dev_contex } devc->channels = rdtech_tc_channels; - devc->dev_info.model_name = g_strndup((const char *)poll_pkt + OFF_MODEL, LEN_MODEL); - devc->dev_info.fw_ver = g_strndup((const char *)poll_pkt + OFF_FW_VER, LEN_FW_VER); - devc->dev_info.serial_num = RL32(poll_pkt + OFF_SERIAL); + devc->dev_info.model_name = g_strndup((const char *)&poll_pkt[OFF_MODEL], LEN_MODEL); + devc->dev_info.fw_ver = g_strndup((const char *)&poll_pkt[OFF_FW_VER], LEN_FW_VER); + devc->dev_info.serial_num = read_u32le(&poll_pkt[OFF_SERIAL]); return SR_OK; } -SR_PRIV int rdtech_tc_poll(const struct sr_dev_inst *sdi) +SR_PRIV int rdtech_tc_poll(const struct sr_dev_inst *sdi, gboolean force) { - struct dev_context *devc = sdi->priv; - struct sr_serial_dev_inst *serial = sdi->conn; + struct dev_context *devc; + int64_t now, elapsed; + struct sr_serial_dev_inst *serial; + int len; - if (serial_write_blocking(serial, &POLL_CMD, sizeof(POLL_CMD) - 1, - SERIAL_WRITE_TIMEOUT_MS) < 0) { + /* + * Don't send the request while receive data is being accumulated. + */ + devc = sdi->priv; + if (!force && devc->buflen) + return SR_OK; + + /* + * Send the request when the transmit interval was reached. Or + * when the caller forced the transmission. + */ + now = g_get_monotonic_time() / 1000; + elapsed = now - devc->cmd_sent_at; + if (!force && elapsed < POLL_PERIOD_MS) + return SR_OK; + + /* + * Transmit another measurement request. Only advance the + * interval after successful transmission. + */ + serial = sdi->conn; + len = serial_write_blocking(serial, + poll_cmd, strlen(poll_cmd), WRITE_TO_MS); + if (len < 0) { sr_err("Unable to send poll request."); return SR_ERR; } - - devc->cmd_sent_at = g_get_monotonic_time() / 1000; + devc->cmd_sent_at = now; return SR_OK; } -static void handle_poll_data(const struct sr_dev_inst *sdi) +static int handle_poll_data(struct sr_dev_inst *sdi) { - struct dev_context *devc = sdi->priv; + struct dev_context *devc; uint8_t poll_pkt[TC_POLL_LEN]; - int i; + size_t i; GSList *ch; + int ret; - sr_spew("Received poll packet (len: %d).", devc->buflen); - if (devc->buflen != TC_POLL_LEN) { - sr_err("Unexpected poll packet length: %i", devc->buflen); - return; + devc = sdi->priv; + sr_spew("Received poll packet (len: %zu).", devc->buflen); + if (devc->buflen < TC_POLL_LEN) { + sr_err("Insufficient poll packet length: %zu", devc->buflen); + return SR_ERR_DATA; } if (process_poll_pkt(devc, poll_pkt) != SR_OK) { sr_err("Failed to process poll packet."); - return; + return SR_ERR_DATA; } - for (ch = sdi->channels, i = 0; ch; ch = g_slist_next(ch), i++) { - bv_send_analog_channel(sdi, ch->data, + i = 0; + for (ch = sdi->channels; ch; ch = g_slist_next(ch)) { + ret = bv_send_analog_channel(sdi, ch->data, &devc->channels[i], poll_pkt, TC_POLL_LEN); + i++; + if (ret != SR_OK) + return ret; } sr_sw_limits_update_samples_read(&devc->limits, 1); + if (sr_sw_limits_check(&devc->limits)) + sr_dev_acquisition_stop(sdi); + + return SR_OK; } -static void recv_poll_data(struct sr_dev_inst *sdi, struct sr_serial_dev_inst *serial) +static int recv_poll_data(struct sr_dev_inst *sdi, struct sr_serial_dev_inst *serial) { - struct dev_context *devc = sdi->priv; + struct dev_context *devc; + size_t space; int len; + int ret; - /* Serial data arrived. */ + /* Receive data became available. Drain the transport layer. */ + devc = sdi->priv; while (devc->buflen < TC_POLL_LEN) { - len = serial_read_nonblocking(serial, devc->buf + devc->buflen, 1); - if (len < 1) - return; - - devc->buflen++; + space = sizeof(devc->buf) - devc->buflen; + len = serial_read_nonblocking(serial, + &devc->buf[devc->buflen], space); + if (len < 0) + return SR_ERR_IO; + if (len == 0) + return SR_OK; + devc->buflen += len; } - if (devc->buflen == TC_POLL_LEN) - handle_poll_data(sdi); + /* + * TODO Want to (re-)synchronize to the packet stream? The + * 'pac1' string literal would be a perfect match for that. + */ + + /* Process packets when their reception has completed. */ + while (devc->buflen >= TC_POLL_LEN) { + ret = handle_poll_data(sdi); + if (ret != SR_OK) + return ret; + devc->buflen -= TC_POLL_LEN; + if (devc->buflen) + memmove(&devc->buf[0], &devc->buf[TC_POLL_LEN], devc->buflen); + } - devc->buflen = 0; + return SR_OK; } SR_PRIV int rdtech_tc_receive_data(int fd, int revents, void *cb_data) @@ -214,30 +274,31 @@ SR_PRIV int rdtech_tc_receive_data(int fd, int revents, void *cb_data) struct sr_dev_inst *sdi; struct dev_context *devc; struct sr_serial_dev_inst *serial; - int64_t now, elapsed; + int ret; (void)fd; if (!(sdi = cb_data)) return TRUE; - if (!(devc = sdi->priv)) return TRUE; + /* Handle availability of receive data. */ serial = sdi->conn; - if (revents == G_IO_IN) - recv_poll_data(sdi, serial); + if (revents == G_IO_IN) { + ret = recv_poll_data(sdi, serial); + if (ret != SR_OK) + sr_dev_acquisition_stop(sdi); + } + /* Check configured acquisition limits. */ if (sr_sw_limits_check(&devc->limits)) { sr_dev_acquisition_stop(sdi); return TRUE; } - now = g_get_monotonic_time() / 1000; - elapsed = now - devc->cmd_sent_at; - - if (elapsed > TC_POLL_PERIOD_MS) - rdtech_tc_poll(sdi); + /* Periodically retransmit measurement requests. */ + (void)rdtech_tc_poll(sdi, FALSE); return TRUE; }