From: Uwe Hermann Date: Fri, 2 Apr 2010 18:18:27 +0000 (+0200) Subject: Start of code base layout restructuring. X-Git-Tag: libsigrok-0.1.0~590 X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=a1bb33afbde769156ad4bef7a60579da64aebbb7;p=libsigrok.git Start of code base layout restructuring. --- a1bb33afbde769156ad4bef7a60579da64aebbb7 diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 00000000..14336ed3 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,47 @@ +## +## This file is part of the sigrok project. +## +## Copyright (C) 2010 Bert Vermeulen +## +## 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 . +## + +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 index 00000000..6a49cde7 --- /dev/null +++ b/backend.c @@ -0,0 +1,40 @@ +/* + * This file is part of the sigrok project. + * + * Copyright (C) 2010 Bert Vermeulen + * + * 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 . + */ + +#include +#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 index 00000000..7f6f575a --- /dev/null +++ b/datastore.c @@ -0,0 +1,100 @@ +/* + * This file is part of the sigrok project. + * + * Copyright (C) 2010 Bert Vermeulen + * + * 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 . + */ + +#include +#include +#include +#include +#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 index 00000000..128eb03d --- /dev/null +++ b/debug.c @@ -0,0 +1,39 @@ +/* + * This file is part of the sigrok project. + * + * Copyright (C) 2010 Bert Vermeulen + * + * 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 . + */ + +#include +#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 index 00000000..cc6b0549 --- /dev/null +++ b/device.c @@ -0,0 +1,235 @@ +/* + * This file is part of the sigrok project. + * + * Copyright (C) 2010 Bert Vermeulen + * + * 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 . + */ + +#include +#include +#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 index 00000000..f9c9a09c --- /dev/null +++ b/filter.c @@ -0,0 +1,66 @@ +/* + * This file is part of the sigrok project. + * + * Copyright (C) 2010 Bert Vermeulen + * + * 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 . + */ + +#include +#include +#include +#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 index 00000000..e765b834 --- /dev/null +++ b/hardware/common/ezusb.c @@ -0,0 +1,84 @@ +/* + * This file is part of the sigrok project. + * + * Copyright (C) 2010 Bert Vermeulen + * + * 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 . + */ + +/* + * Helper functions for the Cypress EZ-USB / FX2 series chips. + */ + +#include +#include +#include +#include +#include +#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 index 00000000..cb7f11a7 --- /dev/null +++ b/hardware/common/serial.c @@ -0,0 +1,57 @@ +/* + * This file is part of the sigrok project. + * + * Copyright (C) 2010 Bert Vermeulen + * + * 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 . + */ + +#include +#include + + +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 index 00000000..bb838140 --- /dev/null +++ b/hardware/common/skeleton.c @@ -0,0 +1,104 @@ +/* + * This file is part of the sigrok project. + * + * Copyright (C) 2010 Bert Vermeulen + * + * 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 . + */ + +#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 index 00000000..5f6a35de --- /dev/null +++ b/hardware/openbench-logic-sniffer/ols.c @@ -0,0 +1,637 @@ +/* + * This file is part of the sigrok project. + * + * Copyright (C) 2010 Bert Vermeulen + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 index 00000000..2b804bea --- /dev/null +++ b/hardware/saleae-logic/saleae-logic.c @@ -0,0 +1,835 @@ +/* + * This file is part of the sigrok project. + * + * Copyright (C) 2010 Bert Vermeulen + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#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 index 00000000..ab32d645 --- /dev/null +++ b/hardware/zeroplus-logic-cube/analyzer.c @@ -0,0 +1,559 @@ +/* + Copyright (c) 2010 Sven Peter + Copyright (c) 2010 Haxx Enterprises + 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 + +#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 index 00000000..4d90e8b4 --- /dev/null +++ b/hardware/zeroplus-logic-cube/analyzer.h @@ -0,0 +1,100 @@ +/* + Copyright (c) 2010 Sven Peter + Copyright (c) 2010 Haxx Enterprises + 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 +#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 index 00000000..1eb1ac32 --- /dev/null +++ b/hardware/zeroplus-logic-cube/gl_usb.c @@ -0,0 +1,183 @@ +/* + Copyright (c) 2010 Sven Peter + Copyright (c) 2010 Haxx Enterprises + 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 +#include +#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 index 00000000..43f5ba9b --- /dev/null +++ b/hardware/zeroplus-logic-cube/gl_usb.h @@ -0,0 +1,49 @@ +/* + Copyright (c) 2010 Sven Peter + Copyright (c) 2010 Haxx Enterprises + 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 + +#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 index 00000000..415a5a5c --- /dev/null +++ b/hardware/zeroplus-logic-cube/zeroplus.c @@ -0,0 +1,551 @@ +/* + * This file is part of the sigrok project. + * + * Copyright (C) 2010 Bert Vermeulen + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#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 index 00000000..6c0f06b3 --- /dev/null +++ b/hwplugin.c @@ -0,0 +1,219 @@ +/* + * This file is part of the sigrok project. + * + * Copyright (C) 2010 Bert Vermeulen + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#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 index 00000000..87cb8656 --- /dev/null +++ b/output/output.c @@ -0,0 +1,40 @@ +/* + * This file is part of the sigrok project. + * + * Copyright (C) 2010 Bert Vermeulen + * + * 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 . + */ + +#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 index 00000000..6bd885f9 --- /dev/null +++ b/output/output_skeleton.c @@ -0,0 +1,58 @@ +/* + * This file is part of the sigrok project. + * + * Copyright (C) 2010 YOURNAME + * + * 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 . + */ + +#include + +#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 index 00000000..9ef30033 --- /dev/null +++ b/output/output_text.c @@ -0,0 +1,278 @@ +/* + * This file is part of the sigrok project. + * + * Copyright (C) 2010 Bert Vermeulen + * + * 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 . + */ + +#include +#include +#include +#include +#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 index 00000000..604d4b74 --- /dev/null +++ b/session.c @@ -0,0 +1,284 @@ +/* + * This file is part of the sigrok project. + * + * Copyright (C) 2010 Bert Vermeulen + * + * 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 . + */ + +#include +#include +#include +#include +#include +#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 index 00000000..3729a43c --- /dev/null +++ b/sigrok.h @@ -0,0 +1,382 @@ +/* + * This file is part of the sigrok project. + * + * Copyright (C) 2010 Bert Vermeulen + * + * 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 . + */ + +#ifndef SIGROK_SIGROK_H +#define SIGROK_SIGROK_H + +#include +#include +#include +#include +#include +#include + +/* 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