2 * This file is part of the libsigrok project.
4 * Copyright (C) 2023 Mathieu Pilato <pilato.mathieu@free.fr>
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.
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.
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/>.
24 /* Duration of scan. */
25 #define ATORCH_PROBE_TIMEOUT_MS 10000
29 * 2 magic header bytes
31 * N payload bytes, determined by message type
34 /* Position of message type byte in a message. */
35 #define HEADER_MSGTYPE_IDX 2
36 #define PAYLOAD_START_IDX 3
38 /* Length of each message type. */
39 #define MSGLEN_REPORT (4 + 32)
40 #define MSGLEN_REPLY (4 + 4)
41 #define MSGLEN_COMMAND (4 + 6)
43 /* Minimal length of a valid message. */
46 static const uint8_t header_magic[] = {
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, },
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, },
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), },
73 static size_t get_length_for_msg_type(uint8_t msg_type)
81 return MSGLEN_COMMAND;
87 static void log_atorch_msg(const uint8_t *buf, size_t len)
91 if (sr_log_loglevel_get() < SR_LOG_DBG)
94 text = sr_hexdump_new(buf, len);
95 sr_dbg("Atorch msg: %s", text->str);
96 sr_hexdump_free(text);
99 static const uint8_t *locate_next_valid_msg(struct dev_context *devc)
101 uint8_t *valid_msg_ptr;
102 size_t valid_msg_len;
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) {
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) {
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;
134 static const uint8_t *receive_msg(struct sr_serial_dev_inst *serial,
135 struct dev_context *devc)
138 const uint8_t *valid_msg_ptr;
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;
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);
156 /* Locate next start of message. */
157 valid_msg_ptr = locate_next_valid_msg(devc);
159 return valid_msg_ptr;
163 static const struct atorch_device_profile *find_profile_for_device_type(uint8_t dev_type)
167 for (i = 0; i < ARRAY_SIZE(atorch_profiles); i++) {
168 if (atorch_profiles[i].device_type == dev_type)
169 return &atorch_profiles[i];
174 static void parse_report_msg(struct sr_dev_inst *sdi, const uint8_t *report_ptr)
176 struct dev_context *devc;
182 std_session_send_df_frame_begin(sdi);
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);
189 std_session_send_df_frame_end(sdi);
191 sr_sw_limits_update_frames_read(&devc->limits, 1);
192 if (sr_sw_limits_check(&devc->limits))
193 sr_dev_acquisition_stop(sdi);
196 SR_PRIV int atorch_probe(struct sr_serial_dev_inst *serial, struct dev_context *devc)
199 const struct atorch_device_profile *p;
200 const uint8_t *msg_ptr;
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]);
215 sr_err("Unrecognized device type (0x%.4" PRIx8 ").",
216 devc->buf[PAYLOAD_START_IDX]);
219 g_usleep(100 * 1000);
224 SR_PRIV int atorch_receive_data_callback(int fd, int revents, void *cb_data)
226 struct sr_dev_inst *sdi;
227 struct dev_context *devc;
228 const uint8_t *msg_ptr;
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);