From 2dd64d9c118fcc76953b6dc2f80a3bfb4fcb7574 Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Tue, 28 Nov 2017 18:25:12 -0500 Subject: [PATCH] Initial blissbox support, requires firmware 2.0. Currently limited to platforms with libusb support. --- input/include/blissbox.h | 98 +++++++++++++++++++++++++++ tasks/task_autodetect.c | 141 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 239 insertions(+) create mode 100644 input/include/blissbox.h diff --git a/input/include/blissbox.h b/input/include/blissbox.h new file mode 100644 index 0000000000..bddb5b6bb6 --- /dev/null +++ b/input/include/blissbox.h @@ -0,0 +1,98 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2016 - Daniel De Matteis + * Copyright (C) 2016-2017 - 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 . + */ + +#ifndef __BLISSBOX_H +#define __BLISSBOX_H + +#include + +#define BLISSBOX_VID 0x16d0 /* requires firmware 2.0 */ +#define BLISSBOX_PID 0x0d04 /* first of 4 controllers, each one increments PID by 1 */ +#define BLISSBOX_MAX_PADS 4 +#define BLISSBOX_MAX_PAD_INDEX (BLISSBOX_MAX_PADS - 1) + +#define BLISSBOX_USB_FEATURE_REPORT_ID 17 + +RETRO_BEGIN_DECLS + +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_KEYPAD", 43}, + {"ATMARK", 10}, + {"BALLY", 42}, + {"CD32", 24}, + {"CDI", 33}, + {"COL", 1}, + {"DC_ASCI", 15}, + {"DC_PAD", 16}, + {"FC_ARKANOID", 53}, + {"FC_NES", 52}, + {"GC", 9}, + {"GC_WHEEL", 18}, + {"GEN_3", 20}, + {"GEN_6", 21}, + {"GRAVIS_EX", 38}, + {"gx4000", 2}, + {"HAMMERHEAD", 40}, + {"HPD", 7}, + {"INTELI", 14}, + {"JAG", 11}, + {"MSSW", 39}, + {"N64", 19}, + {"NEO", 49}, + {"NES", 17}, + {"PADDLES", 41}, + {"PC_FX", 26}, + {"PC_GAMEPAD", 46}, + {"PSX_DIGITAL", 65}, + {"PSX_DS", 115}, + {"PSX_DS2", 121}, + {"PSX_FS", 83}, + {"PSX_NEGCON", 51}, + {"PSX_WHEEL", 12}, + {"SAC", 34}, + {"SATURN_ANALOG", 8}, + {"SATURN_DIGITAL", 3}, + {"SMS", 22}, + {"SPEEK", 45}, + {"TG16", 23}, + {"THREE_DO", 25}, + {"THREE_DO_ANALOG", 37}, + {"VEC", 5}, + {"WII_NUNCHUK", 13}, + {"ZXSINC", 44}, + {"NES_ARKANOID", 30}, + {"NES_GUN", 28}, + {"NES_POWERPAD", 36}, + {"SNES", 27}, + {"V_BOY", 29}, + {"WII_CLASSIC", 31}, + {"WII_MPLUS", 32}, + {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 7a25d9bc61..c2ea1f176e 100644 --- a/tasks/task_autodetect.c +++ b/tasks/task_autodetect.c @@ -1,6 +1,7 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2010-2014 - Hans-Kristian Arntzen * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2016-2017 - 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- @@ -24,7 +25,14 @@ #include #include +#ifdef __FreeBSD__ +#include +#else +#include +#endif + #include "../input/input_driver.h" +#include "../input/include/blissbox.h" #include "../configuration.h" #include "../file_path_special.h" @@ -33,6 +41,17 @@ #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 64 +#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}; + +static struct libusb_device_handle *autoconfig_libusb_handle = NULL; + typedef struct autoconfig_disconnect autoconfig_disconnect_t; typedef struct autoconfig_params autoconfig_params_t; @@ -333,6 +352,126 @@ static void input_autoconfigure_params_free(autoconfig_params_t *params) params->autoconfig_directory = NULL; } +static const blissbox_pad_type_t* input_autoconfigure_get_blissbox_pad_type(int vid, int pid) +{ +#ifdef HAVE_LIBUSB + unsigned char answer[USB_PACKET_CTRL_LEN] = {0}; + unsigned i; + int ret = libusb_init(NULL); + + if (ret < 0) + { + RARCH_ERR("[Autoconfig]: 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("[Autoconfig]: 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("[Autoconfig]: Error during libusb_set_configuration.\n"); + goto error; + } + + ret = libusb_claim_interface(autoconfig_libusb_handle, 0); + + if (ret < 0) + { + RARCH_ERR("[Autoconfig]: 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("[Autoconfig]: 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("[Autoconfig]: Could not find pad type for Bliss-Box in port#%d.\n", pid - BLISSBOX_PID); + + return NULL; +#else + return NULL; +#endif +error: + libusb_close(autoconfig_libusb_handle); + libusb_exit(NULL); + return NULL; +} + +static void input_autoconfigure_override_handler(autoconfig_params_t *params) +{ + if (params->vid == BLISSBOX_VID) + { + 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. Fetching 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 ", sizeof(name)); + strlcat(name, pad->name, sizeof(name)); + + params->name = strdup(name); + + blissbox_pads[index] = pad; + } + else + { + int count = sizeof(blissbox_pad_types) / sizeof(blissbox_pad_types[0]); + /* use NULL entry to mark as an unconnected port */ + blissbox_pads[index] = &blissbox_pad_types[count - 1]; + } + } + } +} + static void input_autoconfigure_connect_handler(retro_task_t *task) { autoconfig_params_t *params = (autoconfig_params_t*)task->state; @@ -499,6 +638,8 @@ bool input_autoconfigure_connect( state->max_users = *( input_driver_get_uint(INPUT_ACTION_MAX_USERS)); + input_autoconfigure_override_handler(state); + if (!string_is_empty(state->name)) input_config_set_device_name(state->idx, state->name); input_config_set_pid(state->idx, state->pid);