diff --git a/.gitignore b/.gitignore index 35f44c8..e26ae2f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ src/*.o tiny-AES-c/*.o -stlink-tool \ No newline at end of file +stlink-tool +stlink-tool.exe +/.vscode diff --git a/Makefile b/Makefile index df4affb..8a2d4bc 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,20 @@ -CFLAGS := -Wall -Wextra -Werror $(shell pkg-config --cflags libusb-1.0) -LDFLAGS := $(shell pkg-config --libs libusb-1.0) - -OBJS := src/main.o src/stlink.o src/crypto.o tiny-AES-c/aes.o +ifeq ($(OS),Windows_NT) + ifeq ($(MINGW_CHOST),i686-w64-mingw32) + LIBARCH := _32 + endif + ifeq ($(MINGW_CHOST),x86_64-w64-mingw32) + LIBARCH := _64 + endif + LIBARCH ?= + CC := gcc.exe + CFLAGS := -DWINDOWS -Wall -Wextra -Werror -Wno-unused-parameter -Wno-error=unused-parameter -Ilibusb + LDFLAGS := -Llibusb -lusb-1.0$(LIBARCH) -lWs2_32 -lmsvcrt + OBJS := src/main.o src/getopt.o src/stlink.o src/crypto.o tiny-AES-c/aes.o +else + CFLAGS := -Wall -Wextra -Werror -Wno-unused-parameter -Wno-error=unused-parameter $(shell pkg-config --cflags libusb-1.0) -g -Og + LDFLAGS := $(shell pkg-config --libs libusb-1.0) + OBJS := src/main.o src/stlink.o src/crypto.o tiny-AES-c/aes.o +endif %.o: %.c $(CC) $(CFLAGS) -o $@ -c $< @@ -11,4 +24,5 @@ stlink-tool: $(OBJS) clean: rm -f src/*.o + rm -f tiny-AES-c/*.o rm -f stlink-tool diff --git a/README.md b/README.md index 6a2ae2a..5c7e013 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,124 @@ # stlink-tool -libusb tool for flashing chinese ST-Link dongles. Please note that similarly to ST's updater, the uploaded firmware won't replace the bootloader (meaning that you should be able to reflash the original afterwards using [ST's firmware update utility](http://www.st.com/en/development-tools/stsw-link007.html)). +libusb tool for flashing ST-Link dongles. Please note that similarly to ST's updater, the uploaded firmware won't replace the bootloader (meaning that you should be able to reflash the original afterwards using [ST's firmware update utility](http://www.st.com/en/development-tools/stsw-link007.html)). ``` -Usage: ./stlink-tool [options] firmware.bin +Usage: stlink-tool.exe [options] [firmware.bin] Options: - -p Probe the ST-Link adapter - -h Show help + -h, --help Show help + -p, --probe Probe the ST-Link adapter + -d, --decrypt KEY Decrypt Firmware using KEY. Pass "" to use internal key. + -sd, --save_dec Save decripted firmware as filename + .dec + -t, --st_type TYPE Change STLink type to TYPE. + A for "STM32 Debugger+Audio" + B for "STM32 Debug+Mass storage+VCP" + E for "STM32 Debug+Mass storage+VCP" + F for "STM8/STM32 Debug+Mass storage+VCP+Bridge" (for STLink V3) + G for "STM8 Debug+Mass storage+VCP" + J for "STM32 Debugger" + M for "STM8/STM32 Debugger" + S for "STM8 Debugger" + -v, --ver S.J.X Change reported STLink sersion. + S is STLink version, J is JTAG version, + X is SWIM or MSD version. + -f, --fix Flash Anti-Clone Tag and Firmware Exists/EOF Tag + +Options for Modifying Device Config (Only for STLink v2 and up): + --usb_cur CURRENT Set the MaxPower reported in USB Descriptor + to CURRENT(mA) + --msd_name VOLUME Set the volsume name of the MSD drive to VOLUME. + --mbed_name NAME Set the MBED board name to NAME. + --dfu_opt OPT Set DFU Options to OPT. + OPT is the Decimal value of Bit Field: + bit1: "No Power Off" + bit2: "Autostart" + --dynamic_opt OPT Set Dynamic Option to OPT. + 'V': MSD Off + 'M': MSD On + 'W': MSD Always Off + --mco_out OPT Set MCO Output to OPT. OPT is the Hex value of: + Lower Nybble(MCO Source): + 0: None + 1: HSI + 2: HSE + 3: PLL + Upper Nybble (Divider): + Divider - 1 (Valid Divider 1-5) + --startup OPT Set Startup Preferences to OPT. + 0: High Power + 1: Balanced + 2: Low Power + 3: Default + To remove a configuration you can use the "" argument with the option + (Ex. --usb_cur "") or prefix the option with rm_ (Ex. --rm_usb_cur). + +Application in Flash is started when called without argument, after firmware +load or configuration change. ``` +stlink-tool has been tested under Debian based Linux and Windows x86/x64. + +## New stlink-tool Features + +* show ST-Link dongle information +* can show and modify device configuration (show is only for ST-Link V2.1) +* can modify STLink type and reported firmware version +* can add "Anti-Clone" Tag and "Firmware Flashed/EOF" Tag (to make flashed firmware bootable without needing to exit DFU on V2.1) +* can decrypt and flash firmwares taken from `STLinkUpgrade.jar` + +Examples: + +STLinkV2 + +``` +STLinkV2 Bootloader Found +STLink Type: M [STM8/STM32 Debugger] +Firmware Version: V2J38S7 + +Bootloader PID: 3748 +Reported Flash Size: 64KB + +STLink ID: ************************ +Firmware Encryption Key: ******************************** +Anti-Clone Key: ******************************** +Current Mode: 1 +``` + +STLinkV2-1 + +``` +Trying to switch STLINK/Application to bootloader +STLinkV2-1 Bootloader Found +STLink Type: B [STM32 Debug+Mass storage+VCP] +Firmware Version: V2J38M27 + +Current Device Configuration: +USB Current: [300mA] + +Bootloader PID: 3748 +HW Version: V2.0 Flags: 0x000000 +Reported Flash Size: 128KB + +STLink ID: ************************ +Firmware Encryption Key: ******************************** +Anti-Clone Key: ******************************** +Current Mode: 2 +``` + +This changes were done by referencing: +[GMMan/st-link-hack](https://github.com/GMMan/st-link-hack) +[sakana280's fork](https://github.com/sakana280/stlink-tool) + ## Compiling Required dependencies : - * C compiler (both clang and gcc seems to work great) - * libusb1 - * git +* C compiler (both clang and gcc seems to work great) +* libusb1 +* git ``` -git clone https://github.com/jeanthom/stlink-tool +git clone -b stlinkv21 https://github.com/GabyPCgeeK/stlink-tool.git cd stlink-tool git submodule init git submodule update @@ -32,5 +132,6 @@ make ST's firmware upload protocol is USB's DFU protocol with some twists. Every DFU command is issued with the 0xF3 prefix, and the command set does not exactly match USB's. Some documentation : - * http://www.st.com/content/ccc/resource/technical/document/application_note/6a/17/92/02/58/98/45/0c/CD00264379.pdf/files/CD00264379.pdf/jcr:content/translations/en.CD00264379.pdf - * http://www.usb.org/developers/docs/devclass_docs/DFU_1.1.pdf + +* +* diff --git a/libusb/COPYING b/libusb/COPYING new file mode 100644 index 0000000..5ab7695 --- /dev/null +++ b/libusb/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 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. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, 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 library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete 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 distribute a copy of this License along with the +Library. + + 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 Library or any portion +of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +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 Library, 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 Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you 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. + + If distribution of 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 satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be 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. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library 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. + + 9. 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 Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +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 with +this License. + + 11. 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 Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library 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 Library. + +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. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library 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. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +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 Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +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 + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. 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 LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. 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. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; 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. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/libusb/libusb-1.0_32.a b/libusb/libusb-1.0_32.a new file mode 100644 index 0000000..1fe83fe Binary files /dev/null and b/libusb/libusb-1.0_32.a differ diff --git a/libusb/libusb-1.0_64.a b/libusb/libusb-1.0_64.a new file mode 100644 index 0000000..52a494a Binary files /dev/null and b/libusb/libusb-1.0_64.a differ diff --git a/libusb/libusb.h b/libusb/libusb.h new file mode 100644 index 0000000..8a6b0bf --- /dev/null +++ b/libusb/libusb.h @@ -0,0 +1,2066 @@ +/* + * Public libusb header file + * Copyright © 2001 Johannes Erdfelt + * Copyright © 2007-2008 Daniel Drake + * Copyright © 2012 Pete Batard + * Copyright © 2012-2018 Nathan Hjelm + * For more information, please visit: http://libusb.info + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBUSB_H +#define LIBUSB_H + +#ifdef _MSC_VER +/* on MS environments, the inline keyword is available in C++ only */ +#if !defined(__cplusplus) +#define inline __inline +#endif +/* ssize_t is also not available (copy/paste from MinGW) */ +#ifndef _SSIZE_T_DEFINED +#define _SSIZE_T_DEFINED +#undef ssize_t +#ifdef _WIN64 + typedef __int64 ssize_t; +#else + typedef int ssize_t; +#endif /* _WIN64 */ +#endif /* _SSIZE_T_DEFINED */ +#endif /* _MSC_VER */ + +/* stdint.h is not available on older MSVC */ +#if defined(_MSC_VER) && (_MSC_VER < 1600) && (!defined(_STDINT)) && (!defined(_STDINT_H)) +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +#else +#include +#endif + +#if !defined(_WIN32_WCE) +#include +#endif + +#if defined(__linux__) || defined(__APPLE__) || defined(__CYGWIN__) || defined(__HAIKU__) +#include +#endif + +#include +#include + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +#define ZERO_SIZED_ARRAY /* [] - valid C99 code */ +#else +#define ZERO_SIZED_ARRAY 0 /* [0] - non-standard, but usually working code */ +#endif + +/* 'interface' might be defined as a macro on Windows, so we need to + * undefine it so as not to break the current libusb API, because + * libusb_config_descriptor has an 'interface' member + * As this can be problematic if you include windows.h after libusb.h + * in your sources, we force windows.h to be included first. */ +#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) +#include +#if defined(interface) +#undef interface +#endif +#if !defined(__CYGWIN__) +#include +#endif +#endif + +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) +#define LIBUSB_DEPRECATED_FOR(f) \ + __attribute__((deprecated("Use " #f " instead"))) +#elif __GNUC__ >= 3 +#define LIBUSB_DEPRECATED_FOR(f) __attribute__((deprecated)) +#else +#define LIBUSB_DEPRECATED_FOR(f) +#endif /* __GNUC__ */ + +/** \def LIBUSB_CALL + * \ingroup libusb_misc + * libusb's Windows calling convention. + * + * Under Windows, the selection of available compilers and configurations + * means that, unlike other platforms, there is not one true calling + * convention (calling convention: the manner in which parameters are + * passed to functions in the generated assembly code). + * + * Matching the Windows API itself, libusb uses the WINAPI convention (which + * translates to the stdcall convention) and guarantees that the + * library is compiled in this way. The public header file also includes + * appropriate annotations so that your own software will use the right + * convention, even if another convention is being used by default within + * your codebase. + * + * The one consideration that you must apply in your software is to mark + * all functions which you use as libusb callbacks with this LIBUSB_CALL + * annotation, so that they too get compiled for the correct calling + * convention. + * + * On non-Windows operating systems, this macro is defined as nothing. This + * means that you can apply it to your code without worrying about + * cross-platform compatibility. + */ +/* LIBUSB_CALL must be defined on both definition and declaration of libusb + * functions. You'd think that declaration would be enough, but cygwin will + * complain about conflicting types unless both are marked this way. + * The placement of this macro is important too; it must appear after the + * return type, before the function name. See internal documentation for + * API_EXPORTED. + */ +#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) +#define LIBUSB_CALL WINAPI +#else +#define LIBUSB_CALL +#endif + +/** \def LIBUSB_API_VERSION + * \ingroup libusb_misc + * libusb's API version. + * + * Since version 1.0.13, to help with feature detection, libusb defines + * a LIBUSB_API_VERSION macro that gets increased every time there is a + * significant change to the API, such as the introduction of a new call, + * the definition of a new macro/enum member, or any other element that + * libusb applications may want to detect at compilation time. + * + * The macro is typically used in an application as follows: + * \code + * #if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01001234) + * // Use one of the newer features from the libusb API + * #endif + * \endcode + * + * Internally, LIBUSB_API_VERSION is defined as follows: + * (libusb major << 24) | (libusb minor << 16) | (16 bit incremental) + */ +#define LIBUSB_API_VERSION 0x01000107 + +/* The following is kept for compatibility, but will be deprecated in the future */ +#define LIBUSBX_API_VERSION LIBUSB_API_VERSION + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \ingroup libusb_misc + * Convert a 16-bit value from host-endian to little-endian format. On + * little endian systems, this function does nothing. On big endian systems, + * the bytes are swapped. + * \param x the host-endian value to convert + * \returns the value in little-endian byte order + */ +static inline uint16_t libusb_cpu_to_le16(const uint16_t x) +{ + union { + uint8_t b8[2]; + uint16_t b16; + } _tmp; + _tmp.b8[1] = (uint8_t) (x >> 8); + _tmp.b8[0] = (uint8_t) (x & 0xff); + return _tmp.b16; +} + +/** \def libusb_le16_to_cpu + * \ingroup libusb_misc + * Convert a 16-bit value from little-endian to host-endian format. On + * little endian systems, this function does nothing. On big endian systems, + * the bytes are swapped. + * \param x the little-endian value to convert + * \returns the value in host-endian byte order + */ +#define libusb_le16_to_cpu libusb_cpu_to_le16 + +/* standard USB stuff */ + +/** \ingroup libusb_desc + * Device and/or Interface Class codes */ +enum libusb_class_code { + /** In the context of a \ref libusb_device_descriptor "device descriptor", + * this bDeviceClass value indicates that each interface specifies its + * own class information and all interfaces operate independently. + */ + LIBUSB_CLASS_PER_INTERFACE = 0, + + /** Audio class */ + LIBUSB_CLASS_AUDIO = 1, + + /** Communications class */ + LIBUSB_CLASS_COMM = 2, + + /** Human Interface Device class */ + LIBUSB_CLASS_HID = 3, + + /** Physical */ + LIBUSB_CLASS_PHYSICAL = 5, + + /** Printer class */ + LIBUSB_CLASS_PRINTER = 7, + + /** Image class */ + LIBUSB_CLASS_PTP = 6, /* legacy name from libusb-0.1 usb.h */ + LIBUSB_CLASS_IMAGE = 6, + + /** Mass storage class */ + LIBUSB_CLASS_MASS_STORAGE = 8, + + /** Hub class */ + LIBUSB_CLASS_HUB = 9, + + /** Data class */ + LIBUSB_CLASS_DATA = 10, + + /** Smart Card */ + LIBUSB_CLASS_SMART_CARD = 0x0b, + + /** Content Security */ + LIBUSB_CLASS_CONTENT_SECURITY = 0x0d, + + /** Video */ + LIBUSB_CLASS_VIDEO = 0x0e, + + /** Personal Healthcare */ + LIBUSB_CLASS_PERSONAL_HEALTHCARE = 0x0f, + + /** Diagnostic Device */ + LIBUSB_CLASS_DIAGNOSTIC_DEVICE = 0xdc, + + /** Wireless class */ + LIBUSB_CLASS_WIRELESS = 0xe0, + + /** Application class */ + LIBUSB_CLASS_APPLICATION = 0xfe, + + /** Class is vendor-specific */ + LIBUSB_CLASS_VENDOR_SPEC = 0xff +}; + +/** \ingroup libusb_desc + * Descriptor types as defined by the USB specification. */ +enum libusb_descriptor_type { + /** Device descriptor. See libusb_device_descriptor. */ + LIBUSB_DT_DEVICE = 0x01, + + /** Configuration descriptor. See libusb_config_descriptor. */ + LIBUSB_DT_CONFIG = 0x02, + + /** String descriptor */ + LIBUSB_DT_STRING = 0x03, + + /** Interface descriptor. See libusb_interface_descriptor. */ + LIBUSB_DT_INTERFACE = 0x04, + + /** Endpoint descriptor. See libusb_endpoint_descriptor. */ + LIBUSB_DT_ENDPOINT = 0x05, + + /** BOS descriptor */ + LIBUSB_DT_BOS = 0x0f, + + /** Device Capability descriptor */ + LIBUSB_DT_DEVICE_CAPABILITY = 0x10, + + /** HID descriptor */ + LIBUSB_DT_HID = 0x21, + + /** HID report descriptor */ + LIBUSB_DT_REPORT = 0x22, + + /** Physical descriptor */ + LIBUSB_DT_PHYSICAL = 0x23, + + /** Hub descriptor */ + LIBUSB_DT_HUB = 0x29, + + /** SuperSpeed Hub descriptor */ + LIBUSB_DT_SUPERSPEED_HUB = 0x2a, + + /** SuperSpeed Endpoint Companion descriptor */ + LIBUSB_DT_SS_ENDPOINT_COMPANION = 0x30 +}; + +/* Descriptor sizes per descriptor type */ +#define LIBUSB_DT_DEVICE_SIZE 18 +#define LIBUSB_DT_CONFIG_SIZE 9 +#define LIBUSB_DT_INTERFACE_SIZE 9 +#define LIBUSB_DT_ENDPOINT_SIZE 7 +#define LIBUSB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ +#define LIBUSB_DT_HUB_NONVAR_SIZE 7 +#define LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE 6 +#define LIBUSB_DT_BOS_SIZE 5 +#define LIBUSB_DT_DEVICE_CAPABILITY_SIZE 3 + +/* BOS descriptor sizes */ +#define LIBUSB_BT_USB_2_0_EXTENSION_SIZE 7 +#define LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE 10 +#define LIBUSB_BT_CONTAINER_ID_SIZE 20 + +/* We unwrap the BOS => define its max size */ +#define LIBUSB_DT_BOS_MAX_SIZE ((LIBUSB_DT_BOS_SIZE) +\ + (LIBUSB_BT_USB_2_0_EXTENSION_SIZE) +\ + (LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE) +\ + (LIBUSB_BT_CONTAINER_ID_SIZE)) + +#define LIBUSB_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */ +#define LIBUSB_ENDPOINT_DIR_MASK 0x80 + +/** \ingroup libusb_desc + * Endpoint direction. Values for bit 7 of the + * \ref libusb_endpoint_descriptor::bEndpointAddress "endpoint address" scheme. + */ +enum libusb_endpoint_direction { + /** In: device-to-host */ + LIBUSB_ENDPOINT_IN = 0x80, + + /** Out: host-to-device */ + LIBUSB_ENDPOINT_OUT = 0x00 +}; + +#define LIBUSB_TRANSFER_TYPE_MASK 0x03 /* in bmAttributes */ + +/** \ingroup libusb_desc + * Endpoint transfer type. Values for bits 0:1 of the + * \ref libusb_endpoint_descriptor::bmAttributes "endpoint attributes" field. + */ +enum libusb_transfer_type { + /** Control endpoint */ + LIBUSB_TRANSFER_TYPE_CONTROL = 0, + + /** Isochronous endpoint */ + LIBUSB_TRANSFER_TYPE_ISOCHRONOUS = 1, + + /** Bulk endpoint */ + LIBUSB_TRANSFER_TYPE_BULK = 2, + + /** Interrupt endpoint */ + LIBUSB_TRANSFER_TYPE_INTERRUPT = 3, + + /** Stream endpoint */ + LIBUSB_TRANSFER_TYPE_BULK_STREAM = 4, +}; + +/** \ingroup libusb_misc + * Standard requests, as defined in table 9-5 of the USB 3.0 specifications */ +enum libusb_standard_request { + /** Request status of the specific recipient */ + LIBUSB_REQUEST_GET_STATUS = 0x00, + + /** Clear or disable a specific feature */ + LIBUSB_REQUEST_CLEAR_FEATURE = 0x01, + + /* 0x02 is reserved */ + + /** Set or enable a specific feature */ + LIBUSB_REQUEST_SET_FEATURE = 0x03, + + /* 0x04 is reserved */ + + /** Set device address for all future accesses */ + LIBUSB_REQUEST_SET_ADDRESS = 0x05, + + /** Get the specified descriptor */ + LIBUSB_REQUEST_GET_DESCRIPTOR = 0x06, + + /** Used to update existing descriptors or add new descriptors */ + LIBUSB_REQUEST_SET_DESCRIPTOR = 0x07, + + /** Get the current device configuration value */ + LIBUSB_REQUEST_GET_CONFIGURATION = 0x08, + + /** Set device configuration */ + LIBUSB_REQUEST_SET_CONFIGURATION = 0x09, + + /** Return the selected alternate setting for the specified interface */ + LIBUSB_REQUEST_GET_INTERFACE = 0x0A, + + /** Select an alternate interface for the specified interface */ + LIBUSB_REQUEST_SET_INTERFACE = 0x0B, + + /** Set then report an endpoint's synchronization frame */ + LIBUSB_REQUEST_SYNCH_FRAME = 0x0C, + + /** Sets both the U1 and U2 Exit Latency */ + LIBUSB_REQUEST_SET_SEL = 0x30, + + /** Delay from the time a host transmits a packet to the time it is + * received by the device. */ + LIBUSB_SET_ISOCH_DELAY = 0x31, +}; + +/** \ingroup libusb_misc + * Request type bits of the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control + * transfers. */ +enum libusb_request_type { + /** Standard */ + LIBUSB_REQUEST_TYPE_STANDARD = (0x00 << 5), + + /** Class */ + LIBUSB_REQUEST_TYPE_CLASS = (0x01 << 5), + + /** Vendor */ + LIBUSB_REQUEST_TYPE_VENDOR = (0x02 << 5), + + /** Reserved */ + LIBUSB_REQUEST_TYPE_RESERVED = (0x03 << 5) +}; + +/** \ingroup libusb_misc + * Recipient bits of the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control + * transfers. Values 4 through 31 are reserved. */ +enum libusb_request_recipient { + /** Device */ + LIBUSB_RECIPIENT_DEVICE = 0x00, + + /** Interface */ + LIBUSB_RECIPIENT_INTERFACE = 0x01, + + /** Endpoint */ + LIBUSB_RECIPIENT_ENDPOINT = 0x02, + + /** Other */ + LIBUSB_RECIPIENT_OTHER = 0x03, +}; + +#define LIBUSB_ISO_SYNC_TYPE_MASK 0x0C + +/** \ingroup libusb_desc + * Synchronization type for isochronous endpoints. Values for bits 2:3 of the + * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in + * libusb_endpoint_descriptor. + */ +enum libusb_iso_sync_type { + /** No synchronization */ + LIBUSB_ISO_SYNC_TYPE_NONE = 0, + + /** Asynchronous */ + LIBUSB_ISO_SYNC_TYPE_ASYNC = 1, + + /** Adaptive */ + LIBUSB_ISO_SYNC_TYPE_ADAPTIVE = 2, + + /** Synchronous */ + LIBUSB_ISO_SYNC_TYPE_SYNC = 3 +}; + +#define LIBUSB_ISO_USAGE_TYPE_MASK 0x30 + +/** \ingroup libusb_desc + * Usage type for isochronous endpoints. Values for bits 4:5 of the + * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in + * libusb_endpoint_descriptor. + */ +enum libusb_iso_usage_type { + /** Data endpoint */ + LIBUSB_ISO_USAGE_TYPE_DATA = 0, + + /** Feedback endpoint */ + LIBUSB_ISO_USAGE_TYPE_FEEDBACK = 1, + + /** Implicit feedback Data endpoint */ + LIBUSB_ISO_USAGE_TYPE_IMPLICIT = 2, +}; + +/** \ingroup libusb_desc + * A structure representing the standard USB device descriptor. This + * descriptor is documented in section 9.6.1 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_device_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE LIBUSB_DT_DEVICE in this + * context. */ + uint8_t bDescriptorType; + + /** USB specification release number in binary-coded decimal. A value of + * 0x0200 indicates USB 2.0, 0x0110 indicates USB 1.1, etc. */ + uint16_t bcdUSB; + + /** USB-IF class code for the device. See \ref libusb_class_code. */ + uint8_t bDeviceClass; + + /** USB-IF subclass code for the device, qualified by the bDeviceClass + * value */ + uint8_t bDeviceSubClass; + + /** USB-IF protocol code for the device, qualified by the bDeviceClass and + * bDeviceSubClass values */ + uint8_t bDeviceProtocol; + + /** Maximum packet size for endpoint 0 */ + uint8_t bMaxPacketSize0; + + /** USB-IF vendor ID */ + uint16_t idVendor; + + /** USB-IF product ID */ + uint16_t idProduct; + + /** Device release number in binary-coded decimal */ + uint16_t bcdDevice; + + /** Index of string descriptor describing manufacturer */ + uint8_t iManufacturer; + + /** Index of string descriptor describing product */ + uint8_t iProduct; + + /** Index of string descriptor containing device serial number */ + uint8_t iSerialNumber; + + /** Number of possible configurations */ + uint8_t bNumConfigurations; +}; + +/** \ingroup libusb_desc + * A structure representing the standard USB endpoint descriptor. This + * descriptor is documented in section 9.6.6 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_endpoint_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_ENDPOINT LIBUSB_DT_ENDPOINT in + * this context. */ + uint8_t bDescriptorType; + + /** The address of the endpoint described by this descriptor. Bits 0:3 are + * the endpoint number. Bits 4:6 are reserved. Bit 7 indicates direction, + * see \ref libusb_endpoint_direction. + */ + uint8_t bEndpointAddress; + + /** Attributes which apply to the endpoint when it is configured using + * the bConfigurationValue. Bits 0:1 determine the transfer type and + * correspond to \ref libusb_transfer_type. Bits 2:3 are only used for + * isochronous endpoints and correspond to \ref libusb_iso_sync_type. + * Bits 4:5 are also only used for isochronous endpoints and correspond to + * \ref libusb_iso_usage_type. Bits 6:7 are reserved. + */ + uint8_t bmAttributes; + + /** Maximum packet size this endpoint is capable of sending/receiving. */ + uint16_t wMaxPacketSize; + + /** Interval for polling endpoint for data transfers. */ + uint8_t bInterval; + + /** For audio devices only: the rate at which synchronization feedback + * is provided. */ + uint8_t bRefresh; + + /** For audio devices only: the address if the synch endpoint */ + uint8_t bSynchAddress; + + /** Extra descriptors. If libusb encounters unknown endpoint descriptors, + * it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. Must be non-negative. */ + int extra_length; +}; + +/** \ingroup libusb_desc + * A structure representing the standard USB interface descriptor. This + * descriptor is documented in section 9.6.5 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_interface_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_INTERFACE LIBUSB_DT_INTERFACE + * in this context. */ + uint8_t bDescriptorType; + + /** Number of this interface */ + uint8_t bInterfaceNumber; + + /** Value used to select this alternate setting for this interface */ + uint8_t bAlternateSetting; + + /** Number of endpoints used by this interface (excluding the control + * endpoint). */ + uint8_t bNumEndpoints; + + /** USB-IF class code for this interface. See \ref libusb_class_code. */ + uint8_t bInterfaceClass; + + /** USB-IF subclass code for this interface, qualified by the + * bInterfaceClass value */ + uint8_t bInterfaceSubClass; + + /** USB-IF protocol code for this interface, qualified by the + * bInterfaceClass and bInterfaceSubClass values */ + uint8_t bInterfaceProtocol; + + /** Index of string descriptor describing this interface */ + uint8_t iInterface; + + /** Array of endpoint descriptors. This length of this array is determined + * by the bNumEndpoints field. */ + const struct libusb_endpoint_descriptor *endpoint; + + /** Extra descriptors. If libusb encounters unknown interface descriptors, + * it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. Must be non-negative. */ + int extra_length; +}; + +/** \ingroup libusb_desc + * A collection of alternate settings for a particular USB interface. + */ +struct libusb_interface { + /** Array of interface descriptors. The length of this array is determined + * by the num_altsetting field. */ + const struct libusb_interface_descriptor *altsetting; + + /** The number of alternate settings that belong to this interface. + * Must be non-negative. */ + int num_altsetting; +}; + +/** \ingroup libusb_desc + * A structure representing the standard USB configuration descriptor. This + * descriptor is documented in section 9.6.3 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_config_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_CONFIG LIBUSB_DT_CONFIG + * in this context. */ + uint8_t bDescriptorType; + + /** Total length of data returned for this configuration */ + uint16_t wTotalLength; + + /** Number of interfaces supported by this configuration */ + uint8_t bNumInterfaces; + + /** Identifier value for this configuration */ + uint8_t bConfigurationValue; + + /** Index of string descriptor describing this configuration */ + uint8_t iConfiguration; + + /** Configuration characteristics */ + uint8_t bmAttributes; + + /** Maximum power consumption of the USB device from this bus in this + * configuration when the device is fully operation. Expressed in units + * of 2 mA when the device is operating in high-speed mode and in units + * of 8 mA when the device is operating in super-speed mode. */ + uint8_t MaxPower; + + /** Array of interfaces supported by this configuration. The length of + * this array is determined by the bNumInterfaces field. */ + const struct libusb_interface *interface; + + /** Extra descriptors. If libusb encounters unknown configuration + * descriptors, it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. Must be non-negative. */ + int extra_length; +}; + +/** \ingroup libusb_desc + * A structure representing the superspeed endpoint companion + * descriptor. This descriptor is documented in section 9.6.7 of + * the USB 3.0 specification. All multiple-byte fields are represented in + * host-endian format. + */ +struct libusb_ss_endpoint_companion_descriptor { + + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_SS_ENDPOINT_COMPANION in + * this context. */ + uint8_t bDescriptorType; + + + /** The maximum number of packets the endpoint can send or + * receive as part of a burst. */ + uint8_t bMaxBurst; + + /** In bulk EP: bits 4:0 represents the maximum number of + * streams the EP supports. In isochronous EP: bits 1:0 + * represents the Mult - a zero based value that determines + * the maximum number of packets within a service interval */ + uint8_t bmAttributes; + + /** The total number of bytes this EP will transfer every + * service interval. valid only for periodic EPs. */ + uint16_t wBytesPerInterval; +}; + +/** \ingroup libusb_desc + * A generic representation of a BOS Device Capability descriptor. It is + * advised to check bDevCapabilityType and call the matching + * libusb_get_*_descriptor function to get a structure fully matching the type. + */ +struct libusb_bos_dev_capability_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY + * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ + uint8_t bDescriptorType; + /** Device Capability type */ + uint8_t bDevCapabilityType; + /** Device Capability data (bLength - 3 bytes) */ + uint8_t dev_capability_data[ZERO_SIZED_ARRAY]; +}; + +/** \ingroup libusb_desc + * A structure representing the Binary Device Object Store (BOS) descriptor. + * This descriptor is documented in section 9.6.2 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_bos_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_BOS LIBUSB_DT_BOS + * in this context. */ + uint8_t bDescriptorType; + + /** Length of this descriptor and all of its sub descriptors */ + uint16_t wTotalLength; + + /** The number of separate device capability descriptors in + * the BOS */ + uint8_t bNumDeviceCaps; + + /** bNumDeviceCap Device Capability Descriptors */ + struct libusb_bos_dev_capability_descriptor *dev_capability[ZERO_SIZED_ARRAY]; +}; + +/** \ingroup libusb_desc + * A structure representing the USB 2.0 Extension descriptor + * This descriptor is documented in section 9.6.2.1 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_usb_2_0_extension_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY + * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ + uint8_t bDescriptorType; + + /** Capability type. Will have value + * \ref libusb_capability_type::LIBUSB_BT_USB_2_0_EXTENSION + * LIBUSB_BT_USB_2_0_EXTENSION in this context. */ + uint8_t bDevCapabilityType; + + /** Bitmap encoding of supported device level features. + * A value of one in a bit location indicates a feature is + * supported; a value of zero indicates it is not supported. + * See \ref libusb_usb_2_0_extension_attributes. */ + uint32_t bmAttributes; +}; + +/** \ingroup libusb_desc + * A structure representing the SuperSpeed USB Device Capability descriptor + * This descriptor is documented in section 9.6.2.2 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_ss_usb_device_capability_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY + * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ + uint8_t bDescriptorType; + + /** Capability type. Will have value + * \ref libusb_capability_type::LIBUSB_BT_SS_USB_DEVICE_CAPABILITY + * LIBUSB_BT_SS_USB_DEVICE_CAPABILITY in this context. */ + uint8_t bDevCapabilityType; + + /** Bitmap encoding of supported device level features. + * A value of one in a bit location indicates a feature is + * supported; a value of zero indicates it is not supported. + * See \ref libusb_ss_usb_device_capability_attributes. */ + uint8_t bmAttributes; + + /** Bitmap encoding of the speed supported by this device when + * operating in SuperSpeed mode. See \ref libusb_supported_speed. */ + uint16_t wSpeedSupported; + + /** The lowest speed at which all the functionality supported + * by the device is available to the user. For example if the + * device supports all its functionality when connected at + * full speed and above then it sets this value to 1. */ + uint8_t bFunctionalitySupport; + + /** U1 Device Exit Latency. */ + uint8_t bU1DevExitLat; + + /** U2 Device Exit Latency. */ + uint16_t bU2DevExitLat; +}; + +/** \ingroup libusb_desc + * A structure representing the Container ID descriptor. + * This descriptor is documented in section 9.6.2.3 of the USB 3.0 specification. + * All multiple-byte fields, except UUIDs, are represented in host-endian format. + */ +struct libusb_container_id_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY + * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ + uint8_t bDescriptorType; + + /** Capability type. Will have value + * \ref libusb_capability_type::LIBUSB_BT_CONTAINER_ID + * LIBUSB_BT_CONTAINER_ID in this context. */ + uint8_t bDevCapabilityType; + + /** Reserved field */ + uint8_t bReserved; + + /** 128 bit UUID */ + uint8_t ContainerID[16]; +}; + +/** \ingroup libusb_asyncio + * Setup packet for control transfers. */ +struct libusb_control_setup { + /** Request type. Bits 0:4 determine recipient, see + * \ref libusb_request_recipient. Bits 5:6 determine type, see + * \ref libusb_request_type. Bit 7 determines data transfer direction, see + * \ref libusb_endpoint_direction. + */ + uint8_t bmRequestType; + + /** Request. If the type bits of bmRequestType are equal to + * \ref libusb_request_type::LIBUSB_REQUEST_TYPE_STANDARD + * "LIBUSB_REQUEST_TYPE_STANDARD" then this field refers to + * \ref libusb_standard_request. For other cases, use of this field is + * application-specific. */ + uint8_t bRequest; + + /** Value. Varies according to request */ + uint16_t wValue; + + /** Index. Varies according to request, typically used to pass an index + * or offset */ + uint16_t wIndex; + + /** Number of bytes to transfer */ + uint16_t wLength; +}; + +#define LIBUSB_CONTROL_SETUP_SIZE (sizeof(struct libusb_control_setup)) + +/* libusb */ + +struct libusb_context; +struct libusb_device; +struct libusb_device_handle; + +/** \ingroup libusb_lib + * Structure providing the version of the libusb runtime + */ +struct libusb_version { + /** Library major version. */ + const uint16_t major; + + /** Library minor version. */ + const uint16_t minor; + + /** Library micro version. */ + const uint16_t micro; + + /** Library nano version. */ + const uint16_t nano; + + /** Library release candidate suffix string, e.g. "-rc4". */ + const char *rc; + + /** For ABI compatibility only. */ + const char* describe; +}; + +/** \ingroup libusb_lib + * Structure representing a libusb session. The concept of individual libusb + * sessions allows for your program to use two libraries (or dynamically + * load two modules) which both independently use libusb. This will prevent + * interference between the individual libusb users - for example + * libusb_set_option() will not affect the other user of the library, and + * libusb_exit() will not destroy resources that the other user is still + * using. + * + * Sessions are created by libusb_init() and destroyed through libusb_exit(). + * If your application is guaranteed to only ever include a single libusb + * user (i.e. you), you do not have to worry about contexts: pass NULL in + * every function call where a context is required. The default context + * will be used. + * + * For more information, see \ref libusb_contexts. + */ +typedef struct libusb_context libusb_context; + +/** \ingroup libusb_dev + * Structure representing a USB device detected on the system. This is an + * opaque type for which you are only ever provided with a pointer, usually + * originating from libusb_get_device_list(). + * + * Certain operations can be performed on a device, but in order to do any + * I/O you will have to first obtain a device handle using libusb_open(). + * + * Devices are reference counted with libusb_ref_device() and + * libusb_unref_device(), and are freed when the reference count reaches 0. + * New devices presented by libusb_get_device_list() have a reference count of + * 1, and libusb_free_device_list() can optionally decrease the reference count + * on all devices in the list. libusb_open() adds another reference which is + * later destroyed by libusb_close(). + */ +typedef struct libusb_device libusb_device; + + +/** \ingroup libusb_dev + * Structure representing a handle on a USB device. This is an opaque type for + * which you are only ever provided with a pointer, usually originating from + * libusb_open(). + * + * A device handle is used to perform I/O and other operations. When finished + * with a device handle, you should call libusb_close(). + */ +typedef struct libusb_device_handle libusb_device_handle; + +/** \ingroup libusb_dev + * Speed codes. Indicates the speed at which the device is operating. + */ +enum libusb_speed { + /** The OS doesn't report or know the device speed. */ + LIBUSB_SPEED_UNKNOWN = 0, + + /** The device is operating at low speed (1.5MBit/s). */ + LIBUSB_SPEED_LOW = 1, + + /** The device is operating at full speed (12MBit/s). */ + LIBUSB_SPEED_FULL = 2, + + /** The device is operating at high speed (480MBit/s). */ + LIBUSB_SPEED_HIGH = 3, + + /** The device is operating at super speed (5000MBit/s). */ + LIBUSB_SPEED_SUPER = 4, + + /** The device is operating at super speed plus (10000MBit/s). */ + LIBUSB_SPEED_SUPER_PLUS = 5, +}; + +/** \ingroup libusb_dev + * Supported speeds (wSpeedSupported) bitfield. Indicates what + * speeds the device supports. + */ +enum libusb_supported_speed { + /** Low speed operation supported (1.5MBit/s). */ + LIBUSB_LOW_SPEED_OPERATION = 1, + + /** Full speed operation supported (12MBit/s). */ + LIBUSB_FULL_SPEED_OPERATION = 2, + + /** High speed operation supported (480MBit/s). */ + LIBUSB_HIGH_SPEED_OPERATION = 4, + + /** Superspeed operation supported (5000MBit/s). */ + LIBUSB_SUPER_SPEED_OPERATION = 8, +}; + +/** \ingroup libusb_dev + * Masks for the bits of the + * \ref libusb_usb_2_0_extension_descriptor::bmAttributes "bmAttributes" field + * of the USB 2.0 Extension descriptor. + */ +enum libusb_usb_2_0_extension_attributes { + /** Supports Link Power Management (LPM) */ + LIBUSB_BM_LPM_SUPPORT = 2, +}; + +/** \ingroup libusb_dev + * Masks for the bits of the + * \ref libusb_ss_usb_device_capability_descriptor::bmAttributes "bmAttributes" field + * field of the SuperSpeed USB Device Capability descriptor. + */ +enum libusb_ss_usb_device_capability_attributes { + /** Supports Latency Tolerance Messages (LTM) */ + LIBUSB_BM_LTM_SUPPORT = 2, +}; + +/** \ingroup libusb_dev + * USB capability types + */ +enum libusb_bos_type { + /** Wireless USB device capability */ + LIBUSB_BT_WIRELESS_USB_DEVICE_CAPABILITY = 1, + + /** USB 2.0 extensions */ + LIBUSB_BT_USB_2_0_EXTENSION = 2, + + /** SuperSpeed USB device capability */ + LIBUSB_BT_SS_USB_DEVICE_CAPABILITY = 3, + + /** Container ID type */ + LIBUSB_BT_CONTAINER_ID = 4, +}; + +/** \ingroup libusb_misc + * Error codes. Most libusb functions return 0 on success or one of these + * codes on failure. + * You can call libusb_error_name() to retrieve a string representation of an + * error code or libusb_strerror() to get an end-user suitable description of + * an error code. + */ +enum libusb_error { + /** Success (no error) */ + LIBUSB_SUCCESS = 0, + + /** Input/output error */ + LIBUSB_ERROR_IO = -1, + + /** Invalid parameter */ + LIBUSB_ERROR_INVALID_PARAM = -2, + + /** Access denied (insufficient permissions) */ + LIBUSB_ERROR_ACCESS = -3, + + /** No such device (it may have been disconnected) */ + LIBUSB_ERROR_NO_DEVICE = -4, + + /** Entity not found */ + LIBUSB_ERROR_NOT_FOUND = -5, + + /** Resource busy */ + LIBUSB_ERROR_BUSY = -6, + + /** Operation timed out */ + LIBUSB_ERROR_TIMEOUT = -7, + + /** Overflow */ + LIBUSB_ERROR_OVERFLOW = -8, + + /** Pipe error */ + LIBUSB_ERROR_PIPE = -9, + + /** System call interrupted (perhaps due to signal) */ + LIBUSB_ERROR_INTERRUPTED = -10, + + /** Insufficient memory */ + LIBUSB_ERROR_NO_MEM = -11, + + /** Operation not supported or unimplemented on this platform */ + LIBUSB_ERROR_NOT_SUPPORTED = -12, + + /* NB: Remember to update LIBUSB_ERROR_COUNT below as well as the + message strings in strerror.c when adding new error codes here. */ + + /** Other error */ + LIBUSB_ERROR_OTHER = -99, +}; + +/* Total number of error codes in enum libusb_error */ +#define LIBUSB_ERROR_COUNT 14 + +/** \ingroup libusb_asyncio + * Transfer status codes */ +enum libusb_transfer_status { + /** Transfer completed without error. Note that this does not indicate + * that the entire amount of requested data was transferred. */ + LIBUSB_TRANSFER_COMPLETED, + + /** Transfer failed */ + LIBUSB_TRANSFER_ERROR, + + /** Transfer timed out */ + LIBUSB_TRANSFER_TIMED_OUT, + + /** Transfer was cancelled */ + LIBUSB_TRANSFER_CANCELLED, + + /** For bulk/interrupt endpoints: halt condition detected (endpoint + * stalled). For control endpoints: control request not supported. */ + LIBUSB_TRANSFER_STALL, + + /** Device was disconnected */ + LIBUSB_TRANSFER_NO_DEVICE, + + /** Device sent more data than requested */ + LIBUSB_TRANSFER_OVERFLOW, + + /* NB! Remember to update libusb_error_name() + when adding new status codes here. */ +}; + +/** \ingroup libusb_asyncio + * libusb_transfer.flags values */ +enum libusb_transfer_flags { + /** Report short frames as errors */ + LIBUSB_TRANSFER_SHORT_NOT_OK = 1U << 0, + + /** Automatically free() transfer buffer during libusb_free_transfer(). + * Note that buffers allocated with libusb_dev_mem_alloc() should not + * be attempted freed in this way, since free() is not an appropriate + * way to release such memory. */ + LIBUSB_TRANSFER_FREE_BUFFER = 1U << 1, + + /** Automatically call libusb_free_transfer() after callback returns. + * If this flag is set, it is illegal to call libusb_free_transfer() + * from your transfer callback, as this will result in a double-free + * when this flag is acted upon. */ + LIBUSB_TRANSFER_FREE_TRANSFER = 1U << 2, + + /** Terminate transfers that are a multiple of the endpoint's + * wMaxPacketSize with an extra zero length packet. This is useful + * when a device protocol mandates that each logical request is + * terminated by an incomplete packet (i.e. the logical requests are + * not separated by other means). + * + * This flag only affects host-to-device transfers to bulk and interrupt + * endpoints. In other situations, it is ignored. + * + * This flag only affects transfers with a length that is a multiple of + * the endpoint's wMaxPacketSize. On transfers of other lengths, this + * flag has no effect. Therefore, if you are working with a device that + * needs a ZLP whenever the end of the logical request falls on a packet + * boundary, then it is sensible to set this flag on every + * transfer (you do not have to worry about only setting it on transfers + * that end on the boundary). + * + * This flag is currently only supported on Linux. + * On other systems, libusb_submit_transfer() will return + * LIBUSB_ERROR_NOT_SUPPORTED for every transfer where this flag is set. + * + * Available since libusb-1.0.9. + */ + LIBUSB_TRANSFER_ADD_ZERO_PACKET = 1U << 3, +}; + +/** \ingroup libusb_asyncio + * Isochronous packet descriptor. */ +struct libusb_iso_packet_descriptor { + /** Length of data to request in this packet */ + unsigned int length; + + /** Amount of data that was actually transferred */ + unsigned int actual_length; + + /** Status code for this packet */ + enum libusb_transfer_status status; +}; + +struct libusb_transfer; + +/** \ingroup libusb_asyncio + * Asynchronous transfer callback function type. When submitting asynchronous + * transfers, you pass a pointer to a callback function of this type via the + * \ref libusb_transfer::callback "callback" member of the libusb_transfer + * structure. libusb will call this function later, when the transfer has + * completed or failed. See \ref libusb_asyncio for more information. + * \param transfer The libusb_transfer struct the callback function is being + * notified about. + */ +typedef void (LIBUSB_CALL *libusb_transfer_cb_fn)(struct libusb_transfer *transfer); + +/** \ingroup libusb_asyncio + * The generic USB transfer structure. The user populates this structure and + * then submits it in order to request a transfer. After the transfer has + * completed, the library populates the transfer with the results and passes + * it back to the user. + */ +struct libusb_transfer { + /** Handle of the device that this transfer will be submitted to */ + libusb_device_handle *dev_handle; + + /** A bitwise OR combination of \ref libusb_transfer_flags. */ + uint8_t flags; + + /** Address of the endpoint where this transfer will be sent. */ + unsigned char endpoint; + + /** Type of the endpoint from \ref libusb_transfer_type */ + unsigned char type; + + /** Timeout for this transfer in milliseconds. A value of 0 indicates no + * timeout. */ + unsigned int timeout; + + /** The status of the transfer. Read-only, and only for use within + * transfer callback function. + * + * If this is an isochronous transfer, this field may read COMPLETED even + * if there were errors in the frames. Use the + * \ref libusb_iso_packet_descriptor::status "status" field in each packet + * to determine if errors occurred. */ + enum libusb_transfer_status status; + + /** Length of the data buffer. Must be non-negative. */ + int length; + + /** Actual length of data that was transferred. Read-only, and only for + * use within transfer callback function. Not valid for isochronous + * endpoint transfers. */ + int actual_length; + + /** Callback function. This will be invoked when the transfer completes, + * fails, or is cancelled. */ + libusb_transfer_cb_fn callback; + + /** User context data to pass to the callback function. */ + void *user_data; + + /** Data buffer */ + unsigned char *buffer; + + /** Number of isochronous packets. Only used for I/O with isochronous + * endpoints. Must be non-negative. */ + int num_iso_packets; + + /** Isochronous packet descriptors, for isochronous transfers only. */ + struct libusb_iso_packet_descriptor iso_packet_desc[ZERO_SIZED_ARRAY]; +}; + +/** \ingroup libusb_misc + * Capabilities supported by an instance of libusb on the current running + * platform. Test if the loaded library supports a given capability by calling + * \ref libusb_has_capability(). + */ +enum libusb_capability { + /** The libusb_has_capability() API is available. */ + LIBUSB_CAP_HAS_CAPABILITY = 0x0000, + /** Hotplug support is available on this platform. */ + LIBUSB_CAP_HAS_HOTPLUG = 0x0001, + /** The library can access HID devices without requiring user intervention. + * Note that before being able to actually access an HID device, you may + * still have to call additional libusb functions such as + * \ref libusb_detach_kernel_driver(). */ + LIBUSB_CAP_HAS_HID_ACCESS = 0x0100, + /** The library supports detaching of the default USB driver, using + * \ref libusb_detach_kernel_driver(), if one is set by the OS kernel */ + LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER = 0x0101 +}; + +/** \ingroup libusb_lib + * Log message levels. + * - LIBUSB_LOG_LEVEL_NONE (0) : no messages ever printed by the library (default) + * - LIBUSB_LOG_LEVEL_ERROR (1) : error messages are printed to stderr + * - LIBUSB_LOG_LEVEL_WARNING (2) : warning and error messages are printed to stderr + * - LIBUSB_LOG_LEVEL_INFO (3) : informational messages are printed to stderr + * - LIBUSB_LOG_LEVEL_DEBUG (4) : debug and informational messages are printed to stderr + */ +enum libusb_log_level { + LIBUSB_LOG_LEVEL_NONE = 0, + LIBUSB_LOG_LEVEL_ERROR = 1, + LIBUSB_LOG_LEVEL_WARNING = 2, + LIBUSB_LOG_LEVEL_INFO = 3, + LIBUSB_LOG_LEVEL_DEBUG = 4, +}; + +/** \ingroup libusb_lib + * Log callback mode. + * \see libusb_set_log_cb() + */ +enum libusb_log_cb_mode { + + /** Callback function handling all log mesages. */ + LIBUSB_LOG_CB_GLOBAL = 1 << 0, + + /** Callback function handling context related log mesages. */ + LIBUSB_LOG_CB_CONTEXT = 1 << 1 +}; + +/** \ingroup libusb_lib + * Callback function for handling log messages. + * \param ctx the context which is related to the log message, or NULL if it + * is a global log message + * \param level the log level, see \ref libusb_log_level for a description + * \param str the log message + * \see libusb_set_log_cb() + */ +typedef void (LIBUSB_CALL *libusb_log_cb)(libusb_context *ctx, + enum libusb_log_level level, const char *str); + +int LIBUSB_CALL libusb_init(libusb_context **ctx); +void LIBUSB_CALL libusb_exit(libusb_context *ctx); +LIBUSB_DEPRECATED_FOR(libusb_set_option) +void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level); +void LIBUSB_CALL libusb_set_log_cb(libusb_context *ctx, libusb_log_cb cb, int mode); +const struct libusb_version * LIBUSB_CALL libusb_get_version(void); +int LIBUSB_CALL libusb_has_capability(uint32_t capability); +const char * LIBUSB_CALL libusb_error_name(int errcode); +int LIBUSB_CALL libusb_setlocale(const char *locale); +const char * LIBUSB_CALL libusb_strerror(enum libusb_error errcode); + +ssize_t LIBUSB_CALL libusb_get_device_list(libusb_context *ctx, + libusb_device ***list); +void LIBUSB_CALL libusb_free_device_list(libusb_device **list, + int unref_devices); +libusb_device * LIBUSB_CALL libusb_ref_device(libusb_device *dev); +void LIBUSB_CALL libusb_unref_device(libusb_device *dev); + +int LIBUSB_CALL libusb_get_configuration(libusb_device_handle *dev, + int *config); +int LIBUSB_CALL libusb_get_device_descriptor(libusb_device *dev, + struct libusb_device_descriptor *desc); +int LIBUSB_CALL libusb_get_active_config_descriptor(libusb_device *dev, + struct libusb_config_descriptor **config); +int LIBUSB_CALL libusb_get_config_descriptor(libusb_device *dev, + uint8_t config_index, struct libusb_config_descriptor **config); +int LIBUSB_CALL libusb_get_config_descriptor_by_value(libusb_device *dev, + uint8_t bConfigurationValue, struct libusb_config_descriptor **config); +void LIBUSB_CALL libusb_free_config_descriptor( + struct libusb_config_descriptor *config); +int LIBUSB_CALL libusb_get_ss_endpoint_companion_descriptor( + struct libusb_context *ctx, + const struct libusb_endpoint_descriptor *endpoint, + struct libusb_ss_endpoint_companion_descriptor **ep_comp); +void LIBUSB_CALL libusb_free_ss_endpoint_companion_descriptor( + struct libusb_ss_endpoint_companion_descriptor *ep_comp); +int LIBUSB_CALL libusb_get_bos_descriptor(libusb_device_handle *dev_handle, + struct libusb_bos_descriptor **bos); +void LIBUSB_CALL libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos); +int LIBUSB_CALL libusb_get_usb_2_0_extension_descriptor( + struct libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_usb_2_0_extension_descriptor **usb_2_0_extension); +void LIBUSB_CALL libusb_free_usb_2_0_extension_descriptor( + struct libusb_usb_2_0_extension_descriptor *usb_2_0_extension); +int LIBUSB_CALL libusb_get_ss_usb_device_capability_descriptor( + struct libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_ss_usb_device_capability_descriptor **ss_usb_device_cap); +void LIBUSB_CALL libusb_free_ss_usb_device_capability_descriptor( + struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_cap); +int LIBUSB_CALL libusb_get_container_id_descriptor(struct libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_container_id_descriptor **container_id); +void LIBUSB_CALL libusb_free_container_id_descriptor( + struct libusb_container_id_descriptor *container_id); +uint8_t LIBUSB_CALL libusb_get_bus_number(libusb_device *dev); +uint8_t LIBUSB_CALL libusb_get_port_number(libusb_device *dev); +int LIBUSB_CALL libusb_get_port_numbers(libusb_device *dev, uint8_t* port_numbers, int port_numbers_len); +LIBUSB_DEPRECATED_FOR(libusb_get_port_numbers) +int LIBUSB_CALL libusb_get_port_path(libusb_context *ctx, libusb_device *dev, uint8_t* path, uint8_t path_length); +libusb_device * LIBUSB_CALL libusb_get_parent(libusb_device *dev); +uint8_t LIBUSB_CALL libusb_get_device_address(libusb_device *dev); +int LIBUSB_CALL libusb_get_device_speed(libusb_device *dev); +int LIBUSB_CALL libusb_get_max_packet_size(libusb_device *dev, + unsigned char endpoint); +int LIBUSB_CALL libusb_get_max_iso_packet_size(libusb_device *dev, + unsigned char endpoint); + +int LIBUSB_CALL libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev, libusb_device_handle **dev_handle); +int LIBUSB_CALL libusb_open(libusb_device *dev, libusb_device_handle **dev_handle); +void LIBUSB_CALL libusb_close(libusb_device_handle *dev_handle); +libusb_device * LIBUSB_CALL libusb_get_device(libusb_device_handle *dev_handle); + +int LIBUSB_CALL libusb_set_configuration(libusb_device_handle *dev_handle, + int configuration); +int LIBUSB_CALL libusb_claim_interface(libusb_device_handle *dev_handle, + int interface_number); +int LIBUSB_CALL libusb_release_interface(libusb_device_handle *dev_handle, + int interface_number); + +libusb_device_handle * LIBUSB_CALL libusb_open_device_with_vid_pid( + libusb_context *ctx, uint16_t vendor_id, uint16_t product_id); + +int LIBUSB_CALL libusb_set_interface_alt_setting(libusb_device_handle *dev_handle, + int interface_number, int alternate_setting); +int LIBUSB_CALL libusb_clear_halt(libusb_device_handle *dev_handle, + unsigned char endpoint); +int LIBUSB_CALL libusb_reset_device(libusb_device_handle *dev_handle); + +int LIBUSB_CALL libusb_alloc_streams(libusb_device_handle *dev_handle, + uint32_t num_streams, unsigned char *endpoints, int num_endpoints); +int LIBUSB_CALL libusb_free_streams(libusb_device_handle *dev_handle, + unsigned char *endpoints, int num_endpoints); + +unsigned char * LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev_handle, + size_t length); +int LIBUSB_CALL libusb_dev_mem_free(libusb_device_handle *dev_handle, + unsigned char *buffer, size_t length); + +int LIBUSB_CALL libusb_kernel_driver_active(libusb_device_handle *dev_handle, + int interface_number); +int LIBUSB_CALL libusb_detach_kernel_driver(libusb_device_handle *dev_handle, + int interface_number); +int LIBUSB_CALL libusb_attach_kernel_driver(libusb_device_handle *dev_handle, + int interface_number); +int LIBUSB_CALL libusb_set_auto_detach_kernel_driver( + libusb_device_handle *dev_handle, int enable); + +/* async I/O */ + +/** \ingroup libusb_asyncio + * Get the data section of a control transfer. This convenience function is here + * to remind you that the data does not start until 8 bytes into the actual + * buffer, as the setup packet comes first. + * + * Calling this function only makes sense from a transfer callback function, + * or situations where you have already allocated a suitably sized buffer at + * transfer->buffer. + * + * \param transfer a transfer + * \returns pointer to the first byte of the data section + */ +static inline unsigned char *libusb_control_transfer_get_data( + struct libusb_transfer *transfer) +{ + return transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; +} + +/** \ingroup libusb_asyncio + * Get the control setup packet of a control transfer. This convenience + * function is here to remind you that the control setup occupies the first + * 8 bytes of the transfer data buffer. + * + * Calling this function only makes sense from a transfer callback function, + * or situations where you have already allocated a suitably sized buffer at + * transfer->buffer. + * + * \param transfer a transfer + * \returns a casted pointer to the start of the transfer data buffer + */ +static inline struct libusb_control_setup *libusb_control_transfer_get_setup( + struct libusb_transfer *transfer) +{ + return (struct libusb_control_setup *)(void *) transfer->buffer; +} + +/** \ingroup libusb_asyncio + * Helper function to populate the setup packet (first 8 bytes of the data + * buffer) for a control transfer. The wIndex, wValue and wLength values should + * be given in host-endian byte order. + * + * \param buffer buffer to output the setup packet into + * This pointer must be aligned to at least 2 bytes boundary. + * \param bmRequestType see the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field of + * \ref libusb_control_setup + * \param bRequest see the + * \ref libusb_control_setup::bRequest "bRequest" field of + * \ref libusb_control_setup + * \param wValue see the + * \ref libusb_control_setup::wValue "wValue" field of + * \ref libusb_control_setup + * \param wIndex see the + * \ref libusb_control_setup::wIndex "wIndex" field of + * \ref libusb_control_setup + * \param wLength see the + * \ref libusb_control_setup::wLength "wLength" field of + * \ref libusb_control_setup + */ +static inline void libusb_fill_control_setup(unsigned char *buffer, + uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + uint16_t wLength) +{ + struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *) buffer; + setup->bmRequestType = bmRequestType; + setup->bRequest = bRequest; + setup->wValue = libusb_cpu_to_le16(wValue); + setup->wIndex = libusb_cpu_to_le16(wIndex); + setup->wLength = libusb_cpu_to_le16(wLength); +} + +struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer(int iso_packets); +int LIBUSB_CALL libusb_submit_transfer(struct libusb_transfer *transfer); +int LIBUSB_CALL libusb_cancel_transfer(struct libusb_transfer *transfer); +void LIBUSB_CALL libusb_free_transfer(struct libusb_transfer *transfer); +void LIBUSB_CALL libusb_transfer_set_stream_id( + struct libusb_transfer *transfer, uint32_t stream_id); +uint32_t LIBUSB_CALL libusb_transfer_get_stream_id( + struct libusb_transfer *transfer); + +/** \ingroup libusb_asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for a control transfer. + * + * If you pass a transfer buffer to this function, the first 8 bytes will + * be interpreted as a control setup packet, and the wLength field will be + * used to automatically populate the \ref libusb_transfer::length "length" + * field of the transfer. Therefore the recommended approach is: + * -# Allocate a suitably sized data buffer (including space for control setup) + * -# Call libusb_fill_control_setup() + * -# If this is a host-to-device transfer with a data stage, put the data + * in place after the setup packet + * -# Call this function + * -# Call libusb_submit_transfer() + * + * It is also legal to pass a NULL buffer to this function, in which case this + * function will not attempt to populate the length field. Remember that you + * must then populate the buffer and length fields later. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param buffer data buffer. If provided, this function will interpret the + * first 8 bytes as a setup packet and infer the transfer length from that. + * This pointer must be aligned to at least 2 bytes boundary. + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_control_transfer( + struct libusb_transfer *transfer, libusb_device_handle *dev_handle, + unsigned char *buffer, libusb_transfer_cb_fn callback, void *user_data, + unsigned int timeout) +{ + struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *) buffer; + transfer->dev_handle = dev_handle; + transfer->endpoint = 0; + transfer->type = LIBUSB_TRANSFER_TYPE_CONTROL; + transfer->timeout = timeout; + transfer->buffer = buffer; + if (setup) + transfer->length = (int) (LIBUSB_CONTROL_SETUP_SIZE + + libusb_le16_to_cpu(setup->wLength)); + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup libusb_asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for a bulk transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_bulk_transfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, unsigned char endpoint, + unsigned char *buffer, int length, libusb_transfer_cb_fn callback, + void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_BULK; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup libusb_asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for a bulk transfer using bulk streams. + * + * Since version 1.0.19, \ref LIBUSB_API_VERSION >= 0x01000103 + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param stream_id bulk stream id for this transfer + * \param buffer data buffer + * \param length length of data buffer + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_bulk_stream_transfer( + struct libusb_transfer *transfer, libusb_device_handle *dev_handle, + unsigned char endpoint, uint32_t stream_id, + unsigned char *buffer, int length, libusb_transfer_cb_fn callback, + void *user_data, unsigned int timeout) +{ + libusb_fill_bulk_transfer(transfer, dev_handle, endpoint, buffer, + length, callback, user_data, timeout); + transfer->type = LIBUSB_TRANSFER_TYPE_BULK_STREAM; + libusb_transfer_set_stream_id(transfer, stream_id); +} + +/** \ingroup libusb_asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for an interrupt transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_interrupt_transfer( + struct libusb_transfer *transfer, libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *buffer, int length, + libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_INTERRUPT; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup libusb_asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for an isochronous transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param num_iso_packets the number of isochronous packets + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_iso_transfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, unsigned char endpoint, + unsigned char *buffer, int length, int num_iso_packets, + libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->num_iso_packets = num_iso_packets; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup libusb_asyncio + * Convenience function to set the length of all packets in an isochronous + * transfer, based on the num_iso_packets field in the transfer structure. + * + * \param transfer a transfer + * \param length the length to set in each isochronous packet descriptor + * \see libusb_get_max_packet_size() + */ +static inline void libusb_set_iso_packet_lengths( + struct libusb_transfer *transfer, unsigned int length) +{ + int i; + for (i = 0; i < transfer->num_iso_packets; i++) + transfer->iso_packet_desc[i].length = length; +} + +/** \ingroup libusb_asyncio + * Convenience function to locate the position of an isochronous packet + * within the buffer of an isochronous transfer. + * + * This is a thorough function which loops through all preceding packets, + * accumulating their lengths to find the position of the specified packet. + * Typically you will assign equal lengths to each packet in the transfer, + * and hence the above method is sub-optimal. You may wish to use + * libusb_get_iso_packet_buffer_simple() instead. + * + * \param transfer a transfer + * \param packet the packet to return the address of + * \returns the base address of the packet buffer inside the transfer buffer, + * or NULL if the packet does not exist. + * \see libusb_get_iso_packet_buffer_simple() + */ +static inline unsigned char *libusb_get_iso_packet_buffer( + struct libusb_transfer *transfer, unsigned int packet) +{ + int i; + size_t offset = 0; + int _packet; + + /* oops..slight bug in the API. packet is an unsigned int, but we use + * signed integers almost everywhere else. range-check and convert to + * signed to avoid compiler warnings. FIXME for libusb-2. */ + if (packet > INT_MAX) + return NULL; + _packet = (int) packet; + + if (_packet >= transfer->num_iso_packets) + return NULL; + + for (i = 0; i < _packet; i++) + offset += transfer->iso_packet_desc[i].length; + + return transfer->buffer + offset; +} + +/** \ingroup libusb_asyncio + * Convenience function to locate the position of an isochronous packet + * within the buffer of an isochronous transfer, for transfers where each + * packet is of identical size. + * + * This function relies on the assumption that every packet within the transfer + * is of identical size to the first packet. Calculating the location of + * the packet buffer is then just a simple calculation: + * buffer + (packet_size * packet) + * + * Do not use this function on transfers other than those that have identical + * packet lengths for each packet. + * + * \param transfer a transfer + * \param packet the packet to return the address of + * \returns the base address of the packet buffer inside the transfer buffer, + * or NULL if the packet does not exist. + * \see libusb_get_iso_packet_buffer() + */ +static inline unsigned char *libusb_get_iso_packet_buffer_simple( + struct libusb_transfer *transfer, unsigned int packet) +{ + int _packet; + + /* oops..slight bug in the API. packet is an unsigned int, but we use + * signed integers almost everywhere else. range-check and convert to + * signed to avoid compiler warnings. FIXME for libusb-2. */ + if (packet > INT_MAX) + return NULL; + _packet = (int) packet; + + if (_packet >= transfer->num_iso_packets) + return NULL; + + return transfer->buffer + ((int) transfer->iso_packet_desc[0].length * _packet); +} + +/* sync I/O */ + +int LIBUSB_CALL libusb_control_transfer(libusb_device_handle *dev_handle, + uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + unsigned char *data, uint16_t wLength, unsigned int timeout); + +int LIBUSB_CALL libusb_bulk_transfer(libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *actual_length, unsigned int timeout); + +int LIBUSB_CALL libusb_interrupt_transfer(libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *actual_length, unsigned int timeout); + +/** \ingroup libusb_desc + * Retrieve a descriptor from the default control pipe. + * This is a convenience function which formulates the appropriate control + * message to retrieve the descriptor. + * + * \param dev_handle a device handle + * \param desc_type the descriptor type, see \ref libusb_descriptor_type + * \param desc_index the index of the descriptor to retrieve + * \param data output buffer for descriptor + * \param length size of data buffer + * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure + */ +static inline int libusb_get_descriptor(libusb_device_handle *dev_handle, + uint8_t desc_type, uint8_t desc_index, unsigned char *data, int length) +{ + return libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_DESCRIPTOR, (uint16_t) ((desc_type << 8) | desc_index), + 0, data, (uint16_t) length, 1000); +} + +/** \ingroup libusb_desc + * Retrieve a descriptor from a device. + * This is a convenience function which formulates the appropriate control + * message to retrieve the descriptor. The string returned is Unicode, as + * detailed in the USB specifications. + * + * \param dev_handle a device handle + * \param desc_index the index of the descriptor to retrieve + * \param langid the language ID for the string descriptor + * \param data output buffer for descriptor + * \param length size of data buffer + * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure + * \see libusb_get_string_descriptor_ascii() + */ +static inline int libusb_get_string_descriptor(libusb_device_handle *dev_handle, + uint8_t desc_index, uint16_t langid, unsigned char *data, int length) +{ + return libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_DESCRIPTOR, (uint16_t)((LIBUSB_DT_STRING << 8) | desc_index), + langid, data, (uint16_t) length, 1000); +} + +int LIBUSB_CALL libusb_get_string_descriptor_ascii(libusb_device_handle *dev_handle, + uint8_t desc_index, unsigned char *data, int length); + +/* polling and timeouts */ + +int LIBUSB_CALL libusb_try_lock_events(libusb_context *ctx); +void LIBUSB_CALL libusb_lock_events(libusb_context *ctx); +void LIBUSB_CALL libusb_unlock_events(libusb_context *ctx); +int LIBUSB_CALL libusb_event_handling_ok(libusb_context *ctx); +int LIBUSB_CALL libusb_event_handler_active(libusb_context *ctx); +void LIBUSB_CALL libusb_interrupt_event_handler(libusb_context *ctx); +void LIBUSB_CALL libusb_lock_event_waiters(libusb_context *ctx); +void LIBUSB_CALL libusb_unlock_event_waiters(libusb_context *ctx); +int LIBUSB_CALL libusb_wait_for_event(libusb_context *ctx, struct timeval *tv); + +int LIBUSB_CALL libusb_handle_events_timeout(libusb_context *ctx, + struct timeval *tv); +int LIBUSB_CALL libusb_handle_events_timeout_completed(libusb_context *ctx, + struct timeval *tv, int *completed); +int LIBUSB_CALL libusb_handle_events(libusb_context *ctx); +int LIBUSB_CALL libusb_handle_events_completed(libusb_context *ctx, int *completed); +int LIBUSB_CALL libusb_handle_events_locked(libusb_context *ctx, + struct timeval *tv); +int LIBUSB_CALL libusb_pollfds_handle_timeouts(libusb_context *ctx); +int LIBUSB_CALL libusb_get_next_timeout(libusb_context *ctx, + struct timeval *tv); + +/** \ingroup libusb_poll + * File descriptor for polling + */ +struct libusb_pollfd { + /** Numeric file descriptor */ + int fd; + + /** Event flags to poll for from . POLLIN indicates that you + * should monitor this file descriptor for becoming ready to read from, + * and POLLOUT indicates that you should monitor this file descriptor for + * nonblocking write readiness. */ + short events; +}; + +/** \ingroup libusb_poll + * Callback function, invoked when a new file descriptor should be added + * to the set of file descriptors monitored for events. + * \param fd the new file descriptor + * \param events events to monitor for, see \ref libusb_pollfd for a + * description + * \param user_data User data pointer specified in + * libusb_set_pollfd_notifiers() call + * \see libusb_set_pollfd_notifiers() + */ +typedef void (LIBUSB_CALL *libusb_pollfd_added_cb)(int fd, short events, + void *user_data); + +/** \ingroup libusb_poll + * Callback function, invoked when a file descriptor should be removed from + * the set of file descriptors being monitored for events. After returning + * from this callback, do not use that file descriptor again. + * \param fd the file descriptor to stop monitoring + * \param user_data User data pointer specified in + * libusb_set_pollfd_notifiers() call + * \see libusb_set_pollfd_notifiers() + */ +typedef void (LIBUSB_CALL *libusb_pollfd_removed_cb)(int fd, void *user_data); + +const struct libusb_pollfd ** LIBUSB_CALL libusb_get_pollfds( + libusb_context *ctx); +void LIBUSB_CALL libusb_free_pollfds(const struct libusb_pollfd **pollfds); +void LIBUSB_CALL libusb_set_pollfd_notifiers(libusb_context *ctx, + libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb, + void *user_data); + +/** \ingroup libusb_hotplug + * Callback handle. + * + * Callbacks handles are generated by libusb_hotplug_register_callback() + * and can be used to deregister callbacks. Callback handles are unique + * per libusb_context and it is safe to call libusb_hotplug_deregister_callback() + * on an already deregisted callback. + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * For more information, see \ref libusb_hotplug. + */ +typedef int libusb_hotplug_callback_handle; + +/** \ingroup libusb_hotplug + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * Flags for hotplug events */ +typedef enum { + /** Default value when not using any flags. */ + LIBUSB_HOTPLUG_NO_FLAGS = 0U, + + /** Arm the callback and fire it for all matching currently attached devices. */ + LIBUSB_HOTPLUG_ENUMERATE = 1U << 0, +} libusb_hotplug_flag; + +/** \ingroup libusb_hotplug + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * Hotplug events */ +typedef enum { + /** A device has been plugged in and is ready to use */ + LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED = 0x01U, + + /** A device has left and is no longer available. + * It is the user's responsibility to call libusb_close on any handle associated with a disconnected device. + * It is safe to call libusb_get_device_descriptor on a device that has left */ + LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT = 0x02U, +} libusb_hotplug_event; + +/** \ingroup libusb_hotplug + * Wildcard matching for hotplug events */ +#define LIBUSB_HOTPLUG_MATCH_ANY -1 + +/** \ingroup libusb_hotplug + * Hotplug callback function type. When requesting hotplug event notifications, + * you pass a pointer to a callback function of this type. + * + * This callback may be called by an internal event thread and as such it is + * recommended the callback do minimal processing before returning. + * + * libusb will call this function later, when a matching event had happened on + * a matching device. See \ref libusb_hotplug for more information. + * + * It is safe to call either libusb_hotplug_register_callback() or + * libusb_hotplug_deregister_callback() from within a callback function. + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * \param ctx context of this notification + * \param device libusb_device this event occurred on + * \param event event that occurred + * \param user_data user data provided when this callback was registered + * \returns bool whether this callback is finished processing events. + * returning 1 will cause this callback to be deregistered + */ +typedef int (LIBUSB_CALL *libusb_hotplug_callback_fn)(libusb_context *ctx, + libusb_device *device, + libusb_hotplug_event event, + void *user_data); + +/** \ingroup libusb_hotplug + * Register a hotplug callback function + * + * Register a callback with the libusb_context. The callback will fire + * when a matching event occurs on a matching device. The callback is + * armed until either it is deregistered with libusb_hotplug_deregister_callback() + * or the supplied callback returns 1 to indicate it is finished processing events. + * + * If the \ref LIBUSB_HOTPLUG_ENUMERATE is passed the callback will be + * called with a \ref LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED for all devices + * already plugged into the machine. Note that libusb modifies its internal + * device list from a separate thread, while calling hotplug callbacks from + * libusb_handle_events(), so it is possible for a device to already be present + * on, or removed from, its internal device list, while the hotplug callbacks + * still need to be dispatched. This means that when using \ref + * LIBUSB_HOTPLUG_ENUMERATE, your callback may be called twice for the arrival + * of the same device, once from libusb_hotplug_register_callback() and once + * from libusb_handle_events(); and/or your callback may be called for the + * removal of a device for which an arrived call was never made. + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * \param[in] ctx context to register this callback with + * \param[in] events bitwise or of events that will trigger this callback. See \ref + * libusb_hotplug_event + * \param[in] flags hotplug callback flags. See \ref libusb_hotplug_flag + * \param[in] vendor_id the vendor id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY + * \param[in] product_id the product id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY + * \param[in] dev_class the device class to match or \ref LIBUSB_HOTPLUG_MATCH_ANY + * \param[in] cb_fn the function to be invoked on a matching event/device + * \param[in] user_data user data to pass to the callback function + * \param[out] callback_handle pointer to store the handle of the allocated callback (can be NULL) + * \returns LIBUSB_SUCCESS on success LIBUSB_ERROR code on failure + */ +int LIBUSB_CALL libusb_hotplug_register_callback(libusb_context *ctx, + libusb_hotplug_event events, + libusb_hotplug_flag flags, + int vendor_id, int product_id, + int dev_class, + libusb_hotplug_callback_fn cb_fn, + void *user_data, + libusb_hotplug_callback_handle *callback_handle); + +/** \ingroup libusb_hotplug + * Deregisters a hotplug callback. + * + * Deregister a callback from a libusb_context. This function is safe to call from within + * a hotplug callback. + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * \param[in] ctx context this callback is registered with + * \param[in] callback_handle the handle of the callback to deregister + */ +void LIBUSB_CALL libusb_hotplug_deregister_callback(libusb_context *ctx, + libusb_hotplug_callback_handle callback_handle); + +/** \ingroup libusb_lib + * Available option values for libusb_set_option(). + */ +enum libusb_option { + /** Set the log message verbosity. + * + * The default level is LIBUSB_LOG_LEVEL_NONE, which means no messages are ever + * printed. If you choose to increase the message verbosity level, ensure + * that your application does not close the stderr file descriptor. + * + * You are advised to use level LIBUSB_LOG_LEVEL_WARNING. libusb is conservative + * with its message logging and most of the time, will only log messages that + * explain error conditions and other oddities. This will help you debug + * your software. + * + * If the LIBUSB_DEBUG environment variable was set when libusb was + * initialized, this function does nothing: the message verbosity is fixed + * to the value in the environment variable. + * + * If libusb was compiled without any message logging, this function does + * nothing: you'll never get any messages. + * + * If libusb was compiled with verbose debug message logging, this function + * does nothing: you'll always get messages from all levels. + */ + LIBUSB_OPTION_LOG_LEVEL, + + /** Use the UsbDk backend for a specific context, if available. + * + * This option should be set immediately after calling libusb_init(), otherwise + * unspecified behavior may occur. + * + * Only valid on Windows. + */ + LIBUSB_OPTION_USE_USBDK, +}; + +int LIBUSB_CALL libusb_set_option(libusb_context *ctx, enum libusb_option option, ...); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libusb/readme.md b/libusb/readme.md new file mode 100644 index 0000000..2194b9a --- /dev/null +++ b/libusb/readme.md @@ -0,0 +1,3 @@ +Files here are from https://github.com/libusb/libusb/releases/tag/v1.0.23 +libusb-1.0.a is the MinGW64 static library. +Those files were release as LGPL 2.1 \ No newline at end of file diff --git a/src/crypto.c b/src/crypto.c index f962af7..ba2f789 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -21,7 +21,11 @@ #include #include -#include +#ifdef WINDOWS + #include +#else + #include +#endif #include "../tiny-AES-c/aes.h" #include "crypto.h" @@ -53,3 +57,21 @@ void my_encrypt(unsigned char *key, unsigned char *data, unsigned int length) { } convert_to_big_endian(data, length); } + +void my_decrypt(unsigned char *key, unsigned char *data, unsigned int length) { + struct AES_ctx ctx; + unsigned char key_be[16]; + size_t i; + + memcpy(key_be, key, 16); + convert_to_big_endian(key_be, 16); + + AES_init_ctx(&ctx, key_be); + + convert_to_big_endian(data, length); + + for (i = 0; i < length; i += 16) { + AES_ECB_decrypt(&ctx, data+i); + } + convert_to_big_endian(data, length); +} diff --git a/src/crypto.h b/src/crypto.h index 2df225b..3a9f448 100644 --- a/src/crypto.h +++ b/src/crypto.h @@ -20,3 +20,4 @@ */ void my_encrypt(unsigned char *key, unsigned char *data, unsigned int length); +void my_decrypt(unsigned char *key, unsigned char *data, unsigned int length); diff --git a/src/getopt.c b/src/getopt.c new file mode 100644 index 0000000..948c72f --- /dev/null +++ b/src/getopt.c @@ -0,0 +1,1274 @@ +/* Getopt for GNU. +NOTE: getopt is now part of the C library, so if you don't know what +"Keep this file name-space clean" means, talk to drepper@gnu.org +before changing it! +Copyright (C) 1987,88,89,90,91,92,93,94,95,96,98,99,2000,2001 +Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +The GNU C Library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with the GNU C Library; if not, write to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. */ + +/* This tells Alpha OSF/1 not to define a getopt prototype in . +Ditto for AIX 3.2 and . */ +#ifndef _NO_PROTO +# define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +# include +#endif + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems +reject `defined (const)'. */ +# ifndef const +# define const +# endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not +actually compiling the library itself. This code is part of the GNU C +Library, but also included in many other GNU distributions. Compiling +and linking in this code is a waste when using the GNU C library +(especially if it is a shared library). Rather than having every GNU +program understand `configure --with-gnu-libc' and omit the object files, +it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +# include +# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +# define ELIDE_CODE +# endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include +to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them +contain conflicting prototypes for getopt. */ +# include +# include +#endif /* GNU C library. */ + +#ifdef VMS +# include +# if HAVE_STRING_H - 0 +# include +# endif +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. */ +# if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC +# include +# ifndef _ +# define _(msgid) gettext (msgid) +# endif +# else +# define _(msgid) (msgid) +# endif +# if defined _LIBC && defined USE_IN_LIBIO +# include +# endif +#endif + +/* This version of `getopt' appears to the caller like standard Unix `getopt' +but it behaves differently for the user, since it allows the user +to intersperse the options with the other arguments. + +As `getopt' works, it permutes the elements of ARGV so that, +when it is done, all the options precede everything else. Thus +all application programs are extended to handle flexible argument order. + +Setting the environment variable POSIXLY_CORRECT disables permutation. +Then the behavior is completely standard. + +GNU application programs can use a third alternative mode in which +they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. +When `getopt' finds an option that takes an argument, +the argument value is returned here. +Also, when `ordering' is RETURN_IN_ORDER, +each non-option ARGV-element is returned here. */ + +char *optarg; + +/* Index in ARGV of the next element to be scanned. +This is used for communication to and from the caller +and for communication between successive calls to `getopt'. + +On entry to `getopt', zero means this is the first call; initialize. + +When `getopt' returns -1, this is the index of the first of the +non-option elements that the caller should itself scan. + +Otherwise, `optind' communicates from one call to the next +how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Formerly, initialization of getopt depended on optind==0, which +causes problems with re-calling getopt as programs generally don't +know that. */ + +int __getopt_initialized; + +/* The next char to be scanned in the option-element +in which the last option character we returned was found. +This allows us to pick up the scan where we left off. + +If this is zero, or a null string, it means resume the scan +by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message +for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. +This must be initialized on some systems to avoid linking in the +system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + +If the caller did not specify anything, +the default is REQUIRE_ORDER if the environment variable +POSIXLY_CORRECT is defined, PERMUTE otherwise. + +REQUIRE_ORDER means don't recognize them as options; +stop option processing when the first non-option is seen. +This is what Unix does. +This mode of operation is selected by either setting the environment +variable POSIXLY_CORRECT, or using `+' as the first character +of the list of option characters. + +PERMUTE is the default. We permute the contents of ARGV as we scan, +so that eventually all the non-options are at the end. This allows options +to be given in any order, even with programs that were not written to +expect this. + +RETURN_IN_ORDER is an option available to programs that were written +to expect options and other ARGV-elements in any order and that care about +the ordering of the two. We describe each non-option ARGV-element +as if it were the argument of an option with character code 1. +Using `-' as the first character of the list of option characters +selects this mode of operation. + +The special argument `--' forces an end of option-scanning regardless +of the value of `ordering'. In the case of RETURN_IN_ORDER, only +`--' can cause `getopt' to return -1 with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries +because there are many ways it can cause trouble. +On some systems, it contains special magic macros that don't work +in GCC. */ +# include +# define my_index strchr +#else + +#define HAVE_STRING_H 1 +# if HAVE_STRING_H +# include +# else +# include +# endif + +/* Avoid depending on library functions or files +whose names are inconsistent. */ + +#ifndef getenv +extern char *getenv(); +#endif + +static char * +my_index(str, chr) +const char *str; +int chr; +{ + while (*str) + { + if (*str == chr) + return (char *)str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. +If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. +That was relevant to code that was here before. */ +# if (!defined __STDC__ || !__STDC__) && !defined strlen +/* gcc with -traditional declares the built-in strlen to return int, +and has done so at least since version 2.4.5. -- rms. */ +extern int strlen(const char *); +# endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have +been skipped. `first_nonopt' is the index in ARGV of the first of them; +`last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +#ifdef _LIBC +/* Stored original parameters. +XXX This is no good solution. We should rather copy the args so +that we can compare them later. But we must not use malloc(3). */ +extern int __libc_argc; +extern char **__libc_argv; + +/* Bash 2.0 gives us an environment variable containing flags +indicating ARGV elements that should not be considered arguments. */ + +# ifdef USE_NONOPTION_FLAGS +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; + +static int nonoption_flags_max_len; +static int nonoption_flags_len; +# endif + +# ifdef USE_NONOPTION_FLAGS +# define SWAP_FLAGS(ch1, ch2) \ +if (nonoption_flags_len > 0) \ +{ \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ +} +# else +# define SWAP_FLAGS(ch1, ch2) +# endif +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* Exchange two adjacent subsequences of ARGV. +One subsequence is elements [first_nonopt,last_nonopt) +which contains all the non-options that have been skipped so far. +The other is elements [last_nonopt,optind), which contains all +the options processed since those non-options were skipped. + +`first_nonopt' and `last_nonopt' are relocated so that they describe +the new indices of the non-options in ARGV after they are moved. */ + +#if defined __STDC__ && __STDC__ +static void exchange(char **); +#endif + +static void +exchange(argv) +char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + +#if defined _LIBC && defined USE_NONOPTION_FLAGS + /* First make sure the handling of the `__getopt_nonoption_flags' + string can work normally. Our top argument must be in the range + of the string. */ + if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) + { + /* We must extend the array. The user plays games with us and + presents new arguments. */ + char *new_str = malloc(top + 1); + if (new_str == NULL) + nonoption_flags_len = nonoption_flags_max_len = 0; + else + { + memset(__mempcpy(new_str, __getopt_nonoption_flags, + nonoption_flags_max_len), + '\0', top + 1 - nonoption_flags_max_len); + nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS(bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS(bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +#if defined __STDC__ && __STDC__ +static const char *_getopt_initialize(int, char *const *, const char *); +#endif +static const char * +_getopt_initialize(argc, argv, optstring) +int argc; +char *const *argv; +const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind; + + nextchar = NULL; + + posixly_correct = getenv("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + +#if defined _LIBC && defined USE_NONOPTION_FLAGS + if (posixly_correct == NULL + && argc == __libc_argc && argv == __libc_argv) + { + if (nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = nonoption_flags_max_len = strlen(orig_str); + if (nonoption_flags_max_len < argc) + nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *)malloc(nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + nonoption_flags_max_len = -1; + else + memset(__mempcpy(__getopt_nonoption_flags, orig_str, len), + '\0', nonoption_flags_max_len - len); + } + } + nonoption_flags_len = nonoption_flags_max_len; + } + else + nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters +given in OPTSTRING. + +If an element of ARGV starts with '-', and is not exactly "-" or "--", +then it is an option element. The characters of this element +(aside from the initial '-') are option characters. If `getopt' +is called repeatedly, it returns successively each of the option characters +from each of the option elements. + +If `getopt' finds another option character, it returns that character, +updating `optind' and `nextchar' so that the next call to `getopt' can +resume the scan with the following option character or ARGV-element. + +If there are no more option characters, `getopt' returns -1. +Then `optind' is the index in ARGV of the first ARGV-element +that is not an option. (The ARGV-elements have been permuted +so that those that are not options now come last.) + +OPTSTRING is a string containing the legitimate option characters. +If an option character is seen that is not listed in OPTSTRING, +return '?' after printing an error message. If you set `opterr' to +zero, the error message is suppressed but we still return '?'. + +If a char in OPTSTRING is followed by a colon, that means it wants an arg, +so the following text in the same ARGV-element, or the text of the following +ARGV-element, is returned in `optarg'. Two colons mean an option that +wants an optional arg; if there is text in the current ARGV-element, +it is returned in `optarg', otherwise `optarg' is set to zero. + +If OPTSTRING starts with `-' or `+', it requests different methods of +handling the non-option ARGV-elements. +See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + +Long-named options begin with `--' instead of `-'. +Their names may be abbreviated as long as the abbreviation is unique +or is an exact match for some defined option. If they have an +argument, it follows the option name in the same ARGV-element, separated +from the option name by a `=', or else the in next ARGV-element. +When `getopt' finds a long-named option, it returns 0 if that option's +`flag' field is nonzero, the value of the option's `val' field +if the `flag' field is zero. + +The elements of ARGV aren't really const, because we permute them. +But we pretend they're const in the prototype to be compatible +with other systems. + +LONGOPTS is a vector of `struct option' terminated by an +element containing a name which is zero. + +LONGIND returns the index in LONGOPT of the long-named option found. +It is only valid when a long-named option has been found by the most +recent call. + +If LONG_ONLY is nonzero, '-' as well as '--' can introduce +long-named options. */ + +int +_getopt_internal(argc, argv, optstring, longopts, longind, long_only) +int argc; +char *const *argv; +const char *optstring; +const struct option *longopts; +int *longind; +int long_only; +{ + int print_errors = opterr; + if (optstring[0] == ':') + print_errors = 0; + + if (argc < 1) + return -1; + + optarg = NULL; + + if (optind == 0 || !__getopt_initialized) + { + if (optind == 0) + optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize(argc, argv, optstring); + __getopt_initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +#if defined _LIBC && defined USE_NONOPTION_FLAGS +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ + || (optind < nonoption_flags_len \ + && __getopt_nonoption_flags[optind] == '1')) +#else +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') +#endif + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > optind) + last_nonopt = optind; + if (first_nonopt > optind) + first_nonopt = optind; + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange((char **)argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc && NONOPTION_P) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp(argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange((char **)argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index(optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp(p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int)(nameend - nextchar) + == (unsigned int)strlen(p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else if (long_only + || pfound->has_arg != p->has_arg + || pfound->flag != p->flag + || pfound->val != p->val) + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + __asprintf(&buf, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[optind]); + + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); + + free(buf); +#else + fprintf(stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[optind]); +#endif + } + nextchar += strlen(nextchar); + optind++; + optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; +#endif + + if (argv[optind - 1][1] == '-') + { + /* --option */ +#if defined _LIBC && defined USE_IN_LIBIO + __asprintf(&buf, _("\ + %s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); +#else + fprintf(stderr, _("\ + %s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); +#endif + } + else + { + /* +option or -option */ +#if defined _LIBC && defined USE_IN_LIBIO + __asprintf(&buf, _("\ + %s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], + pfound->name); +#else + fprintf(stderr, _("\ + %s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], pfound->name); +#endif + } + +#if defined _LIBC && defined USE_IN_LIBIO + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); + + free(buf); +#endif + } + + nextchar += strlen(nextchar); + + optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + __asprintf(&buf, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); + + free(buf); +#else + fprintf(stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); +#endif + } + nextchar += strlen(nextchar); + optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen(nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index(optstring, *nextchar) == NULL) + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; +#endif + + if (argv[optind][1] == '-') + { + /* --option */ +#if defined _LIBC && defined USE_IN_LIBIO + __asprintf(&buf, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); +#else + fprintf(stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); +#endif + } + else + { + /* +option or -option */ +#if defined _LIBC && defined USE_IN_LIBIO + __asprintf(&buf, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[optind][0], nextchar); +#else + fprintf(stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[optind][0], nextchar); +#endif + } + +#if defined _LIBC && defined USE_IN_LIBIO + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); + + free(buf); +#endif + } + nextchar = (char *) ""; + optind++; + optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index(optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; +#endif + + if (posixly_correct) + { + /* 1003.2 specifies the format of this message. */ +#if defined _LIBC && defined USE_IN_LIBIO + __asprintf(&buf, _("%s: illegal option -- %c\n"), + argv[0], c); +#else + fprintf(stderr, _("%s: illegal option -- %c\n"), argv[0], c); +#endif + } + else + { +#if defined _LIBC && defined USE_IN_LIBIO + __asprintf(&buf, _("%s: invalid option -- %c\n"), + argv[0], c); +#else + fprintf(stderr, _("%s: invalid option -- %c\n"), argv[0], c); +#endif + } + +#if defined _LIBC && defined USE_IN_LIBIO + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); + + free(buf); +#endif + } + optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (print_errors) + { + /* 1003.2 specifies the format of this message. */ +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + __asprintf(&buf, _("%s: option requires an argument -- %c\n"), + argv[0], c); + + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); + + free(buf); +#else + fprintf(stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); +#endif + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) + /* Do nothing. */; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp(p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int)(nameend - nextchar) == strlen(p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + __asprintf(&buf, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]); + + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); + + free(buf); +#else + fprintf(stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]); +#endif + } + nextchar += strlen(nextchar); + optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + __asprintf(&buf, _("\ + %s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); + + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); + + free(buf); +#else + fprintf(stderr, _("\ + %s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); +#endif + } + + nextchar += strlen(nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + __asprintf(&buf, _("\ + %s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); + + free(buf); +#else + fprintf(stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); +#endif + } + nextchar += strlen(nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen(nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (print_errors) + { + /* 1003.2 specifies the format of this message. */ +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + __asprintf(&buf, + _("%s: option requires an argument -- %c\n"), + argv[0], c); + + if (_IO_fwide(stderr, 0) > 0) + __fwprintf(stderr, L"%s", buf); + else + fputs(buf, stderr); + + free(buf); +#else + fprintf(stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); +#endif + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt(argc, argv, optstring) +int argc; +char *const *argv; +const char *optstring; +{ + return _getopt_internal(argc, argv, optstring, + (const struct option *) 0, + (int *)0, + 0); +} + + + + +int +getopt_long(int argc, char *const *argv, const char *options, +const struct option *long_options, int *opt_index) +{ + return _getopt_internal(argc, argv, options, long_options, opt_index, 0, 0); +} + +int +getopt_long_only(int argc, char *const *argv, const char *options, +const struct option *long_options, int *opt_index) +{ + return _getopt_internal(argc, argv, options, long_options, opt_index, 1, 0); +} + + + + + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing +the above definition of `getopt'. */ + +int +main(argc, argv) +int argc; +char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt(argc, argv, "abc:d:0123456789"); + if (c == -1) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf("option %c\n", c); + break; + + case 'a': + printf("option a\n"); + break; + + case 'b': + printf("option b\n"); + break; + + case 'c': + printf("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf("non-option ARGV-elements: "); + while (optind < argc) + printf("%s ", argv[optind++]); + printf("\n"); + } + + exit(0); +} + +#endif /* TEST */ diff --git a/src/getopt.h b/src/getopt.h new file mode 100644 index 0000000..6e2fa27 --- /dev/null +++ b/src/getopt.h @@ -0,0 +1,193 @@ +/* Declarations for getopt. + Copyright (C) 1989-1994,1996-1999,2001,2003,2004,2009,2010 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifndef _GETOPT_H + +#ifndef __need_getopt +# define _GETOPT_H 1 +#endif + +/* If __GNU_LIBRARY__ is not already defined, either we are being used + standalone, or this is the first header included in the source file. + If we are being used with glibc, we need to include , but + that does not exist if we are standalone. So: if __GNU_LIBRARY__ is + not defined, include , which will pull in for us + if it's from glibc. (Why ctype.h? It's guaranteed to exist and it + doesn't flood the namespace with stuff the way some other headers do.) */ +#if !defined __GNU_LIBRARY__ +# include +#endif + +#ifndef __THROW +# ifndef __GNUC_PREREQ +# define __GNUC_PREREQ(maj, min) (0) +# endif +# if defined __cplusplus && __GNUC_PREREQ (2,8) +# define __THROW throw () +# else +# define __THROW +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +#ifndef __need_getopt +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ + const char *name; + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +# define no_argument 0 +# define required_argument 1 +# define optional_argument 2 +#endif /* need getopt */ + + +/* Get definitions and prototypes for functions to process the + arguments in ARGV (ARGC of them, minus the program name) for + options given in OPTS. + + Return the option character from OPTS just read. Return -1 when + there are no more options. For unrecognized options, or options + missing arguments, `optopt' is set to the option letter, and '?' is + returned. + + The OPTS string is a list of characters which are recognized option + letters, optionally followed by colons, specifying that that letter + takes an argument, to be placed in `optarg'. + + If a letter in OPTS is followed by two colons, its argument is + optional. This behavior is specific to the GNU `getopt'. + + The argument `--' causes premature termination of argument + scanning, explicitly telling `getopt' that there are no more + options. + + If OPTS begins with `--', then non-option arguments are treated as + arguments to the option '\0'. This behavior is specific to the GNU + `getopt'. */ + +#ifdef __GNU_LIBRARY__ +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int ___argc, char *const *___argv, const char *__shortopts) + __THROW; + +# if defined __need_getopt && defined __USE_POSIX2 \ + && !defined __USE_POSIX_IMPLICITLY && !defined __USE_GNU +/* The GNU getopt has more functionality than the standard version. The + additional functionality can be disable at runtime. This redirection + helps to also do this at runtime. */ +# ifdef __REDIRECT + extern int __REDIRECT_NTH (getopt, (int ___argc, char *const *___argv, + const char *__shortopts), + __posix_getopt); +# else +extern int __posix_getopt (int ___argc, char *const *___argv, + const char *__shortopts) __THROW; +# define getopt __posix_getopt +# endif +# endif +#else /* not __GNU_LIBRARY__ */ +extern int getopt (); +#endif /* __GNU_LIBRARY__ */ + +#ifndef __need_getopt +extern int getopt_long (int ___argc, char *const *___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind) + __THROW; +extern int getopt_long_only (int ___argc, char *const *___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind) + __THROW; + +#endif + +#ifdef __cplusplus +} +#endif + +/* Make sure we later can get all the definitions and declarations. */ +#undef __need_getopt + +#endif /* getopt.h */ diff --git a/src/main.c b/src/main.c index 492f219..df9dcbf 100644 --- a/src/main.c +++ b/src/main.c @@ -19,105 +19,448 @@ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include -#include -#include -#include +#ifdef WINDOWS + #include "getopt.h" +#else + #include +#endif #include "stlink.h" -#define STLINK_VID 0x0483 -#define STLINK_PID 0x3748 +#ifndef min + #define min(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#define max_per_line(x) (++x % 2 == 0 ? '\n' : '\0') + +#define STLINK_VID 0x0483 +#define STLINK_PID 0x3748 +#define STLINK_PIDV21 0x374b +#define STLINK_PIDV21_MSD 0x3752 +#define STLINK_PIDV3 0x374f +#define STLINK_PIDV3_BL 0x374d + +#define OPENMOKO_VID 0x1d50 +#define BMP_APPL_PID 0x6018 +#define BMP_DFU_IF 4 + +enum OptionsVal { + optHELP = 0, + optPROBE, + optDECRYPT, + optSAVE_DEC, + optST_TYPE, + optVERSION, + optFIX, + optUSB_CUR, + optMSD_NAME, + optMBED_NAME, + optDFU_OPT, + optDYN_OPT, + optMCO_OUT, + optSTARTUP +}; + +static struct option long_options[] = { + {"help", 0, 0, optHELP}, + {"h", 0, 0, optHELP}, + + {"probe", 0, 0, optPROBE}, + {"p", 0, 0, optPROBE}, + + {"decrypt", 1, 0, optDECRYPT}, + {"d", 1, 0, optDECRYPT}, + + {"save_dec", 0, 0, optSAVE_DEC}, + {"sd", 0, 0, optSAVE_DEC}, + + {"st_type", 1, 0, optST_TYPE}, + {"t", 1, 0, optST_TYPE}, + + {"ver", 1, 0, optVERSION}, + {"v", 1, 0, optVERSION}, + + {"fix", 0, 0, optFIX}, + {"f", 0, 0, optFIX}, + + {"usb_cur", 1, 0, optUSB_CUR}, + {"rm_usb_cur", 0, 0, optUSB_CUR}, + + {"msd_name", 1, 0, optMSD_NAME}, + {"rm_msd_name", 0, 0, optMSD_NAME}, + + {"mbed_name", 1, 0, optMBED_NAME}, + {"rm_mbed_name", 0, 0, optMBED_NAME}, + + {"dfu_opt", 1, 0, optDFU_OPT}, + {"rm_dfu_opt", 0, 0, optDFU_OPT}, + + {"dynamic_opt", 1, 0, optDYN_OPT}, + {"rm_dynamic_opt", 0, 0, optDYN_OPT}, + + {"mco_out", 1, 0, optMCO_OUT}, + {"rm_mco_out", 0, 0, optMCO_OUT}, + + {"startup", 1, 0, optSTARTUP}, + {"rm_startup", 0, 0, optSTARTUP}, + + {0 ,0, 0, 0} +}; void print_help(char *argv[]) { printf("Usage: %s [options] [firmware.bin]\n", argv[0]); printf("Options:\n"); - printf("\t-p\tProbe the ST-Link adapter\n"); - printf("\t-h\tShow help\n\n"); - printf("\tApplication is started when called without argument or after firmware load\n\n"); + printf(" -h, --help\t\tShow help\n"); + printf(" -p, --probe\t\tProbe the ST-Link adapter\n"); + printf(" -d, --decrypt KEY\tDecrypt Firmware using KEY. Pass \"\" to use internal key.\n"); + printf(" -sd, --save_dec\tSave decripted firmware as filename + .dec\n"); + printf(" -t, --st_type TYPE\tChange STLink type to TYPE.\n"); + for (int i = 'A'; i <= 'Z'; i++) { + if (st_types[i]) + printf("\t\t\t %c for \"%s\"\n", (char)i, st_types[i]); + } + printf(" -v, --ver S.J.X\tChange reported STLink sersion.\n\t\t\t S is STLink version, J is JTAG version,\n\t\t\t X is SWIM or MSD version.\n"); + printf(" -f, --fix\t\tFlash Anti-Clone Tag and Firmware Exists/EOF Tag\n\n"); + printf("Options for Modifying Device Config (Only for STLink v2 and up):\n"); + printf(" --usb_cur CURRENT\tSet the MaxPower reported in USB Descriptor\n\t\t\tto CURRENT(mA)\n"); + printf(" --msd_name VOLUME\tSet the volsume name of the MSD drive to VOLUME.\n"); + printf(" --mbed_name NAME\tSet the MBED board name to NAME.\n"); + printf(" --dfu_opt OPT\t\tSet DFU Options to OPT.\n\t\t\tOPT is the Decimal value of Bit Field:\n\t\t\t bit1: \"No Power Off\"\n\t\t\t bit2: \"Autostart\"\n"); + printf(" --dynamic_opt OPT\tSet Dynamic Option to OPT.\n\t\t\t 'V': MSD Off\n\t\t\t 'M': MSD On\n\t\t\t 'W': MSD Always Off\n"); + printf(" --mco_out OPT\t\tSet MCO Output to OPT. OPT is the Hex value of:\n\t\t\t Lower Nybble(MCO Source):\n\t\t\t 0: None\n\t\t\t 1: HSI\n\t\t\t 2: HSE\n\t\t\t 3: PLL\n\t\t\t Upper Nybble (Divider):\n\t\t\t Divider - 1 (Valid Divider 1-5)\n"); + printf(" --startup OPT\t\tSet Startup Preferences to OPT.\n\t\t\t 0: High Power\n\t\t\t 1: Balanced\n\t\t\t 2: Low Power\n\t\t\t 3: Default\n"); + printf(" To remove a configuration you can use the \"\" argument with the option\n (Ex. --usb_cur \"\") or prefix the option with rm_ (Ex. --rm_usb_cur).\n\n"); + printf("Application in Flash is started when called without argument, after firmware\nload or configuration change.\n\n"); } +#include int main(int argc, char *argv[]) { - libusb_context *usb_ctx; - libusb_device_handle *dev_handle; - struct STLinkInfos infos; - int res, i, opt, probe = 0; + struct STLinkInfo info; + struct STLinkConfig config; + int res = EXIT_FAILURE, i, opt; + bool probe = false, decrypt = false, save_decrypted = false, flash_config = false, fix_config = false; + char* boot_ver = ""; + char ver_type = 'S'; + + memset(&info.config, 0, sizeof(info.config)); + memset(info.config.raw_config, 0xFF, sizeof(info.config.raw_config)); + memset(&config, 0, sizeof(config)); + memset(config.raw_config, 0xFF, sizeof(config.raw_config)); - while ((opt = getopt(argc, argv, "hp")) != -1) { + while ((opt = getopt_long_only(argc, argv, ":", long_options, NULL)) != -1) { + //printf("%d/%d %d %c %s", optind, argc, opt, optopt, optarg); switch (opt) { - case 'p': /* Probe mode */ - probe = 1; - break; - case 'h': /* Help */ - print_help(argv); - return EXIT_SUCCESS; - break; - default: - print_help(argv); - return EXIT_FAILURE; - break; + case optPROBE: /* Probe mode */ + probe = true; + break; + case optDECRYPT: + decrypt = true; + if (optarg && strlen(optarg) > 0) { + info.decrypt_key = optarg; + } else { + info.decrypt_key = NULL; + } + break; + case optSAVE_DEC: + save_decrypted = true; + break; + case optST_TYPE: + if (optarg && strlen(optarg) > 0) { + config.modify[confST_TYPE] = modADD; + config.stlink_type = optarg[0]; + } + break; + case optVERSION: + if (optarg && strlen(optarg) > 0) { + char* st_v = strtok(optarg, "."); + char* jt_v = strtok(NULL, "."); + char* sw_v = strtok(NULL, "."); + + config.modify[confVERSION] = modADD; + config.soft_version = (atoi(st_v) & 0xF) << 12 | (atoi(jt_v) & 0x3F) << 6 | (atoi(sw_v) & 0x3F); + } + break; + case optFIX: + fix_config = true; + break; + case optUSB_CUR: + if (optarg && strlen(optarg) > 0) { + config.modify[confUSB_CUR] = modADD; + config.usb_current = atoi(optarg); + } else { + config.modify[confUSB_CUR] = modREMOVE; + } + break; + case optMSD_NAME: + if (optarg && strlen(optarg) > 0) { + config.modify[confMSD_NAME] = modADD; + memcpy(config.volume, optarg, min(sizeof(config.volume) - 1, strlen(optarg))); + config.volume[11] = '\0'; + } else { + config.modify[confMSD_NAME] = modREMOVE; + } + break; + case optMBED_NAME: + if (optarg && strlen(optarg) > 0) { + config.modify[confMBED_NAME] = modADD; + memcpy(config.mbed_name, optarg, min(sizeof(config.mbed_name) - 1, strlen(optarg))); + config.mbed_name[4] = '\0'; + } else { + config.modify[confMBED_NAME] = modREMOVE; + } + break; + case optDFU_OPT: + if (optarg && strlen(optarg) > 0) { + config.modify[confDFU_OPT] = modADD; + config.dfu_option = atoi(optarg); + } else { + config.modify[confDFU_OPT] = modREMOVE; + } + break; + case optDYN_OPT: + if (optarg && strlen(optarg) > 0) { + config.modify[confDYN_OPT] = modADD; + config.dynamic_option = optarg[0]; + } else { + config.modify[confDYN_OPT] = modREMOVE; + } + break; + case optMCO_OUT: + if (optarg && strlen(optarg) > 0) { + config.modify[confMCO_OUT] = modADD; + config.mco_output = strtol(optarg, NULL, 16); + } else { + config.modify[confMCO_OUT] = modREMOVE; + } + break; + case optSTARTUP: + if (optarg && strlen(optarg) > 0) { + config.modify[confSTARTUP] = modADD; + config.startup_pref = atoi(optarg); + } else { + config.modify[confSTARTUP] = modREMOVE; + } + break; + case optHELP: /* Help */ + print_help(argv); + return EXIT_SUCCESS; + break; + default: + print_help(argv); + return EXIT_FAILURE; + break; } } int do_load = (optind < argc); - res = libusb_init(&usb_ctx); - (void)res; - - dev_handle = libusb_open_device_with_vid_pid(usb_ctx, - STLINK_VID, - STLINK_PID); - if (!dev_handle) { + res = libusb_init(&info.stinfo_usb_ctx); +rescan: + info.stinfo_dev_handle = NULL; + libusb_device **devs; + int n_devs = libusb_get_device_list(info.stinfo_usb_ctx, &devs); + if (n_devs < 0) + goto exit_libusb; + for (int i = 0; devs[i]; i++) { + libusb_device *dev = devs[i]; + struct libusb_device_descriptor desc; + int res = libusb_get_device_descriptor(dev, &desc); + if (res < 0) + continue; + if ((desc.idVendor == OPENMOKO_VID) && (desc.idProduct == BMP_APPL_PID)) { + res = libusb_open(dev, &info.stinfo_dev_handle); + if (res < 0) { + fprintf(stderr, "Can not open BMP/Application!\n"); + continue; + } + libusb_claim_interface(info.stinfo_dev_handle, BMP_DFU_IF); + res = libusb_control_transfer(info.stinfo_dev_handle, + /* bmRequestType */ LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ 0, /*DFU_DETACH,*/ + /* wValue */ 1000, + /* wIndex */ BMP_DFU_IF, + /* Data */ NULL, + /* wLength */ 0, + 5000 ); + libusb_release_interface(info.stinfo_dev_handle, 0); + if (res < 0) { + fprintf(stderr, "BMP Switch failed\n"); + continue; + } + libusb_free_device_list(devs, 1); + usleep(2000000); + goto rescan; + break; + } + if (desc.idVendor != STLINK_VID) + continue; + switch (desc.idProduct) { + case STLINK_PID: + res = libusb_open(dev, &info.stinfo_dev_handle); + if (res < 0) { + fprintf(stderr, "Can not open STLINK/Bootloader!\n"); + continue; + } + info.stinfo_ep_in = 1 | LIBUSB_ENDPOINT_IN; + info.stinfo_ep_out = 2 | LIBUSB_ENDPOINT_OUT; + info.stinfo_bl_type = STLINK_BL_V2; + //fprintf(stderr, "STLinkV2, STLinkV2-1 Bootloader found\n"); + break; + case STLINK_PIDV3_BL: + res = libusb_open(dev, &info.stinfo_dev_handle); + if (res < 0) { + fprintf(stderr, "Can not open STLINK-V3/Bootloader!\n"); + continue; + } + info.stinfo_ep_in = 1 | LIBUSB_ENDPOINT_IN; + info.stinfo_ep_out = 1 | LIBUSB_ENDPOINT_OUT; + info.stinfo_bl_type = STLINK_BL_V3; + //fprintf(stderr, "StlinkV3 Bootloader found\n"); + break; + case STLINK_PIDV21: + case STLINK_PIDV21_MSD: + case STLINK_PIDV3: + res = libusb_open(dev, &info.stinfo_dev_handle); + if (res < 0) { + fprintf(stderr, "Can not open STLINK/Application!\n"); + continue; + } + if (libusb_claim_interface(info.stinfo_dev_handle, 0)) { + fprintf(stderr, "Unable to claim USB interface ! Please close all programs that " + "may communicate with an ST-Link dongle.\n"); + continue; + } + res = stlink_dfu_mode(info.stinfo_dev_handle, 0); + if (res != 0x8000) { + libusb_release_interface(info.stinfo_dev_handle, 0); + return 0; + } + stlink_dfu_mode(info.stinfo_dev_handle, 1); + libusb_release_interface(info.stinfo_dev_handle, 0); + libusb_free_device_list(devs, 1); + fprintf(stderr, "Trying to switch STLINK/Application to bootloader\n"); + usleep(3000000); + goto rescan; + break; + } + if (info.stinfo_dev_handle) + break; + } + libusb_free_device_list(devs, 1); + if (!info.stinfo_dev_handle) { fprintf(stderr, "No ST-Link in DFU mode found. Replug ST-Link to flash!\n"); return EXIT_FAILURE; } - if (libusb_claim_interface(dev_handle, 0)) { - fprintf(stderr, "Unable to claim USB interface ! Please close all programs that may communicate with an ST-Link dongle.\n"); + if (libusb_claim_interface(info.stinfo_dev_handle, 0)) { + fprintf(stderr, "Unable to claim USB interface ! Please close all programs that " + "may communicate with an ST-Link dongle.\n"); return EXIT_FAILURE; } - if (stlink_read_infos(dev_handle, &infos)) { - libusb_release_interface(dev_handle, 0); + if (stlink_read_info(&info)) { + libusb_release_interface(info.stinfo_dev_handle, 0); return EXIT_FAILURE; } + + switch (info.stinfo_bl_type) { + case STLINK_BL_V2: + boot_ver = "2"; + break; + case STLINK_BL_V21: + boot_ver = "2-1"; + ver_type = 'M'; + break; + case STLINK_BL_V3: + boot_ver = "3"; + break; + } + + printf("STLinkV%s Bootloader Found\n", boot_ver); + char msd_opt = info.config.dynamic_option == 'V' ? 'A' : 0; + printf("STLink Type: %c [%s]\n", info.config.stlink_type, st_types[(uint8_t)info.config.stlink_type - msd_opt]); + printf("Firmware Version: V%uJ%u%c%u\n\n", info.stlink_version, + info.jtag_version, ver_type, info.swim_version); + + if (info.mode > 1) { + i = 0; + printf("Current Device Configuration:\n"); + if (info.config.modify[confUSB_CUR] == modADD) + printf("USB Current: [%umA] %c", info.config.usb_current, max_per_line(i)); + if (info.config.modify[confMSD_NAME] == modADD) + printf("MSD Volume: [%s] %c", info.config.volume, max_per_line(i)); + if (info.config.modify[confMBED_NAME] == modADD) + printf("MBED Board Name: [%s] %c", info.config.mbed_name, max_per_line(i)); + if (info.config.modify[confDFU_OPT] == modADD) + printf("DFU Options: [%s] %c", stlink_get_dev_config(&info.config, confDFU_OPT), max_per_line(i)); + if (info.config.modify[confDYN_OPT] == modADD) + printf("Dynamic Options: [%s] %c", stlink_get_dev_config(&info.config, confDYN_OPT), max_per_line(i)); + if (info.config.modify[confMCO_OUT] == modADD) + printf("MCO Output: [%u] %c", info.config.mco_output, max_per_line(i)); + if (info.config.modify[confSTARTUP] == modADD) + printf("Startup Pref: [%s]", stlink_get_dev_config(&info.config, confSTARTUP)); + printf("\n\n"); + } + + printf("Bootloader PID: %04X\n", info.bootloader_pid); + if (info.mode > 1) + printf("HW Version: V%u.%u Flags: 0x%06X\n", info.hardware_mayor, info.hardware_minor, info.hardware_flags); + printf("Reported Flash Size: %uKB%s%s\n\n", info.reported_flash_size, info.hardware_flags & 0x0001 ? " (128KB Overwrite Flag)" : "", info.hardware_flags & 0x0002 ? " (20KB Reserved Flash)" : ""); - printf("Firmware version : V%dJ%dS%d\n", infos.stlink_version, - infos.jtag_version, infos.swim_version); - printf("Loader version : %d\n", infos.loader_version); - printf("ST-Link ID : "); - for (i = 0; i < 12; i++) { - printf("%02X", infos.id[i]); + printf("STLink ID: "); + for (i = 0; i < 12; i += 4) { + printf("%02X", info.id[i + 3]); + printf("%02X", info.id[i + 2]); + printf("%02X", info.id[i + 1]); + printf("%02X", info.id[i + 0]); + } + printf("\n"); + + printf("Firmware Encryption Key: "); + for (i = 0; i < 16; i++) { + printf("%02X", info.firmware_key[i]); } printf("\n"); - printf("Firmware encryption key : "); + + printf("Anti-Clone Key: "); for (i = 0; i < 16; i++) { - printf("%02X", infos.firmware_key[i]); + printf("%02X", info.anti_clone[i]); } printf("\n"); - res = stlink_current_mode(dev_handle); + res = stlink_current_mode(&info); if (res < 0) { - libusb_release_interface(dev_handle, 0); + libusb_release_interface(info.stinfo_dev_handle, 0); return EXIT_FAILURE; } - printf("Current mode : %d\n", res); + printf("Current Mode: %d\n\n", res); - if (res != 1) { + if (res & 0xfffc) { printf("ST-Link dongle is not in the correct mode. Please unplug and plug the dongle again.\n"); - libusb_release_interface(dev_handle, 0); + libusb_release_interface(info.stinfo_dev_handle, 0); return EXIT_SUCCESS; } if (!probe) { - if (do_load) { - stlink_flash(dev_handle, argv[optind], 0x8004000, 1024, &infos); + for (int i = 0; i < 9; i++) { + if (config.modify[i] != modCOPY) { + flash_config = true; + break; + } } - stlink_exit_dfu(dev_handle); - } - libusb_release_interface(dev_handle, 0); - libusb_exit(usb_ctx); + if (do_load) + if (stlink_flash(&info, argv[optind], decrypt, save_decrypted)) + flash_config = fix_config = false; + + if (flash_config || fix_config) { + stlink_flash_config_area(&info, &config); + } + stlink_exit_dfu(&info); + } + libusb_release_interface(info.stinfo_dev_handle, 0); +exit_libusb: + libusb_exit(info.stinfo_usb_ctx); return EXIT_SUCCESS; } diff --git a/src/stlink.c b/src/stlink.c index a596e2d..c19ee43 100644 --- a/src/stlink.c +++ b/src/stlink.c @@ -19,22 +19,22 @@ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include -#include #include #include #include -#include -#include + +#ifdef WINDOWS + #include +#else + #include +#endif + #include -#include +#include #include "crypto.h" #include "stlink.h" -#define EP_IN 1 | LIBUSB_ENDPOINT_IN -#define EP_OUT 2 | LIBUSB_ENDPOINT_OUT - #define USB_TIMEOUT 5000 #define DFU_DETACH 0x00 @@ -45,127 +45,590 @@ #define DFU_GETSTATE 0x05 #define DFU_ABORT 0x06 #define DFU_EXIT 0x07 +#define ST_DFU_INFO 0xF1 +#define ST_DFU_MAGIC 0xF3 #define GET_COMMAND 0x00 #define SET_ADDRESS_POINTER_COMMAND 0x21 #define ERASE_COMMAND 0x41 +#define ERASE_SECTOR_COMMAND 0x42 #define READ_UNPROTECT_COMMAND 0x92 -int stlink_read_infos(libusb_device_handle *dev_handle, - struct STLinkInfos *infos) { - unsigned char data[20]; +char typeA[] = "STM32 Debugger+Audio"; +char typeB1[] = "STM32 Debug+Mass storage+VCP"; +char typeB2[] = "STM32 Debug+VCP"; +char typeE[] = "STM32 Debug+Mass storage+VCP"; +char typeF1[] = "STM8/STM32 Debug+Mass storage+VCP+Bridge"; +char typeF2[] = "STM8/STM32 Debug+2VCP+Bridge"; +char typeG1[] = "STM8 Debug+Mass storage+VCP"; +char typeG2[] = "STM8 Debug+VCP"; +char typeJ[] = "STM32 Debugger"; +char typeM[] = "STM8/STM32 Debugger"; +char typeS[] = "STM8 Debugger"; + +char* st_types[] = {[0] = "Error", ['A'] = typeA, ['B'] = typeB1, ['E'] = typeE, ['F'] = typeF1, ['G'] = typeG1, ['J'] = typeJ, ['M'] = typeM, ['S'] = typeS, [0xFF] = "Not Set", ['B' - 'A'] = typeB2, ['F' - 'A'] = typeF2, ['G' - 'A'] = typeG2}; + +static int stlink_erase(struct STLinkInfo *info, uint32_t address); +static int stlink_set_address(struct STLinkInfo *info, uint32_t address); +static int stlink_dfu_status(struct STLinkInfo *info, struct DFUStatus *status); + +char* stlink_get_dev_config(struct STLinkConfig *config, enum ConfigTypes config_type) { + switch (config_type) { + case confDFU_OPT: + switch (config->dfu_option) { + case 1: + return "No Power Off"; + case 2: + return "Autostart"; + case 3: + return "No Power Off, Autostart"; + } + break; + case confDYN_OPT: + switch (config->dynamic_option) { + case 'V': + return "MSD Off"; + case 'W': + return "MSD Always Off"; + case 'M': + return "MSD On"; + } + break; + case confSTARTUP: + switch (config->startup_pref) { + case 0: + return "High Power"; + case 1: + return "Balanced"; + case 2: + return "Low Power"; + case 3: + return "Default"; + } + default: + break; + } + return ""; +} + +int stlink_flash_anticlone_tag(struct STLinkInfo *info) { + int res; + + res = stlink_set_address(info, 0x08003C00); + if (res) { + fprintf(stderr, "Set Address Error at 0x%08x\n", 0x08003C00); + return res; + } + res = stlink_dfu_download(info, info->anti_clone, 16, 2); + if (res) { + fprintf(stderr, "Download Error at 0x%08x\n", 0x08003C00); + return res; + } + + printf("Downloaded Anticlone Tag\n"); + return 0; +} + +int stlink_flash_stlink_type(struct STLinkInfo *info, char st_type) { + uint8_t data[16]; + int res; + + memset(data, 0xFF, sizeof(data)); + data[0] = st_type; + + res = stlink_set_address(info, 0x08003C20); + if (res) { + fprintf(stderr, "Set Address Error at 0x%08x\n", 0x08003C20); + return res; + } + res = stlink_dfu_download(info, data, 16, 2); + if (res) { + fprintf(stderr, "Download Error at 0x%08x\n", 0x08003C20); + return res; + } + + printf("Downloaded STLink Type\n"); + return 0; +} + +int stlink_flash_dev_config(struct STLinkInfo *info, struct STLinkConfig *config) { + int res; + if (config) { + switch (config->modify[confUSB_CUR]) { + case modADD: + info->config.raw_config[0] = 'P'; + info->config.raw_config[1] = config->usb_current / 2; + break; + case modREMOVE: + *(uint16_t*)(info->config.raw_config) = 0xFFFF; + case modCOPY: + break; + } + switch (config->modify[confMSD_NAME]) { + case modADD: + info->config.raw_config[2] = 'V'; + memset(info->config.raw_config+3, 0x20, 11); + memcpy(info->config.raw_config+3, config->volume, strlen(config->volume)); + break; + case modREMOVE: + memset(info->config.raw_config+2, 0xFF, 12); + case modCOPY: + break; + } + switch (config->modify[confMBED_NAME]) { + case modADD: + info->config.raw_config[15] = 'B'; + memset(info->config.raw_config+16, 0xFF, 4); + memcpy(info->config.raw_config+16, config->mbed_name, strlen(config->mbed_name)); + break; + case modREMOVE: + memset(info->config.raw_config+15, 0xFF, 5); + case modCOPY: + break; + } + switch (config->modify[confDFU_OPT]) { + case modADD: + info->config.raw_config[20] = 'F'; + info->config.raw_config[21] = config->dfu_option; + break; + case modREMOVE: + *(uint16_t*)(info->config.raw_config+20) = 0xFFFF; + case modCOPY: + break; + } + switch (config->modify[confDYN_OPT]) { + case modADD: + info->config.raw_config[22] = 'D'; + info->config.raw_config[23] = (uint8_t)config->dynamic_option; + break; + case modREMOVE: + *(uint16_t*)(info->config.raw_config+22) = 0xFFFF; + case modCOPY: + break; + } + switch (config->modify[confMCO_OUT]) { + case modADD: + info->config.raw_config[26] = 'O'; + info->config.raw_config[27] = config->mco_output; + break; + case modREMOVE: + *(uint16_t*)(info->config.raw_config+26) = 0xFFFF; + case modCOPY: + break; + } + switch (config->modify[confSTARTUP]) { + case modADD: + info->config.raw_config[32] = 'C'; + info->config.raw_config[33] = config->startup_pref; + break; + case modREMOVE: + *(uint16_t*)(info->config.raw_config+32) = 0xFFFF; + case modCOPY: + break; + } + } + + res = stlink_set_address(info, 0x08003C30); + if (res) { + fprintf(stderr, "Set Address Error at 0x%08x\n", 0x08003C30); + return res; + } + res = stlink_dfu_download(info, info->config.raw_config, 0x40, 2); + if (res) { + fprintf(stderr, "Download Error at 0x%08x\n", 0x08003C30); + return res; + } + + printf("Downloaded Device Configuration\n"); + return 0; +} + +int stlink_flash_software_version(struct STLinkInfo *info, uint16_t version) { + uint8_t data[16]; + int res; + + memset(data, 0xFF, sizeof(data)); + *(uint16_t*)(data+14) = version; + //data[14] = 0x20; + //data[15] = 0x41; + + res = stlink_set_address(info, 0x08003FF0); + if (res) { + fprintf(stderr, "Set Address Error at 0x%08x\n", 0x08003FF0); + return res; + } + res = stlink_dfu_download(info, data, 16, 2); + if (res) { + fprintf(stderr, "Download Error at 0x%08x\n", 0x08003FF0); + return res; + } + + printf("Downloaded Software Version\n"); + return 0; +} + +int stlink_flash_firmware_exists_flag(struct STLinkInfo *info) { + uint8_t data[16]; + int res; + + uint32_t address = 0x08000000 | (((info->hardware_flags & 0x000001 ? 128 : info->flash_size) << 10) - 16); + memset(data, 0xFF, sizeof(data)); + *(uint32_t*)(data+12) = 0xA50027D3; + + res = stlink_erase(info, address); + if (res) { + fprintf(stderr, "Erase Error at 0x%08x\n", address); + return res; + } + + res = stlink_set_address(info, address); + if (res) { + fprintf(stderr, "Set Address Error at 0x%08x\n", address); + return res; + } + res = stlink_dfu_download(info, data, 16, 2); + if (res) { + fprintf(stderr, "Download Error at 0x%08x\n", address); + return res; + } + + printf("Downloaded Firmware Exists Tag\n"); + return 0; +} + +int stlink_flash_config_area(struct STLinkInfo *info, struct STLinkConfig *config) { + int res; + char st_type = info->config.stlink_type; + uint16_t version = htons(info->software_version); + if (config){ + if (config->modify[confST_TYPE] == modADD) + st_type = config->stlink_type; + if (config->modify[confVERSION] == modADD) { + version = htons(config->soft_version); + } + } + + res = stlink_erase(info, 0x08003C00); + if (res) { + fprintf(stderr, "Erase error at 0x%08x\n", 0x08003C00); + return res; + } + res = stlink_flash_anticlone_tag(info); + if (res) { + fprintf(stderr, "Error Downloading Anticlone Tag\n"); + return res; + } + res = stlink_flash_stlink_type(info, st_type); + if (res) { + fprintf(stderr, "Error Downloading STLink Type\n"); + return res; + } + res = stlink_flash_dev_config(info, config); + if (res) { + fprintf(stderr, "Error Downloading Device Configuration\n"); + return res; + } + res = stlink_flash_software_version(info, version); + if (res) { + fprintf(stderr, "Error Downloading Software Version\n"); + return res; + } + res = stlink_flash_firmware_exists_flag(info); + if (res) { + fprintf(stderr, "Error Downloading Firmware Exists Tag\n"); + return res; + } + + return 0; +} + +int stlink_dfu_mode(libusb_device_handle *dev_handle, int trigger) { + unsigned char data[16]; + int rw_bytes, res; + + memset(data, 0, sizeof(data)); + + data[0] = 0xF9; + if (trigger) data[1] = DFU_DNLOAD; + /* Write */ + res = libusb_bulk_transfer(dev_handle, + 1 | LIBUSB_ENDPOINT_OUT, + data, + sizeof(data), + &rw_bytes, + USB_TIMEOUT); + if (res) { + fprintf(stderr, "USB transfer failure\n"); + return -1; + } + if (!trigger) { + /* Read */ + libusb_bulk_transfer(dev_handle, + 1 | LIBUSB_ENDPOINT_IN, + data, + 2, + &rw_bytes, + USB_TIMEOUT); + if (res) { + fprintf(stderr, "stlink_read_info() failure\n"); + return -1; + } + } + return data[0] << 8 | data[1]; +} + +int stlink_read_info(struct STLinkInfo *info) { + unsigned char data[0x40]; int res, rw_bytes; memset(data, 0, sizeof(data)); - data[0] = 0xF1; + data[0] = ST_DFU_INFO; data[1] = 0x80; /* Write */ - res = libusb_bulk_transfer(dev_handle, - EP_OUT, - data, - 16, - &rw_bytes, - USB_TIMEOUT); + res = libusb_bulk_transfer(info->stinfo_dev_handle, + info->stinfo_ep_out, + data, + 16, + &rw_bytes, + USB_TIMEOUT); if (res) { - fprintf(stderr, "USB transfer failure\n"); + fprintf(stderr, "stlink_read_info out transfer failure\n"); return -1; } /* Read */ - res = libusb_bulk_transfer(dev_handle, - EP_IN, - data, - 6, - &rw_bytes, - USB_TIMEOUT); + res = libusb_bulk_transfer(info->stinfo_dev_handle, + info->stinfo_ep_in, + data, + 6, + &rw_bytes, + USB_TIMEOUT); if (res) { - fprintf(stderr, "USB transfer failure\n"); + fprintf(stderr, "stlink_read_info in transfer failure\n"); return -1; } - infos->stlink_version = data[0] >> 4; - infos->jtag_version = (data[0] & 0x0F) << 2 | (data[1] & 0xC0) >> 6; - infos->swim_version = data[1] & 0x3F; - infos->loader_version = data[5] << 8 | data[4]; + info->software_version = ntohs(*(uint16_t*)data); + info->bootloader_pid = data[5] << 8 | data[4]; memset(data, 0, sizeof(data)); - data[0] = 0xF3; + data[0] = ST_DFU_MAGIC; data[1] = 0x08; /* Write */ - res = libusb_bulk_transfer(dev_handle, - EP_OUT, - data, - 16, - &rw_bytes, - USB_TIMEOUT); + res = libusb_bulk_transfer(info->stinfo_dev_handle, + info->stinfo_ep_out, + data, + 16, + &rw_bytes, + USB_TIMEOUT); if (res) { fprintf(stderr, "USB transfer failure\n"); return -1; } /* Read */ - libusb_bulk_transfer(dev_handle, - EP_IN, - data, - 20, - &rw_bytes, - USB_TIMEOUT); + res = libusb_bulk_transfer(info->stinfo_dev_handle, + info->stinfo_ep_in, + data, + 20, + &rw_bytes, + USB_TIMEOUT); if (res) { fprintf(stderr, "USB transfer failure\n"); return -1; } - memcpy(infos->id, data+8, 12); + info->reported_flash_size = data[1] << 8 | data[0]; + info->flash_size = info->reported_flash_size; + info->reserved_flash = 0; + info->config.stlink_type = data[4]; + + res = stlink_current_mode(info); + if (res < 0) { + return -1; + } + + if (info->stinfo_bl_type == STLINK_BL_V2) { + switch(info->mode) { + case 0: + info->stinfo_bl_type = STLINK_BL_V2; //TODO + break; + case 1: + info->stinfo_bl_type = STLINK_BL_V2; + break; + case 2: + info->stinfo_bl_type = STLINK_BL_V21; + break; + default: + info->stinfo_bl_type = STLINK_BL_V3; + break; + } + } + memcpy(info->id, data+8, 12); /* Firmware encryption key generation */ - memcpy(infos->firmware_key, data, 4); - memcpy(infos->firmware_key+4, data+8, 12); - my_encrypt((unsigned char*)"I am key, wawawa", infos->firmware_key, 16); + memcpy(info->firmware_key, data, 4); + memcpy(info->firmware_key+4, data+8, 12); + my_encrypt((unsigned char*)"I am key, wawawa", info->firmware_key, 16); + /* Anti-Clone Tag generation */ + memcpy(info->anti_clone, data, 4); + memcpy(info->anti_clone+4, data+8, 12); + my_encrypt((unsigned char*)"What are you doing", info->anti_clone, 16); + + if (info->mode > 1) { + memset(data, 0, sizeof(data)); + + data[0] = ST_DFU_MAGIC; + data[1] = 0x09; + *(uint16_t*)(data+2) = 0x40; + + // Write // + res = libusb_bulk_transfer(info->stinfo_dev_handle, + info->stinfo_ep_out, + data, + 16, + &rw_bytes, + USB_TIMEOUT); + if (res) { + fprintf(stderr, "USB transfer failureW\n"); + return -1; + } + + // Read // + res = libusb_bulk_transfer(info->stinfo_dev_handle, + info->stinfo_ep_in, + data, + 0x40, + &rw_bytes, + USB_TIMEOUT); + if (res && (res != -9)) { + fprintf(stderr, "USB transfer failureR %d\n", res); + return -1; + } else if (res == -9) { + printf("Bootloader DFU doesn't support 'get device config' command.\n"); + } else { + memcpy(info->config.raw_config, data, 0x40); + /* printf("Info3: "); + for (int i = 0; i < 0x40; i++) { + printf("%02X ", info->config.raw_config[i]); + } + printf("\n"); */ + + if (data[0] == 'P') { + info->config.modify[confUSB_CUR] = modADD; + info->config.usb_current = data[1] * 2; + } + if (data[2] == 'V') { + info->config.modify[confMSD_NAME] = modADD; + memcpy(info->config.volume, data+3, 11); + } + if (data[15] == 'B') { + info->config.modify[confMBED_NAME] = modADD; + memcpy(info->config.mbed_name, data+16, 4); + } + if (data[20] == 'F') { + info->config.modify[confDFU_OPT] = modADD; + info->config.dfu_option = data[21]; + } + if (data[22] == 'D') { + info->config.modify[confDYN_OPT] = modADD; + info->config.dynamic_option = (char)data[23]; + } + if (data[26] == 'O') { + info->config.modify[confMCO_OUT] = modADD; + info->config.mco_output = data[27]; + } + if (data[32] == 'C') { + info->config.modify[confSTARTUP] = modADD; + info->config.startup_pref = data[33]; + } + } + + memset(data, 0, sizeof(data)); + + data[0] = ST_DFU_MAGIC; + data[1] = 0x0A; + + // Write // + res = libusb_bulk_transfer(info->stinfo_dev_handle, + info->stinfo_ep_out, + data, + 16, + &rw_bytes, + USB_TIMEOUT); + if (res) { + fprintf(stderr, "USB transfer failureW\n"); + return -1; + } + + // Read // + res = libusb_bulk_transfer(info->stinfo_dev_handle, + info->stinfo_ep_in, + data, + 16, + &rw_bytes, + USB_TIMEOUT); + if (res && (res != -9)) { + fprintf(stderr, "USB transfer failureR %d\n", res); + return -1; + } else if (res == -9) { + printf("Bootloader DFU doesn't support 'get hardware version' command.\n"); + } else { + info->hardware_version = data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0]; + if (info->hardware_flags & 0x000001) + info->flash_size = 128; + if (info->hardware_flags & 0x000002) + info->reserved_flash = 20; + } + } return 0; } -int stlink_current_mode(libusb_device_handle *dev_handle) { +int stlink_current_mode(struct STLinkInfo *info) { unsigned char data[16]; int rw_bytes, res; - + memset(data, 0, sizeof(data)); data[0] = 0xF5; /* Write */ - res = libusb_bulk_transfer(dev_handle, - EP_OUT, - data, - sizeof(data), - &rw_bytes, - USB_TIMEOUT); + res = libusb_bulk_transfer(info->stinfo_dev_handle, + info->stinfo_ep_out, + data, + sizeof(data), + &rw_bytes, + USB_TIMEOUT); if (res) { fprintf(stderr, "USB transfer failure\n"); return -1; } /* Read */ - libusb_bulk_transfer(dev_handle, - EP_IN, - data, - 2, - &rw_bytes, - USB_TIMEOUT); + libusb_bulk_transfer(info->stinfo_dev_handle, + info->stinfo_ep_in, + data, + 2, + &rw_bytes, + USB_TIMEOUT); if (res) { - fprintf(stderr, "stlink_read_infos() failure\n"); + fprintf(stderr, "stlink_read_info() failure\n"); return -1; } + if (data[0] == 0) + info->mode = data[1]; + return data[0] << 8 | data[1]; } uint16_t stlink_checksum(const unsigned char *firmware, - size_t len) { + size_t len) { unsigned int i; int ret = 0; @@ -176,49 +639,47 @@ uint16_t stlink_checksum(const unsigned char *firmware, return (uint16_t)ret & 0xFFFF; } -int stlink_dfu_download(libusb_device_handle *dev_handle, - unsigned char *data, - size_t data_len, - uint16_t wBlockNum, - struct STLinkInfos *stlink_infos) { +int stlink_dfu_download(struct STLinkInfo *info, + unsigned char *data, + const size_t data_len, + const uint16_t wBlockNum) { unsigned char download_request[16]; struct DFUStatus dfu_status; int rw_bytes, res; - memset(download_request, 0, sizeof(download_request)); - download_request[0] = 0xF3; + download_request[0] = ST_DFU_MAGIC; download_request[1] = DFU_DNLOAD; *(uint16_t*)(download_request+2) = wBlockNum; /* wValue */ *(uint16_t*)(download_request+4) = stlink_checksum(data, data_len); /* wIndex */ *(uint16_t*)(download_request+6) = data_len; /* wLength */ if (wBlockNum >= 2) { - my_encrypt(stlink_infos->firmware_key, data, data_len); + my_encrypt(info->firmware_key, data, data_len); } - res = libusb_bulk_transfer(dev_handle, - EP_OUT, - download_request, - sizeof(download_request), - &rw_bytes, - USB_TIMEOUT); + res = libusb_bulk_transfer(info->stinfo_dev_handle, + info->stinfo_ep_out, + download_request, + sizeof(download_request), + &rw_bytes, + USB_TIMEOUT); if (res || rw_bytes != sizeof(download_request)) { fprintf(stderr, "USB transfer failure\n"); return -1; } - res = libusb_bulk_transfer(dev_handle, - EP_OUT, - data, - data_len, - &rw_bytes, - USB_TIMEOUT); + res = libusb_bulk_transfer(info->stinfo_dev_handle, + info->stinfo_ep_out, + data, + data_len, + &rw_bytes, + USB_TIMEOUT); if (res || rw_bytes != (int)data_len) { fprintf(stderr, "USB transfer failure\n"); return -1; } - if (stlink_dfu_status(dev_handle, &dfu_status)) { + if (stlink_dfu_status(info, &dfu_status)) { return -1; } @@ -234,7 +695,7 @@ int stlink_dfu_download(libusb_device_handle *dev_handle, usleep(dfu_status.bwPollTimeout * 1000); - if (stlink_dfu_status(dev_handle, &dfu_status)) { + if (stlink_dfu_status(info, &dfu_status)) { return -1; } @@ -243,10 +704,10 @@ int stlink_dfu_download(libusb_device_handle *dev_handle, fprintf(stderr, "Read-only protection active\n"); return -3; } else if (dfu_status.bStatus == errTARGET) { - fprintf(stderr, "Invalid address error\n"); + fprintf(stderr, "Invalid Address Error\n"); return -3; } else { - fprintf(stderr, "Unknown error : %d\n", dfu_status.bStatus); + fprintf(stderr, "Unknown Error : %d\n", dfu_status.bStatus); return -3; } } @@ -254,33 +715,32 @@ int stlink_dfu_download(libusb_device_handle *dev_handle, return 0; } -int stlink_dfu_status(libusb_device_handle *dev_handle, - struct DFUStatus *status) { +int stlink_dfu_status(struct STLinkInfo *info, struct DFUStatus *status) { unsigned char data[16]; int rw_bytes, res; memset(data, 0, sizeof(data)); - data[0] = 0xF3; + data[0] = ST_DFU_MAGIC; data[1] = DFU_GETSTATUS; data[6] = 0x06; /* wLength */ - res = libusb_bulk_transfer(dev_handle, - EP_OUT, - data, - 16, - &rw_bytes, - USB_TIMEOUT); + res = libusb_bulk_transfer(info->stinfo_dev_handle, + info->stinfo_ep_out, + data, + 16, + &rw_bytes, + USB_TIMEOUT); if (res || rw_bytes != 16) { fprintf(stderr, "USB transfer failure\n"); return -1; } - res = libusb_bulk_transfer(dev_handle, - EP_IN, - data, - 6, - &rw_bytes, - USB_TIMEOUT); + res = libusb_bulk_transfer(info->stinfo_dev_handle, + info->stinfo_ep_in, + data, + 6, + &rw_bytes, + USB_TIMEOUT); if (res || rw_bytes != 6) { fprintf(stderr, "USB transfer failure\n"); return -1; @@ -294,25 +754,37 @@ int stlink_dfu_status(libusb_device_handle *dev_handle, return 0; } -int stlink_erase(libusb_device_handle *dev_handle, - uint32_t address) { - unsigned char erase_command[5]; +int stlink_erase(struct STLinkInfo *info, uint32_t address) { + unsigned char command[5]; int res; - erase_command[0] = ERASE_COMMAND; - erase_command[1] = address & 0xFF; - erase_command[2] = (address >> 8) & 0xFF; - erase_command[3] = (address >> 16) & 0xFF; - erase_command[4] = (address >> 24) & 0xFF; + command[0] = ERASE_COMMAND; + command[1] = address & 0xFF; + command[2] = (address >> 8) & 0xFF; + command[3] = (address >> 16) & 0xFF; + command[4] = (address >> 24) & 0xFF; + + res = stlink_dfu_download(info, command, sizeof(command), 0); - res = stlink_dfu_download(dev_handle, erase_command, - sizeof(erase_command), 0, NULL); - return res; } -int stlink_set_address(libusb_device_handle *dev_handle, - uint32_t address) { +int stlink_sector_erase(struct STLinkInfo *info, uint32_t sector) { + unsigned char command[5]; + int res; + + command[0] = ERASE_SECTOR_COMMAND; + command[1] = sector & 0xFF; + command[2] = 0; + command[3] = 0; + command[4] = 0; + + res = stlink_dfu_download(info, command, sizeof(command), 0); + + return res; +} + +int stlink_set_address(struct STLinkInfo *info, uint32_t address) { unsigned char set_address_command[5]; int res; @@ -322,97 +794,173 @@ int stlink_set_address(libusb_device_handle *dev_handle, set_address_command[3] = (address >> 16) & 0xFF; set_address_command[4] = (address >> 24) & 0xFF; - res = stlink_dfu_download(dev_handle, set_address_command, - sizeof(set_address_command), 0, NULL); + res = stlink_dfu_download(info, set_address_command, sizeof(set_address_command), 0); return res; } -int stlink_flash(libusb_device_handle *dev_handle, - const char *filename, - unsigned int base_offset, - unsigned int chunk_size, - struct STLinkInfos *stlink_infos) { - unsigned char *firmware, firmware_chunk[chunk_size]; - unsigned int cur_chunk_size, flashed_bytes, file_size; - int fd, res; +int stlink_flash(struct STLinkInfo *info, const char *filename, bool decrypt, bool save) { + uint32_t file_size, file_read_size; + + FILE *fd; + int res; struct stat firmware_stat; + uint8_t* firmware; - fd = open(filename, O_RDONLY); - if (fd == -1) { - fprintf(stderr, "File opening failed\n"); + fd = fopen(filename, "rb"); + if (fd == NULL) { + fprintf(stderr, "Opening File %s Failed\n", filename); return -1; } - fstat(fd, &firmware_stat); + stat(filename, &firmware_stat); file_size = firmware_stat.st_size; + if (!file_size) + return -1; - firmware = mmap(NULL, file_size, PROT_WRITE, MAP_PRIVATE, fd, 0); - if (firmware == MAP_FAILED) { - fprintf(stderr, "mmap failure\n"); - return -1; + printf("Loaded firmware : %s, size : %d bytes\n", filename, (int)file_size); + if (file_size > ((uint32_t)(info->flash_size - 1 - 16 - info->reserved_flash) << 10)) { + printf("Firmware Size is larger than Flash Size. Continue? [Y/n]: "); + while (1) { + int c = getchar(); + if (c != '\n') + while ((getchar()) != '\n'); + if (c > 0) + c = tolower(c); + if (c == 'n') + return -1; + if (c == 'y' || c == '\n') + break; + } } - printf("Loaded firmware : %s, size : %d bytes\n", filename, (int)file_size); + int chunk_size = (2 << 10); + int padding = (16 - (file_size % 16)) % 16; + firmware = malloc(file_size + padding); + memset(firmware, 0xFF, file_size + padding); + file_read_size = fread(firmware, sizeof(unsigned char), file_size, fd); + if (file_read_size <= 0) { + fprintf(stderr, "File Read Failed\n"); + free(firmware); + return file_read_size; + } - flashed_bytes = 0; + if (decrypt) { + if (info->decrypt_key) + printf("Decrypting Firmware Using Key \"%s\"\n", info->decrypt_key); + else { + info->decrypt_key = "best performance"; + } + + for(unsigned int i = 0; i < file_size; i += 0xC00) { + my_decrypt((unsigned char*)info->decrypt_key, firmware + i, (i + 0xC00) < file_size ? 0xC00 : file_size - i); + } + printf("Decrypted Firmware\n"); + + if (save) { + char* dec_filename = malloc(strlen(filename) + 5); + sprintf(dec_filename, "%s.dec", filename); + printf("Saving Decrypted Firmware as %s\n", dec_filename); + + FILE* fdw = fopen(dec_filename, "wb"); + if (fdw) { + fwrite(firmware, sizeof(unsigned char), file_size, fdw); + fclose(fdw); + } + free(dec_filename); + } + } + file_size += padding; + + printf("Firmware Type %s\n\n", (info->stinfo_bl_type == STLINK_BL_V3) ? "V3" : "V2"); + unsigned int base_offset; + base_offset = (info->stinfo_bl_type == STLINK_BL_V3) ? 0x08020000 : 0x08004000; + + unsigned int flashed_bytes = 0; while (flashed_bytes < file_size) { - if ((flashed_bytes+chunk_size) > file_size) { - cur_chunk_size = file_size - flashed_bytes; + unsigned int cur_chunk_size; + if ((flashed_bytes + chunk_size) > file_size) { + cur_chunk_size = file_size - flashed_bytes; } else { - cur_chunk_size = chunk_size; + cur_chunk_size = chunk_size; } - - res = stlink_erase(dev_handle, base_offset+flashed_bytes); - if (res) { - fprintf(stderr, "Erase error\n"); - return res; + int wdl = 2; + if (info->stinfo_bl_type == STLINK_BL_V3) { + if (((base_offset + flashed_bytes) & ((1 << 14) - 1)) == 0) { + uint32_t sector_start[8] = {0x08000000, 0x08004000, 0x08008000, 0x0800C000, 0x08010000, 0x08020000, 0x08040000, 0x08060000}; + int sector = -1; + int i = 0; + for (; i < 8; i++) { + if (sector_start[i] == base_offset + flashed_bytes) { + sector = i; + break; + } + } + if (i < 0) { + fprintf(stderr, "No sector match for address %08x\n", base_offset + flashed_bytes); + return i; + } + printf("Erase Sector %d\n", sector); + res = stlink_sector_erase(info, sector); + if (res) { + fprintf(stderr, "Erase Sector %d failed\n", sector); + return res; + } + printf("Erase Sector %d done\n", sector); + } else { + wdl = 3; + } + } else { + res = stlink_erase(info, base_offset + flashed_bytes); + if (res) { + fprintf(stderr, "Erase Error at 0x%08x\n", base_offset + flashed_bytes); + return res; + } } - - res = stlink_set_address(dev_handle, base_offset+flashed_bytes); + res = stlink_set_address(info, base_offset+flashed_bytes); if (res) { - fprintf(stderr, "Erase error\n"); + fprintf(stderr, "Set Address Error at 0x%08x\n", base_offset + flashed_bytes); return res; + } else { + //printf("set address to 0x%08x done\n", base_offset + flashed_bytes); } - - memcpy(firmware_chunk, firmware+flashed_bytes, cur_chunk_size); - memset(firmware_chunk+cur_chunk_size, 0xff, chunk_size-cur_chunk_size); - res = stlink_dfu_download(dev_handle, firmware_chunk, chunk_size, 2, stlink_infos); + res = stlink_dfu_download(info, firmware + flashed_bytes, cur_chunk_size, wdl); if (res) { - fprintf(stderr, "Erase error\n"); + fprintf(stderr, "Download Error at 0x%08x\n", base_offset + flashed_bytes); return res; + } else { + printf("Download at 0x%08x done. %.1f%%\r", base_offset + flashed_bytes, ((float)(flashed_bytes + cur_chunk_size) / file_size) * 100.0); } - printf("."); fflush(stdout); /* Flush stdout buffer */ flashed_bytes += cur_chunk_size; } - munmap(firmware, file_size); - close(fd); + free(firmware); + fclose(fd); - printf("\n"); + printf("Downloaded Firmware File \n"); return 0; } -int stlink_exit_dfu(libusb_device_handle *dev_handle) { +int stlink_exit_dfu(struct STLinkInfo *info) { unsigned char data[16]; int rw_bytes, res; memset(data, 0, sizeof(data)); - data[0] = 0xF3; + data[0] = ST_DFU_MAGIC; data[1] = DFU_EXIT; - res = libusb_bulk_transfer(dev_handle, - EP_OUT, - data, - 16, - &rw_bytes, - USB_TIMEOUT); + res = libusb_bulk_transfer(info->stinfo_dev_handle, + info->stinfo_ep_out, + data, + 16, + &rw_bytes, + USB_TIMEOUT); if (res || rw_bytes != 16) { fprintf(stderr, "USB transfer failure\n"); return -1; diff --git a/src/stlink.h b/src/stlink.h index d3f1c74..4a9a4ca 100644 --- a/src/stlink.h +++ b/src/stlink.h @@ -18,6 +18,28 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#ifndef _STLINK_H +#define _STLINK_H + +#include +#include +#include +#include + +#ifdef WINDOWS + #ifndef bool + #define bool unsigned char + #endif + #ifndef true + #define true 1 + #endif + + #ifndef false + #define false 0 + #endif +#else + #include +#endif enum DeviceStatus { OK = 0x00, @@ -52,13 +74,42 @@ enum DeviceState { dfuERROR = 10 }; -struct STLinkInfos { - uint8_t firmware_key[16]; - uint8_t id[12]; - uint8_t stlink_version; - uint8_t jtag_version; - uint8_t swim_version; - uint16_t loader_version; +enum BlTypes { + STLINK_BL_V2 = 0, + STLINK_BL_V21, + STLINK_BL_V3 +}; + +enum ConfigTypes { + confVERSION = 0, + confST_TYPE, + confUSB_CUR, + confMSD_NAME, + confMBED_NAME, + confDFU_OPT, + confDYN_OPT, + confMCO_OUT, + confSTARTUP +}; + +enum ModifyAction { + modCOPY = 0, + modADD, + modREMOVE +}; + +struct STLinkConfig { + enum ModifyAction modify[9]; + uint8_t raw_config[0x40]; + uint16_t soft_version; + char stlink_type; + uint16_t usb_current; + char volume[12]; + char mbed_name[5]; + uint8_t dfu_option; + char dynamic_option; + uint8_t mco_output; + uint8_t startup_pref; }; struct DFUStatus { @@ -68,23 +119,55 @@ struct DFUStatus { unsigned char iString : 8; }; -int stlink_read_infos(libusb_device_handle *dev_handle, - struct STLinkInfos *infos); -int stlink_current_mode(libusb_device_handle *dev_handle); -int stlink_dfu_status(libusb_device_handle *dev_handle, - struct DFUStatus *status); -int stlink_dfu_download(libusb_device_handle *dev_handle, +struct STLinkInfo { + uint8_t firmware_key[16]; + uint8_t anti_clone[16]; + uint8_t id[12]; + struct STLinkConfig config; + + union { + struct { + uint16_t swim_version: 6; + uint16_t jtag_version: 6; + uint16_t stlink_version: 4; + }; + uint16_t software_version; + } __attribute__((packed)); + + uint16_t bootloader_pid; + union { + struct { + uint32_t hardware_flags: 24; + uint32_t hardware_minor: 4; + uint32_t hardware_mayor: 4; + }; + uint32_t hardware_version; + } __attribute__((packed)); + uint8_t flash_size; + uint8_t reported_flash_size; + uint8_t reserved_flash; + uint8_t mode; + libusb_context *stinfo_usb_ctx; + libusb_device_handle *stinfo_dev_handle; + unsigned char stinfo_ep_in; + unsigned char stinfo_ep_out; + enum BlTypes stinfo_bl_type; + char* decrypt_key; +}; + +extern char* st_types[]; + +int stlink_flash_config_area(struct STLinkInfo *info, struct STLinkConfig *config); +char* stlink_get_dev_config(struct STLinkConfig *config, enum ConfigTypes config_type); + +int stlink_dfu_mode(libusb_device_handle *dev_handle, int trigger); +int stlink_read_info(struct STLinkInfo *info); +int stlink_current_mode(struct STLinkInfo *info); +int stlink_dfu_download(struct STLinkInfo *stlink_info, unsigned char *data, - size_t data_len, - uint16_t wBlockNum, - struct STLinkInfos *stlink_infos); -int stlink_erase(libusb_device_handle *dev_handle, - uint32_t address); -int stlink_set_address(libusb_device_handle *dev_handle, - uint32_t address); -int stlink_flash(libusb_device_handle *dev_handle, - const char *filename, - unsigned int base_offset, - unsigned int chunk_size, - struct STLinkInfos *stlink_infos); -int stlink_exit_dfu(libusb_device_handle *dev_handle); + const size_t data_len, + const uint16_t wBlockNum); +int stlink_flash(struct STLinkInfo *stlink_info, const char *filename, bool decrypt, bool save); +int stlink_exit_dfu(struct STLinkInfo *info); + +#endif //_STLINK_H