Try to improve real wiimotes on Windows.

This commit is contained in:
Jordan Woyak 2013-02-11 15:21:58 -06:00
parent b8fd5c0c30
commit c2d2fb8c7c
3 changed files with 112 additions and 72 deletions

View File

@ -270,10 +270,22 @@ bool Wiimote::Connect()
return false; return false;
} }
hid_overlap = OVERLAPPED(); #if 0
hid_overlap.hEvent = CreateEvent(NULL, 1, 1, _T("")); HIDD_ATTRIBUTES attr;
hid_overlap.Offset = 0; attr.Size = sizeof(attr);
hid_overlap.OffsetHigh = 0; if (!HidD_GetAttributes(dev_handle, &attr))
{
CloseHandle(dev_handle);
dev_handle = 0;
return false;
}
#endif
hid_overlap_read = OVERLAPPED();
hid_overlap_read.hEvent = CreateEvent(NULL, true, false, NULL);
hid_overlap_write = OVERLAPPED();
hid_overlap_write.hEvent = CreateEvent(NULL, true, false, NULL);
// TODO: thread isn't started here now, do this elsewhere // TODO: thread isn't started here now, do this elsewhere
// This isn't as drastic as it sounds, since the process in which the threads // This isn't as drastic as it sounds, since the process in which the threads
@ -296,7 +308,8 @@ void Wiimote::Disconnect()
CloseHandle(dev_handle); CloseHandle(dev_handle);
dev_handle = 0; dev_handle = 0;
CloseHandle(hid_overlap.hEvent); CloseHandle(hid_overlap_read.hEvent);
CloseHandle(hid_overlap_write.hEvent);
} }
bool Wiimote::IsConnected() const bool Wiimote::IsConnected() const
@ -307,20 +320,20 @@ bool Wiimote::IsConnected() const
// positive = read packet // positive = read packet
// negative = didn't read packet // negative = didn't read packet
// zero = error // zero = error
int Wiimote::IORead(unsigned char* buf) int Wiimote::IORead(u8* buf)
{ {
// used below for a warning // used below for a warning
*buf = 0; *buf = 0;
DWORD bytes; DWORD bytes;
ResetEvent(hid_overlap.hEvent); ResetEvent(hid_overlap_read.hEvent);
if (!ReadFile(dev_handle, buf, MAX_PAYLOAD - 1, &bytes, &hid_overlap)) if (!ReadFile(dev_handle, buf, MAX_PAYLOAD - 1, &bytes, &hid_overlap_read))
{ {
auto const err = GetLastError(); auto const err = GetLastError();
if (ERROR_IO_PENDING == err) if (ERROR_IO_PENDING == err)
{ {
auto const r = WaitForSingleObject(hid_overlap.hEvent, WIIMOTE_DEFAULT_TIMEOUT); auto const r = WaitForSingleObject(hid_overlap_read.hEvent, WIIMOTE_DEFAULT_TIMEOUT);
if (WAIT_TIMEOUT == r) if (WAIT_TIMEOUT == r)
{ {
// Timeout - cancel and continue // Timeout - cancel and continue
@ -338,7 +351,7 @@ int Wiimote::IORead(unsigned char* buf)
} }
else if (WAIT_OBJECT_0 == r) else if (WAIT_OBJECT_0 == r)
{ {
if (!GetOverlappedResult(dev_handle, &hid_overlap, &bytes, TRUE)) if (!GetOverlappedResult(dev_handle, &hid_overlap_read, &bytes, TRUE))
{ {
WARN_LOG(WIIMOTE, "GetOverlappedResult failed on wiimote %i.", index + 1); WARN_LOG(WIIMOTE, "GetOverlappedResult failed on wiimote %i.", index + 1);
bytes = 0; bytes = 0;
@ -380,70 +393,76 @@ int Wiimote::IORead(unsigned char* buf)
int Wiimote::IOWrite(const u8* buf, int len) int Wiimote::IOWrite(const u8* buf, int len)
{ {
u8 big_buf[MAX_PAYLOAD];
if (len < MAX_PAYLOAD)
{
std::copy(buf, buf + len, big_buf);
std::fill(big_buf + len, big_buf + MAX_PAYLOAD, 0);
buf = big_buf;
}
DWORD bytes = 0;
switch (stack) switch (stack)
{ {
case MSBT_STACK_UNKNOWN: case MSBT_STACK_UNKNOWN:
{ {
// Try to auto-detect the stack type // Try to auto-detect the stack type
stack = MSBT_STACK_BLUESOLEIL;
auto i = WriteFile(dev_handle, buf + 1, MAX_PAYLOAD - 1, &bytes, &hid_overlap); if (IOWrite(buf, len))
if (i) return 1;
stack = MSBT_STACK_MS;
if (IOWrite(buf, len))
{ {
// Bluesoleil will always return 1 here, even if it's not connected // Don't mind me, just a random sleep to fix stuff on Windows
stack = MSBT_STACK_BLUESOLEIL; SLEEP(1000);
return i; return 1;
} }
i = HidD_SetOutputReport(dev_handle, (unsigned char*) buf + 1, len - 1); stack = MSBT_STACK_UNKNOWN;
if (i)
{
stack = MSBT_STACK_MS;
return i;
}
auto const dw = GetLastError();
// Checking for 121 = timeout on semaphore/device off/disconnected to
// avoid trouble with other stacks toshiba/widcomm
if (dw == 121)
{
NOTICE_LOG(WIIMOTE, "IOWrite[MSBT_STACK_UNKNOWN]: Timeout");
return 0;
}
else
{
ERROR_LOG(WIIMOTE, "IOWrite[MSBT_STACK_UNKNOWN]: ERROR: %08x", dw);
return 0;
}
break; break;
} }
case MSBT_STACK_MS: case MSBT_STACK_MS:
{ {
auto i = HidD_SetOutputReport(dev_handle, (unsigned char*) buf + 1, len - 1); auto result = HidD_SetOutputReport(dev_handle, const_cast<u8*>(buf) + 1, len - 1);
auto dw = GetLastError(); //FlushFileBuffers(dev_handle);
if (dw == 121) if (!result)
{ {
// Semaphore timeout auto err = GetLastError();
NOTICE_LOG(WIIMOTE, "WiimoteIOWrite[MSBT_STACK_MS]: Unable to send data to wiimote"); if (err == 121)
return 0; {
// Semaphore timeout
NOTICE_LOG(WIIMOTE, "WiimoteIOWrite[MSBT_STACK_MS]: Unable to send data to wiimote");
}
else
{
ERROR_LOG(WIIMOTE, "IOWrite[MSBT_STACK_MS]: ERROR: %08x", err);
}
} }
return i; return result;
break; break;
} }
case MSBT_STACK_BLUESOLEIL: case MSBT_STACK_BLUESOLEIL:
return WriteFile(dev_handle, buf + 1, MAX_PAYLOAD - 1, &bytes, &hid_overlap); {
u8 big_buf[MAX_PAYLOAD];
if (len < MAX_PAYLOAD)
{
std::copy(buf, buf + len, big_buf);
std::fill(big_buf + len, big_buf + MAX_PAYLOAD, 0);
buf = big_buf;
}
ResetEvent(hid_overlap_write.hEvent);
DWORD bytes = 0;
if (WriteFile(dev_handle, buf + 1, MAX_PAYLOAD - 1, &bytes, &hid_overlap_write))
{
// WriteFile always returns true with bluesoleil.
return 1;
}
else
{
auto const err = GetLastError();
if (ERROR_IO_PENDING == err)
{
CancelIo(dev_handle);
}
}
break; break;
} }
}
return 0; return 0;
} }
@ -555,6 +574,7 @@ bool ForgetWiimote(HANDLE, BLUETOOTH_DEVICE_INFO_STRUCT& btdi)
// We don't want "remembered" devices. // We don't want "remembered" devices.
// SetServiceState seems to just fail with them. // SetServiceState seems to just fail with them.
// Make Windows forget about them. // Make Windows forget about them.
// This is also required to detect a disconnect for some reason..
NOTICE_LOG(WIIMOTE, "Removing remembered wiimote."); NOTICE_LOG(WIIMOTE, "Removing remembered wiimote.");
Bth_BluetoothRemoveDevice(&btdi.Address); Bth_BluetoothRemoveDevice(&btdi.Address);

View File

@ -232,6 +232,12 @@ Report Wiimote::ProcessReadQueue()
void Wiimote::Update() void Wiimote::Update()
{ {
if (!IsConnected())
{
HandleWiimoteDisconnect(index);
return;
}
// Pop through the queued reports // Pop through the queued reports
Report const rpt = ProcessReadQueue(); Report const rpt = ProcessReadQueue();
@ -252,18 +258,17 @@ bool Wiimote::Prepare(int _index)
// core buttons, no continuous reporting // core buttons, no continuous reporting
u8 const mode_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_CMD_REPORT_TYPE, 0, 0x30}; u8 const mode_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_CMD_REPORT_TYPE, 0, 0x30};
// Set the active LEDs. // Set the active LEDs and turn on rumble.
u8 const led_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_CMD_LED, u8(WIIMOTE_LED_1 << index)}; u8 const led_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_CMD_LED, u8(WIIMOTE_LED_1 << index) | 0x1};
// Rumble briefly // Turn off rumble
u8 rumble_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_CMD_RUMBLE, 1}; u8 rumble_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_CMD_RUMBLE, 0};
// TODO: request status and check for sane response? // TODO: request status and check for sane response?
return (IOWrite(mode_report, sizeof(mode_report)) return (IOWrite(mode_report, sizeof(mode_report))
&& IOWrite(led_report, sizeof(led_report)) && IOWrite(led_report, sizeof(led_report))
&& IOWrite(rumble_report, sizeof(rumble_report)) && (SLEEP(200), IOWrite(rumble_report, sizeof(rumble_report))));
&& (rumble_report[2] = 0, SLEEP(200), IOWrite(rumble_report, sizeof(rumble_report))));
} }
void Wiimote::EmuStart() void Wiimote::EmuStart()
@ -375,6 +380,8 @@ void Wiimote::ThreadFunc()
{ {
Common::SetCurrentThreadName("Wiimote Device Thread"); Common::SetCurrentThreadName("Wiimote Device Thread");
Host_ConnectWiimote(index, true);
// main loop // main loop
while (m_run_thread && IsConnected()) while (m_run_thread && IsConnected())
{ {
@ -408,13 +415,13 @@ void LoadSettings()
void Initialize() void Initialize()
{ {
NOTICE_LOG(WIIMOTE, "WiimoteReal::Initialize");
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock); std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
if (g_real_wiimotes_initialized) if (g_real_wiimotes_initialized)
return; return;
NOTICE_LOG(WIIMOTE, "WiimoteReal::Initialize");
g_wiimote_scanner.WantWiimotes(0 != CalculateWantedWiimotes()); g_wiimote_scanner.WantWiimotes(0 != CalculateWantedWiimotes());
g_wiimote_scanner.StartScanning(); g_wiimote_scanner.StartScanning();
@ -423,9 +430,7 @@ void Initialize()
} }
void Shutdown(void) void Shutdown(void)
{ {
NOTICE_LOG(WIIMOTE, "WiimoteReal::Shutdown");
g_wiimote_scanner.StopScanning(); g_wiimote_scanner.StopScanning();
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock); std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
@ -433,6 +438,8 @@ void Shutdown(void)
if (!g_real_wiimotes_initialized) if (!g_real_wiimotes_initialized)
return; return;
NOTICE_LOG(WIIMOTE, "WiimoteReal::Shutdown");
g_real_wiimotes_initialized = false; g_real_wiimotes_initialized = false;
for (unsigned int i = 0; i < MAX_WIIMOTES; ++i) for (unsigned int i = 0; i < MAX_WIIMOTES; ++i)
@ -466,8 +473,6 @@ void TryToConnectWiimote(Wiimote* wm)
g_wiimotes[i] = wm; g_wiimotes[i] = wm;
wm->StartThread(); wm->StartThread();
Host_ConnectWiimote(i, true);
NOTICE_LOG(WIIMOTE, "Connected to wiimote %i.", i + 1); NOTICE_LOG(WIIMOTE, "Connected to wiimote %i.", i + 1);
@ -485,8 +490,6 @@ void HandleWiimoteDisconnect(int index)
{ {
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock); std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
Host_ConnectWiimote(index, false);
if (g_wiimotes[index]) if (g_wiimotes[index])
{ {
delete g_wiimotes[index]; delete g_wiimotes[index];
@ -511,12 +514,24 @@ void Refresh()
{ {
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock); std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
CheckForDisconnectedWiimotes(); std::vector<Wiimote*> found_wiimotes;
if (0 != CalculateWantedWiimotes()) if (0 != CalculateWantedWiimotes())
{ {
HandleFoundWiimotes(g_wiimote_scanner.FindWiimotes()); found_wiimotes = g_wiimote_scanner.FindWiimotes();
} }
CheckForDisconnectedWiimotes();
// Brief rumble for already connected wiimotes.
for (int i = 0; i != MAX_WIIMOTES; ++i)
{
// kinda sloppy
if (g_wiimotes[i])
g_wiimotes[i]->Prepare(i);
}
HandleFoundWiimotes(found_wiimotes);
} }
g_wiimote_scanner.StartScanning(); g_wiimote_scanner.StartScanning();
@ -546,8 +561,13 @@ void Update(int _WiimoteNumber)
if (g_wiimotes[_WiimoteNumber]) if (g_wiimotes[_WiimoteNumber])
g_wiimotes[_WiimoteNumber]->Update(); g_wiimotes[_WiimoteNumber]->Update();
else
// Wiimote::Update() may remove the wiimote if it was disconnected.
if (!g_wiimotes[_WiimoteNumber])
{
NOTICE_LOG(WIIMOTE, "Emulating disconnect of wiimote %d.", _WiimoteNumber + 1);
Host_ConnectWiimote(_WiimoteNumber, false); Host_ConnectWiimote(_WiimoteNumber, false);
}
} }
void StateChange(EMUSTATE_CHANGE newState) void StateChange(EMUSTATE_CHANGE newState)

View File

@ -96,7 +96,7 @@ public:
std::string devicepath; // Unique wiimote reference std::string devicepath; // Unique wiimote reference
//ULONGLONG btaddr; // Bluetooth address //ULONGLONG btaddr; // Bluetooth address
HANDLE dev_handle; // HID handle HANDLE dev_handle; // HID handle
OVERLAPPED hid_overlap; // Overlap handle OVERLAPPED hid_overlap_read, hid_overlap_write; // Overlap handle
enum win_bt_stack_t stack; // Type of bluetooth stack to use enum win_bt_stack_t stack; // Type of bluetooth stack to use
#endif #endif