la8: Add support for the ChronoVu LA16.
The ChronoVu LA16 is a new logic analyzer from ChronoVu with some differences in features compared to the LA8, e.g. - Supports 16 channels (instead of 8). - Max. 200MHz samplerate (instead of 100MHz). - Supports state triggering (low and high channel value) and edge triggering (rising or falling edge), the LA8 only supports state triggering. This driver now supports both the LA8 and LA16, but it needed a few changes: - Add support for detecting multiple device instances at all. - Add support for both LA8 and/or LA16 devices being detected. - Add a device profile struct for LA8-/LA16-specific device properties. - Move the samplerates list to devc (it's different for LA8 and LA16). - Split scan() into two functions, one for scanning, one for adding a device. - Expand some variables and fields from uint8_t to uint16_t in order to support 16 channels. - Update the samplerate related functions to support the LA16's 200MHz. - Various other minor updates in order to better handle both device types. - Various error handling improvements and simplifications. - Also, replace time() with g_get_monotonic_time() everywhere. This also fixes bug #247 (which was related to incorrect handling of resources during scan and open of the device, which was exposed by PulseView allowing multiple consecutive scan/close/open calls).
This commit is contained in:
parent
b172c1301e
commit
00910580bb
|
@ -35,6 +35,10 @@ ATTRS{idVendor}=="16d0", ATTRS{idProduct}=="0498", MODE="664", GROUP="plugdev"
|
||||||
# CEM DT-8852
|
# CEM DT-8852
|
||||||
ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", MODE="664", GROUP="plugdev"
|
ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
|
# ChronoVu LA8 (new VID/PID)
|
||||||
|
# ChronoVu LA16 (new VID/PID)
|
||||||
|
ATTRS{idVendor}=="0403", ATTRS{idProduct}=="8867", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
# CWAV USBee AX
|
# CWAV USBee AX
|
||||||
# ARMFLY AX-Pro (clone of the CWAV USBee AX)
|
# ARMFLY AX-Pro (clone of the CWAV USBee AX)
|
||||||
# ARMFLY Mini-Logic (clone of the CWAV USBee AX)
|
# ARMFLY Mini-Logic (clone of the CWAV USBee AX)
|
||||||
|
@ -56,7 +60,8 @@ ATTRS{idVendor}=="08a9", ATTRS{idProduct}=="0009", MODE="664", GROUP="plugdev"
|
||||||
ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="8613", MODE="664", GROUP="plugdev"
|
ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="8613", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
# Dangerous Prototypes Buspirate (v3)
|
# Dangerous Prototypes Buspirate (v3)
|
||||||
# ChronoVu LA8
|
# ChronoVu LA8 (old VID/PID)
|
||||||
|
# ChronoVu LA16 (old VID/PID)
|
||||||
ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", MODE="664", GROUP="plugdev"
|
ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", MODE="664", GROUP="plugdev"
|
||||||
|
|
||||||
# Dangerous Prototypes Buspirate (v4)
|
# Dangerous Prototypes Buspirate (v4)
|
||||||
|
|
|
@ -23,28 +23,24 @@
|
||||||
SR_PRIV struct sr_dev_driver chronovu_la8_driver_info;
|
SR_PRIV struct sr_dev_driver chronovu_la8_driver_info;
|
||||||
static struct sr_dev_driver *di = &chronovu_la8_driver_info;
|
static struct sr_dev_driver *di = &chronovu_la8_driver_info;
|
||||||
|
|
||||||
/*
|
static const int32_t hwcaps[] = {
|
||||||
* This will be initialized via config_list()/SR_CONF_SAMPLERATE.
|
|
||||||
*
|
|
||||||
* Min: 1 sample per 0.01us -> sample time is 0.084s, samplerate 100MHz
|
|
||||||
* Max: 1 sample per 2.55us -> sample time is 21.391s, samplerate 392.15kHz
|
|
||||||
*/
|
|
||||||
SR_PRIV uint64_t cv_samplerates[255] = { 0 };
|
|
||||||
|
|
||||||
SR_PRIV const int32_t cv_hwcaps[] = {
|
|
||||||
SR_CONF_LOGIC_ANALYZER,
|
SR_CONF_LOGIC_ANALYZER,
|
||||||
SR_CONF_SAMPLERATE,
|
SR_CONF_SAMPLERATE,
|
||||||
SR_CONF_LIMIT_MSEC, /* TODO: Not yet implemented. */
|
SR_CONF_LIMIT_MSEC, /* TODO: Not yet implemented. */
|
||||||
SR_CONF_LIMIT_SAMPLES, /* TODO: Not yet implemented. */
|
SR_CONF_LIMIT_SAMPLES, /* TODO: Not yet implemented. */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/* The ChronoVu LA8/LA16 can have multiple VID/PID pairs. */
|
||||||
* The ChronoVu LA8 can have multiple PIDs. Older versions shipped with
|
static struct {
|
||||||
* a standard FTDI USB VID/PID of 0403:6001, newer ones have 0403:8867.
|
uint16_t vid;
|
||||||
*/
|
uint16_t pid;
|
||||||
static const uint16_t usb_pids[] = {
|
int model;
|
||||||
0x6001,
|
const char *iproduct;
|
||||||
0x8867,
|
} vid_pid[] = {
|
||||||
|
{ 0x0403, 0x6001, CHRONOVU_LA8, "ChronoVu LA8" },
|
||||||
|
{ 0x0403, 0x8867, CHRONOVU_LA8, "ChronoVu LA8" },
|
||||||
|
{ 0x0403, 0x6001, CHRONOVU_LA16, "ChronoVu LA16" },
|
||||||
|
{ 0x0403, 0x8867, CHRONOVU_LA16, "ChronoVu LA16" },
|
||||||
};
|
};
|
||||||
|
|
||||||
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);
|
||||||
|
@ -69,106 +65,127 @@ static int init(struct sr_context *sr_ctx)
|
||||||
return std_init(sr_ctx, di, LOG_PREFIX);
|
return std_init(sr_ctx, di, LOG_PREFIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
static GSList *scan(GSList *options)
|
static int add_device(int idx, int model, GSList **devices)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
|
unsigned int i;
|
||||||
struct sr_dev_inst *sdi;
|
struct sr_dev_inst *sdi;
|
||||||
struct sr_channel *ch;
|
|
||||||
struct drv_context *drvc;
|
struct drv_context *drvc;
|
||||||
struct dev_context *devc;
|
struct dev_context *devc;
|
||||||
GSList *devices;
|
struct sr_channel *ch;
|
||||||
unsigned int i;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
(void)options;
|
ret = SR_OK;
|
||||||
|
|
||||||
drvc = di->priv;
|
drvc = di->priv;
|
||||||
|
|
||||||
devices = NULL;
|
|
||||||
|
|
||||||
/* Allocate memory for our private device context. */
|
/* Allocate memory for our private device context. */
|
||||||
devc = g_try_malloc(sizeof(struct dev_context));
|
devc = g_try_malloc(sizeof(struct dev_context));
|
||||||
|
|
||||||
/* Set some sane defaults. */
|
/* Set some sane defaults. */
|
||||||
devc->ftdic = NULL;
|
devc->prof = &cv_profiles[model];
|
||||||
devc->cur_samplerate = SR_MHZ(100); /* 100MHz == max. samplerate */
|
devc->ftdic = NULL; /* Will be set in the open() API call. */
|
||||||
|
devc->cur_samplerate = 0; /* Set later (different for LA8/LA16). */
|
||||||
devc->limit_msec = 0;
|
devc->limit_msec = 0;
|
||||||
devc->limit_samples = 0;
|
devc->limit_samples = 0;
|
||||||
devc->cb_data = NULL;
|
devc->cb_data = NULL;
|
||||||
memset(devc->mangled_buf, 0, BS);
|
memset(devc->mangled_buf, 0, BS);
|
||||||
devc->final_buf = NULL;
|
devc->final_buf = NULL;
|
||||||
devc->trigger_pattern = 0x00; /* Value irrelevant, see trigger_mask. */
|
devc->trigger_pattern = 0x0000; /* Irrelevant, see trigger_mask. */
|
||||||
devc->trigger_mask = 0x00; /* All channels are "don't care". */
|
devc->trigger_mask = 0x0000; /* All channels: "don't care". */
|
||||||
devc->trigger_timeout = 10; /* Default to 10s trigger timeout. */
|
devc->trigger_edgemask = 0x0000; /* All channels: "state triggered". */
|
||||||
devc->trigger_found = 0;
|
devc->trigger_found = 0;
|
||||||
devc->done = 0;
|
devc->done = 0;
|
||||||
devc->block_counter = 0;
|
devc->block_counter = 0;
|
||||||
devc->divcount = 0; /* 10ns sample period == 100MHz samplerate */
|
devc->divcount = 0;
|
||||||
devc->usb_pid = 0;
|
devc->usb_vid = vid_pid[idx].vid;
|
||||||
|
devc->usb_pid = vid_pid[idx].pid;
|
||||||
|
memset(devc->samplerates, 0, sizeof(uint64_t) * 255);
|
||||||
|
|
||||||
/* Allocate memory where we'll store the de-mangled data. */
|
/* Allocate memory where we'll store the de-mangled data. */
|
||||||
if (!(devc->final_buf = g_try_malloc(SDRAM_SIZE))) {
|
if (!(devc->final_buf = g_try_malloc(SDRAM_SIZE))) {
|
||||||
sr_err("Failed to allocate memory for sample buffer.");
|
sr_err("Failed to allocate memory for sample buffer.");
|
||||||
|
ret = SR_ERR_MALLOC;
|
||||||
goto err_free_devc;
|
goto err_free_devc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate memory for the FTDI context (ftdic) and initialize it. */
|
/* We now know the device, set its max. samplerate as default. */
|
||||||
if (!(devc->ftdic = ftdi_new())) {
|
devc->cur_samplerate = devc->prof->max_samplerate;
|
||||||
sr_err("Failed to initialize libftdi.");
|
|
||||||
goto err_free_final_buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check for the device and temporarily open it. */
|
|
||||||
for (i = 0; i < ARRAY_SIZE(usb_pids); i++) {
|
|
||||||
sr_dbg("Probing for VID/PID %04x:%04x.", USB_VENDOR_ID,
|
|
||||||
usb_pids[i]);
|
|
||||||
ret = ftdi_usb_open_desc(devc->ftdic, USB_VENDOR_ID,
|
|
||||||
usb_pids[i], USB_DESCRIPTION, NULL);
|
|
||||||
if (ret == 0) {
|
|
||||||
sr_dbg("Found LA8 device (%04x:%04x).",
|
|
||||||
USB_VENDOR_ID, usb_pids[i]);
|
|
||||||
devc->usb_pid = usb_pids[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (devc->usb_pid == 0)
|
|
||||||
goto err_free_ftdic;
|
|
||||||
|
|
||||||
/* Register the device with libsigrok. */
|
/* Register the device with libsigrok. */
|
||||||
sdi = sr_dev_inst_new(0, SR_ST_INITIALIZING,
|
sdi = sr_dev_inst_new(0, SR_ST_INITIALIZING,
|
||||||
USB_VENDOR_NAME, USB_MODEL_NAME, USB_MODEL_VERSION);
|
"ChronoVu", devc->prof->modelname, NULL);
|
||||||
if (!sdi) {
|
if (!sdi) {
|
||||||
sr_err("Failed to create device instance.");
|
sr_err("Failed to create device instance.");
|
||||||
goto err_close_ftdic;
|
ret = SR_ERR;
|
||||||
|
goto err_free_final_buf;
|
||||||
}
|
}
|
||||||
sdi->driver = di;
|
sdi->driver = di;
|
||||||
sdi->priv = devc;
|
sdi->priv = devc;
|
||||||
|
|
||||||
for (i = 0; cv_channel_names[i]; i++) {
|
for (i = 0; i < devc->prof->num_channels; i++) {
|
||||||
if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE,
|
if (!(ch = sr_channel_new(i, SR_CHANNEL_LOGIC, TRUE,
|
||||||
cv_channel_names[i])))
|
cv_channel_names[i]))) {
|
||||||
return NULL;
|
ret = SR_ERR;
|
||||||
|
goto err_free_dev_inst;
|
||||||
|
}
|
||||||
sdi->channels = g_slist_append(sdi->channels, ch);
|
sdi->channels = g_slist_append(sdi->channels, ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
devices = g_slist_append(devices, sdi);
|
*devices = g_slist_append(*devices, sdi);
|
||||||
drvc->instances = g_slist_append(drvc->instances, sdi);
|
drvc->instances = g_slist_append(drvc->instances, sdi);
|
||||||
|
|
||||||
/* Close device. We'll reopen it again when we need it. */
|
return SR_OK;
|
||||||
(void) cv_close(devc); /* Log, but ignore errors. */
|
|
||||||
|
|
||||||
return devices;
|
err_free_dev_inst:
|
||||||
|
sr_dev_inst_free(sdi);
|
||||||
err_close_ftdic:
|
|
||||||
(void) cv_close(devc); /* Log, but ignore errors. */
|
|
||||||
err_free_ftdic:
|
|
||||||
ftdi_free(devc->ftdic); /* NOT free() or g_free()! */
|
|
||||||
err_free_final_buf:
|
err_free_final_buf:
|
||||||
g_free(devc->final_buf);
|
g_free(devc->final_buf);
|
||||||
err_free_devc:
|
err_free_devc:
|
||||||
g_free(devc);
|
g_free(devc);
|
||||||
err_free_nothing:
|
|
||||||
|
|
||||||
return NULL;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GSList *scan(GSList *options)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
unsigned int i;
|
||||||
|
GSList *devices;
|
||||||
|
struct ftdi_context *ftdic;
|
||||||
|
|
||||||
|
(void)options;
|
||||||
|
|
||||||
|
devices = NULL;
|
||||||
|
|
||||||
|
/* Allocate memory for the FTDI context and initialize it. */
|
||||||
|
if (!(ftdic = ftdi_new())) {
|
||||||
|
sr_err("Failed to initialize libftdi.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for LA8 and/or LA16 devices with various VID/PIDs. */
|
||||||
|
for (i = 0; i < ARRAY_SIZE(vid_pid); i++) {
|
||||||
|
ret = ftdi_usb_open_desc(ftdic, vid_pid[i].vid,
|
||||||
|
vid_pid[i].pid, vid_pid[i].iproduct, NULL);
|
||||||
|
if (ret < 0)
|
||||||
|
continue; /* No device found. */
|
||||||
|
|
||||||
|
sr_dbg("Found %s device (%04x:%04x).",
|
||||||
|
vid_pid[i].iproduct, vid_pid[i].vid, vid_pid[i].pid);
|
||||||
|
|
||||||
|
if ((ret = add_device(i, vid_pid[i].model, &devices)) < 0)
|
||||||
|
sr_dbg("Failed to add device: %d.", ret);
|
||||||
|
|
||||||
|
if ((ret = ftdi_usb_close(ftdic)) < 0)
|
||||||
|
sr_dbg("Failed to close FTDI device (%d): %s.",
|
||||||
|
ret, ftdi_get_error_string(ftdic));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close USB device, deinitialize and free the FTDI context. */
|
||||||
|
ftdi_free(ftdic);
|
||||||
|
ftdic = NULL;
|
||||||
|
|
||||||
|
return devices;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GSList *dev_list(void)
|
static GSList *dev_list(void)
|
||||||
|
@ -181,19 +198,26 @@ static int dev_open(struct sr_dev_inst *sdi)
|
||||||
struct dev_context *devc;
|
struct dev_context *devc;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
ret = SR_ERR;
|
||||||
|
|
||||||
if (!(devc = sdi->priv))
|
if (!(devc = sdi->priv))
|
||||||
return SR_ERR_BUG;
|
return SR_ERR_BUG;
|
||||||
|
|
||||||
sr_dbg("Opening LA8 device (%04x:%04x).", USB_VENDOR_ID,
|
/* Allocate memory for the FTDI context and initialize it. */
|
||||||
devc->usb_pid);
|
if (!(devc->ftdic = ftdi_new())) {
|
||||||
|
sr_err("Failed to initialize libftdi.");
|
||||||
|
return SR_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
sr_dbg("Opening %s device (%04x:%04x).", devc->prof->modelname,
|
||||||
|
devc->usb_vid, devc->usb_pid);
|
||||||
|
|
||||||
/* Open the device. */
|
/* Open the device. */
|
||||||
if ((ret = ftdi_usb_open_desc(devc->ftdic, USB_VENDOR_ID,
|
if ((ret = ftdi_usb_open_desc(devc->ftdic, devc->usb_vid,
|
||||||
devc->usb_pid, USB_DESCRIPTION, NULL)) < 0) {
|
devc->usb_pid, devc->prof->iproduct, NULL)) < 0) {
|
||||||
sr_err("Failed to open FTDI device (%d): %s.",
|
sr_err("Failed to open FTDI device (%d): %s.",
|
||||||
ret, ftdi_get_error_string(devc->ftdic));
|
ret, ftdi_get_error_string(devc->ftdic));
|
||||||
(void) cv_close_usb_reset_sequencer(devc); /* Ignore errors. */
|
goto err_ftdi_free;
|
||||||
return SR_ERR;
|
|
||||||
}
|
}
|
||||||
sr_dbg("Device opened successfully.");
|
sr_dbg("Device opened successfully.");
|
||||||
|
|
||||||
|
@ -201,8 +225,7 @@ static int dev_open(struct sr_dev_inst *sdi)
|
||||||
if ((ret = ftdi_usb_purge_buffers(devc->ftdic)) < 0) {
|
if ((ret = ftdi_usb_purge_buffers(devc->ftdic)) < 0) {
|
||||||
sr_err("Failed to purge FTDI buffers (%d): %s.",
|
sr_err("Failed to purge FTDI buffers (%d): %s.",
|
||||||
ret, ftdi_get_error_string(devc->ftdic));
|
ret, ftdi_get_error_string(devc->ftdic));
|
||||||
(void) cv_close_usb_reset_sequencer(devc); /* Ignore errors. */
|
goto err_ftdi_free;
|
||||||
goto err_dev_open_close_ftdic;
|
|
||||||
}
|
}
|
||||||
sr_dbg("FTDI buffers purged successfully.");
|
sr_dbg("FTDI buffers purged successfully.");
|
||||||
|
|
||||||
|
@ -210,8 +233,7 @@ static int dev_open(struct sr_dev_inst *sdi)
|
||||||
if ((ret = ftdi_setflowctrl(devc->ftdic, SIO_RTS_CTS_HS)) < 0) {
|
if ((ret = ftdi_setflowctrl(devc->ftdic, SIO_RTS_CTS_HS)) < 0) {
|
||||||
sr_err("Failed to enable FTDI flow control (%d): %s.",
|
sr_err("Failed to enable FTDI flow control (%d): %s.",
|
||||||
ret, ftdi_get_error_string(devc->ftdic));
|
ret, ftdi_get_error_string(devc->ftdic));
|
||||||
(void) cv_close_usb_reset_sequencer(devc); /* Ignore errors. */
|
goto err_ftdi_free;
|
||||||
goto err_dev_open_close_ftdic;
|
|
||||||
}
|
}
|
||||||
sr_dbg("FTDI flow control enabled successfully.");
|
sr_dbg("FTDI flow control enabled successfully.");
|
||||||
|
|
||||||
|
@ -222,24 +244,25 @@ static int dev_open(struct sr_dev_inst *sdi)
|
||||||
|
|
||||||
return SR_OK;
|
return SR_OK;
|
||||||
|
|
||||||
err_dev_open_close_ftdic:
|
err_ftdi_free:
|
||||||
(void) cv_close(devc); /* Log, but ignore errors. */
|
ftdi_free(devc->ftdic); /* Close device (if open), free FTDI context. */
|
||||||
return SR_ERR;
|
devc->ftdic = NULL;
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dev_close(struct sr_dev_inst *sdi)
|
static int dev_close(struct sr_dev_inst *sdi)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
struct dev_context *devc;
|
struct dev_context *devc;
|
||||||
|
|
||||||
|
if (sdi->status != SR_ST_ACTIVE)
|
||||||
|
return SR_OK;
|
||||||
|
|
||||||
devc = sdi->priv;
|
devc = sdi->priv;
|
||||||
|
|
||||||
if (sdi->status == SR_ST_ACTIVE) {
|
if (devc->ftdic && (ret = ftdi_usb_close(devc->ftdic)) < 0)
|
||||||
sr_dbg("Status ACTIVE, closing device.");
|
sr_err("Failed to close FTDI device (%d): %s.",
|
||||||
(void) cv_close_usb_reset_sequencer(devc); /* Ignore errors. */
|
ret, ftdi_get_error_string(devc->ftdic));
|
||||||
} else {
|
|
||||||
sr_spew("Status not ACTIVE, nothing to do.");
|
|
||||||
}
|
|
||||||
|
|
||||||
sdi->status = SR_ST_INACTIVE;
|
sdi->status = SR_ST_INACTIVE;
|
||||||
|
|
||||||
return SR_OK;
|
return SR_OK;
|
||||||
|
@ -285,7 +308,7 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi,
|
||||||
|
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case SR_CONF_SAMPLERATE:
|
case SR_CONF_SAMPLERATE:
|
||||||
if (set_samplerate(sdi, g_variant_get_uint64(data)) < 0)
|
if (cv_set_samplerate(sdi, g_variant_get_uint64(data)) < 0)
|
||||||
return SR_ERR;
|
return SR_ERR;
|
||||||
break;
|
break;
|
||||||
case SR_CONF_LIMIT_MSEC:
|
case SR_CONF_LIMIT_MSEC:
|
||||||
|
@ -310,21 +333,23 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
|
||||||
{
|
{
|
||||||
GVariant *gvar, *grange[2];
|
GVariant *gvar, *grange[2];
|
||||||
GVariantBuilder gvb;
|
GVariantBuilder gvb;
|
||||||
|
struct dev_context *devc;
|
||||||
|
|
||||||
(void)sdi;
|
|
||||||
(void)cg;
|
(void)cg;
|
||||||
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case SR_CONF_DEVICE_OPTIONS:
|
case SR_CONF_DEVICE_OPTIONS:
|
||||||
*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
|
*data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
|
||||||
cv_hwcaps, ARRAY_SIZE(cv_hwcaps),
|
hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
|
||||||
sizeof(int32_t));
|
|
||||||
break;
|
break;
|
||||||
case SR_CONF_SAMPLERATE:
|
case SR_CONF_SAMPLERATE:
|
||||||
cv_fill_samplerates_if_needed();
|
if (!sdi || !sdi->priv || !(devc = sdi->priv))
|
||||||
|
return SR_ERR_BUG;
|
||||||
|
cv_fill_samplerates_if_needed(sdi);
|
||||||
g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
|
g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}"));
|
||||||
gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
|
gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"),
|
||||||
cv_samplerates, ARRAY_SIZE(cv_samplerates),
|
devc->samplerates,
|
||||||
|
ARRAY_SIZE(devc->samplerates),
|
||||||
sizeof(uint64_t));
|
sizeof(uint64_t));
|
||||||
g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
|
g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar);
|
||||||
*data = g_variant_builder_end(&gvb);
|
*data = g_variant_builder_end(&gvb);
|
||||||
|
@ -335,7 +360,9 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
|
||||||
*data = g_variant_new_tuple(grange, 2);
|
*data = g_variant_new_tuple(grange, 2);
|
||||||
break;
|
break;
|
||||||
case SR_CONF_TRIGGER_TYPE:
|
case SR_CONF_TRIGGER_TYPE:
|
||||||
*data = g_variant_new_string(TRIGGER_TYPE);
|
if (!sdi || !sdi->priv || !(devc = sdi->priv) || !devc->prof)
|
||||||
|
return SR_ERR_BUG;
|
||||||
|
*data = g_variant_new_string(devc->prof->trigger_type);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return SR_ERR_NA;
|
return SR_ERR_NA;
|
||||||
|
@ -395,8 +422,8 @@ static int receive_data(int fd, int revents, void *cb_data)
|
||||||
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, void *cb_data)
|
||||||
{
|
{
|
||||||
struct dev_context *devc;
|
struct dev_context *devc;
|
||||||
uint8_t buf[4];
|
uint8_t buf[8];
|
||||||
int bytes_written;
|
int bytes_to_write, bytes_written;
|
||||||
|
|
||||||
if (sdi->status != SR_ST_ACTIVE)
|
if (sdi->status != SR_ST_ACTIVE)
|
||||||
return SR_ERR_DEV_CLOSED;
|
return SR_ERR_DEV_CLOSED;
|
||||||
|
@ -411,7 +438,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
|
||||||
return SR_ERR_BUG;
|
return SR_ERR_BUG;
|
||||||
}
|
}
|
||||||
|
|
||||||
devc->divcount = cv_samplerate_to_divcount(devc->cur_samplerate);
|
devc->divcount = cv_samplerate_to_divcount(sdi, devc->cur_samplerate);
|
||||||
if (devc->divcount == 0xff) {
|
if (devc->divcount == 0xff) {
|
||||||
sr_err("Invalid divcount/samplerate.");
|
sr_err("Invalid divcount/samplerate.");
|
||||||
return SR_ERR;
|
return SR_ERR;
|
||||||
|
@ -423,19 +450,29 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fill acquisition parameters into buf[]. */
|
/* Fill acquisition parameters into buf[]. */
|
||||||
buf[0] = devc->divcount;
|
if (devc->prof->model == CHRONOVU_LA8) {
|
||||||
buf[1] = 0xff; /* This byte must always be 0xff. */
|
buf[0] = devc->divcount;
|
||||||
buf[2] = devc->trigger_pattern;
|
buf[1] = 0xff; /* This byte must always be 0xff. */
|
||||||
buf[3] = devc->trigger_mask;
|
buf[2] = devc->trigger_pattern & 0xff;
|
||||||
|
buf[3] = devc->trigger_mask & 0xff;
|
||||||
|
bytes_to_write = 4;
|
||||||
|
} else {
|
||||||
|
buf[0] = devc->divcount;
|
||||||
|
buf[1] = 0xff; /* This byte must always be 0xff. */
|
||||||
|
buf[2] = (devc->trigger_pattern & 0xff00) >> 8; /* LSB */
|
||||||
|
buf[3] = (devc->trigger_pattern & 0x00ff) >> 0; /* MSB */
|
||||||
|
buf[4] = (devc->trigger_mask & 0xff00) >> 8; /* LSB */
|
||||||
|
buf[5] = (devc->trigger_mask & 0x00ff) >> 0; /* MSB */
|
||||||
|
buf[6] = (devc->trigger_edgemask & 0xff00) >> 8; /* LSB */
|
||||||
|
buf[7] = (devc->trigger_edgemask & 0x00ff) >> 0; /* MSB */
|
||||||
|
bytes_to_write = 8;
|
||||||
|
}
|
||||||
|
|
||||||
/* Start acquisition. */
|
/* Start acquisition. */
|
||||||
bytes_written = cv_write(devc, buf, 4);
|
bytes_written = cv_write(devc, buf, bytes_to_write);
|
||||||
|
|
||||||
if (bytes_written < 0) {
|
if (bytes_written < 0 || bytes_written != bytes_to_write) {
|
||||||
sr_err("Acquisition failed to start: %d.", bytes_written);
|
sr_err("Acquisition failed to start.");
|
||||||
return SR_ERR;
|
|
||||||
} else if (bytes_written != 4) {
|
|
||||||
sr_err("Acquisition failed to start: %d.", bytes_written);
|
|
||||||
return SR_ERR;
|
return SR_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -447,8 +484,8 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data)
|
||||||
std_session_send_df_header(cb_data, LOG_PREFIX);
|
std_session_send_df_header(cb_data, LOG_PREFIX);
|
||||||
|
|
||||||
/* Time when we should be done (for detecting trigger timeouts). */
|
/* Time when we should be done (for detecting trigger timeouts). */
|
||||||
devc->done = (devc->divcount + 1) * 0.08388608 + time(NULL)
|
devc->done = (devc->divcount + 1) * devc->prof->trigger_constant +
|
||||||
+ devc->trigger_timeout;
|
g_get_monotonic_time() + (10 * G_TIME_SPAN_SECOND);
|
||||||
devc->block_counter = 0;
|
devc->block_counter = 0;
|
||||||
devc->trigger_found = 0;
|
devc->trigger_found = 0;
|
||||||
|
|
||||||
|
|
|
@ -20,37 +20,56 @@
|
||||||
|
|
||||||
#include "protocol.h"
|
#include "protocol.h"
|
||||||
|
|
||||||
/* Channels are numbered 0-7. */
|
SR_PRIV const struct cv_profile cv_profiles[] = {
|
||||||
SR_PRIV const char *cv_channel_names[NUM_CHANNELS + 1] = {
|
{ CHRONOVU_LA8, "LA8", "ChronoVu LA8", 8, SR_MHZ(100), "01",
|
||||||
"0", "1", "2", "3", "4", "5", "6", "7",
|
0.8388608 },
|
||||||
NULL,
|
{ CHRONOVU_LA16, "LA16", "ChronoVu LA16", 16, SR_MHZ(200), "01rf",
|
||||||
|
0.042 },
|
||||||
|
{ 0, NULL, NULL, 0, 0, NULL, 0.0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
SR_PRIV void cv_fill_samplerates_if_needed(void)
|
/* LA8: channels are numbered 0-7. LA16: channels are numbered 0-15. */
|
||||||
|
SR_PRIV const char *cv_channel_names[] = {
|
||||||
|
"0", "1", "2", "3", "4", "5", "6", "7",
|
||||||
|
"8", "9", "10", "11", "12", "13", "14", "15",
|
||||||
|
};
|
||||||
|
|
||||||
|
static int close_usb_reset_sequencer(struct dev_context *devc);
|
||||||
|
|
||||||
|
SR_PRIV void cv_fill_samplerates_if_needed(const struct sr_dev_inst *sdi)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
struct dev_context *devc;
|
||||||
|
|
||||||
if (cv_samplerates[0] != 0)
|
devc = sdi->priv;
|
||||||
|
|
||||||
|
if (devc->samplerates[0] != 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (i = 0; i < 255; i++)
|
for (i = 0; i < 255; i++)
|
||||||
cv_samplerates[254 - i] = SR_MHZ(100) / (i + 1);
|
devc->samplerates[254 - i] = devc->prof->max_samplerate / (i + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the given samplerate is supported by the hardware.
|
* Check if the given samplerate is supported by the hardware.
|
||||||
*
|
*
|
||||||
|
* @param sdi Device instance.
|
||||||
* @param samplerate The samplerate (in Hz) to check.
|
* @param samplerate The samplerate (in Hz) to check.
|
||||||
|
*
|
||||||
* @return 1 if the samplerate is supported/valid, 0 otherwise.
|
* @return 1 if the samplerate is supported/valid, 0 otherwise.
|
||||||
*/
|
*/
|
||||||
static int is_valid_samplerate(uint64_t samplerate)
|
static int is_valid_samplerate(const struct sr_dev_inst *sdi,
|
||||||
|
uint64_t samplerate)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
struct dev_context *devc;
|
||||||
|
|
||||||
cv_fill_samplerates_if_needed();
|
devc = sdi->priv;
|
||||||
|
|
||||||
|
cv_fill_samplerates_if_needed(sdi);
|
||||||
|
|
||||||
for (i = 0; i < 255; i++) {
|
for (i = 0; i < 255; i++) {
|
||||||
if (cv_samplerates[i] == samplerate)
|
if (devc->samplerates[i] == samplerate)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,26 +81,41 @@ static int is_valid_samplerate(uint64_t samplerate)
|
||||||
/**
|
/**
|
||||||
* Convert a samplerate (in Hz) to the 'divcount' value the device wants.
|
* Convert a samplerate (in Hz) to the 'divcount' value the device wants.
|
||||||
*
|
*
|
||||||
* LA8 hardware: sample period = (divcount + 1) * 10ns.
|
* The divcount value can be 0x00 - 0xfe (0xff is not valid).
|
||||||
* Min. value for divcount: 0x00 (10ns sample period, 100MHz samplerate).
|
|
||||||
* Max. value for divcount: 0xfe (2550ns sample period, 392.15kHz samplerate).
|
|
||||||
*
|
*
|
||||||
|
* LA8:
|
||||||
|
* sample period = (divcount + 1) * 10ns.
|
||||||
|
* divcount = 0x00: 10ns period, 100MHz samplerate.
|
||||||
|
* divcount = 0xfe: 2550ns period, 392.15kHz samplerate.
|
||||||
|
*
|
||||||
|
* LA16:
|
||||||
|
* sample period = (divcount + 1) * 5ns.
|
||||||
|
* divcount = 0x00: 5ns period, 200MHz samplerate.
|
||||||
|
* divcount = 0xfe: 1275ns period, ~784.31kHz samplerate.
|
||||||
|
*
|
||||||
|
* @param sdi Device instance.
|
||||||
* @param samplerate The samplerate in Hz.
|
* @param samplerate The samplerate in Hz.
|
||||||
|
*
|
||||||
* @return The divcount value as needed by the hardware, or 0xff upon errors.
|
* @return The divcount value as needed by the hardware, or 0xff upon errors.
|
||||||
*/
|
*/
|
||||||
SR_PRIV uint8_t cv_samplerate_to_divcount(uint64_t samplerate)
|
SR_PRIV uint8_t cv_samplerate_to_divcount(const struct sr_dev_inst *sdi,
|
||||||
|
uint64_t samplerate)
|
||||||
{
|
{
|
||||||
|
struct dev_context *devc;
|
||||||
|
|
||||||
|
devc = sdi->priv;
|
||||||
|
|
||||||
if (samplerate == 0) {
|
if (samplerate == 0) {
|
||||||
sr_err("Can't convert invalid samplerate of 0 Hz.");
|
sr_err("Can't convert invalid samplerate of 0 Hz.");
|
||||||
return 0xff;
|
return 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_valid_samplerate(samplerate)) {
|
if (!is_valid_samplerate(sdi, samplerate)) {
|
||||||
sr_err("Can't get divcount, samplerate invalid.");
|
sr_err("Can't get divcount, samplerate invalid.");
|
||||||
return 0xff;
|
return 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (SR_MHZ(100) / samplerate) - 1;
|
return (devc->prof->max_samplerate / samplerate) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -100,22 +134,16 @@ SR_PRIV int cv_write(struct dev_context *devc, uint8_t *buf, int size)
|
||||||
|
|
||||||
/* Note: Caller ensures devc/devc->ftdic/buf != NULL and size > 0. */
|
/* Note: Caller ensures devc/devc->ftdic/buf != NULL and size > 0. */
|
||||||
|
|
||||||
if (!buf)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
|
|
||||||
if (size < 0)
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
|
|
||||||
bytes_written = ftdi_write_data(devc->ftdic, buf, size);
|
bytes_written = ftdi_write_data(devc->ftdic, buf, size);
|
||||||
|
|
||||||
if (bytes_written < 0) {
|
if (bytes_written < 0) {
|
||||||
sr_err("Failed to write data (%d): %s.",
|
sr_err("Failed to write data (%d): %s.",
|
||||||
bytes_written, ftdi_get_error_string(devc->ftdic));
|
bytes_written, ftdi_get_error_string(devc->ftdic));
|
||||||
(void) cv_close_usb_reset_sequencer(devc); /* Ignore errors. */
|
(void) close_usb_reset_sequencer(devc); /* Ignore errors. */
|
||||||
} else if (bytes_written != size) {
|
} else if (bytes_written != size) {
|
||||||
sr_err("Failed to write data, only %d/%d bytes written.",
|
sr_err("Failed to write data, only %d/%d bytes written.",
|
||||||
size, bytes_written);
|
size, bytes_written);
|
||||||
(void) cv_close_usb_reset_sequencer(devc); /* Ignore errors. */
|
(void) close_usb_reset_sequencer(devc); /* Ignore errors. */
|
||||||
}
|
}
|
||||||
|
|
||||||
return bytes_written;
|
return bytes_written;
|
||||||
|
@ -151,47 +179,20 @@ static int cv_read(struct dev_context *devc, uint8_t *buf, int size)
|
||||||
return bytes_read;
|
return bytes_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
SR_PRIV int cv_close(struct dev_context *devc)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!devc) {
|
|
||||||
sr_err("%s: devc was NULL.", __func__);
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!devc->ftdic) {
|
|
||||||
sr_err("%s: devc->ftdic was NULL.", __func__);
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((ret = ftdi_usb_close(devc->ftdic)) < 0) {
|
|
||||||
sr_err("%s: ftdi_usb_close: (%d) %s.",
|
|
||||||
__func__, ret, ftdi_get_error_string(devc->ftdic));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the USB port and reset the sequencer logic.
|
* Close the USB port and reset the sequencer logic.
|
||||||
*
|
*
|
||||||
* @param devc The struct containing private per-device-instance data.
|
* @param devc The struct containing private per-device-instance data.
|
||||||
|
*
|
||||||
* @return SR_OK upon success, SR_ERR_ARG upon invalid arguments.
|
* @return SR_OK upon success, SR_ERR_ARG upon invalid arguments.
|
||||||
*/
|
*/
|
||||||
SR_PRIV int cv_close_usb_reset_sequencer(struct dev_context *devc)
|
static int close_usb_reset_sequencer(struct dev_context *devc)
|
||||||
{
|
{
|
||||||
/* Magic sequence of bytes for resetting the sequencer logic. */
|
/* Magic sequence of bytes for resetting the sequencer logic. */
|
||||||
uint8_t buf[8] = {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
|
uint8_t buf[8] = {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!devc)
|
/* Note: Caller checked that devc and devc->ftdic != NULL. */
|
||||||
return SR_ERR_ARG;
|
|
||||||
|
|
||||||
if (!devc->ftdic) {
|
|
||||||
sr_err("devc->ftdic was NULL.");
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (devc->ftdic->usb_dev) {
|
if (devc->ftdic->usb_dev) {
|
||||||
/* Reset the sequencer logic, then wait 100ms. */
|
/* Reset the sequencer logic, then wait 100ms. */
|
||||||
|
@ -204,18 +205,18 @@ SR_PRIV int cv_close_usb_reset_sequencer(struct dev_context *devc)
|
||||||
|
|
||||||
/* Log errors, but ignore them (i.e., don't abort). */
|
/* Log errors, but ignore them (i.e., don't abort). */
|
||||||
if ((ret = ftdi_usb_purge_buffers(devc->ftdic)) < 0)
|
if ((ret = ftdi_usb_purge_buffers(devc->ftdic)) < 0)
|
||||||
sr_err("%s: ftdi_usb_purge_buffers: (%d) %s.",
|
sr_err("Failed to purge FTDI buffers (%d): %s.",
|
||||||
__func__, ret, ftdi_get_error_string(devc->ftdic));
|
ret, ftdi_get_error_string(devc->ftdic));
|
||||||
if ((ret = ftdi_usb_reset(devc->ftdic)) < 0)
|
if ((ret = ftdi_usb_reset(devc->ftdic)) < 0)
|
||||||
sr_err("%s: ftdi_usb_reset: (%d) %s.", __func__,
|
sr_err("Failed to reset FTDI device (%d): %s.",
|
||||||
ret, ftdi_get_error_string(devc->ftdic));
|
ret, ftdi_get_error_string(devc->ftdic));
|
||||||
if ((ret = ftdi_usb_close(devc->ftdic)) < 0)
|
if ((ret = ftdi_usb_close(devc->ftdic)) < 0)
|
||||||
sr_err("%s: ftdi_usb_close: (%d) %s.", __func__,
|
sr_err("Failed to close FTDI device (%d): %s.",
|
||||||
ret, ftdi_get_error_string(devc->ftdic));
|
ret, ftdi_get_error_string(devc->ftdic));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Close USB device, deinitialize and free the FTDI context. */
|
/* Close USB device, deinitialize and free the FTDI context. */
|
||||||
ftdi_free(devc->ftdic); /* Returns void. */
|
ftdi_free(devc->ftdic);
|
||||||
devc->ftdic = NULL;
|
devc->ftdic = NULL;
|
||||||
|
|
||||||
return SR_OK;
|
return SR_OK;
|
||||||
|
@ -227,23 +228,16 @@ SR_PRIV int cv_close_usb_reset_sequencer(struct dev_context *devc)
|
||||||
* A reset is required after a failed read/write operation or upon timeouts.
|
* A reset is required after a failed read/write operation or upon timeouts.
|
||||||
*
|
*
|
||||||
* @param devc The struct containing private per-device-instance data.
|
* @param devc The struct containing private per-device-instance data.
|
||||||
|
*
|
||||||
* @return SR_OK upon success, SR_ERR upon failure.
|
* @return SR_OK upon success, SR_ERR upon failure.
|
||||||
*/
|
*/
|
||||||
static int cv_reset(struct dev_context *devc)
|
static int reset_device(struct dev_context *devc)
|
||||||
{
|
{
|
||||||
uint8_t buf[BS];
|
uint8_t buf[BS];
|
||||||
time_t done, now;
|
gint64 done, now;
|
||||||
int bytes_read;
|
int bytes_read;
|
||||||
|
|
||||||
if (!devc) {
|
/* Note: Caller checked that devc and devc->ftdic != NULL. */
|
||||||
sr_err("%s: devc was NULL.", __func__);
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!devc->ftdic) {
|
|
||||||
sr_err("%s: devc->ftdic was NULL.", __func__);
|
|
||||||
return SR_ERR_ARG;
|
|
||||||
}
|
|
||||||
|
|
||||||
sr_dbg("Resetting the device.");
|
sr_dbg("Resetting the device.");
|
||||||
|
|
||||||
|
@ -251,15 +245,15 @@ static int cv_reset(struct dev_context *devc)
|
||||||
* Purge pending read data from the FTDI hardware FIFO until
|
* Purge pending read data from the FTDI hardware FIFO until
|
||||||
* no more data is left, or a timeout occurs (after 20s).
|
* no more data is left, or a timeout occurs (after 20s).
|
||||||
*/
|
*/
|
||||||
done = 20 + time(NULL);
|
done = (20 * G_TIME_SPAN_SECOND) + g_get_monotonic_time();
|
||||||
do {
|
do {
|
||||||
/* Try to read bytes until none are left (or errors occur). */
|
/* Try to read bytes until none are left (or errors occur). */
|
||||||
bytes_read = cv_read(devc, (uint8_t *)&buf, BS);
|
bytes_read = cv_read(devc, (uint8_t *)&buf, BS);
|
||||||
now = time(NULL);
|
now = g_get_monotonic_time();
|
||||||
} while ((done > now) && (bytes_read > 0));
|
} while ((done > now) && (bytes_read > 0));
|
||||||
|
|
||||||
/* Reset the sequencer logic and close the USB port. */
|
/* Reset the sequencer logic and close the USB port. */
|
||||||
(void) cv_close_usb_reset_sequencer(devc); /* Ignore errors. */
|
(void) close_usb_reset_sequencer(devc); /* Ignore errors. */
|
||||||
|
|
||||||
sr_dbg("Device reset finished.");
|
sr_dbg("Device reset finished.");
|
||||||
|
|
||||||
|
@ -271,12 +265,13 @@ SR_PRIV int cv_configure_channels(const struct sr_dev_inst *sdi)
|
||||||
struct dev_context *devc;
|
struct dev_context *devc;
|
||||||
const struct sr_channel *ch;
|
const struct sr_channel *ch;
|
||||||
const GSList *l;
|
const GSList *l;
|
||||||
uint8_t channel_bit;
|
uint16_t channel_bit;
|
||||||
char *tc;
|
char *tc;
|
||||||
|
|
||||||
devc = sdi->priv;
|
devc = sdi->priv;
|
||||||
devc->trigger_pattern = 0;
|
devc->trigger_pattern = 0x0000; /* Default to "low" trigger. */
|
||||||
devc->trigger_mask = 0; /* Default to "don't care" for all channels. */
|
devc->trigger_mask = 0x0000; /* Default to "don't care". */
|
||||||
|
devc->trigger_edgemask = 0x0000; /* Default to "state triggered". */
|
||||||
|
|
||||||
for (l = sdi->channels; l; l = l->next) {
|
for (l = sdi->channels; l; l = l->next) {
|
||||||
ch = (struct sr_channel *)l->data;
|
ch = (struct sr_channel *)l->data;
|
||||||
|
@ -295,32 +290,43 @@ SR_PRIV int cv_configure_channels(const struct sr_dev_inst *sdi)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Note: Must only be run if ch->trigger != NULL. */
|
/* Note: Must only be run if ch->trigger != NULL. */
|
||||||
if (ch->index < 0 || ch->index > 7) {
|
if (ch->index < 0 || ch->index > (int)devc->prof->num_channels - 1) {
|
||||||
sr_err("%s: Invalid channel index %d, must be "
|
sr_err("Invalid channel index %d, must be "
|
||||||
"between 0 and 7.", __func__, ch->index);
|
"between 0 and %d.", ch->index,
|
||||||
|
devc->prof->num_channels - 1);
|
||||||
return SR_ERR;
|
return SR_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
channel_bit = (1 << (ch->index));
|
channel_bit = (1 << (ch->index));
|
||||||
|
|
||||||
/* Configure the channel's trigger mask and trigger pattern. */
|
/* Configure the channel's trigger pattern/mask/edgemask. */
|
||||||
for (tc = ch->trigger; tc && *tc; tc++) {
|
for (tc = ch->trigger; tc && *tc; tc++) {
|
||||||
devc->trigger_mask |= channel_bit;
|
devc->trigger_mask |= channel_bit;
|
||||||
|
|
||||||
/* Sanity check, LA8 only supports low/high trigger. */
|
/* Sanity check, LA8 only supports low/high trigger. */
|
||||||
if (*tc != '0' && *tc != '1') {
|
if ((devc->prof->model == CHRONOVU_LA8) &&
|
||||||
sr_err("%s: Invalid trigger '%c', only "
|
(*tc != '0' && *tc != '1')) {
|
||||||
"'0'/'1' supported.", __func__, *tc);
|
sr_err("Invalid trigger '%c', only "
|
||||||
|
"'0'/'1' supported.", *tc);
|
||||||
return SR_ERR;
|
return SR_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*tc == '1')
|
/* state: 1 == high, edge: 1 == rising edge. */
|
||||||
|
if (*tc == '1' || *tc == 'r')
|
||||||
devc->trigger_pattern |= channel_bit;
|
devc->trigger_pattern |= channel_bit;
|
||||||
|
|
||||||
|
/* LA16 (but not LA8) supports edge triggering. */
|
||||||
|
if ((devc->prof->model == CHRONOVU_LA16)) {
|
||||||
|
if (*tc == 'r' || *tc == 'f')
|
||||||
|
devc->trigger_edgemask |= channel_bit;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sr_dbg("Trigger mask = 0x%x, trigger pattern = 0x%x.",
|
sr_dbg("Trigger pattern/mask/edgemask = 0x%04x / 0x%04x / 0x%04x.",
|
||||||
devc->trigger_mask, devc->trigger_pattern);
|
devc->trigger_pattern, devc->trigger_mask,
|
||||||
|
devc->trigger_edgemask);
|
||||||
|
|
||||||
return SR_OK;
|
return SR_OK;
|
||||||
}
|
}
|
||||||
|
@ -335,16 +341,15 @@ SR_PRIV int cv_set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate
|
||||||
|
|
||||||
sr_spew("Trying to set samplerate to %" PRIu64 "Hz.", samplerate);
|
sr_spew("Trying to set samplerate to %" PRIu64 "Hz.", samplerate);
|
||||||
|
|
||||||
cv_fill_samplerates_if_needed();
|
cv_fill_samplerates_if_needed(sdi);
|
||||||
|
|
||||||
/* Check if this is a samplerate supported by the hardware. */
|
/* Check if this is a samplerate supported by the hardware. */
|
||||||
if (!is_valid_samplerate(samplerate)) {
|
if (!is_valid_samplerate(sdi, samplerate)) {
|
||||||
sr_dbg("Failed to set invalid samplerate (%" PRIu64 "Hz).",
|
sr_dbg("Failed to set invalid samplerate (%" PRIu64 "Hz).",
|
||||||
samplerate);
|
samplerate);
|
||||||
return SR_ERR;
|
return SR_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set the new samplerate. */
|
|
||||||
devc->cur_samplerate = samplerate;
|
devc->cur_samplerate = samplerate;
|
||||||
|
|
||||||
sr_dbg("Samplerate set to %" PRIu64 "Hz.", devc->cur_samplerate);
|
sr_dbg("Samplerate set to %" PRIu64 "Hz.", devc->cur_samplerate);
|
||||||
|
@ -362,8 +367,8 @@ SR_PRIV int cv_set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate
|
||||||
*/
|
*/
|
||||||
SR_PRIV int cv_read_block(struct dev_context *devc)
|
SR_PRIV int cv_read_block(struct dev_context *devc)
|
||||||
{
|
{
|
||||||
int i, byte_offset, m, mi, p, index, bytes_read;
|
int i, byte_offset, m, mi, p, q, index, bytes_read;
|
||||||
time_t now;
|
gint64 now;
|
||||||
|
|
||||||
/* Note: Caller checked that devc and devc->ftdic != NULL. */
|
/* Note: Caller checked that devc and devc->ftdic != NULL. */
|
||||||
|
|
||||||
|
@ -375,16 +380,16 @@ SR_PRIV int cv_read_block(struct dev_context *devc)
|
||||||
if ((bytes_read == 0) && (devc->block_counter == 0)) {
|
if ((bytes_read == 0) && (devc->block_counter == 0)) {
|
||||||
do {
|
do {
|
||||||
sr_spew("Reading block 0 (again).");
|
sr_spew("Reading block 0 (again).");
|
||||||
|
/* Note: If bytes_read < 0 cv_read() will log errors. */
|
||||||
bytes_read = cv_read(devc, devc->mangled_buf, BS);
|
bytes_read = cv_read(devc, devc->mangled_buf, BS);
|
||||||
/* TODO: How to handle read errors here? */
|
now = g_get_monotonic_time();
|
||||||
now = time(NULL);
|
|
||||||
} while ((devc->done > now) && (bytes_read == 0));
|
} while ((devc->done > now) && (bytes_read == 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if block read was successful or a timeout occured. */
|
/* Check if block read was successful or a timeout occured. */
|
||||||
if (bytes_read != BS) {
|
if (bytes_read != BS) {
|
||||||
sr_err("Trigger timed out. Bytes read: %d.", bytes_read);
|
sr_err("Trigger timed out. Bytes read: %d.", bytes_read);
|
||||||
(void) cv_reset(devc); /* Ignore errors. */
|
(void) reset_device(devc); /* Ignore errors. */
|
||||||
return SR_ERR;
|
return SR_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,9 +399,16 @@ SR_PRIV int cv_read_block(struct dev_context *devc)
|
||||||
m = byte_offset / (1024 * 1024);
|
m = byte_offset / (1024 * 1024);
|
||||||
mi = m * (1024 * 1024);
|
mi = m * (1024 * 1024);
|
||||||
for (i = 0; i < BS; i++) {
|
for (i = 0; i < BS; i++) {
|
||||||
p = i & (1 << 0);
|
if (devc->prof->model == CHRONOVU_LA8) {
|
||||||
index = m * 2 + (((byte_offset + i) - mi) / 2) * 16;
|
p = i & (1 << 0);
|
||||||
index += (devc->divcount == 0) ? p : (1 - p);
|
index = m * 2 + (((byte_offset + i) - mi) / 2) * 16;
|
||||||
|
index += (devc->divcount == 0) ? p : (1 - p);
|
||||||
|
} else {
|
||||||
|
p = i & (1 << 0);
|
||||||
|
q = i & (1 << 1);
|
||||||
|
index = m * 4 + (((byte_offset + i) - mi) / 4) * 32;
|
||||||
|
index += q + (1 - p);
|
||||||
|
}
|
||||||
devc->final_buf[index] = devc->mangled_buf[i];
|
devc->final_buf[index] = devc->mangled_buf[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,7 +423,9 @@ SR_PRIV void cv_send_block_to_session_bus(struct dev_context *devc, int block)
|
||||||
struct sr_datafeed_logic logic;
|
struct sr_datafeed_logic logic;
|
||||||
int trigger_point; /* Relative trigger point (in this block). */
|
int trigger_point; /* Relative trigger point (in this block). */
|
||||||
|
|
||||||
/* Note: No sanity checks on devc/block, caller is responsible. */
|
/* Note: Caller ensures devc/devc->ftdic != NULL and block > 0. */
|
||||||
|
|
||||||
|
/* TODO: Implement/test proper trigger support for the LA16. */
|
||||||
|
|
||||||
/* Check if we can find the trigger condition in this block. */
|
/* Check if we can find the trigger condition in this block. */
|
||||||
trigger_point = -1;
|
trigger_point = -1;
|
||||||
|
@ -426,7 +440,7 @@ SR_PRIV void cv_send_block_to_session_bus(struct dev_context *devc, int block)
|
||||||
* no trigger conditions were specified by the user. In that
|
* no trigger conditions were specified by the user. In that
|
||||||
* case we don't want to send an SR_DF_TRIGGER packet at all.
|
* case we don't want to send an SR_DF_TRIGGER packet at all.
|
||||||
*/
|
*/
|
||||||
if (devc->trigger_mask == 0x00)
|
if (devc->trigger_mask == 0x0000)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
sample = *(devc->final_buf + (block * BS) + i);
|
sample = *(devc->final_buf + (block * BS) + i);
|
||||||
|
@ -446,7 +460,7 @@ SR_PRIV void cv_send_block_to_session_bus(struct dev_context *devc, int block)
|
||||||
packet.type = SR_DF_LOGIC;
|
packet.type = SR_DF_LOGIC;
|
||||||
packet.payload = &logic;
|
packet.payload = &logic;
|
||||||
logic.length = BS;
|
logic.length = BS;
|
||||||
logic.unitsize = 1;
|
logic.unitsize = devc->prof->num_channels / 8;
|
||||||
logic.data = devc->final_buf + (block * BS);
|
logic.data = devc->final_buf + (block * BS);
|
||||||
sr_session_send(devc->cb_data, &packet);
|
sr_session_send(devc->cb_data, &packet);
|
||||||
return;
|
return;
|
||||||
|
@ -469,7 +483,7 @@ SR_PRIV void cv_send_block_to_session_bus(struct dev_context *devc, int block)
|
||||||
packet.type = SR_DF_LOGIC;
|
packet.type = SR_DF_LOGIC;
|
||||||
packet.payload = &logic;
|
packet.payload = &logic;
|
||||||
logic.length = trigger_point;
|
logic.length = trigger_point;
|
||||||
logic.unitsize = 1;
|
logic.unitsize = devc->prof->num_channels / 8;
|
||||||
logic.data = devc->final_buf + (block * BS);
|
logic.data = devc->final_buf + (block * BS);
|
||||||
sr_session_send(devc->cb_data, &packet);
|
sr_session_send(devc->cb_data, &packet);
|
||||||
}
|
}
|
||||||
|
@ -490,7 +504,7 @@ SR_PRIV void cv_send_block_to_session_bus(struct dev_context *devc, int block)
|
||||||
packet.type = SR_DF_LOGIC;
|
packet.type = SR_DF_LOGIC;
|
||||||
packet.payload = &logic;
|
packet.payload = &logic;
|
||||||
logic.length = BS - trigger_point;
|
logic.length = BS - trigger_point;
|
||||||
logic.unitsize = 1;
|
logic.unitsize = devc->prof->num_channels / 8;
|
||||||
logic.data = devc->final_buf + (block * BS) + trigger_point;
|
logic.data = devc->final_buf + (block * BS) + trigger_point;
|
||||||
sr_session_send(devc->cb_data, &packet);
|
sr_session_send(devc->cb_data, &packet);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,27 +24,38 @@
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <ftdi.h>
|
#include <ftdi.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
#include "libsigrok.h"
|
#include "libsigrok.h"
|
||||||
#include "libsigrok-internal.h"
|
#include "libsigrok-internal.h"
|
||||||
|
|
||||||
#define LOG_PREFIX "chronovu-la8"
|
#define LOG_PREFIX "la8/la16"
|
||||||
|
|
||||||
#define USB_VENDOR_ID 0x0403
|
|
||||||
#define USB_DESCRIPTION "ChronoVu LA8"
|
|
||||||
#define USB_VENDOR_NAME "ChronoVu"
|
|
||||||
#define USB_MODEL_NAME "LA8"
|
|
||||||
#define USB_MODEL_VERSION ""
|
|
||||||
|
|
||||||
#define NUM_CHANNELS 8
|
|
||||||
#define TRIGGER_TYPE "01"
|
|
||||||
#define SDRAM_SIZE (8 * 1024 * 1024)
|
#define SDRAM_SIZE (8 * 1024 * 1024)
|
||||||
#define MAX_NUM_SAMPLES SDRAM_SIZE
|
#define MAX_NUM_SAMPLES SDRAM_SIZE
|
||||||
|
|
||||||
#define BS 4096 /* Block size */
|
#define BS 4096 /* Block size */
|
||||||
#define NUM_BLOCKS 2048 /* Number of blocks */
|
#define NUM_BLOCKS 2048 /* Number of blocks */
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CHRONOVU_LA8,
|
||||||
|
CHRONOVU_LA16,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cv_profile {
|
||||||
|
int model;
|
||||||
|
const char *modelname;
|
||||||
|
const char *iproduct; /* USB iProduct string */
|
||||||
|
unsigned int num_channels;
|
||||||
|
uint64_t max_samplerate;
|
||||||
|
const char *trigger_type;
|
||||||
|
float trigger_constant;
|
||||||
|
};
|
||||||
|
|
||||||
/* Private, per-device-instance driver context. */
|
/* Private, per-device-instance driver context. */
|
||||||
struct dev_context {
|
struct dev_context {
|
||||||
|
/** Device profile struct for this device. */
|
||||||
|
const struct cv_profile *prof;
|
||||||
|
|
||||||
/** FTDI device context (used by libftdi). */
|
/** FTDI device context (used by libftdi). */
|
||||||
struct ftdi_context *ftdic;
|
struct ftdi_context *ftdic;
|
||||||
|
|
||||||
|
@ -67,31 +78,39 @@ struct dev_context {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An 8MB buffer where we'll store the de-mangled samples.
|
* An 8MB buffer where we'll store the de-mangled samples.
|
||||||
* Format: Each sample is 1 byte, MSB is channel 7, LSB is channel 0.
|
* LA8: Each sample is 1 byte, MSB is channel 7, LSB is channel 0.
|
||||||
|
* LA16: Each sample is 2 bytes, MSB is channel 15, LSB is channel 0.
|
||||||
*/
|
*/
|
||||||
uint8_t *final_buf;
|
uint8_t *final_buf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trigger pattern (MSB = channel 7, LSB = channel 0).
|
* Trigger pattern.
|
||||||
* A 1 bit matches a high signal, 0 matches a low signal on a channel.
|
* A 1 bit matches a high signal, 0 matches a low signal on a channel.
|
||||||
* Only low/high triggers (but not e.g. rising/falling) are supported.
|
*
|
||||||
|
* If the resp. 'trigger_edgemask' bit is set, 1 means "rising edge",
|
||||||
|
* and 0 means "falling edge".
|
||||||
*/
|
*/
|
||||||
uint8_t trigger_pattern;
|
uint16_t trigger_pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trigger mask (MSB = channel 7, LSB = channel 0).
|
* Trigger mask.
|
||||||
* A 1 bit means "must match trigger_pattern", 0 means "don't care".
|
* A 1 bit means "must match trigger_pattern", 0 means "don't care".
|
||||||
*/
|
*/
|
||||||
uint8_t trigger_mask;
|
uint16_t trigger_mask;
|
||||||
|
|
||||||
/** Time (in seconds) before the trigger times out. */
|
/**
|
||||||
uint64_t trigger_timeout;
|
* Trigger edge mask.
|
||||||
|
* A 1 bit means "edge triggered", 0 means "state triggered".
|
||||||
|
*
|
||||||
|
* Edge triggering is only supported on LA16 (but not LA8).
|
||||||
|
*/
|
||||||
|
uint16_t trigger_edgemask;
|
||||||
|
|
||||||
/** Tells us whether an SR_DF_TRIGGER packet was already sent. */
|
/** Tells us whether an SR_DF_TRIGGER packet was already sent. */
|
||||||
int trigger_found;
|
int trigger_found;
|
||||||
|
|
||||||
/** Used for keeping track how much time has passed. */
|
/** Used for keeping track how much time has passed. */
|
||||||
time_t done;
|
gint64 done;
|
||||||
|
|
||||||
/** Counter/index for the data block to be read. */
|
/** Counter/index for the data block to be read. */
|
||||||
int block_counter;
|
int block_counter;
|
||||||
|
@ -99,19 +118,21 @@ struct dev_context {
|
||||||
/** The divcount value (determines the sample period). */
|
/** The divcount value (determines the sample period). */
|
||||||
uint8_t divcount;
|
uint8_t divcount;
|
||||||
|
|
||||||
/** This ChronoVu device's USB PID. */
|
/** This ChronoVu device's USB VID/PID. */
|
||||||
|
uint16_t usb_vid;
|
||||||
uint16_t usb_pid;
|
uint16_t usb_pid;
|
||||||
|
|
||||||
|
/** Samplerates supported by this device. */
|
||||||
|
uint64_t samplerates[255];
|
||||||
};
|
};
|
||||||
|
|
||||||
/* protocol.c */
|
/* protocol.c */
|
||||||
extern const int32_t cv_hwcaps[];
|
|
||||||
extern uint64_t cv_samplerates[];
|
|
||||||
extern SR_PRIV const char *cv_channel_names[];
|
extern SR_PRIV const char *cv_channel_names[];
|
||||||
SR_PRIV void cv_fill_samplerates_if_needed(void);
|
extern const struct cv_profile cv_profiles[];
|
||||||
SR_PRIV uint8_t cv_samplerate_to_divcount(uint64_t samplerate);
|
SR_PRIV void cv_fill_samplerates_if_needed(const struct sr_dev_inst *sdi);
|
||||||
|
SR_PRIV uint8_t cv_samplerate_to_divcount(const struct sr_dev_inst *sdi,
|
||||||
|
uint64_t samplerate);
|
||||||
SR_PRIV int cv_write(struct dev_context *devc, uint8_t *buf, int size);
|
SR_PRIV int cv_write(struct dev_context *devc, uint8_t *buf, int size);
|
||||||
SR_PRIV int cv_close(struct dev_context *devc);
|
|
||||||
SR_PRIV int cv_close_usb_reset_sequencer(struct dev_context *devc);
|
|
||||||
SR_PRIV int cv_configure_channels(const struct sr_dev_inst *sdi);
|
SR_PRIV int cv_configure_channels(const struct sr_dev_inst *sdi);
|
||||||
SR_PRIV int cv_set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate);
|
SR_PRIV int cv_set_samplerate(const struct sr_dev_inst *sdi, uint64_t samplerate);
|
||||||
SR_PRIV int cv_read_block(struct dev_context *devc);
|
SR_PRIV int cv_read_block(struct dev_context *devc);
|
||||||
|
|
Loading…
Reference in New Issue