diff options
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 @@ -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, ®_list, num_regs, + struct reg **reg_list; + int retval = target_get_gdb_reg_list(curr, ®_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, ¤t_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, ®_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, ®_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, - ®_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, ®_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, + ®_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, ®_list, ®_list_size, + retval = target_get_gdb_reg_list_noread(target, ®_list, ®_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, ®_list, ®_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, ®_list, ®_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, ®_list, + retval = target_get_gdb_reg_list_noread(target, ®_list, ®_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, ®_list, + retval = target_get_gdb_reg_list_noread(target, ®_list, ®_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 > 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 —pretty —numstat and
+—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 > 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"; +} |