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