]> sigrok.org Git - libsigrok.git/blame - src/hardware/cem-dt-885x/protocol.c
Drop SR_CONF_SET flag from SR_CONF_CONTINUOUS options
[libsigrok.git] / src / hardware / cem-dt-885x / protocol.c
CommitLineData
8fa9368e
BV
1/*
2 * This file is part of the libsigrok project.
3 *
4 * Copyright (C) 2013 Bert Vermeulen <bert@biot.com>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
6ec6c43b 20#include <config.h>
e37c4b39 21#include <string.h>
8fa9368e
BV
22#include "protocol.h"
23
e37c4b39 24/* Length of expected payload for each token. */
329733d9 25static const int token_payloads[][2] = {
e37c4b39
BV
26 { TOKEN_WEIGHT_TIME_FAST, 0 },
27 { TOKEN_WEIGHT_TIME_SLOW, 0 },
28 { TOKEN_HOLD_MAX, 0 },
29 { TOKEN_HOLD_MIN, 0 },
30 { TOKEN_TIME, 3 },
31 { TOKEN_MEAS_RANGE_OVER, 0 },
32 { TOKEN_MEAS_RANGE_UNDER, 0 },
33 { TOKEN_STORE_FULL, 0 },
34 { TOKEN_RECORDING_ON, 0 },
35 { TOKEN_MEAS_WAS_READOUT, 1 },
36 { TOKEN_MEAS_WAS_BARGRAPH, 0 },
37 { TOKEN_MEASUREMENT, 2 },
38 { TOKEN_HOLD_NONE, 0 },
39 { TOKEN_BATTERY_LOW, 0 },
40 { TOKEN_MEAS_RANGE_OK, 0 },
41 { TOKEN_STORE_OK, 0 },
42 { TOKEN_RECORDING_OFF, 0 },
43 { TOKEN_WEIGHT_FREQ_A, 1 },
44 { TOKEN_WEIGHT_FREQ_C, 1 },
45 { TOKEN_BATTERY_OK, 0 },
46 { TOKEN_MEAS_RANGE_30_80, 0 },
47 { TOKEN_MEAS_RANGE_30_130, 0 },
48 { TOKEN_MEAS_RANGE_50_100, 0 },
49 { TOKEN_MEAS_RANGE_80_130, 0 },
50};
51
52static int find_token_payload_len(unsigned char c)
8fa9368e 53{
e37c4b39 54 unsigned int i;
8fa9368e 55
e37c4b39
BV
56 for (i = 0; i < ARRAY_SIZE(token_payloads); i++) {
57 if (token_payloads[i][0] == c)
58 return token_payloads[i][1];
59 }
60
61 return -1;
62}
63
64/* Process measurement or setting (0xa5 command). */
65static void process_mset(const struct sr_dev_inst *sdi)
66{
8fa9368e 67 struct dev_context *devc;
e37c4b39 68 struct sr_datafeed_packet packet;
5faebab2 69 struct sr_datafeed_analog_old analog;
e37c4b39
BV
70 GString *dbg;
71 float fvalue;
72 int i;
8fa9368e 73
e37c4b39
BV
74 devc = sdi->priv;
75 if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
76 dbg = g_string_sized_new(128);
77 g_string_printf(dbg, "got command 0x%.2x token 0x%.2x",
78 devc->cmd, devc->token);
79 if (devc->buf_len) {
80 g_string_append_printf(dbg, " payload");
81 for (i = 0; i < devc->buf_len; i++)
82 g_string_append_printf(dbg, " %.2x", devc->buf[i]);
83 }
84 sr_spew("%s", dbg->str);
85 g_string_free(dbg, TRUE);
86 }
87
0c5f2abc 88 switch (devc->token) {
e37c4b39
BV
89 case TOKEN_WEIGHT_TIME_FAST:
90 devc->cur_mqflags |= SR_MQFLAG_SPL_TIME_WEIGHT_F;
91 devc->cur_mqflags &= ~SR_MQFLAG_SPL_TIME_WEIGHT_S;
92 break;
93 case TOKEN_WEIGHT_TIME_SLOW:
94 devc->cur_mqflags |= SR_MQFLAG_SPL_TIME_WEIGHT_S;
95 devc->cur_mqflags &= ~SR_MQFLAG_SPL_TIME_WEIGHT_F;
96 break;
97 case TOKEN_WEIGHT_FREQ_A:
98 devc->cur_mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_A;
99 devc->cur_mqflags &= ~SR_MQFLAG_SPL_FREQ_WEIGHT_C;
100 break;
101 case TOKEN_WEIGHT_FREQ_C:
102 devc->cur_mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_C;
103 devc->cur_mqflags &= ~SR_MQFLAG_SPL_FREQ_WEIGHT_A;
104 break;
105 case TOKEN_HOLD_MAX:
106 devc->cur_mqflags |= SR_MQFLAG_HOLD | SR_MQFLAG_MAX;
107 devc->cur_mqflags &= ~SR_MQFLAG_MIN;
108 break;
109 case TOKEN_HOLD_MIN:
110 devc->cur_mqflags |= SR_MQFLAG_HOLD | SR_MQFLAG_MIN;
111 devc->cur_mqflags &= ~SR_MQFLAG_MAX;
112 break;
113 case TOKEN_HOLD_NONE:
114 devc->cur_mqflags &= ~(SR_MQFLAG_MAX | SR_MQFLAG_MIN | SR_MQFLAG_HOLD);
115 break;
116 case TOKEN_MEASUREMENT:
117 fvalue = ((devc->buf[0] & 0xf0) >> 4) * 100;
118 fvalue += (devc->buf[0] & 0x0f) * 10;
119 fvalue += ((devc->buf[1] & 0xf0) >> 4);
120 fvalue += (devc->buf[1] & 0x0f) / 10.0;
bc114328
BV
121 devc->last_spl = fvalue;
122 break;
123 case TOKEN_MEAS_WAS_READOUT:
124 case TOKEN_MEAS_WAS_BARGRAPH:
125 if (devc->cur_mqflags & (SR_MQFLAG_MAX | SR_MQFLAG_MIN)) {
126 if (devc->token == TOKEN_MEAS_WAS_BARGRAPH) {
127 /* The device still sends bargraph measurements even
128 * when in max/min hold mode. Suppress them here, unless
129 * they're readout values. This duplicates the behavior
130 * of the device display exactly. */
131 break;
132 }
133 }
5faebab2 134 memset(&analog, 0, sizeof(struct sr_datafeed_analog_old));
e37c4b39
BV
135 analog.mq = SR_MQ_SOUND_PRESSURE_LEVEL;
136 analog.mqflags = devc->cur_mqflags;
137 analog.unit = SR_UNIT_DECIBEL_SPL;
ba7dd8bb 138 analog.channels = sdi->channels;
e37c4b39 139 analog.num_samples = 1;
bc114328 140 analog.data = &devc->last_spl;
5faebab2 141 packet.type = SR_DF_ANALOG_OLD;
e37c4b39 142 packet.payload = &analog;
695dc859 143 sr_session_send(sdi, &packet);
e37c4b39
BV
144
145 devc->num_samples++;
146 if (devc->limit_samples && devc->num_samples >= devc->limit_samples)
695dc859 147 sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi);
e37c4b39 148 break;
e1af0e85
BV
149 case TOKEN_RECORDING_ON:
150 devc->recording = TRUE;
151 break;
152 case TOKEN_RECORDING_OFF:
153 devc->recording = FALSE;
154 break;
f157b2ee
BV
155 case TOKEN_MEAS_RANGE_30_80:
156 case TOKEN_MEAS_RANGE_30_130:
157 case TOKEN_MEAS_RANGE_50_100:
158 case TOKEN_MEAS_RANGE_80_130:
159 devc->cur_meas_range = devc->token;
160 break;
e37c4b39
BV
161 case TOKEN_TIME:
162 case TOKEN_STORE_OK:
163 case TOKEN_STORE_FULL:
e37c4b39
BV
164 case TOKEN_BATTERY_OK:
165 case TOKEN_BATTERY_LOW:
166 case TOKEN_MEAS_RANGE_OK:
167 case TOKEN_MEAS_RANGE_OVER:
168 case TOKEN_MEAS_RANGE_UNDER:
f3f19d11 169 /* Not useful, or not expressible in sigrok. */
e37c4b39
BV
170 break;
171 }
172
173}
174
cea26f6e
BV
175static void send_data(const struct sr_dev_inst *sdi, unsigned char *data,
176 uint64_t num_samples)
177{
178 struct dev_context *devc;
179 struct sr_datafeed_packet packet;
5faebab2 180 struct sr_datafeed_analog_old analog;
cea26f6e
BV
181 float fbuf[SAMPLES_PER_PACKET];
182 unsigned int i;
183
184 devc = sdi->priv;
185
0a1f7b09 186 for (i = 0; i < num_samples; i++) {
cea26f6e
BV
187 fbuf[i] = ((data[i * 2] & 0xf0) >> 4) * 100;
188 fbuf[i] += (data[i * 2] & 0x0f) * 10;
189 fbuf[i] += ((data[i * 2 + 1] & 0xf0) >> 4);
190 fbuf[i] += (data[i * 2 + 1] & 0x0f) / 10.0;
191 }
5faebab2 192 memset(&analog, 0, sizeof(struct sr_datafeed_analog_old));
cea26f6e
BV
193 analog.mq = SR_MQ_SOUND_PRESSURE_LEVEL;
194 analog.mqflags = devc->cur_mqflags;
195 analog.unit = SR_UNIT_DECIBEL_SPL;
ba7dd8bb 196 analog.channels = sdi->channels;
cea26f6e
BV
197 analog.num_samples = num_samples;
198 analog.data = fbuf;
5faebab2 199 packet.type = SR_DF_ANALOG_OLD;
cea26f6e 200 packet.payload = &analog;
695dc859 201 sr_session_send(sdi, &packet);
cea26f6e
BV
202
203 devc->num_samples += analog.num_samples;
204 if (devc->limit_samples && devc->num_samples >= devc->limit_samples)
695dc859 205 sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi);
cea26f6e
BV
206
207 return;
208}
209
e1af0e85
BV
210static void process_byte(const struct sr_dev_inst *sdi, const unsigned char c,
211 int handle_packets)
e37c4b39
BV
212{
213 struct dev_context *devc;
cea26f6e
BV
214 struct sr_datafeed_packet packet;
215 struct sr_datafeed_meta meta;
216 struct sr_config *src;
14cf708f 217 gint64 cur_time;
e37c4b39 218 int len;
8fa9368e
BV
219
220 if (!(devc = sdi->priv))
e37c4b39
BV
221 return;
222
223 if (c == 0xff) {
224 /* Device is in hold mode */
225 devc->cur_mqflags |= SR_MQFLAG_HOLD;
226
14cf708f
BV
227 if (devc->hold_last_sent == 0) {
228 /* First hold notification. */
229 devc->hold_last_sent = g_get_monotonic_time();
230 /* When the device leaves hold mode, it starts from scratch. */
231 devc->state = ST_INIT;
232 } else {
233 cur_time = g_get_monotonic_time();
234 if (cur_time - devc->hold_last_sent > HOLD_REPEAT_INTERVAL) {
235 /* Force the last measurement out again. */
236 devc->cmd = 0xa5;
237 devc->token = TOKEN_MEAS_WAS_READOUT;
e1af0e85
BV
238 if (handle_packets)
239 process_mset(sdi);
14cf708f
BV
240 devc->hold_last_sent = cur_time;
241 }
242 }
e37c4b39 243
e37c4b39
BV
244 return;
245 }
246 devc->cur_mqflags &= ~SR_MQFLAG_HOLD;
14cf708f 247 devc->hold_last_sent = 0;
e37c4b39
BV
248
249 if (devc->state == ST_INIT) {
250 if (c == 0xa5) {
251 devc->cmd = c;
252 devc->token = 0x00;
253 devc->state = ST_GET_TOKEN;
254 } else if (c == 0xbb) {
255 devc->cmd = c;
256 devc->buf_len = 0;
cea26f6e
BV
257 devc->state = ST_GET_LOG_HEADER;
258 sr_dbg("got command 0xbb");
e37c4b39
BV
259 }
260 } else if (devc->state == ST_GET_TOKEN) {
261 devc->token = c;
262 devc->buf_len = 0;
263 len = find_token_payload_len(devc->token);
264 if (len == -1 || len > 0) {
265 devc->buf_len = 0;
266 devc->state = ST_GET_DATA;
267 } else {
e1af0e85
BV
268 if (handle_packets)
269 process_mset(sdi);
e37c4b39
BV
270 devc->state = ST_INIT;
271 }
272 } else if (devc->state == ST_GET_DATA) {
273 len = find_token_payload_len(devc->token);
274 if (len == -1) {
275 /* We don't know this token. */
276 sr_dbg("Unknown 0xa5 token 0x%.2x", devc->token);
277 if (c == 0xa5 || c == 0xbb) {
278 /* Looks like a new command however. */
e1af0e85
BV
279 if (handle_packets)
280 process_mset(sdi);
e37c4b39
BV
281 devc->state = ST_INIT;
282 } else {
283 devc->buf[devc->buf_len++] = c;
284 if (devc->buf_len > BUF_SIZE) {
285 /* Shouldn't happen, ignore. */
286 devc->state = ST_INIT;
287 }
288 }
289 } else {
290 devc->buf[devc->buf_len++] = c;
291 if (devc->buf_len == len) {
e1af0e85
BV
292 if (handle_packets)
293 process_mset(sdi);
e37c4b39
BV
294 devc->state = ST_INIT;
295 } else if (devc->buf_len > BUF_SIZE) {
296 /* Shouldn't happen, ignore. */
297 devc->state = ST_INIT;
298 }
299 }
cea26f6e
BV
300 } else if (devc->state == ST_GET_LOG_HEADER) {
301 sr_dbg("log header: 0x%.2x", c);
302 if (devc->buf_len < 2)
303 devc->buf[devc->buf_len++] = c;
304 if (devc->buf_len == 2) {
305 sr_dbg("Device says it has %d bytes stored.",
306 ((devc->buf[0] << 8) + devc->buf[1]) - 100);
307 devc->buf_len = 0;
308 devc->state = ST_GET_LOG_RECORD_META;
309 }
310 } else if (devc->state == ST_GET_LOG_RECORD_META) {
311 sr_dbg("log meta: 0x%.2x", c);
312 if (c == RECORD_END) {
313 devc->state = ST_INIT;
314 /* Stop acquisition after transferring all stored
315 * records. Otherwise the frontend would have no
316 * way to tell where stored data ends and live
317 * measurements begin. */
695dc859 318 sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi);
cea26f6e
BV
319 } else if (c == RECORD_DATA) {
320 devc->buf_len = 0;
321 devc->state = ST_GET_LOG_RECORD_DATA;
322 } else {
323 /* RECORD_DBA/RECORD_DBC + 7 bytes of metadata */
324 devc->buf[devc->buf_len++] = c;
325 if (devc->buf_len < 8)
326 /* Keep filling up the record header. */
327 return;
328 if (devc->buf[0] == RECORD_DBA)
329 devc->cur_mqflags = SR_MQFLAG_SPL_FREQ_WEIGHT_A;
330 else if (devc->buf[0] == RECORD_DBC)
331 devc->cur_mqflags = SR_MQFLAG_SPL_FREQ_WEIGHT_C;
332 else {
333 /* Shouldn't happen. */
334 sr_dbg("Unknown record token 0x%.2x", c);
335 return;
336 }
337 packet.type = SR_DF_META;
338 packet.payload = &meta;
339 src = sr_config_new(SR_CONF_SAMPLE_INTERVAL,
340 g_variant_new_uint64(devc->buf[7] * 1000));
341 meta.config = g_slist_append(NULL, src);
695dc859 342 sr_session_send(sdi, &packet);
cea26f6e
BV
343 g_free(src);
344 devc->buf_len = 0;
345 }
346 } else if (devc->state == ST_GET_LOG_RECORD_DATA) {
347 sr_dbg("log data: 0x%.2x", c);
348 if (c == RECORD_DBA || c == RECORD_DBC || c == RECORD_DATA || c == RECORD_END) {
349 /* Work around off-by-one bug in device firmware. This
350 * happens only on the last record, i.e. before RECORD_END */
351 if (devc->buf_len & 1)
352 devc->buf_len--;
353 /* Done with this set of samples */
354 send_data(sdi, devc->buf, devc->buf_len / 2);
355 devc->buf_len = 0;
356
357 /* Process this meta marker in the right state. */
358 devc->state = ST_GET_LOG_RECORD_META;
359 process_byte(sdi, c, handle_packets);
360 } else {
361 devc->buf[devc->buf_len++] = c;
362 if (devc->buf_len == SAMPLES_PER_PACKET * 2) {
363 send_data(sdi, devc->buf, devc->buf_len / 2);
364 devc->buf_len = 0;
365 }
366 }
e37c4b39
BV
367 }
368
369}
370
371SR_PRIV int cem_dt_885x_receive_data(int fd, int revents, void *cb_data)
372{
373 const struct sr_dev_inst *sdi;
cea26f6e 374 struct dev_context *devc;
e37c4b39 375 struct sr_serial_dev_inst *serial;
cea26f6e 376 unsigned char c, cmd;
e37c4b39
BV
377
378 (void)fd;
379
380 if (!(sdi = cb_data))
8fa9368e
BV
381 return TRUE;
382
cea26f6e 383 devc = sdi->priv;
e37c4b39 384 serial = sdi->conn;
8fa9368e 385 if (revents == G_IO_IN) {
d7b269da 386 if (serial_read_nonblocking(serial, &c, 1) != 1)
e37c4b39 387 return TRUE;
e1af0e85 388 process_byte(sdi, c, TRUE);
cea26f6e
BV
389
390 if (devc->enable_data_source_memory) {
391 if (devc->state == ST_GET_LOG_HEADER) {
392 /* Memory transfer started. */
393 devc->enable_data_source_memory = FALSE;
394 } else {
395 /* Tell device to start transferring from memory. */
396 cmd = CMD_TRANSFER_MEMORY;
d7b269da 397 serial_write_nonblocking(serial, &cmd, 1);
cea26f6e
BV
398 }
399 }
8fa9368e
BV
400 }
401
402 return TRUE;
403}
e1af0e85 404
a4eb4b29 405static int wait_for_token(const struct sr_dev_inst *sdi, int8_t *tokens, int timeout)
e1af0e85
BV
406{
407 struct dev_context *devc;
408 struct sr_serial_dev_inst *serial;
409 gint64 start_time;
410 int i;
411 unsigned char c;
412
413 serial = sdi->conn;
414 devc = sdi->priv;
415 devc->state = ST_INIT;
416 start_time = g_get_monotonic_time() / 1000;
417 while (TRUE) {
d7b269da 418 if (serial_read_nonblocking(serial, &c, 1) != 1)
e1af0e85
BV
419 /* Device might have gone away. */
420 return SR_ERR;
421 process_byte(sdi, c, FALSE);
422 if (devc->state != ST_INIT)
423 /* Wait for a whole packet to get processed. */
424 continue;
425 for (i = 0; tokens[i] != -1; i++) {
426 if (devc->token == tokens[i]) {
427 sr_spew("wait_for_token: got token 0x%.2x", devc->token);
428 return SR_OK;
429 }
430 }
431 if (timeout && g_get_monotonic_time() / 1000 - start_time > timeout)
432 return SR_ERR_TIMEOUT;
433 }
434
435 return SR_OK;
436}
437
438/* cmd is the command to send, tokens are the tokens that denote the state
439 * which the command affects. The first token is the desired state. */
d87c1766 440static int cem_dt_885x_toggle(const struct sr_dev_inst *sdi, uint8_t cmd,
a4eb4b29 441 int8_t *tokens, int timeout)
e1af0e85
BV
442{
443 struct dev_context *devc;
444 struct sr_serial_dev_inst *serial;
445
446 serial = sdi->conn;
447 devc = sdi->priv;
448
449 /* The device doesn't respond to commands very well. The
450 * only thing to do is wait for the token that will confirm
451 * whether the command worked or not, and resend if needed. */
452 while (TRUE) {
d7b269da 453 if (serial_write_nonblocking(serial, (const void *)&cmd, 1) != 1)
e1af0e85 454 return SR_ERR;
be733919 455 if (wait_for_token(sdi, tokens, timeout) == SR_ERR)
e1af0e85
BV
456 return SR_ERR;
457 if (devc->token == tokens[0])
458 /* It worked. */
459 break;
460 }
461
462 return SR_OK;
463}
464
0cd9107d
BV
465SR_PRIV gboolean cem_dt_885x_recording_get(const struct sr_dev_inst *sdi,
466 int *state)
e1af0e85
BV
467{
468 struct dev_context *devc;
a4eb4b29 469 int8_t tokens[5];
e1af0e85
BV
470
471 devc = sdi->priv;
e1af0e85
BV
472 if (devc->recording == -1) {
473 /* Didn't pick up device state yet. */
474 tokens[0] = TOKEN_RECORDING_ON;
475 tokens[1] = TOKEN_RECORDING_OFF;
476 tokens[2] = -1;
0cd9107d 477 if (wait_for_token(sdi, tokens, 510) != SR_OK)
e1af0e85
BV
478 return SR_ERR;
479 }
0cd9107d 480 *state = devc->token == TOKEN_RECORDING_ON;
e1af0e85 481
0cd9107d 482 return SR_OK;
e1af0e85
BV
483}
484
0cd9107d
BV
485SR_PRIV int cem_dt_885x_recording_set(const struct sr_dev_inst *sdi,
486 gboolean state)
e1af0e85
BV
487{
488 struct dev_context *devc;
489 int ret;
a4eb4b29 490 int8_t tokens[5];
e1af0e85
BV
491
492 devc = sdi->priv;
493
494 /* The toggle below needs the desired state in first position. */
0cd9107d 495 if (state) {
e1af0e85
BV
496 tokens[0] = TOKEN_RECORDING_ON;
497 tokens[1] = TOKEN_RECORDING_OFF;
498 } else {
499 tokens[0] = TOKEN_RECORDING_OFF;
500 tokens[1] = TOKEN_RECORDING_ON;
501 }
502 tokens[2] = -1;
503
504 if (devc->recording == -1) {
505 /* Didn't pick up device state yet. */
506 if (wait_for_token(sdi, tokens, 0) != SR_OK)
507 return SR_ERR;
508 if (devc->token == tokens[0])
509 /* Nothing to do. */
510 return SR_OK;
0cd9107d 511 } else if (devc->recording == state)
e1af0e85
BV
512 /* Nothing to do. */
513 return SR_OK;
514
be733919
BV
515 /* Recording state notifications are sent at 2Hz, so allow just over
516 * that, 510ms, for the state to come in. */
517 ret = cem_dt_885x_toggle(sdi, CMD_TOGGLE_RECORDING, tokens, 510);
518
519 return ret;
520}
521
522SR_PRIV int cem_dt_885x_weight_freq_get(const struct sr_dev_inst *sdi)
523{
524 struct dev_context *devc;
525 int cur_setting;
a4eb4b29 526 int8_t tokens[5];
be733919
BV
527
528 devc = sdi->priv;
529
530 cur_setting = devc->cur_mqflags & (SR_MQFLAG_SPL_FREQ_WEIGHT_A | SR_MQFLAG_SPL_FREQ_WEIGHT_C);
531 if (cur_setting == 0) {
532 /* Didn't pick up device state yet. */
533 tokens[0] = TOKEN_WEIGHT_FREQ_A;
534 tokens[1] = TOKEN_WEIGHT_FREQ_C;
535 tokens[2] = -1;
536 if (wait_for_token(sdi, tokens, 0) != SR_OK)
537 return SR_ERR;
538 if (devc->token == TOKEN_WEIGHT_FREQ_A)
539 return SR_MQFLAG_SPL_FREQ_WEIGHT_A;
540 else
541 return SR_MQFLAG_SPL_FREQ_WEIGHT_C;
542 } else
543 return cur_setting;
be733919
BV
544}
545
546SR_PRIV int cem_dt_885x_weight_freq_set(const struct sr_dev_inst *sdi, int freqw)
547{
548 struct dev_context *devc;
549 int cur_setting, ret;
a4eb4b29 550 int8_t tokens[5];
be733919
BV
551
552 devc = sdi->priv;
553
554 cur_setting = devc->cur_mqflags & (SR_MQFLAG_SPL_FREQ_WEIGHT_A | SR_MQFLAG_SPL_FREQ_WEIGHT_C);
555 if (cur_setting == freqw)
556 /* Already set to this frequency weighting. */
557 return SR_OK;
558
559 /* The toggle below needs the desired state in first position. */
560 if (freqw == SR_MQFLAG_SPL_FREQ_WEIGHT_A) {
561 tokens[0] = TOKEN_WEIGHT_FREQ_A;
562 tokens[1] = TOKEN_WEIGHT_FREQ_C;
563 } else {
564 tokens[0] = TOKEN_WEIGHT_FREQ_C;
565 tokens[1] = TOKEN_WEIGHT_FREQ_A;
566 }
567 tokens[2] = -1;
568
569 if (cur_setting == 0) {
570 /* Didn't pick up device state yet. */
571 if (wait_for_token(sdi, tokens, 0) != SR_OK)
572 return SR_ERR;
573 if (devc->token == tokens[0])
574 /* Nothing to do. */
575 return SR_OK;
576 }
577
578 /* 10ms timeout seems to work best for this. */
579 ret = cem_dt_885x_toggle(sdi, CMD_TOGGLE_WEIGHT_FREQ, tokens, 10);
e1af0e85
BV
580
581 return ret;
582}
1487ce4f
BV
583
584SR_PRIV int cem_dt_885x_weight_time_get(const struct sr_dev_inst *sdi)
585{
586 struct dev_context *devc;
587 int cur_setting;
a4eb4b29 588 int8_t tokens[5];
1487ce4f
BV
589
590 devc = sdi->priv;
591
592 cur_setting = devc->cur_mqflags & (SR_MQFLAG_SPL_TIME_WEIGHT_F | SR_MQFLAG_SPL_TIME_WEIGHT_S);
593 if (cur_setting == 0) {
594 /* Didn't pick up device state yet. */
595 tokens[0] = TOKEN_WEIGHT_TIME_FAST;
596 tokens[1] = TOKEN_WEIGHT_TIME_SLOW;
597 tokens[2] = -1;
598 if (wait_for_token(sdi, tokens, 0) != SR_OK)
599 return SR_ERR;
600 if (devc->token == TOKEN_WEIGHT_TIME_FAST)
601 return SR_MQFLAG_SPL_TIME_WEIGHT_F;
602 else
603 return SR_MQFLAG_SPL_TIME_WEIGHT_S;
604 } else
605 return cur_setting;
1487ce4f
BV
606}
607
608SR_PRIV int cem_dt_885x_weight_time_set(const struct sr_dev_inst *sdi, int timew)
609{
610 struct dev_context *devc;
611 int cur_setting, ret;
a4eb4b29 612 int8_t tokens[5];
1487ce4f
BV
613
614 devc = sdi->priv;
615
616 cur_setting = devc->cur_mqflags & (SR_MQFLAG_SPL_TIME_WEIGHT_F | SR_MQFLAG_SPL_TIME_WEIGHT_S);
617 if (cur_setting == timew)
618 /* Already set to this time weighting. */
619 return SR_OK;
620
621 /* The toggle below needs the desired state in first position. */
622 if (timew == SR_MQFLAG_SPL_TIME_WEIGHT_F) {
623 tokens[0] = TOKEN_WEIGHT_TIME_FAST;
624 tokens[1] = TOKEN_WEIGHT_TIME_SLOW;
625 } else {
626 tokens[0] = TOKEN_WEIGHT_TIME_SLOW;
627 tokens[1] = TOKEN_WEIGHT_TIME_FAST;
628 }
629 tokens[2] = -1;
630
631 if (cur_setting == 0) {
632 /* Didn't pick up device state yet. */
633 if (wait_for_token(sdi, tokens, 0) != SR_OK)
634 return SR_ERR;
635 if (devc->token == tokens[0])
636 /* Nothing to do. */
637 return SR_OK;
638 }
639
640 /* 51ms timeout seems to work best for this. */
641 ret = cem_dt_885x_toggle(sdi, CMD_TOGGLE_WEIGHT_TIME, tokens, 51);
642
643 return ret;
644}
a90e480c
BV
645
646SR_PRIV int cem_dt_885x_holdmode_get(const struct sr_dev_inst *sdi,
647 gboolean *holdmode)
648{
649 struct dev_context *devc;
a4eb4b29 650 int8_t tokens[5];
a90e480c
BV
651
652 devc = sdi->priv;
653
654 if (devc->cur_mqflags == 0) {
655 tokens[0] = TOKEN_HOLD_MAX;
656 tokens[1] = TOKEN_HOLD_MIN;
657 tokens[2] = TOKEN_HOLD_NONE;
658 tokens[3] = -1;
659 if (wait_for_token(sdi, tokens, 0) != SR_OK)
660 return SR_ERR;
661 if (devc->token == TOKEN_HOLD_MAX)
662 devc->cur_mqflags = SR_MQFLAG_MAX;
663 else if (devc->token == TOKEN_HOLD_MIN)
664 devc->cur_mqflags = SR_MQFLAG_MIN;
665 }
666 *holdmode = devc->cur_mqflags & (SR_MQFLAG_MAX | SR_MQFLAG_MIN);
667
668 return SR_OK;
669}
670
671SR_PRIV int cem_dt_885x_holdmode_set(const struct sr_dev_inst *sdi, int holdmode)
672{
673 struct dev_context *devc;
674 int cur_setting, ret;
a4eb4b29 675 int8_t tokens[5];
a90e480c
BV
676
677 devc = sdi->priv;
678
679 /* The toggle below needs the desired state in first position. */
680 if (holdmode == SR_MQFLAG_MAX) {
681 tokens[0] = TOKEN_HOLD_MAX;
682 tokens[1] = TOKEN_HOLD_MIN;
683 tokens[2] = TOKEN_HOLD_NONE;
684 } else if (holdmode == SR_MQFLAG_MIN) {
685 tokens[0] = TOKEN_HOLD_MIN;
686 tokens[1] = TOKEN_HOLD_MAX;
687 tokens[2] = TOKEN_HOLD_NONE;
688 } else {
689 tokens[0] = TOKEN_HOLD_NONE;
690 tokens[1] = TOKEN_HOLD_MAX;
691 tokens[2] = TOKEN_HOLD_MIN;
692 }
693 tokens[3] = -1;
694
695 if (devc->cur_mqflags == 0) {
696 /* Didn't pick up device state yet. */
697 if (wait_for_token(sdi, tokens, 0) != SR_OK)
698 return SR_ERR;
699 if (devc->token == tokens[0])
700 /* Nothing to do. */
701 return SR_OK;
702 } else {
703 cur_setting = devc->cur_mqflags & (SR_MQFLAG_MAX | SR_MQFLAG_MIN);
704 if (cur_setting == holdmode)
705 /* Already set correctly. */
706 return SR_OK;
707 }
708
709 /* 51ms timeout seems to work best for this. */
710 ret = cem_dt_885x_toggle(sdi, CMD_TOGGLE_HOLD_MAX_MIN, tokens, 51);
711
712 return ret;
713}
f157b2ee
BV
714
715SR_PRIV int cem_dt_885x_meas_range_get(const struct sr_dev_inst *sdi,
716 uint64_t *low, uint64_t *high)
717{
718 struct dev_context *devc;
a4eb4b29 719 int8_t tokens[5];
f157b2ee
BV
720
721 devc = sdi->priv;
722 if (devc->cur_meas_range == 0) {
723 tokens[0] = TOKEN_MEAS_RANGE_30_130;
724 tokens[1] = TOKEN_MEAS_RANGE_30_80;
725 tokens[2] = TOKEN_MEAS_RANGE_50_100;
726 tokens[3] = TOKEN_MEAS_RANGE_80_130;
727 tokens[4] = -1;
728 if (wait_for_token(sdi, tokens, 0) != SR_OK)
729 return SR_ERR;
730 devc->cur_meas_range = devc->token;
731 }
732
733 switch (devc->cur_meas_range) {
734 case TOKEN_MEAS_RANGE_30_130:
735 *low = 30;
736 *high = 130;
737 break;
738 case TOKEN_MEAS_RANGE_30_80:
739 *low = 30;
740 *high = 80;
741 break;
742 case TOKEN_MEAS_RANGE_50_100:
743 *low = 50;
744 *high = 100;
745 break;
746 case TOKEN_MEAS_RANGE_80_130:
747 *low = 80;
748 *high = 130;
749 break;
750 default:
751 return SR_ERR;
752 }
753
754 return SR_OK;
755}
756
757SR_PRIV int cem_dt_885x_meas_range_set(const struct sr_dev_inst *sdi,
758 uint64_t low, uint64_t high)
759{
760 struct dev_context *devc;
761 int ret;
a4eb4b29 762 int8_t token, tokens[6];
f157b2ee
BV
763
764 devc = sdi->priv;
765 if (low == 30 && high == 130)
766 token = TOKEN_MEAS_RANGE_30_130;
767 else if (low == 30 && high == 80)
768 token = TOKEN_MEAS_RANGE_30_80;
769 else if (low == 50 && high == 100)
770 token = TOKEN_MEAS_RANGE_50_100;
771 else if (low == 80 && high == 130)
772 token = TOKEN_MEAS_RANGE_80_130;
773 else
774 return SR_ERR;
775
776 sr_dbg("want 0x%.2x", token);
777 /* The toggle below needs the desired state in first position. */
778 tokens[0] = token;
779 tokens[1] = TOKEN_MEAS_RANGE_30_130;
780 tokens[2] = TOKEN_MEAS_RANGE_30_80;
781 tokens[3] = TOKEN_MEAS_RANGE_50_100;
782 tokens[4] = TOKEN_MEAS_RANGE_80_130;
783 tokens[5] = -1;
784
785 if (devc->cur_meas_range == 0) {
786 /* 110ms should be enough for two of these announcements */
787 if (wait_for_token(sdi, tokens, 110) != SR_OK)
788 return SR_ERR;
789 devc->cur_meas_range = devc->token;
790 }
791
792 if (devc->cur_meas_range == token)
793 /* Already set to this range. */
794 return SR_OK;
795
796 /* For measurement range, it works best to ignore announcements of the
797 * current setting and keep resending the toggle quickly. */
798 tokens[1] = -1;
799 ret = cem_dt_885x_toggle(sdi, CMD_TOGGLE_MEAS_RANGE, tokens, 11);
800
801 return ret;
802}
4c22355f
BV
803
804SR_PRIV int cem_dt_885x_power_off(const struct sr_dev_inst *sdi)
805{
806 struct sr_serial_dev_inst *serial;
807 char c, cmd;
808
809 serial = sdi->conn;
810
4c22355f
BV
811 cmd = CMD_TOGGLE_POWER_OFF;
812 while (TRUE) {
813 serial_flush(serial);
d7b269da 814 if (serial_write_nonblocking(serial, (const void *)&cmd, 1) != 1)
4c22355f
BV
815 return SR_ERR;
816 /* It never takes more than 23ms for the next token to arrive. */
817 g_usleep(25 * 1000);
d7b269da 818 if (serial_read_nonblocking(serial, &c, 1) != 1)
4c22355f
BV
819 /* Device is no longer responding. Good! */
820 break;
821 }
822
823 /* In case the user manually turns on the device again, reset
824 * the port back to blocking. */
825 serial_close(serial);
826 serial_open(serial, SERIAL_RDWR);
827
828 return SR_OK;
829}