--- src/sigrok.c | 325 +++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 292 insertions(+), 33 deletions(-)
diff --git a/src/sigrok.c b/src/sigrok.c index 7189366..ea1e95e 100644 --- a/src/sigrok.c +++ b/src/sigrok.c @@ -22,72 +22,336 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <time.h> +#include <glib.h> #include <libsigrok/libsigrok.h> -static char *conn = NULL; -static char *serialcomm = NULL; +#define DEFAULT_MIN_WRITE_INTERVAL 1 + +GSList *config_devices; +static struct sr_session *session = NULL; +static int num_devices; static int loglevel = SR_LOG_WARN; static struct sr_context *sr_ctx; -static const char *config_keys[] = { - "loglevel", - "conn", - "serialcomm", +struct config_device { + char *name; + char *driver; + char *conn; + char *serialcomm; + struct sr_dev_inst *sdi; + int min_write_interval; + struct timespec last_write; }; -static int cd_logger(void *cb_data, int loglevel, const char *format, va_list args) +static int cd_logger(void *cb_data, int msg_loglevel, const char *format, + va_list args) { char s[512]; - vsnprintf(s, 512, format, args); - plugin_log(LOG_INFO, "sigrok: %s", s); + if (msg_loglevel <= loglevel) { + vsnprintf(s, 512, format, args); + plugin_log(LOG_INFO, "sigrok: %s", s); + } return 0; } -static int plugin_config(const char *key, const char *value) +static int plugin_config_device(oconfig_item_t *ci) { + oconfig_item_t *item; + struct config_device *cfdev; + int i; + + if (ci->values_num != 1 || ci->values[0].type != OCONFIG_TYPE_STRING) { + ERROR("Invalid device name."); + return 1; + } - if (!strcmp(key, "loglevel")) - loglevel = atoi(value); - else if (!strcmp(key, "conn")) - conn = strdup(value); - else if (!strcmp(key, "serialcomm")) - serialcomm = strdup(value); - else + if (!(cfdev = malloc(sizeof(struct config_device)))) { + ERROR("malloc() failed."); return 1; + } + memset(cfdev, 0, sizeof(struct config_device)); + cfdev->name = strdup(ci->values[0].value.string); + cfdev->min_write_interval = DEFAULT_MIN_WRITE_INTERVAL; + + for (i = 0; i < ci->children_num; i++) { + item = ci->children + i; + if (item->values_num != 1) { + ERROR("Missing value for '%s'.", item->key); + return 1; + } + if (!strcasecmp(item->key, "driver")) { + cfdev->driver = strdup(item->values[0].value.string); + } else if (!strcasecmp(item->key, "conn")) { + cfdev->conn = strdup(item->values[0].value.string); + } else if (!strcasecmp(item->key, "serialcomm")) { + cfdev->serialcomm = strdup(item->values[0].value.string); + } else if (!strcasecmp(item->key, "interval")) { + cfdev->min_write_interval = item->values[0].value.number; + } else { + ERROR("Invalid keyword '%s'.", item->key); + return 1; + } + } + + config_devices = g_slist_append(config_devices, cfdev); + + return 0; +} + +static int plugin_config(oconfig_item_t *ci) +{ + oconfig_item_t *item; + int i; + + for (i = 0; i < ci->children_num; i++) { + item = ci->children + i; + if (!strcasecmp(item->key, "loglevel")) { + if (item->values_num != 1 + || item->values[0].type != OCONFIG_TYPE_NUMBER + || item->values[0].value.number < 0 + || item->values[0].value.number > 5) { + ERROR("Invalid loglevel"); + return 1; + } + loglevel = item->values[0].value.number; + } else if (!strcasecmp(item->key, "Device")) { + if (plugin_config_device(item) != 0) + return 1; + } else { + ERROR("Invalid keyword '%s'.", item->key); + return 1; + } + } + + return 0; +} + +static void free_drvopts(struct sr_config *src) +{ + g_variant_unref(src->data); + g_free(src); +} + +static int elapsed_time(struct timespec *last_write, struct timespec *elapsed) +{ + struct timespec cur_time; + + if (clock_gettime(CLOCK_REALTIME, &cur_time) != 0) { + ERROR("clock_gettime() failed."); + return -1; + } + + if (cur_time.tv_nsec >= last_write->tv_nsec) { + elapsed->tv_nsec = cur_time.tv_nsec - last_write->tv_nsec; + elapsed->tv_sec = cur_time.tv_sec - last_write->tv_sec; + } else { + elapsed->tv_nsec = 1000000000L + cur_time.tv_nsec - last_write->tv_nsec; + elapsed->tv_sec = cur_time.tv_sec - last_write->tv_sec - 1; + } + + return 0; +} + +static void plugin_feed_callback(const struct sr_dev_inst *sdi, + const struct sr_datafeed_packet *packet, void *cb_data) +{ + const struct sr_datafeed_analog *analog; + struct config_device *cfdev; + GSList *l; + struct timespec elapsed; + value_t *values; + value_list_t vl = VALUE_LIST_INIT; + int num_probes, s, p; + + if (packet->type == SR_DF_END) { + /* TODO: try to restart acquisition after a delay? */ + INFO("oops! ended"); + return; + } + + /* Find this device's configuration. */ + cfdev = NULL; + for (l = config_devices; l; l = l->next) { + cfdev = l->data; + if (cfdev->sdi == sdi) { + /* Found it. */ + break; + } + cfdev = NULL; + } + if (!cfdev) { + ERROR("Unknown device instance in sigrok driver %s.", sdi->driver->name); + return; + } + + if (packet->type == SR_DF_ANALOG) { + if (elapsed_time(&cfdev->last_write, &elapsed) != 0) + return; + if (elapsed.tv_sec < cfdev->min_write_interval) + return; + + analog = packet->payload; + num_probes = g_slist_length(analog->probes); + if (!(values = malloc(sizeof(value_t) * num_probes))) { + ERROR("malloc() failed."); + return; + } + for (s = 0; s < analog->num_samples; s++) { + for (p = 0; p < num_probes; p++) { + values[s + p].gauge = analog->data[s + p]; + } + } + vl.values = values; + vl.values_len = num_probes; + sstrncpy(vl.host, hostname_g, sizeof(vl.host)); + sstrncpy(vl.plugin, "sigrok", sizeof(vl.plugin)); + ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), + "%s", cfdev->name); + sstrncpy(vl.type, "gauge", sizeof(vl.type)); + plugin_dispatch_values(&vl); + + if (clock_gettime(CLOCK_REALTIME, &cfdev->last_write) != 0) { + ERROR("clock_gettime() failed."); + return; + } + } + +} + +static int plugin_read(user_data_t *ud) +{ + + sr_session_iteration(FALSE); return 0; } static int plugin_init(void) { - int ret; + struct sr_dev_driver *drv, **drvlist; + struct sr_config *src; + GSList *devlist, *drvopts, *l; + struct config_device *cfdev; + struct timespec ts; + int ret, i; + char hwident[512]; sr_log_callback_set(cd_logger, NULL); sr_log_loglevel_set(loglevel); if ((ret = sr_init(&sr_ctx)) != SR_OK) { - INFO("Failed to initialize libsigrok: %s.", sr_strerror(ret)); + ERROR("Failed to initialize libsigrok: %s.", sr_strerror(ret)); return 1; } - return 0; -} + if (!(session = sr_session_new())) + return 1; -static int plugin_read(user_data_t *ud) -{ + num_devices = 0; + drvlist = sr_driver_list(); + for (l = config_devices; l; l = l->next) { + cfdev = l->data; + drv = NULL; + for (i = 0; drvlist[i]; i++) { + if (!strcmp(drvlist[i]->name, cfdev->driver)) { + drv = drvlist[i]; + break; + } + } + if (!drv) { + ERROR("sigrok: Unknown driver '%s'.", cfdev->driver); + return 1; + } + + if (sr_driver_init(sr_ctx, drv) != SR_OK) + return 1; + + drvopts = NULL; + if (cfdev->conn) { + if (!(src = malloc(sizeof(struct sr_config)))) + return 1; + src->key = SR_CONF_CONN; + src->data = g_variant_new_string(cfdev->conn); + drvopts = g_slist_append(drvopts, src); + } + if (cfdev->serialcomm) { + if (!(src = malloc(sizeof(struct sr_config)))) + return 1; + src->key = SR_CONF_SERIALCOMM; + src->data = g_variant_new_string(cfdev->serialcomm); + drvopts = g_slist_append(drvopts, src); + } + devlist = sr_driver_scan(drv, drvopts); + g_slist_free_full(drvopts, (GDestroyNotify)free_drvopts); + if (!devlist) { + INFO("sigrok: No devices found."); + return 0; + } + if (g_slist_length(devlist) > 1) { + INFO("sigrok: %d sigrok devices for device entry '%s': must be 1.", + g_slist_length(devlist), cfdev->name); + return 1; + } + cfdev->sdi = devlist->data; + g_slist_free(devlist); + ssnprintf(hwident, sizeof(hwident), "%s %s %s", + cfdev->sdi->vendor ? cfdev->sdi->vendor : "", + cfdev->sdi->model ? cfdev->sdi->model : "", + cfdev->sdi->version ? cfdev->sdi->version : ""); + INFO("sigrok: Device '%s' is a %s.", cfdev->name, hwident); + + if (sr_dev_open(cfdev->sdi) != SR_OK) + return 1; + + if (sr_session_dev_add(cfdev->sdi) != SR_OK) + return 1; + + num_devices++; + } + + if (num_devices > 0) { + /* Do this only when we're sure there's hardware to talk to. */ + ts.tv_sec = 0; + ts.tv_nsec = 1000000L; + plugin_register_complex_read("sigrok", "sigrok", plugin_read, + &ts, NULL); + + if (sr_session_datafeed_callback_add(plugin_feed_callback, NULL) != SR_OK) + return 1; + + if (sr_session_start() != SR_OK) + return 1; + } else + sr_session_destroy(); return 0; } static int plugin_shutdown(void) { - if (conn) - free(conn); - if (serialcomm) - free(serialcomm); + struct config_device *cfdev; + GSList *l; + + if (num_devices > 0) { + sr_session_stop(); + sr_session_dev_remove_all(); + sr_session_destroy(); + } + + for (l = config_devices; l; l = l->next) { + cfdev = l->data; + free(cfdev->name); + if (cfdev->driver) + free(cfdev->driver); + if (cfdev->conn) + free(cfdev->conn); + if (cfdev->serialcomm) + free(cfdev->serialcomm); + } sr_exit(sr_ctx); @@ -96,14 +360,9 @@ static int plugin_shutdown(void) void module_register(void) { - struct timespec ts; - plugin_register_config("sigrok", plugin_config, config_keys, - STATIC_ARRAY_SIZE(config_keys)); + plugin_register_complex_config("sigrok", plugin_config); plugin_register_init("sigrok", plugin_init); plugin_register_shutdown("sigrok", plugin_shutdown); - ts.tv_sec = 0; - ts.tv_nsec = 1000000000L; - plugin_register_complex_read("sigrok", "sigrok", plugin_read, &ts, NULL); } -- 1.8.1.2 _______________________________________________ collectd mailing list collectd@verplant.org http://mailman.verplant.org/listinfo/collectd