diff --git a/Makefile.common b/Makefile.common index dc96774b20..e5924dd99f 100644 --- a/Makefile.common +++ b/Makefile.common @@ -198,6 +198,7 @@ OBJ += frontend/frontend.o \ $(LIBRETRO_COMM_DIR)/hash/rhash.o \ audio/audio_driver.o \ $(LIBRETRO_COMM_DIR)/audio/audio_mixer.o \ + input/common/input_common.o \ input/input_driver.o \ input/input_mapper.o \ led/led_driver.o \ diff --git a/Makefile.wiiu b/Makefile.wiiu index b3f57e8918..59b35b388b 100644 --- a/Makefile.wiiu +++ b/Makefile.wiiu @@ -6,8 +6,8 @@ DEBUG = 0 GRIFFIN_BUILD = 0 SALAMANDER_BUILD = 0 WHOLE_ARCHIVE_LINK = 0 +WIIU_HID = 1 HAVE_RUNAHEAD = 1 -WIIU_HID = 0 WIIU_LOG_RPX = 0 BUILD_DIR = objs/wiiu PC_DEVELOPMENT_IP_ADDRESS ?= @@ -33,6 +33,7 @@ OBJ += wiiu/input/wpad_driver.o OBJ += wiiu/input/kpad_driver.o OBJ += wiiu/input/pad_functions.o OBJ += wiiu/system/memory.o +OBJ += wiiu/system/atomic.o OBJ += wiiu/system/exception_handler.o OBJ += wiiu/fs/sd_fat_devoptab.o OBJ += wiiu/fs/fs_utils.o @@ -49,15 +50,11 @@ ifeq ($(WIIU_HID),1) OBJ += wiiu/input/hidpad_driver.o OBJ += wiiu/input/wiiu_hid.o OBJ += input/connect/joypad_connection.o \ - input/connect/connect_ps2adapter.o \ - input/connect/connect_psxadapter.o \ - input/connect/connect_ps3.o \ - input/connect/connect_ps4.o \ - input/connect/connect_wii.o \ - input/connect/connect_nesusb.o \ - input/connect/connect_snesusb.o \ - input/connect/connect_wiiupro.o \ - input/connect/connect_wiiugca.o + input/common/hid/hid_device_driver.o \ + input/common/hid/device_wiiu_gca.o \ + input/common/hid/device_ds3.o \ + input/common/hid/device_ds4.o \ + input/common/hid/device_null.o endif ifeq ($(SALAMANDER_BUILD),1) diff --git a/input/common/hid/device_ds3.c b/input/common/hid/device_ds3.c new file mode 100644 index 0000000000..4b26cd5634 --- /dev/null +++ b/input/common/hid/device_ds3.c @@ -0,0 +1,356 @@ +/* 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 . + */ + +#include "hid_device_driver.h" + +#define DS3_ACTIVATION_REPORT_ID 0xf4 +#define DS3_RUMBLE_REPORT_ID 0x01 + +typedef struct ds3_instance { + void *handle; + joypad_connection_t *pad; + int slot; + bool led_set; + uint32_t buttons; + int16_t analog_state[3][2]; + uint16_t motors[2]; + uint8_t data[64]; +} ds3_instance_t; + +static uint8_t activation_packet[] = { +#if defined(IOS) + 0x53, 0xF4, +#elif defined(HAVE_WIIUSB_HID) + 0x02, +#endif + 0x42, 0x0c, 0x00, 0x00 +}; + +#if defined(WIIU) +#define PACKET_OFFSET 2 +#elif defined(HAVE_WIIUSB_HID) +#define PACKET_OFFSET 1 +#else +#define PACKET_OFFSET 0 +#endif + +#define LED_OFFSET 11 +#define MOTOR1_OFFSET 4 +#define MOTOR2_OFFSET 6 + +static uint8_t control_packet[] = { + 0x52, 0x01, + 0x00, 0xff, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0xff, 0x27, 0x10, 0x00, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 +}; + +static int control_packet_size = sizeof(control_packet); + +extern pad_connection_interface_t ds3_pad_connection; + +static void update_pad_state(ds3_instance_t *instance); +static void update_analog_state(ds3_instance_t *instance); + +static int32_t send_activation_packet(ds3_instance_t *instance) +{ + int32_t result; +#if defined(WIIU) + result = HID_SET_REPORT(instance->handle, + HID_REPORT_FEATURE, + DS3_ACTIVATION_REPORT_ID, + activation_packet, + sizeof(activation_packet)); +#else + HID_SEND_CONTROL(instance->handle, + activation_packet, sizeof(activation_packet)); +#endif + + return result; +} + +static uint32_t set_protocol(ds3_instance_t *instance, int protocol) +{ + uint32_t result = 0; +#if defined(WIIU) + result = HID_SET_PROTOCOL(instance->handle, 1); +#endif + + return result; +} + +static int32_t send_control_packet(ds3_instance_t *instance) +{ + uint8_t packet_buffer[control_packet_size]; + int32_t result = 0; + memcpy(packet_buffer, control_packet, control_packet_size); + + packet_buffer[LED_OFFSET] = 0; + if(instance->pad) { + packet_buffer[LED_OFFSET] = 1 << ((instance->slot % 4) + 1); + } + packet_buffer[MOTOR1_OFFSET] = instance->motors[1] >> 8; + packet_buffer[MOTOR2_OFFSET] = instance->motors[0] >> 8; + +#if defined(HAVE_WIIUSB_HID) + packet_buffer[1] = 0x03; +#endif + +#if defined(WIIU) + result = HID_SET_REPORT(instance->handle, + HID_REPORT_OUTPUT, + DS3_RUMBLE_REPORT_ID, + packet_buffer+PACKET_OFFSET, + control_packet_size-PACKET_OFFSET); +#else + HID_SEND_CONTROL(instance->handle, + packet_buffer+PACKET_OFFSET, + control_packet_size-PACKET_OFFSET); +#endif /* WIIU */ + return result; +} + +static void *ds3_init(void *handle) +{ + ds3_instance_t *instance; + int errors = 0; + RARCH_LOG("[ds3]: init\n"); + instance = (ds3_instance_t *)calloc(1, sizeof(ds3_instance_t)); + if(!instance) + goto error; + + memset(instance, 0, sizeof(ds3_instance_t)); + instance->handle = handle; + + RARCH_LOG("[ds3]: setting protocol\n"); + + /* this might fail, but we don't care. */ + set_protocol(instance, 1); + + RARCH_LOG("[ds3]: sending control packet\n"); + if(send_control_packet(instance) < 0) + errors++; + + RARCH_LOG("[ds3]: sending activation packet\n"); + if(send_activation_packet(instance) < 0) + errors++; + + if(errors) + goto error; + + instance->pad = hid_pad_register(instance, &ds3_pad_connection); + if(!instance->pad) + goto error; + + RARCH_LOG("[ds3]: init complete.\n"); + return instance; + + error: + RARCH_ERR("[ds3]: init failed.\n"); + if(instance) + free(instance); + return NULL; +} + +static void ds3_free(void *data) +{ + ds3_instance_t *instance = (ds3_instance_t *)data; + + if(instance) { + hid_pad_deregister(instance->pad); + free(instance); + } +} + +static void ds3_handle_packet(void *data, uint8_t *packet, size_t size) +{ + ds3_instance_t *instance = (ds3_instance_t *)data; + + if(!instance || !instance->pad) + return; + + instance->pad->iface->packet_handler(data, packet, size); +} + +static bool ds3_detect(uint16_t vendor_id, uint16_t product_id) +{ + return vendor_id == VID_SONY && product_id == PID_SONY_DS3; +} + +hid_device_t ds3_hid_device = { + ds3_init, + ds3_free, + ds3_handle_packet, + ds3_detect, + "Sony DualShock 3" +}; + +/** + * pad interface implementation + */ + +static void *ds3_pad_init(void *data, uint32_t slot, hid_driver_t *driver) +{ + ds3_instance_t *pad = (ds3_instance_t *)data; + pad->slot = slot; + + return data; +} + +static void ds3_pad_deinit(void *data) +{ + ds3_instance_t *pad = (ds3_instance_t *)data; + if(pad) { + input_autoconfigure_disconnect(pad->slot, ds3_pad_connection.get_name(pad)); + } +} + +static void ds3_get_buttons(void *data, input_bits_t *state) +{ + ds3_instance_t *pad = (ds3_instance_t *)data; + + if(pad) + { + BITS_COPY16_PTR(state, pad->buttons); + + if(pad->buttons & 0x10000) + BIT256_SET_PTR(state, RARCH_MENU_TOGGLE); + } else { + BIT256_CLEAR_ALL_PTR(state); + } +} + +static void ds3_packet_handler(void *data, uint8_t *packet, uint16_t size) +{ + ds3_instance_t *instance = (ds3_instance_t *)data; + + if(instance->pad && !instance->led_set) + { + send_control_packet(instance); + instance->led_set = true; + } + + if(size > control_packet_size) + { + RARCH_ERR("[ds3]: Expecting packet to be %d but was %d\n", + control_packet_size, size); + return; + } + + memcpy(instance->data, packet, size); + update_pad_state(instance); + update_analog_state(instance); +} + +static void update_analog_state(ds3_instance_t *instance) +{ + int pad_axis; + int16_t interpolated; + unsigned stick, axis; + + for(pad_axis = 0; pad_axis < 4; pad_axis++) + { + axis = pad_axis % 2 ? 0 : 1; + stick = pad_axis / 2; + interpolated = instance->data[6+pad_axis]; + instance->analog_state[stick][axis] = (interpolated - 128) * 256; + } +} + +static void update_pad_state(ds3_instance_t *instance) +{ + uint32_t i, pressed_keys; + + static const uint32_t button_mapping[17] = + { + RETRO_DEVICE_ID_JOYPAD_SELECT, + RETRO_DEVICE_ID_JOYPAD_L3, + RETRO_DEVICE_ID_JOYPAD_R3, + RETRO_DEVICE_ID_JOYPAD_START, + RETRO_DEVICE_ID_JOYPAD_UP, + RETRO_DEVICE_ID_JOYPAD_RIGHT, + RETRO_DEVICE_ID_JOYPAD_DOWN, + RETRO_DEVICE_ID_JOYPAD_LEFT, + RETRO_DEVICE_ID_JOYPAD_L2, + RETRO_DEVICE_ID_JOYPAD_R2, + RETRO_DEVICE_ID_JOYPAD_L, + RETRO_DEVICE_ID_JOYPAD_R, + RETRO_DEVICE_ID_JOYPAD_X, + RETRO_DEVICE_ID_JOYPAD_A, + RETRO_DEVICE_ID_JOYPAD_B, + RETRO_DEVICE_ID_JOYPAD_Y, + 16 /* PS button */ + }; + + instance->buttons = 0; + + pressed_keys = instance->data[2]|(instance->data[3] << 8)|((instance->data[4] & 0x01) << 16); + + for(i = 0; i < 17; i++) + instance->buttons |= (pressed_keys & (1 << i)) ? + (1 << button_mapping[i]) : 0; +} + +static void ds3_set_rumble(void *data, enum retro_rumble_effect effect, uint16_t strength) +{ + ds3_instance_t *pad = (ds3_instance_t *)data; +} + +static int16_t ds3_get_axis(void *data, unsigned axis) +{ + axis_data axis_data; + ds3_instance_t *pad = (ds3_instance_t *)data; + + gamepad_read_axis_data(axis, &axis_data); + + if(!pad || axis_data.axis >= 4) + return 0; + + return gamepad_get_axis_value(pad->analog_state, &axis_data); +} + +static const char *ds3_get_name(void *data) +{ + ds3_instance_t *pad = (ds3_instance_t *)data; + return "Sony DualShock 3"; +} + +static bool ds3_button(void *data, uint16_t joykey) +{ + ds3_instance_t *pad = (ds3_instance_t *)data; + if(!pad || joykey > 31) + return false; + + return pad->buttons & (1 << joykey); +} + +pad_connection_interface_t ds3_pad_connection = { + ds3_pad_init, + ds3_pad_deinit, + ds3_packet_handler, + ds3_set_rumble, + ds3_get_buttons, + ds3_get_axis, + ds3_get_name, + ds3_button +}; diff --git a/input/common/hid/device_ds4.c b/input/common/hid/device_ds4.c new file mode 100644 index 0000000000..af923cee13 --- /dev/null +++ b/input/common/hid/device_ds4.c @@ -0,0 +1,154 @@ +/* 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 . + */ + +#include "hid_device_driver.h" + +extern pad_connection_interface_t ds4_pad_connection; + +typedef struct ds4_instance { + void *handle; + joypad_connection_t *pad; + int slot; + uint32_t buttons; + uint16_t motors[2]; + uint8_t data[64]; +} ds4_instance_t; + +/** + * I'm leaving this code in here for posterity, and because maybe it can + * be used on other platforms. But using the DS4 on the Wii U directly is + * impossible because it doesn't generate a HID event. Which makes me think + * it's not a HID device at all--at least, not over USB. + * + * I imagine it might be useful in Bluetooth mode, though. + */ +static void *ds4_init(void *handle) +{ + ds4_instance_t *instance; + instance = (ds4_instance_t *)calloc(1, sizeof(ds4_instance_t)); + if(!instance) + goto error; + + memset(instance, 0, sizeof(ds4_instance_t)); + instance->handle = handle; + instance->pad = hid_pad_register(instance, &ds4_pad_connection); + if(!instance->pad) + goto error; + + RARCH_LOG("[ds4]: init complete.\n"); + return instance; + + error: + RARCH_ERR("[ds4]: init failed.\n"); + if(instance) + free(instance); + + return NULL; +} + +static void ds4_free(void *data) +{ + ds4_instance_t *instance = (ds4_instance_t *)data; + + if(instance) { + hid_pad_deregister(instance->pad); + free(instance); + } +} + +static void ds4_handle_packet(void *data, uint8_t *buffer, size_t size) +{ + ds4_instance_t *instance = (ds4_instance_t *)data; + + if(instance && instance->pad) + instance->pad->iface->packet_handler(instance->pad->data, buffer, size); +} + +static bool ds4_detect(uint16_t vendor_id, uint16_t product_id) +{ + return vendor_id == VID_SONY && product_id == PID_SONY_DS4; +} + +hid_device_t ds4_hid_device = { + ds4_init, + ds4_free, + ds4_handle_packet, + ds4_detect, + "Sony DualShock 4" +}; + +static void *ds4_pad_init(void *data, uint32_t slot, hid_driver_t *driver) +{ + ds4_instance_t *instance = (ds4_instance_t *)data; + + if(!instance) + return NULL; + + instance->slot = slot; + return instance; +} + +static void ds4_pad_deinit(void *data) +{ +} + +static void ds4_get_buttons(void *data, input_bits_t *state) +{ + ds4_instance_t *instance = (ds4_instance_t *)data; + if(!instance) + return; + + /* TODO: get buttons */ +} + +static void ds4_packet_handler(void *data, uint8_t *packet, uint16_t size) +{ + ds4_instance_t *instance = (ds4_instance_t *)data; + if(!instance) + return; + + RARCH_LOG_BUFFER(packet, size); +} + +static void ds4_set_rumble(void *data, enum retro_rumble_effect effect, uint16_t strength) +{ +} + +static int16_t ds4_get_axis(void *data, unsigned axis) +{ + return 0; +} + +static const char *ds4_get_name(void *data) +{ + return "Sony DualShock 4"; +} + +static bool ds4_button(void *data, uint16_t joykey) +{ + return false; +} + +pad_connection_interface_t ds4_pad_connection = { + ds4_pad_init, + ds4_pad_deinit, + ds4_packet_handler, + ds4_set_rumble, + ds4_get_buttons, + ds4_get_axis, + ds4_get_name, + ds4_button +}; diff --git a/input/common/hid/device_null.c b/input/common/hid/device_null.c new file mode 100644 index 0000000000..f5676c87b7 --- /dev/null +++ b/input/common/hid/device_null.c @@ -0,0 +1,219 @@ +/* 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 . + */ + +#include "hid_device_driver.h" + +extern pad_connection_interface_t null_pad_connection; + +/* + * This is the instance data structure for the pad you are implementing. + * This is a good starting point, but you can add/remove things as makes + * sense for the pad you're writing for. The pointer to this structure + * will be passed in as a void pointer to the methods you implement below. + */ +typedef struct null_instance { + void *handle; /* a handle to the HID subsystem adapter */ + joypad_connection_t *pad; /* a pointer to the joypad connection you assign + in init() */ + int slot; /* which slot does this pad occupy? */ + uint32_t buttons; /* a bitmap of the digital buttons for the pad */ + uint16_t motors[2]; /* rumble strength, if appropriate */ + uint8_t data[64]; /* a buffer large enough to hold the device's + max rx packet */ +} null_instance_t; + +/** + * Use the HID_ macros (see input/include/hid_driver.h) to send data packets + * to the device. When this method returns, the device needs to be in a state + * where we can read data packets from the device. So, if there's any + * activation packets (see the ds3 and Wii U GameCube adapter drivers for + * examples), send them here. + * + * While you *can* allocate the retro pad here, it isn't mandatory (see + * the Wii U GC adapter). + * + * If initialization fails, return NULL. + */ +static void *null_init(void *handle) +{ + null_instance_t *instance; + instance = (null_instance_t *)calloc(1, sizeof(null_instance_t)); + if(!instance) + goto error; + + memset(instance, 0, sizeof(null_instance_t)); + instance->handle = handle; + instance->pad = hid_pad_register(instance, &null_pad_connection); + if(!instance->pad) + goto error; + + RARCH_LOG("[null]: init complete.\n"); + return instance; + + error: + RARCH_ERR("[null]: init failed.\n"); + if(instance) + free(instance); + + return NULL; +} + +/* + * Gets called when the pad is disconnected. It must clean up any memory + * allocated and used by the instance data. + */ +static void null_free(void *data) +{ + null_instance_t *instance = (null_instance_t *)data; + + if(instance) { + hid_pad_deregister(instance->pad); + free(instance); + } +} + +/** + * Handle a single packet from the device. + * For most pads you'd just forward it onto the pad driver (see below). + * A more complicated example is in the Wii U GC adapter driver. + */ +static void null_handle_packet(void *data, uint8_t *buffer, size_t size) +{ + null_instance_t *instance = (null_instance_t *)data; + + if(instance && instance->pad) + instance->pad->iface->packet_handler(instance->pad->data, buffer, size); +} + +/** + * Return true if the passed in VID and PID are supported by the driver. + */ +static bool null_detect(uint16_t vendor_id, uint16_t product_id) +{ + return vendor_id == VID_NONE && product_id == PID_NONE; +} + +/** + * Assign function pointers to the driver structure. + */ +hid_device_t null_hid_device = { + null_init, + null_free, + null_handle_packet, + null_detect, + "Null HID device" +}; + +/** + * This is called via hid_pad_register(). In the common case where the + * device only controls one pad, you can simply return the data parameter. + * But if you need to track multiple pads attached to the same HID device + * (see: Wii U GC adapter), you can allocate that memory here. + */ +static void *null_pad_init(void *data, uint32_t slot, hid_driver_t *driver) +{ + null_instance_t *instance = (null_instance_t *)data; + + if(!instance) + return NULL; + + instance->slot = slot; + return instance; +} + +/** + * If you allocate any memory in null_pad_init() above, de-allocate it here. + */ +static void null_pad_deinit(void *data) +{ +} + +/** + * Translate the button data from the pad into the input_bits_t format + * that RetroArch can use. + */ +static void null_get_buttons(void *data, input_bits_t *state) +{ + null_instance_t *instance = (null_instance_t *)data; + if(!instance) + return; + + /* TODO: get buttons */ +} + +/** + * Handle a single packet for the pad. + */ +static void null_packet_handler(void *data, uint8_t *packet, uint16_t size) +{ + null_instance_t *instance = (null_instance_t *)data; + if(!instance) + return; + + RARCH_LOG_BUFFER(packet, size); +} + +/** + * If the pad doesn't support rumble, then this can just be a no-op. + */ +static void null_set_rumble(void *data, enum retro_rumble_effect effect, uint16_t strength) +{ +} + +/** + * Read analog sticks. + * If the pad doesn't have any analog axis, just return 0 here. + * + * The return value must conform to the following characteristics: + * - (0, 0) is center + * - (-32768,-32768) is top-left + * - (32767,32767) is bottom-right + */ +static int16_t null_get_axis(void *data, unsigned axis) +{ + return 0; +} + +/** + * The name the pad will show up as in the UI, also used to auto-assign + * buttons in input/input_autodetect_builtin.c + */ +static const char *null_get_name(void *data) +{ + return "Null HID Pad"; +} + +/** + * Read the state of a single button. + */ +static bool null_button(void *data, uint16_t joykey) +{ + return false; +} + +/** + * Fill in the joypad interface + */ +pad_connection_interface_t null_pad_connection = { + null_pad_init, + null_pad_deinit, + null_packet_handler, + null_set_rumble, + null_get_buttons, + null_get_axis, + null_get_name, + null_button +}; diff --git a/input/common/hid/device_wiiu_gca.c b/input/common/hid/device_wiiu_gca.c new file mode 100644 index 0000000000..1a26800041 --- /dev/null +++ b/input/common/hid/device_wiiu_gca.c @@ -0,0 +1,363 @@ +/* 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 . + */ +#include +#include "hid_device_driver.h" + +#ifdef WII +static uint8_t activation_packet[] = { 0x01, 0x13 }; +#else +static uint8_t activation_packet[] = { 0x13 }; +#endif + +#define GCA_PORT_INITIALIZING 0x00 +#define GCA_PORT_POWERED 0x04 +#define GCA_PORT_CONNECTED 0x10 + + +typedef struct wiiu_gca_instance { + void *handle; + bool online; + uint8_t device_state[37]; + joypad_connection_t *pads[4]; +} wiiu_gca_instance_t; + +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 + int16_t analog_state[3][2]; // analog state +} gca_pad_t; + + +static void update_pad_state(wiiu_gca_instance_t *instance); +static void unregister_pad(wiiu_gca_instance_t *instance, int port); + +extern pad_connection_interface_t wiiu_gca_pad_connection; + +static void *wiiu_gca_init(void *handle) +{ + 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; +} + +static void wiiu_gca_free(void *data) { + wiiu_gca_instance_t *instance = (wiiu_gca_instance_t *)data; + int i; + + if(instance) { + instance->online = false; + + for(i = 0; i < 4; i++) + unregister_pad(instance, i); + + free(instance); + } +} + +static void wiiu_gca_handle_packet(void *data, uint8_t *buffer, size_t size) +{ + wiiu_gca_instance_t *instance = (wiiu_gca_instance_t *)data; + if(!instance || !instance->online) + { + RARCH_WARN("[gca]: instance null or not ready yet.\n"); + return; + } + + if(size > sizeof(instance->device_state)) + { + RARCH_WARN("[gca]: packet size %d is too big for buffer of size %d\n", + size, sizeof(instance->device_state)); + return; + } + + memcpy(instance->device_state, buffer, size); + update_pad_state(instance); +} + +static void update_pad_state(wiiu_gca_instance_t *instance) +{ + int i, port; + unsigned char port_connected; + + if(!instance || !instance->online) + return; + + joypad_connection_t *pad; + /* process each pad */ + //RARCH_LOG_BUFFER(instance->device_state, 37); + for(i = 1; i < 37; i += 9) + { + port = i / 9; + pad = instance->pads[port]; + + port_connected = instance->device_state[i] & GCA_PORT_CONNECTED; + + if(port_connected) + { + 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]; + if(pad == NULL) + { + RARCH_ERR("[gca]: Failed to register pad.\n"); + break; + } + } + + 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); + } + } + } +} + +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; + + hid_pad_deregister(pad); +} + +static bool wiiu_gca_detect(uint16_t vendor_id, uint16_t product_id) { + return vendor_id == VID_NINTENDO && product_id == PID_NINTENDO_GCA; +} + +hid_device_t wiiu_gca_hid_device = { + wiiu_gca_init, + wiiu_gca_free, + wiiu_gca_handle_packet, + wiiu_gca_detect, + "Wii U Gamecube Adapter" +}; + +/** + * 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) +{ + gca_pad_t *pad = (gca_pad_t *)data; + + if(pad) + { + input_autoconfigure_disconnect(pad->slot, wiiu_gca_pad_connection.get_name(pad)); + free(pad); + } +} + +static void wiiu_gca_get_buttons(void *data, input_bits_t *state) +{ + gca_pad_t *pad = (gca_pad_t *)data; + if(pad) + { + BITS_COPY16_PTR(state, pad->buttons); + } else { + BIT256_CLEAR_ALL_PTR(state); + } +} + +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; +} + +#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 + } +} + + +/** + * 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) + */ +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); + update_buttons(pad); + update_analog_state(pad); +} + + + +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) +{ + axis_data axis_data; + + gca_pad_t *pad = (gca_pad_t *)data; + + gamepad_read_axis_data(axis, &axis_data); + + if(!pad || axis_data.axis >= 4) + return 0; + + return gamepad_get_axis_value(pad->analog_state, &axis_data); +} + +static const char *wiiu_gca_get_name(void *data) +{ + gca_pad_t *pad = (gca_pad_t *)data; + + return "GameCube Controller"; +} + +/** + * 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 + */ + +static bool wiiu_gca_button(void *data, uint16_t joykey) +{ + gca_pad_t *pad = (gca_pad_t *)data; + + if(!pad || joykey > 31) + return false; + + return pad->buttons & (1 << joykey); +} + +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, + wiiu_gca_get_name, + wiiu_gca_button +}; diff --git a/input/common/hid/hid_device_driver.c b/input/common/hid/hid_device_driver.c new file mode 100644 index 0000000000..ccab2dc569 --- /dev/null +++ b/input/common/hid/hid_device_driver.c @@ -0,0 +1,156 @@ +/* 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 . + */ + +#include "hid_device_driver.h" + +hid_driver_instance_t hid_instance = {0}; + +hid_device_t *hid_device_list[] = { + &wiiu_gca_hid_device, + &ds3_hid_device, +/* &ds4_hid_device, */ + NULL /* must be last entry in list */ +}; + +hid_device_t *hid_device_driver_lookup(uint16_t vendor_id, uint16_t product_id) { + int i = 0; + + for(i = 0; hid_device_list[i] != NULL; i++) { + if(hid_device_list[i]->detect(vendor_id, product_id)) + return hid_device_list[i]; + } + + return NULL; +} + +joypad_connection_t *hid_pad_register(void *pad_handle, pad_connection_interface_t *iface) +{ + int slot; + joypad_connection_t *result; + + if(!pad_handle) + return NULL; + + slot = pad_connection_find_vacant_pad(hid_instance.pad_list); + if(slot < 0) + { + RARCH_ERR("[hid]: failed to find a vacant pad.\n"); + return NULL; + } + + result = &(hid_instance.pad_list[slot]); + result->iface = iface; + result->data = iface->init(pad_handle, slot, hid_instance.os_driver); + result->connected = true; + input_pad_connect(slot, hid_instance.pad_driver); + + return result; +} + +void hid_pad_deregister(joypad_connection_t *pad) +{ + if(!pad) + return; + + if(pad->data) { + pad->iface->deinit(pad->data); + pad->data = NULL; + } + + pad->iface = NULL; + pad->connected = false; +} + +static bool init_pad_list(hid_driver_instance_t *instance, unsigned slots) +{ + if(!instance || slots > MAX_USERS) + return false; + + if(instance->pad_list) + return true; + + RARCH_LOG("[hid]: initializing pad list...\n"); + instance->pad_list = pad_connection_init(slots); + if(!instance->pad_list) + return false; + + instance->max_slot = slots; + + return true; +} + +/** + * Fill in instance with data from initialized hid subsystem. + * + * @argument instance the hid_driver_instance_t struct to fill in + * @argument hid_driver the HID driver to initialize + * @argument pad_driver the gamepad driver to handle HID pads detected by the HID driver. + * + * @returns true if init is successful, false otherwise. + */ +bool hid_init(hid_driver_instance_t *instance, + hid_driver_t *hid_driver, + input_device_driver_t *pad_driver, + unsigned slots) +{ + RARCH_LOG("[hid]: initializing instance with %d pad slots\n", slots); + if(!instance || !hid_driver || !pad_driver || slots > MAX_USERS) + return false; + + RARCH_LOG("[hid]: initializing HID subsystem driver...\n"); + instance->os_driver_data = hid_driver->init(); + if(!instance->os_driver_data) + return false; + + if(!init_pad_list(instance, slots)) + { + hid_driver->free(instance->os_driver_data); + instance->os_driver_data = NULL; + return false; + } + + instance->os_driver = hid_driver; + instance->pad_driver = pad_driver; + + RARCH_LOG("[hid]: instance initialization complete.\n"); + + return true; +} + +/** + * Tear down the HID system set up by hid_init() + * + * @argument instance the hid_driver_instance_t to tear down. + */ +void hid_deinit(hid_driver_instance_t *instance) +{ + if(!instance) + return; + + RARCH_LOG("[hid]: destroying instance\n"); + + if(instance->os_driver && instance->os_driver_data) + { + RARCH_LOG("[hid]: tearing down HID subsystem driver...\n"); + instance->os_driver->free(instance->os_driver_data); + } + + RARCH_LOG("[hid]: destroying pad data...\n"); + pad_connection_destroy(instance->pad_list); + + RARCH_LOG("[hid]: wiping instance data...\n"); + memset(instance, 0, sizeof(hid_driver_instance_t)); +} diff --git a/input/common/hid/hid_device_driver.h b/input/common/hid/hid_device_driver.h new file mode 100644 index 0000000000..8e764f0f78 --- /dev/null +++ b/input/common/hid/hid_device_driver.h @@ -0,0 +1,46 @@ +/* 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 . + */ + +#ifndef HID_DEVICE_DRIVER__H +#define HID_DEVICE_DRIVER__H + +#include "../../input_driver.h" +#include "../../connect/joypad_connection.h" +#include "../../include/hid_driver.h" +#include "../../include/gamepad.h" +#include "../../../verbosity.h" +#include "../../../tasks/tasks_internal.h" + +typedef struct hid_device { + void *(*init)(void *handle); + void (*free)(void *data); + void (*handle_packet)(void *data, uint8_t *buffer, size_t size); + bool (*detect)(uint16_t vid, uint16_t pid); + const char *name; +} hid_device_t; + +extern hid_device_t wiiu_gca_hid_device; +extern hid_device_t ds3_hid_device; +extern hid_device_t ds4_hid_device; +extern hid_driver_instance_t hid_instance; + +hid_device_t *hid_device_driver_lookup(uint16_t vendor_id, uint16_t product_id); +joypad_connection_t *hid_pad_register(void *pad_handle, pad_connection_interface_t *iface); +void hid_pad_deregister(joypad_connection_t *pad); +bool hid_init(hid_driver_instance_t *instance, hid_driver_t *hid_driver, input_device_driver_t *pad_driver, unsigned slots); +void hid_deinit(hid_driver_instance_t *instance); + +#endif /* HID_DEVICE_DRIVER__H */ diff --git a/input/common/input_common.c b/input/common/input_common.c new file mode 100644 index 0000000000..4f6e656363 --- /dev/null +++ b/input/common/input_common.c @@ -0,0 +1,78 @@ +/* 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 . + */ + +#include "../include/gamepad.h" + +enum pad_axes { + AXIS_LEFT_ANALOG_X, + AXIS_LEFT_ANALOG_Y, + AXIS_RIGHT_ANALOG_X, + AXIS_RIGHT_ANALOG_Y, + AXIS_INVALID +}; + +static int16_t clamp_axis(int16_t value, bool is_negative) +{ + if(is_negative && value > 0) + return 0; + if(!is_negative && value < 0) + return 0; + + return value; +} + +void gamepad_read_axis_data(uint32_t axis, axis_data *data) +{ + if(!data) + return; + + data->axis = AXIS_POS_GET(axis); + data->is_negative = false; + + if(data->axis >= AXIS_INVALID) + { + data->axis = AXIS_NEG_GET(axis); + data->is_negative = true; + } +} + +int16_t gamepad_get_axis_value(int16_t state[3][2], axis_data *data) +{ + int16_t value = 0; + + if(!data) + return 0; + + switch(data->axis) + { + case AXIS_LEFT_ANALOG_X: + value = state[RETRO_DEVICE_INDEX_ANALOG_LEFT][RETRO_DEVICE_ID_ANALOG_X]; + break; + case AXIS_LEFT_ANALOG_Y: + value = state[RETRO_DEVICE_INDEX_ANALOG_LEFT][RETRO_DEVICE_ID_ANALOG_Y]; + break; + case AXIS_RIGHT_ANALOG_X: + value = state[RETRO_DEVICE_INDEX_ANALOG_RIGHT][RETRO_DEVICE_ID_ANALOG_X]; + break; + case AXIS_RIGHT_ANALOG_Y: + value = state[RETRO_DEVICE_INDEX_ANALOG_RIGHT][RETRO_DEVICE_ID_ANALOG_Y]; + break; + } + + return clamp_axis(value, data->is_negative); +} + + diff --git a/input/connect/connect_ps3.c b/input/connect/connect_ps3.c index 5625ee09d1..52d0032ecd 100644 --- a/input/connect/connect_ps3.c +++ b/input/connect/connect_ps3.c @@ -21,10 +21,7 @@ #include #include "joypad_connection.h" #include "../input_defines.h" - -#ifdef WIIU -#include -#endif +#include "../common/hid/hid_device_driver.h" struct hidpad_ps3_data { diff --git a/input/connect/connect_ps4.c b/input/connect/connect_ps4.c index 1de5d8d056..f33844e6b1 100644 --- a/input/connect/connect_ps4.c +++ b/input/connect/connect_ps4.c @@ -25,6 +25,7 @@ #include "joypad_connection.h" #include "../input_defines.h" #include "../../driver.h" +#include "../common/hid/hid_device_driver.h" enum connect_ps4_dpad_states { diff --git a/input/connect/connect_wii.c b/input/connect/connect_wii.c index be27bfcfef..2008be9ecc 100644 --- a/input/connect/connect_wii.c +++ b/input/connect/connect_wii.c @@ -26,6 +26,7 @@ #include "joypad_connection.h" #include "../input_defines.h" +#include "../common/hid/hid_device_driver.h" /* wiimote state flags*/ #define WIIMOTE_STATE_DEV_FOUND 0x0001 diff --git a/input/connect/connect_wiiugca.c b/input/connect/connect_wiiugca.c index 8283f2ac9d..357e479b58 100644 --- a/input/connect/connect_wiiugca.c +++ b/input/connect/connect_wiiugca.c @@ -21,6 +21,7 @@ #include #include "joypad_connection.h" #include "../input_defines.h" +#include "../common/hid/hid_device_driver.h" struct hidpad_wiiugca_data { diff --git a/input/connect/connect_wiiupro.c b/input/connect/connect_wiiupro.c index dd9751114c..a6194fb922 100644 --- a/input/connect/connect_wiiupro.c +++ b/input/connect/connect_wiiupro.c @@ -23,6 +23,7 @@ #include "joypad_connection.h" #include "../input_defines.h" #include "../../driver.h" +#include "../common/hid/hid_device_driver.h" struct wiiupro_buttons { diff --git a/input/connect/joypad_connection.h b/input/connect/joypad_connection.h index 67bc34bcb9..62a454609a 100644 --- a/input/connect/joypad_connection.h +++ b/input/connect/joypad_connection.h @@ -61,10 +61,9 @@ typedef struct pad_connection_interface void (*get_buttons)(void *data, input_bits_t *state); int16_t (*get_axis)(void *data, unsigned axis); const char* (*get_name)(void *data); + bool (*button)(void *data, uint16_t joykey); } pad_connection_interface_t; -typedef struct joypad_connection joypad_connection_t; - extern pad_connection_interface_t pad_connection_wii; extern pad_connection_interface_t pad_connection_wiiupro; extern pad_connection_interface_t pad_connection_ps3; diff --git a/input/drivers/wiiu_input.c b/input/drivers/wiiu_input.c index 35ae955f68..ba00ed971a 100644 --- a/input/drivers/wiiu_input.c +++ b/input/drivers/wiiu_input.c @@ -33,7 +33,11 @@ #include "wiiu_dbg.h" +#ifdef WIIU_HID +#define MAX_PADS 16 +#else #define MAX_PADS 5 +#endif static uint8_t keyboardChannel = 0x00; static bool keyboardState[RETROK_LAST] = { 0 }; diff --git a/input/drivers_hid/null_hid.c b/input/drivers_hid/null_hid.c index 85056ecbb1..44d1cc397b 100644 --- a/input/drivers_hid/null_hid.c +++ b/input/drivers_hid/null_hid.c @@ -18,6 +18,7 @@ #include "../input_defines.h" #include "../input_driver.h" +#include "../include/hid_driver.h" typedef struct null_hid { diff --git a/input/drivers_joypad/wiiu_joypad.c b/input/drivers_joypad/wiiu_joypad.c index 1f1b476439..5728a90c6d 100644 --- a/input/drivers_joypad/wiiu_joypad.c +++ b/input/drivers_joypad/wiiu_joypad.c @@ -14,11 +14,13 @@ * If not, see . */ -#include +#include "../../wiiu/input/wiiu_input.h" #include "wiiu_dbg.h" static input_device_driver_t *pad_drivers[MAX_USERS]; +extern pad_connection_listener_t wiiu_pad_connection_listener; + static bool ready = false; @@ -31,64 +33,30 @@ static int16_t wiiu_joypad_axis(unsigned pad, uint32_t axis); static void wiiu_joypad_poll(void); static const char *wiiu_joypad_name(unsigned pad); -/** - * Translates a pad to its appropriate driver. - * Note that this is a helper for build_pad_map and shouldn't be - * used directly. - */ -static input_device_driver_t *get_driver_for_pad(unsigned pad) -{ - if(wpad_driver.query_pad(pad)) - return &wpad_driver; - if(kpad_driver.query_pad(pad)) - return &kpad_driver; - -#ifdef WIIU_HID - return &hidpad_driver; -#else - return NULL; -#endif -} - -/** - * Populates the pad_driver array. We do this once at init time so - * that lookups at runtime are constant time. - */ -static void build_pad_map(void) -{ - unsigned i; - - for(i = 0; i < MAX_USERS; i++) - { - pad_drivers[i] = get_driver_for_pad(i); - } -} - static bool wiiu_joypad_init(void* data) { - /* the sub-drivers have to init first, otherwise - * build_pad_map will fail (because all lookups will return false). */ - wpad_driver.init(data); - kpad_driver.init(data); + set_connection_listener(&wiiu_pad_connection_listener); + hid_instance.pad_list = pad_connection_init(MAX_USERS); + hid_instance.max_slot = MAX_USERS; + + wpad_driver.init(data); + kpad_driver.init(data); #ifdef WIIU_HID - hidpad_driver.init(data); + hidpad_driver.init(data); #endif - build_pad_map(); + ready = true; + (void)data; - ready = true; - (void)data; - - return true; + return true; } static bool wiiu_joypad_query_pad(unsigned pad) { -#ifdef WIIU_HID - return ready && pad < MAX_USERS; -#else - return ready && pad < 5; -#endif + return ready && + pad < MAX_USERS && + pad_drivers[pad] != NULL && + pad_drivers[pad]->query_pad(pad); } static void wiiu_joypad_destroy(void) @@ -137,10 +105,17 @@ static void wiiu_joypad_poll(void) static const char* wiiu_joypad_name(unsigned pad) { - if(!wiiu_joypad_query_pad(pad)) - return "N/A"; + if(!wiiu_joypad_query_pad(pad)) + return "N/A"; - return pad_drivers[pad]->name(pad); + return pad_drivers[pad]->name(pad); +} + +static void wiiu_joypad_connection_listener(unsigned pad, + input_device_driver_t *driver) +{ + if(pad < MAX_USERS) + pad_drivers[pad] = driver; } input_device_driver_t wiiu_joypad = @@ -156,3 +131,8 @@ input_device_driver_t wiiu_joypad = wiiu_joypad_name, "wiiu", }; + +pad_connection_listener_t wiiu_pad_connection_listener = +{ + wiiu_joypad_connection_listener +}; diff --git a/input/include/gamepad.h b/input/include/gamepad.h new file mode 100644 index 0000000000..d895b55f25 --- /dev/null +++ b/input/include/gamepad.h @@ -0,0 +1,35 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2016-2017 - Andrés Suárez + * + * 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 GAMEPAD_H__ +#define GAMEPAD_H__ + +#include "../input_driver.h" + +struct pad_connection_listener_interface { + void (*connected)(unsigned port, input_device_driver_t *driver); +}; + +typedef struct _axis_data { + int32_t axis; + bool is_negative; +} axis_data; + +void gamepad_read_axis_data(uint32_t axis, axis_data *data); +int16_t gamepad_get_axis_value(int16_t state[3][2], axis_data *data); + +#endif /* GAMEPAD_H__ */ diff --git a/input/include/hid_driver.h b/input/include/hid_driver.h new file mode 100644 index 0000000000..02b6529f63 --- /dev/null +++ b/input/include/hid_driver.h @@ -0,0 +1,84 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2016-2017 - Andrés Suárez + * + * 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 HID_DRIVER_H__ +#define HID_DRIVER_H__ + +#include "../connect/joypad_connection.h" +#include "../input_driver.h" + +/* what is 1? */ +#define HID_REPORT_OUTPUT 2 +#define HID_REPORT_FEATURE 3 +/* are there more? */ + +/* + * This is the interface for the HID subsystem. + * + * The handle parameter is the pointer returned by init() and stores the implementation + * state data for the HID driver. + */ + +struct hid_driver +{ + void *(*init)(void); + bool (*query_pad)(void *handle, unsigned pad); + void (*free)(const void *handle); + bool (*button)(void *handle, unsigned pad, uint16_t button); + void (*get_buttons)(void *handle, unsigned pad, input_bits_t *state); + int16_t (*axis)(void *handle, unsigned pad, uint32_t axis); + void (*poll)(void *handle); + bool (*set_rumble)(void *handle, unsigned pad, enum retro_rumble_effect effect, uint16_t); + const char *(*name)(void *handle, unsigned pad); + const char *ident; + void (*send_control)(void *handle, uint8_t *buf, size_t size); + int32_t (*set_report)(void *handle, uint8_t, uint8_t, void *data, uint32_t size); + int32_t (*set_idle)(void *handle, uint8_t amount); + int32_t (*set_protocol)(void *handle, uint8_t protocol); + int32_t (*read)(void *handle, void *buf, size_t size); +}; + +#define HID_GET_BUTTONS(pad, state) hid_instance.os_driver->get_buttons( \ + hid_instance.os_driver_data, pad, state) +#define HID_BUTTON(pad, key) hid_instance.os_driver->button( \ + hid_instance.os_driver_data, pad, key) +#define HID_AXIS(pad, axis) hid_instance.os_driver->axis( \ + hid_instance.os_driver_data, pad, axis) +#define HID_PAD_NAME(pad) \ + hid_instance.os_driver->name(hid_instance.os_driver_data, pad) +#define HID_SET_PROTOCOL(pad, protocol) \ + hid_instance.os_driver->set_protocol(pad, protocol) +#define HID_SET_REPORT(pad, rpttype, rptid, data, len) \ + hid_instance.os_driver->set_report(pad, rpttype, rptid, data, len) +#define HID_SEND_CONTROL(pad, data, len) \ + hid_instance.os_driver->send_control(pad, data, len) +#define HID_POLL() hid_instance.os_driver->poll( \ + hid_instance.os_driver_data) +#define HID_MAX_SLOT() hid_instance.max_slot +#define HID_PAD_CONNECTION_PTR(slot) &(hid_instance.pad_list[(slot)]) + + + +struct hid_driver_instance { + hid_driver_t *os_driver; + void *os_driver_data; + input_device_driver_t *pad_driver; + joypad_connection_t *pad_list; + unsigned max_slot; +}; + +#endif /* HID_DRIVER_H__ */ diff --git a/input/include/hid_types.h b/input/include/hid_types.h new file mode 100644 index 0000000000..28734e9dd8 --- /dev/null +++ b/input/include/hid_types.h @@ -0,0 +1,24 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2016-2017 - Andrés Suárez + * + * 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 HID_TYPES_H__ +#define HID_TYPES_H__ + +typedef struct hid_driver hid_driver_t; +typedef struct hid_driver_instance hid_driver_instance_t; + +#endif /* HID_TYPES_H__ */ diff --git a/input/input_autodetect_builtin.c b/input/input_autodetect_builtin.c index 0e76b04f4e..db3443cd45 100644 --- a/input/input_autodetect_builtin.c +++ b/input/input_autodetect_builtin.c @@ -29,7 +29,7 @@ #endif #ifdef WIIU -#include +#include #endif #define DECL_BTN(btn, bind) "input_" #btn "_btn = " #bind "\n" @@ -228,6 +228,56 @@ DECL_AXIS(r_y_minus, +3) #ifdef WIIU +#define WIIUINPUT_GAMECUBE_DEFAULT_BINDS \ +DECL_BTN_EX(a, 8, "A") \ +DECL_BTN_EX(b, 0, "B") \ +DECL_BTN_EX(x, 9, "X") \ +DECL_BTN_EX(y, 1, "Y") \ +DECL_BTN_EX(left, 6, "D-Pad Left") \ +DECL_BTN_EX(right, 7, "D-Pad Right") \ +DECL_BTN_EX(down, 5, "D-Pad Down") \ +DECL_BTN_EX(up, 4, "D-Pad Up") \ +DECL_BTN_EX(start, 3, "Start/Pause") \ +DECL_BTN_EX(select, 2, "Z") \ +DECL_BTN_EX(r, 10, "R Trigger") \ +DECL_BTN_EX(l, 11, "L Trigger") \ +DECL_AXIS_EX(l_x_plus, +1, "Analog right") \ +DECL_AXIS_EX(l_x_minus, -1, "Analog left") \ +DECL_AXIS_EX(l_y_plus, +0, "Analog up") \ +DECL_AXIS_EX(l_y_minus, -0, "Analog down") \ +DECL_AXIS_EX(r_x_plus, +3, "C-stick right") \ +DECL_AXIS_EX(r_x_minus, -3, "C-stick left") \ +DECL_AXIS_EX(r_y_plus, +2, "C-stick up") \ +DECL_AXIS_EX(r_y_minus, -2, "C-stick down") + +#define WIIUINPUT_DS3_DEFAULT_BINDS \ +DECL_BTN_EX(menu_toggle, 16, "Playstation") \ +DECL_BTN_EX(select, 2, "Select") \ +DECL_BTN_EX(start, 3, "Start") \ +DECL_BTN_EX(a, 8, "Circle") \ +DECL_BTN_EX(y, 1, "Triangle") \ +DECL_BTN_EX(b, 0, "Cross") \ +DECL_BTN_EX(x, 9, "Square") \ +DECL_BTN_EX(r, 11, "R1") \ +DECL_BTN_EX(l, 10, "L1") \ +DECL_BTN_EX(r2, 13, "R2") \ +DECL_BTN_EX(l2, 12, "L2") \ +DECL_BTN_EX(up, 4, "D-Pad Up") \ +DECL_BTN_EX(down, 5, "D-Pad Down") \ +DECL_BTN_EX(left, 6, "D-Pad left") \ +DECL_BTN_EX(right, 7, "D-Pad Right") \ +DECL_BTN_EX(r3, 15, "R3") \ +DECL_BTN_EX(l3, 14, "L3") \ +DECL_AXIS_EX(l_x_plus, +1, "L Analog right") \ +DECL_AXIS_EX(l_x_minus, -1, "L Analog left") \ +DECL_AXIS_EX(l_y_plus, +0, "L Analog up") \ +DECL_AXIS_EX(l_y_minus, -0, "L Analog down") \ +DECL_AXIS_EX(r_x_plus, +3, "R Analog right") \ +DECL_AXIS_EX(r_x_minus, -3, "R Analog left") \ +DECL_AXIS_EX(r_y_plus, +2, "R Analog up") \ +DECL_AXIS_EX(r_y_minus, -2, "R Analog down") + + #define WIIUINPUT_GAMEPAD_DEFAULT_BINDS \ DECL_BTN_EX(menu_toggle, 1, "Home") \ DECL_BTN_EX(select, 2, "-") \ @@ -612,6 +662,8 @@ const char* const input_builtin_autoconfs[] = DECL_AUTOCONF_DEVICE(PAD_NAME_NUNCHUK, "wiiu", WIIUINPUT_NUNCHUK_DEFAULT_BINDS), DECL_AUTOCONF_DEVICE(PAD_NAME_CLASSIC, "wiiu", WIIUINPUT_CLASSIC_CONTROLLER_DEFAULT_BINDS), DECL_AUTOCONF_DEVICE(PAD_NAME_HID, "wiiu", WIIUINPUT_GAMEPAD_DEFAULT_BINDS), + DECL_AUTOCONF_DEVICE("GameCube Controller", "wiiu", WIIUINPUT_GAMECUBE_DEFAULT_BINDS), + DECL_AUTOCONF_DEVICE("Sony DualShock 3", "wiiu", WIIUINPUT_DS3_DEFAULT_BINDS), #endif #ifdef __CELLOS_LV2__ DECL_AUTOCONF_DEVICE("SixAxis Controller", "ps3", PS3INPUT_DEFAULT_BINDS), diff --git a/input/input_driver.c b/input/input_driver.c index 2f5255285b..742b34b1b9 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -58,6 +58,22 @@ #include "../verbosity.h" #include "../tasks/tasks_internal.h" #include "../command.h" +#include "include/gamepad.h" + +static pad_connection_listener_t *pad_connection_listener = NULL; + +void set_connection_listener(pad_connection_listener_t *listener) +{ + pad_connection_listener = listener; +} + +void fire_connection_listener(unsigned port, input_device_driver_t *driver) +{ + if(!pad_connection_listener) + return; + + pad_connection_listener->connected(port, driver); +} static const input_driver_t *input_drivers[] = { #ifdef __CELLOS_LV2__ @@ -1819,6 +1835,21 @@ bool input_mouse_button_raw(unsigned port, unsigned id) return false; } +void input_pad_connect(unsigned port, input_device_driver_t *driver) +{ + if(port >= MAX_USERS || !driver) + { + RARCH_ERR("[input]: input_pad_connect: bad parameters\n"); + return; + } + + fire_connection_listener(port, driver); + + if(!input_autoconfigure_connect(driver->name(port), NULL, driver->ident, + port, 0, 0)) + input_config_set_device_name(port, driver->name(port)); +} + /** * input_conv_analog_id_to_bind_id: * @idx : Analog key index. diff --git a/input/input_driver.h b/input/input_driver.h index 6ffc894387..f47e8830eb 100644 --- a/input/input_driver.h +++ b/input/input_driver.h @@ -22,6 +22,8 @@ #include #include +#include "input_types.h" + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -35,22 +37,12 @@ #include "input_defines.h" #include "../msg_hash.h" +#include "include/hid_types.h" +#include "include/hid_driver.h" +#include "include/gamepad.h" RETRO_BEGIN_DECLS -typedef struct -{ - uint32_t data[8]; - uint16_t analogs[8]; -} input_bits_t; - -typedef struct rarch_joypad_driver input_device_driver_t; - -typedef struct hid_driver hid_driver_t; - -/* Keyboard line reader. Handles textual input in a direct fashion. */ -typedef struct input_keyboard_line input_keyboard_line_t; - enum input_device_type { INPUT_DEVICE_TYPE_NONE = 0, @@ -127,14 +119,14 @@ struct retro_keybind char *joyaxis_label; }; -typedef struct rarch_joypad_info +struct rarch_joypad_info { uint16_t joy_idx; const struct retro_keybind *auto_binds; float axis_threshold; -} rarch_joypad_info_t; +}; -typedef struct input_driver +struct input_driver { /* Inits input driver. */ @@ -170,7 +162,7 @@ typedef struct input_driver const input_device_driver_t *(*get_sec_joypad_driver)(void *data); bool (*keyboard_mapping_is_blocked)(void *data); void (*keyboard_mapping_set_block)(void *data, bool value); -} input_driver_t; +}; struct rarch_joypad_driver { @@ -187,25 +179,6 @@ struct rarch_joypad_driver const char *ident; }; -struct hid_driver -{ - void *(*init)(void); - bool (*query_pad)(void *, unsigned); - void (*free)(const void *); - bool (*button)(void *, unsigned, uint16_t); - void (*get_buttons)(void *, unsigned, input_bits_t *); - int16_t (*axis)(void *, unsigned, uint32_t); - void (*poll)(void *); - bool (*set_rumble)(void *, unsigned, enum retro_rumble_effect, uint16_t); - const char *(*name)(void *, unsigned); - const char *ident; - void (*send_control)(void *data, uint8_t *buf, size_t size); - int32_t (*set_report)(void *, uint8_t, uint8_t, void *, uint32_t); - int32_t (*set_idle)(void *, uint8_t); - int32_t (*set_protocol)(void *, uint8_t); - -}; - /** * input_driver_find_handle: * @index : index of driver to get handle to. @@ -599,6 +572,15 @@ bool input_joypad_button_raw(const input_device_driver_t *driver, bool input_joypad_hat_raw(const input_device_driver_t *driver, unsigned joypad, unsigned hat_dir, unsigned hat); +/** + * input_pad_connect: + * @port : Joystick number. + * @driver : handle for joypad driver handling joystick's input + * + * Registers a newly connected pad with RetroArch. + **/ +void input_pad_connect(unsigned port, input_device_driver_t *driver); + /** * input_mouse_button_raw: * @port : Mouse number. @@ -627,6 +609,9 @@ const char *input_joypad_name(const input_device_driver_t *driver, bool input_config_get_bind_idx(unsigned port, unsigned *joy_idx_real); #ifdef HAVE_HID + +#include "include/hid_driver.h" + /** * hid_driver_find_handle: * @index : index of driver to get handle to. @@ -676,11 +661,11 @@ typedef void (*input_keyboard_line_complete_t)(void *userdata, typedef bool (*input_keyboard_press_t)(void *userdata, unsigned code); -typedef struct input_keyboard_ctx_wait +struct input_keyboard_ctx_wait { void *userdata; input_keyboard_press_t cb; -} input_keyboard_ctx_wait_t; +}; /** * input_keyboard_event: @@ -808,6 +793,9 @@ uint16_t input_config_get_vid(unsigned port); void input_config_reset(void); +void set_connection_listener(pad_connection_listener_t *listener); +void fire_connection_listener(unsigned port, input_device_driver_t *driver); + extern input_device_driver_t dinput_joypad; extern input_device_driver_t linuxraw_joypad; extern input_device_driver_t parport_joypad; @@ -856,7 +844,6 @@ extern hid_driver_t iohidmanager_hid; extern hid_driver_t btstack_hid; extern hid_driver_t libusb_hid; extern hid_driver_t wiiusb_hid; -extern hid_driver_t wiiu_hid; extern hid_driver_t null_hid; #endif diff --git a/input/input_types.h b/input/input_types.h new file mode 100644 index 0000000000..0bc64d8c42 --- /dev/null +++ b/input/input_types.h @@ -0,0 +1,32 @@ +/* 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 . + */ + +#ifndef __INPUT_TYPES__H +#define __INPUT_TYPES__H + +typedef struct rarch_joypad_driver input_device_driver_t; +typedef struct input_keyboard_line input_keyboard_line_t; +typedef struct rarch_joypad_info rarch_joypad_info_t; +typedef struct input_driver input_driver_t; +typedef struct input_keyboard_ctx_wait input_keyboard_ctx_wait_t; +typedef struct { + uint32_t data[8]; + uint16_t analogs[8]; +} input_bits_t; +typedef struct joypad_connection joypad_connection_t; +typedef struct pad_connection_listener_interface pad_connection_listener_t; + +#endif /* __INPUT_TYPES__H */ diff --git a/verbosity.c b/verbosity.c index 1465b59a4c..4fc7686f50 100644 --- a/verbosity.c +++ b/verbosity.c @@ -188,6 +188,37 @@ void RARCH_LOG_V(const char *tag, const char *fmt, va_list ap) #endif } +void RARCH_LOG_BUFFER(uint8_t *data, size_t size) +{ + int i, offset; + int padding = size % 16; + uint8_t buf[16]; + + RARCH_LOG("== %d-byte buffer ==================\n", size); + for(i = 0, offset = 0; i < size; i++) + { + buf[offset] = data[i]; + offset++; + + if(offset == 16) + { + offset = 0; + RARCH_LOG("%02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x%02x%02x%02x%02x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], + buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); + } + } + if(padding) + { + for(i = padding; i < 16; i++) + buf[i] = 0xff; + RARCH_LOG("%02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x%02x%02x%02x%02x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], + buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); + } + RARCH_LOG("==================================\n"); +} + void RARCH_LOG(const char *fmt, ...) { va_list ap; diff --git a/verbosity.h b/verbosity.h index 5f5cdcf30a..42ae964f58 100644 --- a/verbosity.h +++ b/verbosity.h @@ -86,7 +86,7 @@ void logger_send_v(const char *__format, va_list args); logger_send_v(fmt, vp); \ } while (0) -#else +#else /* IS_SALAMANDER */ #define RARCH_LOG(...) do { \ logger_send("" __VA_ARGS__); \ @@ -123,11 +123,14 @@ void logger_send_v(const char *__format, va_list args); logger_send("[OUTPUT] " tag); \ logger_send_v(fmt, vp); \ } while (0) - #endif -#else + +#define RARCH_LOG_BUFFER(...) do { } while(0) + +#else /* HAVE_LOGGER */ void RARCH_LOG_V(const char *tag, const char *fmt, va_list ap); void RARCH_LOG(const char *fmt, ...); +void RARCH_LOG_BUFFER(uint8_t *buffer, size_t size); void RARCH_LOG_OUTPUT(const char *msg, ...); void RARCH_WARN(const char *fmt, ...); void RARCH_ERR(const char *fmt, ...); @@ -135,7 +138,7 @@ void RARCH_ERR(const char *fmt, ...); #define RARCH_LOG_OUTPUT_V RARCH_LOG_V #define RARCH_WARN_V RARCH_LOG_V #define RARCH_ERR_V RARCH_LOG_V -#endif +#endif /* HAVE_LOGGER */ RETRO_END_DECLS diff --git a/wiiu/include/wiiu/os/atomic.h b/wiiu/include/wiiu/os/atomic.h new file mode 100644 index 0000000000..a5e5dfbcf4 --- /dev/null +++ b/wiiu/include/wiiu/os/atomic.h @@ -0,0 +1,14 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +uint8_t SwapAtomic8(uint8_t *ptr, uint8_t value); +uint16_t SwapAtomic16(uint16_t *ptr, uint16_t value); +uint32_t SwapAtomic32(uint32_t *ptr, uint32_t value); + +#ifdef __cplusplus +} +#endif diff --git a/wiiu/include/wiiu/pad_driver.h b/wiiu/include/wiiu/pad_driver.h deleted file mode 100644 index 376b899f43..0000000000 --- a/wiiu/include/wiiu/pad_driver.h +++ /dev/null @@ -1,183 +0,0 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2014-2017 - Ali Bouhlel - * 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 . - */ - -#ifndef __PAD_DRIVER__H -#define __PAD_DRIVER__H - -#ifdef HAVE_CONFIG_H -#include "../../config.h" -#endif /* HAVE_CONFIG_H */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../../input/input_driver.h" -#include "../../input/connect/joypad_connection.h" -#include "../../tasks/tasks_internal.h" -#include "../../retroarch.h" -#include "../../verbosity.h" -#include "../../command.h" -#include "../../gfx/video_driver.h" - -/** - * Magic button sequence that triggers an exit. Useful for if the visuals are - * corrupted, but won't work in the case of a hard lock. - */ -#define PANIC_BUTTON_MASK (VPAD_BUTTON_R | VPAD_BUTTON_L | VPAD_BUTTON_STICK_R | VPAD_BUTTON_STICK_L) - -/** - * Applies a standard transform to the Wii U gamepad's analog stick. - * No idea where 0x7ff0 comes from. - */ - -#define WIIU_ANALOG_FACTOR 0x7ff0 -#define WIIU_READ_STICK(stick) ((stick) * WIIU_ANALOG_FACTOR) - -/** - * the wiimote driver uses these to delimit which pads correspond to the - * wiimotes. - */ -#define PAD_GAMEPAD 0 -#define WIIU_WIIMOTE_CHANNELS 4 - -/** - * These are used by the wiimote driver to identify the wiimote configuration - * attached to the channel. - */ -/* wiimote with Wii U Pro controller */ -#define WIIMOTE_TYPE_PRO 0x1f -/* wiimote with Classic Controller */ -#define WIIMOTE_TYPE_CLASSIC 0x02 -/* wiimote with nunchuk */ -#define WIIMOTE_TYPE_NUNCHUK 0x01 -/* wiimote plus (no accessory attached) */ -#define WIIMOTE_TYPE_WIIPLUS 0x00 -/* wiimote not attached on this channel */ -#define WIIMOTE_TYPE_NONE 0xFD - -/** - * These are used to map pad names to controller mappings. You can - * change these relatively free-form. - */ - -#define PAD_NAME_WIIU_GAMEPAD "WiiU Gamepad" -#define PAD_NAME_WIIU_PRO "WiiU Pro Controller" -#define PAD_NAME_WIIMOTE "Wiimote Controller" -#define PAD_NAME_NUNCHUK "Wiimote+Nunchuk Controller" -#define PAD_NAME_CLASSIC "Classic Controller" -#define PAD_NAME_HID "HID Controller" - -/** - * The Wii U gamepad and wiimotes have 3 sets of x/y axes. The third - * is used by the gamepad for the touchpad driver; the wiimotes is - * currently unimplemented, but could be used for future IR pointer - * support. - */ -#define WIIU_DEVICE_INDEX_TOUCHPAD 2 - -typedef struct _axis_data axis_data; - -struct _axis_data { - int32_t axis; - bool is_negative; -}; - -typedef struct _wiiu_pad_functions wiiu_pad_functions_t; - -struct _wiiu_pad_functions { - int16_t (*get_axis_value)(int32_t axis, int16_t state[3][2], bool is_negative); - void (*set_axis_value)(int16_t state[3][2], int16_t left_x, int16_t left_y, - int16_t right_x, int16_t right_y, int16_t touch_x, int16_t touch_y); - void (*read_axis_data)(uint32_t axis, axis_data *data); - void (*connect)(unsigned pad, input_device_driver_t *driver); -}; - -/** - * HID driver data structures - */ - -typedef struct wiiu_hid { - /* used to register for HID notifications */ - HIDClient *client; - /* list of HID pads */ - joypad_connection_t *connections; - /* size of connections list */ - unsigned connections_size; - /* thread state data for HID polling thread */ - OSThread *polling_thread; - /* stack space for polling thread */ - void *polling_thread_stack; - /* watch variable to tell the polling thread to terminate */ - volatile bool polling_thread_quit; -} wiiu_hid_t; - -typedef struct wiiu_adapter wiiu_adapter_t; - -struct wiiu_adapter { - wiiu_adapter_t *next; - wiiu_hid_t *hid; - uint8_t state; - uint8_t *rx_buffer; - int32_t rx_size; - uint8_t *tx_buffer; - int32_t tx_size; - int32_t slot; - uint32_t handle; - uint8_t interface_index; -}; - -typedef struct wiiu_attach wiiu_attach_event; - -struct wiiu_attach { - wiiu_attach_event *next; - uint32_t type; - uint32_t handle; - uint16_t vendor_id; - uint16_t product_id; - uint8_t interface_index; - uint8_t is_keyboard; - uint8_t is_mouse; - uint16_t max_packet_size_rx; - uint16_t max_packet_size_tx; -}; - -typedef struct _wiiu_event_list wiiu_event_list; -typedef struct _wiiu_adapter_list wiiu_adapter_list; - -struct _wiiu_event_list { - OSFastMutex lock; - wiiu_attach_event *list; -}; - -struct _wiiu_adapter_list { - OSFastMutex lock; - wiiu_adapter_t *list; -}; - -extern wiiu_pad_functions_t pad_functions; -extern input_device_driver_t wiiu_joypad; -extern input_device_driver_t wpad_driver; -extern input_device_driver_t kpad_driver; -extern input_device_driver_t hidpad_driver; -extern hid_driver_t wiiu_hid; - -#endif /* __PAD_DRIVER__H */ diff --git a/wiiu/include/wiiu/pad_strings.h b/wiiu/include/wiiu/pad_strings.h new file mode 100644 index 0000000000..e28a9036e6 --- /dev/null +++ b/wiiu/include/wiiu/pad_strings.h @@ -0,0 +1,32 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2014-2017 - Ali Bouhlel + * 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 . + */ + +#ifndef __PAD_DRIVER__H +#define __PAD_DRIVER__H + +/** + * These are used to map pad names to controller mappings. You can + * change these relatively free-form. + */ + +#define PAD_NAME_WIIU_GAMEPAD "WiiU Gamepad" +#define PAD_NAME_WIIU_PRO "WiiU Pro Controller" +#define PAD_NAME_WIIMOTE "Wiimote Controller" +#define PAD_NAME_NUNCHUK "Wiimote+Nunchuk Controller" +#define PAD_NAME_CLASSIC "Classic Controller" +#define PAD_NAME_HID "HID Controller" + +#endif /* __PAD_DRIVER__H */ diff --git a/wiiu/include/wiiu/syshid.h b/wiiu/include/wiiu/syshid.h index 1f55718dd9..664e52eef6 100644 --- a/wiiu/include/wiiu/syshid.h +++ b/wiiu/include/wiiu/syshid.h @@ -1,15 +1,6 @@ #pragma once #include -/* - * Report types for the report_type parameter in HIDSetReport() - */ - -/* what is 1? */ -#define HID_REPORT_OUTPUT 2 -#define HID_REPORT_FEATURE 3 -/* are there more? */ - typedef struct { uint32_t handle; diff --git a/wiiu/input/hidpad_driver.c b/wiiu/input/hidpad_driver.c index 9bcbc8f3d0..03efe64d5c 100644 --- a/wiiu/input/hidpad_driver.c +++ b/wiiu/input/hidpad_driver.c @@ -14,7 +14,8 @@ * If not, see . */ -#include +#include "wiiu_input.h" +#include "wiiu_hid.h" static bool hidpad_init(void *data); static bool hidpad_query_pad(unsigned pad); @@ -27,51 +28,17 @@ static const char *hidpad_name(unsigned pad); static bool ready = false; -static wiiu_hid_t *hid_data; -static hid_driver_t *hid_driver; - -static unsigned to_slot(unsigned pad) +static bool init_hid_driver(void) { - return pad - (WIIU_WIIMOTE_CHANNELS+1); -} - -const void *get_hid_data(void) -{ - return hid_data; -} - -static hid_driver_t *init_hid_driver(void) -{ - joypad_connection_t *connections = NULL; - unsigned connections_size = MAX_USERS - (WIIU_WIIMOTE_CHANNELS+1); - - hid_data = (wiiu_hid_t *)wiiu_hid.init(); - connections = pad_connection_init(connections_size); - - if (!hid_data || !connections) - goto error; - - hid_data->connections = connections; - hid_data->connections_size = connections_size; - return &wiiu_hid; - -error: - if (connections) - free(connections); - if (hid_data) - { - wiiu_hid.free(hid_data); - hid_data = NULL; - } - return NULL; + return hid_init(&hid_instance, &wiiu_hid, &hidpad_driver, MAX_USERS); } static bool hidpad_init(void *data) { (void *)data; + int i; - hid_driver = init_hid_driver(); - if (!hid_driver) + if(!init_hid_driver()) { RARCH_ERR("Failed to initialize HID driver.\n"); return false; @@ -85,23 +52,14 @@ static bool hidpad_init(void *data) static bool hidpad_query_pad(unsigned pad) { - return ready && (pad > WIIU_WIIMOTE_CHANNELS && pad < MAX_USERS); + return ready && pad < MAX_USERS; } static void hidpad_destroy(void) { ready = false; - if(hid_driver) { - hid_driver->free(hid_data); - hid_data = NULL; - hid_driver = NULL; - } - - if(hid_data) { - free(hid_data); - hid_data = NULL; - } + hid_deinit(&hid_instance); } static bool hidpad_button(unsigned pad, uint16_t button) @@ -109,11 +67,7 @@ static bool hidpad_button(unsigned pad, uint16_t button) if (!hidpad_query_pad(pad)) return false; -#if 0 - return hid_driver->button(hid_data, to_slot(pad), button); -#else - return false; -#endif + return HID_BUTTON(pad, button); } static void hidpad_get_buttons(unsigned pad, input_bits_t *state) @@ -121,30 +75,21 @@ static void hidpad_get_buttons(unsigned pad, input_bits_t *state) if (!hidpad_query_pad(pad)) BIT256_CLEAR_ALL_PTR(state); -#if 0 - hid_driver->get_buttons(hid_data, to_slot(pad), state); -#endif - BIT256_CLEAR_ALL_PTR(state); + HID_GET_BUTTONS(pad, state); } static int16_t hidpad_axis(unsigned pad, uint32_t axis) { - if (!hidpad_query_pad(pad)); - return 0; + if (!hidpad_query_pad(pad)) + return 0; -#if 0 - return hid_driver->axis(hid_data, to_slot(pad), axis); -#else - return 0; -#endif + return HID_AXIS(pad, axis); } static void hidpad_poll(void) { -#if 0 if (ready) - hid_driver->poll(hid_data); -#endif + HID_POLL(); } static const char *hidpad_name(unsigned pad) @@ -152,11 +97,7 @@ static const char *hidpad_name(unsigned pad) if (!hidpad_query_pad(pad)) return "N/A"; -#if 1 - return PAD_NAME_HID; -#else - return hid_driver->name(hid_data, to_slot(pad)); -#endif + return HID_PAD_NAME(pad); } input_device_driver_t hidpad_driver = diff --git a/wiiu/input/kpad_driver.c b/wiiu/input/kpad_driver.c index de6efe7f64..a3c7078357 100644 --- a/wiiu/input/kpad_driver.c +++ b/wiiu/input/kpad_driver.c @@ -20,7 +20,7 @@ * controllers. */ -#include +#include "wiiu_input.h" static bool kpad_init(void *data); static bool kpad_query_pad(unsigned pad); @@ -30,6 +30,7 @@ static void kpad_get_buttons(unsigned pad, input_bits_t *state); static int16_t kpad_axis(unsigned pad, uint32_t axis); static void kpad_poll(void); static const char *kpad_name(unsigned pad); +static void kpad_deregister(unsigned channel); typedef struct _wiimote_state wiimote_state; @@ -52,17 +53,30 @@ wiimote_state wiimotes[WIIU_WIIMOTE_CHANNELS] = { { 0, {{0,0},{0,0},{0,0}}, WIIMOTE_TYPE_NONE }, }; -static unsigned to_wiimote_channel(unsigned pad) -{ - if (pad == PAD_GAMEPAD || pad > WIIU_WIIMOTE_CHANNELS) - return 0xffffffff; +static int channel_slot_map[] = { -1, -1, -1, -1 }; - return pad-1; +static int to_wiimote_channel(unsigned pad) +{ + int i; + + for(i = 0; i < WIIU_WIIMOTE_CHANNELS; i++) + if(channel_slot_map[i] == pad) + return i; + + return -1; } -static unsigned to_retro_pad(unsigned channel) +static int get_slot_for_channel(unsigned channel) { - return channel+1; + int slot = pad_connection_find_vacant_pad(hid_instance.pad_list); + if(slot >= 0) + { + RARCH_LOG("[kpad]: got slot %d\n", slot); + channel_slot_map[channel] = slot; + hid_instance.pad_list[slot].connected = true; + } + + return slot; } static bool kpad_init(void *data) @@ -75,7 +89,7 @@ static bool kpad_init(void *data) static bool kpad_query_pad(unsigned pad) { - return ready && pad <= WIIU_WIIMOTE_CHANNELS && pad > PAD_GAMEPAD; + return ready && pad < MAX_USERS; } static void kpad_destroy(void) @@ -88,27 +102,34 @@ static bool kpad_button(unsigned pad, uint16_t button_bit) if (!kpad_query_pad(pad)) return false; - return wiimotes[to_wiimote_channel(pad)].button_state + int channel = to_wiimote_channel(pad); + if(channel < 0) + return false; + + return wiimotes[channel].button_state & (UINT64_C(1) << button_bit); } static void kpad_get_buttons(unsigned pad, input_bits_t *state) { - if (!kpad_query_pad(pad)) + int channel = to_wiimote_channel(pad); + + if (!kpad_query_pad(pad) || channel < 0) BIT256_CLEAR_ALL_PTR(state); else - BITS_COPY16_PTR(state, wiimotes[to_wiimote_channel(pad)].button_state); + BITS_COPY16_PTR(state, wiimotes[channel].button_state); } static int16_t kpad_axis(unsigned pad, uint32_t axis) { + int channel = to_wiimote_channel(pad); axis_data data; - if (!kpad_query_pad(pad) || axis == AXIS_NONE) + if (!kpad_query_pad(pad) || channel < 0 || axis == AXIS_NONE) return 0; pad_functions.read_axis_data(axis, &data); return pad_functions.get_axis_value(data.axis, - wiimotes[to_wiimote_channel(pad)].analog_state, + wiimotes[channel].analog_state, data.is_negative); } @@ -116,8 +137,19 @@ static void kpad_register(unsigned channel, uint8_t device_type) { if (wiimotes[channel].type != device_type) { + int slot; + + kpad_deregister(channel); + slot = get_slot_for_channel(channel); + + if(slot < 0) + { + RARCH_ERR("Couldn't get a slot for this remote.\n"); + return; + } + wiimotes[channel].type = device_type; - pad_functions.connect(to_retro_pad(channel), &kpad_driver); + input_pad_connect(slot, &kpad_driver); } } @@ -161,6 +193,21 @@ static void kpad_poll_one_channel(unsigned channel, KPADData *kpad) } } +static void kpad_deregister(unsigned channel) +{ + int slot = channel_slot_map[channel]; + + if(slot >= 0) + { + input_autoconfigure_disconnect(slot, kpad_driver.name(slot)); + wiimotes[channel].type = WIIMOTE_TYPE_NONE; + hid_instance.pad_list[slot].connected = false; + channel_slot_map[channel] = -1; + } +} + +static int poll_failures[WIIU_WIIMOTE_CHANNELS] = { 0, 0, 0, 0 }; + static void kpad_poll(void) { unsigned channel; @@ -172,9 +219,16 @@ static void kpad_poll(void) memset(&kpad, 0, sizeof(kpad)); result = KPADRead(channel, &kpad, 1); + /* this is a hack to prevent spurious disconnects */ + /* TODO: use KPADSetConnectCallback and use callbacks to detect */ + /* pad disconnects properly. */ if (result == 0) { + poll_failures[channel]++; + if(poll_failures[channel] > 5) + kpad_deregister(channel); continue; } + poll_failures[channel] = 0; kpad_poll_one_channel(channel, &kpad); } @@ -182,11 +236,11 @@ static void kpad_poll(void) static const char *kpad_name(unsigned pad) { - pad = to_wiimote_channel(pad); - if (pad >= WIIU_WIIMOTE_CHANNELS) + int channel = to_wiimote_channel(pad); + if (channel < 0) return "unknown"; - switch(wiimotes[pad].type) + switch(wiimotes[channel].type) { case WIIMOTE_TYPE_PRO: return PAD_NAME_WIIU_PRO; diff --git a/wiiu/input/pad_functions.c b/wiiu/input/pad_functions.c index fe352e5da4..5aefbaf23c 100644 --- a/wiiu/input/pad_functions.c +++ b/wiiu/input/pad_functions.c @@ -14,7 +14,7 @@ * If not, see . */ -#include +#include "wiiu_input.h" enum wiiu_pad_axes { AXIS_LEFT_ANALOG_X, @@ -75,29 +75,8 @@ void wiiu_pad_set_axis_value(int16_t state[3][2], int16_t left_x, int16_t left_y state[WIIU_DEVICE_INDEX_TOUCHPAD][RETRO_DEVICE_ID_ANALOG_Y] = touch_y; } - -void wiiu_pad_read_axis_data(uint32_t axis, axis_data *data) -{ - data->axis = AXIS_POS_GET(axis); - data->is_negative = false; - - if(data->axis >= AXIS_INVALID) - { - data->axis = AXIS_NEG_GET(axis); - data->is_negative = true; - } -} - -void wiiu_pad_connect(unsigned pad, input_device_driver_t *driver) -{ - if(!input_autoconfigure_connect(driver->name(pad), NULL, driver->ident, - pad, VID_NONE, PID_NONE)) - input_config_set_device_name(pad, driver->name(pad)); -} - wiiu_pad_functions_t pad_functions = { wiiu_pad_get_axis_value, wiiu_pad_set_axis_value, - wiiu_pad_read_axis_data, - wiiu_pad_connect + gamepad_read_axis_data, }; diff --git a/wiiu/input/wiiu_hid.c b/wiiu/input/wiiu_hid.c index d0becc716e..962e9323d8 100644 --- a/wiiu/input/wiiu_hid.c +++ b/wiiu/input/wiiu_hid.c @@ -15,65 +15,82 @@ */ #include "wiiu_hid.h" +#include static wiiu_event_list events; static wiiu_adapter_list adapters; +static void report_hid_error(const char *msg, wiiu_adapter_t *adapter, int32_t error); + static bool wiiu_hid_joypad_query(void *data, unsigned slot) { wiiu_hid_t *hid = (wiiu_hid_t *)data; if (!hid) return false; - return slot < hid->connections_size; + return slot < HID_MAX_SLOT(); +} + +static joypad_connection_t *get_pad(wiiu_hid_t *hid, unsigned slot) +{ + if(!wiiu_hid_joypad_query(hid, slot)) + return NULL; + + joypad_connection_t *result = HID_PAD_CONNECTION_PTR(slot); + if(!result || !result->connected || !result->iface || !result->data) + return NULL; + + return result; } static const char *wiiu_hid_joypad_name(void *data, unsigned slot) { - if (!wiiu_hid_joypad_query(data, slot)) + joypad_connection_t *pad = get_pad((wiiu_hid_t *)data, slot); + + if(!pad) return NULL; - wiiu_hid_t *hid = (wiiu_hid_t *)data; - - return hid->connections[slot].iface->get_name(data); + return pad->iface->get_name(pad->data); } -static void wiiu_hid_joypad_get_buttons(void *data, unsigned port, - input_bits_t *state) +static void wiiu_hid_joypad_get_buttons(void *data, unsigned slot, input_bits_t *state) { - (void)data; - (void)port; + joypad_connection_t *pad = get_pad((wiiu_hid_t *)data, slot); - BIT256_CLEAR_ALL_PTR(state); + if(pad) + pad->iface->get_buttons(pad->data, state); } -static bool wiiu_hid_joypad_button(void *data, unsigned port, uint16_t joykey) +static bool wiiu_hid_joypad_button(void *data, unsigned slot, uint16_t joykey) { - (void)data; - (void)port; - (void)joykey; + joypad_connection_t *pad = get_pad((wiiu_hid_t *)data, slot); - return false; + if(!pad) + return false; + + return pad->iface->button(pad->data, joykey); } -static bool wiiu_hid_joypad_rumble(void *data, unsigned pad, +static bool wiiu_hid_joypad_rumble(void *data, unsigned slot, enum retro_rumble_effect effect, uint16_t strength) { - (void)data; - (void)pad; - (void)effect; - (void)strength; + joypad_connection_t *pad = get_pad((wiiu_hid_t *)data, slot); + if(!pad) + return false; + + pad->iface->set_rumble(pad->data, effect, strength); return false; } -static int16_t wiiu_hid_joypad_axis(void *data, unsigned port, uint32_t joyaxis) +static int16_t wiiu_hid_joypad_axis(void *data, unsigned slot, uint32_t joyaxis) { - (void)data; - (void)port; - (void)joyaxis; + joypad_connection_t *pad = get_pad((wiiu_hid_t *)data, slot); - return 0; + if(!pad) + return 0; + + return pad->iface->get_axis(pad->data, joyaxis); } static void *wiiu_hid_init(void) @@ -90,7 +107,6 @@ static void *wiiu_hid_init(void) if (!hid->polling_thread) goto error; - RARCH_LOG("[hid]: Registering HIDClient\n"); HIDAddClient(client, wiiu_attach_callback); hid->client = client; @@ -99,6 +115,7 @@ static void *wiiu_hid_init(void) error: RARCH_LOG("[hid]: initialization failed. cleaning up.\n"); + stop_polling_thread(hid); delete_hid(hid); delete_hidclient(client); @@ -130,7 +147,11 @@ static void wiiu_hid_free(const void *data) static void wiiu_hid_poll(void *data) { - (void)data; + wiiu_hid_t *hid = (wiiu_hid_t *)data; + if(hid == NULL) + return; + + synchronized_process_adapters(hid); } static void wiiu_hid_send_control(void *data, uint8_t *buf, size_t size) @@ -163,14 +184,17 @@ static int32_t wiiu_hid_set_report(void *data, uint8_t report_type, uint8_t report_id, void *report_data, uint32_t report_length) { wiiu_adapter_t *adapter = (wiiu_adapter_t *)data; - if (!adapter) + if (!adapter || report_length > adapter->tx_size) return -1; + memset(adapter->tx_buffer, 0, adapter->tx_size); + memcpy(adapter->tx_buffer, report_data, report_length); + return HIDSetReport(adapter->handle, report_type, report_id, - report_data, - report_length, + adapter->tx_buffer, + adapter->tx_size, NULL, NULL); } @@ -198,6 +222,25 @@ static int32_t wiiu_hid_set_protocol(void *data, uint8_t protocol) NULL, NULL); } +static int32_t wiiu_hid_read(void *data, void *buffer, size_t size) +{ + wiiu_adapter_t *adapter = (wiiu_adapter_t *)data; + int32_t result; + + if(!adapter) + return -1; + + if(size > adapter->rx_size) + return -1; + + result = HIDRead(adapter->handle, buffer, size, NULL, NULL); + if(result < 0) + report_hid_error("read failed", adapter, result); + + return result; +} + + static void start_polling_thread(wiiu_hid_t *hid) { OSThreadAttributes attributes = OS_THREAD_ATTRIB_AFFINITY_CPU2; @@ -286,23 +329,88 @@ static void log_device(HIDDevice *device) } +static uint8_t try_init_driver(wiiu_adapter_t *adapter) +{ + adapter->driver_handle = adapter->driver->init(adapter); + if(adapter->driver_handle == NULL) { + RARCH_ERR("[hid]: Failed to initialize driver: %s\n", + adapter->driver->name); + return ADAPTER_STATE_DONE; + } + + return ADAPTER_STATE_READY; +} + +static void synchronized_process_adapters(wiiu_hid_t *hid) +{ + wiiu_adapter_t *adapter = NULL; + wiiu_adapter_t *prev = NULL, *adapter_next = NULL; + bool keep_prev = false; + + OSFastMutex_Lock(&(adapters.lock)); + for(adapter = adapters.list; adapter != NULL; adapter = adapter_next) + { + adapter_next = adapter->next; + + switch(adapter->state) + { + case ADAPTER_STATE_NEW: + adapter->state = try_init_driver(adapter); + break; + case ADAPTER_STATE_READY: + case ADAPTER_STATE_READING: + case ADAPTER_STATE_DONE: + break; + case ADAPTER_STATE_GC: + /* remove from the list */ + if(prev == NULL) + adapters.list = adapter->next; + else + prev->next = adapter->next; + + /* adapter is no longer valid after this point */ + delete_adapter(adapter); + /* signal not to update prev ptr since adapter is now invalid */ + keep_prev = true; + break; + default: + RARCH_ERR("[hid]: Invalid adapter state: %d\n", adapter->state); + break; + } + prev = keep_prev ? prev : adapter; + keep_prev = false; + } + OSFastMutex_Unlock(&(adapters.lock)); +} + static void synchronized_add_event(wiiu_attach_event *event) { - OSFastMutex_Lock(&(events.lock)); - event->next = events.list; - events.list = event; - OSFastMutex_Unlock(&(events.lock)); + wiiu_attach_event *head = (wiiu_attach_event *)SwapAtomic32((uint32_t *)&events.list, 0); + + event->next = head; + head = event; + + SwapAtomic32((uint32_t *)&events.list, (uint32_t)head); } static wiiu_attach_event *synchronized_get_events_list(void) { - wiiu_attach_event *list; - OSFastMutex_Lock(&(events.lock)); - list = events.list; - events.list = NULL; - OSFastMutex_Unlock(&(events.lock)); + return (wiiu_attach_event *)SwapAtomic32((uint32_t *)&events.list, 0); +} - return list; +static wiiu_adapter_t *synchronized_lookup_adapter(uint32_t handle) +{ + OSFastMutex_Lock(&(adapters.lock)); + wiiu_adapter_t *iterator; + + for(iterator = adapters.list; iterator != NULL; iterator = iterator->next) + { + if(iterator->handle == handle) + break; + } + OSFastMutex_Unlock(&(adapters.lock)); + + return iterator; } static void synchronized_add_to_adapters_list(wiiu_adapter_t *adapter) @@ -318,25 +426,24 @@ static int32_t wiiu_attach_callback(HIDClient *client, { wiiu_attach_event *event = NULL; - log_device(device); - - switch(attach) - { - case HID_DEVICE_ATTACH: - case HID_DEVICE_DETACH: - if (device) - event = new_attach_event(device); - - if(!event) - goto error; - - event->type = attach; - synchronized_add_event(event); - return DEVICE_USED; - default: - break; + if(attach) { + RARCH_LOG("[hid]: Device attach event generated.\n"); + log_device(device); + } else { + RARCH_LOG("[hid]: Device detach event generated.\n"); } + if (device) + event = new_attach_event(device); + + if(!event) + goto error; + + event->type = attach; + synchronized_add_event(event); + + return DEVICE_USED; + error: delete_attach_event(event); return DEVICE_UNUSED; @@ -344,6 +451,13 @@ error: static void wiiu_hid_detach(wiiu_hid_t *hid, wiiu_attach_event *event) { + wiiu_adapter_t *adapter = synchronized_lookup_adapter(event->handle); + + /* this will signal the read loop to stop for this adapter + * the read loop method will update this to ADAPTER_STATE_GC + * so the adapter poll method can clean it up. */ + if(adapter) + adapter->connected = false; } @@ -358,127 +472,92 @@ static void wiiu_hid_attach(wiiu_hid_t *hid, wiiu_attach_event *event) } adapter->hid = hid; - adapter->slot = pad_connection_pad_init(hid->connections, - "hid", event->vendor_id, event->product_id, adapter, - &wiiu_hid); + adapter->driver = event->driver; + adapter->state = ADAPTER_STATE_NEW; - if(adapter->slot < 0) - { - RARCH_ERR("[hid]: No available slots.\n"); - goto error; - } - - RARCH_LOG("[hid]: got slot %d\n", adapter->slot); - - if(!pad_connection_has_interface(hid->connections, adapter->slot)) - { - RARCH_ERR("[hid]: Interface not found for HID device with vid=0x%04x pid=0x%04x\n", - event->vendor_id, event->product_id); - goto error; - } - - RARCH_LOG("[hid]: adding to adapter list\n"); synchronized_add_to_adapters_list(adapter); - RARCH_LOG("[hid]: starting read loop\n"); - wiiu_start_read_loop(adapter); - return; error: delete_adapter(adapter); } -void wiiu_start_read_loop(wiiu_adapter_t *adapter) -{ - adapter->state = ADAPTER_STATE_READING; -#if 0 - RARCH_LOG("HIDRead(0x%08x, 0x%08x, %d, 0x%08x, 0x%08x)\n", - adapter->handle, adapter->rx_buffer, adapter->rx_size, - wiiu_hid_read_loop_callback, adapter); -#endif - HIDRead(adapter->handle, adapter->rx_buffer, adapter->rx_size, wiiu_hid_read_loop_callback, adapter); -} - -/** - * Takes a buffer and formats it for the log file, 16 bytes per line. - * - * When the end of the buffer is reached, it is padded out with 0xff. So e.g. - * a 5-byte buffer might look like: - * - * 5 bytes read fro HIDRead: - * 0102030405ffffff ffffffffffffffff - * ================================== - */ -static void log_buffer(uint8_t *data, uint32_t len) -{ - int i, o; - int padding = len % 16; - uint8_t buf[16]; - - (uint8_t *)data; - (uint32_t)len; - - RARCH_LOG("%d bytes read from HIDRead:\n", len); - - for(i = 0, o = 0; i < len; i++) - { - buf[o] = data[i]; - o++; - if(o == 16) - { - o = 0; - RARCH_LOG("%02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x%02x%02x%02x%02x\n", - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], - buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); - } - } - - if(padding) - { - for(i = padding; i < 16; i++) - buf[i] = 0xff; - - RARCH_LOG("%02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x%02x%02x%02x%02x\n", - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], - buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); - } - RARCH_LOG("==================================\n"); - -} - -static void wiiu_hid_do_read(wiiu_adapter_t *adapter, - uint8_t *data, uint32_t length) -{ -#if 0 - log_buffer(data, length); -#endif - - /* TODO: get this data to the connect_xxx driver somehow. */ -} - static void wiiu_hid_read_loop_callback(uint32_t handle, int32_t error, uint8_t *buffer, uint32_t buffer_size, void *userdata) { - wiiu_adapter_t *adapter = (wiiu_adapter_t *)userdata; - if(!adapter) - { - RARCH_ERR("read_loop_callback: bad userdata\n"); - return; - } + wiiu_adapter_t *adapter = (wiiu_adapter_t *)userdata; + if(!adapter) + { + RARCH_ERR("read_loop_callback: bad userdata\n"); + return; + } - if(adapter->hid->polling_thread_quit) - { - RARCH_LOG("Shutting down read loop for slot %d\n", adapter->slot); - adapter->state = ADAPTER_STATE_DONE; - return; - } + if(error < 0) + { + report_hid_error("async read failed", adapter, error); + } - wiiu_hid_do_read(adapter, buffer, buffer_size); + if(adapter->state == ADAPTER_STATE_READING) { + adapter->state = ADAPTER_STATE_READY; - adapter->state = ADAPTER_STATE_READING; - HIDRead(adapter->handle, adapter->rx_buffer, adapter->rx_size, - wiiu_hid_read_loop_callback, adapter); + if(error == 0) { + adapter->driver->handle_packet(adapter->driver_handle, + buffer, buffer_size); + } + } +} + +static void report_hid_error(const char *msg, wiiu_adapter_t *adapter, int32_t error) +{ + if(error >= 0) + return; + + int16_t hid_error_code = error & 0xffff; + int16_t error_category = (error >> 16) & 0xffff; + const char *device = (adapter && adapter->driver) ? adapter->driver->name : "unknown"; + + switch(hid_error_code) + { + case -100: + RARCH_ERR("[hid]: Invalid RM command (%s)\n", device); + break; + case -102: + RARCH_ERR("[hid]: Invalid IOCTL command (%s)\n", device); + break; + case -103: + RARCH_ERR("[hid]: bad vector count (%s)\n", device); + break; + case -104: + RARCH_ERR("[hid]: invalid memory bank (%s)\n", device); + break; + case -105: + RARCH_ERR("[hid]: invalid memory alignment (%s)\n", device); + break; + case -106: + RARCH_ERR("[hid]: invalid data size (%s)\n", device); + break; + case -107: + RARCH_ERR("[hid]: request cancelled (%s)\n", device); + break; + case -108: + RARCH_ERR("[hid]: request timed out (%s)\n", device); + break; + case -109: + RARCH_ERR("[hid]: request aborted (%s)\n", device); + break; + case -110: + RARCH_ERR("[hid]: client priority error (%s)\n", device); + break; + case -111: + RARCH_ERR("[hid]: invalid device handle (%s)\n", device); + break; +#if 0 + default: + RARCH_ERR("[hid]: Unknown error (%d:%d: %s)\n", + error_category, hid_error_code, device); +#endif + } } /** @@ -492,27 +571,29 @@ static void wiiu_hid_polling_thread_cleanup(OSThread *thread, void *stack) RARCH_LOG("Waiting for in-flight reads to finish.\n"); + /* We don't need to protect the adapter list here because nothing else + will access it during this method (the HID system is shut down, and + the only other access is the polling thread that just stopped */ do { - OSFastMutex_Lock(&(adapters.lock)); incomplete = 0; for(adapter = adapters.list; adapter != NULL; adapter = adapter->next) { if(adapter->state == ADAPTER_STATE_READING) incomplete++; } - /* We are clear for shutdown. Clean up the list - * while we are holding the lock. */ + if(incomplete == 0) { + RARCH_LOG("All in-flight reads complete.\n"); while(adapters.list != NULL) { + RARCH_LOG("[hid]: shutting down adapter..\n"); adapter = adapters.list; adapters.list = adapter->next; delete_adapter(adapter); } } - OSFastMutex_Unlock(&(adapters.lock)); if(incomplete) usleep(5000); @@ -522,9 +603,7 @@ static void wiiu_hid_polling_thread_cleanup(OSThread *thread, void *stack) RARCH_WARN("[hid]: timed out waiting for in-flight read to finish.\n"); incomplete = 0; } - }while(incomplete); - - RARCH_LOG("All in-flight reads complete.\n"); + } while(incomplete); } static void wiiu_handle_attach_events(wiiu_hid_t *hid, wiiu_attach_event *list) @@ -545,20 +624,45 @@ static void wiiu_handle_attach_events(wiiu_hid_t *hid, wiiu_attach_event *list) } } +static void wiiu_poll_adapter(wiiu_adapter_t *adapter) +{ + if(!adapter->connected) { + adapter->state = ADAPTER_STATE_DONE; + return; + } + + adapter->state = ADAPTER_STATE_READING; + HIDRead(adapter->handle, adapter->rx_buffer, adapter->rx_size, + wiiu_hid_read_loop_callback, adapter); +} + +static void wiiu_poll_adapters(wiiu_hid_t *hid) +{ + wiiu_adapter_t *it; + OSFastMutex_Lock(&(adapters.lock)); + + for(it = adapters.list; it != NULL; it = it->next) + { + if(it->state == ADAPTER_STATE_READY) + wiiu_poll_adapter(it); + + if(it->state == ADAPTER_STATE_DONE) + it->state = ADAPTER_STATE_GC; + } + + OSFastMutex_Unlock(&(adapters.lock)); +} + static int wiiu_hid_polling_thread(int argc, const char **argv) { wiiu_hid_t *hid = (wiiu_hid_t *)argv; - int i = 0; RARCH_LOG("[hid]: polling thread is starting\n"); while(!hid->polling_thread_quit) { wiiu_handle_attach_events(hid, synchronized_get_events_list()); - usleep(10000); - i += 10000; - if(i >= (1000 * 1000 * 3)) - i = 0; + wiiu_poll_adapters(hid); } RARCH_LOG("[hid]: polling thread is stopping\n"); @@ -613,6 +717,13 @@ static void delete_hidclient(HIDClient *client) free(client); } +static void init_cachealigned_buffer(int32_t min_size, uint8_t **out_buf_ptr, int32_t *actual_size) +{ + *actual_size = (min_size + 0x3f) & ~0x3f; + + *out_buf_ptr = alloc_zeroed(64, *actual_size); +} + static wiiu_adapter_t *new_adapter(wiiu_attach_event *event) { wiiu_adapter_t *adapter = alloc_zeroed(32, sizeof(wiiu_adapter_t)); @@ -622,10 +733,9 @@ static wiiu_adapter_t *new_adapter(wiiu_attach_event *event) adapter->handle = event->handle; adapter->interface_index = event->interface_index; - adapter->rx_size = event->max_packet_size_rx; - adapter->rx_buffer = alloc_zeroed(32, adapter->rx_size); - adapter->tx_size = event->max_packet_size_tx; - adapter->tx_buffer = alloc_zeroed(32, adapter->tx_size); + init_cachealigned_buffer(event->max_packet_size_rx, &adapter->rx_buffer, &adapter->rx_size); + init_cachealigned_buffer(event->max_packet_size_tx, &adapter->tx_buffer, &adapter->tx_size); + adapter->connected = true; return adapter; } @@ -645,15 +755,29 @@ static void delete_adapter(wiiu_adapter_t *adapter) free(adapter->tx_buffer); adapter->tx_buffer = NULL; } + if(adapter->driver && adapter->driver_handle) { + adapter->driver->free(adapter->driver_handle); + adapter->driver_handle = NULL; + adapter->driver = NULL; + } + free(adapter); } static wiiu_attach_event *new_attach_event(HIDDevice *device) { + hid_device_t *driver = hid_device_driver_lookup(device->vid, device->pid); + if(!driver) + { + RARCH_ERR("[hid]: Failed to locate driver for device vid=%04x pid=%04x\n", + device->vid, device->pid); + return NULL; + } wiiu_attach_event *event = alloc_zeroed(4, sizeof(wiiu_attach_event)); if(!event) return NULL; + event->driver = driver; event->handle = device->handle; event->vendor_id = device->vid; event->product_id = device->pid; @@ -699,6 +823,7 @@ hid_driver_t wiiu_hid = { wiiu_hid_send_control, wiiu_hid_set_report, wiiu_hid_set_idle, - wiiu_hid_set_protocol + wiiu_hid_set_protocol, + wiiu_hid_read, }; diff --git a/wiiu/input/wiiu_hid.h b/wiiu/input/wiiu_hid.h index 1c2ede6f73..0c96c9599c 100644 --- a/wiiu/input/wiiu_hid.h +++ b/wiiu/input/wiiu_hid.h @@ -17,14 +17,89 @@ #ifndef __WIIU_HID__H #define __WIIU_HID__H -#include +#include "wiiu_hid_types.h" + +#include "wiiu_input.h" #define DEVICE_UNUSED 0 #define DEVICE_USED 1 +/* Adapter has been detected and needs to be initialized */ #define ADAPTER_STATE_NEW 0 -#define ADAPTER_STATE_READING 1 -#define ADAPTER_STATE_DONE 2 +/* Adapter has been initialized successfully */ +#define ADAPTER_STATE_READY 1 +/* The read loop has been started */ +#define ADAPTER_STATE_READING 2 +/* The read loop is shutting down */ +#define ADAPTER_STATE_DONE 3 +/* The read loop has fully stopped and the adapter can be freed */ +#define ADAPTER_STATE_GC 4 + +struct wiiu_hid { + /* used to register for HID notifications */ + HIDClient *client; + /* thread state data for the HID input polling thread */ + OSThread *polling_thread; + /* stack space for polling thread */ + void *polling_thread_stack; + /* watch variable for telling the polling thread to terminate */ + volatile bool polling_thread_quit; +}; + +/** + * Each HID device attached to the WiiU gets its own adapter, which + * connects the HID subsystem with the HID device driver. + */ +struct wiiu_adapter { + wiiu_adapter_t *next; + hid_device_t *driver; + void *driver_handle; + wiiu_hid_t *hid; + uint8_t state; + uint8_t *rx_buffer; + int32_t rx_size; + uint8_t *tx_buffer; + int32_t tx_size; + uint32_t handle; + uint8_t interface_index; + bool connected; +}; + +/** + * When a HID device is connected, the OS generates an attach + * event; the attach event handler translate them into these + * structures. + */ +struct wiiu_attach { + wiiu_attach_event *next; + hid_device_t *driver; + uint32_t type; + uint32_t handle; + uint16_t vendor_id; + uint16_t product_id; + uint8_t interface_index; + uint8_t is_keyboard; + uint8_t is_mouse; + uint16_t max_packet_size_rx; + uint16_t max_packet_size_tx; +}; + +struct _wiiu_event_list { + OSFastMutex lock; + wiiu_attach_event *list; +}; + +struct _wiiu_adapter_list { + OSFastMutex lock; + wiiu_adapter_t *list; +}; + +extern wiiu_pad_functions_t pad_functions; +extern input_device_driver_t wiiu_joypad; +extern input_device_driver_t wpad_driver; +extern input_device_driver_t kpad_driver; +extern input_device_driver_t hidpad_driver; +extern hid_driver_t wiiu_hid; static void *alloc_zeroed(size_t alignment, size_t size); static OSThread *new_thread(void); @@ -46,7 +121,9 @@ static wiiu_attach_event *synchronized_get_events_list(void); static void wiiu_handle_attach_events(wiiu_hid_t *hid, wiiu_attach_event *list); static void wiiu_hid_attach(wiiu_hid_t *hid, wiiu_attach_event *event); static void wiiu_hid_detach(wiiu_hid_t *hid, wiiu_attach_event *event); +static void synchronized_process_adapters(wiiu_hid_t *hid); static void synchronized_add_to_adapters_list(wiiu_adapter_t *adapter); +static wiiu_adapter_t *synchronized_remove_from_adapters_list(uint32_t handle); static void synchronized_add_event(wiiu_attach_event *event); static void wiiu_start_read_loop(wiiu_adapter_t *adapter); static void wiiu_hid_read_loop_callback(uint32_t handle, int32_t error, diff --git a/wiiu/input/wiiu_hid_types.h b/wiiu/input/wiiu_hid_types.h new file mode 100644 index 0000000000..b73fcf3a6d --- /dev/null +++ b/wiiu/input/wiiu_hid_types.h @@ -0,0 +1,27 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2013-2014 - Jason Fetters + * 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 . + */ + +#ifndef __WIIU_HID_TYPES__H +#define __WIIU_HID_TYPES__H + +typedef struct wiiu_hid wiiu_hid_t; +typedef struct wiiu_adapter wiiu_adapter_t; +typedef struct wiiu_attach wiiu_attach_event; +typedef struct _wiiu_event_list wiiu_event_list; +typedef struct _wiiu_adapter_list wiiu_adapter_list; +typedef struct _wiiu_pad_functions wiiu_pad_functions_t; + +#endif /* __WIIU_HID_TYPES__H */ diff --git a/wiiu/input/wiiu_input.h b/wiiu/input/wiiu_input.h new file mode 100644 index 0000000000..37416bc7e6 --- /dev/null +++ b/wiiu/input/wiiu_input.h @@ -0,0 +1,68 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2013-2014 - Jason Fetters + * 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 . + */ + +#ifndef __WIIU_INPUT__H +#define __WIIU_INPUT__H + +#include "wiiu_hid_types.h" +#include "../../input/include/gamepad.h" + +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../input/input_driver.h" +#include "../../input/common/hid/hid_device_driver.h" +#include "../../tasks/tasks_internal.h" +#include "../../input/connect/joypad_connection.h" +#include "../../retroarch.h" +#include "../../verbosity.h" +#include "../../command.h" +#include "../../gfx/video_driver.h" +#include "wiiu_hid.h" + +#define WIIMOTE_TYPE_WIIPLUS 0x00 +#define WIIMOTE_TYPE_NUNCHUK 0x01 +#define WIIMOTE_TYPE_CLASSIC 0x02 +#define WIIMOTE_TYPE_PRO 0x1f +#define WIIMOTE_TYPE_NONE 0xfd + +#define WIIU_DEVICE_INDEX_TOUCHPAD 2 + +#define PAD_GAMEPAD 0 +#define WIIU_WIIMOTE_CHANNELS 4 + +#define WIIU_ANALOG_FACTOR 0x7ff0 +#define WIIU_READ_STICK(stick) ((stick) * WIIU_ANALOG_FACTOR) + +struct _wiiu_pad_functions { + int16_t (*get_axis_value)(int32_t axis, int16_t state[3][2], bool is_negative); + void (*set_axis_value)(int16_t state[3][2], int16_t left_x, int16_t left_y, + int16_t right_x, int16_t right_y, int16_t touch_x, int16_t touch_y); + void (*read_axis_data)(uint32_t axis, axis_data *data); + void (*connect)(unsigned pad, input_device_driver_t *driver); +}; + +#endif /* __WIIU_INPUT__H */ diff --git a/wiiu/input/wpad_driver.c b/wiiu/input/wpad_driver.c index 9dd778e19a..4933a2dc3e 100644 --- a/wiiu/input/wpad_driver.c +++ b/wiiu/input/wpad_driver.c @@ -21,7 +21,9 @@ * - For HID controllers, see hid_driver.c */ -#include +#include "wiiu_input.h" + +#define PANIC_BUTTON_MASK (VPAD_BUTTON_R | VPAD_BUTTON_L | VPAD_BUTTON_STICK_R | VPAD_BUTTON_STICK_L) static bool ready = false; static uint64_t button_state = 0; @@ -184,7 +186,12 @@ static void wpad_poll(void) static bool wpad_init(void *data) { - pad_functions.connect(PAD_GAMEPAD, &wpad_driver); + int slot = pad_connection_find_vacant_pad(hid_instance.pad_list); + if(slot < 0) + return false; + + hid_instance.pad_list[slot].connected = true; + input_pad_connect(slot, &wpad_driver); wpad_poll(); ready = true; @@ -193,7 +200,7 @@ static bool wpad_init(void *data) static bool wpad_query_pad(unsigned pad) { - return ready && pad == PAD_GAMEPAD; + return ready && pad < MAX_USERS; } static void wpad_destroy(void) diff --git a/wiiu/system/atomic.c b/wiiu/system/atomic.c new file mode 100644 index 0000000000..ec16f75876 --- /dev/null +++ b/wiiu/system/atomic.c @@ -0,0 +1,36 @@ +/* devkitPPC is missing a few functions that are kinda needed for some cores. + * This should add them back in. + */ + +#include + +/** + * Performs an atomic swap of a single piece of data. In each function, + * value is written to ptr, and the previous value is returned. + * + * From GCC docs: + * + * "This builtin is not a full barrier, but rather a *release* barrier. This + * means that references after the builtin cannot move to (or be speculated + * to) before the builtin, but previous memory stores may not be globally + * visible yet, and previous memory loads may not yet be satisfied." + * + * https://gcc.gnu.org/onlinedocs/gcc-4.5.0/gcc/Atomic-Builtins.html + * + * This builtin seems to be implemented in the Wii U GCC toolchain. But since + * this is GCC-specific, I'm not going to put it into a more general-use + * location. + */ +uint8_t SwapAtomic8(uint8_t *ptr, uint8_t value) +{ + return __sync_lock_test_and_set(ptr, value); +} +uint16_t SwapAtomic16(uint16_t *ptr, uint16_t value) +{ + return __sync_lock_test_and_set(ptr, value); +} +uint32_t SwapAtomic32(uint32_t *ptr, uint32_t value) +{ + return __sync_lock_test_and_set(ptr, value); +} +