diff --git a/Makefile.common b/Makefile.common index ee7dcf175d..96217ee911 100644 --- a/Makefile.common +++ b/Makefile.common @@ -230,6 +230,7 @@ OBJ += \ managers/state_manager.o \ gfx/drivers_font_renderer/bitmapfont.o \ tasks/task_autodetect.o \ + tasks/task_autodetect_blissbox.o \ input/input_autodetect_builtin.o \ input/input_keymaps.o \ input/input_remapping.o \ diff --git a/griffin/griffin.c b/griffin/griffin.c index 5f8bd10d51..91b9b2a5ee 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -606,6 +606,7 @@ FONTS INPUT ============================================================ */ #include "../tasks/task_autodetect.c" +#include "../tasks/task_autodetect_blissbox.c" #ifdef HAVE_AUDIOMIXER #include "../tasks/task_audio_mixer.c" #endif diff --git a/input/include/blissbox.h b/input/include/blissbox.h index 35686fe02e..cb840ef2a0 100644 --- a/input/include/blissbox.h +++ b/input/include/blissbox.h @@ -31,77 +31,12 @@ RETRO_BEGIN_DECLS -typedef struct { +typedef struct +{ const char *name; int index; } blissbox_pad_type_t; -const blissbox_pad_type_t blissbox_pad_types[] = -{ - {"A5200", 6}, - {"A5200_TB", 50}, - {"A7800", 4}, - {"ATARI", 0}, - {"ATARI_KEYPAD", 43}, - {"ATMARK", 10}, - {"BALLY", 42}, - {"CD32", 24}, - {"CDI", 33}, - {"COL", 1}, - {"COL_FLASHBACK", 48}, /* 3.0 */ - {"DC_ASCI", 15}, - {"DC_PAD", 16}, - {"DC_TWIN", 35}, /* 3.0 */ - {"FC_ARKANOID", 53}, - {"FC_NES", 52}, - {"GC", 9}, - {"GC_WHEEL", 18}, - {"GEN_3", 20}, - {"GEN_6", 21}, - {"GRAVIS_EX", 38}, - {"HAMMERHEAD", 40}, - {"HPD", 7}, - {"INTELI", 14}, - {"JAG", 11}, - {"MSSW", 39}, - {"N64", 19}, - {"NEO", 49}, - {"NES", 17}, - {"NES_ARKANOID", 30}, - {"NES_GUN", 28}, - {"NES_POWERPAD", 36}, - {"PADDLES", 41}, - {"PC_FX", 26}, - {"PC_GAMEPAD", 46}, - {"PSX_DIGITAL", 65}, - {"PSX_DS", 115}, - {"PSX_DS2", 121}, - {"PSX_FS", 83}, - {"PSX_JOGCON", 227}, /* 3.0 */ - {"PSX_NEGCON", 51}, - {"PSX_WHEEL", 12}, - {"SAC", 34}, - {"SATURN_ANALOG", 8}, - {"SATURN_DIGITAL", 3}, - {"SMS", 22}, - {"SNES", 27}, - {"SNESS_NTT", 47}, /* 3.0 */ - {"SPEEK", 45}, - {"TG16", 23}, - {"TG16_6BUTTON", 54}, /* 3.0 */ - {"THREE_DO", 25}, - {"THREE_DO_ANALOG", 37}, - {"VEC", 5}, - {"V_BOY", 29}, - {"WII_CLASSIC", 31}, - {"WII_DRUM", 55}, /* 3.0 */ - {"WII_MPLUS", 32}, - {"WII_NUNCHUK", 13}, - {"ZXSINC", 44}, - {"gx4000", 2}, - {NULL, 0}, /* used to mark unconnected ports, do not remove */ -}; - RETRO_END_DECLS #endif diff --git a/tasks/task_autodetect.c b/tasks/task_autodetect.c index 314d4fb0fa..29b6207302 100644 --- a/tasks/task_autodetect.c +++ b/tasks/task_autodetect.c @@ -21,56 +21,9 @@ #include #include -#include #include -#include -#include #include -#ifdef HAVE_LIBUSB -#ifdef __FreeBSD__ -#include -#else -#include -#endif -#endif - -#if defined(_WIN32) && !defined(_XBOX) && !defined(_MSC_VER) && _WIN32_WINNT >= 0x0500 -/* MinGW Win32 HID API */ -#include -#include -#include -#ifdef __NO_INLINE__ -/* Workaround MinGW issue where compiling without -O2 (which sets __NO_INLINE__) causes the strsafe functions - * to never be defined (only declared). - */ -#define __CRT_STRSAFE_IMPL -#endif -#include -#include -#include -#include -#include -#ifdef __cplusplus -extern "C" { -#endif -#include -#ifdef __cplusplus -} -#endif - -/* Why doesn't including cguid.h work to get a GUID_NULL instead? */ -#ifdef __cplusplus -EXTERN_C __attribute__((weak)) -const GUID GUID_NULL = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}}; -#else -__attribute__((weak)) -const GUID GUID_NULL = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}}; -#endif -#endif - -#include "../input/include/blissbox.h" - #include "../configuration.h" #include "../file_path_special.h" #include "../list_special.h" @@ -78,22 +31,9 @@ const GUID GUID_NULL = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}}; #include "../retroarch.h" #include "tasks_internal.h" - -/* HID Class-Specific Requests values. See section 7.2 of the HID specifications */ -#define USB_HID_GET_REPORT 0x01 -#define USB_CTRL_IN LIBUSB_ENDPOINT_IN|LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE -#define USB_PACKET_CTRL_LEN 5 -#define USB_TIMEOUT 5000 /* timeout in ms */ - -/* only one blissbox per machine is currently supported */ -static const blissbox_pad_type_t *blissbox_pads[BLISSBOX_MAX_PADS] = {NULL}; - -#ifdef HAVE_LIBUSB -static struct libusb_device_handle *autoconfig_libusb_handle = NULL; -#endif +#include "../input/include/blissbox.h" typedef struct autoconfig_disconnect autoconfig_disconnect_t; -typedef struct autoconfig_params autoconfig_params_t; struct autoconfig_disconnect { @@ -101,16 +41,6 @@ struct autoconfig_disconnect char *msg; }; -struct autoconfig_params -{ - int32_t vid; - int32_t pid; - unsigned idx; - uint32_t max_users; - char *name; - char *autoconfig_directory; -}; - static bool input_autoconfigured[MAX_USERS]; static unsigned input_device_name_index[MAX_INPUT_DEVICES]; static bool input_autoconfigure_swap_override; @@ -418,407 +348,6 @@ static void input_autoconfigure_params_free(autoconfig_params_t *params) params->autoconfig_directory = NULL; } -#ifdef _WIN32 -static const blissbox_pad_type_t* input_autoconfigure_get_blissbox_pad_type_win32(int vid, int pid) -{ - /* TODO: Remove the check for !defined(_MSC_VER) after making sure this builds on MSVC */ - - /* HID API is available since Windows 2000 */ -#if defined(_WIN32) && !defined(_XBOX) && !defined(_MSC_VER) && _WIN32_WINNT >= 0x0500 - HDEVINFO hDeviceInfo; - SP_DEVINFO_DATA DeviceInfoData; - SP_DEVICE_INTERFACE_DATA deviceInterfaceData; - HANDLE hDeviceHandle = INVALID_HANDLE_VALUE; - BOOL bResult = TRUE; - BOOL success = FALSE; - GUID guidDeviceInterface = {0}; - PSP_DEVICE_INTERFACE_DETAIL_DATA - pInterfaceDetailData = NULL; - ULONG requiredLength = 0; - LPTSTR lpDevicePath = NULL; - char *devicePath = NULL; - DWORD index = 0; - DWORD intIndex = 0; - size_t nLength = 0; - unsigned len = 0; - unsigned i = 0; - char vidPidString[32] = {0}; - char vidString[5] = {0}; - char pidString[5] = {0}; - char report[USB_PACKET_CTRL_LEN + 1] = {0}; - - snprintf(vidString, sizeof(vidString), "%04x", vid); - snprintf(pidString, sizeof(pidString), "%04x", pid); - - strlcat(vidPidString, "vid_", sizeof(vidPidString)); - strlcat(vidPidString, vidString, sizeof(vidPidString)); - strlcat(vidPidString, "&pid_", sizeof(vidPidString)); - strlcat(vidPidString, pidString, sizeof(vidPidString)); - - HidD_GetHidGuid(&guidDeviceInterface); - - if (!memcmp(&guidDeviceInterface, &GUID_NULL, sizeof(GUID_NULL))) - { - RARCH_ERR("[Autoconf]: null guid\n"); - return NULL; - } - - /* Get information about all the installed devices for the specified - * device interface class. - */ - hDeviceInfo = SetupDiGetClassDevs( - &guidDeviceInterface, - NULL, - NULL, - DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); - - if (hDeviceInfo == INVALID_HANDLE_VALUE) - { - RARCH_ERR("[Autoconf]: Error in SetupDiGetClassDevs: %d.\n", - GetLastError()); - goto done; - } - - /* Enumerate all the device interfaces in the device information set. */ - DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); - - while (!success) - { - success = SetupDiEnumDeviceInfo(hDeviceInfo, index, &DeviceInfoData); - - /* Reset for this iteration */ - if (lpDevicePath) - { - LocalFree(lpDevicePath); - lpDevicePath = NULL; - } - - if (pInterfaceDetailData) - { - LocalFree(pInterfaceDetailData); - pInterfaceDetailData = NULL; - } - - /* Check if this is the last item */ - if (GetLastError() == ERROR_NO_MORE_ITEMS) - break; - - deviceInterfaceData.cbSize = sizeof(SP_INTERFACE_DEVICE_DATA); - - /* Get information about the device interface. */ - for (intIndex = 0; (bResult = SetupDiEnumDeviceInterfaces( - hDeviceInfo, - &DeviceInfoData, - &guidDeviceInterface, - intIndex, - &deviceInterfaceData)); intIndex++) - { - /* Check if this is the last item */ - if (GetLastError() == ERROR_NO_MORE_ITEMS) - break; - - /* Check for some other error */ - if (!bResult) - { - RARCH_ERR("[Autoconf]: Error in SetupDiEnumDeviceInterfaces: %d.\n", GetLastError()); - goto done; - } - - /* Interface data is returned in SP_DEVICE_INTERFACE_DETAIL_DATA - * which we need to allocate, so we have to call this function twice. - * First to get the size so that we know how much to allocate, and - * second to do the actual call with the allocated buffer. - */ - - bResult = SetupDiGetDeviceInterfaceDetail( - hDeviceInfo, - &deviceInterfaceData, - NULL, 0, - &requiredLength, - NULL); - - /* Check for some other error */ - if (!bResult) - { - if ((ERROR_INSUFFICIENT_BUFFER == GetLastError()) && (requiredLength > 0)) - { - /* we got the size, now allocate buffer */ - pInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LPTR, requiredLength); - - if (!pInterfaceDetailData) - { - RARCH_ERR("[Autoconf]: Error allocating memory for the device detail buffer.\n"); - goto done; - } - } - else - { - RARCH_ERR("[Autoconf]: Other error: %d.\n", GetLastError()); - goto done; - } - } - - /* get the interface detailed data */ - pInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); - - /* Now call it with the correct size and allocated buffer */ - bResult = SetupDiGetDeviceInterfaceDetail( - hDeviceInfo, - &deviceInterfaceData, - pInterfaceDetailData, - requiredLength, - NULL, - &DeviceInfoData); - - /* Check for some other error */ - if (!bResult) - { - RARCH_LOG("[Autoconf]: Error in SetupDiGetDeviceInterfaceDetail: %d.\n", GetLastError()); - goto done; - } - - /* copy device path */ - nLength = _tcslen(pInterfaceDetailData->DevicePath) + 1; - lpDevicePath = (TCHAR*)LocalAlloc(LPTR, nLength * sizeof(TCHAR)); - - StringCchCopy(lpDevicePath, nLength, pInterfaceDetailData->DevicePath); - - devicePath = (char*)malloc(nLength); - - for (len = 0; len < nLength; len++) - devicePath[len] = lpDevicePath[len]; - - lpDevicePath[nLength - 1] = 0; - - if (strstr(devicePath, vidPidString)) - goto found; - } - - success = FALSE; - index++; - } - - if (!lpDevicePath) - { - RARCH_ERR("[Autoconf]: No devicepath. Error %d.", GetLastError()); - goto done; - } - -found: - /* Open the device */ - hDeviceHandle = CreateFileA( - devicePath, - GENERIC_READ, /* | GENERIC_WRITE,*/ - FILE_SHARE_READ, /* | FILE_SHARE_WRITE,*/ - NULL, - OPEN_EXISTING, - 0, /*FILE_FLAG_OVERLAPPED,*/ - NULL); - - if (hDeviceHandle == INVALID_HANDLE_VALUE) - { - /* Windows sometimes erroneously fails to open with a sharing violation: - * https://github.com/signal11/hidapi/issues/231 - * If this happens, trying again with read + write usually works for some reason. - */ - - /* Open the device */ - hDeviceHandle = CreateFileA( - devicePath, - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, - OPEN_EXISTING, - 0, /*FILE_FLAG_OVERLAPPED,*/ - NULL); - - if (hDeviceHandle == INVALID_HANDLE_VALUE) - { - RARCH_ERR("[Autoconf]: Can't open device for reading and writing: %d.", GetLastError()); - runloop_msg_queue_push("Bliss-Box already in use. Please make sure other programs are not using it.", 2, 300, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); - goto done; - } - } - -done: - free(devicePath); - LocalFree(lpDevicePath); - LocalFree(pInterfaceDetailData); - bResult = SetupDiDestroyDeviceInfoList(hDeviceInfo); - - devicePath = NULL; - lpDevicePath = NULL; - pInterfaceDetailData = NULL; - - if (!bResult) - RARCH_ERR("[Autoconf]: Could not destroy device info list.\n"); - - if (!hDeviceHandle || hDeviceHandle == INVALID_HANDLE_VALUE) - { - /* device is not connected */ - return NULL; - } - - report[0] = BLISSBOX_USB_FEATURE_REPORT_ID; - - HidD_GetFeature(hDeviceHandle, report, sizeof(report)); - - CloseHandle(hDeviceHandle); - - for (i = 0; i < sizeof(blissbox_pad_types) / sizeof(blissbox_pad_types[0]); i++) - { - const blissbox_pad_type_t *pad = &blissbox_pad_types[i]; - - if (!pad || string_is_empty(pad->name)) - continue; - - if (pad->index == report[0]) - return pad; - } - - RARCH_LOG("[Autoconf]: Could not find connected pad in Bliss-Box port#%d.\n", pid - BLISSBOX_PID); -#endif - - return NULL; -} -#else -static const blissbox_pad_type_t* input_autoconfigure_get_blissbox_pad_type_libusb(int vid, int pid) -{ -#ifdef HAVE_LIBUSB - unsigned i; - unsigned char answer[USB_PACKET_CTRL_LEN] = {0}; - int ret = libusb_init(NULL); - - if (ret < 0) - { - RARCH_ERR("[Autoconf]: Could not initialize libusb.\n"); - return NULL; - } - - autoconfig_libusb_handle = libusb_open_device_with_vid_pid(NULL, vid, pid); - - if (!autoconfig_libusb_handle) - { - RARCH_ERR("[Autoconf]: Could not find or open libusb device %d:%d.\n", vid, pid); - goto error; - } - -#ifdef __linux__ - libusb_detach_kernel_driver(autoconfig_libusb_handle, 0); -#endif - - ret = libusb_set_configuration(autoconfig_libusb_handle, 1); - - if (ret < 0) - { - RARCH_ERR("[Autoconf]: Error during libusb_set_configuration.\n"); - goto error; - } - - ret = libusb_claim_interface(autoconfig_libusb_handle, 0); - - if (ret < 0) - { - RARCH_ERR("[Autoconf]: Error during libusb_claim_interface.\n"); - goto error; - } - - ret = libusb_control_transfer(autoconfig_libusb_handle, USB_CTRL_IN, USB_HID_GET_REPORT, BLISSBOX_USB_FEATURE_REPORT_ID, 0, answer, USB_PACKET_CTRL_LEN, USB_TIMEOUT); - - if (ret < 0) - RARCH_ERR("[Autoconf]: Error during libusb_control_transfer.\n"); - - libusb_release_interface(autoconfig_libusb_handle, 0); - -#ifdef __linux__ - libusb_attach_kernel_driver(autoconfig_libusb_handle, 0); -#endif - - libusb_close(autoconfig_libusb_handle); - libusb_exit(NULL); - - for (i = 0; i < sizeof(blissbox_pad_types) / sizeof(blissbox_pad_types[0]); i++) - { - const blissbox_pad_type_t *pad = &blissbox_pad_types[i]; - - if (!pad || string_is_empty(pad->name)) - continue; - - if (pad->index == answer[0]) - return pad; - } - - RARCH_LOG("[Autoconf]: Could not find connected pad in Bliss-Box port#%d.\n", pid - BLISSBOX_PID); - - return NULL; - -error: - libusb_close(autoconfig_libusb_handle); - libusb_exit(NULL); -#endif - - return NULL; -} -#endif - -static const blissbox_pad_type_t* input_autoconfigure_get_blissbox_pad_type(int vid, int pid) -{ -#if defined(_WIN32) -#if defined(_MSC_VER) || defined(_XBOX) - /* no MSVC/XBOX support */ - return NULL; -#else - /* MinGW */ - return input_autoconfigure_get_blissbox_pad_type_win32(vid, pid); -#endif -#else - return input_autoconfigure_get_blissbox_pad_type_libusb(vid, pid); -#endif -} - -static void input_autoconfigure_override_handler(autoconfig_params_t *params) -{ - if (params->vid == BLISSBOX_VID) - { - if (params->pid == BLISSBOX_UPDATE_MODE_PID) - RARCH_LOG("[Autoconf]: Bliss-Box in update mode detected. Ignoring.\n"); - else if (params->pid == BLISSBOX_OLD_PID) - RARCH_LOG("[Autoconf]: Bliss-Box 1.0 firmware detected. Please update to 2.0 or later.\n"); - else if (params->pid >= BLISSBOX_PID && params->pid <= BLISSBOX_PID + BLISSBOX_MAX_PAD_INDEX) - { - const blissbox_pad_type_t *pad; - char name[255] = {0}; - int index = params->pid - BLISSBOX_PID; - - RARCH_LOG("[Autoconf]: Bliss-Box detected. Getting pad type...\n"); - - if (blissbox_pads[index]) - pad = blissbox_pads[index]; - else - pad = input_autoconfigure_get_blissbox_pad_type(params->vid, params->pid); - - if (pad && !string_is_empty(pad->name)) - { - RARCH_LOG("[Autoconf]: Found Bliss-Box pad type: %s (%d) in port#%d\n", pad->name, pad->index, index); - - if (params->name) - free(params->name); - - /* override name given to autoconfig so it knows what kind of pad this is */ - strlcat(name, "Bliss-Box 4-Play ", sizeof(name)); - strlcat(name, pad->name, sizeof(name)); - - params->name = strdup(name); - - blissbox_pads[index] = pad; - } - /* use NULL entry to mark as an unconnected port */ - else - blissbox_pads[index] = &blissbox_pad_types[ARRAY_SIZE(blissbox_pad_types) - 1]; - } - } -} - static void input_autoconfigure_connect_handler(retro_task_t *task) { autoconfig_params_t *params = (autoconfig_params_t*)task->state; @@ -999,7 +528,8 @@ void input_autoconfigure_connect( state->max_users = *( input_driver_get_uint(INPUT_ACTION_MAX_USERS)); - input_autoconfigure_override_handler(state); + if (state->vid == BLISSBOX_VID) + input_autoconfigure_override_handler(state); if (!string_is_empty(state->name)) input_config_set_device_name(state->idx, state->name); diff --git a/tasks/task_autodetect_blissbox.c b/tasks/task_autodetect_blissbox.c new file mode 100644 index 0000000000..c4a6b0ae51 --- /dev/null +++ b/tasks/task_autodetect_blissbox.c @@ -0,0 +1,551 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2016-2019 - Brad Parker + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include +#include + +#include +#include +#include + +#include "../verbosity.h" +#include "../retroarch.h" + +#include "tasks_internal.h" + +#ifdef HAVE_LIBUSB +#ifdef __FreeBSD__ +#include +#else +#include +#endif +#endif + +#if defined(_WIN32) && !defined(_XBOX) && !defined(_MSC_VER) && _WIN32_WINNT >= 0x0500 +/* MinGW Win32 HID API */ +#include +#include +#include +#ifdef __NO_INLINE__ +/* Workaround MinGW issue where compiling without -O2 (which sets __NO_INLINE__) causes the strsafe functions + * to never be defined (only declared). + */ +#define __CRT_STRSAFE_IMPL +#endif +#include +#include +#include +#include +#include +#ifdef __cplusplus +extern "C" { +#endif +#include +#ifdef __cplusplus +} +#endif + +/* Why doesn't including cguid.h work to get a GUID_NULL instead? */ +#ifdef __cplusplus +EXTERN_C __attribute__((weak)) +const GUID GUID_NULL = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}}; +#else +__attribute__((weak)) +const GUID GUID_NULL = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}}; +#endif +#endif + +#include "../input/include/blissbox.h" + +/* HID Class-Specific Requests values. See section 7.2 of the HID specifications */ +#define USB_HID_GET_REPORT 0x01 +#define USB_CTRL_IN LIBUSB_ENDPOINT_IN|LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE +#define USB_PACKET_CTRL_LEN 5 +#define USB_TIMEOUT 5000 /* timeout in ms */ + +const blissbox_pad_type_t blissbox_pad_types[] = +{ + {"A5200", 6}, + {"A5200_TB", 50}, + {"A7800", 4}, + {"ATARI", 0}, + {"ATARI_KEYPAD", 43}, + {"ATMARK", 10}, + {"BALLY", 42}, + {"CD32", 24}, + {"CDI", 33}, + {"COL", 1}, + {"COL_FLASHBACK", 48}, /* 3.0 */ + {"DC_ASCI", 15}, + {"DC_PAD", 16}, + {"DC_TWIN", 35}, /* 3.0 */ + {"FC_ARKANOID", 53}, + {"FC_NES", 52}, + {"GC", 9}, + {"GC_WHEEL", 18}, + {"GEN_3", 20}, + {"GEN_6", 21}, + {"GRAVIS_EX", 38}, + {"HAMMERHEAD", 40}, + {"HPD", 7}, + {"INTELI", 14}, + {"JAG", 11}, + {"MSSW", 39}, + {"N64", 19}, + {"NEO", 49}, + {"NES", 17}, + {"NES_ARKANOID", 30}, + {"NES_GUN", 28}, + {"NES_POWERPAD", 36}, + {"PADDLES", 41}, + {"PC_FX", 26}, + {"PC_GAMEPAD", 46}, + {"PSX_DIGITAL", 65}, + {"PSX_DS", 115}, + {"PSX_DS2", 121}, + {"PSX_FS", 83}, + {"PSX_JOGCON", 227}, /* 3.0 */ + {"PSX_NEGCON", 51}, + {"PSX_WHEEL", 12}, + {"SAC", 34}, + {"SATURN_ANALOG", 8}, + {"SATURN_DIGITAL", 3}, + {"SMS", 22}, + {"SNES", 27}, + {"SNESS_NTT", 47}, /* 3.0 */ + {"SPEEK", 45}, + {"TG16", 23}, + {"TG16_6BUTTON", 54}, /* 3.0 */ + {"THREE_DO", 25}, + {"THREE_DO_ANALOG", 37}, + {"VEC", 5}, + {"V_BOY", 29}, + {"WII_CLASSIC", 31}, + {"WII_DRUM", 55}, /* 3.0 */ + {"WII_MPLUS", 32}, + {"WII_NUNCHUK", 13}, + {"ZXSINC", 44}, + {"gx4000", 2}, + {NULL, 0}, /* used to mark unconnected ports, do not remove */ +}; + + +/* only one blissbox per machine is currently supported */ +static const blissbox_pad_type_t *blissbox_pads[BLISSBOX_MAX_PADS] = {NULL}; + +#ifdef HAVE_LIBUSB +static struct libusb_device_handle *autoconfig_libusb_handle = NULL; +#endif + +#ifdef _WIN32 +static const blissbox_pad_type_t* input_autoconfigure_get_blissbox_pad_type_win32(int vid, int pid) +{ + /* TODO: Remove the check for !defined(_MSC_VER) after making sure this builds on MSVC */ + + /* HID API is available since Windows 2000 */ +#if defined(_WIN32) && !defined(_XBOX) && !defined(_MSC_VER) && _WIN32_WINNT >= 0x0500 + HDEVINFO hDeviceInfo; + SP_DEVINFO_DATA DeviceInfoData; + SP_DEVICE_INTERFACE_DATA deviceInterfaceData; + HANDLE hDeviceHandle = INVALID_HANDLE_VALUE; + BOOL bResult = TRUE; + BOOL success = FALSE; + GUID guidDeviceInterface = {0}; + PSP_DEVICE_INTERFACE_DETAIL_DATA + pInterfaceDetailData = NULL; + ULONG requiredLength = 0; + LPTSTR lpDevicePath = NULL; + char *devicePath = NULL; + DWORD index = 0; + DWORD intIndex = 0; + size_t nLength = 0; + unsigned len = 0; + unsigned i = 0; + char vidPidString[32] = {0}; + char vidString[5] = {0}; + char pidString[5] = {0}; + char report[USB_PACKET_CTRL_LEN + 1] = {0}; + + snprintf(vidString, sizeof(vidString), "%04x", vid); + snprintf(pidString, sizeof(pidString), "%04x", pid); + + strlcat(vidPidString, "vid_", sizeof(vidPidString)); + strlcat(vidPidString, vidString, sizeof(vidPidString)); + strlcat(vidPidString, "&pid_", sizeof(vidPidString)); + strlcat(vidPidString, pidString, sizeof(vidPidString)); + + HidD_GetHidGuid(&guidDeviceInterface); + + if (!memcmp(&guidDeviceInterface, &GUID_NULL, sizeof(GUID_NULL))) + { + RARCH_ERR("[Autoconf]: null guid\n"); + return NULL; + } + + /* Get information about all the installed devices for the specified + * device interface class. + */ + hDeviceInfo = SetupDiGetClassDevs( + &guidDeviceInterface, + NULL, + NULL, + DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + + if (hDeviceInfo == INVALID_HANDLE_VALUE) + { + RARCH_ERR("[Autoconf]: Error in SetupDiGetClassDevs: %d.\n", + GetLastError()); + goto done; + } + + /* Enumerate all the device interfaces in the device information set. */ + DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + + while (!success) + { + success = SetupDiEnumDeviceInfo(hDeviceInfo, index, &DeviceInfoData); + + /* Reset for this iteration */ + if (lpDevicePath) + { + LocalFree(lpDevicePath); + lpDevicePath = NULL; + } + + if (pInterfaceDetailData) + { + LocalFree(pInterfaceDetailData); + pInterfaceDetailData = NULL; + } + + /* Check if this is the last item */ + if (GetLastError() == ERROR_NO_MORE_ITEMS) + break; + + deviceInterfaceData.cbSize = sizeof(SP_INTERFACE_DEVICE_DATA); + + /* Get information about the device interface. */ + for (intIndex = 0; (bResult = SetupDiEnumDeviceInterfaces( + hDeviceInfo, + &DeviceInfoData, + &guidDeviceInterface, + intIndex, + &deviceInterfaceData)); intIndex++) + { + /* Check if this is the last item */ + if (GetLastError() == ERROR_NO_MORE_ITEMS) + break; + + /* Check for some other error */ + if (!bResult) + { + RARCH_ERR("[Autoconf]: Error in SetupDiEnumDeviceInterfaces: %d.\n", GetLastError()); + goto done; + } + + /* Interface data is returned in SP_DEVICE_INTERFACE_DETAIL_DATA + * which we need to allocate, so we have to call this function twice. + * First to get the size so that we know how much to allocate, and + * second to do the actual call with the allocated buffer. + */ + + bResult = SetupDiGetDeviceInterfaceDetail( + hDeviceInfo, + &deviceInterfaceData, + NULL, 0, + &requiredLength, + NULL); + + /* Check for some other error */ + if (!bResult) + { + if ((ERROR_INSUFFICIENT_BUFFER == GetLastError()) && (requiredLength > 0)) + { + /* we got the size, now allocate buffer */ + pInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LPTR, requiredLength); + + if (!pInterfaceDetailData) + { + RARCH_ERR("[Autoconf]: Error allocating memory for the device detail buffer.\n"); + goto done; + } + } + else + { + RARCH_ERR("[Autoconf]: Other error: %d.\n", GetLastError()); + goto done; + } + } + + /* get the interface detailed data */ + pInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + + /* Now call it with the correct size and allocated buffer */ + bResult = SetupDiGetDeviceInterfaceDetail( + hDeviceInfo, + &deviceInterfaceData, + pInterfaceDetailData, + requiredLength, + NULL, + &DeviceInfoData); + + /* Check for some other error */ + if (!bResult) + { + RARCH_LOG("[Autoconf]: Error in SetupDiGetDeviceInterfaceDetail: %d.\n", GetLastError()); + goto done; + } + + /* copy device path */ + nLength = _tcslen(pInterfaceDetailData->DevicePath) + 1; + lpDevicePath = (TCHAR*)LocalAlloc(LPTR, nLength * sizeof(TCHAR)); + + StringCchCopy(lpDevicePath, nLength, pInterfaceDetailData->DevicePath); + + devicePath = (char*)malloc(nLength); + + for (len = 0; len < nLength; len++) + devicePath[len] = lpDevicePath[len]; + + lpDevicePath[nLength - 1] = 0; + + if (strstr(devicePath, vidPidString)) + goto found; + } + + success = FALSE; + index++; + } + + if (!lpDevicePath) + { + RARCH_ERR("[Autoconf]: No devicepath. Error %d.", GetLastError()); + goto done; + } + +found: + /* Open the device */ + hDeviceHandle = CreateFileA( + devicePath, + GENERIC_READ, /* | GENERIC_WRITE,*/ + FILE_SHARE_READ, /* | FILE_SHARE_WRITE,*/ + NULL, + OPEN_EXISTING, + 0, /*FILE_FLAG_OVERLAPPED,*/ + NULL); + + if (hDeviceHandle == INVALID_HANDLE_VALUE) + { + /* Windows sometimes erroneously fails to open with a sharing violation: + * https://github.com/signal11/hidapi/issues/231 + * If this happens, trying again with read + write usually works for some reason. + */ + + /* Open the device */ + hDeviceHandle = CreateFileA( + devicePath, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + 0, /*FILE_FLAG_OVERLAPPED,*/ + NULL); + + if (hDeviceHandle == INVALID_HANDLE_VALUE) + { + RARCH_ERR("[Autoconf]: Can't open device for reading and writing: %d.", GetLastError()); + runloop_msg_queue_push("Bliss-Box already in use. Please make sure other programs are not using it.", 2, 300, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + goto done; + } + } + +done: + free(devicePath); + LocalFree(lpDevicePath); + LocalFree(pInterfaceDetailData); + bResult = SetupDiDestroyDeviceInfoList(hDeviceInfo); + + devicePath = NULL; + lpDevicePath = NULL; + pInterfaceDetailData = NULL; + + if (!bResult) + RARCH_ERR("[Autoconf]: Could not destroy device info list.\n"); + + if (!hDeviceHandle || hDeviceHandle == INVALID_HANDLE_VALUE) + { + /* device is not connected */ + return NULL; + } + + report[0] = BLISSBOX_USB_FEATURE_REPORT_ID; + + HidD_GetFeature(hDeviceHandle, report, sizeof(report)); + + CloseHandle(hDeviceHandle); + + for (i = 0; i < sizeof(blissbox_pad_types) / sizeof(blissbox_pad_types[0]); i++) + { + const blissbox_pad_type_t *pad = &blissbox_pad_types[i]; + + if (!pad || string_is_empty(pad->name)) + continue; + + if (pad->index == report[0]) + return pad; + } + + RARCH_LOG("[Autoconf]: Could not find connected pad in Bliss-Box port#%d.\n", pid - BLISSBOX_PID); +#endif + + return NULL; +} +#else +static const blissbox_pad_type_t* input_autoconfigure_get_blissbox_pad_type_libusb(int vid, int pid) +{ +#ifdef HAVE_LIBUSB + unsigned i; + unsigned char answer[USB_PACKET_CTRL_LEN] = {0}; + int ret = libusb_init(NULL); + + if (ret < 0) + { + RARCH_ERR("[Autoconf]: Could not initialize libusb.\n"); + return NULL; + } + + autoconfig_libusb_handle = libusb_open_device_with_vid_pid(NULL, vid, pid); + + if (!autoconfig_libusb_handle) + { + RARCH_ERR("[Autoconf]: Could not find or open libusb device %d:%d.\n", vid, pid); + goto error; + } + +#ifdef __linux__ + libusb_detach_kernel_driver(autoconfig_libusb_handle, 0); +#endif + + ret = libusb_set_configuration(autoconfig_libusb_handle, 1); + + if (ret < 0) + { + RARCH_ERR("[Autoconf]: Error during libusb_set_configuration.\n"); + goto error; + } + + ret = libusb_claim_interface(autoconfig_libusb_handle, 0); + + if (ret < 0) + { + RARCH_ERR("[Autoconf]: Error during libusb_claim_interface.\n"); + goto error; + } + + ret = libusb_control_transfer(autoconfig_libusb_handle, USB_CTRL_IN, USB_HID_GET_REPORT, BLISSBOX_USB_FEATURE_REPORT_ID, 0, answer, USB_PACKET_CTRL_LEN, USB_TIMEOUT); + + if (ret < 0) + RARCH_ERR("[Autoconf]: Error during libusb_control_transfer.\n"); + + libusb_release_interface(autoconfig_libusb_handle, 0); + +#ifdef __linux__ + libusb_attach_kernel_driver(autoconfig_libusb_handle, 0); +#endif + + libusb_close(autoconfig_libusb_handle); + libusb_exit(NULL); + + for (i = 0; i < sizeof(blissbox_pad_types) / sizeof(blissbox_pad_types[0]); i++) + { + const blissbox_pad_type_t *pad = &blissbox_pad_types[i]; + + if (!pad || string_is_empty(pad->name)) + continue; + + if (pad->index == answer[0]) + return pad; + } + + RARCH_LOG("[Autoconf]: Could not find connected pad in Bliss-Box port#%d.\n", pid - BLISSBOX_PID); + + return NULL; + +error: + libusb_close(autoconfig_libusb_handle); + libusb_exit(NULL); +#endif + + return NULL; +} +#endif + +static const blissbox_pad_type_t* input_autoconfigure_get_blissbox_pad_type(int vid, int pid) +{ +#if defined(_WIN32) +#if defined(_MSC_VER) || defined(_XBOX) + /* no MSVC/XBOX support */ + return NULL; +#else + /* MinGW */ + return input_autoconfigure_get_blissbox_pad_type_win32(vid, pid); +#endif +#else + return input_autoconfigure_get_blissbox_pad_type_libusb(vid, pid); +#endif +} + +void input_autoconfigure_override_handler(void *data) +{ + autoconfig_params_t *params = (autoconfig_params_t*)data; + + if (params->pid == BLISSBOX_UPDATE_MODE_PID) + RARCH_LOG("[Autoconf]: Bliss-Box in update mode detected. Ignoring.\n"); + else if (params->pid == BLISSBOX_OLD_PID) + RARCH_LOG("[Autoconf]: Bliss-Box 1.0 firmware detected. Please update to 2.0 or later.\n"); + else if (params->pid >= BLISSBOX_PID && params->pid <= BLISSBOX_PID + BLISSBOX_MAX_PAD_INDEX) + { + const blissbox_pad_type_t *pad; + char name[255] = {0}; + int index = params->pid - BLISSBOX_PID; + + RARCH_LOG("[Autoconf]: Bliss-Box detected. Getting pad type...\n"); + + if (blissbox_pads[index]) + pad = blissbox_pads[index]; + else + pad = input_autoconfigure_get_blissbox_pad_type(params->vid, params->pid); + + if (pad && !string_is_empty(pad->name)) + { + RARCH_LOG("[Autoconf]: Found Bliss-Box pad type: %s (%d) in port#%d\n", pad->name, pad->index, index); + + if (params->name) + free(params->name); + + /* override name given to autoconfig so it knows what kind of pad this is */ + strlcat(name, "Bliss-Box 4-Play ", sizeof(name)); + strlcat(name, pad->name, sizeof(name)); + + params->name = strdup(name); + + blissbox_pads[index] = pad; + } + /* use NULL entry to mark as an unconnected port */ + else + blissbox_pads[index] = &blissbox_pad_types[ARRAY_SIZE(blissbox_pad_types) - 1]; + } +} diff --git a/tasks/tasks_internal.h b/tasks/tasks_internal.h index 6e10f8a455..c92e927952 100644 --- a/tasks/tasks_internal.h +++ b/tasks/tasks_internal.h @@ -46,6 +46,19 @@ typedef struct nbio_buf char *path; } nbio_buf_t; +typedef struct autoconfig_params autoconfig_params_t; + +struct autoconfig_params +{ + int32_t vid; + int32_t pid; + unsigned idx; + uint32_t max_users; + char *name; + char *autoconfig_directory; +}; + + #ifdef HAVE_NETWORKING typedef struct { @@ -159,6 +172,8 @@ unsigned input_autoconfigure_get_device_name_index(unsigned i); void input_autoconfigure_reset(void); +void input_autoconfigure_override_handler(void *data); + void input_autoconfigure_connect( const char *name, const char *display_name,