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