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);