]> sigrok.org Git - libsigrok.git/blame - src/hardware/atorch/protocol.c
output/csv: use intermediate time_t var, silence compiler warning
[libsigrok.git] / src / hardware / atorch / protocol.c
CommitLineData
cc9653de
MP
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>
a5800e90 21#include <string.h>
cc9653de
MP
22#include "protocol.h"
23
a5800e90
MP
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
46static const uint8_t header_magic[] = {
47 0xff, 0x55,
48};
49
50static 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
58static 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
68static 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
73static 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
87static 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
99static 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
134static 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
163static 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
174static 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);
f40d8479 186 feed_queue_analog_submit_one(devc->feeds[i], val, 1);
a5800e90
MP
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
196SR_PRIV int atorch_probe(struct sr_serial_dev_inst *serial, struct dev_context *devc)
cc9653de 197{
a5800e90
MP
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
224SR_PRIV int atorch_receive_data_callback(int fd, int revents, void *cb_data)
225{
226 struct sr_dev_inst *sdi;
cc9653de 227 struct dev_context *devc;
a5800e90 228 const uint8_t *msg_ptr;
cc9653de
MP
229
230 (void)fd;
231
232 sdi = cb_data;
cc9653de 233 devc = sdi->priv;
a5800e90
MP
234
235 if (!sdi || !devc)
cc9653de
MP
236 return TRUE;
237
a5800e90
MP
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 }
cc9653de
MP
243 }
244
245 return TRUE;
246}