--- /dev/null
+##
+## 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
+
--- /dev/null
+/*
+ * 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();
+
+}
+
+
--- /dev/null
+/*
+ * 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;
+}
+
+
--- /dev/null
+/*
+ * 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");
+
+}
--- /dev/null
+/*
+ * 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);
+
+}
+
+
--- /dev/null
+/*
+ * 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;
+}
+
+
--- /dev/null
+/*
+ * 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;
+}
+
+
--- /dev/null
+/*
+ * 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;
+}
+
+
--- /dev/null
+/*
+ * 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
+};
+
--- /dev/null
+/*
+ * 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
+};
+
--- /dev/null
+/*
+ * 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
+};
+
--- /dev/null
+/*
+ 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;
+}
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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;
+}
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ * 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
+};
+
--- /dev/null
+/*
+ * 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);
+
+}
+
+
+
+
--- /dev/null
+/*
+ * 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;
+}
+
+
--- /dev/null
+/*
+ * 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
+};
+
+
--- /dev/null
+/*
+ * 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
+};
+
--- /dev/null
+/*
+ * 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;
+}
+
+
+
+
+
+
+
+
--- /dev/null
+/*
+ * 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