aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMegan Wachs <megan@sifive.com>2018-08-31 09:02:55 -0700
committerMegan Wachs <megan@sifive.com>2018-08-31 09:02:55 -0700
commit4b29af433d2a4b37771739b0f781e2df54f18d12 (patch)
treec16be888b79f57d6d421d7f8f125fb35b1843fcf
parenta0afcba66dda9b3213fc061dd744c79eaa134dc4 (diff)
parent167f0071d1b3670d5179e4f83447c882aae64fc2 (diff)
downloadriscv-openocd-sba_tests.zip
riscv-openocd-sba_tests.tar.gz
riscv-openocd-sba_tests.tar.bz2
Merge remote-tracking branch 'origin/riscv' into sba_testssba_tests
-rw-r--r--.gitmodules8
-rw-r--r--.travis.yml4
m---------src/jtag/drivers/libjaylink0
-rw-r--r--src/jtag/drivers/libjaylink/.gitignore24
-rw-r--r--src/jtag/drivers/libjaylink/AUTHORS2
-rw-r--r--src/jtag/drivers/libjaylink/COPYING339
-rw-r--r--src/jtag/drivers/libjaylink/ChangeLog1
-rw-r--r--src/jtag/drivers/libjaylink/Doxyfile.in2301
-rw-r--r--src/jtag/drivers/libjaylink/HACKING68
-rw-r--r--src/jtag/drivers/libjaylink/Makefile.am28
-rw-r--r--src/jtag/drivers/libjaylink/NEWS7
-rw-r--r--src/jtag/drivers/libjaylink/README70
-rwxr-xr-xsrc/jtag/drivers/libjaylink/autogen.sh34
-rw-r--r--src/jtag/drivers/libjaylink/configure.ac140
-rw-r--r--src/jtag/drivers/libjaylink/contrib/99-libjaylink.rules41
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink.pc.in11
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/Makefile.am62
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/buffer.c140
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/core.c219
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/device.c1707
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/discovery.c106
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/discovery_tcp.c349
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/discovery_usb.c280
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/emucom.c287
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/error.c118
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/fileio.c499
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/jtag.c259
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/libjaylink-internal.h320
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/libjaylink.h589
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/list.c115
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/log.c266
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/socket.c257
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/strutil.c66
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/swd.c148
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/swo.c453
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/target.c533
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/transport.c309
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/transport_tcp.c601
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/transport_usb.c620
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/util.c56
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/version.c128
-rw-r--r--src/jtag/drivers/libjaylink/libjaylink/version.h.in53
-rw-r--r--src/jtag/drivers/libjaylink/m4/jaylink.m491
-rw-r--r--src/target/riscv/opcodes.h3
-rw-r--r--src/target/riscv/program.h4
-rw-r--r--src/target/riscv/riscv-013.c456
-rw-r--r--src/target/riscv/riscv.c26
-rw-r--r--src/target/riscv/riscv.h1
m---------tools/git2cl0
-rw-r--r--tools/git2cl/COPYING339
-rw-r--r--tools/git2cl/README45
-rw-r--r--tools/git2cl/README.html392
-rwxr-xr-xtools/git2cl/git2cl372
53 files changed, 13337 insertions, 10 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
index 88a6b8d..3691a1c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -55,6 +55,8 @@ script:
# 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 | ./tools/scripts/checkpatch.pl --no-signoff -
+ - git diff -U20 HEAD~40 |
+ filterdiff -x "src/jtag/drivers/libjaylink/*" -x "tools/git2cl/*" |
+ ./tools/scripts/checkpatch.pl --no-signoff -
- ./bootstrap && ./configure --enable-remote-bitbang --enable-jtag_vpi $CONFIGURE_ARGS && make
- file src/$EXECUTABLE
diff --git a/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/target/riscv/opcodes.h b/src/target/riscv/opcodes.h
index dd51c80..de85aad 100644
--- a/src/target/riscv/opcodes.h
+++ b/src/target/riscv/opcodes.h
@@ -224,6 +224,9 @@ static uint32_t ebreak_c(void)
return MATCH_C_EBREAK;
}
+static uint32_t wfi(void) __attribute__ ((unused));
+static uint32_t wfi(void) { return MATCH_WFI; }
+
static uint32_t fence_i(void) __attribute__ ((unused));
static uint32_t fence_i(void)
{
diff --git a/src/target/riscv/program.h b/src/target/riscv/program.h
index d641be1..310460c 100644
--- a/src/target/riscv/program.h
+++ b/src/target/riscv/program.h
@@ -52,8 +52,8 @@ int riscv_program_insert(struct riscv_program *p, riscv_insn_t i);
* memory. */
int riscv_program_save_to_dscratch(struct riscv_program *p, enum gdb_regno to_save);
-/* Helpers to assembly various instructions. Return 0 on success. These might
- * assembly into a multi-instruction sequence that overwrites some other
+/* Helpers to assemble various instructions. Return 0 on success. These might
+ * assemble into a multi-instruction sequence that overwrites some other
* register, but those will be properly saved and restored. */
int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
int riscv_program_lhr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c
index 3ab8587..d3afc8a 100644
--- a/src/target/riscv/riscv-013.c
+++ b/src/target/riscv/riscv-013.c
@@ -71,6 +71,7 @@ void write_memory_sba_simple(struct target *target, target_addr_t addr, uint32_t
uint32_t write_size, uint32_t sbcs);
void read_memory_sba_simple(struct target *target, target_addr_t addr,
uint32_t *rd_buf, uint32_t read_size, uint32_t sbcs);
+static int riscv013_test_compliance(struct target *target);
/**
* Since almost everything can be accomplish by scanning the dbus register, all
@@ -1594,6 +1595,7 @@ static int init_target(struct command_context *cmd_ctx,
generic_info->dmi_read = &dmi_read;
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->version_specific = calloc(1, sizeof(riscv013_info_t));
if (!generic_info->version_specific)
return ERROR_FAIL;
@@ -3376,3 +3378,457 @@ void riscv013_clear_abstract_error(struct target *target)
/* Clear the error status. */
dmi_write(target, DMI_ABSTRACTCS, abstractcs & DMI_ABSTRACTCS_CMDERR);
}
+
+#define COMPLIANCE_TEST(b, message) \
+{ \
+ int pass = 0; \
+ if (b) { \
+ pass = 1; \
+ passed_tests++; \
+ } \
+ LOG_INFO("%s test %d (%s)\n", (pass) ? "PASSED" : "FAILED", total_tests, message); \
+ assert(pass); \
+ total_tests++; \
+}
+
+#define COMPLIANCE_MUST_PASS(b) COMPLIANCE_TEST(ERROR_OK == (b), "Regular calls must return ERROR_OK")
+
+#define COMPLIANCE_READ(target, addr, value) COMPLIANCE_MUST_PASS(dmi_read(target, addr, value))
+#define COMPLIANCE_WRITE(target, addr, value) COMPLIANCE_MUST_PASS(dmi_write(target, addr, value))
+
+#define COMPLIANCE_CHECK_RO(target, addr) \
+{ \
+ uint32_t orig; \
+ uint32_t inverse; \
+ COMPLIANCE_READ(target, &orig, addr); \
+ COMPLIANCE_WRITE(target, addr, ~orig); \
+ COMPLIANCE_READ(target, &inverse, addr); \
+ COMPLIANCE_TEST(orig == inverse, "Register must be read-only"); \
+}
+
+int riscv013_test_compliance(struct target *target)
+{
+ LOG_INFO("Testing Compliance against RISC-V Debug Spec v0.13");
+
+ if (!riscv_rtos_enabled(target)) {
+ LOG_ERROR("Please run with -rtos riscv to run compliance test.");
+ return ERROR_FAIL;
+ }
+
+ int total_tests = 0;
+ int passed_tests = 0;
+
+ uint32_t dmcontrol_orig = DMI_DMCONTROL_DMACTIVE;
+ uint32_t dmcontrol;
+ uint32_t testvar;
+ uint32_t testvar_read;
+ riscv_reg_t value;
+ RISCV013_INFO(info);
+
+ /* All the bits of HARTSEL are covered by the examine sequence. */
+
+ /* hartreset */
+ /* This field is optional. Either we can read and write it to 1/0,
+ or it is tied to 0. This check doesn't really do anything, but
+ it does attempt to set the bit to 1 and then back to 0, which needs to
+ work if its implemented. */
+ COMPLIANCE_WRITE(target, DMI_DMCONTROL, set_field(dmcontrol_orig, DMI_DMCONTROL_HARTRESET, 1));
+ COMPLIANCE_WRITE(target, DMI_DMCONTROL, set_field(dmcontrol_orig, DMI_DMCONTROL_HARTRESET, 0));
+ COMPLIANCE_READ(target, &dmcontrol, DMI_DMCONTROL);
+ COMPLIANCE_TEST((get_field(dmcontrol, DMI_DMCONTROL_HARTRESET) == 0),
+ "DMCONTROL.hartreset can be 0 or RW.");
+
+ /* hasel */
+ COMPLIANCE_WRITE(target, DMI_DMCONTROL, set_field(dmcontrol_orig, DMI_DMCONTROL_HASEL, 1));
+ COMPLIANCE_WRITE(target, DMI_DMCONTROL, set_field(dmcontrol_orig, DMI_DMCONTROL_HASEL, 0));
+ COMPLIANCE_READ(target, &dmcontrol, DMI_DMCONTROL);
+ COMPLIANCE_TEST((get_field(dmcontrol, DMI_DMCONTROL_HASEL) == 0),
+ "DMCONTROL.hasel can be 0 or RW.");
+ /* TODO: test that hamask registers exist if hasel does. */
+
+ /* haltreq */
+ COMPLIANCE_MUST_PASS(riscv_halt_all_harts(target));
+ /* This bit is not actually readable according to the spec, so nothing to check.*/
+
+ /* DMSTATUS */
+ COMPLIANCE_CHECK_RO(target, DMI_DMSTATUS);
+
+ /* resumereq */
+ /* This bit is not actually readable according to the spec, so nothing to check.*/
+ COMPLIANCE_MUST_PASS(riscv_resume_all_harts(target));
+
+ /* Halt all harts again so the test can continue.*/
+ COMPLIANCE_MUST_PASS(riscv_halt_all_harts(target));
+
+ /* HARTINFO: Read-Only. This is per-hart, so need to adjust hartsel. */
+ uint32_t hartinfo;
+ COMPLIANCE_READ(target, &hartinfo, DMI_HARTINFO);
+ for (int hartsel = 0; hartsel < riscv_count_harts(target); hartsel++) {
+ COMPLIANCE_MUST_PASS(riscv_set_current_hartid(target, hartsel));
+
+ COMPLIANCE_CHECK_RO(target, DMI_HARTINFO);
+
+ /* $dscratch CSRs */
+ uint32_t nscratch = get_field(hartinfo, DMI_HARTINFO_NSCRATCH);
+ for (unsigned int d = 0; d < nscratch; d++) {
+ riscv_reg_t testval, testval_read;
+ /* Because DSCRATCH is not guaranteed to last across PB executions, need to put
+ this all into one PB execution. Which may not be possible on all implementations.*/
+ if (info->progbufsize >= 5) {
+ for (testval = 0x0011223300112233;
+ testval != 0xDEAD;
+ testval = testval == 0x0011223300112233 ? ~testval : 0xDEAD) {
+ COMPLIANCE_TEST(register_write_direct(target, GDB_REGNO_S0, testval) == ERROR_OK,
+ "Need to be able to write S0 in order to test DSCRATCH.");
+ struct riscv_program program32;
+ riscv_program_init(&program32, target);
+ riscv_program_csrw(&program32, GDB_REGNO_S0, GDB_REGNO_DSCRATCH + d);
+ riscv_program_csrr(&program32, GDB_REGNO_S1, GDB_REGNO_DSCRATCH + d);
+ riscv_program_fence(&program32);
+ riscv_program_ebreak(&program32);
+ COMPLIANCE_TEST(riscv_program_exec(&program32, target) == ERROR_OK,
+ "Accessing DSCRATCH with program buffer should succeed.");
+ COMPLIANCE_TEST(register_read_direct(target, &testval_read, GDB_REGNO_S1) == ERROR_OK,
+ "Need to be able to read S1 in order to test DSCRATCH.");
+ if (riscv_xlen(target) > 32) {
+ COMPLIANCE_TEST(testval == testval_read,
+ "All DSCRATCH registers in HARTINFO must be R/W.");
+ } else {
+ COMPLIANCE_TEST(testval_read == (testval & 0xFFFFFFFF),
+ "All DSCRATCH registers in HARTINFO must be R/W.");
+ }
+ }
+ }
+ }
+ /* TODO: dataaccess */
+ if (get_field(hartinfo, DMI_HARTINFO_DATAACCESS)) {
+ /* TODO: Shadowed in memory map. */
+ /* TODO: datasize */
+ /* TODO: dataaddr */
+ } else {
+ /* TODO: Shadowed in CSRs. */
+ /* TODO: datasize */
+ /* TODO: dataaddr */
+ }
+
+ }
+
+ /* HALTSUM -- TODO: More than 32 harts. Would need to loop over this to set hartsel */
+ /* TODO: HALTSUM2, HALTSUM3 */
+ /* HALTSUM0 */
+ uint32_t expected_haltsum0 = 0;
+ for (int i = 0; i < MIN(riscv_count_harts(target), 32); i++)
+ expected_haltsum0 |= (1 << i);
+
+ COMPLIANCE_READ(target, &testvar_read, DMI_HALTSUM0);
+ COMPLIANCE_TEST(testvar_read == expected_haltsum0,
+ "HALTSUM0 should report summary of up to 32 halted harts");
+
+ COMPLIANCE_WRITE(target, DMI_HALTSUM0, 0xffffffff);
+ COMPLIANCE_READ(target, &testvar_read, DMI_HALTSUM0);
+ COMPLIANCE_TEST(testvar_read == expected_haltsum0, "HALTSUM0 should be R/O");
+
+ COMPLIANCE_WRITE(target, DMI_HALTSUM0, 0x0);
+ COMPLIANCE_READ(target, &testvar_read, DMI_HALTSUM0);
+ COMPLIANCE_TEST(testvar_read == expected_haltsum0, "HALTSUM0 should be R/O");
+
+ /* HALTSUM1 */
+ uint32_t expected_haltsum1 = 0;
+ for (int i = 0; i < MIN(riscv_count_harts(target), 1024); i += 32)
+ expected_haltsum1 |= (1 << (i/32));
+
+ COMPLIANCE_READ(target, &testvar_read, DMI_HALTSUM1);
+ COMPLIANCE_TEST(testvar_read == expected_haltsum1,
+ "HALTSUM1 should report summary of up to 1024 halted harts");
+
+ COMPLIANCE_WRITE(target, DMI_HALTSUM1, 0xffffffff);
+ COMPLIANCE_READ(target, &testvar_read, DMI_HALTSUM1);
+ COMPLIANCE_TEST(testvar_read == expected_haltsum1, "HALTSUM1 should be R/O");
+
+ COMPLIANCE_WRITE(target, DMI_HALTSUM1, 0x0);
+ COMPLIANCE_READ(target, &testvar_read, DMI_HALTSUM1);
+ COMPLIANCE_TEST(testvar_read == expected_haltsum1, "HALTSUM1 should be R/O");
+
+ /* TODO: HAWINDOWSEL */
+
+ /* TODO: HAWINDOW */
+
+ /* ABSTRACTCS */
+
+ uint32_t abstractcs;
+ COMPLIANCE_READ(target, &abstractcs, DMI_ABSTRACTCS);
+
+ /* Check that all reported Data Words are really R/W */
+ for (int invert = 0; invert < 2; invert++) {
+ for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT); i++) {
+ testvar = (i + 1) * 0x11111111;
+ if (invert)
+ testvar = ~testvar;
+ COMPLIANCE_WRITE(target, DMI_DATA0 + i, testvar);
+ }
+ for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT); i++) {
+ testvar = (i + 1) * 0x11111111;
+ if (invert)
+ testvar = ~testvar;
+ COMPLIANCE_READ(target, &testvar_read, DMI_DATA0 + i);
+ COMPLIANCE_TEST(testvar_read == testvar, "All reported DATA words must be R/W");
+ }
+ }
+
+ /* Check that all reported ProgBuf words are really R/W */
+ for (int invert = 0; invert < 2; invert++) {
+ for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE); i++) {
+ testvar = (i + 1) * 0x11111111;
+ if (invert)
+ testvar = ~testvar;
+ COMPLIANCE_WRITE(target, DMI_PROGBUF0 + i, testvar);
+ }
+ for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE); i++) {
+ testvar = (i + 1) * 0x11111111;
+ if (invert)
+ testvar = ~testvar;
+ COMPLIANCE_READ(target, &testvar_read, DMI_PROGBUF0 + i);
+ COMPLIANCE_TEST(testvar_read == testvar, "All reported PROGBUF words must be R/W");
+ }
+ }
+
+ /* TODO: Cause and clear all error types */
+
+ /* COMMAND
+ According to the spec, this register is only W, so can't really check the read result.
+ But at any rate, this is not legal and should cause an error. */
+ COMPLIANCE_WRITE(target, DMI_COMMAND, 0xAAAAAAAA);
+ COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTCS);
+ COMPLIANCE_TEST(get_field(testvar_read, DMI_ABSTRACTCS_CMDERR) == CMDERR_NOT_SUPPORTED, \
+ "Illegal COMMAND should result in UNSUPPORTED");
+ COMPLIANCE_WRITE(target, DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR);
+
+ COMPLIANCE_WRITE(target, DMI_COMMAND, 0x55555555);
+ COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTCS);
+ COMPLIANCE_TEST(get_field(testvar_read, DMI_ABSTRACTCS_CMDERR) == CMDERR_NOT_SUPPORTED, \
+ "Illegal COMMAND should result in UNSUPPORTED");
+ COMPLIANCE_WRITE(target, DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR);
+
+ /* Basic Abstract Commands */
+ for (unsigned int i = 1; i < 32; i = i << 1) {
+ riscv_reg_t testval = i | ((i + 1ULL) << 32);
+ riscv_reg_t testval_read;
+ COMPLIANCE_TEST(ERROR_OK == register_write_direct(target, GDB_REGNO_ZERO + i, testval),
+ "GPR Writes should be supported.");
+ COMPLIANCE_MUST_PASS(write_abstract_arg(target, 0, 0xDEADBEEFDEADBEEF, 64));
+ COMPLIANCE_TEST(ERROR_OK == register_read_direct(target, &testval_read, GDB_REGNO_ZERO + i),
+ "GPR Reads should be supported.");
+ if (riscv_xlen(target) > 32) {
+ /* Dummy comment to satisfy linter, since removing the brances here doesn't actually compile. */
+ COMPLIANCE_TEST(testval == testval_read, "GPR Reads and writes should be supported.");
+ } else {
+ /* Dummy comment to satisfy linter, since removing the brances here doesn't actually compile. */
+ COMPLIANCE_TEST((testval & 0xFFFFFFFF) == testval_read, "GPR Reads and writes should be supported.");
+ }
+ }
+
+ /* ABSTRACTAUTO
+ See which bits are actually writable */
+ COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0xFFFFFFFF);
+ uint32_t abstractauto;
+ uint32_t busy;
+ COMPLIANCE_READ(target, &abstractauto, DMI_ABSTRACTAUTO);
+ COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0x0);
+ if (abstractauto > 0) {
+ /* This mechanism only works when you have a reasonable sized progbuf, which is not
+ a true compliance requirement. */
+ if (info->progbufsize >= 3) {
+
+ testvar = 0;
+ COMPLIANCE_TEST(ERROR_OK == register_write_direct(target, GDB_REGNO_S0, 0),
+ "Need to be able to write S0 to test ABSTRACTAUTO");
+ struct riscv_program program;
+ COMPLIANCE_MUST_PASS(riscv_program_init(&program, target));
+ /* This is also testing that WFI() is a NOP during debug mode. */
+ COMPLIANCE_MUST_PASS(riscv_program_insert(&program, wfi()));
+ COMPLIANCE_MUST_PASS(riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, 1));
+ COMPLIANCE_MUST_PASS(riscv_program_ebreak(&program));
+ COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0x0);
+ COMPLIANCE_MUST_PASS(riscv_program_exec(&program, target));
+ testvar++;
+ COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0xFFFFFFFF);
+ COMPLIANCE_READ(target, &abstractauto, DMI_ABSTRACTAUTO);
+ uint32_t autoexec_data = get_field(abstractauto, DMI_ABSTRACTAUTO_AUTOEXECDATA);
+ uint32_t autoexec_progbuf = get_field(abstractauto, DMI_ABSTRACTAUTO_AUTOEXECPROGBUF);
+ for (unsigned int i = 0; i < 12; i++) {
+ COMPLIANCE_READ(target, &testvar_read, DMI_DATA0 + i);
+ do {
+ COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTCS);
+ busy = get_field(testvar_read, DMI_ABSTRACTCS_BUSY);
+ } while (busy);
+ if (autoexec_data & (1 << i)) {
+ COMPLIANCE_TEST(i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT),
+ "AUTOEXEC may be writable up to DATACOUNT bits.");
+ testvar++;
+ }
+ }
+ for (unsigned int i = 0; i < 16; i++) {
+ COMPLIANCE_READ(target, &testvar_read, DMI_PROGBUF0 + i);
+ do {
+ COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTCS);
+ busy = get_field(testvar_read, DMI_ABSTRACTCS_BUSY);
+ } while (busy);
+ if (autoexec_progbuf & (1 << i)) {
+ COMPLIANCE_TEST(i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE),
+ "AUTOEXEC may be writable up to PROGBUFSIZE bits.");
+ testvar++;
+ }
+ }
+
+ COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0);
+ COMPLIANCE_TEST(ERROR_OK == register_read_direct(target, &value, GDB_REGNO_S0),
+ "Need to be able to read S0 to test ABSTRACTAUTO");
+
+ COMPLIANCE_TEST(testvar == value,
+ "ABSTRACTAUTO should cause COMMAND to run the expected number of times.");
+ }
+ }
+
+ /* Single-Step each hart. */
+ for (int hartsel = 0; hartsel < riscv_count_harts(target); hartsel++) {
+ COMPLIANCE_MUST_PASS(riscv_set_current_hartid(target, hartsel));
+ COMPLIANCE_MUST_PASS(riscv013_on_step(target));
+ COMPLIANCE_MUST_PASS(riscv013_step_current_hart(target));
+ COMPLIANCE_TEST(riscv_halt_reason(target, hartsel) == RISCV_HALT_SINGLESTEP,
+ "Single Step should result in SINGLESTEP");
+ }
+
+ /* Core Register Tests */
+ uint64_t bogus_dpc = 0xdeadbeef;
+ for (int hartsel = 0; hartsel < riscv_count_harts(target); hartsel++) {
+ COMPLIANCE_MUST_PASS(riscv_set_current_hartid(target, hartsel));
+
+ /* DCSR Tests */
+ COMPLIANCE_MUST_PASS(register_write_direct(target, GDB_REGNO_DCSR, 0x0));
+ COMPLIANCE_MUST_PASS(register_read_direct(target, &value, GDB_REGNO_DCSR));
+ COMPLIANCE_TEST(value != 0, "Not all bits in DCSR are writable by Debugger");
+ COMPLIANCE_MUST_PASS(register_write_direct(target, GDB_REGNO_DCSR, 0xFFFFFFFF));
+ COMPLIANCE_MUST_PASS(register_read_direct(target, &value, GDB_REGNO_DCSR));
+ COMPLIANCE_TEST(value != 0, "At least some bits in DCSR must be 1");
+
+ /* DPC. Note that DPC is sign-extended. */
+ riscv_reg_t dpcmask = 0xFFFFFFFCUL;
+ riscv_reg_t dpc;
+
+ if (riscv_xlen(target) > 32)
+ dpcmask |= (0xFFFFFFFFULL << 32);
+
+ if (riscv_supports_extension(target, riscv_current_hartid(target), 'C'))
+ dpcmask |= 0x2;
+
+ COMPLIANCE_MUST_PASS(register_write_direct(target, GDB_REGNO_DPC, dpcmask));
+ COMPLIANCE_MUST_PASS(register_read_direct(target, &dpc, GDB_REGNO_DPC));
+ COMPLIANCE_TEST(dpcmask == dpc,
+ "DPC must be sign-extended to XLEN and writable to all-1s (except the least significant bits)");
+ COMPLIANCE_MUST_PASS(register_write_direct(target, GDB_REGNO_DPC, 0));
+ COMPLIANCE_MUST_PASS(register_read_direct(target, &dpc, GDB_REGNO_DPC));
+ COMPLIANCE_TEST(dpc == 0, "DPC must be writable to 0.");
+ if (hartsel == 0)
+ bogus_dpc = dpc; /* For a later test step */
+ }
+
+ /* NDMRESET
+ Asserting non-debug module reset should not reset Debug Module state.
+ But it should reset Hart State, e.g. DPC should get a different value.
+ Also make sure that DCSR reports cause of 'HALT' even though previously we single-stepped.
+ */
+
+ /* Write some registers. They should not be impacted by ndmreset. */
+ COMPLIANCE_WRITE(target, DMI_COMMAND, 0xFFFFFFFF);
+
+ for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE); i++) {
+ testvar = (i + 1) * 0x11111111;
+ COMPLIANCE_WRITE(target, DMI_PROGBUF0 + i, testvar);
+ }
+
+ for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT); i++) {
+ testvar = (i + 1) * 0x11111111;
+ COMPLIANCE_WRITE(target, DMI_DATA0 + i, testvar);
+ }
+
+ COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0xFFFFFFFF);
+ COMPLIANCE_READ(target, &abstractauto, DMI_ABSTRACTAUTO);
+
+ /* Pulse reset. */
+ target->reset_halt = true;
+ COMPLIANCE_MUST_PASS(riscv_set_current_hartid(target, 0));
+ COMPLIANCE_TEST(ERROR_OK == assert_reset(target), "Must be able to assert NDMRESET");
+ COMPLIANCE_TEST(ERROR_OK == deassert_reset(target), "Must be able to deassert NDMRESET");
+
+ /* Verify that most stuff is not affected by ndmreset. */
+ COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTCS);
+ COMPLIANCE_TEST(get_field(testvar_read, DMI_ABSTRACTCS_CMDERR) == CMDERR_NOT_SUPPORTED,
+ "NDMRESET should not affect DMI_ABSTRACTCS");
+ COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTAUTO);
+ COMPLIANCE_TEST(testvar_read == abstractauto, "NDMRESET should not affect DMI_ABSTRACTAUTO");
+
+ /* Clean up to avoid future test failures */
+ COMPLIANCE_WRITE(target, DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR);
+ COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0);
+
+ for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE); i++) {
+ testvar = (i + 1) * 0x11111111;
+ COMPLIANCE_READ(target, &testvar_read, DMI_PROGBUF0 + i);
+ COMPLIANCE_TEST(testvar_read == testvar, "PROGBUF words must not be affected by NDMRESET");
+ }
+
+ for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT); i++) {
+ testvar = (i + 1) * 0x11111111;
+ COMPLIANCE_READ(target, &testvar_read, DMI_DATA0 + i);
+ COMPLIANCE_TEST(testvar_read == testvar, "DATA words must not be affected by NDMRESET");
+ }
+
+ /* Verify that DPC *is* affected by ndmreset. Since we don't know what it *should* be,
+ just verify that at least it's not the bogus value anymore. */
+
+ COMPLIANCE_TEST(bogus_dpc != 0xdeadbeef, "BOGUS DPC should have been set somehow (bug in compliance test)");
+ COMPLIANCE_MUST_PASS(register_read_direct(target, &value, GDB_REGNO_DPC));
+ COMPLIANCE_TEST(bogus_dpc != value, "NDMRESET should move DPC to reset value.");
+
+ COMPLIANCE_TEST(riscv_halt_reason(target, 0) == RISCV_HALT_INTERRUPT,
+ "After NDMRESET halt, DCSR should report cause of halt");
+
+ /* DMACTIVE -- deasserting DMACTIVE should reset all the above values. */
+
+ /* Toggle dmactive */
+ COMPLIANCE_WRITE(target, DMI_DMCONTROL, 0);
+ COMPLIANCE_WRITE(target, DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE);
+ COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTCS);
+ COMPLIANCE_TEST(get_field(testvar_read, DMI_ABSTRACTCS_CMDERR) == 0, "ABSTRACTCS.cmderr should reset to 0");
+ COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTAUTO);
+ COMPLIANCE_TEST(testvar_read == 0, "ABSTRACTAUTO should reset to 0");
+
+ for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE); i++) {
+ COMPLIANCE_READ(target, &testvar_read, DMI_PROGBUF0 + i);
+ COMPLIANCE_TEST(testvar_read == 0, "PROGBUF words should reset to 0");
+ }
+
+ for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT); i++) {
+ COMPLIANCE_READ(target, &testvar_read, DMI_DATA0 + i);
+ COMPLIANCE_TEST(testvar_read == 0, "DATA words should reset to 0");
+ }
+
+ /*
+ * TODO:
+ * DCSR.cause priorities
+ * DCSR.stoptime/stopcycle
+ * DCSR.stepie
+ * DCSR.ebreak
+ * DCSR.prv
+ */
+
+ /* Halt every hart for any follow-up tests*/
+ COMPLIANCE_MUST_PASS(riscv_halt_all_harts(target));
+
+ LOG_INFO("PASSED %d of %d TESTS\n", passed_tests, total_tests);
+
+ if (total_tests == passed_tests)
+ return ERROR_OK;
+ else
+ return ERROR_FAIL;
+}
diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c
index 7b965f2..dceed61 100644
--- a/src/target/riscv/riscv.c
+++ b/src/target/riscv/riscv.c
@@ -1323,6 +1323,25 @@ COMMAND_HANDLER(riscv_set_reset_timeout_sec)
return ERROR_OK;
}
+COMMAND_HANDLER(riscv_test_compliance) {
+
+ struct target *target = get_current_target(CMD_CTX);
+
+ RISCV_INFO(r);
+
+ if (CMD_ARGC > 0) {
+ LOG_ERROR("Command does not take any parameters.");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ if (r->test_compliance) {
+ return r->test_compliance(target);
+ } else {
+ LOG_ERROR("This target does not support this command (may implement an older version of the spec).");
+ return ERROR_FAIL;
+ }
+}
+
COMMAND_HANDLER(riscv_set_prefer_sba)
{
if (CMD_ARGC != 1) {
@@ -1569,6 +1588,13 @@ COMMAND_HANDLER(riscv_test_sba_config_reg)
static const struct command_registration riscv_exec_command_handlers[] = {
{
+ .name = "test_compliance",
+ .handler = riscv_test_compliance,
+ .mode = COMMAND_EXEC,
+ .usage = "riscv test_compliance",
+ .help = "Runs a basic compliance test suite against the RISC-V Debug Spec."
+ },
+ {
.name = "set_command_timeout_sec",
.handler = riscv_set_command_timeout_sec,
.mode = COMMAND_ANY,
diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h
index 3123db2..deca21e 100644
--- a/src/target/riscv/riscv.h
+++ b/src/target/riscv/riscv.h
@@ -129,6 +129,7 @@ typedef struct {
int (*test_sba_config_reg)(struct target *target, target_addr_t legal_address,
uint32_t num_words, target_addr_t illegal_address, bool run_sbbusyerror_test);
+ int (*test_compliance)(struct target *target);
} riscv_info_t;
/* Wall-clock timeout for a command/access. Settable via RISC-V Target commands.*/
diff --git a/tools/git2cl b/tools/git2cl
deleted file mode 160000
-Subproject 8373c9f74993e218a08819cbcdbab3f3564bbeb
diff --git a/tools/git2cl/COPYING b/tools/git2cl/COPYING
new file mode 100644
index 0000000..d511905
--- /dev/null
+++ b/tools/git2cl/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/tools/git2cl/README b/tools/git2cl/README
new file mode 100644
index 0000000..4084080
--- /dev/null
+++ b/tools/git2cl/README
@@ -0,0 +1,45 @@
+git2cl
+======
+
+This is a quick'n'dirty tool to convert git logs to GNU ChangeLog
+format.
+
+The tool invokes 'git log' internally unless you pipe a log to it.
+Thus, typically you would use it as follows:
+
+...........................................................................
+jas@mocca:~/src/libtasn1$ git2cl > ChangeLog
+jas@mocca:~/src/libtasn1$
+...........................................................................
+
+If you don't want git2cl to invoke git log internally, you can use it
+as a pipe. It needs a git log generated with --pretty --numstat and
+--summary. You can use it as follows:
+
+...........................................................................
+jas@mocca:~/src/libtasn1$ git log --pretty --numstat --summary | ~/src/git2cl/git2cl > ChangeLog
+jas@mocca:~/src/libtasn1$
+...........................................................................
+
+The output format is specified by:
+
+link:http://www.gnu.org/prep/standards/html_node/Change-Logs.html[]
+
+My inspiration for writing this tool was the
+link:http://www.red-bean.com/cvs2cl/[cvs2cl] tool, which I have been
+using in several projects. Replacing it was necessary to seriously
+consider switching from CVS to GIT for my projects.
+
+The canonical home page for git2cl is:
+link:http://josefsson.org/git2cl/[] and its repository can be found at
+link:http://repo.or.cz/w/git2cl.git[].
+
+Credits
+-------
+
+Luis Mondesi contributed several improvements.
+
+Support
+-------
+
+Try talking to mailto:simon@josefsson.org[Simon Josefsson].
diff --git a/tools/git2cl/README.html b/tools/git2cl/README.html
new file mode 100644
index 0000000..f82da5d
--- /dev/null
+++ b/tools/git2cl/README.html
@@ -0,0 +1,392 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<meta name="generator" content="AsciiDoc 8.2.7" />
+<style type="text/css">
+/* Debug borders */
+p, li, dt, dd, div, pre, h1, h2, h3, h4, h5, h6 {
+/*
+ border: 1px solid red;
+*/
+}
+
+body {
+ margin: 1em 5% 1em 5%;
+}
+
+a {
+ color: blue;
+ text-decoration: underline;
+}
+a:visited {
+ color: fuchsia;
+}
+
+em {
+ font-style: italic;
+ color: navy;
+}
+
+strong {
+ font-weight: bold;
+ color: #083194;
+}
+
+tt {
+ color: navy;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ color: #527bbd;
+ font-family: sans-serif;
+ margin-top: 1.2em;
+ margin-bottom: 0.5em;
+ line-height: 1.3;
+}
+
+h1, h2, h3 {
+ border-bottom: 2px solid silver;
+}
+h2 {
+ padding-top: 0.5em;
+}
+h3 {
+ float: left;
+}
+h3 + * {
+ clear: left;
+}
+
+div.sectionbody {
+ font-family: serif;
+ margin-left: 0;
+}
+
+hr {
+ border: 1px solid silver;
+}
+
+p {
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+}
+
+ul, ol, li > p {
+ margin-top: 0;
+}
+
+pre {
+ padding: 0;
+ margin: 0;
+}
+
+span#author {
+ color: #527bbd;
+ font-family: sans-serif;
+ font-weight: bold;
+ font-size: 1.1em;
+}
+span#email {
+}
+span#revision {
+ font-family: sans-serif;
+}
+
+div#footer {
+ font-family: sans-serif;
+ font-size: small;
+ border-top: 2px solid silver;
+ padding-top: 0.5em;
+ margin-top: 4.0em;
+}
+div#footer-text {
+ float: left;
+ padding-bottom: 0.5em;
+}
+div#footer-badges {
+ float: right;
+ padding-bottom: 0.5em;
+}
+
+div#preamble,
+div.tableblock, div.imageblock, div.exampleblock, div.verseblock,
+div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
+div.admonitionblock {
+ margin-right: 10%;
+ margin-top: 1.5em;
+ margin-bottom: 1.5em;
+}
+div.admonitionblock {
+ margin-top: 2.5em;
+ margin-bottom: 2.5em;
+}
+
+div.content { /* Block element content. */
+ padding: 0;
+}
+
+/* Block element titles. */
+div.title, caption.title {
+ color: #527bbd;
+ font-family: sans-serif;
+ font-weight: bold;
+ text-align: left;
+ margin-top: 1.0em;
+ margin-bottom: 0.5em;
+}
+div.title + * {
+ margin-top: 0;
+}
+
+td div.title:first-child {
+ margin-top: 0.0em;
+}
+div.content div.title:first-child {
+ margin-top: 0.0em;
+}
+div.content + div.title {
+ margin-top: 0.0em;
+}
+
+div.sidebarblock > div.content {
+ background: #ffffee;
+ border: 1px solid silver;
+ padding: 0.5em;
+}
+
+div.listingblock {
+ margin-right: 0%;
+}
+div.listingblock > div.content {
+ border: 1px solid silver;
+ background: #f4f4f4;
+ padding: 0.5em;
+}
+
+div.quoteblock {
+ padding-left: 2.0em;
+}
+div.quoteblock > div.attribution {
+ padding-top: 0.5em;
+ text-align: right;
+}
+
+div.verseblock {
+ padding-left: 2.0em;
+}
+div.verseblock > div.content {
+ white-space: pre;
+}
+div.verseblock > div.attribution {
+ padding-top: 0.75em;
+ text-align: left;
+}
+/* DEPRECATED: Pre version 8.2.7 verse style literal block. */
+div.verseblock + div.attribution {
+ text-align: left;
+}
+
+div.admonitionblock .icon {
+ vertical-align: top;
+ font-size: 1.1em;
+ font-weight: bold;
+ text-decoration: underline;
+ color: #527bbd;
+ padding-right: 0.5em;
+}
+div.admonitionblock td.content {
+ padding-left: 0.5em;
+ border-left: 2px solid silver;
+}
+
+div.exampleblock > div.content {
+ border-left: 2px solid silver;
+ padding: 0.5em;
+}
+
+div.imageblock div.content { padding-left: 0; }
+div.imageblock img { border: 1px solid silver; }
+span.image img { border-style: none; }
+
+dl {
+ margin-top: 0.8em;
+ margin-bottom: 0.8em;
+}
+dt {
+ margin-top: 0.5em;
+ margin-bottom: 0;
+ font-style: normal;
+}
+dd > *:first-child {
+ margin-top: 0.1em;
+}
+
+ul, ol {
+ list-style-position: outside;
+}
+div.olist > ol {
+ list-style-type: decimal;
+}
+div.olist2 > ol {
+ list-style-type: lower-alpha;
+}
+
+div.tableblock > table {
+ border: 3px solid #527bbd;
+}
+thead {
+ font-family: sans-serif;
+ font-weight: bold;
+}
+tfoot {
+ font-weight: bold;
+}
+
+div.hlist {
+ margin-top: 0.8em;
+ margin-bottom: 0.8em;
+}
+div.hlist td {
+ padding-bottom: 15px;
+}
+td.hlist1 {
+ vertical-align: top;
+ font-style: normal;
+ padding-right: 0.8em;
+}
+td.hlist2 {
+ vertical-align: top;
+}
+
+@media print {
+ div#footer-badges { display: none; }
+}
+
+div#toctitle {
+ color: #527bbd;
+ font-family: sans-serif;
+ font-size: 1.1em;
+ font-weight: bold;
+ margin-top: 1.0em;
+ margin-bottom: 0.1em;
+}
+
+div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+div.toclevel2 {
+ margin-left: 2em;
+ font-size: 0.9em;
+}
+div.toclevel3 {
+ margin-left: 4em;
+ font-size: 0.9em;
+}
+div.toclevel4 {
+ margin-left: 6em;
+ font-size: 0.9em;
+}
+/* Workarounds for IE6's broken and incomplete CSS2. */
+
+div.sidebar-content {
+ background: #ffffee;
+ border: 1px solid silver;
+ padding: 0.5em;
+}
+div.sidebar-title, div.image-title {
+ color: #527bbd;
+ font-family: sans-serif;
+ font-weight: bold;
+ margin-top: 0.0em;
+ margin-bottom: 0.5em;
+}
+
+div.listingblock div.content {
+ border: 1px solid silver;
+ background: #f4f4f4;
+ padding: 0.5em;
+}
+
+div.quoteblock-attribution {
+ padding-top: 0.5em;
+ text-align: right;
+}
+
+div.verseblock-content {
+ white-space: pre;
+}
+div.verseblock-attribution {
+ padding-top: 0.75em;
+ text-align: left;
+}
+
+div.exampleblock-content {
+ border-left: 2px solid silver;
+ padding-left: 0.5em;
+}
+
+/* IE6 sets dynamically generated links as visited. */
+div#toc a:visited { color: blue; }
+
+/* Because IE6 child selector is broken. */
+div.olist2 ol {
+ list-style-type: lower-alpha;
+}
+div.olist2 div.olist ol {
+ list-style-type: decimal;
+}
+</style>
+<title>git2cl</title>
+</head>
+<body>
+<div id="header">
+<h1>git2cl</h1>
+</div>
+<div id="preamble">
+<div class="sectionbody">
+<div class="para"><p>This is a quick'n'dirty tool to convert git logs to GNU ChangeLog
+format.</p></div>
+<div class="para"><p>The tool invokes <em>git log</em> internally unless you pipe a log to it.
+Thus, typically you would use it as follows:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>jas@mocca:~/src/libtasn1$ git2cl &gt; ChangeLog
+jas@mocca:~/src/libtasn1$</tt></pre>
+</div></div>
+<div class="para"><p>If you don't want git2cl to invoke git log internally, you can use it
+as a pipe. It needs a git log generated with &#8212;pretty &#8212;numstat and
+&#8212;summary. You can use it as follows:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>jas@mocca:~/src/libtasn1$ git log --pretty --numstat --summary | ~/src/git2cl/git2cl &gt; ChangeLog
+jas@mocca:~/src/libtasn1$</tt></pre>
+</div></div>
+<div class="para"><p>The output format is specified by:</p></div>
+<div class="para"><p><a href="http://www.gnu.org/prep/standards/html_node/Change-Logs.html">http://www.gnu.org/prep/standards/html_node/Change-Logs.html</a></p></div>
+<div class="para"><p>My inspiration for writing this tool was the
+<a href="http://www.red-bean.com/cvs2cl/">cvs2cl</a> tool, which I have been
+using in several projects. Replacing it was necessary to seriously
+consider switching from CVS to GIT for my projects.</p></div>
+<div class="para"><p>The canonical home page for git2cl is:
+<a href="http://josefsson.org/git2cl/">http://josefsson.org/git2cl/</a> and its repository can be found at
+<a href="http://repo.or.cz/w/git2cl.git">http://repo.or.cz/w/git2cl.git</a>.</p></div>
+</div>
+</div>
+<h2 id="_credits">Credits</h2>
+<div class="sectionbody">
+<div class="para"><p>Luis Mondesi contributed several improvements.</p></div>
+</div>
+<h2 id="_support">Support</h2>
+<div class="sectionbody">
+<div class="para"><p>Try talking to <a href="mailto:simon@josefsson.org">Simon Josefsson</a>.</p></div>
+</div>
+<div id="footer">
+<div id="footer-text">
+Last updated 2008-08-27 12:42:17 CEST
+</div>
+</div>
+</body>
+</html>
diff --git a/tools/git2cl/git2cl b/tools/git2cl/git2cl
new file mode 100755
index 0000000..1c2ab3f
--- /dev/null
+++ b/tools/git2cl/git2cl
@@ -0,0 +1,372 @@
+#!/usr/bin/perl
+
+# Copyright (C) 2007, 2008 Simon Josefsson <simon@josefsson.org>
+# Copyright (C) 2007 Luis Mondesi <lemsx1@gmail.com>
+# * calls git directly. To use it just:
+# cd ~/Project/my_git_repo; git2cl > ChangeLog
+# * implements strptime()
+# * fixes bugs in $comment parsing
+# - copy input before we remove leading spaces
+# - skip "merge branch" statements as they don't
+# have information about files (i.e. we never
+# go into $state 2)
+# - behaves like a pipe/filter if input is given from the CLI
+# else it calls git log by itself
+#
+# The functions mywrap, last_line_len, wrap_log_entry are derived from
+# the cvs2cl tool, see <http://www.red-bean.com/cvs2cl/>:
+# Copyright (C) 2001,2002,2003,2004 Martyn J. Pearce <fluffy@cpan.org>
+# Copyright (C) 1999 Karl Fogel <kfogel@red-bean.com>
+#
+# git2cl is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# git2cl is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with git2cl; see the file COPYING. If not, write to the Free
+# Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+use strict;
+use POSIX qw(strftime);
+use Text::Wrap qw(wrap);
+use FileHandle;
+
+use constant EMPTY_LOG_MESSAGE => '*** empty log message ***';
+
+# this is a helper hash for stptime.
+# Assumes you are calling 'git log ...' with LC_ALL=C
+my %month = (
+ 'Jan'=>0,
+ 'Feb'=>1,
+ 'Mar'=>2,
+ 'Apr'=>3,
+ 'May'=>4,
+ 'Jun'=>5,
+ 'Jul'=>6,
+ 'Aug'=>7,
+ 'Sep'=>8,
+ 'Oct'=>9,
+ 'Nov'=>10,
+ 'Dec'=>11,
+);
+
+my $fh = new FileHandle;
+
+sub key_ready
+{
+ my ($rin, $nfd);
+ vec($rin, fileno(STDIN), 1) = 1;
+ return $nfd = select($rin, undef, undef, 0);
+}
+
+sub strptime {
+ my $str = shift;
+ return undef if not defined $str;
+
+ # we are parsing this format
+ # Fri Oct 26 00:42:56 2007 -0400
+ # to these fields
+ # sec, min, hour, mday, mon, year, wday = -1, yday = -1, isdst = -1
+ # Luis Mondesi <lemsx1@gmail.com>
+ my @date;
+ if ($str =~ /([[:alpha:]]{3})\s+([[:alpha:]]{3})\s+([[:digit:]]{1,2})\s+([[:digit:]]{1,2}):([[:digit:]]{1,2}):([[:digit:]]{1,2})\s+([[:digit:]]{4})/){
+ push(@date,$6,$5,$4,$3,$month{$2},($7 - 1900),-1,-1,-1);
+ } else {
+ die ("Cannot parse date '$str'\n'");
+ }
+ return @date;
+}
+
+sub mywrap {
+ my ($indent1, $indent2, @text) = @_;
+ # If incoming text looks preformatted, don't get clever
+ my $text = Text::Wrap::wrap($indent1, $indent2, @text);
+ if ( grep /^\s+/m, @text ) {
+ return $text;
+ }
+ my @lines = split /\n/, $text;
+ $indent2 =~ s!^((?: {8})+)!"\t" x (length($1)/8)!e;
+ $lines[0] =~ s/^$indent1\s+/$indent1/;
+ s/^$indent2\s+/$indent2/
+ for @lines[1..$#lines];
+ my $newtext = join "\n", @lines;
+ $newtext .= "\n"
+ if substr($text, -1) eq "\n";
+ return $newtext;
+}
+
+sub last_line_len {
+ my $files_list = shift;
+ my @lines = split (/\n/, $files_list);
+ my $last_line = pop (@lines);
+ return length ($last_line);
+}
+
+# A custom wrap function, sensitive to some common constructs used in
+# log entries.
+sub wrap_log_entry {
+ my $text = shift; # The text to wrap.
+ my $left_pad_str = shift; # String to pad with on the left.
+
+ # These do NOT take left_pad_str into account:
+ my $length_remaining = shift; # Amount left on current line.
+ my $max_line_length = shift; # Amount left for a blank line.
+
+ my $wrapped_text = ''; # The accumulating wrapped entry.
+ my $user_indent = ''; # Inherited user_indent from prev line.
+
+ my $first_time = 1; # First iteration of the loop?
+ my $suppress_line_start_match = 0; # Set to disable line start checks.
+
+ my @lines = split (/\n/, $text);
+ while (@lines) # Don't use `foreach' here, it won't work.
+ {
+ my $this_line = shift (@lines);
+ chomp $this_line;
+
+ if ($this_line =~ /^(\s+)/) {
+ $user_indent = $1;
+ }
+ else {
+ $user_indent = '';
+ }
+
+ # If it matches any of the line-start regexps, print a newline now...
+ if ($suppress_line_start_match)
+ {
+ $suppress_line_start_match = 0;
+ }
+ elsif (($this_line =~ /^(\s*)\*\s+[a-zA-Z0-9]/)
+ || ($this_line =~ /^(\s*)\* [a-zA-Z0-9_\.\/\+-]+/)
+ || ($this_line =~ /^(\s*)\([a-zA-Z0-9_\.\/\+-]+(\)|,\s*)/)
+ || ($this_line =~ /^(\s+)(\S+)/)
+ || ($this_line =~ /^(\s*)- +/)
+ || ($this_line =~ /^()\s*$/)
+ || ($this_line =~ /^(\s*)\*\) +/)
+ || ($this_line =~ /^(\s*)[a-zA-Z0-9](\)|\.|\:) +/))
+ {
+ $length_remaining = $max_line_length - (length ($user_indent));
+ }
+
+ # Now that any user_indent has been preserved, strip off leading
+ # whitespace, so up-folding has no ugly side-effects.
+ $this_line =~ s/^\s*//;
+
+ # Accumulate the line, and adjust parameters for next line.
+ my $this_len = length ($this_line);
+ if ($this_len == 0)
+ {
+ # Blank lines should cancel any user_indent level.
+ $user_indent = '';
+ $length_remaining = $max_line_length;
+ }
+ elsif ($this_len >= $length_remaining) # Line too long, try breaking it.
+ {
+ # Walk backwards from the end. At first acceptable spot, break
+ # a new line.
+ my $idx = $length_remaining - 1;
+ if ($idx < 0) { $idx = 0 };
+ while ($idx > 0)
+ {
+ if (substr ($this_line, $idx, 1) =~ /\s/)
+ {
+ my $line_now = substr ($this_line, 0, $idx);
+ my $next_line = substr ($this_line, $idx);
+ $this_line = $line_now;
+
+ # Clean whitespace off the end.
+ chomp $this_line;
+
+ # The current line is ready to be printed.
+ $this_line .= "\n${left_pad_str}";
+
+ # Make sure the next line is allowed full room.
+ $length_remaining = $max_line_length - (length ($user_indent));
+
+ # Strip next_line, but then preserve any user_indent.
+ $next_line =~ s/^\s*//;
+
+ # Sneak a peek at the user_indent of the upcoming line, so
+ # $next_line (which will now precede it) can inherit that
+ # indent level. Otherwise, use whatever user_indent level
+ # we currently have, which might be none.
+ my $next_next_line = shift (@lines);
+ if ((defined ($next_next_line)) && ($next_next_line =~ /^(\s+)/)) {
+ $next_line = $1 . $next_line if (defined ($1));
+ # $length_remaining = $max_line_length - (length ($1));
+ $next_next_line =~ s/^\s*//;
+ }
+ else {
+ $next_line = $user_indent . $next_line;
+ }
+ if (defined ($next_next_line)) {
+ unshift (@lines, $next_next_line);
+ }
+ unshift (@lines, $next_line);
+
+ # Our new next line might, coincidentally, begin with one of
+ # the line-start regexps, so we temporarily turn off
+ # sensitivity to that until we're past the line.
+ $suppress_line_start_match = 1;
+
+ last;
+ }
+ else
+ {
+ $idx--;
+ }
+ }
+
+ if ($idx == 0)
+ {
+ # We bottomed out because the line is longer than the
+ # available space. But that could be because the space is
+ # small, or because the line is longer than even the maximum
+ # possible space. Handle both cases below.
+
+ if ($length_remaining == ($max_line_length - (length ($user_indent))))
+ {
+ # The line is simply too long -- there is no hope of ever
+ # breaking it nicely, so just insert it verbatim, with
+ # appropriate padding.
+ $this_line = "\n${left_pad_str}${this_line}";
+ }
+ else
+ {
+ # Can't break it here, but may be able to on the next round...
+ unshift (@lines, $this_line);
+ $length_remaining = $max_line_length - (length ($user_indent));
+ $this_line = "\n${left_pad_str}";
+ }
+ }
+ }
+ else # $this_len < $length_remaining, so tack on what we can.
+ {
+ # Leave a note for the next iteration.
+ $length_remaining = $length_remaining - $this_len;
+
+ if ($this_line =~ /\.$/)
+ {
+ $this_line .= " ";
+ $length_remaining -= 2;
+ }
+ else # not a sentence end
+ {
+ $this_line .= " ";
+ $length_remaining -= 1;
+ }
+ }
+
+ # Unconditionally indicate that loop has run at least once.
+ $first_time = 0;
+
+ $wrapped_text .= "${user_indent}${this_line}";
+ }
+
+ # One last bit of padding.
+ $wrapped_text .= "\n";
+
+ return $wrapped_text;
+}
+
+# main
+
+my @date;
+my $author;
+my @files;
+my $comment;
+
+my $state; # 0-header 1-comment 2-files
+my $done = 0;
+
+$state = 0;
+
+# if reading from STDIN, we assume that we are
+# getting git log as input
+if (key_ready())
+{
+
+ #my $dummyfh; # don't care about writing
+ #($fh,$dummyfh) = FileHandle::pipe;
+ $fh->fdopen(*STDIN, 'r');
+}
+else
+{
+ $fh->open("LC_ALL=C git log --pretty --numstat --summary|")
+ or die("Cannot execute git log...$!\n");
+}
+
+while (my $_l = <$fh>) {
+ #print STDERR "debug ($state, " . (@date ? (strftime "%Y-%m-%d", @date) : "") . "): `$_'\n";
+ if ($state == 0) {
+ if ($_l =~ m,^Author: (.*),) {
+ $author = $1;
+ }
+ if ($_l =~ m,^Date: (.*),) {
+ @date = strptime($1);
+ }
+ $state = 1 if ($_l =~ m,^$, and $author and (@date+0>0));
+ } elsif ($state == 1) {
+ # * modifying our input text is a bad choice
+ # let's make a copy of it first, then we remove spaces
+ # * if we meet a "merge branch" statement, we need to start
+ # over and find a real entry
+ # Luis Mondesi <lemsx1@gmail.com>
+ my $_s = $_l;
+ $_s =~ s/^ //g;
+ if ($_s =~ m/^Merge branch/)
+ {
+ $state=0;
+ next;
+ }
+ $comment = $comment . $_s;
+ $state = 2 if ($_l =~ m,^$,);
+ } elsif ($state == 2) {
+ if ($_l =~ m,^([0-9]+)\t([0-9]+)\t(.*)$,) {
+ push @files, $3;
+ }
+ $done = 1 if ($_l =~ m,^$,);
+ }
+
+ if ($done) {
+ print (strftime "%Y-%m-%d $author\n\n", @date);
+
+ my $files = join (", ", @files);
+ $files = mywrap ("\t", "\t", "* $files"), ": ";
+
+ if (index($comment, EMPTY_LOG_MESSAGE) > -1 ) {
+ $comment = "[no log message]\n";
+ }
+
+ my $files_last_line_len = 0;
+ $files_last_line_len = last_line_len($files) + 1;
+ my $msg = wrap_log_entry($comment, "\t", 69-$files_last_line_len, 69);
+
+ $msg =~ s/[ \t]+\n/\n/g;
+
+ print "$files: $msg\n";
+
+ @date = ();
+ $author = "";
+ @files = ();
+ $comment = "";
+
+ $state = 0;
+ $done = 0;
+ }
+}
+
+if (@date + 0)
+{
+ print (strftime "%Y-%m-%d $author\n\n", @date);
+ my $msg = wrap_log_entry($comment, "\t", 69, 69);
+ $msg =~ s/[ \t]+\n/\n/g;
+ print "\t* $msg\n";
+}