+/** Dump contents of 14-byte message.
+ * @param buf Pointer to array of 14 data bytes.
+ * @param[in] raw Write only data bytes, no interpretation.
+ */
+void dump_msg14(guchar* buf, gboolean raw)
+{
+ if (!buf)
+ return;
+
+ if (raw)
+ sr_spew("msg14: 0x %02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x",
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6],
+ buf[7], buf[8], buf[9], buf[10], buf[11], buf[12],
+ buf[13]);
+ else
+ sr_spew("msg14: 0x a=%d c1=%02x c2=%02x cmd=%02x dta=%02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x chs=%02x",
+ buf[1] == 0x2b?buf[0] >> 2:buf[0] % 0x0f, buf[1], buf[2], buf[3], buf[4], buf[5],
+ buf[6], buf[7], buf[8], buf[9], buf[10], buf[11],
+ buf[12], buf[13]);
+}
+
+/** Calc checksum for 14 byte message type.
+ *
+ * @param[in] dta Pointer to array of 13 data bytes.
+ * @return Checksum.
+ */
+static guchar calc_chksum_14(guchar* dta)
+{
+ guchar cnt, chs;
+
+ for (chs = 0, cnt = 0; cnt < 13; cnt++)
+ chs += dta[cnt];
+
+ return (64 - chs) & MASK_6BITS;
+}
+
+/** Check 14-byte message, Metrahit 2x. */
+static int chk_msg14(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ int retc;
+ gboolean isreq; /* Message is request to multimeter (otherwise response) */
+ uint8_t addr; /* Adaptor address */
+
+ retc = SR_OK;
+
+ /* Check parameters and message */
+ if (!sdi || !(devc = sdi->priv))
+ return SR_ERR_ARG;
+
+ if (devc->buflen != 14) {
+ sr_err("process_msg_14(): Msg len 14 expected!");
+ return SR_ERR_ARG;
+ }
+
+ isreq = devc->buf[1] == 0x2b;
+ if (isreq)
+ addr = devc->buf[0] >> 2;
+ else
+ addr = devc->buf[0] & 0x0f;
+
+ if ((devc->addr != addr) && !(isreq && (addr == 0))) {
+ sr_err("process_msg_14(): Address mismatch, msg for other device!");
+ retc = SR_ERR_ARG;
+ }
+
+ if (devc->buf[1] == 0) { /* Error msg from device! */
+ retc = SR_ERR_ARG;
+ switch (devc->buf[2]) {
+ case 1: /* Not used */
+ sr_err("Device: Illegal error code!");
+ break;
+ case 2: /* Incorrect check sum of received block */
+ sr_err("Device: Incorrect checksum in cmd!");
+ break;
+ case 3: /* Incorrect length of received block */
+ sr_err("Device: Incorrect block length in cmd!");
+ break;
+ case 4: /* Incorrect 2nd or 3rd byte */
+ sr_err("Device: Incorrect byte 2 or 3 in cmd!");
+ break;
+ case 5: /* Parameter out of range */
+ sr_err("Device: Parameter out of range!");
+ break;
+ default:
+ sr_err("Device: Unknown error code!");
+ }
+ retc = SR_ERR_ARG;
+ }
+ else if (!isreq && ((devc->buf[1] != 0x27) || (devc->buf[2] != 0x3f))) {
+ sr_err("process_msg_14(): byte 1/2 unexpected!");
+ retc = SR_ERR_ARG;
+ }
+
+ if (calc_chksum_14(devc->buf) != devc->buf[13]) {
+ sr_err("process_msg_14(): Invalid checksum!");
+ retc = SR_ERR_ARG;
+ }
+
+ if (retc != SR_OK)
+ dump_msg14(devc->buf, TRUE);
+
+ return retc;
+}
+
+/** Check 14-byte message, Metrahit 2x. */
+SR_PRIV int process_msg14(struct sr_dev_inst *sdi)
+{
+ struct dev_context *devc;
+ int retc;
+ uint8_t addr;
+ uint8_t cnt, dgt;
+
+ if ((retc = chk_msg14(sdi)) != SR_OK)
+ return retc;
+
+ devc = sdi->priv;
+
+ clean_ctmv_rs_v(devc);
+ addr = devc->buf[0] & MASK_6BITS;
+ if (addr != devc->addr)
+ sr_info("Device address mismatch %d/%d!", addr, devc->addr);
+
+ switch (devc->buf[3]) { /* That's the command this reply is for */
+ /* 0 cannot occur, the respective message is not a 14-byte message */
+ case 1: /* Read first free and occupied address */
+ sr_spew("Cmd %d unimplemented!", devc->buf[3]);
+ break;
+ case 2: /* Clear all RAM in multimeter */
+ sr_spew("Cmd %d unimplemented!", devc->buf[3]);
+ break;
+ case 3: /* Read firmware version and status */
+ sr_spew("Cmd 3, Read firmware and status", devc->buf[3]);
+ switch (devc->cmd_idx) {
+ case 0:
+ devc->fw_ver_maj = devc->buf[5];
+ devc->fw_ver_min = devc->buf[4];
+ sr_spew("Firmware version %d.%d", (int)devc->fw_ver_maj, (int)devc->fw_ver_min);
+ sr_spew("Rotary Switch Position (1..10): %d", (int)devc->buf[6]);
+ /** Docs say values 0..9, but that's not true */
+ sr_spew("Measurement Function: %d ", (int)devc->buf[7]);
+ decode_ctmv_2x(devc->buf[7], devc);
+ sr_spew("Range: 0x%x", devc->buf[8]);
+ decode_rs_2x_TR2(devc->buf[8] & 0x0f, devc); /* Docs wrong, uses conversion table TR_2! */
+ devc->autorng = (devc->buf[8] & 0x20) == 0;
+ // TODO 9, 10: 29S special functions
+ devc->ubatt = 0.1 * (float)devc->buf[11];
+ devc->model = gmc_decode_model_bd(devc->buf[12]);
+ sr_spew("Model=%s, battery voltage=%2.1f V", gmc_model_str(devc->model), (double)devc->ubatt);
+ break;
+ case 1:
+ sr_spew("Internal version %d.%d", (int)devc->buf[5], (int)devc->buf[4]);
+ sr_spew("Comm mode: 0x%x", (int)devc->buf[6]);
+ sr_spew("Block cnt%%64: %d", (int)devc->buf[7]);
+ sr_spew("drpCi: %d drpCh: %d", (int)devc->buf[8], (int)devc->buf[9]);
+ // Semantics undocumented. Possibly Metrahit 29S dropouts stuff?
+ break;
+ default:
+ sr_spew("Cmd 3: Unknown cmd_idx=%d", devc->cmd_idx);
+ break;
+ }
+ break;
+ case 4: /* Set real time, date, sample rate, trigger, ... */
+ sr_spew("Cmd %d unimplemented!", devc->buf[3]);
+ break;
+ case 5: /* Read real time, date, sample rate, trigger... */
+ sr_spew("Cmd %d unimplemented!", devc->buf[3]);
+ break;
+ case 6: /* Set modes or power off */
+ sr_spew("Cmd %d unimplemented!", devc->buf[3]);
+ break;
+ case 7: /* Set measurement function, range, autom/man. */
+ sr_spew("Cmd %d unimplemented!", devc->buf[3]);
+ break;
+ case 8: /* Get one measurement value */
+ sr_spew("Cmd 8, get one measurement value");
+ sr_spew("Measurement Function: %d ", (int)devc->buf[5]);
+ decode_ctmv_2x(devc->buf[5], devc);
+ if (!(devc->buf[6] & 0x10)) /* If bit4=0, old data. */
+ return SR_OK;
+
+ decode_rs_2x_TR2(devc->buf[6] & 0x0f, devc); // The docs say conversion table TR_3, but that does not work
+ setmqf(devc, SR_MQFLAG_AUTORANGE, devc->autorng);
+ /* 6 digits */
+ for (cnt = 0; cnt < 6; cnt++) {
+ dgt = bc(devc->buf[7 + cnt]);
+ if (dgt == 10) { /* Overload */
+ devc->value = NAN;
+ devc->scale = 1.0;
+ break;
+ }
+ else if (dgt == 13) { /* FUSE */
+ sr_err("FUSE!");
+ }
+ else if (dgt == 14) { /* Function recognition mode, OPEN */
+ sr_info("Function recognition mode, OPEN!");
+ devc->value = NAN;
+ devc->scale = 1.0;
+ break;
+ }
+ devc->value += pow(10.0, cnt) * dgt;
+ }
+ sr_spew("process_msg14() value=%f scale=%f scale1000=%d mq=%d "
+ "unit=%d mqflags=0x%02llx", devc->value, devc->scale,
+ devc->scale1000, devc->mq, devc->unit, devc->mqflags);
+ if (devc->value != NAN)
+ devc->value *= devc->scale * pow(1000.0, devc->scale1000);
+
+ send_value(sdi);
+
+ break;
+ default:
+ sr_spew("Unknown cmd %d!", devc->buf[3]);
+ break;
+ }
+
+ return SR_OK;
+}
+
+/** Data reception callback function. */