pacdrive hidapi implementation

This commit is contained in:
din
2025-03-22 13:03:48 -05:00
committed by teejusb
parent 63a68ae38a
commit 0b13eb17f2
6 changed files with 202 additions and 517 deletions
+4 -6
View File
@@ -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
@@ -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 <cstdint>
#include <libusb.h>
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<RString> 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.
*/
@@ -1,89 +0,0 @@
#ifndef LIGHTSDRIVER_LINUXPACDRIVE_H
#define LIGHTSDRIVER_LINUXPACDRIVE_H
#include "LightsDriver.h"
#include <cstdint>
#include <libusb.h>
#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.
*/
+97 -123
View File
@@ -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<RString> 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<RString> 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;
}
}
+97 -11
View File
@@ -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 <cstdint>
#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.
*/
+4 -2
View File
@@ -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)