]> sigrok.org Git - libsigrok.git/blob - src/hardware/atorch/protocol.c
output/csv: use intermediate time_t var, silence compiler warning
[libsigrok.git] / src / hardware / atorch / protocol.c
1 /*
2  * This file is part of the libsigrok project.
3  *
4  * Copyright (C) 2023 Mathieu Pilato <pilato.mathieu@free.fr>
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 #include <string.h>
22 #include "protocol.h"
23
24 /* Duration of scan. */
25 #define ATORCH_PROBE_TIMEOUT_MS 10000
26
27 /*
28  * Message layout:
29  * 2 magic header bytes
30  * 1 message type byte
31  * N payload bytes, determined by message type
32  */
33
34 /* Position of message type byte in a message. */
35 #define HEADER_MSGTYPE_IDX      2
36 #define PAYLOAD_START_IDX       3
37
38 /* Length of each message type. */
39 #define MSGLEN_REPORT   (4 + 32)
40 #define MSGLEN_REPLY    (4 + 4)
41 #define MSGLEN_COMMAND  (4 + 6)
42
43 /* Minimal length of a valid message. */
44 #define MSGLEN_MIN      4
45
46 static const uint8_t header_magic[] = {
47         0xff, 0x55,
48 };
49
50 static const struct atorch_channel_desc atorch_dc_power_meter_channels[] = {
51         { "V", { 4, BVT_BE_UINT24, }, { 100, 1e3, }, 1, SR_MQ_VOLTAGE, SR_UNIT_VOLT, SR_MQFLAG_DC, },
52         { "I", { 7, BVT_BE_UINT24, }, { 1, 1e3, }, 3, SR_MQ_CURRENT, SR_UNIT_AMPERE, SR_MQFLAG_DC, },
53         { "C", { 10, BVT_BE_UINT24, }, { 10, 1e3, }, 2, SR_MQ_ENERGY, SR_UNIT_AMPERE_HOUR, 0, },
54         { "E", { 13, BVT_BE_UINT32, }, { 10, 1, }, -2, SR_MQ_ENERGY, SR_UNIT_WATT_HOUR, 0, },
55         { "T", { 24, BVT_BE_UINT16, }, { 1, 1, }, 0, SR_MQ_TEMPERATURE, SR_UNIT_CELSIUS, 0, },
56 };
57
58 static const struct atorch_channel_desc atorch_usb_power_meter_channels[] = {
59         { "V", { 4, BVT_BE_UINT24, }, { 10, 1e3, }, 2, SR_MQ_VOLTAGE, SR_UNIT_VOLT, SR_MQFLAG_DC, },
60         { "I", { 7, BVT_BE_UINT24, }, { 10, 1e3, }, 2, SR_MQ_CURRENT, SR_UNIT_AMPERE, SR_MQFLAG_DC, },
61         { "C", { 10, BVT_BE_UINT24, }, { 1, 1e3, }, 3, SR_MQ_ENERGY, SR_UNIT_AMPERE_HOUR, 0, },
62         { "E", { 13, BVT_BE_UINT32, }, { 10, 1e3, }, 2, SR_MQ_ENERGY, SR_UNIT_WATT_HOUR, 0, },
63         { "D-", { 17, BVT_BE_UINT16, }, { 10, 1e3, }, 2, SR_MQ_VOLTAGE, SR_UNIT_VOLT, SR_MQFLAG_DC, },
64         { "D+", { 19, BVT_BE_UINT16, }, { 10, 1e3, }, 2, SR_MQ_VOLTAGE, SR_UNIT_VOLT, SR_MQFLAG_DC, },
65         { "T", { 21, BVT_BE_UINT16, }, { 1, 1, }, 0, SR_MQ_TEMPERATURE, SR_UNIT_CELSIUS, 0, },
66 };
67
68 static const struct atorch_device_profile atorch_profiles[] = {
69         { 0x02, "DC Meter", ARRAY_AND_SIZE(atorch_dc_power_meter_channels), },
70         { 0x03, "USB Meter", ARRAY_AND_SIZE(atorch_usb_power_meter_channels), },
71 };
72
73 static size_t get_length_for_msg_type(uint8_t msg_type)
74 {
75         switch (msg_type) {
76         case MSG_REPORT:
77                 return MSGLEN_REPORT;
78         case MSG_REPLY:
79                 return MSGLEN_REPLY;
80         case MSG_COMMAND:
81                 return MSGLEN_COMMAND;
82         default:
83                 return 0;
84         }
85 }
86
87 static void log_atorch_msg(const uint8_t *buf, size_t len)
88 {
89         GString *text;
90
91         if (sr_log_loglevel_get() < SR_LOG_DBG)
92                 return;
93
94         text = sr_hexdump_new(buf, len);
95         sr_dbg("Atorch msg: %s", text->str);
96         sr_hexdump_free(text);
97 }
98
99 static const uint8_t *locate_next_valid_msg(struct dev_context *devc)
100 {
101         uint8_t *valid_msg_ptr;
102         size_t valid_msg_len;
103         uint8_t *msg_ptr;
104
105         /* Enough byte to make a message? */
106         while (devc->rd_idx + MSGLEN_MIN <= devc->wr_idx) {
107                 /* Look for header magic. */
108                 msg_ptr = devc->buf + devc->rd_idx;
109                 if (memcmp(msg_ptr, header_magic, sizeof(header_magic)) != 0) {
110                         devc->rd_idx += 1;
111                         continue;
112                 }
113
114                 /* Determine msg type and length. */
115                 valid_msg_len = get_length_for_msg_type(msg_ptr[HEADER_MSGTYPE_IDX]);
116                 if (!valid_msg_len) {
117                         devc->rd_idx += 2;
118                         continue;
119                 }
120
121                 /* Do we have the complete message? */
122                 if (devc->rd_idx + valid_msg_len <= devc->wr_idx) {
123                         valid_msg_ptr = msg_ptr;
124                         devc->rd_idx += valid_msg_len;
125                         log_atorch_msg(valid_msg_ptr, valid_msg_len);
126                         return valid_msg_ptr;
127                 }
128
129                 return NULL;
130         }
131         return NULL;
132 }
133
134 static const uint8_t *receive_msg(struct sr_serial_dev_inst *serial,
135         struct dev_context *devc)
136 {
137         size_t len;
138         const uint8_t *valid_msg_ptr;
139
140         while (1) {
141                 /* Remove bytes already processed. */
142                 if (devc->rd_idx > 0) {
143                         len = devc->wr_idx - devc->rd_idx;
144                         memmove(devc->buf, devc->buf + devc->rd_idx, len);
145                         devc->wr_idx -= devc->rd_idx;
146                         devc->rd_idx = 0;
147                 }
148
149                 /* Read more bytes to process. */
150                 len = ATORCH_BUFSIZE - devc->wr_idx;
151                 len = serial_read_nonblocking(serial, devc->buf + devc->wr_idx, len);
152                 if (len <= 0)
153                         return NULL;
154                 devc->wr_idx += len;
155
156                 /* Locate next start of message. */
157                 valid_msg_ptr = locate_next_valid_msg(devc);
158                 if (valid_msg_ptr)
159                         return valid_msg_ptr;
160         }
161 }
162
163 static const struct atorch_device_profile *find_profile_for_device_type(uint8_t dev_type)
164 {
165         size_t i;
166
167         for (i = 0; i < ARRAY_SIZE(atorch_profiles); i++) {
168                 if (atorch_profiles[i].device_type == dev_type)
169                         return &atorch_profiles[i];
170         }
171         return NULL;
172 }
173
174 static void parse_report_msg(struct sr_dev_inst *sdi, const uint8_t *report_ptr)
175 {
176         struct dev_context *devc;
177         float val;
178         size_t i;
179
180         devc = sdi->priv;
181
182         std_session_send_df_frame_begin(sdi);
183
184         for (i = 0; i < devc->profile->channel_count; i++) {
185                 bv_get_value(&val, &devc->profile->channels[i].spec, report_ptr);
186                 feed_queue_analog_submit_one(devc->feeds[i], val, 1);
187         }
188
189         std_session_send_df_frame_end(sdi);
190
191         sr_sw_limits_update_frames_read(&devc->limits, 1);
192         if (sr_sw_limits_check(&devc->limits))
193                 sr_dev_acquisition_stop(sdi);
194 }
195
196 SR_PRIV int atorch_probe(struct sr_serial_dev_inst *serial, struct dev_context *devc)
197 {
198         int64_t deadline_us;
199         const struct atorch_device_profile *p;
200         const uint8_t *msg_ptr;
201
202         devc->wr_idx = 0;
203         devc->rd_idx = 0;
204
205         deadline_us = g_get_monotonic_time();
206         deadline_us += ATORCH_PROBE_TIMEOUT_MS * 1000;
207         while (g_get_monotonic_time() <= deadline_us) {
208                 msg_ptr = receive_msg(serial, devc);
209                 if (msg_ptr && msg_ptr[HEADER_MSGTYPE_IDX] == MSG_REPORT) {
210                         p = find_profile_for_device_type(msg_ptr[PAYLOAD_START_IDX]);
211                         if (p) {
212                                 devc->profile = p;
213                                 return SR_OK;
214                         }
215                         sr_err("Unrecognized device type (0x%.4" PRIx8 ").",
216                                devc->buf[PAYLOAD_START_IDX]);
217                         return SR_ERR;
218                 }
219                 g_usleep(100 * 1000);
220         }
221         return SR_ERR;
222 }
223
224 SR_PRIV int atorch_receive_data_callback(int fd, int revents, void *cb_data)
225 {
226         struct sr_dev_inst *sdi;
227         struct dev_context *devc;
228         const uint8_t *msg_ptr;
229
230         (void)fd;
231
232         sdi = cb_data;
233         devc = sdi->priv;
234
235         if (!sdi || !devc)
236                 return TRUE;
237
238         if (revents & G_IO_IN) {
239                 while ((msg_ptr = receive_msg(sdi->conn, devc))) {
240                         if (msg_ptr[HEADER_MSGTYPE_IDX] == MSG_REPORT)
241                                 parse_report_msg(sdi, msg_ptr);
242                 }
243         }
244
245         return TRUE;
246 }