aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules8
-rw-r--r--.travis.yml70
-rw-r--r--HACKING26
-rw-r--r--configure.ac1
-rw-r--r--contrib/loaders/erase_check/armv7m_erase_check.s5
-rw-r--r--contrib/loaders/flash/stm32/stm32f1x.S5
-rw-r--r--contrib/loaders/flash/stm32/stm32f2x.S5
-rw-r--r--contrib/loaders/flash/stm32/stm32lx.S5
-rw-r--r--doc/openocd.texi2
m---------jimtcl0
-rw-r--r--src/flash/nor/fespi.c4
-rw-r--r--src/jtag/drivers/ftdi.c361
m---------src/jtag/drivers/libjaylink0
-rw-r--r--src/jtag/drivers/libjaylink/.gitignore24
-rw-r--r--src/jtag/drivers/libjaylink/AUTHORS2
-rw-r--r--src/jtag/drivers/libjaylink/COPYING339
-rw-r--r--src/jtag/drivers/libjaylink/ChangeLog1
-rw-r--r--src/jtag/drivers/libjaylink/Doxyfile.in2301
-rw-r--r--src/jtag/drivers/libjaylink/HACKING68
-rw-r--r--src/jtag/drivers/libjaylink/Makefile.am28
-rw-r--r--src/jtag/drivers/libjaylink/NEWS7
-rw-r--r--src/jtag/drivers/libjaylink/README70
-rwxr-xr-xsrc/jtag/drivers/libjaylink/autogen.sh34
-rw-r--r--src/jtag/drivers/libjaylink/configure.ac140
-rw-r--r--src/jtag/drivers/libjaylink/contrib/99-libjaylink.rules41
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink.pc.in11
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/Makefile.am62
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/buffer.c140
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/core.c219
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/device.c1707
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/discovery.c106
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/discovery_tcp.c349
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/discovery_usb.c280
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/emucom.c287
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/error.c118
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/fileio.c499
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/jtag.c259
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/libjaylink-internal.h320
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/libjaylink.h589
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/list.c115
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/log.c266
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/socket.c257
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/strutil.c66
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/swd.c148
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/swo.c453
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/target.c533
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/transport.c309
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/transport_tcp.c601
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/transport_usb.c620
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/util.c56
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/version.c128
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/version.h.in53
-rw-r--r--src/jtag/drivers/libjaylink/m4/jaylink.m491
-rw-r--r--src/jtag/drivers/mpsse.c7
-rw-r--r--src/jtag/drivers/stlink_usb.c3
-rw-r--r--src/rtos/Makefile.am4
-rw-r--r--src/rtos/hwthread.c152
-rw-r--r--src/rtos/riscv_debug.c352
-rw-r--r--src/rtos/riscv_debug.h13
-rw-r--r--src/rtos/rtos.c49
-rw-r--r--src/rtos/rtos.h10
-rw-r--r--src/server/gdb_server.c94
-rw-r--r--src/server/gdb_server.h2
-rw-r--r--src/target/breakpoints.c61
-rw-r--r--src/target/register.c23
-rw-r--r--src/target/register.h2
-rw-r--r--src/target/riscv/debug_defines.h735
-rw-r--r--src/target/riscv/riscv-011.c33
-rw-r--r--src/target/riscv/riscv-013.c211
-rw-r--r--src/target/riscv/riscv.c277
-rw-r--r--src/target/riscv/riscv.h22
-rw-r--r--src/target/target.c65
-rw-r--r--src/target/target.h10
-rw-r--r--src/target/target_type.h7
-rw-r--r--tcl/board/sifive-e31arty-cjtag.cfg23
-rw-r--r--tcl/interface/ftdi/olimex-arm-jtag-cjtag.cfg27
-rw-r--r--tcl/target/1986Be1T.cfg (renamed from tcl/target/1986ве1т.cfg)0
-rw-r--r--tcl/target/K1879x61R.cfg (renamed from tcl/target/к1879xб1я.cfg)0
-rwxr-xr-xtools/filter_openocd_log.py120
m---------tools/git2cl0
-rw-r--r--tools/git2cl/COPYING339
-rw-r--r--tools/git2cl/README45
-rw-r--r--tools/git2cl/README.html392
-rwxr-xr-xtools/git2cl/git2cl372
84 files changed, 15116 insertions, 523 deletions
diff --git a/.gitmodules b/.gitmodules
index 5865ff9..b99c87a 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,9 +1,3 @@
-[submodule "tools/git2cl"]
- path = tools/git2cl
- url = http://repo.or.cz/r/git2cl.git
[submodule "jimtcl"]
path = jimtcl
- url = http://repo.or.cz/r/jimtcl.git
-[submodule "src/jtag/drivers/libjaylink"]
- path = src/jtag/drivers/libjaylink
- url = http://repo.or.cz/r/libjaylink.git
+ url = https://github.com/msteveb/jimtcl
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..3749a8a
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,70 @@
+language: c
+dist: trusty
+
+matrix:
+ include:
+ - os: linux
+ env:
+ - BUILD=x86_64-linux-gnu
+ - EXECUTABLE=openocd
+ addons:
+ apt:
+ packages:
+ - patchutils
+ compiler: gcc
+
+ - os: linux
+ env:
+ - BUILD=i686-linux-gnu
+ - CFLAGS=-m32
+ - EXECUTABLE=openocd
+ addons:
+ apt:
+ packages:
+ - gcc-multilib patchutils
+ compiler: gcc
+
+ - os: linux
+ env:
+ - BUILD=x86_64-linux-gnu
+ - EXECUTABLE=openocd
+ addons:
+ apt:
+ packages:
+ - patchutils
+ compiler: clang
+
+ - os: linux
+ env:
+ - BUILD=i686-linux-gnu
+ - CFLAGS=-m32
+ - CONFIGURE_ARGS="--disable-target64"
+ - EXECUTABLE=openocd
+ compiler: clang
+ addons:
+ apt:
+ packages:
+ - gcc-multilib patchutils
+
+ - os: linux
+ env:
+ - BUILD=i686-w64-mingw
+ - CONFIGURE_ARGS="--build=i686-unknown-linux-gnu --host=i686-w64-mingw32"
+ - EXECUTABLE=openocd.exe
+ compiler: i686-w64-mingw32-gcc
+ addons:
+ apt:
+ packages:
+ - binutils-mingw-w64-i686 gcc-mingw-w64-i686 g++-mingw-w64-i686 patchutils
+
+script:
+ # Ideally we'd diff back to where we either branched off OpenOCD or master,
+ # or riscv. But that's tricky, and the default git clone only gets the last
+ # 50 changes any case. Most merges won't consist of more than 40 changes,
+ # so this should work fine most of the time, and be a lot better than not
+ # checking at all.
+ - git diff -U20 HEAD~40 |
+ filterdiff -x "b/src/jtag/drivers/libjaylink/*" -x "b/tools/git2cl/*" |
+ ./tools/scripts/checkpatch.pl --no-signoff -
+ - ./bootstrap && ./configure --enable-remote-bitbang --enable-jtag_vpi $CONFIGURE_ARGS && make
+ - file src/$EXECUTABLE
diff --git a/HACKING b/HACKING
index 39d98bb..a6bb6aa 100644
--- a/HACKING
+++ b/HACKING
@@ -77,6 +77,32 @@ patch:
src/openocd -s ../tcl -f /path/to/openocd.cfg
@endcode
+- Runtime coverage testing
+
+ Apply the following patch to prevent OpenOCD from killing itself:
+ @code
+--- a/src/openocd.c
++++ b/src/openocd.c
+@@ -372,8 +372,6 @@ int openocd_main(int argc, char *argv[])
+
+ if (ERROR_FAIL == ret)
+ return EXIT_FAILURE;
+- else if (ERROR_OK != ret)
+- exit_on_signal(ret);
+
+ return ret;
+ }
+ @endcode
+
+ Configure your OpenOCD binary with coverage support as follows:
+ @code
+ LDFLAGS="-fprofile-arcs -ftest-coverage"
+ CFLAGS="-fprofile-arcs -ftest-coverage" ./configure
+ @endcode
+
+ Now every time OpenOCD is run, coverage info in your build directory is
+ updated. Running `gcov src/path/file.c` will generate a report.
+
Please consider performing these additonal checks where appropriate
(especially Clang Static Analyzer for big portions of new code) and
mention the results (e.g. "Valgrind-clean, no new Clang analyzer
diff --git a/configure.ac b/configure.ac
index d4338df..ab05cc6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -110,6 +110,7 @@ m4_define([ADAPTER_OPT], [m4_translit(ADAPTER_ARG($1), [_], [-])])
m4_define([USB1_ADAPTERS],
[[[ftdi], [MPSSE mode of FTDI based devices], [FTDI]],
+ [[ftdi_oscan1], [cJTAG OSCAN1 tunneled thru MPSSE], [FTDI_OSCAN1]],
[[stlink], [ST-Link JTAG Programmer], [HLADAPTER_STLINK]],
[[ti_icdi], [TI ICDI JTAG Programmer], [HLADAPTER_ICDI]],
[[ulink], [Keil ULINK JTAG Programmer], [ULINK]],
diff --git a/contrib/loaders/erase_check/armv7m_erase_check.s b/contrib/loaders/erase_check/armv7m_erase_check.s
index 3303c87..163fa8c 100644
--- a/contrib/loaders/erase_check/armv7m_erase_check.s
+++ b/contrib/loaders/erase_check/armv7m_erase_check.s
@@ -11,11 +11,6 @@
* 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, write to the *
- * Free Software Foundation, Inc., *
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
***************************************************************************/
/*
diff --git a/contrib/loaders/flash/stm32/stm32f1x.S b/contrib/loaders/flash/stm32/stm32f1x.S
index 7b64c67..a1c4135 100644
--- a/contrib/loaders/flash/stm32/stm32f1x.S
+++ b/contrib/loaders/flash/stm32/stm32f1x.S
@@ -11,11 +11,6 @@
* 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, write to the *
- * Free Software Foundation, Inc., *
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
***************************************************************************/
.text
diff --git a/contrib/loaders/flash/stm32/stm32f2x.S b/contrib/loaders/flash/stm32/stm32f2x.S
index f6f5b30..8caf5ba 100644
--- a/contrib/loaders/flash/stm32/stm32f2x.S
+++ b/contrib/loaders/flash/stm32/stm32f2x.S
@@ -14,11 +14,6 @@
* 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, write to the *
- * Free Software Foundation, Inc., *
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
***************************************************************************/
.text
diff --git a/contrib/loaders/flash/stm32/stm32lx.S b/contrib/loaders/flash/stm32/stm32lx.S
index bcae7a4..399be8b 100644
--- a/contrib/loaders/flash/stm32/stm32lx.S
+++ b/contrib/loaders/flash/stm32/stm32lx.S
@@ -20,11 +20,6 @@
* 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, write to the *
- * Free Software Foundation, Inc., *
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
***************************************************************************/
diff --git a/doc/openocd.texi b/doc/openocd.texi
index 027e6d2..9dfb04b 100644
--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -3479,7 +3479,7 @@ How long (in milliseconds) OpenOCD should wait after deasserting
nTRST (active-low JTAG TAP reset) before starting new JTAG operations.
@end deffn
-@anchor {reset_config}
+@anchor{reset_config}
@deffn {Command} reset_config mode_flag ...
This command displays or modifies the reset configuration
of your combination of JTAG board and target in target
diff --git a/jimtcl b/jimtcl
-Subproject a9bf5975fd0f89974d689a2d9ebd0873c8d6478
+Subproject 51f65c6d38fbf86e1f0b036ad336761fd2ab7fa
diff --git a/src/flash/nor/fespi.c b/src/flash/nor/fespi.c
index 1c343a8..5d8f810 100644
--- a/src/flash/nor/fespi.c
+++ b/src/flash/nor/fespi.c
@@ -159,9 +159,7 @@ FLASH_BANK_COMMAND_HANDLER(fespi_flash_bank_command)
fespi_info->probed = 0;
fespi_info->ctrl_base = 0;
if (CMD_ARGC >= 7) {
- int temp;
- COMMAND_PARSE_NUMBER(int, CMD_ARGV[6], temp);
- fespi_info->ctrl_base = (uint32_t) temp;
+ COMMAND_PARSE_ADDRESS(CMD_ARGV[6], fespi_info->ctrl_base);
LOG_DEBUG("ASSUMING FESPI device at ctrl_base = " TARGET_ADDR_FMT,
fespi_info->ctrl_base);
}
diff --git a/src/jtag/drivers/ftdi.c b/src/jtag/drivers/ftdi.c
index e7f3c3d..33b66b7 100644
--- a/src/jtag/drivers/ftdi.c
+++ b/src/jtag/drivers/ftdi.c
@@ -74,6 +74,7 @@
#include <jtag/swd.h>
#include <transport/transport.h>
#include <helper/time_support.h>
+#include <helper/log.h>
#if IS_CYGWIN == 1
#include <windows.h>
@@ -84,6 +85,18 @@
/* FTDI access library includes */
#include "mpsse.h"
+#define DEBUG_IO(expr...) DEBUG_JTAG_IO(expr)
+
+#if BUILD_FTDI_OSCAN1 == 1
+#define DO_CLOCK_DATA clock_data
+#define DO_CLOCK_TMS_CS clock_tms_cs
+#define DO_CLOCK_TMS_CS_OUT clock_tms_cs_out
+#else
+#define DO_CLOCK_DATA mpsse_clock_data
+#define DO_CLOCK_TMS_CS mpsse_clock_tms_cs
+#define DO_CLOCK_TMS_CS_OUT mpsse_clock_tms_cs_out
+#endif
+
#define JTAG_MODE (LSB_FIRST | POS_EDGE_IN | NEG_EDGE_OUT)
#define JTAG_MODE_ALT (LSB_FIRST | NEG_EDGE_IN | NEG_EDGE_OUT)
#define SWD_MODE (LSB_FIRST | POS_EDGE_IN | NEG_EDGE_OUT)
@@ -95,6 +108,31 @@ static uint8_t ftdi_jtag_mode = JTAG_MODE;
static bool swd_mode;
+#if BUILD_FTDI_OSCAN1 == 1
+/*
+ The cJTAG 2-wire OSCAN1 protocol, in lieu of 4-wire JTAG, is a configuration option
+ for some SoCs. An FTDI-based adapter that can be configured to appropriately drive
+ the bidirectional pin TMSC is able to drive OSCAN1 protocol. For example, an Olimex
+ ARM-USB-TINY-H with the ARM-JTAG-SWD adapter, connected to a cJTAG-enabled
+ target board is such a topology. A TCK cycle with TMS=1/TDI=N translates to a TMSC
+ output of N, and a TCK cycle with TMS=0 translates to a TMSC input from the target back
+ to the adapter/probe. The OSCAN1 protocol uses 3 TCK cycles to generate the data flow
+ that is equivalent to that of a single TCK cycle in 4-wire JTAG. The OSCAN1-related
+ code in this module translates IR/DR scan commanads and JTAG state traversal commands
+ to the two-wire clocking and signaling of OSCAN1 protocol, if placed into oscan1 mode
+ during initialization.
+*/
+static void oscan1_reset_online_activate(void);
+static void oscan1_mpsse_clock_data(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in,
+ unsigned in_offset, unsigned length, uint8_t mode);
+static void oscan1_mpsse_clock_tms_cs(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in,
+ unsigned in_offset, unsigned length, bool tdi, uint8_t mode);
+static void oscan1_mpsse_clock_tms_cs_out(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset,
+ unsigned length, bool tdi, uint8_t mode);
+
+static bool oscan1_mode;
+#endif
+
#define MAX_USB_IDS 8
/* vid = pid = 0 marks the end of the list */
static uint16_t ftdi_vid[MAX_USB_IDS + 1] = { 0 };
@@ -240,6 +278,35 @@ static int ftdi_get_signal(const struct signal *s, uint16_t * value_out)
return ERROR_OK;
}
+#if BUILD_FTDI_OSCAN1 == 1
+static void clock_data(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in,
+ unsigned in_offset, unsigned length, uint8_t mode)
+{
+ if (oscan1_mode)
+ oscan1_mpsse_clock_data(ctx, out, out_offset, in, in_offset, length, mode);
+ else
+ mpsse_clock_data(ctx, out, out_offset, in, in_offset, length, mode);
+}
+
+static void clock_tms_cs(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in,
+ unsigned in_offset, unsigned length, bool tdi, uint8_t mode)
+{
+ if (oscan1_mode)
+ oscan1_mpsse_clock_tms_cs(ctx, out, out_offset, in, in_offset, length, tdi, mode);
+ else
+ mpsse_clock_tms_cs(ctx, out, out_offset, in, in_offset, length, tdi, mode);
+}
+
+static void clock_tms_cs_out(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset,
+ unsigned length, bool tdi, uint8_t mode)
+{
+ if (oscan1_mode)
+ oscan1_mpsse_clock_tms_cs_out(ctx, out, out_offset, length, tdi, mode);
+ else
+ mpsse_clock_tms_cs_out(ctx, out, out_offset, length, tdi, mode);
+}
+#endif
+
/**
* Function move_to_state
* moves the TAP controller from the current state to a
@@ -268,7 +335,7 @@ static void move_to_state(tap_state_t goal_state)
for (int i = 0; i < tms_count; i++)
tap_set_state(tap_state_transition(tap_get_state(), (tms_bits >> i) & 1));
- mpsse_clock_tms_cs_out(mpsse_ctx,
+ DO_CLOCK_TMS_CS_OUT(mpsse_ctx,
&tms_bits,
0,
tms_count,
@@ -322,7 +389,7 @@ static void ftdi_end_state(tap_state_t state)
static void ftdi_execute_runtest(struct jtag_command *cmd)
{
int i;
- uint8_t zero = 0;
+ static const uint8_t zero;
DEBUG_JTAG_IO("runtest %i cycles, end in %s",
cmd->cmd.runtest->num_cycles,
@@ -336,7 +403,7 @@ static void ftdi_execute_runtest(struct jtag_command *cmd)
while (i > 0) {
/* there are no state transitions in this code, so omit state tracking */
unsigned this_len = i > 7 ? 7 : i;
- mpsse_clock_tms_cs_out(mpsse_ctx, &zero, 0, this_len, false, ftdi_jtag_mode);
+ DO_CLOCK_TMS_CS_OUT(mpsse_ctx, &zero, 0, this_len, false, ftdi_jtag_mode);
i -= this_len;
}
@@ -371,7 +438,7 @@ static void ftdi_execute_tms(struct jtag_command *cmd)
DEBUG_JTAG_IO("TMS: %d bits", cmd->cmd.tms->num_bits);
/* TODO: Missing tap state tracking, also missing from ft2232.c! */
- mpsse_clock_tms_cs_out(mpsse_ctx,
+ DO_CLOCK_TMS_CS_OUT(mpsse_ctx,
cmd->cmd.tms->bits,
0,
cmd->cmd.tms->num_bits,
@@ -418,7 +485,7 @@ static void ftdi_execute_pathmove(struct jtag_command *cmd)
state_count++;
if (bit_count == 7 || num_states == 0) {
- mpsse_clock_tms_cs_out(mpsse_ctx,
+ DO_CLOCK_TMS_CS_OUT(mpsse_ctx,
&tms_byte,
0,
bit_count,
@@ -472,7 +539,7 @@ static void ftdi_execute_scan(struct jtag_command *cmd)
if (i == cmd->cmd.scan->num_fields - 1 && tap_get_state() != tap_get_end_state()) {
/* Last field, and we're leaving IRSHIFT/DRSHIFT. Clock last bit during tap
* movement. This last field can't have length zero, it was checked above. */
- mpsse_clock_data(mpsse_ctx,
+ DO_CLOCK_DATA(mpsse_ctx,
field->out_value,
0,
field->in_value,
@@ -483,7 +550,7 @@ static void ftdi_execute_scan(struct jtag_command *cmd)
if (field->out_value)
bit_copy(&last_bit, 0, field->out_value, field->num_bits - 1, 1);
uint8_t tms_bits = 0x01;
- mpsse_clock_tms_cs(mpsse_ctx,
+ DO_CLOCK_TMS_CS(mpsse_ctx,
&tms_bits,
0,
field->in_value,
@@ -492,7 +559,7 @@ static void ftdi_execute_scan(struct jtag_command *cmd)
last_bit,
ftdi_jtag_mode);
tap_set_state(tap_state_transition(tap_get_state(), 1));
- mpsse_clock_tms_cs_out(mpsse_ctx,
+ DO_CLOCK_TMS_CS_OUT(mpsse_ctx,
&tms_bits,
1,
1,
@@ -500,7 +567,7 @@ static void ftdi_execute_scan(struct jtag_command *cmd)
ftdi_jtag_mode);
tap_set_state(tap_state_transition(tap_get_state(), 0));
} else
- mpsse_clock_data(mpsse_ctx,
+ DO_CLOCK_DATA(mpsse_ctx,
field->out_value,
0,
field->in_value,
@@ -585,7 +652,7 @@ static void ftdi_execute_stableclocks(struct jtag_command *cmd)
while (num_cycles > 0) {
/* there are no state transitions in this code, so omit state tracking */
unsigned this_len = num_cycles > 7 ? 7 : num_cycles;
- mpsse_clock_tms_cs_out(mpsse_ctx, &tms, 0, this_len, false, ftdi_jtag_mode);
+ DO_CLOCK_TMS_CS_OUT(mpsse_ctx, &tms, 0, this_len, false, ftdi_jtag_mode);
num_cycles -= this_len;
}
@@ -599,12 +666,18 @@ static void ftdi_execute_command(struct jtag_command *cmd)
switch (cmd->type) {
case JTAG_RESET:
ftdi_execute_reset(cmd);
+#if BUILD_FTDI_OSCAN1 == 1
+ oscan1_reset_online_activate(); /* put the target back into OSCAN1 mode */
+#endif
break;
case JTAG_RUNTEST:
ftdi_execute_runtest(cmd);
break;
case JTAG_TLR_RESET:
ftdi_execute_statemove(cmd);
+#if BUILD_FTDI_OSCAN1 == 1
+ oscan1_reset_online_activate(); /* put the target back into OSCAN1 mode */
+#endif
break;
case JTAG_PATHMOVE:
ftdi_execute_pathmove(cmd);
@@ -678,6 +751,17 @@ static int ftdi_initialize(void)
/* A dummy SWD_EN would have zero mask */
if (sig->data_mask)
ftdi_set_signal(sig, '1');
+#if BUILD_FTDI_OSCAN1 == 1
+ } else if (oscan1_mode) {
+ struct signal *sig = find_signal_by_name("JTAG_SEL");
+ if (!sig) {
+ LOG_ERROR("OSCAN1 mode is active but JTAG_SEL signal is not defined");
+ return ERROR_JTAG_INIT_FAILED;
+ }
+ /* A dummy JTAG_SEL would have zero mask */
+ if (sig->data_mask)
+ ftdi_set_signal(sig, '0');
+#endif
}
mpsse_set_data_bits_low_byte(mpsse_ctx, output & 0xff, direction & 0xff);
@@ -710,6 +794,240 @@ static int ftdi_quit(void)
return ERROR_OK;
}
+#if BUILD_FTDI_OSCAN1 == 1
+static void oscan1_mpsse_clock_data(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in,
+ unsigned in_offset, unsigned length, uint8_t mode)
+{
+ static const uint8_t zero;
+ static const uint8_t one = 1;
+
+ DEBUG_IO("oscan1_mpsse_clock_data: %sout %d bits", in ? "in" : "", length);
+
+ for (unsigned i = 0; i < length; i++) {
+ int bitnum;
+ uint8_t bit;
+
+ /* OSCAN1 uses 3 separate clocks */
+
+ /* drive TMSC to the *negation* of the desired TDI value */
+ bitnum = out_offset + i;
+ bit = out ? ((out[bitnum/8] >> (bitnum%8)) & 0x1) : 0;
+
+ /* Try optimized case first: if desired TDI bit is 1, then we
+ can fuse what would otherwise be the first two MPSSE commands */
+ if (bit) {
+ const uint8_t tmsbits = 0x3; /* 1, 1 */
+ mpsse_clock_tms_cs_out(mpsse_ctx, &tmsbits, 0, 2, false, mode);
+ } else {
+ /* Can't fuse because TDI varies; less efficient */
+ mpsse_clock_tms_cs_out(mpsse_ctx, &one, 0, 1, bit ? 0 : 1, mode);
+
+ /* drive TMSC to desired TMS value (always zero in this context) */
+ mpsse_clock_tms_cs_out(mpsse_ctx, &one, 0, 1, false, mode);
+ }
+
+ /* drive another TCK without driving TMSC (TDO cycle) */
+ mpsse_clock_tms_cs(mpsse_ctx, &zero, 0, in, in_offset+i, 1, false, mode);
+ }
+}
+
+
+static void oscan1_mpsse_clock_tms_cs(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in,
+ unsigned in_offset, unsigned length, bool tdi, uint8_t mode)
+{
+ static const uint8_t zero;
+ static const uint8_t one = 1;
+
+ DEBUG_IO("oscan1_mpsse_clock_tms_cs: %sout %d bits, tdi=%d", in ? "in" : "", length, tdi);
+
+ for (unsigned i = 0; i < length; i++) {
+ int bitnum;
+ uint8_t tmsbit;
+ uint8_t tdibit;
+
+ /* OSCAN1 uses 3 separate clocks */
+
+ /* drive TMSC to the *negation* of the desired TDI value */
+ tdibit = tdi ? 0 : 1;
+
+ /* drive TMSC to desired TMS value */
+ bitnum = out_offset + i;
+ tmsbit = ((out[bitnum/8] >> (bitnum%8)) & 0x1);
+
+ if (tdibit == tmsbit) {
+ /* Can squash into a single MPSSE command */
+ const uint8_t tmsbits = 0x3;
+ mpsse_clock_tms_cs_out(mpsse_ctx, &tmsbits, 0, 2, tdibit, mode);
+ } else {
+ /* Unoptimized case, can't formulate with a single command */
+ mpsse_clock_tms_cs_out(mpsse_ctx, &one, 0, 1, tdibit, mode);
+ mpsse_clock_tms_cs_out(mpsse_ctx, &one, 0, 1, (tmsbit != 0), mode);
+ }
+
+ /* drive another TCK without driving TMSC (TDO cycle) */
+ mpsse_clock_tms_cs(mpsse_ctx, &zero, 0, in, in_offset+i, 1, false, mode);
+ }
+}
+
+
+static void oscan1_mpsse_clock_tms_cs_out(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset,
+ unsigned length, bool tdi, uint8_t mode)
+{
+ oscan1_mpsse_clock_tms_cs(ctx, out, out_offset, 0, 0, length, tdi, mode);
+}
+
+
+static void oscan1_set_tck_tms_tdi(struct signal *tck, char tckvalue, struct signal *tms,
+ char tmsvalue, struct signal *tdi, char tdivalue)
+{
+ ftdi_set_signal(tms, tmsvalue);
+ ftdi_set_signal(tdi, tdivalue);
+ ftdi_set_signal(tck, tckvalue);
+}
+
+static void oscan1_reset_online_activate(void)
+{
+ /* After TAP reset, the OSCAN1-to-JTAG adapter is in offline and
+ non-activated state. Escape sequences are needed to bring
+ the TAP online and activated into OSCAN1 mode. */
+
+ struct signal *tck = find_signal_by_name("TCK");
+ struct signal *tdi = find_signal_by_name("TDI");
+ struct signal *tms = find_signal_by_name("TMS");
+ struct signal *tdo = find_signal_by_name("TDO");
+ uint16_t tdovalue;
+
+ static const struct {
+ int8_t tck;
+ int8_t tms;
+ int8_t tdi;
+ } sequence[] = {
+ /* TCK=0, TMS=1, TDI=0 (drive TMSC to 0 baseline) */
+ {'0', '1', '0'},
+
+ /* Drive cJTAG escape sequence for TAP reset - 8 TMSC edges */
+ /* TCK=1, TMS=1, TDI=0 (rising edge of TCK with TMSC still 0) */
+ {'1', '1', '0'},
+ /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */
+ {'1', '1', '1'},
+ /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */
+ {'1', '1', '0'},
+ /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */
+ {'1', '1', '1'},
+ /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */
+ {'1', '1', '0'},
+ /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */
+ {'1', '1', '1'},
+ /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */
+ {'1', '1', '0'},
+ /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */
+ {'1', '1', '1'},
+ /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK with TMSC still 0) */
+ {'0', '1', '0'},
+
+ /* Drive cJTAG escape sequence for SELECT */
+ /* TCK=1, TMS=1, TDI=0 (rising edge of TCK with TMSC still 0, TAP reset that was just setup occurs here too) */
+ {'1', '1', '0'},
+ /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */
+ {'1', '1', '1'},
+ /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */
+ {'1', '1', '0'},
+ /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */
+ {'1', '1', '1'},
+ /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */
+ {'1', '1', '0'},
+ /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */
+ {'1', '1', '1'},
+ /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK with TMSC still 0) */
+ {'0', '1', '0'},
+
+ /* Drive cJTAG escape sequence for activation */
+ /* TCK=1, TMS=1, TDI=0 (rising edge TCK with TMSC still 0... online mode activated... also OAC bit0==0) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (rising edge TCK... OAC bit1==0) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=1 (falling edge TCK) */
+ {'0', '1', '1'},
+ /* TCK=1, TMS=1, TDI=1 (rising edge TCK... OAC bit2==1) */
+ {'1', '1', '1'},
+ /* TCK=0, TMS=1, TDI=1 (falling edge TCK, TMSC stays high) */
+ {'0', '1', '1'},
+ /* TCK=1, TMS=1, TDI=1 (rising edge TCK... OAC bit3==1) */
+ {'1', '1', '1'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (rising edge TCK... EC bit0==0) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (rising edge TCK... EC bit1==0) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (rising edge TCK... EC bit2==0) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=1 (falling edge TCK) */
+ {'0', '1', '1'},
+ /* TCK=1, TMS=1, TDI=1 (rising edge TCK... EC bit3==1) */
+ {'1', '1', '1'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (rising edge TCK... CP bit0==0) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (rising edge TCK... CP bit1==0) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (rising edge TCK... CP bit2==0) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (rising edge TCK... CP bit3==0) */
+ {'1', '1', '0'},
+ };
+
+
+ if (!oscan1_mode)
+ return;
+
+
+ if (!tck) {
+ LOG_ERROR("Can't run cJTAG online/activate escape sequences: TCK signal is not defined");
+ return;
+ }
+
+ if (!tdi) {
+ LOG_ERROR("Can't run cJTAG online/activate escape sequences: TDI signal is not defined");
+ return;
+ }
+
+ if (!tms) {
+ LOG_ERROR("Can't run cJTAG online/activate escape sequences: TMS signal is not defined");
+ return;
+ }
+
+ if (!tdo) {
+ LOG_ERROR("Can't run cJTAG online/activate escape sequences: TDO signal is not defined");
+ return;
+ }
+
+ /* Send the sequence to the adapter */
+ for (size_t i = 0; i < sizeof(sequence)/sizeof(sequence[0]); i++)
+ oscan1_set_tck_tms_tdi(tck, sequence[i].tck, tms, sequence[i].tms, tdi, sequence[i].tdi);
+
+ ftdi_get_signal(tdo, &tdovalue); /* Just to force a flush */
+}
+
+#endif /* #if BUILD_FTDI_OSCAN1 == 1 */
+
COMMAND_HANDLER(ftdi_handle_device_desc_command)
{
if (CMD_ARGC == 1) {
@@ -935,6 +1253,20 @@ COMMAND_HANDLER(ftdi_handle_tdo_sample_edge_command)
return ERROR_OK;
}
+#if BUILD_FTDI_OSCAN1 == 1
+COMMAND_HANDLER(ftdi_handle_oscan1_mode_command)
+{
+ if (CMD_ARGC > 1)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (CMD_ARGC == 1)
+ COMMAND_PARSE_ON_OFF(CMD_ARGV[0], oscan1_mode);
+
+ command_print(CMD_CTX, "oscan1 mode: %s.", oscan1_mode ? "on" : "off");
+ return ERROR_OK;
+}
+#endif
+
static const struct command_registration ftdi_command_handlers[] = {
{
.name = "ftdi_device_desc",
@@ -1003,6 +1335,15 @@ static const struct command_registration ftdi_command_handlers[] = {
"allow signalling speed increase)",
.usage = "(rising|falling)",
},
+#if BUILD_FTDI_OSCAN1 == 1
+ {
+ .name = "ftdi_oscan1_mode",
+ .handler = &ftdi_handle_oscan1_mode_command,
+ .mode = COMMAND_ANY,
+ .help = "set to 'on' to use OSCAN1 mode for signaling, otherwise 'off' (default is 'off')",
+ .usage = "(on|off)",
+ },
+#endif
COMMAND_REGISTRATION_DONE
};
diff --git a/src/jtag/drivers/libjaylink b/src/jtag/drivers/libjaylink
deleted file mode 160000
-Subproject 8645845c1abebd004e991ba9a7f808f4fd0c608
diff --git a/src/jtag/drivers/libjaylink/.gitignore b/src/jtag/drivers/libjaylink/.gitignore
new file mode 100644
index 0000000..0197dc0
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/.gitignore
@@ -0,0 +1,24 @@
+aclocal.m4
+autom4te.cache
+build-aux
+config.h*
+config.log
+config.status
+configure
+configure.gnu
+.deps
+doxy/
+Doxyfile
+INSTALL
+*.la
+libjaylink.pc
+.libs
+libtool
+*.lo
+m4/libtool.m4
+m4/lt*.m4
+Makefile
+Makefile.in
+*.o
+stamp-h1
+version.h
diff --git a/src/jtag/drivers/libjaylink/AUTHORS b/src/jtag/drivers/libjaylink/AUTHORS
new file mode 100644
index 0000000..507d6e0
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/AUTHORS
@@ -0,0 +1,2 @@
+Please check the source code files and/or Git commit history for a list of all
+authors and contributors.
diff --git a/src/jtag/drivers/libjaylink/COPYING b/src/jtag/drivers/libjaylink/COPYING
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 2 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, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/src/jtag/drivers/libjaylink/ChangeLog b/src/jtag/drivers/libjaylink/ChangeLog
new file mode 100644
index 0000000..fade3c9
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/ChangeLog
@@ -0,0 +1 @@
+Please check the Git commit history for a detailed list of changes.
diff --git a/src/jtag/drivers/libjaylink/Doxyfile.in b/src/jtag/drivers/libjaylink/Doxyfile.in
new file mode 100644
index 0000000..86c5460
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/Doxyfile.in
@@ -0,0 +1,2301 @@
+# Doxyfile 1.8.6
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME = "libjaylink"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER = "@JAYLINK_VERSION_PACKAGE@"
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF = "Library to access J-Link devices"
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
+# the documentation. The maximum height of the logo should not exceed 55 pixels
+# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
+# to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = doxy
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a
+# new page for each member. If set to NO, the documentation of a member will be
+# part of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C.
+#
+# Note For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by by putting a % sign in front of the word
+# or globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO these classes will be included in the various overviews. This option has
+# no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the
+# todo list. This list is created by putting \todo commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the
+# test list. This list is created by putting \test commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES the list
+# will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. Do not use file names with spaces, bibtex cannot handle them. See
+# also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO doxygen will only warn about wrong or incomplete parameter
+# documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces.
+# Note: If this tag is empty the current directory is searched.
+
+INPUT = @top_srcdir@/libjaylink \
+ @top_builddir@/libjaylink/version.h
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank the
+# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
+# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
+# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
+# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
+# *.qsf, *.as and *.js.
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE = @top_srcdir@/libjaylink/buffer.c \
+ @top_srcdir@/libjaylink/discovery_tcp.c \
+ @top_srcdir@/libjaylink/discovery_usb.c \
+ @top_srcdir@/libjaylink/libjaylink-internal.h \
+ @top_srcdir@/libjaylink/list.c \
+ @top_srcdir@/libjaylink/socket.c \
+ @top_srcdir@/libjaylink/transport.c \
+ @top_srcdir@/libjaylink/transport_tcp.c \
+ @top_srcdir@/libjaylink/transport_usb.c
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER ) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
+# defined cascading style sheet that is included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefor more robust against future updates.
+# Doxygen will copy the style sheet file to the output directory. For an example
+# see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the stylesheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler ( hhc.exe). If non-empty
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated (
+# YES) or that it should be included in the master .chm file ( NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated (
+# YES) or a normal table of contents ( NO) in the .chm file.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 1
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using prerendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavours of web server based searching depending on the
+# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
+# searching and an index file used by the script. When EXTERNAL_SEARCH is
+# enabled the indexing and searching needs to be provided by external tools. See
+# the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. To get the times font for
+# instance you can specify
+# EXTRA_PACKAGES=times
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
+# replace them by respectively the title of the page, the current date and time,
+# only the current date, the version number of doxygen, the project name (see
+# PROJECT_NAME), or the project number (see PROJECT_NUMBER).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS = YES
+
+# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen
+# Definitions (see http://autogen.sf.net) file that captures the structure of
+# the code including all documentation. Note that this feature is still
+# experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names
+# in the source code. If set to NO only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES the includes files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED = JAYLINK_API= \
+ JAYLINK_PRIV
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all refrences to function-like macros that are alone on a line, have an
+# all uppercase name, and do not end with a semicolon. Such function macros are
+# typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have an unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
+# class index. If set to NO only the inherited external classes will be listed.
+# The default value is: NO.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in
+# the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS = YES
+
+# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS = 0
+
+# When you want a differently looking font n the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot.
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif and svg.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS = YES
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP = YES
diff --git a/src/jtag/drivers/libjaylink/HACKING b/src/jtag/drivers/libjaylink/HACKING
new file mode 100644
index 0000000..a654e2a
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/HACKING
@@ -0,0 +1,68 @@
+Hacking
+=======
+
+This document describes how to start hacking on the libjaylink project.
+Make sure you read through the README file before continuing.
+
+
+Coding style
+------------
+
+This project uses the Linux kernel coding style where appropiate, see
+<https://www.kernel.org/doc/Documentation/CodingStyle> for details.
+
+Amendments to the Linux kernel coding style:
+
+ - Do not use goto statements.
+ - Always declare variables at the beginng of a function.
+ - Do not assign values to variables at declaration time.
+
+
+Contributions
+-------------
+
+The following ways can be used to submit a contribution to the libjaylink
+project:
+
+ - Send patches generated with `git format-patch`.
+ - Push your changes to a public Git repository and send the URL where to pull
+ them from.
+
+In any case, send directly to <jaylink-dev@marcschink.de>.
+Before submitting, please consider the following:
+
+ - Every single patch must be compilable.
+ - Your contribution must work on all operating systems supported by
+ libjaylink.
+ - Develop your contribution against the current Git master branch.
+ - Check your contribution for memory leaks and similar errors by using
+ *valgrind*.
+
+
+Bug reports
+-----------
+
+Send bug reports directly to <jaylink-dev@marcschink.de>.
+Please try to include all of the following information in your report:
+
+ - Instructions to reproduce the bug (e.g., command-line invocations)
+ - Debug log output of libjaylink
+ - Information about your environment:
+ - Version of libjaylink
+ - Debug hardware, including hardware and firmware version (e.g.,
+ J-Link Ultra V4 compiled Sep 4 2015 18:12:49)
+ - Operating system (e.g., Debian GNU/Linux 8.4)
+ - Backtraces if the application using libjaylink is crashing.
+
+If the bug report is for a regression, additionally include the information
+above about the working version where appropiate.
+
+Please develop and attach a patch that solves the reported bug, if possible.
+See the guidelines for contributions above.
+
+
+Random notes
+------------
+
+ - Always use `log_err()`, `log_warn()`, `log_info()` and `log_dbg()` to output
+ messages. Never use `printf()` or similar functions directly.
diff --git a/src/jtag/drivers/libjaylink/Makefile.am b/src/jtag/drivers/libjaylink/Makefile.am
new file mode 100644
index 0000000..6cead16
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/Makefile.am
@@ -0,0 +1,28 @@
+##
+## This file is part of the libjaylink project.
+##
+## Copyright (C) 2014-2015 Marc Schink <jaylink-dev@marcschink.de>
+##
+## 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 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+
+ACLOCAL_AMFLAGS = -I m4
+SUBDIRS = libjaylink
+
+if !SUBPROJECT_BUILD
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libjaylink.pc
+endif
+
+EXTRA_DIST = HACKING contrib/99-libjaylink.rules
diff --git a/src/jtag/drivers/libjaylink/NEWS b/src/jtag/drivers/libjaylink/NEWS
new file mode 100644
index 0000000..39244f6
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/NEWS
@@ -0,0 +1,7 @@
+News
+====
+
+0.1.0 (2016-12-28)
+------------------
+
+ * Initial release.
diff --git a/src/jtag/drivers/libjaylink/README b/src/jtag/drivers/libjaylink/README
new file mode 100644
index 0000000..d01c904
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/README
@@ -0,0 +1,70 @@
+libjaylink
+==========
+
+libjaylink is a shared library written in C to access SEGGER J-Link and
+compatible devices.
+
+
+Requirements
+------------
+
+libjaylink requires the following packages:
+
+ - GCC (>= 4.0) or Clang
+ - Make
+ - pkg-config >= 0.23
+ - libusb >= 1.0.9
+ - Doxygen (optional, only required for API documentation)
+
+If you're building libjaylink from Git, the following packages are additionally
+required:
+
+ - Git
+ - Libtool
+ - Autoconf >= 2.64
+ - Automake >= 1.9
+
+
+Building and installing
+-----------------------
+
+In order to get and build the latest Git version of libjaylink, run the
+following commands:
+
+ $ git clone git://git.zapb.de/libjaylink.git
+ $ cd libjaylink
+ $ ./autogen.sh
+ $ ./configure
+ $ make
+
+After `make` finishes without any errors, use the following command to install
+libjaylink:
+
+ $ make install
+
+
+Portability
+-----------
+
+libjaylink supports the following operating systems:
+
+ - GNU/Linux
+ - FreeBSD
+ - OpenBSD
+ - NetBSD
+ - Microsoft Windows
+ - Cygwin, MinGW and MSYS2
+ - OS X
+
+
+Copyright and license
+---------------------
+
+libjaylink is licensed under the terms of the GNU General Public License (GPL),
+version 2 or later. See COPYING file for details.
+
+
+Website
+-------
+
+<http://git.zapb.de/libjaylink.git>
diff --git a/src/jtag/drivers/libjaylink/autogen.sh b/src/jtag/drivers/libjaylink/autogen.sh
new file mode 100755
index 0000000..1df262f
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/autogen.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+##
+## This file is part of the libjaylink project.
+##
+## Copyright (C) 2014-2015 Marc Schink <jaylink-dev@marcschink.de>
+##
+## 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 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+
+OS=`uname -s`
+LIBTOOLIZE=libtoolize
+
+if [ "x$OS" = "xDarwin" ]; then
+ LIBTOOLIZE=glibtoolize
+fi
+
+echo "Generating build system..."
+
+$LIBTOOLIZE --install --copy || exit 1
+aclocal -I m4 || exit 1
+autoheader || exit 1
+autoconf || exit 1
+automake --add-missing --copy || exit 1
diff --git a/src/jtag/drivers/libjaylink/configure.ac b/src/jtag/drivers/libjaylink/configure.ac
new file mode 100644
index 0000000..de5919c
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/configure.ac
@@ -0,0 +1,140 @@
+##
+## This file is part of the libjaylink project.
+##
+## Copyright (C) 2014-2015 Marc Schink <jaylink-dev@marcschink.de>
+##
+## 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 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+
+AC_PREREQ([2.64])
+
+AC_INIT([libjaylink], [0.2.0], [jaylink-dev@marcschink.de], [libjaylink],
+ [http://git.zapb.de/libjaylink.git])
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_MACRO_DIR([m4])
+AC_CONFIG_AUX_DIR([build-aux])
+
+AC_CANONICAL_HOST
+
+AM_INIT_AUTOMAKE([-Wall -Werror])
+
+# Enable additional compiler warnings via -Wall and -Wextra. Use hidden
+# visibility for all non-static symbols by default with -fvisibility=hidden.
+JAYLINK_CFLAGS="-Wall -Wextra -Werror -fvisibility=hidden"
+
+# Checks for programs.
+AC_PROG_CC
+
+# Automake >= 1.12 requires AM_PROG_AR when using options -Wall and -Werror.
+# To be compatible with older versions of Automake use AM_PROG_AR if it's
+# defined only. This line must occur before LT_INIT.
+m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
+
+# Initialize libtool.
+LT_INIT
+
+# Initialize pkg-config.
+PKG_PROG_PKG_CONFIG
+
+# Checks for libraries.
+
+# Check for libusb-1.0 which is always needed.
+PKG_CHECK_MODULES([libusb], [libusb-1.0 >= 1.0.9],
+ [HAVE_LIBUSB=yes], [HAVE_LIBUSB=no])
+
+AS_IF([test "x$HAVE_LIBUSB" = "xyes"],
+ [libusb_msg="yes"], [libusb_msg="no (missing: libusb-1.0)"])
+
+# Checks for header files.
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_C_BIGENDIAN
+
+# Checks for library functions.
+
+# Disable progress and informational output of libtool.
+AC_SUBST([AM_LIBTOOLFLAGS], '--silent')
+
+JAYLINK_SET_PACKAGE_VERSION([JAYLINK_VERSION_PACKAGE], [AC_PACKAGE_VERSION])
+
+# Libtool interface version of libjaylink. This is not the same as the package
+# version. For information about the versioning system of libtool, see:
+# http://www.gnu.org/software/libtool/manual/libtool.html#Libtool-versioning
+JAYLINK_SET_LIBRARY_VERSION([JAYLINK_VERSION_LIBRARY], [0:0:0])
+
+AC_ARG_ENABLE([subproject-build], AS_HELP_STRING([--enable-subproject-build],
+ [enable sub-project build [default=no]]))
+
+AM_CONDITIONAL([SUBPROJECT_BUILD],
+ [test "x$enable_subproject_build" = "xyes"])
+
+AC_ARG_WITH([libusb], [AS_HELP_STRING([--without-libusb],
+ [disable libusb support [default=detect]])])
+
+AS_IF([test "x$with_libusb" != "xno"],
+ [with_libusb="yes"])
+
+AS_IF([test "x$with_libusb$HAVE_LIBUSB" = "xyesyes"],
+ [AC_DEFINE([HAVE_LIBUSB], [1], [Define to 1 if libusb is available.])])
+
+AS_IF([test "x$with_libusb" != "xyes"],
+ [libusb_msg="no (disabled)"])
+
+AS_IF([test "x$with_libusb$HAVE_LIBUSB" = "xyesyes"],
+ [JAYLINK_PKG_LIBS="libusb-1.0"])
+
+AM_CONDITIONAL([HAVE_LIBUSB],
+ [test "x$with_libusb$HAVE_LIBUSB" = "xyesyes"])
+
+# Libtool interface version is not used for sub-project build as libjaylink is
+# built as libtool convenience library.
+AS_IF([test "x$enable_subproject_build" != "xyes"],
+ [JAYLINK_LDFLAGS="-version-info $JAYLINK_VERSION_LIBRARY"])
+
+# Use C99 compatible stdio functions on MinGW instead of the incompatible
+# functions provided by Microsoft.
+AS_CASE([$host_os], [mingw*],
+ [AC_DEFINE([__USE_MINGW_ANSI_STDIO], [1],
+ [Define to 1 to use C99 compatible stdio functions on MinGW.])])
+
+# Add the Winsock2 library on MinGW for socket and other network-related
+# functions.
+AS_CASE([$host_os], [mingw*], [JAYLINK_LIBS="$JAYLINK_LIBS -lws2_32"])
+
+AC_SUBST([JAYLINK_CFLAGS])
+AC_SUBST([JAYLINK_LDFLAGS])
+AC_SUBST([JAYLINK_LIBS])
+AC_SUBST([JAYLINK_PKG_LIBS])
+
+AC_CONFIG_FILES([Makefile])
+AC_CONFIG_FILES([libjaylink/Makefile])
+AC_CONFIG_FILES([libjaylink/version.h])
+AC_CONFIG_FILES([libjaylink.pc])
+AC_CONFIG_FILES([Doxyfile])
+
+AC_OUTPUT
+
+echo
+echo "libjaylink configuration summary:"
+echo " - Package version ................ $JAYLINK_VERSION_PACKAGE"
+echo " - Library version ................ $JAYLINK_VERSION_LIBRARY"
+echo " - Installation prefix ............ $prefix"
+echo " - Building on .................... $build"
+echo " - Building for ................... $host"
+
+echo
+echo "Enabled transports:"
+echo " - USB ............................ $libusb_msg"
+echo " - TCP ............................ yes"
+echo
diff --git a/src/jtag/drivers/libjaylink/contrib/99-libjaylink.rules b/src/jtag/drivers/libjaylink/contrib/99-libjaylink.rules
new file mode 100644
index 0000000..120e51a
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/contrib/99-libjaylink.rules
@@ -0,0 +1,41 @@
+##
+## This file is part of the libjaylink project.
+##
+## Copyright (C) 2015 Marc Schink <jaylink-dev@marcschink.de>
+##
+## 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 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+
+ACTION!="add", GOTO="libjaylink_end_rules"
+SUBSYSTEM!="usb", GOTO="libjaylink_end_rules"
+
+ATTRS{idVendor}=="1366", ATTRS{idProduct}=="0101", MODE="664", GROUP="plugdev"
+ATTRS{idVendor}=="1366", ATTRS{idProduct}=="0102", MODE="664", GROUP="plugdev"
+ATTRS{idVendor}=="1366", ATTRS{idProduct}=="0103", MODE="664", GROUP="plugdev"
+ATTRS{idVendor}=="1366", ATTRS{idProduct}=="0104", MODE="664", GROUP="plugdev"
+ATTRS{idVendor}=="1366", ATTRS{idProduct}=="0105", MODE="664", GROUP="plugdev"
+ATTRS{idVendor}=="1366", ATTRS{idProduct}=="0107", MODE="664", GROUP="plugdev"
+ATTRS{idVendor}=="1366", ATTRS{idProduct}=="0108", MODE="664", GROUP="plugdev"
+
+ATTRS{idVendor}=="1366", ATTRS{idProduct}=="1010", MODE="664", GROUP="plugdev"
+ATTRS{idVendor}=="1366", ATTRS{idProduct}=="1011", MODE="664", GROUP="plugdev"
+ATTRS{idVendor}=="1366", ATTRS{idProduct}=="1012", MODE="664", GROUP="plugdev"
+ATTRS{idVendor}=="1366", ATTRS{idProduct}=="1013", MODE="664", GROUP="plugdev"
+ATTRS{idVendor}=="1366", ATTRS{idProduct}=="1014", MODE="664", GROUP="plugdev"
+ATTRS{idVendor}=="1366", ATTRS{idProduct}=="1015", MODE="664", GROUP="plugdev"
+ATTRS{idVendor}=="1366", ATTRS{idProduct}=="1016", MODE="664", GROUP="plugdev"
+ATTRS{idVendor}=="1366", ATTRS{idProduct}=="1017", MODE="664", GROUP="plugdev"
+ATTRS{idVendor}=="1366", ATTRS{idProduct}=="1018", MODE="664", GROUP="plugdev"
+
+LABEL="libjaylink_end_rules"
diff --git a/src/jtag/drivers/libjaylink/libjaylink.pc.in b/src/jtag/drivers/libjaylink/libjaylink.pc.in
new file mode 100644
index 0000000..a5efd3a
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/libjaylink.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libjaylink
+Description: Library to access J-Link devices
+Version: @VERSION@
+Requires.private: @JAYLINK_PKG_LIBS@
+Libs: -L${libdir} -ljaylink
+Cflags: -I${includedir}
diff --git a/src/jtag/drivers/libjaylink/libjaylink/Makefile.am b/src/jtag/drivers/libjaylink/libjaylink/Makefile.am
new file mode 100644
index 0000000..62c5489
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/libjaylink/Makefile.am
@@ -0,0 +1,62 @@
+##
+## This file is part of the libjaylink project.
+##
+## Copyright (C) 2014-2015 Marc Schink <jaylink-dev@marcschink.de>
+##
+## 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 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+
+if SUBPROJECT_BUILD
+noinst_LTLIBRARIES = libjaylink.la
+else
+lib_LTLIBRARIES = libjaylink.la
+
+library_includedir = $(includedir)/libjaylink
+library_include_HEADERS = libjaylink.h
+nodist_library_include_HEADERS = version.h
+endif
+
+libjaylink_la_SOURCES = \
+ buffer.c \
+ core.c \
+ device.c \
+ discovery.c \
+ discovery_tcp.c \
+ emucom.c \
+ error.c \
+ fileio.c \
+ jtag.c \
+ list.c \
+ log.c \
+ socket.c \
+ strutil.c \
+ swd.c \
+ swo.c \
+ target.c \
+ transport.c \
+ transport_tcp.c \
+ util.c \
+ version.c
+
+libjaylink_la_CFLAGS = $(JAYLINK_CFLAGS)
+libjaylink_la_LDFLAGS = $(JAYLINK_LDFLAGS) -no-undefined
+libjaylink_la_LIBADD = $(JAYLINK_LIBS)
+
+if HAVE_LIBUSB
+libjaylink_la_SOURCES += discovery_usb.c transport_usb.c
+libjaylink_la_CFLAGS += $(libusb_CFLAGS)
+libjaylink_la_LIBADD += $(libusb_LIBS)
+endif
+
+noinst_HEADERS = libjaylink-internal.h
diff --git a/src/jtag/drivers/libjaylink/libjaylink/buffer.c b/src/jtag/drivers/libjaylink/libjaylink/buffer.c
new file mode 100644
index 0000000..527c25e
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/libjaylink/buffer.c
@@ -0,0 +1,140 @@
+/*
+ * This file is part of the libjaylink project.
+ *
+ * Copyright (C) 2014-2015 Marc Schink <jaylink-dev@marcschink.de>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "libjaylink-internal.h"
+
+/**
+ * @file
+ *
+ * Buffer helper functions.
+ */
+
+/**
+ * Write a 16-bit unsigned integer value to a buffer.
+ *
+ * The value is stored in the buffer in device byte order.
+ *
+ * @param[out] buffer Buffer to write the value into.
+ * @param[in] value Value to write into the buffer in host byte order.
+ * @param[in] offset Offset of the value within the buffer in bytes.
+ */
+JAYLINK_PRIV void buffer_set_u16(uint8_t *buffer, uint16_t value,
+ size_t offset)
+{
+ /*
+ * Store the value in the buffer and swap byte order depending on the
+ * host byte order.
+ */
+#ifdef WORDS_BIGENDIAN
+ buffer[offset + 0] = value;
+ buffer[offset + 1] = value >> 8;
+#else
+ memcpy(buffer + offset, &value, sizeof(value));
+#endif
+}
+
+/**
+ * Read a 16-bit unsigned integer value from a buffer.
+ *
+ * The value in the buffer is expected to be stored in device byte order.
+ *
+ * @param[in] buffer Buffer to read the value from.
+ * @param[in] offset Offset of the value within the buffer in bytes.
+ *
+ * @return The value read from the buffer in host byte order.
+ */
+JAYLINK_PRIV uint16_t buffer_get_u16(const uint8_t *buffer, size_t offset)
+{
+ uint16_t value;
+
+ /*
+ * Read the value from the buffer and swap byte order depending on the
+ * host byte order.
+ */
+#ifdef WORDS_BIGENDIAN
+ value = (((uint16_t)buffer[offset + 1])) | \
+ (((uint16_t)buffer[offset + 0]) << 8);
+#else
+ memcpy(&value, buffer + offset, sizeof(value));
+#endif
+
+ return value;
+}
+
+/**
+ * Write a 32-bit unsigned integer value to a buffer.
+ *
+ * The value is stored in the buffer in device byte order.
+ *
+ * @param[out] buffer Buffer to write the value into.
+ * @param[in] value Value to write into the buffer in host byte order.
+ * @param[in] offset Offset of the value within the buffer in bytes.
+ */
+JAYLINK_PRIV void buffer_set_u32(uint8_t *buffer, uint32_t value,
+ size_t offset)
+{
+ /*
+ * Store the value in the buffer and swap byte order depending on the
+ * host byte order.
+ */
+#ifdef WORDS_BIGENDIAN
+ buffer[offset + 0] = value;
+ buffer[offset + 1] = value >> 8;
+ buffer[offset + 2] = value >> 16;
+ buffer[offset + 3] = value >> 24;
+#else
+ memcpy(buffer + offset, &value, sizeof(value));
+#endif
+}
+
+/**
+ * Read a 32-bit unsigned integer value from a buffer.
+ *
+ * The value in the buffer is expected to be stored in device byte order.
+ *
+ * @param[in] buffer Buffer to read the value from.
+ * @param[in] offset Offset of the value within the buffer in bytes.
+ *
+ * @return The value read from the buffer in host byte order.
+ */
+JAYLINK_PRIV uint32_t buffer_get_u32(const uint8_t *buffer, size_t offset)
+{
+ uint32_t value;
+
+ /*
+ * Read the value from the buffer and swap byte order depending on the
+ * host byte order.
+ */
+#ifdef WORDS_BIGENDIAN
+ value = (((uint32_t)buffer[offset + 3])) | \
+ (((uint32_t)buffer[offset + 2]) << 8) | \
+ (((uint32_t)buffer[offset + 1]) << 16) | \
+ (((uint32_t)buffer[offset + 0]) << 24);
+#else
+ memcpy(&value, buffer + offset, sizeof(value));
+#endif
+
+ return value;
+}
diff --git a/src/jtag/drivers/libjaylink/libjaylink/core.c b/src/jtag/drivers/libjaylink/libjaylink/core.c
new file mode 100644
index 0000000..509b89d
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/libjaylink/core.c
@@ -0,0 +1,219 @@
+/*
+ * This file is part of the libjaylink project.
+ *
+ * Copyright (C) 2014-2016 Marc Schink <jaylink-dev@marcschink.de>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdbool.h>
+#ifdef _WIN32
+#include <winsock2.h>
+#endif
+#ifdef HAVE_LIBUSB
+#include <libusb.h>
+#endif
+
+#include "libjaylink.h"
+#include "libjaylink-internal.h"
+
+/**
+ * @mainpage
+ *
+ * @section sec_intro Introduction
+ *
+ * This document describes the API of libjaylink.
+ *
+ * libjaylink is a shared library written in C to access SEGGER J-Link and
+ * compatible devices.
+ *
+ * @section sec_error Error handling
+ *
+ * The libjaylink functions which can fail use the return value to indicate an
+ * error. The functions typically return an error code of #jaylink_error.
+ * For each function, all possible error codes and their detailed descriptions
+ * are documented. As the possible error codes returned by a function may
+ * change it is recommended to also always cover unexpected values when
+ * checking for error codes to be compatible with later versions of libjaylink.
+ *
+ * There are a few exceptions where a function directly returns the result
+ * instead of an error code because it is more convenient from an API
+ * perspective and because there is only a single reason for failure which is
+ * clearly distinguishable from the result.
+ *
+ * @section sec_license Copyright and license
+ *
+ * libjaylink is licensed under the terms of the GNU General Public
+ * License (GPL), version 2 or later.
+ *
+ * @section sec_website Website
+ *
+ * <a href="http://git.zapb.de/libjaylink.git">git.zapb.de/libjaylink.git</a>
+ */
+
+/**
+ * @file
+ *
+ * Core library functions.
+ */
+
+/**
+ * Initialize libjaylink.
+ *
+ * This function must be called before any other libjaylink function is called.
+ *
+ * @param[out] ctx Newly allocated libjaylink context on success, and undefined
+ * on failure.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_MALLOC Memory allocation error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_init(struct jaylink_context **ctx)
+{
+ int ret;
+ struct jaylink_context *context;
+#ifdef _WIN32
+ WSADATA wsa_data;
+#endif
+
+ if (!ctx)
+ return JAYLINK_ERR_ARG;
+
+ context = malloc(sizeof(struct jaylink_context));
+
+ if (!context)
+ return JAYLINK_ERR_MALLOC;
+
+#ifdef HAVE_LIBUSB
+ if (libusb_init(&context->usb_ctx) != LIBUSB_SUCCESS) {
+ free(context);
+ return JAYLINK_ERR;
+ }
+#endif
+
+#ifdef _WIN32
+ ret = WSAStartup(MAKEWORD(2, 2), &wsa_data);
+
+ if (ret != 0) {
+#ifdef HAVE_LIBUSB
+ libusb_exit(context->usb_ctx);
+#endif
+ free(context);
+ return JAYLINK_ERR;
+ }
+
+ if (LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2) {
+#ifdef HAVE_LIBUSB
+ libusb_exit(context->usb_ctx);
+#endif
+ free(context);
+ return JAYLINK_ERR;
+ }
+#endif
+
+ context->devs = NULL;
+ context->discovered_devs = NULL;
+
+ /* Show error and warning messages by default. */
+ context->log_level = JAYLINK_LOG_LEVEL_WARNING;
+
+ context->log_callback = &log_vprintf;
+ context->log_callback_data = NULL;
+
+ ret = jaylink_log_set_domain(context, JAYLINK_LOG_DOMAIN_DEFAULT);
+
+ if (ret != JAYLINK_OK) {
+#ifdef HAVE_LIBUSB
+ libusb_exit(context->usb_ctx);
+#endif
+#ifdef _WIN32
+ WSACleanup();
+#endif
+ free(context);
+ return ret;
+ }
+
+ *ctx = context;
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Shutdown libjaylink.
+ *
+ * @param[in,out] ctx libjaylink context.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_exit(struct jaylink_context *ctx)
+{
+ struct list *item;
+
+ if (!ctx)
+ return JAYLINK_ERR_ARG;
+
+ item = ctx->discovered_devs;
+
+ while (item) {
+ jaylink_unref_device((struct jaylink_device *)item->data);
+ item = item->next;
+ }
+
+ list_free(ctx->discovered_devs);
+ list_free(ctx->devs);
+
+#ifdef HAVE_LIBUSB
+ libusb_exit(ctx->usb_ctx);
+#endif
+#ifdef _WIN32
+ WSACleanup();
+#endif
+ free(ctx);
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Check for a capability of libjaylink.
+ *
+ * @param[in] cap Capability to check for.
+ *
+ * @retval true Capability is supported.
+ * @retval false Capability is not supported or invalid argument.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API bool jaylink_library_has_cap(enum jaylink_capability cap)
+{
+ switch (cap) {
+#ifdef HAVE_LIBUSB
+ case JAYLINK_CAP_HIF_USB:
+ return true;
+#endif
+ default:
+ return false;
+ }
+}
diff --git a/src/jtag/drivers/libjaylink/libjaylink/device.c b/src/jtag/drivers/libjaylink/libjaylink/device.c
new file mode 100644
index 0000000..a3bddf6
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/libjaylink/device.c
@@ -0,0 +1,1707 @@
+/*
+ * This file is part of the libjaylink project.
+ *
+ * Copyright (C) 2014-2016 Marc Schink <jaylink-dev@marcschink.de>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#ifdef _WIN32
+#include <winsock2.h>
+#else
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_LIBUSB
+#include <libusb.h>
+#endif
+
+#include "libjaylink.h"
+#include "libjaylink-internal.h"
+
+/**
+ * @file
+ *
+ * Device enumeration and handling.
+ */
+
+/** @cond PRIVATE */
+#define CMD_GET_VERSION 0x01
+#define CMD_GET_HW_STATUS 0x07
+#define CMD_REGISTER 0x09
+#define CMD_GET_HW_INFO 0xc1
+#define CMD_GET_COUNTERS 0xc2
+#define CMD_GET_FREE_MEMORY 0xd4
+#define CMD_GET_CAPS 0xe8
+#define CMD_GET_EXT_CAPS 0xed
+#define CMD_GET_HW_VERSION 0xf0
+#define CMD_READ_CONFIG 0xf2
+#define CMD_WRITE_CONFIG 0xf3
+
+#define REG_CMD_REGISTER 0x64
+#define REG_CMD_UNREGISTER 0x65
+
+/** Size of the registration header in bytes. */
+#define REG_HEADER_SIZE 8
+/** Minimum registration information size in bytes. */
+#define REG_MIN_SIZE 0x4c
+/** Maximum registration information size in bytes. */
+#define REG_MAX_SIZE 0x200
+/** Size of a connection entry in bytes. */
+#define REG_CONN_INFO_SIZE 16
+/** @endcond */
+
+/** @private */
+JAYLINK_PRIV struct jaylink_device *device_allocate(
+ struct jaylink_context *ctx)
+{
+ struct jaylink_device *dev;
+ struct list *list;
+
+ dev = malloc(sizeof(struct jaylink_device));
+
+ if (!dev)
+ return NULL;
+
+ list = list_prepend(ctx->devs, dev);
+
+ if (!list) {
+ free(dev);
+ return NULL;
+ }
+
+ ctx->devs = list;
+
+ dev->ctx = ctx;
+ dev->ref_count = 1;
+
+ return dev;
+}
+
+static struct jaylink_device **allocate_device_list(size_t length)
+{
+ struct jaylink_device **list;
+
+ list = malloc(sizeof(struct jaylink_device *) * (length + 1));
+
+ if (!list)
+ return NULL;
+
+ list[length] = NULL;
+
+ return list;
+}
+
+/**
+ * Get available devices.
+ *
+ * @param[in,out] ctx libjaylink context.
+ * @param[out] devs Newly allocated array which contains instances of available
+ * devices on success, and undefined on failure. The array is
+ * NULL-terminated and must be free'd by the caller with
+ * jaylink_free_devices().
+ * @param[out] count Number of available devices on success, and undefined on
+ * failure. Can be NULL.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_MALLOC Memory allocation error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @see jaylink_discovery_scan()
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_get_devices(struct jaylink_context *ctx,
+ struct jaylink_device ***devs, size_t *count)
+{
+ size_t num;
+ struct list *item;
+ struct jaylink_device **tmp;
+ struct jaylink_device *dev;
+ size_t i;
+
+ if (!ctx || !devs)
+ return JAYLINK_ERR_ARG;
+
+ num = list_length(ctx->discovered_devs);
+ tmp = allocate_device_list(num);
+
+ if (!tmp) {
+ log_err(ctx, "Failed to allocate device list.");
+ return JAYLINK_ERR_MALLOC;
+ }
+
+ item = ctx->discovered_devs;
+
+ for (i = 0; i < num; i++) {
+ dev = (struct jaylink_device *)item->data;
+ tmp[i] = jaylink_ref_device(dev);
+ item = item->next;
+ }
+
+ if (count)
+ *count = num;
+
+ *devs = tmp;
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Free devices.
+ *
+ * @param[in,out] devs Array of device instances. Must be NULL-terminated.
+ * @param[in] unref Determines whether the device instances should be
+ * unreferenced.
+ *
+ * @see jaylink_get_devices()
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API void jaylink_free_devices(struct jaylink_device **devs, bool unref)
+{
+ size_t i;
+
+ if (!devs)
+ return;
+
+ if (unref) {
+ for (i = 0; devs[i]; i++)
+ jaylink_unref_device(devs[i]);
+ }
+
+ free(devs);
+}
+
+/**
+ * Get the host interface of a device.
+ *
+ * @param[in] dev Device instance.
+ * @param[out] iface Host interface of the device on success, and undefined on
+ * failure.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_device_get_host_interface(
+ const struct jaylink_device *dev,
+ enum jaylink_host_interface *iface)
+{
+ if (!dev || !iface)
+ return JAYLINK_ERR_ARG;
+
+ *iface = dev->iface;
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Get the serial number of a device.
+ *
+ * @note This serial number is for enumeration purpose only and might differ
+ * from the real serial number of the device.
+ *
+ * @param[in] dev Device instance.
+ * @param[out] serial_number Serial number of the device on success, and
+ * undefined on failure.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_NOT_AVAILABLE Serial number is not available.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_device_get_serial_number(
+ const struct jaylink_device *dev, uint32_t *serial_number)
+{
+ if (!dev || !serial_number)
+ return JAYLINK_ERR_ARG;
+
+ if (!dev->valid_serial_number)
+ return JAYLINK_ERR_NOT_AVAILABLE;
+
+ *serial_number = dev->serial_number;
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Get the USB address of a device.
+ *
+ * @note Identification of a device with the USB address is deprecated and the
+ * serial number should be used instead.
+ *
+ * @param[in] dev Device instance.
+ * @param[out] address USB address of the device on success, and undefined on
+ * failure.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_NOT_SUPPORTED Supported for devices with host interface
+ * #JAYLINK_HIF_USB only.
+ *
+ * @see jaylink_device_get_serial_number()
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_device_get_usb_address(
+ const struct jaylink_device *dev,
+ enum jaylink_usb_address *address)
+{
+ if (!dev || !address)
+ return JAYLINK_ERR_ARG;
+
+ if (dev->iface != JAYLINK_HIF_USB)
+ return JAYLINK_ERR_NOT_SUPPORTED;
+
+#ifdef HAVE_LIBUSB
+ *address = dev->usb_address;
+
+ return JAYLINK_OK;
+#else
+ return JAYLINK_ERR_NOT_SUPPORTED;
+#endif
+}
+
+/**
+ * Get the IPv4 address string of a device.
+ *
+ * @param[in] dev Device instance.
+ * @param[out] address IPv4 address string in quad-dotted decimal format of the
+ * device on success and undefined on failure.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_NOT_SUPPORTED Supported for devices with host interface
+ * #JAYLINK_HIF_TCP only.
+ *
+ * @since 0.2.0
+ */
+JAYLINK_API int jaylink_device_get_ipv4_address(
+ const struct jaylink_device *dev, char *address)
+{
+ if (!dev || !address)
+ return JAYLINK_ERR_ARG;
+
+ if (dev->iface != JAYLINK_HIF_TCP)
+ return JAYLINK_ERR_NOT_SUPPORTED;
+
+ memcpy(address, dev->ipv4_address, sizeof(dev->ipv4_address));
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Get the MAC address of a device.
+ *
+ * @param[in] dev Device instance.
+ * @param[out] address MAC address of the device on success and undefined on
+ * failure. The length of the MAC address is
+ * #JAYLINK_MAC_ADDRESS_LENGTH bytes.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_NOT_SUPPORTED Supported for devices with host interface
+ * #JAYLINK_HIF_TCP only.
+ * @retval JAYLINK_ERR_NOT_AVAILABLE MAC address is not available.
+ *
+ * @since 0.2.0
+ */
+JAYLINK_API int jaylink_device_get_mac_address(
+ const struct jaylink_device *dev, uint8_t *address)
+{
+ if (!dev || !address)
+ return JAYLINK_ERR_ARG;
+
+ if (dev->iface != JAYLINK_HIF_TCP)
+ return JAYLINK_ERR_NOT_SUPPORTED;
+
+ if (!dev->has_mac_address)
+ return JAYLINK_ERR_NOT_AVAILABLE;
+
+ memcpy(address, dev->mac_address, sizeof(dev->mac_address));
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Get the hardware version of a device.
+ *
+ * @note The hardware type can not be obtained by this function, use
+ * jaylink_get_hardware_version() instead.
+ *
+ * @param[in] dev Device instance.
+ * @param[out] version Hardware version of the device on success and undefined
+ * on failure.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_NOT_SUPPORTED Supported for devices with host interface
+ * #JAYLINK_HIF_TCP only.
+ * @retval JAYLINK_ERR_NOT_AVAILABLE Hardware version is not available.
+ *
+ * @since 0.2.0
+ */
+JAYLINK_API int jaylink_device_get_hardware_version(
+ const struct jaylink_device *dev,
+ struct jaylink_hardware_version *version)
+{
+ if (!dev || !version)
+ return JAYLINK_ERR_ARG;
+
+ if (dev->iface != JAYLINK_HIF_TCP)
+ return JAYLINK_ERR_NOT_SUPPORTED;
+
+ if (!dev->has_hw_version)
+ return JAYLINK_ERR_NOT_AVAILABLE;
+
+ *version = dev->hw_version;
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Get the product name of a device.
+ *
+ * @param[in] dev Device instance.
+ * @param[out] name Product name of the device on success and undefined on
+ * failure. The maximum length of the product name is
+ * #JAYLINK_PRODUCT_NAME_MAX_LENGTH bytes.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_NOT_SUPPORTED Supported for devices with host interface
+ * #JAYLINK_HIF_TCP only.
+ * @retval JAYLINK_ERR_NOT_AVAILABLE Product name is not available.
+ *
+ * @since 0.2.0
+ */
+JAYLINK_API int jaylink_device_get_product_name(
+ const struct jaylink_device *dev, char *name)
+{
+ if (!dev || !name)
+ return JAYLINK_ERR_ARG;
+
+ if (dev->iface != JAYLINK_HIF_TCP)
+ return JAYLINK_ERR_NOT_SUPPORTED;
+
+ if (!dev->has_product_name)
+ return JAYLINK_ERR_NOT_AVAILABLE;
+
+ memcpy(name, dev->product_name, sizeof(dev->product_name));
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Get the nickname of a device.
+ *
+ * @param[in] dev Device instance.
+ * @param[out] nickname Nickname of the device on success and undefined on
+ * failure. The maximum length of the nickname is
+ * #JAYLINK_NICKNAME_MAX_LENGTH bytes.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_NOT_SUPPORTED Supported for devices with host interface
+ * #JAYLINK_HIF_TCP only.
+ * @retval JAYLINK_ERR_NOT_AVAILABLE Nickname is not available.
+ *
+ * @since 0.2.0
+ */
+JAYLINK_API int jaylink_device_get_nickname(const struct jaylink_device *dev,
+ char *nickname)
+{
+ if (!dev || !nickname)
+ return JAYLINK_ERR_ARG;
+
+ if (dev->iface != JAYLINK_HIF_TCP)
+ return JAYLINK_ERR_NOT_SUPPORTED;
+
+ if (!dev->has_nickname)
+ return JAYLINK_ERR_NOT_AVAILABLE;
+
+ memcpy(nickname, dev->nickname, sizeof(dev->nickname));
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Increment the reference count of a device.
+ *
+ * @param[in,out] dev Device instance.
+ *
+ * @return The given device instance on success, or NULL on invalid argument.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API struct jaylink_device *jaylink_ref_device(
+ struct jaylink_device *dev)
+{
+ if (!dev)
+ return NULL;
+
+ dev->ref_count++;
+
+ return dev;
+}
+
+/**
+ * Decrement the reference count of a device.
+ *
+ * @param[in,out] dev Device instance.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API void jaylink_unref_device(struct jaylink_device *dev)
+{
+ struct jaylink_context *ctx;
+
+ if (!dev)
+ return;
+
+ dev->ref_count--;
+
+ if (!dev->ref_count) {
+ ctx = dev->ctx;
+ ctx->devs = list_remove(dev->ctx->devs, dev);
+
+ if (dev->iface == JAYLINK_HIF_USB) {
+#ifdef HAVE_LIBUSB
+ log_dbg(ctx, "Device destroyed (bus:address = "
+ "%03u:%03u).",
+ libusb_get_bus_number(dev->usb_dev),
+ libusb_get_device_address(dev->usb_dev));
+
+ libusb_unref_device(dev->usb_dev);
+#endif
+ } else if (dev->iface == JAYLINK_HIF_TCP) {
+ log_dbg(ctx, "Device destroyed (IPv4 address = %s).",
+ dev->ipv4_address);
+ } else {
+ log_err(ctx, "BUG: Invalid host interface: %u.",
+ dev->iface);
+ }
+
+ free(dev);
+ }
+}
+
+static struct jaylink_device_handle *allocate_device_handle(
+ struct jaylink_device *dev)
+{
+ struct jaylink_device_handle *devh;
+
+ devh = malloc(sizeof(struct jaylink_device_handle));
+
+ if (!devh)
+ return NULL;
+
+ devh->dev = jaylink_ref_device(dev);
+
+ return devh;
+}
+
+static void free_device_handle(struct jaylink_device_handle *devh)
+{
+ jaylink_unref_device(devh->dev);
+ free(devh);
+}
+
+/**
+ * Open a device.
+ *
+ * @param[in,out] dev Device instance.
+ * @param[out] devh Newly allocated handle for the opened device on success,
+ * and undefined on failure.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_MALLOC Memory allocation error.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_open(struct jaylink_device *dev,
+ struct jaylink_device_handle **devh)
+{
+ int ret;
+ struct jaylink_device_handle *handle;
+
+ if (!dev || !devh)
+ return JAYLINK_ERR_ARG;
+
+ handle = allocate_device_handle(dev);
+
+ if (!handle) {
+ log_err(dev->ctx, "Device handle malloc failed.");
+ return JAYLINK_ERR_MALLOC;
+ }
+
+ ret = transport_open(handle);
+
+ if (ret != JAYLINK_OK) {
+ free_device_handle(handle);
+ return ret;
+ }
+
+ *devh = handle;
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Close a device.
+ *
+ * @param[in,out] devh Device instance.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_close(struct jaylink_device_handle *devh)
+{
+ int ret;
+
+ if (!devh)
+ return JAYLINK_ERR_ARG;
+
+ ret = transport_close(devh);
+ free_device_handle(devh);
+
+ return ret;
+}
+
+/**
+ * Get the device instance from a device handle.
+ *
+ * @note The reference count of the device instance is not increased.
+ *
+ * @param[in] devh Device handle.
+ *
+ * @return The device instance on success, or NULL on invalid argument.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API struct jaylink_device *jaylink_get_device(
+ struct jaylink_device_handle *devh)
+{
+ if (!devh)
+ return NULL;
+
+ return devh->dev;
+}
+
+/**
+ * Retrieve the firmware version of a device.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[out] version Newly allocated string which contains the firmware
+ * version on success, and undefined if @p length is zero
+ * or on failure. The string is null-terminated and must be
+ * free'd by the caller.
+ * @param[out] length Length of the firmware version string including trailing
+ * null-terminator on success, and undefined on failure.
+ * Zero if no firmware version string is available.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_MALLOC Memory allocation error.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_get_firmware_version(
+ struct jaylink_device_handle *devh, char **version,
+ size_t *length)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[2];
+ uint16_t dummy;
+ char *tmp;
+
+ if (!devh || !version || !length)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write_read(devh, 1, 2, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_GET_VERSION;
+
+ ret = transport_write(devh, buf, 1);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, buf, 2);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ dummy = buffer_get_u16(buf, 0);
+ *length = dummy;
+
+ if (!dummy)
+ return JAYLINK_OK;
+
+ ret = transport_start_read(devh, dummy);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ tmp = malloc(dummy);
+
+ if (!tmp) {
+ log_err(ctx, "Firmware version string malloc failed.");
+ return JAYLINK_ERR_MALLOC;
+ }
+
+ ret = transport_read(devh, (uint8_t *)tmp, dummy);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ free(tmp);
+ return ret;
+ }
+
+ /* Last byte is reserved for null-terminator. */
+ tmp[dummy - 1] = 0;
+ *version = tmp;
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Retrieve the hardware information of a device.
+ *
+ * @note This function must only be used if the device has the
+ * #JAYLINK_DEV_CAP_GET_HW_INFO capability.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[in] mask A bit field where each set bit represents hardware
+ * information to request. See #jaylink_hardware_info for a
+ * description of the hardware information and their bit
+ * positions.
+ * @param[out] info Array to store the hardware information on success. Its
+ * content is undefined on failure. The array must be large
+ * enough to contain at least as many elements as bits set in
+ * @a mask.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_get_hardware_info(struct jaylink_device_handle *devh,
+ uint32_t mask, uint32_t *info)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[5];
+ unsigned int i;
+ unsigned int num;
+ unsigned int length;
+
+ if (!devh || !mask || !info)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ num = 0;
+
+ for (i = 0; i < 32; i++) {
+ if (mask & (1 << i))
+ num++;
+ }
+
+ length = num * sizeof(uint32_t);
+
+ ret = transport_start_write_read(devh, 5, length, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_GET_HW_INFO;
+ buffer_set_u32(buf, mask, 1);
+
+ ret = transport_write(devh, buf, 5);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, (uint8_t *)info, length);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ for (i = 0; i < num; i++)
+ info[i] = buffer_get_u32((uint8_t *)info,
+ i * sizeof(uint32_t));
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Retrieve the counter values of a device.
+ *
+ * @note This function must only be used if the device has the
+ * #JAYLINK_DEV_CAP_GET_COUNTERS capability.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[in] mask A bit field where each set bit represents a counter value to
+ * request. See #jaylink_counter for a description of the
+ * counters and their bit positions.
+ * @param[out] values Array to store the counter values on success. Its content
+ * is undefined on failure. The array must be large enough
+ * to contain at least as many elements as bits set in @p
+ * mask.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @since 0.2.0
+ */
+JAYLINK_API int jaylink_get_counters(struct jaylink_device_handle *devh,
+ uint32_t mask, uint32_t *values)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[5];
+ unsigned int i;
+ unsigned int num;
+ unsigned int length;
+
+ if (!devh || !mask || !values)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ num = 0;
+
+ for (i = 0; i < 32; i++) {
+ if (mask & (1 << i))
+ num++;
+ }
+
+ length = num * sizeof(uint32_t);
+ ret = transport_start_write_read(devh, 5, length, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_GET_COUNTERS;
+ buffer_set_u32(buf, mask, 1);
+
+ ret = transport_write(devh, buf, 5);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, (uint8_t *)values, length);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ for (i = 0; i < num; i++)
+ values[i] = buffer_get_u32((uint8_t *)values,
+ i * sizeof(uint32_t));
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Retrieve the hardware version of a device.
+ *
+ * @note This function must only be used if the device has the
+ * #JAYLINK_DEV_CAP_GET_HW_VERSION capability.
+ *
+ * @warning This function may return a value for @p version where
+ * #jaylink_hardware_version::type is not covered by
+ * #jaylink_hardware_type.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[out] version Hardware version on success, and undefined on failure.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_get_hardware_version(
+ struct jaylink_device_handle *devh,
+ struct jaylink_hardware_version *version)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[4];
+ uint32_t tmp;
+
+ if (!devh || !version)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write_read(devh, 1, 4, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_GET_HW_VERSION;
+
+ ret = transport_write(devh, buf, 1);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, buf, 4);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ tmp = buffer_get_u32(buf, 0);
+
+ version->type = (tmp / 1000000) % 100;
+ version->major = (tmp / 10000) % 100;
+ version->minor = (tmp / 100) % 100;
+ version->revision = tmp % 100;
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Retrieve the hardware status of a device.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[out] status Hardware status on success, and undefined on failure.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_get_hardware_status(struct jaylink_device_handle *devh,
+ struct jaylink_hardware_status *status)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[8];
+
+ if (!devh || !status)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write_read(devh, 1, 8, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_GET_HW_STATUS;
+
+ ret = transport_write(devh, buf, 1);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, buf, 8);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ status->target_voltage = buffer_get_u16(buf, 0);
+ status->tck = buf[2];
+ status->tdi = buf[3];
+ status->tdo = buf[4];
+ status->tms = buf[5];
+ status->tres = buf[6];
+ status->trst = buf[7];
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Retrieve the capabilities of a device.
+ *
+ * The capabilities are stored in a 32-bit bit array consisting of
+ * #JAYLINK_DEV_CAPS_SIZE bytes where each individual bit represents a
+ * capability. The first bit of this array is the least significant bit of the
+ * first byte and the following bits are sequentially numbered in order of
+ * increasing bit significance and byte index. A set bit indicates a supported
+ * capability. See #jaylink_device_capability for a description of the
+ * capabilities and their bit positions.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[out] caps Buffer to store capabilities on success. Its content is
+ * undefined on failure. The buffer must be large enough to
+ * contain at least #JAYLINK_DEV_CAPS_SIZE bytes.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @see jaylink_get_extended_caps()
+ * @see jaylink_has_cap()
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_get_caps(struct jaylink_device_handle *devh,
+ uint8_t *caps)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[1];
+
+ if (!devh || !caps)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write_read(devh, 1, JAYLINK_DEV_CAPS_SIZE, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_GET_CAPS;
+
+ ret = transport_write(devh, buf, 1);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, caps, JAYLINK_DEV_CAPS_SIZE);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Retrieve the extended capabilities of a device.
+ *
+ * The extended capabilities are stored in a 256-bit bit array consisting of
+ * #JAYLINK_DEV_EXT_CAPS_SIZE bytes. See jaylink_get_caps() for a further
+ * description of how the capabilities are represented in this bit array. For a
+ * description of the capabilities and their bit positions, see
+ * #jaylink_device_capability.
+ *
+ * @note This function must only be used if the device has the
+ * #JAYLINK_DEV_CAP_GET_EXT_CAPS capability.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[out] caps Buffer to store capabilities on success. Its content is
+ * undefined on failure. The buffer must be large enough to
+ * contain at least #JAYLINK_DEV_EXT_CAPS_SIZE bytes.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @see jaylink_get_caps()
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_get_extended_caps(struct jaylink_device_handle *devh,
+ uint8_t *caps)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[1];
+
+ if (!devh || !caps)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write_read(devh, 1, JAYLINK_DEV_EXT_CAPS_SIZE,
+ true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_GET_EXT_CAPS;
+
+ ret = transport_write(devh, buf, 1);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, caps, JAYLINK_DEV_EXT_CAPS_SIZE);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Retrieve the size of free memory of a device.
+ *
+ * @note This function must only be used if the device has the
+ * #JAYLINK_DEV_CAP_GET_FREE_MEMORY capability.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[out] size Size of free memory in bytes on success, and undefined on
+ * failure.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_get_free_memory(struct jaylink_device_handle *devh,
+ uint32_t *size)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[4];
+
+ if (!devh || !size)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write_read(devh, 1, 4, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_GET_FREE_MEMORY;
+
+ ret = transport_write(devh, buf, 1);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, buf, 4);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ *size = buffer_get_u32(buf, 0);
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Read the raw configuration data of a device.
+ *
+ * @note This function must only be used if the device has the
+ * #JAYLINK_DEV_CAP_READ_CONFIG capability.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[out] config Buffer to store configuration data on success. Its
+ * content is undefined on failure. The buffer must be large
+ * enough to contain at least
+ * #JAYLINK_DEV_CONFIG_SIZE bytes.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_read_raw_config(struct jaylink_device_handle *devh,
+ uint8_t *config)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[1];
+
+ if (!devh || !config)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write_read(devh, 1, JAYLINK_DEV_CONFIG_SIZE,
+ true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_READ_CONFIG;
+
+ ret = transport_write(devh, buf, 1);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, config, JAYLINK_DEV_CONFIG_SIZE);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Write the raw configuration data of a device.
+ *
+ * @note This function must only be used if the device has the
+ * #JAYLINK_DEV_CAP_WRITE_CONFIG capability.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[in] config Buffer to write configuration data from. The size of the
+ * configuration data is expected to be
+ * #JAYLINK_DEV_CONFIG_SIZE bytes.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_write_raw_config(struct jaylink_device_handle *devh,
+ const uint8_t *config)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[1];
+
+ if (!devh || !config)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write(devh, 1 + JAYLINK_DEV_CONFIG_SIZE, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_WRITE_CONFIG;
+
+ ret = transport_write(devh, buf, 1);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_write(devh, config, JAYLINK_DEV_CONFIG_SIZE);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ return JAYLINK_OK;
+}
+
+static void parse_conn_table(struct jaylink_connection *conns,
+ const uint8_t *buffer, uint16_t num, uint16_t entry_size)
+{
+ unsigned int i;
+ size_t offset;
+ struct in_addr in;
+
+ offset = 0;
+
+ for (i = 0; i < num; i++) {
+ conns[i].pid = buffer_get_u32(buffer, offset);
+
+ in.s_addr = buffer_get_u32(buffer, offset + 4);
+ /*
+ * Use inet_ntoa() instead of inet_ntop() because the latter
+ * requires at least Windows Vista.
+ */
+ strcpy(conns[i].hid, inet_ntoa(in));
+
+ conns[i].iid = buffer[offset + 8];
+ conns[i].cid = buffer[offset + 9];
+ conns[i].handle = buffer_get_u16(buffer, offset + 10);
+ conns[i].timestamp = buffer_get_u32(buffer, offset + 12);
+ offset = offset + entry_size;
+ }
+}
+
+static bool _inet_pton(const char *str, struct in_addr *in)
+{
+#ifdef _WIN32
+ int ret;
+ struct sockaddr_in sock_in;
+ int length;
+
+ length = sizeof(sock_in);
+
+ /*
+ * Use WSAStringToAddress() instead of inet_pton() because the latter
+ * requires at least Windows Vista.
+ */
+ ret = WSAStringToAddress((LPTSTR)str, AF_INET, NULL,
+ (LPSOCKADDR)&sock_in, &length);
+
+ if (ret != 0)
+ return false;
+
+ *in = sock_in.sin_addr;
+#else
+ if (inet_pton(AF_INET, str, in) != 1)
+ return false;
+#endif
+
+ return true;
+}
+
+/**
+ * Register a connection on a device.
+ *
+ * A connection can be registered by using 0 as handle. Additional information
+ * about the connection can be attached whereby the timestamp is a read-only
+ * value and therefore ignored for registration. On success, a new handle
+ * greater than 0 is obtained from the device.
+ *
+ * However, if an obtained handle does not appear in the list of device
+ * connections, the connection was not registered because the maximum number of
+ * connections on the device is reached.
+ *
+ * @note This function must only be used if the device has the
+ * #JAYLINK_DEV_CAP_REGISTER capability.
+ *
+ * Example code:
+ * @code{.c}
+ * static bool register_connection(struct jaylink_device_handle *devh,
+ * struct jaylink_connection *conn)
+ * {
+ * int ret;
+ * struct jaylink_connection conns[JAYLINK_MAX_CONNECTIONS];
+ * bool found_handle;
+ * size_t count;
+ * size_t i;
+ *
+ * conn->handle = 0;
+ * conn->pid = 0;
+ * strcpy(conn->hid, "0.0.0.0");
+ * conn->iid = 0;
+ * conn->cid = 0;
+ *
+ * ret = jaylink_register(devh, conn, conns, &count);
+ *
+ * if (ret != JAYLINK_OK) {
+ * printf("jaylink_register() failed: %s.\n",
+ * jaylink_strerror(ret));
+ * return false;
+ * }
+ *
+ * found_handle = false;
+ *
+ * for (i = 0; i < count; i++) {
+ * if (conns[i].handle == conn->handle) {
+ * found_handle = true;
+ * break;
+ * }
+ * }
+ *
+ * if (!found_handle) {
+ * printf("Maximum number of connections reached.\n");
+ * return false;
+ * }
+ *
+ * printf("Connection successfully registered.\n");
+ *
+ * return true;
+ * }
+ * @endcode
+ *
+ * @param[in,out] devh Device handle.
+ * @param[in,out] connection Connection to register on the device.
+ * @param[out] connections Array to store device connections on success.
+ * Its content is undefined on failure. The array must
+ * be large enough to contain at least
+ * #JAYLINK_MAX_CONNECTIONS elements.
+ * @param[out] count Number of device connections on success, and undefined on
+ * failure.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_PROTO Protocol violation.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @see jaylink_unregister()
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_register(struct jaylink_device_handle *devh,
+ struct jaylink_connection *connection,
+ struct jaylink_connection *connections, size_t *count)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[REG_MAX_SIZE];
+ uint16_t handle;
+ uint16_t num;
+ uint16_t entry_size;
+ uint32_t size;
+ uint32_t table_size;
+ uint16_t info_size;
+ struct in_addr in;
+
+ if (!devh || !connection || !connections || !count)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+
+ buf[0] = CMD_REGISTER;
+ buf[1] = REG_CMD_REGISTER;
+ buffer_set_u32(buf, connection->pid, 2);
+
+ if (!_inet_pton(connection->hid, &in))
+ return JAYLINK_ERR_ARG;
+
+ buffer_set_u32(buf, in.s_addr, 6);
+
+ buf[10] = connection->iid;
+ buf[11] = connection->cid;
+ buffer_set_u16(buf, connection->handle, 12);
+
+ ret = transport_start_write_read(devh, 14, REG_MIN_SIZE, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_write(devh, buf, 14);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, buf, REG_MIN_SIZE);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ handle = buffer_get_u16(buf, 0);
+ num = buffer_get_u16(buf, 2);
+ entry_size = buffer_get_u16(buf, 4);
+ info_size = buffer_get_u16(buf, 6);
+
+ if (num > JAYLINK_MAX_CONNECTIONS) {
+ log_err(ctx, "Maximum number of device connections exceeded: "
+ "%u.", num);
+ return JAYLINK_ERR_PROTO;
+ }
+
+ if (entry_size != REG_CONN_INFO_SIZE) {
+ log_err(ctx, "Invalid connection entry size: %u bytes.",
+ entry_size);
+ return JAYLINK_ERR_PROTO;
+ }
+
+ table_size = num * entry_size;
+ size = REG_HEADER_SIZE + table_size + info_size;
+
+ if (size > REG_MAX_SIZE) {
+ log_err(ctx, "Maximum registration information size exceeded: "
+ "%u bytes.", size);
+ return JAYLINK_ERR_PROTO;
+ }
+
+ if (size > REG_MIN_SIZE) {
+ ret = transport_start_read(devh, size - REG_MIN_SIZE);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_read() failed: %s.",
+ jaylink_strerror(ret));
+ return JAYLINK_ERR;
+ }
+
+ ret = transport_read(devh, buf + REG_MIN_SIZE,
+ size - REG_MIN_SIZE);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return JAYLINK_ERR;
+ }
+ }
+
+ if (!handle) {
+ log_err(ctx, "Obtained invalid connection handle.");
+ return JAYLINK_ERR_PROTO;
+ }
+
+ connection->handle = handle;
+ parse_conn_table(connections, buf + REG_HEADER_SIZE, num, entry_size);
+
+ *count = num;
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Unregister a connection from a device.
+ *
+ * @note This function must only be used if the device has the
+ * #JAYLINK_DEV_CAP_REGISTER capability.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[in,out] connection Connection to unregister from the device.
+ * @param[out] connections Array to store device connections on success.
+ * Its content is undefined on failure. The array must
+ * be large enough to contain at least
+ * #JAYLINK_MAX_CONNECTIONS elements.
+ * @param[out] count Number of device connections on success, and undefined on
+ * failure.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_PROTO Protocol violation.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @see jaylink_register()
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_unregister(struct jaylink_device_handle *devh,
+ const struct jaylink_connection *connection,
+ struct jaylink_connection *connections, size_t *count)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[REG_MAX_SIZE];
+ uint16_t num;
+ uint16_t entry_size;
+ uint32_t size;
+ uint32_t table_size;
+ uint16_t info_size;
+ struct in_addr in;
+
+ if (!devh || !connection || !connections || !count)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+
+ buf[0] = CMD_REGISTER;
+ buf[1] = REG_CMD_UNREGISTER;
+ buffer_set_u32(buf, connection->pid, 2);
+
+ if (!_inet_pton(connection->hid, &in))
+ return JAYLINK_ERR_ARG;
+
+ buffer_set_u32(buf, in.s_addr, 6);
+
+ buf[10] = connection->iid;
+ buf[11] = connection->cid;
+ buffer_set_u16(buf, connection->handle, 12);
+
+ ret = transport_start_write_read(devh, 14, REG_MIN_SIZE, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_write(devh, buf, 14);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, buf, REG_MIN_SIZE);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ num = buffer_get_u16(buf, 2);
+ entry_size = buffer_get_u16(buf, 4);
+ info_size = buffer_get_u16(buf, 6);
+
+ if (num > JAYLINK_MAX_CONNECTIONS) {
+ log_err(ctx, "Maximum number of device connections exceeded: "
+ "%u.", num);
+ return JAYLINK_ERR_PROTO;
+ }
+
+ if (entry_size != REG_CONN_INFO_SIZE) {
+ log_err(ctx, "Invalid connection entry size: %u bytes.",
+ entry_size);
+ return JAYLINK_ERR_PROTO;
+ }
+
+ table_size = num * entry_size;
+ size = REG_HEADER_SIZE + table_size + info_size;
+
+ if (size > REG_MAX_SIZE) {
+ log_err(ctx, "Maximum registration information size exceeded: "
+ "%u bytes.", size);
+ return JAYLINK_ERR_PROTO;
+ }
+
+ if (size > REG_MIN_SIZE) {
+ ret = transport_start_read(devh, size - REG_MIN_SIZE);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_read() failed: %s.",
+ jaylink_strerror(ret));
+ return JAYLINK_ERR;
+ }
+
+ ret = transport_read(devh, buf + REG_MIN_SIZE,
+ size - REG_MIN_SIZE);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return JAYLINK_ERR;
+ }
+ }
+
+ parse_conn_table(connections, buf + REG_HEADER_SIZE, num, entry_size);
+
+ *count = num;
+
+ return JAYLINK_OK;
+}
diff --git a/src/jtag/drivers/libjaylink/libjaylink/discovery.c b/src/jtag/drivers/libjaylink/libjaylink/discovery.c
new file mode 100644
index 0000000..1ac96e7
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/libjaylink/discovery.c
@@ -0,0 +1,106 @@
+/*
+ * This file is part of the libjaylink project.
+ *
+ * Copyright (C) 2014-2016 Marc Schink <jaylink-dev@marcschink.de>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "libjaylink.h"
+#include "libjaylink-internal.h"
+
+/**
+ * @file
+ *
+ * Device discovery.
+ */
+
+static void clear_discovery_list(struct jaylink_context *ctx)
+{
+ struct list *item;
+ struct list *tmp;
+ struct jaylink_device *dev;
+
+ item = ctx->discovered_devs;
+
+ while (item) {
+ dev = (struct jaylink_device *)item->data;
+ jaylink_unref_device(dev);
+
+ tmp = item;
+ item = item->next;
+ free(tmp);
+ }
+
+ ctx->discovered_devs = NULL;
+}
+
+/**
+ * Scan for devices.
+ *
+ * @param[in,out] ctx libjaylink context.
+ * @param[in] ifaces Host interfaces to scan for devices. Use bitwise OR to
+ * specify multiple interfaces, or 0 to use all available
+ * interfaces. See #jaylink_host_interface for a description
+ * of the interfaces.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @see jaylink_get_devices()
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_discovery_scan(struct jaylink_context *ctx,
+ uint32_t ifaces)
+{
+ int ret;
+
+ if (!ctx)
+ return JAYLINK_ERR_ARG;
+
+ if (!ifaces)
+ ifaces = JAYLINK_HIF_USB | JAYLINK_HIF_TCP;
+
+ clear_discovery_list(ctx);
+
+#ifdef HAVE_LIBUSB
+ if (ifaces & JAYLINK_HIF_USB) {
+ ret = discovery_usb_scan(ctx);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "USB device discovery failed.");
+ return ret;
+ }
+ }
+#endif
+
+ if (ifaces & JAYLINK_HIF_TCP) {
+ ret = discovery_tcp_scan(ctx);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "TCP/IP device discovery failed.");
+ return ret;
+ }
+ }
+
+ return JAYLINK_OK;
+}
diff --git a/src/jtag/drivers/libjaylink/libjaylink/discovery_tcp.c b/src/jtag/drivers/libjaylink/libjaylink/discovery_tcp.c
new file mode 100644
index 0000000..002fa67
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/libjaylink/discovery_tcp.c
@@ -0,0 +1,349 @@
+/*
+ * This file is part of the libjaylink project.
+ *
+ * Copyright (C) 2015-2017 Marc Schink <jaylink-dev@marcschink.de>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <ctype.h>
+#ifdef _WIN32
+#include <winsock2.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+
+#include "libjaylink.h"
+#include "libjaylink-internal.h"
+
+/**
+ * @file
+ *
+ * Device discovery (TCP/IP).
+ */
+
+/** @cond PRIVATE */
+/** Size of the advertisement message in bytes. */
+#define ADV_MESSAGE_SIZE 128
+
+/** Device discovery port number. */
+#define DISC_PORT 19020
+
+/** Size of the discovery message in bytes. */
+#define DISC_MESSAGE_SIZE 64
+
+/** Discovery timeout in milliseconds. */
+#define DISC_TIMEOUT 20
+/** @endcond */
+
+static bool compare_devices(const void *a, const void *b)
+{
+ const struct jaylink_device *dev;
+ const struct jaylink_device *new_dev;
+
+ dev = a;
+ new_dev = b;
+
+ if (dev->iface != JAYLINK_HIF_TCP)
+ return false;
+
+ if (memcmp(dev->ipv4_address, new_dev->ipv4_address,
+ sizeof(dev->ipv4_address)) != 0)
+ return false;
+
+ if (dev->serial_number != new_dev->serial_number)
+ return false;
+
+ if (memcmp(dev->mac_address, new_dev->mac_address,
+ sizeof(dev->mac_address)) != 0)
+ return false;
+
+ if (strcmp(dev->product_name, new_dev->product_name) != 0)
+ return false;
+
+ if (strcmp(dev->nickname, new_dev->nickname) != 0)
+ return false;
+
+ if (dev->hw_version.type != new_dev->hw_version.type)
+ return false;
+
+ if (dev->hw_version.major != new_dev->hw_version.major)
+ return false;
+
+ if (dev->hw_version.minor != new_dev->hw_version.minor)
+ return false;
+
+ if (dev->hw_version.revision != new_dev->hw_version.revision)
+ return false;
+
+ return true;
+}
+
+static struct jaylink_device *find_device(struct list *list,
+ const struct jaylink_device *dev)
+{
+ struct list *item;
+
+ item = list_find_custom(list, &compare_devices, dev);
+
+ if (item)
+ return item->data;
+
+ return NULL;
+}
+
+static bool parse_adv_message(struct jaylink_device *dev,
+ const uint8_t *buffer)
+{
+ struct in_addr in;
+ uint32_t tmp;
+
+ if (memcmp(buffer, "Found", 5) != 0)
+ return false;
+
+ /*
+ * Use inet_ntoa() instead of inet_ntop() because the latter requires
+ * at least Windows Vista.
+ */
+ memcpy(&in, buffer + 16, 4);
+ memcpy(dev->ipv4_address, inet_ntoa(in), sizeof(dev->ipv4_address));
+
+ memcpy(dev->mac_address, buffer + 32, sizeof(dev->mac_address));
+ dev->has_mac_address = true;
+
+ dev->serial_number = buffer_get_u32(buffer, 48);
+ dev->valid_serial_number = true;
+
+ tmp = buffer_get_u32(buffer, 52);
+ dev->hw_version.type = (tmp / 1000000) % 100;
+ dev->hw_version.major = (tmp / 10000) % 100;
+ dev->hw_version.minor = (tmp / 100) % 100;
+ dev->hw_version.revision = tmp % 100;
+ dev->has_hw_version = true;
+
+ memcpy(dev->product_name, buffer + 64, sizeof(dev->product_name));
+ dev->product_name[JAYLINK_PRODUCT_NAME_MAX_LENGTH - 1] = '\0';
+ dev->has_product_name = isprint((unsigned char)dev->product_name[0]);
+
+ memcpy(dev->nickname, buffer + 96, sizeof(dev->nickname));
+ dev->nickname[JAYLINK_NICKNAME_MAX_LENGTH - 1] = '\0';
+ dev->has_nickname = isprint((unsigned char)dev->nickname[0]);
+
+ return true;
+}
+
+static struct jaylink_device *probe_device(struct jaylink_context *ctx,
+ struct sockaddr_in *addr, const uint8_t *buffer)
+{
+ struct jaylink_device tmp;
+ struct jaylink_device *dev;
+
+ /*
+ * Use inet_ntoa() instead of inet_ntop() because the latter requires
+ * at least Windows Vista.
+ */
+ log_dbg(ctx, "Received advertisement message (IPv4 address = %s).",
+ inet_ntoa(addr->sin_addr));
+
+ if (!parse_adv_message(&tmp, buffer)) {
+ log_dbg(ctx, "Received invalid advertisement message.");
+ return NULL;
+ }
+
+ log_dbg(ctx, "Found device (IPv4 address = %s).", tmp.ipv4_address);
+ log_dbg(ctx, "Device: MAC address = %02x:%02x:%02x:%02x:%02x:%02x.",
+ tmp.mac_address[0], tmp.mac_address[1], tmp.mac_address[2],
+ tmp.mac_address[3], tmp.mac_address[4], tmp.mac_address[5]);
+ log_dbg(ctx, "Device: Serial number = %u.", tmp.serial_number);
+
+ if (tmp.has_product_name)
+ log_dbg(ctx, "Device: Product = %s.", tmp.product_name);
+
+ if (tmp.has_nickname)
+ log_dbg(ctx, "Device: Nickname = %s.", tmp.nickname);
+
+ dev = find_device(ctx->discovered_devs, &tmp);
+
+ if (dev) {
+ log_dbg(ctx, "Ignoring already discovered device.");
+ return NULL;
+ }
+
+ dev = find_device(ctx->devs, &tmp);
+
+ if (dev) {
+ log_dbg(ctx, "Using existing device instance.");
+ return jaylink_ref_device(dev);
+ }
+
+ log_dbg(ctx, "Allocating new device instance.");
+
+ dev = device_allocate(ctx);
+
+ if (!dev) {
+ log_warn(ctx, "Device instance malloc failed.");
+ return NULL;
+ }
+
+ dev->iface = JAYLINK_HIF_TCP;
+
+ dev->serial_number = tmp.serial_number;
+ dev->valid_serial_number = tmp.valid_serial_number;
+
+ memcpy(dev->ipv4_address, tmp.ipv4_address, sizeof(dev->ipv4_address));
+
+ memcpy(dev->mac_address, tmp.mac_address, sizeof(dev->mac_address));
+ dev->has_mac_address = tmp.has_mac_address;
+
+ memcpy(dev->product_name, tmp.product_name, sizeof(dev->product_name));
+ dev->has_product_name = tmp.has_product_name;
+
+ memcpy(dev->nickname, tmp.nickname, sizeof(dev->nickname));
+ dev->has_nickname = tmp.has_nickname;
+
+ dev->hw_version = tmp.hw_version;
+ dev->has_hw_version = tmp.has_hw_version;
+
+ return dev;
+}
+
+/** @private */
+JAYLINK_PRIV int discovery_tcp_scan(struct jaylink_context *ctx)
+{
+ int ret;
+ int sock;
+ int opt_value;
+ fd_set rfds;
+ struct sockaddr_in addr;
+ size_t addr_length;
+ struct timeval timeout;
+ uint8_t buf[ADV_MESSAGE_SIZE];
+ struct jaylink_device *dev;
+ size_t length;
+ size_t num_devs;
+
+ sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+
+ if (sock < 0) {
+ log_err(ctx, "Failed to create discovery socket.");
+ return JAYLINK_ERR;
+ }
+
+ opt_value = true;
+
+ if (!socket_set_option(sock, SOL_SOCKET, SO_BROADCAST, &opt_value,
+ sizeof(opt_value))) {
+ log_err(ctx, "Failed to enable broadcast option for discovery "
+ "socket.");
+ socket_close(sock);
+ return JAYLINK_ERR;
+ }
+
+ memset(&addr, 0, sizeof(struct sockaddr_in));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(DISC_PORT);
+ addr.sin_addr.s_addr = INADDR_ANY;
+
+ if (!socket_bind(sock, (struct sockaddr *)&addr,
+ sizeof(struct sockaddr_in))) {
+ log_err(ctx, "Failed to bind discovery socket.");
+ socket_close(sock);
+ return JAYLINK_ERR;
+ }
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(DISC_PORT);
+ addr.sin_addr.s_addr = INADDR_BROADCAST;
+
+ memset(buf, 0, DISC_MESSAGE_SIZE);
+ memcpy(buf, "Discover", 8);
+
+ log_dbg(ctx, "Sending discovery message.");
+
+ length = DISC_MESSAGE_SIZE;
+
+ if (!socket_sendto(sock, (char *)buf, &length, 0,
+ (const struct sockaddr *)&addr, sizeof(addr))) {
+ log_err(ctx, "Failed to send discovery message.");
+ socket_close(sock);
+ return JAYLINK_ERR_IO;
+ }
+
+ if (length < DISC_MESSAGE_SIZE) {
+ log_err(ctx, "Only sent %zu bytes of discovery message.",
+ length);
+ socket_close(sock);
+ return JAYLINK_ERR_IO;
+ }
+
+ timeout.tv_sec = DISC_TIMEOUT / 1000;
+ timeout.tv_usec = (DISC_TIMEOUT % 1000) * 1000;
+
+ num_devs = 0;
+
+ while (true) {
+ FD_ZERO(&rfds);
+ FD_SET(sock, &rfds);
+
+ ret = select(sock + 1, &rfds, NULL, NULL, &timeout);
+
+ if (ret <= 0)
+ break;
+
+ if (!FD_ISSET(sock, &rfds))
+ continue;
+
+ length = ADV_MESSAGE_SIZE;
+ addr_length = sizeof(struct sockaddr_in);
+
+ if (!socket_recvfrom(sock, buf, &length, 0,
+ (struct sockaddr *)&addr, &addr_length)) {
+ log_warn(ctx, "Failed to receive advertisement "
+ "message.");
+ continue;
+ }
+
+ /*
+ * Filter out messages with an invalid size. This includes the
+ * broadcast message we sent before.
+ */
+ if (length != ADV_MESSAGE_SIZE)
+ continue;
+
+ dev = probe_device(ctx, &addr, buf);
+
+ if (dev) {
+ ctx->discovered_devs = list_prepend(
+ ctx->discovered_devs, dev);
+ num_devs++;
+ }
+ }
+
+ socket_close(sock);
+
+ if (ret < 0) {
+ log_err(ctx, "select() failed.");
+ return JAYLINK_ERR;
+ }
+
+ log_dbg(ctx, "Found %zu TCP/IP device(s).", num_devs);
+
+ return JAYLINK_OK;
+}
diff --git a/src/jtag/drivers/libjaylink/libjaylink/discovery_usb.c b/src/jtag/drivers/libjaylink/libjaylink/discovery_usb.c
new file mode 100644
index 0000000..48d5322
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/libjaylink/discovery_usb.c
@@ -0,0 +1,280 @@
+/*
+ * This file is part of the libjaylink project.
+ *
+ * Copyright (C) 2014-2016 Marc Schink <jaylink-dev@marcschink.de>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "libjaylink.h"
+#include "libjaylink-internal.h"
+
+/*
+ * libusb.h includes windows.h and therefore must be included after anything
+ * that includes winsock2.h.
+ */
+#include <libusb.h>
+
+/**
+ * @file
+ *
+ * Device discovery (USB).
+ */
+
+/** @cond PRIVATE */
+/** USB Vendor ID (VID) of SEGGER products. */
+#define USB_VENDOR_ID 0x1366
+
+/* USB Product IDs (PID) and their corresponding USB addresses. */
+static const uint16_t pids[][2] = {
+ {0x0101, 0},
+ {0x0102, 1},
+ {0x0103, 2},
+ {0x0104, 3},
+ {0x0105, 0},
+ {0x0107, 0},
+ {0x0108, 0},
+ {0x1010, 0},
+ {0x1011, 0},
+ {0x1012, 0},
+ {0x1013, 0},
+ {0x1014, 0},
+ {0x1015, 0},
+ {0x1016, 0},
+ {0x1017, 0},
+ {0x1018, 0}
+};
+
+/** Maximum length of the USB string descriptor for the serial number. */
+#define USB_SERIAL_NUMBER_LENGTH 12
+
+/**
+ * Maximum number of digits in a serial number
+ *
+ * The serial number of a device consists of at most 9 digits but user defined
+ * serial numbers are allowed with up to 10 digits.
+ */
+#define MAX_SERIAL_NUMBER_DIGITS 10
+/** @endcond */
+
+static bool parse_serial_number(const char *str, uint32_t *serial_number)
+{
+ size_t length;
+
+ length = strlen(str);
+
+ /*
+ * Skip the first digits which are not part of a valid serial number.
+ * This is necessary because some devices erroneously use random digits
+ * instead of zeros for padding.
+ */
+ if (length > MAX_SERIAL_NUMBER_DIGITS)
+ str = str + (length - MAX_SERIAL_NUMBER_DIGITS);
+
+ if (jaylink_parse_serial_number(str, serial_number) != JAYLINK_OK)
+ return false;
+
+ return true;
+}
+
+static bool compare_devices(const void *a, const void *b)
+{
+ const struct jaylink_device *dev;
+ const struct libusb_device *usb_dev;
+
+ dev = a;
+ usb_dev = b;
+
+ if (dev->iface != JAYLINK_HIF_USB)
+ return false;
+
+ if (dev->usb_dev == usb_dev)
+ return true;
+
+ return false;
+}
+
+static struct jaylink_device *find_device(const struct jaylink_context *ctx,
+ const struct libusb_device *usb_dev)
+{
+ struct list *item;
+
+ item = list_find_custom(ctx->devs, &compare_devices, usb_dev);
+
+ if (item)
+ return item->data;
+
+ return NULL;
+}
+
+static struct jaylink_device *probe_device(struct jaylink_context *ctx,
+ struct libusb_device *usb_dev)
+{
+ int ret;
+ struct libusb_device_descriptor desc;
+ struct libusb_device_handle *usb_devh;
+ struct jaylink_device *dev;
+ char buf[USB_SERIAL_NUMBER_LENGTH + 1];
+ uint8_t usb_address;
+ uint32_t serial_number;
+ bool valid_serial_number;
+ bool found_device;
+ size_t i;
+
+ ret = libusb_get_device_descriptor(usb_dev, &desc);
+
+ if (ret != LIBUSB_SUCCESS) {
+ log_warn(ctx, "Failed to get device descriptor: %s.",
+ libusb_error_name(ret));
+ return NULL;
+ }
+
+ if (desc.idVendor != USB_VENDOR_ID)
+ return NULL;
+
+ found_device = false;
+
+ for (i = 0; i < sizeof(pids) / sizeof(pids[0]); i++) {
+ if (pids[i][0] == desc.idProduct) {
+ found_device = true;
+ usb_address = pids[i][1];
+ break;
+ }
+ }
+
+ if (!found_device)
+ return NULL;
+
+ log_dbg(ctx, "Found device (VID:PID = %04x:%04x, bus:address = "
+ "%03u:%03u).", desc.idVendor, desc.idProduct,
+ libusb_get_bus_number(usb_dev),
+ libusb_get_device_address(usb_dev));
+
+ /*
+ * Search for an already allocated device instance for this device and
+ * if found return a reference to it.
+ */
+ dev = find_device(ctx, usb_dev);
+
+ if (dev) {
+ log_dbg(ctx, "Device: USB address = %u.", dev->usb_address);
+
+ if (dev->valid_serial_number)
+ log_dbg(ctx, "Device: Serial number = %u.",
+ dev->serial_number);
+ else
+ log_dbg(ctx, "Device: Serial number = N/A.");
+
+ log_dbg(ctx, "Using existing device instance.");
+ return jaylink_ref_device(dev);
+ }
+
+ /* Open the device to be able to retrieve its serial number. */
+ ret = libusb_open(usb_dev, &usb_devh);
+
+ if (ret != LIBUSB_SUCCESS) {
+ log_warn(ctx, "Failed to open device: %s.",
+ libusb_error_name(ret));
+ return NULL;
+ }
+
+ serial_number = 0;
+ valid_serial_number = true;
+
+ ret = libusb_get_string_descriptor_ascii(usb_devh, desc.iSerialNumber,
+ (unsigned char *)buf, USB_SERIAL_NUMBER_LENGTH + 1);
+
+ libusb_close(usb_devh);
+
+ if (ret < 0) {
+ log_warn(ctx, "Failed to retrieve serial number: %s.",
+ libusb_error_name(ret));
+ valid_serial_number = false;
+ }
+
+ if (valid_serial_number) {
+ if (!parse_serial_number(buf, &serial_number)) {
+ log_warn(ctx, "Failed to parse serial number.");
+ return NULL;
+ }
+ }
+
+ log_dbg(ctx, "Device: USB address = %u.", usb_address);
+
+ if (valid_serial_number)
+ log_dbg(ctx, "Device: Serial number = %u.", serial_number);
+ else
+ log_dbg(ctx, "Device: Serial number = N/A.");
+
+ log_dbg(ctx, "Allocating new device instance.");
+
+ dev = device_allocate(ctx);
+
+ if (!dev) {
+ log_warn(ctx, "Device instance malloc failed.");
+ return NULL;
+ }
+
+ dev->iface = JAYLINK_HIF_USB;
+ dev->usb_dev = libusb_ref_device(usb_dev);
+ dev->usb_address = usb_address;
+ dev->serial_number = serial_number;
+ dev->valid_serial_number = valid_serial_number;
+
+ return dev;
+}
+
+JAYLINK_PRIV int discovery_usb_scan(struct jaylink_context *ctx)
+{
+ ssize_t ret;
+ struct libusb_device **devs;
+ struct jaylink_device *dev;
+ size_t num;
+ size_t i;
+
+ ret = libusb_get_device_list(ctx->usb_ctx, &devs);
+
+ if (ret == LIBUSB_ERROR_IO) {
+ log_err(ctx, "Failed to retrieve device list: input/output "
+ "error.");
+ return JAYLINK_ERR_IO;
+ } else if (ret < 0) {
+ log_err(ctx, "Failed to retrieve device list: %s.",
+ libusb_error_name(ret));
+ return JAYLINK_ERR;
+ }
+
+ num = 0;
+
+ for (i = 0; devs[i]; i++) {
+ dev = probe_device(ctx, devs[i]);
+
+ if (!dev)
+ continue;
+
+ ctx->discovered_devs = list_prepend(ctx->discovered_devs, dev);
+ num++;
+ }
+
+ libusb_free_device_list(devs, true);
+ log_dbg(ctx, "Found %zu USB device(s).", num);
+
+ return JAYLINK_OK;
+}
diff --git a/src/jtag/drivers/libjaylink/libjaylink/emucom.c b/src/jtag/drivers/libjaylink/libjaylink/emucom.c
new file mode 100644
index 0000000..035cb99
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/libjaylink/emucom.c
@@ -0,0 +1,287 @@
+/*
+ * This file is part of the libjaylink project.
+ *
+ * Copyright (C) 2015-2016 Marc Schink <jaylink-dev@marcschink.de>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "libjaylink.h"
+#include "libjaylink-internal.h"
+
+/**
+ * @file
+ *
+ * Emulator communication (EMUCOM).
+ */
+
+/** @cond PRIVATE */
+#define CMD_EMUCOM 0xee
+
+#define EMUCOM_CMD_READ 0x00
+#define EMUCOM_CMD_WRITE 0x01
+
+/** Bitmask for the error indication bit of an EMUCOM status code. */
+#define EMUCOM_ERR 0x80000000
+
+/** Error code indicating that the channel is not supported by the device. */
+#define EMUCOM_ERR_NOT_SUPPORTED 0x80000001
+
+/**
+ * Error code indicating that the channel is not available for the requested
+ * number of bytes to be read.
+ *
+ * The number of bytes available on this channel is encoded in the lower
+ * 24 bits of the EMUCOM status code.
+ *
+ * @see EMUCOM_AVAILABLE_BYTES_MASK
+ */
+#define EMUCOM_ERR_NOT_AVAILABLE 0x81000000
+
+/**
+ * Bitmask to extract the number of available bytes on a channel from an EMUCOM
+ * status code.
+ */
+#define EMUCOM_AVAILABLE_BYTES_MASK 0x00ffffff
+/** @endcond */
+
+/**
+ * Read from an EMUCOM channel.
+ *
+ * @note This function must only be used if the device has the
+ * #JAYLINK_DEV_CAP_EMUCOM capability.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[in] channel Channel to read data from.
+ * @param[out] buffer Buffer to store read data on success. Its content is
+ * undefined on failure.
+ * @param[in,out] length Number of bytes to read. On success, the value gets
+ * updated with the actual number of bytes read. Unless
+ * otherwise specified, the value is undefined on
+ * failure.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_PROTO Protocol violation.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR_DEV_NOT_SUPPORTED Channel is not supported by the
+ * device.
+ * @retval JAYLINK_ERR_DEV_NOT_AVAILABLE Channel is not available for the
+ * requested amount of data. @p length is
+ * updated with the number of bytes
+ * available on this channel.
+ * @retval JAYLINK_ERR_DEV Unspecified device error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_emucom_read(struct jaylink_device_handle *devh,
+ uint32_t channel, uint8_t *buffer, uint32_t *length)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[10];
+ uint32_t tmp;
+
+ if (!devh || !buffer || !length)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write_read(devh, 10, 4, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_EMUCOM;
+ buf[1] = EMUCOM_CMD_READ;
+
+ buffer_set_u32(buf, channel, 2);
+ buffer_set_u32(buf, *length, 6);
+
+ ret = transport_write(devh, buf, 10);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, buf, 4);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ tmp = buffer_get_u32(buf, 0);
+
+ if (tmp == EMUCOM_ERR_NOT_SUPPORTED)
+ return JAYLINK_ERR_DEV_NOT_SUPPORTED;
+
+ if ((tmp & ~EMUCOM_AVAILABLE_BYTES_MASK) == EMUCOM_ERR_NOT_AVAILABLE) {
+ *length = tmp & EMUCOM_AVAILABLE_BYTES_MASK;
+ return JAYLINK_ERR_DEV_NOT_AVAILABLE;
+ }
+
+ if (tmp & EMUCOM_ERR) {
+ log_err(ctx, "Failed to read from channel 0x%x: 0x%x.",
+ channel, tmp);
+ return JAYLINK_ERR_DEV;
+ }
+
+ if (tmp > *length) {
+ log_err(ctx, "Requested at most %u bytes but device "
+ "returned %u bytes.", *length, tmp);
+ return JAYLINK_ERR_PROTO;
+ }
+
+ *length = tmp;
+
+ if (!tmp)
+ return JAYLINK_OK;
+
+ ret = transport_start_read(devh, tmp);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, buffer, tmp);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Write to an EMUCOM channel.
+ *
+ * @note This function must only be used if the device has the
+ * #JAYLINK_DEV_CAP_EMUCOM capability.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[in] channel Channel to write data to.
+ * @param[in] buffer Buffer to write data from.
+ * @param[in,out] length Number of bytes to write. On success, the value gets
+ * updated with the actual number of bytes written. The
+ * value is undefined on failure.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_PROTO Protocol violation.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR_DEV_NOT_SUPPORTED Channel is not supported by the
+ * device.
+ * @retval JAYLINK_ERR_DEV Unspecified device error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_emucom_write(struct jaylink_device_handle *devh,
+ uint32_t channel, const uint8_t *buffer, uint32_t *length)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[10];
+ uint32_t tmp;
+
+ if (!devh || !buffer || !length)
+ return JAYLINK_ERR_ARG;
+
+ if (!*length)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write(devh, 10, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_EMUCOM;
+ buf[1] = EMUCOM_CMD_WRITE;
+
+ buffer_set_u32(buf, channel, 2);
+ buffer_set_u32(buf, *length, 6);
+
+ ret = transport_write(devh, buf, 10);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_start_write_read(devh, *length, 4, false);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_write(devh, buffer, *length);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, buf, 4);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ tmp = buffer_get_u32(buf, 0);
+
+ if (tmp == EMUCOM_ERR_NOT_SUPPORTED)
+ return JAYLINK_ERR_DEV_NOT_SUPPORTED;
+
+ if (tmp & EMUCOM_ERR) {
+ log_err(ctx, "Failed to write to channel 0x%x: 0x%x.",
+ channel, tmp);
+ return JAYLINK_ERR_DEV;
+ }
+
+ if (tmp > *length) {
+ log_err(ctx, "Only %u bytes were supposed to be written, but "
+ "the device reported %u written bytes.", *length, tmp);
+ return JAYLINK_ERR_PROTO;
+ }
+
+ *length = tmp;
+
+ return JAYLINK_OK;
+}
diff --git a/src/jtag/drivers/libjaylink/libjaylink/error.c b/src/jtag/drivers/libjaylink/libjaylink/error.c
new file mode 100644
index 0000000..2c696fc
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/libjaylink/error.c
@@ -0,0 +1,118 @@
+/*
+ * This file is part of the libjaylink project.
+ *
+ * Copyright (C) 2014-2015 Marc Schink <jaylink-dev@marcschink.de>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "libjaylink.h"
+
+/**
+ * @file
+ *
+ * Error handling.
+ */
+
+/**
+ * Return a human-readable description of a libjaylink error code.
+ *
+ * @param[in] error_code A libjaylink error code. See #jaylink_error for valid
+ * values.
+ *
+ * @return A string which describes the given error code, or the string
+ * <i>unknown error</i> if the error code is not known. The string is
+ * null-terminated and must not be free'd by the caller.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API const char *jaylink_strerror(int error_code)
+{
+ switch (error_code) {
+ case JAYLINK_OK:
+ return "no error";
+ case JAYLINK_ERR:
+ return "unspecified error";
+ case JAYLINK_ERR_ARG:
+ return "invalid argument";
+ case JAYLINK_ERR_MALLOC:
+ return "memory allocation error";
+ case JAYLINK_ERR_TIMEOUT:
+ return "timeout occurred";
+ case JAYLINK_ERR_PROTO:
+ return "protocol violation";
+ case JAYLINK_ERR_NOT_AVAILABLE:
+ return "entity not available";
+ case JAYLINK_ERR_NOT_SUPPORTED:
+ return "operation not supported";
+ case JAYLINK_ERR_IO:
+ return "input/output error";
+ case JAYLINK_ERR_DEV:
+ return "device: unspecified error";
+ case JAYLINK_ERR_DEV_NOT_SUPPORTED:
+ return "device: operation not supported";
+ case JAYLINK_ERR_DEV_NOT_AVAILABLE:
+ return "device: entity not available";
+ case JAYLINK_ERR_DEV_NO_MEMORY:
+ return "device: not enough memory to perform operation";
+ default:
+ return "unknown error";
+ }
+}
+
+/**
+ * Return the name of a libjaylink error code.
+ *
+ * @param[in] error_code A libjaylink error code. See #jaylink_error for valid
+ * values.
+ *
+ * @return A string which contains the name for the given error code, or the
+ * string <i>unknown error code</i> if the error code is not known. The
+ * string is null-terminated and must not be free'd by the caller.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API const char *jaylink_strerror_name(int error_code)
+{
+ switch (error_code) {
+ case JAYLINK_OK:
+ return "JAYLINK_OK";
+ case JAYLINK_ERR:
+ return "JAYLINK_ERR";
+ case JAYLINK_ERR_ARG:
+ return "JAYLINK_ERR_ARG";
+ case JAYLINK_ERR_MALLOC:
+ return "JAYLINK_ERR_MALLOC";
+ case JAYLINK_ERR_TIMEOUT:
+ return "JAYLINK_ERR_TIMEOUT";
+ case JAYLINK_ERR_PROTO:
+ return "JAYLINK_ERR_PROTO";
+ case JAYLINK_ERR_NOT_AVAILABLE:
+ return "JAYLINK_ERR_NOT_AVAILABLE";
+ case JAYLINK_ERR_NOT_SUPPORTED:
+ return "JAYLINK_ERR_NOT_SUPPORTED";
+ case JAYLINK_ERR_IO:
+ return "JAYLINK_ERR_IO";
+ case JAYLINK_ERR_DEV:
+ return "JAYLINK_ERR_DEV";
+ case JAYLINK_ERR_DEV_NOT_SUPPORTED:
+ return "JAYLINK_ERR_DEV_NOT_SUPPORTED";
+ case JAYLINK_ERR_DEV_NOT_AVAILABLE:
+ return "JAYLINK_ERR_DEV_NOT_AVAILABLE";
+ case JAYLINK_ERR_DEV_NO_MEMORY:
+ return "JAYLINK_ERR_DEV_NO_MEMORY";
+ default:
+ return "unknown error code";
+ }
+}
diff --git a/src/jtag/drivers/libjaylink/libjaylink/fileio.c b/src/jtag/drivers/libjaylink/libjaylink/fileio.c
new file mode 100644
index 0000000..933c366
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/libjaylink/fileio.c
@@ -0,0 +1,499 @@
+/*
+ * This file is part of the libjaylink project.
+ *
+ * Copyright (C) 2015 Marc Schink <jaylink-dev@marcschink.de>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "libjaylink.h"
+#include "libjaylink-internal.h"
+
+/**
+ * @file
+ *
+ * File I/O functions.
+ */
+
+/** @cond PRIVATE */
+#define CMD_FILE_IO 0x1e
+
+#define FILE_IO_CMD_READ 0x64
+#define FILE_IO_CMD_WRITE 0x65
+#define FILE_IO_CMD_GET_SIZE 0x66
+#define FILE_IO_CMD_DELETE 0x67
+
+#define FILE_IO_PARAM_FILENAME 0x01
+#define FILE_IO_PARAM_OFFSET 0x02
+#define FILE_IO_PARAM_LENGTH 0x03
+
+#define FILE_IO_ERR 0x80000000
+/** @endcond */
+
+/**
+ * Read from a file.
+ *
+ * The maximum amount of data that can be read from a file at once is
+ * #JAYLINK_FILE_MAX_TRANSFER_SIZE bytes. Multiple reads in conjunction with
+ * the @p offset parameter are needed for larger files.
+ *
+ * @note This function must only be used if the device has the
+ * #JAYLINK_DEV_CAP_FILE_IO capability.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[in] filename Name of the file to read from. The length of the name
+ * must not exceed #JAYLINK_FILE_NAME_MAX_LENGTH bytes.
+ * @param[out] buffer Buffer to store read data on success. Its content is
+ * undefined on failure
+ * @param[in] offset Offset in bytes relative to the beginning of the file from
+ * where to start reading.
+ * @param[in,out] length Number of bytes to read. On success, the value gets
+ * updated with the actual number of bytes read. The
+ * value is undefined on failure.
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR_DEV Unspecified device error, or the file was not found.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_file_read(struct jaylink_device_handle *devh,
+ const char *filename, uint8_t *buffer, uint32_t offset,
+ uint32_t *length)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[18 + JAYLINK_FILE_NAME_MAX_LENGTH];
+ size_t filename_length;
+ uint32_t tmp;
+
+ if (!devh || !filename || !buffer || !length)
+ return JAYLINK_ERR_ARG;
+
+ if (!*length)
+ return JAYLINK_ERR_ARG;
+
+ if (*length > JAYLINK_FILE_MAX_TRANSFER_SIZE)
+ return JAYLINK_ERR_ARG;
+
+ filename_length = strlen(filename);
+
+ if (!filename_length)
+ return JAYLINK_ERR_ARG;
+
+ if (filename_length > JAYLINK_FILE_NAME_MAX_LENGTH)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write(devh, 18 + filename_length, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_FILE_IO;
+ buf[1] = FILE_IO_CMD_READ;
+ buf[2] = 0x00;
+
+ buf[3] = filename_length;
+ buf[4] = FILE_IO_PARAM_FILENAME;
+ memcpy(buf + 5, filename, filename_length);
+
+ buf[filename_length + 5] = 0x04;
+ buf[filename_length + 6] = FILE_IO_PARAM_OFFSET;
+ buffer_set_u32(buf, offset, filename_length + 7);
+
+ buf[filename_length + 11] = 0x04;
+ buf[filename_length + 12] = FILE_IO_PARAM_LENGTH;
+ buffer_set_u32(buf, *length, filename_length + 13);
+
+ buf[filename_length + 17] = 0x00;
+
+ ret = transport_write(devh, buf, 18 + filename_length);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_start_read(devh, *length);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, buffer, *length);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_start_read(devh, 4);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, buf, 4);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ tmp = buffer_get_u32(buf, 0);
+
+ if (tmp & FILE_IO_ERR)
+ return JAYLINK_ERR_DEV;
+
+ *length = tmp;
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Write to a file.
+ *
+ * If a file does not exist, a new file is created.
+ *
+ * The maximum amount of data that can be written to a file at once is
+ * #JAYLINK_FILE_MAX_TRANSFER_SIZE bytes. Multiple writes in conjunction with
+ * the @p offset parameter are needed for larger files.
+ *
+ * @note This function must only be used if the device has the
+ * #JAYLINK_DEV_CAP_FILE_IO capability.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[in] filename Name of the file to write to. The length of the name
+ * must not exceed #JAYLINK_FILE_NAME_MAX_LENGTH bytes.
+ * @param[in] buffer Buffer to write data from.
+ * @param[in] offset Offset in bytes relative to the beginning of the file from
+ * where to start writing.
+ * @param[in,out] length Number of bytes to write. On success, the value gets
+ * updated with the actual number of bytes written. The
+ * value is undefined on failure.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR_DEV Unspecified device error, or the file was not found.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_file_write(struct jaylink_device_handle *devh,
+ const char *filename, const uint8_t *buffer, uint32_t offset,
+ uint32_t *length)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[18 + JAYLINK_FILE_NAME_MAX_LENGTH];
+ size_t filename_length;
+ uint32_t tmp;
+
+ if (!devh || !filename || !buffer || !length)
+ return JAYLINK_ERR_ARG;
+
+ if (!*length)
+ return JAYLINK_ERR_ARG;
+
+ if (*length > JAYLINK_FILE_MAX_TRANSFER_SIZE)
+ return JAYLINK_ERR_ARG;
+
+ filename_length = strlen(filename);
+
+ if (!filename_length)
+ return JAYLINK_ERR_ARG;
+
+ if (filename_length > JAYLINK_FILE_NAME_MAX_LENGTH)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write(devh, 18 + filename_length, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_FILE_IO;
+ buf[1] = FILE_IO_CMD_WRITE;
+ buf[2] = 0x00;
+
+ buf[3] = filename_length;
+ buf[4] = FILE_IO_PARAM_FILENAME;
+ memcpy(buf + 5, filename, filename_length);
+
+ buf[filename_length + 5] = 0x04;
+ buf[filename_length + 6] = FILE_IO_PARAM_OFFSET;
+ buffer_set_u32(buf, offset, filename_length + 7);
+
+ buf[filename_length + 11] = 0x04;
+ buf[filename_length + 12] = FILE_IO_PARAM_LENGTH;
+ buffer_set_u32(buf, *length, filename_length + 13);
+
+ buf[filename_length + 17] = 0x00;
+
+ ret = transport_write(devh, buf, 18 + filename_length);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_start_write(devh, *length, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_write(devh, buffer, *length);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_start_read(devh, 4);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, buf, 4);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ tmp = buffer_get_u32(buf, 0);
+
+ if (tmp & FILE_IO_ERR)
+ return JAYLINK_ERR_DEV;
+
+ *length = tmp;
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Retrieve the size of a file.
+ *
+ * @note This function must only be used if the device has the
+ * #JAYLINK_DEV_CAP_FILE_IO capability.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[in] filename Name of the file to retrieve the size of. The length
+ * of the name must not exceed
+ * #JAYLINK_FILE_NAME_MAX_LENGTH bytes.
+ * @param[out] size Size of the file in bytes on success, and undefined on
+ * failure.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR_DEV Unspecified device error, or the file was not found.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_file_get_size(struct jaylink_device_handle *devh,
+ const char *filename, uint32_t *size)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[6 + JAYLINK_FILE_NAME_MAX_LENGTH];
+ size_t length;
+ uint32_t tmp;
+
+ if (!devh || !filename || !size)
+ return JAYLINK_ERR_ARG;
+
+ length = strlen(filename);
+
+ if (!length)
+ return JAYLINK_ERR_ARG;
+
+ if (length > JAYLINK_FILE_NAME_MAX_LENGTH)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write(devh, 6 + length, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_FILE_IO;
+ buf[1] = FILE_IO_CMD_GET_SIZE;
+ buf[2] = 0x00;
+
+ buf[3] = length;
+ buf[4] = FILE_IO_PARAM_FILENAME;
+ memcpy(buf + 5, filename, length);
+
+ buf[length + 5] = 0x00;
+
+ ret = transport_write(devh, buf, 6 + length);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_start_read(devh, 4);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, buf, 4);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ tmp = buffer_get_u32(buf, 0);
+
+ if (tmp & FILE_IO_ERR)
+ return JAYLINK_ERR_DEV;
+
+ *size = tmp;
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Delete a file.
+ *
+ * @note This function must only be used if the device has the
+ * #JAYLINK_DEV_CAP_FILE_IO capability.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[in] filename Name of the file to delete. The length of the name
+ * must not exceed #JAYLINK_FILE_NAME_MAX_LENGTH bytes.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR_DEV Unspecified device error, or the file was not found.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_file_delete(struct jaylink_device_handle *devh,
+ const char *filename)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[6 + JAYLINK_FILE_NAME_MAX_LENGTH];
+ size_t length;
+ uint32_t tmp;
+
+ if (!devh || !filename)
+ return JAYLINK_ERR_ARG;
+
+ length = strlen(filename);
+
+ if (!length)
+ return JAYLINK_ERR_ARG;
+
+ if (length > JAYLINK_FILE_NAME_MAX_LENGTH)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write(devh, 6 + length, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_FILE_IO;
+ buf[1] = FILE_IO_CMD_DELETE;
+ buf[2] = 0x00;
+
+ buf[3] = length;
+ buf[4] = FILE_IO_PARAM_FILENAME;
+ memcpy(buf + 5, filename, length);
+
+ buf[length + 5] = 0x00;
+
+ ret = transport_write(devh, buf, 6 + length);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_start_read(devh, 4);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, buf, 4);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ tmp = buffer_get_u32(buf, 0);
+
+ if (tmp & FILE_IO_ERR)
+ return JAYLINK_ERR_DEV;
+
+ return JAYLINK_OK;
+}
diff --git a/src/jtag/drivers/libjaylink/libjaylink/jtag.c b/src/jtag/drivers/libjaylink/libjaylink/jtag.c
new file mode 100644
index 0000000..c0c65de
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/libjaylink/jtag.c
@@ -0,0 +1,259 @@
+/*
+ * This file is part of the libjaylink project.
+ *
+ * Copyright (C) 2014-2015 Marc Schink <jaylink-dev@marcschink.de>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "libjaylink.h"
+#include "libjaylink-internal.h"
+
+/**
+ * @file
+ *
+ * JTAG functions.
+ */
+
+/** @cond PRIVATE */
+#define CMD_JTAG_IO_V2 0xce
+#define CMD_JTAG_IO_V3 0xcf
+#define CMD_JTAG_CLEAR_TRST 0xde
+#define CMD_JTAG_SET_TRST 0xdf
+
+/**
+ * Error code indicating that there is not enough free memory on the device to
+ * perform the JTAG I/O operation.
+ */
+#define JTAG_IO_ERR_NO_MEMORY 0x06
+/** @endcond */
+
+/**
+ * Perform a JTAG I/O operation.
+ *
+ * @note This function must only be used if the #JAYLINK_TIF_JTAG interface is
+ * available and selected. Nevertheless, this function can be used if the
+ * device doesn't have the #JAYLINK_DEV_CAP_SELECT_TIF capability.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[in] tms Buffer to read TMS data from.
+ * @param[in] tdi Buffer to read TDI data from.
+ * @param[out] tdo Buffer to store TDO data on success. Its content is
+ * undefined on failure. The buffer must be large enough to
+ * contain at least the specified number of bits to transfer.
+ * @param[in] length Number of bits to transfer.
+ * @param[in] version Version of the JTAG command to use.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR_DEV_NO_MEMORY Not enough memory on the device to perform
+ * the operation.
+ * @retval JAYLINK_ERR_DEV Unspecified device error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @see jaylink_select_interface()
+ * @see jaylink_set_speed()
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_jtag_io(struct jaylink_device_handle *devh,
+ const uint8_t *tms, const uint8_t *tdi, uint8_t *tdo,
+ uint16_t length, enum jaylink_jtag_version version)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[4];
+ uint16_t num_bytes;
+ uint16_t read_length;
+ uint8_t status;
+ uint8_t cmd;
+
+ if (!devh || !tms || !tdi || !tdo || !length)
+ return JAYLINK_ERR_ARG;
+
+ num_bytes = (length + 7) / 8;
+ read_length = num_bytes;
+
+ switch (version) {
+ case JAYLINK_JTAG_VERSION_2:
+ cmd = CMD_JTAG_IO_V2;
+ break;
+ case JAYLINK_JTAG_VERSION_3:
+ cmd = CMD_JTAG_IO_V3;
+ /* In this version, the response includes a status byte. */
+ read_length++;
+ break;
+ default:
+ return JAYLINK_ERR_ARG;
+ }
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write_read(devh, 4 + 2 * num_bytes,
+ read_length, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = cmd;
+ buf[1] = 0x00;
+ buffer_set_u16(buf, length, 2);
+
+ ret = transport_write(devh, buf, 4);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_write(devh, tms, num_bytes);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_write(devh, tdi, num_bytes);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, tdo, num_bytes);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ if (version == JAYLINK_JTAG_VERSION_2)
+ return JAYLINK_OK;
+
+ ret = transport_read(devh, &status, 1);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ if (status == JTAG_IO_ERR_NO_MEMORY) {
+ return JAYLINK_ERR_DEV_NO_MEMORY;
+ } else if (status > 0) {
+ log_err(ctx, "JTAG I/O operation failed: 0x%x.", status);
+ return JAYLINK_ERR_DEV;
+ }
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Clear the JTAG test reset (TRST) signal.
+ *
+ * @param[in,out] devh Device handle.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_jtag_clear_trst(struct jaylink_device_handle *devh)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[1];
+
+ if (!devh)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write(devh, 1, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_JTAG_CLEAR_TRST;
+
+ ret = transport_write(devh, buf, 1);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Set the JTAG test reset (TRST) signal.
+ *
+ * @param[in,out] devh Device handle.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_jtag_set_trst(struct jaylink_device_handle *devh)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[1];
+
+ if (!devh)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write(devh, 1, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_JTAG_SET_TRST;
+
+ ret = transport_write(devh, buf, 1);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ return JAYLINK_OK;
+}
diff --git a/src/jtag/drivers/libjaylink/libjaylink/libjaylink-internal.h b/src/jtag/drivers/libjaylink/libjaylink/libjaylink-internal.h
new file mode 100644
index 0000000..f97ec14
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/libjaylink/libjaylink-internal.h
@@ -0,0 +1,320 @@
+/*
+ * This file is part of the libjaylink project.
+ *
+ * Copyright (C) 2014-2016 Marc Schink <jaylink-dev@marcschink.de>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBJAYLINK_LIBJAYLINK_INTERNAL_H
+#define LIBJAYLINK_LIBJAYLINK_INTERNAL_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#ifdef _WIN32
+#include <ws2tcpip.h>
+#else
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_LIBUSB
+#include <libusb.h>
+#endif
+
+#include "libjaylink.h"
+
+/**
+ * @file
+ *
+ * Internal libjaylink header file.
+ */
+
+/** Macro to mark private libjaylink symbol. */
+#if defined(_WIN32) || defined(__MSYS__) || defined(__CYGWIN__)
+#define JAYLINK_PRIV
+#else
+#define JAYLINK_PRIV __attribute__ ((visibility ("hidden")))
+#endif
+
+/** Calculate the minimum of two numeric values. */
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+struct jaylink_context {
+#ifdef HAVE_LIBUSB
+ /** libusb context. */
+ struct libusb_context *usb_ctx;
+#endif
+ /**
+ * List of allocated device instances.
+ *
+ * Used to prevent multiple device instances for the same device.
+ */
+ struct list *devs;
+ /** List of recently discovered devices. */
+ struct list *discovered_devs;
+ /** Current log level. */
+ enum jaylink_log_level log_level;
+ /** Log callback function. */
+ jaylink_log_callback log_callback;
+ /** User data to be passed to the log callback function. */
+ void *log_callback_data;
+ /** Log domain. */
+ char log_domain[JAYLINK_LOG_DOMAIN_MAX_LENGTH + 1];
+};
+
+struct jaylink_device {
+ /** libjaylink context. */
+ struct jaylink_context *ctx;
+ /** Number of references held on this device instance. */
+ size_t ref_count;
+ /** Host interface. */
+ enum jaylink_host_interface iface;
+ /**
+ * Serial number of the device.
+ *
+ * This number is for enumeration purpose only and can differ from the
+ * real serial number of the device.
+ */
+ uint32_t serial_number;
+ /** Indicates whether the serial number is valid. */
+ bool valid_serial_number;
+#ifdef HAVE_LIBUSB
+ /** libusb device instance. */
+ struct libusb_device *usb_dev;
+ /** USB address of the device. */
+ uint8_t usb_address;
+#endif
+ /**
+ * IPv4 address.
+ *
+ * The address is encoded as string in quad-dotted decimal format.
+ *
+ * This field is used for devices with host interface #JAYLINK_HIF_TCP
+ * only.
+ */
+ char ipv4_address[INET_ADDRSTRLEN];
+ /**
+ * Media Access Control (MAC) address.
+ *
+ * This field is used for devices with host interface #JAYLINK_HIF_TCP
+ * only.
+ */
+ uint8_t mac_address[JAYLINK_MAC_ADDRESS_LENGTH];
+ /** Indicates whether the MAC address is available. */
+ bool has_mac_address;
+ /**
+ * Product name.
+ *
+ * This field is used for devices with host interface #JAYLINK_HIF_TCP
+ * only.
+ */
+ char product_name[JAYLINK_PRODUCT_NAME_MAX_LENGTH];
+ /** Indicates whether the product name is available. */
+ bool has_product_name;
+ /**
+ * Nickname.
+ *
+ * This field is used for devices with host interface #JAYLINK_HIF_TCP
+ * only.
+ */
+ char nickname[JAYLINK_NICKNAME_MAX_LENGTH];
+ /** Indicates whether the nickname is available. */
+ bool has_nickname;
+ /**
+ * Hardware version.
+ *
+ * This field is used for devices with host interface #JAYLINK_HIF_TCP
+ * only.
+ */
+ struct jaylink_hardware_version hw_version;
+ /** Indicates whether the hardware version is available. */
+ bool has_hw_version;
+};
+
+struct jaylink_device_handle {
+ /** Device instance. */
+ struct jaylink_device *dev;
+ /**
+ * Buffer for write and read operations.
+ *
+ * Note that write and read operations are always processed
+ * consecutively and therefore the same buffer can be used for both.
+ */
+ uint8_t *buffer;
+ /** Buffer size. */
+ size_t buffer_size;
+ /** Number of bytes left for the read operation. */
+ size_t read_length;
+ /** Number of bytes available in the buffer to be read. */
+ size_t bytes_available;
+ /** Current read position in the buffer. */
+ size_t read_pos;
+ /**
+ * Number of bytes left to be written before the write operation will
+ * be performed.
+ */
+ size_t write_length;
+ /**
+ * Current write position in the buffer.
+ *
+ * This is equivalent to the number of bytes in the buffer and used for
+ * write operations only.
+ */
+ size_t write_pos;
+#ifdef HAVE_LIBUSB
+ /** libusb device handle. */
+ struct libusb_device_handle *usb_devh;
+ /** USB interface number of the device. */
+ uint8_t interface_number;
+ /** USB interface IN endpoint of the device. */
+ uint8_t endpoint_in;
+ /** USB interface OUT endpoint of the device. */
+ uint8_t endpoint_out;
+#endif
+ /**
+ * Socket descriptor.
+ *
+ * This field is used for devices with host interface #JAYLINK_HIF_TCP
+ * only.
+ */
+ int sock;
+};
+
+struct list {
+ void *data;
+ struct list *next;
+};
+
+typedef bool (*list_compare_callback)(const void *data, const void *user_data);
+
+/*--- buffer.c --------------------------------------------------------------*/
+
+JAYLINK_PRIV void buffer_set_u16(uint8_t *buffer, uint16_t value,
+ size_t offset);
+JAYLINK_PRIV uint16_t buffer_get_u16(const uint8_t *buffer, size_t offset);
+JAYLINK_PRIV void buffer_set_u32(uint8_t *buffer, uint32_t value,
+ size_t offset);
+JAYLINK_PRIV uint32_t buffer_get_u32(const uint8_t *buffer, size_t offset);
+
+/*--- device.c --------------------------------------------------------------*/
+
+JAYLINK_PRIV struct jaylink_device *device_allocate(
+ struct jaylink_context *ctx);
+
+/*--- discovery_tcp.c -------------------------------------------------------*/
+
+JAYLINK_PRIV int discovery_tcp_scan(struct jaylink_context *ctx);
+
+/*--- discovery_usb.c -------------------------------------------------------*/
+
+JAYLINK_PRIV int discovery_usb_scan(struct jaylink_context *ctx);
+
+/*--- list.c ----------------------------------------------------------------*/
+
+JAYLINK_PRIV struct list *list_prepend(struct list *list, void *data);
+JAYLINK_PRIV struct list *list_remove(struct list *list, const void *data);
+JAYLINK_PRIV struct list *list_find_custom(struct list *list,
+ list_compare_callback callback, const void *user_data);
+JAYLINK_PRIV size_t list_length(struct list *list);
+JAYLINK_PRIV void list_free(struct list *list);
+
+/*--- log.c -----------------------------------------------------------------*/
+
+JAYLINK_PRIV int log_vprintf(const struct jaylink_context *ctx,
+ enum jaylink_log_level level, const char *format, va_list args,
+ void *user_data);
+JAYLINK_PRIV void log_err(const struct jaylink_context *ctx,
+ const char *format, ...);
+JAYLINK_PRIV void log_warn(const struct jaylink_context *ctx,
+ const char *format, ...);
+JAYLINK_PRIV void log_info(const struct jaylink_context *ctx,
+ const char *format, ...);
+JAYLINK_PRIV void log_dbg(const struct jaylink_context *ctx,
+ const char *format, ...);
+JAYLINK_PRIV void log_dbgio(const struct jaylink_context *ctx,
+ const char *format, ...);
+
+/*--- socket.c --------------------------------------------------------------*/
+
+JAYLINK_PRIV bool socket_close(int sock);
+JAYLINK_PRIV bool socket_bind(int sock, const struct sockaddr *address,
+ size_t length);
+JAYLINK_PRIV bool socket_send(int sock, const void *buffer, size_t *length,
+ int flags);
+JAYLINK_PRIV bool socket_recv(int sock, void *buffer, size_t *length,
+ int flags);
+JAYLINK_PRIV bool socket_sendto(int sock, const void *buffer, size_t *length,
+ int flags, const struct sockaddr *address,
+ size_t address_length);
+JAYLINK_PRIV bool socket_recvfrom(int sock, void *buffer, size_t *length,
+ int flags, struct sockaddr *address, size_t *address_length);
+JAYLINK_PRIV bool socket_set_option(int sock, int level, int option,
+ const void *value, size_t length);
+
+/*--- transport.c -----------------------------------------------------------*/
+
+JAYLINK_PRIV int transport_open(struct jaylink_device_handle *devh);
+JAYLINK_PRIV int transport_close(struct jaylink_device_handle *devh);
+JAYLINK_PRIV int transport_start_write_read(struct jaylink_device_handle *devh,
+ size_t write_length, size_t read_length, bool has_command);
+JAYLINK_PRIV int transport_start_write(struct jaylink_device_handle *devh,
+ size_t length, bool has_command);
+JAYLINK_PRIV int transport_start_read(struct jaylink_device_handle *devh,
+ size_t length);
+JAYLINK_PRIV int transport_write(struct jaylink_device_handle *devh,
+ const uint8_t *buffer, size_t length);
+JAYLINK_PRIV int transport_read(struct jaylink_device_handle *devh,
+ uint8_t *buffer, size_t length);
+
+/*--- transport_usb.c -------------------------------------------------------*/
+
+JAYLINK_PRIV int transport_usb_open(struct jaylink_device_handle *devh);
+JAYLINK_PRIV int transport_usb_close(struct jaylink_device_handle *devh);
+JAYLINK_PRIV int transport_usb_start_write_read(
+ struct jaylink_device_handle *devh, size_t write_length,
+ size_t read_length, bool has_command);
+JAYLINK_PRIV int transport_usb_start_write(struct jaylink_device_handle *devh,
+ size_t length, bool has_command);
+JAYLINK_PRIV int transport_usb_start_read(struct jaylink_device_handle *devh,
+ size_t length);
+JAYLINK_PRIV int transport_usb_write(struct jaylink_device_handle *devh,
+ const uint8_t *buffer, size_t length);
+JAYLINK_PRIV int transport_usb_read(struct jaylink_device_handle *devh,
+ uint8_t *buffer, size_t length);
+
+/*--- transport_tcp.c -------------------------------------------------------*/
+
+JAYLINK_PRIV int transport_tcp_open(struct jaylink_device_handle *devh);
+JAYLINK_PRIV int transport_tcp_close(struct jaylink_device_handle *devh);
+JAYLINK_PRIV int transport_tcp_start_write_read(
+ struct jaylink_device_handle *devh, size_t write_length,
+ size_t read_length, bool has_command);
+JAYLINK_PRIV int transport_tcp_start_write(struct jaylink_device_handle *devh,
+ size_t length, bool has_command);
+JAYLINK_PRIV int transport_tcp_start_read(struct jaylink_device_handle *devh,
+ size_t length);
+JAYLINK_PRIV int transport_tcp_write(struct jaylink_device_handle *devh,
+ const uint8_t *buffer, size_t length);
+JAYLINK_PRIV int transport_tcp_read(struct jaylink_device_handle *devh,
+ uint8_t *buffer, size_t length);
+
+#endif /* LIBJAYLINK_LIBJAYLINK_INTERNAL_H */
diff --git a/src/jtag/drivers/libjaylink/libjaylink/libjaylink.h b/src/jtag/drivers/libjaylink/libjaylink/libjaylink.h
new file mode 100644
index 0000000..223aa84
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/libjaylink/libjaylink.h
@@ -0,0 +1,589 @@
+/*
+ * This file is part of the libjaylink project.
+ *
+ * Copyright (C) 2014-2016 Marc Schink <jaylink-dev@marcschink.de>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBJAYLINK_LIBJAYLINK_H
+#define LIBJAYLINK_LIBJAYLINK_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#ifdef _WIN32
+#include <ws2tcpip.h>
+#else
+#include <arpa/inet.h>
+#endif
+
+/**
+ * @file
+ *
+ * Public libjaylink header file to be used by applications.
+ */
+
+/** Error codes returned by libjaylink functions. */
+enum jaylink_error {
+ /** No error. */
+ JAYLINK_OK = 0,
+ /** Unspecified error. */
+ JAYLINK_ERR = -1,
+ /** Invalid argument. */
+ JAYLINK_ERR_ARG = -2,
+ /** Memory allocation error. */
+ JAYLINK_ERR_MALLOC = -3,
+ /** Timeout occurred. */
+ JAYLINK_ERR_TIMEOUT = -4,
+ /** Protocol violation. */
+ JAYLINK_ERR_PROTO = -5,
+ /** Entity not available. */
+ JAYLINK_ERR_NOT_AVAILABLE = -6,
+ /** Operation not supported. */
+ JAYLINK_ERR_NOT_SUPPORTED = -7,
+ /** Input/output error. */
+ JAYLINK_ERR_IO = -8,
+ /** Device: unspecified error. */
+ JAYLINK_ERR_DEV = -1000,
+ /** Device: operation not supported. */
+ JAYLINK_ERR_DEV_NOT_SUPPORTED = -1001,
+ /** Device: entity not available. */
+ JAYLINK_ERR_DEV_NOT_AVAILABLE = -1002,
+ /** Device: not enough memory to perform operation. */
+ JAYLINK_ERR_DEV_NO_MEMORY = -1003
+};
+
+/** libjaylink log levels. */
+enum jaylink_log_level {
+ /** Output no messages. */
+ JAYLINK_LOG_LEVEL_NONE = 0,
+ /** Output error messages. */
+ JAYLINK_LOG_LEVEL_ERROR = 1,
+ /** Output warnings. */
+ JAYLINK_LOG_LEVEL_WARNING = 2,
+ /** Output informational messages. */
+ JAYLINK_LOG_LEVEL_INFO = 3,
+ /** Output debug messages. */
+ JAYLINK_LOG_LEVEL_DEBUG = 4,
+ /** Output I/O debug messages. */
+ JAYLINK_LOG_LEVEL_DEBUG_IO = 5
+};
+
+/** Default libjaylink log domain. */
+#define JAYLINK_LOG_DOMAIN_DEFAULT "jaylink: "
+
+/** Maximum length of a libjaylink log domain in bytes. */
+#define JAYLINK_LOG_DOMAIN_MAX_LENGTH 32
+
+/** libjaylink capabilities. */
+enum jaylink_capability {
+ /** Library supports USB as host interface. */
+ JAYLINK_CAP_HIF_USB = 0
+};
+
+/** Host interfaces. */
+enum jaylink_host_interface {
+ /** Universal Serial Bus (USB). */
+ JAYLINK_HIF_USB = (1 << 0),
+ /** Transmission Control Protocol (TCP). */
+ JAYLINK_HIF_TCP = (1 << 1)
+};
+
+/**
+ * USB addresses.
+ *
+ * The USB address is a way to identify USB devices and is related to the USB
+ * Product ID (PID) of a device.
+ */
+enum jaylink_usb_address {
+ /** USB address 0 (Product ID 0x0101). */
+ JAYLINK_USB_ADDRESS_0 = 0,
+ /** USB address 1 (Product ID 0x0102). */
+ JAYLINK_USB_ADDRESS_1 = 1,
+ /** USB address 2 (Product ID 0x0103). */
+ JAYLINK_USB_ADDRESS_2 = 2,
+ /** USB address 3 (Product ID 0x0104). */
+ JAYLINK_USB_ADDRESS_3 = 3
+};
+
+/** Device capabilities. */
+enum jaylink_device_capability {
+ /** Device supports retrieval of the hardware version. */
+ JAYLINK_DEV_CAP_GET_HW_VERSION = 1,
+ /** Device supports adaptive clocking. */
+ JAYLINK_DEV_CAP_ADAPTIVE_CLOCKING = 3,
+ /** Device supports reading configuration data. */
+ JAYLINK_DEV_CAP_READ_CONFIG = 4,
+ /** Device supports writing configuration data. */
+ JAYLINK_DEV_CAP_WRITE_CONFIG = 5,
+ /** Device supports retrieval of target interface speeds. */
+ JAYLINK_DEV_CAP_GET_SPEEDS = 9,
+ /** Device supports retrieval of free memory size. */
+ JAYLINK_DEV_CAP_GET_FREE_MEMORY = 11,
+ /** Device supports retrieval of hardware information. */
+ JAYLINK_DEV_CAP_GET_HW_INFO = 12,
+ /** Device supports the setting of the target power supply. */
+ JAYLINK_DEV_CAP_SET_TARGET_POWER = 13,
+ /** Device supports target interface selection. */
+ JAYLINK_DEV_CAP_SELECT_TIF = 17,
+ /** Device supports retrieval of counter values. */
+ JAYLINK_DEV_CAP_GET_COUNTERS = 19,
+ /** Device supports capturing of SWO trace data. */
+ JAYLINK_DEV_CAP_SWO = 23,
+ /** Device supports file I/O operations. */
+ JAYLINK_DEV_CAP_FILE_IO = 26,
+ /** Device supports registration of connections. */
+ JAYLINK_DEV_CAP_REGISTER = 27,
+ /** Device supports retrieval of extended capabilities. */
+ JAYLINK_DEV_CAP_GET_EXT_CAPS = 31,
+ /** Device supports EMUCOM. */
+ JAYLINK_DEV_CAP_EMUCOM = 33,
+ /** Device supports ethernet connectivity. */
+ JAYLINK_DEV_CAP_ETHERNET = 38
+};
+
+/** Hardware information. */
+enum jaylink_hardware_info {
+ /**
+ * Status of the target power supply.
+ *
+ * This indicates whether the target power supply on pin 19 of the
+ * 20-pin JTAG / SWD connector is enabled or disabled.
+ *
+ * @see jaylink_set_target_power()
+ */
+ JAYLINK_HW_INFO_TARGET_POWER = (1 << 0),
+ /** Current consumption of the target in mA. */
+ JAYLINK_HW_INFO_ITARGET = (1 << 2),
+ /** Peak current consumption of the target in mA. */
+ JAYLINK_HW_INFO_ITARGET_PEAK = (1 << 3)
+};
+
+/** Device counters. */
+enum jaylink_counter {
+ /** Time the device is connected to a target in milliseconds. */
+ JAYLINK_COUNTER_TARGET_TIME = (1 << 0),
+ /**
+ * Number of times the device was connected or disconnected from a
+ * target.
+ */
+ JAYLINK_COUNTER_TARGET_CONNECTIONS = (1 << 1)
+};
+
+/** Device hardware types. */
+enum jaylink_hardware_type {
+ /** J-Link. */
+ JAYLINK_HW_TYPE_JLINK = 0,
+ /** Flasher. */
+ JAYLINK_HW_TYPE_FLASHER = 2,
+ /** J-Link Pro. */
+ JAYLINK_HW_TYPE_JLINK_PRO = 3
+};
+
+/** Target interfaces. */
+enum jaylink_target_interface {
+ /** Joint Test Action Group, IEEE 1149.1 (JTAG). */
+ JAYLINK_TIF_JTAG = 0,
+ /** Serial Wire Debug (SWD). */
+ JAYLINK_TIF_SWD = 1,
+ /** Background Debug Mode 3 (BDM3). */
+ JAYLINK_TIF_BDM3 = 2,
+ /** Renesas’ single-wire debug interface (FINE). */
+ JAYLINK_TIF_FINE = 3,
+ /** 2-wire JTAG for PIC32 compliant devices. */
+ JAYLINK_TIF_2W_JTAG_PIC32 = 4,
+};
+
+/**
+ * JTAG command versions.
+ *
+ * The JTAG command version only affects the device and the communication
+ * protocol. The behaviour of a JTAG operation is not affected at all.
+ */
+enum jaylink_jtag_version {
+ /**
+ * JTAG command version 2.
+ *
+ * This version is obsolete for major hardware version 5 and above. Use
+ * #JAYLINK_JTAG_VERSION_3 for these versions instead.
+ */
+ JAYLINK_JTAG_VERSION_2 = 1,
+ /** JTAG command version 3. */
+ JAYLINK_JTAG_VERSION_3 = 2
+};
+
+/** Serial Wire Output (SWO) capture modes. */
+enum jaylink_swo_mode {
+ /** Universal Asynchronous Receiver Transmitter (UART). */
+ JAYLINK_SWO_MODE_UART = 0
+};
+
+/** Target interface speed information. */
+struct jaylink_speed {
+ /** Base frequency in Hz. */
+ uint32_t freq;
+ /** Minimum frequency divider. */
+ uint16_t div;
+};
+
+/** Serial Wire Output (SWO) speed information. */
+struct jaylink_swo_speed {
+ /** Base frequency in Hz. */
+ uint32_t freq;
+ /** Minimum frequency divider. */
+ uint32_t min_div;
+ /** Maximum frequency divider. */
+ uint32_t max_div;
+ /** Minimum prescaler. */
+ uint32_t min_prescaler;
+ /** Maximum prescaler. */
+ uint32_t max_prescaler;
+};
+
+/** Device hardware version. */
+struct jaylink_hardware_version {
+ /** Hardware type. */
+ enum jaylink_hardware_type type;
+ /** Major version. */
+ uint8_t major;
+ /** Minor version. */
+ uint8_t minor;
+ /** Revision number. */
+ uint8_t revision;
+};
+
+/** Device hardware status. */
+struct jaylink_hardware_status {
+ /** Target reference voltage in mV. */
+ uint16_t target_voltage;
+ /** TCK pin state. */
+ bool tck;
+ /** TDI pin state. */
+ bool tdi;
+ /** TDO pin state. */
+ bool tdo;
+ /** TMS pin state. */
+ bool tms;
+ /** TRES pin state. */
+ bool tres;
+ /** TRST pin state. */
+ bool trst;
+};
+
+/** Device connection. */
+struct jaylink_connection {
+ /** Handle. */
+ uint16_t handle;
+ /**
+ * Process ID (PID).
+ *
+ * Identification of the client process. Usually this is the
+ * Process ID (PID) of the client process in an arbitrary format.
+ */
+ uint32_t pid;
+ /**
+ * Host ID (HID).
+ *
+ * IPv4 address string of the client in quad-dotted decimal format
+ * (e.g. 192.0.2.235). The address 0.0.0.0 should be used for the
+ * registration of an USB connection.
+ */
+ char hid[INET_ADDRSTRLEN];
+ /** IID. */
+ uint8_t iid;
+ /** CID. */
+ uint8_t cid;
+ /**
+ * Timestamp of the last registration in milliseconds.
+ *
+ * The timestamp is relative to the time the device was powered up.
+ */
+ uint32_t timestamp;
+};
+
+/** Target interface speed value for adaptive clocking. */
+#define JAYLINK_SPEED_ADAPTIVE_CLOCKING 0xffff
+
+/** Size of the device configuration data in bytes. */
+#define JAYLINK_DEV_CONFIG_SIZE 256
+
+/** Number of bytes required to store device capabilities. */
+#define JAYLINK_DEV_CAPS_SIZE 4
+
+/** Number of bytes required to store extended device capabilities. */
+#define JAYLINK_DEV_EXT_CAPS_SIZE 32
+
+/** Maximum number of connections that can be registered on a device. */
+#define JAYLINK_MAX_CONNECTIONS 16
+
+/** Media Access Control (MAC) address length in bytes. */
+#define JAYLINK_MAC_ADDRESS_LENGTH 6
+
+/**
+ * Maximum length of a device's nickname including trailing null-terminator in
+ * bytes.
+ */
+#define JAYLINK_NICKNAME_MAX_LENGTH 32
+
+/**
+ * Maximum length of a device's product name including trailing null-terminator
+ * in bytes.
+ */
+#define JAYLINK_PRODUCT_NAME_MAX_LENGTH 32
+
+/** Maximum length of a filename in bytes. */
+#define JAYLINK_FILE_NAME_MAX_LENGTH 255
+
+/** Maximum transfer size for a file in bytes. */
+#define JAYLINK_FILE_MAX_TRANSFER_SIZE 0x100000
+
+/**
+ * EMUCOM channel with the system time of the device in milliseconds.
+ *
+ * The channel is read-only and the time is encoded in 4 bytes. The byte order
+ * is little-endian.
+ */
+#define JAYLINK_EMUCOM_CHANNEL_TIME 0x0
+
+/**
+ * Offset of EMUCOM user channels.
+ *
+ * User channels are available to implement vendor and/or device specific
+ * functionalities. All channels below are reserved.
+ */
+#define JAYLINK_EMUCOM_CHANNEL_USER 0x10000
+
+/**
+ * @struct jaylink_context
+ *
+ * Opaque structure representing a libjaylink context.
+ */
+struct jaylink_context;
+
+/**
+ * @struct jaylink_device
+ *
+ * Opaque structure representing a device.
+ */
+struct jaylink_device;
+
+/**
+ * @struct jaylink_device_handle
+ *
+ * Opaque structure representing a handle of a device.
+ */
+struct jaylink_device_handle;
+
+/** Macro to mark public libjaylink API symbol. */
+#ifdef _WIN32
+#define JAYLINK_API
+#else
+#define JAYLINK_API __attribute__ ((visibility ("default")))
+#endif
+
+/**
+ * Log callback function type.
+ *
+ * @param[in] ctx libjaylink context.
+ * @param[in] level Log level.
+ * @param[in] format Message format in printf()-style.
+ * @param[in] args Message arguments.
+ * @param[in,out] user_data User data passed to the callback function.
+ *
+ * @return Number of characters printed on success, or a negative error code on
+ * failure.
+ */
+typedef int (*jaylink_log_callback)(const struct jaylink_context *ctx,
+ enum jaylink_log_level level, const char *format, va_list args,
+ void *user_data);
+
+/*--- core.c ----------------------------------------------------------------*/
+
+JAYLINK_API int jaylink_init(struct jaylink_context **ctx);
+JAYLINK_API int jaylink_exit(struct jaylink_context *ctx);
+JAYLINK_API bool jaylink_library_has_cap(enum jaylink_capability cap);
+
+/*--- device.c --------------------------------------------------------------*/
+
+JAYLINK_API int jaylink_get_devices(struct jaylink_context *ctx,
+ struct jaylink_device ***devs, size_t *count);
+JAYLINK_API void jaylink_free_devices(struct jaylink_device **devs,
+ bool unref);
+JAYLINK_API int jaylink_device_get_host_interface(
+ const struct jaylink_device *dev,
+ enum jaylink_host_interface *iface);
+JAYLINK_API int jaylink_device_get_serial_number(
+ const struct jaylink_device *dev, uint32_t *serial_number);
+JAYLINK_API int jaylink_device_get_usb_address(
+ const struct jaylink_device *dev,
+ enum jaylink_usb_address *address);
+JAYLINK_API int jaylink_device_get_ipv4_address(
+ const struct jaylink_device *dev, char *address);
+JAYLINK_API int jaylink_device_get_mac_address(
+ const struct jaylink_device *dev, uint8_t *address);
+JAYLINK_API int jaylink_device_get_hardware_version(
+ const struct jaylink_device *dev,
+ struct jaylink_hardware_version *version);
+JAYLINK_API int jaylink_device_get_product_name(
+ const struct jaylink_device *dev, char *name);
+JAYLINK_API int jaylink_device_get_nickname(const struct jaylink_device *dev,
+ char *nickname);
+JAYLINK_API struct jaylink_device *jaylink_ref_device(
+ struct jaylink_device *dev);
+JAYLINK_API void jaylink_unref_device(struct jaylink_device *dev);
+JAYLINK_API int jaylink_open(struct jaylink_device *dev,
+ struct jaylink_device_handle **devh);
+JAYLINK_API int jaylink_close(struct jaylink_device_handle *devh);
+JAYLINK_API struct jaylink_device *jaylink_get_device(
+ struct jaylink_device_handle *devh);
+JAYLINK_API int jaylink_get_firmware_version(
+ struct jaylink_device_handle *devh, char **version,
+ size_t *length);
+JAYLINK_API int jaylink_get_hardware_info(struct jaylink_device_handle *devh,
+ uint32_t mask, uint32_t *info);
+JAYLINK_API int jaylink_get_counters(struct jaylink_device_handle *devh,
+ uint32_t mask, uint32_t *values);
+JAYLINK_API int jaylink_get_hardware_version(
+ struct jaylink_device_handle *devh,
+ struct jaylink_hardware_version *version);
+JAYLINK_API int jaylink_get_hardware_status(struct jaylink_device_handle *devh,
+ struct jaylink_hardware_status *status);
+JAYLINK_API int jaylink_get_caps(struct jaylink_device_handle *devh,
+ uint8_t *caps);
+JAYLINK_API int jaylink_get_extended_caps(struct jaylink_device_handle *devh,
+ uint8_t *caps);
+JAYLINK_API int jaylink_get_free_memory(struct jaylink_device_handle *devh,
+ uint32_t *size);
+JAYLINK_API int jaylink_read_raw_config(struct jaylink_device_handle *devh,
+ uint8_t *config);
+JAYLINK_API int jaylink_write_raw_config(struct jaylink_device_handle *devh,
+ const uint8_t *config);
+JAYLINK_API int jaylink_register(struct jaylink_device_handle *devh,
+ struct jaylink_connection *connection,
+ struct jaylink_connection *connections, size_t *count);
+JAYLINK_API int jaylink_unregister(struct jaylink_device_handle *devh,
+ const struct jaylink_connection *connection,
+ struct jaylink_connection *connections, size_t *count);
+
+/*--- discovery.c -----------------------------------------------------------*/
+
+JAYLINK_API int jaylink_discovery_scan(struct jaylink_context *ctx,
+ uint32_t ifaces);
+
+/*--- emucom.c --------------------------------------------------------------*/
+
+JAYLINK_API int jaylink_emucom_read(struct jaylink_device_handle *devh,
+ uint32_t channel, uint8_t *buffer, uint32_t *length);
+JAYLINK_API int jaylink_emucom_write(struct jaylink_device_handle *devh,
+ uint32_t channel, const uint8_t *buffer, uint32_t *length);
+
+/*--- error.c ---------------------------------------------------------------*/
+
+JAYLINK_API const char *jaylink_strerror(int error_code);
+JAYLINK_API const char *jaylink_strerror_name(int error_code);
+
+/*--- fileio.c --------------------------------------------------------------*/
+
+JAYLINK_API int jaylink_file_read(struct jaylink_device_handle *devh,
+ const char *filename, uint8_t *buffer, uint32_t offset,
+ uint32_t *length);
+JAYLINK_API int jaylink_file_write(struct jaylink_device_handle *devh,
+ const char *filename, const uint8_t *buffer, uint32_t offset,
+ uint32_t *length);
+JAYLINK_API int jaylink_file_get_size(struct jaylink_device_handle *devh,
+ const char *filename, uint32_t *size);
+JAYLINK_API int jaylink_file_delete(struct jaylink_device_handle *devh,
+ const char *filename);
+
+/*--- jtag.c ----------------------------------------------------------------*/
+
+JAYLINK_API int jaylink_jtag_io(struct jaylink_device_handle *devh,
+ const uint8_t *tms, const uint8_t *tdi, uint8_t *tdo,
+ uint16_t length, enum jaylink_jtag_version version);
+JAYLINK_API int jaylink_jtag_clear_trst(struct jaylink_device_handle *devh);
+JAYLINK_API int jaylink_jtag_set_trst(struct jaylink_device_handle *devh);
+
+/*--- log.c -----------------------------------------------------------------*/
+
+JAYLINK_API int jaylink_log_set_level(struct jaylink_context *ctx,
+ enum jaylink_log_level level);
+JAYLINK_API int jaylink_log_get_level(const struct jaylink_context *ctx,
+ enum jaylink_log_level *level);
+JAYLINK_API int jaylink_log_set_callback(struct jaylink_context *ctx,
+ jaylink_log_callback callback, void *user_data);
+JAYLINK_API int jaylink_log_set_domain(struct jaylink_context *ctx,
+ const char *domain);
+JAYLINK_API const char *jaylink_log_get_domain(
+ const struct jaylink_context *ctx);
+
+/*--- strutil.c -------------------------------------------------------------*/
+
+JAYLINK_API int jaylink_parse_serial_number(const char *str,
+ uint32_t *serial_number);
+
+/*--- swd.c -----------------------------------------------------------------*/
+
+JAYLINK_API int jaylink_swd_io(struct jaylink_device_handle *devh,
+ const uint8_t *direction, const uint8_t *out, uint8_t *in,
+ uint16_t length);
+
+/*--- swo.c -----------------------------------------------------------------*/
+
+JAYLINK_API int jaylink_swo_start(struct jaylink_device_handle *devh,
+ enum jaylink_swo_mode mode, uint32_t baudrate, uint32_t size);
+JAYLINK_API int jaylink_swo_stop(struct jaylink_device_handle *devh);
+JAYLINK_API int jaylink_swo_read(struct jaylink_device_handle *devh,
+ uint8_t *buffer, uint32_t *length);
+JAYLINK_API int jaylink_swo_get_speeds(struct jaylink_device_handle *devh,
+ enum jaylink_swo_mode mode, struct jaylink_swo_speed *speed);
+
+/*--- target.c --------------------------------------------------------------*/
+
+JAYLINK_API int jaylink_set_speed(struct jaylink_device_handle *devh,
+ uint16_t speed);
+JAYLINK_API int jaylink_get_speeds(struct jaylink_device_handle *devh,
+ struct jaylink_speed *speed);
+JAYLINK_API int jaylink_select_interface(struct jaylink_device_handle *devh,
+ enum jaylink_target_interface iface,
+ enum jaylink_target_interface *prev_iface);
+JAYLINK_API int jaylink_get_available_interfaces(
+ struct jaylink_device_handle *devh, uint32_t *ifaces);
+JAYLINK_API int jaylink_get_selected_interface(
+ struct jaylink_device_handle *devh,
+ enum jaylink_target_interface *iface);
+JAYLINK_API int jaylink_clear_reset(struct jaylink_device_handle *devh);
+JAYLINK_API int jaylink_set_reset(struct jaylink_device_handle *devh);
+JAYLINK_API int jaylink_set_target_power(struct jaylink_device_handle *devh,
+ bool enable);
+
+/*--- util.c ----------------------------------------------------------------*/
+
+JAYLINK_API bool jaylink_has_cap(const uint8_t *caps, uint32_t cap);
+
+/*--- version.c -------------------------------------------------------------*/
+
+JAYLINK_API int jaylink_version_package_get_major(void);
+JAYLINK_API int jaylink_version_package_get_minor(void);
+JAYLINK_API int jaylink_version_package_get_micro(void);
+JAYLINK_API const char *jaylink_version_package_get_string(void);
+JAYLINK_API int jaylink_version_library_get_current(void);
+JAYLINK_API int jaylink_version_library_get_revision(void);
+JAYLINK_API int jaylink_version_library_get_age(void);
+JAYLINK_API const char *jaylink_version_library_get_string(void);
+
+#include "version.h"
+
+#endif /* LIBJAYLINK_LIBJAYLINK_H */
diff --git a/src/jtag/drivers/libjaylink/libjaylink/list.c b/src/jtag/drivers/libjaylink/libjaylink/list.c
new file mode 100644
index 0000000..7c54e50
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/libjaylink/list.c
@@ -0,0 +1,115 @@
+/*
+ * This file is part of the libjaylink project.
+ *
+ * Copyright (C) 2014-2016 Marc Schink <jaylink-dev@marcschink.de>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+
+#include "libjaylink-internal.h"
+
+/**
+ * @file
+ *
+ * Singly-linked list functions.
+ */
+
+/** @private */
+JAYLINK_PRIV struct list *list_prepend(struct list *list, void *data)
+{
+ struct list *item;
+
+ item = malloc(sizeof(struct list));
+
+ if (!item)
+ return NULL;
+
+ item->data = data;
+ item->next = list;
+
+ return item;
+}
+
+/** @private */
+JAYLINK_PRIV struct list *list_remove(struct list *list, const void *data)
+{
+ struct list *item;
+ struct list *tmp;
+
+ if (!list)
+ return NULL;
+
+ item = list;
+
+ if (item->data == data) {
+ tmp = item->next;
+ free(item);
+ return tmp;
+ }
+
+ while (item->next) {
+ if (item->next->data == data) {
+ tmp = item->next;
+ item->next = item->next->next;
+ free(tmp);
+ break;
+ }
+
+ item = item->next;
+ }
+
+ return list;
+}
+
+/** @private */
+JAYLINK_PRIV struct list *list_find_custom(struct list *list,
+ list_compare_callback callback, const void *user_data)
+{
+ if (!callback)
+ return NULL;
+
+ while (list) {
+ if (callback(list->data, user_data))
+ return list;
+
+ list = list->next;
+ }
+
+ return NULL;
+}
+
+/** @private */
+JAYLINK_PRIV size_t list_length(struct list *list)
+{
+ size_t length;
+
+ for (length = 0; list; length++)
+ list = list->next;
+
+ return length;
+}
+
+/** @private */
+JAYLINK_PRIV void list_free(struct list *list)
+{
+ struct list *tmp;
+
+ while (list) {
+ tmp = list;
+ list = list->next;
+ free(tmp);
+ }
+}
diff --git a/src/jtag/drivers/libjaylink/libjaylink/log.c b/src/jtag/drivers/libjaylink/libjaylink/log.c
new file mode 100644
index 0000000..07ef172
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/libjaylink/log.c
@@ -0,0 +1,266 @@
+/*
+ * This file is part of the libjaylink project.
+ *
+ * Copyright (C) 2014-2015 Marc Schink <jaylink-dev@marcschink.de>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "libjaylink.h"
+#include "libjaylink-internal.h"
+
+/**
+ * @file
+ *
+ * Logging functions.
+ */
+
+/**
+ * Set the libjaylink log level.
+ *
+ * @param[in,out] ctx libjaylink context.
+ * @param[in] level Log level to set.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_log_set_level(struct jaylink_context *ctx,
+ enum jaylink_log_level level)
+{
+ if (!ctx)
+ return JAYLINK_ERR_ARG;
+
+ if (level > JAYLINK_LOG_LEVEL_DEBUG_IO)
+ return JAYLINK_ERR_ARG;
+
+ ctx->log_level = level;
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Get the libjaylink log level.
+ *
+ * @param[in] ctx libjaylink context.
+ * @param[out] level Log level on success, and undefined on failure.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_log_get_level(const struct jaylink_context *ctx,
+ enum jaylink_log_level *level)
+{
+ if (!ctx || !level)
+ return JAYLINK_ERR_ARG;
+
+ *level = ctx->log_level;
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Set the libjaylink log callback function.
+ *
+ * @param[in,out] ctx libjaylink context.
+ * @param[in] callback Callback function to use, or NULL to use the default log
+ * function.
+ * @param[in] user_data User data to be passed to the callback function.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_log_set_callback(struct jaylink_context *ctx,
+ jaylink_log_callback callback, void *user_data)
+{
+ if (!ctx)
+ return JAYLINK_ERR_ARG;
+
+ if (callback) {
+ ctx->log_callback = callback;
+ ctx->log_callback_data = user_data;
+ } else {
+ ctx->log_callback = &log_vprintf;
+ ctx->log_callback_data = NULL;
+ }
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Set the libjaylink log domain.
+ *
+ * The log domain is a string which is used as prefix for all log messages to
+ * differentiate them from messages of other libraries.
+ *
+ * The maximum length of the log domain is #JAYLINK_LOG_DOMAIN_MAX_LENGTH
+ * bytes, excluding the trailing null-terminator. A log domain which exceeds
+ * this length will be silently truncated.
+ *
+ * @param[in,out] ctx libjaylink context.
+ * @param[in] domain Log domain to use. To set the default log domain, use
+ * #JAYLINK_LOG_DOMAIN_DEFAULT.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_log_set_domain(struct jaylink_context *ctx,
+ const char *domain)
+{
+ int ret;
+
+ if (!ctx || !domain)
+ return JAYLINK_ERR_ARG;
+
+ ret = snprintf(ctx->log_domain, JAYLINK_LOG_DOMAIN_MAX_LENGTH + 1,
+ "%s", domain);
+
+ if (ret < 0)
+ return JAYLINK_ERR;
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Get the libjaylink log domain.
+ *
+ * @param[in] ctx libjaylink context.
+ *
+ * @return A string which contains the current log domain on success, or NULL
+ * on failure. The string is null-terminated and must not be free'd by
+ * the caller.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API const char *jaylink_log_get_domain(
+ const struct jaylink_context *ctx)
+{
+ if (!ctx)
+ return NULL;
+
+ return ctx->log_domain;
+}
+
+/** @private */
+JAYLINK_PRIV int log_vprintf(const struct jaylink_context *ctx,
+ enum jaylink_log_level level, const char *format, va_list args,
+ void *user_data)
+{
+ (void)user_data;
+
+ /*
+ * Filter out messages with higher verbosity than the verbosity of the
+ * current log level.
+ */
+ if (level > ctx->log_level)
+ return 0;
+
+ if (ctx->log_domain[0] != '\0')
+ fprintf(stderr, "%s", ctx->log_domain);
+
+ vfprintf(stderr, format, args);
+ fprintf(stderr, "\n");
+
+ return 0;
+}
+
+/** @private */
+JAYLINK_PRIV void log_err(const struct jaylink_context *ctx,
+ const char *format, ...)
+{
+ va_list args;
+
+ if (!ctx)
+ return;
+
+ va_start(args, format);
+ ctx->log_callback(ctx, JAYLINK_LOG_LEVEL_ERROR, format, args,
+ ctx->log_callback_data);
+ va_end(args);
+}
+
+/** @private */
+JAYLINK_PRIV void log_warn(const struct jaylink_context *ctx,
+ const char *format, ...)
+{
+ va_list args;
+
+ if (!ctx)
+ return;
+
+ va_start(args, format);
+ ctx->log_callback(ctx, JAYLINK_LOG_LEVEL_WARNING, format, args,
+ ctx->log_callback_data);
+ va_end(args);
+}
+
+/** @private */
+JAYLINK_PRIV void log_info(const struct jaylink_context *ctx,
+ const char *format, ...)
+{
+ va_list args;
+
+ if (!ctx)
+ return;
+
+ va_start(args, format);
+ ctx->log_callback(ctx, JAYLINK_LOG_LEVEL_INFO, format, args,
+ ctx->log_callback_data);
+ va_end(args);
+}
+
+/** @private */
+JAYLINK_PRIV void log_dbg(const struct jaylink_context *ctx,
+ const char *format, ...)
+{
+ va_list args;
+
+ if (!ctx)
+ return;
+
+ va_start(args, format);
+ ctx->log_callback(ctx, JAYLINK_LOG_LEVEL_DEBUG, format, args,
+ ctx->log_callback_data);
+ va_end(args);
+}
+
+/** @private */
+JAYLINK_PRIV void log_dbgio(const struct jaylink_context *ctx,
+ const char *format, ...)
+{
+ va_list args;
+
+ if (!ctx)
+ return;
+
+ va_start(args, format);
+ ctx->log_callback(ctx, JAYLINK_LOG_LEVEL_DEBUG_IO, format, args,
+ ctx->log_callback_data);
+ va_end(args);
+}
diff --git a/src/jtag/drivers/libjaylink/libjaylink/socket.c b/src/jtag/drivers/libjaylink/libjaylink/socket.c
new file mode 100644
index 0000000..f2a6588
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/libjaylink/socket.c
@@ -0,0 +1,257 @@
+/*
+ * This file is part of the libjaylink project.
+ *
+ * Copyright (C) 2016-2017 Marc Schink <jaylink-dev@marcschink.de>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef _WIN32
+#include <winsock2.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#endif
+
+#include "libjaylink.h"
+#include "libjaylink-internal.h"
+
+/**
+ * @file
+ *
+ * Socket abstraction layer.
+ */
+
+/**
+ * Close a socket.
+ *
+ * @param[in] sock Socket descriptor.
+ *
+ * @return Whether the socket was successfully closed.
+ */
+JAYLINK_PRIV bool socket_close(int sock)
+{
+ int ret;
+
+#ifdef _WIN32
+ ret = closesocket(sock);
+#else
+ ret = close(sock);
+#endif
+
+ if (!ret)
+ return true;
+
+ return false;
+}
+
+/**
+ * Bind an address to a socket.
+ *
+ * @param[in] sock Socket descriptor.
+ * @param[in] address Address to be bound to the socket.
+ * @param[in] length Length of the structure pointed to by @p address in bytes.
+ *
+ * @return Whether the address was successfully assigned to the socket.
+ */
+JAYLINK_PRIV bool socket_bind(int sock, const struct sockaddr *address,
+ size_t length)
+{
+ int ret;
+
+ ret = bind(sock, address, length);
+
+#ifdef _WIN32
+ if (ret == SOCKET_ERROR)
+ return false;
+#else
+ if (ret < 0)
+ return false;
+#endif
+
+ return true;
+}
+
+/**
+ * Send a message on a socket.
+ *
+ * @param[in] sock Socket descriptor.
+ * @param[in] buffer Buffer of the message to be sent.
+ * @param[in,out] length Length of the message in bytes. On success, the value
+ * gets updated with the actual number of bytes sent. The
+ * value is undefined on failure.
+ * @param[in] flags Flags to modify the function behaviour. Use bitwise OR to
+ * specify multiple flags.
+ *
+ * @return Whether the message was sent successfully.
+ */
+JAYLINK_PRIV bool socket_send(int sock, const void *buffer, size_t *length,
+ int flags)
+{
+ ssize_t ret;
+
+ ret = send(sock, buffer, *length, flags);
+#ifdef _WIN32
+ if (ret == SOCKET_ERROR)
+ return false;
+#else
+ if (ret < 0)
+ return false;
+#endif
+ *length = ret;
+
+ return true;
+}
+
+/**
+ * Receive a message from a socket.
+ *
+ * @param[in] sock Socket descriptor.
+ * @param[out] buffer Buffer to store the received message on success. Its
+ * content is undefined on failure.
+ * @param[in,out] length Maximum length of the message in bytes. On success,
+ * the value gets updated with the actual number of
+ * received bytes. The value is undefined on failure.
+ * @param[in] flags Flags to modify the function behaviour. Use bitwise OR to
+ * specify multiple flags.
+ *
+ * @return Whether a message was successfully received.
+ */
+JAYLINK_PRIV bool socket_recv(int sock, void *buffer, size_t *length,
+ int flags)
+{
+ ssize_t ret;
+
+ ret = recv(sock, buffer, *length, flags);
+
+#ifdef _WIN32
+ if (ret == SOCKET_ERROR)
+ return false;
+#else
+ if (ret < 0)
+ return false;
+#endif
+
+ *length = ret;
+
+ return true;
+}
+
+/**
+ * Send a message on a socket.
+ *
+ * @param[in] sock Socket descriptor.
+ * @param[in] buffer Buffer to send message from.
+ * @param[in,out] length Number of bytes to send. On success, the value gets
+ * updated with the actual number of bytes sent. The
+ * value is undefined on failure.
+ * @param[in] flags Flags to modify the function behaviour. Use bitwise OR to
+ * specify multiple flags.
+ * @param[in] address Destination address of the message.
+ * @param[in] address_length Length of the structure pointed to by @p address
+ * in bytes.
+ *
+ * @return Whether the message was successfully sent.
+ */
+JAYLINK_PRIV bool socket_sendto(int sock, const void *buffer, size_t *length,
+ int flags, const struct sockaddr *address,
+ size_t address_length)
+{
+ ssize_t ret;
+
+ ret = sendto(sock, buffer, *length, flags, address, address_length);
+
+#ifdef _WIN32
+ if (ret == SOCKET_ERROR)
+ return false;
+#else
+ if (ret < 0)
+ return false;
+#endif
+
+ *length = ret;
+
+ return true;
+}
+
+/**
+ * Receive a message from a socket.
+ *
+ * @param[in] sock Socket descriptor.
+ * @param[out] buffer Buffer to store the received message on success. Its
+ * content is undefined on failure.
+ * @param[in,out] length Maximum length of the message in bytes. On success,
+ * the value gets updated with the actual number of
+ * received bytes. The value is undefined on failure.
+ * @param[in] flags Flags to modify the function behaviour. Use bitwise OR to
+ * specify multiple flags.
+ * @param[out] address Structure to store the source address of the message on
+ * success. Its content is undefined on failure.
+ * Can be NULL.
+ * @param[in,out] address_length Length of the structure pointed to by
+ * @p address in bytes. On success, the value
+ * gets updated with the actual length of the
+ * structure. The value is undefined on failure.
+ * Should be NULL if @p address is NULL.
+ *
+ * @return Whether a message was successfully received.
+ */
+JAYLINK_PRIV bool socket_recvfrom(int sock, void *buffer, size_t *length,
+ int flags, struct sockaddr *address, size_t *address_length)
+{
+ ssize_t ret;
+#ifdef _WIN32
+ int tmp;
+
+ tmp = *address_length;
+ ret = recvfrom(sock, buffer, *length, flags, address, &tmp);
+
+ if (ret == SOCKET_ERROR)
+ return false;
+#else
+ socklen_t tmp;
+
+ tmp = *address_length;
+ ret = recvfrom(sock, buffer, *length, flags, address, &tmp);
+
+ if (ret < 0)
+ return false;
+#endif
+
+ *address_length = tmp;
+ *length = ret;
+
+ return true;
+}
+
+/**
+ * Set an option on a socket.
+ *
+ * @param[in] sock Socket descriptor.
+ * @param[in] level Level at which the option is defined.
+ * @param[in] option Option to set the value for.
+ * @param[in] value Buffer of the value to be set.
+ * @param[in] length Length of the value buffer in bytes.
+ *
+ * @return Whether the option was set successfully.
+ */
+JAYLINK_PRIV bool socket_set_option(int sock, int level, int option,
+ const void *value, size_t length)
+{
+ if (!setsockopt(sock, level, option, value, length))
+ return true;
+
+ return false;
+}
diff --git a/src/jtag/drivers/libjaylink/libjaylink/strutil.c b/src/jtag/drivers/libjaylink/libjaylink/strutil.c
new file mode 100644
index 0000000..283ed17
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/libjaylink/strutil.c
@@ -0,0 +1,66 @@
+/*
+ * This file is part of the libjaylink project.
+ *
+ * Copyright (C) 2016 Marc Schink <jaylink-dev@marcschink.de>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "libjaylink.h"
+
+/**
+ * @file
+ *
+ * String utility functions.
+ */
+
+/**
+ * Convert a string representation of a serial number to an integer.
+ *
+ * The string representation of the serial number must be in decimal form.
+ *
+ * @param[in] str String representation to convert.
+ * @param[out] serial_number Serial number on success, and undefined on
+ * failure.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR Conversion error. Serial number is invalid or string
+ * representation contains invalid character(s).
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_parse_serial_number(const char *str,
+ uint32_t *serial_number)
+{
+ char *end_ptr;
+ unsigned long long tmp;
+
+ if (!str || !serial_number)
+ return JAYLINK_ERR_ARG;
+
+ errno = 0;
+ tmp = strtoull(str, &end_ptr, 10);
+
+ if (*end_ptr != '\0' || errno != 0 || tmp > UINT32_MAX)
+ return JAYLINK_ERR;
+
+ *serial_number = tmp;
+
+ return JAYLINK_OK;
+}
diff --git a/src/jtag/drivers/libjaylink/libjaylink/swd.c b/src/jtag/drivers/libjaylink/libjaylink/swd.c
new file mode 100644
index 0000000..29265b7
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/libjaylink/swd.c
@@ -0,0 +1,148 @@
+/*
+ * This file is part of the libjaylink project.
+ *
+ * Copyright (C) 2014-2015 Marc Schink <jaylink-dev@marcschink.de>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "libjaylink.h"
+#include "libjaylink-internal.h"
+
+/**
+ * @file
+ *
+ * Serial Wire Debug (SWD) functions.
+ */
+
+/** @cond PRIVATE */
+#define CMD_SWD_IO 0xcf
+
+/**
+ * Error code indicating that there is not enough free memory on the device to
+ * perform the SWD I/O operation.
+ */
+#define SWD_IO_ERR_NO_MEMORY 0x06
+/** @endcond */
+
+/**
+ * Perform a SWD I/O operation.
+ *
+ * @note This function must only be used if the #JAYLINK_TIF_SWD interface is
+ * available and selected.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[in] direction Buffer to read the transfer direction from.
+ * @param[in] out Buffer to read host-to-target data from.
+ * @param[out] in Buffer to store target-to-host data on success. Its content
+ * is undefined on failure. The buffer must be large enough to
+ * contain at least the specified number of bits to transfer.
+ * @param[in] length Total number of bits to transfer from host to target and
+ * vice versa.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR_DEV_NO_MEMORY Not enough memory on the device to perform
+ * the operation.
+ * @retval JAYLINK_ERR_DEV Unspecified device error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @see jaylink_select_interface()
+ * @see jaylink_set_speed()
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_swd_io(struct jaylink_device_handle *devh,
+ const uint8_t *direction, const uint8_t *out, uint8_t *in,
+ uint16_t length)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint16_t num_bytes;
+ uint8_t buf[4];
+ uint8_t status;
+
+ if (!devh || !direction || !out || !in || !length)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ num_bytes = (length + 7) / 8;
+
+ ret = transport_start_write_read(devh, 4 + 2 * num_bytes,
+ num_bytes + 1, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_SWD_IO;
+ buf[1] = 0x00;
+ buffer_set_u16(buf, length, 2);
+
+ ret = transport_write(devh, buf, 4);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_write(devh, direction, num_bytes);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_write(devh, out, num_bytes);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, in, num_bytes);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, &status, 1);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ if (status == SWD_IO_ERR_NO_MEMORY) {
+ return JAYLINK_ERR_DEV_NO_MEMORY;
+ } else if (status > 0) {
+ log_err(ctx, "SWD I/O operation failed: 0x%x.", status);
+ return JAYLINK_ERR_DEV;
+ }
+
+ return JAYLINK_OK;
+}
diff --git a/src/jtag/drivers/libjaylink/libjaylink/swo.c b/src/jtag/drivers/libjaylink/libjaylink/swo.c
new file mode 100644
index 0000000..6037f64
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/libjaylink/swo.c
@@ -0,0 +1,453 @@
+/*
+ * This file is part of the libjaylink project.
+ *
+ * Copyright (C) 2015 Marc Schink <jaylink-dev@marcschink.de>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "libjaylink.h"
+#include "libjaylink-internal.h"
+
+/**
+ * @file
+ *
+ * Serial Wire Output (SWO) functions.
+ */
+
+/** @cond PRIVATE */
+#define CMD_SWO 0xeb
+
+#define SWO_CMD_START 0x64
+#define SWO_CMD_STOP 0x65
+#define SWO_CMD_READ 0x66
+#define SWO_CMD_GET_SPEEDS 0x6e
+
+#define SWO_PARAM_MODE 0x01
+#define SWO_PARAM_BAUDRATE 0x02
+#define SWO_PARAM_READ_SIZE 0x03
+#define SWO_PARAM_BUFFER_SIZE 0x04
+
+#define SWO_ERR 0x80000000
+/** @endcond */
+
+/**
+ * Start SWO capture.
+ *
+ * @note This function must be used only if the device has the
+ * #JAYLINK_DEV_CAP_SWO capability.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[in] mode Mode to capture data with.
+ * @param[in] baudrate Baudrate to capture data in bit per second.
+ * @param[in] size Device internal buffer size in bytes to use for capturing.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR_DEV Unspecified device error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @see jaylink_swo_get_speeds()
+ * @see jaylink_get_free_memory()
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_swo_start(struct jaylink_device_handle *devh,
+ enum jaylink_swo_mode mode, uint32_t baudrate, uint32_t size)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[32];
+ uint32_t status;
+
+ if (!devh || !baudrate || !size)
+ return JAYLINK_ERR_ARG;
+
+ if (mode != JAYLINK_SWO_MODE_UART)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write_read(devh, 21, 4, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_SWO;
+ buf[1] = SWO_CMD_START;
+
+ buf[2] = 0x04;
+ buf[3] = SWO_PARAM_MODE;
+ buffer_set_u32(buf, mode, 4);
+
+ buf[8] = 0x04;
+ buf[9] = SWO_PARAM_BAUDRATE;
+ buffer_set_u32(buf, baudrate, 10);
+
+ buf[14] = 0x04;
+ buf[15] = SWO_PARAM_BUFFER_SIZE;
+ buffer_set_u32(buf, size, 16);
+
+ buf[20] = 0x00;
+
+ ret = transport_write(devh, buf, 21);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, buf, 4);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ status = buffer_get_u32(buf, 0);
+
+ if (status > 0) {
+ log_err(ctx, "Failed to start capture: 0x%x.", status);
+ return JAYLINK_ERR_DEV;
+ }
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Stop SWO capture.
+ *
+ * @note This function must be used only if the device has the
+ * #JAYLINK_DEV_CAP_SWO capability.
+ *
+ * @param[in,out] devh Device handle.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR_DEV Unspecified device error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @see jaylink_swo_start()
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_swo_stop(struct jaylink_device_handle *devh)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[4];
+ uint32_t status;
+
+ if (!devh)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write_read(devh, 3, 4, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_SWO;
+ buf[1] = SWO_CMD_STOP;
+ buf[2] = 0x00;
+
+ ret = transport_write(devh, buf, 3);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, buf, 4);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ status = buffer_get_u32(buf, 0);
+
+ if (status > 0) {
+ log_err(ctx, "Failed to stop capture: 0x%x.", status);
+ return JAYLINK_ERR_DEV;
+ }
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Read SWO trace data.
+ *
+ * @note This function must be used only if the device has the
+ * #JAYLINK_DEV_CAP_SWO capability.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[out] buffer Buffer to store trace data on success. Its content is
+ * undefined on failure.
+ * @param[in,out] length Maximum number of bytes to read. On success, the value
+ * gets updated with the actual number of bytes read. The
+ * value is undefined on failure.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_PROTO Protocol violation.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR_DEV Unspecified device error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @see jaylink_swo_start()
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_swo_read(struct jaylink_device_handle *devh,
+ uint8_t *buffer, uint32_t *length)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[32];
+ uint32_t status;
+ uint32_t tmp;
+
+ if (!devh || !buffer || !length)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write_read(devh, 9, 8, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_SWO;
+ buf[1] = SWO_CMD_READ;
+
+ buf[2] = 0x04;
+ buf[3] = SWO_PARAM_READ_SIZE;
+ buffer_set_u32(buf, *length, 4);
+
+ buf[8] = 0x00;
+
+ ret = transport_write(devh, buf, 9);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, buf, 8);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ status = buffer_get_u32(buf, 0);
+ tmp = buffer_get_u32(buf, 4);
+
+ if (tmp > *length) {
+ log_err(ctx, "Received %u bytes but only %u bytes were "
+ "requested.", tmp, *length);
+ return JAYLINK_ERR_PROTO;
+ }
+
+ *length = tmp;
+
+ if (tmp > 0) {
+ ret = transport_start_read(devh, tmp);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, buffer, tmp);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+ }
+
+ if (status > 0) {
+ log_err(ctx, "Failed to read data: 0x%x.", status);
+ return JAYLINK_ERR_DEV;
+ }
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Retrieve SWO speeds.
+
+ * The speeds are calculated as follows:
+ *
+ * @par
+ * <tt>speeds = @a freq / n</tt> with <tt>n >= @a min_div</tt> and
+ * <tt>n <= @a max_div</tt>, where @p n is an integer
+ *
+ * Assuming, for example, a base frequency @a freq of 4500 kHz, a minimum
+ * divider @a min_div of 1 and a maximum divider @a max_div of 8 then the
+ * highest possible SWO speed is 4500 kHz / 1 = 4500 kHz. The next highest
+ * speed is 2250 kHz for a divider of 2, and so on. Accordingly, the lowest
+ * possible speed is 4500 kHz / 8 = 562.5 kHz.
+ *
+ * @note This function must be used only if the device has the
+ * #JAYLINK_DEV_CAP_SWO capability.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[in] mode Capture mode to retrieve speeds for.
+ * @param[out] speed Speed information on success, and undefined on failure.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_PROTO Protocol violation.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR_DEV Unspecified device error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_swo_get_speeds(struct jaylink_device_handle *devh,
+ enum jaylink_swo_mode mode, struct jaylink_swo_speed *speed)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[24];
+ uint32_t tmp;
+ uint32_t length;
+
+ if (!devh || !speed)
+ return JAYLINK_ERR_ARG;
+
+ if (mode != JAYLINK_SWO_MODE_UART)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write_read(devh, 9, 4, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_SWO;
+ buf[1] = SWO_CMD_GET_SPEEDS;
+
+ buf[2] = 0x04;
+ buf[3] = SWO_PARAM_MODE;
+ buffer_set_u32(buf, mode, 4);
+
+ buf[8] = 0x00;
+
+ ret = transport_write(devh, buf, 9);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, buf, 4);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ tmp = buffer_get_u32(buf, 0);
+
+ if (tmp & SWO_ERR) {
+ log_err(ctx, "Failed to retrieve speed information: 0x%x.",
+ tmp);
+ return JAYLINK_ERR_DEV;
+ }
+
+ length = tmp;
+
+ if (length != 28) {
+ log_err(ctx, "Unexpected number of bytes received: %u.",
+ length);
+ return JAYLINK_ERR_PROTO;
+ }
+
+ length = length - 4;
+ ret = transport_start_read(devh, length);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, buf, length);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ speed->freq = buffer_get_u32(buf, 4);
+ speed->min_div = buffer_get_u32(buf, 8);
+
+ if (!speed->min_div) {
+ log_err(ctx, "Minimum frequency divider is zero.");
+ return JAYLINK_ERR_PROTO;
+ }
+
+ speed->max_div = buffer_get_u32(buf, 12);
+
+ if (speed->max_div < speed->min_div) {
+ log_err(ctx, "Maximum frequency divider is less than minimum "
+ "frequency divider.");
+ return JAYLINK_ERR_PROTO;
+ }
+
+ speed->min_prescaler = buffer_get_u32(buf, 16);
+ speed->max_prescaler = buffer_get_u32(buf, 20);
+
+ if (speed->max_prescaler < speed->min_prescaler) {
+ log_err(ctx, "Maximum prescaler is less than minimum "
+ "prescaler.");
+ return JAYLINK_ERR_PROTO;
+ }
+
+ return JAYLINK_OK;
+}
diff --git a/src/jtag/drivers/libjaylink/libjaylink/target.c b/src/jtag/drivers/libjaylink/libjaylink/target.c
new file mode 100644
index 0000000..264335b
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/libjaylink/target.c
@@ -0,0 +1,533 @@
+/*
+ * This file is part of the libjaylink project.
+ *
+ * Copyright (C) 2014-2015 Marc Schink <jaylink-dev@marcschink.de>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "libjaylink.h"
+#include "libjaylink-internal.h"
+
+/**
+ * @file
+ *
+ * Target related functions.
+ */
+
+/** @cond PRIVATE */
+#define CMD_SET_SPEED 0x05
+#define CMD_SET_TARGET_POWER 0x08
+#define CMD_GET_SPEEDS 0xc0
+#define CMD_SELECT_TIF 0xc7
+#define CMD_CLEAR_RESET 0xdc
+#define CMD_SET_RESET 0xdd
+
+#define TIF_GET_SELECTED 0xfe
+#define TIF_GET_AVAILABLE 0xff
+/** @endcond */
+
+/**
+ * Set the target interface speed.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[in] speed Speed in kHz or #JAYLINK_SPEED_ADAPTIVE_CLOCKING for
+ * adaptive clocking. Speed of 0 kHz is not allowed and
+ * adaptive clocking must only be used if the device has the
+ * #JAYLINK_DEV_CAP_ADAPTIVE_CLOCKING capability.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @see jaylink_get_speeds()
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_set_speed(struct jaylink_device_handle *devh,
+ uint16_t speed)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[3];
+
+ if (!devh || !speed)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write(devh, 3, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_SET_SPEED;
+ buffer_set_u16(buf, speed, 1);
+
+ ret = transport_write(devh, buf, 3);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Retrieve target interface speeds.
+ *
+ * The speeds are applicable for the currently selected target interface only
+ * and calculated as follows:
+ *
+ * @par
+ * <tt>speeds = @a freq / n</tt> with <tt>n >= @a div</tt>, where @p n is an
+ * integer
+ *
+ * Assuming, for example, a base frequency @a freq of 4 MHz and a minimum
+ * divider @a div of 4 then the highest possible target interface speed is
+ * 4 MHz / 4 = 1 MHz. The next highest speed is 800 kHz for a divider of 5, and
+ * so on.
+ *
+ * @note This function must only be used if the device has the
+ * #JAYLINK_DEV_CAP_GET_SPEEDS capability.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[out] speed Speed information on success, and undefined on failure.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_PROTO Protocol violation.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @see jaylink_select_interface()
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_get_speeds(struct jaylink_device_handle *devh,
+ struct jaylink_speed *speed)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[6];
+ uint16_t div;
+
+ if (!devh || !speed)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write_read(devh, 1, 6, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_GET_SPEEDS;
+
+ ret = transport_write(devh, buf, 1);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, buf, 6);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ div = buffer_get_u16(buf, 4);
+
+ if (!div) {
+ log_err(ctx, "Minimum frequency divider is zero.");
+ return JAYLINK_ERR_PROTO;
+ }
+
+ speed->freq = buffer_get_u32(buf, 0);
+ speed->div = div;
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Select the target interface.
+ *
+ * @note This function must only be used if the device has the
+ * #JAYLINK_DEV_CAP_SELECT_TIF capability.
+ *
+ * @warning This function may return a value for @p prev_iface which is not
+ * covered by #jaylink_target_interface.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[in] iface Target interface to select.
+ * @param[out] prev_iface Previously selected target interface on success, and
+ * undefined on failure. Can be NULL.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @see jaylink_get_available_interfaces()
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_select_interface(struct jaylink_device_handle *devh,
+ enum jaylink_target_interface iface,
+ enum jaylink_target_interface *prev_iface)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[4];
+
+ if (!devh)
+ return JAYLINK_ERR_ARG;
+
+ switch (iface) {
+ case JAYLINK_TIF_JTAG:
+ case JAYLINK_TIF_SWD:
+ case JAYLINK_TIF_BDM3:
+ case JAYLINK_TIF_FINE:
+ case JAYLINK_TIF_2W_JTAG_PIC32:
+ break;
+ default:
+ return JAYLINK_ERR_ARG;
+ }
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write_read(devh, 2, 4, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_SELECT_TIF;
+ buf[1] = iface;
+
+ ret = transport_write(devh, buf, 2);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, buf, 4);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ if (prev_iface)
+ *prev_iface = buffer_get_u32(buf, 0);
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Retrieve the available target interfaces.
+ *
+ * The target interfaces are stored in a 32-bit bit field where each individual
+ * bit represents a target interface. A set bit indicates an available target
+ * interface. See #jaylink_target_interface for a description of the target
+ * interfaces and their bit positions.
+ *
+ * @note This function must only be used if the device has the
+ * #JAYLINK_DEV_CAP_SELECT_TIF capability.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[out] ifaces Target interfaces on success, and undefined on failure.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @see jaylink_select_interface()
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_get_available_interfaces(
+ struct jaylink_device_handle *devh, uint32_t *ifaces)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[4];
+
+ if (!devh || !ifaces)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write_read(devh, 2, 4, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_SELECT_TIF;
+ buf[1] = TIF_GET_AVAILABLE;
+
+ ret = transport_write(devh, buf, 2);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, buf, 4);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ *ifaces = buffer_get_u32(buf, 0);
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Retrieve the selected target interface.
+ *
+ * @note This function must only be used if the device has the
+ * #JAYLINK_DEV_CAP_SELECT_TIF capability.
+ *
+ * @warning This function may return a value for @p iface which is not covered
+ * by #jaylink_target_interface.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[out] iface Selected target interface on success, and undefined on
+ * failure.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @see jaylink_select_interface()
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_get_selected_interface(
+ struct jaylink_device_handle *devh,
+ enum jaylink_target_interface *iface)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[4];
+
+ if (!devh || !iface)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write_read(devh, 2, 4, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_SELECT_TIF;
+ buf[1] = TIF_GET_SELECTED;
+
+ ret = transport_write(devh, buf, 2);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ ret = transport_read(devh, buf, 4);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_read() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ *iface = buffer_get_u32(buf, 0);
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Clear the target reset signal.
+ *
+ * @param[in,out] devh Device handle.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_clear_reset(struct jaylink_device_handle *devh)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[1];
+
+ if (!devh)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write(devh, 1, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_CLEAR_RESET;
+
+ ret = transport_write(devh, buf, 1);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Set the target reset signal.
+ *
+ * @param[in,out] devh Device handle.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_set_reset(struct jaylink_device_handle *devh)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[1];
+
+ if (!devh)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write(devh, 1, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_SET_RESET;
+
+ ret = transport_write(devh, buf, 1);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ return JAYLINK_OK;
+}
+
+/**
+ * Set the target power supply.
+ *
+ * If enabled, the target is supplied with 5 V from pin 19 of the 20-pin
+ * JTAG / SWD connector.
+ *
+ * @note This function must only be used if the device has the
+ * #JAYLINK_DEV_CAP_SET_TARGET_POWER capability.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[in] enable Determines whether to enable or disable the target power
+ * supply.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR Other error conditions.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_set_target_power(struct jaylink_device_handle *devh,
+ bool enable)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[2];
+
+ if (!devh)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+ ret = transport_start_write(devh, 2, true);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_start_wrte() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ buf[0] = CMD_SET_TARGET_POWER;
+ buf[1] = enable;
+
+ ret = transport_write(devh, buf, 2);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "transport_write() failed: %s.",
+ jaylink_strerror(ret));
+ return ret;
+ }
+
+ return JAYLINK_OK;
+}
diff --git a/src/jtag/drivers/libjaylink/libjaylink/transport.c b/src/jtag/drivers/libjaylink/libjaylink/transport.c
new file mode 100644
index 0000000..0c276b3
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/libjaylink/transport.c
@@ -0,0 +1,309 @@
+/*
+ * This file is part of the libjaylink project.
+ *
+ * Copyright (C) 2014-2015 Marc Schink <jaylink-dev@marcschink.de>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "libjaylink.h"
+#include "libjaylink-internal.h"
+
+/**
+ * @file
+ *
+ * Transport abstraction layer.
+ */
+
+/**
+ * Open a device.
+ *
+ * This function must be called before any other function of the transport
+ * abstraction layer for the given device handle is called.
+ *
+ * @param[in,out] devh Device handle.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR Other error conditions.
+ */
+JAYLINK_PRIV int transport_open(struct jaylink_device_handle *devh)
+{
+ int ret;
+
+ switch (devh->dev->iface) {
+#ifdef HAVE_LIBUSB
+ case JAYLINK_HIF_USB:
+ ret = transport_usb_open(devh);
+ break;
+#endif
+ case JAYLINK_HIF_TCP:
+ ret = transport_tcp_open(devh);
+ break;
+ default:
+ log_err(devh->dev->ctx, "BUG: Invalid host interface: %u.",
+ devh->dev->iface);
+ return JAYLINK_ERR;
+ }
+
+ return ret;
+}
+
+/**
+ * Close a device.
+ *
+ * After this function has been called no other function of the transport
+ * abstraction layer for the given device handle must be called.
+ *
+ * @param[in,out] devh Device handle.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR Other error conditions.
+ */
+JAYLINK_PRIV int transport_close(struct jaylink_device_handle *devh)
+{
+ int ret;
+
+ switch (devh->dev->iface) {
+#ifdef HAVE_LIBUSB
+ case JAYLINK_HIF_USB:
+ ret = transport_usb_close(devh);
+ break;
+#endif
+ case JAYLINK_HIF_TCP:
+ ret = transport_tcp_close(devh);
+ break;
+ default:
+ log_err(devh->dev->ctx, "BUG: Invalid host interface: %u.",
+ devh->dev->iface);
+ return JAYLINK_ERR;
+ }
+
+ return ret;
+}
+
+/**
+ * Start a write operation for a device.
+ *
+ * The data of a write operation must be written with at least one call of
+ * transport_write(). It is required that all data of a write operation is
+ * written before an other write and/or read operation is started.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[in] length Number of bytes of the write operation.
+ * @param[in] has_command Determines whether the data of the write operation
+ * contains the protocol command.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ */
+JAYLINK_PRIV int transport_start_write(struct jaylink_device_handle *devh,
+ size_t length, bool has_command)
+{
+ int ret;
+
+ switch (devh->dev->iface) {
+#ifdef HAVE_LIBUSB
+ case JAYLINK_HIF_USB:
+ ret = transport_usb_start_write(devh, length, has_command);
+ break;
+#endif
+ case JAYLINK_HIF_TCP:
+ ret = transport_tcp_start_write(devh, length, has_command);
+ break;
+ default:
+ log_err(devh->dev->ctx, "BUG: Invalid host interface: %u.",
+ devh->dev->iface);
+ return JAYLINK_ERR;
+ }
+
+ return ret;
+}
+
+/**
+ * Start a read operation for a device.
+ *
+ * The data of a read operation must be read with at least one call of
+ * transport_read(). It is required that all data of a read operation is read
+ * before an other write and/or read operation is started.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[in] length Number of bytes of the read operation.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ */
+JAYLINK_PRIV int transport_start_read(struct jaylink_device_handle *devh,
+ size_t length)
+{
+ int ret;
+
+ switch (devh->dev->iface) {
+#ifdef HAVE_LIBUSB
+ case JAYLINK_HIF_USB:
+ ret = transport_usb_start_read(devh, length);
+ break;
+#endif
+ case JAYLINK_HIF_TCP:
+ ret = transport_tcp_start_read(devh, length);
+ break;
+ default:
+ log_err(devh->dev->ctx, "BUG: Invalid host interface: %u.",
+ devh->dev->iface);
+ return JAYLINK_ERR;
+ }
+
+ return ret;
+}
+
+/**
+ * Start a write and read operation for a device.
+ *
+ * This function starts a write and read operation as the consecutive call of
+ * transport_start_write() and transport_start_read() but has a different
+ * meaning from the protocol perspective and can therefore not be replaced by
+ * these functions and vice versa.
+ *
+ * @note The write operation must be completed first before the read operation
+ * must be processed.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[in] write_length Number of bytes of the write operation.
+ * @param[in] read_length Number of bytes of the read operation.
+ * @param[in] has_command Determines whether the data of the write operation
+ * contains the protocol command.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ */
+JAYLINK_PRIV int transport_start_write_read(struct jaylink_device_handle *devh,
+ size_t write_length, size_t read_length, bool has_command)
+{
+ int ret;
+
+ switch (devh->dev->iface) {
+#ifdef HAVE_LIBUSB
+ case JAYLINK_HIF_USB:
+ ret = transport_usb_start_write_read(devh, write_length,
+ read_length, has_command);
+ break;
+#endif
+ case JAYLINK_HIF_TCP:
+ ret = transport_tcp_start_write_read(devh, write_length,
+ read_length, has_command);
+ break;
+ default:
+ log_err(devh->dev->ctx, "BUG: Invalid host interface: %u.",
+ devh->dev->iface);
+ return JAYLINK_ERR;
+ }
+
+ return ret;
+}
+
+/**
+ * Write data to a device.
+ *
+ * Before this function is used transport_start_write() or
+ * transport_start_write_read() must be called to start a write operation. The
+ * total number of written bytes must not exceed the number of bytes of the
+ * write operation.
+ *
+ * @note A write operation will be performed and the data will be sent to the
+ * device when the number of written bytes reaches the number of bytes of
+ * the write operation. Before that the data will be written into a
+ * buffer.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[in] buffer Buffer to write data from.
+ * @param[in] length Number of bytes to write.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR Other error conditions.
+ */
+JAYLINK_PRIV int transport_write(struct jaylink_device_handle *devh,
+ const uint8_t *buffer, size_t length)
+{
+ int ret;
+
+ switch (devh->dev->iface) {
+#ifdef HAVE_LIBUSB
+ case JAYLINK_HIF_USB:
+ ret = transport_usb_write(devh, buffer, length);
+ break;
+#endif
+ case JAYLINK_HIF_TCP:
+ ret = transport_tcp_write(devh, buffer, length);
+ break;
+ default:
+ log_err(devh->dev->ctx, "BUG: Invalid host interface: %u.",
+ devh->dev->iface);
+ return JAYLINK_ERR;
+ }
+
+ return ret;
+}
+
+/**
+ * Read data from a device.
+ *
+ * Before this function is used transport_start_read() or
+ * transport_start_write_read() must be called to start a read operation. The
+ * total number of read bytes must not exceed the number of bytes of the read
+ * operation.
+ *
+ * @param[in,out] devh Device handle.
+ * @param[out] buffer Buffer to read data into on success. Its content is
+ * undefined on failure.
+ * @param[in] length Number of bytes to read.
+ *
+ * @retval JAYLINK_OK Success.
+ * @retval JAYLINK_ERR_ARG Invalid arguments.
+ * @retval JAYLINK_ERR_TIMEOUT A timeout occurred.
+ * @retval JAYLINK_ERR_IO Input/output error.
+ * @retval JAYLINK_ERR Other error conditions.
+ */
+JAYLINK_PRIV int transport_read(struct jaylink_device_handle *devh,
+ uint8_t *buffer, size_t length)
+{
+ int ret;
+
+ switch (devh->dev->iface) {
+#ifdef HAVE_LIBUSB
+ case JAYLINK_HIF_USB:
+ ret = transport_usb_read(devh, buffer, length);
+ break;
+#endif
+ case JAYLINK_HIF_TCP:
+ ret = transport_tcp_read(devh, buffer, length);
+ break;
+ default:
+ log_err(devh->dev->ctx, "BUG: Invalid host interface: %u.",
+ devh->dev->iface);
+ return JAYLINK_ERR;
+ }
+
+ return ret;
+}
diff --git a/src/jtag/drivers/libjaylink/libjaylink/transport_tcp.c b/src/jtag/drivers/libjaylink/libjaylink/transport_tcp.c
new file mode 100644
index 0000000..7e10179
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/libjaylink/transport_tcp.c
@@ -0,0 +1,601 @@
+/*
+ * This file is part of the libjaylink project.
+ *
+ * Copyright (C) 2015-2017 Marc Schink <jaylink-dev@marcschink.de>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+
+#ifdef _WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#endif
+
+#include "libjaylink.h"
+#include "libjaylink-internal.h"
+
+/**
+ * @file
+ *
+ * Transport abstraction layer (TCP/IP).
+ */
+
+/** @cond PRIVATE */
+#define CMD_SERVER 0x00
+#define CMD_CLIENT 0x07
+
+/**
+ * Response status code indicating that the maximum number of simultaneous
+ * connections on the device has been reached.
+ */
+#define RESP_MAX_CONNECTIONS 0xfe
+
+/** Buffer size in bytes. */
+#define BUFFER_SIZE 2048
+
+/** Timeout of a receive operation in milliseconds. */
+#define RECV_TIMEOUT 5000
+/** Timeout of a send operation in milliseconds. */
+#define SEND_TIMEOUT 5000
+
+/** String of the port number for the J-Link TCP/IP protocol. */
+#define PORT_STRING "19020"
+
+/** Size of the server's hello message in bytes. */
+#define SERVER_HELLO_SIZE 4
+/**
+ * Maximum length of the server name including trailing null-terminator in
+ * bytes.
+ */
+#define SERVER_NAME_MAX_LENGTH 256
+/** @endcond */
+
+static int initialize_handle(struct jaylink_device_handle *devh)
+{
+ struct jaylink_context *ctx;
+
+ ctx = devh->dev->ctx;
+
+ devh->buffer_size = BUFFER_SIZE;
+ devh->buffer = malloc(devh->buffer_size);
+
+ if (!devh->buffer) {
+ log_err(ctx, "Transport buffer malloc failed.");
+ return JAYLINK_ERR_MALLOC;
+ }
+
+ devh->read_length = 0;
+ devh->bytes_available = 0;
+ devh->read_pos = 0;
+
+ devh->write_length = 0;
+ devh->write_pos = 0;
+
+ return JAYLINK_OK;
+}
+
+static void cleanup_handle(struct jaylink_device_handle *devh)
+{
+ free(devh->buffer);
+}
+
+static int _recv(struct jaylink_device_handle *devh, uint8_t *buffer,
+ size_t length)
+{
+ struct jaylink_context *ctx;
+ size_t tmp;
+
+ ctx = devh->dev->ctx;
+
+ while (length > 0) {
+ tmp = length;
+
+ if (!socket_recv(devh->sock, buffer, &tmp, 0)) {
+ log_err(ctx, "Failed to receive data from device.");
+ return JAYLINK_ERR_IO;
+ } else if (!tmp) {
+ log_err(ctx, "Failed to receive data from device: "
+ "remote connection closed.");
+ return JAYLINK_ERR_IO;
+ }
+
+ buffer += tmp;
+ length -= tmp;
+
+ log_dbgio(ctx, "Received %zu bytes from device.", tmp);
+ }
+
+ return JAYLINK_OK;
+}
+
+static int handle_server_hello(struct jaylink_device_handle *devh)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ uint8_t buf[SERVER_HELLO_SIZE];
+ char name[SERVER_NAME_MAX_LENGTH];
+ uint16_t proto_version;
+ size_t length;
+
+ ctx = devh->dev->ctx;
+
+ ret = _recv(devh, buf, sizeof(buf));
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "Failed to receive hello message.");
+ return ret;
+ }
+
+ if (buf[0] == RESP_MAX_CONNECTIONS) {
+ log_err(ctx, "Maximum number of connections reached.");
+ return JAYLINK_ERR;
+ }
+
+ if (buf[0] != CMD_SERVER) {
+ log_err(ctx, "Invalid hello message received.");
+ return JAYLINK_ERR_PROTO;
+ }
+
+ proto_version = buffer_get_u16(buf, 1);
+
+ log_dbg(ctx, "Protocol version: 0x%04x.", proto_version);
+
+ length = buf[3];
+ ret = _recv(devh, (uint8_t *)name, length);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "Failed to receive server name.");
+ return ret;
+ }
+
+ name[length] = '\0';
+
+ log_dbg(ctx, "Server name: %s.", name);
+
+ return JAYLINK_OK;
+}
+
+static int set_socket_timeouts(struct jaylink_device_handle *devh)
+{
+ struct jaylink_context *ctx;
+
+ ctx = devh->dev->ctx;
+#ifdef _WIN32
+ DWORD timeout;
+
+ timeout = RECV_TIMEOUT;
+
+ if (!socket_set_option(devh->sock, SOL_SOCKET, SO_RCVTIMEO, &timeout,
+ sizeof(timeout))) {
+ log_err(ctx, "Failed to set socket receive timeout.");
+ return JAYLINK_ERR;
+ }
+
+ timeout = SEND_TIMEOUT;
+
+ if (!socket_set_option(devh->sock, SOL_SOCKET, SO_SNDTIMEO, &timeout,
+ sizeof(timeout))) {
+ log_err(ctx, "Failed to set socket send timeout.");
+ return JAYLINK_ERR;
+ }
+#else
+ struct timeval timeout;
+
+ timeout.tv_sec = RECV_TIMEOUT / 1000;
+ timeout.tv_usec = (RECV_TIMEOUT % 1000) * 1000;
+
+ if (!socket_set_option(devh->sock, SOL_SOCKET, SO_RCVTIMEO, &timeout,
+ sizeof(struct timeval))) {
+ log_err(ctx, "Failed to set socket receive timeout.");
+ return JAYLINK_ERR;
+ }
+
+ timeout.tv_sec = SEND_TIMEOUT / 1000;
+ timeout.tv_usec = (SEND_TIMEOUT % 1000) * 1000;
+
+ if (!socket_set_option(devh->sock, SOL_SOCKET, SO_SNDTIMEO, &timeout,
+ sizeof(struct timeval))) {
+ log_err(ctx, "Failed to set socket send timeout.");
+ return JAYLINK_ERR;
+ }
+#endif
+ return JAYLINK_OK;
+}
+
+JAYLINK_PRIV int transport_tcp_open(struct jaylink_device_handle *devh)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ struct jaylink_device *dev;
+ struct addrinfo hints;
+ struct addrinfo *info;
+ struct addrinfo *rp;
+ int sock;
+
+ dev = devh->dev;
+ ctx = dev->ctx;
+
+ log_dbg(ctx, "Trying to open device (IPv4 address = %s).",
+ dev->ipv4_address);
+
+ ret = initialize_handle(devh);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "Initialize device handle failed.");
+ return ret;
+ }
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+
+ ret = getaddrinfo(dev->ipv4_address, PORT_STRING, &hints, &info);
+
+ if (ret != 0) {
+ log_err(ctx, "Address lookup failed.");
+ cleanup_handle(devh);
+ return JAYLINK_ERR;
+ }
+
+ sock = -1;
+
+ for (rp = info; rp != NULL; rp = rp->ai_next) {
+ sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+
+ if (sock < 0)
+ continue;
+
+ if (!connect(sock, info->ai_addr, info->ai_addrlen))
+ break;
+
+ socket_close(sock);
+ sock = -1;
+ }
+
+ freeaddrinfo(info);
+
+ if (sock < 0) {
+ log_err(ctx, "Failed to open device.");
+ cleanup_handle(devh);
+ return JAYLINK_ERR;
+ }
+
+ log_dbg(ctx, "Device opened successfully.");
+
+ devh->sock = sock;
+ ret = set_socket_timeouts(devh);
+
+ if (ret != JAYLINK_OK) {
+ socket_close(sock);
+ cleanup_handle(devh);
+ return ret;
+ }
+
+ ret = handle_server_hello(devh);
+
+ if (ret != JAYLINK_OK) {
+ socket_close(sock);
+ cleanup_handle(devh);
+ return ret;
+ }
+
+ return JAYLINK_OK;
+}
+
+JAYLINK_PRIV int transport_tcp_close(struct jaylink_device_handle *devh)
+{
+ struct jaylink_context *ctx;
+
+ ctx = devh->dev->ctx;
+
+ log_dbg(ctx, "Closing device (IPv4 address = %s).",
+ devh->dev->ipv4_address);
+
+ cleanup_handle(devh);
+
+ log_dbg(ctx, "Device closed successfully.");
+
+ return JAYLINK_OK;
+}
+
+JAYLINK_PRIV int transport_tcp_start_write(struct jaylink_device_handle *devh,
+ size_t length, bool has_command)
+{
+ struct jaylink_context *ctx;
+
+ if (!length)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+
+ log_dbgio(ctx, "Starting write operation (length = %zu bytes).",
+ length);
+
+ if (devh->write_pos > 0)
+ log_warn(ctx, "Last write operation left %zu bytes in the "
+ "buffer.", devh->write_pos);
+
+ if (devh->write_length > 0)
+ log_warn(ctx, "Last write operation was not performed.");
+
+ devh->write_length = length;
+ devh->write_pos = 0;
+
+ if (has_command) {
+ devh->buffer[0] = CMD_CLIENT;
+ devh->write_pos++;
+ }
+
+ return JAYLINK_OK;
+}
+
+JAYLINK_PRIV int transport_tcp_start_read(struct jaylink_device_handle *devh,
+ size_t length)
+{
+ struct jaylink_context *ctx;
+
+ if (!length)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+
+ log_dbgio(ctx, "Starting read operation (length = %zu bytes).",
+ length);
+
+ if (devh->bytes_available > 0)
+ log_dbg(ctx, "Last read operation left %zu bytes in the "
+ "buffer.", devh->bytes_available);
+
+ if (devh->read_length > 0)
+ log_warn(ctx, "Last read operation left %zu bytes.",
+ devh->read_length);
+
+ devh->read_length = length;
+
+ return JAYLINK_OK;
+}
+
+JAYLINK_PRIV int transport_tcp_start_write_read(
+ struct jaylink_device_handle *devh, size_t write_length,
+ size_t read_length, bool has_command)
+{
+ struct jaylink_context *ctx;
+
+ if (!read_length || !write_length)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+
+ log_dbgio(ctx, "Starting write / read operation (length = "
+ "%zu / %zu bytes).", write_length, read_length);
+
+ if (devh->write_pos > 0)
+ log_warn(ctx, "Last write operation left %zu bytes in the "
+ "buffer.", devh->write_pos);
+
+ if (devh->write_length > 0)
+ log_warn(ctx, "Last write operation was not performed.");
+
+ if (devh->bytes_available > 0)
+ log_warn(ctx, "Last read operation left %zu bytes in the "
+ "buffer.", devh->bytes_available);
+
+ if (devh->read_length > 0)
+ log_warn(ctx, "Last read operation left %zu bytes.",
+ devh->read_length);
+
+ devh->write_length = write_length;
+ devh->write_pos = 0;
+
+ if (has_command) {
+ devh->buffer[0] = CMD_CLIENT;
+ devh->write_pos++;
+ }
+
+ devh->read_length = read_length;
+ devh->bytes_available = 0;
+ devh->read_pos = 0;
+
+ return JAYLINK_OK;
+}
+
+static int _send(struct jaylink_device_handle *devh, const uint8_t *buffer,
+ size_t length)
+{
+ struct jaylink_context *ctx;
+ size_t tmp;
+
+ ctx = devh->dev->ctx;
+
+ while (length > 0) {
+ tmp = length;
+
+ if (!socket_send(devh->sock, buffer, &tmp, 0)) {
+ log_err(ctx, "Failed to send data to device.");
+ return JAYLINK_ERR_IO;
+ }
+
+ buffer += tmp;
+ length -= tmp;
+
+ log_dbgio(ctx, "Sent %zu bytes to device.", tmp);
+ }
+
+ return JAYLINK_OK;
+}
+
+static bool adjust_buffer(struct jaylink_device_handle *devh, size_t size)
+{
+ struct jaylink_context *ctx;
+ uint8_t *buffer;
+ size_t num;
+
+ ctx = devh->dev->ctx;
+
+ /* Adjust buffer size to a multiple of BUFFER_SIZE bytes. */
+ num = size / BUFFER_SIZE;
+
+ if (size % BUFFER_SIZE > 0)
+ num++;
+
+ size = num * BUFFER_SIZE;
+ buffer = realloc(devh->buffer, size);
+
+ if (!buffer) {
+ log_err(ctx, "Failed to adjust buffer size to %zu bytes.",
+ size);
+ return false;
+ }
+
+ devh->buffer = buffer;
+ devh->buffer_size = size;
+
+ log_dbg(ctx, "Adjusted buffer size to %zu bytes.", size);
+
+ return true;
+}
+
+JAYLINK_PRIV int transport_tcp_write(struct jaylink_device_handle *devh,
+ const uint8_t *buffer, size_t length)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ size_t tmp;
+
+ ctx = devh->dev->ctx;
+
+ if (length > devh->write_length) {
+ log_err(ctx, "Requested to write %zu bytes but only %zu bytes "
+ "are expected for the write operation.", length,
+ devh->write_length);
+ return JAYLINK_ERR_ARG;
+ }
+
+ /*
+ * Store data in the buffer if the expected number of bytes for the
+ * write operation is not reached.
+ */
+ if (length < devh->write_length) {
+ if (devh->write_pos + length > devh->buffer_size) {
+ if (!adjust_buffer(devh, devh->write_pos + length))
+ return JAYLINK_ERR_MALLOC;
+ }
+
+ memcpy(devh->buffer + devh->write_pos, buffer, length);
+
+ devh->write_length -= length;
+ devh->write_pos += length;
+
+ log_dbgio(ctx, "Wrote %zu bytes into buffer.", length);
+ return JAYLINK_OK;
+ }
+
+ /*
+ * Expected number of bytes for this write operation is reached and
+ * therefore the write operation will be performed.
+ */
+ devh->write_length = 0;
+
+ /* Send data directly to the device if the buffer is empty. */
+ if (!devh->write_pos)
+ return _send(devh, buffer, length);
+
+ tmp = MIN(length, devh->buffer_size - devh->write_pos);
+
+ /*
+ * Fill up the internal buffer in order to reduce the number of
+ * messages sent to the device for performance reasons.
+ */
+ memcpy(devh->buffer + devh->write_pos, buffer, tmp);
+
+ length -= tmp;
+ buffer += tmp;
+
+ log_dbgio(ctx, "Buffer filled up with %zu bytes.", tmp);
+
+ ret = _send(devh, devh->buffer, devh->write_pos + tmp);
+
+ devh->write_pos = 0;
+
+ if (ret != JAYLINK_OK)
+ return ret;
+
+ if (!length)
+ return JAYLINK_OK;
+
+ return _send(devh, buffer, length);
+}
+
+JAYLINK_PRIV int transport_tcp_read(struct jaylink_device_handle *devh,
+ uint8_t *buffer, size_t length)
+{
+ int ret;
+ struct jaylink_context *ctx;
+
+ ctx = devh->dev->ctx;
+
+ if (length > devh->read_length) {
+ log_err(ctx, "Requested to read %zu bytes but only %zu bytes "
+ "are expected for the read operation.", length,
+ devh->read_length);
+ return JAYLINK_ERR_ARG;
+ }
+
+ if (length <= devh->bytes_available) {
+ memcpy(buffer, devh->buffer + devh->read_pos, length);
+
+ devh->read_length -= length;
+ devh->bytes_available -= length;
+ devh->read_pos += length;
+
+ log_dbgio(ctx, "Read %zu bytes from buffer.", length);
+ return JAYLINK_OK;
+ }
+
+ if (devh->bytes_available) {
+ memcpy(buffer, devh->buffer + devh->read_pos,
+ devh->bytes_available);
+
+ buffer += devh->bytes_available;
+ length -= devh->bytes_available;
+ devh->read_length -= devh->bytes_available;
+
+ log_dbgio(ctx, "Read %zu bytes from buffer to flush it.",
+ devh->bytes_available);
+
+ devh->bytes_available = 0;
+ devh->read_pos = 0;
+ }
+
+ ret = _recv(devh, buffer, length);
+
+ if (ret != JAYLINK_OK)
+ return ret;
+
+ devh->read_length -= length;
+
+ return JAYLINK_OK;
+}
diff --git a/src/jtag/drivers/libjaylink/libjaylink/transport_usb.c b/src/jtag/drivers/libjaylink/libjaylink/transport_usb.c
new file mode 100644
index 0000000..dfe9eac
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/libjaylink/transport_usb.c
@@ -0,0 +1,620 @@
+/*
+ * This file is part of the libjaylink project.
+ *
+ * Copyright (C) 2014-2016 Marc Schink <jaylink-dev@marcschink.de>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "libjaylink.h"
+#include "libjaylink-internal.h"
+
+/**
+ * @file
+ *
+ * Transport abstraction layer (USB).
+ */
+
+/** Timeout of an USB transfer in milliseconds. */
+#define USB_TIMEOUT 1000
+
+/**
+ * Number of consecutive timeouts before an USB transfer will be treated as
+ * timed out.
+ */
+#define NUM_TIMEOUTS 2
+
+/** Chunk size in bytes in which data is transferred. */
+#define CHUNK_SIZE 2048
+
+static int initialize_handle(struct jaylink_device_handle *devh)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ struct libusb_config_descriptor *config;
+ const struct libusb_interface *interface;
+ const struct libusb_interface_descriptor *desc;
+ const struct libusb_endpoint_descriptor *epdesc;
+ bool found_interface;
+ bool found_endpoint_in;
+ bool found_endpoint_out;
+ uint8_t i;
+
+ ctx = devh->dev->ctx;
+ devh->interface_number = 0;
+
+ /*
+ * Retrieve active configuration descriptor to determine the endpoints
+ * for the interface number of the device.
+ */
+ ret = libusb_get_active_config_descriptor(devh->dev->usb_dev, &config);
+
+ if (ret != LIBUSB_SUCCESS) {
+ log_err(ctx, "Failed to get configuration descriptor: %s.",
+ libusb_error_name(ret));
+ return JAYLINK_ERR;
+ }
+
+ found_interface = false;
+
+ for (i = 0; i < config->bNumInterfaces; i++) {
+ interface = &config->interface[i];
+ desc = &interface->altsetting[0];
+
+ if (desc->bInterfaceClass != LIBUSB_CLASS_VENDOR_SPEC)
+ continue;
+
+ if (desc->bInterfaceSubClass != LIBUSB_CLASS_VENDOR_SPEC)
+ continue;
+
+ if (desc->bNumEndpoints < 2)
+ continue;
+
+ found_interface = true;
+ devh->interface_number = i;
+ break;
+ }
+
+ if (!found_interface) {
+ log_err(ctx, "No suitable interface found.");
+ libusb_free_config_descriptor(config);
+ return JAYLINK_ERR;
+ }
+
+ found_endpoint_in = false;
+ found_endpoint_out = false;
+
+ for (i = 0; i < desc->bNumEndpoints; i++) {
+ epdesc = &desc->endpoint[i];
+
+ if (epdesc->bEndpointAddress & LIBUSB_ENDPOINT_IN) {
+ devh->endpoint_in = epdesc->bEndpointAddress;
+ found_endpoint_in = true;
+ } else {
+ devh->endpoint_out = epdesc->bEndpointAddress;
+ found_endpoint_out = true;
+ }
+ }
+
+ libusb_free_config_descriptor(config);
+
+ if (!found_endpoint_in) {
+ log_err(ctx, "Interface IN endpoint not found.");
+ return JAYLINK_ERR;
+ }
+
+ if (!found_endpoint_out) {
+ log_err(ctx, "Interface OUT endpoint not found.");
+ return JAYLINK_ERR;
+ }
+
+ log_dbg(ctx, "Using endpoint %02x (IN) and %02x (OUT).",
+ devh->endpoint_in, devh->endpoint_out);
+
+ /* Buffer size must be a multiple of CHUNK_SIZE bytes. */
+ devh->buffer_size = CHUNK_SIZE;
+ devh->buffer = malloc(devh->buffer_size);
+
+ if (!devh->buffer) {
+ log_err(ctx, "Transport buffer malloc failed.");
+ return JAYLINK_ERR_MALLOC;
+ }
+
+ devh->read_length = 0;
+ devh->bytes_available = 0;
+ devh->read_pos = 0;
+
+ devh->write_length = 0;
+ devh->write_pos = 0;
+
+ return JAYLINK_OK;
+}
+
+static void cleanup_handle(struct jaylink_device_handle *devh)
+{
+ free(devh->buffer);
+}
+
+JAYLINK_PRIV int transport_usb_open(struct jaylink_device_handle *devh)
+{
+ int ret;
+ struct jaylink_device *dev;
+ struct jaylink_context *ctx;
+ struct libusb_device_handle *usb_devh;
+
+ dev = devh->dev;
+ ctx = dev->ctx;
+
+ log_dbg(ctx, "Trying to open device (bus:address = %03u:%03u).",
+ libusb_get_bus_number(dev->usb_dev),
+ libusb_get_device_address(dev->usb_dev));
+
+ ret = initialize_handle(devh);
+
+ if (ret != JAYLINK_OK) {
+ log_err(ctx, "Initialize device handle failed.");
+ return ret;
+ }
+
+ ret = libusb_open(dev->usb_dev, &usb_devh);
+
+ if (ret != LIBUSB_SUCCESS) {
+ log_err(ctx, "Failed to open device: %s.",
+ libusb_error_name(ret));
+ cleanup_handle(devh);
+ return JAYLINK_ERR;
+ }
+
+ ret = libusb_claim_interface(usb_devh, devh->interface_number);
+
+ if (ret != LIBUSB_SUCCESS) {
+ log_err(ctx, "Failed to claim interface: %s.",
+ libusb_error_name(ret));
+ cleanup_handle(devh);
+ libusb_close(usb_devh);
+ return JAYLINK_ERR;
+ }
+
+ log_dbg(ctx, "Device opened successfully.");
+
+ devh->usb_devh = usb_devh;
+
+ return JAYLINK_OK;
+}
+
+JAYLINK_PRIV int transport_usb_close(struct jaylink_device_handle *devh)
+{
+ int ret;
+ struct jaylink_device *dev;
+ struct jaylink_context *ctx;
+
+ dev = devh->dev;
+ ctx = dev->ctx;
+
+ log_dbg(ctx, "Closing device (bus:address = %03u:%03u).",
+ libusb_get_bus_number(dev->usb_dev),
+ libusb_get_device_address(dev->usb_dev));
+
+ ret = libusb_release_interface(devh->usb_devh, devh->interface_number);
+
+ libusb_close(devh->usb_devh);
+ cleanup_handle(devh);
+
+ if (ret != LIBUSB_SUCCESS) {
+ log_err(ctx, "Failed to release interface: %s.",
+ libusb_error_name(ret));
+ return JAYLINK_ERR;
+ }
+
+ log_dbg(ctx, "Device closed successfully.");
+
+ return JAYLINK_OK;
+}
+
+JAYLINK_PRIV int transport_usb_start_write(struct jaylink_device_handle *devh,
+ size_t length, bool has_command)
+{
+ struct jaylink_context *ctx;
+
+ (void)has_command;
+
+ if (!length)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+
+ log_dbgio(ctx, "Starting write operation (length = %zu bytes).", length);
+
+ if (devh->write_pos > 0)
+ log_warn(ctx, "Last write operation left %zu bytes in the "
+ "buffer.", devh->write_pos);
+
+ if (devh->write_length > 0)
+ log_warn(ctx, "Last write operation was not performed.");
+
+ devh->write_length = length;
+ devh->write_pos = 0;
+
+ return JAYLINK_OK;
+}
+
+JAYLINK_PRIV int transport_usb_start_read(struct jaylink_device_handle *devh,
+ size_t length)
+{
+ struct jaylink_context *ctx;
+
+ if (!length)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+
+ log_dbgio(ctx, "Starting read operation (length = %zu bytes).",
+ length);
+
+ if (devh->bytes_available > 0)
+ log_dbg(ctx, "Last read operation left %zu bytes in the "
+ "buffer.", devh->bytes_available);
+
+ if (devh->read_length > 0)
+ log_warn(ctx, "Last read operation left %zu bytes.",
+ devh->read_length);
+
+ devh->read_length = length;
+
+ return JAYLINK_OK;
+}
+
+JAYLINK_PRIV int transport_usb_start_write_read(
+ struct jaylink_device_handle *devh, size_t write_length,
+ size_t read_length, bool has_command)
+{
+ struct jaylink_context *ctx;
+
+ (void)has_command;
+
+ if (!read_length || !write_length)
+ return JAYLINK_ERR_ARG;
+
+ ctx = devh->dev->ctx;
+
+ log_dbgio(ctx, "Starting write / read operation (length = "
+ "%zu / %zu bytes).", write_length, read_length);
+
+ if (devh->write_pos > 0)
+ log_warn(ctx, "Last write operation left %zu bytes in the "
+ "buffer.", devh->write_pos);
+
+ if (devh->write_length > 0)
+ log_warn(ctx, "Last write operation was not performed.");
+
+ if (devh->bytes_available > 0)
+ log_warn(ctx, "Last read operation left %zu bytes in the "
+ "buffer.", devh->bytes_available);
+
+ if (devh->read_length > 0)
+ log_warn(ctx, "Last read operation left %zu bytes.",
+ devh->read_length);
+
+ devh->write_length = write_length;
+ devh->write_pos = 0;
+
+ devh->read_length = read_length;
+ devh->bytes_available = 0;
+ devh->read_pos = 0;
+
+ return JAYLINK_OK;
+}
+
+static int usb_recv(struct jaylink_device_handle *devh, uint8_t *buffer,
+ size_t *length)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ unsigned int tries;
+ int transferred;
+
+ ctx = devh->dev->ctx;
+
+ tries = NUM_TIMEOUTS;
+ transferred = 0;
+
+ while (tries > 0 && !transferred) {
+ /* Always request CHUNK_SIZE bytes from the device. */
+ ret = libusb_bulk_transfer(devh->usb_devh, devh->endpoint_in,
+ (unsigned char *)buffer, CHUNK_SIZE, &transferred,
+ USB_TIMEOUT);
+
+ if (ret == LIBUSB_ERROR_TIMEOUT) {
+ log_warn(ctx, "Failed to receive data from "
+ "device: %s.", libusb_error_name(ret));
+ tries--;
+ continue;
+ } else if (ret != LIBUSB_SUCCESS) {
+ log_err(ctx, "Failed to receive data from "
+ "device: %s.", libusb_error_name(ret));
+ return JAYLINK_ERR;
+ }
+
+ log_dbgio(ctx, "Received %i bytes from device.", transferred);
+ }
+
+ /* Ignore a possible timeout if at least one byte was received. */
+ if (transferred > 0) {
+ *length = transferred;
+ return JAYLINK_OK;
+ }
+
+ log_err(ctx, "Receiving data from device timed out.");
+
+ return JAYLINK_ERR_TIMEOUT;
+}
+
+static bool adjust_buffer(struct jaylink_device_handle *devh, size_t size)
+{
+ struct jaylink_context *ctx;
+ size_t num_chunks;
+ uint8_t *buffer;
+
+ ctx = devh->dev->ctx;
+
+ /* Adjust buffer size to a multiple of CHUNK_SIZE bytes. */
+ num_chunks = size / CHUNK_SIZE;
+
+ if (size % CHUNK_SIZE > 0)
+ num_chunks++;
+
+ size = num_chunks * CHUNK_SIZE;
+ buffer = realloc(devh->buffer, size);
+
+ if (!buffer) {
+ log_err(ctx, "Failed to adjust buffer size to %zu bytes.",
+ size);
+ return false;
+ }
+
+ devh->buffer = buffer;
+ devh->buffer_size = size;
+
+ log_dbg(ctx, "Adjusted buffer size to %zu bytes.", size);
+
+ return true;
+}
+
+static int usb_send(struct jaylink_device_handle *devh, const uint8_t *buffer,
+ size_t length)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ unsigned int tries;
+ int transferred;
+
+ ctx = devh->dev->ctx;
+ tries = NUM_TIMEOUTS;
+
+ while (tries > 0 && length > 0) {
+ /* Send data in chunks of CHUNK_SIZE bytes to the device. */
+ ret = libusb_bulk_transfer(devh->usb_devh, devh->endpoint_out,
+ (unsigned char *)buffer, MIN(CHUNK_SIZE, length),
+ &transferred, USB_TIMEOUT);
+
+ if (ret == LIBUSB_SUCCESS) {
+ tries = NUM_TIMEOUTS;
+ } else if (ret == LIBUSB_ERROR_TIMEOUT) {
+ log_warn(ctx, "Failed to send data to device: %s.",
+ libusb_error_name(ret));
+ tries--;
+ } else {
+ log_err(ctx, "Failed to send data to device: %s.",
+ libusb_error_name(ret));
+ return JAYLINK_ERR;
+ }
+
+ buffer += transferred;
+ length -= transferred;
+
+ log_dbgio(ctx, "Sent %i bytes to device.", transferred);
+ }
+
+ if (!length)
+ return JAYLINK_OK;
+
+ log_err(ctx, "Sending data to device timed out.");
+
+ return JAYLINK_ERR_TIMEOUT;
+}
+
+JAYLINK_PRIV int transport_usb_write(struct jaylink_device_handle *devh,
+ const uint8_t *buffer, size_t length)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ size_t num_chunks;
+ size_t fill_bytes;
+ size_t tmp;
+
+ ctx = devh->dev->ctx;
+
+ if (length > devh->write_length) {
+ log_err(ctx, "Requested to write %zu bytes but only %zu bytes "
+ "are expected for the write operation.", length,
+ devh->write_length);
+ return JAYLINK_ERR_ARG;
+ }
+
+ /*
+ * Store data in the buffer if the expected number of bytes for the
+ * write operation is not reached.
+ */
+ if (length < devh->write_length) {
+ if (devh->write_pos + length > devh->buffer_size) {
+ if (!adjust_buffer(devh, devh->write_pos + length))
+ return JAYLINK_ERR_MALLOC;
+ }
+
+ memcpy(devh->buffer + devh->write_pos, buffer, length);
+
+ devh->write_length -= length;
+ devh->write_pos += length;
+
+ log_dbgio(ctx, "Wrote %zu bytes into buffer.", length);
+ return JAYLINK_OK;
+ }
+
+ /*
+ * Expected number of bytes for this write operation is reached and
+ * therefore the write operation will be performed.
+ */
+ devh->write_length = 0;
+
+ /* Send data directly to the device if the buffer is empty. */
+ if (!devh->write_pos)
+ return usb_send(devh, buffer, length);
+
+ /*
+ * Calculate the number of bytes to fill up the buffer to reach a
+ * multiple of CHUNK_SIZE bytes. This ensures that the data from the
+ * buffer will be sent to the device in chunks of CHUNK_SIZE bytes.
+ * Note that this is why the buffer size must be a multiple of
+ * CHUNK_SIZE bytes.
+ */
+ num_chunks = devh->write_pos / CHUNK_SIZE;
+
+ if (devh->write_pos % CHUNK_SIZE)
+ num_chunks++;
+
+ fill_bytes = (num_chunks * CHUNK_SIZE) - devh->write_pos;
+ tmp = MIN(length, fill_bytes);
+
+ if (tmp > 0) {
+ memcpy(devh->buffer + devh->write_pos, buffer, tmp);
+
+ length -= tmp;
+ buffer += tmp;
+
+ log_dbgio(ctx, "Buffer filled up with %zu bytes.", tmp);
+ }
+
+ /* Send buffered data to the device. */
+ ret = usb_send(devh, devh->buffer, devh->write_pos + tmp);
+ devh->write_pos = 0;
+
+ if (ret != JAYLINK_OK)
+ return ret;
+
+ if (!length)
+ return JAYLINK_OK;
+
+ /* Send remaining data to the device. */
+ return usb_send(devh, buffer, length);
+}
+
+JAYLINK_PRIV int transport_usb_read(struct jaylink_device_handle *devh,
+ uint8_t *buffer, size_t length)
+{
+ int ret;
+ struct jaylink_context *ctx;
+ size_t bytes_received;
+ size_t tmp;
+
+ ctx = devh->dev->ctx;
+
+ if (length > devh->read_length) {
+ log_err(ctx, "Requested to read %zu bytes but only %zu bytes "
+ "are expected for the read operation.", length,
+ devh->read_length);
+ return JAYLINK_ERR_ARG;
+ }
+
+ if (length <= devh->bytes_available) {
+ memcpy(buffer, devh->buffer + devh->read_pos, length);
+
+ devh->read_length -= length;
+ devh->bytes_available -= length;
+ devh->read_pos += length;
+
+ log_dbgio(ctx, "Read %zu bytes from buffer.", length);
+ return JAYLINK_OK;
+ }
+
+ if (devh->bytes_available) {
+ memcpy(buffer, devh->buffer + devh->read_pos,
+ devh->bytes_available);
+
+ buffer += devh->bytes_available;
+ length -= devh->bytes_available;
+ devh->read_length -= devh->bytes_available;
+
+ log_dbgio(ctx, "Read %zu bytes from buffer to flush it.",
+ devh->bytes_available);
+
+ devh->bytes_available = 0;
+ devh->read_pos = 0;
+ }
+
+ while (length > 0) {
+ /*
+ * If less than CHUNK_SIZE bytes are requested from the device,
+ * store the received data into the internal buffer instead of
+ * directly into the user provided buffer. This is necessary to
+ * prevent a possible buffer overflow because the number of
+ * requested bytes from the device is always CHUNK_SIZE and
+ * therefore up to CHUNK_SIZE bytes may be received.
+ * Note that this is why the internal buffer size must be at
+ * least CHUNK_SIZE bytes.
+ */
+ if (length < CHUNK_SIZE) {
+ ret = usb_recv(devh, devh->buffer, &bytes_received);
+
+ if (ret != JAYLINK_OK)
+ return ret;
+
+ tmp = MIN(bytes_received, length);
+ memcpy(buffer, devh->buffer, tmp);
+
+ /*
+ * Setup the buffer for the remaining data if more data
+ * was received from the device than was requested.
+ */
+ if (bytes_received > length) {
+ devh->bytes_available = bytes_received - tmp;
+ devh->read_pos = tmp;
+ }
+
+ buffer += tmp;
+ length -= tmp;
+ devh->read_length -= tmp;
+
+ log_dbgio(ctx, "Read %zu bytes from buffer.", tmp);
+ } else {
+ ret = usb_recv(devh, buffer, &bytes_received);
+
+ if (ret != JAYLINK_OK)
+ return ret;
+
+ buffer += bytes_received;
+ length -= bytes_received;
+ devh->read_length -= bytes_received;
+
+ log_dbgio(ctx, "Read %zu bytes from device.",
+ bytes_received);
+ }
+ }
+
+ return JAYLINK_OK;
+}
diff --git a/src/jtag/drivers/libjaylink/libjaylink/util.c b/src/jtag/drivers/libjaylink/libjaylink/util.c
new file mode 100644
index 0000000..4862d4e
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/libjaylink/util.c
@@ -0,0 +1,56 @@
+/*
+ * This file is part of the libjaylink project.
+ *
+ * Copyright (C) 2014-2015 Marc Schink <jaylink-dev@marcschink.de>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdbool.h>
+
+#include "libjaylink.h"
+
+/**
+ * @file
+ *
+ * Utility functions.
+ */
+
+/**
+ * Check for a capability.
+ *
+ * The capabilities are expected to be stored in a bit array consisting of one
+ * or more bytes where each individual bit represents a capability. The first
+ * bit of this array is the least significant bit of the first byte and the
+ * following bits are sequentially numbered in order of increasing bit
+ * significance and byte index. A set bit indicates a supported capability.
+ *
+ * @param[in] caps Buffer with capabilities.
+ * @param[in] cap Bit position of the capability to check for.
+ *
+ * @retval true Capability is supported.
+ * @retval false Capability is not supported or invalid argument.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API bool jaylink_has_cap(const uint8_t *caps, uint32_t cap)
+{
+ if (!caps)
+ return false;
+
+ if (caps[cap / 8] & (1 << (cap % 8)))
+ return true;
+
+ return false;
+}
diff --git a/src/jtag/drivers/libjaylink/libjaylink/version.c b/src/jtag/drivers/libjaylink/libjaylink/version.c
new file mode 100644
index 0000000..88bc023
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/libjaylink/version.c
@@ -0,0 +1,128 @@
+/*
+ * This file is part of the libjaylink project.
+ *
+ * Copyright (C) 2015 Marc Schink <jaylink-dev@marcschink.de>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "libjaylink.h"
+
+/**
+ * @file
+ *
+ * Package and library version functions.
+ */
+
+/**
+ * Get the major version number of the libjaylink package.
+ *
+ * @return The major version number of the libjaylink package.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_version_package_get_major(void)
+{
+ return JAYLINK_VERSION_PACKAGE_MAJOR;
+}
+
+/**
+ * Get the minor version number of the libjaylink package.
+ *
+ * @return The minor version number of the libjaylink package.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_version_package_get_minor(void)
+{
+ return JAYLINK_VERSION_PACKAGE_MINOR;
+}
+
+/**
+ * Get the micro version number of the libjaylink package.
+ *
+ * @return The micro version number of the libjaylink package.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_version_package_get_micro(void)
+{
+ return JAYLINK_VERSION_PACKAGE_MICRO;
+}
+
+/**
+ * Get the version number string of the libjaylink package.
+ *
+ * @return A string which contains the version number of the libjaylink
+ * package. The string is null-terminated and must not be free'd by the
+ * caller.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API const char *jaylink_version_package_get_string(void)
+{
+ return JAYLINK_VERSION_PACKAGE_STRING;
+}
+
+/**
+ * Get the <i>current</i> version number of the libjaylink libtool interface.
+ *
+ * @return The <i>current</i> version number of the libjaylink libtool
+ * interface.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_version_library_get_current(void)
+{
+ return JAYLINK_VERSION_LIBRARY_CURRENT;
+}
+
+/**
+ * Get the <i>revision</i> version number of the libjaylink libtool interface.
+ *
+ * @return The <i>revision</i> version number of the libjaylink libtool
+ * interface.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_version_library_get_revision(void)
+{
+ return JAYLINK_VERSION_LIBRARY_REVISION;
+}
+
+/**
+ * Get the <i>age</i> version number of the libjaylink libtool interface.
+ *
+ * @return The <i>age</i> version number of the libjaylink libtool interface.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API int jaylink_version_library_get_age(void)
+{
+ return JAYLINK_VERSION_LIBRARY_AGE;
+}
+
+/**
+ * Get the version number string of the libjaylink libtool interface.
+ *
+ * @return A string which contains the version number of the libjaylink libtool
+ * interface. The string is null-terminated and must not be free'd by
+ * the caller.
+ *
+ * @since 0.1.0
+ */
+JAYLINK_API const char *jaylink_version_library_get_string(void)
+{
+ return JAYLINK_VERSION_LIBRARY_STRING;
+}
diff --git a/src/jtag/drivers/libjaylink/libjaylink/version.h.in b/src/jtag/drivers/libjaylink/libjaylink/version.h.in
new file mode 100644
index 0000000..d6a7796
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/libjaylink/version.h.in
@@ -0,0 +1,53 @@
+/*
+ * This file is part of the libjaylink project.
+ *
+ * Copyright (C) 2015 Marc Schink <jaylink-dev@marcschink.de>
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBJAYLINK_VERSION_H
+#define LIBJAYLINK_VERSION_H
+
+/**
+ * @file
+ *
+ * Package and library version macros.
+ */
+
+/** Major version number of the libjaylink package. */
+#define JAYLINK_VERSION_PACKAGE_MAJOR @JAYLINK_VERSION_PACKAGE_MAJOR@
+
+/** Minor version number of the libjaylink package. */
+#define JAYLINK_VERSION_PACKAGE_MINOR @JAYLINK_VERSION_PACKAGE_MINOR@
+
+/** Micro version number of the libjaylink package. */
+#define JAYLINK_VERSION_PACKAGE_MICRO @JAYLINK_VERSION_PACKAGE_MICRO@
+
+/** Version number string of the libjaylink package. */
+#define JAYLINK_VERSION_PACKAGE_STRING "@JAYLINK_VERSION_PACKAGE@"
+
+/** <i>Current</i> version number of the libjaylink libtool interface. */
+#define JAYLINK_VERSION_LIBRARY_CURRENT @JAYLINK_VERSION_LIBRARY_CURRENT@
+
+/** <i>Revision</i> version number of the libjaylink libtool interface. */
+#define JAYLINK_VERSION_LIBRARY_REVISION @JAYLINK_VERSION_LIBRARY_REVISION@
+
+/** <i>Age</i> version number of the libjaylink libtool interface. */
+#define JAYLINK_VERSION_LIBRARY_AGE @JAYLINK_VERSION_LIBRARY_AGE@
+
+/** Version number string of the libjaylink libtool interface. */
+#define JAYLINK_VERSION_LIBRARY_STRING "@JAYLINK_VERSION_LIBRARY@"
+
+#endif /* LIBJAYLINK_VERSION_H */
diff --git a/src/jtag/drivers/libjaylink/m4/jaylink.m4 b/src/jtag/drivers/libjaylink/m4/jaylink.m4
new file mode 100644
index 0000000..749568d
--- /dev/null
+++ b/src/jtag/drivers/libjaylink/m4/jaylink.m4
@@ -0,0 +1,91 @@
+##
+## This file is part of the libjaylink project.
+##
+## Copyright (C) 2016 Marc Schink <jaylink-dev@marcschink.de>
+##
+## 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 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+
+# serial 20161011
+
+## _JAYLINK_SET_PACKAGE_VERSION(prefix, version, major, minor, micro)
+##
+m4_define([_JAYLINK_SET_PACKAGE_VERSION], [
+ m4_assert([$# == 5])
+
+ # Get the short Git revision hash of the current commit.
+ git_version=`git --git-dir="$srcdir/.git" rev-parse \
+ --short HEAD 2> /dev/null`
+
+ # Try to get the release tag for the package version from the current
+ # commit.
+ tag=`git --git-dir="$srcdir/.git" describe --match "$2" \
+ --exact-match 2> /dev/null`
+
+ version=$2
+
+ # If Git is available, append the short Git revision hash of the
+ # current commit to the version string if there is no release tag for
+ # the package version on it.
+ AS_IF([test -n "$git_version" && test -z "$tag"],
+ [version="$version-git-$git_version"])
+
+ AC_SUBST([$1_MAJOR], [$3])
+ AC_SUBST([$1_MINOR], [$4])
+ AC_SUBST([$1_MICRO], [$5])
+ AC_SUBST([$1], [$version])
+])
+
+## JAYLINK_SET_PACKAGE_VERSION(prefix, version)
+##
+## Parse the package version string of the format <major>.<minor>.<micro> and
+## set the variables <prefix>_{MAJOR,MINOR,MICRO} to their corresponding
+## values.
+##
+## Set the variable <prefix> to the package version string. If Git is
+## available, append the short Git revision hash of the current commit to the
+## version string if there is no release tag for the package version on it.
+##
+AC_DEFUN([JAYLINK_SET_PACKAGE_VERSION], [
+ m4_assert([$# == 2])
+
+ _JAYLINK_SET_PACKAGE_VERSION([$1], [$2],
+ m4_unquote(m4_split(m4_expand([$2]), [\.])))
+])
+
+## _JAYLINK_SET_LIBRARY_VERSION(prefix, version, current, revision, age)
+##
+m4_define([_JAYLINK_SET_LIBRARY_VERSION], [
+ m4_assert([$# == 5])
+
+ AC_SUBST([$1_CURRENT], [$3])
+ AC_SUBST([$1_REVISION], [$4])
+ AC_SUBST([$1_AGE], [$5])
+ AC_SUBST([$1], [$2])
+])
+
+## JAYLINK_SET_LIBRARY_VERSION(prefix, version)
+##
+## Parse the library version string of the format <current>:<revision>:<age>
+## and set the variables <prefix>_{CURRENT,REVISION,AGE} to their corresponding
+## values.
+##
+## Set the variable <prefix> to the library version string.
+##
+AC_DEFUN([JAYLINK_SET_LIBRARY_VERSION], [
+ m4_assert([$# == 2])
+
+ _JAYLINK_SET_LIBRARY_VERSION([$1], [$2],
+ m4_unquote(m4_split([$2], [:])))
+])
diff --git a/src/jtag/drivers/mpsse.c b/src/jtag/drivers/mpsse.c
index 8924892..43e924e 100644
--- a/src/jtag/drivers/mpsse.c
+++ b/src/jtag/drivers/mpsse.c
@@ -22,6 +22,7 @@
#include "mpsse.h"
#include "helper/log.h"
+#include "helper/time_support.h"
#include <libusb.h>
/* Compatibility define for older libusb-1.0 */
@@ -892,6 +893,7 @@ int mpsse_flush(struct mpsse_ctx *ctx)
}
/* Polling loop, more or less taken from libftdi */
+ int64_t start = timeval_ms();
while (!write_result.done || !read_result.done) {
struct timeval timeout_usb;
@@ -914,6 +916,11 @@ int mpsse_flush(struct mpsse_ctx *ctx)
break;
}
}
+
+ if (timeval_ms() - start > 2000) {
+ LOG_ERROR("Timed out handling USB events in mpsse_flush().");
+ break;
+ }
}
error_check:
diff --git a/src/jtag/drivers/stlink_usb.c b/src/jtag/drivers/stlink_usb.c
index 12e1175..ea3dc00 100644
--- a/src/jtag/drivers/stlink_usb.c
+++ b/src/jtag/drivers/stlink_usb.c
@@ -437,8 +437,7 @@ struct jtag_xfer {
struct libusb_transfer *transfer;
};
-static int jtag_libusb_bulk_transfer_n(
- jtag_libusb_device_handle * dev_handle,
+static int jtag_libusb_bulk_transfer_n(jtag_libusb_device_handle *dev_handle,
struct jtag_xfer *transfers,
size_t n_transfers,
int timeout)
diff --git a/src/rtos/Makefile.am b/src/rtos/Makefile.am
index 38adb6e..b3e14f8 100644
--- a/src/rtos/Makefile.am
+++ b/src/rtos/Makefile.am
@@ -15,6 +15,7 @@ noinst_LTLIBRARIES += %D%/librtos.la
%D%/chromium-ec.c \
%D%/embKernel.c \
%D%/mqx.c \
+ %D%/riscv_debug.c \
%D%/uCOS-III.c \
%D%/nuttx.c \
%D%/hwthread.c \
@@ -26,7 +27,8 @@ noinst_LTLIBRARIES += %D%/librtos.la
%D%/rtos_embkernel_stackings.h \
%D%/rtos_mqx_stackings.h \
%D%/rtos_ucos_iii_stackings.h \
- %D%/nuttx_header.h
+ %D%/nuttx_header.h \
+ %D%/riscv_debug.h
%C%_librtos_la_CFLAGS = $(AM_CFLAGS)
diff --git a/src/rtos/hwthread.c b/src/rtos/hwthread.c
index 2d9e42f..90ba958 100644
--- a/src/rtos/hwthread.c
+++ b/src/rtos/hwthread.c
@@ -31,10 +31,13 @@
static bool hwthread_detect_rtos(struct target *target);
static int hwthread_create(struct target *target);
static int hwthread_update_threads(struct rtos *rtos);
+static int hwthread_get_thread_reg(struct rtos *rtos, int64_t thread_id,
+ uint32_t reg_num, struct rtos_reg *rtos_reg);
static int hwthread_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
- struct rtos_reg **reg_list, int *num_regs);
+ struct rtos_reg **reg_list, int *num_regs);
static int hwthread_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]);
static int hwthread_smp_init(struct target *target);
+int hwthread_set_reg(struct rtos *rtos, uint32_t reg_num, uint8_t *reg_value);
#define HW_THREAD_NAME_STR_SIZE (32)
@@ -51,8 +54,10 @@ const struct rtos_type hwthread_rtos = {
.create = hwthread_create,
.update_threads = hwthread_update_threads,
.get_thread_reg_list = hwthread_get_thread_reg_list,
+ .get_thread_reg = hwthread_get_thread_reg,
.get_symbol_list_to_lookup = hwthread_get_symbol_list_to_lookup,
.smp_init = hwthread_smp_init,
+ .set_reg = hwthread_set_reg,
};
struct hwthread_params {
@@ -87,13 +92,10 @@ static int hwthread_update_threads(struct rtos *rtos)
enum target_debug_reason current_reason = DBG_REASON_UNDEFINED;
if (rtos == NULL)
- return -1;
+ return ERROR_FAIL;
target = rtos->target;
- /* wipe out previous thread details if any */
- rtos_free_threadlist(rtos);
-
/* determine the number of "threads" */
if (target->smp) {
for (head = target->head; head != NULL; head = head->next) {
@@ -107,6 +109,11 @@ static int hwthread_update_threads(struct rtos *rtos)
} else
thread_list_size = 1;
+ /* Wipe out previous thread details if any, but preserve threadid. */
+ int64_t current_threadid = rtos->current_threadid;
+ rtos_free_threadlist(rtos);
+ rtos->current_threadid = current_threadid;
+
/* create space for new thread details */
rtos->thread_details = malloc(sizeof(struct thread_detail) * thread_list_size);
@@ -201,64 +208,114 @@ static int hwthread_smp_init(struct target *target)
return hwthread_update_threads(target->rtos);
}
-static int hwthread_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
- struct rtos_reg **rtos_reg_list, int *num_regs)
+static struct target *find_thread(struct target *target, int64_t thread_id)
{
- struct target_list *head;
- struct target *target;
- struct target *curr;
- struct reg **reg_list;
- int retval;
-
- if (rtos == NULL)
- return ERROR_FAIL;
-
- target = rtos->target;
-
/* Find the thread with that thread_id */
+ if (target == NULL)
+ return NULL;
if (target->smp) {
- curr = NULL;
- for (head = target->head; head != NULL; head = head->next) {
- curr = head->target;
-
- if (thread_id == threadid_from_target(curr))
- break;
+ for (struct target_list *head = target->head; head != NULL; head = head->next) {
+ if (thread_id == threadid_from_target(head->target))
+ return head->target;
}
+ } else if (thread_id == threadid_from_target(target)) {
+ return target;
+ }
+ return NULL;
+}
- if (head == NULL)
- return ERROR_FAIL;
- } else {
- curr = target;
- if (thread_id != threadid_from_target(curr))
- return ERROR_FAIL;
+static int hwthread_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
+ struct rtos_reg **rtos_reg_list, int *rtos_reg_list_size)
+{
+ if (rtos == NULL)
+ return ERROR_FAIL;
- }
+ struct target *target = rtos->target;
+
+ struct target *curr = find_thread(target, thread_id);
+ if (curr == NULL)
+ return ERROR_FAIL;
if (!target_was_examined(curr))
return ERROR_FAIL;
- retval = target_get_gdb_reg_list(curr, &reg_list, num_regs,
+ struct reg **reg_list;
+ int retval = target_get_gdb_reg_list(curr, &reg_list, rtos_reg_list_size,
REG_CLASS_GENERAL);
if (retval != ERROR_OK)
return retval;
- *rtos_reg_list = calloc(*num_regs, sizeof(struct rtos_reg));
+ *rtos_reg_list = calloc(*rtos_reg_list_size, sizeof(struct rtos_reg));
if (*rtos_reg_list == NULL) {
free(reg_list);
return ERROR_FAIL;
}
- for (int i = 0; i < *num_regs; i++) {
+ for (int i = 0; i < *rtos_reg_list_size; i++) {
(*rtos_reg_list)[i].number = (*reg_list)[i].number;
(*rtos_reg_list)[i].size = (*reg_list)[i].size;
memcpy((*rtos_reg_list)[i].value, (*reg_list)[i].value,
- ((*reg_list)[i].size + 7) / 8);
+ ((*reg_list)[i].size + 7) / 8);
}
-
free(reg_list);
return ERROR_OK;
+}
+
+static int hwthread_get_thread_reg(struct rtos *rtos, int64_t thread_id,
+ uint32_t reg_num, struct rtos_reg *rtos_reg)
+{
+ if (rtos == NULL)
+ return ERROR_FAIL;
+
+ struct target *target = rtos->target;
+
+ struct target *curr = find_thread(target, thread_id);
+ if (curr == NULL) {
+ LOG_ERROR("Couldn't find RTOS thread for id %" PRId64 ".", thread_id);
+ return ERROR_FAIL;
+ }
+
+ if (!target_was_examined(curr)) {
+ LOG_ERROR("Target %d hasn't been examined yet.", curr->coreid);
+ return ERROR_FAIL;
+ }
+
+ struct reg *reg = register_get_by_number(curr->reg_cache, reg_num, true);
+ if (!reg) {
+ LOG_ERROR("Couldn't find register %d in thread %" PRId64 ".", reg_num,
+ thread_id);
+ return ERROR_FAIL;
+ }
+
+ if (reg->type->get(reg) != ERROR_OK)
+ return ERROR_FAIL;
+
+ rtos_reg->number = reg->number;
+ rtos_reg->size = reg->size;
+ unsigned bytes = (reg->size + 7) / 8;
+ assert(bytes <= sizeof(rtos_reg->value));
+ memcpy(rtos_reg->value, reg->value, bytes);
+
+ return ERROR_OK;
+}
+int hwthread_set_reg(struct rtos *rtos, uint32_t reg_num, uint8_t *reg_value)
+{
+ if (rtos == NULL)
+ return ERROR_FAIL;
+
+ struct target *target = rtos->target;
+
+ struct target *curr = find_thread(target, rtos->current_thread);
+ if (curr == NULL)
+ return ERROR_FAIL;
+
+ struct reg *reg = register_get_by_number(curr->reg_cache, reg_num, true);
+ if (!reg)
+ return ERROR_FAIL;
+
+ return reg->type->set(reg, reg_value);
}
static int hwthread_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[])
@@ -272,26 +329,10 @@ static int hwthread_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]
static int hwthread_target_for_threadid(struct connection *connection, int64_t thread_id, struct target **p_target)
{
struct target *target = get_target_from_connection(connection);
- struct target_list *head;
- struct target *curr;
- if (target->smp) {
- /* Find the thread with that thread_id */
- curr = NULL;
- for (head = target->head; head != NULL; head = head->next) {
- curr = head->target;
-
- if (thread_id == threadid_from_target(curr))
- break;
- }
-
- if (head == NULL)
- return ERROR_FAIL;
- } else {
- curr = target;
- if (thread_id != threadid_from_target(curr))
- return ERROR_FAIL;
- }
+ struct target *curr = find_thread(target, thread_id);
+ if (curr == NULL)
+ return ERROR_FAIL;
*p_target = curr;
@@ -312,6 +353,7 @@ static int hwthread_thread_packet(struct connection *connection, const char *pac
int64_t current_threadid;
if (packet[0] == 'H' && packet[1] == 'g') {
+ /* Never reached, because this case is handled by rtos_thread_packet(). */
sscanf(packet, "Hg%16" SCNx64, &current_threadid);
if (current_threadid > 0) {
diff --git a/src/rtos/riscv_debug.c b/src/rtos/riscv_debug.c
new file mode 100644
index 0000000..726f326
--- /dev/null
+++ b/src/rtos/riscv_debug.c
@@ -0,0 +1,352 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "riscv_debug.h"
+#include "target/register.h"
+#include "target/target.h"
+#include "target/riscv/riscv.h"
+#include "server/gdb_server.h"
+#include "helper/binarybuffer.h"
+
+static int riscv_gdb_thread_packet(struct connection *connection, const char *packet, int packet_size);
+static int riscv_gdb_v_packet(struct connection *connection, const char *packet, int packet_size);
+
+static bool riscv_detect_rtos(struct target *target)
+{
+ LOG_ERROR("riscv_detect_rtos() unimplemented");
+ return -1;
+}
+
+static int riscv_create_rtos(struct target *target)
+{
+ LOG_DEBUG("RISC-V Debug 'RTOS' created: this doesn't mean you're running an RTOS, just that you have multi-hart support on RISC-V");
+
+ struct riscv_rtos *r = calloc(1, sizeof(*r));
+ target->rtos->rtos_specific_params = r;
+
+ target->rtos->current_threadid = 1;
+ target->rtos->current_thread = 1;
+
+ target->rtos->gdb_thread_packet = riscv_gdb_thread_packet;
+ target->rtos->gdb_v_packet = riscv_gdb_v_packet;
+
+ return JIM_OK;
+}
+
+int riscv_update_threads(struct rtos *rtos)
+{
+ LOG_DEBUG("Updating the RISC-V Hart List");
+
+ struct target *target = rtos->target;
+
+ /* Figures out how many harts there are on the system. */
+ int hart_count = riscv_count_harts(rtos->target);
+ if (rtos->thread_count != hart_count) {
+ rtos_free_threadlist(rtos);
+ rtos->thread_count = hart_count;
+ rtos->thread_details = calloc(rtos->thread_count, sizeof(*rtos->thread_details));
+ for (int i = 0; i < rtos->thread_count; ++i) {
+ LOG_DEBUG(" Setting up Hart %d", i);
+ rtos->thread_details[i].threadid = i + 1;
+ rtos->thread_details[i].exists = true;
+ if (asprintf(&rtos->thread_details[i].thread_name_str, "Hart %d", i) < 0)
+ LOG_ERROR("riscv_update_threads() failed asprintf");
+ if (asprintf(&rtos->thread_details[i].extra_info_str, "RV%d",
+ riscv_xlen_of_hart(target, i)) < 0)
+ LOG_ERROR("riscv_update_threads() failed asprintf");
+ }
+ }
+ return JIM_OK;
+}
+
+static int riscv_gdb_thread_packet(struct connection *connection, const char *packet, int packet_size)
+{
+ struct target *target = get_target_from_connection(connection);
+ struct rtos *rtos = target->rtos;
+ struct riscv_rtos *r = (struct riscv_rtos *)(target->rtos->rtos_specific_params);
+
+ char *packet_stttrr = malloc(packet_size + 1);
+ memset(packet_stttrr, '\0', packet_size + 1);
+ memcpy(packet_stttrr, packet, packet_size);
+ LOG_DEBUG("handling packet '%s'", packet_stttrr);
+
+ switch (packet[0]) {
+ case 'q':
+ if (strncmp(packet, "qfThreadInfo", 12) == 0) {
+ riscv_update_threads(target->rtos);
+ r->qs_thread_info_offset = 1;
+
+ char m[16];
+ snprintf(m, 16, "m%08x", (int)rtos->thread_details[0].threadid);
+ gdb_put_packet(connection, m, strlen(m));
+ return ERROR_OK;
+ }
+
+ if (strncmp(packet, "qsThreadInfo", 12) == 0) {
+ if (r->qs_thread_info_offset >= rtos->thread_count) {
+ gdb_put_packet(connection, "l", 1);
+ return ERROR_OK;
+ }
+
+ int tid = r->qs_thread_info_offset++;
+ char m[16];
+ snprintf(m, 16, "m%08x", (int)rtos->thread_details[tid].threadid);
+ gdb_put_packet(connection, m, strlen(m));
+ return ERROR_OK;
+ }
+
+ if (strncmp(packet, "qAttached", 9) == 0) {
+ gdb_put_packet(connection, "1", 1);
+ return ERROR_OK;
+ }
+
+ if (strncmp(packet, "qThreadExtraInfo", 16) == 0) {
+ char tid_str[32];
+ memcpy(tid_str, packet + 17, packet_size - 17);
+ tid_str[packet_size - 17] = '\0';
+ char *end;
+ int tid = strtol(tid_str, &end, 16);
+ if (*end != '\0') {
+ LOG_ERROR("Got qThreadExtraInfo with non-numeric TID: '%s'", tid_str);
+ gdb_put_packet(connection, NULL, 0);
+ return ERROR_FAIL;
+ }
+
+ char m[16];
+ snprintf(m, 16, "hart %d", tid);
+ char h[33];
+ h[0] = '\0';
+ for (size_t i = 0; i < strlen(m); ++i) {
+ char byte[3];
+ snprintf(byte, 3, "%02x", m[i]);
+ strncat(h, byte, 32);
+ }
+ gdb_put_packet(connection, h, strlen(h));
+ return ERROR_OK;
+ }
+
+ if (strcmp(packet, "qTStatus") == 0) {
+ gdb_put_packet(connection, "T0", 2);
+ return ERROR_OK;
+ }
+
+ if (strcmp(packet, "qC") == 0) {
+ char rep_str[32];
+ snprintf(rep_str, 32, "QC%" PRIx64, rtos->current_threadid);
+ gdb_put_packet(connection, rep_str, strlen(rep_str));
+ return ERROR_OK;
+ }
+
+ return GDB_THREAD_PACKET_NOT_CONSUMED;
+
+ case 'Q':
+ return GDB_THREAD_PACKET_NOT_CONSUMED;
+
+ case 'H':
+ /* ‘H op thread-id’
+ *
+ * Set thread for subsequent operations (‘m’, ‘M’, ‘g’, ‘G’,
+ * et.al.). Depending on the operation to be performed, op
+ * should be ‘c’ for step and continue operations (note that
+ * this is deprecated, supporting the ‘vCont’ command is a
+ * better option), and ‘g’ for other operations. The thread
+ * designator thread-id has the format and interpretation
+ * described in thread-id syntax.
+ *
+ * Reply:
+ * ‘OK’ for success
+ * ‘E NN’ for an error
+ */
+ {
+ char tid_str[32];
+ memcpy(tid_str, packet + 2, packet_size - 2);
+ tid_str[packet_size - 2] = '\0';
+ char *entptr;
+ int tid = strtol(tid_str, &entptr, 16);
+ if (*entptr != '\0') {
+ LOG_ERROR("Got H packet, but without integer: %s", tid_str);
+ return GDB_THREAD_PACKET_NOT_CONSUMED;
+ }
+
+ switch (tid) {
+ case 0:
+ case -1:
+ riscv_set_all_rtos_harts(target);
+ break;
+ default:
+ riscv_set_rtos_hartid(target, tid - 1);
+ rtos->current_threadid = tid;
+ break;
+ }
+
+ switch (packet[1]) {
+ case 'g':
+ case 'c':
+ gdb_put_packet(connection, "OK", 2);
+ return ERROR_OK;
+ default:
+ LOG_ERROR("Unknown H packet subtype %2x\n", packet[1]);
+ gdb_put_packet(connection, NULL, 0);
+ return ERROR_FAIL;
+ }
+ }
+
+ case 'T':
+ {
+ char tid_str[32];
+ memcpy(tid_str, packet + 1, packet_size - 1);
+ tid_str[packet_size - 1] = '\0';
+ char *end;
+ int tid = strtol(tid_str, &end, 16);
+ if (*end != '\0') {
+ LOG_ERROR("T packet with non-numeric tid %s", tid_str);
+ gdb_put_packet(connection, NULL, 0);
+ return ERROR_FAIL;
+ }
+
+ riscv_update_threads(target->rtos);
+ if (tid <= target->rtos->thread_count) {
+ gdb_put_packet(connection, "OK", 2);
+ return ERROR_OK;
+ } else {
+ gdb_put_packet(connection, "E00", 3);
+ return ERROR_OK;
+ }
+ }
+
+ case 'c':
+ case 's':
+ target->state = TARGET_HALTED;
+ return JIM_OK;
+
+ case 'R':
+ gdb_put_packet(connection, "E00", 3);
+ return JIM_OK;
+
+ default:
+ LOG_ERROR("Unknown packet of type 0x%2.2x", packet[0]);
+ gdb_put_packet(connection, NULL, 0);
+ return JIM_OK;
+ }
+}
+
+static int riscv_gdb_v_packet(struct connection *connection, const char *packet, int packet_size)
+{
+ char *packet_stttrr = malloc(packet_size + 1);
+ memset(packet_stttrr, '\0', packet_size + 1);
+ memcpy(packet_stttrr, packet, packet_size);
+ LOG_DEBUG("handling packet '%s'", packet_stttrr);
+
+ struct target *target = get_target_from_connection(connection);
+
+ if (strcmp(packet_stttrr, "vCont?") == 0) {
+ static const char *message = "OK";
+ gdb_put_packet(connection, (char *)message, strlen(message));
+ return JIM_OK;
+ }
+
+ int threadid;
+ if (sscanf(packet_stttrr, "vCont;s:%d;c", &threadid) == 1) {
+ riscv_set_rtos_hartid(target, threadid - 1);
+ riscv_step_rtos_hart(target);
+
+ gdb_put_packet(connection, "S05", 3);
+ return JIM_OK;
+ }
+
+ if (strcmp(packet_stttrr, "vCont;c") == 0) {
+ target_call_event_callbacks(target, TARGET_EVENT_GDB_START);
+ target_call_event_callbacks(target, TARGET_EVENT_RESUME_START);
+ riscv_set_all_rtos_harts(target);
+ riscv_resume(target, 1, 0, 0, 0);
+ target->state = TARGET_RUNNING;
+ gdb_set_frontend_state_running(connection);
+ target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
+ target_call_event_callbacks(target, TARGET_EVENT_RESUME_END);
+ return JIM_OK;
+ }
+
+ if (strncmp(packet_stttrr, "vCont", 5) == 0)
+ LOG_ERROR("Got unknown vCont-type packet");
+
+ return GDB_THREAD_PACKET_NOT_CONSUMED;
+}
+
+static int riscv_get_thread_reg(struct rtos *rtos, int64_t thread_id,
+ uint32_t reg_num, struct rtos_reg *rtos_reg)
+{
+ LOG_DEBUG("thread_id=%" PRId64 ", reg_num=%d", thread_id, reg_num);
+
+ struct target *target = rtos->target;
+ struct reg *reg = register_get_by_number(target->reg_cache, reg_num, true);
+ if (!reg)
+ return ERROR_FAIL;
+
+ uint64_t reg_value = 0;
+ if (riscv_get_register_on_hart(rtos->target, &reg_value, thread_id - 1,
+ reg_num) != ERROR_OK)
+ return ERROR_FAIL;
+
+ buf_set_u64(rtos_reg->value, 0, 64, reg_value);
+ rtos_reg->number = reg->number;
+ rtos_reg->size = reg->size;
+ return ERROR_OK;
+}
+
+static int riscv_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
+ struct rtos_reg **reg_list, int *num_regs)
+{
+ LOG_DEBUG("Updating RISC-V register list for hart %d", (int)(thread_id - 1));
+
+ /* We return just the GPRs here. */
+
+ *num_regs = 33;
+ int xlen = riscv_xlen_of_hart(rtos->target, thread_id - 1);
+
+ *reg_list = calloc(*num_regs, sizeof(struct rtos_reg));
+ for (int i = 0; i < *num_regs; ++i) {
+ uint64_t reg_value;
+ if (riscv_get_register_on_hart(rtos->target, &reg_value, thread_id - 1,
+ i) != ERROR_OK)
+ return JIM_ERR;
+
+ (*reg_list)[i].number = i;
+ (*reg_list)[i].size = xlen;
+ buf_set_u64((*reg_list)[i].value, 0, 64, reg_value);
+ }
+ return JIM_OK;
+}
+
+static int riscv_set_reg(struct rtos *rtos, uint32_t reg_num,
+ uint8_t *reg_value)
+{
+ struct target *target = rtos->target;
+ struct reg *reg = register_get_by_number(target->reg_cache, reg_num, true);
+ if (!reg)
+ return ERROR_FAIL;
+
+ int hartid = rtos->current_threadid - 1;
+ uint64_t value = buf_get_u64(reg_value, 0, reg->size);
+
+ return riscv_set_register_on_hart(target, hartid, reg_num, value);
+}
+
+static int riscv_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[])
+{
+ *symbol_list = calloc(1, sizeof(symbol_table_elem_t));
+ (*symbol_list)[0].symbol_name = NULL;
+ (*symbol_list)[0].optional = false;
+ return JIM_OK;
+}
+
+const struct rtos_type riscv_rtos = {
+ .name = "riscv",
+ .detect_rtos = riscv_detect_rtos,
+ .create = riscv_create_rtos,
+ .update_threads = riscv_update_threads,
+ .get_thread_reg = riscv_get_thread_reg,
+ .get_thread_reg_list = riscv_get_thread_reg_list,
+ .get_symbol_list_to_lookup = riscv_get_symbol_list_to_lookup,
+ .set_reg = riscv_set_reg,
+};
diff --git a/src/rtos/riscv_debug.h b/src/rtos/riscv_debug.h
new file mode 100644
index 0000000..539edf2
--- /dev/null
+++ b/src/rtos/riscv_debug.h
@@ -0,0 +1,13 @@
+#ifndef RTOS__RISCV_H
+#define RTOS__RISCV_H
+
+#include "rtos.h"
+
+struct riscv_rtos {
+ /* The index into the thread list used to handle */
+ int qs_thread_info_offset;
+};
+
+int riscv_update_threads(struct rtos *rtos);
+
+#endif
diff --git a/src/rtos/rtos.c b/src/rtos/rtos.c
index da0a503..d4eff5b 100644
--- a/src/rtos/rtos.c
+++ b/src/rtos/rtos.c
@@ -38,6 +38,7 @@ extern struct rtos_type mqx_rtos;
extern struct rtos_type uCOS_III_rtos;
extern struct rtos_type nuttx_rtos;
extern struct rtos_type hwthread_rtos;
+extern struct rtos_type riscv_rtos;
static struct rtos_type *rtos_types[] = {
&ThreadX_rtos,
@@ -51,6 +52,7 @@ static struct rtos_type *rtos_types[] = {
&uCOS_III_rtos,
&nuttx_rtos,
&hwthread_rtos,
+ &riscv_rtos,
NULL
};
@@ -87,6 +89,7 @@ static int os_alloc(struct target *target, struct rtos_type *ostype)
/* RTOS drivers can override the packet handler in _create(). */
os->gdb_thread_packet = rtos_thread_packet;
+ os->gdb_v_packet = NULL;
os->gdb_target_for_threadid = rtos_target_for_threadid;
return JIM_OK;
@@ -462,6 +465,7 @@ static int rtos_put_gdb_reg_list(struct connection *connection,
return ERROR_OK;
}
+/** Look through all registers to find this register. */
int rtos_get_gdb_reg(struct connection *connection, int reg_num)
{
struct target *target = get_target_from_connection(connection);
@@ -473,19 +477,31 @@ int rtos_get_gdb_reg(struct connection *connection, int reg_num)
struct rtos_reg *reg_list;
int num_regs;
- LOG_DEBUG("RTOS: getting register %d for thread 0x%" PRIx64
- ", target->rtos->current_thread=0x%" PRIx64 "\r\n",
+ LOG_DEBUG("getting register %d for thread 0x%" PRIx64
+ ", target->rtos->current_thread=0x%" PRIx64,
reg_num,
current_threadid,
target->rtos->current_thread);
- int retval = target->rtos->type->get_thread_reg_list(target->rtos,
- current_threadid,
- &reg_list,
- &num_regs);
- if (retval != ERROR_OK) {
- LOG_ERROR("RTOS: failed to get register list");
- return retval;
+ int retval;
+ if (target->rtos->type->get_thread_reg) {
+ reg_list = calloc(1, sizeof(*reg_list));
+ num_regs = 1;
+ retval = target->rtos->type->get_thread_reg(target->rtos,
+ current_threadid, reg_num, &reg_list[0]);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("RTOS: failed to get register %d", reg_num);
+ return retval;
+ }
+ } else {
+ retval = target->rtos->type->get_thread_reg_list(target->rtos,
+ current_threadid,
+ &reg_list,
+ &num_regs);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("RTOS: failed to get register list");
+ return retval;
+ }
}
for (int i = 0; i < num_regs; ++i) {
@@ -501,6 +517,7 @@ int rtos_get_gdb_reg(struct connection *connection, int reg_num)
return ERROR_FAIL;
}
+/** Return a list of general registers. */
int rtos_get_gdb_reg_list(struct connection *connection)
{
struct target *target = get_target_from_connection(connection);
@@ -534,6 +551,20 @@ int rtos_get_gdb_reg_list(struct connection *connection)
return ERROR_FAIL;
}
+int rtos_set_reg(struct connection *connection, int reg_num,
+ uint8_t *reg_value)
+{
+ struct target *target = get_target_from_connection(connection);
+ int64_t current_threadid = target->rtos->current_threadid;
+ if ((target->rtos != NULL) &&
+ (target->rtos->type->set_reg != NULL) &&
+ (current_threadid != -1) &&
+ (current_threadid != 0)) {
+ return target->rtos->type->set_reg(target->rtos, reg_num, reg_value);
+ }
+ return ERROR_FAIL;
+}
+
int rtos_generic_stack_read(struct target *target,
const struct rtos_register_stacking *stacking,
int64_t stack_ptr,
diff --git a/src/rtos/rtos.h b/src/rtos/rtos.h
index b346047..81829b1 100644
--- a/src/rtos/rtos.h
+++ b/src/rtos/rtos.h
@@ -20,6 +20,7 @@
#define OPENOCD_RTOS_RTOS_H
#include "server/server.h"
+#include "target/target.h"
#include <jim-nvp.h>
typedef int64_t threadid_t;
@@ -49,11 +50,14 @@ struct rtos {
symbol_table_elem_t *symbols;
struct target *target;
/* add a context variable instead of global variable */
+ /* The thread currently selected by gdb. */
int64_t current_threadid;
+ /* The currently selected thread according to the target. */
threadid_t current_thread;
struct thread_detail *thread_details;
int thread_count;
int (*gdb_thread_packet)(struct connection *connection, char const *packet, int packet_size);
+ int (*gdb_v_packet)(struct connection *connection, char const *packet, int packet_size);
int (*gdb_target_for_threadid)(struct connection *connection, int64_t thread_id, struct target **p_target);
void *rtos_specific_params;
};
@@ -70,11 +74,15 @@ struct rtos_type {
int (*create)(struct target *target);
int (*smp_init)(struct target *target);
int (*update_threads)(struct rtos *rtos);
+ /** Return a list of general registers, with their values filled out. */
int (*get_thread_reg_list)(struct rtos *rtos, int64_t thread_id,
struct rtos_reg **reg_list, int *num_regs);
+ int (*get_thread_reg)(struct rtos *rtos, int64_t thread_id,
+ uint32_t reg_num, struct rtos_reg *reg);
int (*get_symbol_list_to_lookup)(symbol_table_elem_t *symbol_list[]);
int (*clean)(struct target *target);
char * (*ps_command)(struct target *target);
+ int (*set_reg)(struct rtos *rtos, uint32_t reg_num, uint8_t *reg_value);
};
struct stack_register_offset {
@@ -104,6 +112,8 @@ struct rtos_register_stacking {
#define GDB_THREAD_PACKET_NOT_CONSUMED (-40)
int rtos_create(Jim_GetOptInfo *goi, struct target *target);
+int rtos_set_reg(struct connection *connection, int reg_num,
+ uint8_t *reg_value);
int rtos_generic_stack_read(struct target *target,
const struct rtos_register_stacking *stacking,
int64_t stack_ptr,
diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c
index 3ade195..2961e70 100644
--- a/src/server/gdb_server.c
+++ b/src/server/gdb_server.c
@@ -392,7 +392,7 @@ static int gdb_put_packet_inner(struct connection *connection,
break;
}
- LOG_WARNING("Discard unexpected char %c", reply);
+ LOG_DEBUG("Discard unexpected char %c", reply);
}
#endif
@@ -724,7 +724,7 @@ static int gdb_output(struct command_context *context, const char *line)
static void gdb_signal_reply(struct target *target, struct connection *connection)
{
struct gdb_connection *gdb_connection = connection->priv;
- char sig_reply[45];
+ char sig_reply[65];
char stop_reason[20];
char current_thread[25];
int sig_reply_len;
@@ -735,17 +735,26 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio
if (target->debug_reason == DBG_REASON_EXIT) {
sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "W00");
} else {
+ struct target *ct;
+ if (target->rtos != NULL) {
+ target->rtos->current_threadid = target->rtos->current_thread;
+ LOG_DEBUG("current_threadid=%" PRId64, target->rtos->current_threadid);
+ target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &ct);
+ } else {
+ ct = target;
+ }
+
if (gdb_connection->ctrl_c) {
signal_var = 0x2;
} else
- signal_var = gdb_last_signal(target);
+ signal_var = gdb_last_signal(ct);
stop_reason[0] = '\0';
- if (target->debug_reason == DBG_REASON_WATCHPOINT) {
+ if (ct->debug_reason == DBG_REASON_WATCHPOINT) {
enum watchpoint_rw hit_wp_type;
target_addr_t hit_wp_address;
- if (watchpoint_hit(target, &hit_wp_type, &hit_wp_address) == ERROR_OK) {
+ if (watchpoint_hit(ct, &hit_wp_type, &hit_wp_address) == ERROR_OK) {
switch (hit_wp_type) {
case WPT_WRITE:
@@ -767,15 +776,9 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio
}
current_thread[0] = '\0';
- if (target->rtos != NULL) {
- struct target *ct;
- snprintf(current_thread, sizeof(current_thread), "thread:%016" PRIx64 ";",
+ if (target->rtos != NULL)
+ snprintf(current_thread, sizeof(current_thread), "thread:%" PRIx64 ";",
target->rtos->current_thread);
- target->rtos->current_threadid = target->rtos->current_thread;
- target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &ct);
- if (!gdb_connection->ctrl_c)
- signal_var = gdb_last_signal(ct);
- }
sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "T%2.2x%s%s",
signal_var, stop_reason, current_thread);
@@ -1306,7 +1309,7 @@ static int gdb_get_register_packet(struct connection *connection,
if ((target->rtos != NULL) && (ERROR_OK == rtos_get_gdb_reg(connection, reg_num)))
return ERROR_OK;
- retval = target_get_gdb_reg_list(target, &reg_list, &reg_list_size,
+ retval = target_get_gdb_reg_list_noread(target, &reg_list, &reg_list_size,
REG_CLASS_ALL);
if (retval != ERROR_OK)
return gdb_error(connection, retval);
@@ -1342,37 +1345,49 @@ static int gdb_set_register_packet(struct connection *connection,
{
struct target *target = get_target_from_connection(connection);
char *separator;
- uint8_t *bin_buf;
int reg_num = strtoul(packet + 1, &separator, 16);
struct reg **reg_list;
int reg_list_size;
int retval;
+#ifdef _DEBUG_GDB_IO_
LOG_DEBUG("-");
+#endif
- retval = target_get_gdb_reg_list(target, &reg_list, &reg_list_size,
+ if (*separator != '=') {
+ LOG_ERROR("GDB 'set register packet', but no '=' following the register number");
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
+ size_t chars = strlen(separator + 1);
+ uint8_t *bin_buf = malloc(chars / 2);
+ gdb_target_to_reg(target, separator + 1, chars, bin_buf);
+
+ if ((target->rtos != NULL) &&
+ (ERROR_OK == rtos_set_reg(connection, reg_num, bin_buf))) {
+ free(bin_buf);
+ gdb_put_packet(connection, "OK", 2);
+ return ERROR_OK;
+ }
+
+ retval = target_get_gdb_reg_list_noread(target, &reg_list, &reg_list_size,
REG_CLASS_ALL);
- if (retval != ERROR_OK)
+ if (retval != ERROR_OK) {
+ free(bin_buf);
return gdb_error(connection, retval);
+ }
if (reg_list_size <= reg_num) {
LOG_ERROR("gdb requested a non-existing register");
+ free(bin_buf);
+ free(reg_list);
return ERROR_SERVER_REMOTE_CLOSED;
}
- if (*separator != '=') {
- LOG_ERROR("GDB 'set register packet', but no '=' following the register number");
- return ERROR_SERVER_REMOTE_CLOSED;
- }
-
- /* convert from GDB-string (target-endian) to hex-string (big-endian) */
- bin_buf = malloc(DIV_ROUND_UP(reg_list[reg_num]->size, 8));
- int chars = (DIV_ROUND_UP(reg_list[reg_num]->size, 8) * 2);
-
- if ((unsigned int)chars != strlen(separator + 1)) {
- LOG_ERROR("gdb sent %zu bits for a %d-bit register (%s)",
- strlen(separator + 1) * 4, chars * 4, reg_list[reg_num]->name);
+ if (chars != (DIV_ROUND_UP(reg_list[reg_num]->size, 8) * 2)) {
+ LOG_ERROR("gdb sent %d bits for a %d-bit register (%s)",
+ (int) chars * 4, reg_list[reg_num]->size, reg_list[reg_num]->name);
free(bin_buf);
+ free(reg_list);
return ERROR_SERVER_REMOTE_CLOSED;
}
@@ -1642,7 +1657,7 @@ static int gdb_breakpoint_watchpoint_packet(struct connection *connection,
char *separator;
int retval;
- LOG_DEBUG("-");
+ LOG_DEBUG("[%d]", target->coreid);
type = strtoul(packet + 1, &separator, 16);
@@ -2208,7 +2223,7 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o
arch_defined_types = calloc(1, sizeof(char *));
- retval = target_get_gdb_reg_list(target, &reg_list,
+ retval = target_get_gdb_reg_list_noread(target, &reg_list,
&reg_list_size, REG_CLASS_ALL);
if (retval != ERROR_OK) {
@@ -2396,10 +2411,11 @@ static int gdb_target_description_supported(struct target *target, int *supporte
char const *architecture = target_get_gdb_arch(target);
- retval = target_get_gdb_reg_list(target, &reg_list,
+ retval = target_get_gdb_reg_list_noread(target, &reg_list,
&reg_list_size, REG_CLASS_ALL);
if (retval != ERROR_OK) {
LOG_ERROR("get register list failed");
+ reg_list = NULL;
goto error;
}
@@ -2887,10 +2903,14 @@ static int gdb_v_packet(struct connection *connection,
char const *packet, int packet_size)
{
struct gdb_connection *gdb_connection = connection->priv;
- struct target *target;
int result;
- target = get_target_from_connection(connection);
+ struct target *target = get_target_from_connection(connection);
+ if (target->rtos != NULL && target->rtos->gdb_v_packet != NULL) {
+ int out = target->rtos->gdb_v_packet(connection, packet, packet_size);
+ if (out != GDB_THREAD_PACKET_NOT_CONSUMED)
+ return out;
+ }
if (strncmp(packet, "vCont", 5) == 0) {
bool handled;
@@ -3710,6 +3730,12 @@ int gdb_register_commands(struct command_context *cmd_ctx)
return register_commands(cmd_ctx, NULL, gdb_command_handlers);
}
+void gdb_set_frontend_state_running(struct connection *connection)
+{
+ struct gdb_connection *gdb_con = connection->priv;
+ gdb_con->frontend_state = TARGET_RUNNING;
+}
+
void gdb_service_free(void)
{
free(gdb_port);
diff --git a/src/server/gdb_server.h b/src/server/gdb_server.h
index 993984b..0c50836 100644
--- a/src/server/gdb_server.h
+++ b/src/server/gdb_server.h
@@ -46,6 +46,8 @@ static inline struct target *get_target_from_connection(struct connection *conne
return gdb_service->target;
}
+void gdb_set_frontend_state_running(struct connection *connection);
+
#define ERROR_GDB_BUFFER_TOO_SMALL (-800)
#define ERROR_GDB_TIMEOUT (-801)
diff --git a/src/target/breakpoints.c b/src/target/breakpoints.c
index 7ad1942..b23b37f 100644
--- a/src/target/breakpoints.c
+++ b/src/target/breakpoints.c
@@ -98,7 +98,9 @@ fail:
return retval;
}
- LOG_DEBUG("added %s breakpoint at " TARGET_ADDR_FMT " of length 0x%8.8x, (BPID: %" PRIu32 ")",
+ LOG_DEBUG("[%d] added %s breakpoint at " TARGET_ADDR_FMT
+ " of length 0x%8.8x, (BPID: %" PRIu32 ")",
+ target->coreid,
breakpoint_type_strings[(*breakpoint_p)->type],
(*breakpoint_p)->address, (*breakpoint_p)->length,
(*breakpoint_p)->unique_id);
@@ -387,8 +389,8 @@ struct breakpoint *breakpoint_find(struct target *target, target_addr_t address)
return NULL;
}
-int watchpoint_add(struct target *target, target_addr_t address, uint32_t length,
- enum watchpoint_rw rw, uint32_t value, uint32_t mask)
+int watchpoint_add_internal(struct target *target, target_addr_t address,
+ uint32_t length, enum watchpoint_rw rw, uint32_t value, uint32_t mask)
{
struct watchpoint *watchpoint = target->watchpoints;
struct watchpoint **watchpoint_p = &target->watchpoints;
@@ -453,6 +455,29 @@ bye:
return ERROR_OK;
}
+int watchpoint_add(struct target *target, target_addr_t address,
+ uint32_t length, enum watchpoint_rw rw, uint32_t value, uint32_t mask)
+{
+ int retval = ERROR_OK;
+ if (target->smp) {
+ struct target_list *head;
+ struct target *curr;
+ head = target->head;
+
+ while (head != (struct target_list *)NULL) {
+ curr = head->target;
+ retval = watchpoint_add_internal(curr, address, length, rw, value,
+ mask);
+ if (retval != ERROR_OK)
+ return retval;
+ head = head->next;
+ }
+ return retval;
+ } else
+ return watchpoint_add_internal(target, address, length, rw, value,
+ mask);
+}
+
static void watchpoint_free(struct target *target, struct watchpoint *watchpoint_to_remove)
{
struct watchpoint *watchpoint = target->watchpoints;
@@ -474,7 +499,7 @@ static void watchpoint_free(struct target *target, struct watchpoint *watchpoint
free(watchpoint);
}
-void watchpoint_remove(struct target *target, target_addr_t address)
+int watchpoint_remove_internal(struct target *target, target_addr_t address)
{
struct watchpoint *watchpoint = target->watchpoints;
@@ -484,10 +509,32 @@ void watchpoint_remove(struct target *target, target_addr_t address)
watchpoint = watchpoint->next;
}
- if (watchpoint)
+ if (watchpoint) {
watchpoint_free(target, watchpoint);
- else
- LOG_ERROR("no watchpoint at address " TARGET_ADDR_FMT " found", address);
+ return 1;
+ } else {
+ if (!target->smp)
+ LOG_ERROR("no watchpoint at address " TARGET_ADDR_FMT " found", address);
+ return 0;
+ }
+}
+
+void watchpoint_remove(struct target *target, target_addr_t address)
+{
+ int found = 0;
+ if (target->smp) {
+ struct target_list *head;
+ struct target *curr;
+ head = target->head;
+ while (head != (struct target_list *)NULL) {
+ curr = head->target;
+ found += watchpoint_remove_internal(curr, address);
+ head = head->next;
+ }
+ if (found == 0)
+ LOG_ERROR("no watchpoint at address " TARGET_ADDR_FMT " found", address);
+ } else
+ watchpoint_remove_internal(target, address);
}
void watchpoint_clear_target(struct target *target)
diff --git a/src/target/register.c b/src/target/register.c
index 5352d2f..4ddda6e 100644
--- a/src/target/register.c
+++ b/src/target/register.c
@@ -36,6 +36,29 @@
* may be separate registers associated with debug or trace modules.
*/
+struct reg *register_get_by_number(struct reg_cache *first,
+ uint32_t reg_num, bool search_all)
+{
+ unsigned i;
+ struct reg_cache *cache = first;
+
+ while (cache) {
+ for (i = 0; i < cache->num_regs; i++) {
+ if (cache->reg_list[i].exist == false)
+ continue;
+ if (cache->reg_list[i].number == reg_num)
+ return &(cache->reg_list[i]);
+ }
+
+ if (search_all)
+ cache = cache->next;
+ else
+ break;
+ }
+
+ return NULL;
+}
+
struct reg *register_get_by_name(struct reg_cache *first,
const char *name, bool search_all)
{
diff --git a/src/target/register.h b/src/target/register.h
index 32c1f39..7c53d6e 100644
--- a/src/target/register.h
+++ b/src/target/register.h
@@ -159,6 +159,8 @@ struct reg_arch_type {
int (*set)(struct reg *reg, uint8_t *buf);
};
+struct reg *register_get_by_number(struct reg_cache *first,
+ uint32_t reg_num, bool search_all);
struct reg *register_get_by_name(struct reg_cache *first,
const char *name, bool search_all);
struct reg_cache **register_get_last_cache_p(struct reg_cache **first);
diff --git a/src/target/riscv/debug_defines.h b/src/target/riscv/debug_defines.h
index d6ddd4f..e6c2c5d 100644
--- a/src/target/riscv/debug_defines.h
+++ b/src/target/riscv/debug_defines.h
@@ -84,8 +84,7 @@
/*
* 0: Version described in spec version 0.11.
*
-* 1: Version described in spec version 0.13 (and later?), which
-* reduces the DMI data width to 32 bits.
+* 1: Version described in spec version 0.13.
*
* 15: Version not described in any available version of this spec.
*/
@@ -134,7 +133,7 @@
* cleared by writing \Fdmireset in \Rdtmcs.
*
* This indicates that the DM itself responded with an error.
-* Note: there are no specified cases in which the DM would
+* There are no specified cases in which the DM would
* respond with an error, and DMI is not required to support
* returning errors.
*
@@ -145,11 +144,6 @@
* needs to give the target more TCK edges between Update-DR and
* Capture-DR. The simplest way to do that is to add extra transitions
* in Run-Test/Idle.
-*
-* (The DTM, DM, and/or component may be in different clock domains,
-* so synchronization may be required. Some relatively fixed number of
-* TCK ticks may be needed for the request to reach the DM, complete,
-* and for the response to be synchronized back into the TCK domain.)
*/
#define DTM_DMI_OP_OFFSET 0
#define DTM_DMI_OP_LENGTH 2
@@ -167,20 +161,28 @@
#define CSR_DCSR_XDEBUGVER_LENGTH 4
#define CSR_DCSR_XDEBUGVER (0xfU << CSR_DCSR_XDEBUGVER_OFFSET)
/*
-* When 1, {\tt ebreak} instructions in Machine Mode enter Debug Mode.
+* 0: {\tt ebreak} instructions in M-mode behave as described in the
+* Privileged Spec.
+*
+* 1: {\tt ebreak} instructions in M-mode enter Debug Mode.
*/
#define CSR_DCSR_EBREAKM_OFFSET 15
#define CSR_DCSR_EBREAKM_LENGTH 1
#define CSR_DCSR_EBREAKM (0x1U << CSR_DCSR_EBREAKM_OFFSET)
/*
-* When 1, {\tt ebreak} instructions in Supervisor Mode enter Debug Mode.
+* 0: {\tt ebreak} instructions in S-mode behave as described in the
+* Privileged Spec.
+*
+* 1: {\tt ebreak} instructions in S-mode enter Debug Mode.
*/
#define CSR_DCSR_EBREAKS_OFFSET 13
#define CSR_DCSR_EBREAKS_LENGTH 1
#define CSR_DCSR_EBREAKS (0x1U << CSR_DCSR_EBREAKS_OFFSET)
/*
-* When 1, {\tt ebreak} instructions in User/Application Mode enter
-* Debug Mode.
+* 0: {\tt ebreak} instructions in U-mode behave as described in the
+* Privileged Spec.
+*
+* 1: {\tt ebreak} instructions in U-mode enter Debug Mode.
*/
#define CSR_DCSR_EBREAKU_OFFSET 12
#define CSR_DCSR_EBREAKU_LENGTH 1
@@ -191,9 +193,10 @@
* 1: Interrupts are enabled during single stepping.
*
* Implementations may hard wire this bit to 0.
-* The debugger must read back the value it
-* writes to check whether the feature is supported. If not
-* supported, interrupt behavior can be emulated by the debugger.
+* In that case interrupt behavior can be emulated by the debugger.
+*
+* The debugger must not change the value of this bit while the hart
+* is running.
*/
#define CSR_DCSR_STEPIE_OFFSET 11
#define CSR_DCSR_STEPIE_LENGTH 1
@@ -201,14 +204,13 @@
/*
* 0: Increment counters as usual.
*
-* 1: Don't increment any counters while in Debug Mode or on {\tt
-* ebreak} instructions that cause entry into Debug Mode. These
-* counters include the {\tt cycle} and {\tt instret} CSRs. This is
-* preferred for most debugging scenarios.
+* 1: Don't increment any hart-local counters while in Debug Mode or
+* on {\tt ebreak} instructions that cause entry into Debug Mode.
+* These counters include the {\tt instret} CSR. On single-hart cores
+* {\tt cycle} should be stopped, but on multi-hart cores it must keep
+* incrementing.
*
-* An implementation may choose not to support writing to this bit.
-* The debugger must read back the value it writes to check whether
-* the feature is supported.
+* An implementation may hardwire this bit to 0 or 1.
*/
#define CSR_DCSR_STOPCOUNT_OFFSET 10
#define CSR_DCSR_STOPCOUNT_LENGTH 1
@@ -218,9 +220,7 @@
*
* 1: Don't increment any hart-local timers while in Debug Mode.
*
-* An implementation may choose not to support writing to this bit.
-* The debugger must read back the value it writes to check whether
-* the feature is supported.
+* An implementation may hardwire this bit to 0 or 1.
*/
#define CSR_DCSR_STOPTIME_OFFSET 9
#define CSR_DCSR_STOPTIME_LENGTH 1
@@ -236,9 +236,16 @@
*
* 2: The Trigger Module caused a breakpoint exception. (priority 4)
*
-* 3: The debugger requested entry to Debug Mode. (priority 2)
+* 3: The debugger requested entry to Debug Mode using \Fhaltreq.
+* (priority 1)
*
-* 4: The hart single stepped because \Fstep was set. (priority 1)
+* 4: The hart single stepped because \Fstep was set. (priority 0, lowest)
+*
+* 5: The hart halted directly out of reset due to \Fresethaltreq. It
+* is also acceptable to report 3 when this happens. (priority 2)
+*
+* 6: The hart halted because it's part of a halt group. (priority 5,
+* highest) Harts may report 3 for this cause instead.
*
* Other values are reserved for future use.
*/
@@ -246,10 +253,11 @@
#define CSR_DCSR_CAUSE_LENGTH 3
#define CSR_DCSR_CAUSE (0x7U << CSR_DCSR_CAUSE_OFFSET)
/*
-* When 1, \Fmprv in \Rmstatus takes effect during debug mode.
-* When 0, it is ignored during debug mode.
-* Implementing this bit is optional.
-* If not implemented it should be tied to 0.
+* 0: \Fmprv in \Rmstatus is ignored in Debug Mode.
+*
+* 1: \Fmprv in \Rmstatus takes effect in Debug Mode.
+*
+* Implementing this bit is optional. It may be tied to either 0 or 1.
*/
#define CSR_DCSR_MPRVEN_OFFSET 4
#define CSR_DCSR_MPRVEN_LENGTH 1
@@ -270,6 +278,9 @@
* If the instruction does not complete due to an exception,
* the hart will immediately enter Debug Mode before executing
* the trap handler, with appropriate exception registers set.
+*
+* The debugger must not change the value of this bit while the hart
+* is running.
*/
#define CSR_DCSR_STEP_OFFSET 2
#define CSR_DCSR_STEP_LENGTH 1
@@ -289,14 +300,14 @@
#define CSR_DCSR_PRV (0x3U << CSR_DCSR_PRV_OFFSET)
#define CSR_DPC 0x7b1
#define CSR_DPC_DPC_OFFSET 0
-#define CSR_DPC_DPC_LENGTH MXLEN
-#define CSR_DPC_DPC (((1L<<MXLEN)-1) << CSR_DPC_DPC_OFFSET)
+#define CSR_DPC_DPC_LENGTH DXLEN
+#define CSR_DPC_DPC (((1L<<DXLEN)-1) << CSR_DPC_DPC_OFFSET)
#define CSR_DSCRATCH0 0x7b2
#define CSR_DSCRATCH1 0x7b3
#define CSR_TSELECT 0x7a0
#define CSR_TSELECT_INDEX_OFFSET 0
-#define CSR_TSELECT_INDEX_LENGTH MXLEN
-#define CSR_TSELECT_INDEX (((1L<<MXLEN)-1) << CSR_TSELECT_INDEX_OFFSET)
+#define CSR_TSELECT_INDEX_LENGTH XLEN
+#define CSR_TSELECT_INDEX (((1L<<XLEN)-1) << CSR_TSELECT_INDEX_OFFSET)
#define CSR_TDATA1 0x7a1
/*
* 0: There is no trigger at this \Rtselect.
@@ -316,20 +327,18 @@
* 5: The trigger is an exception trigger. The remaining bits
* in this register act as described in \Retrigger.
*
+* 12--14: These trigger types are available for non-standard use.
+*
* 15: This trigger exists (so enumeration shouldn't terminate), but
* is not currently available.
*
* Other values are reserved for future use.
-*
-* When this field is written to an unsupported value, it takes on its
-* reset value instead. The reset value is any one of the types
-* supported by the trigger selected by \Rtselect.
*/
-#define CSR_TDATA1_TYPE_OFFSET (MXLEN-4)
+#define CSR_TDATA1_TYPE_OFFSET (XLEN-4)
#define CSR_TDATA1_TYPE_LENGTH 4
#define CSR_TDATA1_TYPE (0xfULL << CSR_TDATA1_TYPE_OFFSET)
/*
-* 0: Both Debug and M Mode can write the {\tt tdata} registers at the
+* 0: Both Debug and M-mode can write the {\tt tdata} registers at the
* selected \Rtselect.
*
* 1: Only Debug Mode can write the {\tt tdata} registers at the
@@ -337,23 +346,23 @@
*
* This bit is only writable from Debug Mode.
*/
-#define CSR_TDATA1_DMODE_OFFSET (MXLEN-5)
+#define CSR_TDATA1_DMODE_OFFSET (XLEN-5)
#define CSR_TDATA1_DMODE_LENGTH 1
#define CSR_TDATA1_DMODE (0x1ULL << CSR_TDATA1_DMODE_OFFSET)
/*
* Trigger-specific data.
*/
#define CSR_TDATA1_DATA_OFFSET 0
-#define CSR_TDATA1_DATA_LENGTH (MXLEN - 5)
-#define CSR_TDATA1_DATA (((1L<<MXLEN - 5)-1) << CSR_TDATA1_DATA_OFFSET)
+#define CSR_TDATA1_DATA_LENGTH (XLEN - 5)
+#define CSR_TDATA1_DATA (((1L<<XLEN - 5)-1) << CSR_TDATA1_DATA_OFFSET)
#define CSR_TDATA2 0x7a2
#define CSR_TDATA2_DATA_OFFSET 0
-#define CSR_TDATA2_DATA_LENGTH MXLEN
-#define CSR_TDATA2_DATA (((1L<<MXLEN)-1) << CSR_TDATA2_DATA_OFFSET)
+#define CSR_TDATA2_DATA_LENGTH XLEN
+#define CSR_TDATA2_DATA (((1L<<XLEN)-1) << CSR_TDATA2_DATA_OFFSET)
#define CSR_TDATA3 0x7a3
#define CSR_TDATA3_DATA_OFFSET 0
-#define CSR_TDATA3_DATA_LENGTH MXLEN
-#define CSR_TDATA3_DATA (((1L<<MXLEN)-1) << CSR_TDATA3_DATA_OFFSET)
+#define CSR_TDATA3_DATA_LENGTH XLEN
+#define CSR_TDATA3_DATA (((1L<<XLEN)-1) << CSR_TDATA3_DATA_OFFSET)
#define CSR_TINFO 0x7a4
/*
* One bit for each possible \Ftype enumerated in \Rtdataone. Bit N
@@ -371,11 +380,60 @@
#define CSR_TINFO_INFO_OFFSET 0
#define CSR_TINFO_INFO_LENGTH 16
#define CSR_TINFO_INFO (0xffffULL << CSR_TINFO_INFO_OFFSET)
+#define CSR_TCONTROL 0x7a5
+/*
+* M-mode previous trigger enable field.
+*
+* When a trap into M-mode is taken, \Fmpte is set to the value of
+* \Fmte.
+ */
+#define CSR_TCONTROL_MPTE_OFFSET 7
+#define CSR_TCONTROL_MPTE_LENGTH 1
+#define CSR_TCONTROL_MPTE (0x1ULL << CSR_TCONTROL_MPTE_OFFSET)
+/*
+* M-mode trigger enable field.
+*
+* 0: Triggers with action=0 do not match/fire while the hart is in M-mode.
+*
+* 1: Triggers do match/fire while the hart is in M-mode.
+*
+* When a trap into M-mode is taken, \Fmte is set to 0. When {\tt
+* mret} is executed, \Fmte is set to the value of \Fmpte.
+ */
+#define CSR_TCONTROL_MTE_OFFSET 3
+#define CSR_TCONTROL_MTE_LENGTH 1
+#define CSR_TCONTROL_MTE (0x1ULL << CSR_TCONTROL_MTE_OFFSET)
+#define CSR_MCONTEXT 0x7a8
+/*
+* Machine mode software can write a context number to this register,
+* which can be used to set triggers that only fire in that specific
+* context.
+*
+* An implementation may tie any number of upper bits in this field to
+* 0. It's recommended to implement no more than 6 bits on RV32, and
+* 13 on RV64.
+ */
+#define CSR_MCONTEXT_MCONTEXT_OFFSET 0
+#define CSR_MCONTEXT_MCONTEXT_LENGTH XLEN
+#define CSR_MCONTEXT_MCONTEXT (((1L<<XLEN)-1) << CSR_MCONTEXT_MCONTEXT_OFFSET)
+#define CSR_SCONTEXT 0x7aa
+/*
+* Supervisor mode software can write a context number to this
+* register, which can be used to set triggers that only fire in that
+* specific context.
+*
+* An implementation may tie any number of high bits in this field to
+* 0. It's recommended to implement no more than 16 bits on RV32, and
+* 34 on RV64.
+ */
+#define CSR_SCONTEXT_DATA_OFFSET 0
+#define CSR_SCONTEXT_DATA_LENGTH XLEN
+#define CSR_SCONTEXT_DATA (((1L<<XLEN)-1) << CSR_SCONTEXT_DATA_OFFSET)
#define CSR_MCONTROL 0x7a1
-#define CSR_MCONTROL_TYPE_OFFSET (MXLEN-4)
+#define CSR_MCONTROL_TYPE_OFFSET (XLEN-4)
#define CSR_MCONTROL_TYPE_LENGTH 4
#define CSR_MCONTROL_TYPE (0xfULL << CSR_MCONTROL_TYPE_OFFSET)
-#define CSR_MCONTROL_DMODE_OFFSET (MXLEN-5)
+#define CSR_MCONTROL_DMODE_OFFSET (XLEN-5)
#define CSR_MCONTROL_DMODE_LENGTH 1
#define CSR_MCONTROL_DMODE (0x1ULL << CSR_MCONTROL_DMODE_OFFSET)
/*
@@ -387,13 +445,21 @@
* corresponds to the maximum NAPOT range, which is $2^{63}$ bytes in
* size.
*/
-#define CSR_MCONTROL_MASKMAX_OFFSET (MXLEN-11)
+#define CSR_MCONTROL_MASKMAX_OFFSET (XLEN-11)
#define CSR_MCONTROL_MASKMAX_LENGTH 6
#define CSR_MCONTROL_MASKMAX (0x3fULL << CSR_MCONTROL_MASKMAX_OFFSET)
/*
+* This field only exists if XLEN is greater than 32. In that case it
+* extends \Fsize. If it does not exist then hardware operates as if
+* the field contains 0.
+ */
+#define CSR_MCONTROL_SIZEHI_OFFSET 21
+#define CSR_MCONTROL_SIZEHI_LENGTH 2
+#define CSR_MCONTROL_SIZEHI (0x3ULL << CSR_MCONTROL_SIZEHI_OFFSET)
+/*
* If this optional bit is implemented, the hardware sets it when this
* trigger matches. The trigger's user can set or clear it at any
-* time. The trigger's user can use this bit to determine which
+* time. It is used to determine which
* trigger(s) matched. If the bit is not implemented, it is always 0
* and writing it has no effect.
*/
@@ -401,9 +467,10 @@
#define CSR_MCONTROL_HIT_LENGTH 1
#define CSR_MCONTROL_HIT (0x1ULL << CSR_MCONTROL_HIT_OFFSET)
/*
-* 0: Perform a match on the virtual address.
+* 0: Perform a match on the virtual base address of the access.
+* (E.g. on a 32-bit read from 0x4000, the base address is 0x4000.)
*
-* 1: Perform a match on the data value loaded/stored, or the
+* 1: Perform a match on the data value loaded or stored, or the
* instruction executed.
*/
#define CSR_MCONTROL_SELECT_OFFSET 19
@@ -412,7 +479,7 @@
/*
* 0: The action for this trigger will be taken just before the
* instruction that triggered it is executed, but after all preceding
-* instructions are are committed.
+* instructions are committed.
*
* 1: The action for this trigger will be taken after the instruction
* that triggered it is executed. It should be taken before the next
@@ -433,23 +500,68 @@
* A chain of triggers that don't all have the same \Ftiming value
* will never fire (unless consecutive instructions match the
* appropriate triggers).
+*
+* If a trigger with \Ftiming of 0 matches, it is
+* implementation-dependent whether that prevents a trigger with
+* \Ftiming of 1 matching as well.
*/
#define CSR_MCONTROL_TIMING_OFFSET 18
#define CSR_MCONTROL_TIMING_LENGTH 1
#define CSR_MCONTROL_TIMING (0x1ULL << CSR_MCONTROL_TIMING_OFFSET)
/*
+* This field contains the 2 low bits of \Fsize. The high bits come
+* from \Fsizehi. The combined value is interpreted as follows:
+*
+* 0: The trigger will attempt to match against an access of any size.
+* The behavior is only well-defined if $|select|=0$, or if the access
+* size is XLEN.
+*
+* 1: The trigger will only match against 8-bit memory accesses.
+*
+* 2: The trigger will only match against 16-bit memory accesses or
+* execution of 16-bit instructions.
+*
+* 3: The trigger will only match against 32-bit memory accesses or
+* execution of 32-bit instructions.
+*
+* 4: The trigger will only match against execution of 48-bit instructions.
+*
+* 5: The trigger will only match against 64-bit memory accesses or
+* execution of 64-bit instructions.
+*
+* 6: The trigger will only match against execution of 80-bit instructions.
+*
+* 7: The trigger will only match against execution of 96-bit instructions.
+*
+* 8: The trigger will only match against execution of 112-bit instructions.
+*
+* 9: The trigger will only match against 128-bit memory accesses or
+* execution of 128-bit instructions.
+ */
+#define CSR_MCONTROL_SIZELO_OFFSET 16
+#define CSR_MCONTROL_SIZELO_LENGTH 2
+#define CSR_MCONTROL_SIZELO (0x3ULL << CSR_MCONTROL_SIZELO_OFFSET)
+/*
* The action to take when the trigger fires. The values are explained
* in Table~\ref{tab:action}.
*/
#define CSR_MCONTROL_ACTION_OFFSET 12
-#define CSR_MCONTROL_ACTION_LENGTH 6
-#define CSR_MCONTROL_ACTION (0x3fULL << CSR_MCONTROL_ACTION_OFFSET)
+#define CSR_MCONTROL_ACTION_LENGTH 4
+#define CSR_MCONTROL_ACTION (0xfULL << CSR_MCONTROL_ACTION_OFFSET)
/*
* 0: When this trigger matches, the configured action is taken.
*
* 1: While this trigger does not match, it prevents the trigger with
* the next index from matching.
*
+* A trigger chain starts on the first trigger with $|chain|=1$ after
+* a trigger with $|chain|=0$, or simply on the first trigger if that
+* has $|chain|=1$. It ends on the first trigger after that which has
+* $|chain|=0$. This final trigger is part of the chain. The action
+* on all but the final trigger is ignored. The action on that final
+* trigger will be taken if and only if all the triggers in the chain
+* match at the same time.
+*
* Because \Fchain affects the next trigger, hardware must zero it in
* writes to \Rmcontrol that set \Fdmode to 0 if the next trigger has
* \Fdmode of 1.
@@ -466,10 +578,14 @@
#define CSR_MCONTROL_CHAIN_LENGTH 1
#define CSR_MCONTROL_CHAIN (0x1ULL << CSR_MCONTROL_CHAIN_OFFSET)
/*
-* 0: Matches when the value equals \Rtdatatwo.
+* 0: Matches when the value equals \Rtdatatwo. Additionally, if
+* \Fselect=0 then it is recommended that the trigger also matches if
+* any of the accessed addresses equal \Rtdatatwo. (E.g. on a 32-bit
+* read from 0x4000, the following addresses are accessed: 0x4000,
+* 0x4001, 0x4002, and 0x4003.)
*
* 1: Matches when the top M bits of the value match the top M bits of
-* \Rtdatatwo. M is MXLEN-1 minus the index of the least-significant
+* \Rtdatatwo. M is XLEN-1 minus the index of the least-significant
* bit containing 0 in \Rtdatatwo.
*
* 2: Matches when the value is greater than (unsigned) or equal to
@@ -491,19 +607,19 @@
#define CSR_MCONTROL_MATCH_LENGTH 4
#define CSR_MCONTROL_MATCH (0xfULL << CSR_MCONTROL_MATCH_OFFSET)
/*
-* When set, enable this trigger in M mode.
+* When set, enable this trigger in M-mode.
*/
#define CSR_MCONTROL_M_OFFSET 6
#define CSR_MCONTROL_M_LENGTH 1
#define CSR_MCONTROL_M (0x1ULL << CSR_MCONTROL_M_OFFSET)
/*
-* When set, enable this trigger in S mode.
+* When set, enable this trigger in S-mode.
*/
#define CSR_MCONTROL_S_OFFSET 4
#define CSR_MCONTROL_S_LENGTH 1
#define CSR_MCONTROL_S (0x1ULL << CSR_MCONTROL_S_OFFSET)
/*
-* When set, enable this trigger in U mode.
+* When set, enable this trigger in U-mode.
*/
#define CSR_MCONTROL_U_OFFSET 3
#define CSR_MCONTROL_U_LENGTH 1
@@ -528,16 +644,16 @@
#define CSR_MCONTROL_LOAD_LENGTH 1
#define CSR_MCONTROL_LOAD (0x1ULL << CSR_MCONTROL_LOAD_OFFSET)
#define CSR_ICOUNT 0x7a1
-#define CSR_ICOUNT_TYPE_OFFSET (MXLEN-4)
+#define CSR_ICOUNT_TYPE_OFFSET (XLEN-4)
#define CSR_ICOUNT_TYPE_LENGTH 4
#define CSR_ICOUNT_TYPE (0xfULL << CSR_ICOUNT_TYPE_OFFSET)
-#define CSR_ICOUNT_DMODE_OFFSET (MXLEN-5)
+#define CSR_ICOUNT_DMODE_OFFSET (XLEN-5)
#define CSR_ICOUNT_DMODE_LENGTH 1
#define CSR_ICOUNT_DMODE (0x1ULL << CSR_ICOUNT_DMODE_OFFSET)
/*
* If this optional bit is implemented, the hardware sets it when this
* trigger matches. The trigger's user can set or clear it at any
-* time. The trigger's user can use this bit to determine which
+* time. It is used to determine which
* trigger(s) matched. If the bit is not implemented, it is always 0
* and writing it has no effect.
*/
@@ -554,21 +670,21 @@
#define CSR_ICOUNT_COUNT_LENGTH 14
#define CSR_ICOUNT_COUNT (0x3fffULL << CSR_ICOUNT_COUNT_OFFSET)
/*
-* When set, every instruction completed or exception taken in M mode decrements \Fcount
+* When set, every instruction completed or exception taken in M-mode decrements \Fcount
* by 1.
*/
#define CSR_ICOUNT_M_OFFSET 9
#define CSR_ICOUNT_M_LENGTH 1
#define CSR_ICOUNT_M (0x1ULL << CSR_ICOUNT_M_OFFSET)
/*
-* When set, every instruction completed or exception taken in S mode decrements \Fcount
+* When set, every instruction completed or exception taken in S-mode decrements \Fcount
* by 1.
*/
#define CSR_ICOUNT_S_OFFSET 7
#define CSR_ICOUNT_S_LENGTH 1
#define CSR_ICOUNT_S (0x1ULL << CSR_ICOUNT_S_OFFSET)
/*
-* When set, every instruction completed or exception taken in U mode decrements \Fcount
+* When set, every instruction completed or exception taken in U-mode decrements \Fcount
* by 1.
*/
#define CSR_ICOUNT_U_OFFSET 6
@@ -582,20 +698,20 @@
#define CSR_ICOUNT_ACTION_LENGTH 6
#define CSR_ICOUNT_ACTION (0x3fULL << CSR_ICOUNT_ACTION_OFFSET)
#define CSR_ITRIGGER 0x7a1
-#define CSR_ITRIGGER_TYPE_OFFSET (MXLEN-4)
+#define CSR_ITRIGGER_TYPE_OFFSET (XLEN-4)
#define CSR_ITRIGGER_TYPE_LENGTH 4
#define CSR_ITRIGGER_TYPE (0xfULL << CSR_ITRIGGER_TYPE_OFFSET)
-#define CSR_ITRIGGER_DMODE_OFFSET (MXLEN-5)
+#define CSR_ITRIGGER_DMODE_OFFSET (XLEN-5)
#define CSR_ITRIGGER_DMODE_LENGTH 1
#define CSR_ITRIGGER_DMODE (0x1ULL << CSR_ITRIGGER_DMODE_OFFSET)
/*
* If this optional bit is implemented, the hardware sets it when this
* trigger matches. The trigger's user can set or clear it at any
-* time. The trigger's user can use this bit to determine which
+* time. It is used to determine which
* trigger(s) matched. If the bit is not implemented, it is always 0
* and writing it has no effect.
*/
-#define CSR_ITRIGGER_HIT_OFFSET (MXLEN-6)
+#define CSR_ITRIGGER_HIT_OFFSET (XLEN-6)
#define CSR_ITRIGGER_HIT_LENGTH 1
#define CSR_ITRIGGER_HIT (0x1ULL << CSR_ITRIGGER_HIT_OFFSET)
/*
@@ -627,23 +743,30 @@
#define CSR_ITRIGGER_ACTION_LENGTH 6
#define CSR_ITRIGGER_ACTION (0x3fULL << CSR_ITRIGGER_ACTION_OFFSET)
#define CSR_ETRIGGER 0x7a1
-#define CSR_ETRIGGER_TYPE_OFFSET (MXLEN-4)
+#define CSR_ETRIGGER_TYPE_OFFSET (XLEN-4)
#define CSR_ETRIGGER_TYPE_LENGTH 4
#define CSR_ETRIGGER_TYPE (0xfULL << CSR_ETRIGGER_TYPE_OFFSET)
-#define CSR_ETRIGGER_DMODE_OFFSET (MXLEN-5)
+#define CSR_ETRIGGER_DMODE_OFFSET (XLEN-5)
#define CSR_ETRIGGER_DMODE_LENGTH 1
#define CSR_ETRIGGER_DMODE (0x1ULL << CSR_ETRIGGER_DMODE_OFFSET)
/*
* If this optional bit is implemented, the hardware sets it when this
* trigger matches. The trigger's user can set or clear it at any
-* time. The trigger's user can use this bit to determine which
+* time. It is used to determine which
* trigger(s) matched. If the bit is not implemented, it is always 0
* and writing it has no effect.
*/
-#define CSR_ETRIGGER_HIT_OFFSET (MXLEN-6)
+#define CSR_ETRIGGER_HIT_OFFSET (XLEN-6)
#define CSR_ETRIGGER_HIT_LENGTH 1
#define CSR_ETRIGGER_HIT (0x1ULL << CSR_ETRIGGER_HIT_OFFSET)
/*
+* When this optional bit is set, non-maskable interrupts cause this
+* trigger to fire, regardless of the values of \Fm, \Fs, and \Fu.
+ */
+#define CSR_ETRIGGER_NMI_OFFSET 10
+#define CSR_ETRIGGER_NMI_LENGTH 1
+#define CSR_ETRIGGER_NMI (0x1ULL << CSR_ETRIGGER_NMI_OFFSET)
+/*
* When set, enable this trigger for exceptions that are taken from M
* mode.
*/
@@ -671,6 +794,54 @@
#define CSR_ETRIGGER_ACTION_OFFSET 0
#define CSR_ETRIGGER_ACTION_LENGTH 6
#define CSR_ETRIGGER_ACTION (0x3fULL << CSR_ETRIGGER_ACTION_OFFSET)
+#define CSR_TEXTRA32 0x7a3
+/*
+* Data used together with \Fmselect.
+ */
+#define CSR_TEXTRA32_MVALUE_OFFSET 26
+#define CSR_TEXTRA32_MVALUE_LENGTH 6
+#define CSR_TEXTRA32_MVALUE (0x3fU << CSR_TEXTRA32_MVALUE_OFFSET)
+/*
+* 0: Ignore \Fmvalue.
+*
+* 1: This trigger will only match if the low bits of
+* \Rmcontext equal \Fmvalue.
+ */
+#define CSR_TEXTRA32_MSELECT_OFFSET 25
+#define CSR_TEXTRA32_MSELECT_LENGTH 1
+#define CSR_TEXTRA32_MSELECT (0x1U << CSR_TEXTRA32_MSELECT_OFFSET)
+/*
+* Data used together with \Fsselect.
+ */
+#define CSR_TEXTRA32_SVALUE_OFFSET 2
+#define CSR_TEXTRA32_SVALUE_LENGTH 16
+#define CSR_TEXTRA32_SVALUE (0xffffU << CSR_TEXTRA32_SVALUE_OFFSET)
+/*
+* 0: Ignore \Fsvalue.
+*
+* 1: This trigger will only match if the low bits of
+* \Rscontext equal \Fsvalue.
+*
+* 2: This trigger will only match if \Fasid in \Rsatp
+* equals the lower ASIDMAX (defined in the Privileged Spec) bits of
+* \Fsvalue.
+ */
+#define CSR_TEXTRA32_SSELECT_OFFSET 0
+#define CSR_TEXTRA32_SSELECT_LENGTH 2
+#define CSR_TEXTRA32_SSELECT (0x3U << CSR_TEXTRA32_SSELECT_OFFSET)
+#define CSR_TEXTRA64 0x7a3
+#define CSR_TEXTRA64_MVALUE_OFFSET 51
+#define CSR_TEXTRA64_MVALUE_LENGTH 13
+#define CSR_TEXTRA64_MVALUE (0x1fffULL << CSR_TEXTRA64_MVALUE_OFFSET)
+#define CSR_TEXTRA64_MSELECT_OFFSET 50
+#define CSR_TEXTRA64_MSELECT_LENGTH 1
+#define CSR_TEXTRA64_MSELECT (0x1ULL << CSR_TEXTRA64_MSELECT_OFFSET)
+#define CSR_TEXTRA64_SVALUE_OFFSET 2
+#define CSR_TEXTRA64_SVALUE_LENGTH 34
+#define CSR_TEXTRA64_SVALUE (0x3ffffffffULL << CSR_TEXTRA64_SVALUE_OFFSET)
+#define CSR_TEXTRA64_SSELECT_OFFSET 0
+#define CSR_TEXTRA64_SSELECT_LENGTH 2
+#define CSR_TEXTRA64_SSELECT (0x3ULL << CSR_TEXTRA64_SSELECT_OFFSET)
#define DMI_DMSTATUS 0x11
/*
* If 1, then there is an implicit {\tt ebreak} instruction at the
@@ -684,39 +855,43 @@
#define DMI_DMSTATUS_IMPEBREAK_LENGTH 1
#define DMI_DMSTATUS_IMPEBREAK (0x1U << DMI_DMSTATUS_IMPEBREAK_OFFSET)
/*
-* This field is 1 when all currently selected harts have been reset but the reset has not been acknowledged.
+* This field is 1 when all currently selected harts have been reset
+* and reset has not been acknowledged for any of them.
*/
#define DMI_DMSTATUS_ALLHAVERESET_OFFSET 19
#define DMI_DMSTATUS_ALLHAVERESET_LENGTH 1
#define DMI_DMSTATUS_ALLHAVERESET (0x1U << DMI_DMSTATUS_ALLHAVERESET_OFFSET)
/*
-* This field is 1 when any currently selected hart has been reset but the reset has not been acknowledged.
+* This field is 1 when at least one currently selected hart has been
+* reset and reset has not been acknowledged for that hart.
*/
#define DMI_DMSTATUS_ANYHAVERESET_OFFSET 18
#define DMI_DMSTATUS_ANYHAVERESET_LENGTH 1
#define DMI_DMSTATUS_ANYHAVERESET (0x1U << DMI_DMSTATUS_ANYHAVERESET_OFFSET)
/*
* This field is 1 when all currently selected harts have acknowledged
-* the previous resume request.
+* their last resume request.
*/
#define DMI_DMSTATUS_ALLRESUMEACK_OFFSET 17
#define DMI_DMSTATUS_ALLRESUMEACK_LENGTH 1
#define DMI_DMSTATUS_ALLRESUMEACK (0x1U << DMI_DMSTATUS_ALLRESUMEACK_OFFSET)
/*
* This field is 1 when any currently selected hart has acknowledged
-* the previous resume request.
+* its last resume request.
*/
#define DMI_DMSTATUS_ANYRESUMEACK_OFFSET 16
#define DMI_DMSTATUS_ANYRESUMEACK_LENGTH 1
#define DMI_DMSTATUS_ANYRESUMEACK (0x1U << DMI_DMSTATUS_ANYRESUMEACK_OFFSET)
/*
-* This field is 1 when all currently selected harts do not exist in this system.
+* This field is 1 when all currently selected harts do not exist in
+* this platform.
*/
#define DMI_DMSTATUS_ALLNONEXISTENT_OFFSET 15
#define DMI_DMSTATUS_ALLNONEXISTENT_LENGTH 1
#define DMI_DMSTATUS_ALLNONEXISTENT (0x1U << DMI_DMSTATUS_ALLNONEXISTENT_OFFSET)
/*
-* This field is 1 when any currently selected hart does not exist in this system.
+* This field is 1 when any currently selected hart does not exist in
+* this platform.
*/
#define DMI_DMSTATUS_ANYNONEXISTENT_OFFSET 14
#define DMI_DMSTATUS_ANYNONEXISTENT_LENGTH 1
@@ -758,9 +933,12 @@
#define DMI_DMSTATUS_ANYHALTED_LENGTH 1
#define DMI_DMSTATUS_ANYHALTED (0x1U << DMI_DMSTATUS_ANYHALTED_OFFSET)
/*
-* 0 when authentication is required before using the DM. 1 when the
-* authentication check has passed. On components that don't implement
-* authentication, this bit must be preset as 1.
+* 0: Authentication is required before using the DM.
+*
+* 1: The authentication check has passed.
+*
+* On components that don't implement authentication, this bit must be
+* preset as 1.
*/
#define DMI_DMSTATUS_AUTHENTICATED_OFFSET 7
#define DMI_DMSTATUS_AUTHENTICATED_LENGTH 1
@@ -787,15 +965,15 @@
#define DMI_DMSTATUS_HASRESETHALTREQ_LENGTH 1
#define DMI_DMSTATUS_HASRESETHALTREQ (0x1U << DMI_DMSTATUS_HASRESETHALTREQ_OFFSET)
/*
-* 0: \Rdevtreeaddrzero--\Rdevtreeaddrthree hold information which
-* is not relevant to the Device Tree.
+* 0: \Rconfstrptrzero--\Rconfstrptrthree hold information which
+* is not relevant to the configuration string.
*
-* 1: \Rdevtreeaddrzero--\Rdevtreeaddrthree registers hold the address of the
-* Device Tree.
+* 1: \Rconfstrptrzero--\Rconfstrptrthree hold the address of the
+* configuration string.
*/
-#define DMI_DMSTATUS_DEVTREEVALID_OFFSET 4
-#define DMI_DMSTATUS_DEVTREEVALID_LENGTH 1
-#define DMI_DMSTATUS_DEVTREEVALID (0x1U << DMI_DMSTATUS_DEVTREEVALID_OFFSET)
+#define DMI_DMSTATUS_CONFSTRPTRVALID_OFFSET 4
+#define DMI_DMSTATUS_CONFSTRPTRVALID_LENGTH 1
+#define DMI_DMSTATUS_CONFSTRPTRVALID (0x1U << DMI_DMSTATUS_CONFSTRPTRVALID_OFFSET)
/*
* 0: There is no Debug Module present.
*
@@ -813,12 +991,12 @@
#define DMI_DMSTATUS_VERSION (0xfU << DMI_DMSTATUS_VERSION_OFFSET)
#define DMI_DMCONTROL 0x10
/*
-* Writes the halt request bit for all currently selected harts.
-* When set to 1, each selected hart will halt if it is not currently
-* halted.
+* Writing 0 clears the halt request bit for all currently selected
+* harts. This may cancel outstanding halt requests for those harts.
*
-* Writing 1 or 0 has no effect on a hart which is already halted, but
-* the bit must be cleared to 0 before the hart is resumed.
+* Writing 1 sets the halt request bit for all currently selected
+* harts. Running harts will halt whenever their halt request bit is
+* set.
*
* Writes apply to the new value of \Fhartsel and \Fhasel.
*/
@@ -826,12 +1004,11 @@
#define DMI_DMCONTROL_HALTREQ_LENGTH 1
#define DMI_DMCONTROL_HALTREQ (0x1U << DMI_DMCONTROL_HALTREQ_OFFSET)
/*
-* Writes the resume request bit for all currently selected harts.
-* When set to 1, each selected hart will resume if it is currently
-* halted.
+* Writing 1 causes the currently selected harts to resume once, if
+* they are halted when the write occurs. It also clears the resume
+* ack bit for those harts.
*
-* The resume request bit is ignored while the halt request bit is
-* set.
+* \Fresumereq is ignored if \Fhaltreq is set.
*
* Writes apply to the new value of \Fhartsel and \Fhasel.
*/
@@ -843,6 +1020,9 @@
* selected harts. To perform a reset the debugger writes 1, and then
* writes 0 to deassert the reset signal.
*
+* While this bit is 1, the debugger must not change which harts are
+* selected.
+*
* If this feature is not implemented, the bit always stays 0, so
* after writing 1 the debugger can read the register back to see if
* the feature is supported.
@@ -853,8 +1033,9 @@
#define DMI_DMCONTROL_HARTRESET_LENGTH 1
#define DMI_DMCONTROL_HARTRESET (0x1U << DMI_DMCONTROL_HARTRESET_OFFSET)
/*
-* Writing 1 to this bit clears the {\tt havereset} bits for
-* any selected harts.
+* 0: No effect.
+*
+* 1: Clears {\tt havereset} for any selected harts.
*
* Writes apply to the new value of \Fhartsel and \Fhasel.
*/
@@ -862,12 +1043,13 @@
#define DMI_DMCONTROL_ACKHAVERESET_LENGTH 1
#define DMI_DMCONTROL_ACKHAVERESET (0x1U << DMI_DMCONTROL_ACKHAVERESET_OFFSET)
/*
-* Selects the definition of currently selected harts.
+* Selects the definition of currently selected harts.
*
-* 0: There is a single currently selected hart, that selected by \Fhartsel.
+* 0: There is a single currently selected hart, that is selected by \Fhartsel.
*
-* 1: There may be multiple currently selected harts -- that selected by \Fhartsel,
-* plus those selected by the hart array mask register.
+* 1: There may be multiple currently selected harts -- the hart
+* selected by \Fhartsel, plus those selected by the hart array mask
+* register.
*
* An implementation which does not implement the hart array mask register
* must tie this field to 0. A debugger which wishes to use the hart array
@@ -893,7 +1075,8 @@
#define DMI_DMCONTROL_HARTSELHI (0x3ffU << DMI_DMCONTROL_HARTSELHI_OFFSET)
/*
* This optional field writes the halt-on-reset request bit for all
-* currently selected harts.
+* currently selected harts, unless \Fclrresethaltreq is
+* simultaneously set to 1.
* When set to 1, each selected hart will halt upon the next deassertion
* of its reset. The halt-on-reset request bit is not automatically
* cleared. The debugger must write to \Fclrresethaltreq to clear it.
@@ -931,20 +1114,26 @@
*
* 0: The module's state, including authentication mechanism,
* takes its reset values (the \Fdmactive bit is the only bit which can
-* be written to something other than its reset value).
+* be written to something other than its reset value). Any accesses
+* to the module may fail. Specifically, \Fversion may not return
+* correct data.
*
* 1: The module functions normally.
*
* No other mechanism should exist that may result in resetting the
-* Debug Module after power up, including the platform's system reset
-* or Debug Transport reset signals.
+* Debug Module after power up, with the possible (but not
+* recommended) exception of a global reset signal that resets the
+* entire platform.
*
* A debugger may pulse this bit low to get the Debug Module into a
* known state.
*
-* Implementations may use this bit to aid debugging, for example by
-* preventing the Debug Module from being power gated while debugging
-* is active.
+* Implementations may pay attention to this bit to further aid
+* debugging, for example by preventing the Debug Module from being
+* power gated while debugging is active.
+*
+* For forward compatibility, \Fversion will always be readable when
+* \Fdmactive is 1.
*/
#define DMI_DMCONTROL_DMACTIVE_OFFSET 0
#define DMI_DMCONTROL_DMACTIVE_LENGTH 1
@@ -960,8 +1149,8 @@
#define DMI_HARTINFO_NSCRATCH_LENGTH 4
#define DMI_HARTINFO_NSCRATCH (0xfU << DMI_HARTINFO_NSCRATCH_OFFSET)
/*
-* 0: The {\tt data} registers are shadowed in the hart by CSR
-* registers. Each CSR register is MXLEN bits in size, and corresponds
+* 0: The {\tt data} registers are shadowed in the hart by CSRs.
+* Each CSR is DXLEN bits in size, and corresponds
* to a single argument, per Table~\ref{tab:datareg}.
*
* 1: The {\tt data} registers are shadowed in the hart's memory map.
@@ -971,7 +1160,7 @@
#define DMI_HARTINFO_DATAACCESS_LENGTH 1
#define DMI_HARTINFO_DATAACCESS (0x1U << DMI_HARTINFO_DATAACCESS_OFFSET)
/*
-* If \Fdataaccess is 0: Number of CSR registers dedicated to
+* If \Fdataaccess is 0: Number of CSRs dedicated to
* shadowing the {\tt data} registers.
*
* If \Fdataaccess is 1: Number of 32-bit words in the memory map
@@ -996,7 +1185,7 @@
#define DMI_HAWINDOWSEL 0x14
/*
* The high bits of this field may be tied to 0, depending on how large
-* the array mask register is. Eg. on a system with 48 harts only bit 0
+* the array mask register is. E.g.\ on a system with 48 harts only bit 0
* of this field may actually be writable.
*/
#define DMI_HAWINDOWSEL_HAWINDOWSEL_OFFSET 0
@@ -1027,20 +1216,26 @@
* they are cleared by writing 1 to them. No abstract command is
* started until the value is reset to 0.
*
+* This field only contains a valid value if \Fbusy is 0.
+*
* 0 (none): No error.
*
* 1 (busy): An abstract command was executing while \Rcommand,
-* \Rabstractcs, \Rabstractauto was written, or when one
+* \Rabstractcs, or \Rabstractauto was written, or when one
* of the {\tt data} or {\tt progbuf} registers was read or written.
+* This status is only written if \Fcmderr contains 0.
*
* 2 (not supported): The requested command is not supported,
* regardless of whether the hart is running or not.
*
* 3 (exception): An exception occurred while executing the command
-* (eg. while executing the Program Buffer).
+* (e.g.\ while executing the Program Buffer).
*
* 4 (halt/resume): The abstract command couldn't execute because the
-* hart wasn't in the required state (running/halted).
+* hart wasn't in the required state (running/halted), or unavailable.
+*
+* 5 (bus): The abstract command failed due to a bus error (e.g.\
+* alignment, access size, or timeout).
*
* 7 (other): The command failed for another reason.
*/
@@ -1049,7 +1244,7 @@
#define DMI_ABSTRACTCS_CMDERR (0x7U << DMI_ABSTRACTCS_CMDERR_OFFSET)
/*
* Number of {\tt data} registers that are implemented as part of the
-* abstract command interface. Valid sizes are 0 - 12.
+* abstract command interface. Valid sizes are 1 -- 12.
*/
#define DMI_ABSTRACTCS_DATACOUNT_OFFSET 0
#define DMI_ABSTRACTCS_DATACOUNT_LENGTH 4
@@ -1071,26 +1266,28 @@
#define DMI_COMMAND_CONTROL (0xffffffU << DMI_COMMAND_CONTROL_OFFSET)
#define DMI_ABSTRACTAUTO 0x18
/*
-* When a bit in this field is 1, read or write accesses to the corresponding {\tt progbuf} word
-* cause the command in \Rcommand to be executed again.
+* When a bit in this field is 1, read or write accesses to the
+* corresponding {\tt progbuf} word cause the command in \Rcommand to
+* be executed again.
*/
#define DMI_ABSTRACTAUTO_AUTOEXECPROGBUF_OFFSET 16
#define DMI_ABSTRACTAUTO_AUTOEXECPROGBUF_LENGTH 16
#define DMI_ABSTRACTAUTO_AUTOEXECPROGBUF (0xffffU << DMI_ABSTRACTAUTO_AUTOEXECPROGBUF_OFFSET)
/*
-* When a bit in this field is 1, read or write accesses to the corresponding {\tt data} word
-* cause the command in \Rcommand to be executed again.
+* When a bit in this field is 1, read or write accesses to the
+* corresponding {\tt data} word cause the command in \Rcommand to be
+* executed again.
*/
#define DMI_ABSTRACTAUTO_AUTOEXECDATA_OFFSET 0
#define DMI_ABSTRACTAUTO_AUTOEXECDATA_LENGTH 12
#define DMI_ABSTRACTAUTO_AUTOEXECDATA (0xfffU << DMI_ABSTRACTAUTO_AUTOEXECDATA_OFFSET)
-#define DMI_DEVTREEADDR0 0x19
-#define DMI_DEVTREEADDR0_ADDR_OFFSET 0
-#define DMI_DEVTREEADDR0_ADDR_LENGTH 32
-#define DMI_DEVTREEADDR0_ADDR (0xffffffffU << DMI_DEVTREEADDR0_ADDR_OFFSET)
-#define DMI_DEVTREEADDR1 0x1a
-#define DMI_DEVTREEADDR2 0x1b
-#define DMI_DEVTREEADDR3 0x1c
+#define DMI_CONFSTRPTR0 0x19
+#define DMI_CONFSTRPTR0_ADDR_OFFSET 0
+#define DMI_CONFSTRPTR0_ADDR_LENGTH 32
+#define DMI_CONFSTRPTR0_ADDR (0xffffffffU << DMI_CONFSTRPTR0_ADDR_OFFSET)
+#define DMI_CONFSTRPTR1 0x1a
+#define DMI_CONFSTRPTR2 0x1b
+#define DMI_CONFSTRPTR3 0x1c
#define DMI_NEXTDM 0x1d
#define DMI_NEXTDM_ADDR_OFFSET 0
#define DMI_NEXTDM_ADDR_LENGTH 32
@@ -1109,6 +1306,55 @@
#define DMI_AUTHDATA_DATA_OFFSET 0
#define DMI_AUTHDATA_DATA_LENGTH 32
#define DMI_AUTHDATA_DATA (0xffffffffU << DMI_AUTHDATA_DATA_OFFSET)
+#define DMI_DMCS2 0x32
+/*
+* This field contains the currently selected external trigger.
+*
+* If a non-existent trigger value is written here, the hardware will
+* change it to a valid one or 0 if no external triggers exist.
+ */
+#define DMI_DMCS2_EXTTRIGGER_OFFSET 7
+#define DMI_DMCS2_EXTTRIGGER_LENGTH 4
+#define DMI_DMCS2_EXTTRIGGER (0xfU << DMI_DMCS2_EXTTRIGGER_OFFSET)
+/*
+* When \Fhgselect is 0, contains the halt group of the hart
+* specified by \Fhartsel.
+*
+* When \Fhgselect is 1, contains the halt group of the external
+* trigger selected by \Fexttrigger.
+*
+* Writes only have an effect if \Fhgwrite is also written 1.
+*
+* An implementation may tie any number of upper bits in this field to
+* 0. If halt groups aren't implemented, then this entire field
+* is 0.
+ */
+#define DMI_DMCS2_HALTGROUP_OFFSET 2
+#define DMI_DMCS2_HALTGROUP_LENGTH 5
+#define DMI_DMCS2_HALTGROUP (0x1fU << DMI_DMCS2_HALTGROUP_OFFSET)
+/*
+* When \Fhgselect is 0, writing 1 changes the halt group of all
+* selected harts to the value written to \Fhaltgroup.
+*
+* When \Fhgselect is 1, writing 1 changes the halt group of the
+* external trigger selected by \Fexttrigger to the value written to
+* \Fhaltgroup.
+*
+* Writing 0 has no effect.
+ */
+#define DMI_DMCS2_HGWRITE_OFFSET 1
+#define DMI_DMCS2_HGWRITE_LENGTH 1
+#define DMI_DMCS2_HGWRITE (0x1U << DMI_DMCS2_HGWRITE_OFFSET)
+/*
+* 0: Operate on harts.
+*
+* 1: Operate on external triggers.
+*
+* If there are no external triggers, this field must be tied to 0.
+ */
+#define DMI_DMCS2_HGSELECT_OFFSET 0
+#define DMI_DMCS2_HGSELECT_LENGTH 1
+#define DMI_DMCS2_HGSELECT (0x1U << DMI_DMCS2_HGSELECT_OFFSET)
#define DMI_HALTSUM0 0x40
#define DMI_HALTSUM0_HALTSUM0_OFFSET 0
#define DMI_HALTSUM0_HALTSUM0_LENGTH 32
@@ -1125,14 +1371,6 @@
#define DMI_HALTSUM3_HALTSUM3_OFFSET 0
#define DMI_HALTSUM3_HALTSUM3_LENGTH 32
#define DMI_HALTSUM3_HALTSUM3 (0xffffffffU << DMI_HALTSUM3_HALTSUM3_OFFSET)
-#define DMI_SBADDRESS3 0x37
-/*
-* Accesses bits 127:96 of the physical address in {\tt sbaddress} (if
-* the system address bus is that wide).
- */
-#define DMI_SBADDRESS3_ADDRESS_OFFSET 0
-#define DMI_SBADDRESS3_ADDRESS_LENGTH 32
-#define DMI_SBADDRESS3_ADDRESS (0xffffffffU << DMI_SBADDRESS3_ADDRESS_OFFSET)
#define DMI_SBCS 0x38
/*
* 0: The System Bus interface conforms to mainline drafts of this
@@ -1151,7 +1389,7 @@
* already in progress (while \Fsbbusy is set). It remains set until
* it's explicitly cleared by the debugger.
*
-* While this field is non-zero, no more system bus accesses can be
+* While this field is set, no more system bus accesses can be
* initiated by the Debug Module.
*/
#define DMI_SBCS_SBBUSYERROR_OFFSET 22
@@ -1191,7 +1429,7 @@
* 4: 128-bit
*
* If \Fsbaccess has an unsupported value when the DM starts a bus
-* access, the access is not performed and \Fsberror is set to 3.
+* access, the access is not performed and \Fsberror is set to 4.
*/
#define DMI_SBCS_SBACCESS_OFFSET 17
#define DMI_SBCS_SBACCESS_LENGTH 3
@@ -1212,12 +1450,12 @@
#define DMI_SBCS_SBREADONDATA (0x1U << DMI_SBCS_SBREADONDATA_OFFSET)
/*
* When the Debug Module's system bus
-* master causes a bus error, this field gets set. The bits in this
+* master encounters an error, this field gets set. The bits in this
* field remain set until they are cleared by writing 1 to them.
* While this field is non-zero, no more system bus accesses can be
* initiated by the Debug Module.
*
-* An implementation may report "Other" (7) for any error condition.
+* An implementation may report ``Other'' (7) for any error condition.
*
* 0: There was no bus error.
*
@@ -1294,6 +1532,14 @@
#define DMI_SBADDRESS2_ADDRESS_OFFSET 0
#define DMI_SBADDRESS2_ADDRESS_LENGTH 32
#define DMI_SBADDRESS2_ADDRESS (0xffffffffU << DMI_SBADDRESS2_ADDRESS_OFFSET)
+#define DMI_SBADDRESS3 0x37
+/*
+* Accesses bits 127:96 of the physical address in {\tt sbaddress} (if
+* the system address bus is that wide).
+ */
+#define DMI_SBADDRESS3_ADDRESS_OFFSET 0
+#define DMI_SBADDRESS3_ADDRESS_LENGTH 32
+#define DMI_SBADDRESS3_ADDRESS (0xffffffffU << DMI_SBADDRESS3_ADDRESS_OFFSET)
#define DMI_SBDATA0 0x3c
/*
* Accesses bits 31:0 of {\tt sbdata}.
@@ -1325,6 +1571,9 @@
#define DMI_SBDATA3_DATA_OFFSET 0
#define DMI_SBDATA3_DATA_LENGTH 32
#define DMI_SBDATA3_DATA (0xffffffffU << DMI_SBDATA3_DATA_OFFSET)
+#define DMI_CUSTOM 0x1f
+#define DMI_CUSTOM0 0x70
+#define DMI_CUSTOM15 0x7f
#define SHORTNAME 0x123
/*
* Description of what this field is used for.
@@ -1346,19 +1595,32 @@
*
* 4: Access the lowest 128 bits of the register.
*
-* If \Fsize specifies a size larger than the register's actual size,
-* then the access must fail. If a register is accessible, then reads of \Fsize
+* If \Faarsize specifies a size larger than the register's actual size,
+* then the access must fail. If a register is accessible, then reads of \Faarsize
* less than or equal to the register's actual size must be supported.
*
* This field controls the Argument Width as referenced in
* Table~\ref{tab:datareg}.
*/
-#define AC_ACCESS_REGISTER_SIZE_OFFSET 20
-#define AC_ACCESS_REGISTER_SIZE_LENGTH 3
-#define AC_ACCESS_REGISTER_SIZE (0x7U << AC_ACCESS_REGISTER_SIZE_OFFSET)
+#define AC_ACCESS_REGISTER_AARSIZE_OFFSET 20
+#define AC_ACCESS_REGISTER_AARSIZE_LENGTH 3
+#define AC_ACCESS_REGISTER_AARSIZE (0x7U << AC_ACCESS_REGISTER_AARSIZE_OFFSET)
+/*
+* 0: No effect. This variant must be supported.
+*
+* 1: After a successful register access, \Fregno is incremented
+* (wrapping around to 0). Supporting this variant is optional.
+ */
+#define AC_ACCESS_REGISTER_AARPOSTINCREMENT_OFFSET 19
+#define AC_ACCESS_REGISTER_AARPOSTINCREMENT_LENGTH 1
+#define AC_ACCESS_REGISTER_AARPOSTINCREMENT (0x1U << AC_ACCESS_REGISTER_AARPOSTINCREMENT_OFFSET)
/*
-* When 1, execute the program in the Program Buffer exactly once
-* after performing the transfer, if any.
+* 0: No effect. This variant must be supported, and is the only
+* supported one if \Fprogbufsize is 0.
+*
+* 1: Execute the program in the Program Buffer exactly once after
+* performing the transfer, if any. Supporting this variant is
+* optional.
*/
#define AC_ACCESS_REGISTER_POSTEXEC_OFFSET 18
#define AC_ACCESS_REGISTER_POSTEXEC_LENGTH 1
@@ -1369,7 +1631,7 @@
* 1: Do the operation specified by \Fwrite.
*
* This bit can be used to just execute the Program Buffer without
-* having to worry about placing valid values into \Fsize or \Fregno.
+* having to worry about placing valid values into \Faarsize or \Fregno.
*/
#define AC_ACCESS_REGISTER_TRANSFER_OFFSET 17
#define AC_ACCESS_REGISTER_TRANSFER_LENGTH 1
@@ -1401,14 +1663,177 @@
#define AC_QUICK_ACCESS_CMDTYPE_OFFSET 24
#define AC_QUICK_ACCESS_CMDTYPE_LENGTH 8
#define AC_QUICK_ACCESS_CMDTYPE (0xffU << AC_QUICK_ACCESS_CMDTYPE_OFFSET)
+#define AC_ACCESS_MEMORY None
+/*
+* This is 2 to indicate Access Memory Command.
+ */
+#define AC_ACCESS_MEMORY_CMDTYPE_OFFSET 24
+#define AC_ACCESS_MEMORY_CMDTYPE_LENGTH 8
+#define AC_ACCESS_MEMORY_CMDTYPE (0xffU << AC_ACCESS_MEMORY_CMDTYPE_OFFSET)
+/*
+* An implementation does not have to implement both virtual and
+* physical accesses, but it must fail accesses that it doesn't
+* support.
+*
+* 0: Addresses are physical (to the hart they are performed on).
+*
+* 1: Addresses are virtual, and translated the way they would be from
+* M-mode, with \Fmprv set.
+ */
+#define AC_ACCESS_MEMORY_AAMVIRTUAL_OFFSET 23
+#define AC_ACCESS_MEMORY_AAMVIRTUAL_LENGTH 1
+#define AC_ACCESS_MEMORY_AAMVIRTUAL (0x1U << AC_ACCESS_MEMORY_AAMVIRTUAL_OFFSET)
+/*
+* 0: Access the lowest 8 bits of the memory location.
+*
+* 1: Access the lowest 16 bits of the memory location.
+*
+* 2: Access the lowest 32 bits of the memory location.
+*
+* 3: Access the lowest 64 bits of the memory location.
+*
+* 4: Access the lowest 128 bits of the memory location.
+ */
+#define AC_ACCESS_MEMORY_AAMSIZE_OFFSET 20
+#define AC_ACCESS_MEMORY_AAMSIZE_LENGTH 3
+#define AC_ACCESS_MEMORY_AAMSIZE (0x7U << AC_ACCESS_MEMORY_AAMSIZE_OFFSET)
+/*
+* After a memory access has completed, if this bit is 1, increment
+* {\tt arg1} (which contains the address used) by the number of bytes
+* encoded in \Faamsize.
+ */
+#define AC_ACCESS_MEMORY_AAMPOSTINCREMENT_OFFSET 19
+#define AC_ACCESS_MEMORY_AAMPOSTINCREMENT_LENGTH 1
+#define AC_ACCESS_MEMORY_AAMPOSTINCREMENT (0x1U << AC_ACCESS_MEMORY_AAMPOSTINCREMENT_OFFSET)
+/*
+* 0: Copy data from the memory location specified in {\tt arg1} into
+* the low bits of {\tt arg0}. Any remaining bits of {\tt arg0} now
+* have an undefined value.
+*
+* 1: Copy data from the low bits of {\tt arg0} into the memory
+* location specified in {\tt arg1}.
+ */
+#define AC_ACCESS_MEMORY_WRITE_OFFSET 16
+#define AC_ACCESS_MEMORY_WRITE_LENGTH 1
+#define AC_ACCESS_MEMORY_WRITE (0x1U << AC_ACCESS_MEMORY_WRITE_OFFSET)
+/*
+* These bits are reserved for target-specific uses.
+ */
+#define AC_ACCESS_MEMORY_TARGET_SPECIFIC_OFFSET 14
+#define AC_ACCESS_MEMORY_TARGET_SPECIFIC_LENGTH 2
+#define AC_ACCESS_MEMORY_TARGET_SPECIFIC (0x3U << AC_ACCESS_MEMORY_TARGET_SPECIFIC_OFFSET)
#define VIRT_PRIV virtual
/*
* Contains the privilege level the hart was operating in when Debug
* Mode was entered. The encoding is described in Table
* \ref{tab:privlevel}, and matches the privilege level encoding from
-* the RISC-V Privileged ISA Specification. A user can write this
+* the Privileged Spec. A user can write this
* value to change the hart's privilege level when exiting Debug Mode.
*/
#define VIRT_PRIV_PRV_OFFSET 0
#define VIRT_PRIV_PRV_LENGTH 2
#define VIRT_PRIV_PRV (0x3U << VIRT_PRIV_PRV_OFFSET)
+#define DMI_SERCS 0x34
+/*
+* Number of supported serial ports.
+ */
+#define DMI_SERCS_SERIALCOUNT_OFFSET 28
+#define DMI_SERCS_SERIALCOUNT_LENGTH 4
+#define DMI_SERCS_SERIALCOUNT (0xfU << DMI_SERCS_SERIALCOUNT_OFFSET)
+/*
+* Select which serial port is accessed by \Rserrx and \Rsertx.
+ */
+#define DMI_SERCS_SERIAL_OFFSET 24
+#define DMI_SERCS_SERIAL_LENGTH 3
+#define DMI_SERCS_SERIAL (0x7U << DMI_SERCS_SERIAL_OFFSET)
+#define DMI_SERCS_ERROR7_OFFSET 23
+#define DMI_SERCS_ERROR7_LENGTH 1
+#define DMI_SERCS_ERROR7 (0x1U << DMI_SERCS_ERROR7_OFFSET)
+#define DMI_SERCS_VALID7_OFFSET 22
+#define DMI_SERCS_VALID7_LENGTH 1
+#define DMI_SERCS_VALID7 (0x1U << DMI_SERCS_VALID7_OFFSET)
+#define DMI_SERCS_FULL7_OFFSET 21
+#define DMI_SERCS_FULL7_LENGTH 1
+#define DMI_SERCS_FULL7 (0x1U << DMI_SERCS_FULL7_OFFSET)
+#define DMI_SERCS_ERROR6_OFFSET 20
+#define DMI_SERCS_ERROR6_LENGTH 1
+#define DMI_SERCS_ERROR6 (0x1U << DMI_SERCS_ERROR6_OFFSET)
+#define DMI_SERCS_VALID6_OFFSET 19
+#define DMI_SERCS_VALID6_LENGTH 1
+#define DMI_SERCS_VALID6 (0x1U << DMI_SERCS_VALID6_OFFSET)
+#define DMI_SERCS_FULL6_OFFSET 18
+#define DMI_SERCS_FULL6_LENGTH 1
+#define DMI_SERCS_FULL6 (0x1U << DMI_SERCS_FULL6_OFFSET)
+#define DMI_SERCS_ERROR5_OFFSET 17
+#define DMI_SERCS_ERROR5_LENGTH 1
+#define DMI_SERCS_ERROR5 (0x1U << DMI_SERCS_ERROR5_OFFSET)
+#define DMI_SERCS_VALID5_OFFSET 16
+#define DMI_SERCS_VALID5_LENGTH 1
+#define DMI_SERCS_VALID5 (0x1U << DMI_SERCS_VALID5_OFFSET)
+#define DMI_SERCS_FULL5_OFFSET 15
+#define DMI_SERCS_FULL5_LENGTH 1
+#define DMI_SERCS_FULL5 (0x1U << DMI_SERCS_FULL5_OFFSET)
+#define DMI_SERCS_ERROR4_OFFSET 14
+#define DMI_SERCS_ERROR4_LENGTH 1
+#define DMI_SERCS_ERROR4 (0x1U << DMI_SERCS_ERROR4_OFFSET)
+#define DMI_SERCS_VALID4_OFFSET 13
+#define DMI_SERCS_VALID4_LENGTH 1
+#define DMI_SERCS_VALID4 (0x1U << DMI_SERCS_VALID4_OFFSET)
+#define DMI_SERCS_FULL4_OFFSET 12
+#define DMI_SERCS_FULL4_LENGTH 1
+#define DMI_SERCS_FULL4 (0x1U << DMI_SERCS_FULL4_OFFSET)
+#define DMI_SERCS_ERROR3_OFFSET 11
+#define DMI_SERCS_ERROR3_LENGTH 1
+#define DMI_SERCS_ERROR3 (0x1U << DMI_SERCS_ERROR3_OFFSET)
+#define DMI_SERCS_VALID3_OFFSET 10
+#define DMI_SERCS_VALID3_LENGTH 1
+#define DMI_SERCS_VALID3 (0x1U << DMI_SERCS_VALID3_OFFSET)
+#define DMI_SERCS_FULL3_OFFSET 9
+#define DMI_SERCS_FULL3_LENGTH 1
+#define DMI_SERCS_FULL3 (0x1U << DMI_SERCS_FULL3_OFFSET)
+#define DMI_SERCS_ERROR2_OFFSET 8
+#define DMI_SERCS_ERROR2_LENGTH 1
+#define DMI_SERCS_ERROR2 (0x1U << DMI_SERCS_ERROR2_OFFSET)
+#define DMI_SERCS_VALID2_OFFSET 7
+#define DMI_SERCS_VALID2_LENGTH 1
+#define DMI_SERCS_VALID2 (0x1U << DMI_SERCS_VALID2_OFFSET)
+#define DMI_SERCS_FULL2_OFFSET 6
+#define DMI_SERCS_FULL2_LENGTH 1
+#define DMI_SERCS_FULL2 (0x1U << DMI_SERCS_FULL2_OFFSET)
+#define DMI_SERCS_ERROR1_OFFSET 5
+#define DMI_SERCS_ERROR1_LENGTH 1
+#define DMI_SERCS_ERROR1 (0x1U << DMI_SERCS_ERROR1_OFFSET)
+#define DMI_SERCS_VALID1_OFFSET 4
+#define DMI_SERCS_VALID1_LENGTH 1
+#define DMI_SERCS_VALID1 (0x1U << DMI_SERCS_VALID1_OFFSET)
+#define DMI_SERCS_FULL1_OFFSET 3
+#define DMI_SERCS_FULL1_LENGTH 1
+#define DMI_SERCS_FULL1 (0x1U << DMI_SERCS_FULL1_OFFSET)
+/*
+* 1 when the debugger-to-core queue for serial port 0 has
+* over or underflowed. This bit will remain set until it is reset by
+* writing 1 to this bit.
+ */
+#define DMI_SERCS_ERROR0_OFFSET 2
+#define DMI_SERCS_ERROR0_LENGTH 1
+#define DMI_SERCS_ERROR0 (0x1U << DMI_SERCS_ERROR0_OFFSET)
+/*
+* 1 when the core-to-debugger queue for serial port 0 is not empty.
+ */
+#define DMI_SERCS_VALID0_OFFSET 1
+#define DMI_SERCS_VALID0_LENGTH 1
+#define DMI_SERCS_VALID0 (0x1U << DMI_SERCS_VALID0_OFFSET)
+/*
+* 1 when the debugger-to-core queue for serial port 0 is full.
+ */
+#define DMI_SERCS_FULL0_OFFSET 0
+#define DMI_SERCS_FULL0_LENGTH 1
+#define DMI_SERCS_FULL0 (0x1U << DMI_SERCS_FULL0_OFFSET)
+#define DMI_SERTX 0x35
+#define DMI_SERTX_DATA_OFFSET 0
+#define DMI_SERTX_DATA_LENGTH 32
+#define DMI_SERTX_DATA (0xffffffffU << DMI_SERTX_DATA_OFFSET)
+#define DMI_SERRX 0x36
+#define DMI_SERRX_DATA_OFFSET 0
+#define DMI_SERRX_DATA_LENGTH 32
+#define DMI_SERRX_DATA (0xffffffffU << DMI_SERRX_DATA_OFFSET)
diff --git a/src/target/riscv/riscv-011.c b/src/target/riscv/riscv-011.c
index eded862..aad5b8b 100644
--- a/src/target/riscv/riscv-011.c
+++ b/src/target/riscv/riscv-011.c
@@ -204,7 +204,6 @@ typedef struct {
* before the interrupt is cleared. */
unsigned int interrupt_high_delay;
- bool need_strict_step;
bool never_halted;
} riscv011_info_t;
@@ -1413,8 +1412,6 @@ static void deinit_target(struct target *target)
static int strict_step(struct target *target, bool announce)
{
- riscv011_info_t *info = get_info(target);
-
LOG_DEBUG("enter");
struct watchpoint *watchpoint = target->watchpoints;
@@ -1433,16 +1430,12 @@ static int strict_step(struct target *target, bool announce)
watchpoint = watchpoint->next;
}
- info->need_strict_step = false;
-
return ERROR_OK;
}
static int step(struct target *target, int current, target_addr_t address,
int handle_breakpoints)
{
- riscv011_info_t *info = get_info(target);
-
jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
if (!current) {
@@ -1455,7 +1448,7 @@ static int step(struct target *target, int current, target_addr_t address,
return result;
}
- if (info->need_strict_step || handle_breakpoints) {
+ if (handle_breakpoints) {
int result = strict_step(target, true);
if (result != ERROR_OK)
return result;
@@ -1486,7 +1479,6 @@ static int examine(struct target *target)
}
RISCV_INFO(r);
- r->hart_count = 1;
riscv011_info_t *info = get_info(target);
info->addrbits = get_field(dtmcontrol, DTMCONTROL_ADDRBITS);
@@ -1848,9 +1840,6 @@ static int handle_halt(struct target *target, bool announce)
break;
case DCSR_CAUSE_HWBP:
target->debug_reason = DBG_REASON_WATCHPOINT;
- /* If we halted because of a data trigger, gdb doesn't know to do
- * the disable-breakpoints-step-enable-breakpoints dance. */
- info->need_strict_step = true;
break;
case DCSR_CAUSE_DEBUGINT:
target->debug_reason = DBG_REASON_DBGRQ;
@@ -1935,26 +1924,10 @@ static int riscv011_poll(struct target *target)
static int riscv011_resume(struct target *target, int current,
target_addr_t address, int handle_breakpoints, int debug_execution)
{
- riscv011_info_t *info = get_info(target);
-
+ RISCV_INFO(r);
jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
- if (!current) {
- if (riscv_xlen(target) > 32) {
- LOG_WARNING("Asked to resume at 32-bit PC on %d-bit target.",
- riscv_xlen(target));
- }
- int result = register_write(target, GDB_REGNO_PC, address);
- if (result != ERROR_OK)
- return result;
- }
-
- if (info->need_strict_step || handle_breakpoints) {
- int result = strict_step(target, false);
- if (result != ERROR_OK)
- return result;
- }
-
+ r->prepped = false;
return resume(target, debug_execution, false);
}
diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c
index 5683e5a..d7bfac9 100644
--- a/src/target/riscv/riscv-013.c
+++ b/src/target/riscv/riscv-013.c
@@ -21,6 +21,7 @@
#include "helper/time_support.h"
#include "helper/list.h"
#include "riscv.h"
+#include "rtos/riscv_debug.h"
#include "debug_defines.h"
#include "rtos/rtos.h"
#include "program.h"
@@ -31,7 +32,8 @@
#define DMI_PROGBUF1 (DMI_PROGBUF0 + 1)
static int riscv013_on_step_or_resume(struct target *target, bool step);
-static int riscv013_step_or_resume_current_hart(struct target *target, bool step);
+static int riscv013_step_or_resume_current_hart(struct target *target,
+ bool step, bool use_hasel);
static void riscv013_clear_abstract_error(struct target *target);
/* Implementations of the functions in riscv_info_t. */
@@ -40,11 +42,11 @@ static int riscv013_get_register(struct target *target,
static int riscv013_set_register(struct target *target, int hartid, int regid, uint64_t value);
static int riscv013_select_current_hart(struct target *target);
static int riscv013_halt_current_hart(struct target *target);
-static int riscv013_resume_current_hart(struct target *target);
+static int riscv013_resume_go(struct target *target);
static int riscv013_step_current_hart(struct target *target);
static int riscv013_on_halt(struct target *target);
static int riscv013_on_step(struct target *target);
-static int riscv013_on_resume(struct target *target);
+static int riscv013_resume_prep(struct target *target);
static bool riscv013_is_halted(struct target *target);
static enum riscv_halt_reason riscv013_halt_reason(struct target *target);
static int riscv013_write_debug_buffer(struct target *target, unsigned index,
@@ -146,12 +148,16 @@ typedef enum {
typedef struct {
struct list_head list;
int abs_chain_position;
+
+ /* The number of harts connected to this DM. */
+ int hart_count;
/* Indicates we already reset this DM, so don't need to do it again. */
bool was_reset;
/* Targets that are connected to this DM. */
struct list_head target_list;
/* The currently selected hartid on this DM. */
int current_hartid;
+ bool hasel_supported;
} dm013_info_t;
typedef struct {
@@ -160,6 +166,8 @@ typedef struct {
} target_list_t;
typedef struct {
+ /* The indexed used to address this hart in its DM. */
+ unsigned index;
/* Number of address bits in the dbus register. */
unsigned abits;
/* Number of abstract command data registers. */
@@ -229,7 +237,7 @@ static riscv013_info_t *get_info(const struct target *target)
* global list of DMs. If it's not in there, then create one and initialize it
* to 0.
*/
-static dm013_info_t *get_dm(struct target *target)
+dm013_info_t *get_dm(struct target *target)
{
RISCV013_INFO(info);
if (info->dm)
@@ -247,9 +255,11 @@ static dm013_info_t *get_dm(struct target *target)
}
if (!dm) {
+ LOG_DEBUG("[%d] Allocating new DM", target->coreid);
dm = calloc(1, sizeof(dm013_info_t));
dm->abs_chain_position = abs_chain_position;
dm->current_hartid = -1;
+ dm->hart_count = -1;
INIT_LIST_HEAD(&dm->target_list);
list_add(&dm->list, &dm_list);
}
@@ -313,7 +323,8 @@ static void decode_dmi(char *text, unsigned address, unsigned data)
{ DMI_DMSTATUS, DMI_DMSTATUS_ANYHALTED, "anyhalted" },
{ DMI_DMSTATUS, DMI_DMSTATUS_AUTHENTICATED, "authenticated" },
{ DMI_DMSTATUS, DMI_DMSTATUS_AUTHBUSY, "authbusy" },
- { DMI_DMSTATUS, DMI_DMSTATUS_DEVTREEVALID, "devtreevalid" },
+ { DMI_DMSTATUS, DMI_DMSTATUS_HASRESETHALTREQ, "hasresethaltreq" },
+ { DMI_DMSTATUS, DMI_DMSTATUS_CONFSTRPTRVALID, "confstrptrvalid" },
{ DMI_DMSTATUS, DMI_DMSTATUS_VERSION, "version" },
{ DMI_ABSTRACTCS, DMI_ABSTRACTCS_PROGBUFSIZE, "progbufsize" },
@@ -662,12 +673,12 @@ uint32_t abstract_register_size(unsigned width)
{
switch (width) {
case 32:
- return set_field(0, AC_ACCESS_REGISTER_SIZE, 2);
+ return set_field(0, AC_ACCESS_REGISTER_AARSIZE, 2);
case 64:
- return set_field(0, AC_ACCESS_REGISTER_SIZE, 3);
+ return set_field(0, AC_ACCESS_REGISTER_AARSIZE, 3);
break;
case 128:
- return set_field(0, AC_ACCESS_REGISTER_SIZE, 4);
+ return set_field(0, AC_ACCESS_REGISTER_AARSIZE, 4);
break;
default:
LOG_ERROR("Unsupported register width: %d", width);
@@ -721,7 +732,7 @@ static int execute_abstract_command(struct target *target, uint32_t command)
LOG_DEBUG("command=0x%x; access register, size=%d, postexec=%d, "
"transfer=%d, write=%d, regno=0x%x",
command,
- 8 << get_field(command, AC_ACCESS_REGISTER_SIZE),
+ 8 << get_field(command, AC_ACCESS_REGISTER_AARSIZE),
get_field(command, AC_ACCESS_REGISTER_POSTEXEC),
get_field(command, AC_ACCESS_REGISTER_TRANSFER),
get_field(command, AC_ACCESS_REGISTER_WRITE),
@@ -797,10 +808,10 @@ static uint32_t access_register_command(struct target *target, uint32_t number,
uint32_t command = set_field(0, DMI_COMMAND_CMDTYPE, 0);
switch (size) {
case 32:
- command = set_field(command, AC_ACCESS_REGISTER_SIZE, 2);
+ command = set_field(command, AC_ACCESS_REGISTER_AARSIZE, 2);
break;
case 64:
- command = set_field(command, AC_ACCESS_REGISTER_SIZE, 3);
+ command = set_field(command, AC_ACCESS_REGISTER_AARSIZE, 3);
break;
default:
assert(0);
@@ -1361,6 +1372,18 @@ static void deinit_target(struct target *target)
info->version_specific = NULL;
}
+static int set_haltgroup(struct target *target, bool *supported)
+{
+ uint32_t write = set_field(DMI_DMCS2_HGWRITE, DMI_DMCS2_HALTGROUP, target->smp);
+ if (dmi_write(target, DMI_DMCS2, write) != ERROR_OK)
+ return ERROR_FAIL;
+ uint32_t read;
+ if (dmi_read(target, &read, DMI_DMCS2) != ERROR_OK)
+ return ERROR_FAIL;
+ *supported = get_field(read, DMI_DMCS2_HALTGROUP) == (unsigned) target->smp;
+ return ERROR_OK;
+}
+
static int examine(struct target *target)
{
/* Don't need to select dbus, since the first thing we do is read dtmcontrol. */
@@ -1383,6 +1406,8 @@ static int examine(struct target *target)
}
riscv013_info_t *info = get_info(target);
+ /* TODO: This won't be true if there are multiple DMs. */
+ info->index = target->coreid;
info->abits = get_field(dtmcontrol, DTM_DTMCS_ABITS);
info->dtmcs_idle = get_field(dtmcontrol, DTM_DTMCS_IDLE);
@@ -1395,7 +1420,8 @@ static int examine(struct target *target)
}
dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_HARTSELLO |
- DMI_DMCONTROL_HARTSELHI | DMI_DMCONTROL_DMACTIVE);
+ DMI_DMCONTROL_HARTSELHI | DMI_DMCONTROL_DMACTIVE |
+ DMI_DMCONTROL_HASEL);
uint32_t dmcontrol;
if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK)
return ERROR_FAIL;
@@ -1406,6 +1432,8 @@ static int examine(struct target *target)
return ERROR_FAIL;
}
+ dm->hasel_supported = get_field(dmcontrol, DMI_DMCONTROL_HASEL);
+
uint32_t dmstatus;
if (dmstatus_read(target, &dmstatus, false) != ERROR_OK)
return ERROR_FAIL;
@@ -1469,10 +1497,35 @@ static int examine(struct target *target)
}
/* Before doing anything else we must first enumerate the harts. */
+ if (dm->hart_count < 0) {
+ for (int i = 0; i < MIN(RISCV_MAX_HARTS, 1 << info->hartsellen); ++i) {
+ r->current_hartid = i;
+ if (riscv013_select_current_hart(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ uint32_t s;
+ if (dmstatus_read(target, &s, true) != ERROR_OK)
+ return ERROR_FAIL;
+ if (get_field(s, DMI_DMSTATUS_ANYNONEXISTENT))
+ break;
+ dm->hart_count = i + 1;
+
+ if (get_field(s, DMI_DMSTATUS_ANYHAVERESET))
+ dmi_write(target, DMI_DMCONTROL,
+ set_hartsel(DMI_DMCONTROL_DMACTIVE | DMI_DMCONTROL_ACKHAVERESET, i));
+ }
+
+ LOG_DEBUG("Detected %d harts.", dm->hart_count);
+ }
+
+ if (dm->hart_count == 0) {
+ LOG_ERROR("No harts found!");
+ return ERROR_FAIL;
+ }
/* Don't call any riscv_* functions until after we've counted the number of
* cores and initialized registers. */
- for (int i = 0; i < MIN(RISCV_MAX_HARTS, 1 << info->hartsellen); ++i) {
+ for (int i = 0; i < dm->hart_count; ++i) {
if (!riscv_rtos_enabled(target) && i != target->coreid)
continue;
@@ -1480,17 +1533,6 @@ static int examine(struct target *target)
if (riscv013_select_current_hart(target) != ERROR_OK)
return ERROR_FAIL;
- uint32_t s;
- if (dmstatus_read(target, &s, true) != ERROR_OK)
- return ERROR_FAIL;
- if (get_field(s, DMI_DMSTATUS_ANYNONEXISTENT))
- break;
- r->hart_count = i + 1;
-
- if (get_field(s, DMI_DMSTATUS_ANYHAVERESET))
- dmi_write(target, DMI_DMCONTROL,
- set_hartsel(DMI_DMCONTROL_DMACTIVE | DMI_DMCONTROL_ACKHAVERESET, i));
-
bool halted = riscv_is_halted(target);
if (!halted) {
if (riscv013_halt_current_hart(target) != ERROR_OK) {
@@ -1524,18 +1566,23 @@ static int examine(struct target *target)
r->misa[i]);
if (!halted)
- riscv013_resume_current_hart(target);
+ riscv013_step_or_resume_current_hart(target, false, false);
}
- LOG_DEBUG("Enumerated %d harts", r->hart_count);
+ target_set_examined(target);
- if (r->hart_count == 0) {
- LOG_ERROR("No harts found!");
- return ERROR_FAIL;
+ if (target->smp) {
+ bool haltgroup_supported;
+ if (set_haltgroup(target, &haltgroup_supported) != ERROR_OK)
+ return ERROR_FAIL;
+ if (haltgroup_supported)
+ LOG_INFO("Core %d made part of halt group %d.", target->coreid,
+ target->smp);
+ else
+ LOG_INFO("Core %d could not be made part of halt group %d.",
+ target->coreid, target->smp);
}
- target_set_examined(target);
-
/* Some regression suites rely on seeing 'Examined RISC-V core' to know
* when they can connect with gdb/telnet.
* We will need to update those suites if we want to change that text. */
@@ -1587,6 +1634,12 @@ int riscv013_authdata_write(struct target *target, uint32_t value)
return ERROR_OK;
}
+static int riscv013_hart_count(struct target *target)
+{
+ dm013_info_t *dm = get_dm(target);
+ return dm->hart_count;
+}
+
static int init_target(struct command_context *cmd_ctx,
struct target *target)
{
@@ -1598,10 +1651,10 @@ static int init_target(struct command_context *cmd_ctx,
generic_info->select_current_hart = &riscv013_select_current_hart;
generic_info->is_halted = &riscv013_is_halted;
generic_info->halt_current_hart = &riscv013_halt_current_hart;
- generic_info->resume_current_hart = &riscv013_resume_current_hart;
+ generic_info->resume_go = &riscv013_resume_go;
generic_info->step_current_hart = &riscv013_step_current_hart;
generic_info->on_halt = &riscv013_on_halt;
- generic_info->on_resume = &riscv013_on_resume;
+ generic_info->resume_prep = &riscv013_resume_prep;
generic_info->on_step = &riscv013_on_step;
generic_info->halt_reason = &riscv013_halt_reason;
generic_info->read_debug_buffer = &riscv013_read_debug_buffer;
@@ -1617,6 +1670,7 @@ static int init_target(struct command_context *cmd_ctx,
generic_info->dmi_write = &dmi_write;
generic_info->test_sba_config_reg = &riscv013_test_sba_config_reg;
generic_info->test_compliance = &riscv013_test_compliance;
+ generic_info->hart_count = &riscv013_hart_count;
generic_info->version_specific = calloc(1, sizeof(riscv013_info_t));
if (!generic_info->version_specific)
return ERROR_FAIL;
@@ -2793,7 +2847,7 @@ struct target_type riscv013_target = {
.poll = &riscv_openocd_poll,
.halt = &riscv_openocd_halt,
- .resume = &riscv_openocd_resume,
+ .resume = &riscv_resume,
.step = &riscv_openocd_step,
.assert_reset = assert_reset,
@@ -2916,17 +2970,74 @@ static int riscv013_halt_current_hart(struct target *target)
return ERROR_OK;
}
-static int riscv013_resume_current_hart(struct target *target)
+/* Select all harts that were prepped and that are selectable, clearing the
+ * prepped flag on the harts that actually were selected. */
+static int select_prepped_harts(struct target *target, bool *use_hasel)
{
- return riscv013_step_or_resume_current_hart(target, false);
+ dm013_info_t *dm = get_dm(target);
+ if (!dm->hasel_supported) {
+ RISCV_INFO(r);
+ r->prepped = false;
+ *use_hasel = false;
+ return ERROR_OK;
+ }
+
+ assert(dm->hart_count);
+ unsigned hawindow_count = (dm->hart_count + 31) / 32;
+ uint32_t hawindow[hawindow_count];
+
+ memset(hawindow, 0, sizeof(uint32_t) * hawindow_count);
+
+ target_list_t *entry;
+ unsigned total_selected = 0;
+ list_for_each_entry(entry, &dm->target_list, list) {
+ struct target *t = entry->target;
+ riscv_info_t *r = riscv_info(t);
+ riscv013_info_t *info = get_info(t);
+ unsigned index = info->index;
+ LOG_DEBUG("index=%d, coreid=%d, prepped=%d", index, t->coreid, r->prepped);
+ if (r->prepped) {
+ hawindow[index / 32] |= 1 << (index % 32);
+ r->prepped = false;
+ total_selected++;
+ }
+ index++;
+ }
+
+ /* Don't use hasel if we only need to talk to one hart. */
+ if (total_selected <= 1) {
+ *use_hasel = false;
+ return ERROR_OK;
+ }
+
+ for (unsigned i = 0; i < hawindow_count; i++) {
+ if (dmi_write(target, DMI_HAWINDOWSEL, i) != ERROR_OK)
+ return ERROR_FAIL;
+ if (dmi_write(target, DMI_HAWINDOW, hawindow[i]) != ERROR_OK)
+ return ERROR_FAIL;
+ }
+
+ *use_hasel = true;
+ return ERROR_OK;
+}
+
+static int riscv013_resume_go(struct target *target)
+{
+ bool use_hasel = false;
+ if (!riscv_rtos_enabled(target)) {
+ if (select_prepped_harts(target, &use_hasel) != ERROR_OK)
+ return ERROR_FAIL;
+ }
+
+ return riscv013_step_or_resume_current_hart(target, false, use_hasel);
}
static int riscv013_step_current_hart(struct target *target)
{
- return riscv013_step_or_resume_current_hart(target, true);
+ return riscv013_step_or_resume_current_hart(target, true, false);
}
-static int riscv013_on_resume(struct target *target)
+static int riscv013_resume_prep(struct target *target)
{
return riscv013_on_step_or_resume(target, false);
}
@@ -3013,7 +3124,7 @@ riscv_insn_t riscv013_read_debug_buffer(struct target *target, unsigned index)
int riscv013_execute_debug_buffer(struct target *target)
{
uint32_t run_program = 0;
- run_program = set_field(run_program, AC_ACCESS_REGISTER_SIZE, 2);
+ run_program = set_field(run_program, AC_ACCESS_REGISTER_AARSIZE, 2);
run_program = set_field(run_program, AC_ACCESS_REGISTER_POSTEXEC, 1);
run_program = set_field(run_program, AC_ACCESS_REGISTER_TRANSFER, 0);
run_program = set_field(run_program, AC_ACCESS_REGISTER_REGNO, 0x1000);
@@ -3429,7 +3540,8 @@ static int riscv013_on_step_or_resume(struct target *target, bool step)
return riscv_set_register(target, GDB_REGNO_DCSR, dcsr);
}
-static int riscv013_step_or_resume_current_hart(struct target *target, bool step)
+static int riscv013_step_or_resume_current_hart(struct target *target,
+ bool step, bool use_hasel)
{
RISCV_INFO(r);
LOG_DEBUG("resuming hart %d (for step?=%d)", r->current_hartid, step);
@@ -3438,13 +3550,15 @@ static int riscv013_step_or_resume_current_hart(struct target *target, bool step
return ERROR_FAIL;
}
- if (maybe_execute_fence_i(target) != ERROR_OK)
- return ERROR_FAIL;
-
/* Issue the resume command, and then wait for the current hart to resume. */
- uint32_t dmcontrol = DMI_DMCONTROL_DMACTIVE;
+ uint32_t dmcontrol = DMI_DMCONTROL_DMACTIVE | DMI_DMCONTROL_RESUMEREQ;
+ if (use_hasel)
+ dmcontrol |= DMI_DMCONTROL_HASEL;
dmcontrol = set_hartsel(dmcontrol, r->current_hartid);
- dmi_write(target, DMI_DMCONTROL, dmcontrol | DMI_DMCONTROL_RESUMEREQ);
+ dmi_write(target, DMI_DMCONTROL, dmcontrol);
+
+ dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_HASEL, 0);
+ dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_RESUMEREQ, 0);
uint32_t dmstatus;
for (size_t i = 0; i < 256; ++i) {
@@ -3460,10 +3574,9 @@ static int riscv013_step_or_resume_current_hart(struct target *target, bool step
return ERROR_OK;
}
+ dmi_write(target, DMI_DMCONTROL, dmcontrol);
+
LOG_ERROR("unable to resume hart %d", r->current_hartid);
- if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK)
- return ERROR_FAIL;
- LOG_ERROR(" dmcontrol=0x%08x", dmcontrol);
if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
return ERROR_FAIL;
LOG_ERROR(" dmstatus =0x%08x", dmstatus);
@@ -3575,7 +3688,7 @@ int riscv013_test_compliance(struct target *target)
/* resumereq */
/* This bit is not actually readable according to the spec, so nothing to check.*/
- COMPLIANCE_MUST_PASS(riscv_resume_all_harts(target));
+ COMPLIANCE_MUST_PASS(riscv_resume(target, true, 0, false, false));
/* Halt all harts again so the test can continue.*/
COMPLIANCE_MUST_PASS(riscv_halt_all_harts(target));
diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c
index 7bae390..2fc3757 100644
--- a/src/target/riscv/riscv.c
+++ b/src/target/riscv/riscv.c
@@ -199,6 +199,8 @@ range_t *expose_csr;
/* Same, but for custom registers. */
range_t *expose_custom;
+static int riscv_resume_go_all_harts(struct target *target);
+
static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
{
struct scan_field field;
@@ -846,16 +848,116 @@ static int riscv_deassert_reset(struct target *target)
return tt->deassert_reset(target);
}
+int riscv_resume_prep_all_harts(struct target *target)
+{
+ RISCV_INFO(r);
+ for (int i = 0; i < riscv_count_harts(target); ++i) {
+ if (!riscv_hart_enabled(target, i))
+ continue;
-static int oldriscv_resume(struct target *target, int current, uint32_t address,
- int handle_breakpoints, int debug_execution)
+ LOG_DEBUG("prep hart %d", i);
+ if (riscv_set_current_hartid(target, i) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_is_halted(target)) {
+ if (r->resume_prep(target) != ERROR_OK)
+ return ERROR_FAIL;
+ } else {
+ LOG_DEBUG(" hart %d requested resume, but was already resumed", i);
+ }
+ }
+ return ERROR_OK;
+}
+
+/**
+ * Get everything ready to resume.
+ */
+static int resume_prep(struct target *target, int current,
+ target_addr_t address, int handle_breakpoints, int debug_execution)
{
- struct target_type *tt = get_target_type(target);
- return tt->resume(target, current, address, handle_breakpoints,
- debug_execution);
+ RISCV_INFO(r);
+ LOG_DEBUG("[%d]", target->coreid);
+
+ if (!current)
+ riscv_set_register(target, GDB_REGNO_PC, address);
+
+ if (target->debug_reason == DBG_REASON_WATCHPOINT) {
+ /* To be able to run off a trigger, disable all the triggers, step, and
+ * then resume as usual. */
+ struct watchpoint *watchpoint = target->watchpoints;
+ bool trigger_temporarily_cleared[RISCV_MAX_HWBPS] = {0};
+
+ int i = 0;
+ int result = ERROR_OK;
+ while (watchpoint && result == ERROR_OK) {
+ LOG_DEBUG("watchpoint %d: set=%d", i, watchpoint->set);
+ trigger_temporarily_cleared[i] = watchpoint->set;
+ if (watchpoint->set)
+ result = riscv_remove_watchpoint(target, watchpoint);
+ watchpoint = watchpoint->next;
+ i++;
+ }
+
+ if (result == ERROR_OK)
+ result = old_or_new_riscv_step(target, true, 0, false);
+
+ watchpoint = target->watchpoints;
+ i = 0;
+ while (watchpoint) {
+ LOG_DEBUG("watchpoint %d: cleared=%d", i, trigger_temporarily_cleared[i]);
+ if (trigger_temporarily_cleared[i]) {
+ if (result == ERROR_OK)
+ result = riscv_add_watchpoint(target, watchpoint);
+ else
+ riscv_add_watchpoint(target, watchpoint);
+ }
+ watchpoint = watchpoint->next;
+ i++;
+ }
+
+ if (result != ERROR_OK)
+ return result;
+ }
+
+ if (r->is_halted) {
+ if (riscv_resume_prep_all_harts(target) != ERROR_OK)
+ return ERROR_FAIL;
+ }
+
+ LOG_DEBUG("[%d] mark as prepped", target->coreid);
+ r->prepped = true;
+
+ return ERROR_OK;
}
-static int old_or_new_riscv_resume(
+/**
+ * Resume all the harts that have been prepped, as close to instantaneous as
+ * possible.
+ */
+static int resume_go(struct target *target, int current,
+ target_addr_t address, int handle_breakpoints, int debug_execution)
+{
+ riscv_info_t *r = riscv_info(target);
+ int result;
+ if (r->is_halted == NULL) {
+ struct target_type *tt = get_target_type(target);
+ result = tt->resume(target, current, address, handle_breakpoints,
+ debug_execution);
+ } else {
+ result = riscv_resume_go_all_harts(target);
+ }
+
+ return result;
+}
+
+static int resume_finish(struct target *target)
+{
+ register_cache_invalidate(target->reg_cache);
+
+ target->state = TARGET_RUNNING;
+ return target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
+}
+
+int riscv_resume(
struct target *target,
int current,
target_addr_t address,
@@ -863,31 +965,43 @@ static int old_or_new_riscv_resume(
int debug_execution
){
LOG_DEBUG("handle_breakpoints=%d", handle_breakpoints);
+ int result = ERROR_OK;
if (target->smp) {
- struct target_list *targets = target->head;
- int result = ERROR_OK;
- while (targets) {
- struct target *t = targets->target;
- riscv_info_t *r = riscv_info(t);
- if (r->is_halted == NULL) {
- if (oldriscv_resume(t, current, address, handle_breakpoints,
+ for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) {
+ struct target *t = tlist->target;
+ if (resume_prep(t, current, address, handle_breakpoints,
+ debug_execution) != ERROR_OK)
+ result = ERROR_FAIL;
+ }
+
+ for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) {
+ struct target *t = tlist->target;
+ riscv_info_t *i = riscv_info(t);
+ if (i->prepped) {
+ if (resume_go(t, current, address, handle_breakpoints,
debug_execution) != ERROR_OK)
result = ERROR_FAIL;
- } else {
- if (riscv_openocd_resume(t, current, address,
- handle_breakpoints, debug_execution) != ERROR_OK)
- result = ERROR_FAIL;
}
- targets = targets->next;
}
- return result;
+
+ for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) {
+ struct target *t = tlist->target;
+ if (resume_finish(t) != ERROR_OK)
+ return ERROR_FAIL;
+ }
+
+ } else {
+ if (resume_prep(target, current, address, handle_breakpoints,
+ debug_execution) != ERROR_OK)
+ result = ERROR_FAIL;
+ if (resume_go(target, current, address, handle_breakpoints,
+ debug_execution) != ERROR_OK)
+ result = ERROR_FAIL;
+ if (resume_finish(target) != ERROR_OK)
+ return ERROR_FAIL;
}
- RISCV_INFO(r);
- if (r->is_halted == NULL)
- return oldriscv_resume(target, current, address, handle_breakpoints, debug_execution);
- else
- return riscv_openocd_resume(target, current, address, handle_breakpoints, debug_execution);
+ return result;
}
static int riscv_select_current_hart(struct target *target)
@@ -958,17 +1072,21 @@ static int riscv_get_gdb_reg_list_internal(struct target *target,
if (read && !target->reg_cache->reg_list[i].valid) {
if (target->reg_cache->reg_list[i].type->get(
&target->reg_cache->reg_list[i]) != ERROR_OK)
- /* This function is called when first connecting to gdb,
- * resulting in an attempt to read all kinds of registers which
- * probably will fail. Ignore these failures, and when
- * encountered stop reading to save time. */
- read = false;
+ return ERROR_FAIL;
}
}
return ERROR_OK;
}
+static int riscv_get_gdb_reg_list_noread(struct target *target,
+ struct reg **reg_list[], int *reg_list_size,
+ enum target_register_class reg_class)
+{
+ return riscv_get_gdb_reg_list_internal(target, reg_list, reg_list_size,
+ reg_class, false);
+}
+
static int riscv_get_gdb_reg_list(struct target *target,
struct reg **reg_list[], int *reg_list_size,
enum target_register_class reg_class)
@@ -1060,7 +1178,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
/* Run algorithm */
LOG_DEBUG("resume at 0x%" TARGET_PRIxADDR, entry_point);
- if (oldriscv_resume(target, 0, entry_point, 0, 0) != ERROR_OK)
+ if (riscv_resume(target, 0, entry_point, 0, 0) != ERROR_OK)
return ERROR_FAIL;
int64_t start = timeval_ms();
@@ -1344,68 +1462,6 @@ int riscv_openocd_halt(struct target *target)
return result;
}
-int riscv_openocd_resume(
- struct target *target,
- int current,
- target_addr_t address,
- int handle_breakpoints,
- int debug_execution)
-{
- LOG_DEBUG("debug_reason=%d", target->debug_reason);
-
- if (!current)
- riscv_set_register(target, GDB_REGNO_PC, address);
-
- if (target->debug_reason == DBG_REASON_WATCHPOINT) {
- /* To be able to run off a trigger, disable all the triggers, step, and
- * then resume as usual. */
- struct watchpoint *watchpoint = target->watchpoints;
- bool trigger_temporarily_cleared[RISCV_MAX_HWBPS] = {0};
-
- int i = 0;
- int result = ERROR_OK;
- while (watchpoint && result == ERROR_OK) {
- LOG_DEBUG("watchpoint %d: set=%d", i, watchpoint->set);
- trigger_temporarily_cleared[i] = watchpoint->set;
- if (watchpoint->set)
- result = riscv_remove_watchpoint(target, watchpoint);
- watchpoint = watchpoint->next;
- i++;
- }
-
- if (result == ERROR_OK)
- result = riscv_step_rtos_hart(target);
-
- watchpoint = target->watchpoints;
- i = 0;
- while (watchpoint) {
- LOG_DEBUG("watchpoint %d: cleared=%d", i, trigger_temporarily_cleared[i]);
- if (trigger_temporarily_cleared[i]) {
- if (result == ERROR_OK)
- result = riscv_add_watchpoint(target, watchpoint);
- else
- riscv_add_watchpoint(target, watchpoint);
- }
- watchpoint = watchpoint->next;
- i++;
- }
-
- if (result != ERROR_OK)
- return result;
- }
-
- int out = riscv_resume_all_harts(target);
- if (out != ERROR_OK) {
- LOG_ERROR("unable to resume all harts");
- return out;
- }
-
- register_cache_invalidate(target->reg_cache);
- target->state = TARGET_RUNNING;
- target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
- return out;
-}
-
int riscv_openocd_step(
struct target *target,
int current,
@@ -1964,7 +2020,7 @@ struct target_type riscv_target = {
.poll = old_or_new_riscv_poll,
.halt = old_or_new_riscv_halt,
- .resume = old_or_new_riscv_resume,
+ .resume = riscv_resume,
.step = old_or_new_riscv_step,
.assert_reset = riscv_assert_reset,
@@ -1976,6 +2032,7 @@ struct target_type riscv_target = {
.checksum_memory = riscv_checksum_memory,
.get_gdb_reg_list = riscv_get_gdb_reg_list,
+ .get_gdb_reg_list_noread = riscv_get_gdb_reg_list_noread,
.add_breakpoint = riscv_add_breakpoint,
.remove_breakpoint = riscv_remove_breakpoint,
@@ -2042,34 +2099,28 @@ int riscv_halt_one_hart(struct target *target, int hartid)
return result;
}
-int riscv_resume_all_harts(struct target *target)
+static int riscv_resume_go_all_harts(struct target *target)
{
+ RISCV_INFO(r);
for (int i = 0; i < riscv_count_harts(target); ++i) {
if (!riscv_hart_enabled(target, i))
continue;
- riscv_resume_one_hart(target, i);
+ LOG_DEBUG("resuming hart %d", i);
+ if (riscv_set_current_hartid(target, i) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_is_halted(target)) {
+ if (r->resume_go(target) != ERROR_OK)
+ return ERROR_FAIL;
+ } else {
+ LOG_DEBUG(" hart %d requested resume, but was already resumed", i);
+ }
}
riscv_invalidate_register_cache(target);
return ERROR_OK;
}
-int riscv_resume_one_hart(struct target *target, int hartid)
-{
- RISCV_INFO(r);
- LOG_DEBUG("resuming hart %d", hartid);
- if (riscv_set_current_hartid(target, hartid) != ERROR_OK)
- return ERROR_FAIL;
- if (!riscv_is_halted(target)) {
- LOG_DEBUG(" hart %d requested resume, but was already resumed", hartid);
- return ERROR_OK;
- }
-
- r->on_resume(target);
- return r->resume_current_hart(target);
-}
-
int riscv_step_rtos_hart(struct target *target)
{
RISCV_INFO(r);
@@ -2130,7 +2181,7 @@ int riscv_xlen_of_hart(const struct target *target, int hartid)
extern struct rtos_type riscv_rtos;
bool riscv_rtos_enabled(const struct target *target)
{
- return false;
+ return target->rtos && target->rtos->type == &riscv_rtos;
}
int riscv_set_current_hartid(struct target *target, int hartid)
@@ -2192,9 +2243,9 @@ int riscv_count_harts(struct target *target)
if (target == NULL)
return 1;
RISCV_INFO(r);
- if (r == NULL)
+ if (r == NULL || r->hart_count == NULL)
return 1;
- return r->hart_count;
+ return r->hart_count(target);
}
bool riscv_has_register(struct target *target, int hartid, int regid)
diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h
index 59414fc..ade1bf0 100644
--- a/src/target/riscv/riscv.h
+++ b/src/target/riscv/riscv.h
@@ -46,9 +46,6 @@ typedef struct {
struct command_context *cmd_ctx;
void *version_specific;
- /* The number of harts on this system. */
- int hart_count;
-
/* The hart that the RTOS thinks is currently being debugged. */
int rtos_hartid;
@@ -100,6 +97,9 @@ typedef struct {
* delays, causing them to be relearned. Used for testing. */
int reset_delays_wait;
+ /* This target has been prepped and is ready to step/resume. */
+ bool prepped;
+
/* Helper functions that target the various RISC-V debug spec
* implementations. */
int (*get_register)(struct target *target,
@@ -109,10 +109,15 @@ typedef struct {
int (*select_current_hart)(struct target *);
bool (*is_halted)(struct target *target);
int (*halt_current_hart)(struct target *);
- int (*resume_current_hart)(struct target *target);
+ /* Resume this target, as well as every other prepped target that can be
+ * resumed near-simultaneously. Clear the prepped flag on any target that
+ * was resumed. */
+ int (*resume_go)(struct target *target);
int (*step_current_hart)(struct target *target);
int (*on_halt)(struct target *target);
- int (*on_resume)(struct target *target);
+ /* Get this target as ready as possible to resume, without actually
+ * resuming. */
+ int (*resume_prep)(struct target *target);
int (*on_step)(struct target *target);
enum riscv_halt_reason (*halt_reason)(struct target *target);
int (*write_debug_buffer)(struct target *target, unsigned index,
@@ -134,6 +139,9 @@ typedef struct {
uint32_t num_words, target_addr_t illegal_address, bool run_sbbusyerror_test);
int (*test_compliance)(struct target *target);
+
+ /* How many harts are attached to the DM that this target is attached to? */
+ int (*hart_count)(struct target *target);
} riscv_info_t;
/* Wall-clock timeout for a command/access. Settable via RISC-V Target commands.*/
@@ -163,7 +171,7 @@ int riscv_openocd_poll(struct target *target);
int riscv_openocd_halt(struct target *target);
-int riscv_openocd_resume(
+int riscv_resume(
struct target *target,
int current,
target_addr_t address,
@@ -191,8 +199,6 @@ void riscv_info_init(struct target *target, riscv_info_t *r);
* the system. */
int riscv_halt_all_harts(struct target *target);
int riscv_halt_one_hart(struct target *target, int hartid);
-int riscv_resume_all_harts(struct target *target);
-int riscv_resume_one_hart(struct target *target, int hartid);
/* Steps the hart that's currently selected in the RTOS, or if there is no RTOS
* then the only hart. */
diff --git a/src/target/target.c b/src/target/target.c
index 5295dd6..a99e981 100644
--- a/src/target/target.c
+++ b/src/target/target.c
@@ -1224,6 +1224,17 @@ int target_get_gdb_reg_list(struct target *target,
return target->type->get_gdb_reg_list(target, reg_list, reg_list_size, reg_class);
}
+int target_get_gdb_reg_list_noread(struct target *target,
+ struct reg **reg_list[], int *reg_list_size,
+ enum target_register_class reg_class)
+{
+ if (target->type->get_gdb_reg_list_noread &&
+ target->type->get_gdb_reg_list_noread(target, reg_list,
+ reg_list_size, reg_class) == ERROR_OK)
+ return ERROR_OK;
+ return target_get_gdb_reg_list(target, reg_list, reg_list_size, reg_class);
+}
+
bool target_supports_gdb_connection(struct target *target)
{
/*
@@ -1593,8 +1604,9 @@ int target_call_event_callbacks(struct target *target, enum target_event event)
target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT);
}
- LOG_DEBUG("target event %i (%s)", event,
- Jim_Nvp_value2name_simple(nvp_target_event, event)->name);
+ LOG_DEBUG("target event %i (%s) for core %d", event,
+ Jim_Nvp_value2name_simple(nvp_target_event, event)->name,
+ target->coreid);
target_handle_event(target, event);
@@ -2824,6 +2836,7 @@ COMMAND_HANDLER(handle_reg_command)
struct reg *reg = NULL;
unsigned count = 0;
char *value;
+ int retval;
LOG_DEBUG("-");
@@ -2845,21 +2858,23 @@ COMMAND_HANDLER(handle_reg_command)
if (reg->exist == false)
continue;
/* only print cached values if they are valid */
- if (reg->valid) {
- value = buf_to_str(reg->value,
- reg->size, 16);
- command_print(CMD_CTX,
- "(%i) %s (/%" PRIu32 "): 0x%s%s",
- count, reg->name,
- reg->size, value,
- reg->dirty
+ if (reg->exist) {
+ if (reg->valid) {
+ value = buf_to_str(reg->value,
+ reg->size, 16);
+ command_print(CMD_CTX,
+ "(%i) %s (/%" PRIu32 "): 0x%s%s",
+ count, reg->name,
+ reg->size, value,
+ reg->dirty
? " (dirty)"
: "");
- free(value);
- } else {
- command_print(CMD_CTX, "(%i) %s (/%" PRIu32 ")",
- count, reg->name,
- reg->size) ;
+ free(value);
+ } else {
+ command_print(CMD_CTX, "(%i) %s (/%" PRIu32 ")",
+ count, reg->name,
+ reg->size) ;
+ }
}
}
cache = cache->next;
@@ -2912,8 +2927,13 @@ COMMAND_HANDLER(handle_reg_command)
if ((CMD_ARGC == 2) && (strcmp(CMD_ARGV[1], "force") == 0))
reg->valid = 0;
- if (reg->valid == 0)
- reg->type->get(reg);
+ if (reg->valid == 0) {
+ retval = reg->type->get(reg);
+ if (retval != ERROR_OK) {
+ LOG_DEBUG("Couldn't get register %s.", reg->name);
+ return retval;
+ }
+ }
value = buf_to_str(reg->value, reg->size, 16);
command_print(CMD_CTX, "%s (/%i): 0x%s", reg->name, (int)(reg->size), value);
free(value);
@@ -2927,7 +2947,12 @@ COMMAND_HANDLER(handle_reg_command)
return ERROR_FAIL;
str_to_buf(CMD_ARGV[1], strlen(CMD_ARGV[1]), buf, reg->size, 0);
- reg->type->set(reg, buf);
+ retval = reg->type->set(reg, buf);
+ if (retval != ERROR_OK) {
+ LOG_DEBUG("Couldn't set register %s.", reg->name);
+ free(buf);
+ return retval;
+ }
value = buf_to_str(reg->value, reg->size, 16);
command_print(CMD_CTX, "%s (/%i): 0x%s", reg->name, (int)(reg->size), value);
@@ -5825,6 +5850,7 @@ static int jim_target_smp(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
int i;
const char *targetname;
int retval, len;
+ static int smp_group = 1;
struct target *target = (struct target *) NULL;
struct target_list *head, *curr, *new;
curr = (struct target_list *) NULL;
@@ -5860,10 +5886,11 @@ static int jim_target_smp(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
while (curr != (struct target_list *)NULL) {
target = curr->target;
- target->smp = 1;
+ target->smp = smp_group;
target->head = head;
curr = curr->next;
}
+ smp_group++;
if (target && target->rtos)
retval = rtos_smp_init(head->target);
diff --git a/src/target/target.h b/src/target/target.h
index 65494af..8dde03d 100644
--- a/src/target/target.h
+++ b/src/target/target.h
@@ -502,6 +502,16 @@ int target_get_gdb_reg_list(struct target *target,
enum target_register_class reg_class);
/**
+ * Obtain the registers for GDB, but don't read register values from the
+ * target.
+ *
+ * This routine is a wrapper for target->type->get_gdb_reg_list_noread.
+ */
+int target_get_gdb_reg_list_noread(struct target *target,
+ struct reg **reg_list[], int *reg_list_size,
+ enum target_register_class reg_class);
+
+/**
* Check if @a target allows GDB connections.
*
* Some target do not implement the necessary code required by GDB.
diff --git a/src/target/target_type.h b/src/target/target_type.h
index 95745c9..4bdea72 100644
--- a/src/target/target_type.h
+++ b/src/target/target_type.h
@@ -111,6 +111,13 @@ struct target_type {
int (*get_gdb_reg_list)(struct target *target, struct reg **reg_list[],
int *reg_list_size, enum target_register_class reg_class);
+ /**
+ * Same as get_gdb_reg_list, but doesn't read the register values.
+ * */
+ int (*get_gdb_reg_list_noread)(struct target *target,
+ struct reg **reg_list[], int *reg_list_size,
+ enum target_register_class reg_class);
+
/* target memory access
* size: 1 = byte (8bit), 2 = half-word (16bit), 4 = word (32bit)
* count: number of items of <size>
diff --git a/tcl/board/sifive-e31arty-cjtag.cfg b/tcl/board/sifive-e31arty-cjtag.cfg
new file mode 100644
index 0000000..58ba23a
--- /dev/null
+++ b/tcl/board/sifive-e31arty-cjtag.cfg
@@ -0,0 +1,23 @@
+#
+# Be sure you include the speed and interface before this file
+# Example:
+# -c "adapter_khz 5000" -f "interface/ftdi/olimex-arm-usb-tiny-h.cfg" -f "board/sifive-e31arty-cjtag.cfg"
+
+set _CHIPNAME riscv
+jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x20000913
+
+
+set _TARGETNAME $_CHIPNAME.cpu
+
+target create $_TARGETNAME.0 riscv -chain-position $_TARGETNAME
+$_TARGETNAME.0 configure -work-area-phys 0x80000000 -work-area-size 10000 -work-area-backup 1
+
+flash bank spi0 fespi 0x40000000 0 0 0 $_TARGETNAME.0 0x20004000
+init
+if {[ info exists pulse_srst]} {
+ oscan1_ftdi_set_signal nSRST 0
+ oscan1_ftdi_set_signal nSRST z
+}
+halt
+flash protect 0 64 last off
+echo "Ready for Remote Connections"
diff --git a/tcl/interface/ftdi/olimex-arm-jtag-cjtag.cfg b/tcl/interface/ftdi/olimex-arm-jtag-cjtag.cfg
new file mode 100644
index 0000000..ad6d4a4
--- /dev/null
+++ b/tcl/interface/ftdi/olimex-arm-jtag-cjtag.cfg
@@ -0,0 +1,27 @@
+#
+# Olimex ARM JTAG SWD adapter
+# https://www.olimex.com/Products/ARM/JTAG/ARM-JTAG-SWD/
+#
+
+#
+# Olimex ARM-USB-TINY-H
+#
+# http://www.olimex.com/dev/arm-usb-tiny-h.html
+#
+
+interface ftdi
+ftdi_oscan1_mode on
+ftdi_device_desc "Olimex OpenOCD JTAG ARM-USB-TINY-H"
+ftdi_vid_pid 0x15ba 0x002a
+
+ftdi_layout_init 0x0808 0x0a1b
+ftdi_layout_signal nSRST -oe 0x0200
+# oscan1_ftdi_layout_signal nTRST -data 0x0100 -oe 0x0100
+ftdi_layout_signal LED -data 0x0800
+
+# These signals are used for cJTAG escape sequence on initialization only
+ftdi_layout_signal TCK -data 0x0001
+ftdi_layout_signal TDI -data 0x0002
+ftdi_layout_signal TDO -input 0x0004
+ftdi_layout_signal TMS -data 0x0008
+ftdi_layout_signal JTAG_SEL -data 0x0100 -oe 0x0100
diff --git a/tcl/target/1986ве1т.cfg b/tcl/target/1986Be1T.cfg
index ecb3f8a..ecb3f8a 100644
--- a/tcl/target/1986ве1т.cfg
+++ b/tcl/target/1986Be1T.cfg
diff --git a/tcl/target/к1879xб1я.cfg b/tcl/target/K1879x61R.cfg
index 7d8c113..7d8c113 100644
--- a/tcl/target/к1879xб1я.cfg
+++ b/tcl/target/K1879x61R.cfg
diff --git a/tools/filter_openocd_log.py b/tools/filter_openocd_log.py
new file mode 100755
index 0000000..da17b17
--- /dev/null
+++ b/tools/filter_openocd_log.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python
+
+import sys
+import re
+
+# This function is the only OpenOCD-specific part of this script.
+def make_canonical(line):
+ # Remove the line number and time stamp.
+ m = re.match(r"(Debug|Error|Info |User |Warn ): \d+ \d+ (.*)", line)
+ if m:
+ return "%s: - - %s\n" % (m.group(1), m.group(2))
+ else:
+ return line
+
+def buf_startswith(buf, sequence):
+ if len(buf) < len(sequence):
+ return False
+ for i, entry in enumerate(sequence):
+ if entry[1] != buf[i][1]:
+ return False
+ return True
+
+def shorten_buffer(outfd, buf, current_repetition):
+ """Do something to the buffer to make it shorter. If we can't compress
+ anything, then print out the first line and remove it."""
+ length_before = len(buf)
+
+ if current_repetition:
+ while buf_startswith(buf, current_repetition[0]):
+ del buf[:len(current_repetition[0])]
+ current_repetition[1] += 1
+ if len(buf) < length_before:
+ return current_repetition
+ outfd.write("## The following %d lines repeat %d times:\n" % (
+ len(current_repetition[0]), current_repetition[1]))
+ for entry in current_repetition[0]:
+ outfd.write("# %s" % entry[1])
+
+ # Look for repeated sequences...
+ repetitions = []
+ for length in range(1, len(buf)/2):
+ # Is there a repeating sequence of `length` lines?
+ matched_lines = 0
+ for i, entry in enumerate(buf[length:]):
+ if entry[1] == buf[i % length][1]:
+ matched_lines += 1
+ else:
+ break
+ if matched_lines >= length:
+ repetitions.append((matched_lines + length, length))
+
+ if repetitions:
+ repetitions.sort(key=lambda entry: (entry[0] * (entry[1] / entry[0]), -entry[1]))
+ matched_lines, length = repetitions[-1]
+ repeated = matched_lines / length
+ if repeated * length >= 3:
+ sequence = buf[:length]
+ del buf[:repeated * length]
+
+ if matched_lines == length_before:
+ # Could be continued...
+ return [sequence, repeated]
+
+ else:
+ outfd.write("## The following %d lines repeat %d times:\n" %
+ (length, repeated))
+ for entry in sequence:
+ outfd.write("# %s" % entry[1])
+ return None
+
+ if len(buf) >= length_before:
+ line, _ = buf[0]
+ outfd.write(line)
+ buf.pop(0)
+
+ if length_before <= len(buf):
+ print "Buffer:"
+ for entry in buf:
+ print "%r" % entry[0]
+ assert False
+
+ return None
+
+def compress_log(infd, outfd, window):
+ """Compress log by finding repeated runs of lines. Runs in O(lines *
+ window**2), which can probably be improved."""
+ # Contains line, canonical tuples
+ buf = []
+ current_repetition = None
+
+ for line in infd:
+ buf.append((line, make_canonical(line)))
+ if len(buf) > window:
+ current_repetition = shorten_buffer(outfd, buf, current_repetition)
+
+ while len(buf) > 0:
+ current_repetition = shorten_buffer(outfd, buf, current_repetition)
+
+def main(args):
+ import argparse
+ parser = argparse.ArgumentParser(
+ description='Combine repeated OpenOCD debug output lines. This is '
+ 'very helpful when looking at verbose log files where e.g. target '
+ 'polling is repeated over and over.',
+ epilog='If no files are specified, read standard input.',
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+ parser.add_argument('file', nargs='*', help='input file')
+ parser.add_argument('-o', '--output', help='output file', default=sys.stdout)
+ parser.add_argument('-w', '--window', type=int, default=100,
+ help='number of lines to consider when looking for repetitions')
+ args = parser.parse_args(args)
+
+ if args.file:
+ for f in args.file:
+ compress_log(open(f, "r"), args.output, args.window)
+ else:
+ compress_log(sys.stdin, args.output, args.window)
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/tools/git2cl b/tools/git2cl
deleted file mode 160000
-Subproject 8373c9f74993e218a08819cbcdbab3f3564bbeb
diff --git a/tools/git2cl/COPYING b/tools/git2cl/COPYING
new file mode 100644
index 0000000..d511905
--- /dev/null
+++ b/tools/git2cl/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 2 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, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/tools/git2cl/README b/tools/git2cl/README
new file mode 100644
index 0000000..4084080
--- /dev/null
+++ b/tools/git2cl/README
@@ -0,0 +1,45 @@
+git2cl
+======
+
+This is a quick'n'dirty tool to convert git logs to GNU ChangeLog
+format.
+
+The tool invokes 'git log' internally unless you pipe a log to it.
+Thus, typically you would use it as follows:
+
+...........................................................................
+jas@mocca:~/src/libtasn1$ git2cl > ChangeLog
+jas@mocca:~/src/libtasn1$
+...........................................................................
+
+If you don't want git2cl to invoke git log internally, you can use it
+as a pipe. It needs a git log generated with --pretty --numstat and
+--summary. You can use it as follows:
+
+...........................................................................
+jas@mocca:~/src/libtasn1$ git log --pretty --numstat --summary | ~/src/git2cl/git2cl > ChangeLog
+jas@mocca:~/src/libtasn1$
+...........................................................................
+
+The output format is specified by:
+
+link:http://www.gnu.org/prep/standards/html_node/Change-Logs.html[]
+
+My inspiration for writing this tool was the
+link:http://www.red-bean.com/cvs2cl/[cvs2cl] tool, which I have been
+using in several projects. Replacing it was necessary to seriously
+consider switching from CVS to GIT for my projects.
+
+The canonical home page for git2cl is:
+link:http://josefsson.org/git2cl/[] and its repository can be found at
+link:http://repo.or.cz/w/git2cl.git[].
+
+Credits
+-------
+
+Luis Mondesi contributed several improvements.
+
+Support
+-------
+
+Try talking to mailto:simon@josefsson.org[Simon Josefsson].
diff --git a/tools/git2cl/README.html b/tools/git2cl/README.html
new file mode 100644
index 0000000..f82da5d
--- /dev/null
+++ b/tools/git2cl/README.html
@@ -0,0 +1,392 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<meta name="generator" content="AsciiDoc 8.2.7" />
+<style type="text/css">
+/* Debug borders */
+p, li, dt, dd, div, pre, h1, h2, h3, h4, h5, h6 {
+/*
+ border: 1px solid red;
+*/
+}
+
+body {
+ margin: 1em 5% 1em 5%;
+}
+
+a {
+ color: blue;
+ text-decoration: underline;
+}
+a:visited {
+ color: fuchsia;
+}
+
+em {
+ font-style: italic;
+ color: navy;
+}
+
+strong {
+ font-weight: bold;
+ color: #083194;
+}
+
+tt {
+ color: navy;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ color: #527bbd;
+ font-family: sans-serif;
+ margin-top: 1.2em;
+ margin-bottom: 0.5em;
+ line-height: 1.3;
+}
+
+h1, h2, h3 {
+ border-bottom: 2px solid silver;
+}
+h2 {
+ padding-top: 0.5em;
+}
+h3 {
+ float: left;
+}
+h3 + * {
+ clear: left;
+}
+
+div.sectionbody {
+ font-family: serif;
+ margin-left: 0;
+}
+
+hr {
+ border: 1px solid silver;
+}
+
+p {
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+}
+
+ul, ol, li > p {
+ margin-top: 0;
+}
+
+pre {
+ padding: 0;
+ margin: 0;
+}
+
+span#author {
+ color: #527bbd;
+ font-family: sans-serif;
+ font-weight: bold;
+ font-size: 1.1em;
+}
+span#email {
+}
+span#revision {
+ font-family: sans-serif;
+}
+
+div#footer {
+ font-family: sans-serif;
+ font-size: small;
+ border-top: 2px solid silver;
+ padding-top: 0.5em;
+ margin-top: 4.0em;
+}
+div#footer-text {
+ float: left;
+ padding-bottom: 0.5em;
+}
+div#footer-badges {
+ float: right;
+ padding-bottom: 0.5em;
+}
+
+div#preamble,
+div.tableblock, div.imageblock, div.exampleblock, div.verseblock,
+div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
+div.admonitionblock {
+ margin-right: 10%;
+ margin-top: 1.5em;
+ margin-bottom: 1.5em;
+}
+div.admonitionblock {
+ margin-top: 2.5em;
+ margin-bottom: 2.5em;
+}
+
+div.content { /* Block element content. */
+ padding: 0;
+}
+
+/* Block element titles. */
+div.title, caption.title {
+ color: #527bbd;
+ font-family: sans-serif;
+ font-weight: bold;
+ text-align: left;
+ margin-top: 1.0em;
+ margin-bottom: 0.5em;
+}
+div.title + * {
+ margin-top: 0;
+}
+
+td div.title:first-child {
+ margin-top: 0.0em;
+}
+div.content div.title:first-child {
+ margin-top: 0.0em;
+}
+div.content + div.title {
+ margin-top: 0.0em;
+}
+
+div.sidebarblock > div.content {
+ background: #ffffee;
+ border: 1px solid silver;
+ padding: 0.5em;
+}
+
+div.listingblock {
+ margin-right: 0%;
+}
+div.listingblock > div.content {
+ border: 1px solid silver;
+ background: #f4f4f4;
+ padding: 0.5em;
+}
+
+div.quoteblock {
+ padding-left: 2.0em;
+}
+div.quoteblock > div.attribution {
+ padding-top: 0.5em;
+ text-align: right;
+}
+
+div.verseblock {
+ padding-left: 2.0em;
+}
+div.verseblock > div.content {
+ white-space: pre;
+}
+div.verseblock > div.attribution {
+ padding-top: 0.75em;
+ text-align: left;
+}
+/* DEPRECATED: Pre version 8.2.7 verse style literal block. */
+div.verseblock + div.attribution {
+ text-align: left;
+}
+
+div.admonitionblock .icon {
+ vertical-align: top;
+ font-size: 1.1em;
+ font-weight: bold;
+ text-decoration: underline;
+ color: #527bbd;
+ padding-right: 0.5em;
+}
+div.admonitionblock td.content {
+ padding-left: 0.5em;
+ border-left: 2px solid silver;
+}
+
+div.exampleblock > div.content {
+ border-left: 2px solid silver;
+ padding: 0.5em;
+}
+
+div.imageblock div.content { padding-left: 0; }
+div.imageblock img { border: 1px solid silver; }
+span.image img { border-style: none; }
+
+dl {
+ margin-top: 0.8em;
+ margin-bottom: 0.8em;
+}
+dt {
+ margin-top: 0.5em;
+ margin-bottom: 0;
+ font-style: normal;
+}
+dd > *:first-child {
+ margin-top: 0.1em;
+}
+
+ul, ol {
+ list-style-position: outside;
+}
+div.olist > ol {
+ list-style-type: decimal;
+}
+div.olist2 > ol {
+ list-style-type: lower-alpha;
+}
+
+div.tableblock > table {
+ border: 3px solid #527bbd;
+}
+thead {
+ font-family: sans-serif;
+ font-weight: bold;
+}
+tfoot {
+ font-weight: bold;
+}
+
+div.hlist {
+ margin-top: 0.8em;
+ margin-bottom: 0.8em;
+}
+div.hlist td {
+ padding-bottom: 15px;
+}
+td.hlist1 {
+ vertical-align: top;
+ font-style: normal;
+ padding-right: 0.8em;
+}
+td.hlist2 {
+ vertical-align: top;
+}
+
+@media print {
+ div#footer-badges { display: none; }
+}
+
+div#toctitle {
+ color: #527bbd;
+ font-family: sans-serif;
+ font-size: 1.1em;
+ font-weight: bold;
+ margin-top: 1.0em;
+ margin-bottom: 0.1em;
+}
+
+div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+div.toclevel2 {
+ margin-left: 2em;
+ font-size: 0.9em;
+}
+div.toclevel3 {
+ margin-left: 4em;
+ font-size: 0.9em;
+}
+div.toclevel4 {
+ margin-left: 6em;
+ font-size: 0.9em;
+}
+/* Workarounds for IE6's broken and incomplete CSS2. */
+
+div.sidebar-content {
+ background: #ffffee;
+ border: 1px solid silver;
+ padding: 0.5em;
+}
+div.sidebar-title, div.image-title {
+ color: #527bbd;
+ font-family: sans-serif;
+ font-weight: bold;
+ margin-top: 0.0em;
+ margin-bottom: 0.5em;
+}
+
+div.listingblock div.content {
+ border: 1px solid silver;
+ background: #f4f4f4;
+ padding: 0.5em;
+}
+
+div.quoteblock-attribution {
+ padding-top: 0.5em;
+ text-align: right;
+}
+
+div.verseblock-content {
+ white-space: pre;
+}
+div.verseblock-attribution {
+ padding-top: 0.75em;
+ text-align: left;
+}
+
+div.exampleblock-content {
+ border-left: 2px solid silver;
+ padding-left: 0.5em;
+}
+
+/* IE6 sets dynamically generated links as visited. */
+div#toc a:visited { color: blue; }
+
+/* Because IE6 child selector is broken. */
+div.olist2 ol {
+ list-style-type: lower-alpha;
+}
+div.olist2 div.olist ol {
+ list-style-type: decimal;
+}
+</style>
+<title>git2cl</title>
+</head>
+<body>
+<div id="header">
+<h1>git2cl</h1>
+</div>
+<div id="preamble">
+<div class="sectionbody">
+<div class="para"><p>This is a quick'n'dirty tool to convert git logs to GNU ChangeLog
+format.</p></div>
+<div class="para"><p>The tool invokes <em>git log</em> internally unless you pipe a log to it.
+Thus, typically you would use it as follows:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>jas@mocca:~/src/libtasn1$ git2cl &gt; ChangeLog
+jas@mocca:~/src/libtasn1$</tt></pre>
+</div></div>
+<div class="para"><p>If you don't want git2cl to invoke git log internally, you can use it
+as a pipe. It needs a git log generated with &#8212;pretty &#8212;numstat and
+&#8212;summary. You can use it as follows:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>jas@mocca:~/src/libtasn1$ git log --pretty --numstat --summary | ~/src/git2cl/git2cl &gt; ChangeLog
+jas@mocca:~/src/libtasn1$</tt></pre>
+</div></div>
+<div class="para"><p>The output format is specified by:</p></div>
+<div class="para"><p><a href="http://www.gnu.org/prep/standards/html_node/Change-Logs.html">http://www.gnu.org/prep/standards/html_node/Change-Logs.html</a></p></div>
+<div class="para"><p>My inspiration for writing this tool was the
+<a href="http://www.red-bean.com/cvs2cl/">cvs2cl</a> tool, which I have been
+using in several projects. Replacing it was necessary to seriously
+consider switching from CVS to GIT for my projects.</p></div>
+<div class="para"><p>The canonical home page for git2cl is:
+<a href="http://josefsson.org/git2cl/">http://josefsson.org/git2cl/</a> and its repository can be found at
+<a href="http://repo.or.cz/w/git2cl.git">http://repo.or.cz/w/git2cl.git</a>.</p></div>
+</div>
+</div>
+<h2 id="_credits">Credits</h2>
+<div class="sectionbody">
+<div class="para"><p>Luis Mondesi contributed several improvements.</p></div>
+</div>
+<h2 id="_support">Support</h2>
+<div class="sectionbody">
+<div class="para"><p>Try talking to <a href="mailto:simon@josefsson.org">Simon Josefsson</a>.</p></div>
+</div>
+<div id="footer">
+<div id="footer-text">
+Last updated 2008-08-27 12:42:17 CEST
+</div>
+</div>
+</body>
+</html>
diff --git a/tools/git2cl/git2cl b/tools/git2cl/git2cl
new file mode 100755
index 0000000..1c2ab3f
--- /dev/null
+++ b/tools/git2cl/git2cl
@@ -0,0 +1,372 @@
+#!/usr/bin/perl
+
+# Copyright (C) 2007, 2008 Simon Josefsson <simon@josefsson.org>
+# Copyright (C) 2007 Luis Mondesi <lemsx1@gmail.com>
+# * calls git directly. To use it just:
+# cd ~/Project/my_git_repo; git2cl > ChangeLog
+# * implements strptime()
+# * fixes bugs in $comment parsing
+# - copy input before we remove leading spaces
+# - skip "merge branch" statements as they don't
+# have information about files (i.e. we never
+# go into $state 2)
+# - behaves like a pipe/filter if input is given from the CLI
+# else it calls git log by itself
+#
+# The functions mywrap, last_line_len, wrap_log_entry are derived from
+# the cvs2cl tool, see <http://www.red-bean.com/cvs2cl/>:
+# Copyright (C) 2001,2002,2003,2004 Martyn J. Pearce <fluffy@cpan.org>
+# Copyright (C) 1999 Karl Fogel <kfogel@red-bean.com>
+#
+# git2cl 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 2, or (at your option)
+# any later version.
+#
+# git2cl 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 git2cl; see the file COPYING. If not, write to the Free
+# Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+use strict;
+use POSIX qw(strftime);
+use Text::Wrap qw(wrap);
+use FileHandle;
+
+use constant EMPTY_LOG_MESSAGE => '*** empty log message ***';
+
+# this is a helper hash for stptime.
+# Assumes you are calling 'git log ...' with LC_ALL=C
+my %month = (
+ 'Jan'=>0,
+ 'Feb'=>1,
+ 'Mar'=>2,
+ 'Apr'=>3,
+ 'May'=>4,
+ 'Jun'=>5,
+ 'Jul'=>6,
+ 'Aug'=>7,
+ 'Sep'=>8,
+ 'Oct'=>9,
+ 'Nov'=>10,
+ 'Dec'=>11,
+);
+
+my $fh = new FileHandle;
+
+sub key_ready
+{
+ my ($rin, $nfd);
+ vec($rin, fileno(STDIN), 1) = 1;
+ return $nfd = select($rin, undef, undef, 0);
+}
+
+sub strptime {
+ my $str = shift;
+ return undef if not defined $str;
+
+ # we are parsing this format
+ # Fri Oct 26 00:42:56 2007 -0400
+ # to these fields
+ # sec, min, hour, mday, mon, year, wday = -1, yday = -1, isdst = -1
+ # Luis Mondesi <lemsx1@gmail.com>
+ my @date;
+ if ($str =~ /([[:alpha:]]{3})\s+([[:alpha:]]{3})\s+([[:digit:]]{1,2})\s+([[:digit:]]{1,2}):([[:digit:]]{1,2}):([[:digit:]]{1,2})\s+([[:digit:]]{4})/){
+ push(@date,$6,$5,$4,$3,$month{$2},($7 - 1900),-1,-1,-1);
+ } else {
+ die ("Cannot parse date '$str'\n'");
+ }
+ return @date;
+}
+
+sub mywrap {
+ my ($indent1, $indent2, @text) = @_;
+ # If incoming text looks preformatted, don't get clever
+ my $text = Text::Wrap::wrap($indent1, $indent2, @text);
+ if ( grep /^\s+/m, @text ) {
+ return $text;
+ }
+ my @lines = split /\n/, $text;
+ $indent2 =~ s!^((?: {8})+)!"\t" x (length($1)/8)!e;
+ $lines[0] =~ s/^$indent1\s+/$indent1/;
+ s/^$indent2\s+/$indent2/
+ for @lines[1..$#lines];
+ my $newtext = join "\n", @lines;
+ $newtext .= "\n"
+ if substr($text, -1) eq "\n";
+ return $newtext;
+}
+
+sub last_line_len {
+ my $files_list = shift;
+ my @lines = split (/\n/, $files_list);
+ my $last_line = pop (@lines);
+ return length ($last_line);
+}
+
+# A custom wrap function, sensitive to some common constructs used in
+# log entries.
+sub wrap_log_entry {
+ my $text = shift; # The text to wrap.
+ my $left_pad_str = shift; # String to pad with on the left.
+
+ # These do NOT take left_pad_str into account:
+ my $length_remaining = shift; # Amount left on current line.
+ my $max_line_length = shift; # Amount left for a blank line.
+
+ my $wrapped_text = ''; # The accumulating wrapped entry.
+ my $user_indent = ''; # Inherited user_indent from prev line.
+
+ my $first_time = 1; # First iteration of the loop?
+ my $suppress_line_start_match = 0; # Set to disable line start checks.
+
+ my @lines = split (/\n/, $text);
+ while (@lines) # Don't use `foreach' here, it won't work.
+ {
+ my $this_line = shift (@lines);
+ chomp $this_line;
+
+ if ($this_line =~ /^(\s+)/) {
+ $user_indent = $1;
+ }
+ else {
+ $user_indent = '';
+ }
+
+ # If it matches any of the line-start regexps, print a newline now...
+ if ($suppress_line_start_match)
+ {
+ $suppress_line_start_match = 0;
+ }
+ elsif (($this_line =~ /^(\s*)\*\s+[a-zA-Z0-9]/)
+ || ($this_line =~ /^(\s*)\* [a-zA-Z0-9_\.\/\+-]+/)
+ || ($this_line =~ /^(\s*)\([a-zA-Z0-9_\.\/\+-]+(\)|,\s*)/)
+ || ($this_line =~ /^(\s+)(\S+)/)
+ || ($this_line =~ /^(\s*)- +/)
+ || ($this_line =~ /^()\s*$/)
+ || ($this_line =~ /^(\s*)\*\) +/)
+ || ($this_line =~ /^(\s*)[a-zA-Z0-9](\)|\.|\:) +/))
+ {
+ $length_remaining = $max_line_length - (length ($user_indent));
+ }
+
+ # Now that any user_indent has been preserved, strip off leading
+ # whitespace, so up-folding has no ugly side-effects.
+ $this_line =~ s/^\s*//;
+
+ # Accumulate the line, and adjust parameters for next line.
+ my $this_len = length ($this_line);
+ if ($this_len == 0)
+ {
+ # Blank lines should cancel any user_indent level.
+ $user_indent = '';
+ $length_remaining = $max_line_length;
+ }
+ elsif ($this_len >= $length_remaining) # Line too long, try breaking it.
+ {
+ # Walk backwards from the end. At first acceptable spot, break
+ # a new line.
+ my $idx = $length_remaining - 1;
+ if ($idx < 0) { $idx = 0 };
+ while ($idx > 0)
+ {
+ if (substr ($this_line, $idx, 1) =~ /\s/)
+ {
+ my $line_now = substr ($this_line, 0, $idx);
+ my $next_line = substr ($this_line, $idx);
+ $this_line = $line_now;
+
+ # Clean whitespace off the end.
+ chomp $this_line;
+
+ # The current line is ready to be printed.
+ $this_line .= "\n${left_pad_str}";
+
+ # Make sure the next line is allowed full room.
+ $length_remaining = $max_line_length - (length ($user_indent));
+
+ # Strip next_line, but then preserve any user_indent.
+ $next_line =~ s/^\s*//;
+
+ # Sneak a peek at the user_indent of the upcoming line, so
+ # $next_line (which will now precede it) can inherit that
+ # indent level. Otherwise, use whatever user_indent level
+ # we currently have, which might be none.
+ my $next_next_line = shift (@lines);
+ if ((defined ($next_next_line)) && ($next_next_line =~ /^(\s+)/)) {
+ $next_line = $1 . $next_line if (defined ($1));
+ # $length_remaining = $max_line_length - (length ($1));
+ $next_next_line =~ s/^\s*//;
+ }
+ else {
+ $next_line = $user_indent . $next_line;
+ }
+ if (defined ($next_next_line)) {
+ unshift (@lines, $next_next_line);
+ }
+ unshift (@lines, $next_line);
+
+ # Our new next line might, coincidentally, begin with one of
+ # the line-start regexps, so we temporarily turn off
+ # sensitivity to that until we're past the line.
+ $suppress_line_start_match = 1;
+
+ last;
+ }
+ else
+ {
+ $idx--;
+ }
+ }
+
+ if ($idx == 0)
+ {
+ # We bottomed out because the line is longer than the
+ # available space. But that could be because the space is
+ # small, or because the line is longer than even the maximum
+ # possible space. Handle both cases below.
+
+ if ($length_remaining == ($max_line_length - (length ($user_indent))))
+ {
+ # The line is simply too long -- there is no hope of ever
+ # breaking it nicely, so just insert it verbatim, with
+ # appropriate padding.
+ $this_line = "\n${left_pad_str}${this_line}";
+ }
+ else
+ {
+ # Can't break it here, but may be able to on the next round...
+ unshift (@lines, $this_line);
+ $length_remaining = $max_line_length - (length ($user_indent));
+ $this_line = "\n${left_pad_str}";
+ }
+ }
+ }
+ else # $this_len < $length_remaining, so tack on what we can.
+ {
+ # Leave a note for the next iteration.
+ $length_remaining = $length_remaining - $this_len;
+
+ if ($this_line =~ /\.$/)
+ {
+ $this_line .= " ";
+ $length_remaining -= 2;
+ }
+ else # not a sentence end
+ {
+ $this_line .= " ";
+ $length_remaining -= 1;
+ }
+ }
+
+ # Unconditionally indicate that loop has run at least once.
+ $first_time = 0;
+
+ $wrapped_text .= "${user_indent}${this_line}";
+ }
+
+ # One last bit of padding.
+ $wrapped_text .= "\n";
+
+ return $wrapped_text;
+}
+
+# main
+
+my @date;
+my $author;
+my @files;
+my $comment;
+
+my $state; # 0-header 1-comment 2-files
+my $done = 0;
+
+$state = 0;
+
+# if reading from STDIN, we assume that we are
+# getting git log as input
+if (key_ready())
+{
+
+ #my $dummyfh; # don't care about writing
+ #($fh,$dummyfh) = FileHandle::pipe;
+ $fh->fdopen(*STDIN, 'r');
+}
+else
+{
+ $fh->open("LC_ALL=C git log --pretty --numstat --summary|")
+ or die("Cannot execute git log...$!\n");
+}
+
+while (my $_l = <$fh>) {
+ #print STDERR "debug ($state, " . (@date ? (strftime "%Y-%m-%d", @date) : "") . "): `$_'\n";
+ if ($state == 0) {
+ if ($_l =~ m,^Author: (.*),) {
+ $author = $1;
+ }
+ if ($_l =~ m,^Date: (.*),) {
+ @date = strptime($1);
+ }
+ $state = 1 if ($_l =~ m,^$, and $author and (@date+0>0));
+ } elsif ($state == 1) {
+ # * modifying our input text is a bad choice
+ # let's make a copy of it first, then we remove spaces
+ # * if we meet a "merge branch" statement, we need to start
+ # over and find a real entry
+ # Luis Mondesi <lemsx1@gmail.com>
+ my $_s = $_l;
+ $_s =~ s/^ //g;
+ if ($_s =~ m/^Merge branch/)
+ {
+ $state=0;
+ next;
+ }
+ $comment = $comment . $_s;
+ $state = 2 if ($_l =~ m,^$,);
+ } elsif ($state == 2) {
+ if ($_l =~ m,^([0-9]+)\t([0-9]+)\t(.*)$,) {
+ push @files, $3;
+ }
+ $done = 1 if ($_l =~ m,^$,);
+ }
+
+ if ($done) {
+ print (strftime "%Y-%m-%d $author\n\n", @date);
+
+ my $files = join (", ", @files);
+ $files = mywrap ("\t", "\t", "* $files"), ": ";
+
+ if (index($comment, EMPTY_LOG_MESSAGE) > -1 ) {
+ $comment = "[no log message]\n";
+ }
+
+ my $files_last_line_len = 0;
+ $files_last_line_len = last_line_len($files) + 1;
+ my $msg = wrap_log_entry($comment, "\t", 69-$files_last_line_len, 69);
+
+ $msg =~ s/[ \t]+\n/\n/g;
+
+ print "$files: $msg\n";
+
+ @date = ();
+ $author = "";
+ @files = ();
+ $comment = "";
+
+ $state = 0;
+ $done = 0;
+ }
+}
+
+if (@date + 0)
+{
+ print (strftime "%Y-%m-%d $author\n\n", @date);
+ my $msg = wrap_log_entry($comment, "\t", 69, 69);
+ $msg =~ s/[ \t]+\n/\n/g;
+ print "\t* $msg\n";
+}