diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bfde4470c5..066a567f68 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,7 +32,6 @@ jobs: libmad0-dev libpulse-dev libudev-dev - libusb-dev libxinerama-dev libx11-dev libxrandr-dev diff --git a/.gitmodules b/.gitmodules index d14924fca8..0d8bdd070f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -37,3 +37,7 @@ path = extern/libtomcrypt url = https://github.com/libtom/libtomcrypt.git shallow = true +[submodule "extern/libusb"] + path = extern/libusb + url = https://github.com/libusb/libusb-cmake.git + shallow = true diff --git a/CMake/Modules/FindLibusb.cmake b/CMake/Modules/FindLibusb.cmake deleted file mode 100644 index 6a9190cf94..0000000000 --- a/CMake/Modules/FindLibusb.cmake +++ /dev/null @@ -1,105 +0,0 @@ -# - Find libusb for portable USB support -# This module will find libusb as published by -# http://libusb.sf.net and -# http://libusb-win32.sf.net -# -# It will use PkgConfig if present and supported, else search -# it on its own. If the LibUSB_ROOT_DIR environment variable -# is defined, it will be used as base path. -# The following standard variables get defined: -# LIBUSB_FOUND: true if LibUSB was found -# LIBUSB_INCLUDE_DIRS: the directory that contains the include file -# LIBUSB_LIBRARIES: the library -if( WIN32 ) - return() -endif() - -include ( CheckLibraryExists ) -include ( CheckIncludeFile ) - -find_package ( PkgConfig ) -if ( PKG_CONFIG_FOUND ) - pkg_check_modules ( PKGCONFIG_LIBUSB libusb ) -endif ( PKG_CONFIG_FOUND ) - -if ( PKGCONFIG_LIBUSB_FOUND ) - set ( LIBUSB_FOUND ${PKGCONFIG_LIBUSB_FOUND} ) - set ( LIBUSB_INCLUDE_DIRS ${PKGCONFIG_LIBUSB_INCLUDE_DIRS} ) - foreach ( i ${PKGCONFIG_LIBUSB_LIBRARIES} ) - find_library ( ${i}_LIBRARY - NAMES ${i} - PATHS ${PKGCONFIG_LIBUSB_LIBRARY_DIRS} - ) - if ( ${i}_LIBRARY ) - list ( APPEND LIBUSB_LIBRARIES ${${i}_LIBRARY} ) - endif ( ${i}_LIBRARY ) - mark_as_advanced ( ${i}_LIBRARY ) - endforeach ( i ) - -else ( PKGCONFIG_LIBUSB_FOUND ) - find_path ( LIBUSB_INCLUDE_DIRS - NAMES - usb.h - PATHS - $ENV{ProgramFiles}/LibUSB-Win32 - $ENV{LibUSB_ROOT_DIR} - PATH_SUFFIXES - include - ) - mark_as_advanced ( LIBUSB_INCLUDE_DIRS ) -# message ( STATUS "LibUSB include dir: ${LIBUSB_INCLUDE_DIRS}" ) - - if ( ${CMAKE_SYSTEM_NAME} STREQUAL "Windows" ) - # LibUSB-Win32 binary distribution contains several libs. - # Use the lib that got compiled with the same compiler. - if ( MSVC ) - if ( WIN32 ) - set ( LibUSB_LIBRARY_PATH_SUFFIX lib/msvc ) - else ( WIN32 ) - set ( LibUSB_LIBRARY_PATH_SUFFIX lib/msvc_x64 ) - endif ( WIN32 ) - elseif ( BORLAND ) - set ( LibUSB_LIBRARY_PATH_SUFFIX lib/bcc ) - elseif ( CMAKE_COMPILER_IS_GNUCC ) - set ( LibUSB_LIBRARY_PATH_SUFFIX lib/gcc ) - endif ( MSVC ) - endif ( ${CMAKE_SYSTEM_NAME} STREQUAL "Windows" ) - - find_library ( usb_LIBRARY - NAMES - libusb usb - PATHS - $ENV{ProgramFiles}/LibUSB-Win32 - $ENV{LibUSB_ROOT_DIR} - PATH_SUFFIXES - ${LibUSB_LIBRARY_PATH_SUFFIX} - ) - mark_as_advanced ( usb_LIBRARY ) - if ( usb_LIBRARY ) - set ( LIBUSB_LIBRARIES ${usb_LIBRARY} ) - endif ( usb_LIBRARY ) - - if ( LIBUSB_INCLUDE_DIRS AND LIBUSB_LIBRARIES ) - set ( LIBUSB_FOUND true ) - endif ( LIBUSB_INCLUDE_DIRS AND LIBUSB_LIBRARIES ) -endif ( PKGCONFIG_LIBUSB_FOUND ) - -if ( LIBUSB_FOUND ) - set ( CMAKE_REQUIRED_INCLUDES "${LIBUSB_INCLUDE_DIRS}" ) - check_include_file ( usb.h LIBUSB_FOUND ) -# message ( STATUS "LibUSB: usb.h is usable: ${LIBUSB_FOUND}" ) -endif ( LIBUSB_FOUND ) -if ( LIBUSB_FOUND ) - check_library_exists ( "${LIBUSB_LIBRARIES}" usb_open "" LIBUSB_FOUND ) -# message ( STATUS "LibUSB: library is usable: ${LIBUSB_FOUND}" ) -endif ( LIBUSB_FOUND ) - -if ( NOT LIBUSB_FOUND ) - if ( NOT LibUSB_FIND_QUIETLY ) - message ( STATUS "LibUSB not found, try setting LibUSB_ROOT_DIR environment variable." ) - endif ( NOT LibUSB_FIND_QUIETLY ) - if ( LibUSB_FIND_REQUIRED ) - message ( FATAL_ERROR "" ) - endif ( LibUSB_FIND_REQUIRED ) -endif ( NOT LIBUSB_FOUND ) -#message ( STATUS "LibUSB: ${LIBUSB_FOUND}" ) \ No newline at end of file diff --git a/StepmaniaCore.cmake b/StepmaniaCore.cmake index 85e9226f79..8c9869c0db 100644 --- a/StepmaniaCore.cmake +++ b/StepmaniaCore.cmake @@ -338,11 +338,6 @@ elseif(LINUX OR BSD) set(OpenGL_GL_PREFERENCE GLVND) find_package(OpenGL REQUIRED) - - find_package(Libusb) - if(NOT LIBUSB_FOUND) - message(FATAL_ERROR "libusb was not found.") - endif() endif(WIN32) # LINUX OR BSD, APPLE configure_file("${SM_SRC_DIR}/config.in.hpp" diff --git a/Utils/Dockerfile-linux-amd64 b/Utils/Dockerfile-linux-amd64 index dd04f5566a..0c64893fa3 100644 --- a/Utils/Dockerfile-linux-amd64 +++ b/Utils/Dockerfile-linux-amd64 @@ -3,7 +3,7 @@ FROM ubuntu:18.04 RUN apt-get update RUN DEBIAN_FRONTEND=noninteractive apt-get install -y curl g++ gcc git make nasm RUN DEBIAN_FRONTEND=noninteractive apt-get install -y libgtk-3-dev -RUN DEBIAN_FRONTEND=noninteractive apt-get install -y libasound2-dev libgl1-mesa-dev libglu1-mesa-dev libjack-dev libpulse-dev libssl-dev libudev-dev libva-dev libxinerama-dev libxrandr-dev libxtst-dev libusb-dev +RUN DEBIAN_FRONTEND=noninteractive apt-get install -y libasound2-dev libgl1-mesa-dev libglu1-mesa-dev libjack-dev libpulse-dev libssl-dev libudev-dev libva-dev libxinerama-dev libxrandr-dev libxtst-dev RUN cd ~ && curl -L -o cmake.sh https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-linux-x86_64.sh && sh cmake.sh --skip-license --prefix=/usr/local && rm cmake.sh diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 6313919bd4..c0f8969092 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -13,3 +13,6 @@ include(CMakeProject-tommath.cmake) include(CMakeProject-png.cmake) include(CMakeProject-ixwebsocket.cmake) include(CMakeProject-miniz.cmake) + +# External projects +include(CMakeProject-libusb.cmake) diff --git a/extern/CMakeProject-libusb.cmake b/extern/CMakeProject-libusb.cmake new file mode 100644 index 0000000000..03600d5d1b --- /dev/null +++ b/extern/CMakeProject-libusb.cmake @@ -0,0 +1,21 @@ +# Build and statically link libusb project + +# libusb is currently used by Linux, but we need to test it under windows too +# and it should work +if(NOT LINUX) + return() +endif() + +set(LIBUSB_SOURCE_DIR "${SM_EXTERN_DIR}/libusb") + +include(ExternalProject) +ExternalProject_Add( + libusb + + SOURCE_DIR "${LIBUSB_SOURCE_DIR}" + INSTALL_COMMAND "" +) + +ExternalProject_Get_Property(libusb SOURCE_DIR BINARY_DIR) +set(LIBUSB_INCLUDE_DIR "${SOURCE_DIR}/libusb/libusb" CACHE INTERNAL "libusb include") +set(LIBUSB_LIBRARY "${BINARY_DIR}/libusb-1.0.a" CACHE INTERNAL "libusb library") diff --git a/extern/libusb b/extern/libusb new file mode 160000 index 0000000000..cec1e49eec --- /dev/null +++ b/extern/libusb @@ -0,0 +1 @@ +Subproject commit cec1e49eec481a6333c218726ecaba8e4f447a07 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 87ae5e3571..f70579acbe 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -438,7 +438,7 @@ else() # Unix / Linux TODO: Remember to find and locate the zip archive files. list(APPEND SMDATA_LINK_LIB ${LIBXTST_LIBRARY}) endif() - list(APPEND SMDATA_LINK_LIB ${LIBUSB_LIBRARIES}) + list(APPEND SMDATA_LINK_LIB ${LIBUSB_LIBRARY}) list(APPEND SMDATA_LINK_LIB ${XRANDR_LIBRARIES} ${XINERAMA_LIBRARIES}) @@ -466,7 +466,7 @@ else() list(APPEND SM_INCLUDE_DIRS "${X11_INCLUDE_DIR}") endif() - list(APPEND SM_INCLUDE_DIRS "${LIBUSB_INCLUDE_DIRS}") + list(APPEND SM_INCLUDE_DIRS "${LIBUSB_INCLUDE_DIR}") endif() endif() diff --git a/src/arch/Lights/LightsDriver_LinuxPacDrive.cpp b/src/arch/Lights/LightsDriver_LinuxPacDrive.cpp index 1fb11e9859..beb41b6e0b 100644 --- a/src/arch/Lights/LightsDriver_LinuxPacDrive.cpp +++ b/src/arch/Lights/LightsDriver_LinuxPacDrive.cpp @@ -6,277 +6,262 @@ #include "LightsDriver_LinuxPacDrive.h" #include +#include -extern "C" { -#include -} REGISTER_LIGHTS_DRIVER_CLASS( LinuxPacDrive ); -#define USB_DIR_OUT 0x00 -#define USB_DIR_IN 0x80 +// 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 USB_TYPE_STANDARD (0x00 << 5) -#define USB_TYPE_CLASS (0x01 << 5) -#define USB_TYPE_VENDOR (0x02 << 5) -#define USB_TYPE_RESERVED (0x03 << 5) +#define HID_IFACE_IN 256 +#define HID_IFACE_OUT 512 -#define USB_RECIP_DEVICE 0x00 -#define USB_RECIP_INTERFACE 0x01 -#define USB_RECIP_ENDPOINT 0x02 -#define USB_RECIP_OTHER 0x03 - -#define HID_GET_REPORT 0x01 -#define HID_SET_REPORT 0x09 -#define HID_IFACE_IN 256 -#define HID_IFACE_OUT 512 - - -/* PacDrives have PIDs 1500 - 1507, but we'll handle that later. */ +// 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; -/* I/O request timeout, in microseconds (so, 10 ms) */ -const unsigned PACDRIVE_TIMEOUT = 10000; - -/* static struct to ensure the USB subsystem is initialized on start */ -struct USBInit -{ - USBInit() { usb_init(); usb_find_busses(); usb_find_devices(); } -}; - -static struct USBInit g_USBInit; - //Adds new preference to allow for different light wiring setups static Preference g_sPacDriveLightOrdering("PacDriveLightOrdering", "openitg"); int iLightingOrder = 0; LightsDriver_LinuxPacDrive::LightsDriver_LinuxPacDrive() { - Device = NULL; - DeviceHandle = NULL; + DeviceHandle = NULL; - FindDevice(); - OpenDevice(); + OpenDevice(); - // clear all lights - WriteDevice( 0 ); + // clear all lights + WriteDevice( 0 ); - RString lightOrder = g_sPacDriveLightOrdering.Get(); - if (lightOrder.CompareNoCase("lumenar") == 0 || lightOrder.CompareNoCase("openitg") == 0) { - iLightingOrder = 1; - } + 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(); + // clear all lights and close the connection + WriteDevice( 0 ); + CloseDevice(); } void LightsDriver_LinuxPacDrive::Set( const LightsState *ls ) { - if ( !DeviceHandle ) return; + if ( !DeviceHandle ) return; uint16_t outb = 0; - switch (iLightingOrder) { - case 1: - //Sets the cabinet light values to follow LumenAR/OpenITG wiring standards + 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 - */ + /* + * 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_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); + 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); + //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_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); + 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; + break; - case 0: - default: - //If all else fails, falls back to original order - //reference page 7 - //http://www.peeweepower.com/stepmania/sm509pacdriveinfo.pdf + 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_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_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_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. + 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; -} + break; + } - - WriteDevice(outb); -} - -void LightsDriver_LinuxPacDrive::FindDevice() -{ - if ( usb_find_busses() < 0 ) - { - LOG->Warn( "libusb: usb_find_busses: %s", usb_strerror() ); - return; - } - - if ( usb_find_devices() < 0 ) - { - LOG->Warn( "libusb: usb_find_devices: %s", usb_strerror() ); - return; - } - - for ( usb_bus *bus = usb_get_busses(); bus; bus = bus->next ) - for ( struct usb_device *dev = bus->devices; dev; dev = dev->next ) - if ( PACDRIVE_VENDOR_ID == dev->descriptor.idVendor && - PACDRIVE_PRODUCT_ID <= dev->descriptor.idProduct && - PACDRIVE_PRODUCT_ID + 8 > dev->descriptor.idProduct ) { - Device = dev; - LOG->Info( "PacDrive device was found vid: 0x%04x pid: 0x%04x", dev->descriptor.idVendor, dev->descriptor.idProduct ); - return; - } - - LOG->Warn( "PacDrive was not found!" ); - Device = NULL; + WriteDevice(outb); } void LightsDriver_LinuxPacDrive::OpenDevice() { - CloseDevice(); + libusb_device **devs; + libusb_device *dev = NULL; + ssize_t result; + int i = 0; - if ( !Device ) return; + CloseDevice(); // Ensure any previously opened device is closed first to prevent conflicts - DeviceHandle = usb_open( Device ); + libusb_context* context = USBContext::getInstance().getContext(); + if (!context) + { + LOG->Warn("libusb: Failed to initialize context"); + return; + } - if ( DeviceHandle == NULL ) { - LOG->Warn( "libusb: usb_open: %s", usb_strerror() ); - return; - } + libusb_set_option(context, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_INFO); - // The device may be claimed by a kernel driver. Attempt to reclaim it. - for ( unsigned iface = 0; iface < Device->config->bNumInterfaces; iface++ ) - { - int result = usb_detach_kernel_driver_np( DeviceHandle, iface ); + result = libusb_get_device_list(context, &devs); + if (result < 0) + { + LOG->Warn("libusb_get_device_list failed: %s", libusb_error_name(result)); + return; + } - // device doesn't understand message, no attached driver, no error -- ignore these - if( result == -EINVAL || result == -ENODATA || result == 0 ) - continue; + 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); - /* we have an error we can't handle; try and get more info. */ - LOG->Warn( "usb_detach_kernel_driver_np: %s\n", usb_strerror() ); + 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; + } + } - // on EPERM, a driver exists and we can't detach - report which one - if ( result == -EPERM ) - { - char szDriverName[16]; - strcpy( szDriverName, "(unknown)" ); - usb_get_driver_np(DeviceHandle, iface, szDriverName, 16); + if (!dev) + { + LOG->Warn("PacDrive was not found"); + libusb_free_device_list(devs, 1); + return; + } - LOG->Warn( "(cannot detach kernel driver \"%s\")", szDriverName ); - } + 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; + } - CloseDevice(); - return; - } + libusb_free_device_list(devs, 1); - if ( usb_set_configuration( DeviceHandle, Device->config->bConfigurationValue) ) { - LOG->Warn( "libusb: usb_set_configuration: %s", usb_strerror() ); - CloseDevice(); - return; - } + 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!"); + } - // attempt to claim all interfaces for this device - for ( unsigned i = 0; i < Device->config->bNumInterfaces; i++ ) - { - if ( usb_claim_interface( DeviceHandle, i ) ) { - LOG->Warn( "Libusb: usb_claim_interface(%i): %s", i, usb_strerror() ); - CloseDevice(); - return; - } - } -} + 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; + } -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); - - int result = usb_control_msg( DeviceHandle, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - HID_SET_REPORT, HID_IFACE_OUT, 0, (char *)&data, expected, - PACDRIVE_TIMEOUT ); - - if( result != expected ) { - LOG->Warn( "PacDrive writing failed: %i (%s)\n", result, usb_strerror() ); - CloseDevice(); - } + LOG->Info("Device claimed"); } void LightsDriver_LinuxPacDrive::CloseDevice() { - if ( !DeviceHandle ) - return; + if (!DeviceHandle) + return; - usb_set_altinterface( DeviceHandle, 0 ); - usb_reset( DeviceHandle ); - usb_close( DeviceHandle ); - DeviceHandle = NULL; + 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(); + } } /* - * Copyright (c) 2008 BoXoRRoXoRs + * 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 diff --git a/src/arch/Lights/LightsDriver_LinuxPacDrive.h b/src/arch/Lights/LightsDriver_LinuxPacDrive.h index a2678938ce..8462b4b912 100644 --- a/src/arch/Lights/LightsDriver_LinuxPacDrive.h +++ b/src/arch/Lights/LightsDriver_LinuxPacDrive.h @@ -4,13 +4,47 @@ #include "LightsDriver.h" #include - -extern "C" { -#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: @@ -20,13 +54,11 @@ public: void Set( const LightsState *ls ); private: - void FindDevice(); void OpenDevice(); void WriteDevice(uint16_t out); void CloseDevice(); - struct usb_device *Device; - usb_dev_handle *DeviceHandle; + libusb_device_handle *DeviceHandle; }; #endif // LIGHTSDRIVER_LINUXPACDRIVE_H