Merge pull request #7374 from lioncash/iowin
IOWin: Make functions internally linked where applicable
This commit is contained in:
commit
4c75331d5d
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue