]> sigrok.org Git - libsigrok.git/blame - src/hardware/rdtech-tc/protocol.c
serial_bt: add TC66C to list of known names, suggest MTU value
[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
f7c74c7f 36static const char *poll_cmd = "getva";
cae33a58 37
a780870c
GS
38/*
39 * Response data (raw sample data) consists of three adjacent chunks
40 * of 64 bytes each. These chunks start with their magic string, and
41 * end in a 32bit checksum field. Measurement values are scattered
42 * across these 192 bytes total size. All multi-byte integer values
43 * are represented in little endian format. Typical size is 32 bits.
44 */
45
46#define MAGIC_PAC1 0x70616331 /* 'pac1' */
47#define MAGIC_PAC2 0x70616332 /* 'pac2' */
48#define MAGIC_PAC3 0x70616333 /* 'pac3' */
cae33a58 49
cae33a58 50#define PAC_LEN 64
a780870c 51#define PAC_CRC_POS (PAC_LEN - sizeof(uint32_t))
cae33a58
AS
52
53/* Offset to PAC block from start of poll data */
54#define OFF_PAC1 (0 * PAC_LEN)
55#define OFF_PAC2 (1 * PAC_LEN)
56#define OFF_PAC3 (2 * PAC_LEN)
a780870c 57#define TC_POLL_LEN (3 * PAC_LEN)
cae33a58
AS
58
59#define OFF_MODEL 4
60#define LEN_MODEL 4
61
62#define OFF_FW_VER 8
63#define LEN_FW_VER 4
64
65#define OFF_SERIAL 12
66
67static const uint8_t AES_KEY[] = {
68 0x58, 0x21, 0xfa, 0x56, 0x01, 0xb2, 0xf0, 0x26,
69 0x87, 0xff, 0x12, 0x04, 0x62, 0x2a, 0x4f, 0xb0,
70 0x86, 0xf4, 0x02, 0x60, 0x81, 0x6f, 0x9a, 0x0b,
71 0xa7, 0xf1, 0x06, 0x61, 0x9a, 0xb8, 0x72, 0x88,
72};
73
b3df7668
GS
74static const struct rdtech_tc_channel_desc rdtech_tc_channels[] = {
75 { "V", { 0 + 48, BVT_LE_UINT32, 1, }, { 100, 1e6, }, 4, SR_MQ_VOLTAGE, SR_UNIT_VOLT },
76 { "I", { 0 + 52, BVT_LE_UINT32, 1, }, { 10, 1e6, }, 5, SR_MQ_CURRENT, SR_UNIT_AMPERE },
77 { "D+", { 64 + 32, BVT_LE_UINT32, 1, }, { 10, 1e3, }, 2, SR_MQ_VOLTAGE, SR_UNIT_VOLT },
78 { "D-", { 64 + 36, BVT_LE_UINT32, 1, }, { 10, 1e3, }, 2, SR_MQ_VOLTAGE, SR_UNIT_VOLT },
79 { "E0", { 64 + 12, BVT_LE_UINT32, 1, }, { 1, 1e3, }, 3, SR_MQ_ENERGY, SR_UNIT_WATT_HOUR },
80 { "E1", { 64 + 20, BVT_LE_UINT32, 1, }, { 1, 1e3, }, 3, SR_MQ_ENERGY, SR_UNIT_WATT_HOUR },
cae33a58
AS
81};
82
a780870c 83static gboolean check_pac_crc(uint8_t *data)
cae33a58
AS
84{
85 uint16_t crc;
86 uint32_t crc_field;
87
a780870c
GS
88 crc = sr_crc16(SR_CRC16_DEFAULT_INIT, data, PAC_CRC_POS);
89 crc_field = read_u32le(&data[PAC_CRC_POS]);
cae33a58
AS
90 if (crc != crc_field) {
91 sr_spew("CRC error. Calculated: %0x" PRIx16 ", expected: %0x" PRIx32,
92 crc, crc_field);
a780870c 93 return FALSE;
cae33a58 94 }
a780870c
GS
95
96 return TRUE;
cae33a58
AS
97}
98
5955b58c 99static int process_poll_pkt(struct dev_context *devc, uint8_t *dst)
cae33a58
AS
100{
101 struct aes256_ctx ctx;
a780870c 102 gboolean ok;
cae33a58
AS
103
104 aes256_set_decrypt_key(&ctx, AES_KEY);
105 aes256_decrypt(&ctx, TC_POLL_LEN, dst, devc->buf);
106
a780870c
GS
107 ok = TRUE;
108 ok &= read_u32be(&dst[OFF_PAC1]) == MAGIC_PAC1;
109 ok &= read_u32be(&dst[OFF_PAC2]) == MAGIC_PAC2;
110 ok &= read_u32be(&dst[OFF_PAC3]) == MAGIC_PAC3;
111 if (!ok) {
112 sr_err("Invalid poll response packet (magic values).");
113 return SR_ERR_DATA;
cae33a58
AS
114 }
115
a780870c
GS
116 ok &= check_pac_crc(&dst[OFF_PAC1]);
117 ok &= check_pac_crc(&dst[OFF_PAC2]);
118 ok &= check_pac_crc(&dst[OFF_PAC3]);
119 if (!ok) {
120 sr_err("Invalid poll response packet (checksum).");
121 return SR_ERR_DATA;
cae33a58
AS
122 }
123
124 return SR_OK;
125}
126
5955b58c 127SR_PRIV int rdtech_tc_probe(struct sr_serial_dev_inst *serial, struct dev_context *devc)
cae33a58
AS
128{
129 int len;
130 uint8_t poll_pkt[TC_POLL_LEN];
131
861fa81f
GS
132 len = serial_write_blocking(serial,
133 poll_cmd, strlen(poll_cmd), WRITE_TO_MS);
134 if (len < 0) {
a780870c 135 sr_err("Failed to send probe request.");
cae33a58
AS
136 return SR_ERR;
137 }
138
861fa81f 139 len = serial_read_blocking(serial, devc->buf, TC_POLL_LEN, PROBE_TO_MS);
cae33a58
AS
140 if (len != TC_POLL_LEN) {
141 sr_err("Failed to read probe response.");
142 return SR_ERR;
143 }
144
145 if (process_poll_pkt(devc, poll_pkt) != SR_OK) {
146 sr_err("Unrecognized TC device!");
147 return SR_ERR;
148 }
149
150 devc->channels = rdtech_tc_channels;
b3df7668 151 devc->channel_count = ARRAY_SIZE(rdtech_tc_channels);
a780870c
GS
152 devc->dev_info.model_name = g_strndup((const char *)&poll_pkt[OFF_MODEL], LEN_MODEL);
153 devc->dev_info.fw_ver = g_strndup((const char *)&poll_pkt[OFF_FW_VER], LEN_FW_VER);
154 devc->dev_info.serial_num = read_u32le(&poll_pkt[OFF_SERIAL]);
cae33a58
AS
155
156 return SR_OK;
157}
158
a82490b6 159SR_PRIV int rdtech_tc_poll(const struct sr_dev_inst *sdi, gboolean force)
cae33a58 160{
c1f9428a 161 struct dev_context *devc;
a82490b6 162 int64_t now, elapsed;
c1f9428a 163 struct sr_serial_dev_inst *serial;
861fa81f 164 int len;
cae33a58 165
8817bd6f
GS
166 /*
167 * Don't send the request while receive data is being accumulated.
168 */
a82490b6 169 devc = sdi->priv;
8817bd6f
GS
170 if (!force && devc->buflen)
171 return SR_OK;
172
173 /*
174 * Send the request when the transmit interval was reached. Or
175 * when the caller forced the transmission.
176 */
a82490b6
GS
177 now = g_get_monotonic_time() / 1000;
178 elapsed = now - devc->cmd_sent_at;
861fa81f 179 if (!force && elapsed < POLL_PERIOD_MS)
a82490b6
GS
180 return SR_OK;
181
8817bd6f
GS
182 /*
183 * Transmit another measurement request. Only advance the
184 * interval after successful transmission.
185 */
c1f9428a 186 serial = sdi->conn;
861fa81f
GS
187 len = serial_write_blocking(serial,
188 poll_cmd, strlen(poll_cmd), WRITE_TO_MS);
189 if (len < 0) {
cae33a58
AS
190 sr_err("Unable to send poll request.");
191 return SR_ERR;
192 }
a82490b6 193 devc->cmd_sent_at = now;
cae33a58
AS
194
195 return SR_OK;
196}
197
269f1e0e 198static int handle_poll_data(struct sr_dev_inst *sdi)
cae33a58 199{
c1f9428a 200 struct dev_context *devc;
cae33a58 201 uint8_t poll_pkt[TC_POLL_LEN];
b3df7668
GS
202 size_t ch_idx;
203 const struct rdtech_tc_channel_desc *pch;
269f1e0e 204 int ret;
b3df7668 205 float v;
cae33a58 206
c1f9428a 207 devc = sdi->priv;
63f46e3e 208 sr_spew("Received poll packet (len: %zu).", devc->buflen);
8817bd6f
GS
209 if (devc->buflen < TC_POLL_LEN) {
210 sr_err("Insufficient poll packet length: %zu", devc->buflen);
269f1e0e 211 return SR_ERR_DATA;
cae33a58
AS
212 }
213
214 if (process_poll_pkt(devc, poll_pkt) != SR_OK) {
215 sr_err("Failed to process poll packet.");
269f1e0e 216 return SR_ERR_DATA;
cae33a58
AS
217 }
218
15ceaf9d
GS
219 ret = SR_OK;
220 std_session_send_df_frame_begin(sdi);
b3df7668
GS
221 for (ch_idx = 0; ch_idx < devc->channel_count; ch_idx++) {
222 pch = &devc->channels[ch_idx];
223 ret = bv_get_value(&v, &pch->spec, poll_pkt, TC_POLL_LEN);
224 if (ret != SR_OK)
15ceaf9d 225 break;
b3df7668 226 ret = feed_queue_analog_submit(devc->feeds[ch_idx], v, 1);
269f1e0e 227 if (ret != SR_OK)
15ceaf9d 228 break;
5955b58c 229 }
15ceaf9d 230 std_session_send_df_frame_end(sdi);
cae33a58 231
15ceaf9d 232 sr_sw_limits_update_frames_read(&devc->limits, 1);
269f1e0e
GS
233 if (sr_sw_limits_check(&devc->limits))
234 sr_dev_acquisition_stop(sdi);
235
15ceaf9d 236 return ret;
cae33a58
AS
237}
238
269f1e0e 239static int recv_poll_data(struct sr_dev_inst *sdi, struct sr_serial_dev_inst *serial)
cae33a58 240{
c1f9428a 241 struct dev_context *devc;
8817bd6f 242 size_t space;
cae33a58 243 int len;
269f1e0e 244 int ret;
cae33a58 245
8817bd6f 246 /* Receive data became available. Drain the transport layer. */
c1f9428a 247 devc = sdi->priv;
cae33a58 248 while (devc->buflen < TC_POLL_LEN) {
8817bd6f
GS
249 space = sizeof(devc->buf) - devc->buflen;
250 len = serial_read_nonblocking(serial,
251 &devc->buf[devc->buflen], space);
269f1e0e
GS
252 if (len < 0)
253 return SR_ERR_IO;
254 if (len == 0)
255 return SR_OK;
256 devc->buflen += len;
cae33a58
AS
257 }
258
8817bd6f
GS
259 /*
260 * TODO Want to (re-)synchronize to the packet stream? The
261 * 'pac1' string literal would be a perfect match for that.
262 */
263
264 /* Process packets when their reception has completed. */
265 while (devc->buflen >= TC_POLL_LEN) {
269f1e0e
GS
266 ret = handle_poll_data(sdi);
267 if (ret != SR_OK)
268 return ret;
8817bd6f
GS
269 devc->buflen -= TC_POLL_LEN;
270 if (devc->buflen)
271 memmove(&devc->buf[0], &devc->buf[TC_POLL_LEN], devc->buflen);
269f1e0e 272 }
269f1e0e
GS
273
274 return SR_OK;
cae33a58
AS
275}
276
277SR_PRIV int rdtech_tc_receive_data(int fd, int revents, void *cb_data)
278{
279 struct sr_dev_inst *sdi;
280 struct dev_context *devc;
281 struct sr_serial_dev_inst *serial;
269f1e0e 282 int ret;
cae33a58
AS
283
284 (void)fd;
285
286 if (!(sdi = cb_data))
287 return TRUE;
cae33a58
AS
288 if (!(devc = sdi->priv))
289 return TRUE;
290
269f1e0e 291 /* Handle availability of receive data. */
cae33a58 292 serial = sdi->conn;
269f1e0e
GS
293 if (revents == G_IO_IN) {
294 ret = recv_poll_data(sdi, serial);
295 if (ret != SR_OK)
296 sr_dev_acquisition_stop(sdi);
297 }
cae33a58 298
269f1e0e 299 /* Check configured acquisition limits. */
cae33a58
AS
300 if (sr_sw_limits_check(&devc->limits)) {
301 sr_dev_acquisition_stop(sdi);
302 return TRUE;
303 }
304
269f1e0e
GS
305 /* Periodically retransmit measurement requests. */
306 (void)rdtech_tc_poll(sdi, FALSE);
cae33a58
AS
307
308 return TRUE;
309}