pacdrive hidapi implementation
This commit is contained in:
@@ -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.
|
||||
*/
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user