]> sigrok.org Git - libsigrok.git/blame - src/dmm/bm52x.c
output/csv: use intermediate time_t var, silence compiler warning
[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
70a23b50 29 * http://web.archive.org/web/20180119175327/http://brymen.com/product-html/images/DownloadList/ProtocolList/BM520-BM520s_List/BM520-BM520s-10000-count-professional-dual-display-mobile-logging-DMMs-protocol.zip
0931639a 30 *
6bee394d
GS
31 * This parser was initially created for BM520s devices and tested with
32 * BM525s. The Brymen BM820s family of devices uses the same protocol,
33 * with just 0x82 instead of 0x52 in request packets and in the fixed
34 * fields of the responses. Which means that the packet parser can get
35 * shared among the BM520s and BM820s devices, but validity check needs
36 * to be individual, and the "wrong" packet request will end up without
37 * a response. Compared to BM520s the BM820s has dBm (in the protocol)
38 * and NCV (not seen in the protocol) and is non-logging (live only).
39 * BM820s support was tested with BM829s.
40 *
0931639a
GS
41 * The parser implementation was tested with a Brymen BM525s meter. Some
42 * of the responses differ from the vendor's documentation:
43 * - Recording session total byte counts don't start after the byte count
44 * field, but instead include this field and the model ID (spans _every_
45 * byte in the stream).
46 * - Recording session start/end markers are referred to as DLE, STX,
47 * and ETX. Observed traffic instead sends 0xee, 0xa0, and 0xc0.
400bc4ff
GS
48 */
49
50/*
51 * TODO
400bc4ff
GS
52 * - Some of the meter's functions and indications cannot get expressed
53 * by means of sigrok MQ and flags terms. Some indicator's meaning is
54 * unknown or uncertain, and thus their state is not evaluated.
55 * - MAX-MIN, the span between extreme values, referred to as Vp-p.
56 * - AVG is not available in BM525s and BM521s.
57 * - LoZ, eliminating ghost voltages.
58 * - LPF, low pass filter.
400bc4ff
GS
59 * - low battery, emits sr_warn() but isn't seen in the feed.
60 * - @, 4-20mA loop, % (main display, left hand side), Hi/Lo. Some of
61 * these are in the vendor's documentation for the DMM packet but not
62 * supported by the BM525s device which motivated the creation of the
63 * parser's and was used to test its operation.
64 * - It's a guess that the many undocumented bits (44 of them) are
65 * related to the bargraph (40 ticks, overflow, sign, 6/10 scale).
66 * - Should T1-T2 have a delta ("relative") decoration? But the meter's
67 * "relative" feature is flexible, accepts any display value as the
68 * reference, including min/max/diff when displayed upon activation.
69 * - The "beep jack" displays "InEr" in the secondary display. This is
70 * not caught here, no PC side message gets emitted.
0931639a
GS
71 * - Support for recordings is mostly untested. It was written to the
72 * letter of the vendor documentation, but was not verified to work
73 * for all of the many meter's modes including ranges. Inspection of
74 * the full byte stream is necessary on one hand since random access
75 * is not available, and useful on the other hand for consistency
76 * checks.
400bc4ff
GS
77 */
78
79#include <config.h>
80#include <libsigrok/libsigrok.h>
81#include "libsigrok-internal.h"
82#include <math.h>
83#include <string.h>
0931639a 84#include <strings.h>
400bc4ff
GS
85
86#define LOG_PREFIX "brymen-bm52x"
87
0931639a
GS
88/*
89 * DMM specific device options, and state keeping. All of it is related
90 * to recorded information in contrast to live readings. There also are
91 * four types of requesting HID reports that need to be sent.
92 */
93
94static 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
101struct 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
112enum bm52x_reqtype {
6bee394d
GS
113 REQ_LIVE_READ_520,
114 REQ_LIVE_READ_820,
0931639a
GS
115 REQ_REC_HEAD,
116 REQ_REC_NEXT,
117 REQ_REC_CURR,
118};
119
400bc4ff 120#ifdef HAVE_SERIAL_COMM
0931639a 121static int bm52x_send_req(struct sr_serial_dev_inst *serial, enum bm52x_reqtype t)
400bc4ff 122{
6bee394d
GS
123 static const uint8_t req_live_520[] = { 0x00, 0x00, 0x52, 0x66, };
124 static const uint8_t req_live_820[] = { 0x00, 0x00, 0x82, 0x66, };
0931639a
GS
125 static const uint8_t req_head[] = { 0x00, 0x00, 0x52, 0x88, };
126 static const uint8_t req_next[] = { 0x00, 0x00, 0x52, 0x89, };
127 static const uint8_t req_curr[] = { 0x00, 0x00, 0x52, 0x8a, };
128 static const uint8_t *req_bytes[] = {
6bee394d
GS
129 [REQ_LIVE_READ_520] = req_live_520,
130 [REQ_LIVE_READ_820] = req_live_820,
0931639a
GS
131 [REQ_REC_HEAD] = req_head,
132 [REQ_REC_NEXT] = req_next,
133 [REQ_REC_CURR] = req_curr,
134 };
6bee394d 135 static const size_t req_len = ARRAY_SIZE(req_live_520);
0931639a
GS
136
137 const uint8_t *p;
138 size_t l;
139 int ret;
400bc4ff 140
0931639a
GS
141 if (t >= ARRAY_SIZE(req_bytes))
142 return SR_ERR_ARG;
143 p = req_bytes[t];
144 l = req_len;
145 ret = serial_write_nonblocking(serial, p, l);
146 if (ret < 0)
147 return ret;
148 if ((size_t)ret != l)
149 return SR_ERR_IO;
400bc4ff
GS
150
151 return SR_OK;
152}
0931639a
GS
153
154SR_PRIV int sr_brymen_bm52x_packet_request(struct sr_serial_dev_inst *serial)
155{
6bee394d
GS
156 return bm52x_send_req(serial, REQ_LIVE_READ_520);
157}
158
159SR_PRIV int sr_brymen_bm82x_packet_request(struct sr_serial_dev_inst *serial)
160{
161 return bm52x_send_req(serial, REQ_LIVE_READ_820);
0931639a 162}
400bc4ff
GS
163#endif
164
0931639a
GS
165/*
166 * The following code interprets live readings ("real-time download")
167 * which arrive in the "traditional" bitmap for LCD segments. Reading
168 * previously recorded measurements ("memory data sets") differs a lot
169 * and is handled in other code paths.
170 */
171
400bc4ff
GS
172SR_PRIV gboolean sr_brymen_bm52x_packet_valid(const uint8_t *buf)
173{
174 if (buf[16] != 0x52)
175 return FALSE;
176 if (buf[17] != 0x52)
177 return FALSE;
178 if (buf[18] != 0x52)
179 return FALSE;
180 if (buf[19] != 0x52)
181 return FALSE;
182
183 return TRUE;
184}
185
6bee394d
GS
186SR_PRIV gboolean sr_brymen_bm82x_packet_valid(const uint8_t *buf)
187{
188 if (buf[16] != 0x82)
189 return FALSE;
190 if (buf[17] != 0x82)
191 return FALSE;
192 if (buf[18] != 0x82)
193 return FALSE;
194 if (buf[19] != 0x82)
195 return FALSE;
196
197 return TRUE;
198}
199
400bc4ff
GS
200/*
201 * Data bytes in the DMM packet encode LCD segments in an unusual order
202 * (bgcpafed) and in an unusual position (bit 4 being the decimal point
203 * for some digits, an additional indicator for others). Fortunately all
204 * eight digits encode their segments in identical ways across the bytes.
205 *
206 * These routines convert LCD segments to characters, and a section of the
207 * DMM packet (which corresponds to the primary or secondary display) to
208 * the text representation of the measurement's value, before regular text
209 * to number conversion is applied, and SI units and their prefixes get
210 * derived from more indicators. It's important to keep in mind similar
211 * indicators exist for main and secondary displays in different locations.
212 */
213
214static 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
250static 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 */
299static 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
529SR_PRIV int sr_brymen_bm52x_parse(const uint8_t *buf, float *val,
530 struct sr_datafeed_analog *analog, void *info)
531{
532 struct brymen_bm52x_info *info_local;
533 size_t ch_idx;
534
535 /*
536 * Scan a portion of the received DMM packet which corresponds
537 * to the caller's specified display. Then prepare to scan a
538 * different portion of the packet for another display. This
539 * routine gets called multiple times for one received packet.
540 */
541 info_local = info;
542 ch_idx = info_local->ch_idx;
543 brymen_bm52x_parse(buf, val, analog, ch_idx);
544 info_local->ch_idx = ch_idx + 1;
545
546 return SR_OK;
547}
0931639a
GS
548
549/*
550 * The above code paths support live readings ("real-time download").
551 * The below code paths support recordings ("memory data sets") which
552 * use different requests and responses and measurement representation
553 * which feels like "a different meter".
554 */
555
556/*
557 * Developer notes, example data for recorded sessions.
558 *
559 * model
560 * 01
561 * total bytes
562 * e6 02 00
563 * session count
564 * 01 00
565 * "DLE/STX" marker
566 * ee a0
567 * PS/NS addresses
568 * 8a 03 a0 60 03 a0
569 * func/sel/stat (DC-V, single display)
570 * 02 00 00
571 * session page length in bytes (3 * 240)
572 * d0 02 00
573 * main[/secondary] display data
574 * 00 00 00 00
575 * checksums and padding
576 * 7c 05 00 00 00 00 00 00
577 * 00 00 80 00 00 80 00 00 80 00 00 80 00 00 00 00 00 80 00 00 80 00 00 80 80 03 00 00 00 00 00 00
578 * 00 00 00 00 00 00 00 00 80 00 00 80 00 00 80 00 00 80 00 00 80 00 00 80 00 03 00 00 00 00 00 00
579 * ...
580 * 00 00 80 00 00 00 00 00 00 00 00 80 00 00 80 00 00 80 00 00 80 00 00 80 00 03 00 00 00 00 00 00
581 * 00 00 80 00 00 80 00 00 80 00 00 80 00 00 80 00 00 80 00 00
582 * "DLE/ETX" marker
583 * ee c0
584 * ae 04 00 00 00 00 00 00
585 *
586 * - Checksum in bytes[25:24] is the mere sum of bytes[0:23].
587 * - Model ID is 0 or 1 -- does this translate to BM521s and BM525s?
588 * - Total byte count _includes_ everything starting at model ID.
589 * - There is no measurements count for a session page, but its length
590 * in bytes, and a dual display flag, which lets us derive the count.
591 * - STX/ETX/DLE markers don't use the expected ASCII codes.
592 */
593
594/*
595 * See vendor doc table 3.1 "Logging interval". Includes sub-1Hz rates,
596 * but also sub-1s intervals. Let's keep both presentations at hand.
597 */
598static 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 */
624static const int bm52x_ranges_volt[16] = { 3, 2, 1, 0, };
625static const int bm52x_ranges_millivolt[16] = { 5, 4, };
626static const int bm52x_ranges_freq[16] = { 3, 2, 1, 0, -1, -2, -3, };
627static const int bm52x_ranges_duty[16] = { 2, 1, };
628static const int bm52x_ranges_ohm[16] = { 1, 0, -1, -2, -3, -4, };
629static const int bm52x_ranges_cond[16] = { 11, };
630static const int bm52x_ranges_cap[16] = { 11, 10, 9, 8, 7, 6, 5, };
631static const int bm52x_ranges_diode[16] = { 3, };
632static const int bm52x_ranges_temp[16] = { 0, };
633static const int bm52x_ranges_amp[16] = { 3, 2, };
634static const int bm52x_ranges_milliamp[16] = { 5, 4, };
635static const int bm52x_ranges_microamp[16] = { 7, 6, };
636
637/** Calculate checksum of four-HID-report responses (recordings). */
638static uint16_t bm52x_rec_checksum(const uint8_t *b, size_t l)
639{
640 uint16_t cs;
641
642 cs = 0;
643 while (l--)
644 cs += *b++;
645
646 return cs;
647}
648
649/**
650 * Retrieve the first/next chunk of recording information.
651 * Support for live readings is theoretical, and unused/untested.
652 */
f93bf8ba 653#ifdef HAVE_SERIAL_COMM
0931639a
GS
654static int bm52x_rec_next_rsp(struct sr_serial_dev_inst *serial,
655 enum bm52x_reqtype req, struct brymen_bm52x_state *state)
656{
657 uint8_t *b;
658 size_t l;
659 int ret;
660
661 /* Seed internal state when sending the HEAD request. */
6bee394d 662 if (req == REQ_REC_HEAD || req == REQ_LIVE_READ_520)
0931639a
GS
663 memset(&state->rsp, 0, sizeof(state->rsp));
664
665 /* Move unprocessed content to the front. */
666 if (state->rsp.read_pos) {
667 b = &state->rsp.buff[0];
668 l = state->rsp.fill_pos - state->rsp.read_pos;
669 if (l)
670 memmove(&b[0], &b[state->rsp.read_pos], l);
671 state->rsp.fill_pos -= state->rsp.read_pos;
672 state->rsp.read_pos = 0;
673 }
674
675 /* Avoid queries for non-existing data. Limit NEXT requests. */
676 if (req == REQ_REC_NEXT && !state->rsp.remain)
677 return SR_ERR_IO;
678
679 /* Add another response chunk to the read buffer. */
680 b = &state->rsp.buff[state->rsp.fill_pos];
6bee394d 681 l = req == REQ_LIVE_READ_520 ? 24 : 32;
0931639a
GS
682 if (sizeof(state->rsp.buff) - state->rsp.fill_pos < l)
683 return SR_ERR_BUG;
684 ret = bm52x_send_req(serial, req);
685 if (ret != SR_OK)
686 return ret;
687 ret = serial_read_blocking(serial, b, l, 1000);
688 if (ret < 0)
689 return ret;
690 if ((size_t)ret != l)
691 return SR_ERR_IO;
692 state->rsp.fill_pos += l;
693
694 /* Devel support: dump the new receive data. */
695 if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
696 GString *text;
697 const char *req_text;
698
6bee394d 699 req_text = (req == REQ_LIVE_READ_520) ? "LIVE" :
0931639a
GS
700 (req == REQ_REC_HEAD) ? "MEM HEAD" :
701 (req == REQ_REC_NEXT) ? "MEM NEXT" :
702 (req == REQ_REC_CURR) ? "MEM CURR" :
703 "<inv>";
704 text = sr_hexdump_new(b, l);
705 sr_spew("%s: %s", req_text, text->str);
706 sr_hexdump_free(text);
707 }
708
709 /* Verify checksum. No CURR repetition is attempted here. */
710 if (l > 24) {
711 uint16_t calc, rcvd;
712
713 calc = bm52x_rec_checksum(b, 24);
714 rcvd = read_u16le(&b[24]);
715 if (calc != rcvd)
716 return SR_ERR_DATA;
717 state->rsp.fill_pos -= 32 - 24;
718 }
719
720 /* Seed amount of total available data from HEAD response. */
721 if (req == REQ_REC_HEAD) {
722 const uint8_t *rdptr;
723
724 rdptr = &state->rsp.buff[0];
725 (void)read_u8_inc(&rdptr); /* model ID */
726 state->rsp.remain = read_u24le_inc(&rdptr); /* byte count */
727 }
728
729 return SR_OK;
730}
f93bf8ba
GS
731#else /* have serial comm */
732static int bm52x_rec_next_rsp(struct sr_serial_dev_inst *serial,
733 enum bm52x_reqtype req, struct brymen_bm52x_state *state)
734{
735 (void)serial;
736 (void)req;
737 (void)state;
738 (void)bm52x_rec_checksum;
739 return SR_ERR_NA;
740}
741#endif /* have serial comm */
0931639a
GS
742
743/** Make sure a minimum amount of response data is available. */
744static 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. */
764static 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. */
786static 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. */
808static 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. */
830static 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
849static 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
893static 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. */
1221static 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. */
1339static 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. */
1346static 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
1352SR_PRIV void *brymen_bm52x_state_init(void)
1353{
1354 return g_malloc0(sizeof(struct brymen_bm52x_state));
1355}
1356
1357SR_PRIV void brymen_bm52x_state_free(void *state)
1358{
1359 g_free(state);
1360}
1361
1362SR_PRIV int brymen_bm52x_config_get(void *st, uint32_t key, GVariant **data,
1363 const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
1364{
1365 struct brymen_bm52x_state *state;
9eba5419 1366 char text[32];
0931639a
GS
1367
1368 state = st;
1369
1370 if (!sdi)
1371 return SR_ERR_NA;
1372 (void)cg;
1373
1374 switch (key) {
1375 case SR_CONF_DATA_SOURCE:
1376 if (!state)
1377 return SR_ERR_ARG;
1378 if (state->sess_idx == 0)
1379 snprintf(text, sizeof(text), "Live");
1380 else
1381 snprintf(text, sizeof(text), "Rec-%zu", state->sess_idx);
1382 *data = g_variant_new_string(text);
1383 return SR_OK;
1384 default:
1385 return SR_ERR_NA;
1386 }
1387}
1388
1389SR_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
1424SR_PRIV int brymen_bm52x_config_list(void *st, uint32_t key, GVariant **data,
1425 const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
1426{
1427 struct brymen_bm52x_state *state;
1428 struct sr_serial_dev_inst *serial;
1429 int ret;
1430 size_t count, idx;
1431 GVariantBuilder gvb;
46a36bf0 1432 char name[32];
0931639a
GS
1433
1434 /*
1435 * Have common keys handled by caller's common code.
1436 * ERR N/A results in the caller's logic handling the request.
1437 * Only handle strictly local properties here in this code path.
1438 */
1439 switch (key) {
1440 case SR_CONF_SCAN_OPTIONS:
1441 /* Scan options. Common property. */
1442 return SR_ERR_NA;
1443 case SR_CONF_DEVICE_OPTIONS:
1444 if (!sdi)
1445 /* Driver options. Common property. */
1446 return SR_ERR_NA;
1447 if (cg)
1448 /* Channel group's devopts. Common error path. */
1449 return SR_ERR_NA;
1450 /* List meter's local device options. Overrides common data. */
1451 *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
1452 devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
1453 return SR_OK;
1454 case SR_CONF_DATA_SOURCE:
1455 state = st;
1456 if (!sdi)
1457 return SR_ERR_ARG;
1458 serial = sdi->conn;
1459 ret = bm52x_rec_get_count(state, serial);
1460 if (ret < 0)
1461 return ret;
1462 count = ret;
1463 g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
1464 for (idx = 0; idx <= count; idx++) {
1465 if (idx == 0)
1466 snprintf(name, sizeof(name), "Live");
1467 else
1468 snprintf(name, sizeof(name), "Rec-%zu", idx);
1469 g_variant_builder_add(&gvb, "s", name);
1470 }
1471 *data = g_variant_builder_end(&gvb);
1472 return SR_OK;
1473 default:
1474 return SR_ERR_NA;
1475 }
1476}
1477
1478/**
1479 * BM520s specific receive routine for recorded measurements.
1480 *
1481 * @param[in] fd File descriptor.
1482 * @param[in] revents Mask of pending events.
1483 * @param[in] cb_data Call back data (receive handler state).
1484 *
1485 * @return TRUE when the routine needs to get re-invoked, FALSE when the
1486 * routine is done and no further invocations are required.
1487 *
1488 * @private
1489 *
1490 * It's an implementation detail that a single invocation will carry out
1491 * all the work that is involved in reading back recorded measurements.
1492 */
1493static 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 */
1559SR_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}