Merge pull request #7374 from lioncash/iowin

IOWin: Make functions internally linked where applicable
This commit is contained in:
Pierre Bourdon 2018-08-27 22:13:24 +02:00 committed by GitHub
commit 4c75331d5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 167 additions and 162 deletions

View File

@ -55,12 +55,14 @@ DYN_FUNC_DECLARE(BluetoothEnumerateInstalledServices);
#undef DYN_FUNC_DECLARE #undef DYN_FUNC_DECLARE
static HINSTANCE s_hid_lib = nullptr; namespace
static HINSTANCE s_bthprops_lib = nullptr; {
HINSTANCE s_hid_lib = nullptr;
HINSTANCE s_bthprops_lib = nullptr;
static bool s_loaded_ok = false; bool s_loaded_ok = false;
std::unordered_map<BTH_ADDR, std::time_t> g_connect_times; std::unordered_map<BTH_ADDR, std::time_t> s_connect_times;
#define DYN_FUNC_UNLOAD(func) p##func = nullptr; #define DYN_FUNC_UNLOAD(func) p##func = nullptr;
@ -161,7 +163,7 @@ bool load_bthprops()
#undef DYN_FUNC_LOAD #undef DYN_FUNC_LOAD
#undef DYN_FUNC_UNLOAD #undef DYN_FUNC_UNLOAD
inline void init_lib() void init_lib()
{ {
static bool initialized = false; static bool initialized = false;
@ -181,6 +183,7 @@ inline void init_lib()
s_loaded_ok = true; s_loaded_ok = true;
} }
} }
} // Anonymous namespace
namespace WiimoteReal namespace WiimoteReal
{ {
@ -195,44 +198,138 @@ bool AttachWiimote(HANDLE hRadio, const BLUETOOTH_RADIO_INFO&, BLUETOOTH_DEVICE_
void RemoveWiimote(BLUETOOTH_DEVICE_INFO_STRUCT&); void RemoveWiimote(BLUETOOTH_DEVICE_INFO_STRUCT&);
bool ForgetWiimote(BLUETOOTH_DEVICE_INFO_STRUCT&); bool ForgetWiimote(BLUETOOTH_DEVICE_INFO_STRUCT&);
WiimoteScannerWindows::WiimoteScannerWindows() namespace
{ {
init_lib(); std::wstring GetDeviceProperty(const HDEVINFO& device_info, const PSP_DEVINFO_DATA device_data,
const DEVPROPKEY* requested_property)
{
DWORD required_size = 0;
DEVPROPTYPE device_property_type;
SetupDiGetDeviceProperty(device_info, device_data, requested_property, &device_property_type,
nullptr, 0, &required_size, 0);
std::vector<BYTE> unicode_buffer(required_size, 0);
BOOL result =
SetupDiGetDeviceProperty(device_info, device_data, requested_property, &device_property_type,
unicode_buffer.data(), required_size, nullptr, 0);
if (!result)
{
return std::wstring();
}
return std::wstring((PWCHAR)unicode_buffer.data());
} }
WiimoteScannerWindows::~WiimoteScannerWindows() int IOWritePerSetOutputReport(HANDLE& dev_handle, const u8* buf, size_t len, DWORD* written)
{ {
// TODO: what do we want here? BOOLEAN result = pHidD_SetOutputReport(dev_handle, const_cast<u8*>(buf) + 1, (ULONG)(len - 1));
#if 0 if (!result)
ProcessWiimotes(false, [](HANDLE, BLUETOOTH_RADIO_INFO&, BLUETOOTH_DEVICE_INFO_STRUCT& btdi) {
{ DWORD err = GetLastError();
RemoveWiimote(btdi); if (err == ERROR_SEM_TIMEOUT)
}); {
#endif NOTICE_LOG(WIIMOTE, "IOWrite[WWM_SET_OUTPUT_REPORT]: Unable to send data to the Wiimote");
}
else if (err != ERROR_GEN_FAILURE)
{
// Some third-party adapters (DolphinBar) use this
// error code to signal the absence of a Wiimote
// linked to the HID device.
WARN_LOG(WIIMOTE, "IOWrite[WWM_SET_OUTPUT_REPORT]: Error: %08x", err);
}
}
if (written)
{
*written = (result ? (DWORD)len : 0);
}
return result;
} }
void WiimoteScannerWindows::Update() int IOWritePerWriteFile(HANDLE& dev_handle, OVERLAPPED& hid_overlap_write,
WinWriteMethod& write_method, const u8* buf, size_t len, DWORD* written)
{ {
if (!s_loaded_ok) DWORD bytes_written;
return; LPCVOID write_buffer = buf + 1;
DWORD bytes_to_write = (DWORD)(len - 1);
bool forgot_some = false; u8 resized_buffer[MAX_PAYLOAD];
ProcessWiimotes(false, [&](HANDLE, BLUETOOTH_RADIO_INFO&, BLUETOOTH_DEVICE_INFO_STRUCT& btdi) { // Resize the buffer, if the underlying HID Class driver needs the buffer to be the size of
forgot_some |= ForgetWiimote(btdi); // HidCaps.OuputReportSize
}); // In case of Wiimote HidCaps.OuputReportSize is 22 Byte.
// This is currently needed by the Toshiba Bluetooth Stack.
if ((write_method == WWM_WRITE_FILE_LARGEST_REPORT_SIZE) && (MAX_PAYLOAD > len))
{
std::copy(buf, buf + len, resized_buffer);
std::fill(resized_buffer + len, resized_buffer + MAX_PAYLOAD, 0);
write_buffer = resized_buffer + 1;
bytes_to_write = MAX_PAYLOAD - 1;
}
// Some hacks that allows disconnects to be detected before connections are handled ResetEvent(hid_overlap_write.hEvent);
// workaround for Wiimote 1 moving to slot 2 on temporary disconnect BOOLEAN result =
if (forgot_some) WriteFile(dev_handle, write_buffer, bytes_to_write, &bytes_written, &hid_overlap_write);
Common::SleepCurrentThread(100); if (!result)
{
const DWORD error = GetLastError();
switch (error)
{
case ERROR_INVALID_USER_BUFFER:
INFO_LOG(WIIMOTE, "IOWrite[WWM_WRITE_FILE]: Falling back to SetOutputReport");
write_method = WWM_SET_OUTPUT_REPORT;
return IOWritePerSetOutputReport(dev_handle, buf, len, written);
case ERROR_IO_PENDING:
// Pending is no error!
break;
default:
WARN_LOG(WIIMOTE, "IOWrite[WWM_WRITE_FILE]: Error on WriteFile: %08x", error);
CancelIo(dev_handle);
return 0;
}
}
if (written)
{
*written = 0;
}
// Wait for completion
DWORD wait_result = WaitForSingleObject(hid_overlap_write.hEvent, WIIMOTE_DEFAULT_TIMEOUT);
if (WAIT_TIMEOUT == wait_result)
{
WARN_LOG(WIIMOTE, "IOWrite[WWM_WRITE_FILE]: A timeout occurred on writing to Wiimote.");
CancelIo(dev_handle);
return 1;
}
else if (WAIT_FAILED == wait_result)
{
WARN_LOG(WIIMOTE, "IOWrite[WWM_WRITE_FILE]: A wait error occurred on writing to Wiimote.");
CancelIo(dev_handle);
return 1;
}
if (written)
{
if (!GetOverlappedResult(dev_handle, &hid_overlap_write, written, TRUE))
{
*written = 0;
}
}
return 1;
} }
// Moves up one node in the device tree and returns its device info data along with an info set only // Moves up one node in the device tree and returns its device info data along with an info set only
// including that device for further processing // including that device for further processing
// See https://msdn.microsoft.com/en-us/library/windows/hardware/ff549417(v=vs.85).aspx // See https://msdn.microsoft.com/en-us/library/windows/hardware/ff549417(v=vs.85).aspx
static bool GetParentDevice(const DEVINST& child_device_instance, HDEVINFO* parent_device_info, bool GetParentDevice(const DEVINST& child_device_instance, HDEVINFO* parent_device_info,
PSP_DEVINFO_DATA parent_device_data) PSP_DEVINFO_DATA parent_device_data)
{ {
ULONG status; ULONG status;
ULONG problem_number; ULONG problem_number;
@ -279,28 +376,6 @@ static bool GetParentDevice(const DEVINST& child_device_instance, HDEVINFO* pare
return true; return true;
} }
std::wstring GetDeviceProperty(const HDEVINFO& device_info, const PSP_DEVINFO_DATA device_data,
const DEVPROPKEY* requested_property)
{
DWORD required_size = 0;
DEVPROPTYPE device_property_type;
SetupDiGetDeviceProperty(device_info, device_data, requested_property, &device_property_type,
nullptr, 0, &required_size, 0);
std::vector<BYTE> unicode_buffer(required_size, 0);
BOOL result =
SetupDiGetDeviceProperty(device_info, device_data, requested_property, &device_property_type,
unicode_buffer.data(), required_size, nullptr, 0);
if (!result)
{
return std::wstring();
}
return std::wstring((PWCHAR)unicode_buffer.data());
}
// The enumerated device nodes/instances are "empty" PDO's that act as interfaces for the HID Class // The enumerated device nodes/instances are "empty" PDO's that act as interfaces for the HID Class
// Driver. // Driver.
// Since those PDO's normaly don't have a FDO and therefore no driver loaded, we need to move one // Since those PDO's normaly don't have a FDO and therefore no driver loaded, we need to move one
@ -308,7 +383,7 @@ std::wstring GetDeviceProperty(const HDEVINFO& device_info, const PSP_DEVINFO_DA
// Then check the provider of the device driver, which will be "Microsoft" in case of the default // Then check the provider of the device driver, which will be "Microsoft" in case of the default
// HID Class Driver // HID Class Driver
// or "TOSHIBA" in case of the Toshiba Bluetooth Stack, because it provides its own Class Driver. // or "TOSHIBA" in case of the Toshiba Bluetooth Stack, because it provides its own Class Driver.
static bool CheckForToshibaStack(const DEVINST& hid_interface_device_instance) bool CheckForToshibaStack(const DEVINST& hid_interface_device_instance)
{ {
HDEVINFO parent_device_info = nullptr; HDEVINFO parent_device_info = nullptr;
SP_DEVINFO_DATA parent_device_data = {}; SP_DEVINFO_DATA parent_device_data = {};
@ -329,7 +404,7 @@ static bool CheckForToshibaStack(const DEVINST& hid_interface_device_instance)
return false; return false;
} }
static WinWriteMethod GetInitialWriteMethod(bool IsUsingToshibaStack) WinWriteMethod GetInitialWriteMethod(bool IsUsingToshibaStack)
{ {
// Currently Toshiba Bluetooth Stack needs the Output buffer to be the size of the largest output // Currently Toshiba Bluetooth Stack needs the Output buffer to be the size of the largest output
// report // report
@ -337,7 +412,7 @@ static WinWriteMethod GetInitialWriteMethod(bool IsUsingToshibaStack)
WWM_WRITE_FILE_ACTUAL_REPORT_SIZE); WWM_WRITE_FILE_ACTUAL_REPORT_SIZE);
} }
static int WriteToHandle(HANDLE& dev_handle, WinWriteMethod& method, const u8* buf, size_t size) int WriteToHandle(HANDLE& dev_handle, WinWriteMethod& method, const u8* buf, size_t size)
{ {
OVERLAPPED hid_overlap_write = OVERLAPPED(); OVERLAPPED hid_overlap_write = OVERLAPPED();
hid_overlap_write.hEvent = CreateEvent(nullptr, true, false, nullptr); hid_overlap_write.hEvent = CreateEvent(nullptr, true, false, nullptr);
@ -350,7 +425,7 @@ static int WriteToHandle(HANDLE& dev_handle, WinWriteMethod& method, const u8* b
return written; return written;
} }
static int ReadFromHandle(HANDLE& dev_handle, u8* buf) int ReadFromHandle(HANDLE& dev_handle, u8* buf)
{ {
OVERLAPPED hid_overlap_read = OVERLAPPED(); OVERLAPPED hid_overlap_read = OVERLAPPED();
hid_overlap_read.hEvent = CreateEvent(nullptr, true, false, nullptr); hid_overlap_read.hEvent = CreateEvent(nullptr, true, false, nullptr);
@ -359,7 +434,7 @@ static int ReadFromHandle(HANDLE& dev_handle, u8* buf)
return read; return read;
} }
static bool IsWiimote(const std::basic_string<TCHAR>& device_path, WinWriteMethod& method) bool IsWiimote(const std::basic_string<TCHAR>& device_path, WinWriteMethod& method)
{ {
HANDLE dev_handle = CreateFile(device_path.c_str(), GENERIC_READ | GENERIC_WRITE, HANDLE dev_handle = CreateFile(device_path.c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
@ -393,6 +468,40 @@ static bool IsWiimote(const std::basic_string<TCHAR>& device_path, WinWriteMetho
} }
return false; return false;
} }
} // Anonymous namespace
WiimoteScannerWindows::WiimoteScannerWindows()
{
init_lib();
}
WiimoteScannerWindows::~WiimoteScannerWindows()
{
// TODO: what do we want here?
#if 0
ProcessWiimotes(false, [](HANDLE, BLUETOOTH_RADIO_INFO&, BLUETOOTH_DEVICE_INFO_STRUCT& btdi)
{
RemoveWiimote(btdi);
});
#endif
}
void WiimoteScannerWindows::Update()
{
if (!s_loaded_ok)
return;
bool forgot_some = false;
ProcessWiimotes(false, [&](HANDLE, BLUETOOTH_RADIO_INFO&, BLUETOOTH_DEVICE_INFO_STRUCT& btdi) {
forgot_some |= ForgetWiimote(btdi);
});
// Some hacks that allows disconnects to be detected before connections are handled
// workaround for Wiimote 1 moving to slot 2 on temporary disconnect
if (forgot_some)
Common::SleepCurrentThread(100);
}
// Find and connect Wiimotes. // Find and connect Wiimotes.
// Does not replace already found Wiimotes even if they are disconnected. // Does not replace already found Wiimotes even if they are disconnected.
@ -686,110 +795,6 @@ int WiimoteWindows::IORead(u8* buf)
return WiimoteReal::IORead(m_dev_handle, m_hid_overlap_read, buf, m_index); return WiimoteReal::IORead(m_dev_handle, m_hid_overlap_read, buf, m_index);
} }
static int IOWritePerSetOutputReport(HANDLE& dev_handle, const u8* buf, size_t len, DWORD* written)
{
BOOLEAN result = pHidD_SetOutputReport(dev_handle, const_cast<u8*>(buf) + 1, (ULONG)(len - 1));
if (!result)
{
DWORD err = GetLastError();
if (err == ERROR_SEM_TIMEOUT)
{
NOTICE_LOG(WIIMOTE, "IOWrite[WWM_SET_OUTPUT_REPORT]: Unable to send data to the Wiimote");
}
else if (err != ERROR_GEN_FAILURE)
{
// Some third-party adapters (DolphinBar) use this
// error code to signal the absence of a Wiimote
// linked to the HID device.
WARN_LOG(WIIMOTE, "IOWrite[WWM_SET_OUTPUT_REPORT]: Error: %08x", err);
}
}
if (written)
{
*written = (result ? (DWORD)len : 0);
}
return result;
}
static int IOWritePerWriteFile(HANDLE& dev_handle, OVERLAPPED& hid_overlap_write,
WinWriteMethod& write_method, const u8* buf, size_t len,
DWORD* written)
{
DWORD bytes_written;
LPCVOID write_buffer = buf + 1;
DWORD bytes_to_write = (DWORD)(len - 1);
u8 resized_buffer[MAX_PAYLOAD];
// Resize the buffer, if the underlying HID Class driver needs the buffer to be the size of
// HidCaps.OuputReportSize
// In case of Wiimote HidCaps.OuputReportSize is 22 Byte.
// This is currently needed by the Toshiba Bluetooth Stack.
if ((write_method == WWM_WRITE_FILE_LARGEST_REPORT_SIZE) && (MAX_PAYLOAD > len))
{
std::copy(buf, buf + len, resized_buffer);
std::fill(resized_buffer + len, resized_buffer + MAX_PAYLOAD, 0);
write_buffer = resized_buffer + 1;
bytes_to_write = MAX_PAYLOAD - 1;
}
ResetEvent(hid_overlap_write.hEvent);
BOOLEAN result =
WriteFile(dev_handle, write_buffer, bytes_to_write, &bytes_written, &hid_overlap_write);
if (!result)
{
const DWORD error = GetLastError();
switch (error)
{
case ERROR_INVALID_USER_BUFFER:
INFO_LOG(WIIMOTE, "IOWrite[WWM_WRITE_FILE]: Falling back to SetOutputReport");
write_method = WWM_SET_OUTPUT_REPORT;
return IOWritePerSetOutputReport(dev_handle, buf, len, written);
case ERROR_IO_PENDING:
// Pending is no error!
break;
default:
WARN_LOG(WIIMOTE, "IOWrite[WWM_WRITE_FILE]: Error on WriteFile: %08x", error);
CancelIo(dev_handle);
return 0;
}
}
if (written)
{
*written = 0;
}
// Wait for completion
DWORD wait_result = WaitForSingleObject(hid_overlap_write.hEvent, WIIMOTE_DEFAULT_TIMEOUT);
if (WAIT_TIMEOUT == wait_result)
{
WARN_LOG(WIIMOTE, "IOWrite[WWM_WRITE_FILE]: A timeout occurred on writing to Wiimote.");
CancelIo(dev_handle);
return 1;
}
else if (WAIT_FAILED == wait_result)
{
WARN_LOG(WIIMOTE, "IOWrite[WWM_WRITE_FILE]: A wait error occurred on writing to Wiimote.");
CancelIo(dev_handle);
return 1;
}
if (written)
{
if (!GetOverlappedResult(dev_handle, &hid_overlap_write, written, TRUE))
{
*written = 0;
}
}
return 1;
}
// As of https://msdn.microsoft.com/en-us/library/windows/hardware/ff543402(v=vs.85).aspx, WriteFile // As of https://msdn.microsoft.com/en-us/library/windows/hardware/ff543402(v=vs.85).aspx, WriteFile
// is the preferred method // is the preferred method
// to send output reports to the HID. WriteFile sends an IRP_MJ_WRITE to the HID Class Driver // to send output reports to the HID. WriteFile sends an IRP_MJ_WRITE to the HID Class Driver
@ -961,7 +966,7 @@ bool AttachWiimote(HANDLE hRadio, const BLUETOOTH_RADIO_INFO& radio_info,
const DWORD hr = pBluetoothSetServiceState( const DWORD hr = pBluetoothSetServiceState(
hRadio, &btdi, &HumanInterfaceDeviceServiceClass_UUID, BLUETOOTH_SERVICE_ENABLE); hRadio, &btdi, &HumanInterfaceDeviceServiceClass_UUID, BLUETOOTH_SERVICE_ENABLE);
g_connect_times[btdi.Address.ullLong] = std::time(nullptr); s_connect_times[btdi.Address.ullLong] = std::time(nullptr);
if (FAILED(hr)) if (FAILED(hr))
{ {
@ -985,8 +990,8 @@ bool ForgetWiimote(BLUETOOTH_DEVICE_INFO_STRUCT& btdi)
// Sometimes SetServiceState takes a while.. // Sometimes SetServiceState takes a while..
auto const avoid_forget_seconds = 5.0; auto const avoid_forget_seconds = 5.0;
auto pair_time = g_connect_times.find(btdi.Address.ullLong); auto pair_time = s_connect_times.find(btdi.Address.ullLong);
if (pair_time == g_connect_times.end() || if (pair_time == s_connect_times.end() ||
std::difftime(time(nullptr), pair_time->second) >= avoid_forget_seconds) std::difftime(time(nullptr), pair_time->second) >= avoid_forget_seconds)
{ {
// Make Windows forget about device so it will re-find it if visible. // Make Windows forget about device so it will re-find it if visible.