Start of code base layout restructuring.
This commit is contained in:
commit
a1bb33afbd
|
@ -0,0 +1,47 @@
|
|||
##
|
||||
## This file is part of the sigrok project.
|
||||
##
|
||||
## Copyright (C) 2010 Bert Vermeulen <bert@biot.com>
|
||||
##
|
||||
## 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
|
||||
## the Free Software Foundation, either version 3 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
##
|
||||
|
||||
AM_CPPFLAGS = -I $(top_srcdir)/libsigrok \
|
||||
-DFIRMWARE_DIR='"$(FIRMWARE_DIR)"'
|
||||
|
||||
lib_LTLIBRARIES = libbackend.la
|
||||
|
||||
libbackend_la_SOURCES = \
|
||||
backend.c \
|
||||
datastore.c \
|
||||
device.c \
|
||||
session.c \
|
||||
hwplugin.c \
|
||||
filter.c \
|
||||
hardware/common/ezusb.c \
|
||||
hardware/common/serial.c \
|
||||
hardware/openbench-logic-sniffer/ols.c \
|
||||
hardware/saleae-logic/saleae-logic.c \
|
||||
hardware/zeroplus-logic-cube/analyzer.c \
|
||||
hardware/zeroplus-logic-cube/analyzer.h \
|
||||
hardware/zeroplus-logic-cube/gl_usb.c \
|
||||
hardware/zeroplus-logic-cube/gl_usb.h \
|
||||
hardware/zeroplus-logic-cube/zeroplus.c \
|
||||
output/output_text.c \
|
||||
output/output.c
|
||||
|
||||
libbackend_la_LIBADD = $(LIBOBJS)
|
||||
|
||||
include_HEADERS = sigrok.h
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* This file is part of the sigrok project.
|
||||
*
|
||||
* Copyright (C) 2010 Bert Vermeulen <bert@biot.com>
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
#include "sigrok.h"
|
||||
|
||||
int sigrok_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = load_hwplugins();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void sigrok_cleanup(void)
|
||||
{
|
||||
|
||||
device_close_all();
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* This file is part of the sigrok project.
|
||||
*
|
||||
* Copyright (C) 2010 Bert Vermeulen <bert@biot.com>
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
#include "sigrok.h"
|
||||
|
||||
static gpointer new_chunk(struct datastore **ds);
|
||||
|
||||
|
||||
|
||||
struct datastore *datastore_new(int unitsize)
|
||||
{
|
||||
struct datastore *ds;
|
||||
|
||||
ds = g_malloc(sizeof(struct datastore));
|
||||
ds->ds_unitsize = unitsize;
|
||||
ds->num_units = 0;
|
||||
ds->chunklist = NULL;
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
|
||||
void datastore_destroy(struct datastore *ds)
|
||||
{
|
||||
GSList *chunk;
|
||||
|
||||
for(chunk = ds->chunklist; chunk; chunk = chunk->next)
|
||||
g_free(chunk->data);
|
||||
g_slist_free(ds->chunklist);
|
||||
g_free(ds);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void datastore_put(struct datastore *ds, void *data, unsigned int length, int in_unitsize, int *probelist)
|
||||
{
|
||||
int capacity, stored, size, num_chunks, chunk_bytes_free, chunk_offset;
|
||||
gpointer chunk;
|
||||
|
||||
if(ds->chunklist == NULL)
|
||||
chunk = new_chunk(&ds);
|
||||
else
|
||||
chunk = g_slist_last(ds->chunklist)->data;
|
||||
num_chunks = g_slist_length(ds->chunklist);
|
||||
capacity = (num_chunks * DATASTORE_CHUNKSIZE);
|
||||
chunk_bytes_free = capacity - (ds->ds_unitsize * ds->num_units);
|
||||
chunk_offset = capacity - (DATASTORE_CHUNKSIZE * (num_chunks - 1)) - chunk_bytes_free;
|
||||
stored = 0;
|
||||
while(stored < length) {
|
||||
if(chunk_bytes_free == 0) {
|
||||
chunk = new_chunk(&ds);
|
||||
chunk_bytes_free = DATASTORE_CHUNKSIZE;
|
||||
chunk_offset = 0;
|
||||
}
|
||||
|
||||
if(length - stored > chunk_bytes_free)
|
||||
size = chunk_bytes_free;
|
||||
else
|
||||
/* last part, won't fill up this chunk */
|
||||
size = length - stored;
|
||||
memcpy(chunk + chunk_offset, data + stored, size);
|
||||
chunk_bytes_free -= size;
|
||||
stored += size;
|
||||
}
|
||||
ds->num_units += stored / ds->ds_unitsize;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static gpointer new_chunk(struct datastore **ds)
|
||||
{
|
||||
gpointer chunk;
|
||||
|
||||
chunk = g_malloc(DATASTORE_CHUNKSIZE * (*ds)->ds_unitsize);
|
||||
(*ds)->chunklist = g_slist_append((*ds)->chunklist, chunk);
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* This file is part of the sigrok project.
|
||||
*
|
||||
* Copyright (C) 2010 Bert Vermeulen <bert@biot.com>
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "sigrok.h"
|
||||
|
||||
void hexdump(unsigned char *address, int length)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < length; i++)
|
||||
{
|
||||
if((i & 0x0f) == 0)
|
||||
{
|
||||
if(i)
|
||||
printf("\n");
|
||||
printf("%.4x ", i);
|
||||
}
|
||||
printf("%.2x ", address[i]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
}
|
|
@ -0,0 +1,235 @@
|
|||
/*
|
||||
* This file is part of the sigrok project.
|
||||
*
|
||||
* Copyright (C) 2010 Bert Vermeulen <bert@biot.com>
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <glib.h>
|
||||
#include "sigrok.h"
|
||||
|
||||
extern struct sigrok_global *global;
|
||||
|
||||
GSList *devices = NULL;
|
||||
|
||||
|
||||
void device_scan(void)
|
||||
{
|
||||
GSList *plugins, *l;
|
||||
struct device_plugin *plugin;
|
||||
int num_devices, i;
|
||||
|
||||
plugins = list_hwplugins();
|
||||
|
||||
/* initialize all plugins first. Since the init() call may involve
|
||||
* a firmware upload and associated delay, we may as well get all
|
||||
* of these out of the way first.
|
||||
*/
|
||||
for(l = plugins; l; l = l->next)
|
||||
{
|
||||
plugin = l->data;
|
||||
g_message("initializing %s plugin", plugin->name);
|
||||
num_devices = plugin->init(NULL);
|
||||
for(i = 0; i < num_devices; i++)
|
||||
{
|
||||
device_new(plugin, i);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void device_close_all(void)
|
||||
{
|
||||
struct device *device;
|
||||
|
||||
while(devices)
|
||||
{
|
||||
device = devices->data;
|
||||
device->plugin->close(device->plugin_index);
|
||||
device_destroy(device);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
GSList *device_list(void)
|
||||
{
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
|
||||
struct device *device_new(struct device_plugin *plugin, int plugin_index)
|
||||
{
|
||||
struct device *device;
|
||||
int num_probes, i;
|
||||
char probename[16];
|
||||
|
||||
device = g_malloc0(sizeof(struct device));
|
||||
device->plugin = plugin;
|
||||
device->plugin_index = plugin_index;
|
||||
devices = g_slist_append(devices, device);
|
||||
|
||||
num_probes = (int) device->plugin->get_device_info(device->plugin_index, DI_NUM_PROBES);
|
||||
for(i = 0; i < num_probes; i++)
|
||||
{
|
||||
snprintf(probename, 16, "%d", i+1);
|
||||
device_probe_add(device, probename);
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
|
||||
void device_clear(struct device *device)
|
||||
{
|
||||
int probenum;
|
||||
|
||||
/* TODO: plugin-specific clear call? */
|
||||
|
||||
if(device->probes)
|
||||
for(probenum = 1; probenum <= g_slist_length(device->probes); probenum++)
|
||||
device_probe_clear(device, probenum);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void device_destroy(struct device *device)
|
||||
{
|
||||
int probenum;
|
||||
|
||||
/* TODO: plugin-specific destroy call, need to decrease refcount in plugin */
|
||||
|
||||
devices = g_slist_remove(devices, device);
|
||||
if(device->probes)
|
||||
{
|
||||
for(probenum = 1; probenum <= g_slist_length(device->probes); probenum++)
|
||||
device_probe_clear(device, probenum);
|
||||
g_slist_free(device->probes);
|
||||
}
|
||||
g_free(device);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void device_probe_clear(struct device *device, int probenum)
|
||||
{
|
||||
struct probe *p;
|
||||
|
||||
p = probe_find(device, probenum);
|
||||
if(!p)
|
||||
return;
|
||||
|
||||
if(p->name)
|
||||
{
|
||||
g_free(p->name);
|
||||
p->name = NULL;
|
||||
}
|
||||
|
||||
if(p->trigger)
|
||||
{
|
||||
g_free(p->trigger);
|
||||
p->trigger = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void device_probe_add(struct device *device, char *name)
|
||||
{
|
||||
struct probe *p;
|
||||
|
||||
p = g_malloc0(sizeof(struct probe));
|
||||
p->index = g_slist_length(device->probes) + 1;
|
||||
p->enabled = TRUE;
|
||||
p->name = g_strdup(name);
|
||||
p->trigger = NULL;
|
||||
device->probes = g_slist_append(device->probes, p);
|
||||
|
||||
}
|
||||
|
||||
|
||||
struct probe *probe_find(struct device *device, int probenum)
|
||||
{
|
||||
GSList *l;
|
||||
struct probe *p, *found_probe;
|
||||
|
||||
found_probe = NULL;
|
||||
for(l = device->probes; l; l = l->next)
|
||||
{
|
||||
p = l->data;
|
||||
if(p->index == probenum)
|
||||
{
|
||||
found_probe = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return found_probe;
|
||||
}
|
||||
|
||||
|
||||
void device_probe_name(struct device *device, int probenum, char *name)
|
||||
{
|
||||
struct probe *p;
|
||||
|
||||
p = probe_find(device, probenum);
|
||||
if(!p)
|
||||
return;
|
||||
|
||||
if(p->name)
|
||||
g_free(p->name);
|
||||
p->name = g_strdup(name);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void device_trigger_clear(struct device *device)
|
||||
{
|
||||
struct probe *p;
|
||||
int probenum;
|
||||
|
||||
if(device->probes)
|
||||
for(probenum = 1; probenum <= g_slist_length(device->probes); probenum++)
|
||||
{
|
||||
p = probe_find(device, probenum);
|
||||
if(p && p->trigger)
|
||||
{
|
||||
g_free(p->trigger);
|
||||
p->trigger = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void device_trigger_set(struct device *device, int probenum, char *trigger)
|
||||
{
|
||||
struct probe *p;
|
||||
|
||||
p = probe_find(device, probenum);
|
||||
if(!p)
|
||||
return;
|
||||
|
||||
if(p->trigger)
|
||||
g_free(p->trigger);
|
||||
|
||||
p->trigger = g_strdup(trigger);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* This file is part of the sigrok project.
|
||||
*
|
||||
* Copyright (C) 2010 Bert Vermeulen <bert@biot.com>
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "sigrok.h"
|
||||
|
||||
/* convert sample from maximum probes -- the way the hardware driver sent
|
||||
* it -- to a sample taking up only as much space as required, with
|
||||
* unused probes removed.
|
||||
*/
|
||||
int filter_probes(int in_unitsize, int out_unitsize, int *probelist,
|
||||
char *data_in, uint64_t length_in, char **data_out, uint64_t *length_out)
|
||||
{
|
||||
int num_enabled_probes, in_offset, out_offset, out_bit, i;
|
||||
uint64_t sample_in, sample_out;
|
||||
|
||||
*data_out = malloc(length_in);
|
||||
num_enabled_probes = 0;
|
||||
for(i = 0; probelist[i]; i++)
|
||||
num_enabled_probes++;
|
||||
|
||||
if(num_enabled_probes != in_unitsize * 8) {
|
||||
in_offset = out_offset = 0;
|
||||
while(in_offset <= length_in - in_unitsize) {
|
||||
memcpy(&sample_in, data_in + in_offset, in_unitsize);
|
||||
sample_out = 0;
|
||||
out_bit = 0;
|
||||
for(i = 0; probelist[i]; i++) {
|
||||
if(sample_in & (1 << (probelist[i]-1)))
|
||||
sample_out |= 1 << out_bit;
|
||||
out_bit++;
|
||||
}
|
||||
memcpy((*data_out) + out_offset, &sample_out, out_unitsize);
|
||||
in_offset += in_unitsize;
|
||||
out_offset += out_unitsize;
|
||||
}
|
||||
*length_out = out_offset;
|
||||
}
|
||||
else {
|
||||
/* all probes are used -- no need to compress anything */
|
||||
memcpy(*data_out, data_in, length_in);
|
||||
*length_out = length_in;
|
||||
}
|
||||
|
||||
return SIGROK_OK;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* This file is part of the sigrok project.
|
||||
*
|
||||
* Copyright (C) 2010 Bert Vermeulen <bert@biot.com>
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Helper functions for the Cypress EZ-USB / FX2 series chips.
|
||||
*/
|
||||
|
||||
#include <libusb.h>
|
||||
#include <glib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
|
||||
int ezusb_reset(struct libusb_device_handle *hdl, int set_clear)
|
||||
{
|
||||
int err;
|
||||
unsigned char buf[1];
|
||||
|
||||
g_message("setting CPU reset mode %s...", set_clear ? "on" : "off");
|
||||
buf[0] = set_clear ? 1 : 0;
|
||||
err = libusb_control_transfer(hdl, LIBUSB_REQUEST_TYPE_VENDOR, 0xa0,
|
||||
0xe600, 0x0000, buf, 1, 100);
|
||||
if(err < 0)
|
||||
g_warning("Unable to send control request: %d", err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int ezusb_install_firmware(libusb_device_handle *hdl, const char *filename)
|
||||
{
|
||||
FILE *fw;
|
||||
int offset, chunksize, err, result;
|
||||
unsigned char buf[4096];
|
||||
|
||||
g_message("Uploading firmware at %s", filename);
|
||||
if((fw = fopen(filename, "r")) == NULL)
|
||||
{
|
||||
g_warning("Unable to open firmware file %s for reading: %s", filename, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
result = 0;
|
||||
offset = 0;
|
||||
while(1)
|
||||
{
|
||||
chunksize = fread(buf, 1, 4096, fw);
|
||||
if(chunksize == 0)
|
||||
break;
|
||||
err = libusb_control_transfer(hdl, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT, 0xa0,
|
||||
offset, 0x0000, buf, chunksize, 100);
|
||||
if(err < 0)
|
||||
{
|
||||
g_warning("Unable to send firmware to device: %d", err);
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
g_message("Uploaded %d bytes", chunksize);
|
||||
offset += chunksize;
|
||||
}
|
||||
fclose(fw);
|
||||
g_message("Firmware upload done");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* This file is part of the sigrok project.
|
||||
*
|
||||
* Copyright (C) 2010 Bert Vermeulen <bert@biot.com>
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <glob.h>
|
||||
#include <glib.h>
|
||||
|
||||
|
||||
char *serial_port_glob[] = {
|
||||
/* Linux */
|
||||
"/dev/ttyS*",
|
||||
"/dev/ttyUSB*",
|
||||
"/dev/ttyACM*",
|
||||
/* MacOS X */
|
||||
"/dev/ttys*",
|
||||
"/dev/tty.USB-*",
|
||||
"/dev/tty.Modem-*",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
GSList *list_serial_ports(void)
|
||||
{
|
||||
glob_t g;
|
||||
GSList *ports;
|
||||
int i, j;
|
||||
|
||||
ports = NULL;
|
||||
for(i = 0; serial_port_glob[i]; i++)
|
||||
{
|
||||
if(!glob(serial_port_glob[i], 0, NULL, &g))
|
||||
{
|
||||
for(j = 0; j < g.gl_pathc; j++)
|
||||
ports = g_slist_append(ports, g_strdup(g.gl_pathv[j]));
|
||||
globfree(&g);
|
||||
}
|
||||
}
|
||||
|
||||
return ports;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* This file is part of the sigrok project.
|
||||
*
|
||||
* Copyright (C) 2010 Bert Vermeulen <bert@biot.com>
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "sigrok.h"
|
||||
|
||||
static int hw_init(char *deviceinfo)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
static int hw_opendev(int device_index)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void hw_closedev(int device_index)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void hw_cleanup(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
static char *hw_get_identifier(int device_index)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
static char *hw_get_device_info(int device_index, int device_info_id)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
static int hw_get_status(int device_index)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
static int *hw_get_capabilities(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
static int hw_set_configuration(int device_index, int capability, char *value)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
static int hw_start_acquisition(int device_index, gpointer session_device_id)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void hw_stop_acquisition(int device_index, gpointer session_device_id)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct device_plugin skeleton_plugin_info = {
|
||||
"skeleton",
|
||||
1,
|
||||
hw_init,
|
||||
hw_cleanup,
|
||||
|
||||
hw_opendev,
|
||||
hw_closedev,
|
||||
hw_get_device_info,
|
||||
hw_get_status,
|
||||
hw_get_capabilities,
|
||||
hw_set_configuration,
|
||||
hw_start_acquisition,
|
||||
hw_stop_acquisition
|
||||
};
|
||||
|
|
@ -0,0 +1,637 @@
|
|||
/*
|
||||
* This file is part of the sigrok project.
|
||||
*
|
||||
* Copyright (C) 2010 Bert Vermeulen <bert@biot.com>
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <inttypes.h>
|
||||
#include <glib.h>
|
||||
#include "sigrok.h"
|
||||
|
||||
#define NUM_PROBES 32
|
||||
#define NUM_TRIGGER_STAGES 4
|
||||
#define TRIGGER_TYPES "01"
|
||||
#define SERIAL_SPEED B115200
|
||||
/* TODO: SERIAL_ bits, parity, stop bit */
|
||||
#define CLOCK_RATE 100000000
|
||||
|
||||
|
||||
/* command opcodes */
|
||||
#define CMD_RESET 0x00
|
||||
#define CMD_ID 0x02
|
||||
#define CMD_SET_FLAGS 0x82
|
||||
#define CMD_SET_DIVIDER 0x80
|
||||
#define CMD_RUN 0x01
|
||||
#define CMD_CAPTURE_SIZE 0x81
|
||||
#define CMD_SET_TRIGGER_MASK_0 0xc0
|
||||
#define CMD_SET_TRIGGER_MASK_1 0xc4
|
||||
#define CMD_SET_TRIGGER_MASK_2 0xc8
|
||||
#define CMD_SET_TRIGGER_MASK_3 0xcc
|
||||
#define CMD_SET_TRIGGER_VALUE_0 0xc1
|
||||
#define CMD_SET_TRIGGER_VALUE_1 0xc5
|
||||
#define CMD_SET_TRIGGER_VALUE_2 0xc9
|
||||
#define CMD_SET_TRIGGER_VALUE_3 0xcd
|
||||
#define CMD_SET_TRIGGER_CONFIG_0 0xc2
|
||||
#define CMD_SET_TRIGGER_CONFIG_1 0xc6
|
||||
#define CMD_SET_TRIGGER_CONFIG_2 0xca
|
||||
#define CMD_SET_TRIGGER_CONFIG_3 0xce
|
||||
|
||||
/* bitmasks for CMD_FLAGS */
|
||||
#define FLAG_DEMUX 0x01
|
||||
#define FLAG_FILTER 0x02
|
||||
#define FLAG_CHANNELGROUP_1 0x04
|
||||
#define FLAG_CHANNELGROUP_2 0x08
|
||||
#define FLAG_CHANNELGROUP_3 0x10
|
||||
#define FLAG_CHANNELGROUP_4 0x20
|
||||
#define FLAG_CLOCK_EXTERNAL 0x40
|
||||
#define FLAG_CLOCK_INVERTED 0x80
|
||||
#define FLAG_RLE 0x0100
|
||||
|
||||
|
||||
static int capabilities[] = {
|
||||
HWCAP_LOGIC_ANALYZER,
|
||||
HWCAP_SAMPLERATE,
|
||||
HWCAP_CAPTURE_RATIO,
|
||||
HWCAP_LIMIT_SAMPLES,
|
||||
0
|
||||
};
|
||||
|
||||
static struct samplerates samplerates = {
|
||||
1,
|
||||
MHZ(200),
|
||||
1,
|
||||
0
|
||||
};
|
||||
|
||||
/* list of struct serial_device_instance */
|
||||
static GSList *device_instances = NULL;
|
||||
|
||||
/* current state of the flag register */
|
||||
uint32_t flag_reg = 0;
|
||||
|
||||
static uint64_t cur_samplerate = 0;
|
||||
static uint64_t limit_samples = 0;
|
||||
/* pre/post trigger capture ratio, in percentage. 0 means no pre-trigger data. */
|
||||
int capture_ratio = 0;
|
||||
static uint32_t probe_mask = 0, trigger_mask[4] = {0}, trigger_value[4] = {0};
|
||||
|
||||
|
||||
|
||||
int send_shortcommand(int fd, uint8_t command)
|
||||
{
|
||||
char buf[1];
|
||||
|
||||
g_message("ols: sending cmd 0x%.2x", command);
|
||||
buf[0] = command;
|
||||
if(write(fd, buf, 1) != 1)
|
||||
return SIGROK_NOK;
|
||||
|
||||
return SIGROK_OK;
|
||||
}
|
||||
|
||||
|
||||
int send_longcommand(int fd, uint8_t command, uint32_t data)
|
||||
{
|
||||
char buf[5];
|
||||
|
||||
g_message("ols: sending cmd 0x%.2x data 0x%.8x", command, data);
|
||||
buf[0] = command;
|
||||
buf[1] = data & 0xff;
|
||||
buf[2] = data & 0xff00 >> 8;
|
||||
buf[3] = data & 0xff0000 >> 16;
|
||||
buf[4] = data & 0xff000000 >> 24;
|
||||
if(write(fd, buf, 5) != 5)
|
||||
return SIGROK_NOK;
|
||||
|
||||
return SIGROK_OK;
|
||||
}
|
||||
|
||||
static int configure_probes(GSList *probes)
|
||||
{
|
||||
struct probe *probe;
|
||||
GSList *l;
|
||||
int probe_bit, changrp_mask, stage, i;
|
||||
char *tc;
|
||||
|
||||
probe_mask = 0;
|
||||
for(i = 0; i < NUM_TRIGGER_STAGES; i++)
|
||||
{
|
||||
trigger_mask[i] = 0;
|
||||
trigger_value[i] = 0;
|
||||
}
|
||||
|
||||
for(l = probes; l; l = l->next)
|
||||
{
|
||||
probe = (struct probe *) l->data;
|
||||
probe_bit = 1 << (probe->index - 1);
|
||||
probe_mask |= probe_bit;
|
||||
if(probe->trigger)
|
||||
{
|
||||
stage = 0;
|
||||
for(tc = probe->trigger; *tc; tc++)
|
||||
{
|
||||
trigger_mask[stage] |= probe_bit;
|
||||
if(*tc == '1')
|
||||
trigger_value[stage] |= probe_bit;
|
||||
stage++;
|
||||
if(stage > 3)
|
||||
{
|
||||
/* only supporting parallel mode, with up to 4 stages */
|
||||
return SIGROK_NOK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* enable/disable channel groups in the flag register according
|
||||
* to the probe mask we just made. The register stores them backwards,
|
||||
* hence shift right from 1000.
|
||||
*/
|
||||
changrp_mask = 0;
|
||||
for(i = 0; i < 4; i++)
|
||||
{
|
||||
if(probe_mask & (0xff << i))
|
||||
changrp_mask |= 8 >> i;
|
||||
}
|
||||
/* but the flag register wants them here */
|
||||
flag_reg |= changrp_mask << 2;
|
||||
|
||||
return SIGROK_OK;
|
||||
}
|
||||
|
||||
|
||||
static int hw_init(char *deviceinfo)
|
||||
{
|
||||
struct sigrok_device_instance *sdi;
|
||||
GSList *ports, *l;
|
||||
GPollFD *fds;
|
||||
struct termios term, *prev_termios;
|
||||
int devcnt, final_devcnt, num_ports, fd, i;
|
||||
char buf[8], **device_names;
|
||||
|
||||
if(deviceinfo)
|
||||
ports = g_slist_append(NULL, g_strdup(deviceinfo));
|
||||
else
|
||||
/* no specific device given, so scan all serial ports */
|
||||
ports = list_serial_ports();
|
||||
|
||||
num_ports = g_slist_length(ports);
|
||||
fds = g_malloc0(num_ports * sizeof(GPollFD));
|
||||
device_names = g_malloc(num_ports * (sizeof(char *)));
|
||||
prev_termios = g_malloc(num_ports * sizeof(struct termios));
|
||||
devcnt = 0;
|
||||
for(l = ports; l; l = l->next) {
|
||||
/* The discovery procedure is like this: first send the Reset command (0x00) 5 times,
|
||||
* since the device could be anywhere in a 5-byte command. Then send the ID command
|
||||
* (0x02). If the device responds with 4 bytes ("OLS1" or "SLA1"), we have a match.
|
||||
* Since it may take the device a while to respond at 115Kb/s, we do all the sending
|
||||
* first, then wait for all of them to respond with g_poll().
|
||||
*/
|
||||
fd = open(l->data, O_RDWR | O_NONBLOCK);
|
||||
if(fd != -1) {
|
||||
tcgetattr(fd, &prev_termios[devcnt]);
|
||||
tcgetattr(fd, &term);
|
||||
cfsetispeed(&term, SERIAL_SPEED);
|
||||
tcsetattr(fd, TCSADRAIN, &term);
|
||||
memset(buf, CMD_RESET, 5);
|
||||
buf[5] = CMD_ID;
|
||||
if(write(fd, buf, 6) == 6) {
|
||||
fds[devcnt].fd = fd;
|
||||
fds[devcnt].events = G_IO_IN;
|
||||
device_names[devcnt] = l->data;
|
||||
devcnt++;
|
||||
g_message("probed device %s", (char *) l->data);
|
||||
}
|
||||
else {
|
||||
/* restore port settings. of course, we've crapped all over the port. */
|
||||
tcsetattr(fd, TCSADRAIN, &prev_termios[devcnt]);
|
||||
g_free(l->data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 2ms should do it, that's enough time for 28 bytes to go over the bus */
|
||||
usleep(2000);
|
||||
|
||||
final_devcnt = 0;
|
||||
g_poll(fds, devcnt, 1);
|
||||
for(i = 0; i < devcnt; i++) {
|
||||
if(fds[i].revents == G_IO_IN) {
|
||||
if(read(fds[i].fd, buf, 4) == 4) {
|
||||
if(!strncmp(buf, "1SLO", 4) || !strncmp(buf, "1ALS", 4)) {
|
||||
if(!strncmp(buf, "1SLO", 4))
|
||||
sdi = sigrok_device_instance_new(final_devcnt, ST_INACTIVE,
|
||||
"Openbench", "Logic Sniffer", "v1.0");
|
||||
else
|
||||
sdi = sigrok_device_instance_new(final_devcnt, ST_INACTIVE,
|
||||
"Sump", "Logic Analyzer", "v1.0");
|
||||
sdi->serial = serial_device_instance_new(device_names[i], -1);
|
||||
device_instances = g_slist_append(device_instances, sdi);
|
||||
final_devcnt++;
|
||||
fds[i].fd = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(fds[i].fd != 0)
|
||||
tcsetattr(fds[i].fd, TCSADRAIN, &prev_termios[i]);
|
||||
close(fds[i].fd);
|
||||
}
|
||||
|
||||
g_free(fds);
|
||||
g_free(device_names);
|
||||
g_free(prev_termios);
|
||||
g_slist_free(ports);
|
||||
|
||||
return final_devcnt;
|
||||
}
|
||||
|
||||
|
||||
static int hw_opendev(int device_index)
|
||||
{
|
||||
struct sigrok_device_instance *sdi;
|
||||
|
||||
if(!(sdi = get_sigrok_device_instance(device_instances, device_index)))
|
||||
return SIGROK_NOK;
|
||||
|
||||
sdi->serial->fd = open(sdi->serial->port, O_RDWR);
|
||||
if(sdi->serial->fd == -1)
|
||||
return SIGROK_NOK;
|
||||
|
||||
sdi->status = ST_ACTIVE;
|
||||
|
||||
return SIGROK_OK;
|
||||
}
|
||||
|
||||
|
||||
static void hw_closedev(int device_index)
|
||||
{
|
||||
struct sigrok_device_instance *sdi;
|
||||
|
||||
if(!(sdi = get_sigrok_device_instance(device_instances, device_index)))
|
||||
return;
|
||||
|
||||
if(sdi->serial->fd != -1) {
|
||||
close(sdi->serial->fd);
|
||||
sdi->serial->fd = -1;
|
||||
sdi->status = ST_INACTIVE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void hw_cleanup(void)
|
||||
{
|
||||
GSList *l;
|
||||
struct sigrok_device_instance *sdi;
|
||||
|
||||
/* properly close all devices */
|
||||
for(l = device_instances; l; l = l->next) {
|
||||
sdi = l->data;
|
||||
if(sdi->serial->fd != -1)
|
||||
close(sdi->serial->fd);
|
||||
sigrok_device_instance_free(sdi);
|
||||
}
|
||||
g_slist_free(device_instances);
|
||||
device_instances = NULL;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void *hw_get_device_info(int device_index, int device_info_id)
|
||||
{
|
||||
struct sigrok_device_instance *sdi;
|
||||
void *info;
|
||||
|
||||
if( !(sdi = get_sigrok_device_instance(device_instances, device_index)) )
|
||||
return NULL;
|
||||
|
||||
info = NULL;
|
||||
switch(device_info_id)
|
||||
{
|
||||
case DI_INSTANCE:
|
||||
info = sdi;
|
||||
break;
|
||||
case DI_NUM_PROBES:
|
||||
info = GINT_TO_POINTER(NUM_PROBES);
|
||||
break;
|
||||
case DI_SAMPLERATES:
|
||||
info = &samplerates;
|
||||
break;
|
||||
case DI_TRIGGER_TYPES:
|
||||
info = (char *) TRIGGER_TYPES;
|
||||
break;
|
||||
case DI_CUR_SAMPLE_RATE:
|
||||
info = &cur_samplerate;
|
||||
break;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
static int hw_get_status(int device_index)
|
||||
{
|
||||
struct sigrok_device_instance *sdi;
|
||||
|
||||
if(!(sdi = get_sigrok_device_instance(device_instances, device_index)))
|
||||
return ST_NOT_FOUND;
|
||||
|
||||
return sdi->status;
|
||||
}
|
||||
|
||||
|
||||
static int *hw_get_capabilities(void)
|
||||
{
|
||||
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
|
||||
static int set_configuration_samplerate(struct sigrok_device_instance *sdi, uint64_t samplerate)
|
||||
{
|
||||
uint32_t divider;
|
||||
|
||||
if(samplerate < samplerates.low || samplerate > samplerates.high)
|
||||
return SIGROK_ERR_BADVALUE;
|
||||
|
||||
if(samplerate > CLOCK_RATE) {
|
||||
flag_reg |= FLAG_DEMUX;
|
||||
divider = (uint8_t) (CLOCK_RATE * 2 / samplerate) - 1;
|
||||
}
|
||||
else {
|
||||
flag_reg &= FLAG_DEMUX;
|
||||
divider = (uint8_t) (CLOCK_RATE / samplerate) - 1;
|
||||
}
|
||||
divider <<= 8;
|
||||
|
||||
g_message("setting samplerate to %"PRIu64" Hz (divider %u, demux %s)", samplerate, divider,
|
||||
flag_reg & FLAG_DEMUX ? "on" : "off");
|
||||
if(send_longcommand(sdi->serial->fd, CMD_SET_DIVIDER, divider) != SIGROK_OK)
|
||||
return SIGROK_NOK;
|
||||
cur_samplerate = samplerate;
|
||||
|
||||
return SIGROK_OK;
|
||||
}
|
||||
|
||||
|
||||
static int hw_set_configuration(int device_index, int capability, void *value)
|
||||
{
|
||||
struct sigrok_device_instance *sdi;
|
||||
int ret;
|
||||
uint64_t *tmp_u64;
|
||||
|
||||
if(!(sdi = get_sigrok_device_instance(device_instances, device_index)))
|
||||
return SIGROK_NOK;
|
||||
|
||||
if(sdi->status != ST_ACTIVE)
|
||||
return SIGROK_NOK;
|
||||
|
||||
if(capability == HWCAP_SAMPLERATE) {
|
||||
tmp_u64 = value;
|
||||
ret = set_configuration_samplerate(sdi, *tmp_u64);
|
||||
}
|
||||
else if(capability == HWCAP_PROBECONFIG)
|
||||
ret = configure_probes( (GSList *) value);
|
||||
else if(capability == HWCAP_LIMIT_SAMPLES) {
|
||||
limit_samples = strtoull(value, NULL, 10);
|
||||
ret = SIGROK_OK;
|
||||
}
|
||||
else if(capability == HWCAP_CAPTURE_RATIO) {
|
||||
capture_ratio = strtol(value, NULL, 10);
|
||||
if(capture_ratio < 0 || capture_ratio > 100) {
|
||||
capture_ratio = 0;
|
||||
ret = SIGROK_NOK;
|
||||
}
|
||||
else
|
||||
ret = SIGROK_OK;
|
||||
}
|
||||
else
|
||||
ret = SIGROK_NOK;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int receive_data(int fd, int revents, void *user_data)
|
||||
{
|
||||
static int num_transfers = 0;
|
||||
static int num_bytes = 0;
|
||||
static char last_sample[4] = {0xff};
|
||||
static unsigned char sample[4];
|
||||
int count, buflen, i;
|
||||
struct datafeed_packet packet;
|
||||
unsigned char byte, *buffer;
|
||||
|
||||
if(num_transfers++ == 0) {
|
||||
/* first time round, means the device started sending data, and will not
|
||||
* stop until done. if it stops sending for longer than it takes to send
|
||||
* a byte, that means it's finished. we'll double that to 30ms to be sure...
|
||||
*/
|
||||
source_remove(fd);
|
||||
source_add(fd, G_IO_IN, 30, receive_data, user_data);
|
||||
}
|
||||
|
||||
/* TODO: / 4 depends on # of channels used */
|
||||
if(revents == G_IO_IN && num_transfers / 4 <= limit_samples){
|
||||
if(read(fd, &byte, 1) != 1)
|
||||
return FALSE;
|
||||
|
||||
sample[num_bytes++] = byte;
|
||||
if(num_bytes == 4) {
|
||||
g_message("got sample 0x%.2x%.2x%.2x%.2x", sample[3], sample[2], sample[1], sample[0]);
|
||||
/* got a full sample */
|
||||
if(flag_reg & FLAG_RLE) {
|
||||
/* in RLE mode -1 should never come in as a sample, because
|
||||
* bit 31 is the "count" flag */
|
||||
/* TODO: endianness may be wrong here, could be sample[3] */
|
||||
if(sample[0] & 0x80 && !(last_sample[0] & 0x80)) {
|
||||
count = (int) (*sample) & 0x7fffffff;
|
||||
buffer = g_malloc(count);
|
||||
buflen = 0;
|
||||
for(i = 0; i < count; i++)
|
||||
{
|
||||
memcpy(buffer + buflen , last_sample, 4);
|
||||
buflen += 4;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* just a single sample, next sample will probably be a count
|
||||
* referring to this -- but this one is still a part of the stream
|
||||
*/
|
||||
buffer = sample;
|
||||
buflen = 4;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* no compression */
|
||||
buffer = sample;
|
||||
buflen = 4;
|
||||
}
|
||||
|
||||
/* send it all to the session bus */
|
||||
packet.type = DF_LOGIC32;
|
||||
packet.length = buflen;
|
||||
packet.payload = buffer;
|
||||
session_bus(user_data, &packet);
|
||||
if(buffer == sample)
|
||||
memcpy(last_sample, buffer, 4);
|
||||
else
|
||||
g_free(buffer);
|
||||
|
||||
num_bytes = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* this is the main loop telling us a timeout was reached -- we're done */
|
||||
tcflush(fd, TCIOFLUSH);
|
||||
close(fd);
|
||||
packet.type = DF_END;
|
||||
packet.length = 0;
|
||||
session_bus(user_data, &packet);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static int hw_start_acquisition(int device_index, gpointer session_device_id)
|
||||
{
|
||||
struct datafeed_packet *packet;
|
||||
struct datafeed_header *header;
|
||||
struct sigrok_device_instance *sdi;
|
||||
uint32_t data;
|
||||
|
||||
if(!(sdi = get_sigrok_device_instance(device_instances, device_index)))
|
||||
return SIGROK_NOK;
|
||||
|
||||
if(sdi->status != ST_ACTIVE)
|
||||
return SIGROK_NOK;
|
||||
|
||||
/* reset again */
|
||||
if(send_longcommand(sdi->serial->fd, CMD_RESET, 0) != SIGROK_OK)
|
||||
return SIGROK_NOK;
|
||||
|
||||
/* send flag register */
|
||||
data = flag_reg << 24;
|
||||
if(send_longcommand(sdi->serial->fd, CMD_SET_FLAGS, data) != SIGROK_OK)
|
||||
return SIGROK_NOK;
|
||||
|
||||
/* send sample limit and pre/post-trigger capture ratio */
|
||||
data = limit_samples / 4 << 16;
|
||||
if(capture_ratio)
|
||||
data |= (limit_samples - (limit_samples / 100 * capture_ratio)) / 4;
|
||||
data = 0x00190019;
|
||||
if(send_longcommand(sdi->serial->fd, CMD_CAPTURE_SIZE, data) != SIGROK_OK)
|
||||
return SIGROK_NOK;
|
||||
|
||||
/* trigger masks */
|
||||
if(send_longcommand(sdi->serial->fd, CMD_SET_TRIGGER_MASK_0, trigger_mask[0]) != SIGROK_OK)
|
||||
return SIGROK_NOK;
|
||||
// if(send_longcommand(sdi->serial->fd, CMD_SET_TRIGGER_MASK_1, trigger_mask[1]) != SIGROK_OK)
|
||||
// return SIGROK_NOK;
|
||||
// if(send_longcommand(sdi->serial->fd, CMD_SET_TRIGGER_MASK_2, trigger_mask[2]) != SIGROK_OK)
|
||||
// return SIGROK_NOK;
|
||||
// if(send_longcommand(sdi->serial->fd, CMD_SET_TRIGGER_MASK_3, trigger_mask[3]) != SIGROK_OK)
|
||||
// return SIGROK_NOK;
|
||||
|
||||
if(send_longcommand(sdi->serial->fd, CMD_SET_TRIGGER_VALUE_0, trigger_value[0]) != SIGROK_OK)
|
||||
return SIGROK_NOK;
|
||||
// if(send_longcommand(sdi->serial->fd, CMD_SET_TRIGGER_VALUE_1, trigger_value[1]) != SIGROK_OK)
|
||||
// return SIGROK_NOK;
|
||||
// if(send_longcommand(sdi->serial->fd, CMD_SET_TRIGGER_VALUE_2, trigger_value[2]) != SIGROK_OK)
|
||||
// return SIGROK_NOK;
|
||||
// if(send_longcommand(sdi->serial->fd, CMD_SET_TRIGGER_VALUE_3, trigger_value[3]) != SIGROK_OK)
|
||||
// return SIGROK_NOK;
|
||||
|
||||
/* trigger configuration */
|
||||
/* TODO: the start flag should only be on the last used stage I think... */
|
||||
if(send_longcommand(sdi->serial->fd, CMD_SET_TRIGGER_CONFIG_0, 0x00000008) != SIGROK_OK)
|
||||
return SIGROK_NOK;
|
||||
// if(send_longcommand(sdi->serial->fd, CMD_SET_TRIGGER_CONFIG_1, 0x00000000) != SIGROK_OK)
|
||||
// return SIGROK_NOK;
|
||||
// if(send_longcommand(sdi->serial->fd, CMD_SET_TRIGGER_CONFIG_2, 0x00000000) != SIGROK_OK)
|
||||
// return SIGROK_NOK;
|
||||
// if(send_longcommand(sdi->serial->fd, CMD_SET_TRIGGER_CONFIG_3, 0x00000000) != SIGROK_OK)
|
||||
// return SIGROK_NOK;
|
||||
|
||||
set_configuration_samplerate(sdi, cur_samplerate);
|
||||
|
||||
/* start acquisition on the device */
|
||||
if(send_shortcommand(sdi->serial->fd, CMD_RUN) != SIGROK_OK)
|
||||
return SIGROK_NOK;
|
||||
|
||||
source_add(sdi->serial->fd, G_IO_IN, -1, receive_data, session_device_id);
|
||||
|
||||
/* send header packet to the session bus */
|
||||
packet = g_malloc(sizeof(struct datafeed_packet));
|
||||
header = g_malloc(sizeof(struct datafeed_header));
|
||||
if(!packet || !header)
|
||||
return SIGROK_NOK;
|
||||
packet->type = DF_HEADER;
|
||||
packet->length = sizeof(struct datafeed_header);
|
||||
packet->payload = (unsigned char *) header;
|
||||
header->feed_version = 1;
|
||||
gettimeofday(&header->starttime, NULL);
|
||||
header->rate = cur_samplerate;
|
||||
header->protocol_id = PROTO_RAW;
|
||||
header->num_probes = NUM_PROBES;
|
||||
session_bus(session_device_id, packet);
|
||||
g_free(header);
|
||||
g_free(packet);
|
||||
|
||||
return SIGROK_OK;
|
||||
}
|
||||
|
||||
|
||||
static void hw_stop_acquisition(int device_index, gpointer session_device_id)
|
||||
{
|
||||
struct datafeed_packet packet;
|
||||
|
||||
packet.type = DF_END;
|
||||
packet.length = 0;
|
||||
session_bus(session_device_id, &packet);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct device_plugin ols_plugin_info = {
|
||||
"sump",
|
||||
1,
|
||||
hw_init,
|
||||
hw_cleanup,
|
||||
|
||||
hw_opendev,
|
||||
hw_closedev,
|
||||
hw_get_device_info,
|
||||
hw_get_status,
|
||||
hw_get_capabilities,
|
||||
hw_set_configuration,
|
||||
hw_start_acquisition,
|
||||
hw_stop_acquisition
|
||||
};
|
||||
|
|
@ -0,0 +1,835 @@
|
|||
/*
|
||||
* This file is part of the sigrok project.
|
||||
*
|
||||
* Copyright (C) 2010 Bert Vermeulen <bert@biot.com>
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
#include <inttypes.h>
|
||||
#include <glib.h>
|
||||
#include <libusb.h>
|
||||
#include "config.h"
|
||||
#include "sigrok.h"
|
||||
|
||||
#define USB_VENDOR 0x0925
|
||||
#define USB_PRODUCT 0x3881
|
||||
#define USB_VENDOR_NAME "Saleae"
|
||||
#define USB_MODEL_NAME "Logic"
|
||||
#define USB_MODEL_VERSION ""
|
||||
|
||||
#define USB_INTERFACE 0
|
||||
#define USB_CONFIGURATION 1
|
||||
#define NUM_PROBES 8
|
||||
#define NUM_TRIGGER_STAGES 4
|
||||
#define TRIGGER_TYPES "01"
|
||||
#define FIRMWARE FIRMWARE_DIR "/saleae-logic.firmware"
|
||||
|
||||
/* delay in ms */
|
||||
#define FIRMWARE_RENUM_DELAY 2000
|
||||
#define NUM_SIMUL_TRANSFERS 10
|
||||
#define MAX_EMPTY_TRANSFERS NUM_SIMUL_TRANSFERS * 2
|
||||
|
||||
/* software trigger implementation: positive values indicate trigger stage */
|
||||
#define TRIGGER_FIRED -1
|
||||
|
||||
|
||||
/* there is only one model Saleae Logic, and this is what it supports */
|
||||
static int capabilities[] = {
|
||||
HWCAP_LOGIC_ANALYZER,
|
||||
HWCAP_SAMPLERATE,
|
||||
|
||||
/* these are really implemented in the driver, not the hardware */
|
||||
HWCAP_LIMIT_SAMPLES,
|
||||
0
|
||||
};
|
||||
|
||||
/* list of struct sigrok_device_instance, maintained by opendev() and closedev() */
|
||||
static GSList *device_instances = NULL;
|
||||
|
||||
/* since we can't keep track of a Saleae Logic device after upgrading the
|
||||
* firmware -- it re-enumerates into a different device address after the
|
||||
* upgrade -- this is like a global lock. No device will open until a proper
|
||||
* delay after the last device was upgraded.
|
||||
*/
|
||||
GTimeVal firmware_updated = {0};
|
||||
|
||||
static libusb_context *usb_context = NULL;
|
||||
|
||||
static uint64_t supported_samplerates[] = {
|
||||
KHZ(200),
|
||||
KHZ(250),
|
||||
KHZ(500),
|
||||
MHZ(1),
|
||||
MHZ(2),
|
||||
MHZ(4),
|
||||
MHZ(8),
|
||||
MHZ(12),
|
||||
MHZ(16),
|
||||
MHZ(24),
|
||||
0
|
||||
};
|
||||
|
||||
static struct samplerates samplerates = {
|
||||
KHZ(200),
|
||||
MHZ(24),
|
||||
0,
|
||||
supported_samplerates
|
||||
};
|
||||
|
||||
/* TODO: all of these should go in a device-specific struct */
|
||||
static uint64_t cur_sample_rate = 0;
|
||||
static uint64_t limit_samples = 0;
|
||||
static uint8_t probe_mask = 0, \
|
||||
trigger_mask[NUM_TRIGGER_STAGES] = {0}, \
|
||||
trigger_value[NUM_TRIGGER_STAGES] = {0}, \
|
||||
trigger_buffer[NUM_TRIGGER_STAGES] = {0};;
|
||||
int trigger_stage = TRIGGER_FIRED;
|
||||
|
||||
|
||||
static int hw_set_configuration(int device_index, int capability, void *value);
|
||||
|
||||
|
||||
/* returns 1 if the device's configuration profile match the Logic firmware's
|
||||
* configuration, 0 otherwise
|
||||
*/
|
||||
int check_conf_profile(libusb_device *dev)
|
||||
{
|
||||
struct libusb_device_descriptor des;
|
||||
struct libusb_config_descriptor *conf_dsc;
|
||||
const struct libusb_interface_descriptor *intf_dsc;
|
||||
int ret;
|
||||
|
||||
ret = -1;
|
||||
conf_dsc = NULL;
|
||||
while(ret == -1)
|
||||
{
|
||||
/* assume it's not a Saleae Logic unless proven wrong */
|
||||
ret = 0;
|
||||
|
||||
if(libusb_get_device_descriptor(dev, &des) != 0)
|
||||
break;
|
||||
|
||||
if(des.bNumConfigurations != 1)
|
||||
/* need exactly 1 configuration */
|
||||
break;
|
||||
|
||||
if(libusb_get_config_descriptor(dev, 0, &conf_dsc) != 0)
|
||||
break;
|
||||
|
||||
if(conf_dsc->bNumInterfaces != 1)
|
||||
/* need exactly 1 interface */
|
||||
break;
|
||||
|
||||
if(conf_dsc->interface[0].num_altsetting != 1)
|
||||
/* need just one alternate setting */
|
||||
break;
|
||||
|
||||
intf_dsc = &(conf_dsc->interface[0].altsetting[0]);
|
||||
if(intf_dsc->bNumEndpoints != 2)
|
||||
/* need 2 endpoints */
|
||||
break;
|
||||
|
||||
if((intf_dsc->endpoint[0].bEndpointAddress & 0x8f) != (1 | LIBUSB_ENDPOINT_OUT))
|
||||
/* first endpoint should be 1 (outbound) */
|
||||
break;
|
||||
|
||||
if((intf_dsc->endpoint[1].bEndpointAddress & 0x8f) != (2 | LIBUSB_ENDPOINT_IN))
|
||||
/* first endpoint should be 2 (inbound) */
|
||||
break;
|
||||
|
||||
/* if we made it here, it must be a Saleae Logic */
|
||||
ret = 1;
|
||||
}
|
||||
if(conf_dsc)
|
||||
libusb_free_config_descriptor(conf_dsc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
struct sigrok_device_instance *sl_open_device(int device_index)
|
||||
{
|
||||
struct sigrok_device_instance *sdi;
|
||||
libusb_device **devlist;
|
||||
struct libusb_device_descriptor des;
|
||||
int err, skip, i;
|
||||
|
||||
if(!(sdi = get_sigrok_device_instance(device_instances, device_index)))
|
||||
return NULL;
|
||||
|
||||
libusb_get_device_list(usb_context, &devlist);
|
||||
if(sdi->status == ST_INITIALIZING)
|
||||
{
|
||||
/* this device was renumerating last time we touched it. opendev() guarantees we've
|
||||
* waited long enough for it to have booted properly, so now we need to find it on
|
||||
* the bus and record its new address.
|
||||
*/
|
||||
skip = 0;
|
||||
for(i = 0; devlist[i]; i++)
|
||||
{
|
||||
if( (err = libusb_get_device_descriptor(devlist[i], &des)) )
|
||||
{
|
||||
g_warning("failed to get device descriptor: %d", err);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(des.idVendor == USB_VENDOR && des.idProduct == USB_PRODUCT)
|
||||
{
|
||||
if(skip != device_index)
|
||||
{
|
||||
/* skip past devices of this type that aren't the one we want */
|
||||
skip++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* should check the bus here, since we know that already... but what
|
||||
* are we going to do if it doesn't match after the right number of skips?
|
||||
*/
|
||||
|
||||
if( !(err = libusb_open(devlist[i], &(sdi->usb->devhdl))) )
|
||||
{
|
||||
sdi->usb->address = libusb_get_device_address(devlist[i]);
|
||||
sdi->status = ST_ACTIVE;
|
||||
g_message("opened device %d on %d.%d interface %d", sdi->index, sdi->usb->bus,
|
||||
sdi->usb->address, USB_INTERFACE);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_warning("failed to open device: %d", err);
|
||||
sdi = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(sdi->status == ST_INACTIVE)
|
||||
{
|
||||
/* this device is fully enumerated, so we need to find this device by
|
||||
* vendor, product, bus and address */
|
||||
libusb_get_device_list(usb_context, &devlist);
|
||||
for(i = 0; devlist[i]; i++)
|
||||
{
|
||||
if( (err = libusb_get_device_descriptor(devlist[i], &des)) )
|
||||
{
|
||||
g_warning("failed to get device descriptor: %d", err);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(des.idVendor == USB_VENDOR && des.idProduct == USB_PRODUCT)
|
||||
{
|
||||
if(libusb_get_bus_number(devlist[i]) == sdi->usb->bus &&
|
||||
libusb_get_device_address(devlist[i]) == sdi->usb->address)
|
||||
{
|
||||
/* found it */
|
||||
if( !(err = libusb_open(devlist[i], &(sdi->usb->devhdl))) )
|
||||
{
|
||||
sdi->status = ST_ACTIVE;
|
||||
g_message("opened device %d on %d.%d interface %d", sdi->index, sdi->usb->bus,
|
||||
sdi->usb->address, USB_INTERFACE);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_warning("failed to open device: %d", err);
|
||||
sdi = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* status must be ST_ACTIVE, i.e. already in use... */
|
||||
sdi = NULL;
|
||||
}
|
||||
libusb_free_device_list(devlist, 1);
|
||||
|
||||
if(sdi && sdi->status != ST_ACTIVE)
|
||||
sdi = NULL;
|
||||
|
||||
return sdi;
|
||||
}
|
||||
|
||||
|
||||
int upload_firmware(libusb_device *dev)
|
||||
{
|
||||
struct libusb_device_handle *hdl;
|
||||
int err;
|
||||
|
||||
g_message("uploading firmware to device on %d.%d",
|
||||
libusb_get_bus_number(dev), libusb_get_device_address(dev));
|
||||
|
||||
err = libusb_open(dev, &hdl);
|
||||
if(err != 0)
|
||||
{
|
||||
g_warning("failed to open device: %d", err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
err = libusb_set_configuration(hdl, USB_CONFIGURATION);
|
||||
if(err != 0)
|
||||
{
|
||||
g_warning("Unable to set configuration: %d", err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if((ezusb_reset(hdl, 1)) < 0)
|
||||
return 1;
|
||||
|
||||
if(ezusb_install_firmware(hdl, FIRMWARE) != 0)
|
||||
return 1;
|
||||
|
||||
if((ezusb_reset(hdl, 0)) < 0)
|
||||
return 1;
|
||||
|
||||
libusb_close(hdl);
|
||||
|
||||
/* remember when the last firmware update was done */
|
||||
g_get_current_time(&firmware_updated);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void close_device(struct sigrok_device_instance *sdi)
|
||||
{
|
||||
|
||||
if(sdi->usb->devhdl)
|
||||
{
|
||||
g_message("closing device %d on %d.%d interface %d", sdi->index, sdi->usb->bus,
|
||||
sdi->usb->address, USB_INTERFACE);
|
||||
libusb_release_interface(sdi->usb->devhdl, USB_INTERFACE);
|
||||
libusb_close(sdi->usb->devhdl);
|
||||
sdi->usb->devhdl = NULL;
|
||||
sdi->status = ST_INACTIVE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static int configure_probes(GSList *probes)
|
||||
{
|
||||
struct probe *probe;
|
||||
GSList *l;
|
||||
int probe_bit, stage, i;
|
||||
char *tc;
|
||||
|
||||
probe_mask = 0;
|
||||
for(i = 0; i < NUM_TRIGGER_STAGES; i++)
|
||||
{
|
||||
trigger_mask[i] = 0;
|
||||
trigger_value[i] = 0;
|
||||
}
|
||||
|
||||
stage = -1;
|
||||
for(l = probes; l; l = l->next)
|
||||
{
|
||||
probe = (struct probe *) l->data;
|
||||
if(probe->enabled == FALSE)
|
||||
continue;
|
||||
probe_bit = 1 << (probe->index - 1);
|
||||
probe_mask |= probe_bit;
|
||||
if(probe->trigger)
|
||||
{
|
||||
stage = 0;
|
||||
for(tc = probe->trigger; *tc; tc++)
|
||||
{
|
||||
trigger_mask[stage] |= probe_bit;
|
||||
if(*tc == '1')
|
||||
trigger_value[stage] |= probe_bit;
|
||||
stage++;
|
||||
if(stage > NUM_TRIGGER_STAGES)
|
||||
return SIGROK_NOK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(stage == -1)
|
||||
/* we didn't configure any triggers, make sure acquisition doesn't wait for any */
|
||||
trigger_stage = TRIGGER_FIRED;
|
||||
else
|
||||
trigger_stage = 0;
|
||||
|
||||
return SIGROK_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* API callbacks
|
||||
*/
|
||||
|
||||
static int hw_init(char *deviceinfo)
|
||||
{
|
||||
struct sigrok_device_instance *sdi;
|
||||
struct libusb_device_descriptor des;
|
||||
libusb_device **devlist;
|
||||
int err, devcnt, i;
|
||||
|
||||
if(libusb_init(&usb_context) != 0) {
|
||||
g_warning("Failed to initialize USB.");
|
||||
return 0;
|
||||
}
|
||||
libusb_set_debug(usb_context, 3);
|
||||
|
||||
/* find all Saleae Logic devices and upload firmware to all of them */
|
||||
devcnt = 0;
|
||||
libusb_get_device_list(usb_context, &devlist);
|
||||
for(i = 0; devlist[i]; i++) {
|
||||
err = libusb_get_device_descriptor(devlist[i], &des);
|
||||
if(err != 0) {
|
||||
g_warning("failed to get device descriptor: %d", err);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(des.idVendor == USB_VENDOR && des.idProduct == USB_PRODUCT) {
|
||||
/* definitely a Saleae Logic */
|
||||
|
||||
sdi = sigrok_device_instance_new(devcnt, ST_INITIALIZING,
|
||||
USB_VENDOR_NAME, USB_MODEL_NAME, USB_MODEL_VERSION);
|
||||
if(!sdi)
|
||||
return 0;
|
||||
device_instances = g_slist_append(device_instances, sdi);
|
||||
|
||||
if(check_conf_profile(devlist[i]) == 0)
|
||||
{
|
||||
if(upload_firmware(devlist[i]) > 0)
|
||||
/* continue on the off chance that the device is in a working state */
|
||||
/* TODO: could maybe try a USB reset, or uploading the firmware again... */
|
||||
g_warning("firmware upload failed for device %d", devcnt);
|
||||
|
||||
sdi->usb = usb_device_instance_new(libusb_get_bus_number(devlist[i]), 0, NULL);
|
||||
}
|
||||
else {
|
||||
/* already has the firmware on it, so fix the new address */
|
||||
sdi->usb = usb_device_instance_new(libusb_get_bus_number(devlist[i]),
|
||||
libusb_get_device_address(devlist[i]), NULL);
|
||||
}
|
||||
devcnt++;
|
||||
}
|
||||
}
|
||||
libusb_free_device_list(devlist, 1);
|
||||
|
||||
return devcnt;
|
||||
}
|
||||
|
||||
|
||||
static int hw_opendev(int device_index)
|
||||
{
|
||||
GTimeVal cur_time;
|
||||
struct sigrok_device_instance *sdi;
|
||||
int timediff, err;
|
||||
unsigned int cur, upd;
|
||||
|
||||
if(firmware_updated.tv_sec > 0) {
|
||||
/* firmware was recently uploaded */
|
||||
g_get_current_time(&cur_time);
|
||||
cur = cur_time.tv_sec * 1000 + cur_time.tv_usec / 1000;
|
||||
upd = firmware_updated.tv_sec * 1000 + firmware_updated.tv_usec / 1000;
|
||||
timediff = cur - upd;
|
||||
if(timediff < FIRMWARE_RENUM_DELAY) {
|
||||
timediff = FIRMWARE_RENUM_DELAY - timediff;
|
||||
g_message("waiting %d ms for device to reset", timediff);
|
||||
g_usleep(timediff * 1000);
|
||||
firmware_updated.tv_sec = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if( !(sdi = sl_open_device(device_index)) ) {
|
||||
g_warning("unable to open device");
|
||||
return SIGROK_NOK;
|
||||
}
|
||||
|
||||
err = libusb_claim_interface(sdi->usb->devhdl, USB_INTERFACE);
|
||||
if(err != 0) {
|
||||
g_warning("Unable to claim interface: %d", err);
|
||||
return SIGROK_NOK;
|
||||
}
|
||||
|
||||
if(cur_sample_rate == 0) {
|
||||
/* sample rate hasn't been set; default to the slowest it has */
|
||||
if(hw_set_configuration(device_index, HWCAP_SAMPLERATE, &supported_samplerates[0]) == SIGROK_NOK)
|
||||
return SIGROK_NOK;
|
||||
}
|
||||
|
||||
return SIGROK_OK;
|
||||
}
|
||||
|
||||
|
||||
static void hw_closedev(int device_index)
|
||||
{
|
||||
struct sigrok_device_instance *sdi;
|
||||
|
||||
if( (sdi = get_sigrok_device_instance(device_instances, device_index)) )
|
||||
close_device(sdi);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void hw_cleanup(void)
|
||||
{
|
||||
GSList *l;
|
||||
|
||||
/* properly close all devices */
|
||||
for(l = device_instances; l; l = l->next)
|
||||
close_device( (struct sigrok_device_instance *) l->data);
|
||||
|
||||
/* and free all their memory */
|
||||
for(l = device_instances; l; l = l->next)
|
||||
g_free(l->data);
|
||||
g_slist_free(device_instances);
|
||||
device_instances = NULL;
|
||||
|
||||
if(usb_context)
|
||||
libusb_exit(usb_context);
|
||||
usb_context = NULL;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void *hw_get_device_info(int device_index, int device_info_id)
|
||||
{
|
||||
struct sigrok_device_instance *sdi;
|
||||
void *info;
|
||||
|
||||
if( !(sdi = get_sigrok_device_instance(device_instances, device_index)) )
|
||||
return NULL;
|
||||
|
||||
info = NULL;
|
||||
switch(device_info_id)
|
||||
{
|
||||
case DI_INSTANCE:
|
||||
info = sdi;
|
||||
break;
|
||||
case DI_NUM_PROBES:
|
||||
info = GINT_TO_POINTER(NUM_PROBES);
|
||||
break;
|
||||
case DI_SAMPLERATES:
|
||||
info = &samplerates;
|
||||
break;
|
||||
case DI_TRIGGER_TYPES:
|
||||
info = TRIGGER_TYPES;
|
||||
break;
|
||||
case DI_CUR_SAMPLE_RATE:
|
||||
info = &cur_sample_rate;
|
||||
break;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
static int hw_get_status(int device_index)
|
||||
{
|
||||
struct sigrok_device_instance *sdi;
|
||||
|
||||
sdi = get_sigrok_device_instance(device_instances, device_index);
|
||||
if(sdi)
|
||||
return sdi->status;
|
||||
else
|
||||
return ST_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
||||
static int *hw_get_capabilities(void)
|
||||
{
|
||||
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
|
||||
static int set_configuration_samplerate(struct sigrok_device_instance *sdi, uint64_t samplerate)
|
||||
{
|
||||
uint8_t divider;
|
||||
int ret, result, i;
|
||||
unsigned char buf[2];
|
||||
|
||||
for(i = 0; supported_samplerates[i]; i++) {
|
||||
if(supported_samplerates[i] == samplerate)
|
||||
break;
|
||||
}
|
||||
if(supported_samplerates[i] == 0)
|
||||
return SIGROK_ERR_BADVALUE;
|
||||
|
||||
divider = (uint8_t) (48 / (float) (samplerate/1000000)) - 1;
|
||||
|
||||
g_message("setting sample rate to %"PRIu64" Hz (divider %d)", samplerate, divider);
|
||||
buf[0] = 0x01;
|
||||
buf[1] = divider;
|
||||
ret = libusb_bulk_transfer(sdi->usb->devhdl, 1 | LIBUSB_ENDPOINT_OUT, buf, 2, &result, 500);
|
||||
if(ret != 0) {
|
||||
g_warning("failed to set samplerate: %d", ret);
|
||||
return SIGROK_NOK;
|
||||
}
|
||||
cur_sample_rate = samplerate;
|
||||
|
||||
return SIGROK_OK;
|
||||
}
|
||||
|
||||
|
||||
static int hw_set_configuration(int device_index, int capability, void *value)
|
||||
{
|
||||
struct sigrok_device_instance *sdi;
|
||||
int ret;
|
||||
uint64_t *tmp_u64;
|
||||
|
||||
if( !(sdi = get_sigrok_device_instance(device_instances, device_index)) )
|
||||
return SIGROK_NOK;
|
||||
|
||||
if(capability == HWCAP_SAMPLERATE) {
|
||||
tmp_u64 = value;
|
||||
ret = set_configuration_samplerate(sdi, *tmp_u64);
|
||||
}
|
||||
else if(capability == HWCAP_PROBECONFIG)
|
||||
ret = configure_probes( (GSList *) value);
|
||||
else if(capability == HWCAP_LIMIT_SAMPLES) {
|
||||
limit_samples = strtoull(value, NULL, 10);
|
||||
ret = SIGROK_OK;
|
||||
}
|
||||
else
|
||||
ret = SIGROK_NOK;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int receive_data(int fd, int revents, void *user_data)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
tv.tv_sec = tv.tv_usec = 0;
|
||||
libusb_handle_events_timeout(usb_context, &tv);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
void receive_transfer(struct libusb_transfer *transfer)
|
||||
{
|
||||
static int num_samples = 0;
|
||||
static int empty_transfer_count = 0;
|
||||
|
||||
struct datafeed_packet packet;
|
||||
void *user_data;
|
||||
int cur_buflen, trigger_offset, i;
|
||||
unsigned char *cur_buf, *new_buf;
|
||||
|
||||
if(transfer == NULL) {
|
||||
/* hw_stop_acquisition() telling us to stop */
|
||||
num_samples = -1;
|
||||
}
|
||||
|
||||
if(num_samples == -1) {
|
||||
/* acquisition has already ended, just free any queued up transfer that come in */
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
else {
|
||||
g_message("receive_transfer(): status %d received %d bytes", transfer->status, transfer->actual_length);
|
||||
|
||||
/* save the incoming transfer before reusing the transfer struct */
|
||||
cur_buf = transfer->buffer;
|
||||
cur_buflen = transfer->actual_length;
|
||||
user_data = transfer->user_data;
|
||||
|
||||
/* fire off a new request */
|
||||
new_buf = g_malloc(4096);
|
||||
transfer->buffer = new_buf;
|
||||
transfer->length = 4096;
|
||||
if(libusb_submit_transfer(transfer) != 0) {
|
||||
/* TODO: stop session? */
|
||||
g_warning("eek");
|
||||
}
|
||||
|
||||
if(cur_buflen == 0) {
|
||||
empty_transfer_count++;
|
||||
if(empty_transfer_count > MAX_EMPTY_TRANSFERS) {
|
||||
/* the FX2 gave up... end the acquisition, the frontend will work
|
||||
* out that the samplecount is short
|
||||
*/
|
||||
packet.type = DF_END;
|
||||
session_bus(user_data, &packet);
|
||||
num_samples = -1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
empty_transfer_count = 0;
|
||||
|
||||
trigger_offset = 0;
|
||||
if(trigger_stage >= 0)
|
||||
{
|
||||
for(i = 0; i < cur_buflen; i++)
|
||||
{
|
||||
if((cur_buf[i] & trigger_mask[trigger_stage]) == trigger_value[trigger_stage])
|
||||
{
|
||||
/* match on this trigger stage */
|
||||
trigger_buffer[trigger_stage] = cur_buf[i];
|
||||
trigger_stage++;
|
||||
if(trigger_stage == NUM_TRIGGER_STAGES || trigger_mask[trigger_stage] == 0)
|
||||
{
|
||||
/* match on all trigger stages, we're done */
|
||||
trigger_offset = i+1;
|
||||
|
||||
/* TODO: send pre-trigger buffer to session bus */
|
||||
|
||||
/* tell the frontend we hit the trigger here */
|
||||
packet.type = DF_TRIGGER;
|
||||
packet.length = 0;
|
||||
session_bus(user_data, &packet);
|
||||
|
||||
/* send the samples that triggered it, since we're skipping past them */
|
||||
packet.type = DF_LOGIC8;
|
||||
packet.length = trigger_stage;
|
||||
packet.payload = trigger_buffer;
|
||||
session_bus(user_data, &packet);
|
||||
break;
|
||||
|
||||
trigger_stage = TRIGGER_FIRED;
|
||||
}
|
||||
}
|
||||
else if(trigger_stage > 0)
|
||||
{
|
||||
/* we had a match before, but not in the next sample. however, we may
|
||||
* have a match on this stage in the next bit -- trigger on 0001 will
|
||||
* fail on seeing 00001, so we need to go back to stage 0 -- but at
|
||||
* the next sample from the one that matched originally, which the
|
||||
* counter increment at the end of the loop takes care of.
|
||||
*/
|
||||
i -= trigger_stage;
|
||||
if(i < -1)
|
||||
/* oops, went back past this buffer */
|
||||
i = -1;
|
||||
/* reset trigger stage */
|
||||
trigger_stage = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(trigger_stage == TRIGGER_FIRED)
|
||||
{
|
||||
/* send the incoming transfer to the session bus */
|
||||
packet.type = DF_LOGIC8;
|
||||
packet.length = cur_buflen - trigger_offset;
|
||||
packet.payload = cur_buf + trigger_offset;
|
||||
session_bus(user_data, &packet);
|
||||
g_free(cur_buf);
|
||||
|
||||
num_samples += cur_buflen;
|
||||
if(num_samples > limit_samples)
|
||||
{
|
||||
/* end the acquisition */
|
||||
packet.type = DF_END;
|
||||
session_bus(user_data, &packet);
|
||||
num_samples = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* TODO: buffer pre-trigger data in capture ratio-sized buffer */
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static int hw_start_acquisition(int device_index, gpointer session_device_id)
|
||||
{
|
||||
struct sigrok_device_instance *sdi;
|
||||
struct datafeed_packet *packet;
|
||||
struct datafeed_header *header;
|
||||
struct libusb_transfer *transfer;
|
||||
const struct libusb_pollfd **lupfd;
|
||||
int size, i;
|
||||
unsigned char *buf;
|
||||
|
||||
if( !(sdi = get_sigrok_device_instance(device_instances, device_index)))
|
||||
return SIGROK_NOK;
|
||||
|
||||
packet = g_malloc(sizeof(struct datafeed_packet));
|
||||
header = g_malloc(sizeof(struct datafeed_header));
|
||||
if(!packet || !header)
|
||||
return SIGROK_NOK;
|
||||
|
||||
/* start with 2K transfer, subsequently increased to 4K */
|
||||
size = 2048;
|
||||
for(i = 0; i < NUM_SIMUL_TRANSFERS; i++) {
|
||||
buf = g_malloc(size);
|
||||
transfer = libusb_alloc_transfer(0);
|
||||
libusb_fill_bulk_transfer(transfer, sdi->usb->devhdl, 2 | LIBUSB_ENDPOINT_IN, buf, size,
|
||||
receive_transfer, session_device_id, 40);
|
||||
if(libusb_submit_transfer(transfer) != 0) {
|
||||
/* TODO: free them all */
|
||||
libusb_free_transfer(transfer);
|
||||
g_free(buf);
|
||||
return SIGROK_NOK;
|
||||
}
|
||||
size = 4096;
|
||||
}
|
||||
|
||||
lupfd = libusb_get_pollfds(usb_context);
|
||||
for(i = 0; lupfd[i]; i++)
|
||||
source_add(lupfd[i]->fd, lupfd[i]->events, -1, receive_data, NULL);
|
||||
free(lupfd);
|
||||
|
||||
packet->type = DF_HEADER;
|
||||
packet->length = sizeof(struct datafeed_header);
|
||||
packet->payload = (unsigned char *) header;
|
||||
header->feed_version = 1;
|
||||
gettimeofday(&header->starttime, NULL);
|
||||
header->rate = cur_sample_rate;
|
||||
header->protocol_id = PROTO_RAW;
|
||||
header->num_probes = NUM_PROBES;
|
||||
session_bus(session_device_id, packet);
|
||||
g_free(header);
|
||||
g_free(packet);
|
||||
|
||||
return SIGROK_OK;
|
||||
}
|
||||
|
||||
|
||||
/* this stops acquisition on ALL devices, ignoring device_index */
|
||||
static void hw_stop_acquisition(int device_index, gpointer session_device_id)
|
||||
{
|
||||
struct datafeed_packet packet;
|
||||
|
||||
packet.type = DF_END;
|
||||
session_bus(session_device_id, &packet);
|
||||
|
||||
receive_transfer(NULL);
|
||||
|
||||
/* TODO: need to cancel and free any queued up transfers */
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct device_plugin saleae_logic_plugin_info = {
|
||||
"saleae-logic",
|
||||
1,
|
||||
hw_init,
|
||||
hw_cleanup,
|
||||
|
||||
hw_opendev,
|
||||
hw_closedev,
|
||||
hw_get_device_info,
|
||||
hw_get_status,
|
||||
hw_get_capabilities,
|
||||
hw_set_configuration,
|
||||
hw_start_acquisition,
|
||||
hw_stop_acquisition
|
||||
};
|
||||
|
|
@ -0,0 +1,559 @@
|
|||
/*
|
||||
Copyright (c) 2010 Sven Peter <sven@fail0verflow.com>
|
||||
Copyright (c) 2010 Haxx Enterprises <bushing@gmail.com>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or
|
||||
without modification, are permitted provided that the following
|
||||
conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#include <assert.h>
|
||||
|
||||
#include "analyzer.h"
|
||||
#include "gl_usb.h"
|
||||
|
||||
enum {
|
||||
HARD_DATA_CHECK_SUM = 0x00,
|
||||
PASS_WORD,
|
||||
|
||||
DEVICE_ID0 = 0x10,
|
||||
DEVICE_ID1,
|
||||
|
||||
START_STATUS = 0x20,
|
||||
DEVICE_STATUS = 0x21,
|
||||
FREQUENCY_REG0 = 0x30,
|
||||
FREQUENCY_REG1,
|
||||
FREQUENCY_REG2,
|
||||
FREQUENCY_REG3,
|
||||
FREQUENCY_REG4,
|
||||
MEMORY_LENGTH,
|
||||
CLOCK_SOURCE,
|
||||
|
||||
TRIGGER_STATUS0 = 0x40,
|
||||
TRIGGER_STATUS1,
|
||||
TRIGGER_STATUS2,
|
||||
TRIGGER_STATUS3,
|
||||
TRIGGER_STATUS4,
|
||||
TRIGGER_STATUS5,
|
||||
TRIGGER_STATUS6,
|
||||
TRIGGER_STATUS7,
|
||||
TRIGGER_STATUS8,
|
||||
|
||||
TRIGGER_COUNT0 = 0x50,
|
||||
TRIGGER_COUNT1,
|
||||
|
||||
TRIGGER_LEVEL0 = 0x55,
|
||||
TRIGGER_LEVEL1,
|
||||
TRIGGER_LEVEL2,
|
||||
TRIGGER_LEVEL3,
|
||||
|
||||
RAMSIZE_TRIGGERBAR_ADDRESS0 = 0x60,
|
||||
RAMSIZE_TRIGGERBAR_ADDRESS1,
|
||||
RAMSIZE_TRIGGERBAR_ADDRESS2,
|
||||
TRIGGERBAR_ADDRESS0,
|
||||
TRIGGERBAR_ADDRESS1,
|
||||
TRIGGERBAR_ADDRESS2,
|
||||
DONT_CARE_TRIGGERBAR,
|
||||
|
||||
FILTER_ENABLE = 0x70,
|
||||
FILTER_STATUS,
|
||||
|
||||
ENABLE_DELAY_TIME0 = 0x7A,
|
||||
ENABLE_DELAY_TIME1,
|
||||
|
||||
ENABLE_INSERT_DATA0 = 0x80,
|
||||
ENABLE_INSERT_DATA1,
|
||||
ENABLE_INSERT_DATA2,
|
||||
ENABLE_INSERT_DATA3,
|
||||
COMPRESSION_TYPE0,
|
||||
COMPRESSION_TYPE1,
|
||||
|
||||
TRIGGER_ADDRESS0 = 0x90,
|
||||
TRIGGER_ADDRESS1,
|
||||
TRIGGER_ADDRESS2,
|
||||
|
||||
NOW_ADDRESS0 = 0x96,
|
||||
NOW_ADDRESS1,
|
||||
NOW_ADDRESS2,
|
||||
|
||||
STOP_ADDRESS0 = 0x9B,
|
||||
STOP_ADDRESS1,
|
||||
STOP_ADDRESS2,
|
||||
|
||||
READ_RAM_STATUS = 0xA0
|
||||
};
|
||||
|
||||
static int g_trigger_status[9] = {0};
|
||||
static int g_trigger_count = 1;
|
||||
|
||||
static int g_filter_status[8] = {0};
|
||||
static int g_filter_enable = 0;
|
||||
|
||||
static int g_freq_value = 100;
|
||||
static int g_freq_scale = FREQ_SCALE_MHZ;
|
||||
static int g_memory_size = MEMORY_SIZE_512K;
|
||||
static int g_ramsize_triggerbar_addr = 0x80000>>2;
|
||||
static int g_triggerbar_addr = 0x3fe;
|
||||
static int g_compression = COMPRESSION_NONE;
|
||||
|
||||
// maybe unk specifies an "endpoint" or "register" of sorts
|
||||
static int analyzer_write_status(libusb_device_handle *devh, unsigned char unk, unsigned char flags)
|
||||
{
|
||||
assert(unk <= 3);
|
||||
return gl_reg_write(devh, START_STATUS, unk << 6 | flags);
|
||||
}
|
||||
|
||||
static int __analyzer_set_freq(libusb_device_handle *devh, int freq, int scale)
|
||||
{
|
||||
int reg0=0, divisor=0, reg2=0;
|
||||
switch (scale) {
|
||||
case FREQ_SCALE_MHZ: // MHz
|
||||
if (freq >= 100 && freq <= 200) {
|
||||
reg0 = freq * 0.1;
|
||||
divisor = 1;
|
||||
reg2 = 0;
|
||||
break;
|
||||
}
|
||||
if (freq >= 50 && freq < 100) {
|
||||
reg0 = freq * 0.2;
|
||||
divisor = 2;
|
||||
reg2 = 0;
|
||||
break;
|
||||
}
|
||||
if (freq >= 10 && freq < 50) {
|
||||
if (freq == 25) {
|
||||
reg0 = 25;
|
||||
divisor = 5;
|
||||
reg2 = 1;
|
||||
break;
|
||||
} else {
|
||||
reg0 = freq * 0.5;
|
||||
divisor = 5;
|
||||
reg2 = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (freq >= 2 && freq < 10) {
|
||||
divisor = 5;
|
||||
reg0 = freq * 2;
|
||||
reg2 = 2;
|
||||
break;
|
||||
}
|
||||
if (freq == 1) {
|
||||
divisor = 5;
|
||||
reg2 = 16;
|
||||
reg0 = 5;
|
||||
break;
|
||||
}
|
||||
divisor = 5;
|
||||
reg0 = 5;
|
||||
reg2 = 64;
|
||||
break;
|
||||
case FREQ_SCALE_HZ: // Hz
|
||||
if (freq >= 500 && freq < 1000) {
|
||||
reg0 = freq * 0.01;
|
||||
divisor = 10;
|
||||
reg2 = 64;
|
||||
break;
|
||||
}
|
||||
if (freq >= 300 && freq < 500) {
|
||||
reg0 = freq * 0.005 * 8;
|
||||
divisor = 5;
|
||||
reg2 = 67;
|
||||
break;
|
||||
}
|
||||
if (freq >= 100 && freq < 300) {
|
||||
reg0 = freq * 0.005 * 16;
|
||||
divisor = 5;
|
||||
reg2 = 68;
|
||||
break;
|
||||
}
|
||||
divisor = 5;
|
||||
reg0 = 5;
|
||||
reg2 = 64;
|
||||
break;
|
||||
case FREQ_SCALE_KHZ: // KHz
|
||||
if (freq >= 500 && freq < 1000) {
|
||||
reg0 = freq * 0.01;
|
||||
divisor = 5;
|
||||
reg2 = 17;
|
||||
break;
|
||||
}
|
||||
if (freq >= 100 && freq < 500) {
|
||||
reg0 = freq * 0.05;
|
||||
divisor = 5;
|
||||
reg2 = 32;
|
||||
break;
|
||||
}
|
||||
if (freq >= 50 && freq < 100) {
|
||||
reg0 = freq * 0.1;
|
||||
divisor = 5;
|
||||
reg2 = 33;
|
||||
break;
|
||||
}
|
||||
if (freq >= 10 && freq < 50) {
|
||||
if (freq == 25) {
|
||||
reg0 = 25;
|
||||
divisor = 5;
|
||||
reg2 = 49;
|
||||
break;
|
||||
}
|
||||
reg0 = freq * 0.5;
|
||||
divisor = 5;
|
||||
reg2 = 48;
|
||||
break;
|
||||
}
|
||||
if (freq >= 2 && freq < 10) {
|
||||
divisor = 5;
|
||||
reg0 = freq * 2;
|
||||
reg2 = 50;
|
||||
break;
|
||||
}
|
||||
divisor = 5;
|
||||
reg0 = 5;
|
||||
reg2 = 64;
|
||||
break;
|
||||
default:
|
||||
divisor = 5;
|
||||
reg0 = 5;
|
||||
reg2 = 64;
|
||||
break;
|
||||
}
|
||||
if (gl_reg_write(devh, FREQUENCY_REG0, divisor) < 0)
|
||||
return -1; // divisor maybe?
|
||||
|
||||
if (gl_reg_write(devh, FREQUENCY_REG1, reg0) < 0)
|
||||
return -1; // 10 / 0.2
|
||||
|
||||
if (gl_reg_write(devh, FREQUENCY_REG2, 0x02) < 0)
|
||||
return -1; // always 2
|
||||
|
||||
if (gl_reg_write(devh, FREQUENCY_REG4, reg2) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __analyzer_set_ramsize_trigger_address(libusb_device_handle *devh, unsigned int address)
|
||||
{
|
||||
gl_reg_write(devh, RAMSIZE_TRIGGERBAR_ADDRESS0, (address >> 0) & 0xFF);
|
||||
gl_reg_write(devh, RAMSIZE_TRIGGERBAR_ADDRESS1, (address >> 8) & 0xFF);
|
||||
gl_reg_write(devh, RAMSIZE_TRIGGERBAR_ADDRESS2, (address >> 16) & 0xFF);
|
||||
}
|
||||
|
||||
static void __analyzer_set_triggerbar_address(libusb_device_handle *devh, unsigned int address)
|
||||
{
|
||||
gl_reg_write(devh, TRIGGERBAR_ADDRESS0, (address >> 0) & 0xFF);
|
||||
gl_reg_write(devh, TRIGGERBAR_ADDRESS1, (address >> 8) & 0xFF);
|
||||
gl_reg_write(devh, TRIGGERBAR_ADDRESS2, (address >> 16) & 0xFF);
|
||||
}
|
||||
|
||||
static void __analyzer_set_compression(libusb_device_handle *devh, unsigned int type)
|
||||
{
|
||||
gl_reg_write(devh, COMPRESSION_TYPE0, (type >> 0) & 0xFF);
|
||||
gl_reg_write(devh, COMPRESSION_TYPE1, (type >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
static void __analyzer_set_trigger_count(libusb_device_handle *devh, unsigned int count)
|
||||
{
|
||||
gl_reg_write(devh, TRIGGER_COUNT0, (count >> 0) & 0xFF);
|
||||
gl_reg_write(devh, TRIGGER_COUNT1, (count >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
static void analyzer_write_enable_insert_data(libusb_device_handle *devh)
|
||||
{
|
||||
gl_reg_write(devh, ENABLE_INSERT_DATA0, 0x12);
|
||||
gl_reg_write(devh, ENABLE_INSERT_DATA1, 0x34);
|
||||
gl_reg_write(devh, ENABLE_INSERT_DATA2, 0x56);
|
||||
gl_reg_write(devh, ENABLE_INSERT_DATA3, 0x78);
|
||||
}
|
||||
|
||||
static void analyzer_set_filter(libusb_device_handle *devh)
|
||||
{
|
||||
int i;
|
||||
gl_reg_write(devh, FILTER_ENABLE, g_filter_enable);
|
||||
for (i = 0; i < 8; i++)
|
||||
gl_reg_write(devh, FILTER_STATUS + i, g_filter_status[i]);
|
||||
}
|
||||
|
||||
void analyzer_reset(libusb_device_handle *devh)
|
||||
{
|
||||
analyzer_write_status(devh, 3, STATUS_FLAG_NONE); // reset device
|
||||
analyzer_write_status(devh, 3, STATUS_FLAG_RESET); // reset device
|
||||
}
|
||||
|
||||
void analyzer_initialize(libusb_device_handle *devh)
|
||||
{
|
||||
analyzer_write_status(devh, 1, STATUS_FLAG_NONE);
|
||||
analyzer_write_status(devh, 1, STATUS_FLAG_INIT);
|
||||
analyzer_write_status(devh, 1, STATUS_FLAG_NONE);
|
||||
}
|
||||
|
||||
void analyzer_wait(libusb_device_handle *devh, int set, int unset)
|
||||
{
|
||||
int status;
|
||||
while(1) {
|
||||
status = gl_reg_read(devh, DEVICE_STATUS);
|
||||
if ((status & set) && ((status & unset) == 0))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void analyzer_read_start(libusb_device_handle *devh)
|
||||
{
|
||||
int i;
|
||||
|
||||
analyzer_write_status(devh, 3, STATUS_FLAG_20 | STATUS_FLAG_READ);
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
(void)gl_reg_read(devh, READ_RAM_STATUS);
|
||||
}
|
||||
|
||||
int analyzer_read_data(libusb_device_handle *devh, void *buffer, unsigned int size)
|
||||
{
|
||||
return gl_read_bulk(devh, buffer, size);
|
||||
}
|
||||
|
||||
void analyzer_read_stop(libusb_device_handle *devh)
|
||||
{
|
||||
analyzer_write_status(devh, 3, STATUS_FLAG_20);
|
||||
analyzer_write_status(devh, 3, STATUS_FLAG_NONE);
|
||||
}
|
||||
|
||||
void analyzer_start(libusb_device_handle *devh)
|
||||
{
|
||||
analyzer_write_status(devh, 1, STATUS_FLAG_NONE);
|
||||
analyzer_write_status(devh, 1, STATUS_FLAG_INIT);
|
||||
analyzer_write_status(devh, 1, STATUS_FLAG_NONE);
|
||||
analyzer_write_status(devh, 1, STATUS_FLAG_GO);
|
||||
}
|
||||
|
||||
void analyzer_configure(libusb_device_handle *devh)
|
||||
{
|
||||
// Write_Start_Status
|
||||
analyzer_write_status(devh, 1, STATUS_FLAG_RESET);
|
||||
analyzer_write_status(devh, 1, STATUS_FLAG_NONE);
|
||||
|
||||
// Start_Config_Outside_Device ?
|
||||
analyzer_write_status(devh, 1, STATUS_FLAG_INIT);
|
||||
analyzer_write_status(devh, 1, STATUS_FLAG_NONE);
|
||||
|
||||
//SetData_To_Frequence_Reg
|
||||
__analyzer_set_freq(devh, g_freq_value, g_freq_scale);
|
||||
|
||||
//SetMemory_Length
|
||||
gl_reg_write(devh, MEMORY_LENGTH, g_memory_size);
|
||||
|
||||
//Sele_Inside_Outside_Clock
|
||||
gl_reg_write(devh, CLOCK_SOURCE, 0x03);
|
||||
|
||||
//Set_Trigger_Status
|
||||
int i;
|
||||
for (i = 0; i < 9; i++)
|
||||
gl_reg_write(devh, TRIGGER_STATUS0 + i, g_trigger_status[i]);
|
||||
|
||||
__analyzer_set_trigger_count(devh, g_trigger_count);
|
||||
|
||||
//Set_Trigger_Level
|
||||
gl_reg_write(devh, TRIGGER_LEVEL0, 0x31);
|
||||
gl_reg_write(devh, TRIGGER_LEVEL1, 0x31);
|
||||
gl_reg_write(devh, TRIGGER_LEVEL2, 0x31);
|
||||
gl_reg_write(devh, TRIGGER_LEVEL3, 0x31);
|
||||
|
||||
__analyzer_set_ramsize_trigger_address(devh, g_ramsize_triggerbar_addr); // size of actual memory >> 2
|
||||
__analyzer_set_triggerbar_address(devh, g_triggerbar_addr);
|
||||
|
||||
//Set_Dont_Care_TriggerBar
|
||||
gl_reg_write(devh, DONT_CARE_TRIGGERBAR, 0x01);
|
||||
|
||||
//Enable_Status
|
||||
analyzer_set_filter(devh);
|
||||
|
||||
//Set_Enable_Delay_Time
|
||||
gl_reg_write(devh, 0x7a, 0x00);
|
||||
gl_reg_write(devh, 0x7b, 0x00);
|
||||
analyzer_write_enable_insert_data(devh);
|
||||
__analyzer_set_compression(devh,g_compression);
|
||||
}
|
||||
|
||||
void analyzer_add_trigger(int channel, int type)
|
||||
{
|
||||
if ((channel & 0xf) >= 8)
|
||||
return;
|
||||
|
||||
if (type == TRIGGER_HIGH || type == TRIGGER_LOW) {
|
||||
int i;
|
||||
if (channel & CHANNEL_A)
|
||||
i = 0;
|
||||
else if (channel & CHANNEL_B)
|
||||
i = 2;
|
||||
else if (channel & CHANNEL_C)
|
||||
i = 4;
|
||||
else if (channel & CHANNEL_D)
|
||||
i = 6;
|
||||
else
|
||||
return;
|
||||
if ((channel & 0xf) >= 4) {
|
||||
i++;
|
||||
channel -= 4;
|
||||
}
|
||||
g_trigger_status[i] |= 1 << ((2 * channel) + (type == TRIGGER_LOW ? 1 : 0));
|
||||
} else {
|
||||
if (type == TRIGGER_POSEDGE)
|
||||
g_trigger_status[8] = 0x40;
|
||||
else if(type == TRIGGER_NEGEDGE)
|
||||
g_trigger_status[8] = 0x80;
|
||||
else
|
||||
g_trigger_status[8] = 0xc0;
|
||||
|
||||
// FIXME: just guessed the index; need to verify
|
||||
if (channel & CHANNEL_B)
|
||||
g_trigger_status[8] += 8;
|
||||
else if (channel & CHANNEL_C)
|
||||
g_trigger_status[8] += 16;
|
||||
else if (channel & CHANNEL_D)
|
||||
g_trigger_status[8] += 24;
|
||||
g_trigger_status[8] += channel % 8;
|
||||
}
|
||||
}
|
||||
|
||||
void analyzer_add_filter(int channel, int type)
|
||||
{
|
||||
if (type != FILTER_HIGH && type != FILTER_LOW)
|
||||
return;
|
||||
if ((channel & 0xf) >= 8)
|
||||
return;
|
||||
|
||||
int i;
|
||||
if (channel & CHANNEL_A)
|
||||
i = 0;
|
||||
else if (channel & CHANNEL_B)
|
||||
i = 2;
|
||||
else if (channel & CHANNEL_C)
|
||||
i = 4;
|
||||
else if (channel & CHANNEL_D)
|
||||
i = 6;
|
||||
else
|
||||
return;
|
||||
if ((channel & 0xf) >= 4) {
|
||||
i++;
|
||||
channel -= 4;
|
||||
}
|
||||
g_filter_status[i] |= 1 << ((2 * channel) + (type == FILTER_LOW ? 1 : 0));
|
||||
g_filter_enable = 1;
|
||||
}
|
||||
|
||||
void analyzer_set_trigger_count(int count)
|
||||
{
|
||||
g_trigger_count = count;
|
||||
}
|
||||
|
||||
void analyzer_set_freq(int freq, int scale)
|
||||
{
|
||||
g_freq_value = freq;
|
||||
g_freq_scale = scale;
|
||||
}
|
||||
|
||||
void analyzer_set_memory_size(unsigned int size)
|
||||
{
|
||||
g_memory_size = size;
|
||||
}
|
||||
|
||||
|
||||
void analyzer_set_ramsize_trigger_address(unsigned int address)
|
||||
{
|
||||
g_ramsize_triggerbar_addr = address;
|
||||
}
|
||||
|
||||
void analyzer_set_triggerbar_address(unsigned int address)
|
||||
{
|
||||
g_triggerbar_addr = address;
|
||||
}
|
||||
|
||||
unsigned int analyzer_read_id(libusb_device_handle *devh)
|
||||
{
|
||||
return gl_reg_read(devh, DEVICE_ID1) << 8 | gl_reg_read(devh, DEVICE_ID0);
|
||||
}
|
||||
|
||||
unsigned int analyzer_get_stop_address(libusb_device_handle *devh)
|
||||
{
|
||||
return gl_reg_read(devh, STOP_ADDRESS2) << 16 | gl_reg_read(devh, STOP_ADDRESS1) << 8 | gl_reg_read(devh, STOP_ADDRESS0);
|
||||
}
|
||||
|
||||
unsigned int analyzer_get_now_address(libusb_device_handle *devh)
|
||||
{
|
||||
return gl_reg_read(devh, NOW_ADDRESS2) << 16 | gl_reg_read(devh, NOW_ADDRESS1) << 8 | gl_reg_read(devh, NOW_ADDRESS0);
|
||||
}
|
||||
|
||||
unsigned int analyzer_get_trigger_address(libusb_device_handle *devh)
|
||||
{
|
||||
return gl_reg_read(devh, TRIGGER_ADDRESS2) << 16 | gl_reg_read(devh, TRIGGER_ADDRESS1) << 8 | gl_reg_read(devh, TRIGGER_ADDRESS0);
|
||||
}
|
||||
|
||||
void analyzer_set_compression(unsigned int type)
|
||||
{
|
||||
g_compression = type;
|
||||
}
|
||||
|
||||
void analyzer_wait_button(libusb_device_handle *devh)
|
||||
{
|
||||
analyzer_wait(devh, STATUS_BUTTON_PRESSED, 0);
|
||||
}
|
||||
|
||||
void analyzer_wait_data(libusb_device_handle *devh)
|
||||
{
|
||||
analyzer_wait(devh, STATUS_READY | 8, STATUS_BUSY);
|
||||
}
|
||||
|
||||
int analyzer_decompress(void *input, unsigned int input_len, void *output, unsigned int output_len)
|
||||
{
|
||||
unsigned char *in = input;
|
||||
unsigned char *out = output;
|
||||
unsigned int A, B, C, count;
|
||||
unsigned int written = 0;
|
||||
|
||||
while (input_len > 0) {
|
||||
A = *in++;
|
||||
B = *in++;
|
||||
C = *in++;
|
||||
count = (*in++) + 1;
|
||||
|
||||
if (count > output_len)
|
||||
count = output_len;
|
||||
output_len -= count;
|
||||
written += count;
|
||||
|
||||
while (count--) {
|
||||
*out++ = A;
|
||||
*out++ = B;
|
||||
*out++ = C;
|
||||
*out++ = 0; // channel D
|
||||
}
|
||||
|
||||
input_len -= 4;
|
||||
if (output_len == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
Copyright (c) 2010 Sven Peter <sven@fail0verflow.com>
|
||||
Copyright (c) 2010 Haxx Enterprises <bushing@gmail.com>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or
|
||||
without modification, are permitted provided that the following
|
||||
conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#ifndef ANALYZER_H__
|
||||
#define ANALYZER_H__ 1
|
||||
#include <libusb.h>
|
||||
#define STATUS_FLAG_NONE 0x00
|
||||
#define STATUS_FLAG_RESET 0x01
|
||||
#define STATUS_FLAG_INIT 0x02
|
||||
#define STATUS_FLAG_GO 0x04
|
||||
#define STATUS_FLAG_PAUSE 0x08
|
||||
#define STATUS_FLAG_READ 0x10
|
||||
#define STATUS_FLAG_20 0x20
|
||||
|
||||
#define MEMORY_SIZE_8K 0x00
|
||||
#define MEMORY_SIZE_64K 0x01
|
||||
#define MEMORY_SIZE_128K 0x02
|
||||
#define MEMORY_SIZE_512K 0x04
|
||||
|
||||
#define STATUS_BUSY 0x01 // WTF / ???
|
||||
#define STATUS_READY 0x02
|
||||
#define STATUS_BUTTON_PRESSED 0x04
|
||||
|
||||
#define CHANNEL_A 0x1000
|
||||
#define CHANNEL_B 0x2000
|
||||
#define CHANNEL_C 0x3000
|
||||
#define CHANNEL_D 0x4000
|
||||
|
||||
#define FREQ_SCALE_HZ 0
|
||||
#define FREQ_SCALE_KHZ 1
|
||||
#define FREQ_SCALE_MHZ 2
|
||||
|
||||
#define FILTER_HIGH 0
|
||||
#define FILTER_LOW 1
|
||||
|
||||
#define COMPRESSION_NONE 0x0001
|
||||
#define COMPRESSION_ENABLE 0x8001
|
||||
#define COMPRESSION_DOUBLE 0x8002
|
||||
|
||||
enum {
|
||||
TRIGGER_HIGH = 0,
|
||||
TRIGGER_LOW,
|
||||
TRIGGER_POSEDGE,
|
||||
TRIGGER_NEGEDGE,
|
||||
TRIGGER_ANYEDGE
|
||||
};
|
||||
|
||||
void analyzer_set_freq(int freq, int scale);
|
||||
void analyzer_set_ramsize_trigger_address(unsigned int address);
|
||||
void analyzer_set_triggerbar_address(unsigned int address);
|
||||
void analyzer_set_compression(unsigned int type);
|
||||
void analyzer_set_memory_size(unsigned int size);
|
||||
void analyzer_add_trigger(int channel, int type);
|
||||
void analyzer_set_trigger_count(int count);
|
||||
void analyzer_add_filter(int channel, int type);
|
||||
|
||||
unsigned int analyzer_read_id(libusb_device_handle *devh);
|
||||
unsigned int analyzer_get_stop_address(libusb_device_handle *devh);
|
||||
unsigned int analyzer_get_now_address(libusb_device_handle *devh);
|
||||
unsigned int analyzer_get_trigger_address(libusb_device_handle *devh);
|
||||
int analyzer_decompress(void *input, unsigned int input_len, void *output, unsigned int output_len);
|
||||
|
||||
void analyzer_reset(libusb_device_handle *devh);
|
||||
void analyzer_initialize(libusb_device_handle *devh);
|
||||
void analyzer_wait(libusb_device_handle *devh, int set, int unset);
|
||||
void analyzer_read_start(libusb_device_handle *devh);
|
||||
int analyzer_read_data(libusb_device_handle *devh, void *buffer, unsigned int size);
|
||||
void analyzer_read_stop(libusb_device_handle *devh);
|
||||
void analyzer_start(libusb_device_handle *devh);
|
||||
void analyzer_configure(libusb_device_handle *devh);
|
||||
|
||||
void analyzer_wait_button(libusb_device_handle *devh);
|
||||
void analyzer_wait_data(libusb_device_handle *devh);
|
||||
#endif
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
Copyright (c) 2010 Sven Peter <sven@fail0verflow.com>
|
||||
Copyright (c) 2010 Haxx Enterprises <bushing@gmail.com>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or
|
||||
without modification, are permitted provided that the following
|
||||
conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#include <libusb.h>
|
||||
#include <stdio.h>
|
||||
#include "gl_usb.h"
|
||||
|
||||
#define CTRL_IN (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_INTERFACE)
|
||||
#define CTRL_OUT (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT | LIBUSB_RECIPIENT_INTERFACE)
|
||||
const int PACKET_CTRL_LEN=2;
|
||||
|
||||
const int PACKET_INT_LEN=2;
|
||||
const int PACKET_BULK_LEN=64;
|
||||
const int INTERFACE=0;
|
||||
const int ENDPOINT_INT_IN=0x81; /* endpoint 0x81 address for IN */
|
||||
const int ENDPOINT_INT_OUT=0x01; /* endpoint 1 address for OUT */
|
||||
const int ENDPOINT_BULK_IN=0x81; /* endpoint 0x81 address for IN */
|
||||
const int ENDPOINT_BULK_OUT=0x02; /* endpoint 1 address for OUT */
|
||||
const int TIMEOUT=5000; /* timeout in ms */
|
||||
|
||||
enum {
|
||||
REQ_READBULK = 0x82,
|
||||
REQ_WRITEADDR,
|
||||
REQ_READDATA,
|
||||
REQ_WRITEDATA,
|
||||
};
|
||||
|
||||
static struct libusb_device_handle *g_devh = NULL;
|
||||
|
||||
int gl_write_address(libusb_device_handle *devh, unsigned int address)
|
||||
{
|
||||
unsigned char packet[8] = {address & 0xFF};
|
||||
int retval = libusb_control_transfer(devh, CTRL_OUT, 0xc, REQ_WRITEADDR, 0, packet, 1,TIMEOUT);
|
||||
if (retval != 1) printf("%s: libusb_control_transfer returned %d\n", __FUNCTION__, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int gl_write_data(libusb_device_handle *devh, unsigned int val)
|
||||
{
|
||||
unsigned char packet[8] = {val & 0xFF};
|
||||
int retval = libusb_control_transfer(devh, CTRL_OUT, 0xc, REQ_WRITEDATA, 0, packet, 1,TIMEOUT);
|
||||
if (retval != 1) printf("%s: libusb_control_transfer returned %d\n", __FUNCTION__, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int gl_read_data(libusb_device_handle *devh)
|
||||
{
|
||||
unsigned char packet[8] = {0};
|
||||
int retval = libusb_control_transfer(devh, CTRL_IN, 0xc, REQ_READDATA, 0, packet, 1,TIMEOUT);
|
||||
if (retval != 1) printf("%s: libusb_control_transfer returned %d, val=%hhx\n", __FUNCTION__, retval, packet[0]);
|
||||
return retval == 1 ? packet[0] : retval;
|
||||
}
|
||||
|
||||
int gl_read_bulk(libusb_device_handle *devh, void *buffer, unsigned int size)
|
||||
{
|
||||
unsigned char packet[8] = {0, 0, 0, 0, size & 0xff, (size & 0xff00) >> 8, (size & 0xff0000) >> 16, (size & 0xff000000)>>24};
|
||||
int transferred = 0;
|
||||
|
||||
int retval = libusb_control_transfer(devh, CTRL_OUT, 0x4, REQ_READBULK, 0, packet, 8, TIMEOUT);
|
||||
if (retval != 8) printf("%s: libusb_control_transfer returned %d\n", __FUNCTION__, retval);
|
||||
|
||||
retval = libusb_bulk_transfer(devh, ENDPOINT_BULK_IN, buffer, size,
|
||||
&transferred, TIMEOUT);
|
||||
if (retval < 0) {
|
||||
fprintf(stderr, "Bulk read error %d\n", retval);
|
||||
}
|
||||
return transferred;
|
||||
}
|
||||
|
||||
int gl_reg_write(libusb_device_handle *devh, unsigned int reg, unsigned int val)
|
||||
{
|
||||
int retval;
|
||||
retval = gl_write_address(devh, reg);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
retval = gl_write_data(devh, val);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int gl_reg_read(libusb_device_handle *devh, unsigned int reg)
|
||||
{
|
||||
int retval;
|
||||
retval = gl_write_address(devh, reg);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
retval = gl_read_data(devh);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int gl_open(int vid)
|
||||
{
|
||||
int r = 1;
|
||||
|
||||
r = libusb_init(NULL);
|
||||
if (r < 0)
|
||||
return GL_ELIBUSB;
|
||||
|
||||
libusb_set_debug(NULL, 0);
|
||||
|
||||
struct libusb_device **devs;
|
||||
struct libusb_device *dev;
|
||||
size_t i = 0;
|
||||
|
||||
if (libusb_get_device_list(NULL, &devs) < 0) {
|
||||
r = GL_EOPEN;
|
||||
goto gl_open_error;
|
||||
}
|
||||
|
||||
while ((dev = devs[i++]) != NULL) {
|
||||
struct libusb_device_descriptor desc;
|
||||
if (libusb_get_device_descriptor(dev, &desc) < 0)
|
||||
break;
|
||||
|
||||
if (desc.idVendor == vid) {
|
||||
if (libusb_open(dev, &g_devh) < 0)
|
||||
g_devh = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
libusb_free_device_list(devs, 1);
|
||||
|
||||
if (!g_devh) {
|
||||
r = GL_EOPEN;
|
||||
goto gl_open_error;
|
||||
}
|
||||
|
||||
r = libusb_set_configuration(g_devh, 1);
|
||||
if (r < 0) {
|
||||
r = GL_ESETCONFIG;
|
||||
goto gl_open_error;
|
||||
}
|
||||
|
||||
r = libusb_claim_interface(g_devh, 0);
|
||||
if (r < 0) {
|
||||
r = GL_ECLAIM;
|
||||
goto gl_open_error;
|
||||
}
|
||||
|
||||
return GL_OK;
|
||||
|
||||
gl_open_error:
|
||||
gl_close();
|
||||
return r;
|
||||
}
|
||||
|
||||
int gl_close(void)
|
||||
{
|
||||
if (g_devh) {
|
||||
libusb_release_interface(g_devh, 0);
|
||||
libusb_reset_device(g_devh);
|
||||
libusb_close(g_devh);
|
||||
}
|
||||
libusb_exit(NULL);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
Copyright (c) 2010 Sven Peter <sven@fail0verflow.com>
|
||||
Copyright (c) 2010 Haxx Enterprises <bushing@gmail.com>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or
|
||||
without modification, are permitted provided that the following
|
||||
conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
#ifndef GL_H__
|
||||
#define GL_G__ 1
|
||||
#include <libusb.h>
|
||||
|
||||
#define GL_OK 0
|
||||
#define GL_ELIBUSB -1
|
||||
#define GL_EOPEN -2
|
||||
#define GL_ESETCONFIG -3
|
||||
#define GL_ECLAIM -4
|
||||
|
||||
int gl_write_address(libusb_device_handle *devh, unsigned int address);
|
||||
int gl_write_data(libusb_device_handle *devh, unsigned int val);
|
||||
int gl_read_data(libusb_device_handle *devh);
|
||||
int gl_read_bulk(libusb_device_handle *devh, void *buffer, unsigned int size);
|
||||
int gl_reg_write(libusb_device_handle *devh, unsigned int reg, unsigned int val);
|
||||
int gl_reg_read(libusb_device_handle *devh, unsigned int reg);
|
||||
int gl_open(int vid);
|
||||
int gl_close(void);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,551 @@
|
|||
/*
|
||||
* This file is part of the sigrok project.
|
||||
*
|
||||
* Copyright (C) 2010 Bert Vermeulen <bert@biot.com>
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
#include <inttypes.h>
|
||||
#include <glib.h>
|
||||
#include <libusb.h>
|
||||
#include "config.h"
|
||||
#include "sigrok.h"
|
||||
#include "analyzer.h"
|
||||
|
||||
#define USB_VENDOR 0x0c12
|
||||
#define USB_VENDOR_NAME "Zeroplus"
|
||||
#define USB_MODEL_NAME "Logic Cube"
|
||||
#define USB_MODEL_VERSION ""
|
||||
|
||||
#define USB_INTERFACE 0
|
||||
#define USB_CONFIGURATION 1
|
||||
#define NUM_TRIGGER_STAGES 4
|
||||
#define TRIGGER_TYPES "01"
|
||||
|
||||
#define PACKET_SIZE 2048 // ??
|
||||
|
||||
typedef struct {
|
||||
unsigned short pid;
|
||||
char model_name[64];
|
||||
unsigned int channels;
|
||||
unsigned int sample_depth; // in Ksamples/channel
|
||||
unsigned int max_sampling_freq;
|
||||
} model_t;
|
||||
|
||||
/* Note -- 16032, 16064 and 16128 *usually* -- but not always -- have the same 128K sample depth */
|
||||
model_t zeroplus_models[] = {
|
||||
{0x7009, "LAP-C(16064)", 16, 64, 100},
|
||||
{0x700A, "LAP-C(16128)", 16, 128, 200},
|
||||
{0x700B, "LAP-C(32128)", 32, 128, 200},
|
||||
{0x700C, "LAP-C(321000)", 32, 1024, 200},
|
||||
{0x700D, "LAP-C(322000)", 32, 2048, 200},
|
||||
{0x700E, "LAP-C(16032)", 16, 32, 100},
|
||||
{0x7016, "LAP-C(162000)", 16, 2048, 200},
|
||||
};
|
||||
|
||||
static int capabilities[] = {
|
||||
HWCAP_LOGIC_ANALYZER,
|
||||
HWCAP_SAMPLERATE,
|
||||
HWCAP_PROBECONFIG,
|
||||
HWCAP_CAPTURE_RATIO,
|
||||
/* these are really implemented in the driver, not the hardware */
|
||||
|
||||
HWCAP_LIMIT_SAMPLES,
|
||||
0
|
||||
};
|
||||
|
||||
/* list of struct sigrok_device_instance, maintained by opendev() and closedev() */
|
||||
static GSList *device_instances = NULL;
|
||||
|
||||
static libusb_context *usb_context = NULL;
|
||||
|
||||
/* The hardware supports more sample rates than these, but these are the options
|
||||
hardcoded into the vendor's Windows GUI */
|
||||
|
||||
// XXX we shouldn't support 150MHz and 200MHz on devices that don't go up that high
|
||||
static uint64_t supported_samplerates[] = {
|
||||
100,
|
||||
500,
|
||||
KHZ(1),
|
||||
KHZ(5),
|
||||
KHZ(25),
|
||||
KHZ(50),
|
||||
KHZ(100),
|
||||
KHZ(200),
|
||||
KHZ(400),
|
||||
KHZ(800),
|
||||
MHZ(1),
|
||||
MHZ(10),
|
||||
MHZ(25),
|
||||
MHZ(50),
|
||||
MHZ(80),
|
||||
MHZ(100),
|
||||
MHZ(150),
|
||||
MHZ(200),
|
||||
0
|
||||
};
|
||||
|
||||
static struct samplerates samplerates = {
|
||||
0,0,0,
|
||||
supported_samplerates
|
||||
};
|
||||
|
||||
/* TODO: all of these should go in a device-specific struct */
|
||||
static uint64_t cur_samplerate = 0;
|
||||
static uint64_t limit_samples = 0;
|
||||
uint8_t num_channels = 32; // XXX this is not getting initialized before it is needed :(
|
||||
uint64_t memory_size = 0;
|
||||
static uint8_t probe_mask = 0, \
|
||||
trigger_mask[NUM_TRIGGER_STAGES] = {0}, \
|
||||
trigger_value[NUM_TRIGGER_STAGES] = {0}, \
|
||||
trigger_buffer[NUM_TRIGGER_STAGES] = {0};;
|
||||
|
||||
|
||||
static int hw_set_configuration(int device_index, int capability, void *value);
|
||||
|
||||
static unsigned int get_memory_size(int type)
|
||||
{
|
||||
if (type == MEMORY_SIZE_8K)
|
||||
return 8*1024;
|
||||
else if (type == MEMORY_SIZE_64K)
|
||||
return 64*1024;
|
||||
else if (type == MEMORY_SIZE_128K)
|
||||
return 128*1024;
|
||||
else if (type == MEMORY_SIZE_512K)
|
||||
return 512*1024;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sigrok_device_instance *zp_open_device(int device_index)
|
||||
{
|
||||
struct sigrok_device_instance *sdi;
|
||||
libusb_device **devlist;
|
||||
struct libusb_device_descriptor des;
|
||||
int err, i, j;
|
||||
|
||||
if(!(sdi = get_sigrok_device_instance(device_instances, device_index)))
|
||||
return NULL;
|
||||
|
||||
libusb_get_device_list(usb_context, &devlist);
|
||||
if(sdi->status == ST_INACTIVE) {
|
||||
/* find the device by vendor, product, bus and address */
|
||||
libusb_get_device_list(usb_context, &devlist);
|
||||
for(i = 0; devlist[i]; i++) {
|
||||
if( (err = libusb_get_device_descriptor(devlist[i], &des)) ) {
|
||||
g_warning("failed to get device descriptor: %d", err);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(des.idVendor == USB_VENDOR) {
|
||||
if(libusb_get_bus_number(devlist[i]) == sdi->usb->bus &&
|
||||
libusb_get_device_address(devlist[i]) == sdi->usb->address) {
|
||||
for (j = 0; j < sizeof(zeroplus_models) / sizeof(zeroplus_models[0]); j++) {
|
||||
if (des.idProduct == zeroplus_models[j].pid) {
|
||||
g_message("Found PID=%04X (%s)", des.idProduct, zeroplus_models[j].model_name);
|
||||
num_channels = zeroplus_models[j].channels;
|
||||
memory_size = zeroplus_models[j].sample_depth * 1024;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (num_channels == 0) {
|
||||
g_warning("Unknown ZeroPlus device %04X", des.idProduct);
|
||||
continue;
|
||||
}
|
||||
/* found it */
|
||||
if( !(err = libusb_open(devlist[i], &(sdi->usb->devhdl))) ) {
|
||||
sdi->status = ST_ACTIVE;
|
||||
g_message("opened device %d on %d.%d interface %d", sdi->index,
|
||||
sdi->usb->bus, sdi->usb->address, USB_INTERFACE);
|
||||
}
|
||||
else {
|
||||
g_warning("failed to open device: %d", err);
|
||||
sdi = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* status must be ST_ACTIVE, i.e. already in use... */
|
||||
sdi = NULL;
|
||||
}
|
||||
libusb_free_device_list(devlist, 1);
|
||||
|
||||
if(sdi && sdi->status != ST_ACTIVE)
|
||||
sdi = NULL;
|
||||
|
||||
return sdi;
|
||||
}
|
||||
|
||||
|
||||
static void close_device(struct sigrok_device_instance *sdi)
|
||||
{
|
||||
|
||||
if(sdi->usb->devhdl)
|
||||
{
|
||||
g_message("closing device %d on %d.%d interface %d", sdi->index, sdi->usb->bus,
|
||||
sdi->usb->address, USB_INTERFACE);
|
||||
libusb_release_interface(sdi->usb->devhdl, USB_INTERFACE);
|
||||
libusb_close(sdi->usb->devhdl);
|
||||
sdi->usb->devhdl = NULL;
|
||||
sdi->status = ST_INACTIVE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static int configure_probes(GSList *probes)
|
||||
{
|
||||
struct probe *probe;
|
||||
GSList *l;
|
||||
int probe_bit, stage, i;
|
||||
char *tc;
|
||||
|
||||
probe_mask = 0;
|
||||
for(i = 0; i < NUM_TRIGGER_STAGES; i++)
|
||||
{
|
||||
trigger_mask[i] = 0;
|
||||
trigger_value[i] = 0;
|
||||
}
|
||||
|
||||
stage = -1;
|
||||
for(l = probes; l; l = l->next)
|
||||
{
|
||||
probe = (struct probe *) l->data;
|
||||
if(probe->enabled == FALSE)
|
||||
continue;
|
||||
probe_bit = 1 << (probe->index - 1);
|
||||
probe_mask |= probe_bit;
|
||||
if(probe->trigger)
|
||||
{
|
||||
stage = 0;
|
||||
for(tc = probe->trigger; *tc; tc++)
|
||||
{
|
||||
trigger_mask[stage] |= probe_bit;
|
||||
if(*tc == '1')
|
||||
trigger_value[stage] |= probe_bit;
|
||||
stage++;
|
||||
if(stage > NUM_TRIGGER_STAGES)
|
||||
return SIGROK_NOK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SIGROK_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* API callbacks
|
||||
*/
|
||||
|
||||
static int hw_init(char *deviceinfo)
|
||||
{
|
||||
struct sigrok_device_instance *sdi;
|
||||
struct libusb_device_descriptor des;
|
||||
libusb_device **devlist;
|
||||
int err, devcnt, i;
|
||||
|
||||
if(libusb_init(&usb_context) != 0) {
|
||||
g_warning("Failed to initialize USB.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* find all ZeroPlus analyzers and add them to device list */
|
||||
devcnt = 0;
|
||||
libusb_get_device_list(usb_context, &devlist);
|
||||
for(i = 0; devlist[i]; i++) {
|
||||
err = libusb_get_device_descriptor(devlist[i], &des);
|
||||
if(err != 0) {
|
||||
g_warning("failed to get device descriptor: %d", err);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(des.idVendor == USB_VENDOR) {
|
||||
/* definitely a Zeroplus */
|
||||
/* TODO: any way to detect specific model/version in the zeroplus range? */
|
||||
sdi = sigrok_device_instance_new(devcnt, ST_INACTIVE,
|
||||
USB_VENDOR_NAME, USB_MODEL_NAME, USB_MODEL_VERSION);
|
||||
if(!sdi)
|
||||
return 0;
|
||||
device_instances = g_slist_append(device_instances, sdi);
|
||||
sdi->usb = usb_device_instance_new(libusb_get_bus_number(devlist[i]),
|
||||
libusb_get_device_address(devlist[i]), NULL);
|
||||
devcnt++;
|
||||
}
|
||||
}
|
||||
libusb_free_device_list(devlist, 1);
|
||||
|
||||
return devcnt;
|
||||
}
|
||||
|
||||
|
||||
static int hw_opendev(int device_index)
|
||||
{
|
||||
struct sigrok_device_instance *sdi;
|
||||
int err;
|
||||
|
||||
if( !(sdi = zp_open_device(device_index)) ) {
|
||||
g_warning("unable to open device");
|
||||
return SIGROK_NOK;
|
||||
}
|
||||
|
||||
err = libusb_claim_interface(sdi->usb->devhdl, USB_INTERFACE);
|
||||
if(err != 0) {
|
||||
g_warning("Unable to claim interface: %d", err);
|
||||
return SIGROK_NOK;
|
||||
}
|
||||
analyzer_reset(sdi->usb->devhdl);
|
||||
analyzer_initialize(sdi->usb->devhdl);
|
||||
analyzer_configure(sdi->usb->devhdl);
|
||||
|
||||
analyzer_set_memory_size(MEMORY_SIZE_512K);
|
||||
// analyzer_set_freq(g_freq, g_freq_scale);
|
||||
analyzer_set_trigger_count(1);
|
||||
// analyzer_set_ramsize_trigger_address((((100 - g_pre_trigger) * get_memory_size(g_memory_size)) / 100) >> 2);
|
||||
analyzer_set_ramsize_trigger_address((100 * get_memory_size(MEMORY_SIZE_512K) / 100) >> 2);
|
||||
|
||||
/* if (g_double_mode == 1)
|
||||
analyzer_set_compression(COMPRESSION_DOUBLE);
|
||||
else if (g_compression == 1)
|
||||
analyzer_set_compression(COMPRESSION_ENABLE);
|
||||
else */
|
||||
analyzer_set_compression(COMPRESSION_NONE);
|
||||
|
||||
if(cur_samplerate == 0) {
|
||||
/* sample rate hasn't been set; default to the slowest it has */
|
||||
if(hw_set_configuration(device_index, HWCAP_SAMPLERATE, &samplerates.low) == SIGROK_NOK)
|
||||
return SIGROK_NOK;
|
||||
}
|
||||
|
||||
return SIGROK_OK;
|
||||
}
|
||||
|
||||
|
||||
static void hw_closedev(int device_index)
|
||||
{
|
||||
struct sigrok_device_instance *sdi;
|
||||
|
||||
if( (sdi = get_sigrok_device_instance(device_instances, device_index)) )
|
||||
close_device(sdi);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void hw_cleanup(void)
|
||||
{
|
||||
GSList *l;
|
||||
|
||||
/* properly close all devices */
|
||||
for(l = device_instances; l; l = l->next)
|
||||
close_device( (struct sigrok_device_instance *) l->data);
|
||||
|
||||
/* and free all their memory */
|
||||
for(l = device_instances; l; l = l->next)
|
||||
g_free(l->data);
|
||||
g_slist_free(device_instances);
|
||||
device_instances = NULL;
|
||||
|
||||
if(usb_context)
|
||||
libusb_exit(usb_context);
|
||||
usb_context = NULL;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void *hw_get_device_info(int device_index, int device_info_id)
|
||||
{
|
||||
struct sigrok_device_instance *sdi;
|
||||
void *info;
|
||||
|
||||
if( !(sdi = get_sigrok_device_instance(device_instances, device_index)) )
|
||||
return NULL;
|
||||
|
||||
info = NULL;
|
||||
switch(device_info_id)
|
||||
{
|
||||
case DI_INSTANCE:
|
||||
info = sdi;
|
||||
break;
|
||||
case DI_NUM_PROBES:
|
||||
info = GINT_TO_POINTER(num_channels);
|
||||
break;
|
||||
case DI_SAMPLERATES:
|
||||
info = &samplerates;
|
||||
break;
|
||||
case DI_TRIGGER_TYPES:
|
||||
info = TRIGGER_TYPES;
|
||||
break;
|
||||
case DI_CUR_SAMPLE_RATE:
|
||||
info = &cur_samplerate;
|
||||
break;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
static int hw_get_status(int device_index)
|
||||
{
|
||||
struct sigrok_device_instance *sdi;
|
||||
|
||||
sdi = get_sigrok_device_instance(device_instances, device_index);
|
||||
if(sdi)
|
||||
return sdi->status;
|
||||
else
|
||||
return ST_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
||||
static int *hw_get_capabilities(void)
|
||||
{
|
||||
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
// XXX this will set the same samplerate for all devices
|
||||
int set_configuration_samplerate(struct sigrok_device_instance *sdi, uint64_t samplerate)
|
||||
{
|
||||
g_message("%s(%llu)", __FUNCTION__, samplerate);
|
||||
if (samplerate > MHZ(1))
|
||||
analyzer_set_freq(samplerate / MHZ(1), FREQ_SCALE_MHZ);
|
||||
else if (samplerate > KHZ(1))
|
||||
analyzer_set_freq(samplerate / KHZ(1), FREQ_SCALE_KHZ);
|
||||
else
|
||||
analyzer_set_freq(samplerate , FREQ_SCALE_HZ);
|
||||
|
||||
cur_samplerate = samplerate;
|
||||
|
||||
return SIGROK_OK;
|
||||
}
|
||||
|
||||
static int hw_set_configuration(int device_index, int capability, void *value)
|
||||
{
|
||||
struct sigrok_device_instance *sdi;
|
||||
uint64_t *tmp_u64;
|
||||
|
||||
if( !(sdi = get_sigrok_device_instance(device_instances, device_index)) )
|
||||
return SIGROK_NOK;
|
||||
|
||||
switch (capability) {
|
||||
case HWCAP_SAMPLERATE:
|
||||
tmp_u64 = value;
|
||||
return set_configuration_samplerate(sdi, *tmp_u64);
|
||||
|
||||
case HWCAP_PROBECONFIG:
|
||||
return configure_probes( (GSList *) value);
|
||||
|
||||
case HWCAP_LIMIT_SAMPLES:
|
||||
limit_samples = strtoull(value, NULL, 10);
|
||||
return SIGROK_OK;
|
||||
|
||||
default:
|
||||
return SIGROK_NOK;
|
||||
}
|
||||
}
|
||||
|
||||
static int hw_start_acquisition(int device_index, gpointer session_device_id)
|
||||
{
|
||||
struct sigrok_device_instance *sdi;
|
||||
struct datafeed_packet packet;
|
||||
struct datafeed_header header;
|
||||
int res;
|
||||
int packet_num;
|
||||
unsigned char *buf;
|
||||
|
||||
if( !(sdi = get_sigrok_device_instance(device_instances, device_index)))
|
||||
return SIGROK_NOK;
|
||||
|
||||
analyzer_start(sdi->usb->devhdl);
|
||||
g_message("Waiting for data");
|
||||
analyzer_wait_data(sdi->usb->devhdl);
|
||||
|
||||
g_message("Stop address = 0x%x", analyzer_get_stop_address(sdi->usb->devhdl));
|
||||
g_message("Now address = 0x%x", analyzer_get_now_address(sdi->usb->devhdl));
|
||||
g_message("Trigger address = 0x%x", analyzer_get_trigger_address(sdi->usb->devhdl));
|
||||
|
||||
packet.type = DF_HEADER;
|
||||
packet.length = sizeof(struct datafeed_header);
|
||||
packet.payload = (unsigned char *) &header;
|
||||
header.feed_version = 1;
|
||||
gettimeofday(&header.starttime, NULL);
|
||||
header.rate = cur_samplerate;
|
||||
header.protocol_id = PROTO_RAW;
|
||||
header.num_probes = num_channels;
|
||||
session_bus(session_device_id, &packet);
|
||||
|
||||
buf = g_malloc(PACKET_SIZE);
|
||||
if (!buf)
|
||||
return SIGROK_NOK;
|
||||
analyzer_read_start(sdi->usb->devhdl);
|
||||
/* send the incoming transfer to the session bus */
|
||||
for(packet_num = 0; packet_num < (memory_size * 4 / PACKET_SIZE); packet_num++) {
|
||||
res = analyzer_read_data(sdi->usb->devhdl, buf, PACKET_SIZE);
|
||||
// g_message("Tried to read %llx bytes, actually read %x bytes", PACKET_SIZE, res);
|
||||
|
||||
packet.type = DF_LOGIC32;
|
||||
packet.length = PACKET_SIZE;
|
||||
packet.payload = buf;
|
||||
session_bus(session_device_id, &packet);
|
||||
}
|
||||
analyzer_read_stop(sdi->usb->devhdl);
|
||||
g_free(buf);
|
||||
|
||||
packet.type = DF_END;
|
||||
session_bus(session_device_id, &packet);
|
||||
|
||||
return SIGROK_OK;
|
||||
}
|
||||
|
||||
|
||||
/* this stops acquisition on ALL devices, ignoring device_index */
|
||||
static void hw_stop_acquisition(int device_index, gpointer session_device_id)
|
||||
{
|
||||
struct datafeed_packet packet;
|
||||
struct sigrok_device_instance *sdi;
|
||||
|
||||
packet.type = DF_END;
|
||||
session_bus(session_device_id, &packet);
|
||||
|
||||
if( !(sdi = get_sigrok_device_instance(device_instances, device_index)))
|
||||
return; // XXX cry?
|
||||
|
||||
analyzer_reset(sdi->usb->devhdl);
|
||||
/* TODO: need to cancel and free any queued up transfers */
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct device_plugin zeroplus_logic_cube_plugin_info = {
|
||||
"zeroplus-logic-cube",
|
||||
1,
|
||||
hw_init,
|
||||
hw_cleanup,
|
||||
|
||||
hw_opendev,
|
||||
hw_closedev,
|
||||
hw_get_device_info,
|
||||
hw_get_status,
|
||||
hw_get_capabilities,
|
||||
hw_set_configuration,
|
||||
hw_start_acquisition,
|
||||
hw_stop_acquisition
|
||||
};
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
* This file is part of the sigrok project.
|
||||
*
|
||||
* Copyright (C) 2010 Bert Vermeulen <bert@biot.com>
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
#include <gmodule.h>
|
||||
#include "sigrok.h"
|
||||
|
||||
source_callback_add source_cb_add = NULL;
|
||||
source_callback_remove source_cb_remove = NULL;
|
||||
|
||||
/* the list of loaded plugins lives here */
|
||||
GSList *plugins;
|
||||
|
||||
/* this enumerates which plugin capabilities correspond to user-settable options */
|
||||
struct hwcap_option hwcap_options[] = {
|
||||
{ HWCAP_SAMPLERATE, T_UINT64, "Sample rate", "samplerate" },
|
||||
{ 0, 0, NULL, NULL }
|
||||
};
|
||||
|
||||
extern struct device_plugin saleae_logic_plugin_info;
|
||||
extern struct device_plugin ols_plugin_info;
|
||||
extern struct device_plugin zeroplus_logic_cube_plugin_info;
|
||||
|
||||
int load_hwplugins(void)
|
||||
{
|
||||
plugins = g_slist_append(plugins, (gpointer *)&saleae_logic_plugin_info);
|
||||
plugins = g_slist_append(plugins, (gpointer *)&ols_plugin_info);
|
||||
plugins = g_slist_append(plugins, (gpointer *)&zeroplus_logic_cube_plugin_info);
|
||||
|
||||
return SIGROK_OK;
|
||||
}
|
||||
|
||||
|
||||
GSList *list_hwplugins(void)
|
||||
{
|
||||
|
||||
return plugins;
|
||||
}
|
||||
|
||||
|
||||
struct sigrok_device_instance *sigrok_device_instance_new(int index, int status,
|
||||
char *vendor, char *model, char *version)
|
||||
{
|
||||
struct sigrok_device_instance *sdi;
|
||||
|
||||
sdi = malloc(sizeof(struct sigrok_device_instance));
|
||||
if(!sdi)
|
||||
return NULL;
|
||||
|
||||
sdi->index = index;
|
||||
sdi->status = status;
|
||||
sdi->instance_type = -1;
|
||||
sdi->vendor = strdup(vendor);
|
||||
sdi->model = strdup(model);
|
||||
sdi->version = strdup(version);
|
||||
sdi->usb = NULL;
|
||||
|
||||
return sdi;
|
||||
}
|
||||
|
||||
|
||||
struct sigrok_device_instance *get_sigrok_device_instance(GSList *device_instances, int device_index)
|
||||
{
|
||||
struct sigrok_device_instance *sdi;
|
||||
GSList *l;
|
||||
|
||||
sdi = NULL;
|
||||
for(l = device_instances; l; l = l->next) {
|
||||
sdi = (struct sigrok_device_instance *) (l->data);
|
||||
if(sdi->index == device_index)
|
||||
return sdi;
|
||||
}
|
||||
g_warning("could not find device index %d instance", device_index);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void sigrok_device_instance_free(struct sigrok_device_instance *sdi)
|
||||
{
|
||||
|
||||
switch(sdi->instance_type) {
|
||||
case USB_INSTANCE:
|
||||
usb_device_instance_free(sdi->usb);
|
||||
break;
|
||||
case SERIAL_INSTANCE:
|
||||
serial_device_instance_free(sdi->serial);
|
||||
break;
|
||||
/* no specific type, nothing extra to free */
|
||||
}
|
||||
|
||||
free(sdi->vendor);
|
||||
free(sdi->model);
|
||||
free(sdi->version);
|
||||
free(sdi);
|
||||
|
||||
}
|
||||
|
||||
|
||||
struct usb_device_instance *usb_device_instance_new(uint8_t bus, uint8_t address,
|
||||
struct libusb_device_handle *hdl)
|
||||
{
|
||||
struct usb_device_instance *udi;
|
||||
|
||||
udi = malloc(sizeof(struct usb_device_instance));
|
||||
if(!udi)
|
||||
return NULL;
|
||||
|
||||
udi->bus = bus;
|
||||
udi->address = address;
|
||||
udi->devhdl = hdl;
|
||||
|
||||
return udi;
|
||||
}
|
||||
|
||||
|
||||
void usb_device_instance_free(struct usb_device_instance *usb)
|
||||
{
|
||||
|
||||
/* nothing to do for this device instance type */
|
||||
|
||||
}
|
||||
|
||||
|
||||
struct serial_device_instance *serial_device_instance_new(char *port, int fd)
|
||||
{
|
||||
struct serial_device_instance *serial;
|
||||
|
||||
serial = malloc(sizeof(struct serial_device_instance));
|
||||
if(!serial)
|
||||
return NULL;
|
||||
|
||||
serial->port = strdup(port);
|
||||
serial->fd = fd;
|
||||
|
||||
return serial;
|
||||
}
|
||||
|
||||
|
||||
void serial_device_instance_free(struct serial_device_instance *serial)
|
||||
{
|
||||
|
||||
free(serial->port);
|
||||
|
||||
}
|
||||
|
||||
|
||||
int find_hwcap(int *capabilities, int hwcap)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; capabilities[i]; i++)
|
||||
if(capabilities[i] == hwcap)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
struct hwcap_option *find_hwcap_option(int hwcap)
|
||||
{
|
||||
struct hwcap_option *hwo;
|
||||
int i;
|
||||
|
||||
hwo = NULL;
|
||||
for(i = 0; hwcap_options[i].capability; i++)
|
||||
{
|
||||
if(hwcap_options[i].capability == hwcap)
|
||||
{
|
||||
hwo = &hwcap_options[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return hwo;
|
||||
}
|
||||
|
||||
|
||||
void source_remove(int fd)
|
||||
{
|
||||
|
||||
if(source_cb_remove)
|
||||
source_cb_remove(fd);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void source_add(int fd, int events, int timeout, receive_data_callback rcv_cb, void *user_data)
|
||||
{
|
||||
|
||||
if(source_cb_add)
|
||||
source_cb_add(fd, events, timeout, rcv_cb, user_data);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* This file is part of the sigrok project.
|
||||
*
|
||||
* Copyright (C) 2010 Bert Vermeulen <bert@biot.com>
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "sigrok.h"
|
||||
|
||||
extern struct output_format output_text_binary;
|
||||
extern struct output_format output_text_hex;
|
||||
|
||||
|
||||
struct output_format *output_module_list[] = {
|
||||
&output_text_binary,
|
||||
&output_text_hex,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct output_format **output_list(void)
|
||||
{
|
||||
|
||||
return output_module_list;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* This file is part of the sigrok project.
|
||||
*
|
||||
* Copyright (C) 2010 YOURNAME <YOUREMAIL>
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "sigrok.h"
|
||||
|
||||
|
||||
|
||||
static void init(struct output *o)
|
||||
{
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
static int data(struct output *o, char *data_in, uint64_t length_in, char **data_out, uint64_t *length_out)
|
||||
{
|
||||
|
||||
return SIGROK_OK;
|
||||
}
|
||||
|
||||
|
||||
static int event(struct output *o, int event_type, char **data_out, uint64_t *length_out)
|
||||
{
|
||||
|
||||
return SIGROK_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
struct output_format output_foo = {
|
||||
"foo",
|
||||
"The foo format",
|
||||
init,
|
||||
data,
|
||||
event
|
||||
};
|
||||
|
||||
|
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
* This file is part of the sigrok project.
|
||||
*
|
||||
* Copyright (C) 2010 Bert Vermeulen <bert@biot.com>
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
#include "sigrok.h"
|
||||
|
||||
#define DEFAULT_BPL_BIN 64
|
||||
#define DEFAULT_BPL_HEX 256
|
||||
|
||||
struct context {
|
||||
int num_enabled_probes;
|
||||
int samples_per_line;
|
||||
int unitsize;
|
||||
int line_offset;
|
||||
int linebuf_len;
|
||||
char *probelist[65];
|
||||
char *linebuf;
|
||||
int spl_cnt;
|
||||
uint8_t *linevalues;
|
||||
char *header;
|
||||
};
|
||||
|
||||
|
||||
static void flush_linebufs(struct context *ctx, GSList *probes, char *outbuf)
|
||||
{
|
||||
static int max_probename_len = 0;
|
||||
int len, i;
|
||||
|
||||
if(ctx->linebuf[0] == 0)
|
||||
return;
|
||||
|
||||
if(max_probename_len == 0) {
|
||||
/* first time through */
|
||||
for(i = 0; ctx->probelist[i]; i++) {
|
||||
len = strlen(ctx->probelist[i]);
|
||||
if(len > max_probename_len)
|
||||
max_probename_len = len;
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0; ctx->probelist[i]; i++) {
|
||||
sprintf(outbuf + strlen(outbuf), "%*s:%s\n", max_probename_len, ctx->probelist[i],
|
||||
ctx->linebuf + i * ctx->linebuf_len);
|
||||
}
|
||||
memset(ctx->linebuf, 0, i * ctx->linebuf_len);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void init(struct output *o, int default_spl)
|
||||
{
|
||||
struct context *ctx;
|
||||
struct probe *probe;
|
||||
GSList *l;
|
||||
uint64_t samplerate;
|
||||
int num_probes;
|
||||
|
||||
ctx = malloc(sizeof(struct context));
|
||||
o->internal = ctx;
|
||||
ctx->num_enabled_probes = 0;
|
||||
for(l = o->device->probes; l; l = l->next) {
|
||||
probe = l->data;
|
||||
if(probe->enabled)
|
||||
ctx->probelist[ctx->num_enabled_probes++] = probe->name;
|
||||
}
|
||||
ctx->probelist[ctx->num_enabled_probes] = 0;
|
||||
ctx->unitsize = (ctx->num_enabled_probes + 7) / 8;
|
||||
ctx->line_offset = 0;
|
||||
ctx->spl_cnt = 0;
|
||||
if(o->param && o->param[0])
|
||||
ctx->samples_per_line = strtoul(o->param, NULL, 10);
|
||||
else
|
||||
ctx->samples_per_line = default_spl;
|
||||
|
||||
ctx->header = malloc(512);
|
||||
num_probes = g_slist_length(o->device->probes);
|
||||
samplerate = *((uint64_t *) o->device->plugin->get_device_info(o->device->plugin_index, DI_CUR_SAMPLE_RATE));
|
||||
snprintf(ctx->header, 512, "Acquisition with %d/%d probes at ", ctx->num_enabled_probes, num_probes);
|
||||
if(samplerate >= GHZ(1))
|
||||
snprintf(ctx->header + strlen(ctx->header), 512, "%"PRIu64" GHz", samplerate / 1000000000);
|
||||
else if(samplerate >= MHZ(1))
|
||||
snprintf(ctx->header + strlen(ctx->header), 512, "%"PRIu64" MHz", samplerate / 1000000);
|
||||
else if(samplerate >= KHZ(1))
|
||||
snprintf(ctx->header + strlen(ctx->header), 512, "%"PRIu64" KHz", samplerate / 1000);
|
||||
else
|
||||
snprintf(ctx->header + strlen(ctx->header), 512, "%"PRIu64" Hz", samplerate);
|
||||
snprintf(ctx->header + strlen(ctx->header), 512, "\n");
|
||||
|
||||
ctx->linebuf_len = ctx->samples_per_line * 2;
|
||||
ctx->linebuf = calloc(1, num_probes * ctx->linebuf_len);
|
||||
ctx->linevalues = calloc(1, num_probes);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static int event(struct output *o, int event_type, char **data_out, uint64_t *length_out)
|
||||
{
|
||||
struct context *ctx;
|
||||
int outsize;
|
||||
char *outbuf;
|
||||
|
||||
ctx = o->internal;
|
||||
switch(event_type) {
|
||||
case DF_TRIGGER:
|
||||
break;
|
||||
case DF_END:
|
||||
outsize = ctx->num_enabled_probes * (ctx->samples_per_line + 20) + 512;
|
||||
outbuf = calloc(1, outsize);
|
||||
flush_linebufs(ctx, o->device->probes, outbuf);
|
||||
*data_out = outbuf;
|
||||
*length_out = strlen(outbuf);
|
||||
free(o->internal);
|
||||
o->internal = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
return SIGROK_OK;
|
||||
}
|
||||
|
||||
|
||||
static void init_binary(struct output *o)
|
||||
{
|
||||
|
||||
init(o, DEFAULT_BPL_BIN);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static int data_binary(struct output *o, char *data_in, uint64_t length_in, char **data_out, uint64_t *length_out)
|
||||
{
|
||||
struct context *ctx;
|
||||
int outsize, offset, p;
|
||||
uint64_t sample;
|
||||
char *outbuf;
|
||||
|
||||
ctx = o->internal;
|
||||
outsize = length_in / ctx->unitsize * ctx->num_enabled_probes * 3 + 512;
|
||||
outbuf = calloc(1, outsize+1);
|
||||
if(ctx->header) {
|
||||
/* the header is still in here, we must be on the first data packet */
|
||||
strncpy(outbuf, ctx->header, outsize);
|
||||
free(ctx->header);
|
||||
ctx->header = NULL;
|
||||
}
|
||||
else
|
||||
outbuf[0] = 0;
|
||||
|
||||
if(length_in > ctx->unitsize) {
|
||||
for(offset = 0; offset <= length_in - ctx->unitsize; offset += ctx->unitsize) {
|
||||
memcpy(&sample, data_in + offset, ctx->unitsize);
|
||||
for(p = 0; p < ctx->num_enabled_probes; p++) {
|
||||
if(sample & ((uint64_t) 1 << p))
|
||||
ctx->linebuf[p * ctx->linebuf_len + ctx->line_offset] = '1';
|
||||
else
|
||||
ctx->linebuf[p * ctx->linebuf_len + ctx->line_offset] = '0';
|
||||
}
|
||||
ctx->line_offset++;
|
||||
ctx->spl_cnt++;
|
||||
|
||||
/* space every 8th bit */
|
||||
if((ctx->spl_cnt & 7) == 0) {
|
||||
for(p = 0; p < ctx->num_enabled_probes; p++)
|
||||
ctx->linebuf[p * ctx->linebuf_len + ctx->line_offset] = ' ';
|
||||
ctx->line_offset++;
|
||||
}
|
||||
|
||||
/* end of line */
|
||||
if(ctx->spl_cnt >= ctx->samples_per_line) {
|
||||
flush_linebufs(ctx, o->device->probes, outbuf);
|
||||
ctx->line_offset = ctx->spl_cnt = 0;
|
||||
}
|
||||
}
|
||||
} else
|
||||
g_message("short buffer (length_in=%"PRIu64")", length_in);
|
||||
|
||||
*data_out = outbuf;
|
||||
*length_out = strlen(outbuf);
|
||||
|
||||
return SIGROK_OK;
|
||||
}
|
||||
|
||||
|
||||
static void init_hex(struct output *o)
|
||||
{
|
||||
|
||||
init(o, DEFAULT_BPL_BIN);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static int data_hex(struct output *o, char *data_in, uint64_t length_in, char **data_out, uint64_t *length_out)
|
||||
{
|
||||
struct context *ctx;
|
||||
int outsize, offset, p;
|
||||
uint64_t sample;
|
||||
char *outbuf;
|
||||
|
||||
ctx = o->internal;
|
||||
outsize = length_in / ctx->unitsize * ctx->num_enabled_probes * 3 + 512;
|
||||
outbuf = calloc(1, outsize+1);
|
||||
if(ctx->header) {
|
||||
/* the header is still in here, we must be on the first data packet */
|
||||
strncpy(outbuf, ctx->header, outsize);
|
||||
free(ctx->header);
|
||||
ctx->header = NULL;
|
||||
}
|
||||
else
|
||||
outbuf[0] = 0;
|
||||
|
||||
ctx->line_offset = 0;
|
||||
for(offset = 0; offset <= length_in - ctx->unitsize; offset += ctx->unitsize) {
|
||||
memcpy(&sample, data_in + offset, ctx->unitsize);
|
||||
for(p = 0; p < ctx->num_enabled_probes; p++) {
|
||||
ctx->linevalues[p] <<= 1;
|
||||
if(sample & ((uint64_t) 1 << p))
|
||||
ctx->linevalues[p] |= 1;
|
||||
sprintf(ctx->linebuf + (p * ctx->linebuf_len) + ctx->line_offset, "%.2x", ctx->linevalues[p]);
|
||||
}
|
||||
ctx->spl_cnt++;
|
||||
|
||||
/* space after every complete hex byte */
|
||||
if((ctx->spl_cnt & 7) == 0) {
|
||||
for(p = 0; p < ctx->num_enabled_probes; p++)
|
||||
ctx->linebuf[p * ctx->linebuf_len + ctx->line_offset + 2] = ' ';
|
||||
ctx->line_offset += 3;
|
||||
}
|
||||
|
||||
/* end of line */
|
||||
if(ctx->spl_cnt >= ctx->samples_per_line) {
|
||||
flush_linebufs(ctx, o->device->probes, outbuf);
|
||||
ctx->line_offset = ctx->spl_cnt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
*data_out = outbuf;
|
||||
*length_out = strlen(outbuf);
|
||||
|
||||
return SIGROK_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct output_format output_text_binary = {
|
||||
"bin",
|
||||
"Text (binary)",
|
||||
init_binary,
|
||||
data_binary,
|
||||
event
|
||||
};
|
||||
|
||||
|
||||
struct output_format output_text_hex = {
|
||||
"hex",
|
||||
"Text (hexadecimal)",
|
||||
init_hex,
|
||||
data_hex,
|
||||
event
|
||||
};
|
||||
|
|
@ -0,0 +1,284 @@
|
|||
/*
|
||||
* This file is part of the sigrok project.
|
||||
*
|
||||
* Copyright (C) 2010 Bert Vermeulen <bert@biot.com>
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <zip.h>
|
||||
#include "sigrok.h"
|
||||
|
||||
/* there can only be one session at a time */
|
||||
struct session *session;
|
||||
|
||||
|
||||
struct session *session_load(char *filename)
|
||||
{
|
||||
struct session *session;
|
||||
|
||||
/* TODO: implement */
|
||||
session = NULL;
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
|
||||
struct session *session_new(void)
|
||||
{
|
||||
|
||||
session = calloc(1, sizeof(struct session));
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
|
||||
void session_destroy(void)
|
||||
{
|
||||
|
||||
g_slist_free(session->devices);
|
||||
|
||||
/* TODO: loop over protocols and free them */
|
||||
|
||||
g_free(session);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void session_device_clear(void)
|
||||
{
|
||||
|
||||
g_slist_free(session->devices);
|
||||
session->devices = NULL;
|
||||
|
||||
}
|
||||
|
||||
|
||||
int session_device_add(struct device *device)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = device->plugin->open(device->plugin_index);
|
||||
if(ret == SIGROK_OK)
|
||||
session->devices = g_slist_append(session->devices, device);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void session_pa_clear(void)
|
||||
{
|
||||
|
||||
/* the protocols are pointers to the global set of PA plugins, so don't free them */
|
||||
g_slist_free(session->analyzers);
|
||||
session->analyzers = NULL;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void session_pa_add(struct analyzer *an)
|
||||
{
|
||||
|
||||
session->analyzers = g_slist_append(session->analyzers, an);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void session_datafeed_callback_clear(void)
|
||||
{
|
||||
|
||||
g_slist_free(session->datafeed_callbacks);
|
||||
session->datafeed_callbacks = NULL;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void session_datafeed_callback_add(datafeed_callback callback)
|
||||
{
|
||||
|
||||
session->datafeed_callbacks = g_slist_append(session->datafeed_callbacks, callback);
|
||||
|
||||
}
|
||||
|
||||
|
||||
int session_start(void)
|
||||
{
|
||||
struct device *device;
|
||||
GSList *l;
|
||||
int ret;
|
||||
|
||||
g_message("starting acquisition");
|
||||
for(l = session->devices; l; l = l->next)
|
||||
{
|
||||
device = l->data;
|
||||
if( (ret = device->plugin->start_acquisition(device->plugin_index, device)) != SIGROK_OK)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void session_stop(void)
|
||||
{
|
||||
struct device *device;
|
||||
GSList *l;
|
||||
|
||||
g_message("stopping acquisition");
|
||||
for(l = session->devices; l; l = l->next)
|
||||
{
|
||||
device = l->data;
|
||||
device->plugin->stop_acquisition(device->plugin_index, device);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void session_bus(struct device *device, struct datafeed_packet *packet)
|
||||
{
|
||||
GSList *l;
|
||||
datafeed_callback cb;
|
||||
|
||||
/* TODO: send packet through PA pipe, and send the output of that to
|
||||
* the callbacks as well
|
||||
*/
|
||||
|
||||
for(l = session->datafeed_callbacks; l; l = l->next)
|
||||
{
|
||||
cb = l->data;
|
||||
cb(device, packet);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void make_metadata(char *filename)
|
||||
{
|
||||
GSList *l, *p;
|
||||
struct device *device;
|
||||
struct probe *probe;
|
||||
FILE *f;
|
||||
int devcnt;
|
||||
|
||||
f = fopen(filename, "wb");
|
||||
|
||||
/* general */
|
||||
|
||||
/* devices */
|
||||
devcnt = 1;
|
||||
for(l = session->devices; l; l = l->next) {
|
||||
device = l->data;
|
||||
fprintf(f, "[device]\n");
|
||||
fprintf(f, "driver = %s\n", device->plugin->name);
|
||||
if(device->datastore)
|
||||
fprintf(f, "capturefile = raw-%d\n", devcnt);
|
||||
for(p = device->probes; p; p = p->next) {
|
||||
probe = p->data;
|
||||
if(probe->enabled)
|
||||
{
|
||||
fprintf(f, "probe %d", probe->index);
|
||||
if(probe->name)
|
||||
fprintf(f, " name \"%s\"", probe->name);
|
||||
if(probe->trigger)
|
||||
fprintf(f, " trigger \"%s\"", probe->trigger);
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
}
|
||||
devcnt++;
|
||||
}
|
||||
|
||||
/* TODO: protocol analyzers */
|
||||
|
||||
fclose(f);
|
||||
|
||||
}
|
||||
|
||||
|
||||
int session_save(char *filename)
|
||||
{
|
||||
GSList *l, *d;
|
||||
struct device *device;
|
||||
struct datastore *ds;
|
||||
struct zip *zipfile;
|
||||
struct zip_source *src;
|
||||
int bufcnt, devcnt, tmpfile, ret, error;
|
||||
char version[1], rawname[16], metafile[32], *buf;
|
||||
|
||||
/* quietly delete it first, libzip wants replace ops otherwise */
|
||||
unlink(filename);
|
||||
|
||||
if( !(zipfile = zip_open(filename, ZIP_CREATE, &error)) )
|
||||
return SIGROK_NOK;
|
||||
|
||||
/* version */
|
||||
version[0] = '1';
|
||||
if( !(src = zip_source_buffer(zipfile, version, 1, 0)) )
|
||||
return SIGROK_NOK;
|
||||
if(zip_add(zipfile, "version", src) == -1) {
|
||||
g_message("error saving version into zipfile: %s", zip_strerror(zipfile));
|
||||
return SIGROK_NOK;
|
||||
}
|
||||
|
||||
/* metadata */
|
||||
strcpy(metafile, "sigrok-meta-XXXXXX");
|
||||
if( (tmpfile = g_mkstemp(metafile)) == -1)
|
||||
return SIGROK_NOK;
|
||||
close(tmpfile);
|
||||
make_metadata(metafile);
|
||||
if( !(src = zip_source_file(zipfile, metafile, 0, -1)) )
|
||||
return SIGROK_NOK;
|
||||
if(zip_add(zipfile, "metadata", src) == -1)
|
||||
return SIGROK_NOK;
|
||||
unlink(metafile);
|
||||
|
||||
/* raw */
|
||||
devcnt = 1;
|
||||
for(l = session->devices; l; l = l->next) {
|
||||
device = l->data;
|
||||
ds = device->datastore;
|
||||
if(ds) {
|
||||
buf = malloc(ds->num_units * ds->ds_unitsize + DATASTORE_CHUNKSIZE);
|
||||
bufcnt = 0;
|
||||
for(d = ds->chunklist; d; d = d->next) {
|
||||
memcpy(buf + bufcnt, d->data, DATASTORE_CHUNKSIZE);
|
||||
bufcnt += DATASTORE_CHUNKSIZE;
|
||||
}
|
||||
if( !(src = zip_source_buffer(zipfile, buf, ds->num_units * ds->ds_unitsize, TRUE)) )
|
||||
return SIGROK_NOK;
|
||||
snprintf(rawname, 15, "raw-%d", devcnt);
|
||||
if(zip_add(zipfile, rawname, src) == -1)
|
||||
return SIGROK_NOK;
|
||||
}
|
||||
devcnt++;
|
||||
}
|
||||
|
||||
if( (ret = zip_close(zipfile)) == -1) {
|
||||
g_message("error saving zipfile: %s", zip_strerror(zipfile));
|
||||
return SIGROK_NOK;
|
||||
}
|
||||
|
||||
return SIGROK_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,382 @@
|
|||
/*
|
||||
* This file is part of the sigrok project.
|
||||
*
|
||||
* Copyright (C) 2010 Bert Vermeulen <bert@biot.com>
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SIGROK_SIGROK_H
|
||||
#define SIGROK_SIGROK_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <glib.h>
|
||||
#include <libusb.h>
|
||||
|
||||
/* Returned status/error codes */
|
||||
#define SIGROK_STATUS_DISABLED 0
|
||||
#define SIGROK_OK 1
|
||||
#define SIGROK_NOK 2
|
||||
#define SIGROK_ERR_BADVALUE 20
|
||||
|
||||
/* Handy little macros */
|
||||
#define KHZ(n) (n * 1000)
|
||||
#define MHZ(n) (n * 1000000)
|
||||
#define GHZ(n) (n * 1000000000)
|
||||
|
||||
/* Data types, used by hardware plugins for set_configuration() */
|
||||
enum {
|
||||
T_UINT64,
|
||||
T_CHAR,
|
||||
};
|
||||
|
||||
enum {
|
||||
PROTO_RAW,
|
||||
};
|
||||
|
||||
/* (Unused) protocol decoder stack entry */
|
||||
struct protocol {
|
||||
char *name;
|
||||
int id;
|
||||
int stackindex;
|
||||
};
|
||||
|
||||
/*
|
||||
* datafeed
|
||||
*/
|
||||
|
||||
/* datafeed_packet.type values */
|
||||
enum {
|
||||
DF_HEADER,
|
||||
DF_END,
|
||||
DF_TRIGGER,
|
||||
DF_LOGIC8,
|
||||
DF_LOGIC16,
|
||||
DF_LOGIC24,
|
||||
DF_LOGIC32,
|
||||
DF_LOGIC48,
|
||||
DF_LOGIC64,
|
||||
};
|
||||
|
||||
struct datafeed_packet {
|
||||
uint16_t type;
|
||||
uint16_t length;
|
||||
void *payload;
|
||||
};
|
||||
|
||||
struct datafeed_header {
|
||||
int feed_version;
|
||||
struct timeval starttime;
|
||||
uint64_t rate;
|
||||
int protocol_id;
|
||||
int num_probes;
|
||||
};
|
||||
|
||||
/*
|
||||
* output
|
||||
*/
|
||||
struct output {
|
||||
struct output_format *format;
|
||||
struct device *device;
|
||||
char *param;
|
||||
void *internal;
|
||||
};
|
||||
|
||||
struct output_format {
|
||||
char *extension;
|
||||
char *description;
|
||||
void (*init) (struct output *o);
|
||||
int (*data) (struct output *o, char *data_in, uint64_t length_in,
|
||||
char **data_out, uint64_t *length_out);
|
||||
int (*event) (struct output *o, int event_type, char **data_out,
|
||||
uint64_t *length_out);
|
||||
};
|
||||
|
||||
struct output_format **output_list(void);
|
||||
int filter_probes(int in_unitsize, int out_unitsize, int *probelist,
|
||||
char *data_in, uint64_t length_in, char **data_out,
|
||||
uint64_t *length_out);
|
||||
|
||||
/*--- analyzer.c ------------------------------------------------------------*/
|
||||
|
||||
struct analyzer {
|
||||
char *name;
|
||||
char *filename;
|
||||
/*
|
||||
* TODO: Parameters? If so, configured plugins need another struct.
|
||||
* TODO: Input and output format?
|
||||
*/
|
||||
};
|
||||
|
||||
/*--- backend.c -------------------------------------------------------------*/
|
||||
|
||||
int sigrok_init(void);
|
||||
void sigrok_cleanup(void);
|
||||
|
||||
/*--- datastore.c -----------------------------------------------------------*/
|
||||
|
||||
/* Size of a chunk in units */
|
||||
#define DATASTORE_CHUNKSIZE 512000
|
||||
|
||||
struct datastore {
|
||||
/* Size in bytes of the number of units stored in this datastore */
|
||||
int ds_unitsize;
|
||||
unsigned int num_units;
|
||||
GSList *chunklist;
|
||||
};
|
||||
|
||||
struct datastore *datastore_new(int unitsize);
|
||||
void datastore_destroy(struct datastore *ds);
|
||||
void datastore_put(struct datastore *ds, void *data, unsigned int length,
|
||||
int in_unitsize, int *probelist);
|
||||
|
||||
/*--- debug.c ---------------------------------------------------------------*/
|
||||
|
||||
void hexdump(unsigned char *address, int length);
|
||||
|
||||
/*--- device.c --------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* This represents a generic device connected to the system.
|
||||
* For device-specific information, ask the plugin. The plugin_index refers
|
||||
* to the device index within that plugin; it may be handling more than one
|
||||
* device. All relevant plugin calls take a device_index parameter for this.
|
||||
*/
|
||||
struct device {
|
||||
/* Which plugin handles this device */
|
||||
struct device_plugin *plugin;
|
||||
/* A plugin may handle multiple devices of the same type */
|
||||
int plugin_index;
|
||||
/* List of struct probe* */
|
||||
GSList *probes;
|
||||
/* Data acquired by this device, if any */
|
||||
struct datastore *datastore;
|
||||
};
|
||||
|
||||
struct probe {
|
||||
int index;
|
||||
gboolean enabled;
|
||||
char *name;
|
||||
char *trigger;
|
||||
};
|
||||
|
||||
extern GSList *devices;
|
||||
|
||||
void device_scan(void);
|
||||
void device_close_all(void);
|
||||
GSList *device_list(void);
|
||||
struct device *device_new(struct device_plugin *plugin, int plugin_index);
|
||||
void device_clear(struct device *device);
|
||||
void device_destroy(struct device *dev);
|
||||
|
||||
void device_probe_clear(struct device *device, int probenum);
|
||||
void device_probe_add(struct device *device, char *name);
|
||||
struct probe *probe_find(struct device *device, int probenum);
|
||||
void device_probe_name(struct device *device, int probenum, char *name);
|
||||
|
||||
void device_trigger_clear(struct device *device);
|
||||
void device_trigger_set(struct device *device, int probenum, char *trigger);
|
||||
|
||||
/*--- hwplugin.c ------------------------------------------------------------*/
|
||||
|
||||
/* Hardware plugin capabilities */
|
||||
enum {
|
||||
HWCAP_DUMMY, // used to terminate lists
|
||||
HWCAP_LOGIC_ANALYZER,
|
||||
HWCAP_SAMPLERATE, // change sample rate
|
||||
HWCAP_PROBECONFIG, // configure probe mask
|
||||
HWCAP_CAPTURE_RATIO, // set pre-trigger / post-trigger ratio
|
||||
HWCAP_LIMIT_MSEC, // set a time limit for sample acquisition
|
||||
HWCAP_LIMIT_SAMPLES, // set a limit on number of samples
|
||||
};
|
||||
|
||||
struct hwcap_option {
|
||||
int capability;
|
||||
int type;
|
||||
char *description;
|
||||
char *shortname;
|
||||
};
|
||||
|
||||
struct sigrok_device_instance {
|
||||
int index;
|
||||
int status;
|
||||
int instance_type;
|
||||
char *vendor;
|
||||
char *model;
|
||||
char *version;
|
||||
union {
|
||||
struct usb_device_instance *usb;
|
||||
struct serial_device_instance *serial;
|
||||
};
|
||||
};
|
||||
|
||||
/* sigrok_device_instance types */
|
||||
enum {
|
||||
USB_INSTANCE,
|
||||
SERIAL_INSTANCE,
|
||||
};
|
||||
|
||||
struct usb_device_instance {
|
||||
uint8_t bus;
|
||||
uint8_t address;
|
||||
struct libusb_device_handle *devhdl;
|
||||
};
|
||||
|
||||
struct serial_device_instance {
|
||||
char *port;
|
||||
int fd;
|
||||
};
|
||||
|
||||
/* Device instance status */
|
||||
enum {
|
||||
ST_NOT_FOUND,
|
||||
/* Found, but still booting */
|
||||
ST_INITIALIZING,
|
||||
/* Live, but not in use */
|
||||
ST_INACTIVE,
|
||||
/* Actively in use in a session */
|
||||
ST_ACTIVE,
|
||||
};
|
||||
|
||||
/*
|
||||
* TODO: This sucks, you just kinda have to "know" the returned type.
|
||||
* TODO: Need a DI to return the number of trigger stages supported.
|
||||
*/
|
||||
|
||||
/* Device info IDs */
|
||||
enum {
|
||||
/* struct sigrok_device_instance for this specific device */
|
||||
DI_INSTANCE,
|
||||
/* The number of probes connected to this device */
|
||||
DI_NUM_PROBES,
|
||||
/* Sample rates supported by this device, (struct samplerates) */
|
||||
DI_SAMPLERATES,
|
||||
/* Types of trigger supported, out of "01crf" (char *) */
|
||||
DI_TRIGGER_TYPES,
|
||||
/* The currently set sample rate in Hz (uint64_t) */
|
||||
DI_CUR_SAMPLE_RATE,
|
||||
};
|
||||
|
||||
/* a device supports either a range of samplerates with steps of a given
|
||||
* granularity, or is limited to a set of defined samplerates. use either
|
||||
* step or list, but not both.
|
||||
*/
|
||||
struct samplerates {
|
||||
uint64_t low;
|
||||
uint64_t high;
|
||||
uint64_t step;
|
||||
uint64_t *list;
|
||||
};
|
||||
|
||||
struct device_plugin {
|
||||
/* plugin-specific */
|
||||
char *name;
|
||||
int api_version;
|
||||
int (*init) (char *deviceinfo);
|
||||
void (*cleanup) (void);
|
||||
|
||||
/* device-specific */
|
||||
int (*open) (int device_index);
|
||||
void (*close) (int device_index);
|
||||
void *(*get_device_info) (int device_index, int device_info_id);
|
||||
int (*get_status) (int device_index);
|
||||
int *(*get_capabilities) (void);
|
||||
int (*set_configuration) (int device_index, int capability, void *value);
|
||||
int (*start_acquisition) (int device_index, gpointer session_device_id);
|
||||
void (*stop_acquisition) (int device_index, gpointer session_device_id);
|
||||
};
|
||||
|
||||
struct gsource_fd {
|
||||
GSource source;
|
||||
GPollFD gpfd;
|
||||
/* Not really using this */
|
||||
GSource *timeout_source;
|
||||
};
|
||||
|
||||
typedef int (*receive_data_callback) (int fd, int revents, void *user_data);
|
||||
|
||||
int load_hwplugins(void);
|
||||
GSList *list_hwplugins(void);
|
||||
|
||||
/* Generic device instances */
|
||||
struct sigrok_device_instance *sigrok_device_instance_new(int index,
|
||||
int status, char *vendor, char *model, char *version);
|
||||
struct sigrok_device_instance *get_sigrok_device_instance(GSList *device_instances, int device_index);
|
||||
void sigrok_device_instance_free(struct sigrok_device_instance *sdi);
|
||||
|
||||
/* USB-specific instances */
|
||||
struct usb_device_instance *usb_device_instance_new(uint8_t bus,
|
||||
uint8_t address, struct libusb_device_handle *hdl);
|
||||
void usb_device_instance_free(struct usb_device_instance *usb);
|
||||
|
||||
/* Serial-specific instances */
|
||||
struct serial_device_instance *serial_device_instance_new(char *port, int fd);
|
||||
void serial_device_instance_free(struct serial_device_instance *serial);
|
||||
|
||||
int find_hwcap(int *capabilities, int hwcap);
|
||||
struct hwcap_option *find_hwcap_option(int hwcap);
|
||||
void source_remove(int fd);
|
||||
void source_add(int fd, int events, int timeout, receive_data_callback rcv_cb, void *user_data);
|
||||
|
||||
/*--- session.c -------------------------------------------------------------*/
|
||||
|
||||
typedef void (*source_callback_remove) (int fd);
|
||||
typedef void (*source_callback_add) (int fd, int events, int timeout,
|
||||
receive_data_callback callback, void *user_data);
|
||||
typedef void (*datafeed_callback) (struct device *device,
|
||||
struct datafeed_packet *packet);
|
||||
|
||||
struct session {
|
||||
/* List of struct device* */
|
||||
GSList *devices;
|
||||
/* List of struct analyzer* */
|
||||
GSList *analyzers;
|
||||
/* datafeed callbacks */
|
||||
GSList *datafeed_callbacks;
|
||||
GTimeVal starttime;
|
||||
};
|
||||
|
||||
/* Session setup */
|
||||
struct session *session_load(char *filename);
|
||||
struct session *session_new(void);
|
||||
void session_destroy(void);
|
||||
void session_device_clear(void);
|
||||
int session_device_add(struct device *device);
|
||||
|
||||
/* Protocol analyzers setup */
|
||||
void session_pa_clear(void);
|
||||
void session_pa_add(struct analyzer *pa);
|
||||
|
||||
/* Datafeed setup */
|
||||
void session_datafeed_callback_clear(void);
|
||||
void session_datafeed_callback_add(datafeed_callback callback);
|
||||
|
||||
/* Session control */
|
||||
int session_start(void);
|
||||
void session_stop(void);
|
||||
void session_bus(struct device *device, struct datafeed_packet *packet);
|
||||
void make_metadata(char *filename);
|
||||
int session_save(char *filename);
|
||||
|
||||
/*--- hwcommon.c ------------------------------------------------------------*/
|
||||
|
||||
int ezusb_reset(struct libusb_device_handle *hdl, int set_clear);
|
||||
int ezusb_install_firmware(libusb_device_handle *hdl, char *filename);
|
||||
|
||||
GSList *list_serial_ports(void);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue