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

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

729 lines
20 KiB
C

/* RetroArch - A frontend for libretro.
* Copyright (C) 2013-2014 - Jason Fetters
* Copyright (C) 2011-2016 - Daniel De Matteis
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <boolean.h>
#include <retro_endianness.h>
#include <retro_miscellaneous.h>
#include "joypad_connection.h"
/* wiimote state flags*/
#define WIIMOTE_STATE_DEV_FOUND 0x0001
#define WIIMOTE_STATE_HANDSHAKE 0x0002 /* Actual connection exists but no handshake yet */
#define WIIMOTE_STATE_HANDSHAKE_COMPLETE 0x0004
#define WIIMOTE_STATE_CONNECTED 0x0008
#define WIIMOTE_STATE_EXP 0x0040
/* Communication channels */
#define WM_SET_REPORT 0x50
/* Commands */
#define WM_CMD_LED 0x11
#define WM_CMD_REPORT_TYPE 0x12
#define WM_CMD_RUMBLE 0x13
#define WM_CMD_IR 0x13
#define WM_CMD_CTRL_STATUS 0x15
#define WM_CMD_WRITE_DATA 0x16
#define WM_CMD_READ_DATA 0x17
#define WM_CMD_IR_2 0x1A
/* Input report IDs */
#define WM_RPT_CTRL_STATUS 0x20
#define WM_RPT_READ 0x21
#define WM_RPT_WRITE 0x22
#define WM_RPT_BTN 0x30
#define WM_RPT_BTN_ACC 0x31
#define WM_RPT_BTN_ACC_IR 0x33
#define WM_RPT_BTN_EXP 0x34
#define WM_RPT_BTN_ACC_EXP 0x35
#define WM_RPT_BTN_IR_EXP 0x36
#define WM_RPT_BTN_ACC_IR_EXP 0x37
#define WM_BT_INPUT 0x01
#define WM_BT_OUTPUT 0x02
/* controller status stuff */
#define WM_MAX_BATTERY_CODE 0xC8
/* offsets in wiimote memory */
#define WM_MEM_OFFSET_CALIBRATION 0x16
#define WM_EXP_MEM_BASE 0x04A40000
#define WM_EXP_MEM_ENABLE 0x04A40040
#define WM_EXP_MEM_CALIBR 0x04A40020
#define EXP_HANDSHAKE_LEN 224
/* controller status flags for the first message byte */
/* bit 1 is unknown */
#define WM_CTRL_STATUS_BYTE1_ATTACHMENT 0x02
#define WM_CTRL_STATUS_BYTE1_SPEAKER_ENABLED 0x04
#define WM_CTRL_STATUS_BYTE1_IR_ENABLED 0x08
#define WM_CTRL_STATUS_BYTE1_LED_1 0x10
#define WM_CTRL_STATUS_BYTE1_LED_2 0x20
#define WM_CTRL_STATUS_BYTE1_LED_3 0x40
#define WM_CTRL_STATUS_BYTE1_LED_4 0x80
/* LED bit masks */
#define WIIMOTE_LED_NONE 0x00
#define WIIMOTE_LED_1 0x10
#define WIIMOTE_LED_2 0x20
#define WIIMOTE_LED_3 0x40
#define WIIMOTE_LED_4 0x80
/* button masks */
#define WIIMOTE_BUTTON_ALL 0x1F9F
#define CLASSIC_CTRL_BUTTON_ALL 0xFEFF
/* expansion codes */
#define EXP_NONE 0
#define EXP_CLASSIC 2
#define IDENT_NUNCHUK 0xA4200000
#define IDENT_CC 0xA4200101
typedef struct axis_t
{
bool has_center;
uint8_t min;
uint8_t center;
uint8_t max;
uint8_t raw_value;
float value;
} axis_t;
typedef struct connect_wii_joystick_t
{
axis_t x;
axis_t y;
} connect_wii_joystick_t;
typedef struct connect_wii_classic_ctrl_t
{
int16_t btns;
struct connect_wii_joystick_t ljs;
struct connect_wii_joystick_t rjs;
} connect_wii_classic_ctrl_t;
/* Generic expansion device plugged into wiimote. */
typedef struct connect_wii_expansion_t
{
/* Type of expansion attached. */
int type;
union
{
struct connect_wii_classic_ctrl_t classic;
} cc;
} connect_wii_expansion_t;
/* Wiimote structure. */
typedef struct connect_wii_wiimote_t
{
/* User specified ID. */
int unid;
struct pad_connection* connection;
send_control_t send_control;
/* Various state flags. */
uint32_t state;
/* Currently lit LEDs. */
uint8_t leds;
/* Battery level. */
float battery_level;
/* The state of the connection handshake. */
uint8_t handshake_state;
/* Wiimote expansion device. */
struct connect_wii_expansion_t exp;
/* What buttons have just been pressed. */
uint16_t btns;
} connect_wii_wiimote;
/* Macro to manage states */
#define WIIMOTE_IS_SET(wm, s) ((wm->state & (s)) == (s))
#define WIIMOTE_ENABLE_STATE(wm, s) (wm->state |= (s))
#define WIIMOTE_DISABLE_STATE(wm, s) (wm->state &= ~(s))
#define WIIMOTE_TOGGLE_STATE(wm, s) ((wm->state & (s)) ? WIIMOTE_DISABLE_STATE(wm, s) : WIIMOTE_ENABLE_STATE(wm, s))
static bool wiimote_is_connected(struct connect_wii_wiimote_t *wm)
{
return WIIMOTE_IS_SET(wm, WIIMOTE_STATE_CONNECTED);
}
/*
* Send a packet to the wiimote.
*
* This function should replace any write()s directly to the wiimote device.
*/
static int wiimote_send(struct connect_wii_wiimote_t* wm,
uint8_t report_type, uint8_t* msg, int len)
{
uint8_t buf[32] = {0};
buf[0] = WM_SET_REPORT | WM_BT_OUTPUT;
buf[1] = report_type;
memcpy(buf+2, msg, len);
#ifdef WIIMOTE_DBG
int x;
printf("[DEBUG] (id %i) SEND: (%x) %.2x ", wm->unid, buf[0], buf[1]);
for (x = 2; x < len+2; ++x)
printf("%.2x ", buf[x]);
printf("\n");
#endif
wm->send_control(wm->connection, buf, len + 2);
return 1;
}
/*
* Request the wiimote controller status.
*
* Controller status includes: battery level, LED status, expansions.
*/
static void wiimote_status(struct connect_wii_wiimote_t* wm)
{
uint8_t buf = 0;
if (!wm || !wiimote_is_connected(wm))
return;
#ifdef WIIMOTE_DBG
printf("Requested wiimote status.\n");
#endif
wiimote_send(wm, WM_CMD_CTRL_STATUS, &buf, 1);
}
static void wiimote_data_report(struct connect_wii_wiimote_t* wm, uint8_t type)
{
uint8_t buf[2] = {0x0,0x0};
if (!wm || !wiimote_is_connected(wm))
return;
buf[1] = type;
/* CUIDADO es un &buf? */
wiimote_send(wm, WM_CMD_REPORT_TYPE, buf, 2);
}
/*
* Set the enabled LEDs.
*
* leds is a bitwise OR of:
* - WIIMOTE_LED_1
* - WIIMOTE_LED_2
* - WIIMOTE_LED_3
* - WIIMOTE_LED_4
*/
static void wiimote_set_leds(struct connect_wii_wiimote_t* wm, int leds)
{
uint8_t buf = {0};
if (!wm || !wiimote_is_connected(wm))
return;
/* Remove the lower 4 bits because they control rumble. */
wm->leds = (leds & 0xF0);
buf = wm->leds;
wiimote_send(wm, WM_CMD_LED, &buf, 1);
}
/* Find what buttons are pressed. */
static void wiimote_pressed_buttons(struct connect_wii_wiimote_t* wm, uint8_t* msg)
{
/* Convert to big endian. */
int16_t *val = (int16_t*)msg;
int16_t now = swap_if_little16(*val) & WIIMOTE_BUTTON_ALL;
wm->btns = now;
}
static int wiimote_classic_ctrl_handshake(struct connect_wii_wiimote_t* wm,
struct connect_wii_classic_ctrl_t* cc, uint8_t* data, uint16_t len)
{
memset(cc, 0, sizeof(*cc));
wm->exp.type = EXP_CLASSIC;
return 1;
}
static float wiimote_normalize_and_interpolate(float min, float max, float t)
{
if (min == max)
return 0.0f;
return (t - min) / (max - min);
}
static void wiimote_process_axis(struct axis_t* axis, uint8_t raw)
{
if (!axis->has_center)
{
axis->has_center = true;
axis->min = raw - 2;
axis->center = raw;
axis->max = raw + 2;
}
if (raw < axis->min)
axis->min = raw;
if (raw > axis->max)
axis->max = raw;
axis->raw_value = raw;
if (raw < axis->center)
axis->value = -wiimote_normalize_and_interpolate(
axis->center, axis->min, raw);
else if (raw > axis->center)
axis->value = wiimote_normalize_and_interpolate(
axis->center, axis->max, raw);
else
axis->value = 0;
}
static void classic_ctrl_event(struct connect_wii_classic_ctrl_t* cc, uint8_t* msg)
{
if (!cc)
return;
cc->btns = ~swap_if_little16(*(int16_t*)(msg + 4)) & CLASSIC_CTRL_BUTTON_ALL;
wiimote_process_axis(&cc->ljs.x, (msg[0] & 0x3F));
wiimote_process_axis(&cc->ljs.y, (msg[1] & 0x3F));
wiimote_process_axis(&cc->rjs.x, ((msg[0] & 0xC0) >> 3) |
((msg[1] & 0xC0) >> 5) | ((msg[2] & 0x80) >> 7));
wiimote_process_axis(&cc->rjs.y, (msg[2] & 0x1F));
}
/*
* Handle data from the expansion.
*/
static void wiimote_handle_expansion(struct connect_wii_wiimote_t* wm, uint8_t* msg)
{
switch (wm->exp.type)
{
case EXP_CLASSIC:
classic_ctrl_event(&wm->exp.cc.classic, msg);
break;
default:
break;
}
}
/*
* Write data to the wiimote.
*/
static int wiimote_write_data(struct connect_wii_wiimote_t* wm,
uint32_t addr, uint8_t* data, uint8_t len)
{
uint8_t buf[21] = {0}; /* the payload is always 23 */
int32_t *buf32 = (int32_t*)buf;
if (!wm || !wiimote_is_connected(wm))
return 0;
if (!data || !len)
return 0;
#ifdef WIIMOTE_DBG
int i = 0;
printf("Writing %i bytes to memory location 0x%x...\n", len, addr);
printf("Write data is: ");
for (; i < len; ++i)
printf("%x ", data[i]);
printf("\n");
#endif
/* the offset is in big endian */
*buf32 = swap_if_little32(addr);
/* length */
*(uint8_t*)(buf + 4) = len;
/* data */
memcpy(buf + 5, data, len);
wiimote_send(wm, WM_CMD_WRITE_DATA, buf, 21);
return 1;
}
/*
* Read data from the wiimote (event version).
*
* The library can only handle one data read request at a time
* because it must keep track of the buffer and other
* events that are specific to that request. So if a request
* has already been made, subsequent requests will be added
* to a pending list and be sent out when the previous
* finishes.
*/
static int wiimote_read_data(struct connect_wii_wiimote_t* wm, uint32_t addr,
uint16_t len)
{
uint8_t buf[6] = {0};
int32_t *buf32 = (int32_t*)buf;
int16_t *buf16 = (int16_t*)(buf + 4);
/* No puden ser mas de 16 lo leido o vendra en trozos! */
if (!wm || !wiimote_is_connected(wm) || !len)
return 0;
/* the offsets are in big endian */
*buf32 = swap_if_little32(addr);
*buf16 = swap_if_little16(len);
#ifdef WIIMOTE_DBG
printf("Request read at address: 0x%x length: %i", addr, len);
#endif
wiimote_send(wm, WM_CMD_READ_DATA, buf, 6);
return 1;
}
/*
* Get initialization data from the Wiimote.
*
* When first called for a connect_wii_wiimote_t structure, a request
* is sent to the wiimote for initialization information.
* This includes factory set accelerometer data.
* The handshake will be concluded when the wiimote responds
* with this data.
*/
static int wiimote_handshake(struct connect_wii_wiimote_t* wm,
uint8_t event, uint8_t* data, uint16_t len)
{
if (!wm)
return 0;
do
{
switch (wm->handshake_state)
{
case 0:
/* no ha habido nunca handshake, debemos forzar un
* mensaje de staus para ver que pasa. */
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE);
wiimote_set_leds(wm, WIIMOTE_LED_NONE);
/* Request the status of the Wiimote to
* see if there is an expansion */
wiimote_status(wm);
wm->handshake_state=1;
return 0;
case 1:
{
/* estamos haciendo handshake o bien se necesita iniciar un
* nuevo handshake ya que se inserta(quita una expansion. */
int attachment = 0;
if(event != WM_RPT_CTRL_STATUS)
return 0;
/* Is an attachment connected to
* the expansion port? */
if ((data[2] & WM_CTRL_STATUS_BYTE1_ATTACHMENT) ==
WM_CTRL_STATUS_BYTE1_ATTACHMENT)
attachment = 1;
#ifdef WIIMOTE_DBG
printf("attachment %d %d\n",attachment,
WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP));
#endif
if (attachment && !WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP))
{
uint8_t buf = 0;
/* Expansion port */
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_EXP);
/* Send the initialization code for the attachment */
if(WIIMOTE_IS_SET(wm,WIIMOTE_STATE_HANDSHAKE_COMPLETE))
{
/* Rehandshake. */
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE_COMPLETE);
/* forzamos un handshake por si venimos
* de un hanshake completo. */
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE);
}
/*Old way. initialize the extension was by writing the
* single encryption byte 0x00 to 0x(4)A40040. */
#if 0
buf = 0x00;
wiimote_write_data(wm, WM_EXP_MEM_ENABLE, &buf, 1);
#endif
/* NEW WAY 0x55 to 0x(4)A400F0, then writing
* 0x00 to 0x(4)A400FB. (support clones) */
buf = 0x55;
wiimote_write_data(wm, 0x04A400F0, &buf, 1);
retro_sleep(100);
buf = 0x00;
wiimote_write_data(wm, 0x04A400FB, &buf, 1);
/* check extension type! */
retro_sleep(100);
wiimote_read_data(wm, WM_EXP_MEM_CALIBR + 220, 4);
#if 0
wiimote_read_data(wm, WM_EXP_MEM_CALIBR, EXP_HANDSHAKE_LEN);
#endif
wm->handshake_state = 4;
return 0;
}
else if (!attachment && WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP))
{
/* Attachment removed */
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_EXP);
wm->exp.type = EXP_NONE;
if(WIIMOTE_IS_SET(wm,WIIMOTE_STATE_HANDSHAKE_COMPLETE))
{
#ifdef WIIMOTE_DBG
printf("rehandshake\n");
#endif
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE_COMPLETE);
/* forzamos un handshake por si venimos
* de un hanshake completo. */
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE);
}
}
if(!attachment && WIIMOTE_IS_SET(wm,WIIMOTE_STATE_HANDSHAKE))
{
wm->handshake_state = 2;
continue;
}
return 0;
}
case 2:
/* Find handshake no expansion. */
#ifdef WIIMOTE_DBG
printf("Finalizado HANDSHAKE SIN EXPANSION\n");
#endif
wiimote_data_report(wm,WM_RPT_BTN);
wm->handshake_state = 6;
continue;
case 3:
/* Find handshake expansion. */
#ifdef WIIMOTE_DBG
printf("Finalizado HANDSHAKE CON EXPANSION\n");
#endif
wiimote_data_report(wm,WM_RPT_BTN_EXP);
wm->handshake_state = 6;
continue;
case 4:
{
uint32_t id;
int32_t *ptr = (int32_t*)data;
if (event != WM_RPT_READ)
return 0;
id = swap_if_little32(*ptr);
switch (id)
{
case IDENT_CC:
retro_sleep(100);
/* pedimos datos de calibracion del JOY! */
wiimote_read_data(wm, WM_EXP_MEM_CALIBR, 16);
wm->handshake_state = 5;
break;
default:
wm->handshake_state = 2;
#if 0
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_EXP);
#endif
continue;
}
}
return 0;
case 5:
if(event != WM_RPT_READ)
return 0;
wiimote_classic_ctrl_handshake(wm, &wm->exp.cc.classic, data,len);
wm->handshake_state = 3;
continue;
case 6:
WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE);
WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE_COMPLETE);
wm->handshake_state = 1;
switch (wm->unid)
{
case 0:
wiimote_set_leds(wm, WIIMOTE_LED_1);
break;
case 1:
wiimote_set_leds(wm, WIIMOTE_LED_2);
break;
case 2:
wiimote_set_leds(wm, WIIMOTE_LED_3);
break;
case 3:
wiimote_set_leds(wm, WIIMOTE_LED_4);
break;
}
return 1;
default:
break;
}
}while(1);
}
static void hidpad_wii_deinit(void *data)
{
struct connect_wii_wiimote_t* device = (struct connect_wii_wiimote_t*)data;
if (device)
free(device);
}
static void* hidpad_wii_init(void *data, uint32_t slot,
send_control_t ptr)
{
struct pad_connection *connection = (struct pad_connection*)data;
struct connect_wii_wiimote_t *device = (struct connect_wii_wiimote_t*)
calloc(1, sizeof(struct connect_wii_wiimote_t));
if (!device)
goto error;
if (!connection)
goto error;
device->connection = connection;
device->unid = slot;
device->state = WIIMOTE_STATE_CONNECTED;
device->exp.type = EXP_NONE;
device->send_control = ptr;
wiimote_handshake(device, -1, NULL, -1);
return device;
error:
hidpad_wii_deinit(device);
return NULL;
}
static int16_t hidpad_wii_get_axis(void *data, unsigned axis)
{
struct connect_wii_wiimote_t* device = (struct connect_wii_wiimote_t*)data;
if (!device)
return 0;
switch (device->exp.type)
{
case EXP_CLASSIC:
switch (axis)
{
case 0:
return device->exp.cc.classic.ljs.x.value * 0x7FFF;
case 1:
return device->exp.cc.classic.ljs.y.value * 0x7FFF;
case 2:
return device->exp.cc.classic.rjs.x.value * 0x7FFF;
case 3:
return device->exp.cc.classic.rjs.y.value * 0x7FFF;
}
break;
default:
break;
}
return 0;
}
static uint64_t hidpad_wii_get_buttons(void *data)
{
struct connect_wii_wiimote_t* device = (struct connect_wii_wiimote_t*)data;
if (!device)
return 0;
return device->btns | (device->exp.cc.classic.btns << 16);
}
static void hidpad_wii_packet_handler(void *data,
uint8_t *packet, uint16_t size)
{
struct connect_wii_wiimote_t* device = (struct connect_wii_wiimote_t*)data;
uint8_t *msg = packet + 2;
if (!device)
return;
switch (packet[1])
{
case WM_RPT_BTN:
wiimote_pressed_buttons(device, msg);
break;
case WM_RPT_READ:
wiimote_pressed_buttons(device, msg);
wiimote_handshake(device, WM_RPT_READ, msg + 5,
((msg[2] & 0xF0) >> 4) + 1);
break;
case WM_RPT_CTRL_STATUS:
wiimote_pressed_buttons(device, msg);
wiimote_handshake(device,WM_RPT_CTRL_STATUS,msg,-1);
break;
case WM_RPT_BTN_EXP:
wiimote_pressed_buttons(device, msg);
wiimote_handle_expansion(device, msg+2);
break;
}
}
static void hidpad_wii_set_rumble(void *data,
enum retro_rumble_effect effect, uint16_t strength)
{
/* TODO */
(void)data;
(void)effect;
(void)strength;
}
pad_connection_interface_t pad_connection_wii = {
hidpad_wii_init,
hidpad_wii_deinit,
hidpad_wii_packet_handler,
hidpad_wii_set_rumble,
hidpad_wii_get_buttons,
hidpad_wii_get_axis,
NULL,
};