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
static HINSTANCE s_hid_lib = nullptr;
static HINSTANCE s_bthprops_lib = nullptr;
namespace
{
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;
@ -161,7 +163,7 @@ bool load_bthprops()
#undef DYN_FUNC_LOAD
#undef DYN_FUNC_UNLOAD
inline void init_lib()
void init_lib()
{
static bool initialized = false;
@ -181,6 +183,7 @@ inline void init_lib()
s_loaded_ok = true;
}
}
} // Anonymous namespace
namespace WiimoteReal
{
@ -195,44 +198,138 @@ bool AttachWiimote(HANDLE hRadio, const BLUETOOTH_RADIO_INFO&, BLUETOOTH_DEVICE_
void RemoveWiimote(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?
#if 0
ProcessWiimotes(false, [](HANDLE, BLUETOOTH_RADIO_INFO&, BLUETOOTH_DEVICE_INFO_STRUCT& btdi)
{
RemoveWiimote(btdi);
});
#endif
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;
}
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)
return;
DWORD bytes_written;
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) {
forgot_some |= ForgetWiimote(btdi);
});
// 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;
}
// 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);
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;
}
// 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
// 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,
PSP_DEVINFO_DATA parent_device_data)
bool GetParentDevice(const DEVINST& child_device_instance, HDEVINFO* parent_device_info,
PSP_DEVINFO_DATA parent_device_data)
{
ULONG status;
ULONG problem_number;
@ -279,28 +376,6 @@ static bool GetParentDevice(const DEVINST& child_device_instance, HDEVINFO* pare
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
// Driver.
// 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
// HID 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;
SP_DEVINFO_DATA parent_device_data = {};
@ -329,7 +404,7 @@ static bool CheckForToshibaStack(const DEVINST& hid_interface_device_instance)
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
// report
@ -337,7 +412,7 @@ static WinWriteMethod GetInitialWriteMethod(bool IsUsingToshibaStack)
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();
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;
}
static int ReadFromHandle(HANDLE& dev_handle, u8* buf)
int ReadFromHandle(HANDLE& dev_handle, u8* buf)
{
OVERLAPPED hid_overlap_read = OVERLAPPED();
hid_overlap_read.hEvent = CreateEvent(nullptr, true, false, nullptr);
@ -359,7 +434,7 @@ static int ReadFromHandle(HANDLE& dev_handle, u8* buf)
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,
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;
}
} // 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.
// 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);
}
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
// is the preferred method
// 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(
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))
{
@ -985,8 +990,8 @@ bool ForgetWiimote(BLUETOOTH_DEVICE_INFO_STRUCT& btdi)
// Sometimes SetServiceState takes a while..
auto const avoid_forget_seconds = 5.0;
auto pair_time = g_connect_times.find(btdi.Address.ullLong);
if (pair_time == g_connect_times.end() ||
auto pair_time = s_connect_times.find(btdi.Address.ullLong);
if (pair_time == s_connect_times.end() ||
std::difftime(time(nullptr), pair_time->second) >= avoid_forget_seconds)
{
// Make Windows forget about device so it will re-find it if visible.