]> sigrok.org Git - libsigrok.git/blame - src/hardware/rdtech-tc/protocol.c
output/csv: use intermediate time_t var, silence compiler warning
[libsigrok.git] / src / hardware / rdtech-tc / protocol.c
CommitLineData
cae33a58
AS
1/*
2 * This file is part of the libsigrok project.
3 *
4 * Copyright (C) 2020 Andreas Sandberg <andreas@sandberg.pp.se>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <config.h>
ea5fc667 21
cae33a58 22#include <glib.h>
cae33a58 23#include <libsigrok/libsigrok.h>
ea5fc667
GS
24#include <math.h>
25#include <nettle/aes.h>
26#include <stdlib.h>
27#include <string.h>
28
cae33a58
AS
29#include "libsigrok-internal.h"
30#include "protocol.h"
31
861fa81f
GS
32#define PROBE_TO_MS 1000
33#define WRITE_TO_MS 1
34#define POLL_PERIOD_MS 100
cae33a58 35
a780870c
GS
36/*
37 * Response data (raw sample data) consists of three adjacent chunks
38 * of 64 bytes each. These chunks start with their magic string, and
39 * end in a 32bit checksum field. Measurement values are scattered
40 * across these 192 bytes total size. All multi-byte integer values
41 * are represented in little endian format. Typical size is 32 bits.
42 */
43
44#define MAGIC_PAC1 0x70616331 /* 'pac1' */
45#define MAGIC_PAC2 0x70616332 /* 'pac2' */
46#define MAGIC_PAC3 0x70616333 /* 'pac3' */
cae33a58 47
cae33a58 48#define PAC_LEN 64
a780870c 49#define PAC_CRC_POS (PAC_LEN - sizeof(uint32_t))
cae33a58
AS
50
51/* Offset to PAC block from start of poll data */
52#define OFF_PAC1 (0 * PAC_LEN)
53#define OFF_PAC2 (1 * PAC_LEN)
54#define OFF_PAC3 (2 * PAC_LEN)
a780870c 55#define TC_POLL_LEN (3 * PAC_LEN)
37b68953
GS
56#if TC_POLL_LEN > RDTECH_TC_RSPBUFSIZE
57# error "response length exceeds receive buffer space"
58#endif
cae33a58
AS
59
60#define OFF_MODEL 4
61#define LEN_MODEL 4
62
63#define OFF_FW_VER 8
64#define LEN_FW_VER 4
65
66#define OFF_SERIAL 12
67
fde2cf21 68static const uint8_t aes_key[] = {
cae33a58
AS
69 0x58, 0x21, 0xfa, 0x56, 0x01, 0xb2, 0xf0, 0x26,
70 0x87, 0xff, 0x12, 0x04, 0x62, 0x2a, 0x4f, 0xb0,
71 0x86, 0xf4, 0x02, 0x60, 0x81, 0x6f, 0x9a, 0x0b,
72 0xa7, 0xf1, 0x06, 0x61, 0x9a, 0xb8, 0x72, 0x88,
73};
74
b3df7668 75static const struct rdtech_tc_channel_desc rdtech_tc_channels[] = {
d555959d
GS
76 { "V", { 0 + 48, BVT_LE_UINT32, }, { 100, 1e6, }, 4, SR_MQ_VOLTAGE, SR_UNIT_VOLT },
77 { "I", { 0 + 52, BVT_LE_UINT32, }, { 10, 1e6, }, 5, SR_MQ_CURRENT, SR_UNIT_AMPERE },
78 { "D+", { 64 + 32, BVT_LE_UINT32, }, { 10, 1e3, }, 2, SR_MQ_VOLTAGE, SR_UNIT_VOLT },
79 { "D-", { 64 + 36, BVT_LE_UINT32, }, { 10, 1e3, }, 2, SR_MQ_VOLTAGE, SR_UNIT_VOLT },
80 { "E0", { 64 + 12, BVT_LE_UINT32, }, { 1, 1e3, }, 3, SR_MQ_ENERGY, SR_UNIT_WATT_HOUR },
81 { "E1", { 64 + 20, BVT_LE_UINT32, }, { 1, 1e3, }, 3, SR_MQ_ENERGY, SR_UNIT_WATT_HOUR },
cae33a58
AS
82};
83
a780870c 84static gboolean check_pac_crc(uint8_t *data)
cae33a58 85{
fde2cf21
GS
86 uint16_t crc_calc;
87 uint32_t crc_recv;
cae33a58 88
fde2cf21
GS
89 crc_calc = sr_crc16(SR_CRC16_DEFAULT_INIT, data, PAC_CRC_POS);
90 crc_recv = read_u32le(&data[PAC_CRC_POS]);
91 if (crc_calc != crc_recv) {
cae33a58 92 sr_spew("CRC error. Calculated: %0x" PRIx16 ", expected: %0x" PRIx32,
fde2cf21 93 crc_calc, crc_recv);
a780870c 94 return FALSE;
cae33a58 95 }
a780870c
GS
96
97 return TRUE;
cae33a58
AS
98}
99
5955b58c 100static int process_poll_pkt(struct dev_context *devc, uint8_t *dst)
cae33a58
AS
101{
102 struct aes256_ctx ctx;
a780870c 103 gboolean ok;
cae33a58 104
fde2cf21 105 aes256_set_decrypt_key(&ctx, aes_key);
cae33a58
AS
106 aes256_decrypt(&ctx, TC_POLL_LEN, dst, devc->buf);
107
a780870c
GS
108 ok = TRUE;
109 ok &= read_u32be(&dst[OFF_PAC1]) == MAGIC_PAC1;
110 ok &= read_u32be(&dst[OFF_PAC2]) == MAGIC_PAC2;
111 ok &= read_u32be(&dst[OFF_PAC3]) == MAGIC_PAC3;
112 if (!ok) {
113 sr_err("Invalid poll response packet (magic values).");
114 return SR_ERR_DATA;
cae33a58
AS
115 }
116
a780870c
GS
117 ok &= check_pac_crc(&dst[OFF_PAC1]);
118 ok &= check_pac_crc(&dst[OFF_PAC2]);
119 ok &= check_pac_crc(&dst[OFF_PAC3]);
120 if (!ok) {
121 sr_err("Invalid poll response packet (checksum).");
122 return SR_ERR_DATA;
cae33a58
AS
123 }
124
1dfda32f
GS
125 if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
126 static const size_t chunk_max = 32;
127
128 const uint8_t *rdptr;
129 size_t rdlen, chunk_addr, chunk_len;
130 GString *txt;
131
132 sr_spew("check passed on decrypted receive data");
133 rdptr = dst;
134 rdlen = TC_POLL_LEN;
135 chunk_addr = 0;
136 while (rdlen) {
137 chunk_len = rdlen;
138 if (chunk_len > chunk_max)
139 chunk_len = chunk_max;
140 txt = sr_hexdump_new(rdptr, chunk_len);
141 sr_spew("%04zx %s", chunk_addr, txt->str);
142 sr_hexdump_free(txt);
143 chunk_addr += chunk_len;
144 rdptr += chunk_len;
145 rdlen -= chunk_len;
146 }
147 }
148
cae33a58
AS
149 return SR_OK;
150}
151
5955b58c 152SR_PRIV int rdtech_tc_probe(struct sr_serial_dev_inst *serial, struct dev_context *devc)
cae33a58 153{
37b68953
GS
154 static const char *poll_cmd_cdc = "getva";
155 static const char *poll_cmd_ble = "bgetva\r\n";
156
cae33a58
AS
157 int len;
158 uint8_t poll_pkt[TC_POLL_LEN];
159
37b68953
GS
160 /* Construct the request text. Which differs across transports. */
161 devc->is_bluetooth = ser_name_is_bt(serial);
162 snprintf(devc->req_text, sizeof(devc->req_text), "%s",
163 devc->is_bluetooth ? poll_cmd_ble : poll_cmd_cdc);
164 sr_dbg("is bluetooth %d -> poll request '%s'.",
165 devc->is_bluetooth, devc->req_text);
166
167 /* Transmit the request. */
861fa81f 168 len = serial_write_blocking(serial,
37b68953 169 devc->req_text, strlen(devc->req_text), WRITE_TO_MS);
861fa81f 170 if (len < 0) {
a780870c 171 sr_err("Failed to send probe request.");
cae33a58
AS
172 return SR_ERR;
173 }
174
37b68953 175 /* Receive a response. */
861fa81f 176 len = serial_read_blocking(serial, devc->buf, TC_POLL_LEN, PROBE_TO_MS);
cae33a58
AS
177 if (len != TC_POLL_LEN) {
178 sr_err("Failed to read probe response.");
179 return SR_ERR;
180 }
181
182 if (process_poll_pkt(devc, poll_pkt) != SR_OK) {
183 sr_err("Unrecognized TC device!");
184 return SR_ERR;
185 }
186
187 devc->channels = rdtech_tc_channels;
b3df7668 188 devc->channel_count = ARRAY_SIZE(rdtech_tc_channels);
a780870c
GS
189 devc->dev_info.model_name = g_strndup((const char *)&poll_pkt[OFF_MODEL], LEN_MODEL);
190 devc->dev_info.fw_ver = g_strndup((const char *)&poll_pkt[OFF_FW_VER], LEN_FW_VER);
191 devc->dev_info.serial_num = read_u32le(&poll_pkt[OFF_SERIAL]);
cae33a58
AS
192
193 return SR_OK;
194}
195
a82490b6 196SR_PRIV int rdtech_tc_poll(const struct sr_dev_inst *sdi, gboolean force)
cae33a58 197{
c1f9428a 198 struct dev_context *devc;
a82490b6 199 int64_t now, elapsed;
c1f9428a 200 struct sr_serial_dev_inst *serial;
861fa81f 201 int len;
cae33a58 202
8817bd6f
GS
203 /*
204 * Don't send the request while receive data is being accumulated.
66d58fcb
GS
205 * Defer request transmission when a previous request has not yet
206 * seen any response data at all (more probable to happen shortly
207 * after connecting to the peripheral).
8817bd6f 208 */
a82490b6 209 devc = sdi->priv;
66d58fcb
GS
210 if (!force) {
211 if (devc->rdlen)
212 return SR_OK;
213 if (!devc->rx_after_tx)
214 return SR_OK;
215 }
8817bd6f
GS
216
217 /*
218 * Send the request when the transmit interval was reached. Or
219 * when the caller forced the transmission.
220 */
a82490b6
GS
221 now = g_get_monotonic_time() / 1000;
222 elapsed = now - devc->cmd_sent_at;
861fa81f 223 if (!force && elapsed < POLL_PERIOD_MS)
a82490b6
GS
224 return SR_OK;
225
8817bd6f
GS
226 /*
227 * Transmit another measurement request. Only advance the
228 * interval after successful transmission.
229 */
c1f9428a 230 serial = sdi->conn;
861fa81f 231 len = serial_write_blocking(serial,
37b68953 232 devc->req_text, strlen(devc->req_text), WRITE_TO_MS);
861fa81f 233 if (len < 0) {
cae33a58
AS
234 sr_err("Unable to send poll request.");
235 return SR_ERR;
236 }
a82490b6 237 devc->cmd_sent_at = now;
66d58fcb 238 devc->rx_after_tx = 0;
cae33a58
AS
239
240 return SR_OK;
241}
242
269f1e0e 243static int handle_poll_data(struct sr_dev_inst *sdi)
cae33a58 244{
c1f9428a 245 struct dev_context *devc;
cae33a58 246 uint8_t poll_pkt[TC_POLL_LEN];
b3df7668
GS
247 size_t ch_idx;
248 const struct rdtech_tc_channel_desc *pch;
269f1e0e 249 int ret;
b3df7668 250 float v;
cae33a58 251
c1f9428a 252 devc = sdi->priv;
e2bcf2e3
GS
253 sr_spew("Received poll packet (len: %zu).", devc->rdlen);
254 if (devc->rdlen < TC_POLL_LEN) {
255 sr_err("Insufficient poll packet length: %zu", devc->rdlen);
269f1e0e 256 return SR_ERR_DATA;
cae33a58
AS
257 }
258
259 if (process_poll_pkt(devc, poll_pkt) != SR_OK) {
260 sr_err("Failed to process poll packet.");
269f1e0e 261 return SR_ERR_DATA;
cae33a58
AS
262 }
263
15ceaf9d
GS
264 ret = SR_OK;
265 std_session_send_df_frame_begin(sdi);
b3df7668
GS
266 for (ch_idx = 0; ch_idx < devc->channel_count; ch_idx++) {
267 pch = &devc->channels[ch_idx];
48b7c901 268 ret = bv_get_value_len(&v, &pch->spec, poll_pkt, TC_POLL_LEN);
b3df7668 269 if (ret != SR_OK)
15ceaf9d 270 break;
f40d8479 271 ret = feed_queue_analog_submit_one(devc->feeds[ch_idx], v, 1);
269f1e0e 272 if (ret != SR_OK)
15ceaf9d 273 break;
5955b58c 274 }
15ceaf9d 275 std_session_send_df_frame_end(sdi);
cae33a58 276
15ceaf9d 277 sr_sw_limits_update_frames_read(&devc->limits, 1);
269f1e0e
GS
278 if (sr_sw_limits_check(&devc->limits))
279 sr_dev_acquisition_stop(sdi);
280
15ceaf9d 281 return ret;
cae33a58
AS
282}
283
269f1e0e 284static int recv_poll_data(struct sr_dev_inst *sdi, struct sr_serial_dev_inst *serial)
cae33a58 285{
c1f9428a 286 struct dev_context *devc;
8817bd6f 287 size_t space;
cae33a58 288 int len;
269f1e0e 289 int ret;
cae33a58 290
8817bd6f 291 /* Receive data became available. Drain the transport layer. */
c1f9428a 292 devc = sdi->priv;
e2bcf2e3
GS
293 while (devc->rdlen < TC_POLL_LEN) {
294 space = sizeof(devc->buf) - devc->rdlen;
8817bd6f 295 len = serial_read_nonblocking(serial,
e2bcf2e3 296 &devc->buf[devc->rdlen], space);
269f1e0e
GS
297 if (len < 0)
298 return SR_ERR_IO;
299 if (len == 0)
300 return SR_OK;
e2bcf2e3 301 devc->rdlen += len;
66d58fcb 302 devc->rx_after_tx += len;
cae33a58
AS
303 }
304
8817bd6f
GS
305 /*
306 * TODO Want to (re-)synchronize to the packet stream? The
307 * 'pac1' string literal would be a perfect match for that.
308 */
309
310 /* Process packets when their reception has completed. */
e2bcf2e3 311 while (devc->rdlen >= TC_POLL_LEN) {
269f1e0e
GS
312 ret = handle_poll_data(sdi);
313 if (ret != SR_OK)
314 return ret;
e2bcf2e3
GS
315 devc->rdlen -= TC_POLL_LEN;
316 if (devc->rdlen)
317 memmove(devc->buf, &devc->buf[TC_POLL_LEN], devc->rdlen);
269f1e0e 318 }
269f1e0e
GS
319
320 return SR_OK;
cae33a58
AS
321}
322
323SR_PRIV int rdtech_tc_receive_data(int fd, int revents, void *cb_data)
324{
325 struct sr_dev_inst *sdi;
326 struct dev_context *devc;
327 struct sr_serial_dev_inst *serial;
269f1e0e 328 int ret;
cae33a58
AS
329
330 (void)fd;
331
332 if (!(sdi = cb_data))
333 return TRUE;
cae33a58
AS
334 if (!(devc = sdi->priv))
335 return TRUE;
336
269f1e0e 337 /* Handle availability of receive data. */
cae33a58 338 serial = sdi->conn;
269f1e0e
GS
339 if (revents == G_IO_IN) {
340 ret = recv_poll_data(sdi, serial);
341 if (ret != SR_OK)
342 sr_dev_acquisition_stop(sdi);
343 }
cae33a58 344
269f1e0e 345 /* Check configured acquisition limits. */
cae33a58
AS
346 if (sr_sw_limits_check(&devc->limits)) {
347 sr_dev_acquisition_stop(sdi);
348 return TRUE;
349 }
350
269f1e0e
GS
351 /* Periodically retransmit measurement requests. */
352 (void)rdtech_tc_poll(sdi, FALSE);
cae33a58
AS
353
354 return TRUE;
355}