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