RetroArch/input/drivers_hid/wiiusb_hid.c
netux79 bca4ccb155 (GX) Refactor of the Wii USB HID support. Now it's working!
In order to have a controller working you need:
1) Have a matching HID autoconfig file in autoconfig/hid for your controller.
2) Create a "connect" driver for the pad in "input/connect" folder (source code of RA).
3) Once you are in RA, change the joystick driver to HID and restart.
4) You may be now able to use you USB HID compatible pad in RA.

I included some "connect" drivers as an example. It also need to include them for compilation.
2016-02-04 17:01:03 -05:00

606 lines
16 KiB
C

/* RetroArch - A frontend for libretro.
* Copyright (C) 2015-2016 - Sergi Granell (xerpi)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <gccore.h>
#include <rthreads/rthreads.h>
#include "../connect/joypad_connection.h"
#include "../input_autodetect.h"
#include "../input_hid_driver.h"
#include "../../verbosity.h"
#define WIIUSB_SC_NONE 0
#define WIIUSB_SC_INTMSG 1
#define WIIUSB_SC_CTRLMSG 2
#define WIIUSB_SC_CTRLMSG2 3
typedef struct wiiusb_hid
{
joypad_connection_t *connections;
struct wiiusb_adapter *adapters_head;
sthread_t *poll_thread;
volatile bool poll_thread_quit;
bool device_detected; /* helps on knowing if a new device has been inserted */
bool removal_cb; /* helps on detecting that a device has just been removed */
bool manual_removal;
} wiiusb_hid_t;
struct wiiusb_adapter
{
wiiusb_hid_t *hid;
struct wiiusb_adapter *next;
bool busy;
int32_t device_id;
int32_t handle;
int32_t endpoint_in;
int32_t endpoint_out;
int32_t endpoint_in_max_size;
int32_t endpoint_out_max_size;
int32_t slot;
uint8_t *data;
uint8_t send_control_type;
uint8_t *send_control_buffer;
uint32_t send_control_size;
};
static void wiiusb_hid_process_control_message(struct wiiusb_adapter* adapter)
{
int32_t r;
switch (adapter->send_control_type)
{
case WIIUSB_SC_INTMSG:
do { r = USB_WriteIntrMsg(adapter->handle, adapter->endpoint_out, adapter->send_control_size, adapter->send_control_buffer);
} while (r < 0);
break;
case WIIUSB_SC_CTRLMSG:
do { r = USB_WriteCtrlMsg(adapter->handle, USB_REQTYPE_INTERFACE_SET, USB_REQ_SETREPORT, (USB_REPTYPE_FEATURE<<8) | 0xf4, 0x0, adapter->send_control_size, adapter->send_control_buffer);
} while (r < 0);
break;
case WIIUSB_SC_CTRLMSG2:
do { r = USB_WriteCtrlMsg(adapter->handle, USB_REQTYPE_INTERFACE_SET, USB_REQ_SETREPORT, (USB_REPTYPE_OUTPUT<<8) | 0x01, 0x0, adapter->send_control_size, adapter->send_control_buffer);
} while (r < 0);
break;
/*default: any other case we do nothing */
}
/* Reset the control type */
adapter->send_control_type = WIIUSB_SC_NONE;
}
static int32_t wiiusb_hid_read_cb(int32_t size, void *data)
{
struct wiiusb_adapter *adapter = (struct wiiusb_adapter*)data;
wiiusb_hid_t *hid = adapter ? adapter->hid : NULL;
if (hid && hid->connections && size > 0)
pad_connection_packet(&hid->connections[adapter->slot], adapter->slot, adapter->data-1, size+1);
if (adapter)
adapter->busy = false;
return size;
}
static void wiiusb_hid_device_send_control(void *data,
uint8_t* data_buf, size_t size)
{
uint8_t control_type;
struct wiiusb_adapter *adapter = (struct wiiusb_adapter*)data;
if (!adapter || !data_buf || !adapter->send_control_buffer)
return;
/* first byte contains the type of control to use
* which can be NONE, INT_MSG, CTRL_MSG, CTRL_MSG2 */
control_type = data_buf[0];
/* decrement size by one as we are getting rid of first byte */
adapter->send_control_size = size - 1;
/* increase the buffer address so we access the actual data */
data_buf++;
memcpy(adapter->send_control_buffer, data_buf, adapter->send_control_size);
/* Activate it so it can be processed in the adapter thread */
adapter->send_control_type = control_type;
}
static void wiiusb_hid_device_add_autodetect(unsigned idx,
const char *device_name, const char *driver_name,
uint16_t dev_vid, uint16_t dev_pid)
{
autoconfig_params_t params = {{0}};
settings_t *settings = config_get_ptr();
params.idx = idx;
params.vid = dev_vid;
params.pid = dev_pid;
strlcpy(params.name, device_name, sizeof(params.name));
strlcpy(params.driver, driver_name, sizeof(params.driver));
strlcpy(settings->input.device_names[idx], device_name,
sizeof(settings->input.device_names[idx]));
input_config_autoconfigure_joypad(&params);
}
static void wiiusb_get_description(usb_device_entry *device,
struct wiiusb_adapter *adapter, usb_devdesc *devdesc)
{
unsigned char c;
unsigned i, k;
for (c = 0; c < devdesc->bNumConfigurations; c++)
{
const usb_configurationdesc *config = &devdesc->configurations[c];
for (i = 0; i < (int)config->bNumInterfaces; i++)
{
const usb_interfacedesc *inter = &config->interfaces[i];
for(k = 0; k < (int)inter->bNumEndpoints; k++)
{
const usb_endpointdesc *epdesc = &inter->endpoints[k];
bool is_int = (epdesc->bmAttributes & 0x03) == USB_ENDPOINT_INTERRUPT;
bool is_out = (epdesc->bEndpointAddress & 0x80) == USB_ENDPOINT_OUT;
bool is_in = (epdesc->bEndpointAddress & 0x80) == USB_ENDPOINT_IN;
if (is_int)
{
if (is_in)
{
adapter->endpoint_in = epdesc->bEndpointAddress;
adapter->endpoint_in_max_size = epdesc->wMaxPacketSize;
}
if (is_out)
{
adapter->endpoint_out = epdesc->bEndpointAddress;
adapter->endpoint_out_max_size = epdesc->wMaxPacketSize;
}
}
}
break;
}
}
}
static const char *wiiusb_hid_joypad_name(void *data, unsigned pad)
{
wiiusb_hid_t *hid = (wiiusb_hid_t*)data;
if (pad >= MAX_USERS)
return NULL;
if (hid)
return pad_connection_get_name(&hid->connections[pad], pad);
return NULL;
}
static int32_t wiiusb_hid_release_adapter(struct wiiusb_adapter *adapter)
{
wiiusb_hid_t *hid;
const char *name;
if (!adapter)
return -1;
hid = adapter->hid;
name = wiiusb_hid_joypad_name(hid, adapter->slot);
input_config_autoconfigure_disconnect(adapter->slot, name);
pad_connection_pad_deinit(&hid->connections[adapter->slot], adapter->slot);
free(adapter->send_control_buffer);
free(adapter->data);
free(adapter);
return 0;
}
static int wiiusb_hid_remove_adapter(struct wiiusb_adapter *adapter)
{
if (!adapter)
return -1;
if (adapter->handle > 0) {
USB_CloseDevice(&adapter->handle);
}
wiiusb_hid_release_adapter(adapter);
return 0;
}
static int wiiusb_hid_removal_cb(int result, void *usrdata)
{
struct wiiusb_adapter *adapter = (struct wiiusb_adapter *)usrdata;
wiiusb_hid_t *hid = adapter ? adapter->hid : NULL;
struct wiiusb_adapter *temp = hid ? hid->adapters_head : NULL;
if (!adapter || !hid || !temp || hid->manual_removal)
return -1;
if (temp == adapter)
hid->adapters_head = adapter->next;
else
while (temp->next)
{
if (temp->next == adapter)
{
temp->next = adapter->next;
break;
}
temp = temp->next;
}
/* get rid of the adapter */
wiiusb_hid_release_adapter(adapter);
/* notify that we pass thru the removal callback */
hid->removal_cb = true;
return 0;
}
static int wiiusb_hid_add_adapter(void *data, usb_device_entry *dev)
{
usb_devdesc desc;
const char *device_name = NULL;
wiiusb_hid_t *hid = (wiiusb_hid_t*)data;
struct wiiusb_adapter *adapter = (struct wiiusb_adapter*)
calloc(1, sizeof(struct wiiusb_adapter));
if (!adapter)
return -1;
if (!hid)
{
free(adapter);
RARCH_ERR("Allocation of adapter failed.\n");
return -1;
}
if (USB_OpenDevice(dev->device_id, dev->vid, dev->pid, &adapter->handle) < 0)
{
RARCH_ERR("Error opening device 0x%p (VID/PID: %04x:%04x).\n",
dev->device_id, dev->vid, dev->pid);
free(adapter);
return -1;
}
adapter->device_id = dev->device_id;
USB_GetDescriptors(adapter->handle, &desc);
wiiusb_get_description(dev, adapter, &desc);
if (adapter->endpoint_in == 0)
{
RARCH_ERR("Could not find HID config for device.\n");
goto error;
}
/* Allocate mem for the send control buffer, 32bit aligned */
adapter->send_control_type = WIIUSB_SC_NONE;
adapter->send_control_buffer = memalign(32, 128);
if (!adapter->send_control_buffer)
{
RARCH_ERR("Error creating send control buffer.\n");
goto error;
}
/* Sent the pad name as dummy, we don't know the
* control name until we get its interface */
adapter->slot = pad_connection_pad_init(hid->connections,
"hid", desc.idVendor, desc.idProduct,
adapter, &wiiusb_hid_device_send_control);
if (adapter->slot == -1)
goto error;
if (!pad_connection_has_interface(hid->connections, adapter->slot))
{
RARCH_ERR(" Interface not found.\n");
goto error;
}
adapter->data = memalign(32, 128);
adapter->hid = hid;
adapter->next = hid->adapters_head;
hid->adapters_head = adapter;
/* Get the name from the interface */
device_name = wiiusb_hid_joypad_name(hid, adapter->slot);
RARCH_LOG("Interface found: [%s].\n", device_name);
RARCH_LOG("Device 0x%p attached (VID/PID: %04x:%04x).\n",
adapter->device_id, desc.idVendor, desc.idProduct);
wiiusb_hid_device_add_autodetect(adapter->slot,
device_name, wiiusb_hid.ident, desc.idVendor, desc.idProduct);
USB_FreeDescriptors(&desc);
USB_DeviceRemovalNotifyAsync(adapter->handle, wiiusb_hid_removal_cb, adapter);
return 0;
error:
if (adapter->send_control_buffer)
free(adapter->send_control_buffer);
if (adapter)
free(adapter);
USB_FreeDescriptors(&desc);
USB_CloseDevice(&adapter->handle);
return -1;
}
static bool wiiusb_hid_new_device(wiiusb_hid_t *hid, int32_t id) {
struct wiiusb_adapter *temp;
if(!hid)
return false; /* false, so we do not proceed to add it */
temp = hid->adapters_head;
while (temp)
{
if (temp->device_id == id)
return false;
temp = temp->next;
}
return true;
}
static void wiiusb_hid_scan_for_devices(wiiusb_hid_t *hid)
{
unsigned i;
u8 count;
usb_device_entry *dev_entries;
dev_entries = (usb_device_entry *)calloc(MAX_USERS, sizeof(*dev_entries));
if (!dev_entries)
goto error;
if (USB_GetDeviceList(dev_entries, MAX_USERS, USB_CLASS_HID, &count) < 0)
goto error;
for (i = 0; i < count; i++)
{
/* first check the device is not already in our list */
if (!wiiusb_hid_new_device(hid, dev_entries[i].device_id))
continue;
if (dev_entries[i].vid > 0 && dev_entries[i].pid > 0)
wiiusb_hid_add_adapter(hid, &dev_entries[i]);
}
error:
if (dev_entries)
free(dev_entries);
}
static void wiiusb_hid_poll_thread(void *data)
{
wiiusb_hid_t *hid = (wiiusb_hid_t*)data;
struct wiiusb_adapter *adapter;
if (!hid)
return;
while (!hid->poll_thread_quit)
{
/* first check for new devices */
if (hid->device_detected)
{
/* turn off the detection flag */
hid->device_detected = false;
/* search for new pads and add them as needed */
wiiusb_hid_scan_for_devices(hid);
}
/* process each active adapter */
for (adapter=hid->adapters_head; adapter; adapter=adapter->next)
{
if (!adapter->busy)
{
/* lock itself while writing or reading */
adapter->busy = true;
if (adapter->send_control_type)
wiiusb_hid_process_control_message(adapter);
USB_ReadIntrMsgAsync(adapter->handle, adapter->endpoint_in, adapter->endpoint_in_max_size,
adapter->data, wiiusb_hid_read_cb, adapter);
}
}
/* Wait 10 milliseconds to process again */
usleep(10000);
}
}
static int wiiusb_hid_change_cb(int result, void *usrdata)
{
wiiusb_hid_t *hid = (wiiusb_hid_t*)usrdata;
if (!hid)
return -1;
if (!hid->removal_cb)
{
/* As it's not coming from the removal callback
then we detected a new device being inserted */
hid->device_detected = true;
}
else
hid->removal_cb = false;
/* Re-submit the change alert */
USB_DeviceChangeNotifyAsync(USB_CLASS_HID, wiiusb_hid_change_cb, usrdata);
return 0;
}
static bool wiiusb_hid_joypad_query(void *data, unsigned pad)
{
return pad < MAX_USERS;
}
static uint64_t wiiusb_hid_joypad_get_buttons(void *data, unsigned port)
{
wiiusb_hid_t *hid = (wiiusb_hid_t*)data;
if (hid)
return pad_connection_get_buttons(&hid->connections[port], port);
return 0;
}
static bool wiiusb_hid_joypad_button(void *data, unsigned port, uint16_t joykey)
{
uint64_t buttons = wiiusb_hid_joypad_get_buttons(data, port);
if (joykey == NO_BTN)
return false;
/* Check hat. */
if (GET_HAT_DIR(joykey))
return false;
/* Check the button. */
if ((port < MAX_USERS) && (joykey < 32))
return ((buttons & (1 << joykey)) != 0);
return false;
}
static bool wiiusb_hid_joypad_rumble(void *data, unsigned pad,
enum retro_rumble_effect effect, uint16_t strength)
{
wiiusb_hid_t *hid = (wiiusb_hid_t*)data;
if (!hid)
return false;
return pad_connection_rumble(&hid->connections[pad], pad, effect, strength);
}
static int16_t wiiusb_hid_joypad_axis(void *data,
unsigned port, uint32_t joyaxis)
{
wiiusb_hid_t *hid = (wiiusb_hid_t*)data;
int16_t val = 0;
if (joyaxis == AXIS_NONE)
return 0;
if (AXIS_NEG_GET(joyaxis) < 4)
{
val = pad_connection_get_axis(&hid->connections[port],
port, AXIS_NEG_GET(joyaxis));
if (val >= 0)
val = 0;
}
else if(AXIS_POS_GET(joyaxis) < 4)
{
val = pad_connection_get_axis(&hid->connections[port],
port, AXIS_POS_GET(joyaxis));
if (val <= 0)
val = 0;
}
return val;
}
static void wiiusb_hid_free(void *data)
{
wiiusb_hid_t *hid = (wiiusb_hid_t*)data;
struct wiiusb_adapter *adapter, *next_adapter;
if (!hid)
return;
hid->poll_thread_quit = true;
sthread_join(hid->poll_thread);
hid->manual_removal = TRUE;
/* remove each of the adapters */
for (adapter = hid->adapters_head; adapter; adapter = next_adapter)
{
next_adapter = adapter->next;
wiiusb_hid_remove_adapter(adapter);
}
pad_connection_destroy(hid->connections);
free(hid);
}
static void *wiiusb_hid_init(void)
{
wiiusb_hid_t *hid = (wiiusb_hid_t*)calloc(1, sizeof(*hid));
if (!hid)
goto error;
hid->connections = pad_connection_init(MAX_USERS);
if (!hid->connections)
goto error;
/* Init hid values */
hid->adapters_head = NULL;
hid->removal_cb = FALSE;
hid->manual_removal = FALSE;
hid->poll_thread_quit = FALSE;
/* we set it initially to TRUE so we force
* to add the already connected pads */
hid->device_detected = TRUE;
hid->poll_thread = sthread_create(wiiusb_hid_poll_thread, hid);
if (!hid->poll_thread)
{
RARCH_ERR("Error initializing poll thread.\n");
goto error;
}
USB_DeviceChangeNotifyAsync(USB_CLASS_HID, wiiusb_hid_change_cb, (void *)hid);
return hid;
error:
wiiusb_hid_free(hid);
return NULL;
}
static void wiiusb_hid_poll(void *data)
{
(void)data;
}
hid_driver_t wiiusb_hid = {
wiiusb_hid_init,
wiiusb_hid_joypad_query,
wiiusb_hid_free,
wiiusb_hid_joypad_button,
wiiusb_hid_joypad_get_buttons,
wiiusb_hid_joypad_axis,
wiiusb_hid_poll,
wiiusb_hid_joypad_rumble,
wiiusb_hid_joypad_name,
"wiiusb",
};