Merge remote-tracking branch 'origin/riscv' into sba_tests

This commit is contained in:
Megan Wachs 2018-08-31 09:02:55 -07:00
commit 4b29af433d
53 changed files with 13337 additions and 12 deletions

8
.gitmodules vendored
View File

@ -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

View File

@ -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

@ -1 +0,0 @@
Subproject commit 8645845c1abebd004e991ba9a7f808f4fd0c608b

24
src/jtag/drivers/libjaylink/.gitignore vendored Normal file
View File

@ -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

View File

@ -0,0 +1,2 @@
Please check the source code files and/or Git commit history for a list of all
authors and contributors.

View File

@ -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.

View File

@ -0,0 +1 @@
Please check the Git commit history for a detailed list of changes.

File diff suppressed because it is too large Load Diff

View File

@ -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.

View File

@ -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

View File

@ -0,0 +1,7 @@
News
====
0.1.0 (2016-12-28)
------------------
* Initial release.

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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}

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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";
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 */

View File

@ -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 */

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 */

View File

@ -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], [:])))
])

View File

@ -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)
{

View File

@ -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);

View File

@ -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;
}

View File

@ -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) {
@ -1568,6 +1587,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,

View File

@ -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.*/

@ -1 +0,0 @@
Subproject commit 8373c9f74993e218a08819cbcdbab3f3564bbeba

339
tools/git2cl/COPYING Normal file
View File

@ -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.

45
tools/git2cl/README Normal file
View File

@ -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].

392
tools/git2cl/README.html Normal file
View File

@ -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>

372
tools/git2cl/git2cl Executable file
View File

@ -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";
}