diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 8d290ff259..6fe518cb83 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -138,7 +138,7 @@ if(WIN32) set(SRCS ${SRCS} Src/HW/BBA-TAP/TAP_Win32.cpp Src/stdafx.cpp Src/HW/WiimoteReal/IOWin.cpp) elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set(SRCS ${SRCS} Src/HW/BBA-TAP/TAP_Apple.cpp Src/HW/WiimoteReal/IOOsx.mm) + set(SRCS ${SRCS} Src/HW/BBA-TAP/TAP_Apple.cpp Src/HW/WiimoteReal/IOdarwin.mm) elseif(UNIX) set(SRCS ${SRCS} Src/HW/BBA-TAP/TAP_Unix.cpp) if((${CMAKE_SYSTEM_NAME} MATCHES "Linux") AND BLUEZ_FOUND) diff --git a/Source/Core/Core/Src/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/Src/HW/WiimoteEmu/WiimoteEmu.cpp index 1589f8f7d0..e34b587169 100644 --- a/Source/Core/Core/Src/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/Src/HW/WiimoteEmu/WiimoteEmu.cpp @@ -695,7 +695,8 @@ void Wiimote::Update() g_refresh_critsec.Enter(); if (g_wiimotes[m_index]) { - u8* const real_data = g_wiimotes[m_index]->ProcessReadQueue(); + Report rpt = g_wiimotes[m_index]->ProcessReadQueue(); + const u8 *real_data = rpt.first; if (real_data) { switch (real_data[1]) @@ -757,11 +758,12 @@ void Wiimote::Update() // copy over report from real-wiimote if (-1 == rptf_size) { - memcpy(data, real_data, MAX_PAYLOAD); - rptf_size = MAX_PAYLOAD; + memcpy(data, real_data, rpt.second); + rptf_size = rpt.second; } - if (real_data != g_wiimotes[m_index]->m_last_data_report) + if (real_data != g_wiimotes[m_index]->\ + m_last_data_report.first) delete[] real_data; } } diff --git a/Source/Core/Core/Src/HW/WiimoteReal/IONix.cpp b/Source/Core/Core/Src/HW/WiimoteReal/IONix.cpp index cc8a51a8d1..5ce8447946 100644 --- a/Source/Core/Core/Src/HW/WiimoteReal/IONix.cpp +++ b/Source/Core/Core/Src/HW/WiimoteReal/IONix.cpp @@ -196,14 +196,14 @@ void Wiimote::RealDisconnect() in_sock = -1; } -unsigned char *Wiimote::IORead() +int Wiimote::IORead(unsigned char *buf) { struct timeval tv; fd_set fds; int r; if (!IsConnected()) - return NULL; + return 0; // Block select for 1/2000th of a second tv.tv_sec = 0; @@ -215,15 +215,14 @@ unsigned char *Wiimote::IORead() if (select(in_sock + 1, &fds, NULL, NULL, &tv) == -1) { ERROR_LOG(WIIMOTE, "Unable to select wiimote %i input socket.", index + 1); - return NULL; + return 0; } if (!FD_ISSET(in_sock, &fds)) - return NULL; + return 0; // Read the pending message into the buffer - unsigned char *buffer = new unsigned char[MAX_PAYLOAD]; - r = read(in_sock, buffer, sizeof(unsigned char) * MAX_PAYLOAD); + r = read(in_sock, buf, MAX_PAYLOAD); if (r == -1) { // Error reading data @@ -237,17 +236,15 @@ unsigned char *Wiimote::IORead() RealDisconnect(); } - delete[] buffer; - return NULL; + return 0; } if (!r) { // Disconnect RealDisconnect(); - delete[] buffer; - return NULL; + return 0; } - return buffer; + return r; } int Wiimote::IOWrite(unsigned char* buf, int len) diff --git a/Source/Core/Core/Src/HW/WiimoteReal/IOWin.cpp b/Source/Core/Core/Src/HW/WiimoteReal/IOWin.cpp index bbd557ce6f..2df7ae216a 100644 --- a/Source/Core/Core/Src/HW/WiimoteReal/IOWin.cpp +++ b/Source/Core/Core/Src/HW/WiimoteReal/IOWin.cpp @@ -255,18 +255,17 @@ void Wiimote::RealDisconnect() ResetEvent(&hid_overlap); } -unsigned char *Wiimote::IORead() +int Wiimote::IORead(unsigned char* buf) { DWORD b, r; init_lib(); if (!IsConnected()) - return NULL; + return 0; - unsigned char *buffer = new unsigned char[MAX_PAYLOAD]; - *buffer = 0; - if (!ReadFile(dev_handle, buffer, sizeof(unsigned char) * MAX_PAYLOAD, &b, &hid_overlap)) + *buf = 0; + if (!ReadFile(dev_handle, buf, MAX_PAYLOAD, &b, &hid_overlap)) { // Partial read b = GetLastError(); @@ -275,8 +274,7 @@ unsigned char *Wiimote::IORead() { // Remote disconnect RealDisconnect(); - delete[] buffer; - return NULL; + return 0; } r = WaitForSingleObject(hid_overlap.hEvent, WIIMOTE_DEFAULT_TIMEOUT); @@ -284,36 +282,33 @@ unsigned char *Wiimote::IORead() { // Timeout - cancel and continue - if (*buffer) + if (*buf) WARN_LOG(WIIMOTE, "Packet ignored. This may indicate a problem (timeout is %i ms).", WIIMOTE_DEFAULT_TIMEOUT); CancelIo(dev_handle); ResetEvent(hid_overlap.hEvent); - delete[] buffer; - return NULL; + return 0; } else if (r == WAIT_FAILED) { WARN_LOG(WIIMOTE, "A wait error occured on reading from wiimote %i.", index + 1); - delete[] buffer; - return NULL; + return 0; } if (!GetOverlappedResult(dev_handle, &hid_overlap, &b, 0)) { - delete[] buffer; - return NULL; + return 0; } } // This needs to be done even if ReadFile fails, essential during init // Move the data over one, so we can add back in data report indicator byte (here, 0xa1) - memmove(buffer + 1, buffer, sizeof(unsigned char) * MAX_PAYLOAD - 1); - buffer[0] = 0xa1; + memmove(buf + 1, buf, MAX_PAYLOAD - 1); + buf[0] = 0xa1; ResetEvent(hid_overlap.hEvent); - return buffer; + return MAX_PAYLOAD; // XXX } int Wiimote::IOWrite(unsigned char* buf, int len) @@ -324,7 +319,7 @@ int Wiimote::IOWrite(unsigned char* buf, int len) init_lib(); if (!IsConnected()) - return NULL; + return 0; switch (stack) { diff --git a/Source/Core/Core/Src/HW/WiimoteReal/IOdarwin.mm b/Source/Core/Core/Src/HW/WiimoteReal/IOdarwin.mm new file mode 100644 index 0000000000..636035887c --- /dev/null +++ b/Source/Core/Core/Src/HW/WiimoteReal/IOdarwin.mm @@ -0,0 +1,282 @@ +#import +extern "C" OSErr UpdateSystemActivity(UInt8 activity); +#define BLUETOOTH_VERSION_USE_CURRENT +#import + +#include "Common.h" +#include "WiimoteReal.h" + +@interface SearchBT: NSObject { +@public + unsigned int maxDevices; +} +@end + +@implementation SearchBT +- (void) deviceInquiryComplete: (IOBluetoothDeviceInquiry *) sender + error: (IOReturn) error + aborted: (BOOL) aborted +{ + CFRunLoopStop(CFRunLoopGetCurrent()); +} + +- (void) deviceInquiryDeviceFound: (IOBluetoothDeviceInquiry *) sender + device: (IOBluetoothDevice *) device +{ + NOTICE_LOG(WIIMOTE, "Discovered bluetooth device at %s: %s", + [[device getAddressString] UTF8String], + [[device getName] UTF8String]); + + if ([[sender foundDevices] count] == maxDevices) + [sender stop]; +} +@end + +@interface ConnectBT: NSObject {} +@end + +@implementation ConnectBT +- (void) l2capChannelData: (IOBluetoothL2CAPChannel *) l2capChannel + data: (unsigned char *) data + length: (NSUInteger) length +{ + IOBluetoothDevice *device = [l2capChannel getDevice]; + WiimoteReal::Wiimote *wm = NULL; + + for (int i = 0; i < MAX_WIIMOTES; i++) { + if (WiimoteReal::g_wiimotes[i] == NULL) + continue; + if ([device isEqual: WiimoteReal::g_wiimotes[i]->btd] == TRUE) + wm = WiimoteReal::g_wiimotes[i]; + } + if (wm == NULL) { + WARN_LOG(WIIMOTE, "Received packet for unknown wiimote"); + return; + } + + if (length > MAX_PAYLOAD) { + WARN_LOG(WIIMOTE, "Dropping packet for wiimote %i, too large", + wm->index + 1); + return; + } + + if (wm->queue[wm->writer].len != 0) { + WARN_LOG(WIIMOTE, "Dropping packet for wiimote %i, queue full", + wm->index + 1); + return; + } + + memcpy(wm->queue[wm->writer].data, data, length); + wm->queue[wm->writer].len = length; + + wm->writer++; + wm->outstanding++; + if (wm->writer == QUEUE_SIZE) + wm->writer = 0; + + if (wm->outstanding > wm->watermark) { + wm->watermark = wm->outstanding; + WARN_LOG(WIIMOTE, "New queue watermark %i for wiimote %i", + wm->watermark, wm->index + 1); + } + + CFRunLoopStop(CFRunLoopGetCurrent()); + + (void)UpdateSystemActivity(1); +} + +- (void) l2capChannelClosed: (IOBluetoothL2CAPChannel *) l2capChannel +{ + IOBluetoothDevice *device = [l2capChannel getDevice]; + WiimoteReal::Wiimote *wm = NULL; + + for (int i = 0; i < MAX_WIIMOTES; i++) { + if (WiimoteReal::g_wiimotes[i] == NULL) + continue; + if ([device isEqual: WiimoteReal::g_wiimotes[i]->btd] == TRUE) + wm = WiimoteReal::g_wiimotes[i]; + } + if (wm == NULL) { + WARN_LOG(WIIMOTE, "Received packet for unknown wiimote"); + return; + } + + WARN_LOG(WIIMOTE, "L2CAP channel was closed for wiimote %i", + wm->index + 1); + + if (l2capChannel == wm->cchan) + wm->cchan = nil; + + if (l2capChannel == wm->ichan) + wm->ichan = nil; +} +@end + +namespace WiimoteReal +{ + +// Find wiimotes. +// wm is an array of max_wiimotes wiimotes +// Returns the total number of found wiimotes. +int FindWiimotes(Wiimote **wm, int max_wiimotes) +{ + IOBluetoothHostController *bth; + IOBluetoothDeviceInquiry *bti; + SearchBT *sbt; + NSEnumerator *en; + int found_devices = 0, found_wiimotes = 0; + + // Count the number of already found wiimotes + for (int i = 0; i < MAX_WIIMOTES; ++i) + if (wm[i]) + found_wiimotes++; + + bth = [[IOBluetoothHostController alloc] init]; + if ([bth addressAsString] == nil) { + WARN_LOG(WIIMOTE, "No bluetooth host controller"); + [bth release]; + return found_wiimotes; + } + + sbt = [[SearchBT alloc] init]; + sbt->maxDevices = max_wiimotes; + bti = [[IOBluetoothDeviceInquiry alloc] init]; + [bti setDelegate: sbt]; + [bti setInquiryLength: 5]; + [bti setSearchCriteria: kBluetoothServiceClassMajorAny + majorDeviceClass: kBluetoothDeviceClassMajorPeripheral + minorDeviceClass: kBluetoothDeviceClassMinorPeripheral2Joystick + ]; + [bti setUpdateNewDeviceNames: FALSE]; + + if ([bti start] == kIOReturnSuccess) + [bti retain]; + else + ERROR_LOG(WIIMOTE, "Unable to do bluetooth discovery"); + + CFRunLoopRun(); + + [bti stop]; + found_devices = [[bti foundDevices] count]; + + NOTICE_LOG(WIIMOTE, "Found %i bluetooth device%c", found_devices, + found_devices == 1 ? '\0' : 's'); + + en = [[bti foundDevices] objectEnumerator]; + for (int i = 0; (i < found_devices) && (found_wiimotes < max_wiimotes); i++) { + IOBluetoothDevice *tmp_btd = [en nextObject]; + + bool new_wiimote = true; + // Determine if this wiimote has already been found. + for (int j = 0; j < MAX_WIIMOTES && new_wiimote; ++j) { + if (wm[j] && [tmp_btd isEqual: wm[j]->btd] == TRUE) + new_wiimote = false; + } + + if (new_wiimote) { + // Find an unused slot + unsigned int k = 0; + for ( ; k < MAX_WIIMOTES && + !(WIIMOTE_SRC_REAL & g_wiimote_sources[k] && !wm[k]); ++k) + {}; + wm[k] = new Wiimote(k); + wm[k]->btd = tmp_btd; + found_wiimotes++; + } + } + + [bth release]; + [bti release]; + [sbt release]; + + return found_wiimotes; +} + +// Connect to a wiimote with a known address. +bool Wiimote::Connect() +{ + ConnectBT *cbt = [[ConnectBT alloc] init]; + + if (IsConnected()) return false; + + [btd openL2CAPChannelSync: &cchan + withPSM: kBluetoothL2CAPPSMHIDControl delegate: cbt]; + [btd openL2CAPChannelSync: &ichan + withPSM: kBluetoothL2CAPPSMHIDInterrupt delegate: cbt]; + if (ichan == NULL || cchan == NULL) { + ERROR_LOG(WIIMOTE, "Unable to open L2CAP channels " + "for wiimote %i", index + 1); + RealDisconnect(); + return false; + } + + NOTICE_LOG(WIIMOTE, "Connected to wiimote %i at %s", + index + 1, [[btd getAddressString] UTF8String]); + + m_connected = true; + + Handshake(); + + SetLEDs(WIIMOTE_LED_1 << index); + + [cbt release]; + + return true; +} + +// Disconnect a wiimote. +void Wiimote::RealDisconnect() +{ + if (!IsConnected()) + return; + + NOTICE_LOG(WIIMOTE, "Disconnecting wiimote %i", index + 1); + + m_connected = false; + + [cchan closeChannel]; + [ichan closeChannel]; + [btd closeConnection]; +} + +int Wiimote::IORead(unsigned char *buf) +{ + int bytes; + + if (!IsConnected()) + return 0; + + if (outstanding == 0) + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, true); + + if (queue[reader].len == 0) + return 0; + + bytes = queue[reader].len; + memcpy(buf, queue[reader].data, bytes); + queue[reader].len = 0; + + reader++; + outstanding--; + if (reader == QUEUE_SIZE) + reader = 0; + + if (buf[0] == '\0') + bytes = 0; + + return bytes; +} + +int Wiimote::IOWrite(unsigned char *buf, int len) +{ + IOReturn ret; + + ret = [cchan writeAsync: buf length: len refcon: nil]; + + if (ret == kIOReturnSuccess) + return len; + else + return 0; +} + +}; diff --git a/Source/Core/Core/Src/HW/WiimoteReal/WiimoteReal.cpp b/Source/Core/Core/Src/HW/WiimoteReal/WiimoteReal.cpp index 4f715c76eb..a29261dcc3 100644 --- a/Source/Core/Core/Src/HW/WiimoteReal/WiimoteReal.cpp +++ b/Source/Core/Core/Src/HW/WiimoteReal/WiimoteReal.cpp @@ -55,7 +55,7 @@ Wiimote::Wiimote(const unsigned int _index) #elif defined(_WIN32) , dev_handle(0), stack(MSBT_STACK_UNKNOWN) #endif - , leds(0), m_last_data_report(NULL), m_channel(0), m_connected(false) + , leds(0), m_channel(0), m_connected(false) { #ifdef __APPLE__ memset(queue, 0, sizeof queue); @@ -67,6 +67,7 @@ Wiimote::Wiimote(const unsigned int _index) bdaddr = (bdaddr_t){{0, 0, 0, 0, 0, 0}}; #endif + ClearReadQueue(); DisableDataReporting(); } @@ -106,15 +107,16 @@ void Wiimote::DisableDataReporting() void Wiimote::ClearReadQueue() { - if (m_last_data_report) + Report rpt; + + if (m_last_data_report.first) { - delete[] m_last_data_report; - m_last_data_report = NULL; + delete[] m_last_data_report.first; + m_last_data_report.first = NULL; } - u8 *rpt; while (m_read_reports.Pop(rpt)) - delete[] rpt; + delete[] rpt.first; } void Wiimote::ControlChannel(const u16 channel, const void* const data, const u32 size) @@ -148,7 +150,7 @@ void Wiimote::InterruptChannel(const u16 channel, const void* const data, const m_channel = channel; // this right? - Wiimote::Report rpt; + Report rpt; rpt.first = new u8[size]; rpt.second = (u8)size; memcpy(rpt.first, (u8*)data, size); @@ -159,10 +161,10 @@ void Wiimote::InterruptChannel(const u16 channel, const void* const data, const //if (WM_REPORT_MODE == ((u8*)data)[1]) //{ // // also delete the last data report - // if (m_last_data_report) + // if (m_last_data_report.first) // { - // delete[] m_last_data_report; - // m_last_data_report = NULL; + // delete[] m_last_data_report.first; + // m_last_data_report.first = NULL; // } // // nice var names :p, this seems to be this one @@ -175,18 +177,18 @@ void Wiimote::InterruptChannel(const u16 channel, const void* const data, const bool Wiimote::Read() { - u8* rpt; - if ((rpt = IORead())) - { - if (m_channel) - { - // Add it to queue - m_read_reports.Push(rpt); - } + Report rpt; + rpt.first = new unsigned char[MAX_PAYLOAD]; + rpt.second = IORead(rpt.first); + + if (rpt.second > 0 && m_channel > 0) { + // Add it to queue + m_read_reports.Push(rpt); return true; } + delete rpt.first; return false; } @@ -205,13 +207,13 @@ bool Wiimote::Write() } // Returns the next report that should be sent -u8* Wiimote::ProcessReadQueue() +Report Wiimote::ProcessReadQueue() { // Pop through the queued reports - u8* rpt = m_last_data_report; + Report rpt = m_last_data_report; while (m_read_reports.Pop(rpt)) { - if (rpt[1] >= WM_REPORT_CORE) + if (rpt.first[1] >= WM_REPORT_CORE) // A data report m_last_data_report = rpt; else @@ -226,15 +228,16 @@ u8* Wiimote::ProcessReadQueue() void Wiimote::Update() { // Pop through the queued reports - u8* const rpt = ProcessReadQueue(); + Report const rpt = ProcessReadQueue(); // Send the report - if (rpt && m_channel) - Core::Callback_WiimoteInterruptChannel(index, m_channel, rpt, MAX_PAYLOAD); + if (rpt.first != NULL && m_channel > 0) + Core::Callback_WiimoteInterruptChannel(index, m_channel, + rpt.first, rpt.second); // Delete the data if it isn't also the last data rpt if (rpt != m_last_data_report) - delete[] rpt; + delete[] rpt.first; } void Wiimote::Disconnect() diff --git a/Source/Core/Core/Src/HW/WiimoteReal/WiimoteReal.h b/Source/Core/Core/Src/HW/WiimoteReal/WiimoteReal.h index 06d0be40f0..60a1ab0d57 100644 --- a/Source/Core/Core/Src/HW/WiimoteReal/WiimoteReal.h +++ b/Source/Core/Core/Src/HW/WiimoteReal/WiimoteReal.h @@ -37,6 +37,9 @@ enum WIIMOTE_SRC_HYBRID = 3, // emu + real }; +// Pointer to data, and size of data +typedef std::pair Report; + namespace WiimoteReal { @@ -51,7 +54,7 @@ public: void InterruptChannel(const u16 channel, const void* const data, const u32 size); void Update(); - u8* ProcessReadQueue(); + Report ProcessReadQueue(); bool Read(); bool Write(); @@ -62,9 +65,6 @@ public: void Rumble(); void SendPacket(const u8 rpt_id, const void* const data, const unsigned int size); - // Pointer to data, and size of data - typedef std::pair Report; - const unsigned int index; #if defined(__APPLE__) @@ -95,7 +95,7 @@ public: unsigned char leds; // Currently lit leds protected: - u8 *m_last_data_report; + Report m_last_data_report; u16 m_channel; private: @@ -104,11 +104,11 @@ private: bool SendRequest(unsigned char report_type, unsigned char* data, int length); bool Handshake(); void SetLEDs(int leds); - unsigned char *IORead(); + int IORead(unsigned char* buf); int IOWrite(unsigned char* buf, int len); - bool m_connected; - Common::FifoQueue m_read_reports; + bool m_connected; + Common::FifoQueue m_read_reports; Common::FifoQueue m_write_reports; }; diff --git a/Source/Core/Core/Src/SConscript b/Source/Core/Core/Src/SConscript index 182c1f0392..da96196739 100644 --- a/Source/Core/Core/Src/SConscript +++ b/Source/Core/Core/Src/SConscript @@ -148,7 +148,7 @@ libs = [ if sys.platform == 'win32': files += [ "HW/BBA-TAP/TAP_Win32.cpp", "stdafx.cpp", "HW/WiimoteReal/IOWin.cpp" ] elif sys.platform == 'darwin': - files += [ "HW/BBA-TAP/TAP_Apple.cpp", "HW/WiimoteReal/IOOsx.mm" ] + files += [ "HW/BBA-TAP/TAP_Apple.cpp", "HW/WiimoteReal/IOdarwin.mm" ] else: files += [ "HW/BBA-TAP/TAP_Unix.cpp", "HW/WiimoteReal/IONix.cpp" ]