Merge remote-tracking branch 'origin/riscv' into sba_tests
This commit is contained in:
commit
4b29af433d
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
|||
Please check the source code files and/or Git commit history for a list of all
|
||||
authors and contributors.
|
|
@ -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.
|
|
@ -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
|
@ -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.
|
|
@ -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
|
|
@ -0,0 +1,7 @@
|
|||
News
|
||||
====
|
||||
|
||||
0.1.0 (2016-12-28)
|
||||
------------------
|
||||
|
||||
* Initial release.
|
|
@ -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>
|
|
@ -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
|
|
@ -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
|
|
@ -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"
|
|
@ -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}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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], [:])))
|
||||
])
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
|
@ -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.
|
|
@ -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].
|
|
@ -0,0 +1,392 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
|
||||
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="generator" content="AsciiDoc 8.2.7" />
|
||||
<style type="text/css">
|
||||
/* Debug borders */
|
||||
p, li, dt, dd, div, pre, h1, h2, h3, h4, h5, h6 {
|
||||
/*
|
||||
border: 1px solid red;
|
||||
*/
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 1em 5% 1em 5%;
|
||||
}
|
||||
|
||||
a {
|
||||
color: blue;
|
||||
text-decoration: underline;
|
||||
}
|
||||
a:visited {
|
||||
color: fuchsia;
|
||||
}
|
||||
|
||||
em {
|
||||
font-style: italic;
|
||||
color: navy;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: bold;
|
||||
color: #083194;
|
||||
}
|
||||
|
||||
tt {
|
||||
color: navy;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: #527bbd;
|
||||
font-family: sans-serif;
|
||||
margin-top: 1.2em;
|
||||
margin-bottom: 0.5em;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
h1, h2, h3 {
|
||||
border-bottom: 2px solid silver;
|
||||
}
|
||||
h2 {
|
||||
padding-top: 0.5em;
|
||||
}
|
||||
h3 {
|
||||
float: left;
|
||||
}
|
||||
h3 + * {
|
||||
clear: left;
|
||||
}
|
||||
|
||||
div.sectionbody {
|
||||
font-family: serif;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 1px solid silver;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
ul, ol, li > p {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
span#author {
|
||||
color: #527bbd;
|
||||
font-family: sans-serif;
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
span#email {
|
||||
}
|
||||
span#revision {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
div#footer {
|
||||
font-family: sans-serif;
|
||||
font-size: small;
|
||||
border-top: 2px solid silver;
|
||||
padding-top: 0.5em;
|
||||
margin-top: 4.0em;
|
||||
}
|
||||
div#footer-text {
|
||||
float: left;
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
div#footer-badges {
|
||||
float: right;
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
|
||||
div#preamble,
|
||||
div.tableblock, div.imageblock, div.exampleblock, div.verseblock,
|
||||
div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
|
||||
div.admonitionblock {
|
||||
margin-right: 10%;
|
||||
margin-top: 1.5em;
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
div.admonitionblock {
|
||||
margin-top: 2.5em;
|
||||
margin-bottom: 2.5em;
|
||||
}
|
||||
|
||||
div.content { /* Block element content. */
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Block element titles. */
|
||||
div.title, caption.title {
|
||||
color: #527bbd;
|
||||
font-family: sans-serif;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
margin-top: 1.0em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
div.title + * {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
td div.title:first-child {
|
||||
margin-top: 0.0em;
|
||||
}
|
||||
div.content div.title:first-child {
|
||||
margin-top: 0.0em;
|
||||
}
|
||||
div.content + div.title {
|
||||
margin-top: 0.0em;
|
||||
}
|
||||
|
||||
div.sidebarblock > div.content {
|
||||
background: #ffffee;
|
||||
border: 1px solid silver;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
div.listingblock {
|
||||
margin-right: 0%;
|
||||
}
|
||||
div.listingblock > div.content {
|
||||
border: 1px solid silver;
|
||||
background: #f4f4f4;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
div.quoteblock {
|
||||
padding-left: 2.0em;
|
||||
}
|
||||
div.quoteblock > div.attribution {
|
||||
padding-top: 0.5em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
div.verseblock {
|
||||
padding-left: 2.0em;
|
||||
}
|
||||
div.verseblock > div.content {
|
||||
white-space: pre;
|
||||
}
|
||||
div.verseblock > div.attribution {
|
||||
padding-top: 0.75em;
|
||||
text-align: left;
|
||||
}
|
||||
/* DEPRECATED: Pre version 8.2.7 verse style literal block. */
|
||||
div.verseblock + div.attribution {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
div.admonitionblock .icon {
|
||||
vertical-align: top;
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
text-decoration: underline;
|
||||
color: #527bbd;
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
div.admonitionblock td.content {
|
||||
padding-left: 0.5em;
|
||||
border-left: 2px solid silver;
|
||||
}
|
||||
|
||||
div.exampleblock > div.content {
|
||||
border-left: 2px solid silver;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
div.imageblock div.content { padding-left: 0; }
|
||||
div.imageblock img { border: 1px solid silver; }
|
||||
span.image img { border-style: none; }
|
||||
|
||||
dl {
|
||||
margin-top: 0.8em;
|
||||
margin-bottom: 0.8em;
|
||||
}
|
||||
dt {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0;
|
||||
font-style: normal;
|
||||
}
|
||||
dd > *:first-child {
|
||||
margin-top: 0.1em;
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
list-style-position: outside;
|
||||
}
|
||||
div.olist > ol {
|
||||
list-style-type: decimal;
|
||||
}
|
||||
div.olist2 > ol {
|
||||
list-style-type: lower-alpha;
|
||||
}
|
||||
|
||||
div.tableblock > table {
|
||||
border: 3px solid #527bbd;
|
||||
}
|
||||
thead {
|
||||
font-family: sans-serif;
|
||||
font-weight: bold;
|
||||
}
|
||||
tfoot {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.hlist {
|
||||
margin-top: 0.8em;
|
||||
margin-bottom: 0.8em;
|
||||
}
|
||||
div.hlist td {
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
td.hlist1 {
|
||||
vertical-align: top;
|
||||
font-style: normal;
|
||||
padding-right: 0.8em;
|
||||
}
|
||||
td.hlist2 {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
@media print {
|
||||
div#footer-badges { display: none; }
|
||||
}
|
||||
|
||||
div#toctitle {
|
||||
color: #527bbd;
|
||||
font-family: sans-serif;
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
margin-top: 1.0em;
|
||||
margin-bottom: 0.1em;
|
||||
}
|
||||
|
||||
div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
div.toclevel2 {
|
||||
margin-left: 2em;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
div.toclevel3 {
|
||||
margin-left: 4em;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
div.toclevel4 {
|
||||
margin-left: 6em;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
/* Workarounds for IE6's broken and incomplete CSS2. */
|
||||
|
||||
div.sidebar-content {
|
||||
background: #ffffee;
|
||||
border: 1px solid silver;
|
||||
padding: 0.5em;
|
||||
}
|
||||
div.sidebar-title, div.image-title {
|
||||
color: #527bbd;
|
||||
font-family: sans-serif;
|
||||
font-weight: bold;
|
||||
margin-top: 0.0em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
div.listingblock div.content {
|
||||
border: 1px solid silver;
|
||||
background: #f4f4f4;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
div.quoteblock-attribution {
|
||||
padding-top: 0.5em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
div.verseblock-content {
|
||||
white-space: pre;
|
||||
}
|
||||
div.verseblock-attribution {
|
||||
padding-top: 0.75em;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
div.exampleblock-content {
|
||||
border-left: 2px solid silver;
|
||||
padding-left: 0.5em;
|
||||
}
|
||||
|
||||
/* IE6 sets dynamically generated links as visited. */
|
||||
div#toc a:visited { color: blue; }
|
||||
|
||||
/* Because IE6 child selector is broken. */
|
||||
div.olist2 ol {
|
||||
list-style-type: lower-alpha;
|
||||
}
|
||||
div.olist2 div.olist ol {
|
||||
list-style-type: decimal;
|
||||
}
|
||||
</style>
|
||||
<title>git2cl</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="header">
|
||||
<h1>git2cl</h1>
|
||||
</div>
|
||||
<div id="preamble">
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>This is a quick'n'dirty tool to convert git logs to GNU ChangeLog
|
||||
format.</p></div>
|
||||
<div class="para"><p>The tool invokes <em>git log</em> internally unless you pipe a log to it.
|
||||
Thus, typically you would use it as follows:</p></div>
|
||||
<div class="literalblock">
|
||||
<div class="content">
|
||||
<pre><tt>jas@mocca:~/src/libtasn1$ git2cl > ChangeLog
|
||||
jas@mocca:~/src/libtasn1$</tt></pre>
|
||||
</div></div>
|
||||
<div class="para"><p>If you don't want git2cl to invoke git log internally, you can use it
|
||||
as a pipe. It needs a git log generated with —pretty —numstat and
|
||||
—summary. You can use it as follows:</p></div>
|
||||
<div class="literalblock">
|
||||
<div class="content">
|
||||
<pre><tt>jas@mocca:~/src/libtasn1$ git log --pretty --numstat --summary | ~/src/git2cl/git2cl > ChangeLog
|
||||
jas@mocca:~/src/libtasn1$</tt></pre>
|
||||
</div></div>
|
||||
<div class="para"><p>The output format is specified by:</p></div>
|
||||
<div class="para"><p><a href="http://www.gnu.org/prep/standards/html_node/Change-Logs.html">http://www.gnu.org/prep/standards/html_node/Change-Logs.html</a></p></div>
|
||||
<div class="para"><p>My inspiration for writing this tool was the
|
||||
<a href="http://www.red-bean.com/cvs2cl/">cvs2cl</a> tool, which I have been
|
||||
using in several projects. Replacing it was necessary to seriously
|
||||
consider switching from CVS to GIT for my projects.</p></div>
|
||||
<div class="para"><p>The canonical home page for git2cl is:
|
||||
<a href="http://josefsson.org/git2cl/">http://josefsson.org/git2cl/</a> and its repository can be found at
|
||||
<a href="http://repo.or.cz/w/git2cl.git">http://repo.or.cz/w/git2cl.git</a>.</p></div>
|
||||
</div>
|
||||
</div>
|
||||
<h2 id="_credits">Credits</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>Luis Mondesi contributed several improvements.</p></div>
|
||||
</div>
|
||||
<h2 id="_support">Support</h2>
|
||||
<div class="sectionbody">
|
||||
<div class="para"><p>Try talking to <a href="mailto:simon@josefsson.org">Simon Josefsson</a>.</p></div>
|
||||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2008-08-27 12:42:17 CEST
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -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";
|
||||
}
|
Loading…
Reference in New Issue