From 9325e207be8606d7d08f4897e9bdf8e7c8e9e251 Mon Sep 17 00:00:00 2001 From: Paul Dino Jones Date: Mon, 31 Oct 2022 15:11:02 +0000 Subject: [PATCH] monocoque initial commit --- .gitignore | 3 + .gitmodules | 3 + .valgrindrc | 109 ++++ CMakeLists.txt | 156 +++++ LICENSE.rst | 339 +++++++++++ README.md | 65 +++ conf/monocoque.config | 15 + conf/revburner.xml | 34 ++ src/arduino/shift_lights/shift_lights.ino | 95 +++ .../shift_lights/shift_lights.ino.orig | 93 +++ src/monocoque/devices/CMakeLists.txt | 22 + src/monocoque/devices/serial/arduino.c | 79 +++ src/monocoque/devices/serial/arduino.h | 11 + src/monocoque/devices/serialdevice.c | 35 ++ src/monocoque/devices/serialdevice.h | 27 + src/monocoque/devices/simdevice.c | 85 +++ src/monocoque/devices/simdevice.h | 34 ++ .../devices/sound/usb_generic_shaker.c | 128 ++++ .../devices/sound/usb_generic_shaker.h | 9 + src/monocoque/devices/sounddevice.c | 38 ++ src/monocoque/devices/sounddevice.h | 45 ++ src/monocoque/devices/tachdevice.c | 80 +++ src/monocoque/devices/tachdevice.h | 31 + src/monocoque/devices/usb/revburner.c | 69 +++ src/monocoque/devices/usb/revburner.h | 8 + src/monocoque/devices/usbdevice.c | 49 ++ src/monocoque/devices/usbdevice.h | 30 + src/monocoque/gameloop/CMakeLists.txt | 11 + src/monocoque/gameloop/gameloop.c | 218 +++++++ src/monocoque/gameloop/gameloop.h | 4 + src/monocoque/gameloop/tachconfig.c | 190 ++++++ src/monocoque/gameloop/tachconfig.h | 9 + src/monocoque/helper/CMakeLists.txt | 13 + src/monocoque/helper/confighelper.c | 237 ++++++++ src/monocoque/helper/confighelper.h | 88 +++ src/monocoque/helper/dirhelper.c | 202 +++++++ src/monocoque/helper/dirhelper.h | 12 + src/monocoque/helper/parameters.c | 153 +++++ src/monocoque/helper/parameters.h | 44 ++ src/monocoque/monocoque.c | 233 ++++++++ src/monocoque/simulatorapi/CMakeLists.txt | 10 + src/monocoque/simulatorapi/ac.h | 21 + src/monocoque/simulatorapi/simapi | 1 + src/monocoque/simulatorapi/simdata.h | 17 + src/monocoque/simulatorapi/simmapper.c | 97 ++++ src/monocoque/simulatorapi/simmapper.h | 25 + src/monocoque/simulatorapi/test.h | 6 + src/monocoque/slog/CMakeLists.txt | 6 + src/monocoque/slog/slog.c | 546 ++++++++++++++++++ src/monocoque/slog/slog.h | 195 +++++++ tests/getmem.c | 45 ++ tests/hidtest.c | 233 ++++++++ tests/pa_devs.c | 269 +++++++++ tests/patest_longsine.c | 282 +++++++++ tests/revburnerparsetest.c | 94 +++ tests/runmemtest.sh | 32 + tests/setmem.c | 110 ++++ tests/setsimdata.c | 62 ++ tests/sharedmemoryproducer.c | 87 +++ tests/simlighttest.c | 106 ++++ tests/test.bin | Bin 0 -> 17 bytes tests/testlibusb.c | 381 ++++++++++++ tests/testrevburner.c | 532 +++++++++++++++++ tests/usesharedmemory.c | 88 +++ udev/69-monocoque.rules | 1 + 65 files changed, 6352 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 .valgrindrc create mode 100644 CMakeLists.txt create mode 100644 LICENSE.rst create mode 100644 README.md create mode 100644 conf/monocoque.config create mode 100644 conf/revburner.xml create mode 100644 src/arduino/shift_lights/shift_lights.ino create mode 100644 src/arduino/shift_lights/shift_lights.ino.orig create mode 100644 src/monocoque/devices/CMakeLists.txt create mode 100644 src/monocoque/devices/serial/arduino.c create mode 100644 src/monocoque/devices/serial/arduino.h create mode 100644 src/monocoque/devices/serialdevice.c create mode 100644 src/monocoque/devices/serialdevice.h create mode 100644 src/monocoque/devices/simdevice.c create mode 100644 src/monocoque/devices/simdevice.h create mode 100644 src/monocoque/devices/sound/usb_generic_shaker.c create mode 100644 src/monocoque/devices/sound/usb_generic_shaker.h create mode 100644 src/monocoque/devices/sounddevice.c create mode 100644 src/monocoque/devices/sounddevice.h create mode 100644 src/monocoque/devices/tachdevice.c create mode 100644 src/monocoque/devices/tachdevice.h create mode 100644 src/monocoque/devices/usb/revburner.c create mode 100644 src/monocoque/devices/usb/revburner.h create mode 100644 src/monocoque/devices/usbdevice.c create mode 100644 src/monocoque/devices/usbdevice.h create mode 100644 src/monocoque/gameloop/CMakeLists.txt create mode 100644 src/monocoque/gameloop/gameloop.c create mode 100644 src/monocoque/gameloop/gameloop.h create mode 100644 src/monocoque/gameloop/tachconfig.c create mode 100644 src/monocoque/gameloop/tachconfig.h create mode 100644 src/monocoque/helper/CMakeLists.txt create mode 100644 src/monocoque/helper/confighelper.c create mode 100644 src/monocoque/helper/confighelper.h create mode 100644 src/monocoque/helper/dirhelper.c create mode 100644 src/monocoque/helper/dirhelper.h create mode 100644 src/monocoque/helper/parameters.c create mode 100644 src/monocoque/helper/parameters.h create mode 100644 src/monocoque/monocoque.c create mode 100644 src/monocoque/simulatorapi/CMakeLists.txt create mode 100755 src/monocoque/simulatorapi/ac.h create mode 160000 src/monocoque/simulatorapi/simapi create mode 100644 src/monocoque/simulatorapi/simdata.h create mode 100644 src/monocoque/simulatorapi/simmapper.c create mode 100644 src/monocoque/simulatorapi/simmapper.h create mode 100755 src/monocoque/simulatorapi/test.h create mode 100644 src/monocoque/slog/CMakeLists.txt create mode 100644 src/monocoque/slog/slog.c create mode 100644 src/monocoque/slog/slog.h create mode 100644 tests/getmem.c create mode 100644 tests/hidtest.c create mode 100644 tests/pa_devs.c create mode 100644 tests/patest_longsine.c create mode 100644 tests/revburnerparsetest.c create mode 100755 tests/runmemtest.sh create mode 100644 tests/setmem.c create mode 100644 tests/setsimdata.c create mode 100644 tests/sharedmemoryproducer.c create mode 100644 tests/simlighttest.c create mode 100644 tests/test.bin create mode 100644 tests/testlibusb.c create mode 100644 tests/testrevburner.c create mode 100644 tests/usesharedmemory.c create mode 100644 udev/69-monocoque.rules diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e4329dd --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/build +/src/monocoque/tags +/src/monocoque/simulatorapi/simapi diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..7fad509 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/monocoque/simulatorapi/simapi"] + path = src/monocoque/simulatorapi/simapi + url = https://github.com/spacefreak18/simapi diff --git a/.valgrindrc b/.valgrindrc new file mode 100644 index 0000000..1cb21f3 --- /dev/null +++ b/.valgrindrc @@ -0,0 +1,109 @@ +{ + argtable arg_parse + Memcheck:Leak + ... + ... + ... + fun:arg_parse + ... + ... +} +{ + Portaudio clone1 + Memcheck:Cond + ... + ... + ... + ... + ... + fun:clone +} +{ + Portaudio clone2 + Memcheck:Cond + ... + ... + ... + ... + fun:clone +} +{ + Portaudio start_thread + Memcheck:Cond + ... + ... + ... + ... + ... + ... + ... + ... + ... + ... + ... + fun:start_thread +} +{ + Portaudio Pa_Initialize + Memcheck:Cond + ... + ... + ... + ... + ... + ... + ... + ... + ... + fun:Pa_Initialize + ... + ... +} +{ + Portaudio Pa_OpenStream + Memcheck:Cond + ... + ... + ... + ... + ... + ... + ... + ... + ... + fun:Pa_OpenStream + ... + ... +} +{ + Portaudio Pa_OpenStream2 + Memcheck:Cond + ... + ... + ... + ... + ... + ... + ... + fun:Pa_OpenStream + ... + ... + ... + ... +} +{ + Portaudio Pa_CloseStream + Memcheck:Cond + ... + ... + ... + ... + ... + ... + ... + ... + ... + fun:usb_generic_shaker_free + ... + ... +} diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..326d222 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,156 @@ + +# minimum CMake version required for C++20 support, among other things +cmake_minimum_required(VERSION 3.15) + +# detect if Monocoque is being used as a sub-project of another CMake project +if(NOT DEFINED PROJECT_NAME) + set(MONOCOQUE_SUBPROJECT OFF) +else() + set(MONOCOQUE_SUBPROJECT ON) +endif() + + +SET_SOURCE_FILES_PROPERTIES( src/monocoque.c PROPERTIES LANGUAGE C) +set(CMAKE_BUILD_TYPE Debug) + + +project(monocoque) + +set(CMAKE_EXE_LINKER_FLAGS "-Wl,--no-as-needed -ldl") +set(LIBUSB_INCLUDE_DIR /usr/include/libusb-1.0) +set(LIBXML_INCLUDE_DIR /usr/include/libxml2) + +FIND_PATH(LIBUSB_INCLUDE_DIR libusb.h + HINTS $ENV{LIBUSB_ROOT} + PATHS ${PC_LIBUSB_INCLUDEDIR} ${PC_LIBUSB_INCLUDE_DIRS} + PATH_SUFFIXES include) + +FIND_LIBRARY(LIBUSB_LIBRARY NAMES usb-1.0 + HINTS $ENV{LIBUSB_ROOT} + PATHS ${PC_LIBUSB_LIBDIR} ${PC_LIBUSB_LIBRARY_DIRS} + PATH_SUFFIXES lib) + +set(HIDAPI_WITH_LIBUSB TRUE) # surely will be used only on Linux +set(BUILD_SHARED_LIBS TRUE) # HIDAPI as static library on all platforms + +add_subdirectory(src/monocoque/gameloop) +add_subdirectory(src/monocoque/simulatorapi) +add_subdirectory(src/monocoque/helper) +add_subdirectory(src/monocoque/devices) +add_subdirectory(src/monocoque/slog) + +add_executable(monocoque src/monocoque/monocoque.c) +target_include_directories(monocoque PUBLIC config ${LIBUSB_INCLUDE_DIR} ${LIBXML_INCLUDE_DIR}) +target_link_libraries(monocoque m ${LIBUSB_LIBRARY} hidapi-libusb portaudio serialport xml2 argtable2 config gameloop helper devices slog simulatorapi) + +add_executable(listusb tests/testlibusb.c) +target_include_directories(listusb PUBLIC ${LIBUSB_INCLUDE_DIR}) +target_link_libraries(listusb ${LIBUSB_LIBRARY} portaudio) +add_test(listusb list-usb-devices listusb) + +add_executable(testrevburner tests/testrevburner.c) +target_include_directories(testrevburner PUBLIC ${LIBUSB_INCLUDE_DIR}) +target_link_libraries(testrevburner ${LIBUSB_LIBRARY}) +add_test(testrevburner testrevburner) + +add_executable(listsound tests/pa_devs.c) +target_include_directories(listsound PUBLIC) +target_link_libraries(listsound m portaudio) +add_test(list-sound-devices listsound) + +add_executable(longsine tests/patest_longsine.c) +target_include_directories(longsine PUBLIC ${LIBUSB_INCLUDE_DIR} ${LIBXML_INCLUDE_DIR}) +target_link_libraries(longsine ${LIBUSB_LIBRARY} m portaudio) +add_test(longsine longsine) + +add_executable(parserevburnerxml tests/revburnerparsetest.c) +target_include_directories(parserevburnerxml PUBLIC ${LIBXML_INCLUDE_DIR}) +target_link_libraries(parserevburnerxml ${LIBUSB_LIBRARY} portaudio xml2) +add_test(parserevburnerxml parserevburnerxml) + +add_executable(setmem tests/setmem.c) +target_include_directories(setmem PUBLIC) +target_link_libraries(setmem) +add_test(setmem setmem) + +add_executable(getmem tests/getmem.c) +target_include_directories(getmem PUBLIC) +target_link_libraries(getmem) +add_test(getmem getmem) + +add_executable(setsimdata tests/setsimdata.c) +target_include_directories(setsimdata PUBLIC) +target_link_libraries(setsimdata) +add_test(setsimdata setsimdata) + +add_executable(hidtest tests/hidtest.c) +target_include_directories(hidtest PUBLIC ${LIBUSB_INCLUDE_DIR}) +target_link_libraries(hidtest ${LIBUSB_LIBRARY} hidapi-libusb) +add_test(hidtest hidtest) + +add_executable(simlighttest tests/simlighttest.c) +target_include_directories(simlighttest PUBLIC) +target_link_libraries(simlighttest serialport) +add_test(simlighttest simlighttest) + + +# used for enabling additional compiler options if supported +include(CheckCXXCompilerFlag) + +function(enable_cxx_compiler_flag_if_supported flag) + message(STATUS "[monocoque] Checking if compiler supports warning flag '${flag}'") + check_cxx_compiler_flag("${flag}" flag_supported) + if(flag_supported) + message(STATUS "[monocoque] Enabling warning flag '${flag}'") + target_compile_options(monocoque INTERFACE "${flag}") + endif() + unset(flag_supported CACHE) +endfunction() + +# enable a large amount of extra warnings, regardless of build mode +if (MSVC) # MSVC supports different warning options to GCC/Clang + enable_cxx_compiler_flag_if_supported("/W3") # set warning level 3 + # if tests are enabled, enable converting all warnings to errors too + if (ENABLE_TESTS) + # add_compile_options(/WX) + enable_cxx_compiler_flag_if_supported("/WX") + endif() +else() # GCC/Clang warning option + # NOTE: GCC and Clang support most of the same options, but neither supports all + # of the others'. By only enabling them if supported, we get graceful failure + # when trying to enable unsupported flags + # e.g. at the time of writing, GCC does not support -Wdocumentation + # + # enable all warnings about 'questionable constructs' + enable_cxx_compiler_flag_if_supported("-Wall") + # issue 'pedantic' warnings for strict ISO compliance + enable_cxx_compiler_flag_if_supported("-pedantic") + # enable 'extra' strict warnings + enable_cxx_compiler_flag_if_supported("-Wextra") + # enable sign conversion warnings + enable_cxx_compiler_flag_if_supported("-Wsign-conversion") + # enable warnings about mistakes in Doxygen documentation + enable_cxx_compiler_flag_if_supported("-Wdocumentation") + # if tests are enabled, enable converting all warnings to errors too + if (ENABLE_TESTS) + enable_cxx_compiler_flag_if_supported("-Werror") + # exclude the following kinds of warnings from being converted into errors + # unknown-pragma is useful to have as a warning but not as an error, if you have + # pragmas which are for the consumption of one compiler only + enable_cxx_compiler_flag_if_supported("-Wno-error=unknown-pragmas") + # unused variable and function warnings are helpful but we don't need them as errors + enable_cxx_compiler_flag_if_supported("-Wno-error=unused-function") + enable_cxx_compiler_flag_if_supported("-Wno-error=unused-variable") + enable_cxx_compiler_flag_if_supported("-Wno-error=unused-parameter") + enable_cxx_compiler_flag_if_supported("-Wno-error=unused-private-field") + enable_cxx_compiler_flag_if_supported("-Wno-error=unused-but-set-variable") + endif() +endif() + +# library +# unit tests --only enable if requested AND we're not building as a sub-project +if(ENABLE_TESTS AND NOT MONOCOQUE_SUBPROJECT) + message(STATUS "[monocoque] Unit Tests Enabled") + add_subdirectory(tests) + enable_testing() +endif() diff --git a/LICENSE.rst b/LICENSE.rst new file mode 100644 index 0000000..7dfe0db --- /dev/null +++ b/LICENSE.rst @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + 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 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..f1ba56d --- /dev/null +++ b/README.md @@ -0,0 +1,65 @@ +# Monocoque +``` +___ |/ /____________________________________ ____ ______ +__ /|_/ /_ __ \_ __ \ __ \ ___/ __ \ __ `/ / / / _ \ +_ / / / / /_/ / / / / /_/ / /__ / /_/ / /_/ // /_/ // __/ +/_/ /_/ \____//_/ /_/\____/\___/ \____/\__, / \__,_/ \___/ + /_/ +``` +Cross Platform device manager for driving and flight simulators, for use with common simulator software titles. + +## Features +- Updates at 120 frames per seconds. +- Modular design for support with various titles and devices. +- Supports bass shakers, tachometers, simlights, simwind etc, through usb and arduino serial. +- Tachometer support is currently limited to the Revburner model. Supports existing revburner xml configuration files. +- Includes utility to configure revburner tachometer +- Can send data to any serial device. So far only tested with arduino. Includes sample arduino sketch for sim lights. +- The support for haptic bass shakers is limited and needs the most work. So far the engine rev is a simple sine wave, which I find convincing. The gear shift event works but not convincing enough for me. + +## Dependencies +- libserialport - arduino serial devices +- hidapi - usb hid devices +- libusb - used by hidapi +- portaudio - sound devices (haptic bass shakers) +- libenet - UDP support +- libxml2 +- argtable2 +- libconfig +- slog (static) +- [wine-linux-shm-adapter](https://github.com/spacefreak18/wine-linux-shm-adapter) +- [simapi](https://github.com/spacefreak18/simapi) + +## Building +This code depends on the shared memory data headers in the simapi [repo](https://github.com/spacefreak18/simapi). When pulling lastest if the submodule does not download run: +``` +git submodule sync --recursive +git submodule update --init --recursive +``` +Then to compile simply: +``` +mkdir build; cd build +cmake .. +make +``` +## Testing + +### Static Analysis +``` + mkdir build; cd build + make clean + CFLAGS=-fanalyzer cmake .. + make +``` +### Valgrind +``` + cd build + valgrind -v --leak-check=full --show-leak-kinds=all --suppressions=../.valgrindrc ./monocoque play +``` + +## ToDo + - more memory testing + - handling null mallocs + - move config code around + - cleanup tests which are basically just copies of the example from their respective projects + - much, much more diff --git a/conf/monocoque.config b/conf/monocoque.config new file mode 100644 index 0000000..233a58b --- /dev/null +++ b/conf/monocoque.config @@ -0,0 +1,15 @@ + +devices = ( { device = "USB"; + type = "Tachometer"; + devid = "98FD:83AC"; + subtype = "Revburner"; + granularity = 4; + config = "/home/paul/.config/monocoque/revburner1.xml"; }, + { device = "Sound"; + type = "Shaker" + devid = "98FD:83AC"; + config = "shaker1.config"; }, + { device = "Serial"; + type = "ShiftLights"; + config = "None"; + devpath = "/dev/ttyACM0"; } ); diff --git a/conf/revburner.xml b/conf/revburner.xml new file mode 100644 index 0000000..c0ef228 --- /dev/null +++ b/conf/revburner.xml @@ -0,0 +1,34 @@ + + + + + 300 + 43600 + + + 500 + 48800 + + + 1000 + 54700 + + + 1500 + 58600 + + + 2000 + 60100 + + + 2500 + 61100 + + + 3000 + 61900 + + + 3000 + diff --git a/src/arduino/shift_lights/shift_lights.ino b/src/arduino/shift_lights/shift_lights.ino new file mode 100644 index 0000000..4a052e6 --- /dev/null +++ b/src/arduino/shift_lights/shift_lights.ino @@ -0,0 +1,95 @@ +#include +#include "../../monocoque/simulatorapi/simdata.h" + +#define BYTE_SIZE sizeof(SimData) + +#define LED_PIN 7 +#define NUM_LEDS 6 +#define BRIGHTNESS 40 + +CRGB leds[NUM_LEDS]; +SimData sd; +int maxrpm = 0; +int rpm = 0; +int numlights = NUM_LEDS; +int pin = LED_PIN; +int lights[6]; + + +void setup() +{ + Serial.begin(9600); + FastLED.addLeds(leds, NUM_LEDS); + FastLED.setMaxPowerInVoltsAndMilliamps(5, 500); + FastLED.setBrightness(BRIGHTNESS); + for (int i = 0; i < numlights; i++) + { + leds[i] = CRGB ( 0, 0, 0); + lights[i] = 0; + } + FastLED.clear(); + + sd.rpms = 0; + sd.maxrpm = 6500; + sd.altitude = 10; + sd.pulses = 40000; + sd.velocity = 10; +} + +void loop() +{ + int l = 0; + char buff[BYTE_SIZE]; + + if (Serial.available() >= BYTE_SIZE) + { + Serial.readBytes(buff, BYTE_SIZE); + memcpy(&sd, &buff, BYTE_SIZE); + rpm = sd.rpms; + maxrpm = sd.maxrpm; + + } + + + while (l < numlights) + { + lights[l] = 0; + l++; + } + l = -1; + int rpmlights = 0; + while (rpm > rpmlights) + { + if (l>=0) + { + lights[l] = 1; + } + l++; + rpmlights = rpmlights + (((maxrpm-250)/numlights)); + } + + l = 0; + FastLED.clear(); + while (l < numlights) + { + + if (l >= numlights / 2) + { + leds[l] = CRGB ( 0, 0, 255); + } + if (l < numlights / 2) + { + leds[l] = CRGB ( 0, 255, 0); + } + if (l == numlights - 1) + { + leds[l] = CRGB ( 255, 0, 0); + } + if (lights[l] <= 0) + { + leds[l] = CRGB ( 0, 0, 0); + } + FastLED.show(); + l++; + } +} diff --git a/src/arduino/shift_lights/shift_lights.ino.orig b/src/arduino/shift_lights/shift_lights.ino.orig new file mode 100644 index 0000000..4bd7cd6 --- /dev/null +++ b/src/arduino/shift_lights/shift_lights.ino.orig @@ -0,0 +1,93 @@ +#include +#include "../../monocoque/simulatorapi/simdata.h" + +#define BYTE_SIZE sizeof(SimData) + +#define LED_PIN 7 +#define NUM_LEDS 6 + +CRGB leds[NUM_LEDS]; +SimData sd; +int maxrpm = 0; +int rpm = 0; +int numlights = NUM_LEDS; +int pin = LED_PIN; +int lights[6]; + + +void setup() { + + Serial.begin(9600); + FastLED.addLeds(leds, NUM_LEDS); + FastLED.setMaxPowerInVoltsAndMilliamps(5, 500); + FastLED.setBrightness(40); + for (int i = 0; i < numlights; i++) + { + leds[i] = CRGB ( 0, 0, 0); + lights[i] = 0; + } + FastLED.clear(); + + sd.rpms = 0; + sd.maxrpm = 6500; + sd.altitude = 10; + sd.pulses = 40000; + sd.velocity = 10; +} + +void loop() { + + int l = 0; + char buff[BYTE_SIZE]; + + if (Serial.available() >= BYTE_SIZE) { + Serial.readBytes(buff, BYTE_SIZE); + memcpy(&sd, &buff, BYTE_SIZE); + rpm = sd.rpms; + maxrpm = sd.maxrpm; + + } + + + while (l < numlights) + { + lights[l] = 0; + l++; + } + l = -1; + int rpmlights = 0; + while (rpm > rpmlights) + { + if (l>=0) + { + lights[l] = 1; + } + l++; + rpmlights = rpmlights + (((maxrpm-250)/numlights)); + } + + l = 0; + FastLED.clear(); + while (l < numlights) + { + + if (l >= numlights / 2) + { + leds[l] = CRGB ( 0, 0, 255); + } + if (l < numlights / 2) + { + leds[l] = CRGB ( 0, 255, 0); + } + if (l == numlights - 1) + { + leds[l] = CRGB ( 255, 0, 0); + } + if (lights[l] <= 0) + { + leds[l] = CRGB ( 0, 0, 0); + } + FastLED.show(); + l++; + } +} diff --git a/src/monocoque/devices/CMakeLists.txt b/src/monocoque/devices/CMakeLists.txt new file mode 100644 index 0000000..11a819b --- /dev/null +++ b/src/monocoque/devices/CMakeLists.txt @@ -0,0 +1,22 @@ +set(devices_source_files + simdevice.h + simdevice.c + usbdevice.h + usbdevice.c + sounddevice.h + sounddevice.c + serialdevice.h + serialdevice.c + tachdevice.h + tachdevice.c + usb/revburner.h + usb/revburner.c + sound/usb_generic_shaker.h + sound/usb_generic_shaker.c + serial/arduino.h + serial/arduino.c +) + +include_directories("." "usb" "sound" "serial") + +add_library(devices STATIC ${devices_source_files}) diff --git a/src/monocoque/devices/serial/arduino.c b/src/monocoque/devices/serial/arduino.c new file mode 100644 index 0000000..1f27c36 --- /dev/null +++ b/src/monocoque/devices/serial/arduino.c @@ -0,0 +1,79 @@ +#include +#include +#include + +#include "arduino.h" +#include "../slog/slog.h" + +#define arduino_timeout 2000 + +int arduino_update(SerialDevice* serialdevice, SimData* simdata) +{ + int result = 1; + if (serialdevice->port) + { + result = check(sp_blocking_write(serialdevice->port, simdata, sizeof(SimData), arduino_timeout)); + } + + return result; +} + +int arduino_init(SerialDevice* serialdevice) +{ + slogi("initializing arduino serial device..."); + int error = 0; + char* port_name = "/dev/ttyACM0"; + slogd("Looking for port %s.\n", port_name); + error = check(sp_get_port_by_name(port_name, &serialdevice->port)); + if (error != 0) + { + return error; + } + + slogd("Opening port.\n"); + check(sp_open(serialdevice->port, SP_MODE_READ_WRITE)); + + slogd("Setting port to 9600 8N1, no flow control.\n"); + check(sp_set_baudrate(serialdevice->port, 9600)); + check(sp_set_bits(serialdevice->port, 8)); + check(sp_set_parity(serialdevice->port, SP_PARITY_NONE)); + check(sp_set_stopbits(serialdevice->port, 1)); + check(sp_set_flowcontrol(serialdevice->port, SP_FLOWCONTROL_NONE)); + + slogd("Successfully setup arduino serial device..."); + return 0; +} + +int arduino_free(SerialDevice* serialdevice) +{ + check(sp_close(serialdevice->port)); + sp_free_port(serialdevice->port); +} + +int check(enum sp_return result) +{ + /* For this example we'll just exit on any error by calling abort(). */ + char* error_message; + + switch (result) + { + case SP_ERR_ARG: + //printf("Error: Invalid argument.\n"); + return 1; + //abort(); + case SP_ERR_FAIL: + error_message = sp_last_error_message(); + printf("Error: Failed: %s\n", error_message); + sp_free_error_message(error_message); + abort(); + case SP_ERR_SUPP: + printf("Error: Not supported.\n"); + abort(); + case SP_ERR_MEM: + printf("Error: Couldn't allocate memory.\n"); + abort(); + case SP_OK: + default: + return result; + } +} diff --git a/src/monocoque/devices/serial/arduino.h b/src/monocoque/devices/serial/arduino.h new file mode 100644 index 0000000..061ac49 --- /dev/null +++ b/src/monocoque/devices/serial/arduino.h @@ -0,0 +1,11 @@ +#ifndef _ARDUINO_H +#define _ARDUINO_H + +#include "../serialdevice.h" + +int arduino_update(SerialDevice* serialdevice, SimData* simdata); +int arduino_init(SerialDevice* serialdevice); +int arduino_free(SerialDevice* serialdevice); +int check(enum sp_return result); + +#endif diff --git a/src/monocoque/devices/serialdevice.c b/src/monocoque/devices/serialdevice.c new file mode 100644 index 0000000..5c1b078 --- /dev/null +++ b/src/monocoque/devices/serialdevice.c @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +#include "serialdevice.h" +#include "serial/arduino.h" +#include "../helper/parameters.h" +#include "../simulatorapi/simdata.h" +#include "../slog/slog.h" + +int serialdev_update(SerialDevice* serialdevice, SimData* simdata) +{ + arduino_update(serialdevice, simdata); + return 0; +} + +int serialdev_free(SerialDevice* serialdevice) +{ + arduino_free(serialdevice); + return 0; +} + +int serialdev_init(SerialDevice* serialdevice) +{ + slogi("initializing serial device..."); + int error = 0; + + serialdevice->type = SERIALDEV_UNKNOWN; + serialdevice->type = SERIALDEV_ARDUINO; + + error = arduino_init(serialdevice); + + return error; +} diff --git a/src/monocoque/devices/serialdevice.h b/src/monocoque/devices/serialdevice.h new file mode 100644 index 0000000..6b34d1e --- /dev/null +++ b/src/monocoque/devices/serialdevice.h @@ -0,0 +1,27 @@ +#ifndef _SERIALDEVICE_H +#define _SERIALDEVICE_H + +#include +#include "../helper/parameters.h" +#include "../simulatorapi/simdata.h" + +typedef enum +{ + SERIALDEV_UNKNOWN = 0, + SERIALDEV_ARDUINO = 1 +} +SerialType; + +typedef struct +{ + int id; + SerialType type; + struct sp_port* port; +} +SerialDevice; + +int serialdev_update(SerialDevice* serialdevice, SimData* simdata); +int serialdev_init(SerialDevice* serialdevice); +int serialdev_free(SerialDevice* serialdevice); + +#endif diff --git a/src/monocoque/devices/simdevice.c b/src/monocoque/devices/simdevice.c new file mode 100644 index 0000000..c57bf0c --- /dev/null +++ b/src/monocoque/devices/simdevice.c @@ -0,0 +1,85 @@ +#include + +#include "simdevice.h" +#include "../helper/parameters.h" +#include "../helper/confighelper.h" +#include "../simulatorapi/simdata.h" +#include "../slog/slog.h" + +int devupdate(SimDevice* simdevice, SimData* simdata) +{ + if (simdevice->initialized==false) + { + return 0; + } + switch ( simdevice->type ) + { + case SIMDEV_USB : + usbdev_update(&simdevice->d.usbdevice, simdata); + break; + case SIMDEV_SOUND : + sounddev_update(&simdevice->d.sounddevice, simdata); + break; + case SIMDEV_SERIAL : + serialdev_update(&simdevice->d.serialdevice, simdata); + break; + } + return 0; +} + +int devfree(SimDevice* simdevice) +{ + + if (simdevice->initialized==false) + { + slogw("Attempt to free an uninitialized device"); + return MONOCOQUE_ERROR_INVALID_DEV; + } + switch ( simdevice->type ) + { + case SIMDEV_USB : + usbdev_free(&simdevice->d.usbdevice); + break; + case SIMDEV_SOUND : + sounddev_free(&simdevice->d.sounddevice); + break; + case SIMDEV_SERIAL : + serialdev_free(&simdevice->d.serialdevice); + break; + } + return 0; +} + +int devinit(SimDevice* simdevice, DeviceSettings* ds) +{ + slogi("initializing simdevice..."); + simdevice->initialized = false; + int err = 0; + + switch ( ds->dev_type ) + { + case SIMDEV_USB : + simdevice->type = SIMDEV_USB; + simdevice->d.usbdevice.type = USBDEV_UNKNOWN; + err = usbdev_init(&simdevice->d.usbdevice, ds); + break; + case SIMDEV_SOUND : + simdevice->type = SIMDEV_SOUND; + err = sounddev_init(&simdevice->d.sounddevice); + break; + case SIMDEV_SERIAL : + simdevice->type = SIMDEV_SERIAL; + err = serialdev_init(&simdevice->d.serialdevice); + break; + default : + sloge("Unknown device type"); + err = MONOCOQUE_ERROR_UNKNOWN_DEV; + break; + } + + if (err==0) + { + simdevice->initialized = true; + } + return err; +} diff --git a/src/monocoque/devices/simdevice.h b/src/monocoque/devices/simdevice.h new file mode 100644 index 0000000..06ac09f --- /dev/null +++ b/src/monocoque/devices/simdevice.h @@ -0,0 +1,34 @@ +#ifndef _SIMDEVICE_H +#define _SIMDEVICE_H + +#include + +#include "usbdevice.h" +#include "sounddevice.h" +#include "serialdevice.h" +#include "../helper/confighelper.h" +#include "../simulatorapi/simdata.h" + + +typedef struct +{ + int id; + bool initialized; + DeviceType type; + + union + { + USBDevice usbdevice; + SoundDevice sounddevice; + SerialDevice serialdevice; + } d; +} +SimDevice; + +int devupdate(SimDevice* simdevice, SimData* simdata); + +int devinit(SimDevice* simdevice, DeviceSettings* ds); + +int devfree(SimDevice* simdevice); + +#endif diff --git a/src/monocoque/devices/sound/usb_generic_shaker.c b/src/monocoque/devices/sound/usb_generic_shaker.c new file mode 100644 index 0000000..5d660d9 --- /dev/null +++ b/src/monocoque/devices/sound/usb_generic_shaker.c @@ -0,0 +1,128 @@ +#include +#include +#include +#include + +#include "portaudio.h" + +#include "usb_generic_shaker.h" +#include "../sounddevice.h" + +#define SAMPLE_RATE (48000) + +#ifndef M_PI +#define M_PI (3.14159265) +#endif + + + +int patestCallback(const void* inputBuffer, + void* outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void* userData) +{ + PATestData* data = (PATestData*)userData; + float* out = (float*)outputBuffer; + memset(out, 0, framesPerBuffer * 2 * sizeof(float)); + unsigned int i; + unsigned int n; + n = data->n; + (void) inputBuffer; /* Prevent unused argument warning. */ + + for( i=0; iamp * sin (2 * M_PI * ((float) n) / (float) SAMPLE_RATE); + + if ( data->gear_sound_data > 0 ) + { + if (n>=1764) + { + n=0; + } + } + else + { + if (n>=data->table_size) + { + n=0; + } + } + + + if ( data->gear_sound_data > 0 ) + { + // right channel only? + // i have my butt hooked up to right channel... make this configurable? + *out++ = v; + } + else + { + *out++ = v; + *out++ = v; + } + } + + data->n=n; + return 0; +} + +int usb_generic_shaker_free(SoundDevice* sounddevice) +{ + int err = 0; + err = Pa_CloseStream( sounddevice->stream ); + if( err != paNoError ) + { + err = Pa_Terminate(); + } + return err; +} + +int usb_generic_shaker_init(SoundDevice* sounddevice) +{ + PaError err; + err = paNoError; + + err = Pa_Initialize(); + if( err != paNoError ) + { + goto error; + } + + sounddevice->outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ + sounddevice->outputParameters.channelCount = 2; /* stereo output */ + sounddevice->outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */ + sounddevice->outputParameters.suggestedLatency = Pa_GetDeviceInfo( sounddevice->outputParameters.device )->defaultLowOutputLatency; + sounddevice->outputParameters.hostApiSpecificStreamInfo = NULL; + + err = Pa_OpenStream( &sounddevice->stream, + NULL, /* No input. */ + &sounddevice->outputParameters, /* As above. */ + SAMPLE_RATE, + 440, /* Frames per buffer. */ + paClipOff, /* No out of range samples expected. */ + patestCallback, + &sounddevice->sounddata ); + if( err != paNoError ) + { + goto error; + } + + + err = Pa_StartStream( sounddevice->stream ); + if( err != paNoError ) + { + goto error; + } + + return err; + +error: + Pa_Terminate(); + //fprintf( stderr, "An error occured while using the portaudio stream\n" ); + //fprintf( stderr, "Error number: %d\n", err ); + //fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} diff --git a/src/monocoque/devices/sound/usb_generic_shaker.h b/src/monocoque/devices/sound/usb_generic_shaker.h new file mode 100644 index 0000000..25b9045 --- /dev/null +++ b/src/monocoque/devices/sound/usb_generic_shaker.h @@ -0,0 +1,9 @@ +#ifndef _USB_GENERIC_SHAKER_H +#define _USB_GENERIC_SHAKER_H + +#include "../sounddevice.h" + +int usb_generic_shaker_init(SoundDevice* sounddevice); +int usb_generic_shaker_free(SoundDevice* sounddevice); + +#endif diff --git a/src/monocoque/devices/sounddevice.c b/src/monocoque/devices/sounddevice.c new file mode 100644 index 0000000..14a6f4d --- /dev/null +++ b/src/monocoque/devices/sounddevice.c @@ -0,0 +1,38 @@ +#include + +#include "sounddevice.h" +#include "sound/usb_generic_shaker.h" +#include "../simulatorapi/simdata.h" +#include "../helper/parameters.h" +#include "../slog/slog.h" + +int sounddev_update(SoundDevice* sounddevice, SimData* simdata) +{ + sounddevice->sounddata.table_size = 44100/(simdata->rpms/60); + + sounddevice->sounddata.gear_sound_data = 0; + if (sounddevice->sounddata.last_gear != simdata->gear) + { + sounddevice->sounddata.gear_sound_data = sounddevice->sounddata.amp; + } + sounddevice->sounddata.last_gear = simdata->gear; +} + +int sounddev_free(SoundDevice* sounddevice) +{ + return usb_generic_shaker_free(sounddevice); +} + +int sounddev_init(SoundDevice* sounddevice) +{ + slogi("initializing standalone sound device..."); + + sounddevice->sounddata.pitch = 1; + sounddevice->sounddata.pitch = 261.626; + sounddevice->sounddata.amp = 32; + sounddevice->sounddata.left_phase = sounddevice->sounddata.right_phase = 0; + sounddevice->sounddata.table_size = 44100/(100/60); + sounddevice->sounddata.last_gear = 0; + + usb_generic_shaker_init(sounddevice); +} diff --git a/src/monocoque/devices/sounddevice.h b/src/monocoque/devices/sounddevice.h new file mode 100644 index 0000000..ef6453b --- /dev/null +++ b/src/monocoque/devices/sounddevice.h @@ -0,0 +1,45 @@ +#ifndef _SOUNDDEVICE_H +#define _SOUNDDEVICE_H + +#include "portaudio.h" + +#include "../simulatorapi/simdata.h" +#include "../helper/parameters.h" + +typedef enum +{ + SOUNDDEV_UNKNOWN = 0, + SOUNDDEV_SHAKER = 1 +} +SoundType; + +#define MAX_TABLE_SIZE (6000) +typedef struct +{ + float sine[MAX_TABLE_SIZE]; + float pitch; + int last_gear; + int left_phase; + int right_phase; + int n; + int table_size; + int amp; + int gear_sound_data; +} +PATestData; + +typedef struct +{ + int id; + SoundType type; + PATestData sounddata; + PaStreamParameters outputParameters; + PaStream* stream; +} +SoundDevice; + +int sounddev_update(SoundDevice* sounddevice, SimData* simdata); +int sounddev_init(SoundDevice* sounddevice); +int sounddev_free(SoundDevice* sounddevice); + +#endif diff --git a/src/monocoque/devices/tachdevice.c b/src/monocoque/devices/tachdevice.c new file mode 100644 index 0000000..26bafdc --- /dev/null +++ b/src/monocoque/devices/tachdevice.c @@ -0,0 +1,80 @@ +#include +#include +#include + +#include "tachdevice.h" +#include "revburner.h" +#include "../../helper/confighelper.h" +#include "../../simulatorapi/simdata.h" +#include "../../slog/slog.h" + +int tachdev_update(TachDevice* tachdevice, SimData* simdata) +{ + // current plan is to just use the revburner xml format for other possible tachometer devices + // with that assumption this same logic is assumed the same for other tachometer devices + // the only difference then being in communication to the physical device + int pulses = simdata->pulses; + switch ( tachdevice->type ) + { + case TACHDEV_UNKNOWN : + case TACHDEV_REVBURNER : + + if (tachdevice->tachsettings.use_pulses == false) + { + slogt("Getting pulses for current tachometer revs"); + if (simdata->rpms < 500) + { + pulses = tachdevice->tachsettings.pulses_array[0]; + } + else + { + slogt("Tach settings size %i",tachdevice->tachsettings.size); + int el = simdata->rpms / 1000; + if (tachdevice->tachsettings.granularity > 0) + { + el = simdata->rpms / (1000 / tachdevice->tachsettings.granularity); + } + if (el >= tachdevice->tachsettings.size - 1) + { + el = tachdevice->tachsettings.size - 1; + } + slogt("Retrieveing element %i", el); + pulses = tachdevice->tachsettings.pulses_array[el]; + } + } + slogt("Settings tachometer pulses to %i", pulses); + revburner_update(tachdevice, pulses); + break; + } + + return 0; +} + +int tachdev_free(TachDevice* tachdevice) +{ + switch ( tachdevice->type ) + { + case TACHDEV_UNKNOWN : + case TACHDEV_REVBURNER : + revburner_update(tachdevice, 0); + revburner_free(tachdevice); + break; + } + + return 0; +} + +int tachdev_init(TachDevice* tachdevice, DeviceSettings* ds) +{ + slogi("initializing tachometer device..."); + int error = 0; + // detection of tach device model + tachdevice->type = TACHDEV_UNKNOWN; + tachdevice->type = TACHDEV_REVBURNER; + + tachdevice->tachsettings = ds->tachsettings; + + error = revburner_init(tachdevice); + + return error; +} diff --git a/src/monocoque/devices/tachdevice.h b/src/monocoque/devices/tachdevice.h new file mode 100644 index 0000000..c8ba246 --- /dev/null +++ b/src/monocoque/devices/tachdevice.h @@ -0,0 +1,31 @@ +#ifndef _TACHDEVICE_H +#define _TACHDEVICE_H + +#include +#include "../helper/confighelper.h" +#include "../simulatorapi/simdata.h" + +//typedef int (*tachdev_update)(int revs); + +typedef enum +{ + TACHDEV_UNKNOWN = 0, + TACHDEV_REVBURNER = 1 +} +TachType; + +typedef struct +{ + int id; + TachType type; + bool use_pulses; + hid_device* handle; + TachometerSettings tachsettings; +} +TachDevice; + +int tachdev_update(TachDevice* tachdevice, SimData* simdata); +int tachdev_init(TachDevice* tachdevice, DeviceSettings* ds); +int tachdev_free(TachDevice* tachdevice); + +#endif diff --git a/src/monocoque/devices/usb/revburner.c b/src/monocoque/devices/usb/revburner.c new file mode 100644 index 0000000..191d284 --- /dev/null +++ b/src/monocoque/devices/usb/revburner.c @@ -0,0 +1,69 @@ +#include + +#include + +#include "tachdevice.h" +#include "../slog/slog.h" + +const int buf_size = 65; + + +int revburner_update(TachDevice* tachdevice, int pulses) +{ + + int res = 0; + + unsigned char bytes[buf_size]; + for (int x = 0; x < buf_size; x++) + { + bytes[x] = 0x00; + } + if ( pulses > 0 ) + { + bytes[3] = (pulses >> 8) & 0xFF; + bytes[2] = pulses & 0xFF; + } + + + if (tachdevice->handle) + { + res = hid_write(tachdevice->handle, bytes, buf_size); + } + else + { + slogd("no handle"); + } + + return res; +} + +int revburner_free(TachDevice* tachdevice) +{ + int res = 0; + + hid_close(tachdevice->handle); + res = hid_exit(); + + return res; +} + +int revburner_init(TachDevice* tachdevice) +{ + slogi("initializing revburner tachometer..."); + //tachdevice->update_tachometer = revburner_device_update; + + int res = 0; + + res = hid_init(); + + tachdevice->handle = hid_open(0x4d8, 0x102, NULL); + + if (!tachdevice->handle) + { + sloge("Could not find attached RevBurner tachometer"); + res = hid_exit(); + return 1; + } + slogd("Found RevBurner Tachometer..."); + return res; +} diff --git a/src/monocoque/devices/usb/revburner.h b/src/monocoque/devices/usb/revburner.h new file mode 100644 index 0000000..b1a4e67 --- /dev/null +++ b/src/monocoque/devices/usb/revburner.h @@ -0,0 +1,8 @@ +#ifndef _REVBURNER_H +#define _REVBURNER_H + +int revburner_update(TachDevice* tachdevice, int pulses); +int revburner_init(TachDevice* tachdevice); +int revburner_free(TachDevice* tachdevice); + +#endif diff --git a/src/monocoque/devices/usbdevice.c b/src/monocoque/devices/usbdevice.c new file mode 100644 index 0000000..2f3ad24 --- /dev/null +++ b/src/monocoque/devices/usbdevice.c @@ -0,0 +1,49 @@ +#include +#include +#include + +#include "usbdevice.h" +#include "../helper/parameters.h" +#include "../simulatorapi/simdata.h" +#include "../slog/slog.h" + +int usbdev_update(USBDevice* usbdevice, SimData* simdata) +{ + switch ( usbdevice->type ) + { + case USBDEV_UNKNOWN : + case USBDEV_TACHOMETER : + tachdev_update(&usbdevice->u.tachdevice, simdata); + break; + } + + return 0; +} + +int usbdev_free(USBDevice* usbdevice) +{ + switch ( usbdevice->type ) + { + case USBDEV_UNKNOWN : + case USBDEV_TACHOMETER : + tachdev_free(&usbdevice->u.tachdevice); + break; + } + + return 0; +} + +int usbdev_init(USBDevice* usbdevice, DeviceSettings* ds) +{ + slogi("initializing usb device..."); + int error = 0; + switch ( usbdevice->type ) + { + case USBDEV_UNKNOWN : + case USBDEV_TACHOMETER : + error = tachdev_init(&usbdevice->u.tachdevice, ds); + break; + } + + return error; +} diff --git a/src/monocoque/devices/usbdevice.h b/src/monocoque/devices/usbdevice.h new file mode 100644 index 0000000..4355042 --- /dev/null +++ b/src/monocoque/devices/usbdevice.h @@ -0,0 +1,30 @@ +#ifndef _USBDEVICE_H +#define _USBDEVICE_H + +#include "tachdevice.h" +#include "../helper/confighelper.h" +#include "../simulatorapi/simdata.h" + +typedef enum +{ + USBDEV_UNKNOWN = 0, + USBDEV_TACHOMETER = 1 +} +USBType; + +typedef struct +{ + int id; + USBType type; + union + { + TachDevice tachdevice; + } u; +} +USBDevice; + +int usbdev_update(USBDevice* usbdevice, SimData* simdata); +int usbdev_init(USBDevice* usbdevice, DeviceSettings* ds); +int usbdev_free(USBDevice* usbdevice); + +#endif diff --git a/src/monocoque/gameloop/CMakeLists.txt b/src/monocoque/gameloop/CMakeLists.txt new file mode 100644 index 0000000..8a32d67 --- /dev/null +++ b/src/monocoque/gameloop/CMakeLists.txt @@ -0,0 +1,11 @@ +set(gameloop_source_files + gameloop.c + gameloop.h + tachconfig.c + tachconfig.h +) + +set(LIBXML_INCLUDE_DIR /usr/include/libxml2) +include_directories("." ${LIBXML_INCLUDE_DIR}) + +add_library(gameloop STATIC ${gameloop_source_files}) diff --git a/src/monocoque/gameloop/gameloop.c b/src/monocoque/gameloop/gameloop.c new file mode 100644 index 0000000..915fa92 --- /dev/null +++ b/src/monocoque/gameloop/gameloop.c @@ -0,0 +1,218 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "gameloop.h" +#include "../helper/parameters.h" +#include "../helper/confighelper.h" +#include "../devices/simdevice.h" +#include "../simulatorapi/simdata.h" +#include "../simulatorapi/simmapper.h" +#include "../slog/slog.h" + +#define DEFAULT_UPDATE_RATE 120.0 + +int showstats(SimData* simdata) +{ + printf("\r"); + for (int i=0; i<4; i++) + { + if (i==0) + { + fputc('s', stdout); + fputc('p', stdout); + fputc('e', stdout); + fputc('e', stdout); + fputc('d', stdout); + fputc(':', stdout); + fputc(' ', stdout); + + int speed = simdata->velocity; + int digits = 0; + while (speed > 0) + { + int mod = speed % 10; + speed = speed / 10; + digits++; + } + speed = simdata->velocity; + int s[digits]; + int digit = 0; + while (speed > 0) + { + int mod = speed % 10; + s[digit] = mod; + speed = speed / 10; + digit++; + } + speed = simdata->velocity; + digit = digits; + while (digit > 0) + { + fputc(s[digit-1]+'0', stdout); + digit--; + } + fputc(' ', stdout); + } + if (i==1) + { + fputc('r', stdout); + fputc('p', stdout); + fputc('m', stdout); + fputc('s', stdout); + fputc(':', stdout); + fputc(' ', stdout); + + int rpms = simdata->rpms; + int digits = 0; + while (rpms > 0) + { + int mod = rpms % 10; + rpms = rpms / 10; + digits++; + } + rpms = simdata->rpms; + int s[digits]; + int digit = 0; + while (rpms > 0) + { + int mod = rpms % 10; + s[digit] = mod; + rpms = rpms / 10; + digit++; + } + rpms = simdata->rpms; + digit = digits; + while (digit > 0) + { + fputc(s[digit-1]+'0', stdout); + digit--; + } + fputc(' ', stdout); + } + if (i==2) + { + fputc('g', stdout); + fputc('e', stdout); + fputc('a', stdout); + fputc('r', stdout); + fputc(':', stdout); + fputc(' ', stdout); + fputc(simdata->gear+'0', stdout); + fputc(' ', stdout); + } + if (i==3) + { + fputc('a', stdout); + fputc('l', stdout); + fputc('t', stdout); + fputc(':', stdout); + fputc(' ', stdout); + + int alt = simdata->altitude; + int digits = 0; + while (alt > 0) + { + int mod = alt % 10; + alt = alt / 10; + digits++; + } + alt = simdata->altitude; + int s[digits]; + int digit = 0; + while (alt > 0) + { + int mod = alt % 10; + s[digit] = mod; + alt = alt / 10; + digit++; + } + alt = simdata->altitude; + digit = digits; + while (digit > 0) + { + fputc(s[digit-1]+'0', stdout); + digit--; + } + fputc(' ', stdout); + } + } + fflush(stdout); +} + +int looper(SimDevice* devices[], int numdevices, Simulator simulator) +{ + + slogi("preparing game loop with %i devices...", numdevices); + SimData* simdata = malloc(sizeof(SimData)); + SimMap* simmap = malloc(sizeof(SimMap)); + + int error = siminit(simdata, simmap, simulator); + + if (error != MONOCOQUE_ERROR_NONE) + { + return error; + } + + struct termios newsettings, canonicalmode; + tcgetattr(0, &canonicalmode); + newsettings = canonicalmode; + newsettings.c_lflag &= (~ICANON & ~ECHO); + newsettings.c_cc[VMIN] = 1; + newsettings.c_cc[VTIME] = 0; + tcsetattr(0, TCSANOW, &newsettings); + char ch; + struct pollfd mypoll = { STDIN_FILENO, POLLIN|POLLPRI }; + + double update_rate = DEFAULT_UPDATE_RATE; + int t=0; + int go = true; + while (go == true) + { + simdatamap(simdata, simmap, simulator); + showstats(simdata); + t++; + if(simdata->rpms<250) + { + simdata->rpms=250; + } + for (int x = 0; x < numdevices; x++) + { + if (devices[x]->type == SIMDEV_SERIAL) + { + if(t>=update_rate) + { + devupdate(devices[x], simdata); + } + } + else + { + devupdate(devices[x], simdata); + } + + } + if(t>=update_rate) + { + t=0; + } + if( poll(&mypoll, 1, 1000.0/update_rate) ) + { + scanf("%c", &ch); + if(ch == 'q') + { + go = false; + } + } + } + + tcsetattr(0, TCSANOW, &canonicalmode); + + free(simdata); + free(simmap); + + return 0; +} diff --git a/src/monocoque/gameloop/gameloop.h b/src/monocoque/gameloop/gameloop.h new file mode 100644 index 0000000..a37822c --- /dev/null +++ b/src/monocoque/gameloop/gameloop.h @@ -0,0 +1,4 @@ +#include "../devices/simdevice.h" +#include "../helper/parameters.h" + +int looper (SimDevice* devices[], int numdevices, Simulator simulator); diff --git a/src/monocoque/gameloop/tachconfig.c b/src/monocoque/gameloop/tachconfig.c new file mode 100644 index 0000000..df1bb03 --- /dev/null +++ b/src/monocoque/gameloop/tachconfig.c @@ -0,0 +1,190 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../devices/simdevice.h" +#include "../simulatorapi/simdata.h" +#include "../slog/slog.h" + +#define DEFAULT_UPDATE_RATE 30.0 + +int WriteXmlFromArrays(int nodes, int rpm_array[], int values_array[], int maxrevs, const char* save_file) +{ + xmlDocPtr doc = NULL; + xmlNodePtr rootnode = NULL, onenode = NULL, settingsvaluenode = NULL, maxdisplayvaluenode = NULL; + char buff[256]; + int i, j; + + doc = xmlNewDoc(BAD_CAST "1.0"); + + rootnode = xmlNewNode(NULL, BAD_CAST "TachometerSettings"); + xmlDocSetRootElement(doc, rootnode); + settingsvaluenode = xmlNewNode(NULL, BAD_CAST "SettingsValues"); + + for(int i = 0; i< nodes; ++i) + { + onenode = xmlNewNode(NULL, BAD_CAST "SettingsItem"); + + char value[10]; + sprintf(value, "%d", values_array[i]); + + char rpm[10]; + sprintf(rpm, "%d", rpm_array[i]); + + xmlNewChild(onenode, NULL, BAD_CAST "Value", BAD_CAST value); + xmlNewChild(onenode, NULL, BAD_CAST "TimeValue", BAD_CAST rpm); + xmlAddChild(settingsvaluenode, onenode); + } + + + xmlAddChild(rootnode, settingsvaluenode); + char revs[10]; + sprintf(revs, "%d", maxrevs); + maxdisplayvaluenode = xmlNewChild(rootnode, NULL, BAD_CAST "MaxDisplayValue", BAD_CAST revs); + + xmlSaveFormatFileEnc(save_file, doc, "UTF-8", 1); + + xmlFreeDoc(doc); + + xmlCleanupParser(); + + return 0; +} + +int config_tachometer(int max_revs, int granularity, const char* save_file, SimDevice* simdevice, SimData* simdata) +{ + int pulses = 0; + int nodes = 0; + + if (max_revs<2000) + { + fprintf(stderr, "revs must be at least 2000\n"); + return 0; + } + + int increment = 1000; + if (granularity == 2) + { + increment = 500; + } + if (granularity == 4) + { + increment = 250; + } + + nodes = ((max_revs/1000)*granularity)+1; + if (granularity >= 4) + { + nodes--; + } + int rpm_array[nodes]; + int values_array[nodes]; + + values_array[0]=250; + values_array[1]=increment; + if (granularity >= 4) + { + values_array[0] = increment; + values_array[1]= increment * 2; + } + + for(int i=2; i to increase, < to decrease, and Return to accept (m increases by 1000, n decreases by 1000, c increases by 100, z decreases by 100...\n", values_array[i]); + + struct pollfd mypoll = { STDIN_FILENO, POLLIN|POLLPRI }; + double update_rate = DEFAULT_UPDATE_RATE; + + int go=1; + while (go>0) + { + + simdata->pulses = pulses; + devupdate(simdevice, simdata); + if( poll(&mypoll, 1, 1000.0/update_rate) ) + { + ch = ' '; + scanf("%c", &ch); + + if (ch == 'n') + { + pulses=pulses-1000; + } + if (ch == 'm') + { + pulses=pulses+1000; + } + if (ch == 'z') + { + pulses=pulses-100; + } + if (ch == 'c') + { + pulses=pulses+100; + } + if (ch == '<') + { + pulses--; + } + if (ch == '>') + { + pulses++; + } + + if (ch == '\n') + { + go=0; + + fprintf(stdout, "set pulses to %i\n", pulses); + rpm_array[i]=pulses; + + } + + + } + } + tcsetattr(0, TCSANOW, &canonicalmode); + } + + WriteXmlFromArrays(nodes, rpm_array, values_array, max_revs, save_file); + sleep(2); + simdata->pulses = 0; + devupdate(simdevice, simdata); + fflush(stdout); + + return 0; +} diff --git a/src/monocoque/gameloop/tachconfig.h b/src/monocoque/gameloop/tachconfig.h new file mode 100644 index 0000000..60574f4 --- /dev/null +++ b/src/monocoque/gameloop/tachconfig.h @@ -0,0 +1,9 @@ +#ifndef _TACHCONFIG_H +#define _TACHCONFIG_H + +#include "../devices/simdevice.h" +#include "../simulatorapi/simdata.h" + +int config_tachometer(int max_revs, int granularity, const char* save_file, SimDevice* simdevice, SimData* simdata); + +#endif diff --git a/src/monocoque/helper/CMakeLists.txt b/src/monocoque/helper/CMakeLists.txt new file mode 100644 index 0000000..8a7dfb9 --- /dev/null +++ b/src/monocoque/helper/CMakeLists.txt @@ -0,0 +1,13 @@ +set(helper_source_files + parameters.c + parameters.h + dirhelper.c + dirhelper.h + confighelper.c + confighelper.h +) + +set(LIBXML_INCLUDE_DIR /usr/include/libxml2) +include_directories("." ${LIBXML_INCLUDE_DIR}) + +add_library(helper STATIC ${helper_source_files}) diff --git a/src/monocoque/helper/confighelper.c b/src/monocoque/helper/confighelper.c new file mode 100644 index 0000000..7d51f40 --- /dev/null +++ b/src/monocoque/helper/confighelper.c @@ -0,0 +1,237 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include "confighelper.h" + +#include "../slog/slog.h" + +int strtogame(const char* game, MonocoqueSettings* ms) +{ + slogd("Checking for %s in list of supported simulators.", game); + if (strcmp(game, "ac") == 0) + { + slogd("Setting simulator to Assetto Corsa"); + ms->sim_name = SIMULATOR_ASSETTO_CORSA; + } + else + if (strcmp(game, "test") == 0) + { + slogd("Setting simulator to Test Data"); + ms->sim_name = SIMULATOR_MONOCOQUE_TEST; + } + else + { + slogi("%s does not appear to be a supported simulator.", game); + return MONOCOQUE_ERROR_INVALID_SIM; + } + return MONOCOQUE_ERROR_NONE; +} + +int strtodev(const char* device_type, DeviceSettings* ds) +{ + ds->is_valid = false; + if (strcmp(device_type, "USB") == 0) + { + ds->dev_type = SIMDEV_USB; + } + else + if (strcmp(device_type, "Sound") == 0) + { + ds->dev_type = SIMDEV_SOUND; + } + else + if (strcmp(device_type, "Serial") == 0) + { + ds->dev_type = SIMDEV_SERIAL; + } + else + { + ds->is_valid = false; + slogi("%s does not appear to be a valid device type, but attempting to continue with other devices", device_type); + return MONOCOQUE_ERROR_INVALID_DEV; + } + ds->is_valid = true; + return MONOCOQUE_ERROR_NONE; +} + +int strtodevtype(const char* device_subtype, DeviceSettings* ds) +{ + ds->is_valid = false; + if (strcmp(device_subtype, "Tachometer") == 0) + { + ds->dev_subtype = SIMDEVTYPE_TACHOMETER; + } + else + if (strcmp(device_subtype, "ShiftLights") == 0) + { + ds->dev_subtype = SIMDEVTYPE_SHIFTLIGHTS; + } + else + if (strcmp(device_subtype, "Shaker") == 0) + { + ds->dev_subtype = SIMDEVTYPE_SHAKER; + } + else + { + ds->is_valid = false; + slogi("%s does not appear to be a valid device sub type, but attempting to continue with other devices", device_subtype); + return MONOCOQUE_ERROR_INVALID_DEV; + } + ds->is_valid = true; + return MONOCOQUE_ERROR_NONE; +} + +int loadtachconfig(const char* config_file, DeviceSettings* ds) +{ + + + xmlNode* rootnode = NULL; + xmlNode* curnode = NULL; + xmlNode* cursubnode = NULL; + xmlNode* cursubsubnode = NULL; + xmlNode* cursubsubsubnode = NULL; + xmlDoc* doc = NULL; + char* buf; + + doc = xmlParseFile(config_file); + if (doc == NULL) + { + sloge("Could not read revburner xml config file %s", config_file); + return 1; + } + + rootnode = xmlDocGetRootElement(doc); + if (rootnode == NULL) + { + xmlFreeDoc(doc); + xmlCleanupParser(); + sloge("Invalid rev burner xml"); + return 1; + } + + int arraysize = 0; + for (curnode = rootnode; curnode; curnode = curnode->next) + { + for (cursubnode = curnode->children; cursubnode; cursubnode = cursubnode->next) + { + for (cursubsubnode = cursubnode->children; cursubsubnode; cursubsubnode = cursubsubnode->next) + { + if (cursubsubnode->type == XML_ELEMENT_NODE) + { + slogt("Xml Element name %s", cursubsubnode->name); + } + if (strcmp(cursubsubnode->name, "SettingsItem") == 0) + { + arraysize++; + } + + } + } + } + + uint32_t pulses_array[arraysize]; + uint32_t rpms_array[arraysize]; + slogt("rev burner settings array size %i", arraysize); + int i = 0; + for (curnode = rootnode; curnode; curnode = curnode->next) + { + if (curnode->type == XML_ELEMENT_NODE) + for (cursubnode = curnode->children; cursubnode; cursubnode = cursubnode->next) + { + for (cursubsubnode = cursubnode->children; cursubsubnode; cursubsubnode = cursubsubnode->next) + { + for (cursubsubsubnode = cursubsubnode->children; cursubsubsubnode; cursubsubsubnode = cursubsubsubnode->next) + { + if (strcmp(cursubsubsubnode->name, "Value") == 0) + { + xmlChar* a = xmlNodeGetContent(cursubsubsubnode); + rpms_array[i] = strtol((char*) a, &buf, 10); + xmlFree(a); + } + if (strcmp(cursubsubsubnode->name, "TimeValue") == 0) + { + xmlChar* a = xmlNodeGetContent(cursubsubsubnode); + pulses_array[i] = strtol((char*) a, &buf, 10); + xmlFree(a); + i++; + } + } + } + } + } + + ds->tachsettings.pulses_array = malloc(sizeof(pulses_array)); + ds->tachsettings.rpms_array = malloc(sizeof(rpms_array)); + ds->tachsettings.size = arraysize; + + memcpy(ds->tachsettings.pulses_array, pulses_array, sizeof(pulses_array)); + memcpy(ds->tachsettings.rpms_array, rpms_array, sizeof(rpms_array)); + + + xmlFreeDoc(doc); + xmlCleanupParser(); + + return 0; +} + +int loadconfig(const char* config_file, DeviceSettings* ds) +{ + if (ds->dev_subtype == SIMDEVTYPE_TACHOMETER) + { + return loadtachconfig(config_file, ds); + } + return 0; +} + +int devsetup(const char* device_type, const char* device_subtype, const char* config_file, MonocoqueSettings* ms, DeviceSettings* ds, config_setting_t* device_settings) +{ + int error = MONOCOQUE_ERROR_NONE; + slogi("Called device setup with %s %s %s", device_type, device_subtype, config_file); + ds->dev_type = SIMDEV_UNKNOWN; + ds->dev_subtype = SIMDEVTYPE_UNKNOWN; + error = strtodev(device_type, ds); + if (error != MONOCOQUE_ERROR_NONE) + { + return error; + } + error = strtodevtype(device_subtype, ds); + if (error != MONOCOQUE_ERROR_NONE) + { + return error; + } + if (ms->program_action == A_PLAY) + { + error = loadconfig(config_file, ds); + } + if (error != MONOCOQUE_ERROR_NONE) + { + return error; + } + + if (ds->dev_subtype == SIMDEVTYPE_TACHOMETER) + { + if (device_settings != NULL) + { + config_setting_lookup_int(device_settings, "granularity", &ds->tachsettings.granularity); + if (ds->tachsettings.granularity < 0 || ds->tachsettings.granularity > 4 || ds->tachsettings.granularity == 3) + { + slogd("No or invalid valid set for tachometer granularity, setting to 1"); + ds->tachsettings.granularity = 1; + } + slogi("Tachometer granularity set to %i", ds->tachsettings.granularity); + } + ds->tachsettings.use_pulses = true; + if (ms->program_action == A_PLAY) + { + ds->tachsettings.use_pulses = false; + } + } + + return error; +} diff --git a/src/monocoque/helper/confighelper.h b/src/monocoque/helper/confighelper.h new file mode 100644 index 0000000..0ef4910 --- /dev/null +++ b/src/monocoque/helper/confighelper.h @@ -0,0 +1,88 @@ +#ifndef _CONFIGHELPER_H +#define _CONFIGHELPER_H + +#include +#include + +#include + +#include "parameters.h" + +typedef enum +{ + SIMDEV_UNKNOWN = 0, + SIMDEV_USB = 1, + SIMDEV_SOUND = 2, + SIMDEV_SERIAL = 3 +} +DeviceType; + +typedef enum +{ + SIMDEVTYPE_UNKNOWN = 0, + SIMDEVTYPE_TACHOMETER = 1, + SIMDEVTYPE_SHAKER = 2, + SIMDEVTYPE_SHIFTLIGHTS = 3 +} +DeviceSubType; + +typedef enum +{ + SIMULATOR_MONOCOQUE_TEST = 0, + SIMULATOR_ASSETTO_CORSA = 1 +} +Simulator; + +typedef enum +{ + SIMULATOR_UPDATE_DEFAULT = 0, + SIMULATOR_UPDATE_RPMS = 1, + SIMULATOR_UPDATE_GEAR = 2, + SIMULATOR_UPDATE_PULSES = 3, + SIMULATOR_UPDATE_VELOCITY = 4, + SIMULATOR_UPDATE_ALTITUDE = 5 +} +SimulatorUpdate; + +typedef enum +{ + MONOCOQUE_ERROR_NONE = 0, + MONOCOQUE_ERROR_UNKNOWN = 1, + MONOCOQUE_ERROR_INVALID_SIM = 2, + MONOCOQUE_ERROR_INVALID_DEV = 3, + MONOCOQUE_ERROR_NODATA = 4, + MONOCOQUE_ERROR_UNKNOWN_DEV = 5 +} +MonocoqueError; + +typedef struct +{ + ProgramAction program_action; + Simulator sim_name; +} +MonocoqueSettings; + +typedef struct +{ + int size; + bool use_pulses; + int granularity; + uint32_t* rpms_array; + uint32_t* pulses_array; +} +TachometerSettings; + +typedef struct +{ + bool is_valid; + DeviceType dev_type; + DeviceSubType dev_subtype; + TachometerSettings tachsettings; +} +DeviceSettings; + +int strtogame(const char* game, MonocoqueSettings* ms); + +int devsetup(const char* device_type, const char* device_subtype, const char* config_files, MonocoqueSettings* ms, DeviceSettings* ds, config_setting_t* device_settings); + +#endif diff --git a/src/monocoque/helper/dirhelper.c b/src/monocoque/helper/dirhelper.c new file mode 100644 index 0000000..49b9ade --- /dev/null +++ b/src/monocoque/helper/dirhelper.c @@ -0,0 +1,202 @@ +#include "dirhelper.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +char* gethome() +{ + char* homedir = getenv("HOME"); + return homedir; + + if (homedir != NULL) + { + printf("Home dir in enviroment"); + printf("%s\n", homedir); + } + + uid_t uid = getuid(); + struct passwd* pw = getpwuid(uid); + + if (pw == NULL) + { + printf("Failed\n"); + exit(EXIT_FAILURE); + } + + return pw->pw_dir; +} + +time_t get_file_creation_time(char* path) +{ + struct stat attr; + stat(path, &attr); + return attr.st_mtime; +} + +void delete_dir(char* path) +{ + + struct dirent* de; + DIR* dr = opendir(path); + + if (dr == NULL) + { + printf("Could not open current directory"); + } + + // Refer http://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html + while ((de = readdir(dr)) != NULL) + { + char* fullpath = ( char* ) malloc(1 + strlen(path) + strlen("/") + strlen(de->d_name)); + strcpy(fullpath, path); + strcat(fullpath, "/"); + strcat(fullpath, de->d_name); + unlink(fullpath); + free(fullpath); + } + closedir(dr); + rmdir(path); + +} + +void delete_oldest_dir(char* path) +{ + char* oldestdir = path; + + struct dirent* de; + DIR* dr = opendir(path); + + if (dr == NULL) + { + printf("Could not open current directory"); + } + + // Refer http://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html + char filename_qfd[100] ; + char* deletepath = NULL; + time_t tempoldest = 0; + while ((de = readdir(dr)) != NULL) + { + struct stat stbuf; + + if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) + { + continue; + } + + char* fullpath = ( char* ) malloc(1 + strlen(path) + strlen(de->d_name)); + strcpy(fullpath, path); + strcat(fullpath, de->d_name); + + stat(fullpath, &stbuf); + if (S_ISDIR(stbuf.st_mode)) + { + strcpy(fullpath, path); + strcat(fullpath, de->d_name); + if (tempoldest == 0) + { + tempoldest = get_file_creation_time(fullpath); + free(deletepath); + deletepath = strdup(fullpath); + } + else + { + time_t t = get_file_creation_time(fullpath); + double diff = tempoldest - t; + if (diff > 0) + { + tempoldest = t; + free(deletepath); + deletepath = strdup(fullpath); + } + } + + } + + free(fullpath); + } + closedir(dr); + delete_dir(deletepath); + free(deletepath); +} + +void restrict_folders_to_cache(char* path, int cachesize) +{ + int numfolders = 0; + + struct dirent* de; + DIR* dr = opendir(path); + + if (dr == NULL) + { + printf("Could not open current directory"); + } + + // Refer http://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html + while ((de = readdir(dr)) != NULL) + { + + if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) + { + continue; + } + + char* fullpath = ( char* ) malloc(1 + strlen(path) + strlen(de->d_name)); + strcpy(fullpath, path); + strcat(fullpath, de->d_name); + strcat(fullpath, "/"); + + struct stat stbuf; + stat(fullpath,&stbuf); + + if (S_ISDIR(stbuf.st_mode)) + { + numfolders++; + } + free(fullpath); + } + + while (numfolders >= cachesize) + { + delete_oldest_dir(path); + numfolders--; + } + closedir(dr); + +} + +bool does_directory_exist(char* path, char* dirname) +{ + struct dirent* de; + DIR* dr = opendir(path); + + if (dr == NULL) + { + printf("Could not open current directory"); + return false; + } + + // Refer http://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html + bool answer = false; + while ((de = readdir(dr)) != NULL) + { + if (strcmp(dirname,de->d_name) == 0) + { + answer = true; + } + } + + closedir(dr); + return answer; +} + diff --git a/src/monocoque/helper/dirhelper.h b/src/monocoque/helper/dirhelper.h new file mode 100644 index 0000000..a95ea4e --- /dev/null +++ b/src/monocoque/helper/dirhelper.h @@ -0,0 +1,12 @@ +#ifndef _DIRHELPER_H +#define _DIRHELPER_H + +#include + +char* gethome(); +char* str2md5(const char* str, int length); +bool does_directory_exist(char* path, char* dirname); +void restrict_folders_to_cache(char* path, int cachesize); +void delete_dir(char* path); + +#endif diff --git a/src/monocoque/helper/parameters.c b/src/monocoque/helper/parameters.c new file mode 100644 index 0000000..9e35939 --- /dev/null +++ b/src/monocoque/helper/parameters.c @@ -0,0 +1,153 @@ +#include "parameters.h" +#include +#include +#include + +#include + +#include +#include + +ConfigError getParameters(int argc, char** argv, Parameters* p) +{ + + ConfigError exitcode = E_SOMETHING_BAD; + + // set return structure defaults + p->program_action = 0; + p->max_revs = 0; + p->verbosity_count = 0; + + // setup argument handling structures + const char* progname = "monocoque"; + + struct arg_lit* arg_verbosity1 = arg_litn("v","verbose", 0, 2, "increase logging verbosity"); + struct arg_lit* arg_verbosity2 = arg_litn("v","verbose", 0, 2, "increase logging verbosity"); + + struct arg_rex* cmd1 = arg_rex1(NULL, NULL, "play", NULL, REG_ICASE, NULL); + struct arg_str* arg_sim = arg_strn("s", "sim", "", 0, 1, NULL); + struct arg_lit* help = arg_litn(NULL,"help", 0, 1, "print this help and exit"); + struct arg_lit* vers = arg_litn(NULL,"version", 0, 1, "print version information and exit"); + struct arg_end* end1 = arg_end(20); + void* argtable1[] = {cmd1,arg_sim,arg_verbosity1,help,vers,end1}; + int nerrors1; + + struct arg_rex* cmd2a = arg_rex1(NULL, NULL, "config", NULL, REG_ICASE, NULL); + struct arg_rex* cmd2b = arg_rex1(NULL, NULL, "tachometer", NULL, REG_ICASE, NULL); + struct arg_int* arg_max_revs = arg_int1("m", "max_revs",NULL,"specify max revs of tachometer"); + struct arg_int* arg_granularity = arg_int0("g", "granularity",NULL,"1 every 1000 revs, 2 every 500 revs, 4 every 250 revs, default 1"); + struct arg_file* arg_save = arg_filen("s", "savefile", "", 1, 1, NULL); + struct arg_lit* help2 = arg_litn(NULL,"help", 0, 1, "print this help and exit"); + struct arg_lit* vers2 = arg_litn(NULL,"version", 0, 1, "print version information and exit"); + struct arg_end* end2 = arg_end(20); + void* argtable2[] = {cmd2a,cmd2b,arg_max_revs,arg_granularity,arg_save,arg_verbosity2,help2,vers2,end2}; + int nerrors2; + + struct arg_lit* help0 = arg_lit0(NULL,"help", "print this help and exit"); + struct arg_lit* version0 = arg_lit0(NULL,"version", "print version information and exit"); + struct arg_end* end0 = arg_end(20); + void* argtable0[] = {help0,version0,end0}; + int nerrors0; + + if (arg_nullcheck(argtable0) != 0) + { + printf("%s: insufficient memory\n",progname); + goto cleanup; + } + if (arg_nullcheck(argtable1) != 0) + { + printf("%s: insufficient memory\n",progname); + goto cleanup; + } + if (arg_nullcheck(argtable2) != 0) + { + printf("%s: insufficient memory\n",progname); + goto cleanup; + } + + arg_granularity->ival[0] = 1; + + nerrors0 = arg_parse(argc,argv,argtable0); + nerrors1 = arg_parse(argc,argv,argtable1); + nerrors2 = arg_parse(argc,argv,argtable2); + + if (nerrors1==0) + { + p->program_action = A_PLAY; + p->sim_string = arg_sim->sval[0]; + p->verbosity_count = arg_verbosity1->count; + exitcode = E_SUCCESS_AND_DO; + } + else + if (nerrors2==0) + { + p->program_action = A_CONFIG_TACH; + p->max_revs = arg_max_revs->ival[0]; + p->granularity = 1; + if (arg_granularity->ival[0] > 0 && arg_granularity->ival[0] < 5 && arg_granularity->ival[0] != 3) + { + p->granularity=arg_granularity->ival[0]; + } + p->save_file = *arg_save->filename; + p->verbosity_count = arg_verbosity2->count; + exitcode = E_SUCCESS_AND_DO; + } + else + { + if (cmd1->count > 0) + { + arg_print_errors(stdout,end1,progname); + printf("Usage: %s ", progname); + arg_print_syntax(stdout,argtable1,"\n"); + } + else + if (cmd2a->count > 0) + { + arg_print_errors(stdout,end2,progname); + printf("Usage: %s ", progname); + arg_print_syntax(stdout,argtable2,"\n"); + } + else + { + if (help->count==0 && vers->count==0) + { + printf("%s: missing command.\n",progname); + printf("Usage 1: %s ", progname); + arg_print_syntax(stdout,argtable1,"\n"); + printf("Usage 2: %s ", progname); + arg_print_syntax(stdout,argtable2,"\n"); + + } + } + exitcode = E_SUCCESS_AND_EXIT; + goto cleanup; + } + + // interpret some special cases before we go through trouble of reading the config file + if (help->count > 0) + { + printf("Usage: %s\n", progname); + printf("Usage 1: %s ", progname); + arg_print_syntax(stdout,argtable1,"\n"); + printf("Usage 2: %s ", progname); + arg_print_syntax(stdout,argtable2,"\n"); + printf("\nReport bugs on the github github.com/spacefreak18/monocoque.\n"); + exitcode = E_SUCCESS_AND_EXIT; + goto cleanup; + } + + if (vers->count > 0) + { + printf("%s Simulator Hardware Manager\n",progname); + printf("October 2022, Paul Dino Jones\n"); + exitcode = E_SUCCESS_AND_EXIT; + goto cleanup; + } + +cleanup: + arg_freetable(argtable0,sizeof(argtable0)/sizeof(argtable0[0])); + arg_freetable(argtable1,sizeof(argtable1)/sizeof(argtable1[0])); + arg_freetable(argtable2,sizeof(argtable2)/sizeof(argtable2[0])); + return exitcode; + +} diff --git a/src/monocoque/helper/parameters.h b/src/monocoque/helper/parameters.h new file mode 100644 index 0000000..b3f3776 --- /dev/null +++ b/src/monocoque/helper/parameters.h @@ -0,0 +1,44 @@ +#ifndef _PARAMETERS_H +#define _PARAMETERS_H + +typedef struct +{ + int program_action; + const char* sim_string; + const char* save_file; + int max_revs; + int granularity; + int verbosity_count; +} +Parameters; + +typedef enum +{ + A_PLAY = 0, + A_CONFIG_TACH = 1, + A_CONFIG_SHAKER = 2 +} +ProgramAction; + +typedef enum +{ + E_SUCCESS_AND_EXIT = 0, + E_SUCCESS_AND_DO = 1, + E_SOMETHING_BAD = 2 +} +ConfigError; + +ConfigError getParameters(int argc, char** argv, Parameters* p); + +struct _errordesc +{ + int code; + char* message; +} static errordesc[] = +{ + { E_SUCCESS_AND_EXIT, "No error and exiting" }, + { E_SUCCESS_AND_DO, "No error and continuing" }, + { E_SOMETHING_BAD, "Something bad happened" }, +}; + +#endif diff --git a/src/monocoque/monocoque.c b/src/monocoque/monocoque.c new file mode 100644 index 0000000..f90df68 --- /dev/null +++ b/src/monocoque/monocoque.c @@ -0,0 +1,233 @@ +#include +#include +#include +#include + +#include +#include "gameloop/gameloop.h" +#include "gameloop/tachconfig.h" +#include "devices/simdevice.h" +#include "helper/parameters.h" +#include "helper/dirhelper.h" +#include "helper/confighelper.h" +#include "simulatorapi/simdata.h" +#include "slog/slog.h" + +int create_dir(char* dir) +{ + struct stat st = {0}; + if (stat(dir, &st) == -1) + { + mkdir(dir, 0700); + } +} + +char* create_user_dir(char* dirtype) +{ + char* home_dir_str = gethome(); + char* config_dir_str = ( char* ) malloc(1 + strlen(home_dir_str) + strlen(dirtype) + strlen("monocoque/")); + strcpy(config_dir_str, home_dir_str); + strcat(config_dir_str, dirtype); + strcat(config_dir_str, "monocoque"); + + create_dir(config_dir_str); + free(config_dir_str); +} + +void display_banner() +{ + printf("______ ______________ ___________________________________ ___________\n"); + printf("___ |/ /_ __ \\__ | / /_ __ \\_ ____/_ __ \\_ __ \\_ / / /__ ____/\n"); + printf("__ /|_/ /_ / / /_ |/ /_ / / / / _ / / / / / / / / /__ __/ \n"); + printf("_ / / / / /_/ /_ /| / / /_/ // /___ / /_/ // /_/ // /_/ / _ /___ \n"); + printf("/_/ /_/ \\____/ /_/ |_/ \\____/ \\____/ \\____/ \\___\\_\\\\____/ /_____/ \n"); +} + +int main(int argc, char** argv) +{ + display_banner(); + + Parameters* p = malloc(sizeof(Parameters)); + MonocoqueSettings* ms = malloc(sizeof(MonocoqueSettings));; + + ConfigError ppe = getParameters(argc, argv, p); + if (ppe == E_SUCCESS_AND_EXIT) + { + goto cleanup_final; + } + ms->program_action = p->program_action; + + char* home_dir_str = gethome(); + create_user_dir("/.config/"); + create_user_dir("/.cache/"); + char* config_file_str = ( char* ) malloc(1 + strlen(home_dir_str) + strlen("/.config/") + strlen("monocoque/monocoque.config")); + char* cache_dir_str = ( char* ) malloc(1 + strlen(home_dir_str) + strlen("/.cache/monocoque/")); + strcpy(config_file_str, home_dir_str); + strcat(config_file_str, "/.config/"); + strcpy(cache_dir_str, home_dir_str); + strcat(cache_dir_str, "/.cache/monocoque/"); + strcat(config_file_str, "monocoque/monocoque.config"); + + slog_config_t slgCfg; + slog_config_get(&slgCfg); + slgCfg.eColorFormat = SLOG_COLORING_TAG; + slgCfg.eDateControl = SLOG_TIME_ONLY; + strcpy(slgCfg.sFileName, "monocoque.log"); + strcpy(slgCfg.sFilePath, cache_dir_str); + slgCfg.nTraceTid = 0; + slgCfg.nToScreen = 1; + slgCfg.nUseHeap = 0; + slgCfg.nToFile = 1; + slgCfg.nFlush = 0; + slgCfg.nFlags = SLOG_FLAGS_ALL; + slog_config_set(&slgCfg); + if (p->verbosity_count < 2) + { + slog_disable(SLOG_TRACE); + } + if (p->verbosity_count < 1) + { + slog_disable(SLOG_DEBUG); + } + + slogi("Loading configuration file: %s", config_file_str); + config_t cfg; + config_init(&cfg); + config_setting_t* config_devices = NULL; + + if (!config_read_file(&cfg, config_file_str)) + { + fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg)); + } + else + { + slogi("Openend monocoque configuration file"); + config_devices = config_lookup(&cfg, "devices"); + } + free(config_file_str); + free(cache_dir_str); + + if (p->program_action == A_CONFIG_TACH) + { + int error = 0; + SimDevice* tachdev = malloc(sizeof(SimDevice)); + SimData* sdata = malloc(sizeof(SimData)); + DeviceSettings* ds = malloc(sizeof(DeviceSettings)); + error = devsetup("USB", "Tachometer", "None", ms, ds, NULL); + error = devinit(tachdev, ds); + slogi("configuring tachometer with max revs: %i, granularity: %i, saving to %s", p->max_revs, p->granularity, p->save_file); + + if (error != MONOCOQUE_ERROR_NONE) + { + sloge("Could not proceed with tachometer configuration due to error: %i", error); + } + else + { + config_tachometer(p->max_revs, p->granularity, p->save_file, tachdev, sdata); + } + devfree(tachdev); + free(tachdev); + free(sdata); + free(ds); + } + else + { + slogi("running monocoque in gameloop mode.."); + int error = 0; + + error = strtogame(p->sim_string, ms); + if (error != MONOCOQUE_ERROR_NONE) + { + goto cleanup_final; + } + + int configureddevices = config_setting_length(config_devices); + int numdevices = 0; + DeviceSettings* ds[configureddevices]; + slogi("found %i devices in configuration", configureddevices); + int i = 0; + while (iis_valid == true) + { + SimDevice* device = malloc(sizeof(SimDevice)); + devinit(device, ds[i]); + devices[j] = device; + j++; + } + i++; + } + + error = looper(devices, numdevices, ms->sim_name); + if (error == MONOCOQUE_ERROR_NONE) + { + slogi("Game loop exited succesfully with error code: %i", error); + } + else + { + sloge("Game loop exited with error code: %i", error); + } + + i = 0; + while (idev_subtype == SIMDEV_USB) + { + free(ds[i]->tachsettings.pulses_array); + free(ds[i]->tachsettings.rpms_array); + } + free(ds[i]); + i++; + } + + i = 0; + while (i +#include "simapi/acdata.h" + +#define AC_PHYSICS_FILE "acpmf_physics" +#define AC_STATIC_FILE "acpmf_static" + +typedef struct +{ + bool has_physics; + bool has_static; + void* physics_map_addr; + void* static_map_addr; + struct SPageFilePhysics ac_physics; + struct SPageFileStatic ac_static; +} +ACMap; + +#endif diff --git a/src/monocoque/simulatorapi/simapi b/src/monocoque/simulatorapi/simapi new file mode 160000 index 0000000..5faf120 --- /dev/null +++ b/src/monocoque/simulatorapi/simapi @@ -0,0 +1 @@ +Subproject commit 5faf120d84378cdbe3bd8a0347f38fb9d0e9a87e diff --git a/src/monocoque/simulatorapi/simdata.h b/src/monocoque/simulatorapi/simdata.h new file mode 100644 index 0000000..43d3f6f --- /dev/null +++ b/src/monocoque/simulatorapi/simdata.h @@ -0,0 +1,17 @@ +#ifndef _SIMDATA_H +#define _SIMDATA_H + +#include + +typedef struct +{ + uint32_t velocity; + uint32_t rpms; + uint32_t gear; + uint32_t pulses; + uint32_t maxrpm; + uint32_t altitude; +} +SimData; + +#endif diff --git a/src/monocoque/simulatorapi/simmapper.c b/src/monocoque/simulatorapi/simmapper.c new file mode 100644 index 0000000..e451106 --- /dev/null +++ b/src/monocoque/simulatorapi/simmapper.c @@ -0,0 +1,97 @@ +#include +#include +#include +#include + +#include "simmapper.h" +#include "simdata.h" +#include "test.h" +#include "ac.h" +#include "../helper/confighelper.h" +#include "../slog/slog.h" + +#include "simapi/acdata.h" + + +int simdatamap(SimData* simdata, SimMap* simmap, Simulator simulator) +{ + switch ( simulator ) + { + case SIMULATOR_MONOCOQUE_TEST : + memcpy(simdata, simmap->addr, sizeof(SimData)); + break; + case SIMULATOR_ASSETTO_CORSA : + memcpy(&simmap->d.ac.ac_physics, simmap->d.ac.physics_map_addr, sizeof(simmap->d.ac.ac_physics)); + if (simmap->d.ac.has_static == true ) + { + memcpy(&simmap->d.ac.ac_static, simmap->d.ac.static_map_addr, sizeof(simmap->d.ac.ac_static)); + simdata->maxrpm = simmap->d.ac.ac_static.maxRpm; + } + simdata->rpms = simmap->d.ac.ac_physics.rpms; + simdata->gear = simmap->d.ac.ac_physics.gear; + simdata->velocity = simmap->d.ac.ac_physics.speedKmh; + simdata->altitude = 1; + break; + } +} + +int siminit(SimData* simdata, SimMap* simmap, Simulator simulator) +{ + slogi("searching for simulator data..."); + int error = MONOCOQUE_ERROR_NONE; + + void* a; + switch ( simulator ) + { + case SIMULATOR_MONOCOQUE_TEST : + simmap->fd = shm_open(TEST_MEM_FILE_LOCATION, O_RDONLY, S_IRUSR | S_IWUSR); + if (simmap->fd == -1) + { + return 10; + } + + simmap->addr = mmap(NULL, sizeof(SimData), PROT_READ, MAP_SHARED, simmap->fd, 0); + if (simmap->addr == MAP_FAILED) + { + return 30; + } + slogi("found data for monocoque test..."); + break; + case SIMULATOR_ASSETTO_CORSA : + + simmap->d.ac.has_physics=false; + simmap->d.ac.has_static=false; + simmap->fd = shm_open(AC_PHYSICS_FILE, O_RDONLY, S_IRUSR | S_IWUSR); + if (simmap->fd == -1) + { + slogd("could not open Assetto Corsa physics engine"); + return MONOCOQUE_ERROR_NODATA; + } + simmap->d.ac.physics_map_addr = mmap(NULL, sizeof(simmap->d.ac.ac_physics), PROT_READ, MAP_SHARED, simmap->fd, 0); + if (simmap->d.ac.physics_map_addr == MAP_FAILED) + { + slogd("could not retrieve Assetto Corsa physics data"); + return 30; + } + simmap->d.ac.has_physics=true; + + simmap->fd = shm_open(AC_STATIC_FILE, O_RDONLY, S_IRUSR | S_IWUSR); + if (simmap->fd == -1) + { + slogd("could not open Assetto Corsa static data"); + return 10; + } + simmap->d.ac.static_map_addr = mmap(NULL, sizeof(simmap->d.ac.ac_static), PROT_READ, MAP_SHARED, simmap->fd, 0); + if (simmap->d.ac.static_map_addr == MAP_FAILED) + { + slogd("could not retrieve Assetto Corsa static data"); + return 30; + } + simmap->d.ac.has_static=true; + + slogi("found data for Assetto Corsa..."); + break; + } + + return error; +} diff --git a/src/monocoque/simulatorapi/simmapper.h b/src/monocoque/simulatorapi/simmapper.h new file mode 100644 index 0000000..96ba785 --- /dev/null +++ b/src/monocoque/simulatorapi/simmapper.h @@ -0,0 +1,25 @@ +#ifndef _SIMMAPPER_H +#define _SIMMAPPEE_H + +#include "ac.h" + +#include "simdata.h" +#include "../helper/confighelper.h" + +#include "simapi/acdata.h" + +typedef struct +{ + void* addr; + int fd; + union + { + ACMap ac; + } d; +} +SimMap; + +int siminit(SimData* simdata, SimMap* simmap, Simulator simulator); +int simdatamap(SimData* simdata, SimMap* simmap, Simulator simulator); + +#endif diff --git a/src/monocoque/simulatorapi/test.h b/src/monocoque/simulatorapi/test.h new file mode 100755 index 0000000..31583b7 --- /dev/null +++ b/src/monocoque/simulatorapi/test.h @@ -0,0 +1,6 @@ +#ifndef _TEST_H +#define _TEST_H + +#define TEST_MEM_FILE_LOCATION "/monocoque_test" + +#endif diff --git a/src/monocoque/slog/CMakeLists.txt b/src/monocoque/slog/CMakeLists.txt new file mode 100644 index 0000000..9196925 --- /dev/null +++ b/src/monocoque/slog/CMakeLists.txt @@ -0,0 +1,6 @@ +set(slog_source_files + slog.c + slog.h +) + +add_library(slog STATIC ${slog_source_files}) diff --git a/src/monocoque/slog/slog.c b/src/monocoque/slog/slog.c new file mode 100644 index 0000000..d67d87d --- /dev/null +++ b/src/monocoque/slog/slog.c @@ -0,0 +1,546 @@ +/* + * The MIT License (MIT) + * + * Copyleft (C) 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "slog.h" + +#if !defined(__APPLE__) && !defined(DARWIN) && !defined(WIN32) +#include +#endif +#include + +#ifdef WIN32 +#include +#endif + +#ifndef PTHREAD_MUTEX_RECURSIVE +#define PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP +#endif + +typedef struct slog +{ + unsigned int nTdSafe:1; + pthread_mutex_t mutex; + slog_config_t config; +} slog_t; + +typedef struct XLogCtx +{ + const char* pFormat; + slog_flag_t eFlag; + slog_date_t date; + uint8_t nFullColor; + uint8_t nNewLine; +} slog_context_t; + +static slog_t g_slog; + +static void slog_sync_init(slog_t* pSlog) +{ + if (!pSlog->nTdSafe) + { + return; + } + pthread_mutexattr_t mutexAttr; + + if (pthread_mutexattr_init(&mutexAttr) || + pthread_mutexattr_settype(&mutexAttr, PTHREAD_MUTEX_RECURSIVE) || + pthread_mutex_init(&pSlog->mutex, &mutexAttr) || + pthread_mutexattr_destroy(&mutexAttr)) + { + printf("<%s:%d> %s: [ERROR] Can not initialize mutex: %d\n", + __FILE__, __LINE__, __FUNCTION__, errno); + + exit(EXIT_FAILURE); + } +} + +static void slog_lock(slog_t* pSlog) +{ + if (pSlog->nTdSafe && pthread_mutex_lock(&pSlog->mutex)) + { + printf("<%s:%d> %s: [ERROR] Can not lock mutex: %d\n", + __FILE__, __LINE__, __FUNCTION__, errno); + + exit(EXIT_FAILURE); + } +} + +static void slog_unlock(slog_t* pSlog) +{ + if (pSlog->nTdSafe && pthread_mutex_unlock(&pSlog->mutex)) + { + printf("<%s:%d> %s: [ERROR] Can not unlock mutex: %d\n", + __FILE__, __LINE__, __FUNCTION__, errno); + + exit(EXIT_FAILURE); + } +} + +static const char* slog_get_indent(slog_flag_t eFlag) +{ + slog_config_t* pCfg = &g_slog.config; + if (!pCfg->nIndent) + { + return SLOG_EMPTY; + } + + switch (eFlag) + { + case SLOG_NOTAG: + return SLOG_INDENT; + case SLOG_NOTE: + case SLOG_INFO: + case SLOG_WARN: + return SLOG_SPACE; + case SLOG_DEBUG: + case SLOG_TRACE: + case SLOG_FATAL: + case SLOG_ERROR: + default: + break; + } + + return SLOG_EMPTY; +} + +static const char* slog_get_tag(slog_flag_t eFlag) +{ + switch (eFlag) + { + case SLOG_NOTE: + return "note"; + case SLOG_INFO: + return "info"; + case SLOG_WARN: + return "warn"; + case SLOG_DEBUG: + return "debug"; + case SLOG_ERROR: + return "error"; + case SLOG_TRACE: + return "trace"; + case SLOG_FATAL: + return "fatal"; + default: + break; + } + + return NULL; +} + +static const char* slog_get_color(slog_flag_t eFlag) +{ + switch (eFlag) + { + case SLOG_NOTAG: + case SLOG_NOTE: + return SLOG_EMPTY; + case SLOG_INFO: + return SLOG_COLOR_GREEN; + case SLOG_WARN: + return SLOG_COLOR_YELLOW; + case SLOG_DEBUG: + return SLOG_COLOR_BLUE; + case SLOG_ERROR: + return SLOG_COLOR_RED; + case SLOG_TRACE: + return SLOG_COLOR_CYAN; + case SLOG_FATAL: + return SLOG_COLOR_MAGENTA; + default: + break; + } + + return SLOG_EMPTY; +} + +uint8_t slog_get_usec() +{ + struct timeval tv; + if (gettimeofday(&tv, NULL) < 0) + { + return 0; + } + return (uint8_t)(tv.tv_usec / 10000); +} + +void slog_get_date(slog_date_t* pDate) +{ + struct tm timeinfo; + time_t rawtime = time(NULL); +#ifdef WIN32 + localtime_s(&timeinfo, &rawtime); +#else + localtime_r(&rawtime, &timeinfo); +#endif + + pDate->nYear = timeinfo.tm_year + 1900; + pDate->nMonth = timeinfo.tm_mon + 1; + pDate->nDay = timeinfo.tm_mday; + pDate->nHour = timeinfo.tm_hour; + pDate->nMin = timeinfo.tm_min; + pDate->nSec = timeinfo.tm_sec; + pDate->nUsec = slog_get_usec(); +} + +static uint32_t slog_get_tid() +{ +#if defined(__APPLE__) || defined(DARWIN) || defined(WIN32) + return (uint32_t)pthread_self(); +#else + return syscall(__NR_gettid); +#endif +} + +static void slog_create_tag(char* pOut, size_t nSize, slog_flag_t eFlag, const char* pColor) +{ + slog_config_t* pCfg = &g_slog.config; + pOut[0] = SLOG_NUL; + + const char* pIndent = slog_get_indent(eFlag); + const char* pTag = slog_get_tag(eFlag); + + if (pTag == NULL) + { + snprintf(pOut, nSize, pIndent); + return; + } + + if (pCfg->eColorFormat != SLOG_COLORING_TAG) + { + snprintf(pOut, nSize, "<%s>%s", pTag, pIndent); + } + else + { + snprintf(pOut, nSize, "%s<%s>%s%s", pColor, pTag, SLOG_COLOR_RESET, pIndent); + } +} + +static void slog_create_tid(char* pOut, int nSize, uint8_t nTraceTid) +{ + if (!nTraceTid) + { + pOut[0] = SLOG_NUL; + } + else + { + snprintf(pOut, nSize, "(%u) ", slog_get_tid()); + } +} + +static void slog_display_message(const slog_context_t* pCtx, const char* pInfo, int nInfoLen, const char* pInput) +{ + slog_config_t* pCfg = &g_slog.config; + int nCbVal = 1; + + const char* pSeparator = nInfoLen > 0 ? pCfg->sSeparator : SLOG_EMPTY; + const char* pReset = pCtx->nFullColor ? SLOG_COLOR_RESET : SLOG_EMPTY; + const char* pNewLine = pCtx->nNewLine ? SLOG_NEWLINE : SLOG_EMPTY; + const char* pMessage = pInput != NULL ? pInput : SLOG_EMPTY; + + if (pCfg->logCallback != NULL) + { + size_t nLength = 0; + char* pLog = NULL; + + nLength += asprintf(&pLog, "%s%s%s%s%s", pInfo, pSeparator, pMessage, pReset, pNewLine); + if (pLog != NULL) + { + nCbVal = pCfg->logCallback(pLog, nLength, pCtx->eFlag, pCfg->pCallbackCtx); + free(pLog); + } + } + + if (pCfg->nToScreen && nCbVal > 0) + { + printf("%s%s%s%s%s", pInfo, pSeparator, pMessage, pReset, pNewLine); + if (pCfg->nFlush) + { + fflush(stdout); + } + } + + if (!pCfg->nToFile || nCbVal < 0) + { + return; + } + const slog_date_t* pDate = &pCtx->date; + + char sFilePath[SLOG_PATH_MAX + SLOG_NAME_MAX + SLOG_DATE_MAX]; + snprintf(sFilePath, sizeof(sFilePath), "%s/%s-%04d-%02d-%02d.log", + pCfg->sFilePath, pCfg->sFileName, pDate->nYear, pDate->nMonth, pDate->nDay); + + FILE* pFile = fopen(sFilePath, "a"); + if (pFile == NULL) + { + return; + } + + fprintf(pFile, "%s%s%s%s%s", pInfo, pSeparator, pMessage, pReset, pNewLine); + fclose(pFile); +} + +static int slog_create_info(const slog_context_t* pCtx, char* pOut, size_t nSize) +{ + slog_config_t* pCfg = &g_slog.config; + const slog_date_t* pDate = &pCtx->date; + + char sDate[SLOG_DATE_MAX + SLOG_NAME_MAX]; + sDate[0] = SLOG_NUL; + + if (pCfg->eDateControl == SLOG_TIME_ONLY) + { + snprintf(sDate, sizeof(sDate), "%02d:%02d:%02d.%03d ", + pDate->nHour,pDate->nMin, pDate->nSec, pDate->nUsec); + } + else + if (pCfg->eDateControl == SLOG_DATE_FULL) + { + snprintf(sDate, sizeof(sDate), "%04d.%02d.%02d-%02d:%02d:%02d.%03d ", + pDate->nYear, pDate->nMonth, pDate->nDay, pDate->nHour, + pDate->nMin, pDate->nSec, pDate->nUsec); + } + + char sTid[SLOG_TAG_MAX], sTag[SLOG_TAG_MAX]; + const char* pColorCode = slog_get_color(pCtx->eFlag); + const char* pColor = pCtx->nFullColor ? pColorCode : SLOG_EMPTY; + + slog_create_tid(sTid, sizeof(sTid), pCfg->nTraceTid); + slog_create_tag(sTag, sizeof(sTag), pCtx->eFlag, pColorCode); + return snprintf(pOut, nSize, "%s%s%s%s", pColor, sTid, sDate, sTag); +} + +static void slog_display_heap(const slog_context_t* pCtx, va_list args) +{ + size_t nBytes = 0; + char* pMessage = NULL; + char sLogInfo[SLOG_INFO_MAX]; + + nBytes += vasprintf(&pMessage, pCtx->pFormat, args); + va_end(args); + + if (pMessage == NULL) + { + printf("<%s:%d> %s%s %s: Can not allocate memory for input: errno(%d)\n", + __FILE__, __LINE__, SLOG_COLOR_RED, SLOG_COLOR_RESET, __FUNCTION__, errno); + + return; + } + + int nLength = slog_create_info(pCtx, sLogInfo, sizeof(sLogInfo)); + slog_display_message(pCtx, sLogInfo, nLength, pMessage); + if (pMessage != NULL) + { + free(pMessage); + } +} + +static void slog_display_stack(const slog_context_t* pCtx, va_list args) +{ + char sMessage[SLOG_MESSAGE_MAX]; + char sLogInfo[SLOG_INFO_MAX]; + + vsnprintf(sMessage, sizeof(sMessage), pCtx->pFormat, args); + int nLength = slog_create_info(pCtx, sLogInfo, sizeof(sLogInfo)); + slog_display_message(pCtx, sLogInfo, nLength, sMessage); +} + +void slog_display(slog_flag_t eFlag, uint8_t nNewLine, const char* pFormat, ...) +{ + slog_lock(&g_slog); + slog_config_t* pCfg = &g_slog.config; + + if ((SLOG_FLAGS_CHECK(g_slog.config.nFlags, eFlag)) && + (g_slog.config.nToScreen || g_slog.config.nToFile)) + { + slog_context_t ctx; + slog_get_date(&ctx.date); + + ctx.eFlag = eFlag; + ctx.pFormat = pFormat; + ctx.nNewLine = nNewLine; + ctx.nFullColor = pCfg->eColorFormat == SLOG_COLORING_FULL ? 1 : 0; + + void(*slog_display_args)(const slog_context_t* pCtx, va_list args); + slog_display_args = pCfg->nUseHeap ? slog_display_heap : slog_display_stack; + + va_list args; + va_start(args, pFormat); + slog_display_args(&ctx, args); + va_end(args); + } + + slog_unlock(&g_slog); +} + +size_t slog_version(char* pDest, size_t nSize, uint8_t nMin) +{ + size_t nLength = 0; + + /* Version short */ + if (nMin) + nLength = snprintf(pDest, nSize, "%d.%d.%d", + SLOG_VERSION_MAJOR, SLOG_VERSION_MINOR, SLOG_BUILD_NUM); + + /* Version long */ + else + nLength = snprintf(pDest, nSize, "%d.%d build %d (%s)", + SLOG_VERSION_MAJOR, SLOG_VERSION_MINOR, SLOG_BUILD_NUM, __DATE__); + + return nLength; +} + +void slog_config_get(slog_config_t* pCfg) +{ + slog_lock(&g_slog); + *pCfg = g_slog.config; + slog_unlock(&g_slog); +} + +void slog_config_set(slog_config_t* pCfg) +{ + slog_lock(&g_slog); + g_slog.config = *pCfg; + slog_unlock(&g_slog); +} + +void slog_enable(slog_flag_t eFlag) +{ + slog_lock(&g_slog); + + if (!SLOG_FLAGS_CHECK(g_slog.config.nFlags, eFlag)) + { + g_slog.config.nFlags |= eFlag; + } + + slog_unlock(&g_slog); +} + +void slog_disable(slog_flag_t eFlag) +{ + slog_lock(&g_slog); + + if (SLOG_FLAGS_CHECK(g_slog.config.nFlags, eFlag)) + { + g_slog.config.nFlags &= ~eFlag; + } + + slog_unlock(&g_slog); +} + +void slog_separator_set(const char* pFormat, ...) +{ + slog_lock(&g_slog); + slog_config_t* pCfg = &g_slog.config; + + va_list args; + va_start(args, pFormat); + + if (vsnprintf(pCfg->sSeparator, sizeof(pCfg->sSeparator), pFormat, args) <= 0) + { + pCfg->sSeparator[0] = ' '; + pCfg->sSeparator[1] = '\0'; + } + + va_end(args); + slog_unlock(&g_slog); +} + +void slog_indent(uint8_t nEnable) +{ + slog_lock(&g_slog); + g_slog.config.nIndent = nEnable; + slog_unlock(&g_slog); +} + +void slog_callback_set(slog_cb_t callback, void* pContext) +{ + slog_lock(&g_slog); + slog_config_t* pCfg = &g_slog.config; + pCfg->pCallbackCtx = pContext; + pCfg->logCallback = callback; + slog_unlock(&g_slog); +} + +void slog_init(const char* pName, uint16_t nFlags, uint8_t nTdSafe) +{ + /* Set up default values */ + slog_config_t* pCfg = &g_slog.config; + pCfg->eColorFormat = SLOG_COLORING_TAG; + pCfg->eDateControl = SLOG_TIME_ONLY; + pCfg->pCallbackCtx = NULL; + pCfg->logCallback = NULL; + pCfg->sSeparator[0] = ' '; + pCfg->sSeparator[1] = '\0'; + pCfg->sFilePath[0] = '.'; + pCfg->sFilePath[1] = '\0'; + pCfg->nTraceTid = 0; + pCfg->nToScreen = 1; + pCfg->nUseHeap = 0; + pCfg->nToFile = 0; + pCfg->nIndent = 0; + pCfg->nFlush = 0; + pCfg->nFlags = nFlags; + + const char* pFileName = (pName != NULL) ? pName : SLOG_NAME_DEFAULT; + snprintf(pCfg->sFileName, sizeof(pCfg->sFileName), "%s", pFileName); + +#ifdef WIN32 + // Enable color support + HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD dwMode = 0; + GetConsoleMode(hOutput, &dwMode); + dwMode |= ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode(hOutput, dwMode); +#endif + + /* Initialize mutex */ + g_slog.nTdSafe = nTdSafe; + slog_sync_init(&g_slog); +} + +void slog_destroy() +{ + g_slog.config.pCallbackCtx = NULL; + g_slog.config.logCallback = NULL; + + if (g_slog.nTdSafe) + { + pthread_mutex_destroy(&g_slog.mutex); + g_slog.nTdSafe = 0; + } +} \ No newline at end of file diff --git a/src/monocoque/slog/slog.h b/src/monocoque/slog/slog.h new file mode 100644 index 0000000..bb53e1e --- /dev/null +++ b/src/monocoque/slog/slog.h @@ -0,0 +1,195 @@ +/* + * The MIT License (MIT) + * + * Copyleft (C) 2015-2020 Sun Dro (f4tb0y@protonmail.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE + */ + +#ifndef __SLOG_H__ +#define __SLOG_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/* SLog version information */ +#define SLOG_VERSION_MAJOR 1 +#define SLOG_VERSION_MINOR 8 +#define SLOG_BUILD_NUM 26 + +/* Supported colors */ +#define SLOG_COLOR_NORMAL "\x1B[0m" +#define SLOG_COLOR_RED "\x1B[31m" +#define SLOG_COLOR_GREEN "\x1B[32m" +#define SLOG_COLOR_YELLOW "\x1B[33m" +#define SLOG_COLOR_BLUE "\x1B[34m" +#define SLOG_COLOR_MAGENTA "\x1B[35m" +#define SLOG_COLOR_CYAN "\x1B[36m" +#define SLOG_COLOR_WHITE "\x1B[37m" +#define SLOG_COLOR_RESET "\033[0m" + +/* Trace source location helpers */ +#define SLOG_TRACE_LVL1(LINE) #LINE +#define SLOG_TRACE_LVL2(LINE) SLOG_TRACE_LVL1(LINE) +#define SLOG_THROW_LOCATION "[" __FILE__ ":" SLOG_TRACE_LVL2(__LINE__) "] " + +/* SLog limits (To be safe while avoiding dynamic allocations) */ +#define SLOG_MESSAGE_MAX 8196 +#define SLOG_VERSION_MAX 128 +#define SLOG_PATH_MAX 2048 +#define SLOG_INFO_MAX 512 +#define SLOG_NAME_MAX 256 +#define SLOG_DATE_MAX 64 +#define SLOG_TAG_MAX 32 +#define SLOG_COLOR_MAX 16 + +#define SLOG_FLAGS_CHECK(c, f) (((c) & (f)) == (f)) +#define SLOG_FLAGS_ALL 255 + +#define SLOG_NAME_DEFAULT "slog" +#define SLOG_NEWLINE "\n" +#define SLOG_INDENT " " +#define SLOG_SPACE " " +#define SLOG_EMPTY "" +#define SLOG_NUL '\0' + +typedef struct SLogDate +{ + uint16_t nYear; + uint8_t nMonth; + uint8_t nDay; + uint8_t nHour; + uint8_t nMin; + uint8_t nSec; + uint8_t nUsec; +} slog_date_t; + +uint8_t slog_get_usec(); +void slog_get_date(slog_date_t* pDate); + +/* Log level flags */ +typedef enum +{ + SLOG_NOTAG = (1 << 0), + SLOG_NOTE = (1 << 1), + SLOG_INFO = (1 << 2), + SLOG_WARN = (1 << 3), + SLOG_DEBUG = (1 << 4), + SLOG_TRACE = (1 << 5), + SLOG_ERROR = (1 << 6), + SLOG_FATAL = (1 << 7) +} slog_flag_t; + +typedef int(*slog_cb_t)(const char* pLog, size_t nLength, slog_flag_t eFlag, void* pCtx); + +/* Output coloring control flags */ +typedef enum +{ + SLOG_COLORING_DISABLE = 0, + SLOG_COLORING_TAG, + SLOG_COLORING_FULL +} slog_coloring_t; + +typedef enum +{ + SLOG_TIME_DISABLE = 0, + SLOG_TIME_ONLY, + SLOG_DATE_FULL +} slog_date_ctrl_t; + +#define slog(...) \ + slog_display(SLOG_NOTAG, 1, __VA_ARGS__) + +#define slogwn(...) \ + slog_display(SLOG_NOTAG, 0, __VA_ARGS__) + +#define slog_note(...) \ + slog_display(SLOG_NOTE, 1, __VA_ARGS__) + +#define slog_info(...) \ + slog_display(SLOG_INFO, 1, __VA_ARGS__) + +#define slog_warn(...) \ + slog_display(SLOG_WARN, 1, __VA_ARGS__) + +#define slog_debug(...) \ + slog_display(SLOG_DEBUG, 1, __VA_ARGS__) + +#define slog_error(...) \ + slog_display(SLOG_ERROR, 1, __VA_ARGS__) + +#define slog_trace(...) \ + slog_display(SLOG_TRACE, 1, SLOG_THROW_LOCATION __VA_ARGS__) + +#define slog_fatal(...) \ + slog_display(SLOG_FATAL, 1, SLOG_THROW_LOCATION __VA_ARGS__) + +/* Short name definitions */ +#define slogn(...) slog_note(__VA_ARGS__) +#define slogi(...) slog_info(__VA_ARGS__) +#define slogw(...) slog_warn(__VA_ARGS__) +#define slogd(...) slog_debug( __VA_ARGS__) +#define sloge(...) slog_error( __VA_ARGS__) +#define slogt(...) slog_trace(__VA_ARGS__) +#define slogf(...) slog_fatal(__VA_ARGS__) + +typedef struct SLogConfig +{ + slog_date_ctrl_t eDateControl; // Display output with date format + slog_coloring_t eColorFormat; // Output color format control + slog_cb_t logCallback; // Log callback to collect logs + void* pCallbackCtx; // Data pointer passed to log callback + + uint8_t nTraceTid; // Trace thread ID and display in output + uint8_t nToScreen; // Enable screen logging + uint8_t nUseHeap; // Use dynamic allocation + uint8_t nToFile; // Enable file logging + uint8_t nIndent; // Enable indentations + uint8_t nFlush; // Flush stdout after screen log + uint16_t nFlags; // Allowed log level flags + + char sSeparator[SLOG_NAME_MAX]; // Separator between info and log + char sFileName[SLOG_NAME_MAX]; // Output file name for logs + char sFilePath[SLOG_PATH_MAX]; // Output file path for logs +} slog_config_t; + +size_t slog_version(char* pDest, size_t nSize, uint8_t nMin); +void slog_config_get(slog_config_t* pCfg); +void slog_config_set(slog_config_t* pCfg); + +void slog_separator_set(const char* pFormat, ...); +void slog_callback_set(slog_cb_t callback, void* pContext); +void slog_indent(uint8_t nEnable); + +void slog_enable(slog_flag_t eFlag); +void slog_disable(slog_flag_t eFlag); + +void slog_init(const char* pName, uint16_t nFlags, uint8_t nTdSafe); +void slog_display(slog_flag_t eFlag, uint8_t nNewLine, const char* pFormat, ...); +void slog_destroy(); // Needed only if the slog_init() function argument nTdSafe > 0 + +#ifdef __cplusplus +} +#endif + +#endif /* __SLOG_H__ */ \ No newline at end of file diff --git a/tests/getmem.c b/tests/getmem.c new file mode 100644 index 0000000..bf7f279 --- /dev/null +++ b/tests/getmem.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include +#include + +#include "../src/monocoque/simulatorapi/simdata.h" +#include "../src/monocoque/simulatorapi/test.h" + +int main(int argc, char* argv[]) +{ + int res; + int fd; + pid_t pid; + void* addr; + SimData* data = malloc(sizeof(SimData)); + + pid = getpid(); + + // get shared memory file descriptor (NOT a file) + fd = shm_open(TEST_MEM_FILE_LOCATION, O_RDONLY, S_IRUSR | S_IWUSR); + if (fd == -1) + { + return 10; + } + + // map shared memory to process address space + addr = mmap(NULL, sizeof(SimData), PROT_READ, MAP_SHARED, fd, 0); + if (addr == MAP_FAILED) + { + return 30; + } + + // place data into memory + + memcpy(data, addr, sizeof(SimData)); + + printf("PID %d: Read from shared memory: \"%s\"\n", pid, data); + printf("PID %d: velocity: %i\n", pid, data->velocity); + printf("PID %d: rpms: %i\n", pid, data->rpms); + printf("PID %d: gear: %i\n", pid, data->gear); + + return 0; +} diff --git a/tests/hidtest.c b/tests/hidtest.c new file mode 100644 index 0000000..5c8ef20 --- /dev/null +++ b/tests/hidtest.c @@ -0,0 +1,233 @@ +#include // printf +#include // wprintf + +#include + +#include + +#define MAX_STR 255 + +int buf_size = 64; + +int main(int argc, char* argv[]) +{ + buf_size++; + 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, 0x102, NULL); + + int num = 321; + char snum[5]; + + // convert 123 to string [buf] + sprintf(snum, "%d", num); + + // print our string + printf("test%s\n", snum); + + if (!handle) + { + printf("Could not find attached rev burner\n"); + res = hid_exit(); + return 0; + } + + // 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); + + + int rpm_array[21] = {41574, 48580, 54308, 58612, 60160, 61113, 61916, 62347, 62777, 63089, 63243, 63505, 63673, 63778, 63908, 64002, 64084, 64140, 64175, 64224, 64313}; + + buf[0] = 0x00; + buf[1] = 0x00; + buf[2] = 0x00; + buf[3] = 0x00; + res = hid_write(handle, buf, buf_size); + + for (i = 0; i<21; i++) + { + int rpms = i * 500; + + fprintf(stderr, "Setting RPM to %i hex as decimal %i\n", rpms, rpm_array[i]); + + unsigned char bytes[buf_size]; + unsigned long nnnnn = rpm_array[i]; + unsigned long xlong = 0; + for (int x = 0; x < buf_size; x++) + { + bytes[x] = 0x00; + } + + bytes[3] = (nnnnn >> 8) & 0xFF; + bytes[2] = nnnnn & 0xFF; + + FILE* write_ptr; + if ( i == 0 ) + { + write_ptr = fopen("test0.bin","wb"); + } + + if ( i == 1 ) + { + write_ptr = fopen("test1.bin","wb"); + } + + if ( i == 2 ) + { + write_ptr = fopen("test2.bin","wb"); + } + + if ( i == 3 ) + { + write_ptr = fopen("test3.bin","wb"); + } + + if ( i == 4 ) + { + write_ptr = fopen("test4.bin","wb"); + } + + if ( i == 5 ) + { + write_ptr = fopen("test5.bin","wb"); + } + + if ( i == 6 ) + { + write_ptr = fopen("test6.bin","wb"); + } + + if ( i == 7 ) + { + write_ptr = fopen("test7.bin","wb"); + } + + if ( i == 8 ) + { + write_ptr = fopen("test8.bin","wb"); + } + + if ( i == 9 ) + { + write_ptr = fopen("test9.bin","wb"); + } + + if ( i == 10 ) + { + write_ptr = fopen("test10.bin","wb"); + } + + if ( i == 11 ) + { + write_ptr = fopen("test11.bin","wb"); + } + + if ( i == 12 ) + { + write_ptr = fopen("test12.bin","wb"); + } + + if ( i == 13 ) + { + write_ptr = fopen("test13.bin","wb"); + } + + if ( i == 14 ) + { + write_ptr = fopen("test14.bin","wb"); + } + + if ( i == 15 ) + { + write_ptr = fopen("test15.bin","wb"); + } + + if ( i == 16 ) + { + write_ptr = fopen("test16.bin","wb"); + } + + if ( i == 17 ) + { + write_ptr = fopen("test17.bin","wb"); + } + + if ( i == 18 ) + { + write_ptr = fopen("test18.bin","wb"); + } + + if ( i == 19 ) + { + write_ptr = fopen("test19.bin","wb"); + } + + if ( i == 20 ) + { + write_ptr = fopen("test20.bin","wb"); + } + + if ( i == 21 ) + { + write_ptr = fopen("test21.bin","wb"); + } + + if ( i == 22 ) + { + write_ptr = fopen("test22.bin","wb"); + } + + if ( i == 23 ) + { + write_ptr = fopen("test23.bin","wb"); + } + + if ( i == 24 ) + { + write_ptr = fopen("test24.bin","wb"); + } + + fwrite(bytes,sizeof(bytes),1,write_ptr); + fclose(write_ptr); + + if (handle) + { + res = hid_write(handle, bytes, buf_size); + } + + sleep(4); + } + + buf[0] = 0x00; + buf[1] = 0x00; + buf[2] = 0x00; + buf[3] = 0x00; + res = hid_write(handle, buf, buf_size); + + sleep(2); + // Close the device + hid_close(handle); + + // Finalize the hidapi library + res = hid_exit(); + + return 0; +} diff --git a/tests/pa_devs.c b/tests/pa_devs.c new file mode 100644 index 0000000..e71bc7c --- /dev/null +++ b/tests/pa_devs.c @@ -0,0 +1,269 @@ +/** @file pa_devs.c + @ingroup examples_src + @brief List available devices, including device information. + @author Phil Burk http://www.softsynth.com + + @note Define PA_USE_ASIO=0 to compile this code on Windows without + ASIO support. +*/ +/* + * $Id: pa_devs.c 1891 2013-05-05 14:00:02Z rbencina $ + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +#include +#include +#include "portaudio.h" + +#ifdef WIN32 +#include + +#if PA_USE_ASIO +#include "pa_asio.h" +#endif +#endif + +/*******************************************************************/ +static void PrintSupportedStandardSampleRates( + const PaStreamParameters* inputParameters, + const PaStreamParameters* outputParameters ) +{ + static double standardSampleRates[] = + { + 8000.0, 9600.0, 11025.0, 12000.0, 16000.0, 22050.0, 24000.0, 32000.0, + 44100.0, 48000.0, 88200.0, 96000.0, 192000.0, -1 /* negative terminated list */ + }; + int i, printCount; + PaError err; + + printCount = 0; + for( i=0; standardSampleRates[i] > 0; i++ ) + { + err = Pa_IsFormatSupported( inputParameters, outputParameters, standardSampleRates[i] ); + if( err == paFormatIsSupported ) + { + if( printCount == 0 ) + { + printf( "\t%8.2f", standardSampleRates[i] ); + printCount = 1; + } + else + if( printCount == 4 ) + { + printf( ",\n\t%8.2f", standardSampleRates[i] ); + printCount = 1; + } + else + { + printf( ", %8.2f", standardSampleRates[i] ); + ++printCount; + } + } + } + if( !printCount ) + { + printf( "None\n" ); + } + else + { + printf( "\n" ); + } +} + +/*******************************************************************/ +int main(void); +int main(void) +{ + int i, numDevices, defaultDisplayed; + const PaDeviceInfo* deviceInfo; + PaStreamParameters inputParameters, outputParameters; + PaError err; + + + err = Pa_Initialize(); + if( err != paNoError ) + { + printf( "ERROR: Pa_Initialize returned 0x%x\n", err ); + goto error; + } + + printf( "PortAudio version number = %d\nPortAudio version text = '%s'\n", + Pa_GetVersion(), Pa_GetVersionText() ); + + + numDevices = Pa_GetDeviceCount(); + if( numDevices < 0 ) + { + printf( "ERROR: Pa_GetDeviceCount returned 0x%x\n", numDevices ); + err = numDevices; + goto error; + } + + printf( "Number of devices = %d\n", numDevices ); + for( i=0; ihostApi )->defaultInputDevice ) + { + const PaHostApiInfo* hostInfo = Pa_GetHostApiInfo( deviceInfo->hostApi ); + printf( "[ Default %s Input", hostInfo->name ); + defaultDisplayed = 1; + } + + if( i == Pa_GetDefaultOutputDevice() ) + { + printf( (defaultDisplayed ? "," : "[") ); + printf( " Default Output" ); + defaultDisplayed = 1; + } + else + if( i == Pa_GetHostApiInfo( deviceInfo->hostApi )->defaultOutputDevice ) + { + const PaHostApiInfo* hostInfo = Pa_GetHostApiInfo( deviceInfo->hostApi ); + printf( (defaultDisplayed ? "," : "[") ); + printf( " Default %s Output", hostInfo->name ); + defaultDisplayed = 1; + } + + if( defaultDisplayed ) + { + printf( " ]\n" ); + } + + /* print device info fields */ +#ifdef WIN32 + { + /* Use wide char on windows, so we can show UTF-8 encoded device names */ + wchar_t wideName[MAX_PATH]; + MultiByteToWideChar(CP_UTF8, 0, deviceInfo->name, -1, wideName, MAX_PATH-1); + wprintf( L"Name = %s\n", wideName ); + } +#else + printf( "Name = %s\n", deviceInfo->name ); +#endif + printf( "Host API = %s\n", Pa_GetHostApiInfo( deviceInfo->hostApi )->name ); + printf( "Max inputs = %d", deviceInfo->maxInputChannels ); + printf( ", Max outputs = %d\n", deviceInfo->maxOutputChannels ); + + printf( "Default low input latency = %8.4f\n", deviceInfo->defaultLowInputLatency ); + printf( "Default low output latency = %8.4f\n", deviceInfo->defaultLowOutputLatency ); + printf( "Default high input latency = %8.4f\n", deviceInfo->defaultHighInputLatency ); + printf( "Default high output latency = %8.4f\n", deviceInfo->defaultHighOutputLatency ); + +#ifdef WIN32 +#if PA_USE_ASIO + /* ASIO specific latency information */ + if( Pa_GetHostApiInfo( deviceInfo->hostApi )->type == paASIO ) + { + long minLatency, maxLatency, preferredLatency, granularity; + + err = PaAsio_GetAvailableLatencyValues( i, + &minLatency, &maxLatency, &preferredLatency, &granularity ); + + printf( "ASIO minimum buffer size = %ld\n", minLatency ); + printf( "ASIO maximum buffer size = %ld\n", maxLatency ); + printf( "ASIO preferred buffer size = %ld\n", preferredLatency ); + + if( granularity == -1 ) + { + printf( "ASIO buffer granularity = power of 2\n" ); + } + else + { + printf( "ASIO buffer granularity = %ld\n", granularity ); + } + } +#endif /* PA_USE_ASIO */ +#endif /* WIN32 */ + + printf( "Default sample rate = %8.2f\n", deviceInfo->defaultSampleRate ); + + /* poll for standard sample rates */ + inputParameters.device = i; + inputParameters.channelCount = deviceInfo->maxInputChannels; + inputParameters.sampleFormat = paInt16; + inputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */ + inputParameters.hostApiSpecificStreamInfo = NULL; + + outputParameters.device = i; + outputParameters.channelCount = deviceInfo->maxOutputChannels; + outputParameters.sampleFormat = paInt16; + outputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */ + outputParameters.hostApiSpecificStreamInfo = NULL; + + if( inputParameters.channelCount > 0 ) + { + printf("Supported standard sample rates\n for half-duplex 16 bit %d channel input = \n", + inputParameters.channelCount ); + PrintSupportedStandardSampleRates( &inputParameters, NULL ); + } + + if( outputParameters.channelCount > 0 ) + { + printf("Supported standard sample rates\n for half-duplex 16 bit %d channel output = \n", + outputParameters.channelCount ); + PrintSupportedStandardSampleRates( NULL, &outputParameters ); + } + + if( inputParameters.channelCount > 0 && outputParameters.channelCount > 0 ) + { + printf("Supported standard sample rates\n for full-duplex 16 bit %d channel input, %d channel output = \n", + inputParameters.channelCount, outputParameters.channelCount ); + PrintSupportedStandardSampleRates( &inputParameters, &outputParameters ); + } + } + + Pa_Terminate(); + + printf("----------------------------------------------\n"); + return 0; + +error: + Pa_Terminate(); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} diff --git a/tests/patest_longsine.c b/tests/patest_longsine.c new file mode 100644 index 0000000..255b66b --- /dev/null +++ b/tests/patest_longsine.c @@ -0,0 +1,282 @@ + +/** @file patest_longsine.c + @ingroup test_src + @brief Play a sine wave until ENTER hit. + @author Phil Burk http://www.softsynth.com +*/ +/* + * $Id: patest_longsine.c 1097 2006-08-26 08:27:53Z rossb $ + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortAudio license; however, + * the PortAudio community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +#include +#include +#include + +#include "portaudio.h" + +#define SAMPLE_RATE (44100) + +#ifndef M_PI +#define M_PI (3.14159265) +#endif + + +#define MAX_TABLE_SIZE (6000) +//#define TABLE_SIZE (3200) +//#define HALF_TABLE (1600) +typedef struct +{ + float sine[MAX_TABLE_SIZE]; + float pitch; + int left_phase; + int right_phase; + int n; + int table_size; + int amp; +} + +paTestData; + +int x = 0; +int y = 0; +long n = 0; +//float note = 256; +float note = 60; +//float note = 161.626; +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback(const void* inputBuffer, + void* outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void* userData) +{ + paTestData* data = (paTestData*)userData; + float* out = (float*)outputBuffer; + unsigned int i; + unsigned int n; + n = data->n; + (void) inputBuffer; /* Prevent unused argument warning. */ + for( i=0; isine[i] = (float) sin( ((double)i/(double) MAX_TABLE_SIZE) * ( ( M_PI * 2. ) ) ); + //data->sine[n] = sin ( 2 * M_PI * ((float) n / (float) MAX_TABLE_SIZE) ); + //data->right_phase = data->left_phase; + //data->left_phase = n; + //data->right_phase = n; + float v = data->amp * sin (2 * M_PI * ((float) n) / (float) SAMPLE_RATE); + //*out++ = data->sine[data->left_phase]; + //*out++ = data->sine[data->right_phase]; + + if (n>=data->table_size) + { + n=0; + } + + *out++ = v; + *out++ = v; + + data->left_phase += 1; + data->right_phase += 1; + /* + float v = 800.81; + if (i>=512) + { + v = -v; + } + *out++ = v; + *out++ = v; + */ + //if( data->left_phase >= MAX_TABLE_SIZE ) data->left_phase -= MAX_TABLE_SIZE; + //if( data->right_phase >= MAX_TABLE_SIZE ) data->right_phase -= MAX_TABLE_SIZE; + if( data->left_phase >= data->table_size ) + { + data->left_phase -= data->table_size; + } + if( data->right_phase >= data->table_size ) + { + data->right_phase -= data->table_size; + } + } + data->n=n; + return 0; +} + +void generatesamples(int table_size, int half_table, int datum, paTestData* data) +{ + datum = 1; + int j; + for( j=data->n; jtable_size; i++,j++ ) + { + //data.sine[i] = (float) amp * sin( ((double)i/(double) TABLE_SIZE) * ( ( M_PI * 2. * freq) ) ); + data->sine[j] = (float) datum * sin( ((double)i/(double) data->table_size) * ( ( M_PI * 2. ) ) ); + /* + data->sine[i] = datum; + if (i>=half_table) { + data->sine[i] = -datum; + } + */ + data->n=j; + } + } +} +/*******************************************************************/ +int main(void); +int main(void) +{ + PaStreamParameters outputParameters; + PaStream* stream; + PaError err; + paTestData data; + int i; + printf("PortAudio Test: output sine wave.\n"); + + float amp = ((float)32); + amp = ((float)32); + //float freq = ((float) freq1/(SAMPLE_RATE/TABLE_SIZE)); + int freq = 32; + // 500 rpms + int table_size = 5292; + int half_table = 2646; + table_size = 200; + half_table = 50; + data.pitch = 1; + data.pitch = 261.626; + data.amp = 2; + //data.pitch = 523.25; + //table_size = 2646; + //half_table = 1323; + //table_size = 3200; + //half_table = 1600; + /* initialise sinusoidal wavetable */ + data.table_size=5292; + //generatesamples(table_size,half_table,5,&data); + data.left_phase = data.right_phase = 0; + + err = Pa_Initialize(); + if( err != paNoError ) + { + goto error; + } + + outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ + outputParameters.channelCount = 2; /* stereo output */ + outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */ + outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency; + outputParameters.hostApiSpecificStreamInfo = NULL; + + err = Pa_OpenStream( &stream, + NULL, /* No input. */ + &outputParameters, /* As above. */ + SAMPLE_RATE, + 440, /* Frames per buffer. */ + paClipOff, /* No out of range samples expected. */ + patestCallback, + &data ); + if( err != paNoError ) + { + goto error; + } + + fprintf(stdout,"Revving to 500 rpm...\n"); + + err = Pa_StartStream( stream ); + if( err != paNoError ) + { + goto error; + } + + //printf("Hit ENTER to stop program.\n"); + //getchar(); + + sleep(10); + + table_size = 2646; + half_table = 1323; + table_size = 100; + data.table_size = 2646; + half_table = 100; + + fprintf(stdout,"Revving to 1000 rpm...\n"); + //generatesamples(table_size,half_table,5,&data); + data.pitch = 523.25; + + sleep(10); + + table_size = 1764; + half_table = 882; + table_size = 400; + half_table = 200; + data.table_size = 1764; + + fprintf(stdout,"Revving to 1500 rpm...\n"); + //generatesamples(table_size,half_table,5,&data); + + sleep(5); + + table_size = 1323; + half_table = 661; + data.table_size = 1323; + + fprintf(stdout,"Revving to 2000 rpm...\n"); + //generatesamples(table_size,half_table,5,&data); + + sleep(5); + err = Pa_CloseStream( stream ); + if( err != paNoError ) + { + goto error; + } + Pa_Terminate(); + + printf("Test finished.\n"); + return err; + +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} diff --git a/tests/revburnerparsetest.c b/tests/revburnerparsetest.c new file mode 100644 index 0000000..eab1757 --- /dev/null +++ b/tests/revburnerparsetest.c @@ -0,0 +1,94 @@ +#include +#include +#include +#include +#include + +void parseStory (xmlDocPtr doc, xmlNodePtr cur) +{ + + xmlChar* key; + cur = cur->xmlChildrenNode; + while (cur != NULL) + { + if ((!xmlStrcmp(cur->name, (const xmlChar*)"Value"))) + { + key = xmlNodeGetContent(cur); + printf("Revs x100: %s\n", (char*) key); + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar*)"TimerValue"))) + { + key = xmlNodeGetContent(cur); + printf("Value to send to RevBurner: %s\n", (char*) key); + xmlFree(key); + } + cur = cur->next; + } + return; +} + +static void parseDoc(char* docname) +{ + + xmlDocPtr doc; + xmlNodePtr cur; + + doc = xmlParseFile(docname); + + if (doc == NULL ) + { + fprintf(stderr,"Document not parsed successfully. \n"); + return; + } + + cur = xmlDocGetRootElement(doc); + + if (cur == NULL) + { + fprintf(stderr,"empty document\n"); + xmlFreeDoc(doc); + return; + } + + if (xmlStrcmp(cur->name, (const xmlChar*) "TachometerSettings")) + { + fprintf(stderr,"document of the wrong type, root node != TachometerSettings"); + xmlFreeDoc(doc); + return; + } + + cur = cur->xmlChildrenNode; + cur = cur->next; + cur = cur->xmlChildrenNode; + while (cur != NULL) + { + + if (!(xmlStrcmp(cur->name, (const xmlChar*)"SettingsItem"))) + { + parseStory (doc, cur); + } + + cur = cur->next; + } + + xmlFreeDoc(doc); + return; +} + +int main(int argc, char** argv) +{ + + char* docname; + + if (argc <= 1) + { + printf("Usage: %s docname\n", argv[0]); + return(0); + } + + docname = argv[1]; + parseDoc (docname); + + return (1); +} diff --git a/tests/runmemtest.sh b/tests/runmemtest.sh new file mode 100755 index 0000000..d751472 --- /dev/null +++ b/tests/runmemtest.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +function is_linux +{ + if [[ "$(uname)" == "Linux" ]]; then + echo 1 + return + fi + + echo 0 +} + +SET="setmem" +GET="getmem" +CFLAGS="" +if [[ "$(is_linux)" == "1" ]]; then + CFLAGS="-lrt" +fi + +cc ${CFLAGS} -o ${SET} ${SET}.c +cc ${CFLAGS} -o ${GET} ${GET}.c + +./${SET} & +sleep 1 +./${GET} +if [[ "$(is_linux)" == "1" ]]; then + echo "/dev/shm:" + ls -l /dev/shm +fi +sleep 1 + +rm ${SET} ${GET} diff --git a/tests/setmem.c b/tests/setmem.c new file mode 100644 index 0000000..9631edc --- /dev/null +++ b/tests/setmem.c @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "../src/monocoque/simulatorapi/simdata.h" +#include "../src/monocoque/simulatorapi/test.h" + +#define DATA "Hello, World! From PID %d" + +int main(int argc, char* argv[]) +{ + int res; + int fd; + int len; + pid_t pid; + void* addr; + + pid = getpid(); + + fd = shm_open(TEST_MEM_FILE_LOCATION, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if (fd == -1) + { + perror("open"); + return 10; + } + + SimData* data = malloc(sizeof(SimData)); + + data->gear=0; + data->rpms=0; + data->maxrpm=0; + data->velocity=0; + //SimData.pulses=0; + + res = ftruncate(fd, sizeof(SimData)); + if (res == -1) + { + perror("ftruncate"); + return 20; + } + + data->gear=1; + data->rpms=500; + data->maxrpm=6500; + data->velocity=25; + //SimData.pulses=38792; + + // map .shared memory to process address space + addr = mmap(NULL, sizeof(SimData), PROT_WRITE, MAP_SHARED, fd, 0); + if (addr == MAP_FAILED) + { + perror("mmap"); + return 30; + } + + printf("size of int %i\n", sizeof(int)); + printf("size of struct %i\n", sizeof(SimData)); + memcpy(addr, data, sizeof(SimData)); + //memcpy(addr, &SimData, sizeof(int)*3); + + printf("PID %d: velocity: %i\n", pid, data->velocity); + printf("PID %d: rpms: %i\n", pid, data->rpms); + printf("PID %d: gear: %i\n", pid, data->gear); + sleep(4); + + + struct termios newsettings, canonicalmode; + tcgetattr(0, &canonicalmode); + newsettings = canonicalmode; + newsettings.c_lflag &= (~ICANON & ~ECHO); + newsettings.c_cc[VMIN] = 1; + newsettings.c_cc[VTIME] = 0; + tcsetattr(0, TCSANOW, &newsettings); + char ch; + + int go=1; + while (go>0) + { + + struct termios info; + tcgetattr(0, &info); + info.c_lflag &= (~ICANON & ~ECHO); + info.c_cc[VMIN] = 1; + info.c_cc[VTIME] = 0; + tcsetattr(0, TCSANOW, &info); + char ch; + scanf("%c", &ch); + + + if (ch == 'q') + { + go=0; + } + + } + tcsetattr(0, TCSANOW, &canonicalmode); + + fd = shm_unlink(TEST_MEM_FILE_LOCATION); + if (fd == -1) + { + perror("unlink"); + return 100; + } + + return 0; +} diff --git a/tests/setsimdata.c b/tests/setsimdata.c new file mode 100644 index 0000000..4e87099 --- /dev/null +++ b/tests/setsimdata.c @@ -0,0 +1,62 @@ +#include +#include +#include +#include +#include +#include + +#include "../src/monocoque/simulatorapi/simdata.h" +#include "../src/monocoque/simulatorapi/test.h" + +#define DATA "Hello, World! From PID %d" + +int main(int argc, char* argv[]) +{ + int res; + int fd; + int len; + pid_t pid; + void* addr; + + pid = getpid(); + + // get shared memory file descriptor (NOT a file) + fd = shm_open(TEST_MEM_FILE_LOCATION, O_RDWR, S_IRUSR | S_IWUSR); + if (fd == -1) + { + perror("open"); + return 10; + } + + // extend shared memory object as by default it's initialized with size 0 + //res = ftruncate(fd, STORAGE_SIZE); + SimData* data = malloc(sizeof(SimData)); + data->velocity=1; + data->gear=4; + data->maxrpm=6500; + data->rpms=1000; + //SimData.pulses=398273; + + res = ftruncate(fd, sizeof(SimData)); + if (res == -1) + { + //perror("ftruncate"); + //return 20; + } + + data->velocity=atoi(argv[1]); + data->rpms=atoi(argv[2]); + data->gear=atoi(argv[3]); + //SimData.pulses=39993; + // map .shared memory to process address space + addr = mmap(NULL, sizeof(SimData), PROT_WRITE, MAP_SHARED, fd, 0); + if (addr == MAP_FAILED) + { + perror("mmap"); + return 30; + } + + memcpy(addr, data, sizeof(SimData)); + + return 0; +} diff --git a/tests/sharedmemoryproducer.c b/tests/sharedmemoryproducer.c new file mode 100644 index 0000000..316d008 --- /dev/null +++ b/tests/sharedmemoryproducer.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void display(char* prog, char* bytes, int n); + +int main(void) +{ + const char* name = "/shm-example"; // file name + const int SIZE = 4096; // file size + + const char* message0 = "Studying "; + const char* message1 = "Operating Systems "; + const char* message2 = "Is Fun!"; + const char* msg_end = "\n"; + + int shm_fd; // file descriptor, from shm_open() + char* shm_base; // base address, from mmap() + char* ptr; // shm_base is fixed, ptr is movable + + /* create the shared memory segment as if it was a file */ + shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666); + if (shm_fd == -1) + { + printf("prod: Shared memory failed: %s\n", strerror(errno)); + exit(1); + } + + /* configure the size of the shared memory segment */ + ftruncate(shm_fd, SIZE); + + /* map the shared memory segment to the address space of the process */ + shm_base = mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); + if (shm_base == MAP_FAILED) + { + printf("prod: Map failed: %s\n", strerror(errno)); + // close and shm_unlink? + exit(1); + } + + /** + * Write to the mapped shared memory region. + * + * We increment the value of ptr after each write, but we + * are ignoring the possibility that sprintf() fails. + */ + display("prod", shm_base, 64); + ptr = shm_base; + ptr += sprintf(ptr, "%s", message0); + ptr += sprintf(ptr, "%s", message1); + ptr += sprintf(ptr, "%s", message2); + ptr += sprintf(ptr, "%s", msg_end); + display("prod", shm_base, 64); + + /* remove the mapped memory segment from the address space of the process */ + if (munmap(shm_base, SIZE) == -1) + { + printf("prod: Unmap failed: %s\n", strerror(errno)); + exit(1); + } + + /* close the shared memory segment as if it was a file */ + if (close(shm_fd) == -1) + { + printf("prod: Close failed: %s\n", strerror(errno)); + exit(1); + } + + return 0; +} + +void display(char* prog, char* bytes, int n) +{ + printf("display: %s\n", prog); + for (int i = 0; i < n; i++) + { + printf("%02x%c", bytes[i], ((i+1)%16) ? ' ' : '\n'); + } + printf("\n"); +} diff --git a/tests/simlighttest.c b/tests/simlighttest.c new file mode 100644 index 0000000..cce39e1 --- /dev/null +++ b/tests/simlighttest.c @@ -0,0 +1,106 @@ +#include +#include +#include +#include +#include "../src/monocoque/simulatorapi/simdata.h" + +/* Helper function for error handling. */ +int check(enum sp_return result); + +int main() +{ + SimData sd; + sd.maxrpm = 6500; + sd.rpms = 0; + sd.altitude = 10; + sd.gear = 1; + sd.velocity = 74; + + char* port_name = "/dev/ttyACM0"; + + /* The ports we will use. */ + struct sp_port* port; + + /* Open and configure each port. */ + printf("Looking for port %s.\n", port_name); + check(sp_get_port_by_name(port_name, &port)); + + printf("Opening port.\n"); + check(sp_open(port, SP_MODE_READ_WRITE)); + + printf("Setting port to 9600 8N1, no flow control.\n"); + check(sp_set_baudrate(port, 9600)); + check(sp_set_bits(port, 8)); + check(sp_set_parity(port, SP_PARITY_NONE)); + check(sp_set_stopbits(port, 1)); + check(sp_set_flowcontrol(port, SP_FLOWCONTROL_NONE)); + + while ( sd.rpms <= sd.maxrpm ) + { + + + unsigned int timeout = 2000; + + /* On success, sp_blocking_write() and sp_blocking_read() + * return the number of bytes sent/received before the + * timeout expired. We'll store that result here. */ + int result; + int size = sizeof(SimData); + + /* Send data. */ + result = check(sp_blocking_write(port, &sd, size, timeout)); + + /* Check whether we sent all of the data. */ + if (result == size) + { + printf("Sent %d bytes successfully, %i rpm.\n", size, sd.rpms); + } + else + { + printf("Timed out, %d/%d bytes sent.\n", result, size); + } + + sd.rpms += 1000; + if ( sd.rpms == sd.maxrpm + 1000 ) + { + sd.rpms = 0; + } + if ( sd.rpms > sd.maxrpm ) + { + sd.rpms = sd.maxrpm; + } + + sleep(1); + } + + check(sp_close(port)); + sp_free_port(port); +} + +/* Helper function for error handling. */ +int check(enum sp_return result) +{ + /* For this example we'll just exit on any error by calling abort(). */ + char* error_message; + + switch (result) + { + case SP_ERR_ARG: + printf("Error: Invalid argument.\n"); + abort(); + case SP_ERR_FAIL: + error_message = sp_last_error_message(); + printf("Error: Failed: %s\n", error_message); + sp_free_error_message(error_message); + abort(); + case SP_ERR_SUPP: + printf("Error: Not supported.\n"); + abort(); + case SP_ERR_MEM: + printf("Error: Couldn't allocate memory.\n"); + abort(); + case SP_OK: + default: + return result; + } +} diff --git a/tests/test.bin b/tests/test.bin new file mode 100644 index 0000000000000000000000000000000000000000..75a58544e5b96a0f782306d31bbb973fb5573d9c GIT binary patch literal 17 OcmZSOImO_^fCK;#*a0X2 literal 0 HcmV?d00001 diff --git a/tests/testlibusb.c b/tests/testlibusb.c new file mode 100644 index 0000000..f124cd7 --- /dev/null +++ b/tests/testlibusb.c @@ -0,0 +1,381 @@ +/* +* Test suite program based of libusb-0.1-compat testlibusb +* Copyright (c) 2013 Nathan Hjelm +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include "libusb-1.0/libusb.h" + +int verbose = 0; + +static void print_endpoint_comp(const struct libusb_ss_endpoint_companion_descriptor* ep_comp) +{ + printf(" USB 3.0 Endpoint Companion:\n"); + printf(" bMaxBurst: %u\n", ep_comp->bMaxBurst); + printf(" bmAttributes: %02xh\n", ep_comp->bmAttributes); + printf(" wBytesPerInterval: %u\n", ep_comp->wBytesPerInterval); +} + +static void print_endpoint(const struct libusb_endpoint_descriptor* endpoint) +{ + int i, ret; + + printf(" Endpoint:\n"); + printf(" bEndpointAddress: %02xh\n", endpoint->bEndpointAddress); + printf(" bmAttributes: %02xh\n", endpoint->bmAttributes); + printf(" wMaxPacketSize: %u\n", endpoint->wMaxPacketSize); + printf(" bInterval: %u\n", endpoint->bInterval); + printf(" bRefresh: %u\n", endpoint->bRefresh); + printf(" bSynchAddress: %u\n", endpoint->bSynchAddress); + + for (i = 0; i < endpoint->extra_length;) + { + if (LIBUSB_DT_SS_ENDPOINT_COMPANION == endpoint->extra[i + 1]) + { + struct libusb_ss_endpoint_companion_descriptor* ep_comp; + + ret = libusb_get_ss_endpoint_companion_descriptor(NULL, endpoint, &ep_comp); + if (LIBUSB_SUCCESS != ret) + { + continue; + } + + print_endpoint_comp(ep_comp); + + libusb_free_ss_endpoint_companion_descriptor(ep_comp); + } + + i += endpoint->extra[i]; + } +} + +static void print_altsetting(const struct libusb_interface_descriptor* interface) +{ + uint8_t i; + + printf(" Interface:\n"); + printf(" bInterfaceNumber: %u\n", interface->bInterfaceNumber); + printf(" bAlternateSetting: %u\n", interface->bAlternateSetting); + printf(" bNumEndpoints: %u\n", interface->bNumEndpoints); + printf(" bInterfaceClass: %u\n", interface->bInterfaceClass); + printf(" bInterfaceSubClass: %u\n", interface->bInterfaceSubClass); + printf(" bInterfaceProtocol: %u\n", interface->bInterfaceProtocol); + printf(" iInterface: %u\n", interface->iInterface); + + for (i = 0; i < interface->bNumEndpoints; i++) + { + print_endpoint(&interface->endpoint[i]); + } +} + +static void print_2_0_ext_cap(struct libusb_usb_2_0_extension_descriptor* usb_2_0_ext_cap) +{ + printf(" USB 2.0 Extension Capabilities:\n"); + printf(" bDevCapabilityType: %u\n", usb_2_0_ext_cap->bDevCapabilityType); + printf(" bmAttributes: %08xh\n", usb_2_0_ext_cap->bmAttributes); +} + +static void print_ss_usb_cap(struct libusb_ss_usb_device_capability_descriptor* ss_usb_cap) +{ + printf(" USB 3.0 Capabilities:\n"); + printf(" bDevCapabilityType: %u\n", ss_usb_cap->bDevCapabilityType); + printf(" bmAttributes: %02xh\n", ss_usb_cap->bmAttributes); + printf(" wSpeedSupported: %u\n", ss_usb_cap->wSpeedSupported); + printf(" bFunctionalitySupport: %u\n", ss_usb_cap->bFunctionalitySupport); + printf(" bU1devExitLat: %u\n", ss_usb_cap->bU1DevExitLat); + printf(" bU2devExitLat: %u\n", ss_usb_cap->bU2DevExitLat); +} + +static void print_bos(libusb_device_handle* handle) +{ + struct libusb_bos_descriptor* bos; + uint8_t i; + int ret; + + ret = libusb_get_bos_descriptor(handle, &bos); + if (ret < 0) + { + return; + } + + printf(" Binary Object Store (BOS):\n"); + printf(" wTotalLength: %u\n", bos->wTotalLength); + printf(" bNumDeviceCaps: %u\n", bos->bNumDeviceCaps); + + for (i = 0; i < bos->bNumDeviceCaps; i++) + { + struct libusb_bos_dev_capability_descriptor* dev_cap = bos->dev_capability[i]; + + if (dev_cap->bDevCapabilityType == LIBUSB_BT_USB_2_0_EXTENSION) + { + struct libusb_usb_2_0_extension_descriptor* usb_2_0_extension; + + ret = libusb_get_usb_2_0_extension_descriptor(NULL, dev_cap, &usb_2_0_extension); + if (ret < 0) + { + return; + } + + print_2_0_ext_cap(usb_2_0_extension); + libusb_free_usb_2_0_extension_descriptor(usb_2_0_extension); + } + else + if (dev_cap->bDevCapabilityType == LIBUSB_BT_SS_USB_DEVICE_CAPABILITY) + { + struct libusb_ss_usb_device_capability_descriptor* ss_dev_cap; + + ret = libusb_get_ss_usb_device_capability_descriptor(NULL, dev_cap, &ss_dev_cap); + if (ret < 0) + { + return; + } + + print_ss_usb_cap(ss_dev_cap); + libusb_free_ss_usb_device_capability_descriptor(ss_dev_cap); + } + } + + libusb_free_bos_descriptor(bos); +} + +static void print_interface(const struct libusb_interface* interface) +{ + int i; + + for (i = 0; i < interface->num_altsetting; i++) + { + print_altsetting(&interface->altsetting[i]); + } +} + +static void print_configuration(struct libusb_config_descriptor* config) +{ + uint8_t i; + + printf(" Configuration:\n"); + printf(" wTotalLength: %u\n", config->wTotalLength); + printf(" bNumInterfaces: %u\n", config->bNumInterfaces); + printf(" bConfigurationValue: %u\n", config->bConfigurationValue); + printf(" iConfiguration: %u\n", config->iConfiguration); + printf(" bmAttributes: %02xh\n", config->bmAttributes); + printf(" MaxPower: %u\n", config->MaxPower); + + for (i = 0; i < config->bNumInterfaces; i++) + { + print_interface(&config->interface[i]); + } +} + +static void print_device(libusb_device* dev, libusb_device_handle* handle) +{ + struct libusb_device_descriptor desc; + unsigned char string[256]; + const char* speed; + int ret; + uint8_t i; + + switch (libusb_get_device_speed(dev)) + { + case LIBUSB_SPEED_LOW: + speed = "1.5M"; + break; + case LIBUSB_SPEED_FULL: + speed = "12M"; + break; + case LIBUSB_SPEED_HIGH: + speed = "480M"; + break; + case LIBUSB_SPEED_SUPER: + speed = "5G"; + break; + case LIBUSB_SPEED_SUPER_PLUS: + speed = "10G"; + break; + default: + speed = "Unknown"; + } + + ret = libusb_get_device_descriptor(dev, &desc); + if (ret < 0) + { + fprintf(stderr, "failed to get device descriptor"); + return; + } + + printf("Dev (bus %u, device %u): %04X - %04X speed: %s\n", + libusb_get_bus_number(dev), libusb_get_device_address(dev), + desc.idVendor, desc.idProduct, speed); + + if (!handle) + { + libusb_open(dev, &handle); + } + + if (handle) + { + if (desc.iManufacturer) + { + ret = libusb_get_string_descriptor_ascii(handle, desc.iManufacturer, string, sizeof(string)); + if (ret > 0) + { + printf(" Manufacturer: %s\n", (char*)string); + } + } + + if (desc.iProduct) + { + ret = libusb_get_string_descriptor_ascii(handle, desc.iProduct, string, sizeof(string)); + if (ret > 0) + { + printf(" Product: %s\n", (char*)string); + } + } + + if (desc.iSerialNumber && verbose) + { + ret = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, string, sizeof(string)); + if (ret > 0) + { + printf(" Serial Number: %s\n", (char*)string); + } + } + } + + if (verbose) + { + for (i = 0; i < desc.bNumConfigurations; i++) + { + struct libusb_config_descriptor* config; + + ret = libusb_get_config_descriptor(dev, i, &config); + if (LIBUSB_SUCCESS != ret) + { + printf(" Couldn't retrieve descriptors\n"); + continue; + } + + print_configuration(config); + + libusb_free_config_descriptor(config); + } + + if (handle && desc.bcdUSB >= 0x0201) + { + print_bos(handle); + } + } + + if (handle) + { + libusb_close(handle); + } +} + +#ifdef __linux__ +#include +#include +#include + +static int test_wrapped_device(const char* device_name) +{ + libusb_device_handle* handle; + int r, fd; + + fd = open(device_name, O_RDWR); + if (fd < 0) + { + printf("Error could not open %s: %s\n", device_name, strerror(errno)); + return 1; + } + r = libusb_wrap_sys_device(NULL, fd, &handle); + if (r) + { + printf("Error wrapping device: %s: %s\n", device_name, libusb_strerror(r)); + close(fd); + return 1; + } + print_device(libusb_get_device(handle), handle); + close(fd); + return 0; +} +#else +static int test_wrapped_device(const char* device_name) +{ + (void)device_name; + printf("Testing wrapped devices is not supported on your platform\n"); + return 1; +} +#endif + +int main(int argc, char* argv[]) +{ + const char* device_name = NULL; + libusb_device** devs; + ssize_t cnt; + int r, i; + + for (i = 1; i < argc; i++) + { + if (!strcmp(argv[i], "-v")) + { + verbose = 1; + } + else + if (!strcmp(argv[i], "-d") && (i + 1) < argc) + { + i++; + device_name = argv[i]; + } + else + { + printf("Usage %s [-v] [-d ]\n", argv[0]); + printf("Note use -d to test libusb_wrap_sys_device()\n"); + return 1; + } + } + + r = libusb_init(NULL); + if (r < 0) + { + return r; + } + + if (device_name) + { + r = test_wrapped_device(device_name); + } + else + { + cnt = libusb_get_device_list(NULL, &devs); + if (cnt < 0) + { + libusb_exit(NULL); + return 1; + } + + for (i = 0; devs[i]; i++) + { + print_device(devs[i], NULL); + } + + libusb_free_device_list(devs, 1); + } + + libusb_exit(NULL); + return r; +} diff --git a/tests/testrevburner.c b/tests/testrevburner.c new file mode 100644 index 0000000..27147af --- /dev/null +++ b/tests/testrevburner.c @@ -0,0 +1,532 @@ +/* +* Test suite program based of libusb-0.1-compat testlibusb +* Copyright (c) 2013 Nathan Hjelm +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include "libusb-1.0/libusb.h" + +#define ENDPOINT_OUT 0x01 +#define ENDPOINT_IN 0x81 +int verbose = 0; + +static void print_endpoint_comp(const struct libusb_ss_endpoint_companion_descriptor* ep_comp) +{ + printf(" USB 3.0 Endpoint Companion:\n"); + printf(" bMaxBurst: %u\n", ep_comp->bMaxBurst); + printf(" bmAttributes: %02xh\n", ep_comp->bmAttributes); + printf(" wBytesPerInterval: %u\n", ep_comp->wBytesPerInterval); +} + +static void print_endpoint(const struct libusb_endpoint_descriptor* endpoint) +{ + int i, ret; + + printf(" Endpoint:\n"); + printf(" bEndpointAddress: %02xh\n", endpoint->bEndpointAddress); + printf(" bmAttributes: %02xh\n", endpoint->bmAttributes); + printf(" wMaxPacketSize: %u\n", endpoint->wMaxPacketSize); + printf(" bInterval: %u\n", endpoint->bInterval); + printf(" bRefresh: %u\n", endpoint->bRefresh); + printf(" bSynchAddress: %u\n", endpoint->bSynchAddress); + + for (i = 0; i < endpoint->extra_length;) + { + if (LIBUSB_DT_SS_ENDPOINT_COMPANION == endpoint->extra[i + 1]) + { + struct libusb_ss_endpoint_companion_descriptor* ep_comp; + + ret = libusb_get_ss_endpoint_companion_descriptor(NULL, endpoint, &ep_comp); + if (LIBUSB_SUCCESS != ret) + { + continue; + } + + print_endpoint_comp(ep_comp); + + libusb_free_ss_endpoint_companion_descriptor(ep_comp); + } + + i += endpoint->extra[i]; + } +} + +static void print_altsetting(const struct libusb_interface_descriptor* interface) +{ + uint8_t i; + + printf(" Interface:\n"); + printf(" bInterfaceNumber: %u\n", interface->bInterfaceNumber); + printf(" bAlternateSetting: %u\n", interface->bAlternateSetting); + printf(" bNumEndpoints: %u\n", interface->bNumEndpoints); + printf(" bInterfaceClass: %u\n", interface->bInterfaceClass); + printf(" bInterfaceSubClass: %u\n", interface->bInterfaceSubClass); + printf(" bInterfaceProtocol: %u\n", interface->bInterfaceProtocol); + printf(" iInterface: %u\n", interface->iInterface); + + for (i = 0; i < interface->bNumEndpoints; i++) + { + print_endpoint(&interface->endpoint[i]); + } +} + +static void print_2_0_ext_cap(struct libusb_usb_2_0_extension_descriptor* usb_2_0_ext_cap) +{ + printf(" USB 2.0 Extension Capabilities:\n"); + printf(" bDevCapabilityType: %u\n", usb_2_0_ext_cap->bDevCapabilityType); + printf(" bmAttributes: %08xh\n", usb_2_0_ext_cap->bmAttributes); +} + +static void print_ss_usb_cap(struct libusb_ss_usb_device_capability_descriptor* ss_usb_cap) +{ + printf(" USB 3.0 Capabilities:\n"); + printf(" bDevCapabilityType: %u\n", ss_usb_cap->bDevCapabilityType); + printf(" bmAttributes: %02xh\n", ss_usb_cap->bmAttributes); + printf(" wSpeedSupported: %u\n", ss_usb_cap->wSpeedSupported); + printf(" bFunctionalitySupport: %u\n", ss_usb_cap->bFunctionalitySupport); + printf(" bU1devExitLat: %u\n", ss_usb_cap->bU1DevExitLat); + printf(" bU2devExitLat: %u\n", ss_usb_cap->bU2DevExitLat); +} + +static void print_bos(libusb_device_handle* handle) +{ + struct libusb_bos_descriptor* bos; + uint8_t i; + int ret; + + ret = libusb_get_bos_descriptor(handle, &bos); + if (ret < 0) + { + return; + } + + printf(" Binary Object Store (BOS):\n"); + printf(" wTotalLength: %u\n", bos->wTotalLength); + printf(" bNumDeviceCaps: %u\n", bos->bNumDeviceCaps); + + for (i = 0; i < bos->bNumDeviceCaps; i++) + { + struct libusb_bos_dev_capability_descriptor* dev_cap = bos->dev_capability[i]; + + if (dev_cap->bDevCapabilityType == LIBUSB_BT_USB_2_0_EXTENSION) + { + struct libusb_usb_2_0_extension_descriptor* usb_2_0_extension; + + ret = libusb_get_usb_2_0_extension_descriptor(NULL, dev_cap, &usb_2_0_extension); + if (ret < 0) + { + return; + } + + print_2_0_ext_cap(usb_2_0_extension); + libusb_free_usb_2_0_extension_descriptor(usb_2_0_extension); + } + else + if (dev_cap->bDevCapabilityType == LIBUSB_BT_SS_USB_DEVICE_CAPABILITY) + { + struct libusb_ss_usb_device_capability_descriptor* ss_dev_cap; + + ret = libusb_get_ss_usb_device_capability_descriptor(NULL, dev_cap, &ss_dev_cap); + if (ret < 0) + { + return; + } + + print_ss_usb_cap(ss_dev_cap); + libusb_free_ss_usb_device_capability_descriptor(ss_dev_cap); + } + } + + libusb_free_bos_descriptor(bos); +} + +static void print_interface(const struct libusb_interface* interface) +{ + int i; + + for (i = 0; i < interface->num_altsetting; i++) + { + print_altsetting(&interface->altsetting[i]); + } +} + +static void print_configuration(struct libusb_config_descriptor* config) +{ + uint8_t i; + + printf(" Configuration:\n"); + printf(" wTotalLength: %u\n", config->wTotalLength); + printf(" bNumInterfaces: %u\n", config->bNumInterfaces); + printf(" bConfigurationValue: %u\n", config->bConfigurationValue); + printf(" iConfiguration: %u\n", config->iConfiguration); + printf(" bmAttributes: %02xh\n", config->bmAttributes); + printf(" MaxPower: %u\n", config->MaxPower); + + for (i = 0; i < config->bNumInterfaces; i++) + { + print_interface(&config->interface[i]); + } +} + + +static void set_rpms(libusb_device* dev, libusb_device_handle* handle) +{ + + int rpms = 0; + int i = 0; + for (i = 1; i<=10; i++) + { + int rpms = i * 1000; + printf("Setting RPM to %i\n", rpms); + //sleep(5); + } +} + +static void print_device(libusb_device* dev, libusb_device_handle* handle) +{ + struct libusb_device_descriptor desc; + unsigned char string[256]; + const char* speed; + int ret; + uint8_t i; + + switch (libusb_get_device_speed(dev)) + { + case LIBUSB_SPEED_LOW: + speed = "1.5M"; + break; + case LIBUSB_SPEED_FULL: + speed = "12M"; + break; + case LIBUSB_SPEED_HIGH: + speed = "480M"; + break; + case LIBUSB_SPEED_SUPER: + speed = "5G"; + break; + case LIBUSB_SPEED_SUPER_PLUS: + speed = "10G"; + break; + default: + speed = "Unknown"; + } + + ret = libusb_get_device_descriptor(dev, &desc); + if (ret < 0) + { + fprintf(stderr, "failed to get device descriptor"); + return; + } + + printf("Dev (bus %u, device %u): %04X - %04X speed: %s\n", + libusb_get_bus_number(dev), libusb_get_device_address(dev), + desc.idVendor, desc.idProduct, speed); + + if (!handle) + { + libusb_open(dev, &handle); + } + + if (handle) + { + if (desc.iManufacturer) + { + ret = libusb_get_string_descriptor_ascii(handle, desc.iManufacturer, string, sizeof(string)); + if (ret > 0) + { + printf(" Manufacturer: %s\n", (char*)string); + } + } + + if (desc.iProduct) + { + ret = libusb_get_string_descriptor_ascii(handle, desc.iProduct, string, sizeof(string)); + if (ret > 0) + { + printf(" Product: %s\n", (char*)string); + } + } + + if (desc.iSerialNumber && verbose) + { + ret = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, string, sizeof(string)); + if (ret > 0) + { + printf(" Serial Number: %s\n", (char*)string); + } + } + } + + if (verbose) + { + for (i = 0; i < desc.bNumConfigurations; i++) + { + struct libusb_config_descriptor* config; + + ret = libusb_get_config_descriptor(dev, i, &config); + if (LIBUSB_SUCCESS != ret) + { + printf(" Couldn't retrieve descriptors\n"); + continue; + } + + print_configuration(config); + + libusb_free_config_descriptor(config); + } + + if (handle && desc.bcdUSB >= 0x0201) + { + print_bos(handle); + } + } + + if (handle) + { + libusb_close(handle); + } +} +/* +static void print_device(libusb_device *dev, libusb_device_handle *handle) +{ + struct libusb_device_descriptor desc; + unsigned char string[256]; + const char *speed; + int ret; + uint8_t i; + + switch (libusb_get_device_speed(dev)) { + case LIBUSB_SPEED_LOW: speed = "1.5M"; break; + case LIBUSB_SPEED_FULL: speed = "12M"; break; + case LIBUSB_SPEED_HIGH: speed = "480M"; break; + case LIBUSB_SPEED_SUPER: speed = "5G"; break; + case LIBUSB_SPEED_SUPER_PLUS: speed = "10G"; break; + default: speed = "Unknown"; + } + + ret = libusb_get_device_descriptor(dev, &desc); + if (ret < 0) { + fprintf(stderr, "failed to get device descriptor"); + return; + } + + printf("Dev (bus %u, device %u): %04X - %04X speed: %s\n", + libusb_get_bus_number(dev), libusb_get_device_address(dev), + desc.idVendor, desc.idProduct, speed); + + if (!handle) + libusb_open(dev, &handle); + + if (handle) { + if (desc.iManufacturer) { + ret = libusb_get_string_descriptor_ascii(handle, desc.iManufacturer, string, sizeof(string)); + if (ret > 0) + printf(" Manufacturer: %s\n", (char *)string); + } + + if (desc.iProduct) { + ret = libusb_get_string_descriptor_ascii(handle, desc.iProduct, string, sizeof(string)); + if (ret > 0) + printf(" Product: %s\n", (char *)string); + } + + if (desc.iSerialNumber && verbose) { + ret = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, string, sizeof(string)); + if (ret > 0) + printf(" Serial Number: %s\n", (char *)string); + } + } + + if (verbose) { + for (i = 0; i < desc.bNumConfigurations; i++) { + struct libusb_config_descriptor *config; + ret = libusb_get_config_descriptor(dev, i, &config); + if (LIBUSB_SUCCESS != ret) { + printf(" Couldn't retrieve descriptors\n"); + continue; + } + + print_configuration(config); + + libusb_free_config_descriptor(config); + } + + if (handle && desc.bcdUSB >= 0x0201) + print_bos(handle); + } + + if (handle) + libusb_close(handle); +} +*/ +#ifdef __linux__ +#include +#include +#include + +static int test_wrapped_device(const char* device_name) +{ + libusb_device_handle* handle; + int r, fd; + + fd = open(device_name, O_RDWR); + if (fd < 0) + { + printf("Error could not open %s: %s\n", device_name, strerror(errno)); + return 1; + } + r = libusb_wrap_sys_device(NULL, fd, &handle); + if (r) + { + printf("Error wrapping device: %s: %s\n", device_name, libusb_strerror(r)); + close(fd); + return 1; + } + print_device(libusb_get_device(handle), handle); + close(fd); + return 0; +} +#else +static int test_wrapped_device(const char* device_name) +{ + (void)device_name; + printf("Testing wrapped devices is not supported on your platform\n"); + return 1; +} +#endif + +int main(int argc, char* argv[]) +{ + const char* device_name = NULL; + libusb_device** devs; + libusb_device* dev; + ssize_t cnt; + int r, i; + + r = libusb_init(NULL); + if (r < 0) + { + return r; + } + + cnt = libusb_get_device_list(NULL, &devs); + if (cnt < 0) + { + libusb_exit(NULL); + return 1; + } + + /* + } + */ + + int devindx = 0; + for (i = 0; i<12; i++) + { + //int b = print_device(&devs[i], NULL); + //if (b == "9823") { + devindx = i; + //break; + //} + } + i = 0; + dev = devs[devindx]; + + print_device(dev, NULL); + + int d = 99; + libusb_device_handle* handle = NULL; + if (!handle) + { + d=libusb_open(dev, &handle); + } + + int len = 0; + + if(libusb_kernel_driver_active(handle, 0) == 1) + { + d = libusb_detach_kernel_driver(handle, 0); + } + + fprintf(stderr,"%i",d); + d = libusb_claim_interface(handle, 0); + + fprintf(stderr,"%i",d); + + //int rpm_array[20] = {41574, 48580, 54308, 58612, 60160, 61916, 62347, 62777, 63089, 63243, 63505, 63673, 63778, 63908, 64002, 64084, 64140, 64175, 64224, 64313}; + //int rpm_array[20] = {41574, 48580, 54308, 58612, 60160, 61416, 61952, 62470, 62969, 63231, 63305, 63673, 63749, 63998, 64002, 64084, 64140, 64175, 64224, 64313}; + //int rpm_array[10] = {58612, 62112, 63053, 63719, 64044, 64313, 64480, 64622, 64719, 64807}; + //int rpm_array[10] = {24701, 42244, 54308, 54308, 64255, 64313, 64480, 64622, 64719, 64807}; + int rpms = 0; + int iiii = 189; + + unsigned char bytes[65]; + + int rpm_array[21] = {41574, 48580, 54308, 58612, 60160, 61113, 61916, 62347, 62777, 63089, 63243, 63505, 63673, 63778, 63908, 64002, 64084, 64140, 64175, 64224, 64313}; + for (i = 0; i<21; i++) + { + int rpms = i * 500; + + printf("Setting RPM to %i hex as decimal %i\n", rpms, rpm_array[i]); + //printf("Setting RPM to %i\n", i); + + unsigned char bytes[65]; + unsigned long nnnnn = rpm_array[i]; + //unsigned long nnnnn = i; + unsigned long xlong = 0; + for (int x = 0; x < 64; x++) + { + bytes[x] = 0x00; + } + bytes[3] = (nnnnn >> 8) & 0xFF; + bytes[2] = nnnnn & 0xFF; + + //bytes[0] = (nnnnn >> 24) & 0xFF; + //bytes[1] = (nnnnn >> 16) & 0xFF; + //bytes[2] = (nnnnn >> 8) & 0xFF; + //bytes[3] = nnnnn & 0xFF; + if (handle) + { + d = libusb_interrupt_transfer(handle, ENDPOINT_OUT, bytes, sizeof(bytes), &len, 0); + } + + sleep(4); + } + + for (int x = 0; x < 65; x++) + { + bytes[x] = 0x00; + } + + if (handle) + { + d = libusb_interrupt_transfer(handle, ENDPOINT_OUT, bytes, sizeof(bytes), &len, 0); + } + + if (handle) + { + libusb_close(handle); + } + + //set_rpms(dev, NULL); + + libusb_free_device_list(devs, 1); + + libusb_exit(NULL); + return r; +} diff --git a/tests/usesharedmemory.c b/tests/usesharedmemory.c new file mode 100644 index 0000000..63d024c --- /dev/null +++ b/tests/usesharedmemory.c @@ -0,0 +1,88 @@ +/** + * Simple program demonstrating shared memory in POSIX systems. + * + * This is the consumer process + * + * Figure 3.18 + * + * @author Gagne, Galvin, Silberschatz + * Operating System Concepts - Ninth Edition + * Copyright John Wiley & Sons - 2013 + * + * modifications by dheller@cse.psu.edu, 31 Jan. 2014 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void display(char* prog, char* bytes, int n); + +int main(void) +{ + const char* name = "/shm-example"; // file name + const int SIZE = 4096; // file size + + int shm_fd; // file descriptor, from shm_open() + char* shm_base; // base address, from mmap() + + /* open the shared memory segment as if it was a file */ + shm_fd = shm_open(name, O_RDONLY, 0666); + if (shm_fd == -1) + { + printf("cons: Shared memory failed: %s\n", strerror(errno)); + exit(1); + } + + /* map the shared memory segment to the address space of the process */ + shm_base = mmap(0, SIZE, PROT_READ, MAP_SHARED, shm_fd, 0); + if (shm_base == MAP_FAILED) + { + printf("cons: Map failed: %s\n", strerror(errno)); + // close and unlink? + exit(1); + } + + /* read from the mapped shared memory segment */ + display("cons", shm_base, 64); // first as bytes, then as a string + printf("%s", shm_base); + + /* remove the mapped shared memory segment from the address space of the process */ + if (munmap(shm_base, SIZE) == -1) + { + printf("cons: Unmap failed: %s\n", strerror(errno)); + exit(1); + } + + /* close the shared memory segment as if it was a file */ + if (close(shm_fd) == -1) + { + printf("cons: Close failed: %s\n", strerror(errno)); + exit(1); + } + + /* remove the shared memory segment from the file system */ + if (shm_unlink(name) == -1) + { + printf("cons: Error removing %s: %s\n", name, strerror(errno)); + exit(1); + } + + return 0; +} + +void display(char* prog, char* bytes, int n) +{ + printf("display: %s\n", prog); + for (int i = 0; i < n; i++) + { + printf("%02x%c", bytes[i], ((i+1)%16) ? ' ' : '\n'); + } + printf("\n"); +} diff --git a/udev/69-monocoque.rules b/udev/69-monocoque.rules new file mode 100644 index 0000000..4b3c284 --- /dev/null +++ b/udev/69-monocoque.rules @@ -0,0 +1 @@ +SUBSYSTEMS=="usb",ATTRS{product}=="Rev Burner",GROUP="input",TAG+="uaccess"