]>
Commit | Line | Data |
---|---|---|
400bc4ff GS |
1 | /* |
2 | * This file is part of the libsigrok project. | |
3 | * | |
4 | * Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org> | |
5 | * Copyright (C) 2019-2020 Gerhard Sittig <gerhard.sittig@gmx.net> | |
6 | * | |
7 | * This program is free software: you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation, either version 3 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 | */ | |
20 | ||
21 | /** | |
22 | * @file | |
23 | * | |
24 | * Brymen BM52x serial protocol parser. The USB protocol (for the cable) | |
25 | * and the packet description (for the meter) were retrieved from: | |
a67016f5 FR |
26 | * http://www.brymen.com/Download2.html |
27 | * http://www.brymen.com/PD02BM520s_protocolDL.html | |
28 | * http://www.brymen.com/images/DownloadList/ProtocolList/BM520-BM520s_List/BM520-BM520s-10000-count-professional-dual-display-mobile-logging-DMMs-protocol.zip | |
70a23b50 | 29 | * http://web.archive.org/web/20180119175327/http://brymen.com/product-html/images/DownloadList/ProtocolList/BM520-BM520s_List/BM520-BM520s-10000-count-professional-dual-display-mobile-logging-DMMs-protocol.zip |
0931639a | 30 | * |
6bee394d GS |
31 | * This parser was initially created for BM520s devices and tested with |
32 | * BM525s. The Brymen BM820s family of devices uses the same protocol, | |
33 | * with just 0x82 instead of 0x52 in request packets and in the fixed | |
34 | * fields of the responses. Which means that the packet parser can get | |
35 | * shared among the BM520s and BM820s devices, but validity check needs | |
36 | * to be individual, and the "wrong" packet request will end up without | |
37 | * a response. Compared to BM520s the BM820s has dBm (in the protocol) | |
38 | * and NCV (not seen in the protocol) and is non-logging (live only). | |
39 | * BM820s support was tested with BM829s. | |
40 | * | |
0931639a GS |
41 | * The parser implementation was tested with a Brymen BM525s meter. Some |
42 | * of the responses differ from the vendor's documentation: | |
43 | * - Recording session total byte counts don't start after the byte count | |
44 | * field, but instead include this field and the model ID (spans _every_ | |
45 | * byte in the stream). | |
46 | * - Recording session start/end markers are referred to as DLE, STX, | |
47 | * and ETX. Observed traffic instead sends 0xee, 0xa0, and 0xc0. | |
400bc4ff GS |
48 | */ |
49 | ||
50 | /* | |
51 | * TODO | |
400bc4ff GS |
52 | * - Some of the meter's functions and indications cannot get expressed |
53 | * by means of sigrok MQ and flags terms. Some indicator's meaning is | |
54 | * unknown or uncertain, and thus their state is not evaluated. | |
55 | * - MAX-MIN, the span between extreme values, referred to as Vp-p. | |
56 | * - AVG is not available in BM525s and BM521s. | |
57 | * - LoZ, eliminating ghost voltages. | |
58 | * - LPF, low pass filter. | |
400bc4ff GS |
59 | * - low battery, emits sr_warn() but isn't seen in the feed. |
60 | * - @, 4-20mA loop, % (main display, left hand side), Hi/Lo. Some of | |
61 | * these are in the vendor's documentation for the DMM packet but not | |
62 | * supported by the BM525s device which motivated the creation of the | |
63 | * parser's and was used to test its operation. | |
64 | * - It's a guess that the many undocumented bits (44 of them) are | |
65 | * related to the bargraph (40 ticks, overflow, sign, 6/10 scale). | |
66 | * - Should T1-T2 have a delta ("relative") decoration? But the meter's | |
67 | * "relative" feature is flexible, accepts any display value as the | |
68 | * reference, including min/max/diff when displayed upon activation. | |
69 | * - The "beep jack" displays "InEr" in the secondary display. This is | |
70 | * not caught here, no PC side message gets emitted. | |
0931639a GS |
71 | * - Support for recordings is mostly untested. It was written to the |
72 | * letter of the vendor documentation, but was not verified to work | |
73 | * for all of the many meter's modes including ranges. Inspection of | |
74 | * the full byte stream is necessary on one hand since random access | |
75 | * is not available, and useful on the other hand for consistency | |
76 | * checks. | |
400bc4ff GS |
77 | */ |
78 | ||
79 | #include <config.h> | |
80 | #include <libsigrok/libsigrok.h> | |
81 | #include "libsigrok-internal.h" | |
82 | #include <math.h> | |
83 | #include <string.h> | |
0931639a | 84 | #include <strings.h> |
400bc4ff GS |
85 | |
86 | #define LOG_PREFIX "brymen-bm52x" | |
87 | ||
0931639a GS |
88 | /* |
89 | * DMM specific device options, and state keeping. All of it is related | |
90 | * to recorded information in contrast to live readings. There also are | |
91 | * four types of requesting HID reports that need to be sent. | |
92 | */ | |
93 | ||
94 | static const uint32_t devopts[] = { | |
95 | SR_CONF_CONTINUOUS, | |
96 | SR_CONF_LIMIT_SAMPLES | SR_CONF_SET, | |
97 | SR_CONF_LIMIT_MSEC | SR_CONF_SET, | |
98 | SR_CONF_DATA_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, | |
99 | }; | |
100 | ||
101 | struct brymen_bm52x_state { | |
102 | size_t sess_idx; | |
103 | struct { | |
104 | uint8_t buff[2 * 32]; | |
105 | size_t fill_pos; | |
106 | size_t read_pos; | |
107 | size_t remain; | |
108 | } rsp; | |
109 | const struct sr_dev_inst *sdi; | |
110 | }; | |
111 | ||
112 | enum bm52x_reqtype { | |
6bee394d GS |
113 | REQ_LIVE_READ_520, |
114 | REQ_LIVE_READ_820, | |
0931639a GS |
115 | REQ_REC_HEAD, |
116 | REQ_REC_NEXT, | |
117 | REQ_REC_CURR, | |
118 | }; | |
119 | ||
400bc4ff | 120 | #ifdef HAVE_SERIAL_COMM |
0931639a | 121 | static int bm52x_send_req(struct sr_serial_dev_inst *serial, enum bm52x_reqtype t) |
400bc4ff | 122 | { |
6bee394d GS |
123 | static const uint8_t req_live_520[] = { 0x00, 0x00, 0x52, 0x66, }; |
124 | static const uint8_t req_live_820[] = { 0x00, 0x00, 0x82, 0x66, }; | |
0931639a GS |
125 | static const uint8_t req_head[] = { 0x00, 0x00, 0x52, 0x88, }; |
126 | static const uint8_t req_next[] = { 0x00, 0x00, 0x52, 0x89, }; | |
127 | static const uint8_t req_curr[] = { 0x00, 0x00, 0x52, 0x8a, }; | |
128 | static const uint8_t *req_bytes[] = { | |
6bee394d GS |
129 | [REQ_LIVE_READ_520] = req_live_520, |
130 | [REQ_LIVE_READ_820] = req_live_820, | |
0931639a GS |
131 | [REQ_REC_HEAD] = req_head, |
132 | [REQ_REC_NEXT] = req_next, | |
133 | [REQ_REC_CURR] = req_curr, | |
134 | }; | |
6bee394d | 135 | static const size_t req_len = ARRAY_SIZE(req_live_520); |
0931639a GS |
136 | |
137 | const uint8_t *p; | |
138 | size_t l; | |
139 | int ret; | |
400bc4ff | 140 | |
0931639a GS |
141 | if (t >= ARRAY_SIZE(req_bytes)) |
142 | return SR_ERR_ARG; | |
143 | p = req_bytes[t]; | |
144 | l = req_len; | |
145 | ret = serial_write_nonblocking(serial, p, l); | |
146 | if (ret < 0) | |
147 | return ret; | |
148 | if ((size_t)ret != l) | |
149 | return SR_ERR_IO; | |
400bc4ff GS |
150 | |
151 | return SR_OK; | |
152 | } | |
0931639a GS |
153 | |
154 | SR_PRIV int sr_brymen_bm52x_packet_request(struct sr_serial_dev_inst *serial) | |
155 | { | |
6bee394d GS |
156 | return bm52x_send_req(serial, REQ_LIVE_READ_520); |
157 | } | |
158 | ||
159 | SR_PRIV int sr_brymen_bm82x_packet_request(struct sr_serial_dev_inst *serial) | |
160 | { | |
161 | return bm52x_send_req(serial, REQ_LIVE_READ_820); | |
0931639a | 162 | } |
400bc4ff GS |
163 | #endif |
164 | ||
0931639a GS |
165 | /* |
166 | * The following code interprets live readings ("real-time download") | |
167 | * which arrive in the "traditional" bitmap for LCD segments. Reading | |
168 | * previously recorded measurements ("memory data sets") differs a lot | |
169 | * and is handled in other code paths. | |
170 | */ | |
171 | ||
400bc4ff GS |
172 | SR_PRIV gboolean sr_brymen_bm52x_packet_valid(const uint8_t *buf) |
173 | { | |
174 | if (buf[16] != 0x52) | |
175 | return FALSE; | |
176 | if (buf[17] != 0x52) | |
177 | return FALSE; | |
178 | if (buf[18] != 0x52) | |
179 | return FALSE; | |
180 | if (buf[19] != 0x52) | |
181 | return FALSE; | |
182 | ||
183 | return TRUE; | |
184 | } | |
185 | ||
6bee394d GS |
186 | SR_PRIV gboolean sr_brymen_bm82x_packet_valid(const uint8_t *buf) |
187 | { | |
188 | if (buf[16] != 0x82) | |
189 | return FALSE; | |
190 | if (buf[17] != 0x82) | |
191 | return FALSE; | |
192 | if (buf[18] != 0x82) | |
193 | return FALSE; | |
194 | if (buf[19] != 0x82) | |
195 | return FALSE; | |
196 | ||
197 | return TRUE; | |
198 | } | |
199 | ||
400bc4ff GS |
200 | /* |
201 | * Data bytes in the DMM packet encode LCD segments in an unusual order | |
202 | * (bgcpafed) and in an unusual position (bit 4 being the decimal point | |
203 | * for some digits, an additional indicator for others). Fortunately all | |
204 | * eight digits encode their segments in identical ways across the bytes. | |
205 | * | |
206 | * These routines convert LCD segments to characters, and a section of the | |
207 | * DMM packet (which corresponds to the primary or secondary display) to | |
208 | * the text representation of the measurement's value, before regular text | |
209 | * to number conversion is applied, and SI units and their prefixes get | |
210 | * derived from more indicators. It's important to keep in mind similar | |
211 | * indicators exist for main and secondary displays in different locations. | |
212 | */ | |
213 | ||
214 | static char brymen_bm52x_parse_digit(uint8_t b) | |
215 | { | |
216 | switch (b & ~0x10) { | |
217 | /* Sign. */ | |
218 | case 0x40: /* ------g */ return '-'; | |
219 | /* Decimal digits. */ | |
220 | case 0xaf: /* abcdef- */ return '0'; | |
221 | case 0xa0: /* -bc---- */ return '1'; | |
222 | case 0xcb: /* ab-de-g */ return '2'; | |
223 | case 0xe9: /* abcd--g */ return '3'; | |
224 | case 0xe4: /* -bc--fg */ return '4'; | |
225 | case 0x6d: /* a-cd-fg */ return '5'; | |
226 | case 0x6f: /* a-cdefg */ return '6'; | |
227 | case 0xa8: /* abc---- */ return '7'; | |
228 | case 0xef: /* abcdefg */ return '8'; | |
229 | case 0xed: /* abcd-fg */ return '9'; | |
230 | /* Temperature units. */ | |
231 | case 0x0f: /* a--def- */ return 'C'; | |
232 | case 0x4e: /* a---efg */ return 'F'; | |
233 | /* OL condition, and diode and "Auto" modes. */ | |
234 | case 0x07: /* ---def- */ return 'L'; | |
235 | case 0xe3: /* -bcde-g */ return 'd'; | |
236 | case 0x20: /* --c---- */ return 'i'; | |
237 | case 0x63: /* --cde-g */ return 'o'; | |
238 | case 0xee: /* abc-efg */ return 'A'; | |
239 | case 0x23: /* --cde-- */ return 'u'; | |
240 | case 0x47: /* ---defg */ return 't'; | |
241 | /* Blank digit. */ | |
242 | case 0x00: /* ------- */ return '\0'; | |
243 | /* Invalid or unknown segment combination. */ | |
244 | default: | |
245 | sr_warn("Unknown encoding for digit: 0x%02x.", b); | |
246 | return '\0'; | |
247 | } | |
248 | } | |
249 | ||
250 | static int brymen_bm52x_parse_digits(const uint8_t *pkt, size_t pktlen, | |
251 | char *txtbuf, float *value, char *temp_unit, int *digits, int signflag) | |
252 | { | |
253 | uint8_t byte; | |
254 | char *txtptr, txtchar; | |
255 | size_t pos; | |
256 | int ret; | |
257 | ||
258 | txtptr = txtbuf; | |
259 | if (digits) | |
260 | *digits = INT_MIN; | |
261 | ||
262 | if (pkt[0] & signflag) | |
263 | *txtptr++ = '-'; | |
264 | for (pos = 0; pos < pktlen; pos++) { | |
265 | byte = pkt[1 + pos]; | |
266 | txtchar = brymen_bm52x_parse_digit(byte); | |
267 | if (pos == 3 && (txtchar == 'C' || txtchar == 'F')) { | |
268 | if (temp_unit) | |
269 | *temp_unit = txtchar; | |
270 | } else if (txtchar) { | |
271 | *txtptr++ = txtchar; | |
272 | if (digits) | |
273 | (*digits)++; | |
274 | } | |
275 | if (pos < 3 && (byte & 0x10)) { | |
276 | *txtptr++ = '.'; | |
277 | if (digits) | |
278 | *digits = 0; | |
279 | } | |
280 | } | |
281 | *txtptr = '\0'; | |
282 | ||
283 | if (digits && *digits < 0) | |
284 | *digits = 0; | |
285 | ||
286 | ret = value ? sr_atof_ascii(txtbuf, value) : SR_OK; | |
287 | if (ret != SR_OK) { | |
288 | sr_dbg("invalid float string: '%s'", txtbuf); | |
289 | return ret; | |
290 | } | |
291 | ||
292 | return SR_OK; | |
293 | } | |
294 | ||
295 | /* | |
296 | * Extract the measurement value and its properties for one of the | |
297 | * meter's displays from the DMM packet. | |
298 | */ | |
299 | static void brymen_bm52x_parse(const uint8_t *buf, float *floatval, | |
300 | struct sr_datafeed_analog *analog, size_t ch_idx) | |
301 | { | |
302 | char txtbuf[16], temp_unit; | |
303 | int ret, digits, scale; | |
304 | int is_diode, is_auto, is_no_temp, is_ol, is_db, is_main_milli; | |
305 | int is_mm_max, is_mm_min, is_mm_avg, is_mm_dash; | |
306 | ||
307 | temp_unit = '\0'; | |
308 | if (ch_idx == 0) { | |
309 | /* | |
310 | * Main display. Note that _some_ of the second display's | |
311 | * indicators are involved in the inspection of the _first_ | |
312 | * display's measurement value. So we have to get the | |
313 | * second display's text buffer here, too. | |
314 | */ | |
315 | (void)brymen_bm52x_parse_digits(&buf[7], 4, txtbuf, | |
316 | NULL, NULL, NULL, 0); | |
317 | is_diode = strcmp(txtbuf, "diod") == 0; | |
318 | is_auto = strcmp(txtbuf, "Auto") == 0; | |
319 | ret = brymen_bm52x_parse_digits(&buf[2], 4, txtbuf, | |
320 | floatval, &temp_unit, &digits, 0x80); | |
321 | is_ol = strstr(txtbuf, "0L") || strstr(txtbuf, "0.L"); | |
322 | is_no_temp = strcmp(txtbuf, "---C") == 0; | |
323 | is_no_temp |= strcmp(txtbuf, "---F") == 0; | |
324 | if (ret != SR_OK && !is_ol) | |
325 | return; | |
326 | ||
327 | /* SI unit, derived from meter's current function. */ | |
328 | is_db = buf[6] & 0x10; | |
329 | is_main_milli = buf[14] & 0x40; | |
330 | if (buf[14] & 0x20) { | |
331 | analog->meaning->mq = SR_MQ_VOLTAGE; | |
332 | analog->meaning->unit = SR_UNIT_VOLT; | |
333 | if (is_diode) { | |
334 | analog->meaning->mqflags |= SR_MQFLAG_DIODE; | |
335 | analog->meaning->mqflags |= SR_MQFLAG_DC; | |
336 | } | |
337 | } else if (buf[14] & 0x10) { | |
338 | analog->meaning->mq = SR_MQ_CURRENT; | |
339 | analog->meaning->unit = SR_UNIT_AMPERE; | |
340 | } else if (buf[14] & 0x01) { | |
341 | analog->meaning->mq = SR_MQ_CAPACITANCE; | |
342 | analog->meaning->unit = SR_UNIT_FARAD; | |
343 | } else if (buf[14] & 0x02) { | |
344 | analog->meaning->mq = SR_MQ_CONDUCTANCE; | |
345 | analog->meaning->unit = SR_UNIT_SIEMENS; | |
346 | } else if (buf[13] & 0x10) { | |
347 | analog->meaning->mq = SR_MQ_FREQUENCY; | |
348 | analog->meaning->unit = SR_UNIT_HERTZ; | |
349 | } else if (buf[7] & 0x01) { | |
350 | analog->meaning->mq = SR_MQ_CONTINUITY; | |
351 | analog->meaning->unit = SR_UNIT_OHM; | |
352 | } else if (buf[13] & 0x20) { | |
353 | analog->meaning->mq = SR_MQ_RESISTANCE; | |
354 | analog->meaning->unit = SR_UNIT_OHM; | |
355 | } else if (is_db && is_main_milli) { | |
356 | analog->meaning->mq = SR_MQ_POWER; | |
357 | analog->meaning->unit = SR_UNIT_DECIBEL_MW; | |
358 | } else if (buf[14] & 0x04) { | |
359 | analog->meaning->mq = SR_MQ_DUTY_CYCLE; | |
360 | analog->meaning->unit = SR_UNIT_PERCENTAGE; | |
361 | } else if ((buf[2] & 0x09) && temp_unit) { | |
362 | if (is_no_temp) | |
363 | return; | |
364 | analog->meaning->mq = SR_MQ_TEMPERATURE; | |
365 | if (temp_unit == 'F') | |
366 | analog->meaning->unit = SR_UNIT_FAHRENHEIT; | |
367 | else | |
368 | analog->meaning->unit = SR_UNIT_CELSIUS; | |
369 | } | |
370 | ||
371 | /* | |
372 | * Remove the MIN/MAX/AVG indicators when all of them | |
373 | * are shown at the same time (indicating that recording | |
374 | * is active, but live readings are shown). This also | |
375 | * removes the MAX-MIN (V p-p) indication which cannot | |
376 | * get represented by SR_MQFLAG_* means. | |
377 | * | |
378 | * Keep the check conditions separate to simplify future | |
379 | * maintenance when Vp-p gets added. Provide the value of | |
380 | * currently unsupported modes just without flags (show | |
381 | * the maximum amount of LCD content on screen that we | |
382 | * can represent in sigrok). | |
383 | */ | |
384 | is_mm_max = buf[1] & 0x01; | |
385 | is_mm_min = buf[1] & 0x08; | |
386 | is_mm_avg = buf[1] & 0x02; | |
387 | is_mm_dash = buf[1] & 0x04; | |
388 | if (is_mm_max && is_mm_min && is_mm_avg) | |
389 | is_mm_max = is_mm_min = is_mm_avg = 0; | |
390 | if (is_mm_max && is_mm_min && is_mm_dash) | |
391 | is_mm_max = is_mm_min = 0; | |
392 | if (is_mm_max && is_mm_min && !is_mm_dash) | |
393 | is_mm_max = is_mm_min = 0; | |
394 | ||
395 | /* AC/DC/Auto flags. Hold/Min/Max/Rel etc flags. */ | |
396 | if (buf[1] & 0x20) | |
397 | analog->meaning->mqflags |= SR_MQFLAG_DC; | |
398 | if (buf[1] & 0x10) | |
399 | analog->meaning->mqflags |= SR_MQFLAG_AC; | |
400 | if (buf[20] & 0x10) | |
401 | analog->meaning->mqflags |= SR_MQFLAG_AUTORANGE; | |
402 | if (buf[20] & 0x80) | |
403 | analog->meaning->mqflags |= SR_MQFLAG_HOLD; | |
404 | if (is_mm_max) | |
405 | analog->meaning->mqflags |= SR_MQFLAG_MAX; | |
406 | if (is_mm_min) | |
407 | analog->meaning->mqflags |= SR_MQFLAG_MIN; | |
408 | if (is_mm_avg) | |
409 | analog->meaning->mqflags |= SR_MQFLAG_AVG; | |
410 | if (buf[2] & 0x40) | |
411 | analog->meaning->mqflags |= SR_MQFLAG_RELATIVE; | |
412 | ||
413 | /* | |
414 | * Remove the "dBm" indication's "m" indicator before the | |
415 | * SI unit's prefixes get inspected. Avoids an interaction | |
416 | * with the "milli" prefix. Strictly speaking BM525s does | |
417 | * not support dBm, but other models do and we may want | |
418 | * to share the protocol parser. | |
419 | */ | |
420 | if (is_db) | |
421 | is_main_milli = 0; | |
422 | ||
423 | /* SI prefix. */ | |
424 | scale = 0; | |
425 | if (buf[14] & 0x08) /* n */ | |
426 | scale = -9; | |
427 | if (buf[14] & 0x80) /* u */ | |
428 | scale = -6; | |
429 | if (is_main_milli) /* m */ | |
430 | scale = -3; | |
431 | if (buf[13] & 0x80) /* k */ | |
432 | scale = +3; | |
433 | if (buf[13] & 0x40) /* M */ | |
434 | scale = +6; | |
435 | if (scale) { | |
436 | *floatval *= pow(10, scale); | |
437 | digits += -scale; | |
438 | } | |
439 | ||
440 | if (is_ol) | |
441 | *floatval = INFINITY; | |
442 | ||
443 | analog->encoding->digits = digits; | |
444 | analog->spec->spec_digits = digits; | |
445 | } else if (ch_idx == 1) { | |
446 | /* | |
447 | * Secondary display. Also inspect _some_ primary display | |
448 | * data, to determine the secondary display's validity. | |
449 | */ | |
450 | (void)brymen_bm52x_parse_digits(&buf[2], 4, txtbuf, | |
451 | NULL, &temp_unit, NULL, 0x80); | |
452 | ret = brymen_bm52x_parse_digits(&buf[7], 4, txtbuf, | |
453 | floatval, NULL, &digits, 0x20); | |
454 | is_diode = strcmp(txtbuf, "diod") == 0; | |
455 | is_auto = strcmp(txtbuf, "Auto") == 0; | |
456 | is_no_temp = strcmp(txtbuf, "---C") == 0; | |
457 | is_no_temp |= strcmp(txtbuf, "---F") == 0; | |
458 | if (is_diode || is_auto) | |
459 | return; | |
460 | if (is_no_temp) | |
461 | return; | |
462 | ||
463 | /* SI unit. */ | |
464 | if (buf[12] & 0x10) { | |
465 | analog->meaning->mq = SR_MQ_VOLTAGE; | |
466 | analog->meaning->unit = SR_UNIT_VOLT; | |
467 | } else if (buf[12] & 0x20) { | |
468 | analog->meaning->mq = SR_MQ_CURRENT; | |
469 | if (buf[11] & 0x10) | |
470 | analog->meaning->unit = SR_UNIT_PERCENTAGE; | |
471 | else | |
472 | analog->meaning->unit = SR_UNIT_AMPERE; | |
473 | } else if (buf[13] & 0x02) { | |
474 | analog->meaning->mq = SR_MQ_RESISTANCE; | |
475 | analog->meaning->unit = SR_UNIT_OHM; | |
476 | } else if (buf[12] & 0x02) { | |
477 | analog->meaning->mq = SR_MQ_CONDUCTANCE; | |
478 | analog->meaning->unit = SR_UNIT_SIEMENS; | |
479 | } else if (buf[12] & 0x01) { | |
480 | analog->meaning->mq = SR_MQ_CAPACITANCE; | |
481 | analog->meaning->unit = SR_UNIT_FARAD; | |
482 | } else if (buf[7] & 0x06) { | |
483 | if (strstr(txtbuf, "---")) | |
484 | return; | |
485 | analog->meaning->mq = SR_MQ_TEMPERATURE; | |
486 | if (temp_unit == 'F') | |
487 | analog->meaning->unit = SR_UNIT_FAHRENHEIT; | |
488 | else | |
489 | analog->meaning->unit = SR_UNIT_CELSIUS; | |
490 | } else if (buf[13] & 0x01) { | |
491 | analog->meaning->mq = SR_MQ_FREQUENCY; | |
492 | analog->meaning->unit = SR_UNIT_HERTZ; | |
493 | } else if (buf[11] & 0x08) { | |
494 | analog->meaning->mq = SR_MQ_DUTY_CYCLE; | |
495 | analog->meaning->unit = SR_UNIT_PERCENTAGE; | |
496 | } | |
497 | ||
498 | /* DC/AC flags. */ | |
499 | if (buf[7] & 0x80) | |
500 | analog->meaning->mqflags |= SR_MQFLAG_DC; | |
501 | if (buf[7] & 0x40) | |
502 | analog->meaning->mqflags |= SR_MQFLAG_AC; | |
503 | ||
504 | /* SI prefix. */ | |
505 | scale = 0; | |
506 | if (buf[12] & 0x04) /* n */ | |
507 | scale = -9; | |
508 | if (buf[12] & 0x40) /* u */ | |
509 | scale = -6; | |
510 | if (buf[12] & 0x80) /* m */ | |
511 | scale = -3; | |
512 | if (buf[13] & 0x04) /* k */ | |
513 | scale = +3; | |
514 | if (buf[13] & 0x08) /* M */ | |
515 | scale = +6; | |
516 | if (scale) { | |
517 | *floatval *= pow(10, scale); | |
518 | digits += -scale; | |
519 | } | |
520 | ||
521 | analog->encoding->digits = digits; | |
522 | analog->spec->spec_digits = digits; | |
523 | } | |
524 | ||
525 | if (buf[7] & 0x08) | |
526 | sr_warn("Battery is low."); | |
527 | } | |
528 | ||
529 | SR_PRIV int sr_brymen_bm52x_parse(const uint8_t *buf, float *val, | |
530 | struct sr_datafeed_analog *analog, void *info) | |
531 | { | |
532 | struct brymen_bm52x_info *info_local; | |
533 | size_t ch_idx; | |
534 | ||
535 | /* | |
536 | * Scan a portion of the received DMM packet which corresponds | |
537 | * to the caller's specified display. Then prepare to scan a | |
538 | * different portion of the packet for another display. This | |
539 | * routine gets called multiple times for one received packet. | |
540 | */ | |
541 | info_local = info; | |
542 | ch_idx = info_local->ch_idx; | |
543 | brymen_bm52x_parse(buf, val, analog, ch_idx); | |
544 | info_local->ch_idx = ch_idx + 1; | |
545 | ||
546 | return SR_OK; | |
547 | } | |
0931639a GS |
548 | |
549 | /* | |
550 | * The above code paths support live readings ("real-time download"). | |
551 | * The below code paths support recordings ("memory data sets") which | |
552 | * use different requests and responses and measurement representation | |
553 | * which feels like "a different meter". | |
554 | */ | |
555 | ||
556 | /* | |
557 | * Developer notes, example data for recorded sessions. | |
558 | * | |
559 | * model | |
560 | * 01 | |
561 | * total bytes | |
562 | * e6 02 00 | |
563 | * session count | |
564 | * 01 00 | |
565 | * "DLE/STX" marker | |
566 | * ee a0 | |
567 | * PS/NS addresses | |
568 | * 8a 03 a0 60 03 a0 | |
569 | * func/sel/stat (DC-V, single display) | |
570 | * 02 00 00 | |
571 | * session page length in bytes (3 * 240) | |
572 | * d0 02 00 | |
573 | * main[/secondary] display data | |
574 | * 00 00 00 00 | |
575 | * checksums and padding | |
576 | * 7c 05 00 00 00 00 00 00 | |
577 | * 00 00 80 00 00 80 00 00 80 00 00 80 00 00 00 00 00 80 00 00 80 00 00 80 80 03 00 00 00 00 00 00 | |
578 | * 00 00 00 00 00 00 00 00 80 00 00 80 00 00 80 00 00 80 00 00 80 00 00 80 00 03 00 00 00 00 00 00 | |
579 | * ... | |
580 | * 00 00 80 00 00 00 00 00 00 00 00 80 00 00 80 00 00 80 00 00 80 00 00 80 00 03 00 00 00 00 00 00 | |
581 | * 00 00 80 00 00 80 00 00 80 00 00 80 00 00 80 00 00 80 00 00 | |
582 | * "DLE/ETX" marker | |
583 | * ee c0 | |
584 | * ae 04 00 00 00 00 00 00 | |
585 | * | |
586 | * - Checksum in bytes[25:24] is the mere sum of bytes[0:23]. | |
587 | * - Model ID is 0 or 1 -- does this translate to BM521s and BM525s? | |
588 | * - Total byte count _includes_ everything starting at model ID. | |
589 | * - There is no measurements count for a session page, but its length | |
590 | * in bytes, and a dual display flag, which lets us derive the count. | |
591 | * - STX/ETX/DLE markers don't use the expected ASCII codes. | |
592 | */ | |
593 | ||
594 | /* | |
595 | * See vendor doc table 3.1 "Logging interval". Includes sub-1Hz rates, | |
596 | * but also sub-1s intervals. Let's keep both presentations at hand. | |
597 | */ | |
598 | static const struct { | |
599 | unsigned int ival_secs; | |
600 | unsigned int freq_rate; | |
601 | } bm52x_rec_ivals[] = { | |
602 | [ 0] = { 0, 20, }, | |
603 | [ 1] = { 0, 10, }, | |
604 | [ 2] = { 0, 2, }, | |
605 | [ 3] = { 1, 1, }, | |
606 | [ 4] = { 2, 0, }, | |
607 | [ 5] = { 3, 0, }, | |
608 | [ 6] = { 4, 0, }, | |
609 | [ 7] = { 5, 0, }, | |
610 | [ 8] = { 10, 0, }, | |
611 | [ 9] = { 15, 0, }, | |
612 | [10] = { 30, 0, }, | |
613 | [11] = { 60, 0, }, | |
614 | [12] = { 120, 0, }, | |
615 | [13] = { 180, 0, }, | |
616 | [14] = { 300, 0, }, | |
617 | [15] = { 600, 0, }, | |
618 | }; | |
619 | ||
620 | /* | |
621 | * See vendor doc table 6 "Range bits". Temperature is not listed there | |
622 | * but keeping it here unifies the processing code paths. | |
623 | */ | |
624 | static const int bm52x_ranges_volt[16] = { 3, 2, 1, 0, }; | |
625 | static const int bm52x_ranges_millivolt[16] = { 5, 4, }; | |
626 | static const int bm52x_ranges_freq[16] = { 3, 2, 1, 0, -1, -2, -3, }; | |
627 | static const int bm52x_ranges_duty[16] = { 2, 1, }; | |
628 | static const int bm52x_ranges_ohm[16] = { 1, 0, -1, -2, -3, -4, }; | |
629 | static const int bm52x_ranges_cond[16] = { 11, }; | |
630 | static const int bm52x_ranges_cap[16] = { 11, 10, 9, 8, 7, 6, 5, }; | |
631 | static const int bm52x_ranges_diode[16] = { 3, }; | |
632 | static const int bm52x_ranges_temp[16] = { 0, }; | |
633 | static const int bm52x_ranges_amp[16] = { 3, 2, }; | |
634 | static const int bm52x_ranges_milliamp[16] = { 5, 4, }; | |
635 | static const int bm52x_ranges_microamp[16] = { 7, 6, }; | |
636 | ||
637 | /** Calculate checksum of four-HID-report responses (recordings). */ | |
638 | static uint16_t bm52x_rec_checksum(const uint8_t *b, size_t l) | |
639 | { | |
640 | uint16_t cs; | |
641 | ||
642 | cs = 0; | |
643 | while (l--) | |
644 | cs += *b++; | |
645 | ||
646 | return cs; | |
647 | } | |
648 | ||
649 | /** | |
650 | * Retrieve the first/next chunk of recording information. | |
651 | * Support for live readings is theoretical, and unused/untested. | |
652 | */ | |
f93bf8ba | 653 | #ifdef HAVE_SERIAL_COMM |
0931639a GS |
654 | static int bm52x_rec_next_rsp(struct sr_serial_dev_inst *serial, |
655 | enum bm52x_reqtype req, struct brymen_bm52x_state *state) | |
656 | { | |
657 | uint8_t *b; | |
658 | size_t l; | |
659 | int ret; | |
660 | ||
661 | /* Seed internal state when sending the HEAD request. */ | |
6bee394d | 662 | if (req == REQ_REC_HEAD || req == REQ_LIVE_READ_520) |
0931639a GS |
663 | memset(&state->rsp, 0, sizeof(state->rsp)); |
664 | ||
665 | /* Move unprocessed content to the front. */ | |
666 | if (state->rsp.read_pos) { | |
667 | b = &state->rsp.buff[0]; | |
668 | l = state->rsp.fill_pos - state->rsp.read_pos; | |
669 | if (l) | |
670 | memmove(&b[0], &b[state->rsp.read_pos], l); | |
671 | state->rsp.fill_pos -= state->rsp.read_pos; | |
672 | state->rsp.read_pos = 0; | |
673 | } | |
674 | ||
675 | /* Avoid queries for non-existing data. Limit NEXT requests. */ | |
676 | if (req == REQ_REC_NEXT && !state->rsp.remain) | |
677 | return SR_ERR_IO; | |
678 | ||
679 | /* Add another response chunk to the read buffer. */ | |
680 | b = &state->rsp.buff[state->rsp.fill_pos]; | |
6bee394d | 681 | l = req == REQ_LIVE_READ_520 ? 24 : 32; |
0931639a GS |
682 | if (sizeof(state->rsp.buff) - state->rsp.fill_pos < l) |
683 | return SR_ERR_BUG; | |
684 | ret = bm52x_send_req(serial, req); | |
685 | if (ret != SR_OK) | |
686 | return ret; | |
687 | ret = serial_read_blocking(serial, b, l, 1000); | |
688 | if (ret < 0) | |
689 | return ret; | |
690 | if ((size_t)ret != l) | |
691 | return SR_ERR_IO; | |
692 | state->rsp.fill_pos += l; | |
693 | ||
694 | /* Devel support: dump the new receive data. */ | |
695 | if (sr_log_loglevel_get() >= SR_LOG_SPEW) { | |
696 | GString *text; | |
697 | const char *req_text; | |
698 | ||
6bee394d | 699 | req_text = (req == REQ_LIVE_READ_520) ? "LIVE" : |
0931639a GS |
700 | (req == REQ_REC_HEAD) ? "MEM HEAD" : |
701 | (req == REQ_REC_NEXT) ? "MEM NEXT" : | |
702 | (req == REQ_REC_CURR) ? "MEM CURR" : | |
703 | "<inv>"; | |
704 | text = sr_hexdump_new(b, l); | |
705 | sr_spew("%s: %s", req_text, text->str); | |
706 | sr_hexdump_free(text); | |
707 | } | |
708 | ||
709 | /* Verify checksum. No CURR repetition is attempted here. */ | |
710 | if (l > 24) { | |
711 | uint16_t calc, rcvd; | |
712 | ||
713 | calc = bm52x_rec_checksum(b, 24); | |
714 | rcvd = read_u16le(&b[24]); | |
715 | if (calc != rcvd) | |
716 | return SR_ERR_DATA; | |
717 | state->rsp.fill_pos -= 32 - 24; | |
718 | } | |
719 | ||
720 | /* Seed amount of total available data from HEAD response. */ | |
721 | if (req == REQ_REC_HEAD) { | |
722 | const uint8_t *rdptr; | |
723 | ||
724 | rdptr = &state->rsp.buff[0]; | |
725 | (void)read_u8_inc(&rdptr); /* model ID */ | |
726 | state->rsp.remain = read_u24le_inc(&rdptr); /* byte count */ | |
727 | } | |
728 | ||
729 | return SR_OK; | |
730 | } | |
f93bf8ba GS |
731 | #else /* have serial comm */ |
732 | static int bm52x_rec_next_rsp(struct sr_serial_dev_inst *serial, | |
733 | enum bm52x_reqtype req, struct brymen_bm52x_state *state) | |
734 | { | |
735 | (void)serial; | |
736 | (void)req; | |
737 | (void)state; | |
738 | (void)bm52x_rec_checksum; | |
739 | return SR_ERR_NA; | |
740 | } | |
741 | #endif /* have serial comm */ | |
0931639a GS |
742 | |
743 | /** Make sure a minimum amount of response data is available. */ | |
744 | static const uint8_t *bm52x_rec_ensure(struct sr_serial_dev_inst *serial, | |
745 | size_t min_count, struct brymen_bm52x_state *state) | |
746 | { | |
747 | size_t got; | |
748 | const uint8_t *read_ptr; | |
749 | int ret; | |
750 | ||
751 | got = state->rsp.fill_pos - state->rsp.read_pos; | |
752 | if (got >= min_count) { | |
753 | read_ptr = &state->rsp.buff[state->rsp.read_pos]; | |
754 | return read_ptr; | |
755 | } | |
756 | ret = bm52x_rec_next_rsp(serial, REQ_REC_NEXT, state); | |
757 | if (ret < 0) | |
758 | return NULL; | |
759 | read_ptr = &state->rsp.buff[state->rsp.read_pos]; | |
760 | return read_ptr; | |
761 | } | |
762 | ||
763 | /** Get a u8 quantity of response data, with auto-fetch and position increment. */ | |
764 | static uint8_t bm52x_rec_get_u8(struct sr_serial_dev_inst *serial, | |
765 | struct brymen_bm52x_state *state) | |
766 | { | |
767 | const uint8_t *read_ptr; | |
768 | uint8_t value; | |
769 | size_t length; | |
770 | ||
771 | length = sizeof(value); | |
772 | if (length > state->rsp.remain) { | |
773 | state->rsp.remain = 0; | |
774 | return 0; | |
775 | } | |
776 | read_ptr = bm52x_rec_ensure(serial, length, state); | |
777 | if (!read_ptr) | |
778 | return 0; | |
779 | value = read_u8(read_ptr); | |
780 | state->rsp.read_pos += length; | |
781 | state->rsp.remain -= length; | |
782 | return value; | |
783 | } | |
784 | ||
785 | /** Get a u16 quantity of response data, with auto-fetch and position increment. */ | |
786 | static uint16_t bm52x_rec_get_u16(struct sr_serial_dev_inst *serial, | |
787 | struct brymen_bm52x_state *state) | |
788 | { | |
789 | const uint8_t *read_ptr; | |
790 | uint16_t value; | |
791 | size_t length; | |
792 | ||
793 | length = sizeof(value); | |
794 | if (length > state->rsp.remain) { | |
795 | state->rsp.remain = 0; | |
796 | return 0; | |
797 | } | |
798 | read_ptr = bm52x_rec_ensure(serial, length, state); | |
799 | if (!read_ptr) | |
800 | return 0; | |
801 | value = read_u16le(read_ptr); | |
802 | state->rsp.read_pos += length; | |
803 | state->rsp.remain -= length; | |
804 | return value; | |
805 | } | |
806 | ||
807 | /** Get a u24 quantity of response data, with auto-fetch and position increment. */ | |
808 | static uint32_t bm52x_rec_get_u24(struct sr_serial_dev_inst *serial, | |
809 | struct brymen_bm52x_state *state) | |
810 | { | |
811 | const uint8_t *read_ptr; | |
812 | uint32_t value; | |
813 | size_t length; | |
814 | ||
815 | length = 24 / sizeof(uint8_t) / 8; | |
816 | if (length > state->rsp.remain) { | |
817 | state->rsp.remain = 0; | |
818 | return 0; | |
819 | } | |
820 | read_ptr = bm52x_rec_ensure(serial, length, state); | |
821 | if (!read_ptr) | |
822 | return 0; | |
823 | value = read_u24le(read_ptr); | |
824 | state->rsp.read_pos += length; | |
825 | state->rsp.remain -= length; | |
826 | return value; | |
827 | } | |
828 | ||
829 | /** Get the HEAD chunk of recording data, determine session page count. */ | |
830 | static int bm52x_rec_get_count(struct brymen_bm52x_state *state, | |
831 | struct sr_serial_dev_inst *serial) | |
832 | { | |
833 | int ret; | |
834 | size_t byte_count, sess_count; | |
835 | ||
836 | memset(&state->rsp, 0, sizeof(state->rsp)); | |
837 | ret = bm52x_rec_next_rsp(serial, REQ_REC_HEAD, state); | |
838 | if (ret != SR_OK) | |
839 | return ret; | |
840 | ||
841 | (void)bm52x_rec_get_u8(serial, state); /* model ID */ | |
842 | byte_count = bm52x_rec_get_u24(serial, state); /* total bytes count */ | |
843 | sess_count = bm52x_rec_get_u16(serial, state); /* session count */ | |
844 | sr_dbg("bytes %zu, sessions %zu", byte_count, sess_count); | |
845 | ||
846 | return sess_count; | |
847 | } | |
848 | ||
849 | static double bm52x_rec_get_value(uint32_t raw, const int *ranges, int *digits) | |
850 | { | |
851 | uint16_t val_digs; | |
852 | gboolean is_neg, is_ol, low_batt; | |
853 | uint8_t range; | |
854 | double value; | |
855 | int decimals; | |
856 | ||
857 | val_digs = raw >> 8; | |
858 | is_neg = raw & (1u << 7); | |
859 | is_ol = raw & (1u << 6); | |
860 | low_batt = raw & (1u << 5); | |
861 | range = raw & 0x0f; | |
862 | sr_dbg("item: %s%u, %s %s, range %01x", | |
863 | is_neg ? "-" : "+", val_digs, | |
864 | is_ol ? "OL" : "ol", low_batt ? "BATT" : "batt", | |
865 | range); | |
866 | ||
867 | /* Convert to number. OL takes precedence. */ | |
868 | *digits = 0; | |
869 | value = val_digs; | |
870 | if (ranges && ranges[range]) { | |
871 | decimals = ranges[range]; | |
872 | value /= pow(10, decimals); | |
873 | *digits = decimals; | |
874 | } | |
875 | if (is_ol) | |
876 | value = INFINITY; | |
877 | if (is_neg) | |
878 | value *= -1; | |
879 | ||
880 | /* | |
881 | * Implementor's note: "Low battery" conditions are worth a | |
882 | * warning since the reading could be incorrect. Rate limiting | |
883 | * is not needed since the Brymen DMM will stop recording in | |
884 | * that case, so at most the last sample in the session page | |
885 | * could be affected. | |
886 | */ | |
887 | if (low_batt) | |
888 | sr_warn("Recording was taken when battery was low."); | |
889 | ||
890 | return value; | |
891 | } | |
892 | ||
893 | static int bm52x_rec_prep_feed(uint8_t bfunc, uint8_t bsel, uint8_t bstat, | |
894 | struct sr_datafeed_analog *analog1, struct sr_datafeed_analog *analog2, | |
895 | double *value1, double *value2, const int **ranges1, const int **ranges2, | |
896 | const struct sr_dev_inst *sdi) | |
897 | { | |
898 | struct sr_channel *ch; | |
899 | gboolean is_amp, is_deg_f; | |
900 | enum sr_mq *mq1, *mq2; | |
901 | enum sr_unit *unit1, *unit2; | |
902 | enum sr_mqflag *mqf1, *mqf2; | |
903 | enum sr_unit unit_c_f; | |
904 | const int *r_a_ma; | |
905 | ||
906 | /* Prepare general submission on first channel. */ | |
907 | analog1->data = value1; | |
908 | analog1->encoding->unitsize = sizeof(*value1); | |
909 | analog1->num_samples = 1; | |
910 | ch = g_slist_nth_data(sdi->channels, 0); | |
911 | analog1->meaning->channels = g_slist_append(NULL, ch); | |
912 | *ranges1 = NULL; | |
913 | mq1 = &analog1->meaning->mq; | |
914 | mqf1 = &analog1->meaning->mqflags; | |
915 | unit1 = &analog1->meaning->unit; | |
916 | ||
917 | /* Prepare general submission on second channel. */ | |
918 | analog2->data = value2; | |
919 | analog2->encoding->unitsize = sizeof(*value2); | |
920 | analog2->num_samples = 1; | |
921 | ch = g_slist_nth_data(sdi->channels, 1); | |
922 | analog2->meaning->channels = g_slist_append(NULL, ch); | |
923 | *ranges2 = NULL; | |
924 | mq2 = &analog2->meaning->mq; | |
925 | mqf2 = &analog2->meaning->mqflags; | |
926 | unit2 = &analog2->meaning->unit; | |
927 | ||
928 | /* Derive main/secondary display functions from bfunc/bsel/bstat. */ | |
929 | is_amp = bstat & (1u << 5); | |
930 | is_deg_f = bstat & (1u << 4); | |
931 | switch (bfunc) { | |
932 | case 1: /* AC V */ | |
933 | switch (bsel) { | |
934 | case 0: /* AC volt, Hz */ | |
935 | *ranges1 = bm52x_ranges_volt; | |
936 | *mq1 = SR_MQ_VOLTAGE; | |
937 | *mqf1 |= SR_MQFLAG_AC; | |
938 | *unit1 = SR_UNIT_VOLT; | |
939 | *ranges2 = bm52x_ranges_freq; | |
940 | *mq2 = SR_MQ_FREQUENCY; | |
941 | *unit2 = SR_UNIT_HERTZ; | |
942 | break; | |
943 | case 1: /* Hz, AC volt */ | |
944 | *ranges1 = bm52x_ranges_freq; | |
945 | *mq1 = SR_MQ_FREQUENCY; | |
946 | *unit1 = SR_UNIT_HERTZ; | |
947 | *ranges2 = bm52x_ranges_volt; | |
948 | *mq2 = SR_MQ_VOLTAGE; | |
949 | *mqf2 |= SR_MQFLAG_AC; | |
950 | *unit2 = SR_UNIT_VOLT; | |
951 | break; | |
952 | default: | |
953 | return SR_ERR_DATA; | |
954 | } | |
955 | break; | |
956 | case 2: /* DC V */ | |
957 | switch (bsel) { | |
958 | case 0: /* DC V, - */ | |
959 | *ranges1 = bm52x_ranges_volt; | |
960 | *mq1 = SR_MQ_VOLTAGE; | |
961 | *mqf1 |= SR_MQFLAG_DC; | |
962 | *unit1 = SR_UNIT_VOLT; | |
963 | break; | |
964 | case 1: /* DC V, AC V */ | |
965 | *ranges1 = bm52x_ranges_volt; | |
966 | *mq1 = SR_MQ_VOLTAGE; | |
967 | *mqf1 |= SR_MQFLAG_DC; | |
968 | *unit1 = SR_UNIT_VOLT; | |
969 | *ranges2 = bm52x_ranges_volt; | |
970 | *mq2 = SR_MQ_VOLTAGE; | |
971 | *mqf2 |= SR_MQFLAG_AC; | |
972 | *unit2 = SR_UNIT_VOLT; | |
973 | break; | |
974 | case 2: /* DC+AC V, AC V */ | |
975 | *ranges1 = bm52x_ranges_volt; | |
976 | *mq1 = SR_MQ_VOLTAGE; | |
977 | *mqf1 |= SR_MQFLAG_DC; | |
978 | *mqf1 |= SR_MQFLAG_AC; | |
979 | *unit1 = SR_UNIT_VOLT; | |
980 | *ranges2 = bm52x_ranges_volt; | |
981 | *mq2 = SR_MQ_VOLTAGE; | |
982 | *mqf2 |= SR_MQFLAG_AC; | |
983 | *unit2 = SR_UNIT_VOLT; | |
984 | break; | |
985 | default: | |
986 | return SR_ERR_DATA; | |
987 | } | |
988 | break; | |
989 | case 3: /* DC mV */ | |
990 | switch (bsel) { | |
991 | case 0: /* DC mV, - */ | |
992 | *ranges1 = bm52x_ranges_millivolt; | |
993 | *mq1 = SR_MQ_VOLTAGE; | |
994 | *mqf1 |= SR_MQFLAG_DC; | |
995 | *unit1 = SR_UNIT_VOLT; | |
996 | break; | |
997 | case 1: /* DC mV, AC mV */ | |
998 | *ranges1 = bm52x_ranges_millivolt; | |
999 | *mq1 = SR_MQ_VOLTAGE; | |
1000 | *mqf1 |= SR_MQFLAG_DC; | |
1001 | *unit1 = SR_UNIT_VOLT; | |
1002 | *ranges2 = bm52x_ranges_millivolt; | |
1003 | *mq2 = SR_MQ_VOLTAGE; | |
1004 | *mqf2 |= SR_MQFLAG_AC; | |
1005 | *unit2 = SR_UNIT_VOLT; | |
1006 | break; | |
1007 | case 2: /* DC+AC mV, AC mV */ | |
1008 | *ranges1 = bm52x_ranges_millivolt; | |
1009 | *mq1 = SR_MQ_VOLTAGE; | |
1010 | *mqf1 |= SR_MQFLAG_DC; | |
1011 | *mqf1 |= SR_MQFLAG_AC; | |
1012 | *unit1 = SR_UNIT_VOLT; | |
1013 | *ranges2 = bm52x_ranges_millivolt; | |
1014 | *mq2 = SR_MQ_VOLTAGE; | |
1015 | *mqf2 |= SR_MQFLAG_AC; | |
1016 | *unit2 = SR_UNIT_VOLT; | |
1017 | break; | |
1018 | case 3: /* Hz, - */ | |
1019 | *ranges1 = bm52x_ranges_freq; | |
1020 | *mq1 = SR_MQ_FREQUENCY; | |
1021 | *unit1 = SR_UNIT_HERTZ; | |
1022 | break; | |
1023 | case 4: /* %, - */ | |
1024 | *ranges1 = bm52x_ranges_duty; | |
1025 | *mq1 = SR_MQ_DUTY_CYCLE; | |
1026 | *unit1 = SR_UNIT_PERCENTAGE; | |
1027 | break; | |
1028 | default: | |
1029 | return SR_ERR_DATA; | |
1030 | } | |
1031 | break; | |
1032 | case 4: /* AC mV */ | |
1033 | switch (bsel) { | |
1034 | case 0: /* AC mV, Hz */ | |
1035 | *ranges1 = bm52x_ranges_millivolt; | |
1036 | *mq1 = SR_MQ_VOLTAGE; | |
1037 | *mqf1 |= SR_MQFLAG_AC; | |
1038 | *unit1 = SR_UNIT_VOLT; | |
1039 | *ranges2 = bm52x_ranges_freq; | |
1040 | *mq2 = SR_MQ_FREQUENCY; | |
1041 | *unit2 = SR_UNIT_HERTZ; | |
1042 | break; | |
1043 | case 1: /* Hz, AC mV */ | |
1044 | *ranges1 = bm52x_ranges_freq; | |
1045 | *mq1 = SR_MQ_FREQUENCY; | |
1046 | *unit1 = SR_UNIT_HERTZ; | |
1047 | *ranges2 = bm52x_ranges_millivolt; | |
1048 | *mq2 = SR_MQ_VOLTAGE; | |
1049 | *mqf2 |= SR_MQFLAG_AC; | |
1050 | *unit2 = SR_UNIT_VOLT; | |
1051 | break; | |
1052 | default: | |
1053 | return SR_ERR_DATA; | |
1054 | } | |
1055 | break; | |
1056 | case 5: /* Res/Cond/Cont */ | |
1057 | switch (bsel) { | |
1058 | case 0: /* Resistance */ | |
1059 | *ranges1 = bm52x_ranges_ohm; | |
1060 | *mq1 = SR_MQ_RESISTANCE; | |
1061 | *unit1 = SR_UNIT_OHM; | |
1062 | break; | |
1063 | case 1: /* Siemens */ | |
1064 | *ranges1 = bm52x_ranges_cond; | |
1065 | *mq1 = SR_MQ_CONDUCTANCE; | |
1066 | *unit1 = SR_UNIT_SIEMENS; | |
1067 | break; | |
1068 | case 2: /* Continuity */ | |
1069 | *ranges1 = bm52x_ranges_ohm; | |
1070 | *mq1 = SR_MQ_CONTINUITY; | |
1071 | *unit1 = SR_UNIT_OHM; | |
1072 | break; | |
1073 | default: | |
1074 | return SR_ERR_DATA; | |
1075 | } | |
1076 | break; | |
1077 | case 6: /* Temperature */ | |
1078 | unit_c_f = is_deg_f ? SR_UNIT_FAHRENHEIT : SR_UNIT_CELSIUS; | |
1079 | switch (bsel) { | |
1080 | case 0: /* T1, - */ | |
1081 | *ranges1 = bm52x_ranges_temp; | |
1082 | *mq1 = SR_MQ_TEMPERATURE; | |
1083 | *unit1 = unit_c_f; | |
1084 | break; | |
1085 | case 1: /* T2, - */ | |
1086 | *ranges1 = bm52x_ranges_temp; | |
1087 | *mq1 = SR_MQ_TEMPERATURE; | |
1088 | *unit1 = unit_c_f; | |
1089 | break; | |
1090 | case 2: /* T1, T2 */ | |
1091 | *ranges1 = bm52x_ranges_temp; | |
1092 | *mq1 = SR_MQ_TEMPERATURE; | |
1093 | *unit1 = unit_c_f; | |
1094 | *ranges2 = bm52x_ranges_temp; | |
1095 | *mq2 = SR_MQ_TEMPERATURE; | |
1096 | *unit2 = unit_c_f; | |
1097 | break; | |
1098 | case 3: /* T1-T2, T2 */ | |
1099 | *ranges1 = bm52x_ranges_temp; | |
1100 | *mq1 = SR_MQ_TEMPERATURE; | |
1101 | *unit1 = unit_c_f; | |
1102 | *ranges2 = bm52x_ranges_temp; | |
1103 | *mq2 = SR_MQ_TEMPERATURE; | |
1104 | *unit2 = unit_c_f; | |
1105 | break; | |
1106 | default: | |
1107 | return SR_ERR_DATA; | |
1108 | } | |
1109 | break; | |
1110 | case 7: /* Cap/Diode */ | |
1111 | switch (bsel) { | |
1112 | case 0: /* Capacitance, - */ | |
1113 | *ranges1 = bm52x_ranges_cap; | |
1114 | *mq1 = SR_MQ_CAPACITANCE; | |
1115 | *unit1 |= SR_UNIT_FARAD; | |
1116 | break; | |
1117 | case 1: /* Diode voltage, - */ | |
1118 | *ranges1 = bm52x_ranges_diode; | |
1119 | *mq1 = SR_MQ_VOLTAGE; | |
1120 | *mqf1 |= SR_MQFLAG_DC; | |
1121 | *mqf1 |= SR_MQFLAG_DIODE; | |
1122 | *unit1 |= SR_UNIT_VOLT; | |
1123 | break; | |
1124 | default: | |
1125 | return SR_ERR_DATA; | |
1126 | } | |
1127 | break; | |
1128 | case 8: /* DC A/mA */ | |
1129 | r_a_ma = is_amp ? bm52x_ranges_amp : bm52x_ranges_milliamp; | |
1130 | switch (bsel) { | |
1131 | case 0: /* DC A/mA, - */ | |
1132 | *ranges1 = r_a_ma; | |
1133 | *mq1 = SR_MQ_CURRENT; | |
1134 | *mqf1 |= SR_MQFLAG_DC; | |
1135 | *unit1 = SR_UNIT_AMPERE; | |
1136 | break; | |
1137 | case 1: /* DC A/mA, AC A/mA */ | |
1138 | *ranges1 = r_a_ma; | |
1139 | *mq1 = SR_MQ_CURRENT; | |
1140 | *mqf1 |= SR_MQFLAG_DC; | |
1141 | *unit1 = SR_UNIT_AMPERE; | |
1142 | *ranges2 = r_a_ma; | |
1143 | *mq2 = SR_MQ_CURRENT; | |
1144 | *mqf2 |= SR_MQFLAG_AC; | |
1145 | *unit2 = SR_UNIT_AMPERE; | |
1146 | break; | |
1147 | case 2: /* DC+AC A/mA, AC A/mA */ | |
1148 | *ranges1 = r_a_ma; | |
1149 | *mq1 = SR_MQ_CURRENT; | |
1150 | *mqf1 |= SR_MQFLAG_DC; | |
1151 | *mqf1 |= SR_MQFLAG_AC; | |
1152 | *unit1 = SR_UNIT_AMPERE; | |
1153 | *ranges2 = r_a_ma; | |
1154 | *mq2 = SR_MQ_CURRENT; | |
1155 | *mqf2 |= SR_MQFLAG_AC; | |
1156 | *unit2 = SR_UNIT_AMPERE; | |
1157 | break; | |
1158 | case 3: /* AC A/mA, Hz */ | |
1159 | *ranges1 = r_a_ma; | |
1160 | *mq1 = SR_MQ_CURRENT; | |
1161 | *mqf1 |= SR_MQFLAG_AC; | |
1162 | *unit1 = SR_UNIT_AMPERE; | |
1163 | *ranges2 = bm52x_ranges_freq; | |
1164 | *mq2 = SR_MQ_FREQUENCY; | |
1165 | *unit2 = SR_UNIT_HERTZ; | |
1166 | break; | |
1167 | default: | |
1168 | return SR_ERR_DATA; | |
1169 | } | |
1170 | break; | |
1171 | case 9: /* DC uA */ | |
1172 | switch (bsel) { | |
1173 | case 0: /* DC uA, - */ | |
1174 | *ranges1 = bm52x_ranges_microamp; | |
1175 | *mq1 = SR_MQ_CURRENT; | |
1176 | *mqf1 |= SR_MQFLAG_DC; | |
1177 | *unit1 = SR_UNIT_AMPERE; | |
1178 | break; | |
1179 | case 1: /* DC uA, AC uA */ | |
1180 | *ranges1 = bm52x_ranges_microamp; | |
1181 | *mq1 = SR_MQ_CURRENT; | |
1182 | *mqf1 |= SR_MQFLAG_DC; | |
1183 | *unit1 = SR_UNIT_AMPERE; | |
1184 | *ranges2 = bm52x_ranges_microamp; | |
1185 | *mq2 = SR_MQ_CURRENT; | |
1186 | *mqf2 |= SR_MQFLAG_AC; | |
1187 | *unit2 = SR_UNIT_AMPERE; | |
1188 | break; | |
1189 | case 2: /* DC+AC uA, AC uA */ | |
1190 | *ranges1 = bm52x_ranges_microamp; | |
1191 | *mq1 = SR_MQ_CURRENT; | |
1192 | *mqf1 |= SR_MQFLAG_DC; | |
1193 | *mqf1 |= SR_MQFLAG_AC; | |
1194 | *unit1 = SR_UNIT_AMPERE; | |
1195 | *ranges2 = bm52x_ranges_microamp; | |
1196 | *mq2 = SR_MQ_CURRENT; | |
1197 | *mqf2 |= SR_MQFLAG_AC; | |
1198 | *unit2 = SR_UNIT_AMPERE; | |
1199 | break; | |
1200 | case 3: /* AC uA, Hz */ | |
1201 | *ranges1 = bm52x_ranges_microamp; | |
1202 | *mq1 = SR_MQ_CURRENT; | |
1203 | *mqf1 |= SR_MQFLAG_AC; | |
1204 | *unit1 = SR_UNIT_AMPERE; | |
1205 | *ranges2 = bm52x_ranges_freq; | |
1206 | *mq2 = SR_MQ_FREQUENCY; | |
1207 | *unit2 = SR_UNIT_HERTZ; | |
1208 | break; | |
1209 | default: | |
1210 | return SR_ERR_DATA; | |
1211 | } | |
1212 | break; | |
1213 | default: | |
1214 | return SR_ERR_DATA; | |
1215 | } | |
1216 | ||
1217 | return SR_OK; | |
1218 | } | |
1219 | ||
1220 | /** Traverse one recorded session page, optionally feed session bus. */ | |
1221 | static int bm52x_rec_read_page_int(const struct sr_dev_inst *sdi, | |
1222 | struct brymen_bm52x_state *state, struct sr_serial_dev_inst *serial, | |
1223 | gboolean skip) | |
1224 | { | |
1225 | uint8_t bfunc, bsel, bstat; | |
1226 | uint8_t ival; | |
1227 | gboolean has_sec_disp; | |
1228 | size_t page_len, meas_len, meas_count; | |
1229 | uint32_t meas_data; | |
1230 | struct sr_datafeed_packet packet; | |
1231 | struct sr_datafeed_analog analog1, analog2; | |
1232 | struct sr_analog_encoding encoding1, encoding2; | |
1233 | struct sr_analog_meaning meaning1, meaning2; | |
1234 | struct sr_analog_spec spec1, spec2; | |
1235 | int digits, ret; | |
1236 | double values[2]; | |
1237 | const int *ranges1, *ranges2; | |
1238 | enum sr_configkey key; | |
1239 | uint64_t num; | |
1240 | ||
1241 | sr_dbg("progress: %s, %s", __func__, skip ? "skip" : "feed"); | |
1242 | ||
1243 | /* Get the header information of the session page (raw). */ | |
1244 | if (bm52x_rec_get_u8(serial, state) != 0xee) /* "DLE" */ | |
1245 | return SR_ERR_DATA; | |
1246 | if (bm52x_rec_get_u8(serial, state) != 0xa0) /* "STX" */ | |
1247 | return SR_ERR_DATA; | |
1248 | (void)bm52x_rec_get_u24(serial, state); /* prev page addr */ | |
1249 | (void)bm52x_rec_get_u24(serial, state); /* next page addr */ | |
1250 | bfunc = bm52x_rec_get_u8(serial, state); /* meter function */ | |
1251 | bsel = bm52x_rec_get_u8(serial, state); /* fun selection */ | |
1252 | bstat = bm52x_rec_get_u8(serial, state); /* status */ | |
1253 | page_len = bm52x_rec_get_u24(serial, state); /* page length */ | |
1254 | sr_dbg("page head: func/sel/state %02x/%02x/%02x, len %zu", | |
1255 | bfunc, bsel, bstat, page_len); | |
1256 | ||
1257 | /* Interpret the header information of the session page. */ | |
1258 | ival = bstat & 0x0f; | |
1259 | has_sec_disp = bstat & (1u << 7); | |
1260 | meas_len = (has_sec_disp ? 2 : 1) * 3; | |
1261 | if (page_len % meas_len) | |
1262 | return SR_ERR_DATA; | |
1263 | meas_count = page_len / meas_len; | |
1264 | sr_dbg("page head: ival %u, %s, samples %zu", | |
1265 | ival, has_sec_disp ? "dual" : "main", meas_count); | |
1266 | ||
1267 | /* Prepare feed to the sigrok session. Send rate/interval. */ | |
1268 | sr_analog_init(&analog1, &encoding1, &meaning1, &spec1, 0); | |
1269 | sr_analog_init(&analog2, &encoding2, &meaning2, &spec2, 0); | |
1270 | ret = bm52x_rec_prep_feed(bfunc, bsel, bstat, | |
1271 | &analog1, &analog2, &values[0], &values[1], | |
1272 | &ranges1, &ranges2, sdi); | |
1273 | if (ret != SR_OK) | |
1274 | return SR_ERR_DATA; | |
1275 | if (!skip) { | |
1276 | memset(&packet, 0, sizeof(packet)); | |
1277 | packet.type = SR_DF_ANALOG; | |
1278 | ||
1279 | if (bm52x_rec_ivals[ival].freq_rate) { | |
1280 | sr_dbg("rate: %u", bm52x_rec_ivals[ival].freq_rate); | |
1281 | key = SR_CONF_SAMPLERATE; | |
1282 | num = bm52x_rec_ivals[ival].freq_rate; | |
1283 | (void)sr_session_send_meta(sdi, | |
1284 | key, g_variant_new_uint64(num)); | |
1285 | } | |
1286 | if (bm52x_rec_ivals[ival].ival_secs) { | |
1287 | sr_dbg("ival: %u", bm52x_rec_ivals[ival].ival_secs); | |
1288 | key = SR_CONF_SAMPLE_INTERVAL; | |
1289 | num = bm52x_rec_ivals[ival].ival_secs * 1000; /* in ms */ | |
1290 | (void)sr_session_send_meta(sdi, | |
1291 | key, g_variant_new_uint64(num)); | |
1292 | } | |
1293 | } | |
1294 | ||
1295 | /* | |
1296 | * Implementor's note: | |
1297 | * Software limits require devc access, which is an internal | |
1298 | * detail of the serial-dmm driver, which this bm52x parser | |
1299 | * is not aware of. So we always provide the complete set of | |
1300 | * recorded samples. Should be acceptable. Duplicating limit | |
1301 | * support in local config get/set is considered undesirable. | |
1302 | */ | |
1303 | while (meas_count--) { | |
1304 | meas_data = bm52x_rec_get_u24(serial, state); | |
1305 | values[0] = bm52x_rec_get_value(meas_data, ranges1, &digits); | |
1306 | if (!skip) { | |
1307 | analog1.encoding->digits = digits; | |
1308 | analog1.spec->spec_digits = digits; | |
1309 | packet.payload = &analog1; | |
1310 | ret = sr_session_send(sdi, &packet); | |
1311 | if (ret != SR_OK) | |
1312 | return ret; | |
1313 | } | |
1314 | ||
1315 | if (!has_sec_disp) | |
1316 | continue; | |
1317 | meas_data = bm52x_rec_get_u24(serial, state); | |
1318 | values[1] = bm52x_rec_get_value(meas_data, ranges2, &digits); | |
1319 | if (!skip) { | |
1320 | analog2.encoding->digits = digits; | |
1321 | analog2.spec->spec_digits = digits; | |
1322 | packet.payload = &analog2; | |
1323 | ret = sr_session_send(sdi, &packet); | |
1324 | if (ret != SR_OK) | |
1325 | return ret; | |
1326 | } | |
1327 | } | |
1328 | ||
1329 | /* Check termination of the session page. */ | |
1330 | if (bm52x_rec_get_u8(serial, state) != 0xee) /* "DLE" */ | |
1331 | return SR_ERR_DATA; | |
1332 | if (bm52x_rec_get_u8(serial, state) != 0xc0) /* "ETX" */ | |
1333 | return SR_ERR_DATA; | |
1334 | ||
1335 | return SR_OK; | |
1336 | } | |
1337 | ||
1338 | /** Skip one recorded session page. */ | |
1339 | static int bm52x_rec_skip_page(const struct sr_dev_inst *sdi, | |
1340 | struct brymen_bm52x_state *state, struct sr_serial_dev_inst *serial) | |
1341 | { | |
1342 | return bm52x_rec_read_page_int(sdi, state, serial, TRUE); | |
1343 | } | |
1344 | ||
1345 | /** Forward one recorded session page. */ | |
1346 | static int bm52x_rec_read_page(const struct sr_dev_inst *sdi, | |
1347 | struct brymen_bm52x_state *state, struct sr_serial_dev_inst *serial) | |
1348 | { | |
1349 | return bm52x_rec_read_page_int(sdi, state, serial, FALSE); | |
1350 | } | |
1351 | ||
1352 | SR_PRIV void *brymen_bm52x_state_init(void) | |
1353 | { | |
1354 | return g_malloc0(sizeof(struct brymen_bm52x_state)); | |
1355 | } | |
1356 | ||
1357 | SR_PRIV void brymen_bm52x_state_free(void *state) | |
1358 | { | |
1359 | g_free(state); | |
1360 | } | |
1361 | ||
1362 | SR_PRIV int brymen_bm52x_config_get(void *st, uint32_t key, GVariant **data, | |
1363 | const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) | |
1364 | { | |
1365 | struct brymen_bm52x_state *state; | |
9eba5419 | 1366 | char text[32]; |
0931639a GS |
1367 | |
1368 | state = st; | |
1369 | ||
1370 | if (!sdi) | |
1371 | return SR_ERR_NA; | |
1372 | (void)cg; | |
1373 | ||
1374 | switch (key) { | |
1375 | case SR_CONF_DATA_SOURCE: | |
1376 | if (!state) | |
1377 | return SR_ERR_ARG; | |
1378 | if (state->sess_idx == 0) | |
1379 | snprintf(text, sizeof(text), "Live"); | |
1380 | else | |
1381 | snprintf(text, sizeof(text), "Rec-%zu", state->sess_idx); | |
1382 | *data = g_variant_new_string(text); | |
1383 | return SR_OK; | |
1384 | default: | |
1385 | return SR_ERR_NA; | |
1386 | } | |
1387 | } | |
1388 | ||
1389 | SR_PRIV int brymen_bm52x_config_set(void *st, uint32_t key, GVariant *data, | |
1390 | const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) | |
1391 | { | |
1392 | struct brymen_bm52x_state *state; | |
1393 | const char *s; | |
1394 | int ret, nr; | |
1395 | ||
1396 | state = st; | |
1397 | ||
1398 | if (!sdi) | |
1399 | return SR_ERR_NA; | |
1400 | (void)cg; | |
1401 | ||
1402 | switch (key) { | |
1403 | case SR_CONF_DATA_SOURCE: | |
1404 | s = g_variant_get_string(data, NULL); | |
1405 | if (!s || !*s) | |
1406 | return SR_ERR_ARG; | |
1407 | if (strcasecmp(s, "Live") == 0) { | |
1408 | state->sess_idx = 0; | |
1409 | return SR_OK; | |
1410 | } | |
1411 | if (strncasecmp(s, "Rec-", strlen("Rec-")) != 0) | |
1412 | return SR_ERR_ARG; | |
1413 | s += strlen("Rec-"); | |
1414 | ret = sr_atoi(s, &nr); | |
1415 | if (ret != SR_OK || nr <= 0 || nr > 999) | |
1416 | return SR_ERR_ARG; | |
1417 | state->sess_idx = nr; | |
1418 | return SR_OK; | |
1419 | default: | |
1420 | return SR_ERR_NA; | |
1421 | } | |
1422 | } | |
1423 | ||
1424 | SR_PRIV int brymen_bm52x_config_list(void *st, uint32_t key, GVariant **data, | |
1425 | const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) | |
1426 | { | |
1427 | struct brymen_bm52x_state *state; | |
1428 | struct sr_serial_dev_inst *serial; | |
1429 | int ret; | |
1430 | size_t count, idx; | |
1431 | GVariantBuilder gvb; | |
46a36bf0 | 1432 | char name[32]; |
0931639a GS |
1433 | |
1434 | /* | |
1435 | * Have common keys handled by caller's common code. | |
1436 | * ERR N/A results in the caller's logic handling the request. | |
1437 | * Only handle strictly local properties here in this code path. | |
1438 | */ | |
1439 | switch (key) { | |
1440 | case SR_CONF_SCAN_OPTIONS: | |
1441 | /* Scan options. Common property. */ | |
1442 | return SR_ERR_NA; | |
1443 | case SR_CONF_DEVICE_OPTIONS: | |
1444 | if (!sdi) | |
1445 | /* Driver options. Common property. */ | |
1446 | return SR_ERR_NA; | |
1447 | if (cg) | |
1448 | /* Channel group's devopts. Common error path. */ | |
1449 | return SR_ERR_NA; | |
1450 | /* List meter's local device options. Overrides common data. */ | |
1451 | *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, | |
1452 | devopts, ARRAY_SIZE(devopts), sizeof(uint32_t)); | |
1453 | return SR_OK; | |
1454 | case SR_CONF_DATA_SOURCE: | |
1455 | state = st; | |
1456 | if (!sdi) | |
1457 | return SR_ERR_ARG; | |
1458 | serial = sdi->conn; | |
1459 | ret = bm52x_rec_get_count(state, serial); | |
1460 | if (ret < 0) | |
1461 | return ret; | |
1462 | count = ret; | |
1463 | g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY); | |
1464 | for (idx = 0; idx <= count; idx++) { | |
1465 | if (idx == 0) | |
1466 | snprintf(name, sizeof(name), "Live"); | |
1467 | else | |
1468 | snprintf(name, sizeof(name), "Rec-%zu", idx); | |
1469 | g_variant_builder_add(&gvb, "s", name); | |
1470 | } | |
1471 | *data = g_variant_builder_end(&gvb); | |
1472 | return SR_OK; | |
1473 | default: | |
1474 | return SR_ERR_NA; | |
1475 | } | |
1476 | } | |
1477 | ||
1478 | /** | |
1479 | * BM520s specific receive routine for recorded measurements. | |
1480 | * | |
1481 | * @param[in] fd File descriptor. | |
1482 | * @param[in] revents Mask of pending events. | |
1483 | * @param[in] cb_data Call back data (receive handler state). | |
1484 | * | |
1485 | * @return TRUE when the routine needs to get re-invoked, FALSE when the | |
1486 | * routine is done and no further invocations are required. | |
1487 | * | |
1488 | * @private | |
1489 | * | |
1490 | * It's an implementation detail that a single invocation will carry out | |
1491 | * all the work that is involved in reading back recorded measurements. | |
1492 | */ | |
1493 | static int bm52x_rec_receive_data(int fd, int revents, void *cb_data) | |
1494 | { | |
1495 | struct brymen_bm52x_state *state; | |
1496 | const struct sr_dev_inst *sdi; | |
1497 | struct sr_dev_inst *sdi_rw; | |
1498 | struct sr_serial_dev_inst *serial; | |
1499 | int ret; | |
1500 | size_t count, idx; | |
1501 | ||
1502 | (void)fd; | |
1503 | (void)revents; | |
1504 | state = cb_data; | |
1505 | ||
1506 | sdi = state->sdi; | |
1507 | serial = sdi->conn; | |
1508 | ||
1509 | ret = bm52x_rec_get_count(state, serial); | |
1510 | if (ret < 0) | |
1511 | return FALSE; | |
1512 | count = ret; | |
1513 | ||
1514 | /* Un-const 'sdi' since sr_dev_acquisition_stop() needs that. */ | |
1515 | sdi_rw = (struct sr_dev_inst *)sdi; | |
1516 | ||
1517 | /* | |
1518 | * Immediate (silent, zero data) stop for non-existent sessions. | |
1519 | * Early exit is an arbitrary implementation detail, in theory | |
1520 | * the loop below would transparently handle the situation when | |
1521 | * users request non-existing session pages. | |
1522 | */ | |
1523 | if (state->sess_idx > count) { | |
1524 | sr_dev_acquisition_stop(sdi_rw); | |
1525 | return FALSE; | |
1526 | } | |
1527 | ||
1528 | /* Iterate all session pages, forward the one of interest. */ | |
1529 | for (idx = 1; idx <= count; idx++) { | |
1530 | if (idx == state->sess_idx) | |
1531 | ret = bm52x_rec_read_page(sdi, state, serial); | |
1532 | else | |
1533 | ret = bm52x_rec_skip_page(sdi, state, serial); | |
1534 | if (ret != SR_OK) | |
1535 | break; | |
1536 | } | |
1537 | ||
1538 | sr_dev_acquisition_stop(sdi_rw); | |
1539 | return FALSE; | |
1540 | } | |
1541 | ||
1542 | /** | |
1543 | * BM520s specific acquisition start callback. | |
1544 | * | |
1545 | * @param[in] st DMM parser state. | |
1546 | * @param[in] sdi Device instance. | |
1547 | * @param[out] cb Receive callback for the acquisition. | |
1548 | * @param[out] cb_data Callback data for receive callback. | |
1549 | * | |
1550 | * @returns SR_OK upon success. | |
1551 | * @returns SR_ERR* upon failure. | |
1552 | * | |
1553 | * @private | |
1554 | * | |
1555 | * The BM520s protocol parser uses common logic and the packet parser | |
1556 | * for live acquisition, but runs a different set of requests and a | |
1557 | * different response layout interpretation for recorded measurements. | |
1558 | */ | |
1559 | SR_PRIV int brymen_bm52x_acquire_start(void *st, const struct sr_dev_inst *sdi, | |
1560 | sr_receive_data_callback *cb, void **cb_data) | |
1561 | { | |
1562 | struct brymen_bm52x_state *state; | |
1563 | ||
1564 | if (!sdi || !st) | |
1565 | return SR_ERR_ARG; | |
1566 | state = st; | |
1567 | ||
1568 | /* Read live measurements. No local override required. */ | |
1569 | if (state->sess_idx == 0) | |
1570 | return SR_OK; | |
1571 | ||
1572 | /* Arrange to read back recorded session. */ | |
1573 | sr_dbg("session page requested: %zu", state->sess_idx); | |
1574 | state->sdi = sdi; | |
1575 | *cb = bm52x_rec_receive_data; | |
1576 | *cb_data = state; | |
1577 | return SR_OK; | |
1578 | } |