]> sigrok.org Git - libsigrok.git/blob - src/dmm/bm52x.c
728b70a690308e6cabb662cbf35dbddf4df67a06
[libsigrok.git] / src / dmm / bm52x.c
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
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.
37  */
38
39 /*
40  * TODO
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.
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?
69  */
70
71 #include <config.h>
72 #include <libsigrok/libsigrok.h>
73 #include "libsigrok-internal.h"
74 #include <math.h>
75 #include <string.h>
76 #include <strings.h>
77
78 #define LOG_PREFIX "brymen-bm52x"
79
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
86 static 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
93 struct 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
104 enum bm52x_reqtype {
105         REQ_LIVE_READ,
106         REQ_REC_HEAD,
107         REQ_REC_NEXT,
108         REQ_REC_CURR,
109 };
110
111 #ifdef HAVE_SERIAL_COMM
112 static int bm52x_send_req(struct sr_serial_dev_inst *serial, enum bm52x_reqtype t)
113 {
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;
129
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;
139
140         return SR_OK;
141 }
142
143 SR_PRIV int sr_brymen_bm52x_packet_request(struct sr_serial_dev_inst *serial)
144 {
145         return bm52x_send_req(serial, REQ_LIVE_READ);
146 }
147 #endif
148
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
156 SR_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
184 static 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
220 static 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  */
269 static 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
499 SR_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 }
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  */
568 static 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  */
594 static const int bm52x_ranges_volt[16] = { 3, 2, 1, 0, };
595 static const int bm52x_ranges_millivolt[16] = { 5, 4, };
596 static const int bm52x_ranges_freq[16] = { 3, 2, 1, 0, -1, -2, -3, };
597 static const int bm52x_ranges_duty[16] = { 2, 1, };
598 static const int bm52x_ranges_ohm[16] = { 1, 0, -1, -2, -3, -4, };
599 static const int bm52x_ranges_cond[16] = { 11, };
600 static const int bm52x_ranges_cap[16] = { 11, 10, 9, 8, 7, 6, 5, };
601 static const int bm52x_ranges_diode[16] = { 3, };
602 static const int bm52x_ranges_temp[16] = { 0, };
603 static const int bm52x_ranges_amp[16] = { 3, 2, };
604 static const int bm52x_ranges_milliamp[16] = { 5, 4, };
605 static const int bm52x_ranges_microamp[16] = { 7, 6, };
606
607 /** Calculate checksum of four-HID-report responses (recordings). */
608 static 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  */
623 static 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. */
702 static 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. */
722 static 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. */
744 static 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. */
766 static 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. */
788 static 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
807 static 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
851 static 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. */
1179 static 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. */
1297 static 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. */
1304 static 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
1310 SR_PRIV void *brymen_bm52x_state_init(void)
1311 {
1312         return g_malloc0(sizeof(struct brymen_bm52x_state));
1313 }
1314
1315 SR_PRIV void brymen_bm52x_state_free(void *state)
1316 {
1317         g_free(state);
1318 }
1319
1320 SR_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
1347 SR_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
1382 SR_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  */
1451 static 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  */
1517 SR_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 }