X-Git-Url: https://sigrok.org/gitweb/?p=libsigrok.git;a=blobdiff_plain;f=src%2Fhardware%2Frdtech-tc%2Fprotocol.c;h=291604a0c524e6887381d77d9a837263751793c2;hp=456681ea49d6e7766db9b972dcd380f70a89a61d;hb=HEAD;hpb=cae33a58743e408a602771d6924ee8c271326f47 diff --git a/src/hardware/rdtech-tc/protocol.c b/src/hardware/rdtech-tc/protocol.c index 456681ea..291604a0 100644 --- a/src/hardware/rdtech-tc/protocol.c +++ b/src/hardware/rdtech-tc/protocol.c @@ -18,36 +18,44 @@ */ #include -#include -#include -#include + #include -#include #include +#include +#include +#include +#include + #include "libsigrok-internal.h" #include "protocol.h" -#define SERIAL_WRITE_TIMEOUT_MS 1 - -#define TC_POLL_LEN 192 -#define TC_POLL_PERIOD_MS 100 -#define TC_TIMEOUT_MS 1000 +#define PROBE_TO_MS 1000 +#define WRITE_TO_MS 1 +#define POLL_PERIOD_MS 100 -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) +#if TC_POLL_LEN > RDTECH_TC_RSPBUFSIZE +# error "response length exceeds receive buffer space" +#endif #define OFF_MODEL 4 #define LEN_MODEL 4 @@ -57,76 +65,115 @@ static const char POLL_CMD[] = "getva"; #define OFF_SERIAL 12 -static const uint8_t AES_KEY[] = { +static const uint8_t aes_key[] = { 0x58, 0x21, 0xfa, 0x56, 0x01, 0xb2, 0xf0, 0x26, 0x87, 0xff, 0x12, 0x04, 0x62, 0x2a, 0x4f, 0xb0, 0x86, 0xf4, 0x02, 0x60, 0x81, 0x6f, 0x9a, 0x0b, 0xa7, 0xf1, 0x06, 0x61, 0x9a, 0xb8, 0x72, 0x88, }; -static const struct binary_analog_channel rdtech_tc_channels[] = { - { "V", { 0 + 48, BVT_LE_UINT32, 1e-4, }, 4, SR_MQ_VOLTAGE, SR_UNIT_VOLT }, - { "I", { 0 + 52, BVT_LE_UINT32, 1e-5, }, 5, SR_MQ_CURRENT, SR_UNIT_AMPERE }, - { "D+", { 64 + 32, BVT_LE_UINT32, 1e-2, }, 2, SR_MQ_VOLTAGE, SR_UNIT_VOLT }, - { "D-", { 64 + 36, BVT_LE_UINT32, 1e-2, }, 2, SR_MQ_VOLTAGE, SR_UNIT_VOLT }, - { "E0", { 64 + 12, BVT_LE_UINT32, 1e-3, }, 3, SR_MQ_ENERGY, SR_UNIT_WATT_HOUR }, - { "E1", { 64 + 20, BVT_LE_UINT32, 1e-3, }, 3, SR_MQ_ENERGY, SR_UNIT_WATT_HOUR }, - { NULL, }, +static const struct rdtech_tc_channel_desc rdtech_tc_channels[] = { + { "V", { 0 + 48, BVT_LE_UINT32, }, { 100, 1e6, }, 4, SR_MQ_VOLTAGE, SR_UNIT_VOLT }, + { "I", { 0 + 52, BVT_LE_UINT32, }, { 10, 1e6, }, 5, SR_MQ_CURRENT, SR_UNIT_AMPERE }, + { "D+", { 64 + 32, BVT_LE_UINT32, }, { 10, 1e3, }, 2, SR_MQ_VOLTAGE, SR_UNIT_VOLT }, + { "D-", { 64 + 36, BVT_LE_UINT32, }, { 10, 1e3, }, 2, SR_MQ_VOLTAGE, SR_UNIT_VOLT }, + { "E0", { 64 + 12, BVT_LE_UINT32, }, { 1, 1e3, }, 3, SR_MQ_ENERGY, SR_UNIT_WATT_HOUR }, + { "E1", { 64 + 20, BVT_LE_UINT32, }, { 1, 1e3, }, 3, SR_MQ_ENERGY, SR_UNIT_WATT_HOUR }, }; -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); + uint16_t crc_calc; + uint32_t crc_recv; - if (crc != crc_field) { + crc_calc = sr_crc16(SR_CRC16_DEFAULT_INIT, data, PAC_CRC_POS); + crc_recv = read_u32le(&data[PAC_CRC_POS]); + if (crc_calc != crc_recv) { sr_spew("CRC error. Calculated: %0x" PRIx16 ", expected: %0x" PRIx32, - crc, crc_field); - return 0; - } else { - return 1; + crc_calc, crc_recv); + return FALSE; } + + return TRUE; } -static int process_poll_pkt(struct dev_context *devc, uint8_t *dst) +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_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; + } + + if (sr_log_loglevel_get() >= SR_LOG_SPEW) { + static const size_t chunk_max = 32; + + const uint8_t *rdptr; + size_t rdlen, chunk_addr, chunk_len; + GString *txt; + + sr_spew("check passed on decrypted receive data"); + rdptr = dst; + rdlen = TC_POLL_LEN; + chunk_addr = 0; + while (rdlen) { + chunk_len = rdlen; + if (chunk_len > chunk_max) + chunk_len = chunk_max; + txt = sr_hexdump_new(rdptr, chunk_len); + sr_spew("%04zx %s", chunk_addr, txt->str); + sr_hexdump_free(txt); + chunk_addr += chunk_len; + rdptr += chunk_len; + rdlen -= chunk_len; + } } return SR_OK; } -SR_PRIV int rdtech_tc_probe(struct sr_serial_dev_inst *serial, struct dev_context *devc) +SR_PRIV int rdtech_tc_probe(struct sr_serial_dev_inst *serial, struct dev_context *devc) { + static const char *poll_cmd_cdc = "getva"; + static const char *poll_cmd_ble = "bgetva\r\n"; + 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."); + /* Construct the request text. Which differs across transports. */ + devc->is_bluetooth = ser_name_is_bt(serial); + snprintf(devc->req_text, sizeof(devc->req_text), "%s", + devc->is_bluetooth ? poll_cmd_ble : poll_cmd_cdc); + sr_dbg("is bluetooth %d -> poll request '%s'.", + devc->is_bluetooth, devc->req_text); + + /* Transmit the request. */ + len = serial_write_blocking(serial, + devc->req_text, strlen(devc->req_text), 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); + /* Receive a response. */ + 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; @@ -138,73 +185,139 @@ 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->channel_count = ARRAY_SIZE(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 = 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; + + /* + * Don't send the request while receive data is being accumulated. + * Defer request transmission when a previous request has not yet + * seen any response data at all (more probable to happen shortly + * after connecting to the peripheral). + */ + devc = sdi->priv; + if (!force) { + if (devc->rdlen) + return SR_OK; + if (!devc->rx_after_tx) + 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; - if (serial_write_blocking(serial, &POLL_CMD, sizeof(POLL_CMD) - 1, - SERIAL_WRITE_TIMEOUT_MS) < 0) { + /* + * Transmit another measurement request. Only advance the + * interval after successful transmission. + */ + serial = sdi->conn; + len = serial_write_blocking(serial, + devc->req_text, strlen(devc->req_text), 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; + devc->rx_after_tx = 0; 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; - GSList *ch; - - 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; + size_t ch_idx; + const struct rdtech_tc_channel_desc *pch; + int ret; + float v; + + devc = sdi->priv; + sr_spew("Received poll packet (len: %zu).", devc->rdlen); + if (devc->rdlen < TC_POLL_LEN) { + sr_err("Insufficient poll packet length: %zu", devc->rdlen); + 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; + } + + ret = SR_OK; + std_session_send_df_frame_begin(sdi); + for (ch_idx = 0; ch_idx < devc->channel_count; ch_idx++) { + pch = &devc->channels[ch_idx]; + ret = bv_get_value_len(&v, &pch->spec, poll_pkt, TC_POLL_LEN); + if (ret != SR_OK) + break; + ret = feed_queue_analog_submit_one(devc->feeds[ch_idx], v, 1); + if (ret != SR_OK) + break; } + std_session_send_df_frame_end(sdi); - for (ch = sdi->channels, i = 0; ch; ch = g_slist_next(ch), i++) { - bv_send_analog_channel(sdi, ch->data, - &devc->channels[i], poll_pkt, TC_POLL_LEN); - } + sr_sw_limits_update_frames_read(&devc->limits, 1); + if (sr_sw_limits_check(&devc->limits)) + sr_dev_acquisition_stop(sdi); - sr_sw_limits_update_samples_read(&devc->limits, 1); + return ret; } -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; - - /* Serial data arrived. */ - while (devc->buflen < TC_POLL_LEN) { - len = serial_read_nonblocking(serial, devc->buf + devc->buflen, 1); - if (len < 1) - return; - - devc->buflen++; + int ret; + + /* Receive data became available. Drain the transport layer. */ + devc = sdi->priv; + while (devc->rdlen < TC_POLL_LEN) { + space = sizeof(devc->buf) - devc->rdlen; + len = serial_read_nonblocking(serial, + &devc->buf[devc->rdlen], space); + if (len < 0) + return SR_ERR_IO; + if (len == 0) + return SR_OK; + devc->rdlen += len; + devc->rx_after_tx += 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->rdlen >= TC_POLL_LEN) { + ret = handle_poll_data(sdi); + if (ret != SR_OK) + return ret; + devc->rdlen -= TC_POLL_LEN; + if (devc->rdlen) + memmove(devc->buf, &devc->buf[TC_POLL_LEN], devc->rdlen); + } - devc->buflen = 0; + return SR_OK; } SR_PRIV int rdtech_tc_receive_data(int fd, int revents, void *cb_data) @@ -212,30 +325,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; }