diff options
author | Megan Wachs <megan@sifive.com> | 2018-08-31 09:02:55 -0700 |
---|---|---|
committer | Megan Wachs <megan@sifive.com> | 2018-08-31 09:02:55 -0700 |
commit | 4b29af433d2a4b37771739b0f781e2df54f18d12 (patch) | |
tree | c16be888b79f57d6d421d7f8f125fb35b1843fcf | |
parent | a0afcba66dda9b3213fc061dd744c79eaa134dc4 (diff) | |
parent | 167f0071d1b3670d5179e4f83447c882aae64fc2 (diff) | |
download | riscv-openocd-4b29af433d2a4b37771739b0f781e2df54f18d12.zip riscv-openocd-4b29af433d2a4b37771739b0f781e2df54f18d12.tar.gz riscv-openocd-4b29af433d2a4b37771739b0f781e2df54f18d12.tar.bz2 |
Merge remote-tracking branch 'origin/riscv' into sba_testssba_tests
53 files changed, 13337 insertions, 10 deletions
diff --git a/.gitmodules b/.gitmodules index 5865ff9..b99c87a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,3 @@ -[submodule "tools/git2cl"] - path = tools/git2cl - url = http://repo.or.cz/r/git2cl.git [submodule "jimtcl"] path = jimtcl - url = http://repo.or.cz/r/jimtcl.git -[submodule "src/jtag/drivers/libjaylink"] - path = src/jtag/drivers/libjaylink - url = http://repo.or.cz/r/libjaylink.git + url = https://github.com/msteveb/jimtcl diff --git a/.travis.yml b/.travis.yml index 88a6b8d..3691a1c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -55,6 +55,8 @@ script: # 50 changes any case. Most merges won't consist of more than 40 changes, # so this should work fine most of the time, and be a lot better than not # checking at all. - - git diff -U20 HEAD~40 | ./tools/scripts/checkpatch.pl --no-signoff - + - git diff -U20 HEAD~40 | + filterdiff -x "src/jtag/drivers/libjaylink/*" -x "tools/git2cl/*" | + ./tools/scripts/checkpatch.pl --no-signoff - - ./bootstrap && ./configure --enable-remote-bitbang --enable-jtag_vpi $CONFIGURE_ARGS && make - file src/$EXECUTABLE diff --git a/src/jtag/drivers/libjaylink b/src/jtag/drivers/libjaylink deleted file mode 160000 -Subproject 8645845c1abebd004e991ba9a7f808f4fd0c608 diff --git a/src/jtag/drivers/libjaylink/.gitignore b/src/jtag/drivers/libjaylink/.gitignore new file mode 100644 index 0000000..0197dc0 --- /dev/null +++ b/src/jtag/drivers/libjaylink/.gitignore @@ -0,0 +1,24 @@ +aclocal.m4 +autom4te.cache +build-aux +config.h* +config.log +config.status +configure +configure.gnu +.deps +doxy/ +Doxyfile +INSTALL +*.la +libjaylink.pc +.libs +libtool +*.lo +m4/libtool.m4 +m4/lt*.m4 +Makefile +Makefile.in +*.o +stamp-h1 +version.h diff --git a/src/jtag/drivers/libjaylink/AUTHORS b/src/jtag/drivers/libjaylink/AUTHORS new file mode 100644 index 0000000..507d6e0 --- /dev/null +++ b/src/jtag/drivers/libjaylink/AUTHORS @@ -0,0 +1,2 @@ +Please check the source code files and/or Git commit history for a list of all +authors and contributors. diff --git a/src/jtag/drivers/libjaylink/COPYING b/src/jtag/drivers/libjaylink/COPYING new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/src/jtag/drivers/libjaylink/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/src/jtag/drivers/libjaylink/ChangeLog b/src/jtag/drivers/libjaylink/ChangeLog new file mode 100644 index 0000000..fade3c9 --- /dev/null +++ b/src/jtag/drivers/libjaylink/ChangeLog @@ -0,0 +1 @@ +Please check the Git commit history for a detailed list of changes. diff --git a/src/jtag/drivers/libjaylink/Doxyfile.in b/src/jtag/drivers/libjaylink/Doxyfile.in new file mode 100644 index 0000000..86c5460 --- /dev/null +++ b/src/jtag/drivers/libjaylink/Doxyfile.in @@ -0,0 +1,2301 @@ +# Doxyfile 1.8.6 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "libjaylink" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = "@JAYLINK_VERSION_PACKAGE@" + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "Library to access J-Link devices" + +# With the PROJECT_LOGO tag one can specify an logo or icon that is included in +# the documentation. The maximum height of the logo should not exceed 55 pixels +# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo +# to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = doxy + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a +# new page for each member. If set to NO, the documentation of a member will be +# part of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. +# +# Note For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by by putting a % sign in front of the word +# or globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO these classes will be included in the various overviews. This option has +# no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the +# todo list. This list is created by putting \todo commands in the +# documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the +# test list. This list is created by putting \test commands in the +# documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if <section_label> ... \endif and \cond <section_label> +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES the list +# will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. Do not use file names with spaces, bibtex cannot handle them. See +# also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO doxygen will only warn about wrong or incomplete parameter +# documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. +# Note: If this tag is empty the current directory is searched. + +INPUT = @top_srcdir@/libjaylink \ + @top_builddir@/libjaylink/version.h + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank the +# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, +# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, +# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, +# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, +# *.qsf, *.as and *.js. + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = @top_srcdir@/libjaylink/buffer.c \ + @top_srcdir@/libjaylink/discovery_tcp.c \ + @top_srcdir@/libjaylink/discovery_usb.c \ + @top_srcdir@/libjaylink/libjaylink-internal.h \ + @top_srcdir@/libjaylink/list.c \ + @top_srcdir@/libjaylink/socket.c \ + @top_srcdir@/libjaylink/transport.c \ + @top_srcdir@/libjaylink/transport_tcp.c \ + @top_srcdir@/libjaylink/transport_usb.c + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# <filter> <input-file> +# +# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER ) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- +# defined cascading style sheet that is included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefor more robust against future updates. +# Doxygen will copy the style sheet file to the output directory. For an example +# see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the stylesheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated ( +# YES) or that it should be included in the master .chm file ( NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated ( +# YES) or a normal table of contents ( NO) in the .chm file. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 1 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use <access key> + S +# (what the <access key> is depends on the OS and browser, but it is typically +# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down +# key> to jump into the search results window, the results can be navigated +# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel +# the search. The filter options can be selected when the cursor is inside the +# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys> +# to select a filter and <Enter> or <escape> to activate or cancel the filter +# option. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using Javascript. There +# are two flavours of web server based searching depending on the +# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for +# searching and an index file used by the script. When EXTERNAL_SEARCH is +# enabled the indexing and searching needs to be provided by external tools. See +# the section "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain the +# search results. +# +# Doxygen ships with an example indexer ( doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: http://xapian.org/). +# +# See the section "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will return the search results when EXTERNAL_SEARCH is enabled. +# +# Doxygen ships with an example indexer ( doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: http://xapian.org/). See the section "External Indexing and +# Searching" for details. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. +# The default file is: searchdata.xml. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of +# to a relative location where the documentation can be found. The format is: +# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ... +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTRA_SEARCH_MAPPINGS = + +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output. +# The default value is: YES. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: latex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. +# +# Note that when enabling USE_PDFLATEX this option is only used for generating +# bitmaps for formulas in the HTML output, but not in the Makefile that is +# written to the output directory. +# The default file is: latex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate +# index for LaTeX. +# The default file is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used by the +# printer. +# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x +# 14 inches) and executive (7.25 x 10.5 inches). +# The default value is: a4. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names +# that should be included in the LaTeX output. To get the times font for +# instance you can specify +# EXTRA_PACKAGES=times +# If left blank no extra packages will be included. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the +# generated LaTeX document. The header should contain everything until the first +# chapter. If it is left blank doxygen will generate a standard header. See +# section "Doxygen usage" for information on how to let doxygen write the +# default header to a separate file. +# +# Note: Only use a user-defined header if you know what you are doing! The +# following commands have a special meaning inside the header: $title, +# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will +# replace them by respectively the title of the page, the current date and time, +# only the current date, the version number of doxygen, the project name (see +# PROJECT_NAME), or the project number (see PROJECT_NUMBER). +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the +# generated LaTeX document. The footer should contain everything after the last +# chapter. If it is left blank doxygen will generate a standard footer. +# +# Note: Only use a user-defined footer if you know what you are doing! +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_FOOTER = + +# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the LATEX_OUTPUT output +# directory. Note that the files will be copied as-is; there are no commands or +# markers available. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_FILES = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is +# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will +# contain links (just like the HTML output) instead of page references. This +# makes the output suitable for online browsing using a PDF viewer. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +PDF_HYPERLINKS = YES + +# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate +# the PDF file directly from the LaTeX files. Set this option to YES to get a +# higher quality PDF documentation. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode +# command to the generated LaTeX files. This will instruct LaTeX to keep running +# if errors occur, instead of asking the user for help. This option is also used +# when generating formulas in HTML. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_BATCHMODE = NO + +# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the +# index chapters (such as File Index, Compound Index, etc.) in the output. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_HIDE_INDICES = NO + +# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source +# code with syntax highlighting in the LaTeX output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. See +# http://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# The default value is: plain. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# Configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The +# RTF output is optimized for Word 97 and may not look too pretty with other RTF +# readers/editors. +# The default value is: NO. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: rtf. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will +# contain hyperlink fields. The RTF file will contain links (just like the HTML +# output) instead of page references. This makes the output suitable for online +# browsing using Word or some other Word compatible readers that support those +# fields. +# +# Note: WordPad (write) and others do not support links. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's config +# file, i.e. a series of assignments. You only have to provide replacements, +# missing definitions are set to their default value. +# +# See also section "Doxygen usage" for information on how to generate the +# default style sheet that doxygen normally uses. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an RTF document. Syntax is +# similar to doxygen's config file. A template extensions file can be generated +# using doxygen -e rtf extensionFile. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for +# classes and files. +# The default value is: NO. + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. A directory man3 will be created inside the directory specified by +# MAN_OUTPUT. +# The default directory is: man. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to the generated +# man pages. In case the manual section does not start with a number, the number +# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is +# optional. +# The default value is: .3. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it +# will generate one additional man file for each entity documented in the real +# man page(s). These additional files only source the real man page, but without +# them the man command would be unable to find the correct page. +# The default value is: NO. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that +# captures the structure of the code including all documentation. +# The default value is: NO. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: xml. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_OUTPUT = xml + +# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program +# listings (including syntax highlighting and cross-referencing information) to +# the XML output. Note that enabling this will significantly increase the size +# of the XML output. +# The default value is: YES. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- + +# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files +# that can be used to generate PDF. +# The default value is: NO. + +GENERATE_DOCBOOK = NO + +# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in +# front of it. +# The default directory is: docbook. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_OUTPUT = docbook + +#--------------------------------------------------------------------------- +# Configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen +# Definitions (see http://autogen.sf.net) file that captures the structure of +# the code including all documentation. Note that this feature is still +# experimental and incomplete at the moment. +# The default value is: NO. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module +# file that captures the structure of the code including all documentation. +# +# Note that this feature is still experimental and incomplete at the moment. +# The default value is: NO. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary +# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI +# output from the Perl module output. +# The default value is: NO. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely +# formatted so it can be parsed by a human reader. This is useful if you want to +# understand what is going on. On the other hand, if this tag is set to NO the +# size of the Perl module output will be much smaller and Perl will parse it +# just the same. +# The default value is: YES. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file are +# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful +# so different doxyrules.make files included by the same Makefile don't +# overwrite each other's variables. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all +# C-preprocessor directives found in the sources and include files. +# The default value is: YES. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names +# in the source code. If set to NO only conditional compilation will be +# performed. Macro expansion can be done in a controlled way by setting +# EXPAND_ONLY_PREDEF to YES. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then +# the macro expansion is limited to the macros specified with the PREDEFINED and +# EXPAND_AS_DEFINED tags. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES the includes files in the +# INCLUDE_PATH will be searched if a #include is found. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by the +# preprocessor. +# This tag requires that the tag SEARCH_INCLUDES is set to YES. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will be +# used. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that are +# defined before the preprocessor is started (similar to the -D option of e.g. +# gcc). The argument of the tag is a list of macros of the form: name or +# name=definition (no spaces). If the definition and the "=" are omitted, "=1" +# is assumed. To prevent a macro definition from being undefined via #undef or +# recursively expanded use the := operator instead of the = operator. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +PREDEFINED = JAYLINK_API= \ + JAYLINK_PRIV + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this +# tag can be used to specify a list of macro names that should be expanded. The +# macro definition that is found in the sources will be used. Use the PREDEFINED +# tag if you want to use a different macro definition that overrules the +# definition found in the source code. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will +# remove all refrences to function-like macros that are alone on a line, have an +# all uppercase name, and do not end with a semicolon. Such function macros are +# typically used for boiler-plate code, and will confuse the parser if not +# removed. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tag files. For each tag +# file the location of the external documentation should be added. The format of +# a tag file without this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where loc1 and loc2 can be relative or absolute paths or URLs. See the +# section "Linking to external documentation" for more information about the use +# of tag files. +# Note: Each tag file must have an unique name (where the name does NOT include +# the path). If a tag file is not located in the directory in which doxygen is +# run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create a +# tag file that is based on the input files it reads. See section "Linking to +# external documentation" for more information about the usage of tag files. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external class will be listed in the +# class index. If set to NO only the inherited external classes will be listed. +# The default value is: NO. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in +# the modules index. If set to NO, only the current project's groups will be +# listed. +# The default value is: YES. + +EXTERNAL_GROUPS = YES + +# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in +# the related pages index. If set to NO, only the current project's pages will +# be listed. +# The default value is: YES. + +EXTERNAL_PAGES = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of 'which perl'). +# The default file (with absolute path) is: /usr/bin/perl. + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram +# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to +# NO turns the diagrams off. Note that this option also works with HAVE_DOT +# disabled, but it is recommended to install and use dot, since it yields more +# powerful graphs. +# The default value is: YES. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see: +# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# You can include diagrams made with dia in doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. + +DIA_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide inheritance +# and usage relations if the target is undocumented or is not a class. +# The default value is: YES. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz (see: +# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent +# Bell Labs. The other options in this section have no effect if this option is +# set to NO +# The default value is: NO. + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed +# to run in parallel. When set to 0 doxygen will base this on the number of +# processors available in the system. You can set it explicitly to a value +# larger than 0 to get control over the balance between CPU load and processing +# speed. +# Minimum value: 0, maximum value: 32, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_NUM_THREADS = 0 + +# When you want a differently looking font n the dot files that doxygen +# generates you can specify the font name using DOT_FONTNAME. You need to make +# sure dot is able to find the font, which can be done by putting it in a +# standard location or by setting the DOTFONTPATH environment variable or by +# setting DOT_FONTPATH to the directory containing the font. +# The default value is: Helvetica. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of +# dot graphs. +# Minimum value: 4, maximum value: 24, default value: 10. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the default font as specified with +# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set +# the path where dot can find it using this tag. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTPATH = + +# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for +# each documented class showing the direct and indirect inheritance relations. +# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a +# graph for each documented class showing the direct and indirect implementation +# dependencies (inheritance, containment, and class references variables) of the +# class with other documented classes. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for +# groups, showing the direct groups dependencies. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside the +# class node. If there are many fields or methods and many nodes the graph may +# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the +# number of items for each type to make the size more manageable. Set this to 0 +# for no limit. Note that the threshold may be exceeded by 50% before the limit +# is enforced. So when you set the threshold to 10, up to 15 fields may appear, +# but if the number exceeds 15, the total amount of fields shown is limited to +# 10. +# Minimum value: 0, maximum value: 100, default value: 10. +# This tag requires that the tag HAVE_DOT is set to YES. + +UML_LIMIT_NUM_FIELDS = 10 + +# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and +# collaboration graphs will show the relations between templates and their +# instances. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +TEMPLATE_RELATIONS = NO + +# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to +# YES then doxygen will generate a graph for each documented file showing the +# direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDE_GRAPH = YES + +# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are +# set to YES then doxygen will generate a graph for each documented file showing +# the direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH tag is set to YES then doxygen will generate a call +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable caller graphs for selected +# functions only using the \callergraph command. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical +# hierarchy of all classes instead of a textual one. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the +# dependencies a directory has on other directories in a graphical way. The +# dependency relations are determined by the #include relations between the +# files in the directories. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. +# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order +# to make the SVG files visible in IE 9+ (other browsers do not have this +# requirement). +# Possible values are: png, jpg, gif and svg. +# The default value is: png. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# +# Note that this requires a modern browser other than Internet Explorer. Tested +# and working are Firefox, Chrome, Safari, and Opera. +# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make +# the SVG files visible. Older versions of IE do not have SVG support. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +INTERACTIVE_SVG = NO + +# The DOT_PATH tag can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the \dotfile +# command). +# This tag requires that the tag HAVE_DOT is set to YES. + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the \mscfile +# command). + +MSCFILE_DIRS = + +# The DIAFILE_DIRS tag can be used to specify one or more directories that +# contain dia files that are included in the documentation (see the \diafile +# command). + +DIAFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes +# that will be shown in the graph. If the number of nodes in a graph becomes +# larger than this value, doxygen will truncate the graph, which is visualized +# by representing a node as a red box. Note that doxygen if the number of direct +# children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that +# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. +# Minimum value: 0, maximum value: 10000, default value: 50. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs +# generated by dot. A depth value of 3 means that only nodes reachable from the +# root by following a path via at most 3 edges will be shown. Nodes that lay +# further from the root node will be omitted. Note that setting this option to 1 +# or 2 may greatly reduce the computation time needed for large code bases. Also +# note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. +# Minimum value: 0, maximum value: 1000, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not seem +# to support this out of the box. +# +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) support +# this, this feature is disabled by default. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_MULTI_TARGETS = YES + +# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page +# explaining the meaning of the various boxes and arrows in the dot generated +# graphs. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot +# files that are used to generate the various graphs. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_CLEANUP = YES diff --git a/src/jtag/drivers/libjaylink/HACKING b/src/jtag/drivers/libjaylink/HACKING new file mode 100644 index 0000000..a654e2a --- /dev/null +++ b/src/jtag/drivers/libjaylink/HACKING @@ -0,0 +1,68 @@ +Hacking +======= + +This document describes how to start hacking on the libjaylink project. +Make sure you read through the README file before continuing. + + +Coding style +------------ + +This project uses the Linux kernel coding style where appropiate, see +<https://www.kernel.org/doc/Documentation/CodingStyle> for details. + +Amendments to the Linux kernel coding style: + + - Do not use goto statements. + - Always declare variables at the beginng of a function. + - Do not assign values to variables at declaration time. + + +Contributions +------------- + +The following ways can be used to submit a contribution to the libjaylink +project: + + - Send patches generated with `git format-patch`. + - Push your changes to a public Git repository and send the URL where to pull + them from. + +In any case, send directly to <jaylink-dev@marcschink.de>. +Before submitting, please consider the following: + + - Every single patch must be compilable. + - Your contribution must work on all operating systems supported by + libjaylink. + - Develop your contribution against the current Git master branch. + - Check your contribution for memory leaks and similar errors by using + *valgrind*. + + +Bug reports +----------- + +Send bug reports directly to <jaylink-dev@marcschink.de>. +Please try to include all of the following information in your report: + + - Instructions to reproduce the bug (e.g., command-line invocations) + - Debug log output of libjaylink + - Information about your environment: + - Version of libjaylink + - Debug hardware, including hardware and firmware version (e.g., + J-Link Ultra V4 compiled Sep 4 2015 18:12:49) + - Operating system (e.g., Debian GNU/Linux 8.4) + - Backtraces if the application using libjaylink is crashing. + +If the bug report is for a regression, additionally include the information +above about the working version where appropiate. + +Please develop and attach a patch that solves the reported bug, if possible. +See the guidelines for contributions above. + + +Random notes +------------ + + - Always use `log_err()`, `log_warn()`, `log_info()` and `log_dbg()` to output + messages. Never use `printf()` or similar functions directly. diff --git a/src/jtag/drivers/libjaylink/Makefile.am b/src/jtag/drivers/libjaylink/Makefile.am new file mode 100644 index 0000000..6cead16 --- /dev/null +++ b/src/jtag/drivers/libjaylink/Makefile.am @@ -0,0 +1,28 @@ +## +## This file is part of the libjaylink project. +## +## Copyright (C) 2014-2015 Marc Schink <jaylink-dev@marcschink.de> +## +## This program is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see <http://www.gnu.org/licenses/>. +## + +ACLOCAL_AMFLAGS = -I m4 +SUBDIRS = libjaylink + +if !SUBPROJECT_BUILD +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libjaylink.pc +endif + +EXTRA_DIST = HACKING contrib/99-libjaylink.rules diff --git a/src/jtag/drivers/libjaylink/NEWS b/src/jtag/drivers/libjaylink/NEWS new file mode 100644 index 0000000..39244f6 --- /dev/null +++ b/src/jtag/drivers/libjaylink/NEWS @@ -0,0 +1,7 @@ +News +==== + +0.1.0 (2016-12-28) +------------------ + + * Initial release. diff --git a/src/jtag/drivers/libjaylink/README b/src/jtag/drivers/libjaylink/README new file mode 100644 index 0000000..d01c904 --- /dev/null +++ b/src/jtag/drivers/libjaylink/README @@ -0,0 +1,70 @@ +libjaylink +========== + +libjaylink is a shared library written in C to access SEGGER J-Link and +compatible devices. + + +Requirements +------------ + +libjaylink requires the following packages: + + - GCC (>= 4.0) or Clang + - Make + - pkg-config >= 0.23 + - libusb >= 1.0.9 + - Doxygen (optional, only required for API documentation) + +If you're building libjaylink from Git, the following packages are additionally +required: + + - Git + - Libtool + - Autoconf >= 2.64 + - Automake >= 1.9 + + +Building and installing +----------------------- + +In order to get and build the latest Git version of libjaylink, run the +following commands: + + $ git clone git://git.zapb.de/libjaylink.git + $ cd libjaylink + $ ./autogen.sh + $ ./configure + $ make + +After `make` finishes without any errors, use the following command to install +libjaylink: + + $ make install + + +Portability +----------- + +libjaylink supports the following operating systems: + + - GNU/Linux + - FreeBSD + - OpenBSD + - NetBSD + - Microsoft Windows + - Cygwin, MinGW and MSYS2 + - OS X + + +Copyright and license +--------------------- + +libjaylink is licensed under the terms of the GNU General Public License (GPL), +version 2 or later. See COPYING file for details. + + +Website +------- + +<http://git.zapb.de/libjaylink.git> diff --git a/src/jtag/drivers/libjaylink/autogen.sh b/src/jtag/drivers/libjaylink/autogen.sh new file mode 100755 index 0000000..1df262f --- /dev/null +++ b/src/jtag/drivers/libjaylink/autogen.sh @@ -0,0 +1,34 @@ +#!/bin/sh +## +## This file is part of the libjaylink project. +## +## Copyright (C) 2014-2015 Marc Schink <jaylink-dev@marcschink.de> +## +## This program is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see <http://www.gnu.org/licenses/>. +## + +OS=`uname -s` +LIBTOOLIZE=libtoolize + +if [ "x$OS" = "xDarwin" ]; then + LIBTOOLIZE=glibtoolize +fi + +echo "Generating build system..." + +$LIBTOOLIZE --install --copy || exit 1 +aclocal -I m4 || exit 1 +autoheader || exit 1 +autoconf || exit 1 +automake --add-missing --copy || exit 1 diff --git a/src/jtag/drivers/libjaylink/configure.ac b/src/jtag/drivers/libjaylink/configure.ac new file mode 100644 index 0000000..de5919c --- /dev/null +++ b/src/jtag/drivers/libjaylink/configure.ac @@ -0,0 +1,140 @@ +## +## This file is part of the libjaylink project. +## +## Copyright (C) 2014-2015 Marc Schink <jaylink-dev@marcschink.de> +## +## This program is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see <http://www.gnu.org/licenses/>. +## + +AC_PREREQ([2.64]) + +AC_INIT([libjaylink], [0.2.0], [jaylink-dev@marcschink.de], [libjaylink], + [http://git.zapb.de/libjaylink.git]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_AUX_DIR([build-aux]) + +AC_CANONICAL_HOST + +AM_INIT_AUTOMAKE([-Wall -Werror]) + +# Enable additional compiler warnings via -Wall and -Wextra. Use hidden +# visibility for all non-static symbols by default with -fvisibility=hidden. +JAYLINK_CFLAGS="-Wall -Wextra -Werror -fvisibility=hidden" + +# Checks for programs. +AC_PROG_CC + +# Automake >= 1.12 requires AM_PROG_AR when using options -Wall and -Werror. +# To be compatible with older versions of Automake use AM_PROG_AR if it's +# defined only. This line must occur before LT_INIT. +m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) + +# Initialize libtool. +LT_INIT + +# Initialize pkg-config. +PKG_PROG_PKG_CONFIG + +# Checks for libraries. + +# Check for libusb-1.0 which is always needed. +PKG_CHECK_MODULES([libusb], [libusb-1.0 >= 1.0.9], + [HAVE_LIBUSB=yes], [HAVE_LIBUSB=no]) + +AS_IF([test "x$HAVE_LIBUSB" = "xyes"], + [libusb_msg="yes"], [libusb_msg="no (missing: libusb-1.0)"]) + +# Checks for header files. + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_BIGENDIAN + +# Checks for library functions. + +# Disable progress and informational output of libtool. +AC_SUBST([AM_LIBTOOLFLAGS], '--silent') + +JAYLINK_SET_PACKAGE_VERSION([JAYLINK_VERSION_PACKAGE], [AC_PACKAGE_VERSION]) + +# Libtool interface version of libjaylink. This is not the same as the package +# version. For information about the versioning system of libtool, see: +# http://www.gnu.org/software/libtool/manual/libtool.html#Libtool-versioning +JAYLINK_SET_LIBRARY_VERSION([JAYLINK_VERSION_LIBRARY], [0:0:0]) + +AC_ARG_ENABLE([subproject-build], AS_HELP_STRING([--enable-subproject-build], + [enable sub-project build [default=no]])) + +AM_CONDITIONAL([SUBPROJECT_BUILD], + [test "x$enable_subproject_build" = "xyes"]) + +AC_ARG_WITH([libusb], [AS_HELP_STRING([--without-libusb], + [disable libusb support [default=detect]])]) + +AS_IF([test "x$with_libusb" != "xno"], + [with_libusb="yes"]) + +AS_IF([test "x$with_libusb$HAVE_LIBUSB" = "xyesyes"], + [AC_DEFINE([HAVE_LIBUSB], [1], [Define to 1 if libusb is available.])]) + +AS_IF([test "x$with_libusb" != "xyes"], + [libusb_msg="no (disabled)"]) + +AS_IF([test "x$with_libusb$HAVE_LIBUSB" = "xyesyes"], + [JAYLINK_PKG_LIBS="libusb-1.0"]) + +AM_CONDITIONAL([HAVE_LIBUSB], + [test "x$with_libusb$HAVE_LIBUSB" = "xyesyes"]) + +# Libtool interface version is not used for sub-project build as libjaylink is +# built as libtool convenience library. +AS_IF([test "x$enable_subproject_build" != "xyes"], + [JAYLINK_LDFLAGS="-version-info $JAYLINK_VERSION_LIBRARY"]) + +# Use C99 compatible stdio functions on MinGW instead of the incompatible +# functions provided by Microsoft. +AS_CASE([$host_os], [mingw*], + [AC_DEFINE([__USE_MINGW_ANSI_STDIO], [1], + [Define to 1 to use C99 compatible stdio functions on MinGW.])]) + +# Add the Winsock2 library on MinGW for socket and other network-related +# functions. +AS_CASE([$host_os], [mingw*], [JAYLINK_LIBS="$JAYLINK_LIBS -lws2_32"]) + +AC_SUBST([JAYLINK_CFLAGS]) +AC_SUBST([JAYLINK_LDFLAGS]) +AC_SUBST([JAYLINK_LIBS]) +AC_SUBST([JAYLINK_PKG_LIBS]) + +AC_CONFIG_FILES([Makefile]) +AC_CONFIG_FILES([libjaylink/Makefile]) +AC_CONFIG_FILES([libjaylink/version.h]) +AC_CONFIG_FILES([libjaylink.pc]) +AC_CONFIG_FILES([Doxyfile]) + +AC_OUTPUT + +echo +echo "libjaylink configuration summary:" +echo " - Package version ................ $JAYLINK_VERSION_PACKAGE" +echo " - Library version ................ $JAYLINK_VERSION_LIBRARY" +echo " - Installation prefix ............ $prefix" +echo " - Building on .................... $build" +echo " - Building for ................... $host" + +echo +echo "Enabled transports:" +echo " - USB ............................ $libusb_msg" +echo " - TCP ............................ yes" +echo diff --git a/src/jtag/drivers/libjaylink/contrib/99-libjaylink.rules b/src/jtag/drivers/libjaylink/contrib/99-libjaylink.rules new file mode 100644 index 0000000..120e51a --- /dev/null +++ b/src/jtag/drivers/libjaylink/contrib/99-libjaylink.rules @@ -0,0 +1,41 @@ +## +## This file is part of the libjaylink project. +## +## Copyright (C) 2015 Marc Schink <jaylink-dev@marcschink.de> +## +## This program is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see <http://www.gnu.org/licenses/>. +## + +ACTION!="add", GOTO="libjaylink_end_rules" +SUBSYSTEM!="usb", GOTO="libjaylink_end_rules" + +ATTRS{idVendor}=="1366", ATTRS{idProduct}=="0101", MODE="664", GROUP="plugdev" +ATTRS{idVendor}=="1366", ATTRS{idProduct}=="0102", MODE="664", GROUP="plugdev" +ATTRS{idVendor}=="1366", ATTRS{idProduct}=="0103", MODE="664", GROUP="plugdev" +ATTRS{idVendor}=="1366", ATTRS{idProduct}=="0104", MODE="664", GROUP="plugdev" +ATTRS{idVendor}=="1366", ATTRS{idProduct}=="0105", MODE="664", GROUP="plugdev" +ATTRS{idVendor}=="1366", ATTRS{idProduct}=="0107", MODE="664", GROUP="plugdev" +ATTRS{idVendor}=="1366", ATTRS{idProduct}=="0108", MODE="664", GROUP="plugdev" + +ATTRS{idVendor}=="1366", ATTRS{idProduct}=="1010", MODE="664", GROUP="plugdev" +ATTRS{idVendor}=="1366", ATTRS{idProduct}=="1011", MODE="664", GROUP="plugdev" +ATTRS{idVendor}=="1366", ATTRS{idProduct}=="1012", MODE="664", GROUP="plugdev" +ATTRS{idVendor}=="1366", ATTRS{idProduct}=="1013", MODE="664", GROUP="plugdev" +ATTRS{idVendor}=="1366", ATTRS{idProduct}=="1014", MODE="664", GROUP="plugdev" +ATTRS{idVendor}=="1366", ATTRS{idProduct}=="1015", MODE="664", GROUP="plugdev" +ATTRS{idVendor}=="1366", ATTRS{idProduct}=="1016", MODE="664", GROUP="plugdev" +ATTRS{idVendor}=="1366", ATTRS{idProduct}=="1017", MODE="664", GROUP="plugdev" +ATTRS{idVendor}=="1366", ATTRS{idProduct}=="1018", MODE="664", GROUP="plugdev" + +LABEL="libjaylink_end_rules" diff --git a/src/jtag/drivers/libjaylink/libjaylink.pc.in b/src/jtag/drivers/libjaylink/libjaylink.pc.in new file mode 100644 index 0000000..a5efd3a --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libjaylink +Description: Library to access J-Link devices +Version: @VERSION@ +Requires.private: @JAYLINK_PKG_LIBS@ +Libs: -L${libdir} -ljaylink +Cflags: -I${includedir} diff --git a/src/jtag/drivers/libjaylink/libjaylink/Makefile.am b/src/jtag/drivers/libjaylink/libjaylink/Makefile.am new file mode 100644 index 0000000..62c5489 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/Makefile.am @@ -0,0 +1,62 @@ +## +## This file is part of the libjaylink project. +## +## Copyright (C) 2014-2015 Marc Schink <jaylink-dev@marcschink.de> +## +## This program is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see <http://www.gnu.org/licenses/>. +## + +if SUBPROJECT_BUILD +noinst_LTLIBRARIES = libjaylink.la +else +lib_LTLIBRARIES = libjaylink.la + +library_includedir = $(includedir)/libjaylink +library_include_HEADERS = libjaylink.h +nodist_library_include_HEADERS = version.h +endif + +libjaylink_la_SOURCES = \ + buffer.c \ + core.c \ + device.c \ + discovery.c \ + discovery_tcp.c \ + emucom.c \ + error.c \ + fileio.c \ + jtag.c \ + list.c \ + log.c \ + socket.c \ + strutil.c \ + swd.c \ + swo.c \ + target.c \ + transport.c \ + transport_tcp.c \ + util.c \ + version.c + +libjaylink_la_CFLAGS = $(JAYLINK_CFLAGS) +libjaylink_la_LDFLAGS = $(JAYLINK_LDFLAGS) -no-undefined +libjaylink_la_LIBADD = $(JAYLINK_LIBS) + +if HAVE_LIBUSB +libjaylink_la_SOURCES += discovery_usb.c transport_usb.c +libjaylink_la_CFLAGS += $(libusb_CFLAGS) +libjaylink_la_LIBADD += $(libusb_LIBS) +endif + +noinst_HEADERS = libjaylink-internal.h diff --git a/src/jtag/drivers/libjaylink/libjaylink/buffer.c b/src/jtag/drivers/libjaylink/libjaylink/buffer.c new file mode 100644 index 0000000..527c25e --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/buffer.c @@ -0,0 +1,140 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-2015 Marc Schink <jaylink-dev@marcschink.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdint.h> +#include <string.h> + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "libjaylink-internal.h" + +/** + * @file + * + * Buffer helper functions. + */ + +/** + * Write a 16-bit unsigned integer value to a buffer. + * + * The value is stored in the buffer in device byte order. + * + * @param[out] buffer Buffer to write the value into. + * @param[in] value Value to write into the buffer in host byte order. + * @param[in] offset Offset of the value within the buffer in bytes. + */ +JAYLINK_PRIV void buffer_set_u16(uint8_t *buffer, uint16_t value, + size_t offset) +{ + /* + * Store the value in the buffer and swap byte order depending on the + * host byte order. + */ +#ifdef WORDS_BIGENDIAN + buffer[offset + 0] = value; + buffer[offset + 1] = value >> 8; +#else + memcpy(buffer + offset, &value, sizeof(value)); +#endif +} + +/** + * Read a 16-bit unsigned integer value from a buffer. + * + * The value in the buffer is expected to be stored in device byte order. + * + * @param[in] buffer Buffer to read the value from. + * @param[in] offset Offset of the value within the buffer in bytes. + * + * @return The value read from the buffer in host byte order. + */ +JAYLINK_PRIV uint16_t buffer_get_u16(const uint8_t *buffer, size_t offset) +{ + uint16_t value; + + /* + * Read the value from the buffer and swap byte order depending on the + * host byte order. + */ +#ifdef WORDS_BIGENDIAN + value = (((uint16_t)buffer[offset + 1])) | \ + (((uint16_t)buffer[offset + 0]) << 8); +#else + memcpy(&value, buffer + offset, sizeof(value)); +#endif + + return value; +} + +/** + * Write a 32-bit unsigned integer value to a buffer. + * + * The value is stored in the buffer in device byte order. + * + * @param[out] buffer Buffer to write the value into. + * @param[in] value Value to write into the buffer in host byte order. + * @param[in] offset Offset of the value within the buffer in bytes. + */ +JAYLINK_PRIV void buffer_set_u32(uint8_t *buffer, uint32_t value, + size_t offset) +{ + /* + * Store the value in the buffer and swap byte order depending on the + * host byte order. + */ +#ifdef WORDS_BIGENDIAN + buffer[offset + 0] = value; + buffer[offset + 1] = value >> 8; + buffer[offset + 2] = value >> 16; + buffer[offset + 3] = value >> 24; +#else + memcpy(buffer + offset, &value, sizeof(value)); +#endif +} + +/** + * Read a 32-bit unsigned integer value from a buffer. + * + * The value in the buffer is expected to be stored in device byte order. + * + * @param[in] buffer Buffer to read the value from. + * @param[in] offset Offset of the value within the buffer in bytes. + * + * @return The value read from the buffer in host byte order. + */ +JAYLINK_PRIV uint32_t buffer_get_u32(const uint8_t *buffer, size_t offset) +{ + uint32_t value; + + /* + * Read the value from the buffer and swap byte order depending on the + * host byte order. + */ +#ifdef WORDS_BIGENDIAN + value = (((uint32_t)buffer[offset + 3])) | \ + (((uint32_t)buffer[offset + 2]) << 8) | \ + (((uint32_t)buffer[offset + 1]) << 16) | \ + (((uint32_t)buffer[offset + 0]) << 24); +#else + memcpy(&value, buffer + offset, sizeof(value)); +#endif + + return value; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/core.c b/src/jtag/drivers/libjaylink/libjaylink/core.c new file mode 100644 index 0000000..509b89d --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/core.c @@ -0,0 +1,219 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-2016 Marc Schink <jaylink-dev@marcschink.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <stdbool.h> +#ifdef _WIN32 +#include <winsock2.h> +#endif +#ifdef HAVE_LIBUSB +#include <libusb.h> +#endif + +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/** + * @mainpage + * + * @section sec_intro Introduction + * + * This document describes the API of libjaylink. + * + * libjaylink is a shared library written in C to access SEGGER J-Link and + * compatible devices. + * + * @section sec_error Error handling + * + * The libjaylink functions which can fail use the return value to indicate an + * error. The functions typically return an error code of #jaylink_error. + * For each function, all possible error codes and their detailed descriptions + * are documented. As the possible error codes returned by a function may + * change it is recommended to also always cover unexpected values when + * checking for error codes to be compatible with later versions of libjaylink. + * + * There are a few exceptions where a function directly returns the result + * instead of an error code because it is more convenient from an API + * perspective and because there is only a single reason for failure which is + * clearly distinguishable from the result. + * + * @section sec_license Copyright and license + * + * libjaylink is licensed under the terms of the GNU General Public + * License (GPL), version 2 or later. + * + * @section sec_website Website + * + * <a href="http://git.zapb.de/libjaylink.git">git.zapb.de/libjaylink.git</a> + */ + +/** + * @file + * + * Core library functions. + */ + +/** + * Initialize libjaylink. + * + * This function must be called before any other libjaylink function is called. + * + * @param[out] ctx Newly allocated libjaylink context on success, and undefined + * on failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_MALLOC Memory allocation error. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_init(struct jaylink_context **ctx) +{ + int ret; + struct jaylink_context *context; +#ifdef _WIN32 + WSADATA wsa_data; +#endif + + if (!ctx) + return JAYLINK_ERR_ARG; + + context = malloc(sizeof(struct jaylink_context)); + + if (!context) + return JAYLINK_ERR_MALLOC; + +#ifdef HAVE_LIBUSB + if (libusb_init(&context->usb_ctx) != LIBUSB_SUCCESS) { + free(context); + return JAYLINK_ERR; + } +#endif + +#ifdef _WIN32 + ret = WSAStartup(MAKEWORD(2, 2), &wsa_data); + + if (ret != 0) { +#ifdef HAVE_LIBUSB + libusb_exit(context->usb_ctx); +#endif + free(context); + return JAYLINK_ERR; + } + + if (LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2) { +#ifdef HAVE_LIBUSB + libusb_exit(context->usb_ctx); +#endif + free(context); + return JAYLINK_ERR; + } +#endif + + context->devs = NULL; + context->discovered_devs = NULL; + + /* Show error and warning messages by default. */ + context->log_level = JAYLINK_LOG_LEVEL_WARNING; + + context->log_callback = &log_vprintf; + context->log_callback_data = NULL; + + ret = jaylink_log_set_domain(context, JAYLINK_LOG_DOMAIN_DEFAULT); + + if (ret != JAYLINK_OK) { +#ifdef HAVE_LIBUSB + libusb_exit(context->usb_ctx); +#endif +#ifdef _WIN32 + WSACleanup(); +#endif + free(context); + return ret; + } + + *ctx = context; + + return JAYLINK_OK; +} + +/** + * Shutdown libjaylink. + * + * @param[in,out] ctx libjaylink context. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_exit(struct jaylink_context *ctx) +{ + struct list *item; + + if (!ctx) + return JAYLINK_ERR_ARG; + + item = ctx->discovered_devs; + + while (item) { + jaylink_unref_device((struct jaylink_device *)item->data); + item = item->next; + } + + list_free(ctx->discovered_devs); + list_free(ctx->devs); + +#ifdef HAVE_LIBUSB + libusb_exit(ctx->usb_ctx); +#endif +#ifdef _WIN32 + WSACleanup(); +#endif + free(ctx); + + return JAYLINK_OK; +} + +/** + * Check for a capability of libjaylink. + * + * @param[in] cap Capability to check for. + * + * @retval true Capability is supported. + * @retval false Capability is not supported or invalid argument. + * + * @since 0.1.0 + */ +JAYLINK_API bool jaylink_library_has_cap(enum jaylink_capability cap) +{ + switch (cap) { +#ifdef HAVE_LIBUSB + case JAYLINK_CAP_HIF_USB: + return true; +#endif + default: + return false; + } +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/device.c b/src/jtag/drivers/libjaylink/libjaylink/device.c new file mode 100644 index 0000000..a3bddf6 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/device.c @@ -0,0 +1,1707 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-2016 Marc Schink <jaylink-dev@marcschink.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> +#ifdef _WIN32 +#include <winsock2.h> +#else +#include <sys/socket.h> +#include <arpa/inet.h> +#endif +#ifdef HAVE_LIBUSB +#include <libusb.h> +#endif + +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/** + * @file + * + * Device enumeration and handling. + */ + +/** @cond PRIVATE */ +#define CMD_GET_VERSION 0x01 +#define CMD_GET_HW_STATUS 0x07 +#define CMD_REGISTER 0x09 +#define CMD_GET_HW_INFO 0xc1 +#define CMD_GET_COUNTERS 0xc2 +#define CMD_GET_FREE_MEMORY 0xd4 +#define CMD_GET_CAPS 0xe8 +#define CMD_GET_EXT_CAPS 0xed +#define CMD_GET_HW_VERSION 0xf0 +#define CMD_READ_CONFIG 0xf2 +#define CMD_WRITE_CONFIG 0xf3 + +#define REG_CMD_REGISTER 0x64 +#define REG_CMD_UNREGISTER 0x65 + +/** Size of the registration header in bytes. */ +#define REG_HEADER_SIZE 8 +/** Minimum registration information size in bytes. */ +#define REG_MIN_SIZE 0x4c +/** Maximum registration information size in bytes. */ +#define REG_MAX_SIZE 0x200 +/** Size of a connection entry in bytes. */ +#define REG_CONN_INFO_SIZE 16 +/** @endcond */ + +/** @private */ +JAYLINK_PRIV struct jaylink_device *device_allocate( + struct jaylink_context *ctx) +{ + struct jaylink_device *dev; + struct list *list; + + dev = malloc(sizeof(struct jaylink_device)); + + if (!dev) + return NULL; + + list = list_prepend(ctx->devs, dev); + + if (!list) { + free(dev); + return NULL; + } + + ctx->devs = list; + + dev->ctx = ctx; + dev->ref_count = 1; + + return dev; +} + +static struct jaylink_device **allocate_device_list(size_t length) +{ + struct jaylink_device **list; + + list = malloc(sizeof(struct jaylink_device *) * (length + 1)); + + if (!list) + return NULL; + + list[length] = NULL; + + return list; +} + +/** + * Get available devices. + * + * @param[in,out] ctx libjaylink context. + * @param[out] devs Newly allocated array which contains instances of available + * devices on success, and undefined on failure. The array is + * NULL-terminated and must be free'd by the caller with + * jaylink_free_devices(). + * @param[out] count Number of available devices on success, and undefined on + * failure. Can be NULL. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_MALLOC Memory allocation error. + * @retval JAYLINK_ERR Other error conditions. + * + * @see jaylink_discovery_scan() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_get_devices(struct jaylink_context *ctx, + struct jaylink_device ***devs, size_t *count) +{ + size_t num; + struct list *item; + struct jaylink_device **tmp; + struct jaylink_device *dev; + size_t i; + + if (!ctx || !devs) + return JAYLINK_ERR_ARG; + + num = list_length(ctx->discovered_devs); + tmp = allocate_device_list(num); + + if (!tmp) { + log_err(ctx, "Failed to allocate device list."); + return JAYLINK_ERR_MALLOC; + } + + item = ctx->discovered_devs; + + for (i = 0; i < num; i++) { + dev = (struct jaylink_device *)item->data; + tmp[i] = jaylink_ref_device(dev); + item = item->next; + } + + if (count) + *count = num; + + *devs = tmp; + + return JAYLINK_OK; +} + +/** + * Free devices. + * + * @param[in,out] devs Array of device instances. Must be NULL-terminated. + * @param[in] unref Determines whether the device instances should be + * unreferenced. + * + * @see jaylink_get_devices() + * + * @since 0.1.0 + */ +JAYLINK_API void jaylink_free_devices(struct jaylink_device **devs, bool unref) +{ + size_t i; + + if (!devs) + return; + + if (unref) { + for (i = 0; devs[i]; i++) + jaylink_unref_device(devs[i]); + } + + free(devs); +} + +/** + * Get the host interface of a device. + * + * @param[in] dev Device instance. + * @param[out] iface Host interface of the device on success, and undefined on + * failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_device_get_host_interface( + const struct jaylink_device *dev, + enum jaylink_host_interface *iface) +{ + if (!dev || !iface) + return JAYLINK_ERR_ARG; + + *iface = dev->iface; + + return JAYLINK_OK; +} + +/** + * Get the serial number of a device. + * + * @note This serial number is for enumeration purpose only and might differ + * from the real serial number of the device. + * + * @param[in] dev Device instance. + * @param[out] serial_number Serial number of the device on success, and + * undefined on failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_NOT_AVAILABLE Serial number is not available. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_device_get_serial_number( + const struct jaylink_device *dev, uint32_t *serial_number) +{ + if (!dev || !serial_number) + return JAYLINK_ERR_ARG; + + if (!dev->valid_serial_number) + return JAYLINK_ERR_NOT_AVAILABLE; + + *serial_number = dev->serial_number; + + return JAYLINK_OK; +} + +/** + * Get the USB address of a device. + * + * @note Identification of a device with the USB address is deprecated and the + * serial number should be used instead. + * + * @param[in] dev Device instance. + * @param[out] address USB address of the device on success, and undefined on + * failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_NOT_SUPPORTED Supported for devices with host interface + * #JAYLINK_HIF_USB only. + * + * @see jaylink_device_get_serial_number() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_device_get_usb_address( + const struct jaylink_device *dev, + enum jaylink_usb_address *address) +{ + if (!dev || !address) + return JAYLINK_ERR_ARG; + + if (dev->iface != JAYLINK_HIF_USB) + return JAYLINK_ERR_NOT_SUPPORTED; + +#ifdef HAVE_LIBUSB + *address = dev->usb_address; + + return JAYLINK_OK; +#else + return JAYLINK_ERR_NOT_SUPPORTED; +#endif +} + +/** + * Get the IPv4 address string of a device. + * + * @param[in] dev Device instance. + * @param[out] address IPv4 address string in quad-dotted decimal format of the + * device on success and undefined on failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_NOT_SUPPORTED Supported for devices with host interface + * #JAYLINK_HIF_TCP only. + * + * @since 0.2.0 + */ +JAYLINK_API int jaylink_device_get_ipv4_address( + const struct jaylink_device *dev, char *address) +{ + if (!dev || !address) + return JAYLINK_ERR_ARG; + + if (dev->iface != JAYLINK_HIF_TCP) + return JAYLINK_ERR_NOT_SUPPORTED; + + memcpy(address, dev->ipv4_address, sizeof(dev->ipv4_address)); + + return JAYLINK_OK; +} + +/** + * Get the MAC address of a device. + * + * @param[in] dev Device instance. + * @param[out] address MAC address of the device on success and undefined on + * failure. The length of the MAC address is + * #JAYLINK_MAC_ADDRESS_LENGTH bytes. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_NOT_SUPPORTED Supported for devices with host interface + * #JAYLINK_HIF_TCP only. + * @retval JAYLINK_ERR_NOT_AVAILABLE MAC address is not available. + * + * @since 0.2.0 + */ +JAYLINK_API int jaylink_device_get_mac_address( + const struct jaylink_device *dev, uint8_t *address) +{ + if (!dev || !address) + return JAYLINK_ERR_ARG; + + if (dev->iface != JAYLINK_HIF_TCP) + return JAYLINK_ERR_NOT_SUPPORTED; + + if (!dev->has_mac_address) + return JAYLINK_ERR_NOT_AVAILABLE; + + memcpy(address, dev->mac_address, sizeof(dev->mac_address)); + + return JAYLINK_OK; +} + +/** + * Get the hardware version of a device. + * + * @note The hardware type can not be obtained by this function, use + * jaylink_get_hardware_version() instead. + * + * @param[in] dev Device instance. + * @param[out] version Hardware version of the device on success and undefined + * on failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_NOT_SUPPORTED Supported for devices with host interface + * #JAYLINK_HIF_TCP only. + * @retval JAYLINK_ERR_NOT_AVAILABLE Hardware version is not available. + * + * @since 0.2.0 + */ +JAYLINK_API int jaylink_device_get_hardware_version( + const struct jaylink_device *dev, + struct jaylink_hardware_version *version) +{ + if (!dev || !version) + return JAYLINK_ERR_ARG; + + if (dev->iface != JAYLINK_HIF_TCP) + return JAYLINK_ERR_NOT_SUPPORTED; + + if (!dev->has_hw_version) + return JAYLINK_ERR_NOT_AVAILABLE; + + *version = dev->hw_version; + + return JAYLINK_OK; +} + +/** + * Get the product name of a device. + * + * @param[in] dev Device instance. + * @param[out] name Product name of the device on success and undefined on + * failure. The maximum length of the product name is + * #JAYLINK_PRODUCT_NAME_MAX_LENGTH bytes. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_NOT_SUPPORTED Supported for devices with host interface + * #JAYLINK_HIF_TCP only. + * @retval JAYLINK_ERR_NOT_AVAILABLE Product name is not available. + * + * @since 0.2.0 + */ +JAYLINK_API int jaylink_device_get_product_name( + const struct jaylink_device *dev, char *name) +{ + if (!dev || !name) + return JAYLINK_ERR_ARG; + + if (dev->iface != JAYLINK_HIF_TCP) + return JAYLINK_ERR_NOT_SUPPORTED; + + if (!dev->has_product_name) + return JAYLINK_ERR_NOT_AVAILABLE; + + memcpy(name, dev->product_name, sizeof(dev->product_name)); + + return JAYLINK_OK; +} + +/** + * Get the nickname of a device. + * + * @param[in] dev Device instance. + * @param[out] nickname Nickname of the device on success and undefined on + * failure. The maximum length of the nickname is + * #JAYLINK_NICKNAME_MAX_LENGTH bytes. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_NOT_SUPPORTED Supported for devices with host interface + * #JAYLINK_HIF_TCP only. + * @retval JAYLINK_ERR_NOT_AVAILABLE Nickname is not available. + * + * @since 0.2.0 + */ +JAYLINK_API int jaylink_device_get_nickname(const struct jaylink_device *dev, + char *nickname) +{ + if (!dev || !nickname) + return JAYLINK_ERR_ARG; + + if (dev->iface != JAYLINK_HIF_TCP) + return JAYLINK_ERR_NOT_SUPPORTED; + + if (!dev->has_nickname) + return JAYLINK_ERR_NOT_AVAILABLE; + + memcpy(nickname, dev->nickname, sizeof(dev->nickname)); + + return JAYLINK_OK; +} + +/** + * Increment the reference count of a device. + * + * @param[in,out] dev Device instance. + * + * @return The given device instance on success, or NULL on invalid argument. + * + * @since 0.1.0 + */ +JAYLINK_API struct jaylink_device *jaylink_ref_device( + struct jaylink_device *dev) +{ + if (!dev) + return NULL; + + dev->ref_count++; + + return dev; +} + +/** + * Decrement the reference count of a device. + * + * @param[in,out] dev Device instance. + * + * @since 0.1.0 + */ +JAYLINK_API void jaylink_unref_device(struct jaylink_device *dev) +{ + struct jaylink_context *ctx; + + if (!dev) + return; + + dev->ref_count--; + + if (!dev->ref_count) { + ctx = dev->ctx; + ctx->devs = list_remove(dev->ctx->devs, dev); + + if (dev->iface == JAYLINK_HIF_USB) { +#ifdef HAVE_LIBUSB + log_dbg(ctx, "Device destroyed (bus:address = " + "%03u:%03u).", + libusb_get_bus_number(dev->usb_dev), + libusb_get_device_address(dev->usb_dev)); + + libusb_unref_device(dev->usb_dev); +#endif + } else if (dev->iface == JAYLINK_HIF_TCP) { + log_dbg(ctx, "Device destroyed (IPv4 address = %s).", + dev->ipv4_address); + } else { + log_err(ctx, "BUG: Invalid host interface: %u.", + dev->iface); + } + + free(dev); + } +} + +static struct jaylink_device_handle *allocate_device_handle( + struct jaylink_device *dev) +{ + struct jaylink_device_handle *devh; + + devh = malloc(sizeof(struct jaylink_device_handle)); + + if (!devh) + return NULL; + + devh->dev = jaylink_ref_device(dev); + + return devh; +} + +static void free_device_handle(struct jaylink_device_handle *devh) +{ + jaylink_unref_device(devh->dev); + free(devh); +} + +/** + * Open a device. + * + * @param[in,out] dev Device instance. + * @param[out] devh Newly allocated handle for the opened device on success, + * and undefined on failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_MALLOC Memory allocation error. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_open(struct jaylink_device *dev, + struct jaylink_device_handle **devh) +{ + int ret; + struct jaylink_device_handle *handle; + + if (!dev || !devh) + return JAYLINK_ERR_ARG; + + handle = allocate_device_handle(dev); + + if (!handle) { + log_err(dev->ctx, "Device handle malloc failed."); + return JAYLINK_ERR_MALLOC; + } + + ret = transport_open(handle); + + if (ret != JAYLINK_OK) { + free_device_handle(handle); + return ret; + } + + *devh = handle; + + return JAYLINK_OK; +} + +/** + * Close a device. + * + * @param[in,out] devh Device instance. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_close(struct jaylink_device_handle *devh) +{ + int ret; + + if (!devh) + return JAYLINK_ERR_ARG; + + ret = transport_close(devh); + free_device_handle(devh); + + return ret; +} + +/** + * Get the device instance from a device handle. + * + * @note The reference count of the device instance is not increased. + * + * @param[in] devh Device handle. + * + * @return The device instance on success, or NULL on invalid argument. + * + * @since 0.1.0 + */ +JAYLINK_API struct jaylink_device *jaylink_get_device( + struct jaylink_device_handle *devh) +{ + if (!devh) + return NULL; + + return devh->dev; +} + +/** + * Retrieve the firmware version of a device. + * + * @param[in,out] devh Device handle. + * @param[out] version Newly allocated string which contains the firmware + * version on success, and undefined if @p length is zero + * or on failure. The string is null-terminated and must be + * free'd by the caller. + * @param[out] length Length of the firmware version string including trailing + * null-terminator on success, and undefined on failure. + * Zero if no firmware version string is available. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_MALLOC Memory allocation error. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_get_firmware_version( + struct jaylink_device_handle *devh, char **version, + size_t *length) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[2]; + uint16_t dummy; + char *tmp; + + if (!devh || !version || !length) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 1, 2, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_GET_VERSION; + + ret = transport_write(devh, buf, 1); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, 2); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + dummy = buffer_get_u16(buf, 0); + *length = dummy; + + if (!dummy) + return JAYLINK_OK; + + ret = transport_start_read(devh, dummy); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + tmp = malloc(dummy); + + if (!tmp) { + log_err(ctx, "Firmware version string malloc failed."); + return JAYLINK_ERR_MALLOC; + } + + ret = transport_read(devh, (uint8_t *)tmp, dummy); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + free(tmp); + return ret; + } + + /* Last byte is reserved for null-terminator. */ + tmp[dummy - 1] = 0; + *version = tmp; + + return JAYLINK_OK; +} + +/** + * Retrieve the hardware information of a device. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_GET_HW_INFO capability. + * + * @param[in,out] devh Device handle. + * @param[in] mask A bit field where each set bit represents hardware + * information to request. See #jaylink_hardware_info for a + * description of the hardware information and their bit + * positions. + * @param[out] info Array to store the hardware information on success. Its + * content is undefined on failure. The array must be large + * enough to contain at least as many elements as bits set in + * @a mask. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_get_hardware_info(struct jaylink_device_handle *devh, + uint32_t mask, uint32_t *info) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[5]; + unsigned int i; + unsigned int num; + unsigned int length; + + if (!devh || !mask || !info) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + num = 0; + + for (i = 0; i < 32; i++) { + if (mask & (1 << i)) + num++; + } + + length = num * sizeof(uint32_t); + + ret = transport_start_write_read(devh, 5, length, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_GET_HW_INFO; + buffer_set_u32(buf, mask, 1); + + ret = transport_write(devh, buf, 5); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, (uint8_t *)info, length); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + for (i = 0; i < num; i++) + info[i] = buffer_get_u32((uint8_t *)info, + i * sizeof(uint32_t)); + + return JAYLINK_OK; +} + +/** + * Retrieve the counter values of a device. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_GET_COUNTERS capability. + * + * @param[in,out] devh Device handle. + * @param[in] mask A bit field where each set bit represents a counter value to + * request. See #jaylink_counter for a description of the + * counters and their bit positions. + * @param[out] values Array to store the counter values on success. Its content + * is undefined on failure. The array must be large enough + * to contain at least as many elements as bits set in @p + * mask. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.2.0 + */ +JAYLINK_API int jaylink_get_counters(struct jaylink_device_handle *devh, + uint32_t mask, uint32_t *values) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[5]; + unsigned int i; + unsigned int num; + unsigned int length; + + if (!devh || !mask || !values) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + num = 0; + + for (i = 0; i < 32; i++) { + if (mask & (1 << i)) + num++; + } + + length = num * sizeof(uint32_t); + ret = transport_start_write_read(devh, 5, length, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_GET_COUNTERS; + buffer_set_u32(buf, mask, 1); + + ret = transport_write(devh, buf, 5); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, (uint8_t *)values, length); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + for (i = 0; i < num; i++) + values[i] = buffer_get_u32((uint8_t *)values, + i * sizeof(uint32_t)); + + return JAYLINK_OK; +} + +/** + * Retrieve the hardware version of a device. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_GET_HW_VERSION capability. + * + * @warning This function may return a value for @p version where + * #jaylink_hardware_version::type is not covered by + * #jaylink_hardware_type. + * + * @param[in,out] devh Device handle. + * @param[out] version Hardware version on success, and undefined on failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_get_hardware_version( + struct jaylink_device_handle *devh, + struct jaylink_hardware_version *version) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[4]; + uint32_t tmp; + + if (!devh || !version) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 1, 4, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_GET_HW_VERSION; + + ret = transport_write(devh, buf, 1); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + tmp = buffer_get_u32(buf, 0); + + version->type = (tmp / 1000000) % 100; + version->major = (tmp / 10000) % 100; + version->minor = (tmp / 100) % 100; + version->revision = tmp % 100; + + return JAYLINK_OK; +} + +/** + * Retrieve the hardware status of a device. + * + * @param[in,out] devh Device handle. + * @param[out] status Hardware status on success, and undefined on failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_get_hardware_status(struct jaylink_device_handle *devh, + struct jaylink_hardware_status *status) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[8]; + + if (!devh || !status) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 1, 8, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_GET_HW_STATUS; + + ret = transport_write(devh, buf, 1); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, 8); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + status->target_voltage = buffer_get_u16(buf, 0); + status->tck = buf[2]; + status->tdi = buf[3]; + status->tdo = buf[4]; + status->tms = buf[5]; + status->tres = buf[6]; + status->trst = buf[7]; + + return JAYLINK_OK; +} + +/** + * Retrieve the capabilities of a device. + * + * The capabilities are stored in a 32-bit bit array consisting of + * #JAYLINK_DEV_CAPS_SIZE bytes where each individual bit represents a + * capability. The first bit of this array is the least significant bit of the + * first byte and the following bits are sequentially numbered in order of + * increasing bit significance and byte index. A set bit indicates a supported + * capability. See #jaylink_device_capability for a description of the + * capabilities and their bit positions. + * + * @param[in,out] devh Device handle. + * @param[out] caps Buffer to store capabilities on success. Its content is + * undefined on failure. The buffer must be large enough to + * contain at least #JAYLINK_DEV_CAPS_SIZE bytes. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @see jaylink_get_extended_caps() + * @see jaylink_has_cap() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_get_caps(struct jaylink_device_handle *devh, + uint8_t *caps) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[1]; + + if (!devh || !caps) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 1, JAYLINK_DEV_CAPS_SIZE, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_GET_CAPS; + + ret = transport_write(devh, buf, 1); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, caps, JAYLINK_DEV_CAPS_SIZE); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + return JAYLINK_OK; +} + +/** + * Retrieve the extended capabilities of a device. + * + * The extended capabilities are stored in a 256-bit bit array consisting of + * #JAYLINK_DEV_EXT_CAPS_SIZE bytes. See jaylink_get_caps() for a further + * description of how the capabilities are represented in this bit array. For a + * description of the capabilities and their bit positions, see + * #jaylink_device_capability. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_GET_EXT_CAPS capability. + * + * @param[in,out] devh Device handle. + * @param[out] caps Buffer to store capabilities on success. Its content is + * undefined on failure. The buffer must be large enough to + * contain at least #JAYLINK_DEV_EXT_CAPS_SIZE bytes. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @see jaylink_get_caps() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_get_extended_caps(struct jaylink_device_handle *devh, + uint8_t *caps) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[1]; + + if (!devh || !caps) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 1, JAYLINK_DEV_EXT_CAPS_SIZE, + true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_GET_EXT_CAPS; + + ret = transport_write(devh, buf, 1); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, caps, JAYLINK_DEV_EXT_CAPS_SIZE); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + return JAYLINK_OK; +} + +/** + * Retrieve the size of free memory of a device. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_GET_FREE_MEMORY capability. + * + * @param[in,out] devh Device handle. + * @param[out] size Size of free memory in bytes on success, and undefined on + * failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_get_free_memory(struct jaylink_device_handle *devh, + uint32_t *size) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[4]; + + if (!devh || !size) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 1, 4, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_GET_FREE_MEMORY; + + ret = transport_write(devh, buf, 1); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + *size = buffer_get_u32(buf, 0); + + return JAYLINK_OK; +} + +/** + * Read the raw configuration data of a device. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_READ_CONFIG capability. + * + * @param[in,out] devh Device handle. + * @param[out] config Buffer to store configuration data on success. Its + * content is undefined on failure. The buffer must be large + * enough to contain at least + * #JAYLINK_DEV_CONFIG_SIZE bytes. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_read_raw_config(struct jaylink_device_handle *devh, + uint8_t *config) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[1]; + + if (!devh || !config) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 1, JAYLINK_DEV_CONFIG_SIZE, + true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_READ_CONFIG; + + ret = transport_write(devh, buf, 1); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, config, JAYLINK_DEV_CONFIG_SIZE); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + return JAYLINK_OK; +} + +/** + * Write the raw configuration data of a device. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_WRITE_CONFIG capability. + * + * @param[in,out] devh Device handle. + * @param[in] config Buffer to write configuration data from. The size of the + * configuration data is expected to be + * #JAYLINK_DEV_CONFIG_SIZE bytes. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_write_raw_config(struct jaylink_device_handle *devh, + const uint8_t *config) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[1]; + + if (!devh || !config) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write(devh, 1 + JAYLINK_DEV_CONFIG_SIZE, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_WRITE_CONFIG; + + ret = transport_write(devh, buf, 1); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_write(devh, config, JAYLINK_DEV_CONFIG_SIZE); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + return JAYLINK_OK; +} + +static void parse_conn_table(struct jaylink_connection *conns, + const uint8_t *buffer, uint16_t num, uint16_t entry_size) +{ + unsigned int i; + size_t offset; + struct in_addr in; + + offset = 0; + + for (i = 0; i < num; i++) { + conns[i].pid = buffer_get_u32(buffer, offset); + + in.s_addr = buffer_get_u32(buffer, offset + 4); + /* + * Use inet_ntoa() instead of inet_ntop() because the latter + * requires at least Windows Vista. + */ + strcpy(conns[i].hid, inet_ntoa(in)); + + conns[i].iid = buffer[offset + 8]; + conns[i].cid = buffer[offset + 9]; + conns[i].handle = buffer_get_u16(buffer, offset + 10); + conns[i].timestamp = buffer_get_u32(buffer, offset + 12); + offset = offset + entry_size; + } +} + +static bool _inet_pton(const char *str, struct in_addr *in) +{ +#ifdef _WIN32 + int ret; + struct sockaddr_in sock_in; + int length; + + length = sizeof(sock_in); + + /* + * Use WSAStringToAddress() instead of inet_pton() because the latter + * requires at least Windows Vista. + */ + ret = WSAStringToAddress((LPTSTR)str, AF_INET, NULL, + (LPSOCKADDR)&sock_in, &length); + + if (ret != 0) + return false; + + *in = sock_in.sin_addr; +#else + if (inet_pton(AF_INET, str, in) != 1) + return false; +#endif + + return true; +} + +/** + * Register a connection on a device. + * + * A connection can be registered by using 0 as handle. Additional information + * about the connection can be attached whereby the timestamp is a read-only + * value and therefore ignored for registration. On success, a new handle + * greater than 0 is obtained from the device. + * + * However, if an obtained handle does not appear in the list of device + * connections, the connection was not registered because the maximum number of + * connections on the device is reached. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_REGISTER capability. + * + * Example code: + * @code{.c} + * static bool register_connection(struct jaylink_device_handle *devh, + * struct jaylink_connection *conn) + * { + * int ret; + * struct jaylink_connection conns[JAYLINK_MAX_CONNECTIONS]; + * bool found_handle; + * size_t count; + * size_t i; + * + * conn->handle = 0; + * conn->pid = 0; + * strcpy(conn->hid, "0.0.0.0"); + * conn->iid = 0; + * conn->cid = 0; + * + * ret = jaylink_register(devh, conn, conns, &count); + * + * if (ret != JAYLINK_OK) { + * printf("jaylink_register() failed: %s.\n", + * jaylink_strerror(ret)); + * return false; + * } + * + * found_handle = false; + * + * for (i = 0; i < count; i++) { + * if (conns[i].handle == conn->handle) { + * found_handle = true; + * break; + * } + * } + * + * if (!found_handle) { + * printf("Maximum number of connections reached.\n"); + * return false; + * } + * + * printf("Connection successfully registered.\n"); + * + * return true; + * } + * @endcode + * + * @param[in,out] devh Device handle. + * @param[in,out] connection Connection to register on the device. + * @param[out] connections Array to store device connections on success. + * Its content is undefined on failure. The array must + * be large enough to contain at least + * #JAYLINK_MAX_CONNECTIONS elements. + * @param[out] count Number of device connections on success, and undefined on + * failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_PROTO Protocol violation. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @see jaylink_unregister() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_register(struct jaylink_device_handle *devh, + struct jaylink_connection *connection, + struct jaylink_connection *connections, size_t *count) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[REG_MAX_SIZE]; + uint16_t handle; + uint16_t num; + uint16_t entry_size; + uint32_t size; + uint32_t table_size; + uint16_t info_size; + struct in_addr in; + + if (!devh || !connection || !connections || !count) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + + buf[0] = CMD_REGISTER; + buf[1] = REG_CMD_REGISTER; + buffer_set_u32(buf, connection->pid, 2); + + if (!_inet_pton(connection->hid, &in)) + return JAYLINK_ERR_ARG; + + buffer_set_u32(buf, in.s_addr, 6); + + buf[10] = connection->iid; + buf[11] = connection->cid; + buffer_set_u16(buf, connection->handle, 12); + + ret = transport_start_write_read(devh, 14, REG_MIN_SIZE, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_write(devh, buf, 14); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, REG_MIN_SIZE); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + handle = buffer_get_u16(buf, 0); + num = buffer_get_u16(buf, 2); + entry_size = buffer_get_u16(buf, 4); + info_size = buffer_get_u16(buf, 6); + + if (num > JAYLINK_MAX_CONNECTIONS) { + log_err(ctx, "Maximum number of device connections exceeded: " + "%u.", num); + return JAYLINK_ERR_PROTO; + } + + if (entry_size != REG_CONN_INFO_SIZE) { + log_err(ctx, "Invalid connection entry size: %u bytes.", + entry_size); + return JAYLINK_ERR_PROTO; + } + + table_size = num * entry_size; + size = REG_HEADER_SIZE + table_size + info_size; + + if (size > REG_MAX_SIZE) { + log_err(ctx, "Maximum registration information size exceeded: " + "%u bytes.", size); + return JAYLINK_ERR_PROTO; + } + + if (size > REG_MIN_SIZE) { + ret = transport_start_read(devh, size - REG_MIN_SIZE); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_read() failed: %s.", + jaylink_strerror(ret)); + return JAYLINK_ERR; + } + + ret = transport_read(devh, buf + REG_MIN_SIZE, + size - REG_MIN_SIZE); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return JAYLINK_ERR; + } + } + + if (!handle) { + log_err(ctx, "Obtained invalid connection handle."); + return JAYLINK_ERR_PROTO; + } + + connection->handle = handle; + parse_conn_table(connections, buf + REG_HEADER_SIZE, num, entry_size); + + *count = num; + + return JAYLINK_OK; +} + +/** + * Unregister a connection from a device. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_REGISTER capability. + * + * @param[in,out] devh Device handle. + * @param[in,out] connection Connection to unregister from the device. + * @param[out] connections Array to store device connections on success. + * Its content is undefined on failure. The array must + * be large enough to contain at least + * #JAYLINK_MAX_CONNECTIONS elements. + * @param[out] count Number of device connections on success, and undefined on + * failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_PROTO Protocol violation. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @see jaylink_register() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_unregister(struct jaylink_device_handle *devh, + const struct jaylink_connection *connection, + struct jaylink_connection *connections, size_t *count) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[REG_MAX_SIZE]; + uint16_t num; + uint16_t entry_size; + uint32_t size; + uint32_t table_size; + uint16_t info_size; + struct in_addr in; + + if (!devh || !connection || !connections || !count) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + + buf[0] = CMD_REGISTER; + buf[1] = REG_CMD_UNREGISTER; + buffer_set_u32(buf, connection->pid, 2); + + if (!_inet_pton(connection->hid, &in)) + return JAYLINK_ERR_ARG; + + buffer_set_u32(buf, in.s_addr, 6); + + buf[10] = connection->iid; + buf[11] = connection->cid; + buffer_set_u16(buf, connection->handle, 12); + + ret = transport_start_write_read(devh, 14, REG_MIN_SIZE, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_write(devh, buf, 14); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, REG_MIN_SIZE); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + num = buffer_get_u16(buf, 2); + entry_size = buffer_get_u16(buf, 4); + info_size = buffer_get_u16(buf, 6); + + if (num > JAYLINK_MAX_CONNECTIONS) { + log_err(ctx, "Maximum number of device connections exceeded: " + "%u.", num); + return JAYLINK_ERR_PROTO; + } + + if (entry_size != REG_CONN_INFO_SIZE) { + log_err(ctx, "Invalid connection entry size: %u bytes.", + entry_size); + return JAYLINK_ERR_PROTO; + } + + table_size = num * entry_size; + size = REG_HEADER_SIZE + table_size + info_size; + + if (size > REG_MAX_SIZE) { + log_err(ctx, "Maximum registration information size exceeded: " + "%u bytes.", size); + return JAYLINK_ERR_PROTO; + } + + if (size > REG_MIN_SIZE) { + ret = transport_start_read(devh, size - REG_MIN_SIZE); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_read() failed: %s.", + jaylink_strerror(ret)); + return JAYLINK_ERR; + } + + ret = transport_read(devh, buf + REG_MIN_SIZE, + size - REG_MIN_SIZE); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return JAYLINK_ERR; + } + } + + parse_conn_table(connections, buf + REG_HEADER_SIZE, num, entry_size); + + *count = num; + + return JAYLINK_OK; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/discovery.c b/src/jtag/drivers/libjaylink/libjaylink/discovery.c new file mode 100644 index 0000000..1ac96e7 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/discovery.c @@ -0,0 +1,106 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-2016 Marc Schink <jaylink-dev@marcschink.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/** + * @file + * + * Device discovery. + */ + +static void clear_discovery_list(struct jaylink_context *ctx) +{ + struct list *item; + struct list *tmp; + struct jaylink_device *dev; + + item = ctx->discovered_devs; + + while (item) { + dev = (struct jaylink_device *)item->data; + jaylink_unref_device(dev); + + tmp = item; + item = item->next; + free(tmp); + } + + ctx->discovered_devs = NULL; +} + +/** + * Scan for devices. + * + * @param[in,out] ctx libjaylink context. + * @param[in] ifaces Host interfaces to scan for devices. Use bitwise OR to + * specify multiple interfaces, or 0 to use all available + * interfaces. See #jaylink_host_interface for a description + * of the interfaces. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @see jaylink_get_devices() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_discovery_scan(struct jaylink_context *ctx, + uint32_t ifaces) +{ + int ret; + + if (!ctx) + return JAYLINK_ERR_ARG; + + if (!ifaces) + ifaces = JAYLINK_HIF_USB | JAYLINK_HIF_TCP; + + clear_discovery_list(ctx); + +#ifdef HAVE_LIBUSB + if (ifaces & JAYLINK_HIF_USB) { + ret = discovery_usb_scan(ctx); + + if (ret != JAYLINK_OK) { + log_err(ctx, "USB device discovery failed."); + return ret; + } + } +#endif + + if (ifaces & JAYLINK_HIF_TCP) { + ret = discovery_tcp_scan(ctx); + + if (ret != JAYLINK_OK) { + log_err(ctx, "TCP/IP device discovery failed."); + return ret; + } + } + + return JAYLINK_OK; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/discovery_tcp.c b/src/jtag/drivers/libjaylink/libjaylink/discovery_tcp.c new file mode 100644 index 0000000..002fa67 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/discovery_tcp.c @@ -0,0 +1,349 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2015-2017 Marc Schink <jaylink-dev@marcschink.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <ctype.h> +#ifdef _WIN32 +#include <winsock2.h> +#else +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif + +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/** + * @file + * + * Device discovery (TCP/IP). + */ + +/** @cond PRIVATE */ +/** Size of the advertisement message in bytes. */ +#define ADV_MESSAGE_SIZE 128 + +/** Device discovery port number. */ +#define DISC_PORT 19020 + +/** Size of the discovery message in bytes. */ +#define DISC_MESSAGE_SIZE 64 + +/** Discovery timeout in milliseconds. */ +#define DISC_TIMEOUT 20 +/** @endcond */ + +static bool compare_devices(const void *a, const void *b) +{ + const struct jaylink_device *dev; + const struct jaylink_device *new_dev; + + dev = a; + new_dev = b; + + if (dev->iface != JAYLINK_HIF_TCP) + return false; + + if (memcmp(dev->ipv4_address, new_dev->ipv4_address, + sizeof(dev->ipv4_address)) != 0) + return false; + + if (dev->serial_number != new_dev->serial_number) + return false; + + if (memcmp(dev->mac_address, new_dev->mac_address, + sizeof(dev->mac_address)) != 0) + return false; + + if (strcmp(dev->product_name, new_dev->product_name) != 0) + return false; + + if (strcmp(dev->nickname, new_dev->nickname) != 0) + return false; + + if (dev->hw_version.type != new_dev->hw_version.type) + return false; + + if (dev->hw_version.major != new_dev->hw_version.major) + return false; + + if (dev->hw_version.minor != new_dev->hw_version.minor) + return false; + + if (dev->hw_version.revision != new_dev->hw_version.revision) + return false; + + return true; +} + +static struct jaylink_device *find_device(struct list *list, + const struct jaylink_device *dev) +{ + struct list *item; + + item = list_find_custom(list, &compare_devices, dev); + + if (item) + return item->data; + + return NULL; +} + +static bool parse_adv_message(struct jaylink_device *dev, + const uint8_t *buffer) +{ + struct in_addr in; + uint32_t tmp; + + if (memcmp(buffer, "Found", 5) != 0) + return false; + + /* + * Use inet_ntoa() instead of inet_ntop() because the latter requires + * at least Windows Vista. + */ + memcpy(&in, buffer + 16, 4); + memcpy(dev->ipv4_address, inet_ntoa(in), sizeof(dev->ipv4_address)); + + memcpy(dev->mac_address, buffer + 32, sizeof(dev->mac_address)); + dev->has_mac_address = true; + + dev->serial_number = buffer_get_u32(buffer, 48); + dev->valid_serial_number = true; + + tmp = buffer_get_u32(buffer, 52); + dev->hw_version.type = (tmp / 1000000) % 100; + dev->hw_version.major = (tmp / 10000) % 100; + dev->hw_version.minor = (tmp / 100) % 100; + dev->hw_version.revision = tmp % 100; + dev->has_hw_version = true; + + memcpy(dev->product_name, buffer + 64, sizeof(dev->product_name)); + dev->product_name[JAYLINK_PRODUCT_NAME_MAX_LENGTH - 1] = '\0'; + dev->has_product_name = isprint((unsigned char)dev->product_name[0]); + + memcpy(dev->nickname, buffer + 96, sizeof(dev->nickname)); + dev->nickname[JAYLINK_NICKNAME_MAX_LENGTH - 1] = '\0'; + dev->has_nickname = isprint((unsigned char)dev->nickname[0]); + + return true; +} + +static struct jaylink_device *probe_device(struct jaylink_context *ctx, + struct sockaddr_in *addr, const uint8_t *buffer) +{ + struct jaylink_device tmp; + struct jaylink_device *dev; + + /* + * Use inet_ntoa() instead of inet_ntop() because the latter requires + * at least Windows Vista. + */ + log_dbg(ctx, "Received advertisement message (IPv4 address = %s).", + inet_ntoa(addr->sin_addr)); + + if (!parse_adv_message(&tmp, buffer)) { + log_dbg(ctx, "Received invalid advertisement message."); + return NULL; + } + + log_dbg(ctx, "Found device (IPv4 address = %s).", tmp.ipv4_address); + log_dbg(ctx, "Device: MAC address = %02x:%02x:%02x:%02x:%02x:%02x.", + tmp.mac_address[0], tmp.mac_address[1], tmp.mac_address[2], + tmp.mac_address[3], tmp.mac_address[4], tmp.mac_address[5]); + log_dbg(ctx, "Device: Serial number = %u.", tmp.serial_number); + + if (tmp.has_product_name) + log_dbg(ctx, "Device: Product = %s.", tmp.product_name); + + if (tmp.has_nickname) + log_dbg(ctx, "Device: Nickname = %s.", tmp.nickname); + + dev = find_device(ctx->discovered_devs, &tmp); + + if (dev) { + log_dbg(ctx, "Ignoring already discovered device."); + return NULL; + } + + dev = find_device(ctx->devs, &tmp); + + if (dev) { + log_dbg(ctx, "Using existing device instance."); + return jaylink_ref_device(dev); + } + + log_dbg(ctx, "Allocating new device instance."); + + dev = device_allocate(ctx); + + if (!dev) { + log_warn(ctx, "Device instance malloc failed."); + return NULL; + } + + dev->iface = JAYLINK_HIF_TCP; + + dev->serial_number = tmp.serial_number; + dev->valid_serial_number = tmp.valid_serial_number; + + memcpy(dev->ipv4_address, tmp.ipv4_address, sizeof(dev->ipv4_address)); + + memcpy(dev->mac_address, tmp.mac_address, sizeof(dev->mac_address)); + dev->has_mac_address = tmp.has_mac_address; + + memcpy(dev->product_name, tmp.product_name, sizeof(dev->product_name)); + dev->has_product_name = tmp.has_product_name; + + memcpy(dev->nickname, tmp.nickname, sizeof(dev->nickname)); + dev->has_nickname = tmp.has_nickname; + + dev->hw_version = tmp.hw_version; + dev->has_hw_version = tmp.has_hw_version; + + return dev; +} + +/** @private */ +JAYLINK_PRIV int discovery_tcp_scan(struct jaylink_context *ctx) +{ + int ret; + int sock; + int opt_value; + fd_set rfds; + struct sockaddr_in addr; + size_t addr_length; + struct timeval timeout; + uint8_t buf[ADV_MESSAGE_SIZE]; + struct jaylink_device *dev; + size_t length; + size_t num_devs; + + sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + if (sock < 0) { + log_err(ctx, "Failed to create discovery socket."); + return JAYLINK_ERR; + } + + opt_value = true; + + if (!socket_set_option(sock, SOL_SOCKET, SO_BROADCAST, &opt_value, + sizeof(opt_value))) { + log_err(ctx, "Failed to enable broadcast option for discovery " + "socket."); + socket_close(sock); + return JAYLINK_ERR; + } + + memset(&addr, 0, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_port = htons(DISC_PORT); + addr.sin_addr.s_addr = INADDR_ANY; + + if (!socket_bind(sock, (struct sockaddr *)&addr, + sizeof(struct sockaddr_in))) { + log_err(ctx, "Failed to bind discovery socket."); + socket_close(sock); + return JAYLINK_ERR; + } + + addr.sin_family = AF_INET; + addr.sin_port = htons(DISC_PORT); + addr.sin_addr.s_addr = INADDR_BROADCAST; + + memset(buf, 0, DISC_MESSAGE_SIZE); + memcpy(buf, "Discover", 8); + + log_dbg(ctx, "Sending discovery message."); + + length = DISC_MESSAGE_SIZE; + + if (!socket_sendto(sock, (char *)buf, &length, 0, + (const struct sockaddr *)&addr, sizeof(addr))) { + log_err(ctx, "Failed to send discovery message."); + socket_close(sock); + return JAYLINK_ERR_IO; + } + + if (length < DISC_MESSAGE_SIZE) { + log_err(ctx, "Only sent %zu bytes of discovery message.", + length); + socket_close(sock); + return JAYLINK_ERR_IO; + } + + timeout.tv_sec = DISC_TIMEOUT / 1000; + timeout.tv_usec = (DISC_TIMEOUT % 1000) * 1000; + + num_devs = 0; + + while (true) { + FD_ZERO(&rfds); + FD_SET(sock, &rfds); + + ret = select(sock + 1, &rfds, NULL, NULL, &timeout); + + if (ret <= 0) + break; + + if (!FD_ISSET(sock, &rfds)) + continue; + + length = ADV_MESSAGE_SIZE; + addr_length = sizeof(struct sockaddr_in); + + if (!socket_recvfrom(sock, buf, &length, 0, + (struct sockaddr *)&addr, &addr_length)) { + log_warn(ctx, "Failed to receive advertisement " + "message."); + continue; + } + + /* + * Filter out messages with an invalid size. This includes the + * broadcast message we sent before. + */ + if (length != ADV_MESSAGE_SIZE) + continue; + + dev = probe_device(ctx, &addr, buf); + + if (dev) { + ctx->discovered_devs = list_prepend( + ctx->discovered_devs, dev); + num_devs++; + } + } + + socket_close(sock); + + if (ret < 0) { + log_err(ctx, "select() failed."); + return JAYLINK_ERR; + } + + log_dbg(ctx, "Found %zu TCP/IP device(s).", num_devs); + + return JAYLINK_OK; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/discovery_usb.c b/src/jtag/drivers/libjaylink/libjaylink/discovery_usb.c new file mode 100644 index 0000000..48d5322 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/discovery_usb.c @@ -0,0 +1,280 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-2016 Marc Schink <jaylink-dev@marcschink.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> +#include <sys/types.h> + +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/* + * libusb.h includes windows.h and therefore must be included after anything + * that includes winsock2.h. + */ +#include <libusb.h> + +/** + * @file + * + * Device discovery (USB). + */ + +/** @cond PRIVATE */ +/** USB Vendor ID (VID) of SEGGER products. */ +#define USB_VENDOR_ID 0x1366 + +/* USB Product IDs (PID) and their corresponding USB addresses. */ +static const uint16_t pids[][2] = { + {0x0101, 0}, + {0x0102, 1}, + {0x0103, 2}, + {0x0104, 3}, + {0x0105, 0}, + {0x0107, 0}, + {0x0108, 0}, + {0x1010, 0}, + {0x1011, 0}, + {0x1012, 0}, + {0x1013, 0}, + {0x1014, 0}, + {0x1015, 0}, + {0x1016, 0}, + {0x1017, 0}, + {0x1018, 0} +}; + +/** Maximum length of the USB string descriptor for the serial number. */ +#define USB_SERIAL_NUMBER_LENGTH 12 + +/** + * Maximum number of digits in a serial number + * + * The serial number of a device consists of at most 9 digits but user defined + * serial numbers are allowed with up to 10 digits. + */ +#define MAX_SERIAL_NUMBER_DIGITS 10 +/** @endcond */ + +static bool parse_serial_number(const char *str, uint32_t *serial_number) +{ + size_t length; + + length = strlen(str); + + /* + * Skip the first digits which are not part of a valid serial number. + * This is necessary because some devices erroneously use random digits + * instead of zeros for padding. + */ + if (length > MAX_SERIAL_NUMBER_DIGITS) + str = str + (length - MAX_SERIAL_NUMBER_DIGITS); + + if (jaylink_parse_serial_number(str, serial_number) != JAYLINK_OK) + return false; + + return true; +} + +static bool compare_devices(const void *a, const void *b) +{ + const struct jaylink_device *dev; + const struct libusb_device *usb_dev; + + dev = a; + usb_dev = b; + + if (dev->iface != JAYLINK_HIF_USB) + return false; + + if (dev->usb_dev == usb_dev) + return true; + + return false; +} + +static struct jaylink_device *find_device(const struct jaylink_context *ctx, + const struct libusb_device *usb_dev) +{ + struct list *item; + + item = list_find_custom(ctx->devs, &compare_devices, usb_dev); + + if (item) + return item->data; + + return NULL; +} + +static struct jaylink_device *probe_device(struct jaylink_context *ctx, + struct libusb_device *usb_dev) +{ + int ret; + struct libusb_device_descriptor desc; + struct libusb_device_handle *usb_devh; + struct jaylink_device *dev; + char buf[USB_SERIAL_NUMBER_LENGTH + 1]; + uint8_t usb_address; + uint32_t serial_number; + bool valid_serial_number; + bool found_device; + size_t i; + + ret = libusb_get_device_descriptor(usb_dev, &desc); + + if (ret != LIBUSB_SUCCESS) { + log_warn(ctx, "Failed to get device descriptor: %s.", + libusb_error_name(ret)); + return NULL; + } + + if (desc.idVendor != USB_VENDOR_ID) + return NULL; + + found_device = false; + + for (i = 0; i < sizeof(pids) / sizeof(pids[0]); i++) { + if (pids[i][0] == desc.idProduct) { + found_device = true; + usb_address = pids[i][1]; + break; + } + } + + if (!found_device) + return NULL; + + log_dbg(ctx, "Found device (VID:PID = %04x:%04x, bus:address = " + "%03u:%03u).", desc.idVendor, desc.idProduct, + libusb_get_bus_number(usb_dev), + libusb_get_device_address(usb_dev)); + + /* + * Search for an already allocated device instance for this device and + * if found return a reference to it. + */ + dev = find_device(ctx, usb_dev); + + if (dev) { + log_dbg(ctx, "Device: USB address = %u.", dev->usb_address); + + if (dev->valid_serial_number) + log_dbg(ctx, "Device: Serial number = %u.", + dev->serial_number); + else + log_dbg(ctx, "Device: Serial number = N/A."); + + log_dbg(ctx, "Using existing device instance."); + return jaylink_ref_device(dev); + } + + /* Open the device to be able to retrieve its serial number. */ + ret = libusb_open(usb_dev, &usb_devh); + + if (ret != LIBUSB_SUCCESS) { + log_warn(ctx, "Failed to open device: %s.", + libusb_error_name(ret)); + return NULL; + } + + serial_number = 0; + valid_serial_number = true; + + ret = libusb_get_string_descriptor_ascii(usb_devh, desc.iSerialNumber, + (unsigned char *)buf, USB_SERIAL_NUMBER_LENGTH + 1); + + libusb_close(usb_devh); + + if (ret < 0) { + log_warn(ctx, "Failed to retrieve serial number: %s.", + libusb_error_name(ret)); + valid_serial_number = false; + } + + if (valid_serial_number) { + if (!parse_serial_number(buf, &serial_number)) { + log_warn(ctx, "Failed to parse serial number."); + return NULL; + } + } + + log_dbg(ctx, "Device: USB address = %u.", usb_address); + + if (valid_serial_number) + log_dbg(ctx, "Device: Serial number = %u.", serial_number); + else + log_dbg(ctx, "Device: Serial number = N/A."); + + log_dbg(ctx, "Allocating new device instance."); + + dev = device_allocate(ctx); + + if (!dev) { + log_warn(ctx, "Device instance malloc failed."); + return NULL; + } + + dev->iface = JAYLINK_HIF_USB; + dev->usb_dev = libusb_ref_device(usb_dev); + dev->usb_address = usb_address; + dev->serial_number = serial_number; + dev->valid_serial_number = valid_serial_number; + + return dev; +} + +JAYLINK_PRIV int discovery_usb_scan(struct jaylink_context *ctx) +{ + ssize_t ret; + struct libusb_device **devs; + struct jaylink_device *dev; + size_t num; + size_t i; + + ret = libusb_get_device_list(ctx->usb_ctx, &devs); + + if (ret == LIBUSB_ERROR_IO) { + log_err(ctx, "Failed to retrieve device list: input/output " + "error."); + return JAYLINK_ERR_IO; + } else if (ret < 0) { + log_err(ctx, "Failed to retrieve device list: %s.", + libusb_error_name(ret)); + return JAYLINK_ERR; + } + + num = 0; + + for (i = 0; devs[i]; i++) { + dev = probe_device(ctx, devs[i]); + + if (!dev) + continue; + + ctx->discovered_devs = list_prepend(ctx->discovered_devs, dev); + num++; + } + + libusb_free_device_list(devs, true); + log_dbg(ctx, "Found %zu USB device(s).", num); + + return JAYLINK_OK; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/emucom.c b/src/jtag/drivers/libjaylink/libjaylink/emucom.c new file mode 100644 index 0000000..035cb99 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/emucom.c @@ -0,0 +1,287 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2015-2016 Marc Schink <jaylink-dev@marcschink.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdint.h> +#include <stdbool.h> + +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/** + * @file + * + * Emulator communication (EMUCOM). + */ + +/** @cond PRIVATE */ +#define CMD_EMUCOM 0xee + +#define EMUCOM_CMD_READ 0x00 +#define EMUCOM_CMD_WRITE 0x01 + +/** Bitmask for the error indication bit of an EMUCOM status code. */ +#define EMUCOM_ERR 0x80000000 + +/** Error code indicating that the channel is not supported by the device. */ +#define EMUCOM_ERR_NOT_SUPPORTED 0x80000001 + +/** + * Error code indicating that the channel is not available for the requested + * number of bytes to be read. + * + * The number of bytes available on this channel is encoded in the lower + * 24 bits of the EMUCOM status code. + * + * @see EMUCOM_AVAILABLE_BYTES_MASK + */ +#define EMUCOM_ERR_NOT_AVAILABLE 0x81000000 + +/** + * Bitmask to extract the number of available bytes on a channel from an EMUCOM + * status code. + */ +#define EMUCOM_AVAILABLE_BYTES_MASK 0x00ffffff +/** @endcond */ + +/** + * Read from an EMUCOM channel. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_EMUCOM capability. + * + * @param[in,out] devh Device handle. + * @param[in] channel Channel to read data from. + * @param[out] buffer Buffer to store read data on success. Its content is + * undefined on failure. + * @param[in,out] length Number of bytes to read. On success, the value gets + * updated with the actual number of bytes read. Unless + * otherwise specified, the value is undefined on + * failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_PROTO Protocol violation. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR_DEV_NOT_SUPPORTED Channel is not supported by the + * device. + * @retval JAYLINK_ERR_DEV_NOT_AVAILABLE Channel is not available for the + * requested amount of data. @p length is + * updated with the number of bytes + * available on this channel. + * @retval JAYLINK_ERR_DEV Unspecified device error. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_emucom_read(struct jaylink_device_handle *devh, + uint32_t channel, uint8_t *buffer, uint32_t *length) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[10]; + uint32_t tmp; + + if (!devh || !buffer || !length) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 10, 4, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_EMUCOM; + buf[1] = EMUCOM_CMD_READ; + + buffer_set_u32(buf, channel, 2); + buffer_set_u32(buf, *length, 6); + + ret = transport_write(devh, buf, 10); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + tmp = buffer_get_u32(buf, 0); + + if (tmp == EMUCOM_ERR_NOT_SUPPORTED) + return JAYLINK_ERR_DEV_NOT_SUPPORTED; + + if ((tmp & ~EMUCOM_AVAILABLE_BYTES_MASK) == EMUCOM_ERR_NOT_AVAILABLE) { + *length = tmp & EMUCOM_AVAILABLE_BYTES_MASK; + return JAYLINK_ERR_DEV_NOT_AVAILABLE; + } + + if (tmp & EMUCOM_ERR) { + log_err(ctx, "Failed to read from channel 0x%x: 0x%x.", + channel, tmp); + return JAYLINK_ERR_DEV; + } + + if (tmp > *length) { + log_err(ctx, "Requested at most %u bytes but device " + "returned %u bytes.", *length, tmp); + return JAYLINK_ERR_PROTO; + } + + *length = tmp; + + if (!tmp) + return JAYLINK_OK; + + ret = transport_start_read(devh, tmp); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buffer, tmp); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + return JAYLINK_OK; +} + +/** + * Write to an EMUCOM channel. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_EMUCOM capability. + * + * @param[in,out] devh Device handle. + * @param[in] channel Channel to write data to. + * @param[in] buffer Buffer to write data from. + * @param[in,out] length Number of bytes to write. On success, the value gets + * updated with the actual number of bytes written. The + * value is undefined on failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_PROTO Protocol violation. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR_DEV_NOT_SUPPORTED Channel is not supported by the + * device. + * @retval JAYLINK_ERR_DEV Unspecified device error. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_emucom_write(struct jaylink_device_handle *devh, + uint32_t channel, const uint8_t *buffer, uint32_t *length) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[10]; + uint32_t tmp; + + if (!devh || !buffer || !length) + return JAYLINK_ERR_ARG; + + if (!*length) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write(devh, 10, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_EMUCOM; + buf[1] = EMUCOM_CMD_WRITE; + + buffer_set_u32(buf, channel, 2); + buffer_set_u32(buf, *length, 6); + + ret = transport_write(devh, buf, 10); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_start_write_read(devh, *length, 4, false); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_write(devh, buffer, *length); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + tmp = buffer_get_u32(buf, 0); + + if (tmp == EMUCOM_ERR_NOT_SUPPORTED) + return JAYLINK_ERR_DEV_NOT_SUPPORTED; + + if (tmp & EMUCOM_ERR) { + log_err(ctx, "Failed to write to channel 0x%x: 0x%x.", + channel, tmp); + return JAYLINK_ERR_DEV; + } + + if (tmp > *length) { + log_err(ctx, "Only %u bytes were supposed to be written, but " + "the device reported %u written bytes.", *length, tmp); + return JAYLINK_ERR_PROTO; + } + + *length = tmp; + + return JAYLINK_OK; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/error.c b/src/jtag/drivers/libjaylink/libjaylink/error.c new file mode 100644 index 0000000..2c696fc --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/error.c @@ -0,0 +1,118 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-2015 Marc Schink <jaylink-dev@marcschink.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "libjaylink.h" + +/** + * @file + * + * Error handling. + */ + +/** + * Return a human-readable description of a libjaylink error code. + * + * @param[in] error_code A libjaylink error code. See #jaylink_error for valid + * values. + * + * @return A string which describes the given error code, or the string + * <i>unknown error</i> if the error code is not known. The string is + * null-terminated and must not be free'd by the caller. + * + * @since 0.1.0 + */ +JAYLINK_API const char *jaylink_strerror(int error_code) +{ + switch (error_code) { + case JAYLINK_OK: + return "no error"; + case JAYLINK_ERR: + return "unspecified error"; + case JAYLINK_ERR_ARG: + return "invalid argument"; + case JAYLINK_ERR_MALLOC: + return "memory allocation error"; + case JAYLINK_ERR_TIMEOUT: + return "timeout occurred"; + case JAYLINK_ERR_PROTO: + return "protocol violation"; + case JAYLINK_ERR_NOT_AVAILABLE: + return "entity not available"; + case JAYLINK_ERR_NOT_SUPPORTED: + return "operation not supported"; + case JAYLINK_ERR_IO: + return "input/output error"; + case JAYLINK_ERR_DEV: + return "device: unspecified error"; + case JAYLINK_ERR_DEV_NOT_SUPPORTED: + return "device: operation not supported"; + case JAYLINK_ERR_DEV_NOT_AVAILABLE: + return "device: entity not available"; + case JAYLINK_ERR_DEV_NO_MEMORY: + return "device: not enough memory to perform operation"; + default: + return "unknown error"; + } +} + +/** + * Return the name of a libjaylink error code. + * + * @param[in] error_code A libjaylink error code. See #jaylink_error for valid + * values. + * + * @return A string which contains the name for the given error code, or the + * string <i>unknown error code</i> if the error code is not known. The + * string is null-terminated and must not be free'd by the caller. + * + * @since 0.1.0 + */ +JAYLINK_API const char *jaylink_strerror_name(int error_code) +{ + switch (error_code) { + case JAYLINK_OK: + return "JAYLINK_OK"; + case JAYLINK_ERR: + return "JAYLINK_ERR"; + case JAYLINK_ERR_ARG: + return "JAYLINK_ERR_ARG"; + case JAYLINK_ERR_MALLOC: + return "JAYLINK_ERR_MALLOC"; + case JAYLINK_ERR_TIMEOUT: + return "JAYLINK_ERR_TIMEOUT"; + case JAYLINK_ERR_PROTO: + return "JAYLINK_ERR_PROTO"; + case JAYLINK_ERR_NOT_AVAILABLE: + return "JAYLINK_ERR_NOT_AVAILABLE"; + case JAYLINK_ERR_NOT_SUPPORTED: + return "JAYLINK_ERR_NOT_SUPPORTED"; + case JAYLINK_ERR_IO: + return "JAYLINK_ERR_IO"; + case JAYLINK_ERR_DEV: + return "JAYLINK_ERR_DEV"; + case JAYLINK_ERR_DEV_NOT_SUPPORTED: + return "JAYLINK_ERR_DEV_NOT_SUPPORTED"; + case JAYLINK_ERR_DEV_NOT_AVAILABLE: + return "JAYLINK_ERR_DEV_NOT_AVAILABLE"; + case JAYLINK_ERR_DEV_NO_MEMORY: + return "JAYLINK_ERR_DEV_NO_MEMORY"; + default: + return "unknown error code"; + } +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/fileio.c b/src/jtag/drivers/libjaylink/libjaylink/fileio.c new file mode 100644 index 0000000..933c366 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/fileio.c @@ -0,0 +1,499 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2015 Marc Schink <jaylink-dev@marcschink.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdint.h> +#include <stdbool.h> +#include <string.h> + +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/** + * @file + * + * File I/O functions. + */ + +/** @cond PRIVATE */ +#define CMD_FILE_IO 0x1e + +#define FILE_IO_CMD_READ 0x64 +#define FILE_IO_CMD_WRITE 0x65 +#define FILE_IO_CMD_GET_SIZE 0x66 +#define FILE_IO_CMD_DELETE 0x67 + +#define FILE_IO_PARAM_FILENAME 0x01 +#define FILE_IO_PARAM_OFFSET 0x02 +#define FILE_IO_PARAM_LENGTH 0x03 + +#define FILE_IO_ERR 0x80000000 +/** @endcond */ + +/** + * Read from a file. + * + * The maximum amount of data that can be read from a file at once is + * #JAYLINK_FILE_MAX_TRANSFER_SIZE bytes. Multiple reads in conjunction with + * the @p offset parameter are needed for larger files. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_FILE_IO capability. + * + * @param[in,out] devh Device handle. + * @param[in] filename Name of the file to read from. The length of the name + * must not exceed #JAYLINK_FILE_NAME_MAX_LENGTH bytes. + * @param[out] buffer Buffer to store read data on success. Its content is + * undefined on failure + * @param[in] offset Offset in bytes relative to the beginning of the file from + * where to start reading. + * @param[in,out] length Number of bytes to read. On success, the value gets + * updated with the actual number of bytes read. The + * value is undefined on failure. + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR_DEV Unspecified device error, or the file was not found. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_file_read(struct jaylink_device_handle *devh, + const char *filename, uint8_t *buffer, uint32_t offset, + uint32_t *length) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[18 + JAYLINK_FILE_NAME_MAX_LENGTH]; + size_t filename_length; + uint32_t tmp; + + if (!devh || !filename || !buffer || !length) + return JAYLINK_ERR_ARG; + + if (!*length) + return JAYLINK_ERR_ARG; + + if (*length > JAYLINK_FILE_MAX_TRANSFER_SIZE) + return JAYLINK_ERR_ARG; + + filename_length = strlen(filename); + + if (!filename_length) + return JAYLINK_ERR_ARG; + + if (filename_length > JAYLINK_FILE_NAME_MAX_LENGTH) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write(devh, 18 + filename_length, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_FILE_IO; + buf[1] = FILE_IO_CMD_READ; + buf[2] = 0x00; + + buf[3] = filename_length; + buf[4] = FILE_IO_PARAM_FILENAME; + memcpy(buf + 5, filename, filename_length); + + buf[filename_length + 5] = 0x04; + buf[filename_length + 6] = FILE_IO_PARAM_OFFSET; + buffer_set_u32(buf, offset, filename_length + 7); + + buf[filename_length + 11] = 0x04; + buf[filename_length + 12] = FILE_IO_PARAM_LENGTH; + buffer_set_u32(buf, *length, filename_length + 13); + + buf[filename_length + 17] = 0x00; + + ret = transport_write(devh, buf, 18 + filename_length); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_start_read(devh, *length); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buffer, *length); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_start_read(devh, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + tmp = buffer_get_u32(buf, 0); + + if (tmp & FILE_IO_ERR) + return JAYLINK_ERR_DEV; + + *length = tmp; + + return JAYLINK_OK; +} + +/** + * Write to a file. + * + * If a file does not exist, a new file is created. + * + * The maximum amount of data that can be written to a file at once is + * #JAYLINK_FILE_MAX_TRANSFER_SIZE bytes. Multiple writes in conjunction with + * the @p offset parameter are needed for larger files. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_FILE_IO capability. + * + * @param[in,out] devh Device handle. + * @param[in] filename Name of the file to write to. The length of the name + * must not exceed #JAYLINK_FILE_NAME_MAX_LENGTH bytes. + * @param[in] buffer Buffer to write data from. + * @param[in] offset Offset in bytes relative to the beginning of the file from + * where to start writing. + * @param[in,out] length Number of bytes to write. On success, the value gets + * updated with the actual number of bytes written. The + * value is undefined on failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR_DEV Unspecified device error, or the file was not found. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_file_write(struct jaylink_device_handle *devh, + const char *filename, const uint8_t *buffer, uint32_t offset, + uint32_t *length) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[18 + JAYLINK_FILE_NAME_MAX_LENGTH]; + size_t filename_length; + uint32_t tmp; + + if (!devh || !filename || !buffer || !length) + return JAYLINK_ERR_ARG; + + if (!*length) + return JAYLINK_ERR_ARG; + + if (*length > JAYLINK_FILE_MAX_TRANSFER_SIZE) + return JAYLINK_ERR_ARG; + + filename_length = strlen(filename); + + if (!filename_length) + return JAYLINK_ERR_ARG; + + if (filename_length > JAYLINK_FILE_NAME_MAX_LENGTH) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write(devh, 18 + filename_length, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_FILE_IO; + buf[1] = FILE_IO_CMD_WRITE; + buf[2] = 0x00; + + buf[3] = filename_length; + buf[4] = FILE_IO_PARAM_FILENAME; + memcpy(buf + 5, filename, filename_length); + + buf[filename_length + 5] = 0x04; + buf[filename_length + 6] = FILE_IO_PARAM_OFFSET; + buffer_set_u32(buf, offset, filename_length + 7); + + buf[filename_length + 11] = 0x04; + buf[filename_length + 12] = FILE_IO_PARAM_LENGTH; + buffer_set_u32(buf, *length, filename_length + 13); + + buf[filename_length + 17] = 0x00; + + ret = transport_write(devh, buf, 18 + filename_length); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_start_write(devh, *length, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_write(devh, buffer, *length); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_start_read(devh, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + tmp = buffer_get_u32(buf, 0); + + if (tmp & FILE_IO_ERR) + return JAYLINK_ERR_DEV; + + *length = tmp; + + return JAYLINK_OK; +} + +/** + * Retrieve the size of a file. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_FILE_IO capability. + * + * @param[in,out] devh Device handle. + * @param[in] filename Name of the file to retrieve the size of. The length + * of the name must not exceed + * #JAYLINK_FILE_NAME_MAX_LENGTH bytes. + * @param[out] size Size of the file in bytes on success, and undefined on + * failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR_DEV Unspecified device error, or the file was not found. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_file_get_size(struct jaylink_device_handle *devh, + const char *filename, uint32_t *size) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[6 + JAYLINK_FILE_NAME_MAX_LENGTH]; + size_t length; + uint32_t tmp; + + if (!devh || !filename || !size) + return JAYLINK_ERR_ARG; + + length = strlen(filename); + + if (!length) + return JAYLINK_ERR_ARG; + + if (length > JAYLINK_FILE_NAME_MAX_LENGTH) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write(devh, 6 + length, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_FILE_IO; + buf[1] = FILE_IO_CMD_GET_SIZE; + buf[2] = 0x00; + + buf[3] = length; + buf[4] = FILE_IO_PARAM_FILENAME; + memcpy(buf + 5, filename, length); + + buf[length + 5] = 0x00; + + ret = transport_write(devh, buf, 6 + length); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_start_read(devh, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + tmp = buffer_get_u32(buf, 0); + + if (tmp & FILE_IO_ERR) + return JAYLINK_ERR_DEV; + + *size = tmp; + + return JAYLINK_OK; +} + +/** + * Delete a file. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_FILE_IO capability. + * + * @param[in,out] devh Device handle. + * @param[in] filename Name of the file to delete. The length of the name + * must not exceed #JAYLINK_FILE_NAME_MAX_LENGTH bytes. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR_DEV Unspecified device error, or the file was not found. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_file_delete(struct jaylink_device_handle *devh, + const char *filename) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[6 + JAYLINK_FILE_NAME_MAX_LENGTH]; + size_t length; + uint32_t tmp; + + if (!devh || !filename) + return JAYLINK_ERR_ARG; + + length = strlen(filename); + + if (!length) + return JAYLINK_ERR_ARG; + + if (length > JAYLINK_FILE_NAME_MAX_LENGTH) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write(devh, 6 + length, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_FILE_IO; + buf[1] = FILE_IO_CMD_DELETE; + buf[2] = 0x00; + + buf[3] = length; + buf[4] = FILE_IO_PARAM_FILENAME; + memcpy(buf + 5, filename, length); + + buf[length + 5] = 0x00; + + ret = transport_write(devh, buf, 6 + length); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_start_read(devh, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + tmp = buffer_get_u32(buf, 0); + + if (tmp & FILE_IO_ERR) + return JAYLINK_ERR_DEV; + + return JAYLINK_OK; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/jtag.c b/src/jtag/drivers/libjaylink/libjaylink/jtag.c new file mode 100644 index 0000000..c0c65de --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/jtag.c @@ -0,0 +1,259 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-2015 Marc Schink <jaylink-dev@marcschink.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdint.h> +#include <stdbool.h> + +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/** + * @file + * + * JTAG functions. + */ + +/** @cond PRIVATE */ +#define CMD_JTAG_IO_V2 0xce +#define CMD_JTAG_IO_V3 0xcf +#define CMD_JTAG_CLEAR_TRST 0xde +#define CMD_JTAG_SET_TRST 0xdf + +/** + * Error code indicating that there is not enough free memory on the device to + * perform the JTAG I/O operation. + */ +#define JTAG_IO_ERR_NO_MEMORY 0x06 +/** @endcond */ + +/** + * Perform a JTAG I/O operation. + * + * @note This function must only be used if the #JAYLINK_TIF_JTAG interface is + * available and selected. Nevertheless, this function can be used if the + * device doesn't have the #JAYLINK_DEV_CAP_SELECT_TIF capability. + * + * @param[in,out] devh Device handle. + * @param[in] tms Buffer to read TMS data from. + * @param[in] tdi Buffer to read TDI data from. + * @param[out] tdo Buffer to store TDO data on success. Its content is + * undefined on failure. The buffer must be large enough to + * contain at least the specified number of bits to transfer. + * @param[in] length Number of bits to transfer. + * @param[in] version Version of the JTAG command to use. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR_DEV_NO_MEMORY Not enough memory on the device to perform + * the operation. + * @retval JAYLINK_ERR_DEV Unspecified device error. + * @retval JAYLINK_ERR Other error conditions. + * + * @see jaylink_select_interface() + * @see jaylink_set_speed() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_jtag_io(struct jaylink_device_handle *devh, + const uint8_t *tms, const uint8_t *tdi, uint8_t *tdo, + uint16_t length, enum jaylink_jtag_version version) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[4]; + uint16_t num_bytes; + uint16_t read_length; + uint8_t status; + uint8_t cmd; + + if (!devh || !tms || !tdi || !tdo || !length) + return JAYLINK_ERR_ARG; + + num_bytes = (length + 7) / 8; + read_length = num_bytes; + + switch (version) { + case JAYLINK_JTAG_VERSION_2: + cmd = CMD_JTAG_IO_V2; + break; + case JAYLINK_JTAG_VERSION_3: + cmd = CMD_JTAG_IO_V3; + /* In this version, the response includes a status byte. */ + read_length++; + break; + default: + return JAYLINK_ERR_ARG; + } + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 4 + 2 * num_bytes, + read_length, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = cmd; + buf[1] = 0x00; + buffer_set_u16(buf, length, 2); + + ret = transport_write(devh, buf, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_write(devh, tms, num_bytes); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_write(devh, tdi, num_bytes); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, tdo, num_bytes); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + if (version == JAYLINK_JTAG_VERSION_2) + return JAYLINK_OK; + + ret = transport_read(devh, &status, 1); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + if (status == JTAG_IO_ERR_NO_MEMORY) { + return JAYLINK_ERR_DEV_NO_MEMORY; + } else if (status > 0) { + log_err(ctx, "JTAG I/O operation failed: 0x%x.", status); + return JAYLINK_ERR_DEV; + } + + return JAYLINK_OK; +} + +/** + * Clear the JTAG test reset (TRST) signal. + * + * @param[in,out] devh Device handle. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_jtag_clear_trst(struct jaylink_device_handle *devh) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[1]; + + if (!devh) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write(devh, 1, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_JTAG_CLEAR_TRST; + + ret = transport_write(devh, buf, 1); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + return JAYLINK_OK; +} + +/** + * Set the JTAG test reset (TRST) signal. + * + * @param[in,out] devh Device handle. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_jtag_set_trst(struct jaylink_device_handle *devh) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[1]; + + if (!devh) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write(devh, 1, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_JTAG_SET_TRST; + + ret = transport_write(devh, buf, 1); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + return JAYLINK_OK; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/libjaylink-internal.h b/src/jtag/drivers/libjaylink/libjaylink/libjaylink-internal.h new file mode 100644 index 0000000..f97ec14 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/libjaylink-internal.h @@ -0,0 +1,320 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-2016 Marc Schink <jaylink-dev@marcschink.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef LIBJAYLINK_LIBJAYLINK_INTERNAL_H +#define LIBJAYLINK_LIBJAYLINK_INTERNAL_H + +#include <stddef.h> +#include <stdint.h> +#include <stdbool.h> +#include <stdarg.h> +#include <sys/types.h> +#ifdef _WIN32 +#include <ws2tcpip.h> +#else +#include <sys/socket.h> +#include <arpa/inet.h> +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_LIBUSB +#include <libusb.h> +#endif + +#include "libjaylink.h" + +/** + * @file + * + * Internal libjaylink header file. + */ + +/** Macro to mark private libjaylink symbol. */ +#if defined(_WIN32) || defined(__MSYS__) || defined(__CYGWIN__) +#define JAYLINK_PRIV +#else +#define JAYLINK_PRIV __attribute__ ((visibility ("hidden"))) +#endif + +/** Calculate the minimum of two numeric values. */ +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +struct jaylink_context { +#ifdef HAVE_LIBUSB + /** libusb context. */ + struct libusb_context *usb_ctx; +#endif + /** + * List of allocated device instances. + * + * Used to prevent multiple device instances for the same device. + */ + struct list *devs; + /** List of recently discovered devices. */ + struct list *discovered_devs; + /** Current log level. */ + enum jaylink_log_level log_level; + /** Log callback function. */ + jaylink_log_callback log_callback; + /** User data to be passed to the log callback function. */ + void *log_callback_data; + /** Log domain. */ + char log_domain[JAYLINK_LOG_DOMAIN_MAX_LENGTH + 1]; +}; + +struct jaylink_device { + /** libjaylink context. */ + struct jaylink_context *ctx; + /** Number of references held on this device instance. */ + size_t ref_count; + /** Host interface. */ + enum jaylink_host_interface iface; + /** + * Serial number of the device. + * + * This number is for enumeration purpose only and can differ from the + * real serial number of the device. + */ + uint32_t serial_number; + /** Indicates whether the serial number is valid. */ + bool valid_serial_number; +#ifdef HAVE_LIBUSB + /** libusb device instance. */ + struct libusb_device *usb_dev; + /** USB address of the device. */ + uint8_t usb_address; +#endif + /** + * IPv4 address. + * + * The address is encoded as string in quad-dotted decimal format. + * + * This field is used for devices with host interface #JAYLINK_HIF_TCP + * only. + */ + char ipv4_address[INET_ADDRSTRLEN]; + /** + * Media Access Control (MAC) address. + * + * This field is used for devices with host interface #JAYLINK_HIF_TCP + * only. + */ + uint8_t mac_address[JAYLINK_MAC_ADDRESS_LENGTH]; + /** Indicates whether the MAC address is available. */ + bool has_mac_address; + /** + * Product name. + * + * This field is used for devices with host interface #JAYLINK_HIF_TCP + * only. + */ + char product_name[JAYLINK_PRODUCT_NAME_MAX_LENGTH]; + /** Indicates whether the product name is available. */ + bool has_product_name; + /** + * Nickname. + * + * This field is used for devices with host interface #JAYLINK_HIF_TCP + * only. + */ + char nickname[JAYLINK_NICKNAME_MAX_LENGTH]; + /** Indicates whether the nickname is available. */ + bool has_nickname; + /** + * Hardware version. + * + * This field is used for devices with host interface #JAYLINK_HIF_TCP + * only. + */ + struct jaylink_hardware_version hw_version; + /** Indicates whether the hardware version is available. */ + bool has_hw_version; +}; + +struct jaylink_device_handle { + /** Device instance. */ + struct jaylink_device *dev; + /** + * Buffer for write and read operations. + * + * Note that write and read operations are always processed + * consecutively and therefore the same buffer can be used for both. + */ + uint8_t *buffer; + /** Buffer size. */ + size_t buffer_size; + /** Number of bytes left for the read operation. */ + size_t read_length; + /** Number of bytes available in the buffer to be read. */ + size_t bytes_available; + /** Current read position in the buffer. */ + size_t read_pos; + /** + * Number of bytes left to be written before the write operation will + * be performed. + */ + size_t write_length; + /** + * Current write position in the buffer. + * + * This is equivalent to the number of bytes in the buffer and used for + * write operations only. + */ + size_t write_pos; +#ifdef HAVE_LIBUSB + /** libusb device handle. */ + struct libusb_device_handle *usb_devh; + /** USB interface number of the device. */ + uint8_t interface_number; + /** USB interface IN endpoint of the device. */ + uint8_t endpoint_in; + /** USB interface OUT endpoint of the device. */ + uint8_t endpoint_out; +#endif + /** + * Socket descriptor. + * + * This field is used for devices with host interface #JAYLINK_HIF_TCP + * only. + */ + int sock; +}; + +struct list { + void *data; + struct list *next; +}; + +typedef bool (*list_compare_callback)(const void *data, const void *user_data); + +/*--- buffer.c --------------------------------------------------------------*/ + +JAYLINK_PRIV void buffer_set_u16(uint8_t *buffer, uint16_t value, + size_t offset); +JAYLINK_PRIV uint16_t buffer_get_u16(const uint8_t *buffer, size_t offset); +JAYLINK_PRIV void buffer_set_u32(uint8_t *buffer, uint32_t value, + size_t offset); +JAYLINK_PRIV uint32_t buffer_get_u32(const uint8_t *buffer, size_t offset); + +/*--- device.c --------------------------------------------------------------*/ + +JAYLINK_PRIV struct jaylink_device *device_allocate( + struct jaylink_context *ctx); + +/*--- discovery_tcp.c -------------------------------------------------------*/ + +JAYLINK_PRIV int discovery_tcp_scan(struct jaylink_context *ctx); + +/*--- discovery_usb.c -------------------------------------------------------*/ + +JAYLINK_PRIV int discovery_usb_scan(struct jaylink_context *ctx); + +/*--- list.c ----------------------------------------------------------------*/ + +JAYLINK_PRIV struct list *list_prepend(struct list *list, void *data); +JAYLINK_PRIV struct list *list_remove(struct list *list, const void *data); +JAYLINK_PRIV struct list *list_find_custom(struct list *list, + list_compare_callback callback, const void *user_data); +JAYLINK_PRIV size_t list_length(struct list *list); +JAYLINK_PRIV void list_free(struct list *list); + +/*--- log.c -----------------------------------------------------------------*/ + +JAYLINK_PRIV int log_vprintf(const struct jaylink_context *ctx, + enum jaylink_log_level level, const char *format, va_list args, + void *user_data); +JAYLINK_PRIV void log_err(const struct jaylink_context *ctx, + const char *format, ...); +JAYLINK_PRIV void log_warn(const struct jaylink_context *ctx, + const char *format, ...); +JAYLINK_PRIV void log_info(const struct jaylink_context *ctx, + const char *format, ...); +JAYLINK_PRIV void log_dbg(const struct jaylink_context *ctx, + const char *format, ...); +JAYLINK_PRIV void log_dbgio(const struct jaylink_context *ctx, + const char *format, ...); + +/*--- socket.c --------------------------------------------------------------*/ + +JAYLINK_PRIV bool socket_close(int sock); +JAYLINK_PRIV bool socket_bind(int sock, const struct sockaddr *address, + size_t length); +JAYLINK_PRIV bool socket_send(int sock, const void *buffer, size_t *length, + int flags); +JAYLINK_PRIV bool socket_recv(int sock, void *buffer, size_t *length, + int flags); +JAYLINK_PRIV bool socket_sendto(int sock, const void *buffer, size_t *length, + int flags, const struct sockaddr *address, + size_t address_length); +JAYLINK_PRIV bool socket_recvfrom(int sock, void *buffer, size_t *length, + int flags, struct sockaddr *address, size_t *address_length); +JAYLINK_PRIV bool socket_set_option(int sock, int level, int option, + const void *value, size_t length); + +/*--- transport.c -----------------------------------------------------------*/ + +JAYLINK_PRIV int transport_open(struct jaylink_device_handle *devh); +JAYLINK_PRIV int transport_close(struct jaylink_device_handle *devh); +JAYLINK_PRIV int transport_start_write_read(struct jaylink_device_handle *devh, + size_t write_length, size_t read_length, bool has_command); +JAYLINK_PRIV int transport_start_write(struct jaylink_device_handle *devh, + size_t length, bool has_command); +JAYLINK_PRIV int transport_start_read(struct jaylink_device_handle *devh, + size_t length); +JAYLINK_PRIV int transport_write(struct jaylink_device_handle *devh, + const uint8_t *buffer, size_t length); +JAYLINK_PRIV int transport_read(struct jaylink_device_handle *devh, + uint8_t *buffer, size_t length); + +/*--- transport_usb.c -------------------------------------------------------*/ + +JAYLINK_PRIV int transport_usb_open(struct jaylink_device_handle *devh); +JAYLINK_PRIV int transport_usb_close(struct jaylink_device_handle *devh); +JAYLINK_PRIV int transport_usb_start_write_read( + struct jaylink_device_handle *devh, size_t write_length, + size_t read_length, bool has_command); +JAYLINK_PRIV int transport_usb_start_write(struct jaylink_device_handle *devh, + size_t length, bool has_command); +JAYLINK_PRIV int transport_usb_start_read(struct jaylink_device_handle *devh, + size_t length); +JAYLINK_PRIV int transport_usb_write(struct jaylink_device_handle *devh, + const uint8_t *buffer, size_t length); +JAYLINK_PRIV int transport_usb_read(struct jaylink_device_handle *devh, + uint8_t *buffer, size_t length); + +/*--- transport_tcp.c -------------------------------------------------------*/ + +JAYLINK_PRIV int transport_tcp_open(struct jaylink_device_handle *devh); +JAYLINK_PRIV int transport_tcp_close(struct jaylink_device_handle *devh); +JAYLINK_PRIV int transport_tcp_start_write_read( + struct jaylink_device_handle *devh, size_t write_length, + size_t read_length, bool has_command); +JAYLINK_PRIV int transport_tcp_start_write(struct jaylink_device_handle *devh, + size_t length, bool has_command); +JAYLINK_PRIV int transport_tcp_start_read(struct jaylink_device_handle *devh, + size_t length); +JAYLINK_PRIV int transport_tcp_write(struct jaylink_device_handle *devh, + const uint8_t *buffer, size_t length); +JAYLINK_PRIV int transport_tcp_read(struct jaylink_device_handle *devh, + uint8_t *buffer, size_t length); + +#endif /* LIBJAYLINK_LIBJAYLINK_INTERNAL_H */ diff --git a/src/jtag/drivers/libjaylink/libjaylink/libjaylink.h b/src/jtag/drivers/libjaylink/libjaylink/libjaylink.h new file mode 100644 index 0000000..223aa84 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/libjaylink.h @@ -0,0 +1,589 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-2016 Marc Schink <jaylink-dev@marcschink.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef LIBJAYLINK_LIBJAYLINK_H +#define LIBJAYLINK_LIBJAYLINK_H + +#include <stddef.h> +#include <stdint.h> +#include <stdbool.h> +#include <stdarg.h> +#ifdef _WIN32 +#include <ws2tcpip.h> +#else +#include <arpa/inet.h> +#endif + +/** + * @file + * + * Public libjaylink header file to be used by applications. + */ + +/** Error codes returned by libjaylink functions. */ +enum jaylink_error { + /** No error. */ + JAYLINK_OK = 0, + /** Unspecified error. */ + JAYLINK_ERR = -1, + /** Invalid argument. */ + JAYLINK_ERR_ARG = -2, + /** Memory allocation error. */ + JAYLINK_ERR_MALLOC = -3, + /** Timeout occurred. */ + JAYLINK_ERR_TIMEOUT = -4, + /** Protocol violation. */ + JAYLINK_ERR_PROTO = -5, + /** Entity not available. */ + JAYLINK_ERR_NOT_AVAILABLE = -6, + /** Operation not supported. */ + JAYLINK_ERR_NOT_SUPPORTED = -7, + /** Input/output error. */ + JAYLINK_ERR_IO = -8, + /** Device: unspecified error. */ + JAYLINK_ERR_DEV = -1000, + /** Device: operation not supported. */ + JAYLINK_ERR_DEV_NOT_SUPPORTED = -1001, + /** Device: entity not available. */ + JAYLINK_ERR_DEV_NOT_AVAILABLE = -1002, + /** Device: not enough memory to perform operation. */ + JAYLINK_ERR_DEV_NO_MEMORY = -1003 +}; + +/** libjaylink log levels. */ +enum jaylink_log_level { + /** Output no messages. */ + JAYLINK_LOG_LEVEL_NONE = 0, + /** Output error messages. */ + JAYLINK_LOG_LEVEL_ERROR = 1, + /** Output warnings. */ + JAYLINK_LOG_LEVEL_WARNING = 2, + /** Output informational messages. */ + JAYLINK_LOG_LEVEL_INFO = 3, + /** Output debug messages. */ + JAYLINK_LOG_LEVEL_DEBUG = 4, + /** Output I/O debug messages. */ + JAYLINK_LOG_LEVEL_DEBUG_IO = 5 +}; + +/** Default libjaylink log domain. */ +#define JAYLINK_LOG_DOMAIN_DEFAULT "jaylink: " + +/** Maximum length of a libjaylink log domain in bytes. */ +#define JAYLINK_LOG_DOMAIN_MAX_LENGTH 32 + +/** libjaylink capabilities. */ +enum jaylink_capability { + /** Library supports USB as host interface. */ + JAYLINK_CAP_HIF_USB = 0 +}; + +/** Host interfaces. */ +enum jaylink_host_interface { + /** Universal Serial Bus (USB). */ + JAYLINK_HIF_USB = (1 << 0), + /** Transmission Control Protocol (TCP). */ + JAYLINK_HIF_TCP = (1 << 1) +}; + +/** + * USB addresses. + * + * The USB address is a way to identify USB devices and is related to the USB + * Product ID (PID) of a device. + */ +enum jaylink_usb_address { + /** USB address 0 (Product ID 0x0101). */ + JAYLINK_USB_ADDRESS_0 = 0, + /** USB address 1 (Product ID 0x0102). */ + JAYLINK_USB_ADDRESS_1 = 1, + /** USB address 2 (Product ID 0x0103). */ + JAYLINK_USB_ADDRESS_2 = 2, + /** USB address 3 (Product ID 0x0104). */ + JAYLINK_USB_ADDRESS_3 = 3 +}; + +/** Device capabilities. */ +enum jaylink_device_capability { + /** Device supports retrieval of the hardware version. */ + JAYLINK_DEV_CAP_GET_HW_VERSION = 1, + /** Device supports adaptive clocking. */ + JAYLINK_DEV_CAP_ADAPTIVE_CLOCKING = 3, + /** Device supports reading configuration data. */ + JAYLINK_DEV_CAP_READ_CONFIG = 4, + /** Device supports writing configuration data. */ + JAYLINK_DEV_CAP_WRITE_CONFIG = 5, + /** Device supports retrieval of target interface speeds. */ + JAYLINK_DEV_CAP_GET_SPEEDS = 9, + /** Device supports retrieval of free memory size. */ + JAYLINK_DEV_CAP_GET_FREE_MEMORY = 11, + /** Device supports retrieval of hardware information. */ + JAYLINK_DEV_CAP_GET_HW_INFO = 12, + /** Device supports the setting of the target power supply. */ + JAYLINK_DEV_CAP_SET_TARGET_POWER = 13, + /** Device supports target interface selection. */ + JAYLINK_DEV_CAP_SELECT_TIF = 17, + /** Device supports retrieval of counter values. */ + JAYLINK_DEV_CAP_GET_COUNTERS = 19, + /** Device supports capturing of SWO trace data. */ + JAYLINK_DEV_CAP_SWO = 23, + /** Device supports file I/O operations. */ + JAYLINK_DEV_CAP_FILE_IO = 26, + /** Device supports registration of connections. */ + JAYLINK_DEV_CAP_REGISTER = 27, + /** Device supports retrieval of extended capabilities. */ + JAYLINK_DEV_CAP_GET_EXT_CAPS = 31, + /** Device supports EMUCOM. */ + JAYLINK_DEV_CAP_EMUCOM = 33, + /** Device supports ethernet connectivity. */ + JAYLINK_DEV_CAP_ETHERNET = 38 +}; + +/** Hardware information. */ +enum jaylink_hardware_info { + /** + * Status of the target power supply. + * + * This indicates whether the target power supply on pin 19 of the + * 20-pin JTAG / SWD connector is enabled or disabled. + * + * @see jaylink_set_target_power() + */ + JAYLINK_HW_INFO_TARGET_POWER = (1 << 0), + /** Current consumption of the target in mA. */ + JAYLINK_HW_INFO_ITARGET = (1 << 2), + /** Peak current consumption of the target in mA. */ + JAYLINK_HW_INFO_ITARGET_PEAK = (1 << 3) +}; + +/** Device counters. */ +enum jaylink_counter { + /** Time the device is connected to a target in milliseconds. */ + JAYLINK_COUNTER_TARGET_TIME = (1 << 0), + /** + * Number of times the device was connected or disconnected from a + * target. + */ + JAYLINK_COUNTER_TARGET_CONNECTIONS = (1 << 1) +}; + +/** Device hardware types. */ +enum jaylink_hardware_type { + /** J-Link. */ + JAYLINK_HW_TYPE_JLINK = 0, + /** Flasher. */ + JAYLINK_HW_TYPE_FLASHER = 2, + /** J-Link Pro. */ + JAYLINK_HW_TYPE_JLINK_PRO = 3 +}; + +/** Target interfaces. */ +enum jaylink_target_interface { + /** Joint Test Action Group, IEEE 1149.1 (JTAG). */ + JAYLINK_TIF_JTAG = 0, + /** Serial Wire Debug (SWD). */ + JAYLINK_TIF_SWD = 1, + /** Background Debug Mode 3 (BDM3). */ + JAYLINK_TIF_BDM3 = 2, + /** Renesas’ single-wire debug interface (FINE). */ + JAYLINK_TIF_FINE = 3, + /** 2-wire JTAG for PIC32 compliant devices. */ + JAYLINK_TIF_2W_JTAG_PIC32 = 4, +}; + +/** + * JTAG command versions. + * + * The JTAG command version only affects the device and the communication + * protocol. The behaviour of a JTAG operation is not affected at all. + */ +enum jaylink_jtag_version { + /** + * JTAG command version 2. + * + * This version is obsolete for major hardware version 5 and above. Use + * #JAYLINK_JTAG_VERSION_3 for these versions instead. + */ + JAYLINK_JTAG_VERSION_2 = 1, + /** JTAG command version 3. */ + JAYLINK_JTAG_VERSION_3 = 2 +}; + +/** Serial Wire Output (SWO) capture modes. */ +enum jaylink_swo_mode { + /** Universal Asynchronous Receiver Transmitter (UART). */ + JAYLINK_SWO_MODE_UART = 0 +}; + +/** Target interface speed information. */ +struct jaylink_speed { + /** Base frequency in Hz. */ + uint32_t freq; + /** Minimum frequency divider. */ + uint16_t div; +}; + +/** Serial Wire Output (SWO) speed information. */ +struct jaylink_swo_speed { + /** Base frequency in Hz. */ + uint32_t freq; + /** Minimum frequency divider. */ + uint32_t min_div; + /** Maximum frequency divider. */ + uint32_t max_div; + /** Minimum prescaler. */ + uint32_t min_prescaler; + /** Maximum prescaler. */ + uint32_t max_prescaler; +}; + +/** Device hardware version. */ +struct jaylink_hardware_version { + /** Hardware type. */ + enum jaylink_hardware_type type; + /** Major version. */ + uint8_t major; + /** Minor version. */ + uint8_t minor; + /** Revision number. */ + uint8_t revision; +}; + +/** Device hardware status. */ +struct jaylink_hardware_status { + /** Target reference voltage in mV. */ + uint16_t target_voltage; + /** TCK pin state. */ + bool tck; + /** TDI pin state. */ + bool tdi; + /** TDO pin state. */ + bool tdo; + /** TMS pin state. */ + bool tms; + /** TRES pin state. */ + bool tres; + /** TRST pin state. */ + bool trst; +}; + +/** Device connection. */ +struct jaylink_connection { + /** Handle. */ + uint16_t handle; + /** + * Process ID (PID). + * + * Identification of the client process. Usually this is the + * Process ID (PID) of the client process in an arbitrary format. + */ + uint32_t pid; + /** + * Host ID (HID). + * + * IPv4 address string of the client in quad-dotted decimal format + * (e.g. 192.0.2.235). The address 0.0.0.0 should be used for the + * registration of an USB connection. + */ + char hid[INET_ADDRSTRLEN]; + /** IID. */ + uint8_t iid; + /** CID. */ + uint8_t cid; + /** + * Timestamp of the last registration in milliseconds. + * + * The timestamp is relative to the time the device was powered up. + */ + uint32_t timestamp; +}; + +/** Target interface speed value for adaptive clocking. */ +#define JAYLINK_SPEED_ADAPTIVE_CLOCKING 0xffff + +/** Size of the device configuration data in bytes. */ +#define JAYLINK_DEV_CONFIG_SIZE 256 + +/** Number of bytes required to store device capabilities. */ +#define JAYLINK_DEV_CAPS_SIZE 4 + +/** Number of bytes required to store extended device capabilities. */ +#define JAYLINK_DEV_EXT_CAPS_SIZE 32 + +/** Maximum number of connections that can be registered on a device. */ +#define JAYLINK_MAX_CONNECTIONS 16 + +/** Media Access Control (MAC) address length in bytes. */ +#define JAYLINK_MAC_ADDRESS_LENGTH 6 + +/** + * Maximum length of a device's nickname including trailing null-terminator in + * bytes. + */ +#define JAYLINK_NICKNAME_MAX_LENGTH 32 + +/** + * Maximum length of a device's product name including trailing null-terminator + * in bytes. + */ +#define JAYLINK_PRODUCT_NAME_MAX_LENGTH 32 + +/** Maximum length of a filename in bytes. */ +#define JAYLINK_FILE_NAME_MAX_LENGTH 255 + +/** Maximum transfer size for a file in bytes. */ +#define JAYLINK_FILE_MAX_TRANSFER_SIZE 0x100000 + +/** + * EMUCOM channel with the system time of the device in milliseconds. + * + * The channel is read-only and the time is encoded in 4 bytes. The byte order + * is little-endian. + */ +#define JAYLINK_EMUCOM_CHANNEL_TIME 0x0 + +/** + * Offset of EMUCOM user channels. + * + * User channels are available to implement vendor and/or device specific + * functionalities. All channels below are reserved. + */ +#define JAYLINK_EMUCOM_CHANNEL_USER 0x10000 + +/** + * @struct jaylink_context + * + * Opaque structure representing a libjaylink context. + */ +struct jaylink_context; + +/** + * @struct jaylink_device + * + * Opaque structure representing a device. + */ +struct jaylink_device; + +/** + * @struct jaylink_device_handle + * + * Opaque structure representing a handle of a device. + */ +struct jaylink_device_handle; + +/** Macro to mark public libjaylink API symbol. */ +#ifdef _WIN32 +#define JAYLINK_API +#else +#define JAYLINK_API __attribute__ ((visibility ("default"))) +#endif + +/** + * Log callback function type. + * + * @param[in] ctx libjaylink context. + * @param[in] level Log level. + * @param[in] format Message format in printf()-style. + * @param[in] args Message arguments. + * @param[in,out] user_data User data passed to the callback function. + * + * @return Number of characters printed on success, or a negative error code on + * failure. + */ +typedef int (*jaylink_log_callback)(const struct jaylink_context *ctx, + enum jaylink_log_level level, const char *format, va_list args, + void *user_data); + +/*--- core.c ----------------------------------------------------------------*/ + +JAYLINK_API int jaylink_init(struct jaylink_context **ctx); +JAYLINK_API int jaylink_exit(struct jaylink_context *ctx); +JAYLINK_API bool jaylink_library_has_cap(enum jaylink_capability cap); + +/*--- device.c --------------------------------------------------------------*/ + +JAYLINK_API int jaylink_get_devices(struct jaylink_context *ctx, + struct jaylink_device ***devs, size_t *count); +JAYLINK_API void jaylink_free_devices(struct jaylink_device **devs, + bool unref); +JAYLINK_API int jaylink_device_get_host_interface( + const struct jaylink_device *dev, + enum jaylink_host_interface *iface); +JAYLINK_API int jaylink_device_get_serial_number( + const struct jaylink_device *dev, uint32_t *serial_number); +JAYLINK_API int jaylink_device_get_usb_address( + const struct jaylink_device *dev, + enum jaylink_usb_address *address); +JAYLINK_API int jaylink_device_get_ipv4_address( + const struct jaylink_device *dev, char *address); +JAYLINK_API int jaylink_device_get_mac_address( + const struct jaylink_device *dev, uint8_t *address); +JAYLINK_API int jaylink_device_get_hardware_version( + const struct jaylink_device *dev, + struct jaylink_hardware_version *version); +JAYLINK_API int jaylink_device_get_product_name( + const struct jaylink_device *dev, char *name); +JAYLINK_API int jaylink_device_get_nickname(const struct jaylink_device *dev, + char *nickname); +JAYLINK_API struct jaylink_device *jaylink_ref_device( + struct jaylink_device *dev); +JAYLINK_API void jaylink_unref_device(struct jaylink_device *dev); +JAYLINK_API int jaylink_open(struct jaylink_device *dev, + struct jaylink_device_handle **devh); +JAYLINK_API int jaylink_close(struct jaylink_device_handle *devh); +JAYLINK_API struct jaylink_device *jaylink_get_device( + struct jaylink_device_handle *devh); +JAYLINK_API int jaylink_get_firmware_version( + struct jaylink_device_handle *devh, char **version, + size_t *length); +JAYLINK_API int jaylink_get_hardware_info(struct jaylink_device_handle *devh, + uint32_t mask, uint32_t *info); +JAYLINK_API int jaylink_get_counters(struct jaylink_device_handle *devh, + uint32_t mask, uint32_t *values); +JAYLINK_API int jaylink_get_hardware_version( + struct jaylink_device_handle *devh, + struct jaylink_hardware_version *version); +JAYLINK_API int jaylink_get_hardware_status(struct jaylink_device_handle *devh, + struct jaylink_hardware_status *status); +JAYLINK_API int jaylink_get_caps(struct jaylink_device_handle *devh, + uint8_t *caps); +JAYLINK_API int jaylink_get_extended_caps(struct jaylink_device_handle *devh, + uint8_t *caps); +JAYLINK_API int jaylink_get_free_memory(struct jaylink_device_handle *devh, + uint32_t *size); +JAYLINK_API int jaylink_read_raw_config(struct jaylink_device_handle *devh, + uint8_t *config); +JAYLINK_API int jaylink_write_raw_config(struct jaylink_device_handle *devh, + const uint8_t *config); +JAYLINK_API int jaylink_register(struct jaylink_device_handle *devh, + struct jaylink_connection *connection, + struct jaylink_connection *connections, size_t *count); +JAYLINK_API int jaylink_unregister(struct jaylink_device_handle *devh, + const struct jaylink_connection *connection, + struct jaylink_connection *connections, size_t *count); + +/*--- discovery.c -----------------------------------------------------------*/ + +JAYLINK_API int jaylink_discovery_scan(struct jaylink_context *ctx, + uint32_t ifaces); + +/*--- emucom.c --------------------------------------------------------------*/ + +JAYLINK_API int jaylink_emucom_read(struct jaylink_device_handle *devh, + uint32_t channel, uint8_t *buffer, uint32_t *length); +JAYLINK_API int jaylink_emucom_write(struct jaylink_device_handle *devh, + uint32_t channel, const uint8_t *buffer, uint32_t *length); + +/*--- error.c ---------------------------------------------------------------*/ + +JAYLINK_API const char *jaylink_strerror(int error_code); +JAYLINK_API const char *jaylink_strerror_name(int error_code); + +/*--- fileio.c --------------------------------------------------------------*/ + +JAYLINK_API int jaylink_file_read(struct jaylink_device_handle *devh, + const char *filename, uint8_t *buffer, uint32_t offset, + uint32_t *length); +JAYLINK_API int jaylink_file_write(struct jaylink_device_handle *devh, + const char *filename, const uint8_t *buffer, uint32_t offset, + uint32_t *length); +JAYLINK_API int jaylink_file_get_size(struct jaylink_device_handle *devh, + const char *filename, uint32_t *size); +JAYLINK_API int jaylink_file_delete(struct jaylink_device_handle *devh, + const char *filename); + +/*--- jtag.c ----------------------------------------------------------------*/ + +JAYLINK_API int jaylink_jtag_io(struct jaylink_device_handle *devh, + const uint8_t *tms, const uint8_t *tdi, uint8_t *tdo, + uint16_t length, enum jaylink_jtag_version version); +JAYLINK_API int jaylink_jtag_clear_trst(struct jaylink_device_handle *devh); +JAYLINK_API int jaylink_jtag_set_trst(struct jaylink_device_handle *devh); + +/*--- log.c -----------------------------------------------------------------*/ + +JAYLINK_API int jaylink_log_set_level(struct jaylink_context *ctx, + enum jaylink_log_level level); +JAYLINK_API int jaylink_log_get_level(const struct jaylink_context *ctx, + enum jaylink_log_level *level); +JAYLINK_API int jaylink_log_set_callback(struct jaylink_context *ctx, + jaylink_log_callback callback, void *user_data); +JAYLINK_API int jaylink_log_set_domain(struct jaylink_context *ctx, + const char *domain); +JAYLINK_API const char *jaylink_log_get_domain( + const struct jaylink_context *ctx); + +/*--- strutil.c -------------------------------------------------------------*/ + +JAYLINK_API int jaylink_parse_serial_number(const char *str, + uint32_t *serial_number); + +/*--- swd.c -----------------------------------------------------------------*/ + +JAYLINK_API int jaylink_swd_io(struct jaylink_device_handle *devh, + const uint8_t *direction, const uint8_t *out, uint8_t *in, + uint16_t length); + +/*--- swo.c -----------------------------------------------------------------*/ + +JAYLINK_API int jaylink_swo_start(struct jaylink_device_handle *devh, + enum jaylink_swo_mode mode, uint32_t baudrate, uint32_t size); +JAYLINK_API int jaylink_swo_stop(struct jaylink_device_handle *devh); +JAYLINK_API int jaylink_swo_read(struct jaylink_device_handle *devh, + uint8_t *buffer, uint32_t *length); +JAYLINK_API int jaylink_swo_get_speeds(struct jaylink_device_handle *devh, + enum jaylink_swo_mode mode, struct jaylink_swo_speed *speed); + +/*--- target.c --------------------------------------------------------------*/ + +JAYLINK_API int jaylink_set_speed(struct jaylink_device_handle *devh, + uint16_t speed); +JAYLINK_API int jaylink_get_speeds(struct jaylink_device_handle *devh, + struct jaylink_speed *speed); +JAYLINK_API int jaylink_select_interface(struct jaylink_device_handle *devh, + enum jaylink_target_interface iface, + enum jaylink_target_interface *prev_iface); +JAYLINK_API int jaylink_get_available_interfaces( + struct jaylink_device_handle *devh, uint32_t *ifaces); +JAYLINK_API int jaylink_get_selected_interface( + struct jaylink_device_handle *devh, + enum jaylink_target_interface *iface); +JAYLINK_API int jaylink_clear_reset(struct jaylink_device_handle *devh); +JAYLINK_API int jaylink_set_reset(struct jaylink_device_handle *devh); +JAYLINK_API int jaylink_set_target_power(struct jaylink_device_handle *devh, + bool enable); + +/*--- util.c ----------------------------------------------------------------*/ + +JAYLINK_API bool jaylink_has_cap(const uint8_t *caps, uint32_t cap); + +/*--- version.c -------------------------------------------------------------*/ + +JAYLINK_API int jaylink_version_package_get_major(void); +JAYLINK_API int jaylink_version_package_get_minor(void); +JAYLINK_API int jaylink_version_package_get_micro(void); +JAYLINK_API const char *jaylink_version_package_get_string(void); +JAYLINK_API int jaylink_version_library_get_current(void); +JAYLINK_API int jaylink_version_library_get_revision(void); +JAYLINK_API int jaylink_version_library_get_age(void); +JAYLINK_API const char *jaylink_version_library_get_string(void); + +#include "version.h" + +#endif /* LIBJAYLINK_LIBJAYLINK_H */ diff --git a/src/jtag/drivers/libjaylink/libjaylink/list.c b/src/jtag/drivers/libjaylink/libjaylink/list.c new file mode 100644 index 0000000..7c54e50 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/list.c @@ -0,0 +1,115 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-2016 Marc Schink <jaylink-dev@marcschink.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> + +#include "libjaylink-internal.h" + +/** + * @file + * + * Singly-linked list functions. + */ + +/** @private */ +JAYLINK_PRIV struct list *list_prepend(struct list *list, void *data) +{ + struct list *item; + + item = malloc(sizeof(struct list)); + + if (!item) + return NULL; + + item->data = data; + item->next = list; + + return item; +} + +/** @private */ +JAYLINK_PRIV struct list *list_remove(struct list *list, const void *data) +{ + struct list *item; + struct list *tmp; + + if (!list) + return NULL; + + item = list; + + if (item->data == data) { + tmp = item->next; + free(item); + return tmp; + } + + while (item->next) { + if (item->next->data == data) { + tmp = item->next; + item->next = item->next->next; + free(tmp); + break; + } + + item = item->next; + } + + return list; +} + +/** @private */ +JAYLINK_PRIV struct list *list_find_custom(struct list *list, + list_compare_callback callback, const void *user_data) +{ + if (!callback) + return NULL; + + while (list) { + if (callback(list->data, user_data)) + return list; + + list = list->next; + } + + return NULL; +} + +/** @private */ +JAYLINK_PRIV size_t list_length(struct list *list) +{ + size_t length; + + for (length = 0; list; length++) + list = list->next; + + return length; +} + +/** @private */ +JAYLINK_PRIV void list_free(struct list *list) +{ + struct list *tmp; + + while (list) { + tmp = list; + list = list->next; + free(tmp); + } +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/log.c b/src/jtag/drivers/libjaylink/libjaylink/log.c new file mode 100644 index 0000000..07ef172 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/log.c @@ -0,0 +1,266 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-2015 Marc Schink <jaylink-dev@marcschink.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdarg.h> + +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/** + * @file + * + * Logging functions. + */ + +/** + * Set the libjaylink log level. + * + * @param[in,out] ctx libjaylink context. + * @param[in] level Log level to set. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_log_set_level(struct jaylink_context *ctx, + enum jaylink_log_level level) +{ + if (!ctx) + return JAYLINK_ERR_ARG; + + if (level > JAYLINK_LOG_LEVEL_DEBUG_IO) + return JAYLINK_ERR_ARG; + + ctx->log_level = level; + + return JAYLINK_OK; +} + +/** + * Get the libjaylink log level. + * + * @param[in] ctx libjaylink context. + * @param[out] level Log level on success, and undefined on failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_log_get_level(const struct jaylink_context *ctx, + enum jaylink_log_level *level) +{ + if (!ctx || !level) + return JAYLINK_ERR_ARG; + + *level = ctx->log_level; + + return JAYLINK_OK; +} + +/** + * Set the libjaylink log callback function. + * + * @param[in,out] ctx libjaylink context. + * @param[in] callback Callback function to use, or NULL to use the default log + * function. + * @param[in] user_data User data to be passed to the callback function. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_log_set_callback(struct jaylink_context *ctx, + jaylink_log_callback callback, void *user_data) +{ + if (!ctx) + return JAYLINK_ERR_ARG; + + if (callback) { + ctx->log_callback = callback; + ctx->log_callback_data = user_data; + } else { + ctx->log_callback = &log_vprintf; + ctx->log_callback_data = NULL; + } + + return JAYLINK_OK; +} + +/** + * Set the libjaylink log domain. + * + * The log domain is a string which is used as prefix for all log messages to + * differentiate them from messages of other libraries. + * + * The maximum length of the log domain is #JAYLINK_LOG_DOMAIN_MAX_LENGTH + * bytes, excluding the trailing null-terminator. A log domain which exceeds + * this length will be silently truncated. + * + * @param[in,out] ctx libjaylink context. + * @param[in] domain Log domain to use. To set the default log domain, use + * #JAYLINK_LOG_DOMAIN_DEFAULT. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_log_set_domain(struct jaylink_context *ctx, + const char *domain) +{ + int ret; + + if (!ctx || !domain) + return JAYLINK_ERR_ARG; + + ret = snprintf(ctx->log_domain, JAYLINK_LOG_DOMAIN_MAX_LENGTH + 1, + "%s", domain); + + if (ret < 0) + return JAYLINK_ERR; + + return JAYLINK_OK; +} + +/** + * Get the libjaylink log domain. + * + * @param[in] ctx libjaylink context. + * + * @return A string which contains the current log domain on success, or NULL + * on failure. The string is null-terminated and must not be free'd by + * the caller. + * + * @since 0.1.0 + */ +JAYLINK_API const char *jaylink_log_get_domain( + const struct jaylink_context *ctx) +{ + if (!ctx) + return NULL; + + return ctx->log_domain; +} + +/** @private */ +JAYLINK_PRIV int log_vprintf(const struct jaylink_context *ctx, + enum jaylink_log_level level, const char *format, va_list args, + void *user_data) +{ + (void)user_data; + + /* + * Filter out messages with higher verbosity than the verbosity of the + * current log level. + */ + if (level > ctx->log_level) + return 0; + + if (ctx->log_domain[0] != '\0') + fprintf(stderr, "%s", ctx->log_domain); + + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + + return 0; +} + +/** @private */ +JAYLINK_PRIV void log_err(const struct jaylink_context *ctx, + const char *format, ...) +{ + va_list args; + + if (!ctx) + return; + + va_start(args, format); + ctx->log_callback(ctx, JAYLINK_LOG_LEVEL_ERROR, format, args, + ctx->log_callback_data); + va_end(args); +} + +/** @private */ +JAYLINK_PRIV void log_warn(const struct jaylink_context *ctx, + const char *format, ...) +{ + va_list args; + + if (!ctx) + return; + + va_start(args, format); + ctx->log_callback(ctx, JAYLINK_LOG_LEVEL_WARNING, format, args, + ctx->log_callback_data); + va_end(args); +} + +/** @private */ +JAYLINK_PRIV void log_info(const struct jaylink_context *ctx, + const char *format, ...) +{ + va_list args; + + if (!ctx) + return; + + va_start(args, format); + ctx->log_callback(ctx, JAYLINK_LOG_LEVEL_INFO, format, args, + ctx->log_callback_data); + va_end(args); +} + +/** @private */ +JAYLINK_PRIV void log_dbg(const struct jaylink_context *ctx, + const char *format, ...) +{ + va_list args; + + if (!ctx) + return; + + va_start(args, format); + ctx->log_callback(ctx, JAYLINK_LOG_LEVEL_DEBUG, format, args, + ctx->log_callback_data); + va_end(args); +} + +/** @private */ +JAYLINK_PRIV void log_dbgio(const struct jaylink_context *ctx, + const char *format, ...) +{ + va_list args; + + if (!ctx) + return; + + va_start(args, format); + ctx->log_callback(ctx, JAYLINK_LOG_LEVEL_DEBUG_IO, format, args, + ctx->log_callback_data); + va_end(args); +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/socket.c b/src/jtag/drivers/libjaylink/libjaylink/socket.c new file mode 100644 index 0000000..f2a6588 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/socket.c @@ -0,0 +1,257 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2016-2017 Marc Schink <jaylink-dev@marcschink.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef _WIN32 +#include <winsock2.h> +#else +#include <sys/types.h> +#include <sys/socket.h> +#include <unistd.h> +#endif + +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/** + * @file + * + * Socket abstraction layer. + */ + +/** + * Close a socket. + * + * @param[in] sock Socket descriptor. + * + * @return Whether the socket was successfully closed. + */ +JAYLINK_PRIV bool socket_close(int sock) +{ + int ret; + +#ifdef _WIN32 + ret = closesocket(sock); +#else + ret = close(sock); +#endif + + if (!ret) + return true; + + return false; +} + +/** + * Bind an address to a socket. + * + * @param[in] sock Socket descriptor. + * @param[in] address Address to be bound to the socket. + * @param[in] length Length of the structure pointed to by @p address in bytes. + * + * @return Whether the address was successfully assigned to the socket. + */ +JAYLINK_PRIV bool socket_bind(int sock, const struct sockaddr *address, + size_t length) +{ + int ret; + + ret = bind(sock, address, length); + +#ifdef _WIN32 + if (ret == SOCKET_ERROR) + return false; +#else + if (ret < 0) + return false; +#endif + + return true; +} + +/** + * Send a message on a socket. + * + * @param[in] sock Socket descriptor. + * @param[in] buffer Buffer of the message to be sent. + * @param[in,out] length Length of the message in bytes. On success, the value + * gets updated with the actual number of bytes sent. The + * value is undefined on failure. + * @param[in] flags Flags to modify the function behaviour. Use bitwise OR to + * specify multiple flags. + * + * @return Whether the message was sent successfully. + */ +JAYLINK_PRIV bool socket_send(int sock, const void *buffer, size_t *length, + int flags) +{ + ssize_t ret; + + ret = send(sock, buffer, *length, flags); +#ifdef _WIN32 + if (ret == SOCKET_ERROR) + return false; +#else + if (ret < 0) + return false; +#endif + *length = ret; + + return true; +} + +/** + * Receive a message from a socket. + * + * @param[in] sock Socket descriptor. + * @param[out] buffer Buffer to store the received message on success. Its + * content is undefined on failure. + * @param[in,out] length Maximum length of the message in bytes. On success, + * the value gets updated with the actual number of + * received bytes. The value is undefined on failure. + * @param[in] flags Flags to modify the function behaviour. Use bitwise OR to + * specify multiple flags. + * + * @return Whether a message was successfully received. + */ +JAYLINK_PRIV bool socket_recv(int sock, void *buffer, size_t *length, + int flags) +{ + ssize_t ret; + + ret = recv(sock, buffer, *length, flags); + +#ifdef _WIN32 + if (ret == SOCKET_ERROR) + return false; +#else + if (ret < 0) + return false; +#endif + + *length = ret; + + return true; +} + +/** + * Send a message on a socket. + * + * @param[in] sock Socket descriptor. + * @param[in] buffer Buffer to send message from. + * @param[in,out] length Number of bytes to send. On success, the value gets + * updated with the actual number of bytes sent. The + * value is undefined on failure. + * @param[in] flags Flags to modify the function behaviour. Use bitwise OR to + * specify multiple flags. + * @param[in] address Destination address of the message. + * @param[in] address_length Length of the structure pointed to by @p address + * in bytes. + * + * @return Whether the message was successfully sent. + */ +JAYLINK_PRIV bool socket_sendto(int sock, const void *buffer, size_t *length, + int flags, const struct sockaddr *address, + size_t address_length) +{ + ssize_t ret; + + ret = sendto(sock, buffer, *length, flags, address, address_length); + +#ifdef _WIN32 + if (ret == SOCKET_ERROR) + return false; +#else + if (ret < 0) + return false; +#endif + + *length = ret; + + return true; +} + +/** + * Receive a message from a socket. + * + * @param[in] sock Socket descriptor. + * @param[out] buffer Buffer to store the received message on success. Its + * content is undefined on failure. + * @param[in,out] length Maximum length of the message in bytes. On success, + * the value gets updated with the actual number of + * received bytes. The value is undefined on failure. + * @param[in] flags Flags to modify the function behaviour. Use bitwise OR to + * specify multiple flags. + * @param[out] address Structure to store the source address of the message on + * success. Its content is undefined on failure. + * Can be NULL. + * @param[in,out] address_length Length of the structure pointed to by + * @p address in bytes. On success, the value + * gets updated with the actual length of the + * structure. The value is undefined on failure. + * Should be NULL if @p address is NULL. + * + * @return Whether a message was successfully received. + */ +JAYLINK_PRIV bool socket_recvfrom(int sock, void *buffer, size_t *length, + int flags, struct sockaddr *address, size_t *address_length) +{ + ssize_t ret; +#ifdef _WIN32 + int tmp; + + tmp = *address_length; + ret = recvfrom(sock, buffer, *length, flags, address, &tmp); + + if (ret == SOCKET_ERROR) + return false; +#else + socklen_t tmp; + + tmp = *address_length; + ret = recvfrom(sock, buffer, *length, flags, address, &tmp); + + if (ret < 0) + return false; +#endif + + *address_length = tmp; + *length = ret; + + return true; +} + +/** + * Set an option on a socket. + * + * @param[in] sock Socket descriptor. + * @param[in] level Level at which the option is defined. + * @param[in] option Option to set the value for. + * @param[in] value Buffer of the value to be set. + * @param[in] length Length of the value buffer in bytes. + * + * @return Whether the option was set successfully. + */ +JAYLINK_PRIV bool socket_set_option(int sock, int level, int option, + const void *value, size_t length) +{ + if (!setsockopt(sock, level, option, value, length)) + return true; + + return false; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/strutil.c b/src/jtag/drivers/libjaylink/libjaylink/strutil.c new file mode 100644 index 0000000..283ed17 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/strutil.c @@ -0,0 +1,66 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2016 Marc Schink <jaylink-dev@marcschink.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <errno.h> + +#include "libjaylink.h" + +/** + * @file + * + * String utility functions. + */ + +/** + * Convert a string representation of a serial number to an integer. + * + * The string representation of the serial number must be in decimal form. + * + * @param[in] str String representation to convert. + * @param[out] serial_number Serial number on success, and undefined on + * failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR Conversion error. Serial number is invalid or string + * representation contains invalid character(s). + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_parse_serial_number(const char *str, + uint32_t *serial_number) +{ + char *end_ptr; + unsigned long long tmp; + + if (!str || !serial_number) + return JAYLINK_ERR_ARG; + + errno = 0; + tmp = strtoull(str, &end_ptr, 10); + + if (*end_ptr != '\0' || errno != 0 || tmp > UINT32_MAX) + return JAYLINK_ERR; + + *serial_number = tmp; + + return JAYLINK_OK; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/swd.c b/src/jtag/drivers/libjaylink/libjaylink/swd.c new file mode 100644 index 0000000..29265b7 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/swd.c @@ -0,0 +1,148 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-2015 Marc Schink <jaylink-dev@marcschink.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdint.h> +#include <stdbool.h> + +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/** + * @file + * + * Serial Wire Debug (SWD) functions. + */ + +/** @cond PRIVATE */ +#define CMD_SWD_IO 0xcf + +/** + * Error code indicating that there is not enough free memory on the device to + * perform the SWD I/O operation. + */ +#define SWD_IO_ERR_NO_MEMORY 0x06 +/** @endcond */ + +/** + * Perform a SWD I/O operation. + * + * @note This function must only be used if the #JAYLINK_TIF_SWD interface is + * available and selected. + * + * @param[in,out] devh Device handle. + * @param[in] direction Buffer to read the transfer direction from. + * @param[in] out Buffer to read host-to-target data from. + * @param[out] in Buffer to store target-to-host data on success. Its content + * is undefined on failure. The buffer must be large enough to + * contain at least the specified number of bits to transfer. + * @param[in] length Total number of bits to transfer from host to target and + * vice versa. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR_DEV_NO_MEMORY Not enough memory on the device to perform + * the operation. + * @retval JAYLINK_ERR_DEV Unspecified device error. + * @retval JAYLINK_ERR Other error conditions. + * + * @see jaylink_select_interface() + * @see jaylink_set_speed() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_swd_io(struct jaylink_device_handle *devh, + const uint8_t *direction, const uint8_t *out, uint8_t *in, + uint16_t length) +{ + int ret; + struct jaylink_context *ctx; + uint16_t num_bytes; + uint8_t buf[4]; + uint8_t status; + + if (!devh || !direction || !out || !in || !length) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + num_bytes = (length + 7) / 8; + + ret = transport_start_write_read(devh, 4 + 2 * num_bytes, + num_bytes + 1, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_SWD_IO; + buf[1] = 0x00; + buffer_set_u16(buf, length, 2); + + ret = transport_write(devh, buf, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_write(devh, direction, num_bytes); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_write(devh, out, num_bytes); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, in, num_bytes); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, &status, 1); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + if (status == SWD_IO_ERR_NO_MEMORY) { + return JAYLINK_ERR_DEV_NO_MEMORY; + } else if (status > 0) { + log_err(ctx, "SWD I/O operation failed: 0x%x.", status); + return JAYLINK_ERR_DEV; + } + + return JAYLINK_OK; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/swo.c b/src/jtag/drivers/libjaylink/libjaylink/swo.c new file mode 100644 index 0000000..6037f64 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/swo.c @@ -0,0 +1,453 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2015 Marc Schink <jaylink-dev@marcschink.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdint.h> +#include <stdbool.h> + +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/** + * @file + * + * Serial Wire Output (SWO) functions. + */ + +/** @cond PRIVATE */ +#define CMD_SWO 0xeb + +#define SWO_CMD_START 0x64 +#define SWO_CMD_STOP 0x65 +#define SWO_CMD_READ 0x66 +#define SWO_CMD_GET_SPEEDS 0x6e + +#define SWO_PARAM_MODE 0x01 +#define SWO_PARAM_BAUDRATE 0x02 +#define SWO_PARAM_READ_SIZE 0x03 +#define SWO_PARAM_BUFFER_SIZE 0x04 + +#define SWO_ERR 0x80000000 +/** @endcond */ + +/** + * Start SWO capture. + * + * @note This function must be used only if the device has the + * #JAYLINK_DEV_CAP_SWO capability. + * + * @param[in,out] devh Device handle. + * @param[in] mode Mode to capture data with. + * @param[in] baudrate Baudrate to capture data in bit per second. + * @param[in] size Device internal buffer size in bytes to use for capturing. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR_DEV Unspecified device error. + * @retval JAYLINK_ERR Other error conditions. + * + * @see jaylink_swo_get_speeds() + * @see jaylink_get_free_memory() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_swo_start(struct jaylink_device_handle *devh, + enum jaylink_swo_mode mode, uint32_t baudrate, uint32_t size) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[32]; + uint32_t status; + + if (!devh || !baudrate || !size) + return JAYLINK_ERR_ARG; + + if (mode != JAYLINK_SWO_MODE_UART) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 21, 4, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_SWO; + buf[1] = SWO_CMD_START; + + buf[2] = 0x04; + buf[3] = SWO_PARAM_MODE; + buffer_set_u32(buf, mode, 4); + + buf[8] = 0x04; + buf[9] = SWO_PARAM_BAUDRATE; + buffer_set_u32(buf, baudrate, 10); + + buf[14] = 0x04; + buf[15] = SWO_PARAM_BUFFER_SIZE; + buffer_set_u32(buf, size, 16); + + buf[20] = 0x00; + + ret = transport_write(devh, buf, 21); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + status = buffer_get_u32(buf, 0); + + if (status > 0) { + log_err(ctx, "Failed to start capture: 0x%x.", status); + return JAYLINK_ERR_DEV; + } + + return JAYLINK_OK; +} + +/** + * Stop SWO capture. + * + * @note This function must be used only if the device has the + * #JAYLINK_DEV_CAP_SWO capability. + * + * @param[in,out] devh Device handle. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR_DEV Unspecified device error. + * @retval JAYLINK_ERR Other error conditions. + * + * @see jaylink_swo_start() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_swo_stop(struct jaylink_device_handle *devh) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[4]; + uint32_t status; + + if (!devh) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 3, 4, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_SWO; + buf[1] = SWO_CMD_STOP; + buf[2] = 0x00; + + ret = transport_write(devh, buf, 3); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + status = buffer_get_u32(buf, 0); + + if (status > 0) { + log_err(ctx, "Failed to stop capture: 0x%x.", status); + return JAYLINK_ERR_DEV; + } + + return JAYLINK_OK; +} + +/** + * Read SWO trace data. + * + * @note This function must be used only if the device has the + * #JAYLINK_DEV_CAP_SWO capability. + * + * @param[in,out] devh Device handle. + * @param[out] buffer Buffer to store trace data on success. Its content is + * undefined on failure. + * @param[in,out] length Maximum number of bytes to read. On success, the value + * gets updated with the actual number of bytes read. The + * value is undefined on failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_PROTO Protocol violation. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR_DEV Unspecified device error. + * @retval JAYLINK_ERR Other error conditions. + * + * @see jaylink_swo_start() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_swo_read(struct jaylink_device_handle *devh, + uint8_t *buffer, uint32_t *length) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[32]; + uint32_t status; + uint32_t tmp; + + if (!devh || !buffer || !length) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 9, 8, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_SWO; + buf[1] = SWO_CMD_READ; + + buf[2] = 0x04; + buf[3] = SWO_PARAM_READ_SIZE; + buffer_set_u32(buf, *length, 4); + + buf[8] = 0x00; + + ret = transport_write(devh, buf, 9); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, 8); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + status = buffer_get_u32(buf, 0); + tmp = buffer_get_u32(buf, 4); + + if (tmp > *length) { + log_err(ctx, "Received %u bytes but only %u bytes were " + "requested.", tmp, *length); + return JAYLINK_ERR_PROTO; + } + + *length = tmp; + + if (tmp > 0) { + ret = transport_start_read(devh, tmp); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buffer, tmp); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + } + + if (status > 0) { + log_err(ctx, "Failed to read data: 0x%x.", status); + return JAYLINK_ERR_DEV; + } + + return JAYLINK_OK; +} + +/** + * Retrieve SWO speeds. + + * The speeds are calculated as follows: + * + * @par + * <tt>speeds = @a freq / n</tt> with <tt>n >= @a min_div</tt> and + * <tt>n <= @a max_div</tt>, where @p n is an integer + * + * Assuming, for example, a base frequency @a freq of 4500 kHz, a minimum + * divider @a min_div of 1 and a maximum divider @a max_div of 8 then the + * highest possible SWO speed is 4500 kHz / 1 = 4500 kHz. The next highest + * speed is 2250 kHz for a divider of 2, and so on. Accordingly, the lowest + * possible speed is 4500 kHz / 8 = 562.5 kHz. + * + * @note This function must be used only if the device has the + * #JAYLINK_DEV_CAP_SWO capability. + * + * @param[in,out] devh Device handle. + * @param[in] mode Capture mode to retrieve speeds for. + * @param[out] speed Speed information on success, and undefined on failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_PROTO Protocol violation. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR_DEV Unspecified device error. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_swo_get_speeds(struct jaylink_device_handle *devh, + enum jaylink_swo_mode mode, struct jaylink_swo_speed *speed) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[24]; + uint32_t tmp; + uint32_t length; + + if (!devh || !speed) + return JAYLINK_ERR_ARG; + + if (mode != JAYLINK_SWO_MODE_UART) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 9, 4, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_SWO; + buf[1] = SWO_CMD_GET_SPEEDS; + + buf[2] = 0x04; + buf[3] = SWO_PARAM_MODE; + buffer_set_u32(buf, mode, 4); + + buf[8] = 0x00; + + ret = transport_write(devh, buf, 9); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + tmp = buffer_get_u32(buf, 0); + + if (tmp & SWO_ERR) { + log_err(ctx, "Failed to retrieve speed information: 0x%x.", + tmp); + return JAYLINK_ERR_DEV; + } + + length = tmp; + + if (length != 28) { + log_err(ctx, "Unexpected number of bytes received: %u.", + length); + return JAYLINK_ERR_PROTO; + } + + length = length - 4; + ret = transport_start_read(devh, length); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, length); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + speed->freq = buffer_get_u32(buf, 4); + speed->min_div = buffer_get_u32(buf, 8); + + if (!speed->min_div) { + log_err(ctx, "Minimum frequency divider is zero."); + return JAYLINK_ERR_PROTO; + } + + speed->max_div = buffer_get_u32(buf, 12); + + if (speed->max_div < speed->min_div) { + log_err(ctx, "Maximum frequency divider is less than minimum " + "frequency divider."); + return JAYLINK_ERR_PROTO; + } + + speed->min_prescaler = buffer_get_u32(buf, 16); + speed->max_prescaler = buffer_get_u32(buf, 20); + + if (speed->max_prescaler < speed->min_prescaler) { + log_err(ctx, "Maximum prescaler is less than minimum " + "prescaler."); + return JAYLINK_ERR_PROTO; + } + + return JAYLINK_OK; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/target.c b/src/jtag/drivers/libjaylink/libjaylink/target.c new file mode 100644 index 0000000..264335b --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/target.c @@ -0,0 +1,533 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-2015 Marc Schink <jaylink-dev@marcschink.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdint.h> +#include <stdbool.h> + +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/** + * @file + * + * Target related functions. + */ + +/** @cond PRIVATE */ +#define CMD_SET_SPEED 0x05 +#define CMD_SET_TARGET_POWER 0x08 +#define CMD_GET_SPEEDS 0xc0 +#define CMD_SELECT_TIF 0xc7 +#define CMD_CLEAR_RESET 0xdc +#define CMD_SET_RESET 0xdd + +#define TIF_GET_SELECTED 0xfe +#define TIF_GET_AVAILABLE 0xff +/** @endcond */ + +/** + * Set the target interface speed. + * + * @param[in,out] devh Device handle. + * @param[in] speed Speed in kHz or #JAYLINK_SPEED_ADAPTIVE_CLOCKING for + * adaptive clocking. Speed of 0 kHz is not allowed and + * adaptive clocking must only be used if the device has the + * #JAYLINK_DEV_CAP_ADAPTIVE_CLOCKING capability. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @see jaylink_get_speeds() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_set_speed(struct jaylink_device_handle *devh, + uint16_t speed) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[3]; + + if (!devh || !speed) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write(devh, 3, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_SET_SPEED; + buffer_set_u16(buf, speed, 1); + + ret = transport_write(devh, buf, 3); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + return JAYLINK_OK; +} + +/** + * Retrieve target interface speeds. + * + * The speeds are applicable for the currently selected target interface only + * and calculated as follows: + * + * @par + * <tt>speeds = @a freq / n</tt> with <tt>n >= @a div</tt>, where @p n is an + * integer + * + * Assuming, for example, a base frequency @a freq of 4 MHz and a minimum + * divider @a div of 4 then the highest possible target interface speed is + * 4 MHz / 4 = 1 MHz. The next highest speed is 800 kHz for a divider of 5, and + * so on. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_GET_SPEEDS capability. + * + * @param[in,out] devh Device handle. + * @param[out] speed Speed information on success, and undefined on failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_PROTO Protocol violation. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @see jaylink_select_interface() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_get_speeds(struct jaylink_device_handle *devh, + struct jaylink_speed *speed) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[6]; + uint16_t div; + + if (!devh || !speed) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 1, 6, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_GET_SPEEDS; + + ret = transport_write(devh, buf, 1); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, 6); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + div = buffer_get_u16(buf, 4); + + if (!div) { + log_err(ctx, "Minimum frequency divider is zero."); + return JAYLINK_ERR_PROTO; + } + + speed->freq = buffer_get_u32(buf, 0); + speed->div = div; + + return JAYLINK_OK; +} + +/** + * Select the target interface. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_SELECT_TIF capability. + * + * @warning This function may return a value for @p prev_iface which is not + * covered by #jaylink_target_interface. + * + * @param[in,out] devh Device handle. + * @param[in] iface Target interface to select. + * @param[out] prev_iface Previously selected target interface on success, and + * undefined on failure. Can be NULL. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @see jaylink_get_available_interfaces() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_select_interface(struct jaylink_device_handle *devh, + enum jaylink_target_interface iface, + enum jaylink_target_interface *prev_iface) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[4]; + + if (!devh) + return JAYLINK_ERR_ARG; + + switch (iface) { + case JAYLINK_TIF_JTAG: + case JAYLINK_TIF_SWD: + case JAYLINK_TIF_BDM3: + case JAYLINK_TIF_FINE: + case JAYLINK_TIF_2W_JTAG_PIC32: + break; + default: + return JAYLINK_ERR_ARG; + } + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 2, 4, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_SELECT_TIF; + buf[1] = iface; + + ret = transport_write(devh, buf, 2); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + if (prev_iface) + *prev_iface = buffer_get_u32(buf, 0); + + return JAYLINK_OK; +} + +/** + * Retrieve the available target interfaces. + * + * The target interfaces are stored in a 32-bit bit field where each individual + * bit represents a target interface. A set bit indicates an available target + * interface. See #jaylink_target_interface for a description of the target + * interfaces and their bit positions. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_SELECT_TIF capability. + * + * @param[in,out] devh Device handle. + * @param[out] ifaces Target interfaces on success, and undefined on failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @see jaylink_select_interface() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_get_available_interfaces( + struct jaylink_device_handle *devh, uint32_t *ifaces) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[4]; + + if (!devh || !ifaces) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 2, 4, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_SELECT_TIF; + buf[1] = TIF_GET_AVAILABLE; + + ret = transport_write(devh, buf, 2); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + *ifaces = buffer_get_u32(buf, 0); + + return JAYLINK_OK; +} + +/** + * Retrieve the selected target interface. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_SELECT_TIF capability. + * + * @warning This function may return a value for @p iface which is not covered + * by #jaylink_target_interface. + * + * @param[in,out] devh Device handle. + * @param[out] iface Selected target interface on success, and undefined on + * failure. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @see jaylink_select_interface() + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_get_selected_interface( + struct jaylink_device_handle *devh, + enum jaylink_target_interface *iface) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[4]; + + if (!devh || !iface) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write_read(devh, 2, 4, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_SELECT_TIF; + buf[1] = TIF_GET_SELECTED; + + ret = transport_write(devh, buf, 2); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + ret = transport_read(devh, buf, 4); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_read() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + *iface = buffer_get_u32(buf, 0); + + return JAYLINK_OK; +} + +/** + * Clear the target reset signal. + * + * @param[in,out] devh Device handle. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_clear_reset(struct jaylink_device_handle *devh) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[1]; + + if (!devh) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write(devh, 1, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_CLEAR_RESET; + + ret = transport_write(devh, buf, 1); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + return JAYLINK_OK; +} + +/** + * Set the target reset signal. + * + * @param[in,out] devh Device handle. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_set_reset(struct jaylink_device_handle *devh) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[1]; + + if (!devh) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write(devh, 1, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_SET_RESET; + + ret = transport_write(devh, buf, 1); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + return JAYLINK_OK; +} + +/** + * Set the target power supply. + * + * If enabled, the target is supplied with 5 V from pin 19 of the 20-pin + * JTAG / SWD connector. + * + * @note This function must only be used if the device has the + * #JAYLINK_DEV_CAP_SET_TARGET_POWER capability. + * + * @param[in,out] devh Device handle. + * @param[in] enable Determines whether to enable or disable the target power + * supply. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_set_target_power(struct jaylink_device_handle *devh, + bool enable) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[2]; + + if (!devh) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + ret = transport_start_write(devh, 2, true); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_start_wrte() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + buf[0] = CMD_SET_TARGET_POWER; + buf[1] = enable; + + ret = transport_write(devh, buf, 2); + + if (ret != JAYLINK_OK) { + log_err(ctx, "transport_write() failed: %s.", + jaylink_strerror(ret)); + return ret; + } + + return JAYLINK_OK; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/transport.c b/src/jtag/drivers/libjaylink/libjaylink/transport.c new file mode 100644 index 0000000..0c276b3 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/transport.c @@ -0,0 +1,309 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-2015 Marc Schink <jaylink-dev@marcschink.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/** + * @file + * + * Transport abstraction layer. + */ + +/** + * Open a device. + * + * This function must be called before any other function of the transport + * abstraction layer for the given device handle is called. + * + * @param[in,out] devh Device handle. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + */ +JAYLINK_PRIV int transport_open(struct jaylink_device_handle *devh) +{ + int ret; + + switch (devh->dev->iface) { +#ifdef HAVE_LIBUSB + case JAYLINK_HIF_USB: + ret = transport_usb_open(devh); + break; +#endif + case JAYLINK_HIF_TCP: + ret = transport_tcp_open(devh); + break; + default: + log_err(devh->dev->ctx, "BUG: Invalid host interface: %u.", + devh->dev->iface); + return JAYLINK_ERR; + } + + return ret; +} + +/** + * Close a device. + * + * After this function has been called no other function of the transport + * abstraction layer for the given device handle must be called. + * + * @param[in,out] devh Device handle. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR Other error conditions. + */ +JAYLINK_PRIV int transport_close(struct jaylink_device_handle *devh) +{ + int ret; + + switch (devh->dev->iface) { +#ifdef HAVE_LIBUSB + case JAYLINK_HIF_USB: + ret = transport_usb_close(devh); + break; +#endif + case JAYLINK_HIF_TCP: + ret = transport_tcp_close(devh); + break; + default: + log_err(devh->dev->ctx, "BUG: Invalid host interface: %u.", + devh->dev->iface); + return JAYLINK_ERR; + } + + return ret; +} + +/** + * Start a write operation for a device. + * + * The data of a write operation must be written with at least one call of + * transport_write(). It is required that all data of a write operation is + * written before an other write and/or read operation is started. + * + * @param[in,out] devh Device handle. + * @param[in] length Number of bytes of the write operation. + * @param[in] has_command Determines whether the data of the write operation + * contains the protocol command. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + */ +JAYLINK_PRIV int transport_start_write(struct jaylink_device_handle *devh, + size_t length, bool has_command) +{ + int ret; + + switch (devh->dev->iface) { +#ifdef HAVE_LIBUSB + case JAYLINK_HIF_USB: + ret = transport_usb_start_write(devh, length, has_command); + break; +#endif + case JAYLINK_HIF_TCP: + ret = transport_tcp_start_write(devh, length, has_command); + break; + default: + log_err(devh->dev->ctx, "BUG: Invalid host interface: %u.", + devh->dev->iface); + return JAYLINK_ERR; + } + + return ret; +} + +/** + * Start a read operation for a device. + * + * The data of a read operation must be read with at least one call of + * transport_read(). It is required that all data of a read operation is read + * before an other write and/or read operation is started. + * + * @param[in,out] devh Device handle. + * @param[in] length Number of bytes of the read operation. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + */ +JAYLINK_PRIV int transport_start_read(struct jaylink_device_handle *devh, + size_t length) +{ + int ret; + + switch (devh->dev->iface) { +#ifdef HAVE_LIBUSB + case JAYLINK_HIF_USB: + ret = transport_usb_start_read(devh, length); + break; +#endif + case JAYLINK_HIF_TCP: + ret = transport_tcp_start_read(devh, length); + break; + default: + log_err(devh->dev->ctx, "BUG: Invalid host interface: %u.", + devh->dev->iface); + return JAYLINK_ERR; + } + + return ret; +} + +/** + * Start a write and read operation for a device. + * + * This function starts a write and read operation as the consecutive call of + * transport_start_write() and transport_start_read() but has a different + * meaning from the protocol perspective and can therefore not be replaced by + * these functions and vice versa. + * + * @note The write operation must be completed first before the read operation + * must be processed. + * + * @param[in,out] devh Device handle. + * @param[in] write_length Number of bytes of the write operation. + * @param[in] read_length Number of bytes of the read operation. + * @param[in] has_command Determines whether the data of the write operation + * contains the protocol command. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + */ +JAYLINK_PRIV int transport_start_write_read(struct jaylink_device_handle *devh, + size_t write_length, size_t read_length, bool has_command) +{ + int ret; + + switch (devh->dev->iface) { +#ifdef HAVE_LIBUSB + case JAYLINK_HIF_USB: + ret = transport_usb_start_write_read(devh, write_length, + read_length, has_command); + break; +#endif + case JAYLINK_HIF_TCP: + ret = transport_tcp_start_write_read(devh, write_length, + read_length, has_command); + break; + default: + log_err(devh->dev->ctx, "BUG: Invalid host interface: %u.", + devh->dev->iface); + return JAYLINK_ERR; + } + + return ret; +} + +/** + * Write data to a device. + * + * Before this function is used transport_start_write() or + * transport_start_write_read() must be called to start a write operation. The + * total number of written bytes must not exceed the number of bytes of the + * write operation. + * + * @note A write operation will be performed and the data will be sent to the + * device when the number of written bytes reaches the number of bytes of + * the write operation. Before that the data will be written into a + * buffer. + * + * @param[in,out] devh Device handle. + * @param[in] buffer Buffer to write data from. + * @param[in] length Number of bytes to write. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + */ +JAYLINK_PRIV int transport_write(struct jaylink_device_handle *devh, + const uint8_t *buffer, size_t length) +{ + int ret; + + switch (devh->dev->iface) { +#ifdef HAVE_LIBUSB + case JAYLINK_HIF_USB: + ret = transport_usb_write(devh, buffer, length); + break; +#endif + case JAYLINK_HIF_TCP: + ret = transport_tcp_write(devh, buffer, length); + break; + default: + log_err(devh->dev->ctx, "BUG: Invalid host interface: %u.", + devh->dev->iface); + return JAYLINK_ERR; + } + + return ret; +} + +/** + * Read data from a device. + * + * Before this function is used transport_start_read() or + * transport_start_write_read() must be called to start a read operation. The + * total number of read bytes must not exceed the number of bytes of the read + * operation. + * + * @param[in,out] devh Device handle. + * @param[out] buffer Buffer to read data into on success. Its content is + * undefined on failure. + * @param[in] length Number of bytes to read. + * + * @retval JAYLINK_OK Success. + * @retval JAYLINK_ERR_ARG Invalid arguments. + * @retval JAYLINK_ERR_TIMEOUT A timeout occurred. + * @retval JAYLINK_ERR_IO Input/output error. + * @retval JAYLINK_ERR Other error conditions. + */ +JAYLINK_PRIV int transport_read(struct jaylink_device_handle *devh, + uint8_t *buffer, size_t length) +{ + int ret; + + switch (devh->dev->iface) { +#ifdef HAVE_LIBUSB + case JAYLINK_HIF_USB: + ret = transport_usb_read(devh, buffer, length); + break; +#endif + case JAYLINK_HIF_TCP: + ret = transport_tcp_read(devh, buffer, length); + break; + default: + log_err(devh->dev->ctx, "BUG: Invalid host interface: %u.", + devh->dev->iface); + return JAYLINK_ERR; + } + + return ret; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/transport_tcp.c b/src/jtag/drivers/libjaylink/libjaylink/transport_tcp.c new file mode 100644 index 0000000..7e10179 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/transport_tcp.c @@ -0,0 +1,601 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2015-2017 Marc Schink <jaylink-dev@marcschink.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <sys/types.h> + +#ifdef _WIN32 +#include <winsock2.h> +#include <ws2tcpip.h> +#else +#include <sys/time.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +#endif + +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/** + * @file + * + * Transport abstraction layer (TCP/IP). + */ + +/** @cond PRIVATE */ +#define CMD_SERVER 0x00 +#define CMD_CLIENT 0x07 + +/** + * Response status code indicating that the maximum number of simultaneous + * connections on the device has been reached. + */ +#define RESP_MAX_CONNECTIONS 0xfe + +/** Buffer size in bytes. */ +#define BUFFER_SIZE 2048 + +/** Timeout of a receive operation in milliseconds. */ +#define RECV_TIMEOUT 5000 +/** Timeout of a send operation in milliseconds. */ +#define SEND_TIMEOUT 5000 + +/** String of the port number for the J-Link TCP/IP protocol. */ +#define PORT_STRING "19020" + +/** Size of the server's hello message in bytes. */ +#define SERVER_HELLO_SIZE 4 +/** + * Maximum length of the server name including trailing null-terminator in + * bytes. + */ +#define SERVER_NAME_MAX_LENGTH 256 +/** @endcond */ + +static int initialize_handle(struct jaylink_device_handle *devh) +{ + struct jaylink_context *ctx; + + ctx = devh->dev->ctx; + + devh->buffer_size = BUFFER_SIZE; + devh->buffer = malloc(devh->buffer_size); + + if (!devh->buffer) { + log_err(ctx, "Transport buffer malloc failed."); + return JAYLINK_ERR_MALLOC; + } + + devh->read_length = 0; + devh->bytes_available = 0; + devh->read_pos = 0; + + devh->write_length = 0; + devh->write_pos = 0; + + return JAYLINK_OK; +} + +static void cleanup_handle(struct jaylink_device_handle *devh) +{ + free(devh->buffer); +} + +static int _recv(struct jaylink_device_handle *devh, uint8_t *buffer, + size_t length) +{ + struct jaylink_context *ctx; + size_t tmp; + + ctx = devh->dev->ctx; + + while (length > 0) { + tmp = length; + + if (!socket_recv(devh->sock, buffer, &tmp, 0)) { + log_err(ctx, "Failed to receive data from device."); + return JAYLINK_ERR_IO; + } else if (!tmp) { + log_err(ctx, "Failed to receive data from device: " + "remote connection closed."); + return JAYLINK_ERR_IO; + } + + buffer += tmp; + length -= tmp; + + log_dbgio(ctx, "Received %zu bytes from device.", tmp); + } + + return JAYLINK_OK; +} + +static int handle_server_hello(struct jaylink_device_handle *devh) +{ + int ret; + struct jaylink_context *ctx; + uint8_t buf[SERVER_HELLO_SIZE]; + char name[SERVER_NAME_MAX_LENGTH]; + uint16_t proto_version; + size_t length; + + ctx = devh->dev->ctx; + + ret = _recv(devh, buf, sizeof(buf)); + + if (ret != JAYLINK_OK) { + log_err(ctx, "Failed to receive hello message."); + return ret; + } + + if (buf[0] == RESP_MAX_CONNECTIONS) { + log_err(ctx, "Maximum number of connections reached."); + return JAYLINK_ERR; + } + + if (buf[0] != CMD_SERVER) { + log_err(ctx, "Invalid hello message received."); + return JAYLINK_ERR_PROTO; + } + + proto_version = buffer_get_u16(buf, 1); + + log_dbg(ctx, "Protocol version: 0x%04x.", proto_version); + + length = buf[3]; + ret = _recv(devh, (uint8_t *)name, length); + + if (ret != JAYLINK_OK) { + log_err(ctx, "Failed to receive server name."); + return ret; + } + + name[length] = '\0'; + + log_dbg(ctx, "Server name: %s.", name); + + return JAYLINK_OK; +} + +static int set_socket_timeouts(struct jaylink_device_handle *devh) +{ + struct jaylink_context *ctx; + + ctx = devh->dev->ctx; +#ifdef _WIN32 + DWORD timeout; + + timeout = RECV_TIMEOUT; + + if (!socket_set_option(devh->sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, + sizeof(timeout))) { + log_err(ctx, "Failed to set socket receive timeout."); + return JAYLINK_ERR; + } + + timeout = SEND_TIMEOUT; + + if (!socket_set_option(devh->sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, + sizeof(timeout))) { + log_err(ctx, "Failed to set socket send timeout."); + return JAYLINK_ERR; + } +#else + struct timeval timeout; + + timeout.tv_sec = RECV_TIMEOUT / 1000; + timeout.tv_usec = (RECV_TIMEOUT % 1000) * 1000; + + if (!socket_set_option(devh->sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, + sizeof(struct timeval))) { + log_err(ctx, "Failed to set socket receive timeout."); + return JAYLINK_ERR; + } + + timeout.tv_sec = SEND_TIMEOUT / 1000; + timeout.tv_usec = (SEND_TIMEOUT % 1000) * 1000; + + if (!socket_set_option(devh->sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, + sizeof(struct timeval))) { + log_err(ctx, "Failed to set socket send timeout."); + return JAYLINK_ERR; + } +#endif + return JAYLINK_OK; +} + +JAYLINK_PRIV int transport_tcp_open(struct jaylink_device_handle *devh) +{ + int ret; + struct jaylink_context *ctx; + struct jaylink_device *dev; + struct addrinfo hints; + struct addrinfo *info; + struct addrinfo *rp; + int sock; + + dev = devh->dev; + ctx = dev->ctx; + + log_dbg(ctx, "Trying to open device (IPv4 address = %s).", + dev->ipv4_address); + + ret = initialize_handle(devh); + + if (ret != JAYLINK_OK) { + log_err(ctx, "Initialize device handle failed."); + return ret; + } + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + ret = getaddrinfo(dev->ipv4_address, PORT_STRING, &hints, &info); + + if (ret != 0) { + log_err(ctx, "Address lookup failed."); + cleanup_handle(devh); + return JAYLINK_ERR; + } + + sock = -1; + + for (rp = info; rp != NULL; rp = rp->ai_next) { + sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + + if (sock < 0) + continue; + + if (!connect(sock, info->ai_addr, info->ai_addrlen)) + break; + + socket_close(sock); + sock = -1; + } + + freeaddrinfo(info); + + if (sock < 0) { + log_err(ctx, "Failed to open device."); + cleanup_handle(devh); + return JAYLINK_ERR; + } + + log_dbg(ctx, "Device opened successfully."); + + devh->sock = sock; + ret = set_socket_timeouts(devh); + + if (ret != JAYLINK_OK) { + socket_close(sock); + cleanup_handle(devh); + return ret; + } + + ret = handle_server_hello(devh); + + if (ret != JAYLINK_OK) { + socket_close(sock); + cleanup_handle(devh); + return ret; + } + + return JAYLINK_OK; +} + +JAYLINK_PRIV int transport_tcp_close(struct jaylink_device_handle *devh) +{ + struct jaylink_context *ctx; + + ctx = devh->dev->ctx; + + log_dbg(ctx, "Closing device (IPv4 address = %s).", + devh->dev->ipv4_address); + + cleanup_handle(devh); + + log_dbg(ctx, "Device closed successfully."); + + return JAYLINK_OK; +} + +JAYLINK_PRIV int transport_tcp_start_write(struct jaylink_device_handle *devh, + size_t length, bool has_command) +{ + struct jaylink_context *ctx; + + if (!length) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + + log_dbgio(ctx, "Starting write operation (length = %zu bytes).", + length); + + if (devh->write_pos > 0) + log_warn(ctx, "Last write operation left %zu bytes in the " + "buffer.", devh->write_pos); + + if (devh->write_length > 0) + log_warn(ctx, "Last write operation was not performed."); + + devh->write_length = length; + devh->write_pos = 0; + + if (has_command) { + devh->buffer[0] = CMD_CLIENT; + devh->write_pos++; + } + + return JAYLINK_OK; +} + +JAYLINK_PRIV int transport_tcp_start_read(struct jaylink_device_handle *devh, + size_t length) +{ + struct jaylink_context *ctx; + + if (!length) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + + log_dbgio(ctx, "Starting read operation (length = %zu bytes).", + length); + + if (devh->bytes_available > 0) + log_dbg(ctx, "Last read operation left %zu bytes in the " + "buffer.", devh->bytes_available); + + if (devh->read_length > 0) + log_warn(ctx, "Last read operation left %zu bytes.", + devh->read_length); + + devh->read_length = length; + + return JAYLINK_OK; +} + +JAYLINK_PRIV int transport_tcp_start_write_read( + struct jaylink_device_handle *devh, size_t write_length, + size_t read_length, bool has_command) +{ + struct jaylink_context *ctx; + + if (!read_length || !write_length) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + + log_dbgio(ctx, "Starting write / read operation (length = " + "%zu / %zu bytes).", write_length, read_length); + + if (devh->write_pos > 0) + log_warn(ctx, "Last write operation left %zu bytes in the " + "buffer.", devh->write_pos); + + if (devh->write_length > 0) + log_warn(ctx, "Last write operation was not performed."); + + if (devh->bytes_available > 0) + log_warn(ctx, "Last read operation left %zu bytes in the " + "buffer.", devh->bytes_available); + + if (devh->read_length > 0) + log_warn(ctx, "Last read operation left %zu bytes.", + devh->read_length); + + devh->write_length = write_length; + devh->write_pos = 0; + + if (has_command) { + devh->buffer[0] = CMD_CLIENT; + devh->write_pos++; + } + + devh->read_length = read_length; + devh->bytes_available = 0; + devh->read_pos = 0; + + return JAYLINK_OK; +} + +static int _send(struct jaylink_device_handle *devh, const uint8_t *buffer, + size_t length) +{ + struct jaylink_context *ctx; + size_t tmp; + + ctx = devh->dev->ctx; + + while (length > 0) { + tmp = length; + + if (!socket_send(devh->sock, buffer, &tmp, 0)) { + log_err(ctx, "Failed to send data to device."); + return JAYLINK_ERR_IO; + } + + buffer += tmp; + length -= tmp; + + log_dbgio(ctx, "Sent %zu bytes to device.", tmp); + } + + return JAYLINK_OK; +} + +static bool adjust_buffer(struct jaylink_device_handle *devh, size_t size) +{ + struct jaylink_context *ctx; + uint8_t *buffer; + size_t num; + + ctx = devh->dev->ctx; + + /* Adjust buffer size to a multiple of BUFFER_SIZE bytes. */ + num = size / BUFFER_SIZE; + + if (size % BUFFER_SIZE > 0) + num++; + + size = num * BUFFER_SIZE; + buffer = realloc(devh->buffer, size); + + if (!buffer) { + log_err(ctx, "Failed to adjust buffer size to %zu bytes.", + size); + return false; + } + + devh->buffer = buffer; + devh->buffer_size = size; + + log_dbg(ctx, "Adjusted buffer size to %zu bytes.", size); + + return true; +} + +JAYLINK_PRIV int transport_tcp_write(struct jaylink_device_handle *devh, + const uint8_t *buffer, size_t length) +{ + int ret; + struct jaylink_context *ctx; + size_t tmp; + + ctx = devh->dev->ctx; + + if (length > devh->write_length) { + log_err(ctx, "Requested to write %zu bytes but only %zu bytes " + "are expected for the write operation.", length, + devh->write_length); + return JAYLINK_ERR_ARG; + } + + /* + * Store data in the buffer if the expected number of bytes for the + * write operation is not reached. + */ + if (length < devh->write_length) { + if (devh->write_pos + length > devh->buffer_size) { + if (!adjust_buffer(devh, devh->write_pos + length)) + return JAYLINK_ERR_MALLOC; + } + + memcpy(devh->buffer + devh->write_pos, buffer, length); + + devh->write_length -= length; + devh->write_pos += length; + + log_dbgio(ctx, "Wrote %zu bytes into buffer.", length); + return JAYLINK_OK; + } + + /* + * Expected number of bytes for this write operation is reached and + * therefore the write operation will be performed. + */ + devh->write_length = 0; + + /* Send data directly to the device if the buffer is empty. */ + if (!devh->write_pos) + return _send(devh, buffer, length); + + tmp = MIN(length, devh->buffer_size - devh->write_pos); + + /* + * Fill up the internal buffer in order to reduce the number of + * messages sent to the device for performance reasons. + */ + memcpy(devh->buffer + devh->write_pos, buffer, tmp); + + length -= tmp; + buffer += tmp; + + log_dbgio(ctx, "Buffer filled up with %zu bytes.", tmp); + + ret = _send(devh, devh->buffer, devh->write_pos + tmp); + + devh->write_pos = 0; + + if (ret != JAYLINK_OK) + return ret; + + if (!length) + return JAYLINK_OK; + + return _send(devh, buffer, length); +} + +JAYLINK_PRIV int transport_tcp_read(struct jaylink_device_handle *devh, + uint8_t *buffer, size_t length) +{ + int ret; + struct jaylink_context *ctx; + + ctx = devh->dev->ctx; + + if (length > devh->read_length) { + log_err(ctx, "Requested to read %zu bytes but only %zu bytes " + "are expected for the read operation.", length, + devh->read_length); + return JAYLINK_ERR_ARG; + } + + if (length <= devh->bytes_available) { + memcpy(buffer, devh->buffer + devh->read_pos, length); + + devh->read_length -= length; + devh->bytes_available -= length; + devh->read_pos += length; + + log_dbgio(ctx, "Read %zu bytes from buffer.", length); + return JAYLINK_OK; + } + + if (devh->bytes_available) { + memcpy(buffer, devh->buffer + devh->read_pos, + devh->bytes_available); + + buffer += devh->bytes_available; + length -= devh->bytes_available; + devh->read_length -= devh->bytes_available; + + log_dbgio(ctx, "Read %zu bytes from buffer to flush it.", + devh->bytes_available); + + devh->bytes_available = 0; + devh->read_pos = 0; + } + + ret = _recv(devh, buffer, length); + + if (ret != JAYLINK_OK) + return ret; + + devh->read_length -= length; + + return JAYLINK_OK; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/transport_usb.c b/src/jtag/drivers/libjaylink/libjaylink/transport_usb.c new file mode 100644 index 0000000..dfe9eac --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/transport_usb.c @@ -0,0 +1,620 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-2016 Marc Schink <jaylink-dev@marcschink.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +#include "libjaylink.h" +#include "libjaylink-internal.h" + +/** + * @file + * + * Transport abstraction layer (USB). + */ + +/** Timeout of an USB transfer in milliseconds. */ +#define USB_TIMEOUT 1000 + +/** + * Number of consecutive timeouts before an USB transfer will be treated as + * timed out. + */ +#define NUM_TIMEOUTS 2 + +/** Chunk size in bytes in which data is transferred. */ +#define CHUNK_SIZE 2048 + +static int initialize_handle(struct jaylink_device_handle *devh) +{ + int ret; + struct jaylink_context *ctx; + struct libusb_config_descriptor *config; + const struct libusb_interface *interface; + const struct libusb_interface_descriptor *desc; + const struct libusb_endpoint_descriptor *epdesc; + bool found_interface; + bool found_endpoint_in; + bool found_endpoint_out; + uint8_t i; + + ctx = devh->dev->ctx; + devh->interface_number = 0; + + /* + * Retrieve active configuration descriptor to determine the endpoints + * for the interface number of the device. + */ + ret = libusb_get_active_config_descriptor(devh->dev->usb_dev, &config); + + if (ret != LIBUSB_SUCCESS) { + log_err(ctx, "Failed to get configuration descriptor: %s.", + libusb_error_name(ret)); + return JAYLINK_ERR; + } + + found_interface = false; + + for (i = 0; i < config->bNumInterfaces; i++) { + interface = &config->interface[i]; + desc = &interface->altsetting[0]; + + if (desc->bInterfaceClass != LIBUSB_CLASS_VENDOR_SPEC) + continue; + + if (desc->bInterfaceSubClass != LIBUSB_CLASS_VENDOR_SPEC) + continue; + + if (desc->bNumEndpoints < 2) + continue; + + found_interface = true; + devh->interface_number = i; + break; + } + + if (!found_interface) { + log_err(ctx, "No suitable interface found."); + libusb_free_config_descriptor(config); + return JAYLINK_ERR; + } + + found_endpoint_in = false; + found_endpoint_out = false; + + for (i = 0; i < desc->bNumEndpoints; i++) { + epdesc = &desc->endpoint[i]; + + if (epdesc->bEndpointAddress & LIBUSB_ENDPOINT_IN) { + devh->endpoint_in = epdesc->bEndpointAddress; + found_endpoint_in = true; + } else { + devh->endpoint_out = epdesc->bEndpointAddress; + found_endpoint_out = true; + } + } + + libusb_free_config_descriptor(config); + + if (!found_endpoint_in) { + log_err(ctx, "Interface IN endpoint not found."); + return JAYLINK_ERR; + } + + if (!found_endpoint_out) { + log_err(ctx, "Interface OUT endpoint not found."); + return JAYLINK_ERR; + } + + log_dbg(ctx, "Using endpoint %02x (IN) and %02x (OUT).", + devh->endpoint_in, devh->endpoint_out); + + /* Buffer size must be a multiple of CHUNK_SIZE bytes. */ + devh->buffer_size = CHUNK_SIZE; + devh->buffer = malloc(devh->buffer_size); + + if (!devh->buffer) { + log_err(ctx, "Transport buffer malloc failed."); + return JAYLINK_ERR_MALLOC; + } + + devh->read_length = 0; + devh->bytes_available = 0; + devh->read_pos = 0; + + devh->write_length = 0; + devh->write_pos = 0; + + return JAYLINK_OK; +} + +static void cleanup_handle(struct jaylink_device_handle *devh) +{ + free(devh->buffer); +} + +JAYLINK_PRIV int transport_usb_open(struct jaylink_device_handle *devh) +{ + int ret; + struct jaylink_device *dev; + struct jaylink_context *ctx; + struct libusb_device_handle *usb_devh; + + dev = devh->dev; + ctx = dev->ctx; + + log_dbg(ctx, "Trying to open device (bus:address = %03u:%03u).", + libusb_get_bus_number(dev->usb_dev), + libusb_get_device_address(dev->usb_dev)); + + ret = initialize_handle(devh); + + if (ret != JAYLINK_OK) { + log_err(ctx, "Initialize device handle failed."); + return ret; + } + + ret = libusb_open(dev->usb_dev, &usb_devh); + + if (ret != LIBUSB_SUCCESS) { + log_err(ctx, "Failed to open device: %s.", + libusb_error_name(ret)); + cleanup_handle(devh); + return JAYLINK_ERR; + } + + ret = libusb_claim_interface(usb_devh, devh->interface_number); + + if (ret != LIBUSB_SUCCESS) { + log_err(ctx, "Failed to claim interface: %s.", + libusb_error_name(ret)); + cleanup_handle(devh); + libusb_close(usb_devh); + return JAYLINK_ERR; + } + + log_dbg(ctx, "Device opened successfully."); + + devh->usb_devh = usb_devh; + + return JAYLINK_OK; +} + +JAYLINK_PRIV int transport_usb_close(struct jaylink_device_handle *devh) +{ + int ret; + struct jaylink_device *dev; + struct jaylink_context *ctx; + + dev = devh->dev; + ctx = dev->ctx; + + log_dbg(ctx, "Closing device (bus:address = %03u:%03u).", + libusb_get_bus_number(dev->usb_dev), + libusb_get_device_address(dev->usb_dev)); + + ret = libusb_release_interface(devh->usb_devh, devh->interface_number); + + libusb_close(devh->usb_devh); + cleanup_handle(devh); + + if (ret != LIBUSB_SUCCESS) { + log_err(ctx, "Failed to release interface: %s.", + libusb_error_name(ret)); + return JAYLINK_ERR; + } + + log_dbg(ctx, "Device closed successfully."); + + return JAYLINK_OK; +} + +JAYLINK_PRIV int transport_usb_start_write(struct jaylink_device_handle *devh, + size_t length, bool has_command) +{ + struct jaylink_context *ctx; + + (void)has_command; + + if (!length) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + + log_dbgio(ctx, "Starting write operation (length = %zu bytes).", length); + + if (devh->write_pos > 0) + log_warn(ctx, "Last write operation left %zu bytes in the " + "buffer.", devh->write_pos); + + if (devh->write_length > 0) + log_warn(ctx, "Last write operation was not performed."); + + devh->write_length = length; + devh->write_pos = 0; + + return JAYLINK_OK; +} + +JAYLINK_PRIV int transport_usb_start_read(struct jaylink_device_handle *devh, + size_t length) +{ + struct jaylink_context *ctx; + + if (!length) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + + log_dbgio(ctx, "Starting read operation (length = %zu bytes).", + length); + + if (devh->bytes_available > 0) + log_dbg(ctx, "Last read operation left %zu bytes in the " + "buffer.", devh->bytes_available); + + if (devh->read_length > 0) + log_warn(ctx, "Last read operation left %zu bytes.", + devh->read_length); + + devh->read_length = length; + + return JAYLINK_OK; +} + +JAYLINK_PRIV int transport_usb_start_write_read( + struct jaylink_device_handle *devh, size_t write_length, + size_t read_length, bool has_command) +{ + struct jaylink_context *ctx; + + (void)has_command; + + if (!read_length || !write_length) + return JAYLINK_ERR_ARG; + + ctx = devh->dev->ctx; + + log_dbgio(ctx, "Starting write / read operation (length = " + "%zu / %zu bytes).", write_length, read_length); + + if (devh->write_pos > 0) + log_warn(ctx, "Last write operation left %zu bytes in the " + "buffer.", devh->write_pos); + + if (devh->write_length > 0) + log_warn(ctx, "Last write operation was not performed."); + + if (devh->bytes_available > 0) + log_warn(ctx, "Last read operation left %zu bytes in the " + "buffer.", devh->bytes_available); + + if (devh->read_length > 0) + log_warn(ctx, "Last read operation left %zu bytes.", + devh->read_length); + + devh->write_length = write_length; + devh->write_pos = 0; + + devh->read_length = read_length; + devh->bytes_available = 0; + devh->read_pos = 0; + + return JAYLINK_OK; +} + +static int usb_recv(struct jaylink_device_handle *devh, uint8_t *buffer, + size_t *length) +{ + int ret; + struct jaylink_context *ctx; + unsigned int tries; + int transferred; + + ctx = devh->dev->ctx; + + tries = NUM_TIMEOUTS; + transferred = 0; + + while (tries > 0 && !transferred) { + /* Always request CHUNK_SIZE bytes from the device. */ + ret = libusb_bulk_transfer(devh->usb_devh, devh->endpoint_in, + (unsigned char *)buffer, CHUNK_SIZE, &transferred, + USB_TIMEOUT); + + if (ret == LIBUSB_ERROR_TIMEOUT) { + log_warn(ctx, "Failed to receive data from " + "device: %s.", libusb_error_name(ret)); + tries--; + continue; + } else if (ret != LIBUSB_SUCCESS) { + log_err(ctx, "Failed to receive data from " + "device: %s.", libusb_error_name(ret)); + return JAYLINK_ERR; + } + + log_dbgio(ctx, "Received %i bytes from device.", transferred); + } + + /* Ignore a possible timeout if at least one byte was received. */ + if (transferred > 0) { + *length = transferred; + return JAYLINK_OK; + } + + log_err(ctx, "Receiving data from device timed out."); + + return JAYLINK_ERR_TIMEOUT; +} + +static bool adjust_buffer(struct jaylink_device_handle *devh, size_t size) +{ + struct jaylink_context *ctx; + size_t num_chunks; + uint8_t *buffer; + + ctx = devh->dev->ctx; + + /* Adjust buffer size to a multiple of CHUNK_SIZE bytes. */ + num_chunks = size / CHUNK_SIZE; + + if (size % CHUNK_SIZE > 0) + num_chunks++; + + size = num_chunks * CHUNK_SIZE; + buffer = realloc(devh->buffer, size); + + if (!buffer) { + log_err(ctx, "Failed to adjust buffer size to %zu bytes.", + size); + return false; + } + + devh->buffer = buffer; + devh->buffer_size = size; + + log_dbg(ctx, "Adjusted buffer size to %zu bytes.", size); + + return true; +} + +static int usb_send(struct jaylink_device_handle *devh, const uint8_t *buffer, + size_t length) +{ + int ret; + struct jaylink_context *ctx; + unsigned int tries; + int transferred; + + ctx = devh->dev->ctx; + tries = NUM_TIMEOUTS; + + while (tries > 0 && length > 0) { + /* Send data in chunks of CHUNK_SIZE bytes to the device. */ + ret = libusb_bulk_transfer(devh->usb_devh, devh->endpoint_out, + (unsigned char *)buffer, MIN(CHUNK_SIZE, length), + &transferred, USB_TIMEOUT); + + if (ret == LIBUSB_SUCCESS) { + tries = NUM_TIMEOUTS; + } else if (ret == LIBUSB_ERROR_TIMEOUT) { + log_warn(ctx, "Failed to send data to device: %s.", + libusb_error_name(ret)); + tries--; + } else { + log_err(ctx, "Failed to send data to device: %s.", + libusb_error_name(ret)); + return JAYLINK_ERR; + } + + buffer += transferred; + length -= transferred; + + log_dbgio(ctx, "Sent %i bytes to device.", transferred); + } + + if (!length) + return JAYLINK_OK; + + log_err(ctx, "Sending data to device timed out."); + + return JAYLINK_ERR_TIMEOUT; +} + +JAYLINK_PRIV int transport_usb_write(struct jaylink_device_handle *devh, + const uint8_t *buffer, size_t length) +{ + int ret; + struct jaylink_context *ctx; + size_t num_chunks; + size_t fill_bytes; + size_t tmp; + + ctx = devh->dev->ctx; + + if (length > devh->write_length) { + log_err(ctx, "Requested to write %zu bytes but only %zu bytes " + "are expected for the write operation.", length, + devh->write_length); + return JAYLINK_ERR_ARG; + } + + /* + * Store data in the buffer if the expected number of bytes for the + * write operation is not reached. + */ + if (length < devh->write_length) { + if (devh->write_pos + length > devh->buffer_size) { + if (!adjust_buffer(devh, devh->write_pos + length)) + return JAYLINK_ERR_MALLOC; + } + + memcpy(devh->buffer + devh->write_pos, buffer, length); + + devh->write_length -= length; + devh->write_pos += length; + + log_dbgio(ctx, "Wrote %zu bytes into buffer.", length); + return JAYLINK_OK; + } + + /* + * Expected number of bytes for this write operation is reached and + * therefore the write operation will be performed. + */ + devh->write_length = 0; + + /* Send data directly to the device if the buffer is empty. */ + if (!devh->write_pos) + return usb_send(devh, buffer, length); + + /* + * Calculate the number of bytes to fill up the buffer to reach a + * multiple of CHUNK_SIZE bytes. This ensures that the data from the + * buffer will be sent to the device in chunks of CHUNK_SIZE bytes. + * Note that this is why the buffer size must be a multiple of + * CHUNK_SIZE bytes. + */ + num_chunks = devh->write_pos / CHUNK_SIZE; + + if (devh->write_pos % CHUNK_SIZE) + num_chunks++; + + fill_bytes = (num_chunks * CHUNK_SIZE) - devh->write_pos; + tmp = MIN(length, fill_bytes); + + if (tmp > 0) { + memcpy(devh->buffer + devh->write_pos, buffer, tmp); + + length -= tmp; + buffer += tmp; + + log_dbgio(ctx, "Buffer filled up with %zu bytes.", tmp); + } + + /* Send buffered data to the device. */ + ret = usb_send(devh, devh->buffer, devh->write_pos + tmp); + devh->write_pos = 0; + + if (ret != JAYLINK_OK) + return ret; + + if (!length) + return JAYLINK_OK; + + /* Send remaining data to the device. */ + return usb_send(devh, buffer, length); +} + +JAYLINK_PRIV int transport_usb_read(struct jaylink_device_handle *devh, + uint8_t *buffer, size_t length) +{ + int ret; + struct jaylink_context *ctx; + size_t bytes_received; + size_t tmp; + + ctx = devh->dev->ctx; + + if (length > devh->read_length) { + log_err(ctx, "Requested to read %zu bytes but only %zu bytes " + "are expected for the read operation.", length, + devh->read_length); + return JAYLINK_ERR_ARG; + } + + if (length <= devh->bytes_available) { + memcpy(buffer, devh->buffer + devh->read_pos, length); + + devh->read_length -= length; + devh->bytes_available -= length; + devh->read_pos += length; + + log_dbgio(ctx, "Read %zu bytes from buffer.", length); + return JAYLINK_OK; + } + + if (devh->bytes_available) { + memcpy(buffer, devh->buffer + devh->read_pos, + devh->bytes_available); + + buffer += devh->bytes_available; + length -= devh->bytes_available; + devh->read_length -= devh->bytes_available; + + log_dbgio(ctx, "Read %zu bytes from buffer to flush it.", + devh->bytes_available); + + devh->bytes_available = 0; + devh->read_pos = 0; + } + + while (length > 0) { + /* + * If less than CHUNK_SIZE bytes are requested from the device, + * store the received data into the internal buffer instead of + * directly into the user provided buffer. This is necessary to + * prevent a possible buffer overflow because the number of + * requested bytes from the device is always CHUNK_SIZE and + * therefore up to CHUNK_SIZE bytes may be received. + * Note that this is why the internal buffer size must be at + * least CHUNK_SIZE bytes. + */ + if (length < CHUNK_SIZE) { + ret = usb_recv(devh, devh->buffer, &bytes_received); + + if (ret != JAYLINK_OK) + return ret; + + tmp = MIN(bytes_received, length); + memcpy(buffer, devh->buffer, tmp); + + /* + * Setup the buffer for the remaining data if more data + * was received from the device than was requested. + */ + if (bytes_received > length) { + devh->bytes_available = bytes_received - tmp; + devh->read_pos = tmp; + } + + buffer += tmp; + length -= tmp; + devh->read_length -= tmp; + + log_dbgio(ctx, "Read %zu bytes from buffer.", tmp); + } else { + ret = usb_recv(devh, buffer, &bytes_received); + + if (ret != JAYLINK_OK) + return ret; + + buffer += bytes_received; + length -= bytes_received; + devh->read_length -= bytes_received; + + log_dbgio(ctx, "Read %zu bytes from device.", + bytes_received); + } + } + + return JAYLINK_OK; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/util.c b/src/jtag/drivers/libjaylink/libjaylink/util.c new file mode 100644 index 0000000..4862d4e --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/util.c @@ -0,0 +1,56 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2014-2015 Marc Schink <jaylink-dev@marcschink.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdbool.h> + +#include "libjaylink.h" + +/** + * @file + * + * Utility functions. + */ + +/** + * Check for a capability. + * + * The capabilities are expected to be stored in a bit array consisting of one + * or more bytes where each individual bit represents a capability. The first + * bit of this array is the least significant bit of the first byte and the + * following bits are sequentially numbered in order of increasing bit + * significance and byte index. A set bit indicates a supported capability. + * + * @param[in] caps Buffer with capabilities. + * @param[in] cap Bit position of the capability to check for. + * + * @retval true Capability is supported. + * @retval false Capability is not supported or invalid argument. + * + * @since 0.1.0 + */ +JAYLINK_API bool jaylink_has_cap(const uint8_t *caps, uint32_t cap) +{ + if (!caps) + return false; + + if (caps[cap / 8] & (1 << (cap % 8))) + return true; + + return false; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/version.c b/src/jtag/drivers/libjaylink/libjaylink/version.c new file mode 100644 index 0000000..88bc023 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/version.c @@ -0,0 +1,128 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2015 Marc Schink <jaylink-dev@marcschink.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "libjaylink.h" + +/** + * @file + * + * Package and library version functions. + */ + +/** + * Get the major version number of the libjaylink package. + * + * @return The major version number of the libjaylink package. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_version_package_get_major(void) +{ + return JAYLINK_VERSION_PACKAGE_MAJOR; +} + +/** + * Get the minor version number of the libjaylink package. + * + * @return The minor version number of the libjaylink package. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_version_package_get_minor(void) +{ + return JAYLINK_VERSION_PACKAGE_MINOR; +} + +/** + * Get the micro version number of the libjaylink package. + * + * @return The micro version number of the libjaylink package. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_version_package_get_micro(void) +{ + return JAYLINK_VERSION_PACKAGE_MICRO; +} + +/** + * Get the version number string of the libjaylink package. + * + * @return A string which contains the version number of the libjaylink + * package. The string is null-terminated and must not be free'd by the + * caller. + * + * @since 0.1.0 + */ +JAYLINK_API const char *jaylink_version_package_get_string(void) +{ + return JAYLINK_VERSION_PACKAGE_STRING; +} + +/** + * Get the <i>current</i> version number of the libjaylink libtool interface. + * + * @return The <i>current</i> version number of the libjaylink libtool + * interface. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_version_library_get_current(void) +{ + return JAYLINK_VERSION_LIBRARY_CURRENT; +} + +/** + * Get the <i>revision</i> version number of the libjaylink libtool interface. + * + * @return The <i>revision</i> version number of the libjaylink libtool + * interface. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_version_library_get_revision(void) +{ + return JAYLINK_VERSION_LIBRARY_REVISION; +} + +/** + * Get the <i>age</i> version number of the libjaylink libtool interface. + * + * @return The <i>age</i> version number of the libjaylink libtool interface. + * + * @since 0.1.0 + */ +JAYLINK_API int jaylink_version_library_get_age(void) +{ + return JAYLINK_VERSION_LIBRARY_AGE; +} + +/** + * Get the version number string of the libjaylink libtool interface. + * + * @return A string which contains the version number of the libjaylink libtool + * interface. The string is null-terminated and must not be free'd by + * the caller. + * + * @since 0.1.0 + */ +JAYLINK_API const char *jaylink_version_library_get_string(void) +{ + return JAYLINK_VERSION_LIBRARY_STRING; +} diff --git a/src/jtag/drivers/libjaylink/libjaylink/version.h.in b/src/jtag/drivers/libjaylink/libjaylink/version.h.in new file mode 100644 index 0000000..d6a7796 --- /dev/null +++ b/src/jtag/drivers/libjaylink/libjaylink/version.h.in @@ -0,0 +1,53 @@ +/* + * This file is part of the libjaylink project. + * + * Copyright (C) 2015 Marc Schink <jaylink-dev@marcschink.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef LIBJAYLINK_VERSION_H +#define LIBJAYLINK_VERSION_H + +/** + * @file + * + * Package and library version macros. + */ + +/** Major version number of the libjaylink package. */ +#define JAYLINK_VERSION_PACKAGE_MAJOR @JAYLINK_VERSION_PACKAGE_MAJOR@ + +/** Minor version number of the libjaylink package. */ +#define JAYLINK_VERSION_PACKAGE_MINOR @JAYLINK_VERSION_PACKAGE_MINOR@ + +/** Micro version number of the libjaylink package. */ +#define JAYLINK_VERSION_PACKAGE_MICRO @JAYLINK_VERSION_PACKAGE_MICRO@ + +/** Version number string of the libjaylink package. */ +#define JAYLINK_VERSION_PACKAGE_STRING "@JAYLINK_VERSION_PACKAGE@" + +/** <i>Current</i> version number of the libjaylink libtool interface. */ +#define JAYLINK_VERSION_LIBRARY_CURRENT @JAYLINK_VERSION_LIBRARY_CURRENT@ + +/** <i>Revision</i> version number of the libjaylink libtool interface. */ +#define JAYLINK_VERSION_LIBRARY_REVISION @JAYLINK_VERSION_LIBRARY_REVISION@ + +/** <i>Age</i> version number of the libjaylink libtool interface. */ +#define JAYLINK_VERSION_LIBRARY_AGE @JAYLINK_VERSION_LIBRARY_AGE@ + +/** Version number string of the libjaylink libtool interface. */ +#define JAYLINK_VERSION_LIBRARY_STRING "@JAYLINK_VERSION_LIBRARY@" + +#endif /* LIBJAYLINK_VERSION_H */ diff --git a/src/jtag/drivers/libjaylink/m4/jaylink.m4 b/src/jtag/drivers/libjaylink/m4/jaylink.m4 new file mode 100644 index 0000000..749568d --- /dev/null +++ b/src/jtag/drivers/libjaylink/m4/jaylink.m4 @@ -0,0 +1,91 @@ +## +## This file is part of the libjaylink project. +## +## Copyright (C) 2016 Marc Schink <jaylink-dev@marcschink.de> +## +## This program is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see <http://www.gnu.org/licenses/>. +## + +# serial 20161011 + +## _JAYLINK_SET_PACKAGE_VERSION(prefix, version, major, minor, micro) +## +m4_define([_JAYLINK_SET_PACKAGE_VERSION], [ + m4_assert([$# == 5]) + + # Get the short Git revision hash of the current commit. + git_version=`git --git-dir="$srcdir/.git" rev-parse \ + --short HEAD 2> /dev/null` + + # Try to get the release tag for the package version from the current + # commit. + tag=`git --git-dir="$srcdir/.git" describe --match "$2" \ + --exact-match 2> /dev/null` + + version=$2 + + # If Git is available, append the short Git revision hash of the + # current commit to the version string if there is no release tag for + # the package version on it. + AS_IF([test -n "$git_version" && test -z "$tag"], + [version="$version-git-$git_version"]) + + AC_SUBST([$1_MAJOR], [$3]) + AC_SUBST([$1_MINOR], [$4]) + AC_SUBST([$1_MICRO], [$5]) + AC_SUBST([$1], [$version]) +]) + +## JAYLINK_SET_PACKAGE_VERSION(prefix, version) +## +## Parse the package version string of the format <major>.<minor>.<micro> and +## set the variables <prefix>_{MAJOR,MINOR,MICRO} to their corresponding +## values. +## +## Set the variable <prefix> to the package version string. If Git is +## available, append the short Git revision hash of the current commit to the +## version string if there is no release tag for the package version on it. +## +AC_DEFUN([JAYLINK_SET_PACKAGE_VERSION], [ + m4_assert([$# == 2]) + + _JAYLINK_SET_PACKAGE_VERSION([$1], [$2], + m4_unquote(m4_split(m4_expand([$2]), [\.]))) +]) + +## _JAYLINK_SET_LIBRARY_VERSION(prefix, version, current, revision, age) +## +m4_define([_JAYLINK_SET_LIBRARY_VERSION], [ + m4_assert([$# == 5]) + + AC_SUBST([$1_CURRENT], [$3]) + AC_SUBST([$1_REVISION], [$4]) + AC_SUBST([$1_AGE], [$5]) + AC_SUBST([$1], [$2]) +]) + +## JAYLINK_SET_LIBRARY_VERSION(prefix, version) +## +## Parse the library version string of the format <current>:<revision>:<age> +## and set the variables <prefix>_{CURRENT,REVISION,AGE} to their corresponding +## values. +## +## Set the variable <prefix> to the library version string. +## +AC_DEFUN([JAYLINK_SET_LIBRARY_VERSION], [ + m4_assert([$# == 2]) + + _JAYLINK_SET_LIBRARY_VERSION([$1], [$2], + m4_unquote(m4_split([$2], [:]))) +]) diff --git a/src/target/riscv/opcodes.h b/src/target/riscv/opcodes.h index dd51c80..de85aad 100644 --- a/src/target/riscv/opcodes.h +++ b/src/target/riscv/opcodes.h @@ -224,6 +224,9 @@ static uint32_t ebreak_c(void) return MATCH_C_EBREAK; } +static uint32_t wfi(void) __attribute__ ((unused)); +static uint32_t wfi(void) { return MATCH_WFI; } + static uint32_t fence_i(void) __attribute__ ((unused)); static uint32_t fence_i(void) { diff --git a/src/target/riscv/program.h b/src/target/riscv/program.h index d641be1..310460c 100644 --- a/src/target/riscv/program.h +++ b/src/target/riscv/program.h @@ -52,8 +52,8 @@ int riscv_program_insert(struct riscv_program *p, riscv_insn_t i); * memory. */ int riscv_program_save_to_dscratch(struct riscv_program *p, enum gdb_regno to_save); -/* Helpers to assembly various instructions. Return 0 on success. These might - * assembly into a multi-instruction sequence that overwrites some other +/* Helpers to assemble various instructions. Return 0 on success. These might + * assemble into a multi-instruction sequence that overwrites some other * register, but those will be properly saved and restored. */ int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o); int riscv_program_lhr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o); diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index 3ab8587..d3afc8a 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -71,6 +71,7 @@ void write_memory_sba_simple(struct target *target, target_addr_t addr, uint32_t uint32_t write_size, uint32_t sbcs); void read_memory_sba_simple(struct target *target, target_addr_t addr, uint32_t *rd_buf, uint32_t read_size, uint32_t sbcs); +static int riscv013_test_compliance(struct target *target); /** * Since almost everything can be accomplish by scanning the dbus register, all @@ -1594,6 +1595,7 @@ static int init_target(struct command_context *cmd_ctx, generic_info->dmi_read = &dmi_read; generic_info->dmi_write = &dmi_write; generic_info->test_sba_config_reg = &riscv013_test_sba_config_reg; + generic_info->test_compliance = &riscv013_test_compliance; generic_info->version_specific = calloc(1, sizeof(riscv013_info_t)); if (!generic_info->version_specific) return ERROR_FAIL; @@ -3376,3 +3378,457 @@ void riscv013_clear_abstract_error(struct target *target) /* Clear the error status. */ dmi_write(target, DMI_ABSTRACTCS, abstractcs & DMI_ABSTRACTCS_CMDERR); } + +#define COMPLIANCE_TEST(b, message) \ +{ \ + int pass = 0; \ + if (b) { \ + pass = 1; \ + passed_tests++; \ + } \ + LOG_INFO("%s test %d (%s)\n", (pass) ? "PASSED" : "FAILED", total_tests, message); \ + assert(pass); \ + total_tests++; \ +} + +#define COMPLIANCE_MUST_PASS(b) COMPLIANCE_TEST(ERROR_OK == (b), "Regular calls must return ERROR_OK") + +#define COMPLIANCE_READ(target, addr, value) COMPLIANCE_MUST_PASS(dmi_read(target, addr, value)) +#define COMPLIANCE_WRITE(target, addr, value) COMPLIANCE_MUST_PASS(dmi_write(target, addr, value)) + +#define COMPLIANCE_CHECK_RO(target, addr) \ +{ \ + uint32_t orig; \ + uint32_t inverse; \ + COMPLIANCE_READ(target, &orig, addr); \ + COMPLIANCE_WRITE(target, addr, ~orig); \ + COMPLIANCE_READ(target, &inverse, addr); \ + COMPLIANCE_TEST(orig == inverse, "Register must be read-only"); \ +} + +int riscv013_test_compliance(struct target *target) +{ + LOG_INFO("Testing Compliance against RISC-V Debug Spec v0.13"); + + if (!riscv_rtos_enabled(target)) { + LOG_ERROR("Please run with -rtos riscv to run compliance test."); + return ERROR_FAIL; + } + + int total_tests = 0; + int passed_tests = 0; + + uint32_t dmcontrol_orig = DMI_DMCONTROL_DMACTIVE; + uint32_t dmcontrol; + uint32_t testvar; + uint32_t testvar_read; + riscv_reg_t value; + RISCV013_INFO(info); + + /* All the bits of HARTSEL are covered by the examine sequence. */ + + /* hartreset */ + /* This field is optional. Either we can read and write it to 1/0, + or it is tied to 0. This check doesn't really do anything, but + it does attempt to set the bit to 1 and then back to 0, which needs to + work if its implemented. */ + COMPLIANCE_WRITE(target, DMI_DMCONTROL, set_field(dmcontrol_orig, DMI_DMCONTROL_HARTRESET, 1)); + COMPLIANCE_WRITE(target, DMI_DMCONTROL, set_field(dmcontrol_orig, DMI_DMCONTROL_HARTRESET, 0)); + COMPLIANCE_READ(target, &dmcontrol, DMI_DMCONTROL); + COMPLIANCE_TEST((get_field(dmcontrol, DMI_DMCONTROL_HARTRESET) == 0), + "DMCONTROL.hartreset can be 0 or RW."); + + /* hasel */ + COMPLIANCE_WRITE(target, DMI_DMCONTROL, set_field(dmcontrol_orig, DMI_DMCONTROL_HASEL, 1)); + COMPLIANCE_WRITE(target, DMI_DMCONTROL, set_field(dmcontrol_orig, DMI_DMCONTROL_HASEL, 0)); + COMPLIANCE_READ(target, &dmcontrol, DMI_DMCONTROL); + COMPLIANCE_TEST((get_field(dmcontrol, DMI_DMCONTROL_HASEL) == 0), + "DMCONTROL.hasel can be 0 or RW."); + /* TODO: test that hamask registers exist if hasel does. */ + + /* haltreq */ + COMPLIANCE_MUST_PASS(riscv_halt_all_harts(target)); + /* This bit is not actually readable according to the spec, so nothing to check.*/ + + /* DMSTATUS */ + COMPLIANCE_CHECK_RO(target, DMI_DMSTATUS); + + /* resumereq */ + /* This bit is not actually readable according to the spec, so nothing to check.*/ + COMPLIANCE_MUST_PASS(riscv_resume_all_harts(target)); + + /* Halt all harts again so the test can continue.*/ + COMPLIANCE_MUST_PASS(riscv_halt_all_harts(target)); + + /* HARTINFO: Read-Only. This is per-hart, so need to adjust hartsel. */ + uint32_t hartinfo; + COMPLIANCE_READ(target, &hartinfo, DMI_HARTINFO); + for (int hartsel = 0; hartsel < riscv_count_harts(target); hartsel++) { + COMPLIANCE_MUST_PASS(riscv_set_current_hartid(target, hartsel)); + + COMPLIANCE_CHECK_RO(target, DMI_HARTINFO); + + /* $dscratch CSRs */ + uint32_t nscratch = get_field(hartinfo, DMI_HARTINFO_NSCRATCH); + for (unsigned int d = 0; d < nscratch; d++) { + riscv_reg_t testval, testval_read; + /* Because DSCRATCH is not guaranteed to last across PB executions, need to put + this all into one PB execution. Which may not be possible on all implementations.*/ + if (info->progbufsize >= 5) { + for (testval = 0x0011223300112233; + testval != 0xDEAD; + testval = testval == 0x0011223300112233 ? ~testval : 0xDEAD) { + COMPLIANCE_TEST(register_write_direct(target, GDB_REGNO_S0, testval) == ERROR_OK, + "Need to be able to write S0 in order to test DSCRATCH."); + struct riscv_program program32; + riscv_program_init(&program32, target); + riscv_program_csrw(&program32, GDB_REGNO_S0, GDB_REGNO_DSCRATCH + d); + riscv_program_csrr(&program32, GDB_REGNO_S1, GDB_REGNO_DSCRATCH + d); + riscv_program_fence(&program32); + riscv_program_ebreak(&program32); + COMPLIANCE_TEST(riscv_program_exec(&program32, target) == ERROR_OK, + "Accessing DSCRATCH with program buffer should succeed."); + COMPLIANCE_TEST(register_read_direct(target, &testval_read, GDB_REGNO_S1) == ERROR_OK, + "Need to be able to read S1 in order to test DSCRATCH."); + if (riscv_xlen(target) > 32) { + COMPLIANCE_TEST(testval == testval_read, + "All DSCRATCH registers in HARTINFO must be R/W."); + } else { + COMPLIANCE_TEST(testval_read == (testval & 0xFFFFFFFF), + "All DSCRATCH registers in HARTINFO must be R/W."); + } + } + } + } + /* TODO: dataaccess */ + if (get_field(hartinfo, DMI_HARTINFO_DATAACCESS)) { + /* TODO: Shadowed in memory map. */ + /* TODO: datasize */ + /* TODO: dataaddr */ + } else { + /* TODO: Shadowed in CSRs. */ + /* TODO: datasize */ + /* TODO: dataaddr */ + } + + } + + /* HALTSUM -- TODO: More than 32 harts. Would need to loop over this to set hartsel */ + /* TODO: HALTSUM2, HALTSUM3 */ + /* HALTSUM0 */ + uint32_t expected_haltsum0 = 0; + for (int i = 0; i < MIN(riscv_count_harts(target), 32); i++) + expected_haltsum0 |= (1 << i); + + COMPLIANCE_READ(target, &testvar_read, DMI_HALTSUM0); + COMPLIANCE_TEST(testvar_read == expected_haltsum0, + "HALTSUM0 should report summary of up to 32 halted harts"); + + COMPLIANCE_WRITE(target, DMI_HALTSUM0, 0xffffffff); + COMPLIANCE_READ(target, &testvar_read, DMI_HALTSUM0); + COMPLIANCE_TEST(testvar_read == expected_haltsum0, "HALTSUM0 should be R/O"); + + COMPLIANCE_WRITE(target, DMI_HALTSUM0, 0x0); + COMPLIANCE_READ(target, &testvar_read, DMI_HALTSUM0); + COMPLIANCE_TEST(testvar_read == expected_haltsum0, "HALTSUM0 should be R/O"); + + /* HALTSUM1 */ + uint32_t expected_haltsum1 = 0; + for (int i = 0; i < MIN(riscv_count_harts(target), 1024); i += 32) + expected_haltsum1 |= (1 << (i/32)); + + COMPLIANCE_READ(target, &testvar_read, DMI_HALTSUM1); + COMPLIANCE_TEST(testvar_read == expected_haltsum1, + "HALTSUM1 should report summary of up to 1024 halted harts"); + + COMPLIANCE_WRITE(target, DMI_HALTSUM1, 0xffffffff); + COMPLIANCE_READ(target, &testvar_read, DMI_HALTSUM1); + COMPLIANCE_TEST(testvar_read == expected_haltsum1, "HALTSUM1 should be R/O"); + + COMPLIANCE_WRITE(target, DMI_HALTSUM1, 0x0); + COMPLIANCE_READ(target, &testvar_read, DMI_HALTSUM1); + COMPLIANCE_TEST(testvar_read == expected_haltsum1, "HALTSUM1 should be R/O"); + + /* TODO: HAWINDOWSEL */ + + /* TODO: HAWINDOW */ + + /* ABSTRACTCS */ + + uint32_t abstractcs; + COMPLIANCE_READ(target, &abstractcs, DMI_ABSTRACTCS); + + /* Check that all reported Data Words are really R/W */ + for (int invert = 0; invert < 2; invert++) { + for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT); i++) { + testvar = (i + 1) * 0x11111111; + if (invert) + testvar = ~testvar; + COMPLIANCE_WRITE(target, DMI_DATA0 + i, testvar); + } + for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT); i++) { + testvar = (i + 1) * 0x11111111; + if (invert) + testvar = ~testvar; + COMPLIANCE_READ(target, &testvar_read, DMI_DATA0 + i); + COMPLIANCE_TEST(testvar_read == testvar, "All reported DATA words must be R/W"); + } + } + + /* Check that all reported ProgBuf words are really R/W */ + for (int invert = 0; invert < 2; invert++) { + for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE); i++) { + testvar = (i + 1) * 0x11111111; + if (invert) + testvar = ~testvar; + COMPLIANCE_WRITE(target, DMI_PROGBUF0 + i, testvar); + } + for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE); i++) { + testvar = (i + 1) * 0x11111111; + if (invert) + testvar = ~testvar; + COMPLIANCE_READ(target, &testvar_read, DMI_PROGBUF0 + i); + COMPLIANCE_TEST(testvar_read == testvar, "All reported PROGBUF words must be R/W"); + } + } + + /* TODO: Cause and clear all error types */ + + /* COMMAND + According to the spec, this register is only W, so can't really check the read result. + But at any rate, this is not legal and should cause an error. */ + COMPLIANCE_WRITE(target, DMI_COMMAND, 0xAAAAAAAA); + COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTCS); + COMPLIANCE_TEST(get_field(testvar_read, DMI_ABSTRACTCS_CMDERR) == CMDERR_NOT_SUPPORTED, \ + "Illegal COMMAND should result in UNSUPPORTED"); + COMPLIANCE_WRITE(target, DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR); + + COMPLIANCE_WRITE(target, DMI_COMMAND, 0x55555555); + COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTCS); + COMPLIANCE_TEST(get_field(testvar_read, DMI_ABSTRACTCS_CMDERR) == CMDERR_NOT_SUPPORTED, \ + "Illegal COMMAND should result in UNSUPPORTED"); + COMPLIANCE_WRITE(target, DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR); + + /* Basic Abstract Commands */ + for (unsigned int i = 1; i < 32; i = i << 1) { + riscv_reg_t testval = i | ((i + 1ULL) << 32); + riscv_reg_t testval_read; + COMPLIANCE_TEST(ERROR_OK == register_write_direct(target, GDB_REGNO_ZERO + i, testval), + "GPR Writes should be supported."); + COMPLIANCE_MUST_PASS(write_abstract_arg(target, 0, 0xDEADBEEFDEADBEEF, 64)); + COMPLIANCE_TEST(ERROR_OK == register_read_direct(target, &testval_read, GDB_REGNO_ZERO + i), + "GPR Reads should be supported."); + if (riscv_xlen(target) > 32) { + /* Dummy comment to satisfy linter, since removing the brances here doesn't actually compile. */ + COMPLIANCE_TEST(testval == testval_read, "GPR Reads and writes should be supported."); + } else { + /* Dummy comment to satisfy linter, since removing the brances here doesn't actually compile. */ + COMPLIANCE_TEST((testval & 0xFFFFFFFF) == testval_read, "GPR Reads and writes should be supported."); + } + } + + /* ABSTRACTAUTO + See which bits are actually writable */ + COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0xFFFFFFFF); + uint32_t abstractauto; + uint32_t busy; + COMPLIANCE_READ(target, &abstractauto, DMI_ABSTRACTAUTO); + COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0x0); + if (abstractauto > 0) { + /* This mechanism only works when you have a reasonable sized progbuf, which is not + a true compliance requirement. */ + if (info->progbufsize >= 3) { + + testvar = 0; + COMPLIANCE_TEST(ERROR_OK == register_write_direct(target, GDB_REGNO_S0, 0), + "Need to be able to write S0 to test ABSTRACTAUTO"); + struct riscv_program program; + COMPLIANCE_MUST_PASS(riscv_program_init(&program, target)); + /* This is also testing that WFI() is a NOP during debug mode. */ + COMPLIANCE_MUST_PASS(riscv_program_insert(&program, wfi())); + COMPLIANCE_MUST_PASS(riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, 1)); + COMPLIANCE_MUST_PASS(riscv_program_ebreak(&program)); + COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0x0); + COMPLIANCE_MUST_PASS(riscv_program_exec(&program, target)); + testvar++; + COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0xFFFFFFFF); + COMPLIANCE_READ(target, &abstractauto, DMI_ABSTRACTAUTO); + uint32_t autoexec_data = get_field(abstractauto, DMI_ABSTRACTAUTO_AUTOEXECDATA); + uint32_t autoexec_progbuf = get_field(abstractauto, DMI_ABSTRACTAUTO_AUTOEXECPROGBUF); + for (unsigned int i = 0; i < 12; i++) { + COMPLIANCE_READ(target, &testvar_read, DMI_DATA0 + i); + do { + COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTCS); + busy = get_field(testvar_read, DMI_ABSTRACTCS_BUSY); + } while (busy); + if (autoexec_data & (1 << i)) { + COMPLIANCE_TEST(i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT), + "AUTOEXEC may be writable up to DATACOUNT bits."); + testvar++; + } + } + for (unsigned int i = 0; i < 16; i++) { + COMPLIANCE_READ(target, &testvar_read, DMI_PROGBUF0 + i); + do { + COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTCS); + busy = get_field(testvar_read, DMI_ABSTRACTCS_BUSY); + } while (busy); + if (autoexec_progbuf & (1 << i)) { + COMPLIANCE_TEST(i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE), + "AUTOEXEC may be writable up to PROGBUFSIZE bits."); + testvar++; + } + } + + COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0); + COMPLIANCE_TEST(ERROR_OK == register_read_direct(target, &value, GDB_REGNO_S0), + "Need to be able to read S0 to test ABSTRACTAUTO"); + + COMPLIANCE_TEST(testvar == value, + "ABSTRACTAUTO should cause COMMAND to run the expected number of times."); + } + } + + /* Single-Step each hart. */ + for (int hartsel = 0; hartsel < riscv_count_harts(target); hartsel++) { + COMPLIANCE_MUST_PASS(riscv_set_current_hartid(target, hartsel)); + COMPLIANCE_MUST_PASS(riscv013_on_step(target)); + COMPLIANCE_MUST_PASS(riscv013_step_current_hart(target)); + COMPLIANCE_TEST(riscv_halt_reason(target, hartsel) == RISCV_HALT_SINGLESTEP, + "Single Step should result in SINGLESTEP"); + } + + /* Core Register Tests */ + uint64_t bogus_dpc = 0xdeadbeef; + for (int hartsel = 0; hartsel < riscv_count_harts(target); hartsel++) { + COMPLIANCE_MUST_PASS(riscv_set_current_hartid(target, hartsel)); + + /* DCSR Tests */ + COMPLIANCE_MUST_PASS(register_write_direct(target, GDB_REGNO_DCSR, 0x0)); + COMPLIANCE_MUST_PASS(register_read_direct(target, &value, GDB_REGNO_DCSR)); + COMPLIANCE_TEST(value != 0, "Not all bits in DCSR are writable by Debugger"); + COMPLIANCE_MUST_PASS(register_write_direct(target, GDB_REGNO_DCSR, 0xFFFFFFFF)); + COMPLIANCE_MUST_PASS(register_read_direct(target, &value, GDB_REGNO_DCSR)); + COMPLIANCE_TEST(value != 0, "At least some bits in DCSR must be 1"); + + /* DPC. Note that DPC is sign-extended. */ + riscv_reg_t dpcmask = 0xFFFFFFFCUL; + riscv_reg_t dpc; + + if (riscv_xlen(target) > 32) + dpcmask |= (0xFFFFFFFFULL << 32); + + if (riscv_supports_extension(target, riscv_current_hartid(target), 'C')) + dpcmask |= 0x2; + + COMPLIANCE_MUST_PASS(register_write_direct(target, GDB_REGNO_DPC, dpcmask)); + COMPLIANCE_MUST_PASS(register_read_direct(target, &dpc, GDB_REGNO_DPC)); + COMPLIANCE_TEST(dpcmask == dpc, + "DPC must be sign-extended to XLEN and writable to all-1s (except the least significant bits)"); + COMPLIANCE_MUST_PASS(register_write_direct(target, GDB_REGNO_DPC, 0)); + COMPLIANCE_MUST_PASS(register_read_direct(target, &dpc, GDB_REGNO_DPC)); + COMPLIANCE_TEST(dpc == 0, "DPC must be writable to 0."); + if (hartsel == 0) + bogus_dpc = dpc; /* For a later test step */ + } + + /* NDMRESET + Asserting non-debug module reset should not reset Debug Module state. + But it should reset Hart State, e.g. DPC should get a different value. + Also make sure that DCSR reports cause of 'HALT' even though previously we single-stepped. + */ + + /* Write some registers. They should not be impacted by ndmreset. */ + COMPLIANCE_WRITE(target, DMI_COMMAND, 0xFFFFFFFF); + + for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE); i++) { + testvar = (i + 1) * 0x11111111; + COMPLIANCE_WRITE(target, DMI_PROGBUF0 + i, testvar); + } + + for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT); i++) { + testvar = (i + 1) * 0x11111111; + COMPLIANCE_WRITE(target, DMI_DATA0 + i, testvar); + } + + COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0xFFFFFFFF); + COMPLIANCE_READ(target, &abstractauto, DMI_ABSTRACTAUTO); + + /* Pulse reset. */ + target->reset_halt = true; + COMPLIANCE_MUST_PASS(riscv_set_current_hartid(target, 0)); + COMPLIANCE_TEST(ERROR_OK == assert_reset(target), "Must be able to assert NDMRESET"); + COMPLIANCE_TEST(ERROR_OK == deassert_reset(target), "Must be able to deassert NDMRESET"); + + /* Verify that most stuff is not affected by ndmreset. */ + COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTCS); + COMPLIANCE_TEST(get_field(testvar_read, DMI_ABSTRACTCS_CMDERR) == CMDERR_NOT_SUPPORTED, + "NDMRESET should not affect DMI_ABSTRACTCS"); + COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTAUTO); + COMPLIANCE_TEST(testvar_read == abstractauto, "NDMRESET should not affect DMI_ABSTRACTAUTO"); + + /* Clean up to avoid future test failures */ + COMPLIANCE_WRITE(target, DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR); + COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0); + + for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE); i++) { + testvar = (i + 1) * 0x11111111; + COMPLIANCE_READ(target, &testvar_read, DMI_PROGBUF0 + i); + COMPLIANCE_TEST(testvar_read == testvar, "PROGBUF words must not be affected by NDMRESET"); + } + + for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT); i++) { + testvar = (i + 1) * 0x11111111; + COMPLIANCE_READ(target, &testvar_read, DMI_DATA0 + i); + COMPLIANCE_TEST(testvar_read == testvar, "DATA words must not be affected by NDMRESET"); + } + + /* Verify that DPC *is* affected by ndmreset. Since we don't know what it *should* be, + just verify that at least it's not the bogus value anymore. */ + + COMPLIANCE_TEST(bogus_dpc != 0xdeadbeef, "BOGUS DPC should have been set somehow (bug in compliance test)"); + COMPLIANCE_MUST_PASS(register_read_direct(target, &value, GDB_REGNO_DPC)); + COMPLIANCE_TEST(bogus_dpc != value, "NDMRESET should move DPC to reset value."); + + COMPLIANCE_TEST(riscv_halt_reason(target, 0) == RISCV_HALT_INTERRUPT, + "After NDMRESET halt, DCSR should report cause of halt"); + + /* DMACTIVE -- deasserting DMACTIVE should reset all the above values. */ + + /* Toggle dmactive */ + COMPLIANCE_WRITE(target, DMI_DMCONTROL, 0); + COMPLIANCE_WRITE(target, DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE); + COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTCS); + COMPLIANCE_TEST(get_field(testvar_read, DMI_ABSTRACTCS_CMDERR) == 0, "ABSTRACTCS.cmderr should reset to 0"); + COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTAUTO); + COMPLIANCE_TEST(testvar_read == 0, "ABSTRACTAUTO should reset to 0"); + + for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE); i++) { + COMPLIANCE_READ(target, &testvar_read, DMI_PROGBUF0 + i); + COMPLIANCE_TEST(testvar_read == 0, "PROGBUF words should reset to 0"); + } + + for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT); i++) { + COMPLIANCE_READ(target, &testvar_read, DMI_DATA0 + i); + COMPLIANCE_TEST(testvar_read == 0, "DATA words should reset to 0"); + } + + /* + * TODO: + * DCSR.cause priorities + * DCSR.stoptime/stopcycle + * DCSR.stepie + * DCSR.ebreak + * DCSR.prv + */ + + /* Halt every hart for any follow-up tests*/ + COMPLIANCE_MUST_PASS(riscv_halt_all_harts(target)); + + LOG_INFO("PASSED %d of %d TESTS\n", passed_tests, total_tests); + + if (total_tests == passed_tests) + return ERROR_OK; + else + return ERROR_FAIL; +} diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index 7b965f2..dceed61 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -1323,6 +1323,25 @@ COMMAND_HANDLER(riscv_set_reset_timeout_sec) return ERROR_OK; } +COMMAND_HANDLER(riscv_test_compliance) { + + struct target *target = get_current_target(CMD_CTX); + + RISCV_INFO(r); + + if (CMD_ARGC > 0) { + LOG_ERROR("Command does not take any parameters."); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + if (r->test_compliance) { + return r->test_compliance(target); + } else { + LOG_ERROR("This target does not support this command (may implement an older version of the spec)."); + return ERROR_FAIL; + } +} + COMMAND_HANDLER(riscv_set_prefer_sba) { if (CMD_ARGC != 1) { @@ -1569,6 +1588,13 @@ COMMAND_HANDLER(riscv_test_sba_config_reg) static const struct command_registration riscv_exec_command_handlers[] = { { + .name = "test_compliance", + .handler = riscv_test_compliance, + .mode = COMMAND_EXEC, + .usage = "riscv test_compliance", + .help = "Runs a basic compliance test suite against the RISC-V Debug Spec." + }, + { .name = "set_command_timeout_sec", .handler = riscv_set_command_timeout_sec, .mode = COMMAND_ANY, diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index 3123db2..deca21e 100644 --- a/src/target/riscv/riscv.h +++ b/src/target/riscv/riscv.h @@ -129,6 +129,7 @@ typedef struct { int (*test_sba_config_reg)(struct target *target, target_addr_t legal_address, uint32_t num_words, target_addr_t illegal_address, bool run_sbbusyerror_test); + int (*test_compliance)(struct target *target); } riscv_info_t; /* Wall-clock timeout for a command/access. Settable via RISC-V Target commands.*/ diff --git a/tools/git2cl b/tools/git2cl deleted file mode 160000 -Subproject 8373c9f74993e218a08819cbcdbab3f3564bbeb diff --git a/tools/git2cl/COPYING b/tools/git2cl/COPYING new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/tools/git2cl/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/tools/git2cl/README b/tools/git2cl/README new file mode 100644 index 0000000..4084080 --- /dev/null +++ b/tools/git2cl/README @@ -0,0 +1,45 @@ +git2cl +====== + +This is a quick'n'dirty tool to convert git logs to GNU ChangeLog +format. + +The tool invokes 'git log' internally unless you pipe a log to it. +Thus, typically you would use it as follows: + +........................................................................... +jas@mocca:~/src/libtasn1$ git2cl > ChangeLog +jas@mocca:~/src/libtasn1$ +........................................................................... + +If you don't want git2cl to invoke git log internally, you can use it +as a pipe. It needs a git log generated with --pretty --numstat and +--summary. You can use it as follows: + +........................................................................... +jas@mocca:~/src/libtasn1$ git log --pretty --numstat --summary | ~/src/git2cl/git2cl > ChangeLog +jas@mocca:~/src/libtasn1$ +........................................................................... + +The output format is specified by: + +link:http://www.gnu.org/prep/standards/html_node/Change-Logs.html[] + +My inspiration for writing this tool was the +link:http://www.red-bean.com/cvs2cl/[cvs2cl] tool, which I have been +using in several projects. Replacing it was necessary to seriously +consider switching from CVS to GIT for my projects. + +The canonical home page for git2cl is: +link:http://josefsson.org/git2cl/[] and its repository can be found at +link:http://repo.or.cz/w/git2cl.git[]. + +Credits +------- + +Luis Mondesi contributed several improvements. + +Support +------- + +Try talking to mailto:simon@josefsson.org[Simon Josefsson]. diff --git a/tools/git2cl/README.html b/tools/git2cl/README.html new file mode 100644 index 0000000..f82da5d --- /dev/null +++ b/tools/git2cl/README.html @@ -0,0 +1,392 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<meta name="generator" content="AsciiDoc 8.2.7" />
+<style type="text/css">
+/* Debug borders */
+p, li, dt, dd, div, pre, h1, h2, h3, h4, h5, h6 {
+/*
+ border: 1px solid red;
+*/
+}
+
+body {
+ margin: 1em 5% 1em 5%;
+}
+
+a {
+ color: blue;
+ text-decoration: underline;
+}
+a:visited {
+ color: fuchsia;
+}
+
+em {
+ font-style: italic;
+ color: navy;
+}
+
+strong {
+ font-weight: bold;
+ color: #083194;
+}
+
+tt {
+ color: navy;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ color: #527bbd;
+ font-family: sans-serif;
+ margin-top: 1.2em;
+ margin-bottom: 0.5em;
+ line-height: 1.3;
+}
+
+h1, h2, h3 {
+ border-bottom: 2px solid silver;
+}
+h2 {
+ padding-top: 0.5em;
+}
+h3 {
+ float: left;
+}
+h3 + * {
+ clear: left;
+}
+
+div.sectionbody {
+ font-family: serif;
+ margin-left: 0;
+}
+
+hr {
+ border: 1px solid silver;
+}
+
+p {
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+}
+
+ul, ol, li > p {
+ margin-top: 0;
+}
+
+pre {
+ padding: 0;
+ margin: 0;
+}
+
+span#author {
+ color: #527bbd;
+ font-family: sans-serif;
+ font-weight: bold;
+ font-size: 1.1em;
+}
+span#email {
+}
+span#revision {
+ font-family: sans-serif;
+}
+
+div#footer {
+ font-family: sans-serif;
+ font-size: small;
+ border-top: 2px solid silver;
+ padding-top: 0.5em;
+ margin-top: 4.0em;
+}
+div#footer-text {
+ float: left;
+ padding-bottom: 0.5em;
+}
+div#footer-badges {
+ float: right;
+ padding-bottom: 0.5em;
+}
+
+div#preamble,
+div.tableblock, div.imageblock, div.exampleblock, div.verseblock,
+div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
+div.admonitionblock {
+ margin-right: 10%;
+ margin-top: 1.5em;
+ margin-bottom: 1.5em;
+}
+div.admonitionblock {
+ margin-top: 2.5em;
+ margin-bottom: 2.5em;
+}
+
+div.content { /* Block element content. */
+ padding: 0;
+}
+
+/* Block element titles. */
+div.title, caption.title {
+ color: #527bbd;
+ font-family: sans-serif;
+ font-weight: bold;
+ text-align: left;
+ margin-top: 1.0em;
+ margin-bottom: 0.5em;
+}
+div.title + * {
+ margin-top: 0;
+}
+
+td div.title:first-child {
+ margin-top: 0.0em;
+}
+div.content div.title:first-child {
+ margin-top: 0.0em;
+}
+div.content + div.title {
+ margin-top: 0.0em;
+}
+
+div.sidebarblock > div.content {
+ background: #ffffee;
+ border: 1px solid silver;
+ padding: 0.5em;
+}
+
+div.listingblock {
+ margin-right: 0%;
+}
+div.listingblock > div.content {
+ border: 1px solid silver;
+ background: #f4f4f4;
+ padding: 0.5em;
+}
+
+div.quoteblock {
+ padding-left: 2.0em;
+}
+div.quoteblock > div.attribution {
+ padding-top: 0.5em;
+ text-align: right;
+}
+
+div.verseblock {
+ padding-left: 2.0em;
+}
+div.verseblock > div.content {
+ white-space: pre;
+}
+div.verseblock > div.attribution {
+ padding-top: 0.75em;
+ text-align: left;
+}
+/* DEPRECATED: Pre version 8.2.7 verse style literal block. */
+div.verseblock + div.attribution {
+ text-align: left;
+}
+
+div.admonitionblock .icon {
+ vertical-align: top;
+ font-size: 1.1em;
+ font-weight: bold;
+ text-decoration: underline;
+ color: #527bbd;
+ padding-right: 0.5em;
+}
+div.admonitionblock td.content {
+ padding-left: 0.5em;
+ border-left: 2px solid silver;
+}
+
+div.exampleblock > div.content {
+ border-left: 2px solid silver;
+ padding: 0.5em;
+}
+
+div.imageblock div.content { padding-left: 0; }
+div.imageblock img { border: 1px solid silver; }
+span.image img { border-style: none; }
+
+dl {
+ margin-top: 0.8em;
+ margin-bottom: 0.8em;
+}
+dt {
+ margin-top: 0.5em;
+ margin-bottom: 0;
+ font-style: normal;
+}
+dd > *:first-child {
+ margin-top: 0.1em;
+}
+
+ul, ol {
+ list-style-position: outside;
+}
+div.olist > ol {
+ list-style-type: decimal;
+}
+div.olist2 > ol {
+ list-style-type: lower-alpha;
+}
+
+div.tableblock > table {
+ border: 3px solid #527bbd;
+}
+thead {
+ font-family: sans-serif;
+ font-weight: bold;
+}
+tfoot {
+ font-weight: bold;
+}
+
+div.hlist {
+ margin-top: 0.8em;
+ margin-bottom: 0.8em;
+}
+div.hlist td {
+ padding-bottom: 15px;
+}
+td.hlist1 {
+ vertical-align: top;
+ font-style: normal;
+ padding-right: 0.8em;
+}
+td.hlist2 {
+ vertical-align: top;
+}
+
+@media print {
+ div#footer-badges { display: none; }
+}
+
+div#toctitle {
+ color: #527bbd;
+ font-family: sans-serif;
+ font-size: 1.1em;
+ font-weight: bold;
+ margin-top: 1.0em;
+ margin-bottom: 0.1em;
+}
+
+div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+div.toclevel2 {
+ margin-left: 2em;
+ font-size: 0.9em;
+}
+div.toclevel3 {
+ margin-left: 4em;
+ font-size: 0.9em;
+}
+div.toclevel4 {
+ margin-left: 6em;
+ font-size: 0.9em;
+}
+/* Workarounds for IE6's broken and incomplete CSS2. */
+
+div.sidebar-content {
+ background: #ffffee;
+ border: 1px solid silver;
+ padding: 0.5em;
+}
+div.sidebar-title, div.image-title {
+ color: #527bbd;
+ font-family: sans-serif;
+ font-weight: bold;
+ margin-top: 0.0em;
+ margin-bottom: 0.5em;
+}
+
+div.listingblock div.content {
+ border: 1px solid silver;
+ background: #f4f4f4;
+ padding: 0.5em;
+}
+
+div.quoteblock-attribution {
+ padding-top: 0.5em;
+ text-align: right;
+}
+
+div.verseblock-content {
+ white-space: pre;
+}
+div.verseblock-attribution {
+ padding-top: 0.75em;
+ text-align: left;
+}
+
+div.exampleblock-content {
+ border-left: 2px solid silver;
+ padding-left: 0.5em;
+}
+
+/* IE6 sets dynamically generated links as visited. */
+div#toc a:visited { color: blue; }
+
+/* Because IE6 child selector is broken. */
+div.olist2 ol {
+ list-style-type: lower-alpha;
+}
+div.olist2 div.olist ol {
+ list-style-type: decimal;
+}
+</style>
+<title>git2cl</title>
+</head>
+<body>
+<div id="header">
+<h1>git2cl</h1>
+</div>
+<div id="preamble">
+<div class="sectionbody">
+<div class="para"><p>This is a quick'n'dirty tool to convert git logs to GNU ChangeLog
+format.</p></div>
+<div class="para"><p>The tool invokes <em>git log</em> internally unless you pipe a log to it.
+Thus, typically you would use it as follows:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>jas@mocca:~/src/libtasn1$ git2cl > ChangeLog
+jas@mocca:~/src/libtasn1$</tt></pre>
+</div></div>
+<div class="para"><p>If you don't want git2cl to invoke git log internally, you can use it
+as a pipe. It needs a git log generated with —pretty —numstat and
+—summary. You can use it as follows:</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>jas@mocca:~/src/libtasn1$ git log --pretty --numstat --summary | ~/src/git2cl/git2cl > ChangeLog
+jas@mocca:~/src/libtasn1$</tt></pre>
+</div></div>
+<div class="para"><p>The output format is specified by:</p></div>
+<div class="para"><p><a href="http://www.gnu.org/prep/standards/html_node/Change-Logs.html">http://www.gnu.org/prep/standards/html_node/Change-Logs.html</a></p></div>
+<div class="para"><p>My inspiration for writing this tool was the
+<a href="http://www.red-bean.com/cvs2cl/">cvs2cl</a> tool, which I have been
+using in several projects. Replacing it was necessary to seriously
+consider switching from CVS to GIT for my projects.</p></div>
+<div class="para"><p>The canonical home page for git2cl is:
+<a href="http://josefsson.org/git2cl/">http://josefsson.org/git2cl/</a> and its repository can be found at
+<a href="http://repo.or.cz/w/git2cl.git">http://repo.or.cz/w/git2cl.git</a>.</p></div>
+</div>
+</div>
+<h2 id="_credits">Credits</h2>
+<div class="sectionbody">
+<div class="para"><p>Luis Mondesi contributed several improvements.</p></div>
+</div>
+<h2 id="_support">Support</h2>
+<div class="sectionbody">
+<div class="para"><p>Try talking to <a href="mailto:simon@josefsson.org">Simon Josefsson</a>.</p></div>
+</div>
+<div id="footer">
+<div id="footer-text">
+Last updated 2008-08-27 12:42:17 CEST
+</div>
+</div>
+</body>
+</html>
diff --git a/tools/git2cl/git2cl b/tools/git2cl/git2cl new file mode 100755 index 0000000..1c2ab3f --- /dev/null +++ b/tools/git2cl/git2cl @@ -0,0 +1,372 @@ +#!/usr/bin/perl + +# Copyright (C) 2007, 2008 Simon Josefsson <simon@josefsson.org> +# Copyright (C) 2007 Luis Mondesi <lemsx1@gmail.com> +# * calls git directly. To use it just: +# cd ~/Project/my_git_repo; git2cl > ChangeLog +# * implements strptime() +# * fixes bugs in $comment parsing +# - copy input before we remove leading spaces +# - skip "merge branch" statements as they don't +# have information about files (i.e. we never +# go into $state 2) +# - behaves like a pipe/filter if input is given from the CLI +# else it calls git log by itself +# +# The functions mywrap, last_line_len, wrap_log_entry are derived from +# the cvs2cl tool, see <http://www.red-bean.com/cvs2cl/>: +# Copyright (C) 2001,2002,2003,2004 Martyn J. Pearce <fluffy@cpan.org> +# Copyright (C) 1999 Karl Fogel <kfogel@red-bean.com> +# +# git2cl is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# git2cl is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with git2cl; see the file COPYING. If not, write to the Free +# Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +use strict; +use POSIX qw(strftime); +use Text::Wrap qw(wrap); +use FileHandle; + +use constant EMPTY_LOG_MESSAGE => '*** empty log message ***'; + +# this is a helper hash for stptime. +# Assumes you are calling 'git log ...' with LC_ALL=C +my %month = ( + 'Jan'=>0, + 'Feb'=>1, + 'Mar'=>2, + 'Apr'=>3, + 'May'=>4, + 'Jun'=>5, + 'Jul'=>6, + 'Aug'=>7, + 'Sep'=>8, + 'Oct'=>9, + 'Nov'=>10, + 'Dec'=>11, +); + +my $fh = new FileHandle; + +sub key_ready +{ + my ($rin, $nfd); + vec($rin, fileno(STDIN), 1) = 1; + return $nfd = select($rin, undef, undef, 0); +} + +sub strptime { + my $str = shift; + return undef if not defined $str; + + # we are parsing this format + # Fri Oct 26 00:42:56 2007 -0400 + # to these fields + # sec, min, hour, mday, mon, year, wday = -1, yday = -1, isdst = -1 + # Luis Mondesi <lemsx1@gmail.com> + my @date; + if ($str =~ /([[:alpha:]]{3})\s+([[:alpha:]]{3})\s+([[:digit:]]{1,2})\s+([[:digit:]]{1,2}):([[:digit:]]{1,2}):([[:digit:]]{1,2})\s+([[:digit:]]{4})/){ + push(@date,$6,$5,$4,$3,$month{$2},($7 - 1900),-1,-1,-1); + } else { + die ("Cannot parse date '$str'\n'"); + } + return @date; +} + +sub mywrap { + my ($indent1, $indent2, @text) = @_; + # If incoming text looks preformatted, don't get clever + my $text = Text::Wrap::wrap($indent1, $indent2, @text); + if ( grep /^\s+/m, @text ) { + return $text; + } + my @lines = split /\n/, $text; + $indent2 =~ s!^((?: {8})+)!"\t" x (length($1)/8)!e; + $lines[0] =~ s/^$indent1\s+/$indent1/; + s/^$indent2\s+/$indent2/ + for @lines[1..$#lines]; + my $newtext = join "\n", @lines; + $newtext .= "\n" + if substr($text, -1) eq "\n"; + return $newtext; +} + +sub last_line_len { + my $files_list = shift; + my @lines = split (/\n/, $files_list); + my $last_line = pop (@lines); + return length ($last_line); +} + +# A custom wrap function, sensitive to some common constructs used in +# log entries. +sub wrap_log_entry { + my $text = shift; # The text to wrap. + my $left_pad_str = shift; # String to pad with on the left. + + # These do NOT take left_pad_str into account: + my $length_remaining = shift; # Amount left on current line. + my $max_line_length = shift; # Amount left for a blank line. + + my $wrapped_text = ''; # The accumulating wrapped entry. + my $user_indent = ''; # Inherited user_indent from prev line. + + my $first_time = 1; # First iteration of the loop? + my $suppress_line_start_match = 0; # Set to disable line start checks. + + my @lines = split (/\n/, $text); + while (@lines) # Don't use `foreach' here, it won't work. + { + my $this_line = shift (@lines); + chomp $this_line; + + if ($this_line =~ /^(\s+)/) { + $user_indent = $1; + } + else { + $user_indent = ''; + } + + # If it matches any of the line-start regexps, print a newline now... + if ($suppress_line_start_match) + { + $suppress_line_start_match = 0; + } + elsif (($this_line =~ /^(\s*)\*\s+[a-zA-Z0-9]/) + || ($this_line =~ /^(\s*)\* [a-zA-Z0-9_\.\/\+-]+/) + || ($this_line =~ /^(\s*)\([a-zA-Z0-9_\.\/\+-]+(\)|,\s*)/) + || ($this_line =~ /^(\s+)(\S+)/) + || ($this_line =~ /^(\s*)- +/) + || ($this_line =~ /^()\s*$/) + || ($this_line =~ /^(\s*)\*\) +/) + || ($this_line =~ /^(\s*)[a-zA-Z0-9](\)|\.|\:) +/)) + { + $length_remaining = $max_line_length - (length ($user_indent)); + } + + # Now that any user_indent has been preserved, strip off leading + # whitespace, so up-folding has no ugly side-effects. + $this_line =~ s/^\s*//; + + # Accumulate the line, and adjust parameters for next line. + my $this_len = length ($this_line); + if ($this_len == 0) + { + # Blank lines should cancel any user_indent level. + $user_indent = ''; + $length_remaining = $max_line_length; + } + elsif ($this_len >= $length_remaining) # Line too long, try breaking it. + { + # Walk backwards from the end. At first acceptable spot, break + # a new line. + my $idx = $length_remaining - 1; + if ($idx < 0) { $idx = 0 }; + while ($idx > 0) + { + if (substr ($this_line, $idx, 1) =~ /\s/) + { + my $line_now = substr ($this_line, 0, $idx); + my $next_line = substr ($this_line, $idx); + $this_line = $line_now; + + # Clean whitespace off the end. + chomp $this_line; + + # The current line is ready to be printed. + $this_line .= "\n${left_pad_str}"; + + # Make sure the next line is allowed full room. + $length_remaining = $max_line_length - (length ($user_indent)); + + # Strip next_line, but then preserve any user_indent. + $next_line =~ s/^\s*//; + + # Sneak a peek at the user_indent of the upcoming line, so + # $next_line (which will now precede it) can inherit that + # indent level. Otherwise, use whatever user_indent level + # we currently have, which might be none. + my $next_next_line = shift (@lines); + if ((defined ($next_next_line)) && ($next_next_line =~ /^(\s+)/)) { + $next_line = $1 . $next_line if (defined ($1)); + # $length_remaining = $max_line_length - (length ($1)); + $next_next_line =~ s/^\s*//; + } + else { + $next_line = $user_indent . $next_line; + } + if (defined ($next_next_line)) { + unshift (@lines, $next_next_line); + } + unshift (@lines, $next_line); + + # Our new next line might, coincidentally, begin with one of + # the line-start regexps, so we temporarily turn off + # sensitivity to that until we're past the line. + $suppress_line_start_match = 1; + + last; + } + else + { + $idx--; + } + } + + if ($idx == 0) + { + # We bottomed out because the line is longer than the + # available space. But that could be because the space is + # small, or because the line is longer than even the maximum + # possible space. Handle both cases below. + + if ($length_remaining == ($max_line_length - (length ($user_indent)))) + { + # The line is simply too long -- there is no hope of ever + # breaking it nicely, so just insert it verbatim, with + # appropriate padding. + $this_line = "\n${left_pad_str}${this_line}"; + } + else + { + # Can't break it here, but may be able to on the next round... + unshift (@lines, $this_line); + $length_remaining = $max_line_length - (length ($user_indent)); + $this_line = "\n${left_pad_str}"; + } + } + } + else # $this_len < $length_remaining, so tack on what we can. + { + # Leave a note for the next iteration. + $length_remaining = $length_remaining - $this_len; + + if ($this_line =~ /\.$/) + { + $this_line .= " "; + $length_remaining -= 2; + } + else # not a sentence end + { + $this_line .= " "; + $length_remaining -= 1; + } + } + + # Unconditionally indicate that loop has run at least once. + $first_time = 0; + + $wrapped_text .= "${user_indent}${this_line}"; + } + + # One last bit of padding. + $wrapped_text .= "\n"; + + return $wrapped_text; +} + +# main + +my @date; +my $author; +my @files; +my $comment; + +my $state; # 0-header 1-comment 2-files +my $done = 0; + +$state = 0; + +# if reading from STDIN, we assume that we are +# getting git log as input +if (key_ready()) +{ + + #my $dummyfh; # don't care about writing + #($fh,$dummyfh) = FileHandle::pipe; + $fh->fdopen(*STDIN, 'r'); +} +else +{ + $fh->open("LC_ALL=C git log --pretty --numstat --summary|") + or die("Cannot execute git log...$!\n"); +} + +while (my $_l = <$fh>) { + #print STDERR "debug ($state, " . (@date ? (strftime "%Y-%m-%d", @date) : "") . "): `$_'\n"; + if ($state == 0) { + if ($_l =~ m,^Author: (.*),) { + $author = $1; + } + if ($_l =~ m,^Date: (.*),) { + @date = strptime($1); + } + $state = 1 if ($_l =~ m,^$, and $author and (@date+0>0)); + } elsif ($state == 1) { + # * modifying our input text is a bad choice + # let's make a copy of it first, then we remove spaces + # * if we meet a "merge branch" statement, we need to start + # over and find a real entry + # Luis Mondesi <lemsx1@gmail.com> + my $_s = $_l; + $_s =~ s/^ //g; + if ($_s =~ m/^Merge branch/) + { + $state=0; + next; + } + $comment = $comment . $_s; + $state = 2 if ($_l =~ m,^$,); + } elsif ($state == 2) { + if ($_l =~ m,^([0-9]+)\t([0-9]+)\t(.*)$,) { + push @files, $3; + } + $done = 1 if ($_l =~ m,^$,); + } + + if ($done) { + print (strftime "%Y-%m-%d $author\n\n", @date); + + my $files = join (", ", @files); + $files = mywrap ("\t", "\t", "* $files"), ": "; + + if (index($comment, EMPTY_LOG_MESSAGE) > -1 ) { + $comment = "[no log message]\n"; + } + + my $files_last_line_len = 0; + $files_last_line_len = last_line_len($files) + 1; + my $msg = wrap_log_entry($comment, "\t", 69-$files_last_line_len, 69); + + $msg =~ s/[ \t]+\n/\n/g; + + print "$files: $msg\n"; + + @date = (); + $author = ""; + @files = (); + $comment = ""; + + $state = 0; + $done = 0; + } +} + +if (@date + 0) +{ + print (strftime "%Y-%m-%d $author\n\n", @date); + my $msg = wrap_log_entry($comment, "\t", 69, 69); + $msg =~ s/[ \t]+\n/\n/g; + print "\t* $msg\n"; +} |