dcttech-usbrelay: implement multiplexer driver for USB relay card
[libsigrok.git] / src / hardware / dcttech-usbrelay / protocol.c
index 111e4ef1206c2df87cc648b2279e4c19abbcd7e3..d1290f48c9a548996a834fa99421dd7d7cd27b5f 100644 (file)
  */
 
 #include <config.h>
+
+#include <string.h>
+
 #include "protocol.h"
 
-SR_PRIV int dcttech_usbrelay_receive_data(int fd, int revents, void *cb_data)
+SR_PRIV int dcttech_usbrelay_update_state(const struct sr_dev_inst *sdi)
 {
-       const struct sr_dev_inst *sdi;
        struct dev_context *devc;
+       uint8_t report[1 + REPORT_BYTECOUNT];
+       int ret;
+       GString *txt;
+
+       devc = sdi->priv;
+
+       /* Get another HID report. */
+       memset(report, 0, sizeof(report));
+       report[0] = REPORT_NUMBER;
+       ret = hid_get_feature_report(devc->hid_dev, report, sizeof(report));
+       if (ret != sizeof(report))
+               return SR_ERR_IO;
+       if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
+               txt = sr_hexdump_new(report, sizeof(report));
+               sr_spew("got report bytes: %s", txt->str);
+               sr_hexdump_free(txt);
+       }
 
-       (void)fd;
+       /* Update relay state cache from HID report content. */
+       devc->relay_state = report[1 + STATE_INDEX];
+       devc->relay_state &= devc->relay_mask;
+
+       return SR_OK;
+}
+
+SR_PRIV int dcttech_usbrelay_switch_cg(const struct sr_dev_inst *sdi,
+       const struct sr_channel_group *cg, gboolean on)
+{
+       struct dev_context *devc;
+       struct channel_group_context *cgc;
+       gboolean is_all;
+       size_t relay_idx;
+       uint8_t report[1 + REPORT_BYTECOUNT];
+       int ret;
+       GString *txt;
 
-       if (!(sdi = cb_data))
-               return TRUE;
+       devc = sdi->priv;
 
-       if (!(devc = sdi->priv))
-               return TRUE;
+       /* Determine if all or a single relay should be turned off or on. */
+       is_all = !cg ? TRUE : FALSE;
+       if (is_all) {
+               relay_idx = 0;
+       } else {
+               cgc = cg->priv;
+               relay_idx = cgc->number;
+       }
 
-       if (revents == G_IO_IN) {
-               /* TODO */
+       /*
+        * Construct and send the HID report. Notice the weird(?) bit
+        * pattern. Bit 1 is low when all relays are affected at once,
+        * and high to control an individual relay? Bit 0 communicates
+        * whether the relay(s) should be on or off? And all other bits
+        * are always set? It's assumed that the explicit assignment of
+        * full byte values simplifies future maintenance.
+        */
+       memset(report, 0, sizeof(report));
+       report[0] = REPORT_NUMBER;
+       if (is_all) {
+               if (on) {
+                       report[1] = 0xfe;
+               } else {
+                       report[1] = 0xfc;
+               }
+       } else {
+               if (on) {
+                       report[1] = 0xff;
+                       report[2] = relay_idx;
+               } else {
+                       report[1] = 0xfd;
+                       report[2] = relay_idx;
+               }
+       }
+       if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
+               txt = sr_hexdump_new(report, sizeof(report));
+               sr_spew("sending report bytes: %s", txt->str);
+               sr_hexdump_free(txt);
        }
+       ret = hid_send_feature_report(devc->hid_dev, report, sizeof(report));
+       if (ret != sizeof(report))
+               return SR_ERR_IO;
+
+       /* Update relay state cache (non-fatal). */
+       (void)dcttech_usbrelay_update_state(sdi);
+
+       return SR_OK;
+}
+
+/* Answers the query from cached relay state. Beware of 1-based indexing. */
+SR_PRIV int dcttech_usbrelay_query_cg(const struct sr_dev_inst *sdi,
+       const struct sr_channel_group *cg, gboolean *on)
+{
+       struct dev_context *devc;
+       struct channel_group_context *cgc;
+       size_t relay_idx;
+       uint32_t relay_mask;
+
+       devc = sdi->priv;
+       if (!cg)
+               return SR_ERR_ARG;
+       cgc = cg->priv;
+       relay_idx = cgc->number;
+       if (relay_idx < 1 || relay_idx > devc->relay_count)
+               return SR_ERR_ARG;
+       relay_mask = 1U << (relay_idx - 1);
+
+       *on = devc->relay_state & relay_mask;
 
-       return TRUE;
+       return SR_OK;
 }