]> sigrok.org Git - libsigrok.git/blobdiff - src/hardware/hp-3457a/api.c
hp-3457a: only probe when conn= was specified, to not break SCPI devices
[libsigrok.git] / src / hardware / hp-3457a / api.c
index 1b652bdd9a0deaf13462da67fd7352c023b4690b..4cfc2d758b54e79625609d408ef0f506d028a5b7 100644 (file)
@@ -31,25 +31,31 @@ static const uint32_t drvopts[] = {
 };
 
 static const uint32_t devopts[] = {
-       SR_CONF_CONTINUOUS | SR_CONF_SET,
+       SR_CONF_CONTINUOUS,
        SR_CONF_LIMIT_SAMPLES | SR_CONF_SET,
        SR_CONF_MEASURED_QUANTITY | SR_CONF_SET,
        SR_CONF_ADC_POWERLINE_CYCLES | SR_CONF_SET | SR_CONF_GET,
 };
 
-SR_PRIV struct sr_dev_driver hp_3457a_driver_info;
+static struct sr_dev_driver hp_3457a_driver_info;
 
 static int create_front_channel(struct sr_dev_inst *sdi, int chan_idx)
 {
        struct sr_channel *channel;
        struct sr_channel_group *front;
+       struct channel_context *chanc;
+
+       chanc = g_malloc(sizeof(*chanc));
+       chanc->location = CONN_FRONT;
 
        channel = sr_channel_new(sdi, chan_idx++, SR_CHANNEL_ANALOG,
                                 TRUE, "Front");
+       channel->priv = chanc;
 
        front = g_malloc0(sizeof(*front));
        front->name = g_strdup("Front");
        front->channels = g_slist_append(front->channels, channel);
+
        sdi->channel_groups = g_slist_append(sdi->channel_groups, front);
 
        return chan_idx;
@@ -58,13 +64,40 @@ static int create_front_channel(struct sr_dev_inst *sdi, int chan_idx)
 static int create_rear_channels(struct sr_dev_inst *sdi, int chan_idx,
                                 const struct rear_card_info *card)
 {
-       (void) sdi;
+       unsigned int i;
+       struct sr_channel *channel;
+       struct sr_channel_group *group;
+       struct channel_context *chanc;
+       char name[16];
 
        /* When card is NULL, we couldn't identify the type of card. */
        if (!card)
                return chan_idx;
 
-       /* TODO: Create channel descriptor for plug-in cards here. */
+       group = g_malloc0(sizeof(*group));
+       group->priv = NULL;
+       group->name = g_strdup(card->cg_name);
+       sdi->channel_groups = g_slist_append(sdi->channel_groups, group);
+
+       for (i = 0; i < card->num_channels; i++) {
+
+               chanc = g_malloc(sizeof(*chanc));
+               chanc->location = CONN_REAR;
+
+               if (card->type == REAR_TERMINALS) {
+                       chanc->index = -1;
+                       g_snprintf(name, sizeof(name), "%s", card->cg_name);
+               } else {
+                       chanc->index = i;
+                       g_snprintf(name, sizeof(name), "%s%u", card->cg_name, i);
+               }
+
+               channel = sr_channel_new(sdi, chan_idx++, SR_CHANNEL_ANALOG,
+                                       FALSE, name);
+               channel->priv = chanc;
+               group->channels = g_slist_append(group->channels, channel);
+       }
+
        return chan_idx;
 }
 
@@ -94,6 +127,14 @@ static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
        struct sr_dev_inst *sdi;
        struct dev_context *devc;
 
+       /*
+        * The device cannot get identified by means of SCPI queries.
+        * Neither shall non-SCPI requests get emitted before reliable
+        * identification of the device. Assume that we only get here
+        * when user specs led us to believe it's safe to communicate
+        * to the expected kind of device.
+        */
+
        /*
         * This command ensures we receive an EOI after every response, so that
         * we don't wait the entire timeout after the response is received.
@@ -105,13 +146,15 @@ static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
        if ((ret != SR_OK) || !response)
                return NULL;
 
-       if (strcmp(response, "HP3457A"))
+       if (strcmp(response, "HP3457A") != 0) {
+               g_free(response);
                return NULL;
+       }
 
        g_free(response);
 
-       devc = g_malloc0(sizeof(struct dev_context));
-       sdi = g_malloc0(sizeof(struct sr_dev_inst));
+       devc = g_malloc0(sizeof(*devc));
+       sdi = g_malloc0(sizeof(*sdi));
        sdi->vendor = g_strdup("Hewlett-Packard");
        sdi->model = g_strdup("3457A");
        sdi->version = get_revision(scpi);
@@ -133,24 +176,17 @@ static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
        return sdi;
 }
 
-static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
-{
-       return std_init(sr_ctx, di, LOG_PREFIX);
-}
-
 static GSList *scan(struct sr_dev_driver *di, GSList *options)
 {
-       return sr_scpi_scan(di->context, options, probe_device);
-}
+       const char *conn;
 
-static GSList *dev_list(const struct sr_dev_driver *di)
-{
-       return ((struct drv_context *)(di->context))->instances;
-}
+       /* Only scan for a device when conn= was specified. */
+       conn = NULL;
+       (void)sr_serial_extract_options(options, &conn, NULL);
+       if (!conn)
+               return NULL;
 
-static int dev_clear(const struct sr_dev_driver *di)
-{
-       return std_dev_clear(di, NULL);
+       return sr_scpi_scan(di->context, options, probe_device);
 }
 
 /*
@@ -180,15 +216,13 @@ static int dev_open(struct sr_dev_inst *sdi)
        if (sr_scpi_open(scpi) != SR_OK)
                return SR_ERR;
 
-       devc=sdi->priv;
+       devc = sdi->priv;
 
        sr_scpi_send(scpi, "PRESET");
        sr_scpi_send(scpi, "INBUF ON");
        sr_scpi_send(scpi, "TRIG HOLD");
        sr_scpi_get_float(scpi, "NPLC?", &devc->nplc);
 
-       sdi->status = SR_ST_ACTIVE;
-
        return SR_OK;
 }
 
@@ -196,35 +230,25 @@ static int dev_close(struct sr_dev_inst *sdi)
 {
        struct sr_scpi_dev_inst *scpi = sdi->conn;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
+       /* Disable scan-advance (preserve relay life). */
+       sr_scpi_send(scpi, "SADV HOLD");
        /* Switch back to auto-triggering. */
        sr_scpi_send(scpi, "TRIG AUTO");
 
        sr_scpi_close(scpi);
 
-       sdi->status = SR_ST_INACTIVE;
-
        return SR_OK;
 }
 
-static int cleanup(const struct sr_dev_driver *di)
-{
-       return dev_clear(di);
-}
-
 static int config_get(uint32_t key, GVariant **data,
        const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       int ret;
        struct dev_context *devc;
 
        (void)cg;
 
        devc = sdi->priv;
 
-       ret = SR_OK;
        switch (key) {
        case SR_CONF_ADC_POWERLINE_CYCLES:
                *data = g_variant_new_double(devc->nplc);
@@ -233,14 +257,12 @@ static int config_get(uint32_t key, GVariant **data,
                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)
+       const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
 {
-       int ret;
        enum sr_mq mq;
        enum sr_mqflag mq_flags;
        struct dev_context *devc;
@@ -248,12 +270,8 @@ static int config_set(uint32_t key, GVariant *data,
 
        (void)cg;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
-
        devc = sdi->priv;
 
-       ret = SR_OK;
        switch (key) {
        case SR_CONF_LIMIT_SAMPLES:
                devc->limit_samples = g_variant_get_uint64(data);
@@ -263,54 +281,41 @@ static int config_set(uint32_t key, GVariant *data,
                mq = g_variant_get_uint32(tuple_child);
                tuple_child = g_variant_get_child_value(data, 1);
                mq_flags = g_variant_get_uint64(tuple_child);
-               ret = hp_3457a_set_mq(sdi, mq, mq_flags);
                g_variant_unref(tuple_child);
-               break;
+               return hp_3457a_set_mq(sdi, mq, mq_flags);
        case SR_CONF_ADC_POWERLINE_CYCLES:
-               ret = hp_3457a_set_nplc(sdi, g_variant_get_double(data));
-               break;
+               return hp_3457a_set_nplc(sdi, g_variant_get_double(data));
        default:
-               ret = SR_ERR_NA;
+               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;
-
-       if (key == SR_CONF_SCAN_OPTIONS) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
-               return SR_OK;
-       } else if ((key == SR_CONF_DEVICE_OPTIONS) && !sdi) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       drvopts, ARRAY_SIZE(drvopts), sizeof(uint32_t));
-               return SR_OK;
-       } else if ((key == SR_CONF_DEVICE_OPTIONS) && !cg) {
-               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
-                       devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
-               return SR_OK;
-       }
-
-       /* From here on, we're only concerned with channel group config. */
-       if (!cg)
-               return SR_ERR_NA;
-
        /*
         * TODO: Implement channel group configuration when adding support for
         * plug-in cards.
         */
 
-       ret = SR_OK;
-       switch (key) {
-       default:
-               ret = SR_ERR_NA;
-       }
+       return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
+}
+
+static void create_channel_index_list(GSList *channels, GArray **arr)
+{
+       struct sr_channel *channel;
+       struct channel_context *chanc;
+       GSList *list_elem;
 
-       return ret;
+       *arr = g_array_new(FALSE, FALSE, sizeof(unsigned int));
+
+       for (list_elem = channels; list_elem; list_elem = list_elem->next) {
+               channel = list_elem->data;
+               chanc = channel->priv;
+               g_array_append_val(*arr, chanc->index);
+       }
 }
 
 /*
@@ -321,17 +326,21 @@ static int config_list(uint32_t key, GVariant **data,
  *   reading for sample N, but a new measurement is made and when we read the
  *   HIRES register, it contains data for sample N+1. This would produce
  *   wrong readings.
+ * SADV AUTO
+ *   Activate the scan-advance feature. This automatically connects the next
+ *   channel in the scan list to the A/D converter. This way, we do not need to
+ *   occupy the HP-IB bus to send channel select commands.
  */
-static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
+static int dev_acquisition_start(const struct sr_dev_inst *sdi)
 {
        int ret;
+       gboolean front_selected, rear_selected;
        struct sr_scpi_dev_inst *scpi;
+       struct sr_channel *channel;
        struct dev_context *devc;
-
-       (void)cb_data;
-
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
+       struct channel_context *chanc;
+       GArray *ch_list;
+       GSList *channels;
 
        scpi = sdi->conn;
        devc = sdi->priv;
@@ -341,7 +350,50 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
        if (ret != SR_OK)
                return ret;
 
-       std_session_send_df_header(sdi, LOG_PREFIX);
+       std_session_send_df_header(sdi);
+
+       front_selected = rear_selected = FALSE;
+       devc->active_channels = NULL;
+
+       for (channels = sdi->channels; channels; channels = channels->next) {
+               channel = channels->data;
+
+               if (!channel->enabled)
+                       continue;
+
+               chanc = channel->priv;
+
+               if (chanc->location == CONN_FRONT)
+                       front_selected = TRUE;
+               if (chanc->location == CONN_REAR)
+                       rear_selected = TRUE;
+
+               devc->active_channels = g_slist_append(devc->active_channels, channel);
+       }
+
+       if (front_selected && rear_selected) {
+               sr_err("Can not use front and rear channels at the same time!");
+               g_slist_free(devc->active_channels);
+               return SR_ERR_ARG;
+       }
+
+       devc->num_active_channels = g_slist_length(devc->active_channels);
+       if (!devc->num_active_channels) {
+               sr_err("Need at least one active channel!");
+               g_slist_free(devc->active_channels);
+               return SR_ERR_ARG;
+       }
+       devc->current_channel = devc->active_channels->data;
+
+       hp_3457a_select_input(sdi, front_selected ? CONN_FRONT : CONN_REAR);
+
+       /* For plug-in cards, use the scan-advance features to scan channels. */
+       if (rear_selected && (devc->rear_card->card_id != REAR_TERMINALS)) {
+               create_channel_index_list(devc->active_channels, &ch_list);
+               hp_3457a_send_scan_list(sdi, (void *)ch_list->data, ch_list->len);
+               sr_scpi_send(scpi, "SADV AUTO");
+               g_array_free(ch_list, TRUE);
+       }
 
        /* Start first measurement. */
        sr_scpi_send(scpi, "TRIG SGL");
@@ -351,25 +403,26 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
        return SR_OK;
 }
 
-static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+static int dev_acquisition_stop(struct sr_dev_inst *sdi)
 {
-       (void)cb_data;
+       struct dev_context *devc;
+
+       devc = sdi->priv;
 
-       if (sdi->status != SR_ST_ACTIVE)
-               return SR_ERR_DEV_CLOSED;
+       g_slist_free(devc->active_channels);
 
        return SR_OK;
 }
 
-SR_PRIV struct sr_dev_driver hp_3457a_driver_info = {
+static struct sr_dev_driver hp_3457a_driver_info = {
        .name = "hp-3457a",
        .longname = "HP 3457A",
        .api_version = 1,
-       .init = init,
-       .cleanup = cleanup,
+       .init = std_init,
+       .cleanup = std_cleanup,
        .scan = scan,
-       .dev_list = dev_list,
-       .dev_clear = dev_clear,
+       .dev_list = std_dev_list,
+       .dev_clear = std_dev_clear,
        .config_get = config_get,
        .config_set = config_set,
        .config_list = config_list,
@@ -379,3 +432,4 @@ SR_PRIV struct sr_dev_driver hp_3457a_driver_info = {
        .dev_acquisition_stop = dev_acquisition_stop,
        .context = NULL,
 };
+SR_REGISTER_DEV_DRIVER(hp_3457a_driver_info);