Merge pull request #1596 from Tilka/wiimote

WiimoteReal: use inheritance instead of #ifdef
This commit is contained in:
Ryan Houdek 2014-12-07 01:03:08 -06:00
commit dec2f9f89b
6 changed files with 305 additions and 321 deletions

View file

@ -9,9 +9,7 @@ namespace WiimoteReal
{ {
WiimoteScanner::WiimoteScanner() WiimoteScanner::WiimoteScanner()
{ {}
return;
}
WiimoteScanner::~WiimoteScanner() WiimoteScanner::~WiimoteScanner()
{} {}
@ -30,43 +28,4 @@ bool WiimoteScanner::IsReady() const
return false; return false;
} }
void Wiimote::InitInternal()
{}
void Wiimote::TeardownInternal()
{}
bool Wiimote::ConnectInternal()
{
return 0;
}
void Wiimote::DisconnectInternal()
{
return;
}
bool Wiimote::IsConnected() const
{
return false;
}
void Wiimote::IOWakeup()
{}
int Wiimote::IORead(u8* buf)
{
return 0;
}
int Wiimote::IOWrite(const u8* buf, size_t len)
{
return 0;
}
void Wiimote::EnablePowerAssertionInternal()
{}
void Wiimote::DisablePowerAssertionInternal()
{}
}; };

View file

@ -13,6 +13,28 @@
namespace WiimoteReal namespace WiimoteReal
{ {
class WiimoteLinux final : public Wiimote
{
public:
WiimoteLinux(bdaddr_t bdaddr);
~WiimoteLinux() override;
protected:
bool ConnectInternal() override;
void DisconnectInternal() override;
bool IsConnected() const override;
void IOWakeup() override;
int IORead(u8* buf) override;
int IOWrite(u8 const* buf, size_t len) override;
private:
bdaddr_t m_bdaddr; // Bluetooth address
int m_cmd_sock; // Command socket
int m_int_sock; // Interrupt socket
int m_wakeup_pipe_w;
int m_wakeup_pipe_r;
};
WiimoteScanner::WiimoteScanner() WiimoteScanner::WiimoteScanner()
: m_want_wiimotes() : m_want_wiimotes()
, device_id(-1) , device_id(-1)
@ -102,8 +124,7 @@ void WiimoteScanner::FindWiimotes(std::vector<Wiimote*> & found_wiimotes, Wiimot
char bdaddr_str[18] = {}; char bdaddr_str[18] = {};
ba2str(&scan_infos[i].bdaddr, bdaddr_str); ba2str(&scan_infos[i].bdaddr, bdaddr_str);
auto* const wm = new Wiimote; Wiimote* wm = new WiimoteLinux(scan_infos[i].bdaddr);
wm->bdaddr = scan_infos[i].bdaddr;
if (IsBalanceBoardName(name)) if (IsBalanceBoardName(name))
{ {
found_board = wm; found_board = wm;
@ -120,10 +141,10 @@ void WiimoteScanner::FindWiimotes(std::vector<Wiimote*> & found_wiimotes, Wiimot
} }
void Wiimote::InitInternal() WiimoteLinux::WiimoteLinux(bdaddr_t bdaddr) : Wiimote(), m_bdaddr(bdaddr)
{ {
cmd_sock = -1; m_cmd_sock = -1;
int_sock = -1; m_int_sock = -1;
int fds[2]; int fds[2];
if (pipe(fds)) if (pipe(fds))
@ -131,69 +152,69 @@ void Wiimote::InitInternal()
ERROR_LOG(WIIMOTE, "pipe failed"); ERROR_LOG(WIIMOTE, "pipe failed");
abort(); abort();
} }
wakeup_pipe_w = fds[1]; m_wakeup_pipe_w = fds[1];
wakeup_pipe_r = fds[0]; m_wakeup_pipe_r = fds[0];
bdaddr = (bdaddr_t){{0, 0, 0, 0, 0, 0}};
} }
void Wiimote::TeardownInternal() WiimoteLinux::~WiimoteLinux()
{ {
close(wakeup_pipe_w); Shutdown();
close(wakeup_pipe_r); close(m_wakeup_pipe_w);
close(m_wakeup_pipe_r);
} }
// Connect to a wiimote with a known address. // Connect to a wiimote with a known address.
bool Wiimote::ConnectInternal() bool WiimoteLinux::ConnectInternal()
{ {
sockaddr_l2 addr; sockaddr_l2 addr;
addr.l2_family = AF_BLUETOOTH; addr.l2_family = AF_BLUETOOTH;
addr.l2_bdaddr = bdaddr; addr.l2_bdaddr = m_bdaddr;
addr.l2_cid = 0; addr.l2_cid = 0;
// Output channel // Output channel
addr.l2_psm = htobs(WM_OUTPUT_CHANNEL); addr.l2_psm = htobs(WM_OUTPUT_CHANNEL);
if ((cmd_sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) == -1 || if ((m_cmd_sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) == -1 ||
connect(cmd_sock, (sockaddr*)&addr, sizeof(addr)) < 0) connect(m_cmd_sock, (sockaddr*)&addr, sizeof(addr)) < 0)
{ {
DEBUG_LOG(WIIMOTE, "Unable to open output socket to wiimote."); DEBUG_LOG(WIIMOTE, "Unable to open output socket to wiimote.");
close(cmd_sock); close(m_cmd_sock);
cmd_sock = -1; m_cmd_sock = -1;
return false; return false;
} }
// Input channel // Input channel
addr.l2_psm = htobs(WM_INPUT_CHANNEL); addr.l2_psm = htobs(WM_INPUT_CHANNEL);
if ((int_sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) == -1 || if ((m_int_sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) == -1 ||
connect(int_sock, (sockaddr*)&addr, sizeof(addr)) < 0) connect(m_int_sock, (sockaddr*)&addr, sizeof(addr)) < 0)
{ {
DEBUG_LOG(WIIMOTE, "Unable to open input socket from wiimote."); DEBUG_LOG(WIIMOTE, "Unable to open input socket from wiimote.");
close(int_sock); close(m_int_sock);
close(cmd_sock); close(m_cmd_sock);
int_sock = cmd_sock = -1; m_int_sock = m_cmd_sock = -1;
return false; return false;
} }
return true; return true;
} }
void Wiimote::DisconnectInternal() void WiimoteLinux::DisconnectInternal()
{ {
close(cmd_sock); close(m_cmd_sock);
close(int_sock); close(m_int_sock);
cmd_sock = -1; m_cmd_sock = -1;
int_sock = -1; m_int_sock = -1;
} }
bool Wiimote::IsConnected() const bool WiimoteLinux::IsConnected() const
{ {
return cmd_sock != -1;// && int_sock != -1; return m_cmd_sock != -1;// && int_sock != -1;
} }
void Wiimote::IOWakeup() void WiimoteLinux::IOWakeup()
{ {
char c = 0; char c = 0;
if (write(wakeup_pipe_w, &c, 1) != 1) if (write(m_wakeup_pipe_w, &c, 1) != 1)
{ {
ERROR_LOG(WIIMOTE, "Unable to write to wakeup pipe."); ERROR_LOG(WIIMOTE, "Unable to write to wakeup pipe.");
} }
@ -202,46 +223,46 @@ void Wiimote::IOWakeup()
// positive = read packet // positive = read packet
// negative = didn't read packet // negative = didn't read packet
// zero = error // zero = error
int Wiimote::IORead(u8* buf) int WiimoteLinux::IORead(u8* buf)
{ {
// Block select for 1/2000th of a second // Block select for 1/2000th of a second
fd_set fds; fd_set fds;
FD_ZERO(&fds); FD_ZERO(&fds);
FD_SET(int_sock, &fds); FD_SET(m_int_sock, &fds);
FD_SET(wakeup_pipe_r, &fds); FD_SET(m_wakeup_pipe_r, &fds);
if (select(int_sock + 1, &fds, nullptr, nullptr, nullptr) == -1) if (select(m_int_sock + 1, &fds, nullptr, nullptr, nullptr) == -1)
{ {
ERROR_LOG(WIIMOTE, "Unable to select wiimote %i input socket.", index + 1); ERROR_LOG(WIIMOTE, "Unable to select wiimote %i input socket.", m_index + 1);
return -1; return -1;
} }
if (FD_ISSET(wakeup_pipe_r, &fds)) if (FD_ISSET(m_wakeup_pipe_r, &fds))
{ {
char c; char c;
if (read(wakeup_pipe_r, &c, 1) != 1) if (read(m_wakeup_pipe_r, &c, 1) != 1)
{ {
ERROR_LOG(WIIMOTE, "Unable to read from wakeup pipe."); ERROR_LOG(WIIMOTE, "Unable to read from wakeup pipe.");
} }
return -1; return -1;
} }
if (!FD_ISSET(int_sock, &fds)) if (!FD_ISSET(m_int_sock, &fds))
return -1; return -1;
// Read the pending message into the buffer // Read the pending message into the buffer
int r = read(int_sock, buf, MAX_PAYLOAD); int r = read(m_int_sock, buf, MAX_PAYLOAD);
if (r == -1) if (r == -1)
{ {
// Error reading data // Error reading data
ERROR_LOG(WIIMOTE, "Receiving data from wiimote %i.", index + 1); ERROR_LOG(WIIMOTE, "Receiving data from wiimote %i.", m_index + 1);
if (errno == ENOTCONN) if (errno == ENOTCONN)
{ {
// This can happen if the bluetooth dongle is disconnected // This can happen if the bluetooth dongle is disconnected
ERROR_LOG(WIIMOTE, "Bluetooth appears to be disconnected. " ERROR_LOG(WIIMOTE, "Bluetooth appears to be disconnected. "
"Wiimote %i will be disconnected.", index + 1); "Wiimote %i will be disconnected.", m_index + 1);
} }
r = 0; r = 0;
@ -250,14 +271,9 @@ int Wiimote::IORead(u8* buf)
return r; return r;
} }
int Wiimote::IOWrite(u8 const* buf, size_t len) int WiimoteLinux::IOWrite(u8 const* buf, size_t len)
{ {
return write(int_sock, buf, (int)len); return write(m_int_sock, buf, (int)len);
} }
void Wiimote::EnablePowerAssertionInternal()
{}
void Wiimote::DisablePowerAssertionInternal()
{}
}; // WiimoteReal }; // WiimoteReal

View file

@ -187,10 +187,30 @@ inline void init_lib()
namespace WiimoteReal namespace WiimoteReal
{ {
class WiimoteWindows final : public Wiimote
{
public:
WiimoteWindows(const std::basic_string<TCHAR>& path);
~WiimoteWindows() override;
protected:
bool ConnectInternal() override;
void DisconnectInternal() override;
bool IsConnected() const override;
void IOWakeup() override;
int IORead(u8* buf) override;
int IOWrite(u8 const* buf, size_t len) override;
private:
std::basic_string<TCHAR> m_devicepath; // Unique wiimote reference
HANDLE m_dev_handle; // HID handle
OVERLAPPED m_hid_overlap_read; // Overlap handles
OVERLAPPED m_hid_overlap_write;
enum win_bt_stack_t m_stack; // Type of bluetooth stack to use
};
int _IOWrite(HANDLE &dev_handle, OVERLAPPED &hid_overlap_write, enum win_bt_stack_t &stack, const u8* buf, size_t len, DWORD* written); int _IOWrite(HANDLE &dev_handle, OVERLAPPED &hid_overlap_write, enum win_bt_stack_t &stack, const u8* buf, size_t len, DWORD* written);
int _IORead(HANDLE &dev_handle, OVERLAPPED &hid_overlap_read, u8* buf, int index); int _IORead(HANDLE &dev_handle, OVERLAPPED &hid_overlap_read, u8* buf, int index);
void _IOWakeup(HANDLE &dev_handle, OVERLAPPED &hid_overlap_read);
template <typename T> template <typename T>
void ProcessWiimotes(bool new_scan, T& callback); void ProcessWiimotes(bool new_scan, T& callback);
@ -272,11 +292,11 @@ void WiimoteScanner::FindWiimotes(std::vector<Wiimote*> & found_wiimotes, Wiimot
// Query the data for this device // Query the data for this device
if (SetupDiGetDeviceInterfaceDetail(device_info, &device_data, detail_data, len, nullptr, nullptr)) if (SetupDiGetDeviceInterfaceDetail(device_info, &device_data, detail_data, len, nullptr, nullptr))
{ {
auto const wm = new Wiimote; std::basic_string<TCHAR> device_path(detail_data->DevicePath);
wm->devicepath = detail_data->DevicePath; Wiimote* wm = new WiimoteWindows(device_path);
bool real_wiimote = false, is_bb = false; bool real_wiimote = false, is_bb = false;
CheckDeviceType(wm->devicepath, real_wiimote, is_bb); CheckDeviceType(device_path, real_wiimote, is_bb);
if (is_bb) if (is_bb)
{ {
found_board = wm; found_board = wm;
@ -500,14 +520,14 @@ bool WiimoteScanner::IsReady() const
} }
// Connect to a wiimote with a known device path. // Connect to a wiimote with a known device path.
bool Wiimote::ConnectInternal() bool WiimoteWindows::ConnectInternal()
{ {
if (IsConnected()) if (IsConnected())
return false; return false;
#ifdef SHARE_WRITE_WIIMOTES #ifdef SHARE_WRITE_WIIMOTES
std::lock_guard<std::mutex> lk(g_connected_wiimotes_lock); std::lock_guard<std::mutex> lk(g_connected_wiimotes_lock);
if (g_connected_wiimotes.count(devicepath) != 0) if (g_connected_wiimotes.count(m_devicepath) != 0)
return false; return false;
auto const open_flags = FILE_SHARE_READ | FILE_SHARE_WRITE; auto const open_flags = FILE_SHARE_READ | FILE_SHARE_WRITE;
@ -519,13 +539,13 @@ bool Wiimote::ConnectInternal()
auto const open_flags = FILE_SHARE_READ; auto const open_flags = FILE_SHARE_READ;
#endif #endif
dev_handle = CreateFile(devicepath.c_str(), m_dev_handle = CreateFile(m_devicepath.c_str(),
GENERIC_READ | GENERIC_WRITE, open_flags, GENERIC_READ | GENERIC_WRITE, open_flags,
nullptr, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr); nullptr, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr);
if (dev_handle == INVALID_HANDLE_VALUE) if (m_dev_handle == INVALID_HANDLE_VALUE)
{ {
dev_handle = 0; m_dev_handle = 0;
return false; return false;
} }
@ -564,52 +584,48 @@ bool Wiimote::ConnectInternal()
} }
*/ */
#ifdef SHARE_WRITE_WIIMOTES #ifdef SHARE_WRITE_WIIMOTES
g_connected_wiimotes.insert(devicepath); g_connected_wiimotes.insert(m_devicepath);
#endif #endif
return true; return true;
} }
void Wiimote::DisconnectInternal() void WiimoteWindows::DisconnectInternal()
{ {
if (!IsConnected()) if (!IsConnected())
return; return;
CloseHandle(dev_handle); CloseHandle(m_dev_handle);
dev_handle = 0; m_dev_handle = 0;
#ifdef SHARE_WRITE_WIIMOTES #ifdef SHARE_WRITE_WIIMOTES
std::lock_guard<std::mutex> lk(g_connected_wiimotes_lock); std::lock_guard<std::mutex> lk(g_connected_wiimotes_lock);
g_connected_wiimotes.erase(devicepath); g_connected_wiimotes.erase(m_devicepath);
#endif #endif
} }
void Wiimote::InitInternal() WiimoteWindows::WiimoteWindows(const std::basic_string<TCHAR>& path) : m_devicepath(path)
{ {
dev_handle = 0; m_dev_handle = 0;
stack = MSBT_STACK_UNKNOWN; m_stack = MSBT_STACK_UNKNOWN;
hid_overlap_read = OVERLAPPED(); m_hid_overlap_read = OVERLAPPED();
hid_overlap_read.hEvent = CreateEvent(nullptr, true, false, nullptr); m_hid_overlap_read.hEvent = CreateEvent(nullptr, true, false, nullptr);
hid_overlap_write = OVERLAPPED(); m_hid_overlap_write = OVERLAPPED();
hid_overlap_write.hEvent = CreateEvent(nullptr, true, false, nullptr); m_hid_overlap_write.hEvent = CreateEvent(nullptr, true, false, nullptr);
} }
void Wiimote::TeardownInternal() WiimoteWindows::~WiimoteWindows()
{ {
CloseHandle(hid_overlap_read.hEvent); Shutdown();
CloseHandle(hid_overlap_write.hEvent); CloseHandle(m_hid_overlap_read.hEvent);
CloseHandle(m_hid_overlap_write.hEvent);
} }
bool Wiimote::IsConnected() const bool WiimoteWindows::IsConnected() const
{ {
return dev_handle != 0; return m_dev_handle != 0;
}
void _IOWakeup(HANDLE &dev_handle, OVERLAPPED &hid_overlap_read)
{
SetEvent(hid_overlap_read.hEvent);
} }
// positive = read packet // positive = read packet
@ -632,7 +648,7 @@ int _IORead(HANDLE &dev_handle, OVERLAPPED &hid_overlap_read, u8* buf, int index
{ {
auto const wait_result = WaitForSingleObject(hid_overlap_read.hEvent, INFINITE); auto const wait_result = WaitForSingleObject(hid_overlap_read.hEvent, INFINITE);
// In case the event was signalled by _IOWakeup before the read completed, cancel it. // In case the event was signalled by IOWakeup before the read completed, cancel it.
CancelIo(dev_handle); CancelIo(dev_handle);
if (WAIT_FAILED == wait_result) if (WAIT_FAILED == wait_result)
@ -666,18 +682,18 @@ int _IORead(HANDLE &dev_handle, OVERLAPPED &hid_overlap_read, u8* buf, int index
return bytes + 1; return bytes + 1;
} }
void Wiimote::IOWakeup() void WiimoteWindows::IOWakeup()
{ {
_IOWakeup(dev_handle, hid_overlap_read); SetEvent(m_hid_overlap_read.hEvent);
} }
// positive = read packet // positive = read packet
// negative = didn't read packet // negative = didn't read packet
// zero = error // zero = error
int Wiimote::IORead(u8* buf) int WiimoteWindows::IORead(u8* buf)
{ {
return _IORead(dev_handle, hid_overlap_read, buf, index); return _IORead(m_dev_handle, m_hid_overlap_read, buf, m_index);
} }
@ -778,16 +794,11 @@ int _IOWrite(HANDLE &dev_handle, OVERLAPPED &hid_overlap_write, enum win_bt_stac
return 0; return 0;
} }
int Wiimote::IOWrite(const u8* buf, size_t len) int WiimoteWindows::IOWrite(const u8* buf, size_t len)
{ {
return _IOWrite(dev_handle, hid_overlap_write, stack, buf, len, nullptr); return _IOWrite(m_dev_handle, m_hid_overlap_write, m_stack, buf, len, nullptr);
} }
void Wiimote::EnablePowerAssertionInternal()
{}
void Wiimote::DisablePowerAssertionInternal()
{}
// invokes callback for each found wiimote bluetooth device // invokes callback for each found wiimote bluetooth device
template <typename T> template <typename T>
void ProcessWiimotes(bool new_scan, T& callback) void ProcessWiimotes(bool new_scan, T& callback)

View file

@ -10,100 +10,41 @@
} }
@end @end
@implementation SearchBT
- (void) deviceInquiryComplete: (IOBluetoothDeviceInquiry *) sender
error: (IOReturn) error
aborted: (BOOL) aborted
{
done = true;
CFRunLoopStop(CFRunLoopGetCurrent());
}
- (void) deviceInquiryDeviceFound: (IOBluetoothDeviceInquiry *) sender
device: (IOBluetoothDevice *) device
{
NOTICE_LOG(WIIMOTE, "Discovered bluetooth device at %s: %s",
[[device addressString] UTF8String],
[[device name] UTF8String]);
if ([[sender foundDevices] count] == maxDevices)
[sender stop];
}
@end
@interface ConnectBT: NSObject {} @interface ConnectBT: NSObject {}
@end @end
@implementation ConnectBT
- (void) l2capChannelData: (IOBluetoothL2CAPChannel *) l2capChannel
data: (unsigned char *) data
length: (NSUInteger) length
{
IOBluetoothDevice *device = [l2capChannel device];
WiimoteReal::Wiimote *wm = nullptr;
std::lock_guard<std::recursive_mutex> lk(WiimoteReal::g_refresh_lock);
for (int i = 0; i < MAX_WIIMOTES; i++)
{
if (WiimoteReal::g_wiimotes[i] == nullptr)
continue;
if ([device isEqual: WiimoteReal::g_wiimotes[i]->btd] == TRUE)
wm = WiimoteReal::g_wiimotes[i];
}
if (wm == nullptr) {
ERROR_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->inputlen != -1) {
WARN_LOG(WIIMOTE, "Dropping packet for wiimote %i, queue full",
wm->index + 1);
return;
}
memcpy(wm->input, data, length);
wm->inputlen = length;
CFRunLoopStop(CFRunLoopGetCurrent());
}
- (void) l2capChannelClosed: (IOBluetoothL2CAPChannel *) l2capChannel
{
IOBluetoothDevice *device = [l2capChannel device];
WiimoteReal::Wiimote *wm = nullptr;
std::lock_guard<std::recursive_mutex> lk(WiimoteReal::g_refresh_lock);
for (int i = 0; i < MAX_WIIMOTES; i++)
{
if (WiimoteReal::g_wiimotes[i] == nullptr)
continue;
if ([device isEqual: WiimoteReal::g_wiimotes[i]->btd] == TRUE)
wm = WiimoteReal::g_wiimotes[i];
}
if (wm == nullptr) {
ERROR_LOG(WIIMOTE, "Channel for unknown wiimote was closed");
return;
}
WARN_LOG(WIIMOTE, "Lost channel to wiimote %i", wm->index + 1);
wm->DisconnectInternal();
}
@end
namespace WiimoteReal namespace WiimoteReal
{ {
class WiimoteDarwin final : public Wiimote
{
public:
WiimoteDarwin(IOBluetoothDevice* device);
~WiimoteDarwin() override;
// These are not protected/private because ConnectBT needs them.
void DisconnectInternal() override;
IOBluetoothDevice* m_btd;
unsigned char* m_input;
int m_inputlen;
protected:
bool ConnectInternal() override;
bool IsConnected() const override;
void IOWakeup() override;
int IORead(u8* buf) override;
int IOWrite(u8 const* buf, size_t len) override;
void EnablePowerAssertionInternal() override;
void DisablePowerAssertionInternal() override;
private:
IOBluetoothL2CAPChannel* m_ichan;
IOBluetoothL2CAPChannel* m_cchan;
bool m_connected;
CFRunLoopRef m_wiimote_thread_run_loop;
IOPMAssertionID m_pm_assertion;
};
WiimoteScanner::WiimoteScanner() WiimoteScanner::WiimoteScanner()
: m_run_thread() : m_run_thread()
, m_want_wiimotes() , m_want_wiimotes()
@ -164,8 +105,7 @@ void WiimoteScanner::FindWiimotes(std::vector<Wiimote*> & found_wiimotes, Wiimot
if (!IsValidBluetoothName([[dev name] UTF8String])) if (!IsValidBluetoothName([[dev name] UTF8String]))
continue; continue;
Wiimote *wm = new Wiimote(); Wiimote* wm = new WiimoteDarwin([dev retain]);
wm->btd = [dev retain];
if (IsBalanceBoardName([[dev name] UTF8String])) if (IsBalanceBoardName([[dev name] UTF8String]))
{ {
@ -188,51 +128,52 @@ bool WiimoteScanner::IsReady() const
return true; return true;
} }
void Wiimote::InitInternal() WiimoteDarwin::WiimoteDarwin(IOBluetoothDevice* device) : m_btd(device)
{ {
inputlen = 0; m_inputlen = 0;
m_connected = false; m_connected = false;
m_wiimote_thread_run_loop = nullptr; m_wiimote_thread_run_loop = nullptr;
btd = nil;
m_pm_assertion = kIOPMNullAssertionID; m_pm_assertion = kIOPMNullAssertionID;
} }
void Wiimote::TeardownInternal() WiimoteDarwin::~WiimoteDarwin()
{ {
Shutdown();
if (m_wiimote_thread_run_loop) if (m_wiimote_thread_run_loop)
{ {
CFRelease(m_wiimote_thread_run_loop); CFRelease(m_wiimote_thread_run_loop);
m_wiimote_thread_run_loop = nullptr; m_wiimote_thread_run_loop = nullptr;
} }
[btd release]; [m_btd release];
btd = nil; m_btd = nil;
DisablePowerAssertionInternal();
} }
// Connect to a wiimote with a known address. // Connect to a wiimote with a known address.
bool Wiimote::ConnectInternal() bool WiimoteDarwin::ConnectInternal()
{ {
if (IsConnected()) if (IsConnected())
return false; return false;
ConnectBT *cbt = [[ConnectBT alloc] init]; ConnectBT *cbt = [[ConnectBT alloc] init];
cchan = ichan = nil; m_cchan = m_ichan = nil;
IOReturn ret = [btd openConnection]; IOReturn ret = [m_btd openConnection];
if (ret) if (ret)
{ {
ERROR_LOG(WIIMOTE, "Unable to open Bluetooth connection to wiimote %i: %x", ERROR_LOG(WIIMOTE, "Unable to open Bluetooth connection to wiimote %i: %x",
index + 1, ret); m_index + 1, ret);
[cbt release]; [cbt release];
return false; return false;
} }
ret = [btd openL2CAPChannelSync: &cchan ret = [m_btd openL2CAPChannelSync: &m_cchan
withPSM: kBluetoothL2CAPPSMHIDControl delegate: cbt]; withPSM: kBluetoothL2CAPPSMHIDControl delegate: cbt];
if (ret) if (ret)
{ {
ERROR_LOG(WIIMOTE, "Unable to open control channel for wiimote %i: %x", ERROR_LOG(WIIMOTE, "Unable to open control channel for wiimote %i: %x",
index + 1, ret); m_index + 1, ret);
goto bad; goto bad;
} }
// Apple docs claim: // Apple docs claim:
@ -240,20 +181,20 @@ bool Wiimote::ConnectInternal()
// success; the channel must be released when the caller is done with it." // success; the channel must be released when the caller is done with it."
// But without this, the channels get over-autoreleased, even though the // But without this, the channels get over-autoreleased, even though the
// refcounting behavior here is clearly correct. // refcounting behavior here is clearly correct.
[cchan retain]; [m_cchan retain];
ret = [btd openL2CAPChannelSync: &ichan ret = [m_btd openL2CAPChannelSync: &m_ichan
withPSM: kBluetoothL2CAPPSMHIDInterrupt delegate: cbt]; withPSM: kBluetoothL2CAPPSMHIDInterrupt delegate: cbt];
if (ret) if (ret)
{ {
WARN_LOG(WIIMOTE, "Unable to open interrupt channel for wiimote %i: %x", WARN_LOG(WIIMOTE, "Unable to open interrupt channel for wiimote %i: %x",
index + 1, ret); m_index + 1, ret);
goto bad; goto bad;
} }
[ichan retain]; [m_ichan retain];
NOTICE_LOG(WIIMOTE, "Connected to wiimote %i at %s", NOTICE_LOG(WIIMOTE, "Connected to wiimote %i at %s",
index + 1, [[btd addressString] UTF8String]); m_index + 1, [[m_btd addressString] UTF8String]);
m_connected = true; m_connected = true;
@ -270,32 +211,32 @@ bad:
} }
// Disconnect a wiimote. // Disconnect a wiimote.
void Wiimote::DisconnectInternal() void WiimoteDarwin::DisconnectInternal()
{ {
[ichan closeChannel]; [m_ichan closeChannel];
[ichan release]; [m_ichan release];
ichan = nil; m_ichan = nil;
[cchan closeChannel]; [m_cchan closeChannel];
[cchan release]; [m_cchan release];
cchan = nil; m_cchan = nil;
[btd closeConnection]; [m_btd closeConnection];
if (!IsConnected()) if (!IsConnected())
return; return;
NOTICE_LOG(WIIMOTE, "Disconnecting wiimote %i", index + 1); NOTICE_LOG(WIIMOTE, "Disconnecting wiimote %i", m_index + 1);
m_connected = false; m_connected = false;
} }
bool Wiimote::IsConnected() const bool WiimoteDarwin::IsConnected() const
{ {
return m_connected; return m_connected;
} }
void Wiimote::IOWakeup() void WiimoteDarwin::IOWakeup()
{ {
if (m_wiimote_thread_run_loop) if (m_wiimote_thread_run_loop)
{ {
@ -303,24 +244,24 @@ void Wiimote::IOWakeup()
} }
} }
int Wiimote::IORead(unsigned char *buf) int WiimoteDarwin::IORead(unsigned char *buf)
{ {
input = buf; m_input = buf;
inputlen = -1; m_inputlen = -1;
CFRunLoopRun(); CFRunLoopRun();
return inputlen; return m_inputlen;
} }
int Wiimote::IOWrite(const unsigned char *buf, size_t len) int WiimoteDarwin::IOWrite(const unsigned char *buf, size_t len)
{ {
IOReturn ret; IOReturn ret;
if (!IsConnected()) if (!IsConnected())
return 0; return 0;
ret = [ichan writeAsync: const_cast<void*>((void *)buf) length: (int)len refcon: nil]; ret = [m_ichan writeAsync: const_cast<void*>((void *)buf) length: (int)len refcon: nil];
if (ret == kIOReturnSuccess) if (ret == kIOReturnSuccess)
return len; return len;
@ -328,7 +269,7 @@ int Wiimote::IOWrite(const unsigned char *buf, size_t len)
return 0; return 0;
} }
void Wiimote::EnablePowerAssertionInternal() void WiimoteDarwin::EnablePowerAssertionInternal()
{ {
if (m_pm_assertion == kIOPMNullAssertionID) if (m_pm_assertion == kIOPMNullAssertionID)
{ {
@ -337,7 +278,7 @@ void Wiimote::EnablePowerAssertionInternal()
} }
} }
void Wiimote::DisablePowerAssertionInternal() void WiimoteDarwin::DisablePowerAssertionInternal()
{ {
if (m_pm_assertion != kIOPMNullAssertionID) if (m_pm_assertion != kIOPMNullAssertionID)
{ {
@ -346,4 +287,94 @@ void Wiimote::DisablePowerAssertionInternal()
} }
} }
} // namespace
@implementation SearchBT
- (void) deviceInquiryComplete: (IOBluetoothDeviceInquiry *) sender
error: (IOReturn) error
aborted: (BOOL) aborted
{
done = true;
CFRunLoopStop(CFRunLoopGetCurrent());
} }
- (void) deviceInquiryDeviceFound: (IOBluetoothDeviceInquiry *) sender
device: (IOBluetoothDevice *) device
{
NOTICE_LOG(WIIMOTE, "Discovered bluetooth device at %s: %s",
[[device addressString] UTF8String],
[[device name] UTF8String]);
if ([[sender foundDevices] count] == maxDevices)
[sender stop];
}
@end
@implementation ConnectBT
- (void) l2capChannelData: (IOBluetoothL2CAPChannel *) l2capChannel
data: (unsigned char *) data
length: (NSUInteger) length
{
IOBluetoothDevice *device = [l2capChannel device];
WiimoteReal::WiimoteDarwin *wm = nullptr;
std::lock_guard<std::recursive_mutex> lk(WiimoteReal::g_refresh_lock);
for (int i = 0; i < MAX_WIIMOTES; i++)
{
if (WiimoteReal::g_wiimotes[i] == nullptr)
continue;
wm = static_cast<WiimoteReal::WiimoteDarwin*>(WiimoteReal::g_wiimotes[i]);
if ([device isEqual: wm->m_btd] != TRUE)
wm = nullptr;
}
if (wm == nullptr) {
ERROR_LOG(WIIMOTE, "Received packet for unknown wiimote");
return;
}
if (length > MAX_PAYLOAD) {
WARN_LOG(WIIMOTE, "Dropping packet for wiimote %i, too large",
wm->m_index + 1);
return;
}
if (wm->m_inputlen != -1) {
WARN_LOG(WIIMOTE, "Dropping packet for wiimote %i, queue full",
wm->m_index + 1);
return;
}
memcpy(wm->m_input, data, length);
wm->m_inputlen = length;
CFRunLoopStop(CFRunLoopGetCurrent());
}
- (void) l2capChannelClosed: (IOBluetoothL2CAPChannel *) l2capChannel
{
IOBluetoothDevice *device = [l2capChannel device];
WiimoteReal::WiimoteDarwin *wm = nullptr;
std::lock_guard<std::recursive_mutex> lk(WiimoteReal::g_refresh_lock);
for (int i = 0; i < MAX_WIIMOTES; i++)
{
if (WiimoteReal::g_wiimotes[i] == nullptr)
continue;
wm = static_cast<WiimoteReal::WiimoteDarwin*>(WiimoteReal::g_wiimotes[i]);
if ([device isEqual: wm->m_btd] != TRUE)
wm = nullptr;
}
if (wm == nullptr) {
ERROR_LOG(WIIMOTE, "Channel for unknown wiimote was closed");
return;
}
WARN_LOG(WIIMOTE, "Lost channel to wiimote %i", wm->m_index + 1);
wm->DisconnectInternal();
}
@end

View file

@ -37,22 +37,18 @@ Wiimote* g_wiimotes[MAX_BBMOTES];
WiimoteScanner g_wiimote_scanner; WiimoteScanner g_wiimote_scanner;
Wiimote::Wiimote() Wiimote::Wiimote()
: index() : m_index()
, m_last_input_report() , m_last_input_report()
, m_channel(0) , m_channel(0)
, m_rumble_state() , m_rumble_state()
, m_need_prepare() , m_need_prepare()
{ {}
InitInternal();
}
Wiimote::~Wiimote() void Wiimote::Shutdown()
{ {
DisablePowerAssertionInternal();
StopThread(); StopThread();
ClearReadQueue(); ClearReadQueue();
m_write_reports.Clear(); m_write_reports.Clear();
TeardownInternal();
} }
// to be called from CPU thread // to be called from CPU thread
@ -62,12 +58,9 @@ void Wiimote::WriteReport(Report rpt)
{ {
bool const new_rumble_state = (rpt[2] & 0x1) != 0; bool const new_rumble_state = (rpt[2] & 0x1) != 0;
// If this is a rumble report and the rumble state didn't change, ignore.
if (WM_RUMBLE == rpt[1] && new_rumble_state == m_rumble_state) if (WM_RUMBLE == rpt[1] && new_rumble_state == m_rumble_state)
{
// If this is a rumble report and the rumble state didn't change, ignore
//ERROR_LOG(WIIMOTE, "Ignoring rumble report.");
return; return;
}
m_rumble_state = new_rumble_state; m_rumble_state = new_rumble_state;
} }
@ -140,7 +133,7 @@ void Wiimote::ControlChannel(const u16 channel, const void* const data, const u3
if (hidp->type == HID_TYPE_SET_REPORT) if (hidp->type == HID_TYPE_SET_REPORT)
{ {
u8 handshake_ok = HID_HANDSHAKE_SUCCESS; u8 handshake_ok = HID_HANDSHAKE_SUCCESS;
Core::Callback_WiimoteInterruptChannel(index, channel, &handshake_ok, sizeof(handshake_ok)); Core::Callback_WiimoteInterruptChannel(m_index, channel, &handshake_ok, sizeof(handshake_ok));
} }
} }
} }
@ -159,7 +152,7 @@ void Wiimote::InterruptChannel(const u16 channel, const void* const _data, const
auto const data = static_cast<const u8*>(_data); auto const data = static_cast<const u8*>(_data);
Report rpt(data, data + size); Report rpt(data, data + size);
WiimoteEmu::Wiimote *const wm = (WiimoteEmu::Wiimote*)::Wiimote::GetConfig()->controllers[index]; WiimoteEmu::Wiimote *const wm = (WiimoteEmu::Wiimote*)::Wiimote::GetConfig()->controllers[m_index];
// Convert output DATA packets to SET_REPORT packets. // Convert output DATA packets to SET_REPORT packets.
// Nintendo Wiimotes work without this translation, but 3rd // Nintendo Wiimotes work without this translation, but 3rd
@ -202,7 +195,7 @@ bool Wiimote::Read()
if (result > 0 && m_channel > 0) if (result > 0 && m_channel > 0)
{ {
if (SConfig::GetInstance().m_LocalCoreStartupParameter.iBBDumpPort > 0 && if (SConfig::GetInstance().m_LocalCoreStartupParameter.iBBDumpPort > 0 &&
index == WIIMOTE_BALANCE_BOARD) m_index == WIIMOTE_BALANCE_BOARD)
{ {
static sf::UdpSocket Socket; static sf::UdpSocket Socket;
Socket.send((char*)rpt.data(), Socket.send((char*)rpt.data(),
@ -218,7 +211,7 @@ bool Wiimote::Read()
} }
else if (0 == result) else if (0 == result)
{ {
ERROR_LOG(WIIMOTE, "Wiimote::IORead failed. Disconnecting Wiimote %d.", index + 1); ERROR_LOG(WIIMOTE, "Wiimote::IORead failed. Disconnecting Wiimote %d.", m_index + 1);
DisconnectInternal(); DisconnectInternal();
} }
@ -235,7 +228,7 @@ bool Wiimote::Write()
if (!is_speaker_data || m_last_audio_report.GetTimeDifference() > 5) if (!is_speaker_data || m_last_audio_report.GetTimeDifference() > 5)
{ {
if (SConfig::GetInstance().m_LocalCoreStartupParameter.iBBDumpPort > 0 && index == WIIMOTE_BALANCE_BOARD) if (SConfig::GetInstance().m_LocalCoreStartupParameter.iBBDumpPort > 0 && m_index == WIIMOTE_BALANCE_BOARD)
{ {
static sf::UdpSocket Socket; static sf::UdpSocket Socket;
Socket.send((char*)rpt.data(), rpt.size(), sf::IpAddress::LocalHost, SConfig::GetInstance().m_LocalCoreStartupParameter.iBBDumpPort); Socket.send((char*)rpt.data(), rpt.size(), sf::IpAddress::LocalHost, SConfig::GetInstance().m_LocalCoreStartupParameter.iBBDumpPort);
@ -290,7 +283,7 @@ void Wiimote::Update()
{ {
if (!IsConnected()) if (!IsConnected())
{ {
HandleWiimoteDisconnect(index); HandleWiimoteDisconnect(m_index);
return; return;
} }
@ -300,14 +293,14 @@ void Wiimote::Update()
// Send the report // Send the report
if (!rpt.empty() && m_channel > 0) if (!rpt.empty() && m_channel > 0)
{ {
Core::Callback_WiimoteInterruptChannel(index, m_channel, Core::Callback_WiimoteInterruptChannel(m_index, m_channel,
rpt.data(), (u32)rpt.size()); rpt.data(), (u32)rpt.size());
} }
} }
void Wiimote::Prepare(int _index) void Wiimote::Prepare(int _index)
{ {
index = _index; m_index = _index;
m_need_prepare = true; m_need_prepare = true;
} }
@ -317,7 +310,7 @@ bool Wiimote::PrepareOnThread()
u8 static const mode_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_REPORT_MODE, 0, WM_REPORT_CORE}; u8 static const mode_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_REPORT_MODE, 0, WM_REPORT_CORE};
// Set the active LEDs and turn on rumble. // Set the active LEDs and turn on rumble.
u8 static const led_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_LEDS, u8(WIIMOTE_LED_1 << (index%WIIMOTE_BALANCE_BOARD) | 0x1)}; u8 static const led_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_LEDS, u8(WIIMOTE_LED_1 << (m_index%WIIMOTE_BALANCE_BOARD) | 0x1)};
// Turn off rumble // Turn off rumble
u8 static const rumble_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_RUMBLE, 0}; u8 static const rumble_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_RUMBLE, 0};
@ -351,7 +344,7 @@ void Wiimote::EmuStop()
void Wiimote::EmuResume() void Wiimote::EmuResume()
{ {
WiimoteEmu::Wiimote *const wm = (WiimoteEmu::Wiimote*)::Wiimote::GetConfig()->controllers[index]; WiimoteEmu::Wiimote *const wm = (WiimoteEmu::Wiimote*)::Wiimote::GetConfig()->controllers[m_index];
m_last_input_report.clear(); m_last_input_report.clear();
@ -508,8 +501,6 @@ void Wiimote::StopThread()
IOWakeup(); IOWakeup();
if (m_wiimote_thread.joinable()) if (m_wiimote_thread.joinable())
m_wiimote_thread.join(); m_wiimote_thread.join();
#if defined(__APPLE__)
#endif
} }
void Wiimote::SetReady() void Wiimote::SetReady()
@ -554,7 +545,7 @@ void Wiimote::ThreadFunc()
m_need_prepare = false; m_need_prepare = false;
if (!PrepareOnThread()) if (!PrepareOnThread())
{ {
ERROR_LOG(WIIMOTE, "Wiimote::PrepareOnThread failed. Disconnecting Wiimote %d.", index + 1); ERROR_LOG(WIIMOTE, "Wiimote::PrepareOnThread failed. Disconnecting Wiimote %d.", m_index + 1);
break; break;
} }
} }

View file

@ -26,8 +26,9 @@ class Wiimote : NonCopyable
{ {
friend class WiimoteEmu::Wiimote; friend class WiimoteEmu::Wiimote;
public: public:
Wiimote(); virtual ~Wiimote() {}
~Wiimote(); // This needs to be called in derived destructors!
void Shutdown();
void ControlChannel(const u16 channel, const void* const data, const u32 size); 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 InterruptChannel(const u16 channel, const void* const data, const u32 size);
@ -47,22 +48,19 @@ public:
void EmuResume(); void EmuResume();
void EmuPause(); void EmuPause();
void EnablePowerAssertionInternal(); virtual void EnablePowerAssertionInternal() {}
void DisablePowerAssertionInternal(); virtual void DisablePowerAssertionInternal() {}
// connecting and disconnecting from physical devices // connecting and disconnecting from physical devices
// (using address inserted by FindWiimotes) // (using address inserted by FindWiimotes)
// these are called from the wiimote's thread. // these are called from the wiimote's thread.
bool ConnectInternal(); virtual bool ConnectInternal() = 0;
void DisconnectInternal(); virtual void DisconnectInternal() = 0;
void InitInternal();
void TeardownInternal();
bool Connect(); bool Connect();
// TODO: change to something like IsRelevant // TODO: change to something like IsRelevant
bool IsConnected() const; virtual bool IsConnected() const = 0;
void Prepare(int index); void Prepare(int index);
bool PrepareOnThread(); bool PrepareOnThread();
@ -73,32 +71,10 @@ public:
void QueueReport(u8 rpt_id, const void* data, unsigned int size); void QueueReport(u8 rpt_id, const void* data, unsigned int size);
int index; int m_index;
#if defined(__APPLE__)
IOBluetoothDevice *btd;
IOBluetoothL2CAPChannel *ichan;
IOBluetoothL2CAPChannel *cchan;
unsigned char* input;
int inputlen;
bool m_connected;
CFRunLoopRef m_wiimote_thread_run_loop;
IOPMAssertionID m_pm_assertion;
#elif defined(__linux__) && HAVE_BLUEZ
bdaddr_t bdaddr; // Bluetooth address
int cmd_sock; // Command socket
int int_sock; // Interrupt socket
int wakeup_pipe_w, wakeup_pipe_r;
#elif defined(_WIN32)
std::basic_string<TCHAR> devicepath; // Unique wiimote reference
//ULONGLONG btaddr; // Bluetooth address
HANDLE dev_handle; // HID handle
OVERLAPPED hid_overlap_read, hid_overlap_write; // Overlap handle
enum win_bt_stack_t stack; // Type of bluetooth stack to use
#endif
protected: protected:
Wiimote();
Report m_last_input_report; Report m_last_input_report;
u16 m_channel; u16 m_channel;
@ -106,9 +82,9 @@ private:
void ClearReadQueue(); void ClearReadQueue();
void WriteReport(Report rpt); void WriteReport(Report rpt);
int IORead(u8* buf); virtual int IORead(u8* buf) = 0;
int IOWrite(u8 const* buf, size_t len); virtual int IOWrite(u8 const* buf, size_t len) = 0;
void IOWakeup(); virtual void IOWakeup() = 0;
void ThreadFunc(); void ThreadFunc();
void SetReady(); void SetReady();