arachnid-labs-re-load-pro: Add initial driver.

This commit is contained in:
Uwe Hermann 2015-12-30 18:44:40 +01:00
parent 6e8d31d468
commit 803db07a1a
3 changed files with 506 additions and 61 deletions

View File

@ -1,7 +1,7 @@
/*
* This file is part of the libsigrok project.
*
* Copyright (C) 2015 Uwe Hermann <uwe@hermann-uwe.de>
* Copyright (C) 2015-2016 Uwe Hermann <uwe@hermann-uwe.de>
*
* 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
@ -19,8 +19,41 @@
*/
#include <config.h>
#include <string.h>
#include "protocol.h"
#define SERIALCOMM "115200/8n1"
#define CMD_VERSION "version\r\n"
#define CMD_MONITOR "monitor 200\r\n"
static const uint32_t scanopts[] = {
SR_CONF_CONN,
SR_CONF_SERIALCOMM,
};
static const uint32_t drvopts[] = {
SR_CONF_ELECTRONIC_LOAD,
};
static const uint32_t devopts[] = {
SR_CONF_CONTINUOUS | SR_CONF_SET,
SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
};
static const uint32_t devopts_cg[] = {
SR_CONF_ENABLED | SR_CONF_GET,
SR_CONF_REGULATION | SR_CONF_GET,
SR_CONF_VOLTAGE | SR_CONF_GET,
SR_CONF_CURRENT | SR_CONF_GET,
SR_CONF_CURRENT_LIMIT | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED | SR_CONF_GET,
SR_CONF_OVER_CURRENT_PROTECTION_ENABLED | SR_CONF_GET,
SR_CONF_OVER_TEMPERATURE_PROTECTION | SR_CONF_GET,
SR_CONF_OVER_TEMPERATURE_PROTECTION_ACTIVE | SR_CONF_GET,
};
SR_PRIV struct sr_dev_driver arachnid_labs_re_load_pro_driver_info;
static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
@ -30,15 +63,94 @@ static int init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
static GSList *scan(struct sr_dev_driver *di, GSList *options)
{
struct sr_dev_inst *sdi;
struct drv_context *drvc;
GSList *devices;
(void)options;
struct dev_context *devc;
struct sr_config *src;
struct sr_serial_dev_inst *serial;
struct sr_channel_group *cg;
struct sr_channel *ch;
GSList *l, *devices;
int ret, len;
const char *conn, *serialcomm;
char buf[100];
char *bufptr;
devices = NULL;
drvc = di->context;
drvc->instances = NULL;
conn = serialcomm = NULL;
for (l = options; l; l = l->next) {
src = l->data;
switch (src->key) {
case SR_CONF_CONN:
conn = g_variant_get_string(src->data, NULL);
break;
case SR_CONF_SERIALCOMM:
serialcomm = g_variant_get_string(src->data, NULL);
break;
}
}
if (!conn)
return NULL;
if (!serialcomm)
serialcomm = SERIALCOMM;
serial = sr_serial_dev_inst_new(conn, serialcomm);
if (serial_open(serial, SERIAL_RDWR) != SR_OK)
return NULL;
serial_flush(serial);
if (serial_write_blocking(serial, CMD_VERSION,
strlen(CMD_VERSION), serial_timeout(serial,
strlen(CMD_VERSION))) < (int)strlen(CMD_VERSION)) {
sr_dbg("Unable to write while probing for hardware.");
serial_close(serial);
return NULL;
}
memset(buf, 0, sizeof(buf));
bufptr = buf;
len = sizeof(buf);
ret = serial_readline(serial, &bufptr, &len, 3000);
if (ret < 0 || len < 9 || strncmp((const char *)&buf, "version ", 8)) {
sr_dbg("Unable to probe version number.");
serial_close(serial);
return NULL;
}
sdi = g_malloc0(sizeof(struct sr_dev_inst));
sdi->status = SR_ST_ACTIVE;
sdi->vendor = g_strdup("Arachnid Labs");
sdi->model = g_strdup("Re:load Pro");
sdi->version = g_strdup(buf + 8);
sdi->driver = &arachnid_labs_re_load_pro_driver_info;
sdi->inst_type = SR_INST_SERIAL;
sdi->conn = serial;
cg = g_malloc0(sizeof(struct sr_channel_group));
cg->name = g_strdup("1");
sdi->channel_groups = g_slist_append(sdi->channel_groups, cg);
ch = sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "V");
cg->channels = g_slist_append(cg->channels, ch);
ch = sr_channel_new(sdi, 0, SR_CHANNEL_ANALOG, TRUE, "I");
cg->channels = g_slist_append(cg->channels, ch);
devc = g_malloc0(sizeof(struct dev_context));
sdi->priv = devc;
drvc->instances = g_slist_append(drvc->instances, sdi);
devices = g_slist_append(devices, sdi);
serial_close(serial);
if (!devices)
sr_serial_dev_inst_free(serial);
return devices;
}
@ -52,40 +164,126 @@ static int dev_clear(const struct sr_dev_driver *di)
return std_dev_clear(di, NULL);
}
static int dev_open(struct sr_dev_inst *sdi)
{
(void)sdi;
sdi->status = SR_ST_ACTIVE;
return SR_OK;
}
static int dev_close(struct sr_dev_inst *sdi)
{
(void)sdi;
sdi->status = SR_ST_INACTIVE;
return SR_OK;
}
static int cleanup(const struct sr_dev_driver *di)
{
return dev_clear(di);
}
static int config_list(uint32_t key, GVariant **data,
const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
GVariantBuilder gvb;
int ret;
/* Always available. */
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;
}
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;
}
if (!sdi)
return SR_ERR_ARG;
ret = SR_OK;
if (!cg) {
/* No channel group: global options. */
switch (key) {
case SR_CONF_DEVICE_OPTIONS:
*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
break;
default:
return SR_ERR_NA;
}
} else {
switch (key) {
case SR_CONF_DEVICE_OPTIONS:
*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
devopts_cg, ARRAY_SIZE(devopts_cg), sizeof(uint32_t));
break;
case SR_CONF_CURRENT_LIMIT:
g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
/* Min, max, step. */
g_variant_builder_add_value(&gvb, g_variant_new_double(0.0));
g_variant_builder_add_value(&gvb, g_variant_new_double(6.0));
g_variant_builder_add_value(&gvb, g_variant_new_double(0.001)); /* 1mA steps */
*data = g_variant_builder_end(&gvb);
break;
default:
return SR_ERR_NA;
}
}
return ret;
}
static int config_get(uint32_t key, GVariant **data,
const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
struct dev_context *devc;
int ret;
float fvalue;
(void)sdi;
(void)data;
(void)cg;
devc = sdi->priv;
/*
* These features/keys are not supported by the hardware:
* - SR_CONF_OVER_VOLTAGE_PROTECTION_ACTIVE
* - SR_CONF_OVER_VOLTAGE_PROTECTION_THRESHOLD
* - SR_CONF_OVER_CURRENT_PROTECTION_ACTIVE
* - SR_CONF_OVER_CURRENT_PROTECTION_THRESHOLD
*/
ret = SR_OK;
switch (key) {
case SR_CONF_LIMIT_SAMPLES:
*data = g_variant_new_uint64(devc->limit_samples);
break;
case SR_CONF_LIMIT_MSEC:
*data = g_variant_new_uint64(devc->limit_msec);
break;
case SR_CONF_ENABLED:
*data = g_variant_new_boolean(TRUE); /* Always on. */
break;
case SR_CONF_REGULATION:
*data = g_variant_new_string("CC"); /* Always CC mode. */
break;
case SR_CONF_VOLTAGE:
if (reloadpro_get_voltage_current(sdi, &fvalue, NULL) < 0)
return SR_ERR;
*data = g_variant_new_double(fvalue);
break;
case SR_CONF_CURRENT:
if (reloadpro_get_voltage_current(sdi, NULL, &fvalue) < 0)
return SR_ERR;
*data = g_variant_new_double(fvalue);
break;
case SR_CONF_CURRENT_LIMIT:
if (reloadpro_get_current_limit(sdi, &fvalue) == SR_OK)
*data = g_variant_new_double(fvalue);
break;
case SR_CONF_OVER_VOLTAGE_PROTECTION_ENABLED:
*data = g_variant_new_boolean(TRUE); /* Always on. */
break;
case SR_CONF_OVER_CURRENT_PROTECTION_ENABLED:
*data = g_variant_new_boolean(TRUE); /* Always on. */
break;
case SR_CONF_OVER_TEMPERATURE_PROTECTION:
*data = g_variant_new_boolean(TRUE); /* Always on. */
break;
case SR_CONF_OVER_TEMPERATURE_PROTECTION_ACTIVE:
*data = g_variant_new_boolean(devc->otp_active);
break;
default:
return SR_ERR_NA;
}
@ -96,16 +294,28 @@ static int config_get(uint32_t key, GVariant **data,
static int config_set(uint32_t key, GVariant *data,
const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
struct dev_context *devc;
int ret;
(void)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);
break;
case SR_CONF_LIMIT_MSEC:
devc->limit_msec = g_variant_get_uint64(data);
break;
case SR_CONF_CURRENT_LIMIT:
ret = reloadpro_set_current_limit(sdi,
g_variant_get_double(data));
break;
default:
ret = SR_ERR_NA;
}
@ -113,43 +323,47 @@ static int config_set(uint32_t key, GVariant *data,
return ret;
}
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;
ret = SR_OK;
switch (key) {
default:
return SR_ERR_NA;
}
return ret;
}
static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
{
(void)sdi;
int ret;
struct dev_context *devc;
struct sr_serial_dev_inst *serial;
(void)cb_data;
if (sdi->status != SR_ST_ACTIVE)
return SR_ERR_DEV_CLOSED;
devc = sdi->priv;
serial = sdi->conn;
/* Send the 'monitor <ms>' command (doesn't have a reply). */
if ((ret = serial_write_blocking(serial, CMD_MONITOR,
strlen(CMD_MONITOR), serial_timeout(serial,
strlen(CMD_MONITOR)))) < (int)strlen(CMD_MONITOR)) {
sr_err("Unable to send 'monitor' command: %d.", ret);
return SR_ERR;
}
/* Poll every 100ms, or whenever some data comes in. */
serial_source_add(sdi->session, serial, G_IO_IN, 100,
reloadpro_receive_data, (void *)sdi);
std_session_send_df_header(cb_data, LOG_PREFIX);
memset(devc->buf, 0, RELOADPRO_BUFSIZE);
devc->buflen = 0;
devc->num_samples = 0;
devc->starttime = g_get_monotonic_time();
return SR_OK;
}
static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
{
(void)cb_data;
if (sdi->status != SR_ST_ACTIVE)
return SR_ERR_DEV_CLOSED;
return SR_OK;
return std_serial_dev_acquisition_stop(sdi, cb_data,
std_serial_dev_close, sdi->conn, LOG_PREFIX);
}
SR_PRIV struct sr_dev_driver arachnid_labs_re_load_pro_driver_info = {
@ -164,8 +378,8 @@ SR_PRIV struct sr_dev_driver arachnid_labs_re_load_pro_driver_info = {
.config_get = config_get,
.config_set = config_set,
.config_list = config_list,
.dev_open = dev_open,
.dev_close = dev_close,
.dev_open = std_serial_dev_open,
.dev_close = std_serial_dev_close,
.dev_acquisition_start = dev_acquisition_start,
.dev_acquisition_stop = dev_acquisition_stop,
.context = NULL,

View File

@ -1,7 +1,7 @@
/*
* This file is part of the libsigrok project.
*
* Copyright (C) 2015 Uwe Hermann <uwe@hermann-uwe.de>
* Copyright (C) 2015-2016 Uwe Hermann <uwe@hermann-uwe.de>
*
* 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
@ -19,22 +19,238 @@
*/
#include <config.h>
#include <string.h>
#include "protocol.h"
SR_PRIV int arachnid_labs_re_load_pro_receive_data(int fd, int revents, void *cb_data)
#define READ_TIMEOUT_MS 1000
static int send_cmd(const struct sr_dev_inst *sdi, const char *cmd,
char *replybuf, int replybufsize)
{
const struct sr_dev_inst *sdi;
char *bufptr;
int len, ret;
struct sr_serial_dev_inst *serial;
serial = sdi->conn;
/* Send the command (blocking, with timeout). */
if ((ret = serial_write_blocking(serial, cmd,
strlen(cmd), serial_timeout(serial,
strlen(cmd)))) < (int)strlen(cmd)) {
sr_err("Unable to send command.");
return SR_ERR;
}
/* Read the reply (blocking, with timeout). */
memset(replybuf, 0, replybufsize);
bufptr = replybuf;
len = replybufsize;
ret = serial_readline(serial, &bufptr, &len, READ_TIMEOUT_MS);
/* If we got 0 characters (possibly one \r or \n), retry once. */
if (len == 0) {
len = replybufsize;
ret = serial_readline(serial, &bufptr, &len, READ_TIMEOUT_MS);
}
if (g_str_has_prefix((const char *)&bufptr, "err ")) {
sr_err("Device replied with an error: '%s'.", bufptr);
return SR_ERR;
}
return ret;
}
SR_PRIV int reloadpro_set_current_limit(const struct sr_dev_inst *sdi,
float current)
{
int ret, ma;
char buf[100];
char *cmd;
if (current < 0 || current > 6) {
sr_err("The current limit must be 0-6 A (was %f A).", current);
return SR_ERR_ARG;
}
/* Hardware expects current in mA, integer (0..6000). */
ma = (int)(current * 1000);
sr_err("Setting current limit to %f A (%d mA).", current, ma);
cmd = g_strdup_printf("set %d\n", ma);
if ((ret = send_cmd(sdi, cmd, (char *)&buf, sizeof(buf))) < 0) {
sr_err("Error sending current limit command: %d.", ret);
g_free(cmd);
return SR_ERR;
}
g_free(cmd);
return SR_OK;
}
SR_PRIV int reloadpro_get_current_limit(const struct sr_dev_inst *sdi,
float *current)
{
int ret;
char buf[100];
if ((ret = send_cmd(sdi, "set\n", (char *)&buf, sizeof(buf))) < 0) {
sr_err("Error sending current limit query: %d.", ret);
return SR_ERR;
}
/* Hardware sends current in mA, integer (0..6000). */
*current = g_ascii_strtod(buf + 4, NULL) / 1000;
return SR_OK;
}
SR_PRIV int reloadpro_get_voltage_current(const struct sr_dev_inst *sdi,
float *voltage, float *current)
{
int ret;
char buf[100];
char **tokens;
if ((ret = send_cmd(sdi, "read\n", (char *)&buf, sizeof(buf))) < 0) {
sr_err("Error sending voltage/current query: %d.", ret);
return SR_ERR;
}
/* Reply: "read <current> <voltage>". */
tokens = g_strsplit((const char *)&buf, " ", 3);
if (voltage)
*voltage = g_ascii_strtod(tokens[2], NULL) / 1000;
if (current)
*current = g_ascii_strtod(tokens[1], NULL) / 1000;
g_strfreev(tokens);
return SR_OK;
}
static void handle_packet(const struct sr_dev_inst *sdi)
{
float voltage, current;
struct sr_datafeed_packet packet;
struct sr_datafeed_analog_old analog;
struct dev_context *devc;
char **tokens;
GSList *l;
devc = sdi->priv;
if (g_str_has_prefix((const char *)devc->buf, "overtemp")) {
sr_dbg("Overtemperature condition!");
devc->otp_active = TRUE;
return;
}
if (!g_str_has_prefix((const char *)devc->buf, "read ")) {
sr_dbg("Unknown packet: '%s'.", devc->buf);
return;
}
tokens = g_strsplit((const char *)devc->buf, " ", 3);
voltage = g_ascii_strtod(tokens[2], NULL) / 1000;
current = g_ascii_strtod(tokens[1], NULL) / 1000;
g_strfreev(tokens);
memset(&analog, 0, sizeof(struct sr_datafeed_analog_old));
/* Begin frame. */
packet.type = SR_DF_FRAME_BEGIN;
sr_session_send(sdi, &packet);
packet.type = SR_DF_ANALOG_OLD;
packet.payload = &analog;
analog.num_samples = 1;
/* Voltage */
l = g_slist_copy(sdi->channels);
l = g_slist_remove_link(l, g_slist_nth(l, 1));
analog.channels = l;
analog.mq = SR_MQ_VOLTAGE;
analog.mqflags = SR_MQFLAG_DC;
analog.unit = SR_UNIT_VOLT;
analog.data = &voltage;
sr_session_send(sdi, &packet);
g_slist_free(l);
/* Current */
l = g_slist_copy(sdi->channels);
l = g_slist_remove_link(l, g_slist_nth(l, 0));
analog.channels = l;
analog.mq = SR_MQ_CURRENT;
analog.mqflags = SR_MQFLAG_DC;
analog.unit = SR_UNIT_AMPERE;
analog.data = &current;
sr_session_send(sdi, &packet);
g_slist_free(l);
/* End frame. */
packet.type = SR_DF_FRAME_END;
sr_session_send(sdi, &packet);
devc->num_samples++;
}
static void handle_new_data(const struct sr_dev_inst *sdi)
{
int len;
struct dev_context *devc;
struct sr_serial_dev_inst *serial;
devc = sdi->priv;
serial = sdi->conn;
/* Try to get as much data as the buffer can hold. */
len = RELOADPRO_BUFSIZE - devc->buflen;
len = serial_read_nonblocking(serial, devc->buf + devc->buflen, len);
if (len == 0)
return; /* No new bytes, nothing to do. */
if (len < 0) {
sr_err("Serial port read error: %d.", len);
return;
}
devc->buflen += len;
if (g_str_has_suffix((const char *)devc->buf, "\n")) {
handle_packet(sdi);
memset(devc->buf, 0, RELOADPRO_BUFSIZE);
devc->buflen = 0;
}
}
SR_PRIV int reloadpro_receive_data(int fd, int revents, void *cb_data)
{
struct sr_dev_inst *sdi;
struct dev_context *devc;
int64_t t;
(void)fd;
if (!(sdi = cb_data))
sdi = cb_data;
devc = sdi->priv;
if (revents != G_IO_IN)
return TRUE;
if (!(devc = sdi->priv))
return TRUE;
handle_new_data(sdi);
if (revents == G_IO_IN) {
if (devc->limit_samples && (devc->num_samples >= devc->limit_samples)) {
sr_info("Requested number of samples reached.");
sdi->driver->dev_acquisition_stop(sdi, cb_data);
return TRUE;
}
if (devc->limit_msec) {
t = (g_get_monotonic_time() - devc->starttime) / 1000;
if (t > (int64_t)devc->limit_msec) {
sr_info("Requested time limit reached.");
sdi->driver->dev_acquisition_stop(sdi, cb_data);
return TRUE;
}
}
return TRUE;

View File

@ -1,7 +1,7 @@
/*
* This file is part of the libsigrok project.
*
* Copyright (C) 2015 Uwe Hermann <uwe@hermann-uwe.de>
* Copyright (C) 2015-2016 Uwe Hermann <uwe@hermann-uwe.de>
*
* 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
@ -26,12 +26,27 @@
#include <libsigrok/libsigrok.h>
#include "libsigrok-internal.h"
#define LOG_PREFIX "arachnid-labs-re-load-pro"
#define LOG_PREFIX "re-load-pro"
#define RELOADPRO_BUFSIZE 100
/** Private, per-device-instance driver context. */
struct dev_context {
uint64_t limit_samples;
uint64_t limit_msec;
uint64_t num_samples;
int64_t starttime;
uint8_t buf[RELOADPRO_BUFSIZE];
int buflen;
gboolean otp_active;
};
SR_PRIV int reloadpro_set_current_limit(const struct sr_dev_inst *sdi,
float current);
SR_PRIV int reloadpro_get_current_limit(const struct sr_dev_inst *sdi,
float *current);
SR_PRIV int reloadpro_get_voltage_current(const struct sr_dev_inst *sdi,
float *voltage, float *current);
SR_PRIV int reloadpro_receive_data(int fd, int revents, void *cb_data);
#endif