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