diff --git a/src/CMakeData-arch.cmake b/src/CMakeData-arch.cmake index dd76fa30d8..62e2260d37 100644 --- a/src/CMakeData-arch.cmake +++ b/src/CMakeData-arch.cmake @@ -227,17 +227,22 @@ if(NOT APPLE) endif() else() # Unix/Linux TODO: Linux HAVE_PARALLEL_PORT if(LINUX) + list(APPEND SMDATA_LINK_LIB "udev") list(APPEND SMDATA_ARCH_LIGHTS_SRC + "arch/Lights/LightsDriver_Linux_Leds.cpp" "arch/Lights/LightsDriver_Linux_PIUIO.cpp" "arch/Lights/LightsDriver_Linux_PIUIO_Leds.cpp" "arch/Lights/LightsDriver_Linux_PIUIOBTN_Leds.cpp" "arch/Lights/LightsDriver_Linux_ITGIO.cpp" + "arch/Lights/LightsDriver_Linux_stac.cpp" "arch/Lights/LightsDriver_LinuxWeedTech.cpp") list(APPEND SMDATA_ARCH_LIGHTS_HPP + "arch/Lights/LightsDriver_Linux_Leds.h" "arch/Lights/LightsDriver_Linux_PIUIO.h" "arch/Lights/LightsDriver_Linux_PIUIO_Leds.h" "arch/Lights/LightsDriver_Linux_PIUIOBTN_Leds.h" "arch/Lights/LightsDriver_Linux_ITGIO.h" + "arch/Lights/LightsDriver_Linux_stac.h" "arch/Lights/LightsDriver_LinuxWeedTech.h") if(WITH_PARALLEL_PORT) list(APPEND SMDATA_ARCH_LIGHTS_SRC diff --git a/src/arch/Lights/LightsDriver_Linux_ITGIO.cpp b/src/arch/Lights/LightsDriver_Linux_ITGIO.cpp index 21c4ab6c2b..f00786bb8e 100644 --- a/src/arch/Lights/LightsDriver_Linux_ITGIO.cpp +++ b/src/arch/Lights/LightsDriver_Linux_ITGIO.cpp @@ -18,121 +18,100 @@ REGISTER_LIGHTS_DRIVER_CLASS2(ITGIO, Linux_ITGIO); -//NOTE: light index 14 is not mapped, this is the coin drop "light" +//NOTE: light index 14 is not mapped, +//this is the coin drop "light" to increment the coin counter. -namespace { - const char *cabinet_leds[NUM_CabinetLight] = { - //UL, UR, LL, LR, Bass L, Bass R - "/sys/class/leds/itgio::output8/brightness", - "/sys/class/leds/itgio::output10/brightness", - "/sys/class/leds/itgio::output9/brightness", - "/sys/class/leds/itgio::output11/brightness", - "/sys/class/leds/itgio::output15/brightness", - "/sys/class/leds/itgio::output15/brightness", +namespace +{ + const int cabinet_lights[NUM_CabinetLight] = { + 8, //LIGHT_MARQUEE_UP_LEFT + 10, //LIGHT_MARQUEE_UP_RIGHT + 9, //LIGHT_MARQUEE_LR_LEFT + 11, //LIGHT_MARQUEE_LR_RIGHT + 15, //LIGHT_BASS_LEFT + 15, //LIGHT_BASS_RIGHT }; - const char *dance_leds[NUM_GameController][NUM_GameButton] = { - { - nullptr, nullptr, nullptr, nullptr, - //player start - "/sys/class/leds/itgio::output13/brightness", - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - //left right up down - "/sys/class/leds/itgio::output1/brightness", - "/sys/class/leds/itgio::output0/brightness", - "/sys/class/leds/itgio::output3/brightness", - "/sys/class/leds/itgio::output2/brightness", - }, - { - nullptr, nullptr, nullptr, nullptr, - //player start - "/sys/class/leds/itgio::output12/brightness", - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - //left right up down - "/sys/class/leds/itgio::output5/brightness", - "/sys/class/leds/itgio::output4/brightness", - "/sys/class/leds/itgio::output7/brightness", - "/sys/class/leds/itgio::output6/brightness", - }, + const int player1_lights[NUM_GameButton] = { + -1, //GAME_BUTTON_MENULEFT + -1, //GAME_BUTTON_MENURIGHT + -1, //GAME_BUTTON_MENUUP + -1, //GAME_BUTTON_MENUDOWN + 13, //GAME_BUTTON_START + -1, //GAME_BUTTON_SELECT + -1, //GAME_BUTTON_BACK + -1, //GAME_BUTTON_COIN + -1, //GAME_BUTTON_OPERATOR + -1, //GAME_BUTTON_EFFECT_UP + -1, //GAME_BUTTON_EFFECT_DOWN + 1, //GAME_BUTTON_CUSTOM_01 + 0, //GAME_BUTTON_CUSTOM_02 + 3, //GAME_BUTTON_CUSTOM_03 + 2, //GAME_BUTTON_CUSTOM_04 + -1, //GAME_BUTTON_CUSTOM_05 + -1, //GAME_BUTTON_CUSTOM_06 + -1, //GAME_BUTTON_CUSTOM_07 + -1, //GAME_BUTTON_CUSTOM_08 + -1, //GAME_BUTTON_CUSTOM_09 + -1, //GAME_BUTTON_CUSTOM_10 + -1, //GAME_BUTTON_CUSTOM_11 + -1, //GAME_BUTTON_CUSTOM_12 + -1, //GAME_BUTTON_CUSTOM_13 + -1, //GAME_BUTTON_CUSTOM_14 + -1, //GAME_BUTTON_CUSTOM_15 + -1, //GAME_BUTTON_CUSTOM_16 + -1, //GAME_BUTTON_CUSTOM_17 + -1, //GAME_BUTTON_CUSTOM_18 + -1, //GAME_BUTTON_CUSTOM_19 }; - bool SetLight(const char *filename, bool on) - { - if (filename == nullptr) - return true; - FILE *f = fopen(filename, "w"); - if (f == nullptr) - { - return false; - } - fprintf(f, "%d", on ? 255 : 0); - fclose(f); - return true; - } -} + const int player2_lights[NUM_GameButton] = { + -1, //GAME_BUTTON_MENULEFT + -1, //GAME_BUTTON_MENURIGHT + -1, //GAME_BUTTON_MENUUP + -1, //GAME_BUTTON_MENUDOWN + 12, //GAME_BUTTON_START + -1, //GAME_BUTTON_SELECT + -1, //GAME_BUTTON_BACK + -1, //GAME_BUTTON_COIN + -1, //GAME_BUTTON_OPERATOR + -1, //GAME_BUTTON_EFFECT_UP + -1, //GAME_BUTTON_EFFECT_DOWN + 5, //GAME_BUTTON_CUSTOM_01 + 4, //GAME_BUTTON_CUSTOM_02 + 7, //GAME_BUTTON_CUSTOM_03 + 6, //GAME_BUTTON_CUSTOM_04 + -1, //GAME_BUTTON_CUSTOM_05 + -1, //GAME_BUTTON_CUSTOM_06 + -1, //GAME_BUTTON_CUSTOM_07 + -1, //GAME_BUTTON_CUSTOM_08 + -1, //GAME_BUTTON_CUSTOM_09 + -1, //GAME_BUTTON_CUSTOM_10 + -1, //GAME_BUTTON_CUSTOM_11 + -1, //GAME_BUTTON_CUSTOM_12 + -1, //GAME_BUTTON_CUSTOM_13 + -1, //GAME_BUTTON_CUSTOM_14 + -1, //GAME_BUTTON_CUSTOM_15 + -1, //GAME_BUTTON_CUSTOM_16 + -1, //GAME_BUTTON_CUSTOM_17 + -1, //GAME_BUTTON_CUSTOM_18 + -1, //GAME_BUTTON_CUSTOM_19 + }; -LightsDriver_Linux_ITGIO::LightsDriver_Linux_ITGIO() +} // namespace + +void LightsDriver_Linux_ITGIO::Set(const LightsState *ls) { -} + SetCabinetLights(cabinet_lights, ls); -LightsDriver_Linux_ITGIO::~LightsDriver_Linux_ITGIO() -{ -} - -void LightsDriver_Linux_ITGIO::SetGameControllerLight(const LightsState *ls, GameController c, GameButton gb) -{ - //only push a change in the light iff it's an actual change. - if (ls->m_bGameButtonLights[c][gb] == previousLS.m_bGameButtonLights[c][gb]) - { - return; - } - - if (!SetLight(dance_leds[c][gb], ls->m_bGameButtonLights[c][gb])) - { - //grab the game info for a proper debug log. - const InputScheme *pInput = &GAMESTATE->GetCurrentGame()->m_InputScheme; - RString sInputName = pInput->m_szName; - - LOG->Warn("Error setting button light %s", GameButtonToString(pInput, gb).c_str()); - } -} - -void LightsDriver_Linux_ITGIO::Set( const LightsState *ls ) -{ - FOREACH_CabinetLight(light) - { - // Only SetLight if LightsState has changed since previous iteration. - // This reduces unncessary strain on udev. -dguzek - if (ls->m_bCabinetLights[light] == previousLS.m_bCabinetLights[light] ) - { - continue; - } - - if (!SetLight(cabinet_leds[light], ls->m_bCabinetLights[light])) - { - LOG->Warn("Error setting cabinet light %s", - CabinetLightToString(light).c_str()); - return; - } - } - - FOREACH_ENUM(GameController, c) - { - //takes care of each of the player buttons (UDLR) - FOREACH_GameButton_Custom(gb) - { - SetGameControllerLight(ls, c, gb); - } - - //Takes care of the cabinet based player lights. - SetGameControllerLight(ls, c, GAME_BUTTON_START); - } + SetGameControllerLights(GameController_1, player1_lights, ls); + SetGameControllerLights(GameController_2, player1_lights, ls); previousLS = *ls; } /* - * (c) 2020 StepMania team + * (c) 2020 din * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -154,4 +133,6 @@ void LightsDriver_Linux_ITGIO::Set( const LightsState *ls ) * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. - */ + * + * i love lamp + */ \ No newline at end of file diff --git a/src/arch/Lights/LightsDriver_Linux_ITGIO.h b/src/arch/Lights/LightsDriver_Linux_ITGIO.h index b5131b05ec..b5404ba85b 100644 --- a/src/arch/Lights/LightsDriver_Linux_ITGIO.h +++ b/src/arch/Lights/LightsDriver_Linux_ITGIO.h @@ -1,27 +1,26 @@ -/* LightsDriver_Linux_ITGIO: Control ITGIO lights via /sys/class/leds */ - #ifndef LightsDriver_Linux_ITGIO_H #define LightsDriver_Linux_ITGIO_H -#include "arch/Lights/LightsDriver.h" +#include "arch/Lights/LightsDriver_Linux_Leds.h" -class LightsDriver_Linux_ITGIO : public LightsDriver +class LightsDriver_Linux_ITGIO : public LightsDriver_Linux_Leds { -private: - LightsState previousLS; - void SetGameControllerLight(const LightsState *ls, GameController c, GameButton gb); - public: - LightsDriver_Linux_ITGIO(); - virtual ~LightsDriver_Linux_ITGIO(); + LightsDriver_Linux_ITGIO() {} + virtual ~LightsDriver_Linux_ITGIO() {} - virtual void Set( const LightsState *ls ); + virtual void Set(const LightsState *ls); + + virtual const char *GetGameControllerLightFile() + { + return "/sys/class/leds/itgio::output%d/brightness"; + } }; #endif /* - * (c) 2020 StepMania team + * (c) 2020 din * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -43,4 +42,6 @@ public: * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. - */ + * + * i love lamp + */ \ No newline at end of file diff --git a/src/arch/Lights/LightsDriver_Linux_Leds.cpp b/src/arch/Lights/LightsDriver_Linux_Leds.cpp new file mode 100644 index 0000000000..583d3e5373 --- /dev/null +++ b/src/arch/Lights/LightsDriver_Linux_Leds.cpp @@ -0,0 +1,163 @@ +#include "global.h" +#include +#if defined(HAVE_UNISTD_H) +#include +#endif +#include +#include + +#if defined(HAVE_FCNTL_H) +#include +#endif + +#include +#include "LightsDriver_Linux_Leds.h" +#include "GameState.h" +#include "Game.h" +#include "RageLog.h" + +bool LightsDriver_Linux_Leds::WriteLight(const char *filename, bool state) +{ + //if we are setting a light that doesn't exist, let the caller know the function was a "success" + //nullptr is used to define that the stepmania light is not mapped by the caller. + if (filename == nullptr) + { + return true; + } + + //LOG->Trace("LED: %s -> %d", filename, state); + + FILE *f = fopen(filename, "w"); + + //if the filename should exist, but doesn't, let the caller know of the failure (if desired) and log it. + if (f == nullptr) + { + LOG->Warn("Failed to set Linux_Led at %s. Check device permissions or udev rules. errno: %d: %s", + filename, errno, strerror(errno)); + return false; + } + + //write and close the file. + fprintf(f, "%d", state ? LINUX_LED_STATE_ON : LINUX_LED_STATE_OFF); + fclose(f); + + return true; +} + +bool LightsDriver_Linux_Leds::IsDance() +{ + pInput = &GAMESTATE->GetCurrentGame()->m_InputScheme; + sInputName = pInput->m_szName; + + return sInputName.EqualsNoCase("dance"); +} + +bool LightsDriver_Linux_Leds::IsPump() +{ + pInput = &GAMESTATE->GetCurrentGame()->m_InputScheme; + sInputName = pInput->m_szName; + + return sInputName.EqualsNoCase("pump"); +} + +void LightsDriver_Linux_Leds::SetLight(const char *filename, bool previous, bool desired) +{ + //don't overload the linux system if the light has not been changed. + if (previous != desired) + { + WriteLight(filename, desired); + } +} + +void LightsDriver_Linux_Leds::SetCabinetLights(const char *stringArray[], const LightsState *ls) +{ + FOREACH_CabinetLight(light) + { + SetLight(stringArray[light], previousLS.m_bCabinetLights[light], ls->m_bCabinetLights[light]); + } +} + +void LightsDriver_Linux_Leds::SetCabinetLights(const int intArray[], const LightsState *ls) +{ + const char *baseFileLocation = GetGameControllerLightFile(); + + if (baseFileLocation != nullptr) + { + char fileName[LINUX_LED_MAX_DIRECTORY_LENGTH]; + + FOREACH_CabinetLight(light) + { + //valid lights are zero and positive. + if (intArray[light] >= 0) + { + //don't waste sprintf time if we don't need to change the light + if (previousLS.m_bCabinetLights[light] != ls->m_bCabinetLights[light]) + { + sprintf(fileName, baseFileLocation, intArray[light]); + SetLight(fileName, previousLS.m_bCabinetLights[light], ls->m_bCabinetLights[light]); + } + } + } + } +} + +void LightsDriver_Linux_Leds::SetGameControllerLights(GameController gc, const char *stringArray[], const LightsState *ls) +{ + //iterate over all gamebuttons, including the menu/start/etc buttons. + FOREACH_ENUM(GameButton, gb) + { + SetLight(stringArray[gb], previousLS.m_bGameButtonLights[gc][gb], ls->m_bGameButtonLights[gc][gb]); + } +} + +void LightsDriver_Linux_Leds::SetGameControllerLights(GameController gc, const int intArray[], const LightsState *ls) +{ + const char *baseFileLocation = GetGameControllerLightFile(); + + if (baseFileLocation != nullptr) + { + char fileName[LINUX_LED_MAX_DIRECTORY_LENGTH]; + + //iterate over all gamebuttons, including the menu/start/etc buttons. + FOREACH_ENUM(GameButton, gb) + { + //valid lights are zero and positive. + if (intArray[gb] >= 0) + { + //don't waste sprintf time if we don't need to change the light + if (previousLS.m_bGameButtonLights[gc][gb] != ls->m_bGameButtonLights[gc][gb]) + { + sprintf(fileName, baseFileLocation, intArray[gb]); + SetLight(fileName, previousLS.m_bGameButtonLights[gc][gb], ls->m_bGameButtonLights[gc][gb]); + } + } + } + } +} + +/* + * (c) 2020 din + * All rights reserved. + * + * 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, and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * 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 OF + * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * i love lamp + */ diff --git a/src/arch/Lights/LightsDriver_Linux_Leds.h b/src/arch/Lights/LightsDriver_Linux_Leds.h new file mode 100644 index 0000000000..1d379e245d --- /dev/null +++ b/src/arch/Lights/LightsDriver_Linux_Leds.h @@ -0,0 +1,66 @@ +/* LightsDriver_Linux_Leds: Control Linux system lights via /sys/class/leds */ + +#ifndef LightsDriver_Linux_Leds_H +#define LightsDriver_Linux_Leds_H + +#include "arch/Lights/LightsDriver.h" + +class LightsDriver_Linux_Leds : public LightsDriver +{ +private: + static const uint8_t LINUX_LED_STATE_ON = 255; + static const uint8_t LINUX_LED_STATE_OFF = 0; + static const int LINUX_LED_MAX_DIRECTORY_LENGTH = PATH_MAX; + + const InputScheme *pInput; + RString sInputName; + + bool WriteLight(const char *filename, bool state); + +protected: + LightsState previousLS; + + bool IsDance(); + bool IsPump(); + + void SetLight(const char *filename, bool previous, bool desired); + + void SetCabinetLights(const char *stringArray[], const LightsState *ls); + void SetCabinetLights(const int intArray[], const LightsState *ls); + + void SetGameControllerLights(GameController gc, const char *stringArray[], const LightsState *ls); + void SetGameControllerLights(GameController gc, const int intArray[], const LightsState *ls); + +public: + virtual void Set(const LightsState *ls) = 0; + virtual const char *GetGameControllerLightFile() = 0; +}; + +#endif + +/* + * (c) 2020 din + * All rights reserved. + * + * 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, and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * 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 OF + * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * i love lamp + */ diff --git a/src/arch/Lights/LightsDriver_Linux_PIUIOBTN_Leds.cpp b/src/arch/Lights/LightsDriver_Linux_PIUIOBTN_Leds.cpp index 68729ec9e5..eea60a7e4e 100644 --- a/src/arch/Lights/LightsDriver_Linux_PIUIOBTN_Leds.cpp +++ b/src/arch/Lights/LightsDriver_Linux_PIUIOBTN_Leds.cpp @@ -10,6 +10,7 @@ #include #endif +#include #include "LightsDriver_Linux_PIUIOBTN_Leds.h" #include "GameState.h" #include "Game.h" @@ -17,75 +18,85 @@ REGISTER_LIGHTS_DRIVER_CLASS2(PIUIOBTN_Leds, Linux_PIUIOBTN_Leds); -namespace { - const char *game_btn_leds[NUM_GameController][NUM_GameButton] = { - { - "/sys/class/leds/piuio::bboutput6/brightness", //P1 GAME_BUTTON_MENULEFT - "/sys/class/leds/piuio::bboutput5/brightness", //P1 GAME_BUTTON_MENURIGHT - nullptr, nullptr, - "/sys/class/leds/piuio::bboutput4/brightness", //P1 GAME_BUTTON_START - "/sys/class/leds/piuio::bboutput7/brightness", //P1 GAME_BUTTON_SELECT - }, - { - "/sys/class/leds/piuio::bboutput2/brightness", //P2 GAME_BUTTON_MENULEFT - "/sys/class/leds/piuio::bboutput1/brightness", //P2 GAME_BUTTON_MENURIGHT - nullptr, nullptr, - "/sys/class/leds/piuio::bboutput0/brightness", //P2 GAME_BUTTON_START - "/sys/class/leds/piuio::bboutput3/brightness", //P2 GAME_BUTTON_SELECT - }, +namespace +{ + const int player1_lights[NUM_GameButton] = { + 6, //GAME_BUTTON_MENULEFT + 5, //GAME_BUTTON_MENURIGHT + -1, //GAME_BUTTON_MENUUP + -1, //GAME_BUTTON_MENUDOWN + 4, //GAME_BUTTON_START + 7, //GAME_BUTTON_SELECT + -1, //GAME_BUTTON_BACK + -1, //GAME_BUTTON_COIN + -1, //GAME_BUTTON_OPERATOR + -1, //GAME_BUTTON_EFFECT_UP + -1, //GAME_BUTTON_EFFECT_DOWN + -1, //GAME_BUTTON_CUSTOM_01 + -1, //GAME_BUTTON_CUSTOM_02 + -1, //GAME_BUTTON_CUSTOM_03 + -1, //GAME_BUTTON_CUSTOM_04 + -1, //GAME_BUTTON_CUSTOM_05 + -1, //GAME_BUTTON_CUSTOM_06 + -1, //GAME_BUTTON_CUSTOM_07 + -1, //GAME_BUTTON_CUSTOM_08 + -1, //GAME_BUTTON_CUSTOM_09 + -1, //GAME_BUTTON_CUSTOM_10 + -1, //GAME_BUTTON_CUSTOM_11 + -1, //GAME_BUTTON_CUSTOM_12 + -1, //GAME_BUTTON_CUSTOM_13 + -1, //GAME_BUTTON_CUSTOM_14 + -1, //GAME_BUTTON_CUSTOM_15 + -1, //GAME_BUTTON_CUSTOM_16 + -1, //GAME_BUTTON_CUSTOM_17 + -1, //GAME_BUTTON_CUSTOM_18 + -1, //GAME_BUTTON_CUSTOM_19 }; - bool SetLight(const char *filename, bool on) - { - if (filename == nullptr) - return true; - FILE *f = fopen(filename, "w"); - if (f == nullptr) - { - return false; - } - fprintf(f, "%d", on ? 255 : 0); - fclose(f); - return true; - } -} + const int player2_lights[NUM_GameButton] = { + 2, //GAME_BUTTON_MENULEFT + 1, //GAME_BUTTON_MENURIGHT + -1, //GAME_BUTTON_MENUUP + -1, //GAME_BUTTON_MENUDOWN + 0, //GAME_BUTTON_START + 3, //GAME_BUTTON_SELECT + -1, //GAME_BUTTON_BACK + -1, //GAME_BUTTON_COIN + -1, //GAME_BUTTON_OPERATOR + -1, //GAME_BUTTON_EFFECT_UP + -1, //GAME_BUTTON_EFFECT_DOWN + -1, //GAME_BUTTON_CUSTOM_01 + -1, //GAME_BUTTON_CUSTOM_02 + -1, //GAME_BUTTON_CUSTOM_03 + -1, //GAME_BUTTON_CUSTOM_04 + -1, //GAME_BUTTON_CUSTOM_05 + -1, //GAME_BUTTON_CUSTOM_06 + -1, //GAME_BUTTON_CUSTOM_07 + -1, //GAME_BUTTON_CUSTOM_08 + -1, //GAME_BUTTON_CUSTOM_09 + -1, //GAME_BUTTON_CUSTOM_10 + -1, //GAME_BUTTON_CUSTOM_11 + -1, //GAME_BUTTON_CUSTOM_12 + -1, //GAME_BUTTON_CUSTOM_13 + -1, //GAME_BUTTON_CUSTOM_14 + -1, //GAME_BUTTON_CUSTOM_15 + -1, //GAME_BUTTON_CUSTOM_16 + -1, //GAME_BUTTON_CUSTOM_17 + -1, //GAME_BUTTON_CUSTOM_18 + -1, //GAME_BUTTON_CUSTOM_19 + }; +} // namespace -LightsDriver_Linux_PIUIOBTN_Leds::LightsDriver_Linux_PIUIOBTN_Leds() +void LightsDriver_Linux_PIUIOBTN_Leds::Set(const LightsState *ls) { -} - -LightsDriver_Linux_PIUIOBTN_Leds::~LightsDriver_Linux_PIUIOBTN_Leds() -{ -} - -void LightsDriver_Linux_PIUIOBTN_Leds::Set( const LightsState *ls ) -{ - const InputScheme *pInput = &GAMESTATE->GetCurrentGame()->m_InputScheme; - RString sInputName = pInput->m_szName; - - FOREACH_ENUM(GameController, c) - { - FOREACH_ENUM( GameButton, gb ) - { - if (ls->m_bGameButtonLights[c][gb] == previousLS.m_bGameButtonLights[c][gb]) - { - continue; - } - - if (!SetLight(game_btn_leds[c][gb], ls->m_bGameButtonLights[c][gb])) - { - LOG->Warn("Error setting button light %s", - GameButtonToString(pInput, gb).c_str()); - return; - } - } - } + SetGameControllerLights(GameController_1, player1_lights, ls); + SetGameControllerLights(GameController_2, player2_lights, ls); previousLS = *ls; } /* - * (c) 2020 StepMania team + * (c) 2020 din * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -107,4 +118,6 @@ void LightsDriver_Linux_PIUIOBTN_Leds::Set( const LightsState *ls ) * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. - */ + * + * i love lamp + */ \ No newline at end of file diff --git a/src/arch/Lights/LightsDriver_Linux_PIUIOBTN_Leds.h b/src/arch/Lights/LightsDriver_Linux_PIUIOBTN_Leds.h index 157533b37c..37fd24793d 100644 --- a/src/arch/Lights/LightsDriver_Linux_PIUIOBTN_Leds.h +++ b/src/arch/Lights/LightsDriver_Linux_PIUIOBTN_Leds.h @@ -1,26 +1,26 @@ -/* LightsDriver_Linux_PIUIOBTN_Leds: Control PIUIO Button Board lights via /sys/class/leds */ - #ifndef LightsDriver_Linux_PIUIOBTN_Leds_H #define LightsDriver_Linux_PIUIOBTN_Leds_H -#include "arch/Lights/LightsDriver.h" +#include "arch/Lights/LightsDriver_Linux_Leds.h" -class LightsDriver_Linux_PIUIOBTN_Leds : public LightsDriver +class LightsDriver_Linux_PIUIOBTN_Leds : public LightsDriver_Linux_Leds { -private: - LightsState previousLS; - public: - LightsDriver_Linux_PIUIOBTN_Leds(); - virtual ~LightsDriver_Linux_PIUIOBTN_Leds(); + LightsDriver_Linux_PIUIOBTN_Leds() {} + virtual ~LightsDriver_Linux_PIUIOBTN_Leds() {} - virtual void Set( const LightsState *ls ); + virtual void Set(const LightsState *ls); + + virtual const char *GetGameControllerLightFile() + { + return "/sys/class/leds/piuio::bboutput%d/brightness"; + } }; #endif /* - * (c) 2020 StepMania team + * (c) 2020 din * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -42,4 +42,6 @@ public: * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. - */ + * + * i love lamp + */ \ No newline at end of file diff --git a/src/arch/Lights/LightsDriver_Linux_PIUIO_Leds.cpp b/src/arch/Lights/LightsDriver_Linux_PIUIO_Leds.cpp index 6fac181a50..0bf3d7e7e2 100644 --- a/src/arch/Lights/LightsDriver_Linux_PIUIO_Leds.cpp +++ b/src/arch/Lights/LightsDriver_Linux_PIUIO_Leds.cpp @@ -18,146 +18,178 @@ REGISTER_LIGHTS_DRIVER_CLASS2(PIUIO_Leds, Linux_PIUIO_Leds); -namespace { - const char *cabinet_leds[NUM_CabinetLight] = { - "/sys/class/leds/piuio::output23/brightness", - "/sys/class/leds/piuio::output26/brightness", - "/sys/class/leds/piuio::output25/brightness", - "/sys/class/leds/piuio::output24/brightness", - "/sys/class/leds/piuio::output10/brightness", - "/sys/class/leds/piuio::output10/brightness", +//NOTE: front usb ports are off by default, index 27 enables them. +//It is currently unmapped. + +namespace +{ + const int cabinet_lights[NUM_CabinetLight] = { + 23, //LIGHT_MARQUEE_UP_LEFT + 26, //LIGHT_MARQUEE_UP_RIGHT + 25, //LIGHT_MARQUEE_LR_LEFT + 24, //LIGHT_MARQUEE_LR_RIGHT + 10, //LIGHT_BASS_LEFT + 10, //LIGHT_BASS_RIGHT }; - const char *dance_leds[NUM_GameController][NUM_GameButton] = { - { - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - "/sys/class/leds/piuio::output20/brightness", - "/sys/class/leds/piuio::output21/brightness", - "/sys/class/leds/piuio::output18/brightness", - "/sys/class/leds/piuio::output19/brightness", - }, - { - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - "/sys/class/leds/piuio::output4/brightness", - "/sys/class/leds/piuio::output5/brightness", - "/sys/class/leds/piuio::output2/brightness", - "/sys/class/leds/piuio::output3/brightness", - }, + const int player1_dance_lights[NUM_GameButton] = { + -1, //GAME_BUTTON_MENULEFT + -1, //GAME_BUTTON_MENURIGHT + -1, //GAME_BUTTON_MENUUP + -1, //GAME_BUTTON_MENUDOWN + -1, //GAME_BUTTON_START + -1, //GAME_BUTTON_SELECT + -1, //GAME_BUTTON_BACK + -1, //GAME_BUTTON_COIN + -1, //GAME_BUTTON_OPERATOR + -1, //GAME_BUTTON_EFFECT_UP + -1, //GAME_BUTTON_EFFECT_DOWN + 20, //GAME_BUTTON_CUSTOM_01 + 21, //GAME_BUTTON_CUSTOM_02 + 18, //GAME_BUTTON_CUSTOM_03 + 19, //GAME_BUTTON_CUSTOM_04 + -1, //GAME_BUTTON_CUSTOM_05 + -1, //GAME_BUTTON_CUSTOM_06 + -1, //GAME_BUTTON_CUSTOM_07 + -1, //GAME_BUTTON_CUSTOM_08 + -1, //GAME_BUTTON_CUSTOM_09 + -1, //GAME_BUTTON_CUSTOM_10 + -1, //GAME_BUTTON_CUSTOM_11 + -1, //GAME_BUTTON_CUSTOM_12 + -1, //GAME_BUTTON_CUSTOM_13 + -1, //GAME_BUTTON_CUSTOM_14 + -1, //GAME_BUTTON_CUSTOM_15 + -1, //GAME_BUTTON_CUSTOM_16 + -1, //GAME_BUTTON_CUSTOM_17 + -1, //GAME_BUTTON_CUSTOM_18 + -1, //GAME_BUTTON_CUSTOM_19 }; - const char *pump_leds[NUM_GameController][NUM_GameButton] = { - { - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - "/sys/class/leds/piuio::output2/brightness", - "/sys/class/leds/piuio::output3/brightness", - "/sys/class/leds/piuio::output4/brightness", - "/sys/class/leds/piuio::output5/brightness", - "/sys/class/leds/piuio::output6/brightness", - }, - { - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - "/sys/class/leds/piuio::output18/brightness", - "/sys/class/leds/piuio::output19/brightness", - "/sys/class/leds/piuio::output20/brightness", - "/sys/class/leds/piuio::output21/brightness", - "/sys/class/leds/piuio::output22/brightness", - }, + const int player2_dance_lights[NUM_GameButton] = { + -1, //GAME_BUTTON_MENULEFT + -1, //GAME_BUTTON_MENURIGHT + -1, //GAME_BUTTON_MENUUP + -1, //GAME_BUTTON_MENUDOWN + -1, //GAME_BUTTON_START + -1, //GAME_BUTTON_SELECT + -1, //GAME_BUTTON_BACK + -1, //GAME_BUTTON_COIN + -1, //GAME_BUTTON_OPERATOR + -1, //GAME_BUTTON_EFFECT_UP + -1, //GAME_BUTTON_EFFECT_DOWN + 4, //GAME_BUTTON_CUSTOM_01 + 5, //GAME_BUTTON_CUSTOM_02 + 2, //GAME_BUTTON_CUSTOM_03 + 3, //GAME_BUTTON_CUSTOM_04 + -1, //GAME_BUTTON_CUSTOM_05 + -1, //GAME_BUTTON_CUSTOM_06 + -1, //GAME_BUTTON_CUSTOM_07 + -1, //GAME_BUTTON_CUSTOM_08 + -1, //GAME_BUTTON_CUSTOM_09 + -1, //GAME_BUTTON_CUSTOM_10 + -1, //GAME_BUTTON_CUSTOM_11 + -1, //GAME_BUTTON_CUSTOM_12 + -1, //GAME_BUTTON_CUSTOM_13 + -1, //GAME_BUTTON_CUSTOM_14 + -1, //GAME_BUTTON_CUSTOM_15 + -1, //GAME_BUTTON_CUSTOM_16 + -1, //GAME_BUTTON_CUSTOM_17 + -1, //GAME_BUTTON_CUSTOM_18 + -1, //GAME_BUTTON_CUSTOM_19 }; - bool SetLight(const char *filename, bool on) - { - if (filename == nullptr) - return true; - FILE *f = fopen(filename, "w"); - if (f == nullptr) - { - return false; - } - fprintf(f, "%d", on ? 255 : 0); - fclose(f); - return true; - } -} + const int player1_pump_lights[NUM_GameButton] = { + -1, //GAME_BUTTON_MENULEFT + -1, //GAME_BUTTON_MENURIGHT + -1, //GAME_BUTTON_MENUUP + -1, //GAME_BUTTON_MENUDOWN + -1, //GAME_BUTTON_START + -1, //GAME_BUTTON_SELECT + -1, //GAME_BUTTON_BACK + -1, //GAME_BUTTON_COIN + -1, //GAME_BUTTON_OPERATOR + -1, //GAME_BUTTON_EFFECT_UP + -1, //GAME_BUTTON_EFFECT_DOWN + 2, //GAME_BUTTON_CUSTOM_01 + 3, //GAME_BUTTON_CUSTOM_02 + 4, //GAME_BUTTON_CUSTOM_03 + 5, //GAME_BUTTON_CUSTOM_04 + 6, //GAME_BUTTON_CUSTOM_05 + -1, //GAME_BUTTON_CUSTOM_06 + -1, //GAME_BUTTON_CUSTOM_07 + -1, //GAME_BUTTON_CUSTOM_08 + -1, //GAME_BUTTON_CUSTOM_09 + -1, //GAME_BUTTON_CUSTOM_10 + -1, //GAME_BUTTON_CUSTOM_11 + -1, //GAME_BUTTON_CUSTOM_12 + -1, //GAME_BUTTON_CUSTOM_13 + -1, //GAME_BUTTON_CUSTOM_14 + -1, //GAME_BUTTON_CUSTOM_15 + -1, //GAME_BUTTON_CUSTOM_16 + -1, //GAME_BUTTON_CUSTOM_17 + -1, //GAME_BUTTON_CUSTOM_18 + -1, //GAME_BUTTON_CUSTOM_19 + }; -LightsDriver_Linux_PIUIO_Leds::LightsDriver_Linux_PIUIO_Leds() + const int player2_pump_lights[NUM_GameButton] = { + -1, //GAME_BUTTON_MENULEFT + -1, //GAME_BUTTON_MENURIGHT + -1, //GAME_BUTTON_MENUUP + -1, //GAME_BUTTON_MENUDOWN + -1, //GAME_BUTTON_START + -1, //GAME_BUTTON_SELECT + -1, //GAME_BUTTON_BACK + -1, //GAME_BUTTON_COIN + -1, //GAME_BUTTON_OPERATOR + -1, //GAME_BUTTON_EFFECT_UP + -1, //GAME_BUTTON_EFFECT_DOWN + 18, //GAME_BUTTON_CUSTOM_01 + 19, //GAME_BUTTON_CUSTOM_02 + 20, //GAME_BUTTON_CUSTOM_03 + 21, //GAME_BUTTON_CUSTOM_04 + 22, //GAME_BUTTON_CUSTOM_05 + -1, //GAME_BUTTON_CUSTOM_06 + -1, //GAME_BUTTON_CUSTOM_07 + -1, //GAME_BUTTON_CUSTOM_08 + -1, //GAME_BUTTON_CUSTOM_09 + -1, //GAME_BUTTON_CUSTOM_10 + -1, //GAME_BUTTON_CUSTOM_11 + -1, //GAME_BUTTON_CUSTOM_12 + -1, //GAME_BUTTON_CUSTOM_13 + -1, //GAME_BUTTON_CUSTOM_14 + -1, //GAME_BUTTON_CUSTOM_15 + -1, //GAME_BUTTON_CUSTOM_16 + -1, //GAME_BUTTON_CUSTOM_17 + -1, //GAME_BUTTON_CUSTOM_18 + -1, //GAME_BUTTON_CUSTOM_19 + }; + +} // namespace + +void LightsDriver_Linux_PIUIO_Leds::Set(const LightsState *ls) { -} + SetCabinetLights(cabinet_lights, ls); -LightsDriver_Linux_PIUIO_Leds::~LightsDriver_Linux_PIUIO_Leds() -{ -} - -void LightsDriver_Linux_PIUIO_Leds::Set( const LightsState *ls ) -{ - FOREACH_CabinetLight(light) + //gamemode is checked here as gamemode can change during runtime + //...although I'd be impressed if someone wanted to swap from pump to dance + //on a cabinet without shutting down first... + //...including swapping pads from your pump cabinet to your itg cabinet... + if (IsDance()) { - // Only SetLight if LightsState has changed since previous iteration. - // This reduces unncessary strain on udev. -dguzek - if (ls->m_bCabinetLights[light] == previousLS.m_bCabinetLights[light] ) - { - continue; - } - - if (!SetLight(cabinet_leds[light], ls->m_bCabinetLights[light])) - { - LOG->Warn("Error setting cabinet light %s", - CabinetLightToString(light).c_str()); - return; - } + SetGameControllerLights(GameController_1, player1_dance_lights, ls); + SetGameControllerLights(GameController_2, player1_dance_lights, ls); } - - const InputScheme *pInput = &GAMESTATE->GetCurrentGame()->m_InputScheme; - RString sInputName = pInput->m_szName; - if (sInputName.EqualsNoCase("dance")) + else if (IsPump()) { - FOREACH_ENUM(GameController, c) - { - FOREACH_GameButton_Custom(gb) - { - if (ls->m_bGameButtonLights[c][gb] == previousLS.m_bGameButtonLights[c][gb]) - { - continue; - } - - if (!SetLight(dance_leds[c][gb], ls->m_bGameButtonLights[c][gb])) - { - LOG->Warn("Error setting button light %s", - GameButtonToString(pInput, gb).c_str()); - return; - } - } - } - } - else if (sInputName.EqualsNoCase("pump")) - { - FOREACH_ENUM(GameController, c) - { - FOREACH_GameButton_Custom(gb) - { - if (ls->m_bGameButtonLights[c][gb] == previousLS.m_bGameButtonLights[c][gb]) - { - continue; - } - - if (!SetLight(pump_leds[c][gb], ls->m_bGameButtonLights[c][gb])) - { - LOG->Warn("Error setting button light %s", - GameButtonToString(pInput, gb).c_str()); - return; - } - } - } - } - else - { - return; + SetGameControllerLights(GameController_1, player1_pump_lights, ls); + SetGameControllerLights(GameController_2, player1_pump_lights, ls); } previousLS = *ls; } /* - * (c) 2014 StepMania team + * (c) 2020 din * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -179,4 +211,6 @@ void LightsDriver_Linux_PIUIO_Leds::Set( const LightsState *ls ) * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. - */ + * + * i love lamp + */ \ No newline at end of file diff --git a/src/arch/Lights/LightsDriver_Linux_PIUIO_Leds.h b/src/arch/Lights/LightsDriver_Linux_PIUIO_Leds.h index 83e0935f4f..dddf9ce4b1 100644 --- a/src/arch/Lights/LightsDriver_Linux_PIUIO_Leds.h +++ b/src/arch/Lights/LightsDriver_Linux_PIUIO_Leds.h @@ -1,26 +1,27 @@ -/* LightsDriver_Linux_PIUIO_Leds: Control PIUIO lights via /sys/class/leds */ - #ifndef LightsDriver_Linux_PIUIO_Leds_H #define LightsDriver_Linux_PIUIO_Leds_H -#include "arch/Lights/LightsDriver.h" +#include "arch/Lights/LightsDriver_Linux_Leds.h" -class LightsDriver_Linux_PIUIO_Leds : public LightsDriver +class LightsDriver_Linux_PIUIO_Leds : public LightsDriver_Linux_Leds { -private: - LightsState previousLS; - public: - LightsDriver_Linux_PIUIO_Leds(); - virtual ~LightsDriver_Linux_PIUIO_Leds(); + LightsDriver_Linux_PIUIO_Leds() {} + virtual ~LightsDriver_Linux_PIUIO_Leds() {} - virtual void Set( const LightsState *ls ); + virtual void Set(const LightsState *ls); + + virtual const char *GetGameControllerLightFile() + { + + return "/sys/class/leds/piuio::output%d/brightness"; + } }; #endif /* - * (c) 2014 StepMania team + * (c) 2020 din * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -42,4 +43,6 @@ public: * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. - */ + * + * i love lamp + */ \ No newline at end of file diff --git a/src/arch/Lights/LightsDriver_Linux_stac.cpp b/src/arch/Lights/LightsDriver_Linux_stac.cpp new file mode 100644 index 0000000000..f5e187f623 --- /dev/null +++ b/src/arch/Lights/LightsDriver_Linux_stac.cpp @@ -0,0 +1,286 @@ +#include "global.h" +#include +#if defined(HAVE_UNISTD_H) +#include +#endif +#include +#include + +#if defined(HAVE_FCNTL_H) +#include +#endif + +#include +#include "LightsDriver_Linux_stac.h" +#include "GameState.h" +#include "Game.h" +#include "RageLog.h" + +#include +#include +#include +#include + +REGISTER_LIGHTS_DRIVER_CLASS2(stac, Linux_stac); + +StacDevice::StacDevice(uint8_t pn) +{ + memset(outputBuffer, 0x00, sizeof(outputBuffer)); + + deviceVID = STAC_VID; + playerNumber = pn; + + switch (playerNumber) + { + case 1: + devicePID = STAC_PID_P1; + break; + + case 2: + devicePID = STAC_PID_P2; + break; + + default: + LOG->Warn("STAC: Invalid player number instantiated, no PID set."); + break; + } +} + +//function adapted from example in: +//https://web.archive.org/web/20190312223426/http://www.signal11.us/oss/udev/ +//allows us to find the /dev/hidraw device that matches the Snek usb VID and PID. +void StacDevice::FindDevice() +{ + struct udev *udev; + struct udev_enumerate *enumerate; + struct udev_list_entry *devices, *dev_list_entry; + struct udev_device *dev; + + const char *devVID; + const char *devPID; + + bool found = false; + + /* Create the udev object */ + udev = udev_new(); + if (!udev) + { + LOG->Warn("Can't create udev"); + return; + } + + /* Create a list of the devices in the 'hidraw' subsystem. */ + enumerate = udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem(enumerate, "hidraw"); + udev_enumerate_scan_devices(enumerate); + devices = udev_enumerate_get_list_entry(enumerate); + /* For each item enumerated, print out its information. + udev_list_entry_foreach is a macro which expands to + a loop. The loop will be executed for each member in + devices, setting dev_list_entry to a list entry + which contains the device's path in /sys. */ + udev_list_entry_foreach(dev_list_entry, devices) + { + const char *path; + + /* Get the filename of the /sys entry for the device + and create a udev_device object (dev) representing it */ + path = udev_list_entry_get_name(dev_list_entry); + dev = udev_device_new_from_syspath(udev, path); + + devicePath = udev_device_get_devnode(dev); + + /* The device pointed to by dev contains information about + the hidraw device. In order to get information about the + USB device, get the parent device with the + subsystem/devtype pair of "usb"/"usb_device". This will + be several levels up the tree, but the function will find + it.*/ + dev = udev_device_get_parent_with_subsystem_devtype( + dev, + "usb", + "usb_device"); + + if (!dev) + { + LOG->Warn("Unable to find parent usb device."); + return; + } + + devVID = udev_device_get_sysattr_value(dev, "idVendor"); + devPID = udev_device_get_sysattr_value(dev, "idProduct"); + + udev_device_unref(dev); + + if (strcmp(devVID, deviceVID) == 0 && + strcmp(devPID, devicePID) == 0) + { + found = true; + break; + } + } + + /* Free the enumerator object */ + udev_enumerate_unref(enumerate); + + udev_unref(udev); + + if (!found) + { + devicePath = nullptr; + } +} + +void StacDevice::Connect() +{ + devicePath = nullptr; + fd = -1; + + FindDevice(); + + if (devicePath == nullptr) + { + LOG->Warn("stac pn%d not found. Please ensure device is connected to system.", playerNumber); + } + else + { + fd = open(devicePath, O_RDWR | O_NONBLOCK); + + if (!IsConnected()) + { + LOG->Warn("Could not open stac pn%d at %s. Check device permissions or udev rules. errno: %d: %s", playerNumber, devicePath, errno, strerror(errno)); + } + else + { + LOG->Info("Successfully opened stac pn%d at: %s", playerNumber, devicePath); + + //ensure the device, when connected, gets a fresh state. + newState = true; + } + } +} + +void StacDevice::Close() +{ + if (fd >= 0) + { + close(fd); + + LOG->Info("Closed stac pn%d at: %s", playerNumber, devicePath); + + fd = -1; + } +} + +void StacDevice::SetInBuffer(int index, bool lightState) +{ + //the first byte is the report ID, so we offset it here to adjust. + uint8_t index_offset = index + 1; + + //each index in the array represents a single light, + //the light will turn on for any value that isn't 0x00 + uint8_t val = lightState ? 0xFF : 0x00; + + //ensure the index is valid and the light value has changed. + if (index_offset < STAC_HIDREPORT_SIZE && outputBuffer[index_offset] != val) + { + outputBuffer[index_offset] = val; + + //signal the loop to push the new buffer to the device. + newState = true; + } +} + +void StacDevice::PushBufferToDevice() +{ + //first check to make sure we have *new* data to send to the device. + if (newState) + { + //Always ensure there is a report ID before writing, + //otherwise linux will complain and throw errors. + outputBuffer[0] = STAC_REPORT_ID; + + int res = write(fd, outputBuffer, STAC_HIDREPORT_SIZE); + + if (res < 0) + { + LOG->Warn("stac pn%d Write Error: %d: %s", playerNumber, errno, strerror(errno)); + + //close on error write to signal a reconnect. + Close(); + } + + newState = false; + } +} + +LightsDriver_Linux_stac::LightsDriver_Linux_stac() +{ + //attempt to connect on game launch + stacPlayer1->Connect(); + stacPlayer2->Connect(); + + //we keep track of if the device has *ever* been connected to signal + //if we want to try to reconnect. + //because if there is no P2 stac connected, then we don't want to go + //searching for something that was *never* there. + haveSeenP1 = stacPlayer1->IsConnected(); + haveSeenP2 = stacPlayer2->IsConnected(); +} + +LightsDriver_Linux_stac::~LightsDriver_Linux_stac() +{ + stacPlayer1->Close(); + stacPlayer2->Close(); + + delete stacPlayer1; + delete stacPlayer2; +} + +void LightsDriver_Linux_stac::HandleState(const LightsState *ls, StacDevice *dev, GameController ctrlNum) +{ + //check to see which game we are running as it can change during gameplay. + const InputScheme *pInput = &GAMESTATE->GetCurrentGame()->m_InputScheme; + RString sInputName = pInput->m_szName; + + if (sInputName.EqualsNoCase("dance")) + { + dev->SetInBuffer(STAC_LIGHTINDEX_BTN1, ls->m_bGameButtonLights[ctrlNum][DANCE_BUTTON_UP]); + dev->SetInBuffer(STAC_LIGHTINDEX_BTN2, ls->m_bGameButtonLights[ctrlNum][DANCE_BUTTON_DOWN]); + dev->SetInBuffer(STAC_LIGHTINDEX_BTN3, ls->m_bGameButtonLights[ctrlNum][DANCE_BUTTON_LEFT]); + dev->SetInBuffer(STAC_LIGHTINDEX_BTN4, ls->m_bGameButtonLights[ctrlNum][DANCE_BUTTON_RIGHT]); + } + else if (sInputName.EqualsNoCase("pump")) + { + dev->SetInBuffer(STAC_LIGHTINDEX_BTN1, ls->m_bGameButtonLights[ctrlNum][PUMP_BUTTON_UPLEFT]); + dev->SetInBuffer(STAC_LIGHTINDEX_BTN2, ls->m_bGameButtonLights[ctrlNum][PUMP_BUTTON_UPRIGHT]); + dev->SetInBuffer(STAC_LIGHTINDEX_BTN3, ls->m_bGameButtonLights[ctrlNum][PUMP_BUTTON_CENTER]); + dev->SetInBuffer(STAC_LIGHTINDEX_BTN4, ls->m_bGameButtonLights[ctrlNum][PUMP_BUTTON_DOWNLEFT]); + dev->SetInBuffer(STAC_LIGHTINDEX_BTN5, ls->m_bGameButtonLights[ctrlNum][PUMP_BUTTON_DOWNRIGHT]); + } + + dev->PushBufferToDevice(); +} + +void LightsDriver_Linux_stac::Set(const LightsState *ls) +{ + if (stacPlayer1->IsConnected()) + { + HandleState(ls, stacPlayer1, GameController_1); + } + else if (haveSeenP1) + { + //only attempt to connect again if it was connected on launch. + stacPlayer1->Connect(); + } + + if (stacPlayer2->IsConnected()) + { + HandleState(ls, stacPlayer2, GameController_2); + } + else if (haveSeenP2) + { + //only attempt to connect again if it was connected on launch. + stacPlayer2->Connect(); + } +} diff --git a/src/arch/Lights/LightsDriver_Linux_stac.h b/src/arch/Lights/LightsDriver_Linux_stac.h new file mode 100644 index 0000000000..a46813dbf1 --- /dev/null +++ b/src/arch/Lights/LightsDriver_Linux_stac.h @@ -0,0 +1,135 @@ +/* LightsDriver_Linux_stac: Control lights for the stac by icedragon.io via direct hidraw writes. */ + +#ifndef LightsDriver_Linux_stac_H +#define LightsDriver_Linux_stac_H + +/* +<<<<<<< ours + * -------------------------- NOTE -------------------------- + * + * This driver needs user read/write access to the icedragon.io stac. + * This can be achieved by using a udev rule like this: + * + * (player 1 then player 2) + * SUBSYSTEMS=="usb", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="eb5b", OWNER="dance", GROUP="dance", MODE="0660" + * SUBSYSTEMS=="usb", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="eb5a", OWNER="dance", GROUP="dance", MODE="0660" + * + * Refer to your distrobution's documentation on how to properly apply a udev rule. + * + * -------------------------- NOTE -------------------------- +======= + * -------------------------- NOTE -------------------------- + * + * This driver needs user read/write access to the icedragon.io stac. + * This can be achieved by using a udev rule like this: + * + * (player 1 then player 2) + * SUBSYSTEMS=="usb", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="eb5b", OWNER="dance", GROUP="dance", MODE="0660" + * SUBSYSTEMS=="usb", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="eb5a", OWNER="dance", GROUP="dance", MODE="0660" + * + * Refer to your distrobution's documentation on how to properly apply a udev rule. + * + * -------------------------- NOTE -------------------------- +>>>>>>> theirs + */ + +#include "arch/Lights/LightsDriver.h" + +//static information about the device(s) in question. +#define STAC_VID "04d8" +#define STAC_PID_P1 "ea4b" +#define STAC_PID_P2 "ea4a" +#define STAC_NUMOF_LIGHTS 5 + +//the first byte of the buffer is a static report id. +#define STAC_HIDREPORT_SIZE (STAC_NUMOF_LIGHTS + 1) +#define STAC_REPORT_ID 0x01 + +//all indicies contain their respective 573 pinouts +enum StacLightIndex +{ + STAC_LIGHTINDEX_BTN1 = 0, + STAC_LIGHTINDEX_BTN2 = 1, + STAC_LIGHTINDEX_BTN3 = 2, + STAC_LIGHTINDEX_BTN4 = 3, + STAC_LIGHTINDEX_BTN5 = 4, + STAC_LIGHTINDEX_MAX +}; + +class StacDevice +{ +public: + const char *deviceVID; + const char *devicePID; + + const char *devicePath; + int fd = -1; + + uint8_t playerNumber = 0; + bool newState = false; + + uint8_t outputBuffer[STAC_HIDREPORT_SIZE]; + + StacDevice(uint8_t pn); + + void FindDevice(); + void Connect(); + void Close(); + + bool IsConnected() + { + //a zero/positive file descriptor is a valid one. + //and if we have opened it, then we are connected. + //if there is an error, then we close it. + return (fd >= 0); + } + + void SetInBuffer(int index, bool lightState); + + void PushBufferToDevice(); +}; + +class LightsDriver_Linux_stac : public LightsDriver +{ +private: + StacDevice *stacPlayer1 = new StacDevice(1); + StacDevice *stacPlayer2 = new StacDevice(2); + + bool haveSeenP1 = false; + bool haveSeenP2 = false; + + void HandleState(const LightsState *ls, StacDevice *dev, GameController ctrlNum); + +public: + LightsDriver_Linux_stac(); + virtual ~LightsDriver_Linux_stac(); + + virtual void Set(const LightsState *ls); +}; + +#endif + +/* + * (c) 2021 StepMania team + * All rights reserved. + * + * 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, and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * 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 OF + * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ \ No newline at end of file