]> sigrok.org Git - libsigrok.git/blame - src/hardware/rdtech-tc/protocol.c
rdtech-tc: concentrate request transmit handling in one spot
[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
32#define SERIAL_WRITE_TIMEOUT_MS 1
33
cae33a58
AS
34#define TC_POLL_PERIOD_MS 100
35#define TC_TIMEOUT_MS 1000
36
f7c74c7f 37static const char *poll_cmd = "getva";
cae33a58 38
a780870c
GS
39/*
40 * Response data (raw sample data) consists of three adjacent chunks
41 * of 64 bytes each. These chunks start with their magic string, and
42 * end in a 32bit checksum field. Measurement values are scattered
43 * across these 192 bytes total size. All multi-byte integer values
44 * are represented in little endian format. Typical size is 32 bits.
45 */
46
47#define MAGIC_PAC1 0x70616331 /* 'pac1' */
48#define MAGIC_PAC2 0x70616332 /* 'pac2' */
49#define MAGIC_PAC3 0x70616333 /* 'pac3' */
cae33a58 50
cae33a58 51#define PAC_LEN 64
a780870c 52#define PAC_CRC_POS (PAC_LEN - sizeof(uint32_t))
cae33a58
AS
53
54/* Offset to PAC block from start of poll data */
55#define OFF_PAC1 (0 * PAC_LEN)
56#define OFF_PAC2 (1 * PAC_LEN)
57#define OFF_PAC3 (2 * PAC_LEN)
a780870c 58#define TC_POLL_LEN (3 * PAC_LEN)
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
68static const uint8_t AES_KEY[] = {
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
75static const struct binary_analog_channel rdtech_tc_channels[] = {
76 { "V", { 0 + 48, BVT_LE_UINT32, 1e-4, }, 4, SR_MQ_VOLTAGE, SR_UNIT_VOLT },
77 { "I", { 0 + 52, BVT_LE_UINT32, 1e-5, }, 5, SR_MQ_CURRENT, SR_UNIT_AMPERE },
78 { "D+", { 64 + 32, BVT_LE_UINT32, 1e-2, }, 2, SR_MQ_VOLTAGE, SR_UNIT_VOLT },
79 { "D-", { 64 + 36, BVT_LE_UINT32, 1e-2, }, 2, SR_MQ_VOLTAGE, SR_UNIT_VOLT },
80 { "E0", { 64 + 12, BVT_LE_UINT32, 1e-3, }, 3, SR_MQ_ENERGY, SR_UNIT_WATT_HOUR },
81 { "E1", { 64 + 20, BVT_LE_UINT32, 1e-3, }, 3, SR_MQ_ENERGY, SR_UNIT_WATT_HOUR },
0988d4ae 82 ALL_ZERO,
cae33a58
AS
83};
84
a780870c 85static gboolean check_pac_crc(uint8_t *data)
cae33a58
AS
86{
87 uint16_t crc;
88 uint32_t crc_field;
89
a780870c
GS
90 crc = sr_crc16(SR_CRC16_DEFAULT_INIT, data, PAC_CRC_POS);
91 crc_field = read_u32le(&data[PAC_CRC_POS]);
cae33a58
AS
92 if (crc != crc_field) {
93 sr_spew("CRC error. Calculated: %0x" PRIx16 ", expected: %0x" PRIx32,
94 crc, crc_field);
a780870c 95 return FALSE;
cae33a58 96 }
a780870c
GS
97
98 return TRUE;
cae33a58
AS
99}
100
5955b58c 101static int process_poll_pkt(struct dev_context *devc, uint8_t *dst)
cae33a58
AS
102{
103 struct aes256_ctx ctx;
a780870c 104 gboolean ok;
cae33a58
AS
105
106 aes256_set_decrypt_key(&ctx, AES_KEY);
107 aes256_decrypt(&ctx, TC_POLL_LEN, dst, devc->buf);
108
a780870c
GS
109 ok = TRUE;
110 ok &= read_u32be(&dst[OFF_PAC1]) == MAGIC_PAC1;
111 ok &= read_u32be(&dst[OFF_PAC2]) == MAGIC_PAC2;
112 ok &= read_u32be(&dst[OFF_PAC3]) == MAGIC_PAC3;
113 if (!ok) {
114 sr_err("Invalid poll response packet (magic values).");
115 return SR_ERR_DATA;
cae33a58
AS
116 }
117
a780870c
GS
118 ok &= check_pac_crc(&dst[OFF_PAC1]);
119 ok &= check_pac_crc(&dst[OFF_PAC2]);
120 ok &= check_pac_crc(&dst[OFF_PAC3]);
121 if (!ok) {
122 sr_err("Invalid poll response packet (checksum).");
123 return SR_ERR_DATA;
cae33a58
AS
124 }
125
126 return SR_OK;
127}
128
5955b58c 129SR_PRIV int rdtech_tc_probe(struct sr_serial_dev_inst *serial, struct dev_context *devc)
cae33a58
AS
130{
131 int len;
132 uint8_t poll_pkt[TC_POLL_LEN];
133
f7c74c7f 134 if (serial_write_blocking(serial, poll_cmd, strlen(poll_cmd),
5955b58c 135 SERIAL_WRITE_TIMEOUT_MS) < 0) {
a780870c 136 sr_err("Failed to send probe request.");
cae33a58
AS
137 return SR_ERR;
138 }
139
140 len = serial_read_blocking(serial, devc->buf, TC_POLL_LEN, TC_TIMEOUT_MS);
141 if (len != TC_POLL_LEN) {
142 sr_err("Failed to read probe response.");
143 return SR_ERR;
144 }
145
146 if (process_poll_pkt(devc, poll_pkt) != SR_OK) {
147 sr_err("Unrecognized TC device!");
148 return SR_ERR;
149 }
150
151 devc->channels = 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;
cae33a58 164
a82490b6
GS
165 devc = sdi->priv;
166 now = g_get_monotonic_time() / 1000;
167 elapsed = now - devc->cmd_sent_at;
168 if (!force && elapsed < TC_POLL_PERIOD_MS)
169 return SR_OK;
170
c1f9428a 171 serial = sdi->conn;
f7c74c7f 172 if (serial_write_blocking(serial, poll_cmd, strlen(poll_cmd),
5955b58c 173 SERIAL_WRITE_TIMEOUT_MS) < 0) {
cae33a58
AS
174 sr_err("Unable to send poll request.");
175 return SR_ERR;
176 }
a82490b6 177 devc->cmd_sent_at = now;
cae33a58
AS
178
179 return SR_OK;
180}
181
182static void handle_poll_data(const struct sr_dev_inst *sdi)
183{
c1f9428a 184 struct dev_context *devc;
cae33a58 185 uint8_t poll_pkt[TC_POLL_LEN];
63f46e3e 186 size_t i;
cae33a58
AS
187 GSList *ch;
188
c1f9428a 189 devc = sdi->priv;
63f46e3e 190 sr_spew("Received poll packet (len: %zu).", devc->buflen);
cae33a58 191 if (devc->buflen != TC_POLL_LEN) {
63f46e3e 192 sr_err("Unexpected poll packet length: %zu", devc->buflen);
cae33a58
AS
193 return;
194 }
195
196 if (process_poll_pkt(devc, poll_pkt) != SR_OK) {
197 sr_err("Failed to process poll packet.");
198 return;
199 }
200
9e0333f0
GS
201 i = 0;
202 for (ch = sdi->channels; ch; ch = g_slist_next(ch)) {
cae33a58 203 bv_send_analog_channel(sdi, ch->data,
5955b58c 204 &devc->channels[i], poll_pkt, TC_POLL_LEN);
9e0333f0 205 i++;
5955b58c 206 }
cae33a58
AS
207
208 sr_sw_limits_update_samples_read(&devc->limits, 1);
209}
210
211static void recv_poll_data(struct sr_dev_inst *sdi, struct sr_serial_dev_inst *serial)
212{
c1f9428a 213 struct dev_context *devc;
cae33a58
AS
214 int len;
215
216 /* Serial data arrived. */
c1f9428a 217 devc = sdi->priv;
cae33a58
AS
218 while (devc->buflen < TC_POLL_LEN) {
219 len = serial_read_nonblocking(serial, devc->buf + devc->buflen, 1);
220 if (len < 1)
221 return;
222
223 devc->buflen++;
224 }
225
226 if (devc->buflen == TC_POLL_LEN)
227 handle_poll_data(sdi);
228
229 devc->buflen = 0;
230}
231
232SR_PRIV int rdtech_tc_receive_data(int fd, int revents, void *cb_data)
233{
234 struct sr_dev_inst *sdi;
235 struct dev_context *devc;
236 struct sr_serial_dev_inst *serial;
cae33a58
AS
237
238 (void)fd;
239
240 if (!(sdi = cb_data))
241 return TRUE;
cae33a58
AS
242 if (!(devc = sdi->priv))
243 return TRUE;
244
245 serial = sdi->conn;
246 if (revents == G_IO_IN)
247 recv_poll_data(sdi, serial);
248
249 if (sr_sw_limits_check(&devc->limits)) {
250 sr_dev_acquisition_stop(sdi);
251 return TRUE;
252 }
253
a82490b6 254 rdtech_tc_poll(sdi, FALSE);
cae33a58
AS
255
256 return TRUE;
257}