]> sigrok.org Git - libsigrok.git/blobdiff - src/hardware/icstation-usbrelay/protocol.c
icstation-usbrelay: Initial ICStation USBRelay driver.
[libsigrok.git] / src / hardware / icstation-usbrelay / protocol.c
index fa32e70064e5b0af927d3bf721c9f3972ef80cdc..78a999655cec0dd59f82422e84d6457e7fd43b82 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the libsigrok project.
  *
- * Copyright (C) 2021 Frank Stettner <frank-stettner@gmx.net>
+ * Copyright (C) 2021-2023 Frank Stettner <frank-stettner@gmx.net>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  */
 
 #include <config.h>
+
 #include "protocol.h"
 
-SR_PRIV int icstation_usbrelay_receive_data(int fd, int revents, void *cb_data)
+#define SERIAL_TIMEOUT_MS              1000
+
+#define ICSTATION_USBRELAY_CMD_ID      0x50
+#define ICSTATION_USBRELAY_CMD_START   0x51
+
+static int icstation_usbrelay_send_byte(struct sr_serial_dev_inst *serial,
+       uint8_t b)
+{
+       int ret;
+
+       ret = serial_write_blocking(serial, &b, sizeof(b), SERIAL_TIMEOUT_MS);
+       if (ret < SR_OK)
+               return SR_ERR_IO;
+       if ((size_t)ret != sizeof(b))
+               return SR_ERR_IO;
+
+       return SR_OK;
+}
+
+static int icstation_usbrelay_recv_byte(struct sr_serial_dev_inst *serial,
+       uint8_t *b)
+{
+       int ret;
+
+       ret = serial_read_blocking(serial, b, sizeof(*b), SERIAL_TIMEOUT_MS);
+       if (ret < SR_OK)
+               return SR_ERR_IO;
+       if ((size_t)ret != sizeof(*b))
+               return SR_ERR_IO;
+
+       return SR_OK;
+}
+
+SR_PRIV int icstation_usbrelay_identify(struct sr_serial_dev_inst *serial,
+       uint8_t *id)
+{
+       int ret;
+
+       if (!id)
+               return SR_ERR_ARG;
+
+       /*
+        * Send the identification request. Receive the device firmware's
+        * identification response.
+        *
+        * BEWARE!
+        * A vendor firmware implementation detail prevents the host from
+        * identifying the device again once command mode was entered.
+        * The UART protocol provides no means to leave command mode.
+        * The subsequent identification request is mistaken instead as
+        * another relay control request! Identifying the device will fail.
+        * The device must be power cycled before it identifies again.
+        */
+       ret = icstation_usbrelay_send_byte(serial, ICSTATION_USBRELAY_CMD_ID);
+       if (ret != SR_OK) {
+               sr_dbg("Could not send identification request.");
+               return SR_ERR_IO;
+       }
+       ret = icstation_usbrelay_recv_byte(serial, id);
+       if (ret != SR_OK) {
+               sr_dbg("Could not receive identification response.");
+               return SR_ERR_IO;
+       }
+       sr_dbg("Identification response 0x%02hhx.", *id);
+
+       return SR_OK;
+}
+
+SR_PRIV int icstation_usbrelay_start(const struct sr_dev_inst *sdi)
+{
+       struct sr_serial_dev_inst *serial;
+
+       if (!sdi)
+               return SR_ERR_ARG;
+       serial = sdi->conn;
+       if (!serial)
+               return SR_ERR_ARG;
+
+       return icstation_usbrelay_send_byte(serial,
+               ICSTATION_USBRELAY_CMD_START);
+}
+
+SR_PRIV int icstation_usbrelay_switch_cg(const struct sr_dev_inst *sdi,
+       const struct sr_channel_group *cg, gboolean on)
 {
-       const struct sr_dev_inst *sdi;
        struct dev_context *devc;
+       struct channel_group_context *cgc;
+       uint8_t state, mask;
+       uint8_t tx_state;
 
-       (void)fd;
+       devc = sdi->priv;
 
-       if (!(sdi = cb_data))
-               return TRUE;
+       /*
+        * The device requires the communication of all relay states
+        * at the same time. Calling applications control individual
+        * relays. The device wants active-low state in the physical
+        * transport. Application uses positive logic (active-high).
+        *
+        * Update the locally cached state from the most recent request.
+        * Invert the result and send it to the device. Only update
+        * the internal cache after successful transmission.
+        */
 
-       if (!(devc = sdi->priv))
-               return TRUE;
+       state = devc->relay_state;
+       if (!cg) {
+               /* Set all relays. */
+               if (on)
+                       state |= devc->relay_mask;
+               else
+                       state &= ~devc->relay_mask;
+       } else {
+               cgc = cg->priv;
+               mask = 1UL << cgc->index;
+               if (on)
+                       state |= mask;
+               else
+                       state &= ~mask;
+       }
 
-       if (revents == G_IO_IN) {
-               /* TODO */
+       tx_state = ~state & devc->relay_mask;
+       sr_spew("Sending status byte: %x", tx_state);
+       if (icstation_usbrelay_send_byte(sdi->conn, tx_state) != SR_OK) {
+               sr_err("Unable to send status byte.");
+               return SR_ERR_IO;
        }
 
-       return TRUE;
+       devc->relay_state = state;
+
+       return SR_OK;
 }