static int set_up_bluetooth_radio(const HANDLE hRadio) { /* NOTE: Order matters for the following two operations: The radio must * allow incoming connections prior to being made discoverable. */ if (!BluetoothIsConnectable(hRadio)) { WINPAIR_DEBUG("Making radio accept incoming connections"); if (BluetoothEnableIncomingConnections(hRadio, TRUE) == FALSE) { WINPAIR_DEBUG("Failed to enable incoming connections"); return 1; } } if (!BluetoothIsDiscoverable(hRadio)) { WINPAIR_DEBUG("Making radio discoverable"); if (BluetoothEnableDiscovery(hRadio, TRUE) == FALSE) { WINPAIR_DEBUG("Failed to make radio discoverable"); return 1; } } if ((BluetoothIsConnectable(hRadio) != FALSE) && (BluetoothIsDiscoverable(hRadio) != FALSE)) { return 0; } else { return 1; } }
static int is_connection_established(const HANDLE hRadio, BLUETOOTH_DEVICE_INFO *device_info) { /* NOTE: Sometimes the Bluetooth connection appears to be established * even though the Move decided that it is not really connected * yet. That is why we cannot simply stop trying to connect after * the first successful check. Instead, we require a minimum * number of successive successful checks to be sure. */ unsigned int i; for (i = 0; i < CONN_CHECK_NUM_TRIES; i++) { /* read device info again to check if we have a connection */ DWORD result = BluetoothGetDeviceInfo(hRadio, device_info); if (result != ERROR_SUCCESS) { WINPAIR_DEBUG("Failed to read device info"); return 0; } if (device_info->fConnected && device_info->fRemembered && is_hid_service_enabled(hRadio, device_info)) { } else { return 0; } Sleep(CONN_CHECK_DELAY); } return 1; }
static int is_hid_service_enabled(const HANDLE hRadio, BLUETOOTH_DEVICE_INFO *device_info) { /* retrieve number of installed services */ DWORD num_services = 0; DWORD result = BluetoothEnumerateInstalledServices(hRadio, device_info, &num_services, NULL); if (result != ERROR_SUCCESS) { /* NOTE: Sometimes we get ERROR_MORE_DATA, sometimes we do not. * The number of services seems to be correct in any case, so * we will just ignore this. */ if (result != ERROR_MORE_DATA) { WINPAIR_DEBUG("Failed to count installed services"); return 0; } } if (num_services == 0) { return 0; } /* retrieve actual list of installed services */ GUID *service_list = (GUID *) calloc(num_services, sizeof(GUID)); if (!service_list) { return 0; } result = BluetoothEnumerateInstalledServices(hRadio, device_info, &num_services, service_list); if (result != ERROR_SUCCESS) { WINPAIR_DEBUG("Failed to enumerate installed services"); return 0; } /* check if the HID service is part of that list */ unsigned int i; int found = 0; GUID service = HumanInterfaceDeviceServiceClass_UUID; for (i = 0; i < num_services; i++) { if (IsEqualGUID(&service_list[i], &service)) { found = 1; break; } } free(service_list); return found; }
static int patch_registry(const BLUETOOTH_ADDRESS *move_addr, const BLUETOOTH_ADDRESS *radio_addr) { int ret = 0; TCHAR sub_key[1024]; HRESULT res = StringCchPrintf( sub_key, 1024, _T("SYSTEM\\CurrentControlSet\\Services\\HidBth\\Parameters\\Devices\\" \ "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"), radio_addr->rgBytes[5], radio_addr->rgBytes[4], radio_addr->rgBytes[3], radio_addr->rgBytes[2], radio_addr->rgBytes[1], radio_addr->rgBytes[0], move_addr->rgBytes[5], move_addr->rgBytes[4], move_addr->rgBytes[3], move_addr->rgBytes[2], move_addr->rgBytes[1], move_addr->rgBytes[0] ); if (FAILED(res)) { WINPAIR_DEBUG("Failed to build registry subkey"); return 1; } /* open registry key for modifying a value */ HKEY hKey; LONG result; result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, sub_key, 0, KEY_SET_VALUE | KEY_WOW64_64KEY, &hKey); if (result != ERROR_SUCCESS) { if (result == ERROR_FILE_NOT_FOUND) { WINPAIR_DEBUG("Failed to open registry key, it does not yet exist"); } else { WINPAIR_DEBUG("Failed to open registry key"); } return 1; } DWORD data = 1; result = RegSetValueEx(hKey, _T("VirtuallyCabled"), 0, REG_DWORD, (const BYTE *) &data, sizeof(data)); if (result != ERROR_SUCCESS) { WINPAIR_DEBUG("Failed to set 'VirtuallyCabled'"); ret = 1; } RegCloseKey(hKey); return ret; }
static int get_bluetooth_device_info(const HANDLE hRadio, const BLUETOOTH_ADDRESS *addr, BLUETOOTH_DEVICE_INFO *device_info) { if (!addr || !device_info) { return 1; } BLUETOOTH_DEVICE_SEARCH_PARAMS search_params; search_params.dwSize = sizeof(search_params); search_params.cTimeoutMultiplier = 4; search_params.fIssueInquiry = FALSE; search_params.fReturnAuthenticated = TRUE; search_params.fReturnConnected = TRUE; search_params.fReturnRemembered = TRUE; search_params.fReturnUnknown = TRUE; search_params.hRadio = hRadio; device_info->dwSize = sizeof(*device_info); HBLUETOOTH_DEVICE_FIND hFind = BluetoothFindFirstDevice(&search_params, device_info); if (!hFind) { if (GetLastError() != ERROR_NO_MORE_ITEMS) { WINPAIR_DEBUG("Failed to enumerate devices"); return 1; } } else { do { /* check if the device's Bluetooth address matches the one we are looking for */ if (device_info->Address.ullLong == addr->ullLong) { break; } } while(BluetoothFindNextDevice(hFind, device_info)); if (!BluetoothFindDeviceClose(hFind)) { WINPAIR_DEBUG("Failed to close device enumeration handle"); return 1; } } return 0; }
static int update_device_info(const HANDLE hRadio, BLUETOOTH_DEVICE_INFO *device_info) { DWORD result = BluetoothGetDeviceInfo(hRadio, device_info); if (result != ERROR_SUCCESS) { WINPAIR_DEBUG("Failed to read device info"); return 1; } return 0; }
static int windows_register_psmove(const char *move_addr_str, const BLUETOOTH_ADDRESS *radio_addr, const HANDLE hRadio) { /* parse controller's Bluetooth device address string */ BLUETOOTH_ADDRESS *move_addr = string_to_btaddr(move_addr_str); if (!move_addr) { WINPAIR_DEBUG("Cannot parse controller address: '%s'", move_addr_str); return 1; } if (!radio_addr) { WINPAIR_DEBUG("Invalid Bluetooth device address for radio"); return 1; } if (set_up_bluetooth_radio(hRadio) != 0) { WINPAIR_DEBUG("Failed to configure Bluetooth radio for use"); return 1; } printf("\n" \ " Unplug the controller.\n" \ "\n" " Now press the controller's PS button. The red status LED\n" \ " will start blinking. Whenever it goes off, press the\n" \ " PS button again. Repeat this until the status LED finally\n" \ " remains lit. Press Ctrl+C to cancel anytime.\n"); if (is_windows8_or_later()) { WINPAIR_DEBUG("Dealing with Windows 8 or later"); handle_windows8_and_later(move_addr, radio_addr, hRadio); } else { WINPAIR_DEBUG("Dealing with Windows version older than 8"); handle_windows_pre8(move_addr, radio_addr, hRadio); } free(move_addr); return 0; }
int windows_get_first_bluetooth_radio(HANDLE *hRadio) { if (!hRadio) { return 1; } BLUETOOTH_FIND_RADIO_PARAMS radio_params; radio_params.dwSize = sizeof(BLUETOOTH_FIND_RADIO_PARAMS); HBLUETOOTH_RADIO_FIND hFind = BluetoothFindFirstRadio(&radio_params, hRadio); if (!hFind) { WINPAIR_DEBUG("Failed to enumerate Bluetooth radios"); return 1; } BluetoothFindRadioClose(hFind); return 0; }
static void handle_windows_pre8(const BLUETOOTH_ADDRESS *move_addr, const BLUETOOTH_ADDRESS *radio_addr, const HANDLE hRadio) { int connected = 0; while (!connected) { BLUETOOTH_DEVICE_INFO device_info; if (get_bluetooth_device_info(hRadio, move_addr, &device_info, 0) != 0) { WINPAIR_DEBUG("No Bluetooth device found matching the given address"); } else { if (is_move_motion_controller(&device_info)) { WINPAIR_DEBUG("Found Move Motion Controller matching the given address"); if (device_info.fConnected) { /* enable HID service only if necessary */ WINPAIR_DEBUG("Checking HID service ..."); if (!is_hid_service_enabled(hRadio, &device_info)) { WINPAIR_DEBUG("Enabling HID service ..."); GUID service = HumanInterfaceDeviceServiceClass_UUID; DWORD result = BluetoothSetServiceState(hRadio, &device_info, &service, BLUETOOTH_SERVICE_ENABLE); if (result != ERROR_SUCCESS) { WINPAIR_DEBUG("Failed to enable HID service"); } } WINPAIR_DEBUG("Verifying successful connection ..."); if (is_connection_established(hRadio, &device_info)) { /* if we have a connection, stop trying to connect this device */ printf("Connection verified.\n"); connected = 1; break; } } } else { WINPAIR_DEBUG("Bluetooth device matching the given address is not a Move Motion Controller"); } } Sleep(SLEEP_BETWEEN_SCANS); } }
int windows_register_psmove(const char *move_addr_str, const HANDLE hRadio) { /* parse controller's Bluetooth device address string */ BLUETOOTH_ADDRESS *move_addr = string_to_btaddr(move_addr_str); if (!move_addr) { WINPAIR_DEBUG("Cannot parse controller address: '%s'", move_addr_str); return 1; } if (set_up_bluetooth_radio(hRadio) != 0) { WINPAIR_DEBUG("Failed to configure Bluetooth radio for use"); return 1; } /* Keep track of the number of times the loop iterates so we may timeout. */ int timeout_duration = 30; // seconds int sleep_interval = 1000; // msec int timeout_iterations = timeout_duration * 1000 / sleep_interval; int loop_count = 0; printf("\n" \ " Unplug the controller.\n" \ "\n" " Now press the controller's PS button. The red status LED\n" \ " will start blinking. Whenever it goes off, press the\n" \ " PS button again. Repeat this until the status LED finally\n" \ " remains lit. Press Ctrl+C to cancel anytime.\n"); for(;;) { BLUETOOTH_DEVICE_INFO device_info; if (get_bluetooth_device_info(hRadio, move_addr, &device_info) != 0) { WINPAIR_DEBUG("No Bluetooth device found matching the given address"); } else { if (is_move_motion_controller(&device_info)) { WINPAIR_DEBUG("Found Move Motion Controller matching the given address"); if (device_info.fConnected) { /* enable HID service only if necessary */ WINPAIR_DEBUG("Checking HID service ..."); if (!is_hid_service_enabled(hRadio, &device_info)) { WINPAIR_DEBUG("Enabling HID service ..."); GUID service = HumanInterfaceDeviceServiceClass_UUID; DWORD result = BluetoothSetServiceState(hRadio, &device_info, &service, BLUETOOTH_SERVICE_ENABLE); if (result != ERROR_SUCCESS) { WINPAIR_DEBUG("Failed to enable HID service"); } } WINPAIR_DEBUG("Verifying successful connection ..."); if (is_connection_established(hRadio, &device_info)) { /* if we have a connection, stop trying to connect this device */ printf("Connection verified.\n"); break; } } } else { WINPAIR_DEBUG("Bluetooth device matching the given address is not a Move Motion Controller"); } } if (loop_count >= timeout_iterations) { printf("\n" " A connection could not be established. This is not\n" " unusual in Windows. Please refer to the README document\n" " for your platform for more information. Press Ctrl+C to cancel."); } /* sleep for 1 second */ Sleep(1000); loop_count++; } free(move_addr); return 0; }
static void handle_windows8_and_later(const BLUETOOTH_ADDRESS *move_addr, const BLUETOOTH_ADDRESS *radio_addr, const HANDLE hRadio) { unsigned int scan = 0; int connected = 0; while (!connected) { BLUETOOTH_DEVICE_INFO device_info; if (get_bluetooth_device_info(hRadio, move_addr, &device_info, scan == 0) != 0) { WINPAIR_DEBUG("No Bluetooth device found matching the given address"); } else { if (is_move_motion_controller(&device_info)) { WINPAIR_DEBUG("Found Move Motion Controller matching the given address"); unsigned int conn_try; for (conn_try = 1; conn_try <= CONN_RETRIES; conn_try++) { WINPAIR_DEBUG("Connection try %d/%d", conn_try, CONN_RETRIES); if (update_device_info(hRadio, &device_info) != 0) { break; } if (device_info.fConnected) { /* Windows 8 (and later) seems to require manual help with setting up * the device in the registry. */ WINPAIR_DEBUG("Patching the registry ..."); if (patch_registry(move_addr, radio_addr) != 0) { WINPAIR_DEBUG("Failed to patch the registry"); } /* enable HID service only if necessary */ WINPAIR_DEBUG("Checking HID service ..."); if (!is_hid_service_enabled(hRadio, &device_info)) { WINPAIR_DEBUG("Enabling HID service ..."); GUID service = HumanInterfaceDeviceServiceClass_UUID; DWORD result = BluetoothSetServiceState(hRadio, &device_info, &service, BLUETOOTH_SERVICE_ENABLE); if (result != ERROR_SUCCESS) { WINPAIR_DEBUG("Failed to enable HID service"); } WINPAIR_DEBUG("Patching the registry ..."); if (patch_registry(move_addr, radio_addr) != 0) { WINPAIR_DEBUG("Failed to patch the registry"); } } WINPAIR_DEBUG("Verifying successful connection ..."); if (is_connection_established(hRadio, &device_info)) { /* if we have a connection, stop trying to connect this device */ printf("Connection verified.\n"); connected = 1; break; } } Sleep(CONN_DELAY); } if(!device_info.fConnected) { BluetoothRemoveDevice(&(device_info.Address)); WINPAIR_DEBUG("Device removed, starting all over again"); } } else { WINPAIR_DEBUG("Bluetooth device matching the given address is not a Move Motion Controller"); } } Sleep(SLEEP_BETWEEN_SCANS); scan = (scan + 1) % BT_SCAN_NEW_INQUIRY; } }