Skip to content

Commit

Permalink
Merge pull request #373 from thp/zcm2u
Browse files Browse the repository at this point in the history
Finishing up support the the ZCM2 PSMove controller (with code cleanups)
  • Loading branch information
thp authored Oct 22, 2018
2 parents 30f1705 + 376d857 commit eca8542
Show file tree
Hide file tree
Showing 6 changed files with 808 additions and 196 deletions.
15 changes: 15 additions & 0 deletions include/psmove.h
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,21 @@ ADDCALL psmove_is_remote(PSMove *move);
ADDAPI char *
ADDCALL psmove_get_serial(PSMove *move);

/**
* \brief Get the model type (ZCM1 or ZCM2) of a controller.
*
* There are two primary version of the PSMove controller.
* The ZCM1 is the original PSMove controller that shipped with the PS3.
* The ZCM2 is the newer PSMove controller that shipped with the PS4.
* The most important difference is that ZCM2 does not have a magnetometer.
*
* \param move A valid \ref PSMove handle
*
* \return The \ref PSMove_Model_Type type of the controller
**/
ADDAPI enum PSMove_Model_Type
ADDCALL psmove_get_model(PSMove *move);

/**
* \brief Pair a controller connected via USB with the computer.
*
Expand Down
181 changes: 175 additions & 6 deletions src/platform/psmove_port_windows.c
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,155 @@ is_connection_established(const HANDLE hRadio, BLUETOOTH_DEVICE_INFO *device_inf
return 1;
}

struct BluetoothAuthenticationCallbackState
{
HANDLE hRadio;
BLUETOOTH_DEVICE_INFO *deviceInfo;
HANDLE hAuthenticationCompleteEvent;
};

static BOOL CALLBACK
bluetooth_auth_callback(
LPVOID pvParam,
PBLUETOOTH_AUTHENTICATION_CALLBACK_PARAMS pAuthCallbackParams)
{
struct BluetoothAuthenticationCallbackState *state= (struct BluetoothAuthenticationCallbackState *)pvParam;

BLUETOOTH_AUTHENTICATE_RESPONSE AuthRes;
memset(&AuthRes, 0, sizeof(BLUETOOTH_AUTHENTICATE_RESPONSE));
AuthRes.authMethod = pAuthCallbackParams->authenticationMethod;
AuthRes.bthAddressRemote = pAuthCallbackParams->deviceInfo.Address;
AuthRes.negativeResponse = 0;

// Send authentication response to authenticate device
DWORD dwRet = BluetoothSendAuthenticationResponseEx(state->hRadio, &AuthRes);
switch (dwRet) {
case ERROR_SUCCESS:
// Flag the device as authenticated
WINPAIR_DEBUG("Bluetooth device authenticated!");
state->deviceInfo->fAuthenticated = TRUE;
break;
case ERROR_CANCELLED:
WINPAIR_DEBUG("Bluetooth device denied passkey response");
break;
case E_FAIL:
WINPAIR_DEBUG("Failure during authentication");
break;
case ERROR_NOT_READY:
WINPAIR_DEBUG("Device not ready");
break;
case ERROR_INVALID_PARAMETER:
WINPAIR_DEBUG("Invalid parameter");
break;
case 1244: // TODO: Is there a named constant for this?
WINPAIR_DEBUG("Not authenticated");
break;
default:
WINPAIR_DEBUG("BluetoothSendAuthenticationResponseEx failed: %d", GetLastError());
break;
}

// Signal the thread that the authentication callback completed
if (!SetEvent(state->hAuthenticationCompleteEvent))
{
WINPAIR_DEBUG("Failed to set event: %d", GetLastError());
}

return TRUE;
}

static int
authenticate_controller(
HANDLE hRadio,
BLUETOOTH_DEVICE_INFO *device_info)
{
int ret;

if (device_info->fAuthenticated == 0)
{
struct BluetoothAuthenticationCallbackState callbackState;
callbackState.deviceInfo= device_info;
callbackState.hAuthenticationCompleteEvent= INVALID_HANDLE_VALUE;
callbackState.hRadio= hRadio;

// Register authentication callback before starting authentication request
HBLUETOOTH_AUTHENTICATION_REGISTRATION hRegHandle = 0;
DWORD dwRet = BluetoothRegisterForAuthenticationEx(
device_info, &hRegHandle, &bluetooth_auth_callback, &callbackState);

if (dwRet == ERROR_SUCCESS)
{
callbackState.hAuthenticationCompleteEvent = CreateEvent(
NULL, // default security attributes
TRUE, // manual-reset event
FALSE, // initial state is non-signaled
TEXT("AuthenticationCompleteEvent")); // object name

// Start the authentication request
dwRet = BluetoothAuthenticateDeviceEx(
NULL,
hRadio,
device_info,
NULL,
MITMProtectionNotRequiredBonding);

if (dwRet == ERROR_NO_MORE_ITEMS)
{
WINPAIR_DEBUG("Already paired.");
ret= 0;
}
else if (dwRet == ERROR_CANCELLED)
{
WINPAIR_DEBUG("User canceled the authentication.");
ret= 1;
}
else if (dwRet == ERROR_INVALID_PARAMETER)
{
WINPAIR_DEBUG("Invalid parameter!");
ret= 1;
}
else
{
// Block on authentication completing
WaitForSingleObject(callbackState.hAuthenticationCompleteEvent, INFINITE);

if (device_info->fAuthenticated)
{
WINPAIR_DEBUG("Successfully paired.");
ret= 0;
}
else
{
WINPAIR_DEBUG("Failed to authenticate!");
ret= 1;
}
}

if (callbackState.hAuthenticationCompleteEvent != INVALID_HANDLE_VALUE)
{
CloseHandle(callbackState.hAuthenticationCompleteEvent);
}
}
else
{
WINPAIR_DEBUG("BluetoothRegisterForAuthenticationEx failed!");
ret= 1;
}

if (hRegHandle != 0)
{
BluetoothUnregisterAuthentication(hRegHandle);
hRegHandle= 0;
}
}
else
{
WINPAIR_DEBUG("Already authenticated.");
ret= 0;
}

return ret;
}

static int
patch_registry(const BLUETOOTH_ADDRESS *move_addr, const BLUETOOTH_ADDRESS *radio_addr)
Expand Down Expand Up @@ -368,7 +517,7 @@ is_windows8_or_later()


static void
handle_windows8_and_later(const BLUETOOTH_ADDRESS *move_addr, const BLUETOOTH_ADDRESS *radio_addr, const HANDLE hRadio)
handle_windows8_and_later(const BLUETOOTH_ADDRESS *move_addr, const BLUETOOTH_ADDRESS *radio_addr, const HANDLE hRadio, enum PSMove_Model_Type model)
{
unsigned int scan = 0;
int connected = 0;
Expand All @@ -380,6 +529,16 @@ handle_windows8_and_later(const BLUETOOTH_ADDRESS *move_addr, const BLUETOOTH_AD
if (is_move_motion_controller(&device_info)) {
WINPAIR_DEBUG("Found Move Motion Controller matching the given address");

if (model == Model_ZCM2)
{
if (authenticate_controller(hRadio, &device_info) != 0)
{
WINPAIR_DEBUG("Failed to authenticate the controller");
Sleep(SLEEP_BETWEEN_SCANS);
continue;
}
}

unsigned int conn_try;
for (conn_try = 1; conn_try <= CONN_RETRIES; conn_try++) {
WINPAIR_DEBUG("Connection try %d/%d", conn_try, CONN_RETRIES);
Expand Down Expand Up @@ -441,7 +600,7 @@ handle_windows8_and_later(const BLUETOOTH_ADDRESS *move_addr, const BLUETOOTH_AD


static void
handle_windows_pre8(const BLUETOOTH_ADDRESS *move_addr, const BLUETOOTH_ADDRESS *radio_addr, const HANDLE hRadio)
handle_windows_pre8(const BLUETOOTH_ADDRESS *move_addr, const BLUETOOTH_ADDRESS *radio_addr, const HANDLE hRadio, enum PSMove_Model_Type model)
{
int connected = 0;
while (!connected) {
Expand All @@ -452,6 +611,16 @@ handle_windows_pre8(const BLUETOOTH_ADDRESS *move_addr, const BLUETOOTH_ADDRESS
if (is_move_motion_controller(&device_info)) {
WINPAIR_DEBUG("Found Move Motion Controller matching the given address");

if (model == Model_ZCM2)
{
if (authenticate_controller(hRadio, &device_info) != 0)
{
WINPAIR_DEBUG("Failed to authenticate the controller");
Sleep(SLEEP_BETWEEN_SCANS);
continue;
}
}

if (device_info.fConnected) {
/* enable HID service only if necessary */
WINPAIR_DEBUG("Checking HID service ...");
Expand Down Expand Up @@ -483,7 +652,7 @@ handle_windows_pre8(const BLUETOOTH_ADDRESS *move_addr, const BLUETOOTH_ADDRESS


static int
windows_register_psmove(const char *move_addr_str, const BLUETOOTH_ADDRESS *radio_addr, const HANDLE hRadio)
windows_register_psmove(const char *move_addr_str, const BLUETOOTH_ADDRESS *radio_addr, const HANDLE hRadio, enum PSMove_Model_Type model)
{
/* parse controller's Bluetooth device address string */
BLUETOOTH_ADDRESS *move_addr = string_to_btaddr(move_addr_str);
Expand Down Expand Up @@ -512,10 +681,10 @@ windows_register_psmove(const char *move_addr_str, const BLUETOOTH_ADDRESS *radi

if (is_windows8_or_later()) {
WINPAIR_DEBUG("Dealing with Windows 8 or later");
handle_windows8_and_later(move_addr, radio_addr, hRadio);
handle_windows8_and_later(move_addr, radio_addr, hRadio, model);
} else {
WINPAIR_DEBUG("Dealing with Windows version older than 8");
handle_windows_pre8(move_addr, radio_addr, hRadio);
handle_windows_pre8(move_addr, radio_addr, hRadio, model);
}

free(move_addr);
Expand Down Expand Up @@ -688,7 +857,7 @@ psmove_port_register_psmove(const char *addr, const char *host, enum PSMove_Mode
return PSMove_False;
}

int res = windows_register_psmove(addr, &radioInfo.address, hRadio);
int res = windows_register_psmove(addr, &radioInfo.address, hRadio, model);
CloseHandle(hRadio);

return (res == 0) ? PSMove_True : PSMove_False;
Expand Down
Loading

0 comments on commit eca8542

Please sign in to comment.