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