Start of code base layout restructuring.

This commit is contained in:
Uwe Hermann 2010-04-02 20:18:27 +02:00
commit a1bb33afbd
22 changed files with 4947 additions and 0 deletions

47
Makefile.am Normal file
View File

@ -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

40
backend.c Normal file
View File

@ -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();
}

100
datastore.c Normal file
View File

@ -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;
}

39
debug.c Normal file
View File

@ -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");
}

235
device.c Normal file
View File

@ -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);
}

66
filter.c Normal file
View File

@ -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;
}

84
hardware/common/ezusb.c Normal file
View File

@ -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;
}

57
hardware/common/serial.c Normal file
View File

@ -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;
}

104
hardware/common/skeleton.c Normal file
View File

@ -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
};

View File

@ -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
};

View File

@ -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
};

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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
};

219
hwplugin.c Normal file
View File

@ -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);
}

40
output/output.c Normal file
View File

@ -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;
}

58
output/output_skeleton.c Normal file
View File

@ -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
};

278
output/output_text.c Normal file
View File

@ -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
};

284
session.c Normal file
View File

@ -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;
}

382
sigrok.h Normal file
View File

@ -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