/*
* 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
#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[] = {
} chan_defs[] = {
{"CC1", SR_CHANNEL_LOGIC},
{"CC2", SR_CHANNEL_LOGIC},
+ {"VBUS_V", SR_CHANNEL_ANALOG},
+ {"VBUS_A", SR_CHANNEL_ANALOG},
{NULL, 0}
};
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;
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)
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));
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);
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;
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.");
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;
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)
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;
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))) {
}
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)
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;
.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,
/*
* 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
#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;
}
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);
+}