]> sigrok.org Git - libsigrok.git/blob - src/hardware/cem-dt-885x/protocol.c
Backport recent changes from mainline.
[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                         sr_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                 sr_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                         sr_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_slist_free(meta.config);
350                         sr_config_free(src);
351                         devc->buf_len = 0;
352                 }
353         } else if (devc->state == ST_GET_LOG_RECORD_DATA) {
354                 sr_dbg("log data: 0x%.2x", c);
355                 if (c == RECORD_DBA || c == RECORD_DBC || c == RECORD_DATA || c == RECORD_END) {
356                         /* Work around off-by-one bug in device firmware. This
357                          * happens only on the last record, i.e. before RECORD_END */
358                         if (devc->buf_len & 1)
359                                 devc->buf_len--;
360                         /* Done with this set of samples */
361                         send_data(sdi, devc->buf, devc->buf_len / 2);
362                         devc->buf_len = 0;
363
364                         /* Process this meta marker in the right state. */
365                         devc->state = ST_GET_LOG_RECORD_META;
366                         process_byte(sdi, c, handle_packets);
367                 } else {
368                         devc->buf[devc->buf_len++] = c;
369                         if (devc->buf_len == SAMPLES_PER_PACKET * 2) {
370                                 send_data(sdi, devc->buf, devc->buf_len / 2);
371                                 devc->buf_len = 0;
372                         }
373                 }
374         }
375
376 }
377
378 SR_PRIV int cem_dt_885x_receive_data(int fd, int revents, void *cb_data)
379 {
380         const struct sr_dev_inst *sdi;
381         struct dev_context *devc;
382         struct sr_serial_dev_inst *serial;
383         unsigned char c, cmd;
384
385         (void)fd;
386
387         if (!(sdi = cb_data))
388                 return TRUE;
389
390         devc = sdi->priv;
391         serial = sdi->conn;
392         if (revents == G_IO_IN) {
393                 if (serial_read_nonblocking(serial, &c, 1) != 1)
394                         return TRUE;
395                 process_byte(sdi, c, TRUE);
396
397                 if (devc->enable_data_source_memory) {
398                         if (devc->state == ST_GET_LOG_HEADER) {
399                                 /* Memory transfer started. */
400                                 devc->enable_data_source_memory = FALSE;
401                         } else {
402                                 /* Tell device to start transferring from memory. */
403                                 cmd = CMD_TRANSFER_MEMORY;
404                                 serial_write_blocking(serial, &cmd, 1, 0);
405                         }
406                 }
407         }
408
409         return TRUE;
410 }
411
412 static int wait_for_token(const struct sr_dev_inst *sdi, int8_t *tokens, int timeout)
413 {
414         struct dev_context *devc;
415         struct sr_serial_dev_inst *serial;
416         gint64 start_time;
417         int i;
418         unsigned char c;
419
420         serial = sdi->conn;
421         devc = sdi->priv;
422         devc->state = ST_INIT;
423         start_time = g_get_monotonic_time() / 1000;
424         while (TRUE) {
425                 if (serial_read_nonblocking(serial, &c, 1) != 1)
426                         /* Device might have gone away. */
427                         return SR_ERR;
428                 process_byte(sdi, c, FALSE);
429                 if (devc->state != ST_INIT)
430                         /* Wait for a whole packet to get processed. */
431                         continue;
432                 for (i = 0; tokens[i] != -1; i++) {
433                         if (devc->token == tokens[i]) {
434                                 sr_spew("wait_for_token: got token 0x%.2x", devc->token);
435                                 return SR_OK;
436                         }
437                 }
438                 if (timeout && g_get_monotonic_time() / 1000 - start_time > timeout)
439                         return SR_ERR_TIMEOUT;
440         }
441
442         return SR_OK;
443 }
444
445 /* cmd is the command to send, tokens are the tokens that denote the state
446  * which the command affects. The first token is the desired state. */
447 static int cem_dt_885x_toggle(const struct sr_dev_inst *sdi, uint8_t cmd,
448                 int8_t *tokens, int timeout)
449 {
450         struct dev_context *devc;
451         struct sr_serial_dev_inst *serial;
452
453         serial = sdi->conn;
454         devc = sdi->priv;
455
456         /* The device doesn't respond to commands very well. The
457          * only thing to do is wait for the token that will confirm
458          * whether the command worked or not, and resend if needed. */
459         while (TRUE) {
460                 if (serial_write_blocking(serial, (const void *)&cmd, 1, 0) < 0)
461                         return SR_ERR;
462                 if (wait_for_token(sdi, tokens, timeout) == SR_ERR)
463                         return SR_ERR;
464                 if (devc->token == tokens[0])
465                         /* It worked. */
466                         break;
467         }
468
469         return SR_OK;
470 }
471
472 SR_PRIV gboolean cem_dt_885x_recording_get(const struct sr_dev_inst *sdi,
473                 int *state)
474 {
475         struct dev_context *devc;
476         int8_t tokens[5];
477
478         devc = sdi->priv;
479         if (devc->recording == -1) {
480                 /* Didn't pick up device state yet. */
481                 tokens[0] = TOKEN_RECORDING_ON;
482                 tokens[1] = TOKEN_RECORDING_OFF;
483                 tokens[2] = -1;
484                 if (wait_for_token(sdi, tokens, 510) != SR_OK)
485                         return SR_ERR;
486         }
487         *state = devc->token == TOKEN_RECORDING_ON;
488
489         return SR_OK;
490 }
491
492 SR_PRIV int cem_dt_885x_recording_set(const struct sr_dev_inst *sdi,
493                 gboolean state)
494 {
495         struct dev_context *devc;
496         int ret;
497         int8_t tokens[5];
498
499         devc = sdi->priv;
500
501         /* The toggle below needs the desired state in first position. */
502         if (state) {
503                 tokens[0] = TOKEN_RECORDING_ON;
504                 tokens[1] = TOKEN_RECORDING_OFF;
505         } else {
506                 tokens[0] = TOKEN_RECORDING_OFF;
507                 tokens[1] = TOKEN_RECORDING_ON;
508         }
509         tokens[2] = -1;
510
511         if (devc->recording == -1) {
512                 /* Didn't pick up device state yet. */
513                 if (wait_for_token(sdi, tokens, 0) != SR_OK)
514                         return SR_ERR;
515                 if (devc->token == tokens[0])
516                         /* Nothing to do. */
517                         return SR_OK;
518         } else if (devc->recording == state)
519                 /* Nothing to do. */
520                 return SR_OK;
521
522         /* Recording state notifications are sent at 2Hz, so allow just over
523          * that, 510ms, for the state to come in. */
524         ret = cem_dt_885x_toggle(sdi, CMD_TOGGLE_RECORDING, tokens, 510);
525
526         return ret;
527 }
528
529 SR_PRIV int cem_dt_885x_weight_freq_get(const struct sr_dev_inst *sdi)
530 {
531         struct dev_context *devc;
532         int cur_setting;
533         int8_t tokens[5];
534
535         devc = sdi->priv;
536
537         cur_setting = devc->cur_mqflags & (SR_MQFLAG_SPL_FREQ_WEIGHT_A | SR_MQFLAG_SPL_FREQ_WEIGHT_C);
538         if (cur_setting == 0) {
539                 /* Didn't pick up device state yet. */
540                 tokens[0] = TOKEN_WEIGHT_FREQ_A;
541                 tokens[1] = TOKEN_WEIGHT_FREQ_C;
542                 tokens[2] = -1;
543                 if (wait_for_token(sdi, tokens, 0) != SR_OK)
544                         return SR_ERR;
545                 if (devc->token == TOKEN_WEIGHT_FREQ_A)
546                         return SR_MQFLAG_SPL_FREQ_WEIGHT_A;
547                 else
548                         return SR_MQFLAG_SPL_FREQ_WEIGHT_C;
549         } else
550                 return cur_setting;
551 }
552
553 SR_PRIV int cem_dt_885x_weight_freq_set(const struct sr_dev_inst *sdi, int freqw)
554 {
555         struct dev_context *devc;
556         int cur_setting, ret;
557         int8_t tokens[5];
558
559         devc = sdi->priv;
560
561         cur_setting = devc->cur_mqflags & (SR_MQFLAG_SPL_FREQ_WEIGHT_A | SR_MQFLAG_SPL_FREQ_WEIGHT_C);
562         if (cur_setting == freqw)
563                 /* Already set to this frequency weighting. */
564                 return SR_OK;
565
566         /* The toggle below needs the desired state in first position. */
567         if (freqw == SR_MQFLAG_SPL_FREQ_WEIGHT_A) {
568                 tokens[0] = TOKEN_WEIGHT_FREQ_A;
569                 tokens[1] = TOKEN_WEIGHT_FREQ_C;
570         } else {
571                 tokens[0] = TOKEN_WEIGHT_FREQ_C;
572                 tokens[1] = TOKEN_WEIGHT_FREQ_A;
573         }
574         tokens[2] = -1;
575
576         if (cur_setting == 0) {
577                 /* Didn't pick up device state yet. */
578                 if (wait_for_token(sdi, tokens, 0) != SR_OK)
579                         return SR_ERR;
580                 if (devc->token == tokens[0])
581                         /* Nothing to do. */
582                         return SR_OK;
583         }
584
585         /* 10ms timeout seems to work best for this. */
586         ret = cem_dt_885x_toggle(sdi, CMD_TOGGLE_WEIGHT_FREQ, tokens, 10);
587
588         return ret;
589 }
590
591 SR_PRIV int cem_dt_885x_weight_time_get(const struct sr_dev_inst *sdi)
592 {
593         struct dev_context *devc;
594         int cur_setting;
595         int8_t tokens[5];
596
597         devc = sdi->priv;
598
599         cur_setting = devc->cur_mqflags & (SR_MQFLAG_SPL_TIME_WEIGHT_F | SR_MQFLAG_SPL_TIME_WEIGHT_S);
600         if (cur_setting == 0) {
601                 /* Didn't pick up device state yet. */
602                 tokens[0] = TOKEN_WEIGHT_TIME_FAST;
603                 tokens[1] = TOKEN_WEIGHT_TIME_SLOW;
604                 tokens[2] = -1;
605                 if (wait_for_token(sdi, tokens, 0) != SR_OK)
606                         return SR_ERR;
607                 if (devc->token == TOKEN_WEIGHT_TIME_FAST)
608                         return SR_MQFLAG_SPL_TIME_WEIGHT_F;
609                 else
610                         return SR_MQFLAG_SPL_TIME_WEIGHT_S;
611         } else
612                 return cur_setting;
613 }
614
615 SR_PRIV int cem_dt_885x_weight_time_set(const struct sr_dev_inst *sdi, int timew)
616 {
617         struct dev_context *devc;
618         int cur_setting, ret;
619         int8_t tokens[5];
620
621         devc = sdi->priv;
622
623         cur_setting = devc->cur_mqflags & (SR_MQFLAG_SPL_TIME_WEIGHT_F | SR_MQFLAG_SPL_TIME_WEIGHT_S);
624         if (cur_setting == timew)
625                 /* Already set to this time weighting. */
626                 return SR_OK;
627
628         /* The toggle below needs the desired state in first position. */
629         if (timew == SR_MQFLAG_SPL_TIME_WEIGHT_F) {
630                 tokens[0] = TOKEN_WEIGHT_TIME_FAST;
631                 tokens[1] = TOKEN_WEIGHT_TIME_SLOW;
632         } else {
633                 tokens[0] = TOKEN_WEIGHT_TIME_SLOW;
634                 tokens[1] = TOKEN_WEIGHT_TIME_FAST;
635         }
636         tokens[2] = -1;
637
638         if (cur_setting == 0) {
639                 /* Didn't pick up device state yet. */
640                 if (wait_for_token(sdi, tokens, 0) != SR_OK)
641                         return SR_ERR;
642                 if (devc->token == tokens[0])
643                         /* Nothing to do. */
644                         return SR_OK;
645         }
646
647         /* 51ms timeout seems to work best for this. */
648         ret = cem_dt_885x_toggle(sdi, CMD_TOGGLE_WEIGHT_TIME, tokens, 51);
649
650         return ret;
651 }
652
653 SR_PRIV int cem_dt_885x_holdmode_get(const struct sr_dev_inst *sdi,
654                 gboolean *holdmode)
655 {
656         struct dev_context *devc;
657         int8_t tokens[5];
658
659         devc = sdi->priv;
660
661         if (devc->cur_mqflags == 0) {
662                 tokens[0] = TOKEN_HOLD_MAX;
663                 tokens[1] = TOKEN_HOLD_MIN;
664                 tokens[2] = TOKEN_HOLD_NONE;
665                 tokens[3] = -1;
666                 if (wait_for_token(sdi, tokens, 0) != SR_OK)
667                         return SR_ERR;
668                 if (devc->token == TOKEN_HOLD_MAX)
669                         devc->cur_mqflags = SR_MQFLAG_MAX;
670                 else if (devc->token == TOKEN_HOLD_MIN)
671                         devc->cur_mqflags = SR_MQFLAG_MIN;
672         }
673         *holdmode = devc->cur_mqflags & (SR_MQFLAG_MAX | SR_MQFLAG_MIN);
674
675         return SR_OK;
676 }
677
678 SR_PRIV int cem_dt_885x_holdmode_set(const struct sr_dev_inst *sdi, int holdmode)
679 {
680         struct dev_context *devc;
681         int cur_setting, ret;
682         int8_t tokens[5];
683
684         devc = sdi->priv;
685
686         /* The toggle below needs the desired state in first position. */
687         if (holdmode == SR_MQFLAG_MAX) {
688                 tokens[0] = TOKEN_HOLD_MAX;
689                 tokens[1] = TOKEN_HOLD_MIN;
690                 tokens[2] = TOKEN_HOLD_NONE;
691         } else if (holdmode == SR_MQFLAG_MIN) {
692                 tokens[0] = TOKEN_HOLD_MIN;
693                 tokens[1] = TOKEN_HOLD_MAX;
694                 tokens[2] = TOKEN_HOLD_NONE;
695         } else {
696                 tokens[0] = TOKEN_HOLD_NONE;
697                 tokens[1] = TOKEN_HOLD_MAX;
698                 tokens[2] = TOKEN_HOLD_MIN;
699         }
700         tokens[3] = -1;
701
702         if (devc->cur_mqflags == 0) {
703                 /* Didn't pick up device state yet. */
704                 if (wait_for_token(sdi, tokens, 0) != SR_OK)
705                         return SR_ERR;
706                 if (devc->token == tokens[0])
707                         /* Nothing to do. */
708                         return SR_OK;
709         } else {
710                 cur_setting = devc->cur_mqflags & (SR_MQFLAG_MAX | SR_MQFLAG_MIN);
711                 if (cur_setting == holdmode)
712                         /* Already set correctly. */
713                         return SR_OK;
714         }
715
716         /* 51ms timeout seems to work best for this. */
717         ret = cem_dt_885x_toggle(sdi, CMD_TOGGLE_HOLD_MAX_MIN, tokens, 51);
718
719         return ret;
720 }
721
722 SR_PRIV int cem_dt_885x_meas_range_get(const struct sr_dev_inst *sdi,
723                 uint64_t *low, uint64_t *high)
724 {
725         struct dev_context *devc;
726         int8_t tokens[5];
727
728         devc = sdi->priv;
729         if (devc->cur_meas_range == 0) {
730                 tokens[0] = TOKEN_MEAS_RANGE_30_130;
731                 tokens[1] = TOKEN_MEAS_RANGE_30_80;
732                 tokens[2] = TOKEN_MEAS_RANGE_50_100;
733                 tokens[3] = TOKEN_MEAS_RANGE_80_130;
734                 tokens[4] = -1;
735                 if (wait_for_token(sdi, tokens, 0) != SR_OK)
736                         return SR_ERR;
737                 devc->cur_meas_range = devc->token;
738         }
739
740         switch (devc->cur_meas_range) {
741         case TOKEN_MEAS_RANGE_30_130:
742                 *low = 30;
743                 *high = 130;
744                 break;
745         case TOKEN_MEAS_RANGE_30_80:
746                 *low = 30;
747                 *high = 80;
748                 break;
749         case TOKEN_MEAS_RANGE_50_100:
750                 *low = 50;
751                 *high = 100;
752                 break;
753         case TOKEN_MEAS_RANGE_80_130:
754                 *low = 80;
755                 *high = 130;
756                 break;
757         default:
758                 return SR_ERR;
759         }
760
761         return SR_OK;
762 }
763
764 SR_PRIV int cem_dt_885x_meas_range_set(const struct sr_dev_inst *sdi,
765                 uint64_t low, uint64_t high)
766 {
767         struct dev_context *devc;
768         int ret;
769         int8_t token, tokens[6];
770
771         devc = sdi->priv;
772         if (low == 30 && high == 130)
773                 token = TOKEN_MEAS_RANGE_30_130;
774         else if (low == 30 && high == 80)
775                 token = TOKEN_MEAS_RANGE_30_80;
776         else if (low == 50 && high == 100)
777                 token = TOKEN_MEAS_RANGE_50_100;
778         else if (low == 80 && high == 130)
779                 token = TOKEN_MEAS_RANGE_80_130;
780         else
781                 return SR_ERR;
782
783         sr_dbg("want 0x%.2x", token);
784         /* The toggle below needs the desired state in first position. */
785         tokens[0] = token;
786         tokens[1] = TOKEN_MEAS_RANGE_30_130;
787         tokens[2] = TOKEN_MEAS_RANGE_30_80;
788         tokens[3] = TOKEN_MEAS_RANGE_50_100;
789         tokens[4] = TOKEN_MEAS_RANGE_80_130;
790         tokens[5] = -1;
791
792         if (devc->cur_meas_range == 0) {
793                 /* 110ms should be enough for two of these announcements */
794                 if (wait_for_token(sdi, tokens, 110) != SR_OK)
795                         return SR_ERR;
796                 devc->cur_meas_range = devc->token;
797         }
798
799         if (devc->cur_meas_range == token)
800                 /* Already set to this range. */
801                 return SR_OK;
802
803         /* For measurement range, it works best to ignore announcements of the
804          * current setting and keep resending the toggle quickly. */
805         tokens[1] = -1;
806         ret = cem_dt_885x_toggle(sdi, CMD_TOGGLE_MEAS_RANGE, tokens, 11);
807
808         return ret;
809 }
810
811 SR_PRIV int cem_dt_885x_power_off(const struct sr_dev_inst *sdi)
812 {
813         struct sr_serial_dev_inst *serial;
814         char c, cmd;
815
816         serial = sdi->conn;
817
818         cmd = CMD_TOGGLE_POWER_OFF;
819         while (TRUE) {
820                 serial_flush(serial);
821                 if (serial_write_blocking(serial, (const void *)&cmd, 1, 0) < 0)
822                         return SR_ERR;
823                 /* It never takes more than 23ms for the next token to arrive. */
824                 g_usleep(25 * 1000);
825                 if (serial_read_nonblocking(serial, &c, 1) != 1)
826                         /* Device is no longer responding. Good! */
827                         break;
828         }
829
830         /* In case the user manually turns on the device again, reset
831          * the port back to blocking. */
832         serial_close(serial);
833         serial_open(serial, SERIAL_RDWR);
834
835         return SR_OK;
836 }