]> sigrok.org Git - libsigrok.git/commitdiff
Start of code base layout restructuring.
authorUwe Hermann <redacted>
Fri, 2 Apr 2010 18:18:27 +0000 (20:18 +0200)
committerUwe Hermann <redacted>
Fri, 2 Apr 2010 18:27:54 +0000 (20:27 +0200)
22 files changed:
Makefile.am [new file with mode: 0644]
backend.c [new file with mode: 0644]
datastore.c [new file with mode: 0644]
debug.c [new file with mode: 0644]
device.c [new file with mode: 0644]
filter.c [new file with mode: 0644]
hardware/common/ezusb.c [new file with mode: 0644]
hardware/common/serial.c [new file with mode: 0644]
hardware/common/skeleton.c [new file with mode: 0644]
hardware/openbench-logic-sniffer/ols.c [new file with mode: 0644]
hardware/saleae-logic/saleae-logic.c [new file with mode: 0644]
hardware/zeroplus-logic-cube/analyzer.c [new file with mode: 0644]
hardware/zeroplus-logic-cube/analyzer.h [new file with mode: 0644]
hardware/zeroplus-logic-cube/gl_usb.c [new file with mode: 0644]
hardware/zeroplus-logic-cube/gl_usb.h [new file with mode: 0644]
hardware/zeroplus-logic-cube/zeroplus.c [new file with mode: 0644]
hwplugin.c [new file with mode: 0644]
output/output.c [new file with mode: 0644]
output/output_skeleton.c [new file with mode: 0644]
output/output_text.c [new file with mode: 0644]
session.c [new file with mode: 0644]
sigrok.h [new file with mode: 0644]

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