2018-02-05 23:21:00 -08:00
|
|
|
/* RetroArch - A frontend for libretro.
|
|
|
|
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
|
|
|
|
* Copyright (C) 2011-2017 - Daniel De Matteis
|
|
|
|
*
|
|
|
|
* 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/>.
|
|
|
|
*/
|
2018-03-25 22:59:30 -07:00
|
|
|
#include <stdio.h>
|
2018-02-05 23:21:00 -08:00
|
|
|
#include "hid_device_driver.h"
|
|
|
|
|
2018-02-21 22:57:28 -08:00
|
|
|
#ifdef WII
|
|
|
|
static uint8_t activation_packet[] = { 0x01, 0x13 };
|
|
|
|
#else
|
|
|
|
static uint8_t activation_packet[] = { 0x13 };
|
|
|
|
#endif
|
|
|
|
|
2018-04-25 21:28:33 -07:00
|
|
|
#define GCA_PORT_INITIALIZING 0x00
|
|
|
|
#define GCA_PORT_POWERED 0x04
|
|
|
|
#define GCA_PORT_CONNECTED 0x10
|
|
|
|
#define GCA_WAVEBIRD_CONNECTED 0x22
|
2018-02-21 22:57:28 -08:00
|
|
|
|
|
|
|
typedef struct wiiu_gca_instance {
|
2018-03-24 22:35:22 -07:00
|
|
|
void *handle;
|
2018-03-11 22:08:06 -07:00
|
|
|
bool online;
|
2018-02-21 22:57:28 -08:00
|
|
|
uint8_t device_state[37];
|
|
|
|
joypad_connection_t *pads[4];
|
|
|
|
} wiiu_gca_instance_t;
|
|
|
|
|
2018-03-25 22:59:30 -07:00
|
|
|
typedef struct gca_pad_data
|
|
|
|
{
|
|
|
|
void *gca_handle; // instance handle for the GCA adapter
|
|
|
|
hid_driver_t *driver; // HID system driver interface
|
|
|
|
uint8_t data[9]; // pad data
|
|
|
|
uint32_t slot; // slot this pad occupies
|
|
|
|
uint32_t buttons; // digital button state
|
2018-04-22 23:47:07 -07:00
|
|
|
int16_t analog_state[3][2]; // analog state
|
2018-03-25 22:59:30 -07:00
|
|
|
} gca_pad_t;
|
|
|
|
|
|
|
|
|
2018-02-21 22:57:28 -08:00
|
|
|
static void update_pad_state(wiiu_gca_instance_t *instance);
|
2018-03-25 22:59:30 -07:00
|
|
|
static void unregister_pad(wiiu_gca_instance_t *instance, int port);
|
2018-02-21 22:57:28 -08:00
|
|
|
|
|
|
|
extern pad_connection_interface_t wiiu_gca_pad_connection;
|
|
|
|
|
2018-03-24 22:35:22 -07:00
|
|
|
static void *wiiu_gca_init(void *handle)
|
2018-02-21 22:57:28 -08:00
|
|
|
{
|
2018-03-25 22:59:30 -07:00
|
|
|
RARCH_LOG("[gca]: allocating driver instance...\n");
|
|
|
|
wiiu_gca_instance_t *instance = calloc(1, sizeof(wiiu_gca_instance_t));
|
|
|
|
if(instance == NULL) goto error;
|
|
|
|
memset(instance, 0, sizeof(wiiu_gca_instance_t));
|
|
|
|
instance->handle = handle;
|
|
|
|
|
|
|
|
hid_instance.os_driver->send_control(handle, activation_packet, sizeof(activation_packet));
|
|
|
|
hid_instance.os_driver->read(handle, instance->device_state, sizeof(instance->device_state));
|
|
|
|
instance->online = true;
|
|
|
|
|
|
|
|
RARCH_LOG("[gca]: init done\n");
|
|
|
|
return instance;
|
|
|
|
|
|
|
|
error:
|
|
|
|
RARCH_ERR("[gca]: init failed\n");
|
|
|
|
if(instance)
|
|
|
|
free(instance);
|
|
|
|
return NULL;
|
2018-02-21 22:57:28 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void wiiu_gca_free(void *data) {
|
2018-03-11 22:08:06 -07:00
|
|
|
wiiu_gca_instance_t *instance = (wiiu_gca_instance_t *)data;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if(instance) {
|
|
|
|
instance->online = false;
|
|
|
|
|
|
|
|
for(i = 0; i < 4; i++)
|
2018-03-25 22:59:30 -07:00
|
|
|
unregister_pad(instance, i);
|
2018-03-11 22:08:06 -07:00
|
|
|
|
|
|
|
free(instance);
|
2018-03-25 22:59:30 -07:00
|
|
|
}
|
2018-02-21 22:57:28 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void wiiu_gca_handle_packet(void *data, uint8_t *buffer, size_t size)
|
|
|
|
{
|
2018-03-25 22:59:30 -07:00
|
|
|
wiiu_gca_instance_t *instance = (wiiu_gca_instance_t *)data;
|
|
|
|
if(!instance || !instance->online)
|
2018-03-28 00:02:09 -07:00
|
|
|
{
|
|
|
|
RARCH_WARN("[gca]: instance null or not ready yet.\n");
|
2018-03-25 22:59:30 -07:00
|
|
|
return;
|
2018-03-28 00:02:09 -07:00
|
|
|
}
|
2018-02-21 22:57:28 -08:00
|
|
|
|
2018-03-25 22:59:30 -07:00
|
|
|
if(size > sizeof(instance->device_state))
|
2018-03-28 00:02:09 -07:00
|
|
|
{
|
|
|
|
RARCH_WARN("[gca]: packet size %d is too big for buffer of size %d\n",
|
|
|
|
size, sizeof(instance->device_state));
|
2018-03-25 22:59:30 -07:00
|
|
|
return;
|
2018-03-28 00:02:09 -07:00
|
|
|
}
|
2018-02-21 22:57:28 -08:00
|
|
|
|
2018-03-25 22:59:30 -07:00
|
|
|
memcpy(instance->device_state, buffer, size);
|
|
|
|
update_pad_state(instance);
|
2018-02-21 22:57:28 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void update_pad_state(wiiu_gca_instance_t *instance)
|
|
|
|
{
|
2018-03-25 22:59:30 -07:00
|
|
|
int i, port;
|
2018-04-18 23:12:45 -07:00
|
|
|
unsigned char port_connected;
|
|
|
|
|
2018-03-11 22:08:06 -07:00
|
|
|
if(!instance || !instance->online)
|
|
|
|
return;
|
|
|
|
|
2018-03-25 22:59:30 -07:00
|
|
|
joypad_connection_t *pad;
|
2018-03-11 22:08:06 -07:00
|
|
|
/* process each pad */
|
|
|
|
for(i = 1; i < 37; i += 9)
|
|
|
|
{
|
2018-03-25 22:59:30 -07:00
|
|
|
port = i / 9;
|
|
|
|
pad = instance->pads[port];
|
|
|
|
|
2018-04-25 21:28:33 -07:00
|
|
|
port_connected = instance->device_state[i];
|
2018-04-18 23:12:45 -07:00
|
|
|
|
2018-04-25 21:28:33 -07:00
|
|
|
if(port_connected > GCA_PORT_POWERED)
|
2018-03-11 22:08:06 -07:00
|
|
|
{
|
2018-04-18 23:12:45 -07:00
|
|
|
if(pad == NULL)
|
|
|
|
{
|
|
|
|
RARCH_LOG("[gca]: Gamepad at port %d connected.\n", port+1);
|
|
|
|
instance->pads[port] = hid_pad_register(instance, &wiiu_gca_pad_connection);
|
|
|
|
pad = instance->pads[port];
|
2018-03-25 22:59:30 -07:00
|
|
|
if(pad == NULL)
|
|
|
|
{
|
2018-04-18 23:12:45 -07:00
|
|
|
RARCH_ERR("[gca]: Failed to register pad.\n");
|
|
|
|
break;
|
2018-03-25 22:59:30 -07:00
|
|
|
}
|
2018-04-18 23:12:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pad->iface->packet_handler(pad->data, &instance->device_state[i], 9);
|
|
|
|
} else {
|
|
|
|
if(pad != NULL) {
|
|
|
|
RARCH_LOG("[gca]: Gamepad at port %d disconnected.\n", port+1);
|
|
|
|
unregister_pad(instance, port);
|
|
|
|
}
|
2018-03-11 22:08:06 -07:00
|
|
|
}
|
|
|
|
}
|
2018-02-21 22:57:28 -08:00
|
|
|
}
|
|
|
|
|
2018-03-11 22:08:06 -07:00
|
|
|
static void unregister_pad(wiiu_gca_instance_t *instance, int slot)
|
|
|
|
{
|
|
|
|
if(!instance || slot < 0 || slot >= 4 || instance->pads[slot] == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
joypad_connection_t *pad = instance->pads[slot];
|
|
|
|
instance->pads[slot] = NULL;
|
2018-04-14 21:30:44 -07:00
|
|
|
|
|
|
|
hid_pad_deregister(pad);
|
2018-02-21 22:57:28 -08:00
|
|
|
}
|
|
|
|
|
2018-02-05 23:21:00 -08:00
|
|
|
static bool wiiu_gca_detect(uint16_t vendor_id, uint16_t product_id) {
|
2018-03-11 22:08:06 -07:00
|
|
|
return vendor_id == VID_NINTENDO && product_id == PID_NINTENDO_GCA;
|
2018-02-05 23:21:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
hid_device_t wiiu_gca_hid_device = {
|
2018-03-11 22:08:06 -07:00
|
|
|
wiiu_gca_init,
|
|
|
|
wiiu_gca_free,
|
|
|
|
wiiu_gca_handle_packet,
|
|
|
|
wiiu_gca_detect,
|
|
|
|
"Wii U Gamecube Adapter"
|
2018-02-05 23:21:00 -08:00
|
|
|
};
|
2018-02-21 22:57:28 -08:00
|
|
|
|
2018-03-25 22:59:30 -07:00
|
|
|
/**
|
|
|
|
* Pad connection interface implementation. This handles each individual
|
|
|
|
* GC controller (as opposed to the above that handles the GCA itself).
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void *wiiu_gca_pad_init(void *data, uint32_t slot, hid_driver_t *driver)
|
|
|
|
{
|
|
|
|
gca_pad_t *pad = (gca_pad_t *)calloc(1, sizeof(gca_pad_t));
|
|
|
|
|
|
|
|
if(!pad)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
memset(pad, 0, sizeof(gca_pad_t));
|
|
|
|
|
|
|
|
pad->gca_handle = data;
|
|
|
|
pad->driver = driver;
|
|
|
|
pad->slot = slot;
|
|
|
|
|
|
|
|
return pad;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void wiiu_gca_pad_deinit(void *data)
|
|
|
|
{
|
2018-04-22 17:34:20 -07:00
|
|
|
gca_pad_t *pad = (gca_pad_t *)data;
|
2018-03-25 22:59:30 -07:00
|
|
|
|
2018-04-22 17:34:20 -07:00
|
|
|
if(pad)
|
|
|
|
{
|
|
|
|
input_autoconfigure_disconnect(pad->slot, wiiu_gca_pad_connection.get_name(pad));
|
|
|
|
free(pad);
|
|
|
|
}
|
2018-03-25 22:59:30 -07:00
|
|
|
}
|
|
|
|
|
2018-04-14 14:34:13 -07:00
|
|
|
static void wiiu_gca_get_buttons(void *data, input_bits_t *state)
|
2018-03-25 22:59:30 -07:00
|
|
|
{
|
|
|
|
gca_pad_t *pad = (gca_pad_t *)data;
|
|
|
|
if(pad)
|
|
|
|
{
|
|
|
|
BITS_COPY16_PTR(state, pad->buttons);
|
|
|
|
} else {
|
|
|
|
BIT256_CLEAR_ALL_PTR(state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-20 00:00:33 -07:00
|
|
|
static void update_buttons(gca_pad_t *pad)
|
|
|
|
{
|
|
|
|
uint32_t i, pressed_keys;
|
|
|
|
|
|
|
|
static const uint32_t button_mapping[12] =
|
|
|
|
{
|
|
|
|
RETRO_DEVICE_ID_JOYPAD_A,
|
|
|
|
RETRO_DEVICE_ID_JOYPAD_B,
|
|
|
|
RETRO_DEVICE_ID_JOYPAD_X,
|
|
|
|
RETRO_DEVICE_ID_JOYPAD_Y,
|
|
|
|
RETRO_DEVICE_ID_JOYPAD_LEFT,
|
|
|
|
RETRO_DEVICE_ID_JOYPAD_RIGHT,
|
|
|
|
RETRO_DEVICE_ID_JOYPAD_DOWN,
|
|
|
|
RETRO_DEVICE_ID_JOYPAD_UP,
|
|
|
|
RETRO_DEVICE_ID_JOYPAD_START,
|
|
|
|
RETRO_DEVICE_ID_JOYPAD_SELECT,
|
|
|
|
RETRO_DEVICE_ID_JOYPAD_R,
|
|
|
|
RETRO_DEVICE_ID_JOYPAD_L,
|
|
|
|
};
|
|
|
|
|
|
|
|
if(!pad)
|
|
|
|
return;
|
|
|
|
|
|
|
|
pressed_keys = pad->data[1] | (pad->data[2] << 8);
|
|
|
|
pad->buttons = 0;
|
|
|
|
|
|
|
|
for(i = 0; i < 12; i++)
|
|
|
|
pad->buttons |= (pressed_keys & (1 << i)) ?
|
|
|
|
(1 << button_mapping[i]) : 0;
|
|
|
|
}
|
|
|
|
|
2018-04-22 23:47:07 -07:00
|
|
|
#if 0
|
|
|
|
const char *axes[] = {
|
|
|
|
"left x",
|
|
|
|
"left y",
|
|
|
|
"right x",
|
|
|
|
"right y"
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void update_analog_state(gca_pad_t *pad)
|
|
|
|
{
|
|
|
|
int pad_axis;
|
|
|
|
int16_t interpolated;
|
|
|
|
unsigned stick, axis;
|
|
|
|
|
|
|
|
/* GameCube analog axis are 8-bit unsigned, where 128/128 is center.
|
|
|
|
* So, we subtract 128 to get a signed, 0-based value and then mulitply
|
|
|
|
* by 256 to get the 16-bit range RetroArch expects. */
|
|
|
|
for(pad_axis = 0; pad_axis < 4; pad_axis++)
|
|
|
|
{
|
|
|
|
axis = pad_axis % 2 ? 0 : 1;
|
|
|
|
stick = pad_axis / 2;
|
|
|
|
interpolated = pad->data[3 + pad_axis];
|
|
|
|
/* libretro requires "up" to be negative, so we invert the y axis */
|
|
|
|
interpolated = (axis) ?
|
|
|
|
((interpolated - 128) * 256) :
|
|
|
|
((interpolated - 128) * -256);
|
|
|
|
|
|
|
|
pad->analog_state[stick][axis] = interpolated;
|
|
|
|
#if 0
|
|
|
|
RARCH_LOG("%s: %d\n", axes[pad_axis], interpolated);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-20 00:00:33 -07:00
|
|
|
|
2018-03-29 23:33:31 -07:00
|
|
|
/**
|
|
|
|
* The USB packet provides a 9-byte data packet for each pad.
|
|
|
|
*
|
|
|
|
* byte 0: connection status (0x14 = connected, 0x04 = disconnected)
|
|
|
|
* bytes 1-2: digital buttons
|
|
|
|
* bytes 3-4: left analog stick x/y
|
|
|
|
* bytes 5-6: right analog stick x/y
|
|
|
|
* bytes 7-8: L/R analog state (note that these have digital buttons too)
|
|
|
|
*/
|
2018-03-25 22:59:30 -07:00
|
|
|
static void wiiu_gca_packet_handler(void *data, uint8_t *packet, uint16_t size)
|
|
|
|
{
|
|
|
|
gca_pad_t *pad = (gca_pad_t *)data;
|
|
|
|
uint32_t i, pressed_keys;
|
|
|
|
|
|
|
|
if(!pad || !packet || size > sizeof(pad->data))
|
|
|
|
return;
|
|
|
|
|
|
|
|
memcpy(pad->data, packet, size);
|
2018-04-20 00:00:33 -07:00
|
|
|
update_buttons(pad);
|
2018-04-22 23:47:07 -07:00
|
|
|
update_analog_state(pad);
|
2018-03-25 22:59:30 -07:00
|
|
|
}
|
|
|
|
|
2018-04-20 00:00:33 -07:00
|
|
|
|
|
|
|
|
2018-03-25 22:59:30 -07:00
|
|
|
static void wiiu_gca_set_rumble(void *data, enum retro_rumble_effect effect, uint16_t strength)
|
|
|
|
{
|
|
|
|
(void)data;
|
|
|
|
(void)effect;
|
|
|
|
(void)strength;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int16_t wiiu_gca_get_axis(void *data, unsigned axis)
|
|
|
|
{
|
2018-04-22 23:47:07 -07:00
|
|
|
axis_data axis_data;
|
2018-03-25 22:59:30 -07:00
|
|
|
|
2018-04-22 23:47:07 -07:00
|
|
|
gca_pad_t *pad = (gca_pad_t *)data;
|
2018-03-25 22:59:30 -07:00
|
|
|
|
2018-04-23 23:22:27 -07:00
|
|
|
gamepad_read_axis_data(axis, &axis_data);
|
2018-03-25 22:59:30 -07:00
|
|
|
|
2018-04-22 23:47:07 -07:00
|
|
|
if(!pad || axis_data.axis >= 4)
|
2018-03-25 22:59:30 -07:00
|
|
|
return 0;
|
|
|
|
|
2018-04-23 23:22:27 -07:00
|
|
|
return gamepad_get_axis_value(pad->analog_state, &axis_data);
|
2018-03-25 22:59:30 -07:00
|
|
|
}
|
|
|
|
|
2018-03-29 23:33:31 -07:00
|
|
|
static const char *wiiu_gca_get_name(void *data)
|
2018-03-25 22:59:30 -07:00
|
|
|
{
|
2018-04-25 21:28:33 -07:00
|
|
|
gca_pad_t *pad = (gca_pad_t *)data;
|
2018-03-25 22:59:30 -07:00
|
|
|
|
2018-04-25 21:28:33 -07:00
|
|
|
return "GameCube Controller";
|
2018-03-25 22:59:30 -07:00
|
|
|
}
|
|
|
|
|
2018-03-30 18:57:34 -07:00
|
|
|
/**
|
|
|
|
* Button bitmask values:
|
|
|
|
* 0x0001 - A 0x0010 - left 0x0100 - Start/Pause
|
|
|
|
* 0x0002 - B 0x0020 - right 0x0200 - Z
|
|
|
|
* 0x0004 - X 0x0040 - down 0x0400 - R
|
|
|
|
* 0x0008 - Y 0x0080 - up 0x0800 - L
|
|
|
|
*/
|
|
|
|
|
2018-03-29 23:33:31 -07:00
|
|
|
static bool wiiu_gca_button(void *data, uint16_t joykey)
|
|
|
|
{
|
|
|
|
gca_pad_t *pad = (gca_pad_t *)data;
|
|
|
|
|
2018-04-20 00:00:33 -07:00
|
|
|
if(!pad || joykey > 31)
|
2018-03-29 23:33:31 -07:00
|
|
|
return false;
|
|
|
|
|
2018-04-20 00:00:33 -07:00
|
|
|
return pad->buttons & (1 << joykey);
|
2018-03-29 23:33:31 -07:00
|
|
|
}
|
|
|
|
|
2018-02-21 22:57:28 -08:00
|
|
|
pad_connection_interface_t wiiu_gca_pad_connection = {
|
|
|
|
wiiu_gca_pad_init,
|
|
|
|
wiiu_gca_pad_deinit,
|
|
|
|
wiiu_gca_packet_handler,
|
|
|
|
wiiu_gca_set_rumble,
|
|
|
|
wiiu_gca_get_buttons,
|
|
|
|
wiiu_gca_get_axis,
|
2018-03-29 23:33:31 -07:00
|
|
|
wiiu_gca_get_name,
|
|
|
|
wiiu_gca_button
|
2018-02-21 22:57:28 -08:00
|
|
|
};
|