*/
#include <config.h>
-#include <stdlib.h>
-#include <math.h>
-#include <string.h>
+
#include <glib.h>
-#include <nettle/aes.h>
#include <libsigrok/libsigrok.h>
+#include <math.h>
+#include <nettle/aes.h>
+#include <stdlib.h>
+#include <string.h>
+
#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
-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
{ "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, },
+ 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)
+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;
}
-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)
{
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.");
+ if (serial_write_blocking(serial, poll_cmd, strlen(poll_cmd),
+ SERIAL_WRITE_TIMEOUT_MS) < 0) {
+ sr_err("Failed to send probe request.");
return SR_ERR;
}
}
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)
{
- struct dev_context *devc = sdi->priv;
- struct sr_serial_dev_inst *serial = sdi->conn;
+ struct dev_context *devc;
+ struct sr_serial_dev_inst *serial;
- if (serial_write_blocking(serial, &POLL_CMD, sizeof(POLL_CMD) - 1,
- SERIAL_WRITE_TIMEOUT_MS) < 0) {
+ serial = sdi->conn;
+ if (serial_write_blocking(serial, poll_cmd, strlen(poll_cmd),
+ SERIAL_WRITE_TIMEOUT_MS) < 0) {
sr_err("Unable to send poll request.");
return SR_ERR;
}
+ devc = sdi->priv;
devc->cmd_sent_at = g_get_monotonic_time() / 1000;
return SR_OK;
static void handle_poll_data(const 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;
+ devc = sdi->priv;
sr_spew("Received poll packet (len: %d).", devc->buflen);
if (devc->buflen != TC_POLL_LEN) {
sr_err("Unexpected poll packet length: %i", devc->buflen);
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);
- }
+ &devc->channels[i], poll_pkt, TC_POLL_LEN);
+ }
sr_sw_limits_update_samples_read(&devc->limits, 1);
}
static void recv_poll_data(struct sr_dev_inst *sdi, struct sr_serial_dev_inst *serial)
{
- struct dev_context *devc = sdi->priv;
+ struct dev_context *devc;
int len;
/* Serial data arrived. */
+ devc = sdi->priv;
while (devc->buflen < TC_POLL_LEN) {
len = serial_read_nonblocking(serial, devc->buf + devc->buflen, 1);
if (len < 1)
if (!(sdi = cb_data))
return TRUE;
-
if (!(devc = sdi->priv))
return TRUE;
now = g_get_monotonic_time() / 1000;
elapsed = now - devc->cmd_sent_at;
-
if (elapsed > TC_POLL_PERIOD_MS)
rdtech_tc_poll(sdi);