From f4ff16d3d27275254acca2045d84f488abacf4e9 Mon Sep 17 00:00:00 2001 From: CEnnis91 Date: Sun, 4 Jul 2021 14:36:46 -0400 Subject: [PATCH 01/10] git subrepo clone (merge) https://github.com/raphnet/mupen64plus-input-raphnetraw.git subrepo: subdir: "mupen64plus-input-raphnetraw" merged: "8611241" upstream: origin: "https://github.com/raphnet/mupen64plus-input-raphnetraw.git" branch: "master" commit: "8611241" git-subrepo: version: "0.4.3" origin: "???" commit: "???" --- mupen64plus-input-raphnetraw/.gitignore | 5 + mupen64plus-input-raphnetraw/.gitrepo | 12 + mupen64plus-input-raphnetraw/COPYING | 340 +++++++++++++ mupen64plus-input-raphnetraw/INSTALL | 26 + mupen64plus-input-raphnetraw/LICENSES | 377 ++++++++++++++ mupen64plus-input-raphnetraw/README.md | 159 ++++++ mupen64plus-input-raphnetraw/RELEASE | 43 ++ .../projects/unix/Makefile | 312 ++++++++++++ .../projects/unix/buildwin32.sh | 24 + .../projects/unix/buildwin64.sh | 24 + mupen64plus-input-raphnetraw/release_src.sh | 38 ++ .../release_windows.sh | 62 +++ mupen64plus-input-raphnetraw/src/gcn64.c | 371 ++++++++++++++ mupen64plus-input-raphnetraw/src/gcn64.h | 59 +++ mupen64plus-input-raphnetraw/src/gcn64_priv.h | 17 + .../src/gcn64_protocol.h | 166 ++++++ mupen64plus-input-raphnetraw/src/gcn64lib.c | 444 ++++++++++++++++ mupen64plus-input-raphnetraw/src/gcn64lib.h | 42 ++ mupen64plus-input-raphnetraw/src/hexdump.c | 29 ++ mupen64plus-input-raphnetraw/src/hexdump.h | 3 + .../src/osal_dynamiclib.h | 30 ++ .../src/osal_dynamiclib_unix.c | 37 ++ .../src/osal_dynamiclib_win32.c | 74 +++ .../src/osal_preproc.h | 45 ++ .../src/plugin_back.c | 475 ++++++++++++++++++ .../src/plugin_back.h | 22 + .../src/plugin_front.c | 331 ++++++++++++ .../src/plugin_front.h | 71 +++ mupen64plus-input-raphnetraw/src/requests.h | 38 ++ mupen64plus-input-raphnetraw/src/version.h | 38 ++ 30 files changed, 3714 insertions(+) create mode 100644 mupen64plus-input-raphnetraw/.gitignore create mode 100644 mupen64plus-input-raphnetraw/.gitrepo create mode 100644 mupen64plus-input-raphnetraw/COPYING create mode 100644 mupen64plus-input-raphnetraw/INSTALL create mode 100644 mupen64plus-input-raphnetraw/LICENSES create mode 100644 mupen64plus-input-raphnetraw/README.md create mode 100644 mupen64plus-input-raphnetraw/RELEASE create mode 100644 mupen64plus-input-raphnetraw/projects/unix/Makefile create mode 100755 mupen64plus-input-raphnetraw/projects/unix/buildwin32.sh create mode 100755 mupen64plus-input-raphnetraw/projects/unix/buildwin64.sh create mode 100755 mupen64plus-input-raphnetraw/release_src.sh create mode 100755 mupen64plus-input-raphnetraw/release_windows.sh create mode 100644 mupen64plus-input-raphnetraw/src/gcn64.c create mode 100644 mupen64plus-input-raphnetraw/src/gcn64.h create mode 100644 mupen64plus-input-raphnetraw/src/gcn64_priv.h create mode 100644 mupen64plus-input-raphnetraw/src/gcn64_protocol.h create mode 100644 mupen64plus-input-raphnetraw/src/gcn64lib.c create mode 100644 mupen64plus-input-raphnetraw/src/gcn64lib.h create mode 100644 mupen64plus-input-raphnetraw/src/hexdump.c create mode 100644 mupen64plus-input-raphnetraw/src/hexdump.h create mode 100644 mupen64plus-input-raphnetraw/src/osal_dynamiclib.h create mode 100644 mupen64plus-input-raphnetraw/src/osal_dynamiclib_unix.c create mode 100644 mupen64plus-input-raphnetraw/src/osal_dynamiclib_win32.c create mode 100644 mupen64plus-input-raphnetraw/src/osal_preproc.h create mode 100644 mupen64plus-input-raphnetraw/src/plugin_back.c create mode 100644 mupen64plus-input-raphnetraw/src/plugin_back.h create mode 100644 mupen64plus-input-raphnetraw/src/plugin_front.c create mode 100644 mupen64plus-input-raphnetraw/src/plugin_front.h create mode 100644 mupen64plus-input-raphnetraw/src/requests.h create mode 100644 mupen64plus-input-raphnetraw/src/version.h diff --git a/mupen64plus-input-raphnetraw/.gitignore b/mupen64plus-input-raphnetraw/.gitignore new file mode 100644 index 00000000..cb2cbd5a --- /dev/null +++ b/mupen64plus-input-raphnetraw/.gitignore @@ -0,0 +1,5 @@ +*.dll +*.so +*.o +*.swp +/projects/unix/_obj*/ diff --git a/mupen64plus-input-raphnetraw/.gitrepo b/mupen64plus-input-raphnetraw/.gitrepo new file mode 100644 index 00000000..48f8db39 --- /dev/null +++ b/mupen64plus-input-raphnetraw/.gitrepo @@ -0,0 +1,12 @@ +; DO NOT EDIT (unless you know what you are doing) +; +; This subdirectory is a git "subrepo", and this file is maintained by the +; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme +; +[subrepo] + remote = https://github.com/raphnet/mupen64plus-input-raphnetraw.git + branch = master + commit = 86112413e98a8648edb11d199673cc24d5799af8 + parent = b4024d75597da162bbc7cb693b8cef0d6da75105 + method = merge + cmdver = 0.4.3 diff --git a/mupen64plus-input-raphnetraw/COPYING b/mupen64plus-input-raphnetraw/COPYING new file mode 100644 index 00000000..60549be5 --- /dev/null +++ b/mupen64plus-input-raphnetraw/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/mupen64plus-input-raphnetraw/INSTALL b/mupen64plus-input-raphnetraw/INSTALL new file mode 100644 index 00000000..b0d4a7d5 --- /dev/null +++ b/mupen64plus-input-raphnetraw/INSTALL @@ -0,0 +1,26 @@ +Mupen64Plus-Input-raphnetraw INSTALL +----------------------------- + +This text file was written to explain the installation process of the +Mupen64Plus-Input-raphnetraw module. + +If this module is part of a Mupen64Plus source code bundle, the user should run +the "m64p_install.sh" script in the root of the unzipped bundle to install all +of the included modules in the bundle. + +If this module is a standalone source code release, you should build the library +from source code and install it via the makefile, like this: + +$ cd projects/unix +$ make all +$ sudo make install + +If you want to build the Mupen64Plus-Input-raphnetraw module for installation in a +home folder for a single user, you may build it like this (replacing + with your desired local installation path): + +$ cd projects/unix +$ make all +$ make install LIBDIR= + + diff --git a/mupen64plus-input-raphnetraw/LICENSES b/mupen64plus-input-raphnetraw/LICENSES new file mode 100644 index 00000000..7777bb71 --- /dev/null +++ b/mupen64plus-input-raphnetraw/LICENSES @@ -0,0 +1,377 @@ +Mupen64Plus-input-raphnetraw LICENSE +----------------------------- + +Mupen64Plus-input-raphnetraw is licensed under the GNU General Public License version 2. + +The author of Mupen64Plus-input-raphnetraw is: + * Raphaël Assénat (raphnet) + +Mupen64Plus-input-raphnetraw is based on Mupen64Plus-input-sdl, its authors being: + * Richard Goedeken (Richard42) + * Sven Eckelmann (ecsv) + * John Chadwick (NMN) + * James Hood (Ebenblues) + * Scott Gorman (okaygo) + * Scott Knauert (Tillin9) + * Jesse Dean (DarkJezter) + * Louai Al-Khanji (slougi) + * Bob Forder (orbitaldecay) + * Jason Espinosa (hasone) + * Bobby Smiles (bsmiles32) + * conchurnavid + * Fayvel + * HyperHacker + * Nebuleon + * s-verma + * and others. + +Mupen64Plus is based on GPL-licensed source code from Mupen64 v0.5, originally written by: + * Hacktarux + * Dave2001 + * Zilmar + * Gregor Anich (Blight) + * Juha Luotio (JttL) + * and others. + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. + diff --git a/mupen64plus-input-raphnetraw/README.md b/mupen64plus-input-raphnetraw/README.md new file mode 100644 index 00000000..1e0c7b80 --- /dev/null +++ b/mupen64plus-input-raphnetraw/README.md @@ -0,0 +1,159 @@ +# Input plugin with direct N64 controller access for raphnet adapters + +This mupen64plus plugin uses the direct controller access feature offered by my N64 to USB +adapters (versions 3 and up) to let the emulated game communicate with the controllers directly. + +* Perfect accuracy: The controller works exactly as it would on a N64 system, without any calibration or tweaking. +* Rumble Pak and Controller Pak (mempak) support (and potentially other accesories too [not confirmed]) +* Support for other peripherals that connect to controller ports (eg: N64 mouse) [not confirmed] +* Low latency + +## Project homepage + +* English: [Direct N64 controller access plugin mupen64plus](http://raphnet.net/programmation/mupen64plus-input-raphnetraw/index_en.php) +* French: [Plugin d'accès direct aux manettes N64 pour mupen64plus](http://raphnet.net/programmation/mupen64plus-input-raphnetraw/index.php) + +## License + +This project is licensed under the terms of the GNU General Public License, version 2. +Source code is available on the project homepage and on GitHub. + +## How to use the plugin + +Regardless of your operating system, you will need to install the plugin file(s) +in a directory mupen64plus will search for plugins (eg: Along the other plugin files). +Then, you must tell mupen64plus to use it. eg: + +``` +mupen64plus --input mupen64plus-input-raphnetraw +``` + +Where to obtain the plugin files is described below. + +### Linux + +Please compile it from sources (howto below). This will generate mupen64plus-input-raphnetraw.so +for your architecture. + +### Windows (32 and 64 bit) + +You may use the releases distributed by raphnet in a zip file. The file contents will look +like this: + +``` +mupen64plus-input-raphnetraw-windows-0.9/ +mupen64plus-input-raphnetraw-windows-0.9/README.md +mupen64plus-input-raphnetraw-windows-0.9/dist_win64/ +mupen64plus-input-raphnetraw-windows-0.9/dist_win64/libhidapi-0.dll +mupen64plus-input-raphnetraw-windows-0.9/dist_win64/mupen64plus-input-raphnetraw.dll +mupen64plus-input-raphnetraw-windows-0.9/dist_win32/ +mupen64plus-input-raphnetraw-windows-0.9/dist_win32/libhidapi-0.dll +mupen64plus-input-raphnetraw-windows-0.9/dist_win32/mupen64plus-input-raphnetraw.dll +``` + +If you use a 64-bit version of mupen64plus, copy the files from the `dist_win64` directory. For +the 32-bit version, copy the files from the `dist_win32` directory. + +## Compiling the Linux / Unix version + +### Prerequesites + +A Linux system such as Debian with the following packages installed: (non-exhaustive list) + +* gcc +* make +* libhidapi-hidraw0 +* libhidapi-dev + +### Environment + +#### Directory layout + +The input-raphnetraw plugin requires a checkout of mupen64plus-core (for the plugin api headers). + +* ...somedir/mupen64plus-input-raphnetraw +* ...somedir/mupen64plus-core + +#### Compilation + +```sh +$ cd .../somedir/ +$ cd mupen64plus-input-raphnetraw/projects/unix +$ make all + +# then, assuming all went well: +$ cp mupen64plus-input-raphnetraw.so /your/mupen64plus-plugin-directory + +# or, if you passed an appropriate PREFIX to make, you can just: +$ make install +``` + +## Win32/64 cross-compilation under Linux + +### System requirements + +A Linux system such as Debian with the following packages installed: (non-exhaustive list) + +* mingw-w64 +* autotools-dev +* autoconf +* automake +* libtool +* git +* make + +### Environment + +#### Directory layout + +The input-raphnetraw plugin requires a checkout of mupen64plus-core (for the plugin api headers) in the +parent directory. Also, each version (32 or 64 bit) requires a matching hidapi build. + +* ...somedir/mupen64plus-input-raphnetraw +* ...somedir/mupen64plus-core +* ...somedir/hidapi +* ...somedir/hidapi64 + +#### preparing the hidapi build + +The buildwin32.sh and buildwin64.sh scripts assume the following: + +* For the 32-bit build an hidapi configured and compiled with --host i686-w64-mingw32 is required here: ../../../hidapi +* For the 64-bit build an hidapi configured and compiled with --host x86_64-w64-mingw32 is required here: ../../../hidapi64 + +hidapi can be obtained like this: + +```sh +$ cd ...somedir +$ git clone https://github.com/signal11/hidapi.git +$ git clone hidapi hidapi64 +``` + +And then to compile the win32 version: +```sh +cd ...somedir/hidapi +./bootstrap +./configure --host i686-w64-mingw32 +make +``` + +And the win64 version: +```sh +cd ...somedir/hidapi64 +./bootstrap +./configure --host x86_64-w64-mingw32 +make +``` + +#### Compilation + +This would cross-compile the 32 and 64-bit windows versions. + +```bash +$ cd .../somedir/ +$ cd mupen64plus-input-raphnetraw/projects/unix + +# 32-bit version +$./buildwin32.sh +$ ./buildwin64.sh +``` diff --git a/mupen64plus-input-raphnetraw/RELEASE b/mupen64plus-input-raphnetraw/RELEASE new file mode 100644 index 00000000..0bce74ec --- /dev/null +++ b/mupen64plus-input-raphnetraw/RELEASE @@ -0,0 +1,43 @@ +# Raphnet raw Input plugin for Mupen64Plus +-------------------------------- + +## v1.1.0 - July 16, 2019 + +* Update to input plugin API 2.1.0 + +## v1.0.2 - November 6, 2018 + +* Add support for the 3.6.x adapter firmware + +## v1.0.1 - November 22, 2017 + +* Add support for the upcoming 3.5.x adapter firmware + (2-player adapters won't work properly otherwise) + +## v1.0.0 - January 15, 2017 + +* Support for multiple adapters + +## v0.9.4 - December 3, 2016 + +* Improved structure and separation for easier maintenance of the code shared between the Project 64 and mupen64plus versions of this plugin. +* Support for the upcoming 3.4.x adapter firmware (will provide even lower latency/overhead) + +## v0.9.3 - November 26, 2016 + +* Only handle channels supported by the adapter. This increases performance (reduces lag) by + preventing meaningless attempts to communicate with controllers connected to non-existing + ports on the adapter. It also fixes issues where games would see more than one controller was present. + +## v0.9.2 - November 24, 2016 + +* Protect against invalid commands received from emulator. This fixes some games that failed to start. + +## v0.9.1 - November 23, 2016 + +* Polling is now disabled only while a ROM is open. + +## v0.9.0 - November 20, 2016 + +* Initial release + diff --git a/mupen64plus-input-raphnetraw/projects/unix/Makefile b/mupen64plus-input-raphnetraw/projects/unix/Makefile new file mode 100644 index 00000000..9ee72264 --- /dev/null +++ b/mupen64plus-input-raphnetraw/projects/unix/Makefile @@ -0,0 +1,312 @@ +#/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +# * Mupen64plus-input-raphnetraw - Makefile * +# * Copyright (C) 2016 Raphael Assenat * +# * * +# * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * +# * * +# * This program is free software; you can redistribute it and/or modify * +# * it under the terms of the GNU General Public License as published by * +# * the Free Software Foundation; either version 2 of the License, or * +# * (at your option) any later version. * +# * * +# * This program is distributed in the hope that it will be useful, * +# * but WITHOUT ANY WARRANTY; without even the implied warranty of * +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +# * GNU General Public License for more details. * +# * * +# * You should have received a copy of the GNU General Public License * +# * along with this program; if not, write to the * +# * Free Software Foundation, Inc., * +# * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * +# * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +# Makefile for raphnetraw Input plugin in Mupen64plus + +# detect operating system +UNAME ?= $(shell uname -s) +OS := NONE +ifeq ("$(UNAME)","Linux") + OS = LINUX + SO_EXTENSION = so + SHARED = -shared +endif +ifeq ("$(UNAME)","linux") + OS = LINUX + SO_EXTENSION = so + SHARED = -shared +endif +ifneq ("$(filter GNU hurd,$(UNAME))","") + OS = LINUX + SO_EXTENSION = so + SHARED = -shared +endif +ifeq ("$(UNAME)","Darwin") + OS = OSX + SO_EXTENSION = dylib + SHARED = -bundle +endif +ifeq ("$(UNAME)","FreeBSD") + OS = FREEBSD + SO_EXTENSION = so + SHARED = -shared +endif +ifeq ("$(UNAME)","OpenBSD") + OS = FREEBSD + SO_EXTENSION = so + SHARED = -shared +endif +ifneq ("$(filter GNU/kFreeBSD kfreebsd,$(UNAME))","") + OS = LINUX + SO_EXTENSION = so + SHARED = -shared +endif +ifeq ("$(patsubst MINGW%,MINGW,$(UNAME))","MINGW") + OS = MINGW + SO_EXTENSION = dll + SHARED = -shared + PIC = 0 +endif +ifeq ("$(OS)","NONE") + $(error OS type "$(UNAME)" not supported. Please file bug report at 'http://code.google.com/p/mupen64plus/issues') +endif + +# detect system architecture, only if it matters for build flags +HOST_CPU ?= $(shell uname -m) +CPU := OTHER +ifneq ("$(filter x86_64 amd64,$(HOST_CPU))","") + CPU := X86 + ifeq ("$(BITS)", "32") + ARCH_DETECTED := 64BITS_32 + PIC ?= 0 + else + ARCH_DETECTED := 64BITS + PIC ?= 1 + endif +endif +ifneq ("$(filter pentium i%86,$(HOST_CPU))","") + CPU := X86 + ARCH_DETECTED := 32BITS + PIC ?= 0 +endif +ifeq ("$(CPU)","OTHER") + PIC ?= 1 +endif + +# base CFLAGS, LDLIBS, and LDFLAGS +OPTFLAGS ?= -O3 -flto +WARNFLAGS ?= -Wall +CFLAGS += $(OPTFLAGS) $(WARNFLAGS) -ffast-math -fno-strict-aliasing -fvisibility=hidden -I../../src -D_GNU_SOURCE=1 +LDFLAGS += $(SHARED) +LDLIBS += -lm + +# Since we are building a shared library, we must compile with -fPIC on some architectures +# On 32-bit x86 systems we do not want to use -fPIC because we don't have to and it has a big performance penalty on this arch +ifeq ($(PIC), 1) + CFLAGS += -fPIC +else + CFLAGS += -fno-PIC +endif + +# tweak flags for 32-bit build on 64-bit system +ifeq ($(ARCH_DETECTED), 64BITS_32) + CFLAGS += -m32 + LDFLAGS += -Wl,-m,elf_i386 +endif + +# set special flags per-system +ifeq ($(OS), FREEBSD) + ifeq ($(ARCH_DETECTED), 64BITS_32) + $(error Do not use the BITS=32 option with FreeBSD, use -m32 and -m elf_i386) + endif +endif +ifeq ($(OS), LINUX) + LDLIBS += -ldl +endif +ifeq ($(OS), OSX) + #xcode-select has been around since XCode 3.0, i.e. OS X 10.5 + OSX_SDK_ROOT = $(shell xcode-select -print-path)/Platforms/MacOSX.platform/Developer/SDKs + OSX_SDK_PATH = $(OSX_SDK_ROOT)/$(shell ls $(OSX_SDK_ROOT) | tail -1) + + ifeq ($(CPU), X86) + ifeq ($(ARCH_DETECTED), 64BITS) + CFLAGS += -arch x86_64 -mmacosx-version-min=10.5 -isysroot $(OSX_SDK_PATH) + LDLIBS += -ldl + else + CFLAGS += -mmmx -msse -fomit-frame-pointer -arch i686 -mmacosx-version-min=10.5 -isysroot $(OSX_SDK_PATH) + LDLIBS += -ldl -read_only_relocs suppress + endif + endif +endif + +ifeq ($(OS), LINUX) + HIDAPI_NAME=hidapi-hidraw +else + HIDAPI_NAME=hidapi +endif + +# test for presence of HIDLIB +ifeq ($(origin HID_CFLAGS) $(origin HID_LDLIBS), undefined undefined) + HIDAPI_CONFIG = $(CROSS_COMPILE)pkg-config $(HIDAPI_NAME) + ifeq ($(shell which $(HIDAPI_CONFIG) 2>/dev/null),) + HIDAPI_CONFIG = $(CROSS_COMPILE)pkg-config $(HIDAPI_NAME) + ifeq ($(shell which $(HIDAPI_CONFIG) 2>/dev/null),) + $(error No HIDAPI development libraries found!) + else + $(warning Using HIDAPI libraries) + endif + endif + HID_CFLAGS += $(shell $(HIDAPI_CONFIG) --cflags) + HID_LDLIBS += $(shell $(HIDAPI_CONFIG) --libs) +endif +CFLAGS += $(HID_CFLAGS) +LDLIBS += $(HID_LDLIBS) + +# set mupen64plus core API header path +ifneq ("$(APIDIR)","") + CFLAGS += "-I$(APIDIR)" +else + TRYDIR = ../../../mupen64plus-core/src/api + ifneq ("$(wildcard $(TRYDIR)/m64p_types.h)","") + CFLAGS += -I$(TRYDIR) + else + TRYDIR = /usr/local/include/mupen64plus + ifneq ("$(wildcard $(TRYDIR)/m64p_types.h)","") + CFLAGS += -I$(TRYDIR) + else + TRYDIR = /usr/include/mupen64plus + ifneq ("$(wildcard $(TRYDIR)/m64p_types.h)","") + CFLAGS += -I$(TRYDIR) + else + $(error Mupen64Plus API header files not found! Use makefile parameter APIDIR to force a location.) + endif + endif + endif +endif + +# reduced compile output when running make without V=1 +ifneq ($(findstring $(MAKEFLAGS),s),s) +ifndef V + Q_CC = @echo ' CC '$@; + Q_LD = @echo ' LD '$@; +endif +endif + +# set base program pointers and flags +CC = $(CROSS_COMPILE)gcc +CXX = $(CROSS_COMPILE)g++ +RM ?= rm -f +INSTALL ?= install +MKDIR ?= mkdir -p +COMPILE.c = $(Q_CC)$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c +LINK.o = $(Q_LD)$(CC) $(CFLAGS) $(LDFLAGS) $(TARGET_ARCH) + +# set special flags for given Makefile parameters +ifeq ($(DEBUG),1) + CFLAGS += -g + INSTALL_STRIP_FLAG ?= +else + ifneq ($(OS),OSX) + INSTALL_STRIP_FLAG ?= -s + endif +endif +ifeq ($(PLUGINDBG), 1) + CFLAGS += -D_DEBUG +endif +ifeq ($(PORTS_1_AND_4), 1) + CFLAGS += -DPORTS_1_AND_4 +endif + +# set installation options +ifeq ($(PREFIX),) + PREFIX := /usr/local +endif +ifeq ($(SHAREDIR),) + SHAREDIR := $(PREFIX)/share/mupen64plus +endif +ifeq ($(LIBDIR),) + LIBDIR := $(PREFIX)/lib +endif +ifeq ($(PLUGINDIR),) + PLUGINDIR := $(LIBDIR)/mupen64plus +endif + +SRCDIR = ../../src +OBJDIR = _obj$(POSTFIX) + +# list of source files to compile +SOURCE = \ + $(SRCDIR)/plugin_front.c \ + $(SRCDIR)/plugin_back.c \ + $(SRCDIR)/gcn64lib.c \ + $(SRCDIR)/gcn64.c \ + $(SRCDIR)/hexdump.c + +ifeq ($(OS),MINGW) +SOURCE += $(SRCDIR)/osal_dynamiclib_win32.c +else +SOURCE += $(SRCDIR)/osal_dynamiclib_unix.c +endif + +# generate a list of object files build, make a temporary directory for them +OBJECTS := $(patsubst $(SRCDIR)/%.c, $(OBJDIR)/%.o, $(filter %.c, $(SOURCE))) +OBJDIRS = $(dir $(OBJECTS)) +$(shell $(MKDIR) $(OBJDIRS)) + +# build targets +TARGET = mupen64plus-input-raphnetraw$(POSTFIX).$(SO_EXTENSION) + +targets: + @echo "Mupen64Plus-input-raphnetraw makefile. " + @echo " Targets:" + @echo " all == Build Mupen64Plus raphnetraw input plugin" + @echo " clean == remove object files" + @echo " rebuild == clean and re-build all" + @echo " install == Install Mupen64Plus raphnetraw input plugin" + @echo " uninstall == Uninstall Mupen64Plus raphnetraw input plugin" + @echo " Options:" + @echo " BITS=32 == build 32-bit binaries on 64-bit machine" + @echo " APIDIR=path == path to find Mupen64Plus Core headers" + @echo " OPTFLAGS=flag == compiler optimization (default: -O3 -flto)" + @echo " WARNFLAGS=flag == compiler warning levels (default: -Wall)" + @echo " PIC=(1|0) == Force enable/disable of position independent code" + @echo " POSTFIX=name == String added to the name of the the build (default: '')" + @echo " Install Options:" + @echo " PREFIX=path == install/uninstall prefix (default: /usr/local)" + @echo " SHAREDIR=path == path to install shared data files (default: PREFIX/share/mupen64plus)" + @echo " LIBDIR=path == library prefix (default: PREFIX/lib)" + @echo " PLUGINDIR=path == path to install plugin libraries (default: LIBDIR/mupen64plus)" + @echo " DESTDIR=path == path to prepend to all installation paths (only for packagers)" + @echo " Debugging Options:" + @echo " DEBUG=1 == add debugging symbols" + @echo " PLUGINDBG=1 == print extra debugging information while running" + @echo " V=1 == show verbose compiler output" + @echo " Build Options, tweaks:" + @echo " PORTS_1_AND_4 == N64 port 4 -> Adapter port 2" + +all: $(TARGET) + +install: $(TARGET) + $(INSTALL) -d "$(DESTDIR)$(PLUGINDIR)" + $(INSTALL) -m 0644 $(INSTALL_STRIP_FLAG) $(TARGET) "$(DESTDIR)$(PLUGINDIR)" + $(INSTALL) -d "$(DESTDIR)$(SHAREDIR)" + +uninstall: + $(RM) "$(DESTDIR)$(PLUGINDIR)/$(TARGET)" + $(RM) "$(DESTDIR)$(SHAREDIR)/InputAutoCfg.ini" + +clean: + $(RM) -r $(OBJDIR) $(TARGET) + +rebuild: clean all + +# build dependency files +CFLAGS += -MD -MP +-include $(OBJECTS:.o=.d) + +# standard build rules +$(OBJDIR)/%.o: $(SRCDIR)/%.c + $(COMPILE.c) -o $@ $< + +$(TARGET): $(OBJECTS) + $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@ + +.PHONY: all clean install uninstall targets diff --git a/mupen64plus-input-raphnetraw/projects/unix/buildwin32.sh b/mupen64plus-input-raphnetraw/projects/unix/buildwin32.sh new file mode 100755 index 00000000..c9be0247 --- /dev/null +++ b/mupen64plus-input-raphnetraw/projects/unix/buildwin32.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +HIDHOME=../../../hidapi +UNAME=MINGW32 +DESTDIR=./dist_win32 + +HID_CFLAGS="-I $HIDHOME/hidapi" +HID_LDLIBS="-L $HIDHOME/windows/.libs/ -lhidapi" +TARGETS=all +MAKEFILE=Makefile + +make -f $MAKEFILE clean +if [ $? -ne 0 ]; then + exit 1 +fi + +make -f $MAKEFILE CROSS_COMPILE=i686-w64-mingw32- UNAME=$UNAME HID_CFLAGS="$HID_CFLAGS" HID_LDLIBS="$HID_LDLIBS" $TARGETS V=1 +if [ $? -ne 0 ]; then + exit 1 +fi + +mkdir -p $DESTDIR +cp mupen64plus-input-raphnetraw.dll $DESTDIR -v +cp $HIDHOME/windows/.libs/*.dll $DESTDIR -v diff --git a/mupen64plus-input-raphnetraw/projects/unix/buildwin64.sh b/mupen64plus-input-raphnetraw/projects/unix/buildwin64.sh new file mode 100755 index 00000000..3f9393af --- /dev/null +++ b/mupen64plus-input-raphnetraw/projects/unix/buildwin64.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +HIDHOME=../../../hidapi64 +UNAME=MINGW64 +DESTDIR=./dist_win64 + +HID_CFLAGS="-I $HIDHOME/hidapi" +HID_LDLIBS="-L $HIDHOME/windows/.libs/ -lhidapi" +TARGETS=all +MAKEFILE=Makefile + +make -f $MAKEFILE clean +if [ $? -ne 0 ]; then + exit 1 +fi + +make -f $MAKEFILE CROSS_COMPILE=x86_64-w64-mingw32- UNAME=$UNAME HID_CFLAGS="$HID_CFLAGS" HID_LDLIBS="$HID_LDLIBS" $TARGETS V=1 +if [ $? -ne 0 ]; then + exit 1 +fi + +mkdir -p $DESTDIR +cp mupen64plus-input-raphnetraw.dll $DESTDIR -v +cp $HIDHOME/windows/.libs/*.dll $DESTDIR -v diff --git a/mupen64plus-input-raphnetraw/release_src.sh b/mupen64plus-input-raphnetraw/release_src.sh new file mode 100755 index 00000000..016d8589 --- /dev/null +++ b/mupen64plus-input-raphnetraw/release_src.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +PREFIX=mupen64plus-input-raphnetraw +HEXFILE=$PREFIX.hex + +echo "Release script for $PREFIX" + +if [ $# -ne 2 ]; then + echo "Syntax: ./release.sh version releasedir" + echo + echo "ex: './release 1.0' will produce $PREFIX-1.0.tar.gz in releasedir out of git HEAD," + echo "and place it in the release directory." + echo + echo "It will also create a tag named v1.0" + exit +fi + +VERSION=$1 +RELEASEDIR=$2 +DIRNAME=$PREFIX-$VERSION +FILENAME=$PREFIX-$VERSION.tar.gz +TAG=v$VERSION + +echo "Version: $VERSION" +echo "Filename: $FILENAME" +echo "Release directory: $RELEASEDIR" +echo "--------" +echo "Ready? Press ENTER to go ahead (or CTRL+C to cancel)" + +read + +if [ -f $RELEASEDIR/$FILENAME ]; then + echo "Release file already exists!" + exit 1 +fi + +git tag $TAG -f +git archive --format=tar --prefix=$DIRNAME/ HEAD | gzip > $RELEASEDIR/$FILENAME diff --git a/mupen64plus-input-raphnetraw/release_windows.sh b/mupen64plus-input-raphnetraw/release_windows.sh new file mode 100755 index 00000000..2fb193e1 --- /dev/null +++ b/mupen64plus-input-raphnetraw/release_windows.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +PROJECT=mupen64plus-input-raphnetraw-windows + +if [ $# -ne 2 ]; then + echo "Syntax: ./release.sh version releasedir" + echo + exit +fi + +VERSION=$1 +RELEASEDIR=$2 +DIRNAME=$PROJECT-$VERSION +FILENAME=$PROJECT-$VERSION.zip + +if [ -d $RELEASEDIR/$DIRNAME ]; then + echo "$RELEASEDIR/$DIRNAME already exists"; + exit 1 +fi + +mkdir -p $RELEASEDIR/$DIRNAME +if [ $? -ne 0 ]; then + echo "Could not mkdir" + exit 1 +fi + +cd projects/unix +if [ $? -ne 0 ]; then + echo "Could not chdir" + exit 1 +fi + +./buildwin32.sh +if [ $? -ne 0 ]; then + echo "Win32 build failed" + exit 1 +fi + +./buildwin64.sh +if [ $? -ne 0 ]; then + echo "Win64 build failed" + exit 1 +fi + +cd .. # exit unix +cd .. # exit projects + +cp projects/unix/dist_win32 -rv $RELEASEDIR/$DIRNAME +cp projects/unix/dist_win64 -rv $RELEASEDIR/$DIRNAME +cp README.md -rv $RELEASEDIR/$DIRNAME +cp README.md -rv $RELEASEDIR/$DIRNAME/readme.txt +cp RELEASE -rv $RELEASEDIR/$DIRNAME/releases.txt + +cd $RELEASEDIR + +zip -r $FILENAME $DIRNAME +echo +echo +echo +echo ------------------------------------- +ls -lh $FILENAME +echo Done diff --git a/mupen64plus-input-raphnetraw/src/gcn64.c b/mupen64plus-input-raphnetraw/src/gcn64.c new file mode 100644 index 00000000..30a2dd27 --- /dev/null +++ b/mupen64plus-input-raphnetraw/src/gcn64.c @@ -0,0 +1,371 @@ +/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware + Copyright (C) 2007-2017 Raphael Assenat + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#include +#include +#include +#include +#include "gcn64.h" +#include "gcn64_priv.h" +#include "gcn64lib.h" +#include "requests.h" + +#include "hidapi.h" + +static int dusbr_verbose = 0; + +#define IS_VERBOSE() (dusbr_verbose) + +struct supported_adapter { + uint16_t vid, pid; + int if_number; + struct gcn64_adapter_caps caps; +}; + +static struct supported_adapter supported_adapters[] = { + /* vid, pid, if_no, { n_raw, bio_support } */ + + { OUR_VENDOR_ID, 0x0017, 1, { 1, 0 } }, // GC/N64 USB v3.0, 3.1.0, 3.1.1 + { OUR_VENDOR_ID, 0x001D, 1, { 1, 0 } }, // GC/N64 USB v3.2.0 ... v3.3.x + { OUR_VENDOR_ID, 0x0020, 1, { 1, 0 } }, // GCN64->USB v3.2.1 (N64 mode) + { OUR_VENDOR_ID, 0x0021, 1, { 1, 0 } }, // GCN64->USB v3.2.1 (GC mode) + { OUR_VENDOR_ID, 0x0022, 1, { 2, 0 } }, // GCN64->USB v3.3.x (2x GC/N64 mode) + { OUR_VENDOR_ID, 0x0030, 1, { 2, 0 } }, // GCN64->USB v3.3.0 (2x N64-only mode) + { OUR_VENDOR_ID, 0x0031, 1, { 2, 0 } }, // GCN64->USB v3.3.0 (2x GC-only mode) + + { OUR_VENDOR_ID, 0x0032, 1, { 1, 1 } }, // GC/N64 USB v3.4.x (GC/N64 mode) + { OUR_VENDOR_ID, 0x0033, 1, { 1, 1 } }, // GC/N64 USB v3.4.x (N64 mode) + { OUR_VENDOR_ID, 0x0034, 1, { 1, 1 } }, // GC/N64 USB v3.4.x (GC mode) + { OUR_VENDOR_ID, 0x0035, 1, { 2, 1 } }, // GC/N64 USB v3.4.x (2x GC/N64 mode) + { OUR_VENDOR_ID, 0x0036, 1, { 2, 1 } }, // GC/N64 USB v3.4.x (2x N64-only mode) + { OUR_VENDOR_ID, 0x0037, 1, { 2, 1 } }, // GC/N64 USB v3.4.x (2x GC-only mode) + + // GC/N64 USB v3.5.x flavours + { OUR_VENDOR_ID, 0x0038, 1, { 1, 1 } }, // (GC/N64 mode) + { OUR_VENDOR_ID, 0x0039, 1, { 1, 1 } }, // (N64 mode) + { OUR_VENDOR_ID, 0x003A, 1, { 1, 1 } }, // (GC mode) + { OUR_VENDOR_ID, 0x003B, 2, { 2, 1 } }, // (2x GC/N64 mode) + { OUR_VENDOR_ID, 0x003C, 2, { 2, 1 } }, // (2x N64-only mode) + { OUR_VENDOR_ID, 0x003D, 2, { 2, 1 } }, // (2x GC-only mode) + + // GC/N64 USB v3.6.x flavours + { OUR_VENDOR_ID, 0x0060, 1, { 1, 1 } }, // (GC/N64 mode) + { OUR_VENDOR_ID, 0x0061, 1, { 1, 1 } }, // (N64 mode) + { OUR_VENDOR_ID, 0x0063, 2, { 2, 1 } }, // (2x GC/N64 mode) + { OUR_VENDOR_ID, 0x0064, 2, { 2, 1 } }, // (2x N64-only mode) + { OUR_VENDOR_ID, 0x0067, 1, { 1, 1 } }, // (GC/N64 in GC keyboard mode) + + { }, // terminator +}; + +int gcn64_init(int verbose) +{ + dusbr_verbose = verbose; + hid_init(); + return 0; +} + +void gcn64_shutdown(void) +{ + hid_exit(); +} + +static char isProductIdHandled(unsigned short pid, int interface_number, struct gcn64_adapter_caps *caps) +{ + int i; + + for (i=0; supported_adapters[i].vid; i++) { + if (pid == supported_adapters[i].pid) { + if (interface_number == supported_adapters[i].if_number) { + if (caps) { + memcpy(caps, &supported_adapters[i].caps, sizeof (struct gcn64_adapter_caps)); + } + return 1; + } + } + } + + return 0; +} + +struct gcn64_list_ctx *gcn64_allocListCtx(void) +{ + struct gcn64_list_ctx *ctx; + ctx = calloc(1, sizeof(struct gcn64_list_ctx)); + return ctx; +} + +void gcn64_freeListCtx(struct gcn64_list_ctx *ctx) +{ + if (ctx) { + if (ctx->devs) { + hid_free_enumeration(ctx->devs); + } + free(ctx); + } +} + +int gcn64_countDevices(void) +{ + struct gcn64_list_ctx *ctx; + struct gcn64_info inf; + int count = 0; + + ctx = gcn64_allocListCtx(); + while (gcn64_listDevices(&inf, ctx)) { + count++; + } + gcn64_freeListCtx(ctx); + + return count; +} + +/** + * \brief List instances of our rgbleds device on the USB busses. + * \param info Pointer to gcn64_info structure to store data + * \param dst Destination buffer for device serial number/id. + * \param dstbuf_size Destination buffer size. + */ +struct gcn64_info *gcn64_listDevices(struct gcn64_info *info, struct gcn64_list_ctx *ctx) +{ + struct gcn64_adapter_caps caps; + + memset(info, 0, sizeof(struct gcn64_info)); + + if (!ctx) { + fprintf(stderr, "gcn64_listDevices: Passed null context\n"); + return NULL; + } + + if (ctx->devs) + goto jumpin; + + if (IS_VERBOSE()) { + printf("Start listing\n"); + } + + ctx->devs = hid_enumerate(OUR_VENDOR_ID, 0x0000); + if (!ctx->devs) { + printf("Hid enumerate returned NULL\n"); + return NULL; + } + + for (ctx->cur_dev = ctx->devs; ctx->cur_dev; ctx->cur_dev = ctx->cur_dev->next) + { + if (IS_VERBOSE()) { + printf("Considering 0x%04x:0x%04x\n", ctx->cur_dev->vendor_id, ctx->cur_dev->product_id); + } + if (isProductIdHandled(ctx->cur_dev->product_id, ctx->cur_dev->interface_number, &caps)) + { + info->usb_vid = ctx->cur_dev->vendor_id; + info->usb_pid = ctx->cur_dev->product_id; + wcsncpy(info->str_prodname, ctx->cur_dev->product_string, PRODNAME_MAXCHARS-1); + wcsncpy(info->str_serial, ctx->cur_dev->serial_number, SERIAL_MAXCHARS-1); + strncpy(info->str_path, ctx->cur_dev->path, PATH_MAXCHARS-1); + memcpy(&info->caps, &caps, sizeof(info->caps)); + return info; + } + + jumpin: + // prevent 'error: label at end of compound statement' + continue; + } + + return NULL; +} + +gcn64_hdl_t gcn64_openDevice(struct gcn64_info *dev) +{ + hid_device *hdev; + gcn64_hdl_t hdl; + + if (!dev) + return NULL; + + if (IS_VERBOSE()) { + printf("Opening device path: '%s'\n", dev->str_path); + } + + hdev = hid_open_path(dev->str_path); + if (!hdev) { + return NULL; + } + + hdl = malloc(sizeof(struct _gcn64_hdl_t)); + if (!hdl) { + perror("malloc"); + hid_close(hdev); + return NULL; + } + hdl->hdev = hdev; + hdl->report_size = 63; + memcpy(&hdl->caps, &dev->caps, sizeof(hdl->caps)); + + if (!dev->caps.bio_support) { + printf("Pre-3.4 version detected. Setting report size to 40 bytes\n"); + hdl->report_size = 40; + } + + return hdl; +} + +gcn64_hdl_t gcn64_openBy(struct gcn64_info *dev, unsigned char flags) +{ + struct gcn64_list_ctx *ctx; + struct gcn64_info inf; + gcn64_hdl_t h; + + if (IS_VERBOSE()) + printf("gcn64_openBy, flags=0x%02x\n", flags); + + ctx = gcn64_allocListCtx(); + if (!ctx) + return NULL; + + while (gcn64_listDevices(&inf, ctx)) { + if (IS_VERBOSE()) + printf("Considering '%s'\n", inf.str_path); + + if (flags & GCN64_FLG_OPEN_BY_SERIAL) { + if (wcscmp(inf.str_serial, dev->str_serial)) + continue; + } + + if (flags & GCN64_FLG_OPEN_BY_PATH) { + if (strcmp(inf.str_path, dev->str_path)) + continue; + } + + if (flags & GCN64_FLG_OPEN_BY_VID) { + if (inf.usb_vid != dev->usb_vid) + continue; + } + + if (flags & GCN64_FLG_OPEN_BY_PID) { + if (inf.usb_pid != dev->usb_pid) + continue; + } + + if (IS_VERBOSE()) + printf("Found device. opening...\n"); + + h = gcn64_openDevice(&inf); + gcn64_freeListCtx(ctx); + return h; + } + + gcn64_freeListCtx(ctx); + return NULL; +} + +void gcn64_closeDevice(gcn64_hdl_t hdl) +{ + hid_device *hdev = hdl->hdev; + + if (hdev) { + hid_close(hdev); + } + + free(hdl); +} + +int gcn64_send_cmd(gcn64_hdl_t hdl, const unsigned char *cmd, int cmdlen) +{ + hid_device *hdev = hdl->hdev; + unsigned char buffer[hdl->report_size+1]; + int n; + + if (cmdlen > (sizeof(buffer)-1)) { + fprintf(stderr, "Error: Command too long\n"); + return -1; + } + + memset(buffer, 0, sizeof(buffer)); + + buffer[0] = 0x00; // report ID set to 0 (device has only one) + memcpy(buffer + 1, cmd, cmdlen); + + n = hid_send_feature_report(hdev, buffer, sizeof(buffer)); + if (n < 0) { + fprintf(stderr, "Could not send feature report (%ls)\n", hid_error(hdev)); + return -1; + } + + return 0; +} + +int gcn64_poll_result(gcn64_hdl_t hdl, unsigned char *cmd, int cmd_maxlen) +{ + hid_device *hdev = hdl->hdev; + unsigned char buffer[hdl->report_size+1]; + int res_len; + int n; + + memset(buffer, 0, sizeof(buffer)); + buffer[0] = 0x00; // report ID set to 0 (device has only one) + + n = hid_get_feature_report(hdev, buffer, sizeof(buffer)); + if (n < 0) { + fprintf(stderr, "Could not send feature report (%ls)\n", hid_error(hdev)); + return -1; + } + if (n==0) { + return 0; + } + res_len = n-1; + + if (res_len>0) { + int copy_len; + + copy_len = res_len; + if (copy_len > cmd_maxlen) { + copy_len = cmd_maxlen; + } + if (cmd) { + memcpy(cmd, buffer+1, copy_len); + } + } + + return res_len; +} + +int gcn64_exchange(gcn64_hdl_t hdl, unsigned char *outcmd, int outlen, unsigned char *result, int result_max) +{ + int n; + + n = gcn64_send_cmd(hdl, outcmd, outlen); + if (n<0) { + fprintf(stderr, "Error sending command\n"); + return -1; + } + + /* Answer to the command comes later. For now, this is polled, but in + * the future an interrupt-in transfer could be used. */ + do { + n = gcn64_poll_result(hdl, result, result_max); + if (n < 0) { + fprintf(stderr, "Error\r\n"); + break; + } + if (n==0) { +// printf("."); fflush(stdout); + } + + } while (n==0); + + return n; +} + diff --git a/mupen64plus-input-raphnetraw/src/gcn64.h b/mupen64plus-input-raphnetraw/src/gcn64.h new file mode 100644 index 00000000..460cb245 --- /dev/null +++ b/mupen64plus-input-raphnetraw/src/gcn64.h @@ -0,0 +1,59 @@ +#ifndef _gcn64_h__ +#define _gcn64_h__ + +#include + +#define OUR_VENDOR_ID 0x289b +#define PRODNAME_MAXCHARS 256 +#define SERIAL_MAXCHARS 256 +#define PATH_MAXCHARS 256 + +struct gcn64_adapter_caps { + int n_raw_channels; + int bio_support; +}; + +struct gcn64_info { + wchar_t str_prodname[PRODNAME_MAXCHARS]; + wchar_t str_serial[SERIAL_MAXCHARS]; + char str_path[PATH_MAXCHARS]; + int usb_vid, usb_pid; + int access; // True unless direct access to read serial/prodname failed due to permissions. + struct gcn64_adapter_caps caps; +}; + +struct gcn64_list_ctx; + +typedef struct _gcn64_hdl_t *gcn64_hdl_t; // Cast from hid_device + +int gcn64_init(int verbose); +void gcn64_shutdown(void); + +struct gcn64_list_ctx *gcn64_allocListCtx(void); +void gcn64_freeListCtx(struct gcn64_list_ctx *ctx); +struct gcn64_info *gcn64_listDevices(struct gcn64_info *info, struct gcn64_list_ctx *ctx); +int gcn64_countDevices(void); + +gcn64_hdl_t gcn64_openDevice(struct gcn64_info *dev); + +#define GCN64_FLG_OPEN_BY_SERIAL 1 /** Serial must match */ +#define GCN64_FLG_OPEN_BY_PATH 2 /** Path must match */ +#define GCN64_FLG_OPEN_BY_VID 4 /** USB VID must match */ +#define GCN64_FLG_OPEN_BY_PID 8 /** USB PID MUST match */ +/** + * \brief Open a device matching a serial number + * \param dev The device structure + * \param flags Flags + * \return A handle to the opened device, or NULL if not found + **/ +gcn64_hdl_t gcn64_openBy(struct gcn64_info *dev, unsigned char flags); + +void gcn64_closeDevice(gcn64_hdl_t hdl); + +int gcn64_send_cmd(gcn64_hdl_t hdl, const unsigned char *cmd, int len); +int gcn64_poll_result(gcn64_hdl_t hdl, unsigned char *cmd, int cmdlen); + +int gcn64_exchange(gcn64_hdl_t hdl, unsigned char *outcmd, int outlen, unsigned char *result, int result_max); + +#endif // _gcn64_h__ + diff --git a/mupen64plus-input-raphnetraw/src/gcn64_priv.h b/mupen64plus-input-raphnetraw/src/gcn64_priv.h new file mode 100644 index 00000000..909e5dae --- /dev/null +++ b/mupen64plus-input-raphnetraw/src/gcn64_priv.h @@ -0,0 +1,17 @@ +#ifndef _gcn64_priv_h__ +#define _gcn64_priv_h__ + +#include "hidapi.h" +#include "gcn64.h" + +struct gcn64_list_ctx { + struct hid_device_info *devs, *cur_dev; +}; + +typedef struct _gcn64_hdl_t { + hid_device *hdev; + int report_size; + struct gcn64_adapter_caps caps; +} *gcn64_hdl_t; + +#endif diff --git a/mupen64plus-input-raphnetraw/src/gcn64_protocol.h b/mupen64plus-input-raphnetraw/src/gcn64_protocol.h new file mode 100644 index 00000000..0c3ba244 --- /dev/null +++ b/mupen64plus-input-raphnetraw/src/gcn64_protocol.h @@ -0,0 +1,166 @@ +#ifndef _gcn64_protocol_h__ +#define _gcn64_protocol_h__ + +#define CONTROLLER_IS_ABSENT 0 +#define CONTROLLER_IS_N64 1 +#define CONTROLLER_IS_GC 2 +#define CONTROLLER_IS_GC_KEYBOARD 3 +#define CONTROLLER_IS_UNKNOWN 4 + + +/* Return many unknown bits, but two are about the expansion port. */ +#define N64_GET_CAPABILITIES 0x00 +#define N64_RESET 0xFF +#define N64_CAPS_REPLY_LENGTH 3 + +#define OFFSET_EXT_REMOVED 22 +#define OFFSET_EXT_PRESENT 23 + +/* Returns button states and axis values */ +#define N64_GET_STATUS 0x01 +#define N64_GET_STATUS_REPLY_LENGTH 4 + +/* Read from the expansion bus. */ +#define N64_EXPANSION_READ 0x02 + +/* Write to the expansion bus. */ +#define N64_EXPANSION_WRITE 0x03 + +/* Return information about controller. */ +#define GC_GETID 0x00 +#define GC_GETID_REPLY_LENGTH 3 + +/* 3-byte get status command. Returns axis and buttons. Also + * controls motor. */ +#define GC_GETSTATUS1 0x40 +#define GC_GETSTATUS2 0x03 +#define GC_GETSTATUS3(rumbling) ((rumbling) ? 0x01 : 0x00) +#define GC_GETSTATUS_REPLY_LENGTH 8 + +/* 3-byte poll keyboard command. + * Source: http://hitmen.c02.at/files/yagcd/yagcd/chap9.html#sec9.3.3 + * */ +#define GC_POLL_KB1 0x54 +#define GC_POLL_KB2 0x00 +#define GC_POLL_KB3 0x00 + +/* Gamecube keycodes are from table 9.3.2: + * http://hitmen.c02.at/files/yagcd/yagcd/chap9.html#sec9.3.2 + * + * But the table appears to have been built using a PC keyboard + * converted (Tototek). + * + * I was working with an ASCII keyboard so I made a few adjustments + * to reflect the /real/ key functions. For instance: + * + * LWIN, RWIN and MENU are in fact the Henkan, muhenkan and katakana/hiragana keys. + * They are equivalemnt to the HID keys International 4, International 5 and International 2. + * The - key (code 0x3F) is also HID international 1. + * The PrntScrn key (code 0x36) is in fact the Yen key (International 3). + * The bracket/brace [{ and }] keys are at different places on this Japanese keyboard, + * but following standard PC practice, some keycodes are named after the US usage. Hence, + * the aforementioned keys are the 2 keys right of P, even if they are not labeled. + */ +#define GC_KEY_RESERVED 0x00 // No key + +#define GC_KEY_HOME 0x06 +#define GC_KEY_END 0x07 +#define GC_KEY_PGUP 0x08 +#define GC_KEY_PGDN 0x09 +#define GC_KEY_SCROLL_LOCK 0x0A + +#define GC_KEY_A 0x10 +#define GC_KEY_B 0x11 +#define GC_KEY_C 0x12 +#define GC_KEY_D 0x13 +#define GC_KEY_E 0x14 +#define GC_KEY_F 0x15 +#define GC_KEY_G 0x16 +#define GC_KEY_H 0x17 +#define GC_KEY_I 0x18 +#define GC_KEY_J 0x19 +#define GC_KEY_K 0x1A +#define GC_KEY_L 0x1B +#define GC_KEY_M 0x1C +#define GC_KEY_N 0x1D +#define GC_KEY_O 0x1E +#define GC_KEY_P 0x1F +#define GC_KEY_Q 0x20 +#define GC_KEY_R 0x21 +#define GC_KEY_S 0x22 +#define GC_KEY_T 0x23 +#define GC_KEY_U 0x24 +#define GC_KEY_V 0x25 +#define GC_KEY_W 0x26 +#define GC_KEY_X 0x27 +#define GC_KEY_Y 0x28 +#define GC_KEY_Z 0x29 +#define GC_KEY_1 0x2A +#define GC_KEY_2 0x2B +#define GC_KEY_3 0x2C +#define GC_KEY_4 0x2D +#define GC_KEY_5 0x2E +#define GC_KEY_6 0x2F +#define GC_KEY_7 0x30 +#define GC_KEY_8 0x31 +#define GC_KEY_9 0x32 +#define GC_KEY_0 0x33 +#define GC_KEY_DASH_UNDERSCORE 0x34 // Key next to 0 +#define GC_KEY_PLUS_EQUAL 0x35 +#define GC_KEY_YEN 0x36 // Yen on ascii keyboard. HID International 3. +#define GC_KEY_OPEN_BRKT_BRACE 0x37 // 1st key right of P +#define GC_KEY_CLOSE_BRKT_BRACE 0x38 // 2nd key right of P +#define GC_KEY_SEMI_COLON_COLON 0x39 // ;: +#define GC_KEY_QUOTES 0x3A // '" + +// The 3rd key after 'L'. HID Keyboard non-us # and ~ +#define GC_KEY_BRACKET_MU 0x3B +#define GC_KEY_COMMA_ST 0x3C // ,< +#define GC_KEY_PERIOD_GT 0x3D // .> +#define GC_KEY_SLASH_QUESTION 0x3E // /? + +// (The extra key before right-shift on japanese keyboards. +// HID code International 1 [HID usage tables Footnote 15-20]). +#define GC_KEY_INTERNATIONAL1 0x3F +#define GC_KEY_F1 0x40 +#define GC_KEY_F2 0x41 +#define GC_KEY_F3 0x42 +#define GC_KEY_F4 0x43 +#define GC_KEY_F5 0x44 +#define GC_KEY_F6 0x45 +#define GC_KEY_F7 0x46 +#define GC_KEY_F8 0x47 +#define GC_KEY_F9 0x48 +#define GC_KEY_F10 0x49 +#define GC_KEY_F11 0x4A +#define GC_KEY_F12 0x4B +#define GC_KEY_ESC 0x4C +#define GC_KEY_INSERT 0x4D +#define GC_KEY_DELETE 0x4E + +// (Hankaku/zenkaku/kanji button). Also known as `~ +#define GC_KEY_HANKAKU 0x4F +#define GC_KEY_BACKSPACE 0x50 +#define GC_KEY_TAB 0x51 + +#define GC_KEY_CAPS_LOCK 0x53 +#define GC_KEY_LEFT_SHIFT 0x54 +#define GC_KEY_RIGHT_SHIFT 0x55 +#define GC_KEY_LEFT_CTRL 0x56 +#define GC_KEY_LEFT_ALT 0x57 +#define GC_KEY_MUHENKAN 0x58 // HID international 5 +#define GC_KEY_SPACE 0x59 +#define GC_KEY_HENKAN 0x5A // HID international 4 +#define GC_KEY_KANA 0x5B // HID international 2 +#define GC_KEY_LEFT 0x5C +#define GC_KEY_DOWN 0x5D +#define GC_KEY_UP 0x5E +#define GC_KEY_RIGHT 0x5F + +#define GC_KEY_ENTER 0x61 + +void gcn64protocol_hwinit(void); +int gcn64_detectController(void); +unsigned char gcn64_transaction(const unsigned char *tx, int tx_len, unsigned char *rx, unsigned char rx_max); + +#endif // _gcn64_protocol_h__ diff --git a/mupen64plus-input-raphnetraw/src/gcn64lib.c b/mupen64plus-input-raphnetraw/src/gcn64lib.c new file mode 100644 index 00000000..445d546a --- /dev/null +++ b/mupen64plus-input-raphnetraw/src/gcn64lib.c @@ -0,0 +1,444 @@ +/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware + Copyright (C) 2007-2015 Raphael Assenat + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#include +#include +#include "gcn64lib.h" +#include "gcn64_priv.h" +#include "requests.h" +#include "gcn64_protocol.h" +#include "hexdump.h" + +int gcn64lib_getConfig(gcn64_hdl_t hdl, unsigned char param, unsigned char *rx, unsigned char rx_max) +{ + unsigned char cmd[2]; + int n; + + if (!hdl) { + return -1; + } + + cmd[0] = RQ_GCN64_GET_CONFIG_PARAM; + cmd[1] = param; + + n = gcn64_exchange(hdl, cmd, 2, rx, rx_max); + if (n<2) + return n; + + n -= 2; + + // Drop the leading CMD and PARAM + if (n) { + memmove(rx, rx+2, n); + } + + return n; +} +int gcn64lib_setConfig(gcn64_hdl_t hdl, unsigned char param, unsigned char *data, unsigned char len) +{ + unsigned char cmd[2 + len]; + int n; + + if (!hdl) { + return -1; + } + + cmd[0] = RQ_GCN64_SET_CONFIG_PARAM; + cmd[1] = param; + memcpy(cmd + 2, data, len); + + n = gcn64_exchange(hdl, cmd, 2 + len, cmd, sizeof(cmd)); + if (n<0) + return n; + + return 0; +} + +int gcn64lib_suspendPolling(gcn64_hdl_t hdl, unsigned char suspend) +{ + unsigned char cmd[2]; + int n; + + if (!hdl) { + return -1; + } + + cmd[0] = RQ_GCN64_SUSPEND_POLLING; + cmd[1] = suspend; + + n = gcn64_exchange(hdl, cmd, 2, cmd, sizeof(cmd)); + if (n<0) + return n; + + return 0; +} + +int gcn64lib_getVersion(gcn64_hdl_t hdl, char *dst, int dstmax) +{ + unsigned char cmd[32]; + int n; + + if (!hdl) { + return -1; + } + + if (dstmax <= 0) + return -1; + + cmd[0] = RQ_GCN64_GET_VERSION; + + n = gcn64_exchange(hdl, cmd, 1, cmd, sizeof(cmd)); + if (n<0) + return n; + + dst[0] = 0; + if (n > 1) { + strncpy(dst, (char*)cmd+1, n); + } + dst[dstmax-1] = 0; + + return 0; +} + +int gcn64lib_getControllerType(gcn64_hdl_t hdl, int chn) +{ + unsigned char cmd[32]; + int n; + + if (!hdl) { + return -1; + } + + cmd[0] = RQ_GCN64_GET_CONTROLLER_TYPE; + cmd[1] = chn; + + n = gcn64_exchange(hdl, cmd, 2, cmd, sizeof(cmd)); + if (n<0) + return n; + if (n<3) + return -1; + + return cmd[2]; +} + +const char *gcn64lib_controllerName(int type) +{ + switch(type) { + case CTL_TYPE_NONE: return "No controller"; + case CTL_TYPE_N64: return "N64 Controller"; + case CTL_TYPE_GC: return "GC Controller"; + case CTL_TYPE_GCKB: return "GC Keyboard"; + default: + return "Unknown"; + } +} + +int gcn64lib_getSignature(gcn64_hdl_t hdl, char *dst, int dstmax) +{ + unsigned char cmd[40]; + int n; + + if (!hdl) { + return -1; + } + + if (dstmax <= 0) + return -1; + + cmd[0] = RQ_GCN64_GET_SIGNATURE; + + n = gcn64_exchange(hdl, cmd, 1, cmd, sizeof(cmd)); + if (n<0) + return n; + + dst[0] = 0; + if (n > 1) { + strncpy(dst, (char*)cmd+1, n); + } + dst[dstmax-1] = 0; + + return 0; +} + +int gcn64lib_forceVibration(gcn64_hdl_t hdl, unsigned char channel, unsigned char vibrate) +{ + unsigned char cmd[3]; + int n; + + if (!hdl) { + return -1; + } + + cmd[0] = RQ_GCN64_SET_VIBRATION; + cmd[1] = channel; + cmd[2] = vibrate; + + n = gcn64_exchange(hdl, cmd, 3, cmd, sizeof(cmd)); + if (n<0) + return n; + + return 0; +} + +int gcn64lib_rawSiCommand(gcn64_hdl_t hdl, unsigned char channel, unsigned char *tx, unsigned char tx_len, unsigned char *rx, unsigned char max_rx) +{ + unsigned char cmd[3 + tx_len]; + unsigned char rep[3 + 64]; + int cmdlen, rx_len, n; + + if (!hdl) { + return -1; + } + + if (!tx) { + return -1; + } + + cmd[0] = RQ_GCN64_RAW_SI_COMMAND; + cmd[1] = channel; + cmd[2] = tx_len; + memcpy(cmd+3, tx, tx_len); + cmdlen = 3 + tx_len; + + n = gcn64_exchange(hdl, cmd, cmdlen, rep, sizeof(rep)); + if (n<0) + return n; + + rx_len = rep[2]; + if (rx) { + memcpy(rx, rep + 3, rx_len); + } + + return rx_len; +} + +int gcn64lib_16bit_scan(gcn64_hdl_t hdl, unsigned short min, unsigned short max) +{ + int id, n; + unsigned char buf[64]; + + if (!hdl) { + return -1; + } + + for (id = min; id<=max; id++) { + buf[0] = id >> 8; + buf[1] = id & 0xff; + n = gcn64lib_rawSiCommand(hdl, 0, buf, 2, buf, sizeof(buf)); + if (n > 0) { + printf("CMD 0x%04x answer: ", id); + printHexBuf(buf, n); + } + } + + return 0; +} + +int gcn64lib_8bit_scan(gcn64_hdl_t hdl, unsigned char min, unsigned char max) +{ + int id, n; + unsigned char buf[64]; + + if (!hdl) { + return -1; + } + + for (id = min; id<=max; id++) { + buf[0] = id; + n = gcn64lib_rawSiCommand(hdl, 0, buf, 1, buf, sizeof(buf)); + if (n > 0) { + printf("CMD 0x%02x answer: ", id); + printHexBuf(buf, n); + } + } + + return 0; +} + +int gcn64lib_bootloader(gcn64_hdl_t hdl) +{ + unsigned char cmd[4]; + int cmdlen; + + if (!hdl) { + return -1; + } + + cmd[0] = RQ_GCN64_JUMP_TO_BOOTLOADER; + cmdlen = 1; + + gcn64_exchange(hdl, cmd, cmdlen, cmd, sizeof(cmd)); + + return 0; +} + +int gcn64lib_n64_expansionWrite(gcn64_hdl_t hdl, unsigned short addr, unsigned char *data, int len) +{ + unsigned char cmd[3 + len]; + int cmdlen; + int n; + + if (!hdl) { + return -1; + } + + cmd[0] = N64_EXPANSION_WRITE; + cmd[1] = addr>>8; // Address high byte + cmd[2] = addr&0xff; // Address low byte + memcpy(cmd + 3, data, len); + cmdlen = 3 + len; + + n = gcn64lib_rawSiCommand(hdl, 0, cmd, cmdlen, cmd, sizeof(cmd)); + if (n != 1) { + printf("expansion write returned != 1 (%d)\n", n); + return -1; + } + + return cmd[0]; +} + +int gcn64lib_n64_expansionRead(gcn64_hdl_t hdl, unsigned short addr, unsigned char *dst, int max_len) +{ + unsigned char cmd[3]; + int n; + + if (!hdl) { + return -1; + } + + cmd[0] = N64_EXPANSION_READ; + cmd[1] = addr>>8; // Address high byte + cmd[2] = addr&0xff; // Address low byte + + n = gcn64lib_rawSiCommand(hdl, 0, cmd, 3, dst, max_len); + if (n < 0) + return n; + + return n; +} + +static int gcn64lib_blockIO_compat(gcn64_hdl_t hdl, struct blockio_op *iops, int n_iops) +{ + int i; + int res; + + for (i=0; icaps.bio_support) { + return gcn64lib_blockIO_compat(hdl, iops, n_iops); + } + else { + // Request format: + // + // RQ_GCN64_BLOCK_IO + // chn, n_tx, n_rx, tx[] + // ... + // + // The adapter stops processing the buffer when the + // buffer ends or when a channel set to 0xff is encountered. + // + memset(iobuf, 0xff, sizeof(iobuf)); + iobuf[0] = RQ_GCN64_BLOCK_IO; + for (p=1, i=0; i sizeof(iobuf)) { + fprintf(stderr, "io blocks do not fit in buffer\n"); + return -1; + } + iobuf[p] = iops[i].chn; + p++; + iobuf[p] = iops[i].tx_len & BIO_RXTX_MASK; + p++; + iobuf[p] = iops[i].rx_len & BIO_RXTX_MASK; + p++; + + memcpy(iobuf + p, iops[i].tx_data, iops[i].tx_len); + p += iops[i].tx_len; + } +#ifdef DEBUG_BLOCKIO + fputs("blockIO request: ", stdout); + printHexBuf(iobuf, sizeof(iobuf)); +#endif + + n = gcn64_exchange(hdl, iobuf, sizeof(iobuf), iobuf, sizeof(iobuf)); + if (n < 0) { + return n; + } + if (n != sizeof(iobuf)) { + fprintf(stderr, "Unexpected iobuf reply size\n"); + return -1; + } +#ifdef DEBUG_BLOCKIO + fputs("blockIO answer.: ", stdout); + printHexBuf(iobuf, sizeof(iobuf)); + printf("\n"); +#endif + // Answer format: + // + // RQ_GCN64_BLOCK_IO + // n_rx, rx[n_rx] + // ... + // + // n_rx will have bits set according to the result. See BIO_RX*. rx will always + // occupy the number of bytes set in the request, regardless of the result. + // + if (iobuf[0] != RQ_GCN64_BLOCK_IO) { + fprintf(stderr, "Invalid iobuf reply\n"); + return -1; + } + + for (p=1,i=0; i= sizeof(iobuf)) { + fprintf(stderr, "blockIO: adapter reports too much received data\n"); + break; + } + + iops[i].rx_len = iobuf[p]; + p++; + if (p + (iops[i].rx_len & BIO_RXTX_MASK) >= sizeof(iobuf)) { + fprintf(stderr, "blockIO: adapter reports too much received data\n"); + break; + } + memcpy(iops[i].rx_data, iobuf + p, iops[i].rx_len & BIO_RXTX_MASK); + p += iops[i].rx_len & BIO_RXTX_MASK; + } + } + + return 0; +} + diff --git a/mupen64plus-input-raphnetraw/src/gcn64lib.h b/mupen64plus-input-raphnetraw/src/gcn64lib.h new file mode 100644 index 00000000..c06639ba --- /dev/null +++ b/mupen64plus-input-raphnetraw/src/gcn64lib.h @@ -0,0 +1,42 @@ +#ifndef _gcn64_lib_h__ +#define _gcn64_lib_h__ + +#include "gcn64.h" + +#define CTL_TYPE_NONE 0 +#define CTL_TYPE_N64 1 +#define CTL_TYPE_GC 2 +#define CTL_TYPE_GCKB 3 + +int gcn64lib_suspendPolling(gcn64_hdl_t hdl, unsigned char suspend); +int gcn64lib_setConfig(gcn64_hdl_t hdl, unsigned char param, unsigned char *data, unsigned char len); +int gcn64lib_getConfig(gcn64_hdl_t hdl, unsigned char param, unsigned char *rx, unsigned char rx_max); +int gcn64lib_rawSiCommand(gcn64_hdl_t hdl, unsigned char channel, unsigned char *tx, unsigned char tx_len, unsigned char *rx, unsigned char max_rx); +int gcn64lib_getVersion(gcn64_hdl_t hdl, char *dst, int dstmax); +int gcn64lib_getSignature(gcn64_hdl_t hdl, char *dst, int dstmax); +int gcn64lib_forceVibration(gcn64_hdl_t hdl, unsigned char channel, unsigned char vibrate); +int gcn64lib_getControllerType(gcn64_hdl_t hdl, int chn); +const char *gcn64lib_controllerName(int type); +int gcn64lib_bootloader(gcn64_hdl_t hdl); + +int gcn64lib_n64_expansionWrite(gcn64_hdl_t hdl, unsigned short addr, unsigned char *data, int len); +int gcn64lib_n64_expansionRead(gcn64_hdl_t hdl, unsigned short addr, unsigned char *dst, int max_len); + +int gcn64lib_8bit_scan(gcn64_hdl_t hdl, unsigned char min, unsigned char max); +int gcn64lib_16bit_scan(gcn64_hdl_t hdl, unsigned short min, unsigned short max); + +#define BIO_RXTX_MASK 0x3F +#define BIO_RX_LEN_TIMEDOUT 0x80 +#define BIO_RX_LEN_PARTIAL 0x40 + +struct blockio_op { + unsigned char chn; + unsigned char tx_len; + unsigned char *tx_data; + unsigned char rx_len; // expected/max bytes + unsigned char *rx_data; +}; + +int gcn64lib_blockIO(gcn64_hdl_t hdl, struct blockio_op *iops, int n_iops); + +#endif // _gcn64_lib_h__ diff --git a/mupen64plus-input-raphnetraw/src/hexdump.c b/mupen64plus-input-raphnetraw/src/hexdump.c new file mode 100644 index 00000000..4027f9a3 --- /dev/null +++ b/mupen64plus-input-raphnetraw/src/hexdump.c @@ -0,0 +1,29 @@ +/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware + Copyright (C) 2007-2015 Raphael Assenat + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#include + +void printHexBuf(unsigned char *buf, int n) +{ + int i; + + for (i=0; i +#include +#include + +#include "m64p_types.h" +#include "osal_dynamiclib.h" + +void * osal_dynlib_getproc(m64p_dynlib_handle LibHandle, const char *pccProcedureName) +{ + if (pccProcedureName == NULL) + return NULL; + + return dlsym(LibHandle, pccProcedureName); +} + + diff --git a/mupen64plus-input-raphnetraw/src/osal_dynamiclib_win32.c b/mupen64plus-input-raphnetraw/src/osal_dynamiclib_win32.c new file mode 100644 index 00000000..751e1b01 --- /dev/null +++ b/mupen64plus-input-raphnetraw/src/osal_dynamiclib_win32.c @@ -0,0 +1,74 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-ui-console - osal_dynamiclib_win32.c * + * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Copyright (C) 2009 Richard Goedeken * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include +#include + +#include "m64p_types.h" +#include "osal_dynamiclib.h" + +m64p_error osal_dynlib_open(m64p_dynlib_handle *pLibHandle, const char *pccLibraryPath) +{ + if (pLibHandle == NULL || pccLibraryPath == NULL) + return M64ERR_INPUT_ASSERT; + + *pLibHandle = LoadLibrary(pccLibraryPath); + + if (*pLibHandle == NULL) + { + char *pchErrMsg; + DWORD dwErr = GetLastError(); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwErr, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &pchErrMsg, 0, NULL); + fprintf(stderr, "LoadLibrary('%s') error: %s\n", pccLibraryPath, pchErrMsg); + LocalFree(pchErrMsg); + return M64ERR_INPUT_NOT_FOUND; + } + + return M64ERR_SUCCESS; +} + +void * osal_dynlib_getproc(m64p_dynlib_handle LibHandle, const char *pccProcedureName) +{ + if (pccProcedureName == NULL) + return NULL; + + return GetProcAddress(LibHandle, pccProcedureName); +} + +m64p_error osal_dynlib_close(m64p_dynlib_handle LibHandle) +{ + int rval = FreeLibrary(LibHandle); + + if (rval == 0) + { + char *pchErrMsg; + DWORD dwErr = GetLastError(); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwErr, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &pchErrMsg, 0, NULL); + fprintf(stderr, "FreeLibrary() error: %s\n", pchErrMsg); + LocalFree(pchErrMsg); + return M64ERR_INTERNAL; + } + + return M64ERR_SUCCESS; +} diff --git a/mupen64plus-input-raphnetraw/src/osal_preproc.h b/mupen64plus-input-raphnetraw/src/osal_preproc.h new file mode 100644 index 00000000..52a451ce --- /dev/null +++ b/mupen64plus-input-raphnetraw/src/osal_preproc.h @@ -0,0 +1,45 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus - osal_preproc.h * + * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Copyright (C) 2009 Richard Goedeken * + * Copyright (C) 2002 Hacktarux * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* this header file is for system-dependent #defines, #includes, and typedefs */ + +#if !defined(OSAL_PREPROC_H) +#define OSAL_PREPROC_H + +#if defined(WIN32) + #define strcasestr strstr + #define strcasecmp _stricmp +#endif // WIN32 + +#if defined(WIN32) && !defined(__MINGW32__) + + // macros + #define osal_inline __inline + +#else /* Not WIN32 */ + + // macros + #define osal_inline inline + +#endif + +#endif // OSAL_PREPROC_H diff --git a/mupen64plus-input-raphnetraw/src/plugin_back.c b/mupen64plus-input-raphnetraw/src/plugin_back.c new file mode 100644 index 00000000..7f81440e --- /dev/null +++ b/mupen64plus-input-raphnetraw/src/plugin_back.c @@ -0,0 +1,475 @@ +/* mupen64plus-input-raphnetraw + * + * Copyright (C) 2016-2017 Raphael Assenat + * + * An input plugin that lets the game under emulation communicate with + * the controllers directly using the direct controller communication + * feature of raphnet V3 adapters[1]. + * + * [1] http://www.raphnet.net/electronique/gcn64_usb_adapter_gen3/index_en.php + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * plugin_back.c : Plugin back code (in contrast to the "front" emulator interface + * + * Revision history: + * 28 Nov 2016 : Initial version + * 1 Dec 2016 : Switch to block IO api + * + */ + +#include +#include +#include "plugin_back.h" +#include "gcn64.h" +#include "gcn64lib.h" +#include "version.h" +#include "hexdump.h" + +#undef ENABLE_TIMING + +#ifdef ENABLE_TIMING +#include + +#if defined(__MINGW32__) || defined(__MINGW64__) +#error Timing not supported under Windows +#endif +#endif + +#undef TIME_RAW_IO +#undef TIME_COMMAND_TO_READ + +#undef TRACE_BLOCK_IO +// Filter +#define EXTENSION_RW_ONLY 0 + +#define MAX_ADAPTERS 4 +#define MAX_CHANNELS 4 + +static void nodebug(int l, const char *m, ...) { } + +static pb_debugFunc DebugMessage = nodebug; + + + +#define MAX_OPS 64 + +struct adapter { + gcn64_hdl_t handle; + struct gcn64_info inf; + struct blockio_op biops[MAX_OPS]; + int n_ops; +}; + +static int g_n_adapters = 0; +struct adapter g_adapters[MAX_ADAPTERS] = { }; + +struct rawChannel { + struct adapter *adapter; + int chn; +}; + +/* Multiple adapters are supported, some are single player, others + * two-player. As they are discovered during scan, their + * channels (corresponding to physical controller ports) are added + * to this table. Then, when plugin_front() calls functions in this + * files with a specified channel, the channel is the index in this + * table. + */ +static struct rawChannel g_channels[MAX_CHANNELS] = { }; +static int g_n_channels = 0; + +int pb_init(pb_debugFunc debugFn) +{ + DebugMessage = debugFn; + gcn64_init(1); + return 0; +} + +static void pb_freeAllAdapters(void) +{ + int i; + + for (i=0; iinf, lctx)) { + + adap->handle = gcn64_openDevice(&adap->inf); + if (!adap->handle) { + DebugMessage(PB_MSG_ERROR, "Could not open gcn64 device serial '%ls'. Skipping it.", adap->inf.str_serial); + continue; + } + + DebugMessage(PB_MSG_INFO, "Found USB device 0x%04x:0x%04x serial '%ls' name '%ls'", + adap->inf.usb_vid, adap->inf.usb_pid, adap->inf.str_serial, adap->inf.str_prodname); + DebugMessage(PB_MSG_INFO, "Adapter supports %d raw channel(s)", adap->inf.caps.n_raw_channels); + + g_n_adapters++; + if (g_n_adapters >= MAX_ADAPTERS) + break; + + adap = &g_adapters[g_n_adapters]; + } + gcn64_freeListCtx(lctx); + + /* Pass 2: Fill the g_channel[] array with the available raw channels. + * For instance, if there are adapters A, B and C (where A and C are single-player + * and B is dual-player), we get this: + * + * [0] = Adapter A, raw channel 0 + * [1] = Adapter B, raw channel 0 + * [2] = Adapter B, raw channel 1 + * [3] = Adapter C, raw channel 0 + * + * */ + for (i=0; iinf.caps.n_raw_channels <= 0) + continue; + + for (j=0; jinf.caps.n_raw_channels; j++) { + if (g_n_channels >= MAX_CHANNELS) { + return g_n_channels; + } + g_channels[g_n_channels].adapter = adap; + g_channels[g_n_channels].chn = j; + DebugMessage(PB_MSG_INFO, "Channel %d: Adapter '%ls' raw channel %d", g_n_channels, adap->inf.str_serial, j); + g_n_channels++; + } + } + + return g_n_channels; +} + +int pb_romOpen(void) +{ + int i; + + for (i=0; i 1 ? 's':' '); + +} + +static void debug_raw_commands_out(unsigned char *command, int channel_id) +{ + int result = command[1]; + int i; + + printf("chn %d: result: 0x%02x : ", channel_id, result); + if (0 == (command[1] & 0xC0)) { + for (i=0; ibiops; + + /* Skip adapters that do not have IO operations queued. */ + if (adap->n_ops <= 0) + continue; + +#ifdef TRACE_BLOCK_IO + for (i=0; in_ops; i++) { + if (EXTENSION_RW_ONLY && (biops[i].tx_data[0] < 0x02)) + continue; + op_was_extio[i] = 1; + printf("Before blockIO: op %d, chn: %d, : tx: 0x%02x, rx: 0x%02x, data: ", i, biops[i].chn, + biops[i].tx_len, biops[i].rx_len); + printHexBuf(biops[i].tx_data, biops[i].tx_len); + } +#endif + +#ifdef TIME_RAW_IO + timing(1, NULL); +#endif + res = gcn64lib_blockIO(adap->handle, biops, adap->n_ops); +#ifdef TIME_RAW_IO + timing(0, "blockIO"); +#endif + + if (res == 0) { + // biops rx_data pointed into PIFram so data is there. But we need + // to patch the RX length parameters (the two most significant bits + // are error bits such as timeout..) + for (i=0; in_ops; i++) { + // in PIFram, the read length is one byte before the tx data. A rare + // occasion to use a negative array index ;) + biops[i].tx_data[-1] = biops[i].rx_len; + +#ifdef TRACE_BLOCK_IO + if (EXTENSION_RW_ONLY && (!op_was_extio[i])) // Read + continue; + + printf("After blockIO: op %d, chn: %d, : tx: 0x%02x, rx: 0x%02x, data: ", i, biops[i].chn, + biops[i].tx_len, biops[i].rx_len); + if (biops[i].rx_len & BIO_RX_LEN_TIMEDOUT) { + printf("Timeout\n"); + } else if (biops[i].rx_len & BIO_RX_LEN_PARTIAL) { + printf("Incomplete\n"); + } else { + printHexBuf(biops[i].rx_data, biops[i].rx_len); + } +#endif + } + } else { + // For debugging + //exit(1); + } + + adap->n_ops = 0; + } + return 0; + + +} + +static int pb_commandIsValid(int Control, unsigned char *Command) +{ + if (Control < 0 || Control >= g_n_channels) { + DebugMessage(PB_MSG_WARNING, "pb_readController called with Control=%d", Control); + return 0; + } + + if (!Command) { + DebugMessage(PB_MSG_WARNING, "pb_readController called with NULL Command pointer"); + return 0; + } + + // When a CIC challenge took place in update_pif_write(), the pif ram + // contains a bunch 0xFF followed by 0x00 at offsets 46 and 47. Offset + // 48 onwards contains the challenge answer. + // + // Then when update_pif_read() is called, the 0xFF bytes are be skipped + // up to the two 0x00 bytes that increase the channel to 2. Then the + // challenge answer is (incorrectly) processed as if it were commands + // for the third controller. + // + // This cause issues with the raphnetraw plugin since it modifies pif ram + // to store the result or command error flags. This corrupts the reponse + // and leads to challenge failure. + // + // As I know of no controller commands above 0x03, the filter below guards + // against this condition... + // + if (Control == 2 && Command[2] > 0x03) { + DebugMessage(PB_MSG_WARNING, "Invalid controller command"); + return 0; + } + + // When Mario Kart 64 uses a controller pak, such PIF ram content + // occurs: + // + // ff 03 21 02 01 f7 ff ff + // ff ff ff ff ff ff ff ff + // ff ff ff ff ff ff ff ff + // ff ff ff ff ff ff ff ff + // ff ff ff ff ff ff ff 21 + // fe 00 00 00 00 00 00 00 + // 00 00 00 00 00 00 00 00 + // 00 00 00 00 00 00 00 00 + // + // It results in this: + // - Transmission of 3 bytes with a 33 bytes return on channel 0, + // - Transmission of 33 bytes with 254 bytes in return on channel 1!? + // + // Obviously the second transaction is an error. The 0xFE (254) that follows + // is where processing should actually stop. This happens to be an invalid length detectable + // by looking at the two most significant bits.. + // + if (Command[0] == 0xFE && Command[1] == 0x00) { + DebugMessage(PB_MSG_WARNING, "Ignoring invalid io operation (T: 0x%02x, R: 0x%02x)", + Command[0], Command[1]); + return 0; + } + + return 1; +} + +int pb_readController(int Control, unsigned char *Command) +{ + struct rawChannel *channel; + struct adapter *adap; + struct blockio_op *biops; + + // Called with -1 at the end of PIF ram. + if (Control == -1) { + return pb_performIo(); + } + + /* Check for out of bounds Control parameter, for + * NULL Command and filter various invalid conditions. */ + if (!pb_commandIsValid(Control, Command)) { + return 0; + } + + /* Add the IO operation to the block io list of + * the adapter serving this channel. */ + + channel = &g_channels[Control]; + adap = channel->adapter; + + if (adap->n_ops >= MAX_OPS) { + DebugMessage(PB_MSG_ERROR, "Too many io ops"); + } else { + biops = adap->biops; + + biops[adap->n_ops].chn = channel->chn; // Control; + biops[adap->n_ops].tx_len = Command[0] & BIO_RXTX_MASK; + biops[adap->n_ops].rx_len = Command[1] & BIO_RXTX_MASK; + biops[adap->n_ops].tx_data = Command + 2; + biops[adap->n_ops].rx_data = Command + 2 + biops[adap->n_ops].tx_len; + + if (biops[adap->n_ops].tx_len == 0 || biops[adap->n_ops].rx_len == 0) { + DebugMessage(PB_MSG_WARNING, "TX or RX was zero"); + return 0; + } + + adap->n_ops++; + } + + return 0; +} diff --git a/mupen64plus-input-raphnetraw/src/plugin_back.h b/mupen64plus-input-raphnetraw/src/plugin_back.h new file mode 100644 index 00000000..d2aa859e --- /dev/null +++ b/mupen64plus-input-raphnetraw/src/plugin_back.h @@ -0,0 +1,22 @@ +#ifndef _plugin_back_h__ +#define _plugin_back_h__ + +/* Those match mupen64plus levels */ +#define PB_MSG_ERROR 1 +#define PB_MSG_WARNING 2 +#define PB_MSG_INFO 3 +#define PB_MSG_STATUS 4 +#define PB_MSG_VERBOSE 5 + +typedef void (*pb_debugFunc)(int level, const char *message, ...); + +int pb_init(pb_debugFunc debugFn); +int pb_shutdown(void); +int pb_scanControllers(void); +int pb_readController(int Control, unsigned char *Command); +int pb_controllerCommand(int Control, unsigned char *Command); +int pb_romOpen(void); +int pb_romClosed(void); + +#endif // _plugin_back_h__ + diff --git a/mupen64plus-input-raphnetraw/src/plugin_front.c b/mupen64plus-input-raphnetraw/src/plugin_front.c new file mode 100644 index 00000000..28bbac7b --- /dev/null +++ b/mupen64plus-input-raphnetraw/src/plugin_front.c @@ -0,0 +1,331 @@ +/* mupen64plus-input-raphnetraw + * + * Copyright (C) 2016 Raphael Assenat + * + * An input plugin that lets the game under emulation communicate with + * the controllers directly using the direct controller communication + * feature of raphnet V3 adapters[1]. + * + * [1] http://www.raphnet.net/electronique/gcn64_usb_adapter_gen3/index_en.php + * + * Based on the Mupen64plus-input-sdl plugin (original banner below) + */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-input-sdl - plugin.c * + * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Copyright (C) 2008-2011 Richard Goedeken * + * Copyright (C) 2008 Tillin9 * + * Copyright (C) 2002 Blight * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include +#include +#include +#include + +#define M64P_PLUGIN_PROTOTYPES 1 +#include "config.h" +#include "m64p_common.h" +#include "m64p_config.h" +#include "m64p_plugin.h" +#include "m64p_types.h" +#include "osal_dynamiclib.h" +#include "plugin_front.h" +#include "plugin_back.h" +#include "version.h" + + +#ifdef __linux__ +#endif /* __linux__ */ + +#include + +#define MAX_CONTROLLERS 4 + +#ifdef PORTS_1_AND_4 +static int emu2adap_portmap[MAX_CONTROLLERS] = { 0, 2, 3, 1 }; +#undef PLUGIN_NAME +#define PLUGIN_NAME "raphnetraw ports 1 and 4" +#else +static int emu2adap_portmap[MAX_CONTROLLERS] = { 0, 1, 2, 3 }; +#endif + +#define EMU_2_ADAP_PORT(a) ((a) == -1 ? -1 : emu2adap_portmap[a]) + +#if 0 +/* definitions of pointers to Core config functions */ +ptr_ConfigOpenSection ConfigOpenSection = NULL; +ptr_ConfigDeleteSection ConfigDeleteSection = NULL; +ptr_ConfigSaveSection ConfigSaveSection = NULL; +ptr_ConfigListParameters ConfigListParameters = NULL; +ptr_ConfigSaveFile ConfigSaveFile = NULL; +ptr_ConfigSetParameter ConfigSetParameter = NULL; +ptr_ConfigGetParameter ConfigGetParameter = NULL; +ptr_ConfigGetParameterHelp ConfigGetParameterHelp = NULL; +ptr_ConfigSetDefaultInt ConfigSetDefaultInt = NULL; +ptr_ConfigSetDefaultFloat ConfigSetDefaultFloat = NULL; +ptr_ConfigSetDefaultBool ConfigSetDefaultBool = NULL; +ptr_ConfigSetDefaultString ConfigSetDefaultString = NULL; +ptr_ConfigGetParamInt ConfigGetParamInt = NULL; +ptr_ConfigGetParamFloat ConfigGetParamFloat = NULL; +ptr_ConfigGetParamBool ConfigGetParamBool = NULL; +ptr_ConfigGetParamString ConfigGetParamString = NULL; + +ptr_ConfigGetSharedDataFilepath ConfigGetSharedDataFilepath = NULL; +ptr_ConfigGetUserConfigPath ConfigGetUserConfigPath = NULL; +ptr_ConfigGetUserDataPath ConfigGetUserDataPath = NULL; +ptr_ConfigGetUserCachePath ConfigGetUserCachePath = NULL; +#endif + +/* static data definitions */ +static void (*l_DebugCallback)(void *, int, const char *) = NULL; +static void *l_DebugCallContext = NULL; +static int l_PluginInit = 0; + +/* Global functions */ +static void DebugMessage(int level, const char *message, ...) +{ + char msgbuf[1024]; + va_list args; + + if (!l_DebugCallback) { + printf("No debug callback!\n"); + return; + } + + va_start(args, message); + vsnprintf(msgbuf, sizeof(msgbuf), message, args); + + (*l_DebugCallback)(l_DebugCallContext, level, msgbuf); + + va_end(args); +} + + +/* Mupen64Plus plugin functions */ +EXPORT m64p_error CALL PluginStartup(m64p_dynlib_handle CoreLibHandle, void *Context, + void (*DebugCallback)(void *, int, const char *)) +{ + ptr_CoreGetAPIVersions CoreAPIVersionFunc; + + int ConfigAPIVersion, DebugAPIVersion, VidextAPIVersion; + + if (l_PluginInit) { + return M64ERR_ALREADY_INIT; + } + + l_DebugCallback = DebugCallback; + l_DebugCallContext = Context; + + /* attach and call the CoreGetAPIVersions function, check Config API version for compatibility */ + CoreAPIVersionFunc = (ptr_CoreGetAPIVersions) osal_dynlib_getproc(CoreLibHandle, "CoreGetAPIVersions"); + if (CoreAPIVersionFunc == NULL) + { + DebugMessage(M64MSG_ERROR, "Core emulator broken; no CoreAPIVersionFunc() function found."); + return M64ERR_INCOMPATIBLE; + } + + (*CoreAPIVersionFunc)(&ConfigAPIVersion, &DebugAPIVersion, &VidextAPIVersion, NULL); + if ((ConfigAPIVersion & 0xffff0000) != (CONFIG_API_VERSION & 0xffff0000) || ConfigAPIVersion < CONFIG_API_VERSION) + { + DebugMessage(M64MSG_ERROR, "Emulator core Config API (v%i.%i.%i) incompatible with plugin (v%i.%i.%i)", + VERSION_PRINTF_SPLIT(ConfigAPIVersion), VERSION_PRINTF_SPLIT(CONFIG_API_VERSION)); + return M64ERR_INCOMPATIBLE; + } +#if 0 + /* Get the core config function pointers from the library handle */ + ConfigOpenSection = (ptr_ConfigOpenSection) osal_dynlib_getproc(CoreLibHandle, "ConfigOpenSection"); + ConfigDeleteSection = (ptr_ConfigDeleteSection) osal_dynlib_getproc(CoreLibHandle, "ConfigDeleteSection"); + ConfigSaveFile = (ptr_ConfigSaveFile) osal_dynlib_getproc(CoreLibHandle, "ConfigSaveFile"); + ConfigSaveSection = (ptr_ConfigSaveSection) osal_dynlib_getproc(CoreLibHandle, "ConfigSaveSection"); + ConfigListParameters = (ptr_ConfigListParameters) osal_dynlib_getproc(CoreLibHandle, "ConfigListParameters"); + ConfigSetParameter = (ptr_ConfigSetParameter) osal_dynlib_getproc(CoreLibHandle, "ConfigSetParameter"); + ConfigGetParameter = (ptr_ConfigGetParameter) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParameter"); + ConfigSetDefaultInt = (ptr_ConfigSetDefaultInt) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultInt"); + ConfigSetDefaultFloat = (ptr_ConfigSetDefaultFloat) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultFloat"); + ConfigSetDefaultBool = (ptr_ConfigSetDefaultBool) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultBool"); + ConfigSetDefaultString = (ptr_ConfigSetDefaultString) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultString"); + ConfigGetParamInt = (ptr_ConfigGetParamInt) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamInt"); + ConfigGetParamFloat = (ptr_ConfigGetParamFloat) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamFloat"); + ConfigGetParamBool = (ptr_ConfigGetParamBool) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamBool"); + ConfigGetParamString = (ptr_ConfigGetParamString) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamString"); + + ConfigGetSharedDataFilepath = (ptr_ConfigGetSharedDataFilepath) osal_dynlib_getproc(CoreLibHandle, "ConfigGetSharedDataFilepath"); + ConfigGetUserConfigPath = (ptr_ConfigGetUserConfigPath) osal_dynlib_getproc(CoreLibHandle, "ConfigGetUserConfigPath"); + ConfigGetUserDataPath = (ptr_ConfigGetUserDataPath) osal_dynlib_getproc(CoreLibHandle, "ConfigGetUserDataPath"); + ConfigGetUserCachePath = (ptr_ConfigGetUserCachePath) osal_dynlib_getproc(CoreLibHandle, "ConfigGetUserCachePath"); + + if (!ConfigOpenSection || !ConfigDeleteSection || !ConfigSaveFile || !ConfigSaveSection || !ConfigSetParameter || !ConfigGetParameter || + !ConfigSetDefaultInt || !ConfigSetDefaultFloat || !ConfigSetDefaultBool || !ConfigSetDefaultString || + !ConfigGetParamInt || !ConfigGetParamFloat || !ConfigGetParamBool || !ConfigGetParamString || + !ConfigGetSharedDataFilepath || !ConfigGetUserConfigPath || !ConfigGetUserDataPath || !ConfigGetUserCachePath) + { + DebugMessage(M64MSG_ERROR, "Couldn't connect to Core configuration functions"); + return M64ERR_INCOMPATIBLE; + } +#endif + + pb_init(DebugMessage); + + l_PluginInit = 1; + return M64ERR_SUCCESS; +} + +EXPORT m64p_error CALL PluginShutdown(void) +{ + if (!l_PluginInit) { + return M64ERR_NOT_INIT; + } + + /* reset some local variables */ + l_DebugCallback = NULL; + l_DebugCallContext = NULL; + + pb_shutdown(); + + l_PluginInit = 0; + + return M64ERR_SUCCESS; +} + +EXPORT m64p_error CALL PluginGetVersion(m64p_plugin_type *PluginType, int *PluginVersion, int *APIVersion, const char **PluginNamePtr, int *Capabilities) +{ + /* set version info */ + if (PluginType != NULL) + *PluginType = M64PLUGIN_INPUT; + + if (PluginVersion != NULL) + *PluginVersion = PLUGIN_VERSION; + + if (APIVersion != NULL) + *APIVersion = INPUT_PLUGIN_API_VERSION; + + if (PluginNamePtr != NULL) + *PluginNamePtr = PLUGIN_NAME; + + if (Capabilities != NULL) + { + *Capabilities = 0; + } + + return M64ERR_SUCCESS; +} + +/****************************************************************** + Function: InitiateControllers + Purpose: This function initialises how each of the controllers + should be handled. + input: - The handle to the main window. + - A controller structure that needs to be filled for + the emulator to know how to handle each controller. + output: none +*******************************************************************/ +EXPORT void CALL InitiateControllers(CONTROL_INFO ControlInfo) +{ + int i, n_controllers, adap_port; + + n_controllers = pb_scanControllers(); + + if (n_controllers <= 0) { + DebugMessage(PB_MSG_ERROR, "No adapters detected\n"); + return; + } + + for (i=0; i + +#define M64P_PLUGIN_PROTOTYPES 1 +#include "m64p_config.h" +#include "m64p_plugin.h" + +#define DEVICE_NO_JOYSTICK (-1) + +// Some stuff from n-rage plugin +#define RD_GETSTATUS 0x00 // get status +#define RD_READKEYS 0x01 // read button values +#define RD_READPAK 0x02 // read from controllerpack +#define RD_WRITEPAK 0x03 // write to controllerpack +#define RD_RESETCONTROLLER 0xff // reset controller +#define RD_READEEPROM 0x04 // read eeprom +#define RD_WRITEEPROM 0x05 // write eeprom + +#define PAK_IO_RUMBLE 0xC000 // the address where rumble-commands are sent to + +/* declarations of pointers to Core config functions */ +extern ptr_ConfigListSections ConfigListSections; +extern ptr_ConfigOpenSection ConfigOpenSection; +extern ptr_ConfigDeleteSection ConfigDeleteSection; +extern ptr_ConfigListParameters ConfigListParameters; +extern ptr_ConfigSaveFile ConfigSaveFile; +extern ptr_ConfigSaveSection ConfigSaveSection; +extern ptr_ConfigSetParameter ConfigSetParameter; +extern ptr_ConfigGetParameter ConfigGetParameter; +extern ptr_ConfigGetParameterHelp ConfigGetParameterHelp; +extern ptr_ConfigSetDefaultInt ConfigSetDefaultInt; +extern ptr_ConfigSetDefaultFloat ConfigSetDefaultFloat; +extern ptr_ConfigSetDefaultBool ConfigSetDefaultBool; +extern ptr_ConfigSetDefaultString ConfigSetDefaultString; +extern ptr_ConfigGetParamInt ConfigGetParamInt; +extern ptr_ConfigGetParamFloat ConfigGetParamFloat; +extern ptr_ConfigGetParamBool ConfigGetParamBool; +extern ptr_ConfigGetParamString ConfigGetParamString; + +extern ptr_ConfigGetSharedDataFilepath ConfigGetSharedDataFilepath; +extern ptr_ConfigGetUserConfigPath ConfigGetUserConfigPath; +extern ptr_ConfigGetUserDataPath ConfigGetUserDataPath; +extern ptr_ConfigGetUserCachePath ConfigGetUserCachePath; + +#endif // __PLUGIN_H__ + diff --git a/mupen64plus-input-raphnetraw/src/requests.h b/mupen64plus-input-raphnetraw/src/requests.h new file mode 100644 index 00000000..9eccf0d1 --- /dev/null +++ b/mupen64plus-input-raphnetraw/src/requests.h @@ -0,0 +1,38 @@ +#ifndef _gcn64_requests_h__ +#define _gcn64_requests_h__ + +/* Commands */ +#define RQ_GCN64_SET_CONFIG_PARAM 0x01 +#define RQ_GCN64_GET_CONFIG_PARAM 0x02 +#define RQ_GCN64_SUSPEND_POLLING 0x03 +#define RQ_GCN64_GET_VERSION 0x04 +#define RQ_GCN64_GET_SIGNATURE 0x05 +#define RQ_GCN64_GET_CONTROLLER_TYPE 0x06 +#define RQ_GCN64_SET_VIBRATION 0x07 +#define RQ_GCN64_RAW_SI_COMMAND 0x80 +#define RQ_GCN64_BLOCK_IO 0x81 +#define RQ_GCN64_JUMP_TO_BOOTLOADER 0xFF + +/* Configuration parameters and constants */ +#define CFG_PARAM_MODE 0x00 + +/* Values for mode */ +#define CFG_MODE_STANDARD 0x00 +#define CFG_MODE_N64_ONLY 0x01 +#define CFG_MODE_GC_ONLY 0x02 + +#define CFG_PARAM_SERIAL 0x01 + +#define CFG_PARAM_POLL_INTERVAL0 0x10 +#define CFG_PARAM_POLL_INTERVAL1 0x11 +#define CFG_PARAM_POLL_INTERVAL2 0x12 +#define CFG_PARAM_POLL_INTERVAL3 0x13 + +#define CFG_PARAM_N64_SQUARE 0x20 // Not implemented +#define CFG_PARAM_GC_MAIN_SQUARE 0x21 // Not implemented +#define CFG_PARAM_GC_CSTICK_SQUARE 0x22 // Not implemented +#define CFG_PARAM_FULL_SLIDERS 0x23 +#define CFG_PARAM_INVERT_TRIG 0x24 + + +#endif diff --git a/mupen64plus-input-raphnetraw/src/version.h b/mupen64plus-input-raphnetraw/src/version.h new file mode 100644 index 00000000..ed73deca --- /dev/null +++ b/mupen64plus-input-raphnetraw/src/version.h @@ -0,0 +1,38 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-input-sdl - version.h * + * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Copyright (C) 2009-2012 Richard Goedeken * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* This header file is for versioning information + * + */ + +#if !defined(VERSION_H) +#define VERSION_H + +#define PLUGIN_NAME "Mupen64Plus raphnetraw Input Plugin" +#define PLUGIN_VERSION 0x010100 +//#define INPUT_PLUGIN_API_VERSION 0x020000 +#define INPUT_PLUGIN_API_VERSION 0x020100 +#define CONFIG_API_VERSION 0x020100 + +#define VERSION_PRINTF_SPLIT(x) (((x) >> 16) & 0xffff), (((x) >> 8) & 0xff), ((x) & 0xff) + +#endif /* #define VERSION_H */ + From c472e801b2c5c3279075e5c65b2062ad720c98a6 Mon Sep 17 00:00:00 2001 From: CEnnis91 Date: Sun, 4 Jul 2021 14:38:09 -0400 Subject: [PATCH 02/10] git subrepo clone https://github.com/libusb/hidapi.git subrepo: subdir: "hidapi" merged: "382138e" upstream: origin: "https://github.com/libusb/hidapi.git" branch: "master" commit: "382138e" git-subrepo: version: "0.4.3" origin: "???" commit: "???" --- hidapi/.appveyor.yml | 33 + hidapi/.builds/alpine.yml | 25 + hidapi/.builds/archlinux.yml | 24 + hidapi/.builds/fedora-mingw.yml | 22 + hidapi/.builds/freebsd.yml | 23 + hidapi/.cirrus.yml | 33 + hidapi/.gitattributes | 7 + hidapi/.github/workflows/builds.yml | 148 + hidapi/.gitignore | 24 + hidapi/.gitrepo | 12 + hidapi/AUTHORS.txt | 18 + hidapi/BUILD.autotools.md | 106 + hidapi/BUILD.cmake.md | 227 ++ hidapi/BUILD.md | 122 + hidapi/CMakeLists.txt | 61 + hidapi/HACKING.txt | 10 + hidapi/LICENSE-bsd.txt | 26 + hidapi/LICENSE-gpl3.txt | 674 +++++ hidapi/LICENSE-orig.txt | 9 + hidapi/LICENSE.txt | 13 + hidapi/Makefile.am | 85 + hidapi/README.md | 182 ++ hidapi/VERSION | 1 + hidapi/android/jni/Android.mk | 19 + hidapi/bootstrap | 2 + hidapi/configure.ac | 251 ++ hidapi/dist/hidapi.podspec | 31 + hidapi/documentation/cmake-gui-drop-down.png | Bin 0 -> 22316 bytes hidapi/documentation/cmake-gui-highlights.png | Bin 0 -> 77327 bytes hidapi/doxygen/Doxyfile | 2482 +++++++++++++++++ hidapi/hidapi/hidapi.h | 498 ++++ hidapi/hidtest/.gitignore | 17 + hidapi/hidtest/CMakeLists.txt | 23 + hidapi/hidtest/Makefile.am | 20 + hidapi/hidtest/test.c | 201 ++ hidapi/libusb/.gitignore | 8 + hidapi/libusb/CMakeLists.txt | 42 + hidapi/libusb/Makefile-manual | 22 + hidapi/libusb/Makefile.am | 34 + hidapi/libusb/Makefile.freebsd | 39 + hidapi/libusb/Makefile.haiku | 39 + hidapi/libusb/Makefile.linux | 42 + hidapi/libusb/hid.c | 1616 +++++++++++ hidapi/linux/.gitignore | 18 + hidapi/linux/CMakeLists.txt | 38 + hidapi/linux/Makefile-manual | 42 + hidapi/linux/Makefile.am | 10 + hidapi/linux/hid.c | 1130 ++++++++ hidapi/m4/.gitignore | 5 + hidapi/m4/ax_pthread.m4 | 309 ++ hidapi/m4/pkg.m4 | 157 ++ hidapi/mac/.gitignore | 17 + hidapi/mac/CMakeLists.txt | 46 + hidapi/mac/Makefile-manual | 27 + hidapi/mac/Makefile.am | 9 + hidapi/mac/hid.c | 1233 ++++++++ hidapi/pc/.gitignore | 1 + hidapi/pc/hidapi-hidraw.pc.in | 11 + hidapi/pc/hidapi-libusb.pc.in | 11 + hidapi/pc/hidapi.pc.in | 11 + hidapi/src/CMakeLists.txt | 167 ++ hidapi/src/cmake/hidapi-config.cmake.in | 52 + hidapi/testgui/.gitignore | 20 + hidapi/testgui/Makefile-manual | 26 + hidapi/testgui/Makefile.am | 43 + hidapi/testgui/Makefile.freebsd | 33 + hidapi/testgui/Makefile.linux | 32 + hidapi/testgui/Makefile.mac | 46 + hidapi/testgui/Makefile.mingw | 32 + .../TestGUI.app.in/Contents/Info.plist | 28 + .../testgui/TestGUI.app.in/Contents/PkgInfo | 1 + .../Resources/English.lproj/InfoPlist.strings | Bin 0 -> 92 bytes .../Contents/Resources/Signal11.icns | Bin 0 -> 21918 bytes hidapi/testgui/copy_to_bundle.sh | 98 + hidapi/testgui/mac_support.h | 17 + hidapi/testgui/mac_support_cocoa.m | 103 + hidapi/testgui/test.cpp | 532 ++++ hidapi/testgui/testgui.sln | 20 + hidapi/testgui/testgui.vcproj | 217 ++ hidapi/udev/69-hid.rules | 33 + hidapi/windows/.gitignore | 17 + hidapi/windows/CMakeLists.txt | 32 + hidapi/windows/Makefile-manual | 14 + hidapi/windows/Makefile.am | 16 + hidapi/windows/Makefile.mingw | 30 + hidapi/windows/ddk_build/.gitignore | 2 + hidapi/windows/ddk_build/hidapi.def | 18 + hidapi/windows/ddk_build/makefile | 49 + hidapi/windows/ddk_build/sources | 23 + hidapi/windows/hid.c | 1074 +++++++ hidapi/windows/hidapi.sln | 41 + hidapi/windows/hidapi.vcproj | 201 ++ hidapi/windows/hidapi.vcxproj | 176 ++ hidapi/windows/hidapi.vcxproj.filters | 27 + hidapi/windows/hidtest.vcproj | 196 ++ hidapi/windows/hidtest.vcxproj | 172 ++ hidapi/windows/hidtest.vcxproj.filters | 22 + 97 files changed, 13956 insertions(+) create mode 100644 hidapi/.appveyor.yml create mode 100644 hidapi/.builds/alpine.yml create mode 100644 hidapi/.builds/archlinux.yml create mode 100644 hidapi/.builds/fedora-mingw.yml create mode 100644 hidapi/.builds/freebsd.yml create mode 100644 hidapi/.cirrus.yml create mode 100644 hidapi/.gitattributes create mode 100644 hidapi/.github/workflows/builds.yml create mode 100644 hidapi/.gitignore create mode 100644 hidapi/.gitrepo create mode 100644 hidapi/AUTHORS.txt create mode 100644 hidapi/BUILD.autotools.md create mode 100644 hidapi/BUILD.cmake.md create mode 100644 hidapi/BUILD.md create mode 100644 hidapi/CMakeLists.txt create mode 100644 hidapi/HACKING.txt create mode 100644 hidapi/LICENSE-bsd.txt create mode 100644 hidapi/LICENSE-gpl3.txt create mode 100644 hidapi/LICENSE-orig.txt create mode 100644 hidapi/LICENSE.txt create mode 100644 hidapi/Makefile.am create mode 100644 hidapi/README.md create mode 100644 hidapi/VERSION create mode 100644 hidapi/android/jni/Android.mk create mode 100755 hidapi/bootstrap create mode 100644 hidapi/configure.ac create mode 100644 hidapi/dist/hidapi.podspec create mode 100644 hidapi/documentation/cmake-gui-drop-down.png create mode 100644 hidapi/documentation/cmake-gui-highlights.png create mode 100644 hidapi/doxygen/Doxyfile create mode 100644 hidapi/hidapi/hidapi.h create mode 100644 hidapi/hidtest/.gitignore create mode 100644 hidapi/hidtest/CMakeLists.txt create mode 100644 hidapi/hidtest/Makefile.am create mode 100644 hidapi/hidtest/test.c create mode 100644 hidapi/libusb/.gitignore create mode 100644 hidapi/libusb/CMakeLists.txt create mode 100644 hidapi/libusb/Makefile-manual create mode 100644 hidapi/libusb/Makefile.am create mode 100644 hidapi/libusb/Makefile.freebsd create mode 100644 hidapi/libusb/Makefile.haiku create mode 100644 hidapi/libusb/Makefile.linux create mode 100644 hidapi/libusb/hid.c create mode 100644 hidapi/linux/.gitignore create mode 100644 hidapi/linux/CMakeLists.txt create mode 100644 hidapi/linux/Makefile-manual create mode 100644 hidapi/linux/Makefile.am create mode 100644 hidapi/linux/hid.c create mode 100644 hidapi/m4/.gitignore create mode 100644 hidapi/m4/ax_pthread.m4 create mode 100644 hidapi/m4/pkg.m4 create mode 100644 hidapi/mac/.gitignore create mode 100644 hidapi/mac/CMakeLists.txt create mode 100644 hidapi/mac/Makefile-manual create mode 100644 hidapi/mac/Makefile.am create mode 100644 hidapi/mac/hid.c create mode 100644 hidapi/pc/.gitignore create mode 100644 hidapi/pc/hidapi-hidraw.pc.in create mode 100644 hidapi/pc/hidapi-libusb.pc.in create mode 100644 hidapi/pc/hidapi.pc.in create mode 100644 hidapi/src/CMakeLists.txt create mode 100644 hidapi/src/cmake/hidapi-config.cmake.in create mode 100644 hidapi/testgui/.gitignore create mode 100644 hidapi/testgui/Makefile-manual create mode 100644 hidapi/testgui/Makefile.am create mode 100644 hidapi/testgui/Makefile.freebsd create mode 100644 hidapi/testgui/Makefile.linux create mode 100644 hidapi/testgui/Makefile.mac create mode 100644 hidapi/testgui/Makefile.mingw create mode 100644 hidapi/testgui/TestGUI.app.in/Contents/Info.plist create mode 100644 hidapi/testgui/TestGUI.app.in/Contents/PkgInfo create mode 100644 hidapi/testgui/TestGUI.app.in/Contents/Resources/English.lproj/InfoPlist.strings create mode 100644 hidapi/testgui/TestGUI.app.in/Contents/Resources/Signal11.icns create mode 100755 hidapi/testgui/copy_to_bundle.sh create mode 100644 hidapi/testgui/mac_support.h create mode 100644 hidapi/testgui/mac_support_cocoa.m create mode 100644 hidapi/testgui/test.cpp create mode 100644 hidapi/testgui/testgui.sln create mode 100644 hidapi/testgui/testgui.vcproj create mode 100644 hidapi/udev/69-hid.rules create mode 100644 hidapi/windows/.gitignore create mode 100644 hidapi/windows/CMakeLists.txt create mode 100644 hidapi/windows/Makefile-manual create mode 100644 hidapi/windows/Makefile.am create mode 100644 hidapi/windows/Makefile.mingw create mode 100644 hidapi/windows/ddk_build/.gitignore create mode 100644 hidapi/windows/ddk_build/hidapi.def create mode 100644 hidapi/windows/ddk_build/makefile create mode 100644 hidapi/windows/ddk_build/sources create mode 100644 hidapi/windows/hid.c create mode 100644 hidapi/windows/hidapi.sln create mode 100644 hidapi/windows/hidapi.vcproj create mode 100644 hidapi/windows/hidapi.vcxproj create mode 100644 hidapi/windows/hidapi.vcxproj.filters create mode 100644 hidapi/windows/hidtest.vcproj create mode 100644 hidapi/windows/hidtest.vcxproj create mode 100644 hidapi/windows/hidtest.vcxproj.filters diff --git a/hidapi/.appveyor.yml b/hidapi/.appveyor.yml new file mode 100644 index 00000000..b7e21c76 --- /dev/null +++ b/hidapi/.appveyor.yml @@ -0,0 +1,33 @@ +os: Visual Studio 2015 + +environment: + matrix: + - BUILD_ENV: msbuild + arch: x64 + - BUILD_ENV: msbuild + arch: Win32 + - BUILD_ENV: cygwin + +install: + - cmd: if %BUILD_ENV%==cygwin ( + C:\cygwin64\setup-x86_64.exe --quiet-mode --no-shortcuts --upgrade-also --packages autoconf,automake ) + +build_script: + - cmd: if %BUILD_ENV%==msbuild ( + msbuild .\windows\hidapi.sln /p:Configuration=Release /p:Platform=%arch% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" ) + - cmd: if %BUILD_ENV%==cygwin ( + C:\cygwin64\bin\bash -exlc "cd $APPVEYOR_BUILD_FOLDER; ./bootstrap; ./configure; make" ) + +artifacts: + # Win32 artifacts + - path: .\windows\Release\hidapi.dll + - path: .\windows\Release\hidapi.lib + - path: .\windows\Release\hidapi.pdb + - path: .\windows\Release\hidtest.exe + - path: .\windows\Release\hidtest.pdb + # x64 artifacts + - path: .\windows\x64\Release\hidapi.dll + - path: .\windows\x64\Release\hidapi.lib + - path: .\windows\x64\Release\hidapi.pdb + - path: .\windows\x64\Release\hidtest.exe + - path: .\windows\x64\Release\hidtest.pdb diff --git a/hidapi/.builds/alpine.yml b/hidapi/.builds/alpine.yml new file mode 100644 index 00000000..9477671a --- /dev/null +++ b/hidapi/.builds/alpine.yml @@ -0,0 +1,25 @@ +image: alpine/edge +packages: +- autoconf +- automake +- libtool +- eudev-dev +- libusb-dev +- linux-headers +sources: +- https://github.com/libusb/hidapi +tasks: +- setup: | + cd hidapi + ./bootstrap + ./configure +- build: | + cd hidapi + make + make DESTDIR=$PWD/root install + make clean +- build-manual: | + cd hidapi/linux + make -f Makefile-manual + cd ../libusb + make -f Makefile-manual diff --git a/hidapi/.builds/archlinux.yml b/hidapi/.builds/archlinux.yml new file mode 100644 index 00000000..0f733100 --- /dev/null +++ b/hidapi/.builds/archlinux.yml @@ -0,0 +1,24 @@ +image: archlinux +packages: +- autoconf +- automake +- libtool +- libusb +- libudev0 +sources: +- https://github.com/libusb/hidapi +tasks: +- setup: | + cd hidapi + ./bootstrap + ./configure +- build: | + cd hidapi + make + make DESTDIR=$PWD/root install + make clean +- build-manual: | + cd hidapi/linux + make -f Makefile-manual + cd ../libusb + make -f Makefile-manual diff --git a/hidapi/.builds/fedora-mingw.yml b/hidapi/.builds/fedora-mingw.yml new file mode 100644 index 00000000..4effecf3 --- /dev/null +++ b/hidapi/.builds/fedora-mingw.yml @@ -0,0 +1,22 @@ +image: fedora/latest +packages: +- autoconf +- automake +- libtool +- mingw64-gcc +- mingw64-gcc-c++ +sources: +- https://github.com/libusb/hidapi +tasks: +- setup: | + cd hidapi + ./bootstrap + mingw64-configure +- build: | + cd hidapi + make + make DESTDIR=$PWD/root install + make clean +- build-manual: | + cd hidapi/windows + make -f Makefile-manual OS=MINGW CC=x86_64-w64-mingw32-gcc diff --git a/hidapi/.builds/freebsd.yml b/hidapi/.builds/freebsd.yml new file mode 100644 index 00000000..355dc493 --- /dev/null +++ b/hidapi/.builds/freebsd.yml @@ -0,0 +1,23 @@ +image: freebsd/latest +packages: +- autoconf +- automake +- gmake +- libiconv +- libtool +- pkgconf +sources: +- https://github.com/libusb/hidapi +tasks: +- setup: | + cd hidapi + ./bootstrap + ./configure +- build: | + cd hidapi + make + make DESTDIR=$PWD/root install + make clean +- build-manual: | + cd hidapi/libusb + gmake -f Makefile-manual diff --git a/hidapi/.cirrus.yml b/hidapi/.cirrus.yml new file mode 100644 index 00000000..b4cf2016 --- /dev/null +++ b/hidapi/.cirrus.yml @@ -0,0 +1,33 @@ +alpine_task: + container: + image: alpine:latest + install_script: apk add autoconf automake g++ gcc libusb-dev libtool linux-headers eudev-dev make musl-dev + script: + - ./bootstrap + - ./configure || { cat config.log; exit 1; } + - make + - make install + +freebsd11_task: + freebsd_instance: + image: freebsd-11-2-release-amd64 + install_script: + - pkg install -y + autoconf automake libiconv libtool pkgconf + script: + - ./bootstrap + - ./configure || { cat config.log; exit 1; } + - make + - make install + +freebsd12_task: + freebsd_instance: + image: freebsd-12-1-release-amd64 + install_script: + - pkg install -y + autoconf automake libiconv libtool pkgconf + script: + - ./bootstrap + - ./configure || { cat config.log; exit 1; } + - make + - make install diff --git a/hidapi/.gitattributes b/hidapi/.gitattributes new file mode 100644 index 00000000..edb79feb --- /dev/null +++ b/hidapi/.gitattributes @@ -0,0 +1,7 @@ +* text=auto + +*.sln text eol=crlf +*.vcproj text eol=crlf + +bootstrap text eol=lf +configure.ac text eol=lf diff --git a/hidapi/.github/workflows/builds.yml b/hidapi/.github/workflows/builds.yml new file mode 100644 index 00000000..72d1c335 --- /dev/null +++ b/hidapi/.github/workflows/builds.yml @@ -0,0 +1,148 @@ +name: GitHub Builds + +on: [push, pull_request] + +jobs: + macos-automake: + + runs-on: macos-latest + + steps: + - uses: actions/checkout@v2 + - name: Install build tools + run: brew install autoconf automake libtool + - name: Configure Automake + run: | + ./bootstrap + ./configure --prefix=$(pwd)/install + - name: Build Automake + run: | + make + make install + - name: Clean build + run: make clean + - name: Build Manual makefile + working-directory: mac + run: make -f Makefile-manual + + macos-cmake: + + runs-on: macos-latest + + steps: + - uses: actions/checkout@v2 + with: + path: hidapisrc + - name: Configure CMake + run: | + rm -rf build install + cmake -B build/shared -S hidapisrc -DCMAKE_INSTALL_PREFIX=install/shared + cmake -B build/framework -S hidapisrc -DCMAKE_INSTALL_PREFIX=install/framework -DCMAKE_FRAMEWORK=ON + - name: Build CMake Shared + working-directory: build/shared + run: make install + - name: Build CMake Framework + working-directory: build/framework + run: make install + - name: Check artifacts + uses: andstor/file-existence-action@v1 + with: + files: "install/shared/lib/libhidapi.dylib, install/shared/include/hidapi/hidapi.h, install/framework/lib/hidapi.framework/hidapi, install/framework/lib/hidapi.framework/Headers/hidapi.h" + allow_failure: true + + ubuntu-cmake: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + path: hidapisrc + - name: Install dependencies + run: sudo apt install libudev-dev libusb-1.0-0-dev + - name: Configure CMake + run: | + rm -rf build install + cmake -B build/shared -S hidapisrc -DCMAKE_INSTALL_PREFIX=install/shared + cmake -B build/static -S hidapisrc -DCMAKE_INSTALL_PREFIX=install/static -DBUILD_SHARED_LIBS=FALSE + - name: Build CMake Shared + working-directory: build/shared + run: make install + - name: Build CMake Static + working-directory: build/static + run: make install + - name: Check artifacts + uses: andstor/file-existence-action@v1 + with: + files: "install/shared/lib/libhidapi-libusb.so, install/shared/lib/libhidapi-hidraw.so, install/shared/include/hidapi/hidapi.h, install/static/lib/libhidapi-libusb.a, install/static/lib/libhidapi-hidraw.a, install/static/include/hidapi/hidapi.h" + allow_failure: true + + windows-cmake-msvc: + + runs-on: windows-latest + + steps: + - uses: actions/checkout@v2 + with: + path: hidapisrc + - name: Configure CMake + shell: cmd + run: | + RMDIR /Q /S build install + cmake -B build -S hidapisrc -DCMAKE_INSTALL_PREFIX=install + - name: Build CMake + working-directory: build + run: cmake --build . --target install + - name: Check artifacts + uses: andstor/file-existence-action@v1 + with: + files: "install/lib/hidapi.lib, install/bin/hidapi.dll, install/include/hidapi/hidapi.h" + allow_failure: true + + windows-cmake-nmake: + + runs-on: windows-latest + + steps: + - uses: actions/checkout@v2 + with: + path: hidapisrc + - name: Configure CMake + shell: cmd + run: | + RMDIR /Q /S build install + call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat" + cmake -G"NMake Makefiles" -B build -S hidapisrc -DCMAKE_INSTALL_PREFIX=install + - name: Build CMake + working-directory: build + shell: cmd + run: | + call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat" + nmake install + - name: Check artifacts + uses: andstor/file-existence-action@v1 + with: + files: "install/lib/hidapi.lib, install/bin/hidapi.dll, install/include/hidapi/hidapi.h" + allow_failure: true + + windows-cmake-mingw: + + runs-on: windows-latest + + steps: + - uses: actions/checkout@v2 + with: + path: hidapisrc + - name: Configure CMake + shell: cmd + run: | + RMDIR /Q /S build install + cmake -G"MinGW Makefiles" -B build -S hidapisrc -DCMAKE_INSTALL_PREFIX=install + - name: Build CMake + working-directory: build + run: cmake --build . --target install + - name: Check artifacts + uses: andstor/file-existence-action@v1 + with: + files: "install/lib/libhidapi.dll.a, install/bin/libhidapi.dll, install/include/hidapi/hidapi.h" + allow_failure: true diff --git a/hidapi/.gitignore b/hidapi/.gitignore new file mode 100644 index 00000000..9963751f --- /dev/null +++ b/hidapi/.gitignore @@ -0,0 +1,24 @@ + +# Autotools-added generated files +Makefile.in +aclocal.m4 +ar-lib +autom4te.cache/ +config.* +configure +compile +depcomp +install-sh +libusb/Makefile.in +linux/Makefile.in +ltmain.sh +mac/Makefile.in +missing +testgui/Makefile.in +windows/Makefile.in + +Makefile +stamp-h1 +libtool + +.DS_Store diff --git a/hidapi/.gitrepo b/hidapi/.gitrepo new file mode 100644 index 00000000..5d64ed0e --- /dev/null +++ b/hidapi/.gitrepo @@ -0,0 +1,12 @@ +; DO NOT EDIT (unless you know what you are doing) +; +; This subdirectory is a git "subrepo", and this file is maintained by the +; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme +; +[subrepo] + remote = https://github.com/libusb/hidapi.git + branch = master + commit = 382138ee67adf539f60fc986a3409cf1f1fc3cee + parent = b0b794fdc973191c6e3c46cc1cd0adb45f81d033 + method = merge + cmdver = 0.4.3 diff --git a/hidapi/AUTHORS.txt b/hidapi/AUTHORS.txt new file mode 100644 index 00000000..e08cb161 --- /dev/null +++ b/hidapi/AUTHORS.txt @@ -0,0 +1,18 @@ + +HIDAPI Authors: + +Alan Ott : + Original Author and Maintainer + Linux, Windows, and Mac implementations + +Ludovic Rousseau : + Formatting for Doxygen documentation + Bug fixes + Correctness fixes + +libusb/hidapi Team: + Development/maintainance since June 4th 2019 + +For a comprehensive list of contributions, see the commit list at github: + https://github.com/libusb/hidapi/commits/master + diff --git a/hidapi/BUILD.autotools.md b/hidapi/BUILD.autotools.md new file mode 100644 index 00000000..1bac2bba --- /dev/null +++ b/hidapi/BUILD.autotools.md @@ -0,0 +1,106 @@ +# Building HIDAPI using Autotools + +To be able to use Autotools to build HIDAPI, it has to be [installed](#installing-autotools)/available in the system. + +Make sure you've checked [prerequisites](BUILD.md#prerequisites) and installed all required dependencies. + +## Installing Autotools + +HIDAPI uses few specific tools/packages from Autotools: `autoconf`, `automake`, `libtool`. + +On different platforms or package managers, those could be named a bit differently or packaged together. +You'll have to check the documentation/package list for your specific package manager. + +### Linux + +On Ubuntu the tools are available via APT: + +```sh +sudo apt install autoconf automake libtool +``` + +### FreeBSD + +FreeBSD Autotools can be installed as: + +```sh +pkg_add -r autotools +``` + +Additionally, on FreeBSD you will need to install GNU make: +```sh +pkg_add -r gmake +``` + +## Building HIDAPI with Autotools + +A simple command list, to build HIDAPI with Autotools as a _shared library_ and install in into your system: + +```sh +./bootstrap # this prepares the configure script +./configure +make # build the library +make install # as root, or using sudo, this will install hidapi into your system +``` + +`./configure` can take several arguments which control the build. A few commonly used options: +```sh + --enable-testgui + # Enable the build of Foxit-based Test GUI. This requires Fox toolkit to + # be installed/available. See README.md#test-gui for remarks. + + --prefix=/usr + # Specify where you want the output headers and libraries to + # be installed. The example above will put the headers in + # /usr/include and the binaries in /usr/lib. The default is to + # install into /usr/local which is fine on most systems. + + --disable-shared + # By default, both shared and static libraries are going to be built/installed. + # This option disables shared library build, if only static library is required. +``` + + +## Cross Compiling + +This section talks about cross compiling HIDAPI for Linux using Autotools. +This is useful for using HIDAPI on embedded Linux targets. These +instructions assume the most raw kind of embedded Linux build, where all +prerequisites will need to be built first. This process will of course vary +based on your embedded Linux build system if you are using one, such as +OpenEmbedded or Buildroot. + +For the purpose of this section, it will be assumed that the following +environment variables are exported. +```sh +$ export STAGING=$HOME/out +$ export HOST=arm-linux +``` + +`STAGING` and `HOST` can be modified to suit your setup. + +### Prerequisites + +Depending on what backend you want to cross-compile, you also need to prepare the dependencies: +`libusb` for libusb HIDAPI backend, or `libudev` for hidraw HIDAPI backend. + +An example of cross-compiling `libusb`. From `libusb` source directory, run: +```sh +./configure --host=$HOST --prefix=$STAGING +make +make install +``` + +An example of cross-comping `libudev` is not covered by this section. +Check `libudev`'s documentation for details. + +### Building HIDAPI + +Build HIDAPI: +```sh +PKG_CONFIG_DIR= \ +PKG_CONFIG_LIBDIR=$STAGING/lib/pkgconfig:$STAGING/share/pkgconfig \ +PKG_CONFIG_SYSROOT_DIR=$STAGING \ +./configure --host=$HOST --prefix=$STAGING +# make / make install - same as for a regular build +``` diff --git a/hidapi/BUILD.cmake.md b/hidapi/BUILD.cmake.md new file mode 100644 index 00000000..c40f8939 --- /dev/null +++ b/hidapi/BUILD.cmake.md @@ -0,0 +1,227 @@ +# Building HIDAPI using CMake + +To build HIDAPI with CMake, it has to be [installed](#installing-cmake)/available in the system. + +Make sure you've checked [prerequisites](BUILD.md#prerequisites) and installed all required dependencies. + +HIDAPI CMake build system allows you to build HIDAPI in two generally different ways: +1) As a [standalone package/library](#standalone-package-build); +2) As [part of a larger CMake project](#hidapi-as-a-subdirectory). + +**TL;DR**: if you're experienced developer and have been working with CMake projects or have been written some of your own - +most of this document may not be of interest for you; just check variables names, its default values and the target names. + +## Installing CMake + +CMake can be installed either using your system's package manager, +or by downloading an installer/prebuilt version from the [official website](https://cmake.org/download/). + +On most \*nix systems, the prefered way to install CMake is via package manager, +e.g. `sudo apt install cmake`. + +On Windows CMake could be provided by your development environment (e.g. by Visual Studio Installer or MinGW installer), +or you may install it system-wise using the installer from the official website. + +On macOS CMake may be installed by Homebrew/MacPorts or using the installer from the official website. + +## Standalone package build + +To build HIDAPI as a standalone package, you follow [general steps](https://cmake.org/runningcmake/) of building any CMake project. + +An example of building HIDAPI with CMake: +```sh +# precondition: create a somewhere on the filesystem (preferably outside of the HIDAPI source) +# this is the place where all intermediate/build files are going to be located +cd +# configure the build +cmake +# build it! +cmake --build . +# install library; by default installs into /usr/local/ +cmake --build . --target install +# NOTE: you need to run install command as root, to be able to install into /usr/local/ +``` +Such invocation will use the default (as per CMake magic) compiler/build environment available in your system. + +You may pass some additional CMake variables to control the build configuration as `-D=value`. +E.g.: +```sh +# install command now would install things into /usr +cmake -DCMAKE_INSTALL_PREFIX=/usr +``` + +
+ Using a specific CMake generator + +An example of using `Ninja` as a CMake generator: + +```sh +cd +# configure the build +cmake -GNinja +# we know, that CMake has generated build files for Ninja, +# so we can use `ninja` directly, instead of `cmake --build .` +ninja +# install library +ninja install +``` + +`-G` here specifies a native build system CMake would generate build files for. +Check [CMake Documentation](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html) for a list of available generators (system-specific). + +

+ +Some of the [standard](https://cmake.org/cmake/help/latest/manual/cmake-variables.7.html) CMake variables you may want to use to configure a build: + +- [`CMAKE_INSTALL_PREFIX`](https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html) - prefix where `install` target would install the library(ies); +- [`CMAKE_BUILD_TYPE`](https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html) - standard possible values: `Debug`, `Release`, `RelWithDebInfo`, `MinSizeRel`; Defaults to `Release` for HIDAPI, if not specified; +- [`BUILD_SHARED_LIBS`](https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html) - when set to TRUE, HIDAPI is built as a shared library, otherwise build statically; Defaults to `TRUE` for HIDAPI, if not specified; + +
+ macOS-specific variables + + - [`CMAKE_FRAMEWORK`](https://cmake.org/cmake/help/latest/variable/CMAKE_FRAMEWORK.html) - (since CMake 3.15) when set to TRUE, HIDAPI is built as a framework library, otherwise build as a regular static/shared library; Defaults to `FALSE` for HIDAPI, if not specified; + - [`CMAKE_OSX_DEPLOYMENT_TARGET`](https://cmake.org/cmake/help/latest/variable/CMAKE_OSX_DEPLOYMENT_TARGET.html) - minimum version of the target platform (e.g. macOS or iOS) on which the target binaries are to be deployed; defaults to a maximum supported target platform by currently used XCode/Toolchain; + +

+ +HIDAPI-specific CMake variables: + +- `HIDAPI_BUILD_HIDTEST` - when set to TRUE, build a small test application `hidtest`; + +
+ Linux-specific variables + + - `HIDAPI_WITH_HIDRAW` - when set to TRUE, build HIDRAW-based implementation of HIDAPI (`hidapi-hidraw`), otherwise don't build it; defaults to TRUE; + - `HIDAPI_WITH_LIBUSB` - when set to TRUE, build LIBUSB-based implementation of HIDAPI (`hidapi-libusb`), otherwise don't build it; defaults to TRUE; + + **NOTE**: at least one of `HIDAPI_WITH_HIDRAW` or `HIDAPI_WITH_LIBUSB` has to be set to TRUE. + +

+ +To see all most-useful CMake variables available for HIDAPI, one of the most convenient ways is too use [`cmake-gui`](https://cmake.org/cmake/help/latest/manual/cmake-gui.1.html) tool ([example](https://cmake.org/runningcmake/)). + +_NOTE_: HIDAPI packages built by CMake can be used with `pkg-config`, as if built with [Autotools](BUILD.autotools.md). + +### MSVC and Ninja +It is possible to build a CMake project (including HIDAPI) using MSVC compiler and Ninja (for medium and larger projects it is so much faster than msbuild). + +For that: +1) Open cmd.exe; +2) Setup MSVC build environment variables, e.g.: `vcvarsall.bat x64`, where: + - `vcvarsall.bat` is an environment setup script of your MSVC toolchain installation;
For MSVC 2019 Community edition it is located at: `C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\`; + - `x64` -a target architecture to build; +3) Follow general build steps, and use `Ninja` as a generator. + +### Using HIDAPI in a CMake project + +When HIDAPI is used as a standalone package (either installed into the system or built manually and installed elsewhere), the simplest way to use it is as showed in the example: + +```cmake +project(my_application) + +add_executable(my_application main.c) + +find_package(hidapi REQUIRED) +target_link_libraries(my_application PRIVATE hidapi::hidapi) +``` + +If HIDAPI isn't installed in your system, or `find_package` cannot find HIDAPI by default for any other reasons, +the recommended way manually specify which HIDAPI package to use is via `hidapi_ROOT` CMake variable, e.g.: +`-Dhidapi_ROOT=`. + +_NOTE_: usage of `hidapi_ROOT` is only possible (and recommended) with CMake 3.12 and higher. For older versions of CMake you'd need to specify [`CMAKE_PREFIX_PATH`](https://cmake.org/cmake/help/latest/variable/CMAKE_PREFIX_PATH.html#variable:CMAKE_PREFIX_PATH) instead. + +Check with [`find_package`](https://cmake.org/cmake/help/latest/command/find_package.html) documentation if you need more details. + +Available CMake targets after successful `find_package(hidapi)`: +- `hidapi::hidapi` - indented to be used in most cases; +- `hidapi::include` - if you need only to include `` but not link against the library; +- `hidapi::winapi` - same as `hidapi::hidapi` on Windows; available only on Windows; +- `hidapi::darwin` - same as `hidapi::hidapi` on macOS; available only on macOS; +- `hidapi::libusb` - available when libusb backend is used/available; +- `hidapi::hidraw` - available when hidraw backend is used/available on Linux; + +**NOTE**: on Linux often both `hidapi::libusb` and `hidapi::hidraw` backends are available; in that case `hidapi::hidapi` is an alias for **`hidapi::hidraw`**. The motivation is that `hidraw` backend is a native Linux kernel implementation of HID protocol, and supports various HID devices (USB, Bluetooth, I2C, etc.). If `hidraw` backend isn't built at all (`hidapi::libusb` is the only target) - `hidapi::hidapi` is an alias for `hidapi::libusb`. +If you're developing a cross-platform application and you are sure you need to use `libusb` backend on Linux, the simple way to achieve this is: +```cmake +if(TARGET hidapi::libusb) + target_link_libraries(my_project PRIVATE hidapi::libusb) +else() + target_link_libraries(my_project PRIVATE hidapi::hidapi) +endif() +``` + +## HIDAPI as a subdirectory + +HIDAPI can be easily used as a subdirectory of a larger CMake project: +```cmake +# root CMakeLists.txt +cmake_minimum_required(VERSION 3.4.3 FATAL_ERROR) + +add_subdirectory(hidapi) +add_subdirectory(my_application) + +# my_application/CMakeLists.txt +project(my_application) + +add_executable(my_application main.c) + +# NOTE: no `find_package` is required, since HIDAPI targets are already a part of the project tree +target_link_libraries(my_application PRIVATE hidapi::hidapi) +``` +Lets call this "larger project" a "host project". + +All of the variables described in [standalone build](#standalone-package-build) section can be used to control HIDAPI build in case of a subdirectory, e.g.: +```cmake +set(HIDAPI_WITH_LIBUSB FALSE) # surely will be used only on Linux +set(BUILD_SHARED_LIBS FALSE) # HIDAPI as static library on all platforms +add_subdirectory(hidapi) +``` + +There are several important differences in the behavior of HIDAPI CMake build system when CMake is built as standalone package vs subdirectory build: + +1) In _standalone build_ a number of standard and HIDAPI-specific variables are marked as _cache variables_ or _options_. +This is done for convenience: when you're building HIDAPI as a standalone package and using tools like `cmake-gui` - those are highlighted as variables that can be changed and has some short description/documentation. E.g.: +![an example of highlighted variables in cmake-gui](documentation/cmake-gui-highlights.png "cmake-gui highlighted variables")
+E.g.2:
+![an example of drop-down menu in cmake-gui](documentation/cmake-gui-drop-down.png "cmake-gui drop-down menu")
+When HIDAPI is built as a _subdirectory_ - **_none of the variables are marked for cache or as options_** by HIDAPI. +This is done to let the host project's developer decide what is important (what needs to be highlighted) and what's not. + +2) The default behavior/default value for some of the variables is a bit different: + - by default, none of HIDAPI targets are [installed](https://cmake.org/cmake/help/latest/command/install.html); if required, HIDAPI targets can be installed by host project _after_ including HIDAPI subdirectory (requires CMake 3.13 or later); **or**, the default installation can be enabled by setting `HIDAPI_INSTALL_TARGETS` variable _before_ including HIDAPI subdirectory. + HIDAPI uses [GNUInstallDirs](https://cmake.org/cmake/help/latest/module/GNUInstallDirs.html) to specify install locations. Variables like `CMAKE_INSTALL_LIBDIR` can be used to control HIDAPI's installation locations. E.g.: + ```cmake + # enable the installation if you need it + set(HIDAPI_INSTALL_TARGETS ON) + # (optionally) change default installation locations if it makes sense for your target platform, etc. + set(CMAKE_INSTALL_LIBDIR "lib64") + add_subdirectory(hidapi) + ``` + - HIDAPI prints its version during the configuration when built as a standalone package; to enable this for subdirectory builds - set `HIDAPI_PRINT_VERSION` to TRUE before including HIDAPI; + +3) In a subdirectory build, HIDAPI _doesn't modify or set any of the CMake variables_ that may change the build behavior. + For instance, in a _standalone build_, if CMAKE_BUILD_TYPE or BUILD_SHARED_LIBS variables are not set, those are defaulted to "Release" and "TRUE" explicitly. + In a _subdirectory build_, even if not set, those variables remain unchanged, so a host project's developer has a full control over the HIDAPI build configuration. + +Available CMake targets after `add_subdirectory(hidapi)` _are the same as in case of [standalone build](#standalone-package-build)_, and a few additional ones: +- `hidapi_include` - the interface library; `hidapi::hidapi` is an alias of it; +- `hidapi_winapi` - library target on Windows; `hidapi::winapi` is an alias of it; +- `hidapi_darwin` - library target on macOS; `hidapi::darwin` is an alias of it; +- `hidapi_libusb` - library target for libusb backend; `hidapi::libusb` is an alias of it; +- `hidapi_hidraw` - library target for hidraw backend; `hidapi::hidraw` is an alias of it; +- `hidapi-libusb` - an alias of `hidapi_libusb` for compatibility with raw library name; +- `hidapi-hidraw` - an alias of `hidapi_hidraw` for compatibility with raw library name; +- `hidapi` - an alias of `hidapi_winapi` or `hidapi_darwin` on Windows or macOS respectfully. + +Advanced: +- Why would I need additional targets described in this section above, if I already have alias targets compatible with `find_package`? + - an example: + ```cmake + add_subdirectory(hidapi) + if(TARGET hidapi_libusb) + # see libusb/hid.c for usage of `NO_ICONV` + target_compile_definitions(hidapi_libusb PRIVATE NO_ICONV) + endif() + ``` diff --git a/hidapi/BUILD.md b/hidapi/BUILD.md new file mode 100644 index 00000000..51abc656 --- /dev/null +++ b/hidapi/BUILD.md @@ -0,0 +1,122 @@ +# Building HIDAPI from Source + +## Table of content + +* [Intro](#intro) +* [Prerequisites](#prerequisites) + * [Linux](#linux) + * [FreeBSD](#freebsd) + * [Mac](#mac) + * [Windows](#windows) +* [Integrating hidapi directly into your source tree](#integrating-hidapi-directly-into-your-source-tree) +* [Building the manual way on Unix platforms](#building-the-manual-way-on-unix-platforms) +* [Building on Windows](#building-on-windows) + +## Intro + +For various reasons you may need to build HIDAPI on your own. + +It can be done in several different ways: +- using [Autotools](BUILD.autotools.md); +- using [CMake](BUILD.cmake.md); +- using [manual makefiles](#building-the-manual-way-on-unix-platforms). + +**Autotools** build system is historically first mature build system for +HIDAPI. Most common usage of it is in its separate README: [BUILD.autotools.md](BUILD.autotools.md). + +**CMake** build system is de facto an industry standard for many open-source and proprietary projects and solutions. +HIDAPI is one of the projects which uses the power of CMake for its advantage. +More documentation is available in its separate README: [BUILD.cmake.md](BUILD.cmake.md). + +If you don't know where to start to build HIDAPI, we recommend starting with [CMake](BUILD.cmake.md) build. + +## Prerequisites: + +Regardless of what build system system you choose to use, there are specific dependencies for each platform/backend. + +### Linux: + +Depending on which backend you're going to build, you'll need to install +additional development packages. For `linux/hidraw` backend you need +development package for `libudev`. For `libusb` backend, naturally, you need +`libusb` development package. + +On Debian/Ubuntu systems these can be installed by running: +```sh +# required only by hidraw backend +sudo apt install libudev-dev +# required only by libusb backend +sudo apt install libusb-1.0-0-dev +``` + +### FreeBSD: + +On FreeBSD you will need to install libiconv. This is done by running +the following: +```sh +pkg_add -r libiconv +``` + +### Mac: + +On Mac make sure you have XCode installed and its Command Line Tools. + +### Windows: + +On Windows you just need a compiler. You may use Visual Studio or Cygwin/MinGW, +depending on which environment is best for your needs. + +## Integrating HIDAPI directly into your source tree + +Instead of using one of the provided build systems, you may want to integrate +HIDAPI directly into your source tree. +Generally it is not encouraged to do so, but if you must, all you need to do: +- add a single source file `hid.c` (for a specific backend); +- setup include directory to `/hidapi`; +- add link libraries, that are specific for each backend. + +Check the manual makefiles for a simple example/reference of what are the dependencies of each specific backend. + +NOTE: if your have a CMake-based project, you're likely be able to use +HIDAPI directly as a subdirectory. Check [BUILD.cmake.md](BUILD.cmake.md) for details. + +## Building the manual way on Unix platforms + +Manual Makefiles are provided mostly to give the user an idea what it takes +to build a program which embeds HIDAPI directly inside of it. These should +really be used as examples only. If you want to build a system-wide shared +library, use one of the build systems mentioned above. + +To build HIDAPI using the manual Makefiles, change to the directory +of your platform and run make. For example, on Linux run: +```sh +cd linux/ +make -f Makefile-manual +``` + +## Building on Windows + +To build the HIDAPI DLL on Windows using Visual Studio, build the `.sln` file +in the `windows/` directory. + +To build HIDAPI using MinGW or Cygwin using Autotools, use a general Autotools + [instruction](BUILD.autotools.md). + +Any windows builds (MSVC or MinGW/Cygwin) are also supported by [CMake](BUILD.cmake.md). + +HIDAPI can also be built using the Windows DDK (now also called the Windows +Driver Kit or WDK). This method was originally required for the HIDAPI build +but not anymore. However, some users still prefer this method. It is not as +well supported anymore but should still work. Patches are welcome if it does +not. To build using the DDK: + + 1. Install the Windows Driver Kit (WDK) from Microsoft. + 2. From the Start menu, in the Windows Driver Kits folder, select Build + Environments, then your operating system, then the x86 Free Build + Environment (or one that is appropriate for your system). + 3. From the console, change directory to the `windows/ddk_build/` directory, + which is part of the HIDAPI distribution. + 4. Type build. + 5. You can find the output files (DLL and LIB) in a subdirectory created + by the build system which is appropriate for your environment. On + Windows XP, this directory is `objfre_wxp_x86/i386`. diff --git a/hidapi/CMakeLists.txt b/hidapi/CMakeLists.txt new file mode 100644 index 00000000..b279a360 --- /dev/null +++ b/hidapi/CMakeLists.txt @@ -0,0 +1,61 @@ +cmake_minimum_required(VERSION 3.1.3 FATAL_ERROR) + +if(NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + add_subdirectory(src) + # compatinility with find_package() vs add_subdirectory + set(hidapi_VERSION "${hidapi_VERSION}" PARENT_SCOPE) + return() +endif() +# All of the below in this file is meant for a standalone build. +# When building as a subdirectory of a larger project, most of the options may not make sense for it, +# so it is up to developer to configure those, e.g.: +# +# # a subfolder of a master project, e.g.: 3rdparty/hidapi/CMakeLists.txt +# +# set(HIDAPI_WITH_HIDRAW OFF) +# set(CMAKE_FRAMEWORK ON) +# # and keep everything else to their defaults +# add_subdirectory(hidapi) +# + +set(DEFAULT_CMAKE_BUILD_TYPES "Debug" "Release" "MinSizeRel" "RelWithDebInfo") +if(NOT DEFINED CMAKE_BUILD_TYPE OR NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release" CACHE STRING "${DEFAULT_CMAKE_BUILD_TYPES}" FORCE) +endif() +# This part is for convenience, when used one of the standard build types with cmake-gui +list(FIND DEFAULT_CMAKE_BUILD_TYPES "${CMAKE_BUILD_TYPE}" _build_type_index) +if(${_build_type_index} GREATER -1) + # set it optionally, so a custom CMAKE_BUILD_TYPE can be used as well, if needed + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS ${DEFAULT_CMAKE_BUILD_TYPES}) +endif() +unset(_build_type_index) +# + +project(hidapi LANGUAGES C) + +if(APPLE) + if(NOT CMAKE_VERSION VERSION_LESS "3.15") + option(CMAKE_FRAMEWORK "Build macOS/iOS Framework version of the library" OFF) + endif() +elseif(NOT WIN32) + if(CMAKE_SYSTEM_NAME MATCHES "Linux") + option(HIDAPI_WITH_HIDRAW "Build HIDRAW-based implementation of HIDAPI" ON) + option(HIDAPI_WITH_LIBUSB "Build LIBUSB-based implementation of HIDAPI" ON) + endif() +endif() + +option(BUILD_SHARED_LIBS "Build shared version of the libraries, otherwise build statically" ON) + +set(HIDAPI_INSTALL_TARGETS ON) +set(HIDAPI_PRINT_VERSION ON) + +add_subdirectory(src) + +set(BUILD_HIDTEST_DEFAULT OFF) +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(BUILD_HIDTEST_DEFAULT ON) +endif() +option(HIDAPI_BUILD_HIDTEST "Build small console test application hidtest" ${BUILD_HIDTEST_DEFAULT}) +if(HIDAPI_BUILD_HIDTEST) + add_subdirectory(hidtest) +endif() diff --git a/hidapi/HACKING.txt b/hidapi/HACKING.txt new file mode 100644 index 00000000..be8c0d76 --- /dev/null +++ b/hidapi/HACKING.txt @@ -0,0 +1,10 @@ +This file is mostly for the maintainer. + +Updating a Version: +1. Update VERSION file. +2. HID_API_VERSION_MAJOR/HID_API_VERSION_MINOR/HID_API_VERSION_PATCH in hidapi.h. + +Firing a new release: +1. Update the Version (if not yet updated). +2. Build hidapi.dll/.lib for x86/x64. +3. Upload Windows binaries to Github release page. diff --git a/hidapi/LICENSE-bsd.txt b/hidapi/LICENSE-bsd.txt new file mode 100644 index 00000000..538cdf95 --- /dev/null +++ b/hidapi/LICENSE-bsd.txt @@ -0,0 +1,26 @@ +Copyright (c) 2010, Alan Ott, Signal 11 Software +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Signal 11 Software nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/hidapi/LICENSE-gpl3.txt b/hidapi/LICENSE-gpl3.txt new file mode 100644 index 00000000..94a9ed02 --- /dev/null +++ b/hidapi/LICENSE-gpl3.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state 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 program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/hidapi/LICENSE-orig.txt b/hidapi/LICENSE-orig.txt new file mode 100644 index 00000000..e3f33808 --- /dev/null +++ b/hidapi/LICENSE-orig.txt @@ -0,0 +1,9 @@ + HIDAPI - Multi-Platform library for + communication with HID devices. + + Copyright 2009, Alan Ott, Signal 11 Software. + All Rights Reserved. + + This software may be used by anyone for any reason so + long as the copyright notice in the source files + remains intact. diff --git a/hidapi/LICENSE.txt b/hidapi/LICENSE.txt new file mode 100644 index 00000000..e1676d4c --- /dev/null +++ b/hidapi/LICENSE.txt @@ -0,0 +1,13 @@ +HIDAPI can be used under one of three licenses. + +1. The GNU General Public License, version 3.0, in LICENSE-gpl3.txt +2. A BSD-Style License, in LICENSE-bsd.txt. +3. The more liberal original HIDAPI license. LICENSE-orig.txt + +The license chosen is at the discretion of the user of HIDAPI. For example: +1. An author of GPL software would likely use HIDAPI under the terms of the +GPL. + +2. An author of commercial closed-source software would likely use HIDAPI +under the terms of the BSD-style license or the original HIDAPI license. + diff --git a/hidapi/Makefile.am b/hidapi/Makefile.am new file mode 100644 index 00000000..00bcb73c --- /dev/null +++ b/hidapi/Makefile.am @@ -0,0 +1,85 @@ + +ACLOCAL_AMFLAGS = -I m4 + +if OS_FREEBSD +pkgconfigdir=$(prefix)/libdata/pkgconfig +else +pkgconfigdir=$(libdir)/pkgconfig +endif + +if OS_LINUX +pkgconfig_DATA=pc/hidapi-hidraw.pc pc/hidapi-libusb.pc +else +pkgconfig_DATA=pc/hidapi.pc +endif + +SUBDIRS= + +if OS_LINUX +SUBDIRS += linux libusb +endif + +if OS_DARWIN +SUBDIRS += mac +endif + +if OS_FREEBSD +SUBDIRS += libusb +endif + +if OS_KFREEBSD +SUBDIRS += libusb +endif + +if OS_HAIKU +SUBDIRS += libusb +endif + +if OS_WINDOWS +SUBDIRS += windows +endif + +SUBDIRS += hidtest + +if BUILD_TESTGUI +SUBDIRS += testgui +endif + +EXTRA_DIST = udev doxygen + +dist_doc_DATA = \ + README.md \ + AUTHORS.txt \ + LICENSE-bsd.txt \ + LICENSE-gpl3.txt \ + LICENSE-orig.txt \ + LICENSE.txt + +SCMCLEAN_TARGETS= \ + aclocal.m4 \ + config.guess \ + config.sub \ + configure \ + config.h.in \ + depcomp \ + install-sh \ + ltmain.sh \ + missing \ + mac/Makefile.in \ + testgui/Makefile.in \ + libusb/Makefile.in \ + Makefile.in \ + linux/Makefile.in \ + windows/Makefile.in \ + m4/libtool.m4 \ + m4/lt~obsolete.m4 \ + m4/ltoptions.m4 \ + m4/ltsugar.m4 \ + m4/ltversion.m4 + +SCMCLEAN_DIR_TARGETS = \ + autom4te.cache + +scm-clean: distclean + rm -f $(SCMCLEAN_TARGETS) + rm -Rf $(SCMCLEAN_DIR_TARGETS) diff --git a/hidapi/README.md b/hidapi/README.md new file mode 100644 index 00000000..53705cbb --- /dev/null +++ b/hidapi/README.md @@ -0,0 +1,182 @@ +## HIDAPI library for Windows, Linux, FreeBSD and macOS + +| CI instance | Status | +|----------------------|--------| +| `Linux/macOS/Windows master` | [![GitHub Builds](https://github.com/libusb/hidapi/workflows/GitHub%20Builds/badge.svg?branch=master)](https://github.com/libusb/hidapi/actions/workflows/builds.yml?query=branch%3Amaster) | +| `Windows master` | [![Build status](https://ci.appveyor.com/api/projects/status/xfmr5fo8w0re8ded/branch/master?svg=true)](https://ci.appveyor.com/project/libusb/hidapi/branch/master) | +| `Linux/BSD, last build (branch/PR)` | [![builds.sr.ht status](https://builds.sr.ht/~z3ntu/hidapi.svg)](https://builds.sr.ht/~z3ntu/hidapi) | + +HIDAPI is a multi-platform library which allows an application to interface +with USB and Bluetooth HID-Class devices on Windows, Linux, FreeBSD, and macOS. +HIDAPI can be either built as a shared library (`.so`, `.dll` or `.dylib`) or +can be embedded directly into a target application by adding a single source +file (per platform) and a single header. + +HIDAPI library was originally developed by Alan Ott ([signal11](https://github.com/signal11)). + +It was moved to [libusb/hidapi](https://github.com/libusb/hidapi) on June 4th, 2019, in order to merge important bugfixes and continue development of the library. + +## Table of Contents + +* [About](#about) + * [Test GUI](#test-gui) +* [What Does the API Look Like?](#what-does-the-api-look-like) +* [License](#license) +* [Installing HIDAPI](#installing-hidapi) +* [Build from Source](#build-from-source) + + +## About + +HIDAPI has four back-ends: +* Windows (using `hid.dll`) +* Linux/hidraw (using the Kernel's hidraw driver) +* libusb (using libusb-1.0 - Linux/BSD/other UNIX-like systems) +* macOS (using IOHidManager) + +On Linux, either the hidraw or the libusb back-end can be used. There are +tradeoffs, and the functionality supported is slightly different. Both are +built by default. It is up to the application linking to hidapi to choose +the backend at link time by linking to either `libhidapi-libusb` or +`libhidapi-hidraw`. + +Note that you will need to install an udev rule file with your application +for unprivileged users to be able to access HID devices with hidapi. Refer +to the [69-hid.rules](udev/69-hid.rules) file in the `udev` directory +for an example. + +__Linux/hidraw__ (`linux/hid.c`): + +This back-end uses the hidraw interface in the Linux kernel, and supports +both USB and Bluetooth HID devices. It requires kernel version at least 2.6.39 +to build. In addition, it will only communicate with devices which have hidraw +nodes associated with them. +Keyboards, mice, and some other devices which are blacklisted from having +hidraw nodes will not work. Fortunately, for nearly all the uses of hidraw, +this is not a problem. + +__Linux/FreeBSD/libusb__ (`libusb/hid.c`): + +This back-end uses libusb-1.0 to communicate directly to a USB device. This +back-end will of course not work with Bluetooth devices. + +### Test GUI + +HIDAPI also comes with a Test GUI. The Test GUI is cross-platform and uses +Fox Toolkit . It will build on every platform +which HIDAPI supports. Since it relies on a 3rd party library, building it +is optional but recommended because it is so useful when debugging hardware. + +NOTE: Test GUI based on Fox Toolkit is not actively developed nor supported +by HIDAPI team. It is kept as a historical artifact. It may even work sometime +or on some platforms, but it is not going to get any new features or bugfixes. + +Instructions for installing Fox-Toolkit on each platform is not provided. +Make sure to use Fox-Toolkit v1.6 if you choose to use it. + +## What Does the API Look Like? + +The API provides the most commonly used HID functions including sending +and receiving of input, output, and feature reports. The sample program, +which communicates with a heavily hacked up version of the Microchip USB +Generic HID sample looks like this (with error checking removed for +simplicity): + +**Warning: Only run the code you understand, and only when it conforms to the +device spec. Writing data at random to your HID devices can break them.** + +```c +#ifdef WIN32 +#include +#endif +#include +#include +#include "hidapi.h" + +#define MAX_STR 255 + +int main(int argc, char* argv[]) +{ + int res; + unsigned char buf[65]; + wchar_t wstr[MAX_STR]; + hid_device *handle; + int i; + + // Initialize the hidapi library + res = hid_init(); + + // Open the device using the VID, PID, + // and optionally the Serial number. + handle = hid_open(0x4d8, 0x3f, NULL); + + // Read the Manufacturer String + res = hid_get_manufacturer_string(handle, wstr, MAX_STR); + wprintf(L"Manufacturer String: %s\n", wstr); + + // Read the Product String + res = hid_get_product_string(handle, wstr, MAX_STR); + wprintf(L"Product String: %s\n", wstr); + + // Read the Serial Number String + res = hid_get_serial_number_string(handle, wstr, MAX_STR); + wprintf(L"Serial Number String: (%d) %s\n", wstr[0], wstr); + + // Read Indexed String 1 + res = hid_get_indexed_string(handle, 1, wstr, MAX_STR); + wprintf(L"Indexed String 1: %s\n", wstr); + + // Toggle LED (cmd 0x80). The first byte is the report number (0x0). + buf[0] = 0x0; + buf[1] = 0x80; + res = hid_write(handle, buf, 65); + + // Request state (cmd 0x81). The first byte is the report number (0x0). + buf[0] = 0x0; + buf[1] = 0x81; + res = hid_write(handle, buf, 65); + + // Read requested state + res = hid_read(handle, buf, 65); + + // Print out the returned buffer. + for (i = 0; i < 4; i++) + printf("buf[%d]: %d\n", i, buf[i]); + + // Close the device + hid_close(handle); + + // Finalize the hidapi library + res = hid_exit(); + + return 0; +} +``` + +You can also use [hidtest/test.c](hidtest/test.c) +as a starting point for your applications. + + +## License + +HIDAPI may be used by one of three licenses as outlined in [LICENSE.txt](LICENSE.txt). + +## Installing HIDAPI + +If you want to build your own application that uses HID devices with HIDAPI, +you need to get HIDAPI development package. + +Depending on what your development environment is, HIDAPI likely to be provided +by your package manager. + +For instance on Ubuntu, HIDAPI is available via APT: +```sh +sudo apt install libhidapi-dev +``` + +HIDAPI package name for other systems/package managers may differ. +Check the documentation/package list of your package manager. + +## Build from Source + +Check [BUILD.md](BUILD.md) for details. diff --git a/hidapi/VERSION b/hidapi/VERSION new file mode 100644 index 00000000..eb1336c8 --- /dev/null +++ b/hidapi/VERSION @@ -0,0 +1 @@ +0.11.0-dev diff --git a/hidapi/android/jni/Android.mk b/hidapi/android/jni/Android.mk new file mode 100644 index 00000000..527b43fd --- /dev/null +++ b/hidapi/android/jni/Android.mk @@ -0,0 +1,19 @@ +LOCAL_PATH:= $(call my-dir) + +HIDAPI_ROOT_REL:= ../.. +HIDAPI_ROOT_ABS:= $(LOCAL_PATH)/../.. + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + $(HIDAPI_ROOT_REL)/libusb/hid.c + +LOCAL_C_INCLUDES += \ + $(HIDAPI_ROOT_ABS)/hidapi \ + $(HIDAPI_ROOT_ABS)/android + +LOCAL_SHARED_LIBRARIES := libusb1.0 + +LOCAL_MODULE := libhidapi + +include $(BUILD_SHARED_LIBRARY) diff --git a/hidapi/bootstrap b/hidapi/bootstrap new file mode 100755 index 00000000..81e9b74b --- /dev/null +++ b/hidapi/bootstrap @@ -0,0 +1,2 @@ +#!/bin/sh -x +autoreconf --install --verbose --force diff --git a/hidapi/configure.ac b/hidapi/configure.ac new file mode 100644 index 00000000..8d9311cc --- /dev/null +++ b/hidapi/configure.ac @@ -0,0 +1,251 @@ +AC_PREREQ(2.63) + +AC_INIT([hidapi],[m4_normalize(m4_builtin([include], VERSION))],[https://github.com/libusb/hidapi/issues]) + +# Library soname version +# Follow the following rules (particularly the ones in the second link): +# http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html +# http://sourceware.org/autobook/autobook/autobook_91.html +lt_current="0" +lt_revision="0" +lt_age="0" +LTLDFLAGS="-version-info ${lt_current}:${lt_revision}:${lt_age}" + +AC_CONFIG_MACRO_DIR([m4]) +AM_INIT_AUTOMAKE([foreign -Wall -Werror]) + +m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) +LT_INIT + +AC_PROG_CC +AC_PROG_CXX +AC_PROG_OBJC +PKG_PROG_PKG_CONFIG + + +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +hidapi_lib_error() { + echo "" + echo " Library $1 was not found on this system." + echo " Please install it and re-run ./configure" + echo "" + exit 1 +} + +hidapi_prog_error() { + echo "" + echo " Program $1 was not found on this system." + echo " This program is part of $2." + echo " Please install it and re-run ./configure" + echo "" + exit 1 +} + +AC_MSG_CHECKING([operating system]) +AC_MSG_RESULT($host) +case $host in +*-linux*) + AC_MSG_RESULT([ (Linux back-end)]) + AC_DEFINE(OS_LINUX, 1, [Linux implementations]) + AC_SUBST(OS_LINUX) + backend="linux" + os="linux" + threads="pthreads" + + # HIDAPI/hidraw libs + PKG_CHECK_MODULES([libudev], [libudev], true, [hidapi_lib_error libudev]) + LIBS_HIDRAW_PR="${LIBS_HIDRAW_PR} $libudev_LIBS" + CFLAGS_HIDRAW="${CFLAGS_HIDRAW} $libudev_CFLAGS" + + # HIDAPI/libusb libs + AC_CHECK_LIB([rt], [clock_gettime], [LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} -lrt"], [hidapi_lib_error librt]) + PKG_CHECK_MODULES([libusb], [libusb-1.0 >= 1.0.9], true, [hidapi_lib_error libusb-1.0]) + LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} $libusb_LIBS" + CFLAGS_LIBUSB="${CFLAGS_LIBUSB} $libusb_CFLAGS" + ;; +*-darwin*) + AC_MSG_RESULT([ (Mac OS X back-end)]) + AC_DEFINE(OS_DARWIN, 1, [Mac implementation]) + AC_SUBST(OS_DARWIN) + backend="mac" + os="darwin" + threads="pthreads" + LIBS="${LIBS} -framework IOKit -framework CoreFoundation -framework AppKit" + ;; +*-freebsd*) + AC_MSG_RESULT([ (FreeBSD back-end)]) + AC_DEFINE(OS_FREEBSD, 1, [FreeBSD implementation]) + AC_SUBST(OS_FREEBSD) + backend="libusb" + os="freebsd" + threads="pthreads" + + CFLAGS="$CFLAGS -I/usr/local/include" + LDFLAGS="$LDFLAGS -L/usr/local/lib" + LIBS="${LIBS}" + PKG_CHECK_MODULES([libusb], [libusb-1.0 >= 1.0.9], true, [hidapi_lib_error libusb-1.0]) + LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} $libusb_LIBS" + CFLAGS_LIBUSB="${CFLAGS_LIBUSB} $libusb_CFLAGS" + AC_CHECK_LIB([iconv], [iconv_open], [LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} -liconv"], [hidapi_lib_error libiconv]) + ;; +*-kfreebsd*) + AC_MSG_RESULT([ (kFreeBSD back-end)]) + AC_DEFINE(OS_KFREEBSD, 1, [kFreeBSD implementation]) + AC_SUBST(OS_KFREEBSD) + backend="libusb" + os="kfreebsd" + threads="pthreads" + + PKG_CHECK_MODULES([libusb], [libusb-1.0 >= 1.0.9], true, [hidapi_lib_error libusb-1.0]) + LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} $libusb_LIBS" + CFLAGS_LIBUSB="${CFLAGS_LIBUSB} $libusb_CFLAGS" + ;; +*-*-haiku) + AC_MSG_RESULT([ (Haiku back-end)]) + AC_DEFINE(OS_HAIKU, 1, [Haiku implementation]) + AC_SUBST(OS_HAIKU) + backend="libusb" + os="haiku" + threads="pthreads" + + PKG_CHECK_MODULES([libusb], [libusb-1.0 >= 1.0.9], true, [hidapi_lib_error libusb-1.0]) + LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} $libusb_LIBS" + CFLAGS_LIBUSB="${CFLAGS_LIBUSB} $libusb_CFLAGS" + AC_CHECK_LIB([iconv], [libiconv_open], [LIBS_LIBUSB_PRIVATE="${LIBS_LIBUSB_PRIVATE} -liconv"], [hidapi_lib_error libiconv]) + ;; +*-mingw*) + AC_MSG_RESULT([ (Windows back-end, using MinGW)]) + backend="windows" + os="windows" + threads="windows" + win_implementation="mingw" + ;; +*-msys*) + AC_MSG_RESULT([ (Windows back-end, using MSYS2)]) + backend="windows" + os="windows" + threads="windows" + win_implementation="mingw" + ;; +*-cygwin*) + AC_MSG_RESULT([ (Windows back-end, using Cygwin)]) + backend="windows" + os="windows" + threads="windows" + win_implementation="cygwin" + ;; +*) + AC_MSG_ERROR([HIDAPI is not supported on your operating system yet]) +esac + +LIBS_HIDRAW="${LIBS} ${LIBS_HIDRAW_PR}" +LIBS_LIBUSB="${LIBS} ${LIBS_LIBUSB_PRIVATE}" +AC_SUBST([LIBS_HIDRAW]) +AC_SUBST([LIBS_LIBUSB]) +AC_SUBST([CFLAGS_LIBUSB]) +AC_SUBST([CFLAGS_HIDRAW]) + +if test "x$os" = xwindows; then + AC_DEFINE(OS_WINDOWS, 1, [Windows implementations]) + AC_SUBST(OS_WINDOWS) + LDFLAGS="${LDFLAGS} -no-undefined" + LIBS="${LIBS} -lsetupapi" +fi + +if test "x$threads" = xpthreads; then + AX_PTHREAD([found_pthreads=yes], [found_pthreads=no]) + + if test "x$found_pthreads" = xyes; then + if test "x$os" = xlinux; then + # Only use pthreads for libusb implementation on Linux. + LIBS_LIBUSB="$PTHREAD_LIBS $LIBS_LIBUSB" + CFLAGS_LIBUSB="$CFLAGS_LIBUSB $PTHREAD_CFLAGS" + # There's no separate CC on Linux for threading, + # so it's ok that both implementations use $PTHREAD_CC + CC="$PTHREAD_CC" + else + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + CC="$PTHREAD_CC" + fi + fi +fi + +# Test GUI +AC_ARG_ENABLE([testgui], + [AS_HELP_STRING([--enable-testgui], + [enable building of test GUI (default n)])], + [testgui_enabled=$enableval], + [testgui_enabled='no']) +AM_CONDITIONAL([BUILD_TESTGUI], [test "x$testgui_enabled" != "xno"]) + +# Configure the MacOS TestGUI app bundle +rm -Rf testgui/TestGUI.app +mkdir -p testgui/TestGUI.app +cp -R ${srcdir}/testgui/TestGUI.app.in/* testgui/TestGUI.app +chmod -R u+w testgui/TestGUI.app +mkdir testgui/TestGUI.app/Contents/MacOS/ + +if test "x$testgui_enabled" != "xno"; then + if test "x$os" = xdarwin; then + # On Mac OS, do not use pkg-config. + AC_CHECK_PROG([foxconfig], [fox-config], [fox-config], false) + if test "x$foxconfig" = "xfalse"; then + hidapi_prog_error fox-config "FOX Toolkit" + fi + LIBS_TESTGUI="${LIBS_TESTGUI} `$foxconfig --libs`" + LIBS_TESTGUI="${LIBS_TESTGUI} -framework Cocoa -L/usr/X11R6/lib" + CFLAGS_TESTGUI="${CFLAGS_TESTGUI} `$foxconfig --cflags`" + OBJCFLAGS="${OBJCFLAGS} -x objective-c++" + elif test "x$os" = xwindows; then + # On Windows, just set the paths for Fox toolkit + if test "x$win_implementation" = xmingw; then + CFLAGS_TESTGUI="-I\$(srcdir)/../../hidapi-externals/fox/include -g -c" + LIBS_TESTGUI=" -mwindows \$(srcdir)/../../hidapi-externals/fox/lib/libFOX-1.6.a -lgdi32 -Wl,--enable-auto-import -static-libgcc -static-libstdc++ -lkernel32" + else + # Cygwin + CFLAGS_TESTGUI="-DWIN32 -I\$(srcdir)/../../hidapi-externals/fox/include -g -c" + LIBS_TESTGUI="\$(srcdir)/../../hidapi-externals/fox/lib/libFOX-cygwin-1.6.a -lgdi32 -Wl,--enable-auto-import -static-libgcc -static-libstdc++ -lkernel32" + fi + else + # On Linux and FreeBSD platforms, use pkg-config to find fox. + PKG_CHECK_MODULES([fox], [fox17], [], [PKG_CHECK_MODULES([fox], [fox])]) + LIBS_TESTGUI="${LIBS_TESTGUI} $fox_LIBS" + if test "x$os" = xfreebsd; then + LIBS_TESTGUI="${LIBS_TESTGUI} -L/usr/local/lib" + fi + CFLAGS_TESTGUI="${CFLAGS_TESTGUI} $fox_CFLAGS" + fi +fi +AC_SUBST([LIBS_TESTGUI]) +AC_SUBST([CFLAGS_TESTGUI]) +AC_SUBST([backend]) + +# OS info for Automake +AM_CONDITIONAL(OS_LINUX, test "x$os" = xlinux) +AM_CONDITIONAL(OS_DARWIN, test "x$os" = xdarwin) +AM_CONDITIONAL(OS_FREEBSD, test "x$os" = xfreebsd) +AM_CONDITIONAL(OS_KFREEBSD, test "x$os" = xkfreebsd) +AM_CONDITIONAL(OS_HAIKU, test "x$os" = xhaiku) +AM_CONDITIONAL(OS_WINDOWS, test "x$os" = xwindows) + +AC_CONFIG_HEADERS([config.h]) + +if test "x$os" = "xlinux"; then + AC_CONFIG_FILES([pc/hidapi-hidraw.pc]) + AC_CONFIG_FILES([pc/hidapi-libusb.pc]) +else + AC_CONFIG_FILES([pc/hidapi.pc]) +fi + +AC_SUBST(LTLDFLAGS) + +AC_CONFIG_FILES([Makefile \ + hidtest/Makefile \ + libusb/Makefile \ + linux/Makefile \ + mac/Makefile \ + testgui/Makefile \ + windows/Makefile]) +AC_OUTPUT diff --git a/hidapi/dist/hidapi.podspec b/hidapi/dist/hidapi.podspec new file mode 100644 index 00000000..b2f061b7 --- /dev/null +++ b/hidapi/dist/hidapi.podspec @@ -0,0 +1,31 @@ +Pod::Spec.new do |spec| + + spec.name = "hidapi" + spec.version = File.read('../VERSION') + spec.summary = "A Simple library for communicating with USB and Bluetooth HID devices on Linux, Mac and Windows." + + spec.description = <<-DESC + HIDAPI is a multi-platform library which allows an application to interface with USB and Bluetooth HID-Class devices on Windows, Linux, FreeBSD, and macOS. HIDAPI can be either built as a shared library (.so, .dll or .dylib) or can be embedded directly into a target application by adding a single source file (per platform) and a single header. + DESC + + spec.homepage = "https://github.com/libusb/hidapi" + + spec.license = { :type=> "GNU GPLv3 or BSD or HIDAPI original", :file => "LICENSE.txt" } + + spec.authors = { "Alan Ott" => "alan@signal11.us", + "Ludovic Rousseau" => "rousseau@debian.org", + "libusb/hidapi Team" => "https://github.com/libusb/hidapi/blob/master/AUTHORS.txt", + } + + spec.platform = :osx + spec.osx.deployment_target = "10.7" + + spec.source = { :git => "https://github.com/libusb/hidapi.git", :tag => "hidapi-#{spec.version}" } + + spec.source_files = "mac/hid.c", "hidapi/hidapi.h" + + spec.public_header_files = "hidapi/hidapi.h" + + spec.frameworks = "IOKit", "CoreFoundation", "AppKit" + +end diff --git a/hidapi/documentation/cmake-gui-drop-down.png b/hidapi/documentation/cmake-gui-drop-down.png new file mode 100644 index 0000000000000000000000000000000000000000..548abe8c1ea29d6a28fd94c1f29da043ded5b43c GIT binary patch literal 22316 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 LKetww$gF literal 0 HcmV?d00001 diff --git a/hidapi/documentation/cmake-gui-highlights.png b/hidapi/documentation/cmake-gui-highlights.png new file mode 100644 index 0000000000000000000000000000000000000000..228838ff7cf8449e4c0fa1f47c12e297d9039eca GIT binary patch literal 77327 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
aH8SwR7+bBRCtK#&Zx~(NZO^`an&0Q3^fq8fCk3 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