]> sigrok.org Git - libsigrok.git/blobdiff - src/hardware/icstation-usbrelay/api.c
icstation-usbrelay: Initial ICStation USBRelay driver.
[libsigrok.git] / src / hardware / icstation-usbrelay / api.c
index 0e329beb2ea5eb6e132b1b047d4f56aa332b6b2b..db576737d8951e6d8f8b3889d363e4666c802ffa 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"
 
-static struct sr_dev_driver icstation_usbrelay_driver_info;
+#define SERIALCOMM "9600/8n1"
 
-static GSList *scan(struct sr_dev_driver *di, GSList *options)
-{
-       struct drv_context *drvc;
-       GSList *devices;
+static const uint32_t scanopts[] = {
+       SR_CONF_CONN,
+       SR_CONF_SERIALCOMM,
+};
 
-       (void)options;
+static const uint32_t drvopts[] = {
+       SR_CONF_MULTIPLEXER,
+};
 
-       devices = NULL;
-       drvc = di->context;
-       drvc->instances = NULL;
+static const uint32_t devopts[] = {
+       SR_CONF_CONN | SR_CONF_GET,
+       SR_CONF_ENABLED | SR_CONF_SET, /* Enable/disable all relays at once. */
+};
 
-       /* TODO: scan for devices, either based on a SR_CONF_CONN option
-        * or on a USB scan. */
+static const uint32_t devopts_cg[] = {
+       SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET,
+};
 
-       return devices;
-}
+static const struct ics_usbrelay_profile supported_ics_usbrelay[] = {
+       { ICSE012A, 0xAB, "ICSE012A", 4 },
+       { ICSE013A, 0xAD, "ICSE013A", 2 },
+       { ICSE014A, 0xAC, "ICSE014A", 8 },
+};
 
-static int dev_open(struct sr_dev_inst *sdi)
+static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
-       (void)sdi;
+       struct sr_dev_inst *sdi;
+       struct dev_context *devc;
+       struct sr_serial_dev_inst *serial;
+       GSList *devices;
+       size_t i, ch_idx;
+       const char *conn, *serialcomm;
+       int ret;
+       uint8_t device_id;
+       const struct ics_usbrelay_profile *profile;
+       struct sr_channel_group *cg;
+       struct channel_group_context *cgc;
 
-       /* TODO: get handle from sdi->conn and open it. */
+       devices = NULL;
 
-       return SR_OK;
-}
+       /* Only scan for a device when conn= was specified. */
+       conn = NULL;
+       serialcomm = SERIALCOMM;
+       if (sr_serial_extract_options(options, &conn, &serialcomm) != SR_OK)
+               return NULL;
+
+       serial = sr_serial_dev_inst_new(conn, serialcomm);
+       if (serial_open(serial, SERIAL_RDWR) != SR_OK)
+               return NULL;
+
+       /* Get device model. */
+       ret = icstation_usbrelay_identify(serial, &device_id);
+       if (ret != SR_OK) {
+               sr_err("Cannot retrieve identification details.");
+               serial_close(serial);
+               return NULL;
+       }
 
-static int dev_close(struct sr_dev_inst *sdi)
-{
-       (void)sdi;
+       for (i = 0; i < ARRAY_SIZE(supported_ics_usbrelay); i++) {
+               profile = &supported_ics_usbrelay[i];
+               if (device_id != profile->id)
+                       continue;
+               sdi = g_malloc0(sizeof(*sdi));
+               sdi->status = SR_ST_INACTIVE;
+               sdi->vendor = g_strdup("ICStation");
+               sdi->model = g_strdup(profile->modelname);
+               sdi->inst_type = SR_INST_SERIAL;
+               sdi->conn = serial;
+               sdi->connection_id = g_strdup(conn);
+
+               devc = g_malloc0(sizeof(*devc));
+               sdi->priv = devc;
+               devc->relay_count = profile->nb_channels;
+               devc->relay_mask = (1U << devc->relay_count) - 1;
+               /* Assume that all relays are off at the start. */
+               devc->relay_state = 0;
+               for (ch_idx = 0; ch_idx < devc->relay_count; ch_idx++) {
+                       cg = g_malloc0(sizeof(*cg));
+                       cg->name = g_strdup_printf("R%zu", ch_idx + 1);
+                       cgc = g_malloc0(sizeof(*cgc));
+                       cg->priv = cgc;
+                       cgc->index = ch_idx;
+                       sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
+               }
+
+               devices = g_slist_append(devices, sdi);
+               break;
+       }
 
-       /* TODO: get handle from sdi->conn and close it. */
+       serial_close(serial);
+       if (!devices) {
+               sr_serial_dev_inst_free(serial);
+               sr_warn("Unknown device identification 0x%02hhx.", device_id);
+       }
 
-       return SR_OK;
+       return std_scan_complete(di, devices);
 }
 
 static int config_get(uint32_t key, GVariant **data,
        const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       int ret;
-
-       (void)sdi;
-       (void)data;
-       (void)cg;
+       struct dev_context *devc;
+       struct channel_group_context *cgc;
+       uint8_t mask;
+       gboolean on;
+
+       if (!sdi || !data)
+               return SR_ERR_ARG;
+       devc = sdi->priv;
+
+       if (!cg) {
+               switch (key) {
+               case SR_CONF_CONN:
+                       *data = g_variant_new_string(sdi->connection_id);
+                       break;
+               default:
+                       return SR_ERR_NA;
+               }
+       }
 
-       ret = SR_OK;
        switch (key) {
-       /* TODO */
+       case SR_CONF_ENABLED:
+               cgc = cg->priv;
+               mask = 1U << cgc->index;
+               on = devc->relay_state & mask;
+               *data = g_variant_new_boolean(on);
+               return SR_OK;
        default:
                return SR_ERR_NA;
        }
 
-       return ret;
+       return SR_OK;
 }
 
 static int config_set(uint32_t key, GVariant *data,
        const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       int ret;
-
-       (void)sdi;
-       (void)data;
-       (void)cg;
-
-       ret = SR_OK;
-       switch (key) {
-       /* TODO */
-       default:
-               ret = SR_ERR_NA;
+       gboolean on;
+
+       if (!cg) {
+               switch (key) {
+               case SR_CONF_ENABLED:
+                       /* Enable/disable all channels at the same time. */
+                       on = g_variant_get_boolean(data);
+                       return icstation_usbrelay_switch_cg(sdi, cg, on);
+               default:
+                       return SR_ERR_NA;
+               }
+       } else {
+               switch (key) {
+               case SR_CONF_ENABLED:
+                       on = g_variant_get_boolean(data);
+                       return icstation_usbrelay_switch_cg(sdi, cg, on);
+               default:
+                       return SR_ERR_NA;
+               }
        }
 
-       return ret;
+       return SR_OK;
 }
 
 static int config_list(uint32_t key, GVariant **data,
        const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       int ret;
-
-       (void)sdi;
-       (void)data;
-       (void)cg;
+       if (!cg) {
+               switch (key) {
+               case SR_CONF_SCAN_OPTIONS:
+               case SR_CONF_DEVICE_OPTIONS:
+                       return STD_CONFIG_LIST(key, data, sdi, cg,
+                               scanopts, drvopts, devopts);
+               default:
+                       return SR_ERR_NA;
+               }
+       }
 
-       ret = SR_OK;
        switch (key) {
-       /* TODO */
+       case SR_CONF_DEVICE_OPTIONS:
+               *data = std_gvar_array_u32(ARRAY_AND_SIZE(devopts_cg));
+               return SR_OK;
        default:
                return SR_ERR_NA;
        }
-
-       return ret;
 }
 
-static int dev_acquisition_start(const struct sr_dev_inst *sdi)
-{
-       /* TODO: configure hardware, reset acquisition state, set up
-        * callbacks and send header packet. */
-
-       (void)sdi;
-
-       return SR_OK;
-}
-
-static int dev_acquisition_stop(struct sr_dev_inst *sdi)
+static int dev_open(struct sr_dev_inst *sdi)
 {
-       /* TODO: stop acquisition. */
+       struct sr_serial_dev_inst *serial;
+       int ret;
 
-       (void)sdi;
+       if (!sdi)
+               return SR_ERR_ARG;
+       serial = sdi->conn;
+       if (!serial)
+               return SR_ERR_ARG;
+
+       ret = std_serial_dev_open(sdi);
+       if (ret != SR_OK)
+               return ret;
+
+       /* Start command mode. */
+       ret = icstation_usbrelay_start(sdi);
+       if (ret != SR_OK) {
+               sr_err("Cannot initiate command mode.");
+               serial_close(serial);
+               return SR_ERR_IO;
+       }
 
        return SR_OK;
 }
 
 static struct sr_dev_driver icstation_usbrelay_driver_info = {
        .name = "icstation-usbrelay",
-       .longname = "icstation-usbrelay",
+       .longname = "ICStation USBRelay",
        .api_version = 1,
        .init = std_init,
        .cleanup = std_cleanup,
@@ -146,9 +246,9 @@ static struct sr_dev_driver icstation_usbrelay_driver_info = {
        .config_set = config_set,
        .config_list = config_list,
        .dev_open = dev_open,
-       .dev_close = dev_close,
-       .dev_acquisition_start = dev_acquisition_start,
-       .dev_acquisition_stop = dev_acquisition_stop,
+       .dev_close = std_serial_dev_close,
+       .dev_acquisition_start = std_dummy_dev_acquisition_start,
+       .dev_acquisition_stop = std_dummy_dev_acquisition_stop,
        .context = NULL,
 };
 SR_REGISTER_DEV_DRIVER(icstation_usbrelay_driver_info);