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