]> sigrok.org Git - libsigrok.git/blob - src/hardware/rdtech-tc/protocol.c
rdtech-tc: alpha-sort and group include directives
[libsigrok.git] / src / hardware / rdtech-tc / protocol.c
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>
21
22 #include <glib.h>
23 #include <libsigrok/libsigrok.h>
24 #include <math.h>
25 #include <nettle/aes.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "libsigrok-internal.h"
30 #include "protocol.h"
31
32 #define SERIAL_WRITE_TIMEOUT_MS 1
33
34 #define TC_POLL_LEN 192
35 #define TC_POLL_PERIOD_MS 100
36 #define TC_TIMEOUT_MS 1000
37
38 static const char POLL_CMD[] = "getva";
39
40 #define MAGIC_PAC1 0x31636170UL
41 #define MAGIC_PAC2 0x32636170UL
42 #define MAGIC_PAC3 0x33636170UL
43
44 /* Length of PAC block excluding CRC */
45 #define PAC_DATA_LEN 60
46 /* Length of PAC block including CRC */
47 #define PAC_LEN 64
48
49 /* Offset to PAC block from start of poll data */
50 #define OFF_PAC1 (0 * PAC_LEN)
51 #define OFF_PAC2 (1 * PAC_LEN)
52 #define OFF_PAC3 (2 * PAC_LEN)
53
54 #define OFF_MODEL 4
55 #define LEN_MODEL 4
56
57 #define OFF_FW_VER 8
58 #define LEN_FW_VER 4
59
60 #define OFF_SERIAL 12
61
62 static const uint8_t AES_KEY[] = {
63         0x58, 0x21, 0xfa, 0x56, 0x01, 0xb2, 0xf0, 0x26,
64         0x87, 0xff, 0x12, 0x04, 0x62, 0x2a, 0x4f, 0xb0,
65         0x86, 0xf4, 0x02, 0x60, 0x81, 0x6f, 0x9a, 0x0b,
66         0xa7, 0xf1, 0x06, 0x61, 0x9a, 0xb8, 0x72, 0x88,
67 };
68
69 static const struct binary_analog_channel rdtech_tc_channels[] = {
70         { "V",  {   0 + 48, BVT_LE_UINT32, 1e-4, }, 4, SR_MQ_VOLTAGE, SR_UNIT_VOLT },
71         { "I",  {   0 + 52, BVT_LE_UINT32, 1e-5, }, 5, SR_MQ_CURRENT, SR_UNIT_AMPERE },
72         { "D+", {  64 + 32, BVT_LE_UINT32, 1e-2, }, 2, SR_MQ_VOLTAGE, SR_UNIT_VOLT },
73         { "D-", {  64 + 36, BVT_LE_UINT32, 1e-2, }, 2, SR_MQ_VOLTAGE, SR_UNIT_VOLT },
74         { "E0", {  64 + 12, BVT_LE_UINT32, 1e-3, }, 3, SR_MQ_ENERGY, SR_UNIT_WATT_HOUR },
75         { "E1", {  64 + 20, BVT_LE_UINT32, 1e-3, }, 3, SR_MQ_ENERGY, SR_UNIT_WATT_HOUR },
76         ALL_ZERO,
77 };
78
79 static int check_pac_crc(uint8_t *data)
80 {
81         uint16_t crc;
82         uint32_t crc_field;
83
84         crc = sr_crc16(SR_CRC16_DEFAULT_INIT, data, PAC_DATA_LEN);
85         crc_field = RL32(data + PAC_DATA_LEN);
86
87         if (crc != crc_field) {
88                 sr_spew("CRC error. Calculated: %0x" PRIx16 ", expected: %0x" PRIx32,
89                         crc, crc_field);
90                 return 0;
91         } else {
92                 return 1;
93         }
94 }
95
96 static int process_poll_pkt(struct dev_context  *devc, uint8_t *dst)
97 {
98         struct aes256_ctx ctx;
99
100         aes256_set_decrypt_key(&ctx, AES_KEY);
101         aes256_decrypt(&ctx, TC_POLL_LEN, dst, devc->buf);
102
103         if (RL32(dst + OFF_PAC1) != MAGIC_PAC1 ||
104             RL32(dst + OFF_PAC2) != MAGIC_PAC2 ||
105             RL32(dst + OFF_PAC3) != MAGIC_PAC3) {
106                 sr_err("Invalid poll packet magic values!");
107                 return SR_ERR;
108         }
109
110         if (!check_pac_crc(dst + OFF_PAC1) ||
111             !check_pac_crc(dst + OFF_PAC2) ||
112             !check_pac_crc(dst + OFF_PAC3)) {
113                 sr_err("Invalid poll checksum!");
114                 return SR_ERR;
115         }
116
117         return SR_OK;
118 }
119
120 SR_PRIV int rdtech_tc_probe(struct sr_serial_dev_inst *serial, struct dev_context  *devc)
121 {
122         int len;
123         uint8_t poll_pkt[TC_POLL_LEN];
124
125         if (serial_write_blocking(serial, &POLL_CMD, sizeof(POLL_CMD) - 1,
126                                   SERIAL_WRITE_TIMEOUT_MS) < 0) {
127                 sr_err("Unable to send probe request.");
128                 return SR_ERR;
129         }
130
131         len = serial_read_blocking(serial, devc->buf, TC_POLL_LEN, TC_TIMEOUT_MS);
132         if (len != TC_POLL_LEN) {
133                 sr_err("Failed to read probe response.");
134                 return SR_ERR;
135         }
136
137         if (process_poll_pkt(devc, poll_pkt) != SR_OK) {
138                 sr_err("Unrecognized TC device!");
139                 return SR_ERR;
140         }
141
142         devc->channels = rdtech_tc_channels;
143         devc->dev_info.model_name = g_strndup((const char *)poll_pkt + OFF_MODEL, LEN_MODEL);
144         devc->dev_info.fw_ver = g_strndup((const char *)poll_pkt + OFF_FW_VER, LEN_FW_VER);
145         devc->dev_info.serial_num = RL32(poll_pkt + OFF_SERIAL);
146
147         return SR_OK;
148 }
149
150 SR_PRIV int rdtech_tc_poll(const struct sr_dev_inst *sdi)
151 {
152         struct dev_context *devc = sdi->priv;
153         struct sr_serial_dev_inst *serial = sdi->conn;
154
155         if (serial_write_blocking(serial, &POLL_CMD, sizeof(POLL_CMD) - 1,
156                                   SERIAL_WRITE_TIMEOUT_MS) < 0) {
157                 sr_err("Unable to send poll request.");
158                 return SR_ERR;
159         }
160
161         devc->cmd_sent_at = g_get_monotonic_time() / 1000;
162
163         return SR_OK;
164 }
165
166 static void handle_poll_data(const struct sr_dev_inst *sdi)
167 {
168         struct dev_context *devc = sdi->priv;
169         uint8_t poll_pkt[TC_POLL_LEN];
170         int i;
171         GSList *ch;
172
173         sr_spew("Received poll packet (len: %d).", devc->buflen);
174         if (devc->buflen != TC_POLL_LEN) {
175                 sr_err("Unexpected poll packet length: %i", devc->buflen);
176                 return;
177         }
178
179         if (process_poll_pkt(devc, poll_pkt) != SR_OK) {
180                 sr_err("Failed to process poll packet.");
181                 return;
182         }
183
184         for (ch = sdi->channels, i = 0; ch; ch = g_slist_next(ch), i++) {
185                 bv_send_analog_channel(sdi, ch->data,
186                                        &devc->channels[i], poll_pkt, TC_POLL_LEN);
187         }
188
189         sr_sw_limits_update_samples_read(&devc->limits, 1);
190 }
191
192 static void recv_poll_data(struct sr_dev_inst *sdi, struct sr_serial_dev_inst *serial)
193 {
194         struct dev_context *devc = sdi->priv;
195         int len;
196
197         /* Serial data arrived. */
198         while (devc->buflen < TC_POLL_LEN) {
199                 len = serial_read_nonblocking(serial, devc->buf + devc->buflen, 1);
200                 if (len < 1)
201                         return;
202
203                 devc->buflen++;
204         }
205
206         if (devc->buflen == TC_POLL_LEN)
207                 handle_poll_data(sdi);
208
209         devc->buflen = 0;
210 }
211
212 SR_PRIV int rdtech_tc_receive_data(int fd, int revents, void *cb_data)
213 {
214         struct sr_dev_inst *sdi;
215         struct dev_context *devc;
216         struct sr_serial_dev_inst *serial;
217         int64_t now, elapsed;
218
219         (void)fd;
220
221         if (!(sdi = cb_data))
222                 return TRUE;
223
224         if (!(devc = sdi->priv))
225                 return TRUE;
226
227         serial = sdi->conn;
228         if (revents == G_IO_IN)
229                 recv_poll_data(sdi, serial);
230
231         if (sr_sw_limits_check(&devc->limits)) {
232                 sr_dev_acquisition_stop(sdi);
233                 return TRUE;
234         }
235
236         now = g_get_monotonic_time() / 1000;
237         elapsed = now - devc->cmd_sent_at;
238
239         if (elapsed > TC_POLL_PERIOD_MS)
240                 rdtech_tc_poll(sdi);
241
242         return TRUE;
243 }