1 // Copyright 2010 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4
5 #include <algorithm>
6 #include <cstdio>
7 #include <cstdlib>
8 #include <ctime>
9 #include <unordered_map>
10 #include <unordered_set>
11
12 #include <windows.h>
13 #include <BluetoothAPIs.h>
14 #include <Cfgmgr32.h>
15 #include <dbt.h>
16 #include <hidsdi.h>
17 #include <setupapi.h>
18
19 // initguid.h must be included before Devpkey.h
20 // clang-format off
21 #include <initguid.h>
22 #include <Devpkey.h>
23 // clang-format on
24
25 #include "Common/CommonFuncs.h"
26 #include "Common/CommonTypes.h"
27 #include "Common/DynamicLibrary.h"
28 #include "Common/Logging/Log.h"
29 #include "Common/ScopeGuard.h"
30 #include "Common/Thread.h"
31 #include "Core/HW/WiimoteCommon/WiimoteConstants.h"
32 #include "Core/HW/WiimoteCommon/WiimoteReport.h"
33 #include "Core/HW/WiimoteCommon/DataReport.h"
34 #include "Core/HW/WiimoteReal/IOWin.h"
35
36 // Create func_t function pointer type and declare a nullptr-initialized static variable of that
37 // type named "pfunc".
38 #define DYN_FUNC_DECLARE(func) \
39 typedef decltype(&func) func##_t; \
40 static func##_t p##func = nullptr;
41
42 DYN_FUNC_DECLARE(HidD_GetHidGuid);
43 DYN_FUNC_DECLARE(HidD_GetAttributes);
44 DYN_FUNC_DECLARE(HidD_SetOutputReport);
45 DYN_FUNC_DECLARE(HidD_GetProductString);
46
47 DYN_FUNC_DECLARE(BluetoothFindDeviceClose);
48 DYN_FUNC_DECLARE(BluetoothFindFirstDevice);
49 DYN_FUNC_DECLARE(BluetoothFindFirstRadio);
50 DYN_FUNC_DECLARE(BluetoothFindNextDevice);
51 DYN_FUNC_DECLARE(BluetoothFindNextRadio);
52 DYN_FUNC_DECLARE(BluetoothFindRadioClose);
53 DYN_FUNC_DECLARE(BluetoothGetRadioInfo);
54 DYN_FUNC_DECLARE(BluetoothRemoveDevice);
55 DYN_FUNC_DECLARE(BluetoothSetServiceState);
56 DYN_FUNC_DECLARE(BluetoothAuthenticateDeviceEx);
57 DYN_FUNC_DECLARE(BluetoothEnumerateInstalledServices);
58
59 #undef DYN_FUNC_DECLARE
60
61 namespace
62 {
63 HINSTANCE s_hid_lib = nullptr;
64 HINSTANCE s_bthprops_lib = nullptr;
65
66 bool s_loaded_ok = false;
67
68 std::unordered_map<BTH_ADDR, std::time_t> s_connect_times;
69
70 #define DYN_FUNC_UNLOAD(func) p##func = nullptr;
71
72 // Attempt to load the function from the given module handle.
73 #define DYN_FUNC_LOAD(module, func) \
74 p##func = (func##_t)::GetProcAddress(module, #func); \
75 if (!p##func) \
76 { \
77 return false; \
78 }
79
load_hid()80 bool load_hid()
81 {
82 auto loader = [&]() {
83 s_hid_lib = ::LoadLibrary(_T("hid.dll"));
84 if (!s_hid_lib)
85 {
86 return false;
87 }
88
89 DYN_FUNC_LOAD(s_hid_lib, HidD_GetHidGuid);
90 DYN_FUNC_LOAD(s_hid_lib, HidD_GetAttributes);
91 DYN_FUNC_LOAD(s_hid_lib, HidD_SetOutputReport);
92 DYN_FUNC_LOAD(s_hid_lib, HidD_GetProductString);
93
94 return true;
95 };
96
97 bool loaded_ok = loader();
98
99 if (!loaded_ok)
100 {
101 DYN_FUNC_UNLOAD(HidD_GetHidGuid);
102 DYN_FUNC_UNLOAD(HidD_GetAttributes);
103 DYN_FUNC_UNLOAD(HidD_SetOutputReport);
104 DYN_FUNC_UNLOAD(HidD_GetProductString);
105
106 if (s_hid_lib)
107 {
108 ::FreeLibrary(s_hid_lib);
109 s_hid_lib = nullptr;
110 }
111 }
112
113 return loaded_ok;
114 }
115
load_bthprops()116 bool load_bthprops()
117 {
118 auto loader = [&]() {
119 s_bthprops_lib = ::LoadLibrary(_T("bthprops.cpl"));
120 if (!s_bthprops_lib)
121 {
122 return false;
123 }
124
125 DYN_FUNC_LOAD(s_bthprops_lib, BluetoothFindDeviceClose);
126 DYN_FUNC_LOAD(s_bthprops_lib, BluetoothFindFirstDevice);
127 DYN_FUNC_LOAD(s_bthprops_lib, BluetoothFindFirstRadio);
128 DYN_FUNC_LOAD(s_bthprops_lib, BluetoothFindNextDevice);
129 DYN_FUNC_LOAD(s_bthprops_lib, BluetoothFindNextRadio);
130 DYN_FUNC_LOAD(s_bthprops_lib, BluetoothFindRadioClose);
131 DYN_FUNC_LOAD(s_bthprops_lib, BluetoothGetRadioInfo);
132 DYN_FUNC_LOAD(s_bthprops_lib, BluetoothRemoveDevice);
133 DYN_FUNC_LOAD(s_bthprops_lib, BluetoothSetServiceState);
134 DYN_FUNC_LOAD(s_bthprops_lib, BluetoothAuthenticateDeviceEx);
135 DYN_FUNC_LOAD(s_bthprops_lib, BluetoothEnumerateInstalledServices);
136
137 return true;
138 };
139
140 bool loaded_ok = loader();
141
142 if (!loaded_ok)
143 {
144 DYN_FUNC_UNLOAD(BluetoothFindDeviceClose);
145 DYN_FUNC_UNLOAD(BluetoothFindFirstDevice);
146 DYN_FUNC_UNLOAD(BluetoothFindFirstRadio);
147 DYN_FUNC_UNLOAD(BluetoothFindNextDevice);
148 DYN_FUNC_UNLOAD(BluetoothFindNextRadio);
149 DYN_FUNC_UNLOAD(BluetoothFindRadioClose);
150 DYN_FUNC_UNLOAD(BluetoothGetRadioInfo);
151 DYN_FUNC_UNLOAD(BluetoothRemoveDevice);
152 DYN_FUNC_UNLOAD(BluetoothSetServiceState);
153 DYN_FUNC_UNLOAD(BluetoothAuthenticateDeviceEx);
154 DYN_FUNC_UNLOAD(BluetoothEnumerateInstalledServices);
155
156 if (s_bthprops_lib)
157 {
158 ::FreeLibrary(s_bthprops_lib);
159 s_bthprops_lib = nullptr;
160 }
161 }
162
163 return loaded_ok;
164 }
165
166 #undef DYN_FUNC_LOAD
167 #undef DYN_FUNC_UNLOAD
168
init_lib()169 void init_lib()
170 {
171 static bool initialized = false;
172 static Common::DynamicLibrary bt_api_lib;
173
174 if (!initialized)
175 {
176 // Only try once
177 initialized = true;
178
179 // After these calls, we know all dynamically loaded APIs will either all be valid or
180 // all nullptr.
181 if (!load_hid() || !load_bthprops())
182 {
183 NOTICE_LOG(WIIMOTE, "Failed to load Bluetooth support libraries, Wiimotes will not function");
184 return;
185 }
186
187 // Try to incref on this dll to prevent it being reloaded continuously (caused by
188 // BluetoothFindFirstRadio)
189 bt_api_lib.Open("BluetoothApis.dll");
190
191 s_loaded_ok = true;
192 }
193 }
194 } // Anonymous namespace
195
196 namespace WiimoteReal
197 {
198 int IOWrite(HANDLE& dev_handle, OVERLAPPED& hid_overlap_write, enum WinWriteMethod& stack,
199 const u8* buf, size_t len, DWORD* written);
200 int IORead(HANDLE& dev_handle, OVERLAPPED& hid_overlap_read, u8* buf, int index);
201
202 template <typename T>
203 void ProcessWiimotes(bool new_scan, const T& callback);
204
205 bool AttachWiimote(HANDLE hRadio, const BLUETOOTH_RADIO_INFO&, BLUETOOTH_DEVICE_INFO_STRUCT&);
206 void RemoveWiimote(BLUETOOTH_DEVICE_INFO_STRUCT&);
207 bool ForgetWiimote(BLUETOOTH_DEVICE_INFO_STRUCT&);
208
209 namespace
210 {
GetDeviceProperty(const HDEVINFO & device_info,const PSP_DEVINFO_DATA device_data,const DEVPROPKEY * requested_property)211 std::wstring GetDeviceProperty(const HDEVINFO& device_info, const PSP_DEVINFO_DATA device_data,
212 const DEVPROPKEY* requested_property)
213 {
214 DWORD required_size = 0;
215 DEVPROPTYPE device_property_type;
216
217 SetupDiGetDeviceProperty(device_info, device_data, requested_property, &device_property_type,
218 nullptr, 0, &required_size, 0);
219
220 std::vector<BYTE> unicode_buffer(required_size, 0);
221
222 BOOL result =
223 SetupDiGetDeviceProperty(device_info, device_data, requested_property, &device_property_type,
224 unicode_buffer.data(), required_size, nullptr, 0);
225 if (!result)
226 {
227 return std::wstring();
228 }
229
230 return std::wstring((PWCHAR)unicode_buffer.data());
231 }
232
IOWritePerSetOutputReport(HANDLE & dev_handle,const u8 * buf,size_t len,DWORD * written)233 int IOWritePerSetOutputReport(HANDLE& dev_handle, const u8* buf, size_t len, DWORD* written)
234 {
235 BOOLEAN result = pHidD_SetOutputReport(dev_handle, const_cast<u8*>(buf) + 1, (ULONG)(len - 1));
236 if (!result)
237 {
238 DWORD err = GetLastError();
239 if (err == ERROR_SEM_TIMEOUT)
240 {
241 NOTICE_LOG(WIIMOTE, "IOWrite[WWM_SET_OUTPUT_REPORT]: Unable to send data to the Wiimote");
242 }
243 else if (err != ERROR_GEN_FAILURE)
244 {
245 // Some third-party adapters (DolphinBar) use this
246 // error code to signal the absence of a Wiimote
247 // linked to the HID device.
248 WARN_LOG(WIIMOTE, "IOWrite[WWM_SET_OUTPUT_REPORT]: Error: %08x", err);
249 }
250 }
251
252 if (written)
253 {
254 *written = (result ? (DWORD)len : 0);
255 }
256
257 return result;
258 }
259
IOWritePerWriteFile(HANDLE & dev_handle,OVERLAPPED & hid_overlap_write,WinWriteMethod & write_method,const u8 * buf,size_t len,DWORD * written)260 int IOWritePerWriteFile(HANDLE& dev_handle, OVERLAPPED& hid_overlap_write,
261 WinWriteMethod& write_method, const u8* buf, size_t len, DWORD* written)
262 {
263 DWORD bytes_written;
264 LPCVOID write_buffer = buf + 1;
265 DWORD bytes_to_write = (DWORD)(len - 1);
266
267 u8 resized_buffer[MAX_PAYLOAD];
268
269 // Resize the buffer, if the underlying HID Class driver needs the buffer to be the size of
270 // HidCaps.OuputReportSize
271 // In case of Wiimote HidCaps.OuputReportSize is 22 Byte.
272 // This is currently needed by the Toshiba Bluetooth Stack.
273 if ((write_method == WWM_WRITE_FILE_LARGEST_REPORT_SIZE) && (MAX_PAYLOAD > len))
274 {
275 std::copy(buf, buf + len, resized_buffer);
276 std::fill(resized_buffer + len, resized_buffer + MAX_PAYLOAD, 0);
277 write_buffer = resized_buffer + 1;
278 bytes_to_write = MAX_PAYLOAD - 1;
279 }
280
281 ResetEvent(hid_overlap_write.hEvent);
282 BOOLEAN result =
283 WriteFile(dev_handle, write_buffer, bytes_to_write, &bytes_written, &hid_overlap_write);
284 if (!result)
285 {
286 const DWORD error = GetLastError();
287
288 switch (error)
289 {
290 case ERROR_INVALID_USER_BUFFER:
291 INFO_LOG(WIIMOTE, "IOWrite[WWM_WRITE_FILE]: Falling back to SetOutputReport");
292 write_method = WWM_SET_OUTPUT_REPORT;
293 return IOWritePerSetOutputReport(dev_handle, buf, len, written);
294 case ERROR_IO_PENDING:
295 // Pending is no error!
296 break;
297 default:
298 WARN_LOG(WIIMOTE, "IOWrite[WWM_WRITE_FILE]: Error on WriteFile: %08x", error);
299 CancelIo(dev_handle);
300 return 0;
301 }
302 }
303
304 if (written)
305 {
306 *written = 0;
307 }
308
309 // Wait for completion
310 DWORD wait_result = WaitForSingleObject(hid_overlap_write.hEvent, WIIMOTE_DEFAULT_TIMEOUT);
311
312 if (WAIT_TIMEOUT == wait_result)
313 {
314 WARN_LOG(WIIMOTE, "IOWrite[WWM_WRITE_FILE]: A timeout occurred on writing to Wiimote.");
315 CancelIo(dev_handle);
316 return 1;
317 }
318 else if (WAIT_FAILED == wait_result)
319 {
320 WARN_LOG(WIIMOTE, "IOWrite[WWM_WRITE_FILE]: A wait error occurred on writing to Wiimote.");
321 CancelIo(dev_handle);
322 return 1;
323 }
324
325 if (written)
326 {
327 if (!GetOverlappedResult(dev_handle, &hid_overlap_write, written, TRUE))
328 {
329 *written = 0;
330 }
331 }
332
333 return 1;
334 }
335
336 // Moves up one node in the device tree and returns its device info data along with an info set only
337 // including that device for further processing
338 // See https://msdn.microsoft.com/en-us/library/windows/hardware/ff549417(v=vs.85).aspx
GetParentDevice(const DEVINST & child_device_instance,HDEVINFO * parent_device_info,PSP_DEVINFO_DATA parent_device_data)339 bool GetParentDevice(const DEVINST& child_device_instance, HDEVINFO* parent_device_info,
340 PSP_DEVINFO_DATA parent_device_data)
341 {
342 ULONG status;
343 ULONG problem_number;
344 CONFIGRET result;
345
346 // Check if that device instance has device node present
347 result = CM_Get_DevNode_Status(&status, &problem_number, child_device_instance, 0);
348 if (result != CR_SUCCESS)
349 {
350 return false;
351 }
352
353 DEVINST parent_device;
354
355 // Get the device instance of the parent
356 result = CM_Get_Parent(&parent_device, child_device_instance, 0);
357 if (result != CR_SUCCESS)
358 {
359 return false;
360 }
361
362 std::vector<WCHAR> parent_device_id(MAX_DEVICE_ID_LEN);
363 ;
364
365 // Get the device id of the parent, required to open the device info
366 result =
367 CM_Get_Device_ID(parent_device, parent_device_id.data(), (ULONG)parent_device_id.size(), 0);
368 if (result != CR_SUCCESS)
369 {
370 return false;
371 }
372
373 // Create a new empty device info set for the device info data
374 (*parent_device_info) = SetupDiCreateDeviceInfoList(nullptr, nullptr);
375
376 // Open the device info data of the parent and put it in the emtpy info set
377 if (!SetupDiOpenDeviceInfo((*parent_device_info), parent_device_id.data(), nullptr, 0,
378 parent_device_data))
379 {
380 SetupDiDestroyDeviceInfoList(parent_device_info);
381 return false;
382 }
383
384 return true;
385 }
386
387 // The enumerated device nodes/instances are "empty" PDO's that act as interfaces for the HID Class
388 // Driver.
389 // Since those PDO's normaly don't have a FDO and therefore no driver loaded, we need to move one
390 // device node up in the device tree.
391 // Then check the provider of the device driver, which will be "Microsoft" in case of the default
392 // HID Class Driver
393 // or "TOSHIBA" in case of the Toshiba Bluetooth Stack, because it provides its own Class Driver.
CheckForToshibaStack(const DEVINST & hid_interface_device_instance)394 bool CheckForToshibaStack(const DEVINST& hid_interface_device_instance)
395 {
396 HDEVINFO parent_device_info = nullptr;
397 SP_DEVINFO_DATA parent_device_data = {};
398 parent_device_data.cbSize = sizeof(SP_DEVINFO_DATA);
399
400 if (GetParentDevice(hid_interface_device_instance, &parent_device_info, &parent_device_data))
401 {
402 std::wstring class_driver_provider =
403 GetDeviceProperty(parent_device_info, &parent_device_data, &DEVPKEY_Device_DriverProvider);
404
405 SetupDiDestroyDeviceInfoList(parent_device_info);
406
407 return (class_driver_provider == L"TOSHIBA");
408 }
409
410 DEBUG_LOG(WIIMOTE, "Unable to detect class driver provider!");
411
412 return false;
413 }
414
GetInitialWriteMethod(bool IsUsingToshibaStack)415 WinWriteMethod GetInitialWriteMethod(bool IsUsingToshibaStack)
416 {
417 // Currently Toshiba Bluetooth Stack needs the Output buffer to be the size of the largest output
418 // report
419 return (IsUsingToshibaStack ? WWM_WRITE_FILE_LARGEST_REPORT_SIZE :
420 WWM_WRITE_FILE_ACTUAL_REPORT_SIZE);
421 }
422
WriteToHandle(HANDLE & dev_handle,WinWriteMethod & method,const u8 * buf,size_t size)423 int WriteToHandle(HANDLE& dev_handle, WinWriteMethod& method, const u8* buf, size_t size)
424 {
425 OVERLAPPED hid_overlap_write = OVERLAPPED();
426 hid_overlap_write.hEvent = CreateEvent(nullptr, true, false, nullptr);
427 if (!hid_overlap_write.hEvent)
428 {
429 return 0;
430 }
431
432 DWORD written = 0;
433 IOWrite(dev_handle, hid_overlap_write, method, buf, size, &written);
434
435 CloseHandle(hid_overlap_write.hEvent);
436
437 return written;
438 }
439
ReadFromHandle(HANDLE & dev_handle,u8 * buf)440 int ReadFromHandle(HANDLE& dev_handle, u8* buf)
441 {
442 OVERLAPPED hid_overlap_read = OVERLAPPED();
443 hid_overlap_read.hEvent = CreateEvent(nullptr, true, false, nullptr);
444 if (!hid_overlap_read.hEvent)
445 {
446 return 0;
447 }
448 const int read = IORead(dev_handle, hid_overlap_read, buf, 1);
449 CloseHandle(hid_overlap_read.hEvent);
450 return read;
451 }
452
IsWiimote(const std::basic_string<TCHAR> & device_path,WinWriteMethod & method)453 bool IsWiimote(const std::basic_string<TCHAR>& device_path, WinWriteMethod& method)
454 {
455 using namespace WiimoteCommon;
456
457 HANDLE dev_handle = CreateFile(device_path.c_str(), GENERIC_READ | GENERIC_WRITE,
458 FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
459 FILE_FLAG_OVERLAPPED, nullptr);
460 if (dev_handle == INVALID_HANDLE_VALUE)
461 return false;
462
463 Common::ScopeGuard handle_guard{[&dev_handle] { CloseHandle(dev_handle); }};
464
465 u8 buf[MAX_PAYLOAD];
466 u8 const req_status_report[] = {WR_SET_REPORT | BT_OUTPUT, u8(OutputReportID::RequestStatus), 0};
467 int invalid_report_count = 0;
468 int rc = WriteToHandle(dev_handle, method, req_status_report, sizeof(req_status_report));
469 while (rc > 0)
470 {
471 rc = ReadFromHandle(dev_handle, buf);
472 if (rc <= 0)
473 break;
474
475 switch (InputReportID(buf[1]))
476 {
477 case InputReportID::Status:
478 return true;
479 default:
480 WARN_LOG(WIIMOTE, "IsWiimote(): Received unexpected report %02x", buf[1]);
481 invalid_report_count++;
482 // If we receive over 15 invalid reports, then this is probably not a Wiimote.
483 if (invalid_report_count > 15)
484 return false;
485 }
486 }
487 return false;
488 }
489 } // Anonymous namespace
490
WiimoteScannerWindows()491 WiimoteScannerWindows::WiimoteScannerWindows()
492 {
493 init_lib();
494 }
495
~WiimoteScannerWindows()496 WiimoteScannerWindows::~WiimoteScannerWindows()
497 {
498 // TODO: what do we want here?
499 #if 0
500 ProcessWiimotes(false, [](HANDLE, BLUETOOTH_RADIO_INFO&, BLUETOOTH_DEVICE_INFO_STRUCT& btdi)
501 {
502 RemoveWiimote(btdi);
503 });
504 #endif
505 }
506
Update()507 void WiimoteScannerWindows::Update()
508 {
509 if (!s_loaded_ok)
510 return;
511
512 bool forgot_some = false;
513
514 ProcessWiimotes(false, [&](HANDLE, BLUETOOTH_RADIO_INFO&, BLUETOOTH_DEVICE_INFO_STRUCT& btdi) {
515 forgot_some |= ForgetWiimote(btdi);
516 });
517
518 // Some hacks that allows disconnects to be detected before connections are handled
519 // workaround for Wiimote 1 moving to slot 2 on temporary disconnect
520 if (forgot_some)
521 Common::SleepCurrentThread(100);
522 }
523
524 // Find and connect Wiimotes.
525 // Does not replace already found Wiimotes even if they are disconnected.
526 // wm is an array of max_wiimotes Wiimotes
527 // Returns the total number of found and connected Wiimotes.
FindWiimotes(std::vector<Wiimote * > & found_wiimotes,Wiimote * & found_board)528 void WiimoteScannerWindows::FindWiimotes(std::vector<Wiimote*>& found_wiimotes,
529 Wiimote*& found_board)
530 {
531 if (!s_loaded_ok)
532 return;
533
534 ProcessWiimotes(true, [](HANDLE hRadio, const BLUETOOTH_RADIO_INFO& rinfo,
535 BLUETOOTH_DEVICE_INFO_STRUCT& btdi) {
536 ForgetWiimote(btdi);
537 AttachWiimote(hRadio, rinfo, btdi);
538 });
539
540 // Get the device id
541 GUID device_id;
542 pHidD_GetHidGuid(&device_id);
543
544 // Get all hid devices connected
545 HDEVINFO const device_info =
546 SetupDiGetClassDevs(&device_id, nullptr, nullptr, (DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
547
548 SP_DEVICE_INTERFACE_DATA device_data = {};
549 device_data.cbSize = sizeof(device_data);
550
551 for (int index = 0;
552 SetupDiEnumDeviceInterfaces(device_info, nullptr, &device_id, index, &device_data); ++index)
553 {
554 // Get the size of the data block required
555 DWORD len;
556 SetupDiGetDeviceInterfaceDetail(device_info, &device_data, nullptr, 0, &len, nullptr);
557 auto detail_data_buf = std::make_unique<u8[]>(len);
558 auto detail_data = reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA>(detail_data_buf.get());
559 detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
560
561 SP_DEVINFO_DATA device_info_data = {};
562 device_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
563
564 // Query the data for this device
565 if (SetupDiGetDeviceInterfaceDetail(device_info, &device_data, detail_data, len, nullptr,
566 &device_info_data))
567 {
568 std::basic_string<TCHAR> device_path(detail_data->DevicePath);
569 bool IsUsingToshibaStack = CheckForToshibaStack(device_info_data.DevInst);
570
571 WinWriteMethod write_method = GetInitialWriteMethod(IsUsingToshibaStack);
572
573 if (!IsNewWiimote(WStringToUTF8(device_path)) || !IsWiimote(device_path, write_method))
574 {
575 continue;
576 }
577
578 auto* wiimote = new WiimoteWindows(device_path, write_method);
579 if (wiimote->IsBalanceBoard())
580 found_board = wiimote;
581 else
582 found_wiimotes.push_back(wiimote);
583 }
584 }
585
586 SetupDiDestroyDeviceInfoList(device_info);
587 }
588
IsReady() const589 bool WiimoteScannerWindows::IsReady() const
590 {
591 if (!s_loaded_ok)
592 {
593 return false;
594 }
595
596 // TODO: don't search for a radio each time
597
598 BLUETOOTH_FIND_RADIO_PARAMS radioParam;
599 radioParam.dwSize = sizeof(radioParam);
600
601 HANDLE hRadio;
602 HBLUETOOTH_RADIO_FIND hFindRadio = pBluetoothFindFirstRadio(&radioParam, &hRadio);
603
604 if (nullptr != hFindRadio)
605 {
606 CloseHandle(hRadio);
607 pBluetoothFindRadioClose(hFindRadio);
608 return true;
609 }
610 else
611 {
612 return false;
613 }
614 }
615
616 // Connect to a Wiimote with a known device path.
ConnectInternal()617 bool WiimoteWindows::ConnectInternal()
618 {
619 if (IsConnected())
620 return true;
621
622 if (!IsNewWiimote(WStringToUTF8(m_devicepath)))
623 return false;
624
625 auto const open_flags = FILE_SHARE_READ | FILE_SHARE_WRITE;
626
627 m_dev_handle = CreateFile(m_devicepath.c_str(), GENERIC_READ | GENERIC_WRITE, open_flags, nullptr,
628 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr);
629
630 if (m_dev_handle == INVALID_HANDLE_VALUE)
631 {
632 m_dev_handle = nullptr;
633 return false;
634 }
635
636 #if 0
637 TCHAR name[128] = {};
638 pHidD_GetProductString(dev_handle, name, 128);
639
640 //ERROR_LOG(WIIMOTE, "Product string: %s", TStrToUTF8(name).c_str());
641
642 if (!IsValidBluetoothName(TStrToUTF8(name)))
643 {
644 CloseHandle(dev_handle);
645 dev_handle = 0;
646 return false;
647 }
648 #endif
649
650 #if 0
651 HIDD_ATTRIBUTES attr;
652 attr.Size = sizeof(attr);
653 if (!pHidD_GetAttributes(dev_handle, &attr))
654 {
655 CloseHandle(dev_handle);
656 dev_handle = 0;
657 return false;
658 }
659 #endif
660
661 // TODO: thread isn't started here now, do this elsewhere
662 // This isn't as drastic as it sounds, since the process in which the threads
663 // reside is normal priority. Needed for keeping audio reports at a decent rate
664 /*
665 if (!SetThreadPriority(m_wiimote_thread.native_handle(), THREAD_PRIORITY_TIME_CRITICAL))
666 {
667 ERROR_LOG(WIIMOTE, "Failed to set Wiimote thread priority");
668 }
669 */
670
671 return true;
672 }
673
DisconnectInternal()674 void WiimoteWindows::DisconnectInternal()
675 {
676 if (!IsConnected())
677 return;
678
679 CloseHandle(m_dev_handle);
680 m_dev_handle = nullptr;
681 }
682
WiimoteWindows(const std::basic_string<TCHAR> & path,WinWriteMethod initial_write_method)683 WiimoteWindows::WiimoteWindows(const std::basic_string<TCHAR>& path,
684 WinWriteMethod initial_write_method)
685 : m_devicepath(path), m_write_method(initial_write_method)
686 {
687 m_dev_handle = nullptr;
688
689 m_hid_overlap_read = OVERLAPPED();
690 m_hid_overlap_read.hEvent = CreateEvent(nullptr, true, false, nullptr);
691
692 m_hid_overlap_write = OVERLAPPED();
693 m_hid_overlap_write.hEvent = CreateEvent(nullptr, true, false, nullptr);
694 }
695
~WiimoteWindows()696 WiimoteWindows::~WiimoteWindows()
697 {
698 Shutdown();
699 CloseHandle(m_hid_overlap_read.hEvent);
700 CloseHandle(m_hid_overlap_write.hEvent);
701 }
702
IsConnected() const703 bool WiimoteWindows::IsConnected() const
704 {
705 return m_dev_handle != nullptr;
706 }
707
708 // See http://wiibrew.org/wiki/Wiimote for the Report IDs and its sizes
GetReportSize(u8 rid)709 size_t GetReportSize(u8 rid)
710 {
711 using namespace WiimoteCommon;
712
713 const auto report_id = static_cast<InputReportID>(rid);
714
715 switch (report_id)
716 {
717 case InputReportID::Status:
718 return sizeof(InputReportStatus);
719 case InputReportID::ReadDataReply:
720 return sizeof(InputReportReadDataReply);
721 case InputReportID::Ack:
722 return sizeof(InputReportAck);
723 default:
724 if (DataReportBuilder::IsValidMode(report_id))
725 return MakeDataReportManipulator(report_id, nullptr)->GetDataSize();
726 else
727 return 0;
728 }
729 }
730
731 // positive = read packet
732 // negative = didn't read packet
733 // zero = error
IORead(HANDLE & dev_handle,OVERLAPPED & hid_overlap_read,u8 * buf,int index)734 int IORead(HANDLE& dev_handle, OVERLAPPED& hid_overlap_read, u8* buf, int index)
735 {
736 // Add data report indicator byte (here, 0xa1)
737 buf[0] = 0xa1;
738 // Used below for a warning
739 buf[1] = 0;
740
741 DWORD bytes = 0;
742 ResetEvent(hid_overlap_read.hEvent);
743 if (!ReadFile(dev_handle, buf + 1, MAX_PAYLOAD - 1, &bytes, &hid_overlap_read))
744 {
745 auto const read_err = GetLastError();
746
747 if (ERROR_IO_PENDING == read_err)
748 {
749 if (!GetOverlappedResult(dev_handle, &hid_overlap_read, &bytes, TRUE))
750 {
751 auto const overlapped_err = GetLastError();
752
753 // In case it was aborted by someone else
754 if (ERROR_OPERATION_ABORTED == overlapped_err)
755 {
756 return -1;
757 }
758
759 WARN_LOG(WIIMOTE, "GetOverlappedResult error %d on Wiimote %i.", overlapped_err, index + 1);
760 return 0;
761 }
762 // If IOWakeup sets the event so GetOverlappedResult returns prematurely, but the request is
763 // still pending
764 else if (hid_overlap_read.Internal == STATUS_PENDING)
765 {
766 // Don't forget to cancel it.
767 CancelIo(dev_handle);
768 return -1;
769 }
770 }
771 else
772 {
773 WARN_LOG(WIIMOTE, "ReadFile error %d on Wiimote %i.", read_err, index + 1);
774 return 0;
775 }
776 }
777
778 // ReadFile will always return 22 bytes read.
779 // So we need to calculate the actual report size by its report ID
780 DWORD report_size = static_cast<DWORD>(GetReportSize(buf[1]));
781 if (report_size == 0)
782 {
783 WARN_LOG(WIIMOTE, "Received unsupported report %d in Wii Remote %i", buf[1], index + 1);
784 return -1;
785 }
786
787 // 1 Byte for the Data Report Byte, another for the Report ID and the actual report size
788 return 1 + 1 + report_size;
789 }
790
IOWakeup()791 void WiimoteWindows::IOWakeup()
792 {
793 SetEvent(m_hid_overlap_read.hEvent);
794 }
795
796 // positive = read packet
797 // negative = didn't read packet
798 // zero = error
IORead(u8 * buf)799 int WiimoteWindows::IORead(u8* buf)
800 {
801 return WiimoteReal::IORead(m_dev_handle, m_hid_overlap_read, buf, m_index);
802 }
803
804 // As of https://msdn.microsoft.com/en-us/library/windows/hardware/ff543402(v=vs.85).aspx, WriteFile
805 // is the preferred method
806 // to send output reports to the HID. WriteFile sends an IRP_MJ_WRITE to the HID Class Driver
807 // (https://msdn.microsoft.com/en-us/library/windows/hardware/ff543402(v=vs.85).aspx).
808 // https://msdn.microsoft.com/en-us/library/windows/hardware/ff541027(v=vs.85).aspx &
809 // https://msdn.microsoft.com/en-us/library/windows/hardware/ff543402(v=vs.85).aspx
810 // state that the used buffer shall be the size of HidCaps.OutputReportSize (the largest output
811 // report).
812 // However as it seems only the Toshiba Bluetooth Stack, which provides its own HID Class Driver, as
813 // well as the HID Class Driver
814 // on Windows 7 enforce this requirement. Whereas on Windows 8/8.1/10 the buffer size can be the
815 // actual used report size.
816 // On Windows 7 when sending a smaller report to the device all bytes of the largest report are
817 // sent, which results in
818 // an error on the Wiimote. Toshiba Bluetooth Stack in contrast only sends the neccessary bytes of
819 // the report to the device.
820 // Therefore it is not possible to use WriteFile on Windows 7 to send data to the Wiimote and the
821 // fallback
822 // to HidP_SetOutputReport is implemented, which in turn does not support "-TR" Wiimotes.
823 // As to why on the later Windows' WriteFile or the HID Class Driver doesn't follow the
824 // documentation, it may be a bug or a feature.
825 // This leads to the following:
826 // - Toshiba Bluetooth Stack: Use WriteFile with resized output buffer
827 // - Windows Default HID Class: Try WriteFile with actual output buffer (will work in Win8/8.1/10)
828 // - When WriteFile fails, fallback to HidP_SetOutputReport (for Win7)
829 // Besides the documentation, WriteFile shall be the preferred method to send data, because it seems
830 // to use the Bluetooth Interrupt/Data Channel,
831 // whereas SetOutputReport uses the Control Channel. This leads to the advantage, that "-TR"
832 // Wiimotes work with WriteFile
833 // as they don't accept output reports via the Control Channel.
IOWrite(HANDLE & dev_handle,OVERLAPPED & hid_overlap_write,WinWriteMethod & write_method,const u8 * buf,size_t len,DWORD * written)834 int IOWrite(HANDLE& dev_handle, OVERLAPPED& hid_overlap_write, WinWriteMethod& write_method,
835 const u8* buf, size_t len, DWORD* written)
836 {
837 switch (write_method)
838 {
839 case WWM_WRITE_FILE_LARGEST_REPORT_SIZE:
840 case WWM_WRITE_FILE_ACTUAL_REPORT_SIZE:
841 return IOWritePerWriteFile(dev_handle, hid_overlap_write, write_method, buf, len, written);
842 case WWM_SET_OUTPUT_REPORT:
843 return IOWritePerSetOutputReport(dev_handle, buf, len, written);
844 }
845
846 return 0;
847 }
848
IOWrite(const u8 * buf,size_t len)849 int WiimoteWindows::IOWrite(const u8* buf, size_t len)
850 {
851 return WiimoteReal::IOWrite(m_dev_handle, m_hid_overlap_write, m_write_method, buf, len, nullptr);
852 }
853
854 // invokes callback for each found Wiimote Bluetooth device
855 template <typename T>
ProcessWiimotes(bool new_scan,const T & callback)856 void ProcessWiimotes(bool new_scan, const T& callback)
857 {
858 BLUETOOTH_DEVICE_SEARCH_PARAMS srch;
859 srch.dwSize = sizeof(srch);
860 srch.fReturnAuthenticated = true;
861 srch.fReturnRemembered = true;
862 // Does not filter properly somehow, so we need to do an additional check on
863 // fConnected BT Devices
864 srch.fReturnConnected = true;
865 srch.fReturnUnknown = true;
866 srch.fIssueInquiry = new_scan;
867 // multiple of 1.28 seconds
868 srch.cTimeoutMultiplier = 2;
869
870 BLUETOOTH_FIND_RADIO_PARAMS radioParam;
871 radioParam.dwSize = sizeof(radioParam);
872
873 HANDLE hRadio;
874
875 // TODO: save radio(s) in the WiimoteScanner constructor?
876
877 // Enumerate BT radios
878 HBLUETOOTH_RADIO_FIND hFindRadio = pBluetoothFindFirstRadio(&radioParam, &hRadio);
879 while (hFindRadio)
880 {
881 BLUETOOTH_RADIO_INFO radioInfo;
882 radioInfo.dwSize = sizeof(radioInfo);
883
884 auto const rinfo_result = pBluetoothGetRadioInfo(hRadio, &radioInfo);
885 if (ERROR_SUCCESS == rinfo_result)
886 {
887 srch.hRadio = hRadio;
888
889 BLUETOOTH_DEVICE_INFO btdi;
890 btdi.dwSize = sizeof(btdi);
891
892 // Enumerate BT devices
893 HBLUETOOTH_DEVICE_FIND hFindDevice = pBluetoothFindFirstDevice(&srch, &btdi);
894 while (hFindDevice)
895 {
896 // btdi.szName is sometimes missing it's content - it's a bt feature..
897 DEBUG_LOG(WIIMOTE, "Authenticated %i connected %i remembered %i ", btdi.fAuthenticated,
898 btdi.fConnected, btdi.fRemembered);
899
900 if (IsValidDeviceName(WStringToUTF8(btdi.szName)))
901 {
902 callback(hRadio, radioInfo, btdi);
903 }
904
905 if (false == pBluetoothFindNextDevice(hFindDevice, &btdi))
906 {
907 pBluetoothFindDeviceClose(hFindDevice);
908 hFindDevice = nullptr;
909 }
910 }
911 }
912
913 if (false == pBluetoothFindNextRadio(hFindRadio, &hRadio))
914 {
915 CloseHandle(hRadio);
916 pBluetoothFindRadioClose(hFindRadio);
917 hFindRadio = nullptr;
918 }
919 }
920 }
921
RemoveWiimote(BLUETOOTH_DEVICE_INFO_STRUCT & btdi)922 void RemoveWiimote(BLUETOOTH_DEVICE_INFO_STRUCT& btdi)
923 {
924 // if (btdi.fConnected)
925 {
926 if (SUCCEEDED(pBluetoothRemoveDevice(&btdi.Address)))
927 {
928 NOTICE_LOG(WIIMOTE, "Removed BT Device", GetLastError());
929 }
930 }
931 }
932
AttachWiimote(HANDLE hRadio,const BLUETOOTH_RADIO_INFO & radio_info,BLUETOOTH_DEVICE_INFO_STRUCT & btdi)933 bool AttachWiimote(HANDLE hRadio, const BLUETOOTH_RADIO_INFO& radio_info,
934 BLUETOOTH_DEVICE_INFO_STRUCT& btdi)
935 {
936 // We don't want "remembered" devices.
937 // SetServiceState will just fail with them..
938 if (!btdi.fConnected && !btdi.fRemembered)
939 {
940 auto const& wm_addr = btdi.Address.rgBytes;
941
942 NOTICE_LOG(WIIMOTE, "Found Wiimote (%02x:%02x:%02x:%02x:%02x:%02x). Enabling HID service.",
943 wm_addr[0], wm_addr[1], wm_addr[2], wm_addr[3], wm_addr[4], wm_addr[5]);
944
945 #if defined(AUTHENTICATE_WIIMOTES)
946 // Authenticate
947 auto const& radio_addr = radio_info.address.rgBytes;
948 // FIXME Not sure this usage of OOB_DATA_INFO is correct...
949 BLUETOOTH_OOB_DATA_INFO oob_data_info = {0};
950 memcpy(&oob_data_info.C[0], &radio_addr[0], sizeof(WCHAR) * 6);
951 const DWORD auth_result = pBluetoothAuthenticateDeviceEx(nullptr, hRadio, &btdi, &oob_data_info,
952 MITMProtectionNotDefined);
953
954 if (ERROR_SUCCESS != auth_result)
955 {
956 ERROR_LOG(WIIMOTE, "AttachWiimote: BluetoothAuthenticateDeviceEx returned %08x", auth_result);
957 }
958
959 DWORD pcServices = 16;
960 GUID guids[16];
961 // If this is not done, the Wii device will not remember the pairing
962 const DWORD srv_result =
963 pBluetoothEnumerateInstalledServices(hRadio, &btdi, &pcServices, guids);
964
965 if (ERROR_SUCCESS != srv_result)
966 {
967 ERROR_LOG(WIIMOTE, "AttachWiimote: BluetoothEnumerateInstalledServices returned %08x",
968 srv_result);
969 }
970 #endif
971 // Activate service
972 const DWORD hr = pBluetoothSetServiceState(
973 hRadio, &btdi, &HumanInterfaceDeviceServiceClass_UUID, BLUETOOTH_SERVICE_ENABLE);
974
975 s_connect_times[btdi.Address.ullLong] = std::time(nullptr);
976
977 if (FAILED(hr))
978 {
979 ERROR_LOG(WIIMOTE, "AttachWiimote: BluetoothSetServiceState returned %08x", hr);
980 }
981 else
982 {
983 return true;
984 }
985 }
986
987 return false;
988 }
989
990 // Removes remembered non-connected devices
ForgetWiimote(BLUETOOTH_DEVICE_INFO_STRUCT & btdi)991 bool ForgetWiimote(BLUETOOTH_DEVICE_INFO_STRUCT& btdi)
992 {
993 if (!btdi.fConnected && btdi.fRemembered)
994 {
995 // Time to avoid RemoveDevice after SetServiceState.
996 // Sometimes SetServiceState takes a while..
997 auto const avoid_forget_seconds = 5.0;
998
999 auto pair_time = s_connect_times.find(btdi.Address.ullLong);
1000 if (pair_time == s_connect_times.end() ||
1001 std::difftime(time(nullptr), pair_time->second) >= avoid_forget_seconds)
1002 {
1003 // Make Windows forget about device so it will re-find it if visible.
1004 // This is also required to detect a disconnect for some reason..
1005 NOTICE_LOG(WIIMOTE, "Removing remembered Wiimote.");
1006 pBluetoothRemoveDevice(&btdi.Address);
1007 return true;
1008 }
1009 }
1010
1011 return false;
1012 }
1013 } // namespace WiimoteReal
1014