+
+SR_PRIV int cem_dt_885x_holdmode_get(const struct sr_dev_inst *sdi,
+ gboolean *holdmode)
+{
+ struct dev_context *devc;
+ int8_t tokens[5];
+
+ devc = sdi->priv;
+
+ if (devc->cur_mqflags == 0) {
+ tokens[0] = TOKEN_HOLD_MAX;
+ tokens[1] = TOKEN_HOLD_MIN;
+ tokens[2] = TOKEN_HOLD_NONE;
+ tokens[3] = -1;
+ if (wait_for_token(sdi, tokens, 0) != SR_OK)
+ return SR_ERR;
+ if (devc->token == TOKEN_HOLD_MAX)
+ devc->cur_mqflags = SR_MQFLAG_MAX;
+ else if (devc->token == TOKEN_HOLD_MIN)
+ devc->cur_mqflags = SR_MQFLAG_MIN;
+ }
+ *holdmode = devc->cur_mqflags & (SR_MQFLAG_MAX | SR_MQFLAG_MIN);
+
+ return SR_OK;
+}
+
+SR_PRIV int cem_dt_885x_holdmode_set(const struct sr_dev_inst *sdi, int holdmode)
+{
+ struct dev_context *devc;
+ int cur_setting, ret;
+ int8_t tokens[5];
+
+ devc = sdi->priv;
+
+ /* The toggle below needs the desired state in first position. */
+ if (holdmode == SR_MQFLAG_MAX) {
+ tokens[0] = TOKEN_HOLD_MAX;
+ tokens[1] = TOKEN_HOLD_MIN;
+ tokens[2] = TOKEN_HOLD_NONE;
+ } else if (holdmode == SR_MQFLAG_MIN) {
+ tokens[0] = TOKEN_HOLD_MIN;
+ tokens[1] = TOKEN_HOLD_MAX;
+ tokens[2] = TOKEN_HOLD_NONE;
+ } else {
+ tokens[0] = TOKEN_HOLD_NONE;
+ tokens[1] = TOKEN_HOLD_MAX;
+ tokens[2] = TOKEN_HOLD_MIN;
+ }
+ tokens[3] = -1;
+
+ if (devc->cur_mqflags == 0) {
+ /* Didn't pick up device state yet. */
+ if (wait_for_token(sdi, tokens, 0) != SR_OK)
+ return SR_ERR;
+ if (devc->token == tokens[0])
+ /* Nothing to do. */
+ return SR_OK;
+ } else {
+ cur_setting = devc->cur_mqflags & (SR_MQFLAG_MAX | SR_MQFLAG_MIN);
+ if (cur_setting == holdmode)
+ /* Already set correctly. */
+ return SR_OK;
+ }
+
+ /* 51ms timeout seems to work best for this. */
+ ret = cem_dt_885x_toggle(sdi, CMD_TOGGLE_HOLD_MAX_MIN, tokens, 51);
+
+ return ret;
+}
+
+SR_PRIV int cem_dt_885x_meas_range_get(const struct sr_dev_inst *sdi,
+ uint64_t *low, uint64_t *high)
+{
+ struct dev_context *devc;
+ int8_t tokens[5];
+
+ devc = sdi->priv;
+ if (devc->cur_meas_range == 0) {
+ tokens[0] = TOKEN_MEAS_RANGE_30_130;
+ tokens[1] = TOKEN_MEAS_RANGE_30_80;
+ tokens[2] = TOKEN_MEAS_RANGE_50_100;
+ tokens[3] = TOKEN_MEAS_RANGE_80_130;
+ tokens[4] = -1;
+ if (wait_for_token(sdi, tokens, 0) != SR_OK)
+ return SR_ERR;
+ devc->cur_meas_range = devc->token;
+ }
+
+ switch (devc->cur_meas_range) {
+ case TOKEN_MEAS_RANGE_30_130:
+ *low = 30;
+ *high = 130;
+ break;
+ case TOKEN_MEAS_RANGE_30_80:
+ *low = 30;
+ *high = 80;
+ break;
+ case TOKEN_MEAS_RANGE_50_100:
+ *low = 50;
+ *high = 100;
+ break;
+ case TOKEN_MEAS_RANGE_80_130:
+ *low = 80;
+ *high = 130;
+ break;
+ default:
+ return SR_ERR;
+ }
+
+ return SR_OK;
+}
+
+SR_PRIV int cem_dt_885x_meas_range_set(const struct sr_dev_inst *sdi,
+ uint64_t low, uint64_t high)
+{
+ struct dev_context *devc;
+ int ret;
+ int8_t token, tokens[6];
+
+ devc = sdi->priv;
+ if (low == 30 && high == 130)
+ token = TOKEN_MEAS_RANGE_30_130;
+ else if (low == 30 && high == 80)
+ token = TOKEN_MEAS_RANGE_30_80;
+ else if (low == 50 && high == 100)
+ token = TOKEN_MEAS_RANGE_50_100;
+ else if (low == 80 && high == 130)
+ token = TOKEN_MEAS_RANGE_80_130;
+ else
+ return SR_ERR;
+
+ sr_dbg("want 0x%.2x", token);
+ /* The toggle below needs the desired state in first position. */
+ tokens[0] = token;
+ tokens[1] = TOKEN_MEAS_RANGE_30_130;
+ tokens[2] = TOKEN_MEAS_RANGE_30_80;
+ tokens[3] = TOKEN_MEAS_RANGE_50_100;
+ tokens[4] = TOKEN_MEAS_RANGE_80_130;
+ tokens[5] = -1;
+
+ if (devc->cur_meas_range == 0) {
+ /* 110ms should be enough for two of these announcements */
+ if (wait_for_token(sdi, tokens, 110) != SR_OK)
+ return SR_ERR;
+ devc->cur_meas_range = devc->token;
+ }
+
+ if (devc->cur_meas_range == token)
+ /* Already set to this range. */
+ return SR_OK;
+
+ /* For measurement range, it works best to ignore announcements of the
+ * current setting and keep resending the toggle quickly. */
+ tokens[1] = -1;
+ ret = cem_dt_885x_toggle(sdi, CMD_TOGGLE_MEAS_RANGE, tokens, 11);
+
+ return ret;
+}
+
+SR_PRIV int cem_dt_885x_power_off(const struct sr_dev_inst *sdi)
+{
+ struct sr_serial_dev_inst *serial;
+ char c, cmd;
+
+ serial = sdi->conn;
+
+ /* Reopen the port in non-blocking mode, so we can properly
+ * detect when the device stops communicating. */
+ serial_close(serial);
+ if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+ return SR_ERR;
+
+ cmd = CMD_TOGGLE_POWER_OFF;
+ while (TRUE) {
+ serial_flush(serial);
+ if (serial_write(serial, (const void *)&cmd, 1) != 1)
+ return SR_ERR;
+ /* It never takes more than 23ms for the next token to arrive. */
+ g_usleep(25 * 1000);
+ if (serial_read(serial, &c, 1) != 1)
+ /* Device is no longer responding. Good! */
+ break;
+ }
+
+ /* In case the user manually turns on the device again, reset
+ * the port back to blocking. */
+ serial_close(serial);
+ serial_open(serial, SERIAL_RDWR);
+
+ return SR_OK;
+}