mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-25 15:19:42 -06:00
Added a method for detecting Wiimote extension.
This is run before a Wiimote will be considered.
This commit is contained in:
@ -138,6 +138,10 @@ inline void init_lib()
|
|||||||
|
|
||||||
namespace WiimoteReal
|
namespace WiimoteReal
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
int _IOWrite(HANDLE &dev_handle, OVERLAPPED &hid_overlap_write, enum win_bt_stack_t &stack, const u8* buf, int len);
|
||||||
|
int _IORead(HANDLE &dev_handle, OVERLAPPED &hid_overlap_read, u8* buf, int index);
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void ProcessWiimotes(bool new_scan, T& callback);
|
void ProcessWiimotes(bool new_scan, T& callback);
|
||||||
@ -212,33 +216,23 @@ void WiimoteScanner::FindWiimotes(std::vector<Wiimote*> & found_wiimotes, Wiimot
|
|||||||
|
|
||||||
// Query the data for this device
|
// Query the data for this device
|
||||||
if (SetupDiGetDeviceInterfaceDetail(device_info, &device_data, detail_data, len, NULL, NULL))
|
if (SetupDiGetDeviceInterfaceDetail(device_info, &device_data, detail_data, len, NULL, NULL))
|
||||||
{
|
{
|
||||||
auto const wm = new Wiimote;
|
auto const wm = new Wiimote;
|
||||||
wm->devicepath = detail_data->DevicePath;
|
wm->devicepath = detail_data->DevicePath;
|
||||||
|
bool real_wiimote = false, is_bb = false;
|
||||||
TCHAR name[128] = {};
|
|
||||||
HANDLE dev_handle = CreateFile(wm->devicepath.c_str(),
|
CheckDeviceType(wm->devicepath, real_wiimote, is_bb);
|
||||||
GENERIC_READ,
|
if (is_bb)
|
||||||
FILE_SHARE_READ,
|
|
||||||
NULL,
|
|
||||||
OPEN_EXISTING,
|
|
||||||
FILE_FLAG_OVERLAPPED,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
if (dev_handle != INVALID_HANDLE_VALUE &&
|
|
||||||
HidD_GetProductString(dev_handle, name, 128) &&
|
|
||||||
IsBalanceBoardName(TStrToUTF8(name)))
|
|
||||||
{
|
{
|
||||||
found_board = wm;
|
found_board = wm;
|
||||||
}
|
}
|
||||||
else
|
else if (real_wiimote)
|
||||||
{
|
{
|
||||||
found_wiimotes.push_back(wm);
|
found_wiimotes.push_back(wm);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if (dev_handle != INVALID_HANDLE_VALUE)
|
|
||||||
{
|
{
|
||||||
CloseHandle(dev_handle);
|
free(wm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,6 +246,169 @@ void WiimoteScanner::FindWiimotes(std::vector<Wiimote*> & found_wiimotes, Wiimot
|
|||||||
// SLEEP(2000);
|
// SLEEP(2000);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
int CheckDeviceType_Write(HANDLE &dev_handle, const u8* buf, int size, int attempts)
|
||||||
|
{
|
||||||
|
OVERLAPPED hid_overlap_write = OVERLAPPED();
|
||||||
|
hid_overlap_write.hEvent = CreateEvent(NULL, true, false, NULL);
|
||||||
|
enum win_bt_stack_t stack = MSBT_STACK_UNKNOWN;
|
||||||
|
|
||||||
|
DWORD written = 0;
|
||||||
|
|
||||||
|
for (; attempts>0; --attempts)
|
||||||
|
{
|
||||||
|
if (_IOWrite(dev_handle, hid_overlap_write, stack, buf, size))
|
||||||
|
{
|
||||||
|
auto const wait_result = WaitForSingleObject(hid_overlap_write.hEvent, WIIMOTE_DEFAULT_TIMEOUT);
|
||||||
|
if (WAIT_TIMEOUT == wait_result)
|
||||||
|
{
|
||||||
|
WARN_LOG(WIIMOTE, "CheckDeviceType_Write: A timeout occurred on writing to Wiimote.");
|
||||||
|
CancelIo(dev_handle);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (WAIT_FAILED == wait_result)
|
||||||
|
{
|
||||||
|
WARN_LOG(WIIMOTE, "CheckDeviceType_Write: A wait error occurred on writing to Wiimote.");
|
||||||
|
CancelIo(dev_handle);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (GetOverlappedResult(dev_handle, &hid_overlap_write, &written, TRUE))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseHandle(hid_overlap_write.hEvent);
|
||||||
|
|
||||||
|
return written;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CheckDeviceType_Read(HANDLE &dev_handle, u8* buf, int attempts)
|
||||||
|
{
|
||||||
|
OVERLAPPED hid_overlap_read = OVERLAPPED();
|
||||||
|
hid_overlap_read.hEvent = CreateEvent(NULL, true, false, NULL);
|
||||||
|
int read = 0;
|
||||||
|
for (; attempts>0; --attempts)
|
||||||
|
{
|
||||||
|
read = _IORead(dev_handle, hid_overlap_read, buf, 1);
|
||||||
|
if (read > 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseHandle(hid_overlap_read.hEvent);
|
||||||
|
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A convoluted way of checking if a device is a Wii Balance Board and if it is a connectable Wiimote.
|
||||||
|
// Because nothing on Windows should be easy.
|
||||||
|
// (We can't seem to easily identify the bluetooth device an HID device belongs to...)
|
||||||
|
void WiimoteScanner::CheckDeviceType(std::basic_string<TCHAR> &devicepath, bool &real_wiimote, bool &is_bb)
|
||||||
|
{
|
||||||
|
real_wiimote = false;
|
||||||
|
is_bb = false;
|
||||||
|
|
||||||
|
#ifdef SHARE_WRITE_WIIMOTES
|
||||||
|
std::lock_guard<std::mutex> lk(g_connected_wiimotes_lock);
|
||||||
|
if (g_connected_wiimotes.count(devicepath) != 0)
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
HANDLE dev_handle = CreateFile(devicepath.c_str(),
|
||||||
|
GENERIC_READ | GENERIC_WRITE,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||||
|
NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED,
|
||||||
|
NULL);
|
||||||
|
if (dev_handle == INVALID_HANDLE_VALUE)
|
||||||
|
return;
|
||||||
|
// enable to only check for official nintendo wiimotes/bb's
|
||||||
|
bool check_vidpid = false;
|
||||||
|
HIDD_ATTRIBUTES attrib;
|
||||||
|
attrib.Size = sizeof(attrib);
|
||||||
|
if (!check_vidpid ||
|
||||||
|
(HidD_GetAttributes(dev_handle, &attrib) &&
|
||||||
|
(attrib.VendorID == 0x057e) &&
|
||||||
|
(attrib.ProductID == 0x0306)))
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
// max_cycles insures we are never stuck here due to bad coding...
|
||||||
|
int max_cycles = 20;
|
||||||
|
u8 buf[MAX_PAYLOAD] = {0};
|
||||||
|
|
||||||
|
u8 const req_status_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_REQUEST_STATUS, 0};
|
||||||
|
|
||||||
|
rc = CheckDeviceType_Write(dev_handle,
|
||||||
|
req_status_report,
|
||||||
|
sizeof(req_status_report),
|
||||||
|
1);
|
||||||
|
while (rc > 0 && --max_cycles > 0)
|
||||||
|
{
|
||||||
|
if ((rc = CheckDeviceType_Read(dev_handle, buf, 1)) <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (buf[1])
|
||||||
|
{
|
||||||
|
case WM_STATUS_REPORT:
|
||||||
|
{
|
||||||
|
real_wiimote = true;
|
||||||
|
wm_status_report * wsr = (wm_status_report*)&buf[2];
|
||||||
|
if (wsr->extension)
|
||||||
|
{
|
||||||
|
// Wiimote with extension, we ask it what kind.
|
||||||
|
u8 read_ext[MAX_PAYLOAD] = {0};
|
||||||
|
read_ext[0] = WM_SET_REPORT | WM_BT_OUTPUT;
|
||||||
|
read_ext[1] = WM_READ_DATA;
|
||||||
|
// Extension type register.
|
||||||
|
*(u32*)&read_ext[2] = Common::swap32(0x4a400fa);
|
||||||
|
// Size.
|
||||||
|
*(u16*)&read_ext[6] = Common::swap16(6);
|
||||||
|
rc = CheckDeviceType_Write(dev_handle, read_ext, 8, 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Normal Wiimote, exit while and be happy.
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WM_ACK_DATA:
|
||||||
|
{
|
||||||
|
real_wiimote = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WM_READ_DATA_REPLY:
|
||||||
|
{
|
||||||
|
wm_read_data_reply * wrdr
|
||||||
|
= (wm_read_data_reply*)&buf[2];
|
||||||
|
// Check if it has returned what we asked.
|
||||||
|
if (Common::swap16(wrdr->address) == 0x00fa)
|
||||||
|
{
|
||||||
|
// 0x020420A40000ULL means balance board.
|
||||||
|
u64 ext_type = (*(u64*)&wrdr->data[0]);
|
||||||
|
is_bb = ext_type == 0x020420A40000ULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ERROR_LOG(WIIMOTE,
|
||||||
|
"CheckDeviceType: GOT UNREQUESTED ADDRESS %X",
|
||||||
|
Common::swap16(wrdr->address));
|
||||||
|
}
|
||||||
|
// force end
|
||||||
|
rc = -1;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
// We let read try again incase there is another packet waiting.
|
||||||
|
// ERROR_LOG(WIIMOTE, "CheckDeviceType: GOT UNKNOWN REPLY: %X", buf[1]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CloseHandle(dev_handle);
|
||||||
|
}
|
||||||
|
|
||||||
bool WiimoteScanner::IsReady() const
|
bool WiimoteScanner::IsReady() const
|
||||||
{
|
{
|
||||||
@ -376,7 +533,7 @@ bool Wiimote::IsConnected() const
|
|||||||
// positive = read packet
|
// positive = read packet
|
||||||
// negative = didn't read packet
|
// negative = didn't read packet
|
||||||
// zero = error
|
// zero = error
|
||||||
int Wiimote::IORead(u8* buf)
|
int _IORead(HANDLE &dev_handle, OVERLAPPED &hid_overlap_read, u8* buf, int index)
|
||||||
{
|
{
|
||||||
// Add data report indicator byte (here, 0xa1)
|
// Add data report indicator byte (here, 0xa1)
|
||||||
buf[0] = 0xa1;
|
buf[0] = 0xa1;
|
||||||
@ -429,78 +586,92 @@ int Wiimote::IORead(u8* buf)
|
|||||||
return bytes + 1;
|
return bytes + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Wiimote::IOWrite(const u8* buf, int len)
|
// positive = read packet
|
||||||
|
// negative = didn't read packet
|
||||||
|
// zero = error
|
||||||
|
int Wiimote::IORead(u8* buf)
|
||||||
|
{
|
||||||
|
return _IORead(dev_handle, hid_overlap_read, buf, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int _IOWrite(HANDLE &dev_handle, OVERLAPPED &hid_overlap_write, enum win_bt_stack_t &stack, const u8* buf, int len)
|
||||||
{
|
{
|
||||||
switch (stack)
|
switch (stack)
|
||||||
{
|
{
|
||||||
case MSBT_STACK_UNKNOWN:
|
case MSBT_STACK_UNKNOWN:
|
||||||
{
|
|
||||||
// Try to auto-detect the stack type
|
|
||||||
stack = MSBT_STACK_BLUESOLEIL;
|
|
||||||
if (IOWrite(buf, len))
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
stack = MSBT_STACK_MS;
|
|
||||||
if (IOWrite(buf, len))
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
stack = MSBT_STACK_UNKNOWN;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case MSBT_STACK_MS:
|
|
||||||
{
|
|
||||||
auto result = HidD_SetOutputReport(dev_handle, const_cast<u8*>(buf) + 1, len - 1);
|
|
||||||
//FlushFileBuffers(dev_handle);
|
|
||||||
|
|
||||||
if (!result)
|
|
||||||
{
|
{
|
||||||
auto err = GetLastError();
|
// Try to auto-detect the stack type
|
||||||
if (err == 121)
|
stack = MSBT_STACK_BLUESOLEIL;
|
||||||
|
if (_IOWrite(dev_handle, hid_overlap_write, stack, buf, len))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
stack = MSBT_STACK_MS;
|
||||||
|
if (_IOWrite(dev_handle, hid_overlap_write, stack, buf, len))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
stack = MSBT_STACK_UNKNOWN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MSBT_STACK_MS:
|
||||||
|
{
|
||||||
|
auto result = HidD_SetOutputReport(dev_handle, const_cast<u8*>(buf) + 1, len - 1);
|
||||||
|
//FlushFileBuffers(dev_handle);
|
||||||
|
|
||||||
|
if (!result)
|
||||||
{
|
{
|
||||||
// Semaphore timeout
|
auto err = GetLastError();
|
||||||
NOTICE_LOG(WIIMOTE, "WiimoteIOWrite[MSBT_STACK_MS]: Unable to send data to the Wiimote");
|
if (err == 121)
|
||||||
|
{
|
||||||
|
// Semaphore timeout
|
||||||
|
NOTICE_LOG(WIIMOTE, "WiimoteIOWrite[MSBT_STACK_MS]: Unable to send data to the Wiimote");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WARN_LOG(WIIMOTE, "IOWrite[MSBT_STACK_MS]: ERROR: %08x", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MSBT_STACK_BLUESOLEIL:
|
||||||
|
{
|
||||||
|
u8 big_buf[MAX_PAYLOAD];
|
||||||
|
if (len < MAX_PAYLOAD)
|
||||||
|
{
|
||||||
|
std::copy(buf, buf + len, big_buf);
|
||||||
|
std::fill(big_buf + len, big_buf + MAX_PAYLOAD, 0);
|
||||||
|
buf = big_buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResetEvent(hid_overlap_write.hEvent);
|
||||||
|
DWORD bytes = 0;
|
||||||
|
if (WriteFile(dev_handle, buf + 1, MAX_PAYLOAD - 1, &bytes, &hid_overlap_write))
|
||||||
|
{
|
||||||
|
// WriteFile always returns true with bluesoleil.
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
WARN_LOG(WIIMOTE, "IOWrite[MSBT_STACK_MS]: ERROR: %08x", err);
|
auto const err = GetLastError();
|
||||||
|
if (ERROR_IO_PENDING == err)
|
||||||
|
{
|
||||||
|
CancelIo(dev_handle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case MSBT_STACK_BLUESOLEIL:
|
|
||||||
{
|
|
||||||
u8 big_buf[MAX_PAYLOAD];
|
|
||||||
if (len < MAX_PAYLOAD)
|
|
||||||
{
|
|
||||||
std::copy(buf, buf + len, big_buf);
|
|
||||||
std::fill(big_buf + len, big_buf + MAX_PAYLOAD, 0);
|
|
||||||
buf = big_buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
ResetEvent(hid_overlap_write.hEvent);
|
|
||||||
DWORD bytes = 0;
|
|
||||||
if (WriteFile(dev_handle, buf + 1, MAX_PAYLOAD - 1, &bytes, &hid_overlap_write))
|
|
||||||
{
|
|
||||||
// WriteFile always returns true with bluesoleil.
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto const err = GetLastError();
|
|
||||||
if (ERROR_IO_PENDING == err)
|
|
||||||
{
|
|
||||||
CancelIo(dev_handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Wiimote::IOWrite(const u8* buf, int len)
|
||||||
|
{
|
||||||
|
return _IOWrite(dev_handle, hid_overlap_write, stack, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
// invokes callback for each found wiimote bluetooth device
|
// invokes callback for each found wiimote bluetooth device
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void ProcessWiimotes(bool new_scan, T& callback)
|
void ProcessWiimotes(bool new_scan, T& callback)
|
||||||
@ -519,7 +690,7 @@ void ProcessWiimotes(bool new_scan, T& callback)
|
|||||||
|
|
||||||
BLUETOOTH_FIND_RADIO_PARAMS radioParam;
|
BLUETOOTH_FIND_RADIO_PARAMS radioParam;
|
||||||
radioParam.dwSize = sizeof(radioParam);
|
radioParam.dwSize = sizeof(radioParam);
|
||||||
|
|
||||||
HANDLE hRadio;
|
HANDLE hRadio;
|
||||||
|
|
||||||
// TODO: save radio(s) in the WiimoteScanner constructor?
|
// TODO: save radio(s) in the WiimoteScanner constructor?
|
||||||
|
@ -137,8 +137,7 @@ private:
|
|||||||
volatile bool m_want_bb;
|
volatile bool m_want_bb;
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
|
void CheckDeviceType(std::basic_string<TCHAR> &devicepath, bool &real_wiimote, bool &is_bb);
|
||||||
|
|
||||||
#elif defined(__linux__) && HAVE_BLUEZ
|
#elif defined(__linux__) && HAVE_BLUEZ
|
||||||
int device_id;
|
int device_id;
|
||||||
int device_sock;
|
int device_sock;
|
||||||
|
Reference in New Issue
Block a user