chromium-twinkie: add analog VBUS channels
Retrieve VBUS voltage and current through packetized console commands if this interface exists and analog channels are enabled. Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
This commit is contained in:
parent
6c71d32f27
commit
9492726042
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* This file is part of the libsigrok project.
|
||||
*
|
||||
* Copyright 2014 Google, Inc
|
||||
* Copyright 2017 Google, Inc
|
||||
*
|
||||
* 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
|
||||
|
@ -31,14 +31,21 @@
|
|||
|
||||
#define USB_INTERFACE 1
|
||||
#define USB_CONFIGURATION 1
|
||||
#define USB_COMMANDS_IFACE 2
|
||||
|
||||
#define MAX_RENUM_DELAY_MS 3000
|
||||
#define NUM_SIMUL_TRANSFERS 32
|
||||
|
||||
#define SAMPLE_RATE SR_KHZ(2400)
|
||||
|
||||
static const char vbus_cmd[] = "tw vbus";
|
||||
|
||||
/* CC1 & CC2 are always present */
|
||||
#define LOGIC_CHANNELS_COUNT 2
|
||||
|
||||
static const int32_t hwopts[] = {
|
||||
SR_CONF_CONN,
|
||||
SR_CONF_NUM_ANALOG_CHANNELS,
|
||||
};
|
||||
|
||||
static const int32_t hwcaps[] = {
|
||||
|
@ -55,6 +62,8 @@ static const struct chan {
|
|||
} chan_defs[] = {
|
||||
{"CC1", SR_CHANNEL_LOGIC},
|
||||
{"CC2", SR_CHANNEL_LOGIC},
|
||||
{"VBUS_V", SR_CHANNEL_ANALOG},
|
||||
{"VBUS_A", SR_CHANNEL_ANALOG},
|
||||
|
||||
{NULL, 0}
|
||||
};
|
||||
|
@ -66,13 +75,17 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
|||
struct sr_dev_inst *sdi;
|
||||
struct sr_usb_dev_inst *usb;
|
||||
struct sr_channel *ch;
|
||||
struct sr_channel_group *cc_grp, *vbus_grp[VBUS_GRP_COUNT];
|
||||
struct sr_config *src;
|
||||
GSList *l, *devices, *conn_devices;
|
||||
struct libusb_device_descriptor des;
|
||||
struct libusb_config_descriptor *cfg;
|
||||
libusb_device **devlist;
|
||||
int ret, i, j;
|
||||
const char *conn;
|
||||
char connection_id[64];
|
||||
/* By default, disable VBUS analog */
|
||||
int vbus_channels = 0;
|
||||
|
||||
drvc = di->context;
|
||||
|
||||
|
@ -83,6 +96,9 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
|||
case SR_CONF_CONN:
|
||||
conn = g_variant_get_string(src->data, NULL);
|
||||
break;
|
||||
case SR_CONF_NUM_ANALOG_CHANNELS:
|
||||
vbus_channels = MIN(2, g_variant_get_int32(src->data));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (conn)
|
||||
|
@ -117,6 +133,12 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
|||
if (des.idVendor != TWINKIE_VID || des.idProduct != TWINKIE_PID)
|
||||
continue;
|
||||
|
||||
if ((ret = libusb_get_active_config_descriptor(devlist[i], &cfg)) != 0) {
|
||||
sr_warn("Failed to get device configuraton: %s.",
|
||||
libusb_error_name(ret));
|
||||
continue;
|
||||
}
|
||||
|
||||
usb_get_port_path(devlist[i], connection_id, sizeof(connection_id));
|
||||
|
||||
sdi = g_malloc0(sizeof(struct sr_dev_inst));
|
||||
|
@ -126,14 +148,53 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
|||
sdi->driver = di;
|
||||
sdi->connection_id = g_strdup(connection_id);
|
||||
|
||||
for (j = 0; chan_defs[j].name; j++)
|
||||
if (!(ch = sr_channel_new(sdi, j, chan_defs[j].type,
|
||||
TRUE, chan_defs[j].name)))
|
||||
return NULL;
|
||||
if (vbus_channels && cfg->bNumInterfaces < 3) {
|
||||
sr_warn("VBUS channels not available in this firmware.");
|
||||
vbus_channels = 0;
|
||||
}
|
||||
|
||||
if (!(devc = g_try_malloc0(sizeof(struct dev_context))))
|
||||
return NULL;
|
||||
sdi->priv = devc;
|
||||
|
||||
cc_grp = g_malloc0(sizeof(struct sr_channel_group));
|
||||
cc_grp->name = g_strdup("CCx");
|
||||
for (j = 0; j < vbus_channels; j++) {
|
||||
vbus_grp[j] = g_malloc0(sizeof(struct sr_channel_group));
|
||||
vbus_grp[j]->name = g_strdup(j == VBUS_V ? "VBUS_V"
|
||||
: "VBUS_A");
|
||||
}
|
||||
|
||||
for (j = 0; chan_defs[j].name; j++) {
|
||||
struct sr_channel_group *grp = cc_grp;
|
||||
if (j >= LOGIC_CHANNELS_COUNT) { /* Analog channels */
|
||||
if (j - LOGIC_CHANNELS_COUNT >= vbus_channels)
|
||||
break;
|
||||
grp = vbus_grp[j - LOGIC_CHANNELS_COUNT];
|
||||
}
|
||||
ch = sr_channel_new(sdi, j, chan_defs[j].type, TRUE,
|
||||
chan_defs[j].name);
|
||||
grp->channels = g_slist_append(grp->channels, ch);
|
||||
}
|
||||
sdi->channel_groups = g_slist_append(NULL, cc_grp);
|
||||
for (j = 0; j < vbus_channels; j++) {
|
||||
sdi->channel_groups = g_slist_append(sdi->channel_groups,
|
||||
vbus_grp[j]);
|
||||
sr_analog_init(&devc->vbus_packet[j],
|
||||
&devc->vbus_encoding,
|
||||
&devc->vbus_meaning[j],
|
||||
&devc->vbus_spec, 3);
|
||||
devc->vbus_meaning[j].channels = vbus_grp[j]->channels;
|
||||
}
|
||||
/* other encoding default settings in sr_analog_init are OK (eg float) */
|
||||
devc->vbus_encoding.is_signed = TRUE;
|
||||
devc->vbus_meaning[VBUS_V].mq = SR_MQ_VOLTAGE;
|
||||
devc->vbus_meaning[VBUS_V].mqflags = SR_MQFLAG_DC;
|
||||
devc->vbus_meaning[VBUS_V].unit = SR_UNIT_VOLT;
|
||||
devc->vbus_meaning[VBUS_A].mq = SR_MQ_CURRENT;
|
||||
devc->vbus_meaning[VBUS_A].unit = SR_UNIT_AMPERE;
|
||||
|
||||
devc->vbus_channels = vbus_channels;
|
||||
drvc->instances = g_slist_append(drvc->instances, sdi);
|
||||
devices = g_slist_append(devices, sdi);
|
||||
|
||||
|
@ -150,17 +211,19 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
|
|||
return devices;
|
||||
}
|
||||
|
||||
static int twinkie_dev_open(struct sr_dev_inst *sdi)
|
||||
static int dev_open(struct sr_dev_inst *sdi)
|
||||
{
|
||||
struct sr_dev_driver *di;
|
||||
libusb_device **devlist;
|
||||
struct sr_usb_dev_inst *usb;
|
||||
struct libusb_device_descriptor des;
|
||||
struct drv_context *drvc;
|
||||
struct dev_context *devc;
|
||||
int ret, i, device_count;
|
||||
char connection_id[64];
|
||||
|
||||
di = sdi->driver;
|
||||
devc = sdi->priv;
|
||||
drvc = di->context;
|
||||
usb = sdi->conn;
|
||||
|
||||
|
@ -222,6 +285,13 @@ static int twinkie_dev_open(struct sr_dev_inst *sdi)
|
|||
libusb_error_name(ret));
|
||||
break;
|
||||
}
|
||||
if (devc->vbus_channels &&
|
||||
(ret = libusb_claim_interface(usb->devhdl, USB_COMMANDS_IFACE))) {
|
||||
sr_err("Unable to claim commands interface %d/%s.", ret,
|
||||
libusb_error_name(ret));
|
||||
/* Cannot use the analog channels for VBUS. */
|
||||
devc->vbus_channels = 0;
|
||||
}
|
||||
|
||||
if ((ret = twinkie_init_device(sdi)) != SR_OK) {
|
||||
sr_err("Failed to init device.");
|
||||
|
@ -248,29 +318,19 @@ static int twinkie_dev_open(struct sr_dev_inst *sdi)
|
|||
return SR_OK;
|
||||
}
|
||||
|
||||
static int dev_open(struct sr_dev_inst *sdi)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = twinkie_dev_open(sdi);
|
||||
if (ret != SR_OK) {
|
||||
sr_err("Unable to open device.");
|
||||
return SR_ERR;
|
||||
}
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
|
||||
static int dev_close(struct sr_dev_inst *sdi)
|
||||
{
|
||||
struct sr_usb_dev_inst *usb;
|
||||
struct dev_context *devc;
|
||||
|
||||
usb = sdi->conn;
|
||||
if (usb->devhdl == NULL)
|
||||
return SR_ERR;
|
||||
|
||||
sr_info("Closing device %d.%d interface %d.",
|
||||
usb->bus, usb->address, USB_INTERFACE);
|
||||
sr_info("Closing device %d.%d.", usb->bus, usb->address);
|
||||
devc = sdi->priv;
|
||||
if (devc->vbus_channels)
|
||||
libusb_release_interface(usb->devhdl, USB_COMMANDS_IFACE);
|
||||
libusb_release_interface(usb->devhdl, USB_INTERFACE);
|
||||
libusb_close(usb->devhdl);
|
||||
usb->devhdl = NULL;
|
||||
|
@ -279,11 +339,6 @@ static int dev_close(struct sr_dev_inst *sdi)
|
|||
return SR_OK;
|
||||
}
|
||||
|
||||
static int dev_clear(const struct sr_dev_driver *di)
|
||||
{
|
||||
return std_dev_clear(di, NULL);
|
||||
}
|
||||
|
||||
static int config_get(uint32_t key, GVariant **data,
|
||||
const struct sr_dev_inst *sdi,
|
||||
const struct sr_channel_group *cg)
|
||||
|
@ -414,7 +469,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
|
|||
struct drv_context *drvc;
|
||||
struct sr_usb_dev_inst *usb;
|
||||
struct libusb_transfer *transfer;
|
||||
unsigned int i, timeout, num_transfers;
|
||||
unsigned int i, timeout, num_transfers, cc_transfers;
|
||||
int ret;
|
||||
unsigned char *buf;
|
||||
size_t size, convsize;
|
||||
|
@ -431,10 +486,12 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
|
|||
memset(devc->cc, 0, sizeof(devc->cc));
|
||||
|
||||
timeout = 1000;
|
||||
num_transfers = 10;
|
||||
cc_transfers = num_transfers = 10;
|
||||
size = 10*1024;
|
||||
convsize = size * 8 * 256 /* largest size : only rollbacks/no edges */;
|
||||
devc->submitted_transfers = 0;
|
||||
if (devc->vbus_channels)
|
||||
num_transfers += 2;
|
||||
|
||||
devc->convbuffer_size = convsize;
|
||||
if (!(devc->convbuffer = g_try_malloc(convsize))) {
|
||||
|
@ -451,7 +508,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
|
|||
}
|
||||
|
||||
devc->num_transfers = num_transfers;
|
||||
for (i = 0; i < num_transfers; i++) {
|
||||
for (i = 0; i < cc_transfers; i++) {
|
||||
if (!(buf = g_try_malloc(size))) {
|
||||
sr_err("USB transfer buffer malloc failed.");
|
||||
if (devc->submitted_transfers)
|
||||
|
@ -477,6 +534,28 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi)
|
|||
devc->transfers[i] = transfer;
|
||||
devc->submitted_transfers++;
|
||||
}
|
||||
if (devc->vbus_channels) {
|
||||
struct libusb_transfer *out_xfer = libusb_alloc_transfer(0);
|
||||
struct libusb_transfer *in_xfer = libusb_alloc_transfer(0);
|
||||
libusb_fill_bulk_transfer(out_xfer, usb->devhdl,
|
||||
2 | LIBUSB_ENDPOINT_OUT, (uint8_t *)vbus_cmd,
|
||||
sizeof(vbus_cmd) - 1, twinkie_vbus_sent,
|
||||
(void *)sdi, timeout);
|
||||
libusb_fill_bulk_transfer(in_xfer, usb->devhdl,
|
||||
2 | LIBUSB_ENDPOINT_IN, (uint8_t *)devc->vbus_data,
|
||||
sizeof(devc->vbus_data),
|
||||
twinkie_vbus_recv, (void *)sdi, timeout);
|
||||
if ((ret = libusb_submit_transfer(out_xfer)) != 0) {
|
||||
sr_err("Failed to submit VBUS transfer: %s.",
|
||||
libusb_error_name(ret));
|
||||
libusb_free_transfer(out_xfer);
|
||||
abort_acquisition(devc);
|
||||
return SR_ERR;
|
||||
}
|
||||
devc->transfers[cc_transfers + 0] = out_xfer;
|
||||
devc->transfers[cc_transfers + 1] = in_xfer;
|
||||
devc->submitted_transfers++;
|
||||
}
|
||||
|
||||
devc->ctx = drvc->sr_ctx;
|
||||
|
||||
|
@ -512,7 +591,7 @@ static struct sr_dev_driver chromium_twinkie_driver_info = {
|
|||
.cleanup = std_cleanup,
|
||||
.scan = scan,
|
||||
.dev_list = std_dev_list,
|
||||
.dev_clear = dev_clear,
|
||||
.dev_clear = NULL,
|
||||
.config_get = config_get,
|
||||
.config_set = config_set,
|
||||
.config_list = config_list,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* This file is part of the libsigrok project.
|
||||
*
|
||||
* Copyright 2014 Google, Inc
|
||||
* Copyright 2017 Google, Inc
|
||||
*
|
||||
* 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
|
||||
|
@ -28,9 +28,16 @@
|
|||
#include "libsigrok-internal.h"
|
||||
#include "protocol.h"
|
||||
|
||||
/* 'twinkie vbus' command output format */
|
||||
#define VBUS_FORMAT "VBUS = %d mV ; %d mA"
|
||||
|
||||
SR_PRIV int twinkie_start_acquisition(const struct sr_dev_inst *sdi)
|
||||
{
|
||||
(void)sdi;
|
||||
struct dev_context *devc = sdi->priv;
|
||||
struct timespec tsample;
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &tsample);
|
||||
devc->vbus_t0 = tsample.tv_nsec + (uint64_t)tsample.tv_sec *1000000000ULL;
|
||||
|
||||
return SR_OK;
|
||||
}
|
||||
|
@ -246,3 +253,100 @@ resubmit:
|
|||
if (libusb_submit_transfer(transfer) != LIBUSB_SUCCESS)
|
||||
free_transfer(transfer);
|
||||
}
|
||||
|
||||
static void export_vbus(const struct sr_dev_inst *sdi, int mv, int ma)
|
||||
{
|
||||
static float tmp_data[VBUS_GRP_COUNT][32768];
|
||||
struct dev_context *devc = sdi->priv;
|
||||
struct sr_datafeed_packet packet[VBUS_GRP_COUNT];
|
||||
uint64_t tlen = devc->vbus_delta * 24 / 10000;
|
||||
uint64_t len = MIN(32768, tlen);
|
||||
unsigned i, g;
|
||||
|
||||
for (g = 0; g < devc->vbus_channels; g++) {
|
||||
float val = g == VBUS_V ? mv/1000.0 : ma/1000.0;
|
||||
packet[g].type = SR_DF_ANALOG;
|
||||
packet[g].payload = &devc->vbus_packet[g];
|
||||
devc->vbus_packet[g].data = tmp_data[g];
|
||||
for (i = 0; i < len; i++)
|
||||
tmp_data[g][i] = val;
|
||||
}
|
||||
|
||||
do {
|
||||
for (g = 0; g < devc->vbus_channels; g++) {
|
||||
devc->vbus_packet[g].num_samples = len;
|
||||
sr_session_send(sdi, &packet[g]);
|
||||
}
|
||||
tlen -= len;
|
||||
len = MIN(32768, tlen);
|
||||
} while (tlen);
|
||||
}
|
||||
|
||||
SR_PRIV void twinkie_vbus_sent(struct libusb_transfer *transfer)
|
||||
{
|
||||
struct sr_dev_inst *sdi = transfer->user_data;
|
||||
struct dev_context *devc = sdi->priv;
|
||||
struct libusb_transfer *in_xfer = devc->transfers[11];
|
||||
struct timespec tsample;
|
||||
uint64_t now;
|
||||
|
||||
/* acquisition has already ended */
|
||||
if (devc->sent_samples < 0 || transfer->status == LIBUSB_TRANSFER_NO_DEVICE)
|
||||
goto abort_vbus;
|
||||
|
||||
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
|
||||
goto abort_vbus;
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &tsample);
|
||||
now = tsample.tv_nsec + (uint64_t)tsample.tv_sec *1000000000ULL;
|
||||
devc->vbus_delta = now - devc->vbus_t0;
|
||||
devc->vbus_t0 = now;
|
||||
if (libusb_submit_transfer(in_xfer) != LIBUSB_SUCCESS)
|
||||
goto abort_vbus;
|
||||
|
||||
return;
|
||||
abort_vbus:
|
||||
libusb_free_transfer(transfer);
|
||||
libusb_free_transfer(in_xfer);
|
||||
devc->transfers[10] = NULL;
|
||||
devc->transfers[11] = NULL;
|
||||
devc->submitted_transfers--;
|
||||
if (devc->submitted_transfers == 0)
|
||||
finish_acquisition(sdi);
|
||||
}
|
||||
|
||||
SR_PRIV void twinkie_vbus_recv(struct libusb_transfer *transfer)
|
||||
{
|
||||
struct sr_dev_inst *sdi = transfer->user_data;
|
||||
struct dev_context *devc = sdi->priv;
|
||||
struct libusb_transfer *out_xfer = devc->transfers[10];
|
||||
|
||||
/* acquisition has already ended */
|
||||
if (devc->sent_samples < 0 || transfer->status == LIBUSB_TRANSFER_NO_DEVICE)
|
||||
goto abort_vbus;
|
||||
|
||||
if (transfer->status == LIBUSB_TRANSFER_COMPLETED &&
|
||||
transfer->actual_length) {
|
||||
int vbus_ma, vbus_mv;
|
||||
int len = transfer->actual_length;
|
||||
if (len > 63)
|
||||
len = 63;
|
||||
devc->vbus_data[len] = 0;
|
||||
if (sscanf(devc->vbus_data, VBUS_FORMAT, &vbus_mv, &vbus_ma) == 2) {
|
||||
export_vbus(sdi, vbus_mv, vbus_ma);
|
||||
}
|
||||
}
|
||||
|
||||
if (libusb_submit_transfer(out_xfer) != LIBUSB_SUCCESS)
|
||||
goto abort_vbus;
|
||||
|
||||
return;
|
||||
abort_vbus:
|
||||
libusb_free_transfer(transfer);
|
||||
libusb_free_transfer(out_xfer);
|
||||
devc->transfers[10] = NULL;
|
||||
devc->transfers[11] = NULL;
|
||||
devc->submitted_transfers--;
|
||||
if (devc->submitted_transfers == 0)
|
||||
finish_acquisition(sdi);
|
||||
}
|
||||
|
|
|
@ -35,6 +35,13 @@ struct cc_context {
|
|||
uint8_t level;
|
||||
};
|
||||
|
||||
enum vbus_group_index {
|
||||
VBUS_V = 0,
|
||||
VBUS_A = 1,
|
||||
|
||||
VBUS_GRP_COUNT = 2
|
||||
};
|
||||
|
||||
/** Private, per-device-instance driver context. */
|
||||
struct dev_context {
|
||||
/** Maximum number of samples to capture, if nonzero. */
|
||||
|
@ -50,10 +57,20 @@ struct dev_context {
|
|||
struct sr_context *ctx;
|
||||
|
||||
struct cc_context cc[2];
|
||||
int vbus_channels;
|
||||
char vbus_data[64];
|
||||
uint64_t vbus_t0;
|
||||
uint64_t vbus_delta;
|
||||
struct sr_datafeed_analog vbus_packet[VBUS_GRP_COUNT];
|
||||
struct sr_analog_meaning vbus_meaning[VBUS_GRP_COUNT];
|
||||
struct sr_analog_encoding vbus_encoding;
|
||||
struct sr_analog_spec vbus_spec;
|
||||
};
|
||||
|
||||
SR_PRIV int twinkie_start_acquisition(const struct sr_dev_inst *sdi);
|
||||
SR_PRIV int twinkie_init_device(const struct sr_dev_inst *sdi);
|
||||
SR_PRIV void twinkie_receive_transfer(struct libusb_transfer *transfer);
|
||||
SR_PRIV void twinkie_vbus_sent(struct libusb_transfer *transfer);
|
||||
SR_PRIV void twinkie_vbus_recv(struct libusb_transfer *transfer);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue