diff --git a/src/CMakeData-arch.cmake b/src/CMakeData-arch.cmake index 9c9bdba91a..69a41eeb5d 100644 --- a/src/CMakeData-arch.cmake +++ b/src/CMakeData-arch.cmake @@ -182,6 +182,7 @@ list(APPEND SMDATA_ARCH_LIGHTS_SRC "arch/Lights/LightsDriver.cpp" "arch/Lights/LightsDriver_SystemMessage.cpp" "arch/Lights/LightsDriver_stac.cpp" "arch/Lights/LightsDriver_snek.cpp" + "arch/Lights/LightsDriver_PacDrive.cpp" "arch/Lights/LightsDriver_HidBlueDot.cpp") list(APPEND SMDATA_ARCH_LIGHTS_HPP "arch/Lights/LightsDriver.h" "arch/Lights/LightsDriver_Export.h" @@ -190,6 +191,7 @@ list(APPEND SMDATA_ARCH_LIGHTS_HPP "arch/Lights/LightsDriver.h" "arch/Lights/SextetUtils.h" "arch/Lights/LightsDriver_stac.h" "arch/Lights/LightsDriver_snek.h" + "arch/Lights/LightsDriver_PacDrive.h" "arch/Lights/LightsDriver_HidBlueDot.h") # TODO: Confirm if Apple can use the export. @@ -197,12 +199,10 @@ if(NOT APPLE) if(WIN32) list(APPEND SMDATA_ARCH_LIGHTS_SRC "arch/Lights/LightsDriver_Win32Serial.cpp" - "arch/Lights/LightsDriver_Win32Parallel.cpp" - "arch/Lights/LightsDriver_PacDrive.cpp") + "arch/Lights/LightsDriver_Win32Parallel.cpp") list(APPEND SMDATA_ARCH_LIGHTS_HPP "arch/Lights/LightsDriver_Win32Parallel.h" - "arch/Lights/LightsDriver_Win32Serial.h" - "arch/Lights/LightsDriver_PacDrive.h") + "arch/Lights/LightsDriver_Win32Serial.h") if(WITH_MINIMAID) list(APPEND SMDATA_ARCH_LIGHTS_SRC "arch/Lights/LightsDriver_Win32Minimaid.cpp") @@ -218,7 +218,6 @@ if(NOT APPLE) "arch/Lights/LightsDriver_Linux_PIUIO_Leds.cpp" "arch/Lights/LightsDriver_Linux_PIUIOBTN_Leds.cpp" "arch/Lights/LightsDriver_Linux_ITGIO.cpp" - "arch/Lights/LightsDriver_LinuxPacDrive.cpp" "arch/Lights/LightsDriver_LinuxWeedTech.cpp") list(APPEND SMDATA_ARCH_LIGHTS_HPP "arch/Lights/LightsDriver_Linux_Leds.h" @@ -226,7 +225,6 @@ if(NOT APPLE) "arch/Lights/LightsDriver_Linux_PIUIO_Leds.h" "arch/Lights/LightsDriver_Linux_PIUIOBTN_Leds.h" "arch/Lights/LightsDriver_Linux_ITGIO.h" - "arch/Lights/LightsDriver_LinuxPacDrive.h" "arch/Lights/LightsDriver_LinuxWeedTech.h") if(WITH_MINIMAID) list(APPEND SMDATA_ARCH_LIGHTS_SRC diff --git a/src/arch/Lights/LightsDriver_LinuxPacDrive.cpp b/src/arch/Lights/LightsDriver_LinuxPacDrive.cpp deleted file mode 100644 index beb41b6e0b..0000000000 --- a/src/arch/Lights/LightsDriver_LinuxPacDrive.cpp +++ /dev/null @@ -1,286 +0,0 @@ -// Cross-platform, libusb-based driver for outputting lights -// via a PacDrive (http://www.ultimarc.com/pacdrive.html) - -#include "global.h" -#include "RageLog.h" -#include "LightsDriver_LinuxPacDrive.h" - -#include -#include - - -REGISTER_LIGHTS_DRIVER_CLASS( LinuxPacDrive ); - -// HID Class-Specific Requests values. See section 7.2 of the HID specifications -#define HID_GET_REPORT 0x01 -#define HID_GET_IDLE 0x02 -#define HID_GET_PROTOCOL 0x03 -#define HID_SET_REPORT 0x09 -#define HID_SET_IDLE 0x0A -#define HID_SET_PROTOCOL 0x0B -#define HID_REPORT_TYPE_INPUT 0x01 -#define HID_REPORT_TYPE_OUTPUT 0x02 -#define HID_REPORT_TYPE_FEATURE 0x03 - -#define HID_IFACE_IN 256 -#define HID_IFACE_OUT 512 - -// PacDrives have PIDs 1500 - 1507, but we'll handle that later. -const unsigned PACDRIVE_TIMEOUT = 10000; -const int PACDRIVE_VENDOR_ID = 0xD209; -const int PACDRIVE_PRODUCT_ID = 0x1500; - -//Adds new preference to allow for different light wiring setups -static Preference g_sPacDriveLightOrdering("PacDriveLightOrdering", "openitg"); -int iLightingOrder = 0; - -LightsDriver_LinuxPacDrive::LightsDriver_LinuxPacDrive() -{ - DeviceHandle = NULL; - - OpenDevice(); - - // clear all lights - WriteDevice( 0 ); - - RString lightOrder = g_sPacDriveLightOrdering.Get(); - if (lightOrder.CompareNoCase("lumenar") == 0 || lightOrder.CompareNoCase("openitg") == 0) { - iLightingOrder = 1; - } -} - -LightsDriver_LinuxPacDrive::~LightsDriver_LinuxPacDrive() -{ - // clear all lights and close the connection - WriteDevice( 0 ); - CloseDevice(); -} - -void LightsDriver_LinuxPacDrive::Set( const LightsState *ls ) -{ - if ( !DeviceHandle ) return; - - uint16_t outb = 0; - - switch (iLightingOrder) { - case 1: - //Sets the cabinet light values to follow LumenAR/OpenITG wiring standards - - /* - * OpenITG PacDrive Order: - * Taken from LightsDriver_PacDrive::SetLightsMappings() in openitg. - * - * 0: Marquee UL - * 1: Marquee UR - * 2: Marquee DL - * 3: Marquee DR - * - * 4: P1 Button - * 5: P2 Button - * - * 6: Bass Left - * 7: Bass Right - * - * 8,9,10,11: P1 L R U D - * 12,13,14,15: P2 L R U D - */ - - if (ls->m_bCabinetLights[LIGHT_MARQUEE_UP_LEFT]) outb |= BIT(0); - if (ls->m_bCabinetLights[LIGHT_MARQUEE_UP_RIGHT]) outb |= BIT(1); - if (ls->m_bCabinetLights[LIGHT_MARQUEE_LR_LEFT]) outb |= BIT(2); - if (ls->m_bCabinetLights[LIGHT_MARQUEE_LR_RIGHT]) outb |= BIT(3); - - if (ls->m_bGameButtonLights[GameController_1][GAME_BUTTON_START]) outb |= BIT(4); - if (ls->m_bGameButtonLights[GameController_2][GAME_BUTTON_START]) outb |= BIT(5); - - //Most PacDrive/Cabinet setups only have *one* bass light, so mux them together here. - if (ls->m_bCabinetLights[LIGHT_BASS_LEFT] || ls->m_bCabinetLights[LIGHT_BASS_RIGHT]) outb |= BIT(6); - if (ls->m_bCabinetLights[LIGHT_BASS_LEFT] || ls->m_bCabinetLights[LIGHT_BASS_RIGHT]) outb |= BIT(7); - - if (ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_LEFT]) outb |= BIT(8); - if (ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_RIGHT]) outb |= BIT(9); - if (ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_UP]) outb |= BIT(10); - if (ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_DOWN]) outb |= BIT(11); - - if (ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_LEFT]) outb |= BIT(12); - if (ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_RIGHT]) outb |= BIT(13); - if (ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_UP]) outb |= BIT(14); - if (ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_DOWN]) outb |= BIT(15); - - break; - - case 0: - default: - //If all else fails, falls back to original order - //reference page 7 - //http://www.peeweepower.com/stepmania/sm509pacdriveinfo.pdf - - if (ls->m_bCabinetLights[LIGHT_MARQUEE_UP_LEFT]) outb |= BIT(0); - if (ls->m_bCabinetLights[LIGHT_MARQUEE_UP_RIGHT]) outb |= BIT(1); - if (ls->m_bCabinetLights[LIGHT_MARQUEE_LR_LEFT]) outb |= BIT(2); - if (ls->m_bCabinetLights[LIGHT_MARQUEE_LR_RIGHT]) outb |= BIT(3); - - if (ls->m_bCabinetLights[LIGHT_BASS_LEFT] || ls->m_bCabinetLights[LIGHT_BASS_RIGHT]) outb |= BIT(4); - - if (ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_LEFT]) outb |= BIT(5); - if (ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_RIGHT]) outb |= BIT(6); - if (ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_UP]) outb |= BIT(7); - if (ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_DOWN]) outb |= BIT(8); - if (ls->m_bGameButtonLights[GameController_1][GAME_BUTTON_START]) outb |= BIT(9); - - if (ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_LEFT]) outb |= BIT(10); - if (ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_RIGHT]) outb |= BIT(11); - if (ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_UP]) outb |= BIT(12); - if (ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_DOWN]) outb |= BIT(13); - if (ls->m_bGameButtonLights[GameController_2][GAME_BUTTON_START]) outb |= BIT(14); - //Bit index 15 is unused. - - break; - } - - WriteDevice(outb); -} - -void LightsDriver_LinuxPacDrive::OpenDevice() -{ - libusb_device **devs; - libusb_device *dev = NULL; - ssize_t result; - int i = 0; - - CloseDevice(); // Ensure any previously opened device is closed first to prevent conflicts - - libusb_context* context = USBContext::getInstance().getContext(); - if (!context) - { - LOG->Warn("libusb: Failed to initialize context"); - return; - } - - libusb_set_option(context, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_INFO); - - result = libusb_get_device_list(context, &devs); - if (result < 0) - { - LOG->Warn("libusb_get_device_list failed: %s", libusb_error_name(result)); - return; - } - - while ((dev = devs[i++]) != NULL) - { - libusb_device_descriptor desc; - // Note since libusb-1.0.16, LIBUSBX_API_VERSION >= 0x01000102, this function always succeeds: - libusb_get_device_descriptor(dev, &desc); - - if (PACDRIVE_VENDOR_ID == desc.idVendor && - PACDRIVE_PRODUCT_ID <= desc.idProduct && - PACDRIVE_PRODUCT_ID + 8 > desc.idProduct) - { - LOG->Info("PacDrive device was found vid: 0x%04x pid: 0x%04x", desc.idVendor, desc.idProduct); - break; - } - } - - if (!dev) - { - LOG->Warn("PacDrive was not found"); - libusb_free_device_list(devs, 1); - return; - } - - result = libusb_open(dev, &DeviceHandle); - if (result < 0) - { - LOG->Warn("libusb_open failed: %s", libusb_error_name(result)); - DeviceHandle = NULL; - libusb_free_device_list(devs, 1); - return; - } - - libusb_free_device_list(devs, 1); - - if (libusb_kernel_driver_active(DeviceHandle, 0) == 1) - { - LOG->Warn("Kernel Driver Active"); - if (libusb_detach_kernel_driver(DeviceHandle, 0) == 0) - LOG->Warn("Kernel Driver Detached!"); - } - - result = libusb_claim_interface(DeviceHandle, 0); - if (result < 0) - { - LOG->Warn("libusb_claim_interface: cannot claim interface: %s", libusb_error_name(result)); - libusb_close(DeviceHandle); - return; - } - - LOG->Info("Device claimed"); -} - -void LightsDriver_LinuxPacDrive::CloseDevice() -{ - if (!DeviceHandle) - return; - - ssize_t result; - - result = libusb_release_interface(DeviceHandle, 0); - if (result != 0) - { - LOG->Warn("libusb_release_interface: %s", libusb_error_name(result)); - } - - libusb_close(DeviceHandle); - DeviceHandle = NULL; -} - -void LightsDriver_LinuxPacDrive::WriteDevice(uint16_t out) -{ - if (!DeviceHandle) return; - - // output is within the first 16 bits - accept a - // 16-bit arg and cast it, for simplicity's sake. - uint32_t data = (out << 16); - int expected = sizeof(data); - - ssize_t result = libusb_control_transfer(DeviceHandle, - LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, - HID_SET_REPORT, - HID_IFACE_OUT, - 0, - (unsigned char*)&data, - expected, - PACDRIVE_TIMEOUT); - - if(result != expected) { - LOG->Warn("PacDrive writing failed: %li (%s)\n", result, libusb_error_name(result)); - CloseDevice(); - } -} - -/* - * Rewritten for libusb 1.0 2024 sirex - * Rewritten for libusb 1.0 2024 sukibaby - * Original libusb 0.1 file Copyright (c) 2008 BoXoRRoXoRs - * 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. - */ diff --git a/src/arch/Lights/LightsDriver_LinuxPacDrive.h b/src/arch/Lights/LightsDriver_LinuxPacDrive.h deleted file mode 100644 index 8462b4b912..0000000000 --- a/src/arch/Lights/LightsDriver_LinuxPacDrive.h +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef LIGHTSDRIVER_LINUXPACDRIVE_H -#define LIGHTSDRIVER_LINUXPACDRIVE_H - -#include "LightsDriver.h" - -#include -#include - -#define BIT(i) (1<<(i)) - -class USBContext -{ -public: - static USBContext& getInstance() - { - static USBContext instance; - return instance; - } - - libusb_context* getContext() { return context; } - -private: - USBContext() - { - int result = libusb_init_context(&context, NULL, 0); - if (result < 0) - { - // initialization error - context = nullptr; - } - } - - ~USBContext() - { - if (context) - { - libusb_exit(context); - } - } - - libusb_context* context; - - // prevent copying - USBContext(const USBContext&) = delete; - USBContext& operator=(const USBContext&) = delete; -}; - -class LightsDriver_LinuxPacDrive: public LightsDriver -{ -public: - LightsDriver_LinuxPacDrive(); - ~LightsDriver_LinuxPacDrive(); - - void Set( const LightsState *ls ); -private: - - void OpenDevice(); - void WriteDevice(uint16_t out); - void CloseDevice(); - - libusb_device_handle *DeviceHandle; -}; - -#endif // LIGHTSDRIVER_LINUXPACDRIVE_H - -/* - * Copyright (c) 2008 BoXoRRoXoRs - * 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. - */ diff --git a/src/arch/Lights/LightsDriver_PacDrive.cpp b/src/arch/Lights/LightsDriver_PacDrive.cpp index da77a8c30f..eff63ffd0c 100644 --- a/src/arch/Lights/LightsDriver_PacDrive.cpp +++ b/src/arch/Lights/LightsDriver_PacDrive.cpp @@ -1,149 +1,123 @@ -// LightsDriver_PacDrive for use with a PacDrive hooked up with LEDs -// You need PacDrive64.dll in the StepMania directory to use this. - #include "global.h" +#include "RageLog.h" #include "LightsDriver_PacDrive.h" -#define WIN32_LEAN_AND_MEAN -#include "windows.h" -#include "RageUtil.h" -#include "Preference.h" +#include "GameState.h" +#include "Game.h" REGISTER_LIGHTS_DRIVER_CLASS(PacDrive); -HINSTANCE PachDLL = nullptr; +static Preference g_sPacDriveLightOrdering("PacDriveLightOrdering", "openitg"); +int iPacDriveLightOrder = 0; -bool PacDriveConnected = false; -typedef int (WINAPI PacInitialize)(void); -PacInitialize* m_pacinit = nullptr; -typedef void (WINAPI PacShutdown)(void); -PacShutdown* m_pacdone = nullptr; -typedef bool (WINAPI PacSetLEDStates)(int, short int); -PacSetLEDStates* m_pacset = nullptr; -int iLightingOrder = 0; - -//Adds new preference to allow for different light wiring setups -static Preference g_sPacDriveLightOrdering("PacDriveLightOrdering", "lumenar"); - - -LightsDriver_PacDrive::LightsDriver_PacDrive() +LightsDriver_PacDrive::LightsDriver_PacDrive() : dev{PACDRIVE_VID, PACDRIVE_PID, PACDRIVE_INTERFACE} { - // init io.dll - PachDLL = LoadLibrary("pacdrive64.dll"); - if(PachDLL == nullptr) + prev_led_state.raw = 0; + memset(state.raw_state, 0x00, sizeof(state.raw_state)); + + // TODO: Check for all 8 of the PacDrive PIDs instead of just the first one. + // Uncertain how many are not just the stock value. + + RString lightOrder = g_sPacDriveLightOrdering.Get(); + if (lightOrder.CompareNoCase("lumenar") == 0 || lightOrder.CompareNoCase("openitg") == 0) { - MessageBox(nullptr, "Could not LoadLibrary( pacdrive64.dll ).", "ERROR", MB_OK ); - return; - } - - //Get the function pointers - m_pacinit = (PacInitialize*)GetProcAddress(PachDLL, "PacInitialize"); - m_pacset = (PacSetLEDStates*)GetProcAddress(PachDLL, "PacSetLEDStates"); - m_pacdone = (PacShutdown*)GetProcAddress(PachDLL, "PacShutdown"); - - int NumPacDrives = 0; - - if (m_pacinit) - NumPacDrives = m_pacinit(); //initialize the pac drive - - if (NumPacDrives == 0) - { - PacDriveConnected = false; // set not connected - MessageBox(nullptr, "Could not find connected PacDrive.", "ERROR", MB_OK); - return; - } - else - { - PacDriveConnected = true; // set connected - m_pacset(0, 0x0); // clear all lights for device i - RString lightOrder = g_sPacDriveLightOrdering.Get(); - if (lightOrder.CompareNoCase("lumenar") == 0 || lightOrder.CompareNoCase("openitg") == 0) { - iLightingOrder = 1; - } + iPacDriveLightOrder = 1; } } LightsDriver_PacDrive::~LightsDriver_PacDrive() { - if (PacDriveConnected && m_pacset) - m_pacset(0, 0x0); // clear all lights for device i - - if (m_pacdone) - m_pacdone(); - - FreeLibrary(PachDLL); } void LightsDriver_PacDrive::Set(const LightsState *ls) { - short int outb = 0; - switch (iLightingOrder) { - case 1: - //Sets the cabinet light values to follow LumenAR/OpenITG wiring standards + if (!dev.FoundOnce()) + return; - if (ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_LEFT]) outb |= BIT(0); - if (ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_RIGHT]) outb |= BIT(1); - if (ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_UP]) outb |= BIT(2); - if (ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_DOWN]) outb |= BIT(3); - if (ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_LEFT]) outb |= BIT(4); - if (ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_RIGHT]) outb |= BIT(5); - if (ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_UP]) outb |= BIT(6); - if (ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_DOWN]) outb |= BIT(7); - if (ls->m_bCabinetLights[LIGHT_MARQUEE_UP_LEFT]) outb |= BIT(8); - if (ls->m_bCabinetLights[LIGHT_MARQUEE_UP_RIGHT]) outb |= BIT(9); - if (ls->m_bCabinetLights[LIGHT_MARQUEE_LR_LEFT]) outb |= BIT(10); - if (ls->m_bCabinetLights[LIGHT_MARQUEE_LR_RIGHT]) outb |= BIT(11); - if (ls->m_bGameButtonLights[GameController_1][GAME_BUTTON_START]) outb |= BIT(12); - if (ls->m_bGameButtonLights[GameController_2][GAME_BUTTON_START]) outb |= BIT(13); - if (ls->m_bCabinetLights[LIGHT_BASS_LEFT] || ls->m_bCabinetLights[LIGHT_BASS_RIGHT]) outb |= BIT(14); + switch (iPacDriveLightOrder) + { + case 1: + // Sets the cabinet light values to follow LumenAR/OpenITG wiring standards + + /* + * OpenITG PacDrive Order: + * Taken from LightsDriver_PacDrive::SetLightsMappings() in openitg. + * (index of 1 as the PacDrive labels them as index 1) + * + * 1: Marquee UL + * 2: Marquee UR + * 3: Marquee DL + * 4: Marquee DR + * + * 5: P1 Button + * 6: P2 Button + * + * 7: Bass Left + * 8: Bass Right + * + * 9,19,11,12: P1 L R U D + * 13,14,15,16: P2 L R U D + */ + + state.leds.led01 = ls->m_bCabinetLights[LIGHT_MARQUEE_UP_LEFT]; + state.leds.led02 = ls->m_bCabinetLights[LIGHT_MARQUEE_UP_RIGHT]; + state.leds.led03 = ls->m_bCabinetLights[LIGHT_MARQUEE_LR_LEFT]; + state.leds.led04 = ls->m_bCabinetLights[LIGHT_MARQUEE_LR_RIGHT]; + + state.leds.led05 = ls->m_bGameButtonLights[GameController_1][GAME_BUTTON_START]; + state.leds.led06 = ls->m_bGameButtonLights[GameController_2][GAME_BUTTON_START]; + + state.leds.led07 = ls->m_bCabinetLights[LIGHT_BASS_LEFT] || ls->m_bCabinetLights[LIGHT_BASS_RIGHT]; + state.leds.led08 = ls->m_bCabinetLights[LIGHT_BASS_LEFT] || ls->m_bCabinetLights[LIGHT_BASS_RIGHT]; + + state.leds.led09 = ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_LEFT]; + state.leds.led10 = ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_RIGHT]; + state.leds.led11 = ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_UP]; + state.leds.led12 = ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_DOWN]; + + state.leds.led13 = ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_LEFT]; + state.leds.led14 = ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_RIGHT]; + state.leds.led15 = ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_UP]; + state.leds.led16 = ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_DOWN]; break; + case 0: default: - //If all else fails, falls back to Minimaid order + // If all else fails, falls back to original order + // reference page 7 + // http://www.peeweepower.com/stepmania/sm509pacdriveinfo.pdf - if (ls->m_bCabinetLights[LIGHT_MARQUEE_UP_LEFT]) outb |= BIT(0); - if (ls->m_bCabinetLights[LIGHT_MARQUEE_UP_RIGHT]) outb |= BIT(1); - if (ls->m_bCabinetLights[LIGHT_MARQUEE_LR_LEFT]) outb |= BIT(2); - if (ls->m_bCabinetLights[LIGHT_MARQUEE_LR_RIGHT]) outb |= BIT(3); - if (ls->m_bCabinetLights[LIGHT_BASS_LEFT] || ls->m_bCabinetLights[LIGHT_BASS_RIGHT]) outb |= BIT(4); - if (ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_LEFT]) outb |= BIT(5); - if (ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_RIGHT]) outb |= BIT(6); - if (ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_UP]) outb |= BIT(7); - if (ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_DOWN]) outb |= BIT(8); - if (ls->m_bGameButtonLights[GameController_1][GAME_BUTTON_START]) outb |= BIT(9); - if (ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_LEFT]) outb |= BIT(10); - if (ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_RIGHT]) outb |= BIT(11); - if (ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_UP]) outb |= BIT(12); - if (ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_DOWN]) outb |= BIT(13); - if (ls->m_bGameButtonLights[GameController_2][GAME_BUTTON_START]) outb |= BIT(14); + state.leds.led01 = ls->m_bCabinetLights[LIGHT_MARQUEE_UP_LEFT]; + state.leds.led02 = ls->m_bCabinetLights[LIGHT_MARQUEE_UP_RIGHT]; + state.leds.led03 = ls->m_bCabinetLights[LIGHT_MARQUEE_LR_LEFT]; + state.leds.led04 = ls->m_bCabinetLights[LIGHT_MARQUEE_LR_RIGHT]; + + state.leds.led05 = ls->m_bCabinetLights[LIGHT_BASS_LEFT] || ls->m_bCabinetLights[LIGHT_BASS_RIGHT]; + + state.leds.led06 = ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_LEFT]; + state.leds.led07 = ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_RIGHT]; + state.leds.led08 = ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_UP]; + state.leds.led09 = ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_DOWN]; + state.leds.led10 = ls->m_bGameButtonLights[GameController_1][GAME_BUTTON_START]; + + state.leds.led11 = ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_LEFT]; + state.leds.led12 = ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_RIGHT]; + state.leds.led13 = ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_UP]; + state.leds.led14 = ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_DOWN]; + state.leds.led15 = ls->m_bGameButtonLights[GameController_2][GAME_BUTTON_START]; + + // led16 is not used. + state.leds.led16 = false; break; } - //ensure m_pacset function call was loaded. - if (m_pacset) - m_pacset(0, outb); -} + // only push on changes. + if (state.leds.raw != prev_led_state.raw) + { + state.report_id = PACDRIVE_HIDREPORT_ID; + state.pad0 = 0; + state.pad1 = 0; -/* Modified 2015 Dave Barribeau for StepMania 5.09 -* (c) 2003-2004 Chris Danford -* 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. -*/ + dev.Write((unsigned char *)&state.raw_state, sizeof(state.raw_state)); + prev_led_state = state.leds; + } +} diff --git a/src/arch/Lights/LightsDriver_PacDrive.h b/src/arch/Lights/LightsDriver_PacDrive.h index 55c48829d6..ad84de68f3 100644 --- a/src/arch/Lights/LightsDriver_PacDrive.h +++ b/src/arch/Lights/LightsDriver_PacDrive.h @@ -1,28 +1,114 @@ -/* - * LightsDriver_PacDrive - */ +/* LightsDriver_PacDrive: Control lights for the PacDrive by Ultimarc using hidapi */ #ifndef LightsDriver_PacDrive_H #define LightsDriver_PacDrive_H +/* + * -------------------------- NOTE -------------------------- + * + * This driver needs user read/write access to PacDrive. + * This can be achieved by using a udev rule like this: + * + * SUBSYSTEMS=="usb", ATTRS{idVendor}=="D209", ATTRS{idProduct}=="1500", OWNER="dance", GROUP="dance", MODE="0660" + * + * Refer to your distribution's documentation on how to properly apply a udev rule. + * + * -------------------------- NOTE -------------------------- + */ + #include "arch/Lights/LightsDriver.h" -#define BIT(i) (1<<(i)) +#include +#include "archutils/Common/HidDevice.h" +// static information about the device in question. +#define PACDRIVE_VID 0xD209 + +// PacDrive PIDs range from 0x1500->0x1507 +#define PACDRIVE_PID 0x1500 +#define PACDRIVE_PID_MAX 8 + +#define PACDRIVE_INTERFACE 0 + +// the first byte of the buffer is a static report id. +// and I have no idea why ultimarc's report is 5 bytes wide... +#define PACDRIVE_HIDREPORT_SIZE 5 +#define PACDRIVE_HIDREPORT_ID 0x00 + +typedef union +{ + struct + { + bool led01 : 1; + bool led02 : 1; + bool led03 : 1; + bool led04 : 1; + bool led05 : 1; + bool led06 : 1; + bool led07 : 1; + bool led08 : 1; + bool led09 : 1; + bool led10 : 1; + bool led11 : 1; + bool led12 : 1; + bool led13 : 1; + bool led14 : 1; + bool led15 : 1; + bool led16 : 1; + }; + uint16_t raw; +} pacdrive_leds_t; + +typedef union +{ + struct + { + uint8_t report_id; + uint8_t pad0; + uint8_t pad1; + pacdrive_leds_t leds; + }; + uint8_t raw_state[PACDRIVE_HIDREPORT_SIZE]; +} pacdrive_state_t; class LightsDriver_PacDrive : public LightsDriver { - public: +private: + HidDevice dev; + + pacdrive_state_t state; + pacdrive_leds_t prev_led_state; + +public: LightsDriver_PacDrive(); virtual ~LightsDriver_PacDrive(); - virtual void Set( const LightsState *ls ); + virtual void Set(const LightsState *ls); }; #endif -/* Modified 2015 - Dave Barribeau for StepMania 5.09 -* 2014 - twistedsymphony -* Created for use with Beware's StepMania 3.9 DDR Extreme Simulation -* feel free to reuse and distribute this code -*/ +/* + * (c) 2025 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. + */ diff --git a/src/archutils/Common/HidDevice.cpp b/src/archutils/Common/HidDevice.cpp index f56995ad15..bedf02e25a 100644 --- a/src/archutils/Common/HidDevice.cpp +++ b/src/archutils/Common/HidDevice.cpp @@ -39,6 +39,9 @@ bool HidDevice::Open(const char* path) if(nonBlockingWrite) hid_set_nonblocking(handle, 1); + if(handle) + LOG->Info("HidDevice opened %04x:%04x:%d by path %s", vid, pid, interfaceNum, path); + return handle != nullptr; } @@ -114,7 +117,6 @@ void HidDevice::Read(unsigned char* data, size_t length) { if (!CheckConnection()) return; - int result = hid_read(handle, data, length); if (result == -1) @@ -127,7 +129,7 @@ void HidDevice::Write(const unsigned char* data, size_t length) { if (!CheckConnection()) return; - + int result = hid_write(handle, data, length); if (result != length)