// 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 #include "Common.h" #include "IniFile.h" #include "StringUtil.h" #include "Timer.h" #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; // removed g_wiimotes_lastfound because Refresh() isn't taking advantage of it volatile bool g_run_wiimote_thread = false; Common::Thread *g_wiimote_threads[MAX_WIIMOTES] = {}; Common::CriticalSection g_refresh_critsec; THREAD_RETURN WiimoteThreadFunc(void* arg); void StartWiimoteThreads(); void StopWiimoteThreads(); Wiimote *g_wiimotes[MAX_WIIMOTES]; Wiimote::Wiimote(wiimote_t* const _wiimote, const unsigned int _index) : index(_index) , wiimote(_wiimote) , m_last_data_report(NULL) , m_channel(0) { // disable reporting DisableDataReporting(); // set LEDs wiiuse_set_leds(wiimote, WIIMOTE_LED_1 << index); // TODO: make Dolphin connect wiimote, maybe } Wiimote::~Wiimote() { ClearReadQueue(); // clear write queue Report rpt; while (m_write_reports.Pop(rpt)) delete[] rpt.first; // disable reporting / wiiuse might do this on shutdown anyway, o well, don't know for sure DisableDataReporting(); } // 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); } void Wiimote::DisableDataReporting() { wm_report_mode rpt; rpt.mode = WM_REPORT_CORE; rpt.all_the_time = 0; rpt.continuous = 0; rpt.rumble = 0; SendPacket(WM_REPORT_MODE, &rpt, sizeof(rpt)); } void Wiimote::ClearReadQueue() { if (m_last_data_report) { delete[] m_last_data_report; m_last_data_report = NULL; } u8 *rpt; while (m_read_reports.Pop(rpt)) delete[] rpt; } 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); const hid_packet* const hidp = (hid_packet*)data; if (hidp->type == HID_TYPE_SET_REPORT) { u8 handshake_ok = HID_HANDSHAKE_SUCCESS; g_WiimoteInitialize.pWiimoteInterruptChannel(index, channel, &handshake_ok, sizeof(handshake_ok)); } } } void Wiimote::InterruptChannel(const u16 channel, const void* const data, const u32 size) { if (0 == m_channel) // first interrupt/control channel sent { ClearReadQueue(); // request status wm_request_status rpt; rpt.rumble = 0; SendPacket(WM_REQUEST_STATUS, &rpt, sizeof(rpt)); } m_channel = channel; // this right? Wiimote::Report rpt; rpt.first = new u8[size]; rpt.second = (u8)size; memcpy(rpt.first, (u8*)data, size); // 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*)data)[1]) { // also delete the last data report if (m_last_data_report) { delete[] m_last_data_report; m_last_data_report = NULL; } // nice var names :p, this seems to be this one ((wm_report_mode*)(rpt.first + 2))->all_the_time = false; //((wm_report_mode*)(data + 2))->continuous = false; } m_write_reports.Push(rpt); } bool Wiimote::Read() { if (wiiuse_io_read(wiimote)) { if (m_channel) { // add it to queue u8* const rpt = new u8[MAX_PAYLOAD]; memcpy(rpt, wiimote->event_buf, MAX_PAYLOAD); m_read_reports.Push(rpt); } return true; } return false; } bool Wiimote::Write() { Report rpt; if (m_write_reports.Pop(rpt)) { wiiuse_io_write(wiimote, rpt.first, rpt.second); delete[] rpt.first; return true; } return false; } // returns the next report that should be sent u8* Wiimote::ProcessReadQueue() { // pop through the queued reports u8* rpt = m_last_data_report; while (m_read_reports.Pop(rpt)) { // a data report if (rpt[1] >= WM_REPORT_CORE) m_last_data_report = rpt; // some other kind of report else return rpt; } // the queue was empty, or there were only data reports return rpt; } void Wiimote::Update() { // pop through the queued reports u8* const rpt = ProcessReadQueue(); // send the report if (rpt && m_channel) g_WiimoteInitialize.pWiimoteInterruptChannel(index, m_channel, rpt, MAX_PAYLOAD); // delete the data if it isn't also the last data rpt if (rpt != m_last_data_report) delete[] rpt; } void Wiimote::Disconnect() { m_channel = 0; // disable reporting DisableDataReporting(); } void LoadSettings() { 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; iInterruptChannel(_channelID, _pData, _Size); g_refresh_critsec.Leave(); } void ControlChannel(int _WiimoteNumber, u16 _channelID, const void* _pData, u32 _Size) { g_refresh_critsec.Enter(); if (g_wiimotes[_WiimoteNumber]) g_wiimotes[_WiimoteNumber]->ControlChannel(_channelID, _pData, _Size); g_refresh_critsec.Leave(); } // Read the Wiimote once void Update(int _WiimoteNumber) { g_refresh_critsec.Enter(); if (g_wiimotes[_WiimoteNumber]) g_wiimotes[_WiimoteNumber]->Update(); g_refresh_critsec.Leave(); } void StateChange(PLUGIN_EMUSTATE newState) { //g_refresh_critsec.Enter(); // enter // TODO: disable/enable auto reporting, maybe //g_refresh_critsec.Leave(); // leave } void StartWiimoteThreads() { g_run_wiimote_thread = true; for (unsigned int i=0; iWaitForDeath(); delete g_wiimote_threads[i]; g_wiimote_threads[i] = NULL; } } THREAD_RETURN WiimoteThreadFunc(void* arg) { Wiimote* const wiimote = (Wiimote*)arg; { char thname[] = "Wiimote # Thread"; thname[8] = (char)('1' + wiimote->index); Common::SetCurrentThreadName(thname); } // rumble briefly wiiuse_rumble(wiimote->wiimote, 1); SLEEP(200); wiiuse_rumble(wiimote->wiimote, 0); // main loop while (g_run_wiimote_thread) { wiimote->Write(); if (false == wiimote->Read()) Common::SleepCurrentThread(1); // sleep if there was nothing to read } return 0; } }; // end of namespace