2010-07-03 08:04:10 +00:00
// Copyright (C) 2003 Dolphin Project.
// This program 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 Foundation, version 2.0.
// This program 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
# include <queue>
# include "Common.h"
# include "IniFile.h"
# include "Thread.h"
# include "StringUtil.h"
# include "Timer.h"
2010-07-29 10:21:48 +00:00
# include "FifoQueue.h"
2010-07-03 08:04:10 +00:00
# include "pluginspecs_wiimote.h"
# include "wiiuse.h"
# include "WiimoteReal.h"
# include "../WiimoteEmu/WiimoteHid.h"
unsigned int g_wiimote_sources [ MAX_WIIMOTES ] ;
namespace WiimoteReal
{
bool g_real_wiimotes_initialized = false ;
wiimote_t * * g_wiimotes_from_wiiuse = NULL ;
unsigned int g_wiimotes_found = 0 ;
2010-07-25 20:56:48 +00:00
volatile unsigned int g_wiimotes_lastfound = 0 ;
2010-07-03 08:04:10 +00:00
volatile bool g_run_wiimote_thread = false ;
Common : : Thread * g_wiimote_thread = NULL ;
2010-07-29 10:21:48 +00:00
Common : : CriticalSection g_refresh_critsec , g_wiimote_critsec ;
2010-07-03 08:04:10 +00:00
THREAD_RETURN WiimoteThreadFunc ( void * arg ) ;
class Wiimote
{
public :
Wiimote ( wiimote_t * const wm , const unsigned int index ) ;
~ Wiimote ( ) ;
void ControlChannel ( const u16 channel , const void * const data , const u32 size ) ;
void InterruptChannel ( const u16 channel , const void * const data , const u32 size ) ;
void Update ( ) ;
void Read ( ) ;
2010-07-29 10:21:48 +00:00
void Write ( ) ;
2010-07-03 08:04:10 +00:00
void Disconnect ( ) ;
2010-07-03 20:14:57 +00:00
void DisableDataReporting ( ) ;
2010-07-03 08:04:10 +00:00
2010-07-29 20:11:55 +00:00
void SendPacket ( const u8 rpt_id , const void * const data , const unsigned int size ) ;
// pointer to data, and size of data
typedef std : : pair < u8 * , u8 > Report ;
2010-07-03 08:04:10 +00:00
private :
void ClearReports ( ) ;
wiimote_t * const m_wiimote ;
const unsigned int m_index ;
u16 m_channel ;
u8 m_last_data_report [ MAX_PAYLOAD ] ;
bool m_last_data_report_valid ;
2010-07-29 10:21:48 +00:00
Common : : FifoQueue < u8 * > m_read_reports ;
2010-07-29 20:11:55 +00:00
Common : : FifoQueue < Report > m_write_reports ;
2010-07-03 08:04:10 +00:00
} ;
Wiimote : : Wiimote ( wiimote_t * const wm , const unsigned int index )
: m_wiimote ( wm )
, m_index ( index )
, m_channel ( 0 )
, m_last_data_report_valid ( false )
{
// disable reporting
2010-07-03 20:14:57 +00:00
DisableDataReporting ( ) ;
2010-07-03 08:04:10 +00:00
// clear all msgs, silly maybe
2010-07-03 20:14:57 +00:00
// - cleared on first InterruptChannel call
//while (wiiuse_io_read(m_wiimote));
2010-07-03 08:04:10 +00:00
//{
// LEDs test
//wm_leds rpt = wm_leds();
//rpt.leds = 1 << i;
//SendPacket(g_wiimotes_from_wiiuse[i], WM_LEDS, &rpt, sizeof(rpt));
//}
2010-07-07 02:19:16 +00:00
// Rumble briefly
wiiuse_rumble ( m_wiimote , 1 ) ;
SLEEP ( 200 ) ;
wiiuse_rumble ( m_wiimote , 0 ) ;
2010-07-03 08:04:10 +00:00
// set LEDs
wiiuse_set_leds ( m_wiimote , WIIMOTE_LED_1 < < m_index ) ;
2010-07-03 20:14:57 +00:00
// TODO: make Dolphin connect wiimote, maybe
2010-07-03 08:04:10 +00:00
}
Wiimote : : ~ Wiimote ( )
{
ClearReports ( ) ;
2010-07-03 20:14:57 +00:00
// disable reporting / wiiuse might do this on shutdown anyway, o well, don't know for sure
DisableDataReporting ( ) ;
2010-07-03 08:04:10 +00:00
// send disconnect message to wii, maybe, i hope, naw shit messes up on emu-stop
//if (g_WiimoteInitialize.pWiimoteInterruptChannel)
//{
// //u8* const rpt = new u8[2];
// //rpt[0] = 0XA1; rpt[1] = 0x15;
2010-07-29 10:21:48 +00:00
// //m_read_reports.push(rpt);
2010-07-03 08:04:10 +00:00
// //Update();
// const u8 rpt[] = { 0xA1, 0x15 };
// g_WiimoteInitialize.pWiimoteInterruptChannel(m_index, m_channel, rpt, sizeof(rpt));
//}
}
2010-07-29 20:11:55 +00:00
// silly, copying data n stuff, o well, don't use this too often
void Wiimote : : SendPacket ( const u8 rpt_id , const void * const data , const unsigned int size )
{
Report rpt ;
rpt . second = size + 2 ;
rpt . first = new u8 [ rpt . second ] ;
rpt . first [ 0 ] = 0xA1 ;
rpt . first [ 1 ] = rpt_id ;
memcpy ( rpt . first + 2 , data , size ) ;
m_write_reports . Push ( rpt ) ;
}
2010-07-03 20:14:57 +00:00
void Wiimote : : DisableDataReporting ( )
{
wm_report_mode rpt = wm_report_mode ( ) ;
rpt . mode = WM_REPORT_CORE ;
2010-07-29 20:11:55 +00:00
SendPacket ( WM_REPORT_MODE , & rpt , sizeof ( rpt ) ) ;
2010-07-03 20:14:57 +00:00
}
2010-07-03 08:04:10 +00:00
void Wiimote : : ClearReports ( )
{
2010-07-03 20:14:57 +00:00
m_last_data_report_valid = false ;
2010-07-29 20:11:55 +00:00
Report rpt ;
while ( m_read_reports . Pop ( rpt . first ) )
delete [ ] rpt . first ;
2010-07-29 10:21:48 +00:00
while ( m_write_reports . Pop ( rpt ) )
2010-07-29 20:11:55 +00:00
delete [ ] rpt . first ;
2010-07-03 08:04:10 +00:00
}
void Wiimote : : ControlChannel ( const u16 channel , const void * const data , const u32 size )
{
// Check for custom communication
if ( 99 = = channel )
Disconnect ( ) ;
else
InterruptChannel ( channel , data , size ) ;
}
void Wiimote : : InterruptChannel ( const u16 channel , const void * const data , const u32 size )
{
2010-07-03 20:14:57 +00:00
if ( 0 = = m_channel ) // first interrupt/control channel sent
{
// clear all msgs, silly maybe
2010-07-31 19:06:44 +00:00
while ( wiiuse_io_read ( m_wiimote ) ) { } ;
2010-07-03 20:14:57 +00:00
// request status
wm_request_status rpt = wm_request_status ( ) ;
2010-07-29 20:11:55 +00:00
SendPacket ( WM_REQUEST_STATUS , & rpt , sizeof ( rpt ) ) ;
2010-07-03 20:14:57 +00:00
}
2010-07-29 10:21:48 +00:00
m_channel = channel ; // this right?
2010-07-29 20:11:55 +00:00
Wiimote : : Report rpt ;
rpt . first = new u8 [ size ] ;
rpt . second = ( u8 ) size ;
memcpy ( rpt . first , ( u8 * ) data , size ) ;
2010-07-29 10:21:48 +00:00
m_write_reports . Push ( rpt ) ;
2010-07-03 08:04:10 +00:00
}
void Wiimote : : Read ( )
{
2010-07-03 20:14:57 +00:00
// if not connected to Dolphin, don't do anything, to keep sanchez happy :p
if ( 0 = = m_channel )
2010-07-03 13:05:55 +00:00
return ;
2010-07-03 08:04:10 +00:00
if ( wiiuse_io_read ( m_wiimote ) )
{
// a data report, save it
if ( m_wiimote - > event_buf [ 1 ] > = 0x30 )
{
2010-07-03 11:20:38 +00:00
memcpy ( m_last_data_report , m_wiimote - > event_buf , MAX_PAYLOAD ) ;
2010-07-03 08:04:10 +00:00
m_last_data_report_valid = true ;
}
else
{
// some other report, add it to queue
2010-07-03 11:20:38 +00:00
u8 * const rpt = new u8 [ MAX_PAYLOAD ] ;
memcpy ( rpt , m_wiimote - > event_buf , MAX_PAYLOAD ) ;
2010-07-29 10:21:48 +00:00
m_read_reports . Push ( rpt ) ;
2010-07-03 08:04:10 +00:00
}
}
}
2010-07-29 10:21:48 +00:00
void Wiimote : : Write ( )
{
2010-07-29 20:11:55 +00:00
Report rpt ;
2010-07-29 10:21:48 +00:00
if ( m_write_reports . Pop ( rpt ) )
{
2010-07-29 20:11:55 +00:00
wiiuse_io_write ( m_wiimote , rpt . first , rpt . second ) ;
delete [ ] rpt . first ;
2010-07-29 10:21:48 +00:00
}
}
2010-07-03 08:04:10 +00:00
void Wiimote : : Update ( )
{
// do we have some queued reports
2010-07-29 10:21:48 +00:00
u8 * rpt ;
if ( m_read_reports . Pop ( rpt ) )
2010-07-03 08:04:10 +00:00
{
2010-07-03 11:20:38 +00:00
g_WiimoteInitialize . pWiimoteInterruptChannel ( m_index , m_channel , rpt , MAX_PAYLOAD ) ;
2010-07-03 08:04:10 +00:00
delete [ ] rpt ;
}
else if ( m_last_data_report_valid )
{
// otherwise send the last data report, if there is one
2010-07-03 11:20:38 +00:00
g_WiimoteInitialize . pWiimoteInterruptChannel ( m_index , m_channel , m_last_data_report , MAX_PAYLOAD ) ;
2010-07-03 08:04:10 +00:00
}
}
void Wiimote : : Disconnect ( )
{
2010-07-03 20:14:57 +00:00
m_channel = 0 ;
2010-07-03 08:04:10 +00:00
// disable reporting
2010-07-03 20:14:57 +00:00
DisableDataReporting ( ) ;
2010-07-03 08:04:10 +00:00
// clear queue
ClearReports ( ) ;
// clear out wiiuse queue, or maybe not, silly? idk
2010-07-31 19:06:44 +00:00
while ( wiiuse_io_read ( m_wiimote ) ) { } ;
2010-07-03 08:04:10 +00:00
}
Wiimote * g_wiimotes [ 4 ] ;
2010-07-03 11:20:38 +00:00
void LoadSettings ( )
2010-07-03 08:04:10 +00:00
{
std : : string ini_filename = ( std : : string ( File : : GetUserPath ( D_CONFIG_IDX ) ) + g_plugin . ini_name + " .ini " ) ;
IniFile inifile ;
inifile . Load ( ini_filename ) ;
for ( unsigned int i = 0 ; i < MAX_WIIMOTES ; + + i )
{
std : : string secname ( " Wiimote " ) ;
secname + = ( char ) ( ' 1 ' + i ) ;
IniFile : : Section & sec = * inifile . GetOrCreateSection ( secname . c_str ( ) ) ;
sec . Get ( " Source " , & g_wiimote_sources [ i ] , WIIMOTE_SRC_EMU ) ;
}
2010-07-03 11:20:38 +00:00
}
unsigned int Initialize ( )
{
// return if already initialized
if ( g_real_wiimotes_initialized )
return g_wiimotes_found ;
2010-07-03 08:04:10 +00:00
memset ( g_wiimotes , 0 , sizeof ( g_wiimotes ) ) ;
// only call wiiuse_find with the number of slots configured for real wiimotes
unsigned int wanted_wiimotes = 0 ;
for ( unsigned int i = 0 ; i < MAX_WIIMOTES ; + + i )
if ( WIIMOTE_SRC_REAL = = g_wiimote_sources [ i ] )
+ + wanted_wiimotes ;
// don't bother initializing wiiuse if we don't want any real wiimotes
if ( 0 = = wanted_wiimotes )
{
g_wiimotes_found = 0 ;
return 0 ;
}
// initialized
g_real_wiimotes_initialized = true ;
2010-07-25 20:56:48 +00:00
# ifdef WIN32
// Alloc memory for wiimote structure only if we're starting fresh
if ( ! g_wiimotes_from_wiiuse )
g_wiimotes_from_wiiuse = wiiuse_init ( MAX_WIIMOTES ) ;
// on windows wiiuse_find() expects as a 3rd parameter the amount of last connected wiimotes instead of the timeout,
// a timeout parameter is useless on win32 here, since at this points we already have the wiimotes discovered and paired up, just not connected.
g_wiimotes_found = wiiuse_find ( g_wiimotes_from_wiiuse , wanted_wiimotes , g_wiimotes_lastfound ) ;
# else
2010-07-03 08:04:10 +00:00
g_wiimotes_from_wiiuse = wiiuse_init ( MAX_WIIMOTES ) ;
g_wiimotes_found = wiiuse_find ( g_wiimotes_from_wiiuse , wanted_wiimotes , 5 ) ;
2010-07-25 20:56:48 +00:00
# endif
g_wiimotes_lastfound = g_wiimotes_found ;
DEBUG_LOG ( WIIMOTE , " Found %i Real Wiimotes, %i wanted and %i previously found " , g_wiimotes_found , wanted_wiimotes , g_wiimotes_lastfound ) ;
2010-07-03 08:04:10 +00:00
2010-07-03 10:54:55 +00:00
g_wiimotes_found =
wiiuse_connect ( g_wiimotes_from_wiiuse , g_wiimotes_found ) ;
DEBUG_LOG ( WIIMOTE , " Connected to %i Real Wiimotes " , g_wiimotes_found ) ;
2010-07-03 08:04:10 +00:00
g_wiimote_critsec . Enter ( ) ; // enter
// create real wiimote class instances, assign wiimotes
2010-07-03 11:20:38 +00:00
for ( unsigned int i = 0 , w = 0 ; i < MAX_WIIMOTES & & w < g_wiimotes_found ; + + i )
2010-07-03 08:04:10 +00:00
{
if ( WIIMOTE_SRC_REAL ! = g_wiimote_sources [ i ] )
continue ;
// create/assign wiimote
2010-07-03 11:20:38 +00:00
g_wiimotes [ i ] = new Wiimote ( g_wiimotes_from_wiiuse [ w + + ] , i ) ;
2010-07-03 08:04:10 +00:00
}
g_wiimote_critsec . Leave ( ) ; // leave
// start wiimote thread
g_run_wiimote_thread = true ;
g_wiimote_thread = new Common : : Thread ( WiimoteThreadFunc , 0 ) ;
return g_wiimotes_found ;
}
void Shutdown ( void )
{
if ( false = = g_real_wiimotes_initialized )
return ;
// Uninitialized
g_real_wiimotes_initialized = false ;
// stop wiimote thread
if ( g_wiimote_thread )
{
g_run_wiimote_thread = false ;
g_wiimote_thread - > WaitForDeath ( ) ;
delete g_wiimote_thread ;
g_wiimote_thread = NULL ;
}
g_wiimote_critsec . Enter ( ) ; // enter
// delete wiimotes
for ( unsigned int i = 0 ; i < MAX_WIIMOTES ; + + i )
if ( g_wiimotes [ i ] )
{
delete g_wiimotes [ i ] ;
g_wiimotes [ i ] = NULL ;
}
g_wiimote_critsec . Leave ( ) ; // leave
// set all LEDs on, idk
//for (unsigned int i=0; i<g_wiimotes_found; ++i)
//{
// wiiuse_set_leds(g_wiimotes_from_wiiuse[i], 0xF0);
//}
2010-07-25 20:56:48 +00:00
// Clean up wiiuse, win32: we cant just delete the struct on win32, since wiiuse_find() maintains it and
// adds/removes wimotes directly to/from it to prevent problems, which would occur when using more than 1 wiimote if we create it from scratch everytime
# ifndef WIN32
2010-07-03 08:04:10 +00:00
wiiuse_cleanup ( g_wiimotes_from_wiiuse , MAX_WIIMOTES ) ;
2010-07-25 20:56:48 +00:00
# endif
2010-07-03 08:04:10 +00:00
}
2010-07-29 10:21:48 +00:00
void Refresh ( ) // this gets called from the GUI thread
2010-07-07 02:19:16 +00:00
{
2010-07-29 10:21:48 +00:00
g_refresh_critsec . Enter ( ) ;
# ifdef __linux__
2010-07-09 04:02:36 +00:00
// make sure real wiimotes have been initialized
if ( ! g_real_wiimotes_initialized )
{
2010-07-30 18:55:36 +00:00
g_refresh_critsec . Leave ( ) ;
2010-07-09 04:02:36 +00:00
Initialize ( ) ;
return ;
}
2010-07-07 02:19:16 +00:00
// find the number of slots configured for real wiimotes
unsigned int wanted_wiimotes = 0 ;
for ( unsigned int i = 0 ; i < MAX_WIIMOTES ; + + i )
if ( g_wiimote_sources [ i ] = = WIIMOTE_SRC_REAL )
+ + wanted_wiimotes ;
// don't scan for wiimotes if we don't want any more
if ( wanted_wiimotes < = g_wiimotes_found )
2010-07-30 18:55:36 +00:00
{
g_refresh_critsec . Leave ( ) ;
2010-07-07 02:19:16 +00:00
return ;
2010-07-30 18:55:36 +00:00
}
2010-07-07 02:19:16 +00:00
// scan for wiimotes
2010-07-30 18:55:36 +00:00
unsigned int num_wiimotes = wiiuse_find ( g_wiimotes_from_wiiuse , wanted_wiimotes , 5 ) ;
2010-07-07 02:19:16 +00:00
DEBUG_LOG ( WIIMOTE , " Found %i Real Wiimotes, %i wanted " , num_wiimotes , wanted_wiimotes ) ;
int num_new_wiimotes = wiiuse_connect ( g_wiimotes_from_wiiuse , num_wiimotes ) ;
DEBUG_LOG ( WIIMOTE , " Connected to %i additional Real Wiimotes " , num_new_wiimotes ) ;
g_wiimote_critsec . Enter ( ) ; // enter
// create real wiimote class instances, and assign wiimotes for the new wiimotes
for ( unsigned int i = g_wiimotes_found , w = g_wiimotes_found ;
i < MAX_WIIMOTES & & w < num_wiimotes ; + + i )
{
if ( g_wiimote_sources [ i ] ! = WIIMOTE_SRC_REAL | | g_wiimotes [ i ] ! = NULL )
continue ;
// create/assign wiimote
g_wiimotes [ i ] = new Wiimote ( g_wiimotes_from_wiiuse [ w + + ] , i ) ;
}
g_wiimotes_found = num_wiimotes ;
g_wiimote_critsec . Leave ( ) ; // leave
2010-07-29 10:21:48 +00:00
# else // windows/ OSX
2010-07-03 08:04:10 +00:00
// should be fine i think
Shutdown ( ) ;
2010-07-29 10:21:48 +00:00
Initialize ( ) ;
2010-07-07 02:19:16 +00:00
# endif
2010-07-03 08:04:10 +00:00
2010-07-29 10:21:48 +00:00
g_refresh_critsec . Leave ( ) ;
}
2010-07-03 08:04:10 +00:00
void InterruptChannel ( int _WiimoteNumber , u16 _channelID , const void * _pData , u32 _Size )
{
2010-07-03 20:14:57 +00:00
u8 * data = ( u8 * ) _pData ;
// some hax, since we just send the last data report to Dolphin on each Update() call
// , make the wiimote only send updated data reports when data changes
// == less bt traffic, eliminates some unneeded packets
if ( WM_REPORT_MODE = = ( ( u8 * ) _pData ) [ 1 ] )
{
// I dont wanna write on the const *_pData
data = new u8 [ _Size ] ;
memcpy ( data , _pData , _Size ) ;
// nice var names :p, this seems to be this one
( ( wm_report_mode * ) ( data + 2 ) ) - > all_the_time = false ;
//((wm_report_mode*)(data + 2))->continuous = false;
}
2010-07-29 10:21:48 +00:00
g_refresh_critsec . Enter ( ) ;
2010-07-03 08:04:10 +00:00
if ( g_wiimotes [ _WiimoteNumber ] )
2010-07-03 20:14:57 +00:00
g_wiimotes [ _WiimoteNumber ] - > InterruptChannel ( _channelID , data , _Size ) ;
2010-07-03 08:04:10 +00:00
2010-07-29 10:21:48 +00:00
g_refresh_critsec . Leave ( ) ;
2010-07-03 20:14:57 +00:00
if ( data ! = _pData )
delete [ ] data ;
2010-07-03 08:04:10 +00:00
}
void ControlChannel ( int _WiimoteNumber , u16 _channelID , const void * _pData , u32 _Size )
{
2010-07-29 10:21:48 +00:00
g_refresh_critsec . Enter ( ) ;
2010-07-03 08:04:10 +00:00
if ( g_wiimotes [ _WiimoteNumber ] )
g_wiimotes [ _WiimoteNumber ] - > ControlChannel ( _channelID , _pData , _Size ) ;
2010-07-29 10:21:48 +00:00
g_refresh_critsec . Leave ( ) ;
2010-07-03 08:04:10 +00:00
}
// Read the Wiimote once
void Update ( int _WiimoteNumber )
{
2010-07-29 10:21:48 +00:00
g_refresh_critsec . Enter ( ) ;
2010-07-03 08:04:10 +00:00
if ( g_wiimotes [ _WiimoteNumber ] )
g_wiimotes [ _WiimoteNumber ] - > Update ( ) ;
2010-07-29 10:21:48 +00:00
g_refresh_critsec . Leave ( ) ;
2010-07-03 08:04:10 +00:00
}
void StateChange ( PLUGIN_EMUSTATE newState )
{
g_wiimote_critsec . Enter ( ) ; // enter
// TODO: disable/enable auto reporting, maybe
g_wiimote_critsec . Leave ( ) ; // leave
}
THREAD_RETURN WiimoteThreadFunc ( void * arg )
{
2010-07-29 10:21:48 +00:00
Common : : SetCurrentThreadName ( " Wiimote Thread " ) ;
2010-07-03 20:14:57 +00:00
2010-07-03 08:04:10 +00:00
while ( g_run_wiimote_thread )
{
g_wiimote_critsec . Enter ( ) ; // enter
for ( unsigned int i = 0 ; i < MAX_WIIMOTES ; + + i )
if ( g_wiimotes [ i ] )
2010-07-29 10:21:48 +00:00
{
2010-07-03 08:04:10 +00:00
g_wiimotes [ i ] - > Read ( ) ;
2010-07-29 10:21:48 +00:00
g_wiimotes [ i ] - > Write ( ) ;
}
2010-07-03 08:04:10 +00:00
g_wiimote_critsec . Leave ( ) ; // leave
Common : : SleepCurrentThread ( 1 ) ;
}
return 0 ;
}
} ; // end of namespace