From 30ac619331fd43cbbc12150dc244a2457b733566 Mon Sep 17 00:00:00 2001 From: Kristian Sloth Lauszus Date: Sun, 30 Jun 2019 20:44:12 +0200 Subject: [PATCH] Initial code for Xbox One S controller support I lack a dongle that support simply paring, so it has been confirmed working yet --- BTD.cpp | 181 +++++++++++++++++++++++++++-- BTD.h | 13 +++ examples/Bluetooth/BTHID/BTHID.ino | 2 +- settings.h | 2 +- 4 files changed, 185 insertions(+), 13 deletions(-) diff --git a/BTD.cpp b/BTD.cpp index 536aa84e..f74cda7a 100644 --- a/BTD.cpp +++ b/BTD.cpp @@ -17,7 +17,7 @@ #include "BTD.h" // To enable serial debugging see "settings.h" -//#define EXTRADEBUG // Uncomment to get even more debugging data +#define EXTRADEBUG // Uncomment to get even more debugging data const uint8_t BTD::BTD_CONTROL_PIPE = 0; const uint8_t BTD::BTD_EVENT_PIPE = 1; @@ -408,7 +408,46 @@ void BTD::HCI_event_task() { hci_set_flag(HCI_FLAG_CMD_COMPLETE); // Set command complete flag if((hcibuf[3] == 0x01) && (hcibuf[4] == 0x10)) { // Parameters from read local version information hci_version = hcibuf[6]; // Used to check if it supports 2.0+EDR - see http://www.bluetooth.org/Technical/AssignedNumbers/hci.htm +#ifdef EXTRADEBUG + if(!hci_check_flag(HCI_FLAG_READ_VERSION)) { + Notify(PSTR("\r\nHCI version: "), 0x80); + D_PrintHex (hci_version, 0x80); + } +#endif hci_set_flag(HCI_FLAG_READ_VERSION); + } else if((hcibuf[3] == 0x04) && (hcibuf[4] == 0x10)) { // Parameters from read local extended features + if(!hci_check_flag(HCI_FLAG_LOCAL_EXTENDED_FEATURES)) { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nPage number: "), 0x80); + D_PrintHex (hcibuf[6], 0x80); + Notify(PSTR("\r\nMaximum page number: "), 0x80); + D_PrintHex (hcibuf[7], 0x80); + Notify(PSTR("\r\nExtended LMP features:"), 0x80); + for(uint8_t i = 0; i < 8; i++) { + Notify(PSTR(" "), 0x80); + D_PrintHex (hcibuf[8 + i], 0x80); + } +#endif +#ifdef DEBUG_USB_HOST + if(hcibuf[6] == 0) { // Page 0 + Notify(PSTR("\r\nLocal "), 0x80); + if(hcibuf[8 + 6] & (1U << 3)) + Notify(PSTR("supports"), 0x80); + else + Notify(PSTR("does NOT support"), 0x80); + Notify(PSTR(" secure simple paring (controller support)"), 0x80); + } else if(hcibuf[6] == 1) { // Page 1 + Notify(PSTR("\r\nLocal "), 0x80); + if(hcibuf[8 + 0] & (1U << 0)) + Notify(PSTR("supports"), 0x80); + else + Notify(PSTR("does NOT support"), 0x80); + Notify(PSTR(" secure simple paring (host support)"), 0x80); + } +#endif + } + + hci_set_flag(HCI_FLAG_LOCAL_EXTENDED_FEATURES); } else if((hcibuf[3] == 0x09) && (hcibuf[4] == 0x10)) { // Parameters from read local bluetooth address for(uint8_t i = 0; i < 6; i++) my_bdaddr[i] = hcibuf[6 + i]; @@ -446,7 +485,7 @@ void BTD::HCI_event_task() { case EV_INQUIRY_RESULT: if(hcibuf[2]) { // Check that there is more than zero responses -#ifdef EXTRADEBUG +#if defined(EXTRADEBUG) && 0 Notify(PSTR("\r\nNumber of responses: "), 0x80); Notify(hcibuf[2], 0x80); #endif @@ -456,7 +495,7 @@ void BTD::HCI_event_task() { for(uint8_t j = 0; j < 3; j++) classOfDevice[j] = hcibuf[j + 4 + offset]; -#ifdef EXTRADEBUG +#if defined(EXTRADEBUG) && 0 Notify(PSTR("\r\nClass of device: "), 0x80); D_PrintHex (classOfDevice[2], 0x80); Notify(PSTR(" "), 0x80); @@ -465,7 +504,7 @@ void BTD::HCI_event_task() { D_PrintHex (classOfDevice[0], 0x80); #endif - if(pairWithWii && classOfDevice[2] == 0x00 && (classOfDevice[1] & 0x05) && (classOfDevice[0] & 0x0C)) { // See http://wiibrew.org/wiki/Wiimote#SDP_information + if(pairWithWii && classOfDevice[2] == 0x00 && (classOfDevice[1] == 0x05) && (classOfDevice[0] & 0x0C)) { // See http://wiibrew.org/wiki/Wiimote#SDP_information checkRemoteName = true; // Check remote name to distinguish between the different controllers for(uint8_t j = 0; j < 6; j++) @@ -473,8 +512,10 @@ void BTD::HCI_event_task() { hci_set_flag(HCI_FLAG_DEVICE_FOUND); break; - } else if(pairWithHIDDevice && (classOfDevice[1] & 0x05) && (classOfDevice[0] & 0xC8)) { // Check if it is a mouse, keyboard or a gamepad - see: http://bluetooth-pentest.narod.ru/software/bluetooth_class_of_device-service_generator.html + } else if(pairWithHIDDevice && (classOfDevice[1] & 0x0F) == 0x05 && (classOfDevice[0] & 0xC8)) { // Check if it is a mouse, keyboard or a gamepad - see: http://bluetooth-pentest.narod.ru/software/bluetooth_class_of_device-service_generator.html #ifdef DEBUG_USB_HOST + checkRemoteName = true; // Used to print name in the serial monitor if serial debugging is enabled + if(classOfDevice[0] & 0x80) Notify(PSTR("\r\nMouse found"), 0x80); if(classOfDevice[0] & 0x40) @@ -524,7 +565,7 @@ void BTD::HCI_event_task() { if(remote_name[i] == '\0') // End of string break; } - // TODO: Altid sæt '\0' i remote name! + // TODO: Always set '\0' in remote name! hci_set_flag(HCI_FLAG_REMOTE_NAME_COMPLETE); } break; @@ -536,7 +577,7 @@ void BTD::HCI_event_task() { for(uint8_t i = 0; i < 3; i++) classOfDevice[i] = hcibuf[i + 8]; - if((classOfDevice[1] & 0x05) && (classOfDevice[0] & 0xC8)) { // Check if it is a mouse, keyboard or a gamepad + if((classOfDevice[1] & 0x0F) == 0x05 && (classOfDevice[0] & 0xC8)) { // Check if it is a mouse, keyboard or a gamepad #ifdef DEBUG_USB_HOST if(classOfDevice[0] & 0x80) Notify(PSTR("\r\nMouse is connecting"), 0x80); @@ -608,18 +649,84 @@ void BTD::HCI_event_task() { hci_state = HCI_DISCONNECT_STATE; } break; + + case EV_READ_REMOTE_EXTENDED_FEATURES_COMPLETE: + if(!hcibuf[2]) { // Check if connected OK + if(!hci_check_flag(HCI_FLAG_REMOTE_EXTENDED_FEATURES)) { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nPage number: "), 0x80); + D_PrintHex (hcibuf[5], 0x80); + Notify(PSTR("\r\nMaximum page number: "), 0x80); + D_PrintHex (hcibuf[6], 0x80); + Notify(PSTR("\r\nExtended LMP features:"), 0x80); + for(uint8_t i = 0; i < 8; i++) { + Notify(PSTR(" "), 0x80); + D_PrintHex (hcibuf[7 + i], 0x80); + } +#endif +#ifdef DEBUG_USB_HOST + if(hcibuf[5] == 0) { // Page 0 + Notify(PSTR("\r\nRemote "), 0x80); + if(hcibuf[7 + 6] & (1U << 3)) + Notify(PSTR("supports"), 0x80); + else + Notify(PSTR("does NOT support"), 0x80); + Notify(PSTR(" secure simple paring (controller support)"), 0x80); + } else if(hcibuf[5] == 1) { // Page 1 + Notify(PSTR("\r\nRemote "), 0x80); + if(hcibuf[7 + 0] & (1U << 0)) + Notify(PSTR("supports"), 0x80); + else + Notify(PSTR("\r\ndoes NOT support"), 0x80); + Notify(PSTR(" secure simple paring (host support)"), 0x80); + } +#endif + } + hci_set_flag(HCI_FLAG_REMOTE_EXTENDED_FEATURES); + } else { + hci_state = HCI_CHECK_DEVICE_SERVICE; +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nFailed to read remote extended featues: "), 0x80); + D_PrintHex (hcibuf[2], 0x80); +#endif + } + break; + + case EV_USER_CONFIRMATION_REQUEST: +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nUser confirmation Request: "), 0x80); +#endif +#ifdef EXTRADEBUG + Notify(PSTR("\r\nNumeric value: "), 0x80); + for(uint8_t i = 0; i < 4; i++) { + Notify(PSTR(" "), 0x80); + D_PrintHex (hcibuf[8 + i], 0x80); + } +#endif + + hci_user_confirmation_request_reply(); + hci_state = HCI_SCANNING_STATE; + break; + /* We will just ignore the following events */ + case EV_MAX_SLOTS_CHANGE: + break; case EV_NUM_COMPLETE_PKT: case EV_ROLE_CHANGED: case EV_PAGE_SCAN_REP_MODE: case EV_LOOPBACK_COMMAND: case EV_DATA_BUFFER_OVERFLOW: case EV_CHANGE_CONNECTION_LINK: - case EV_MAX_SLOTS_CHANGE: case EV_QOS_SETUP_COMPLETE: case EV_LINK_KEY_NOTIFICATION: case EV_ENCRYPTION_CHANGE: case EV_READ_REMOTE_VERSION_INFORMATION_COMPLETE: +#ifdef EXTRADEBUG + if(hcibuf[0] != 0x00) { + Notify(PSTR("\r\nIgnore HCI Event: "), 0x80); + D_PrintHex (hcibuf[0], 0x80); + } +#endif break; #ifdef EXTRADEBUG default: @@ -699,6 +806,14 @@ void BTD::HCI_task() { case HCI_LOCAL_VERSION_STATE: // The local version is used by the PS3BT class if(hci_check_flag(HCI_FLAG_READ_VERSION)) { + hci_read_local_extended_features(0); // "Requests the normal LMP features as returned by Read_Local_Supported_Features" + //hci_read_local_extended_features(1); // Read page 1 + hci_state = HCI_LOCAL_EXTENDED_FEATURES_STATE; + } + break; + + case HCI_LOCAL_EXTENDED_FEATURES_STATE: + if(hci_check_flag(HCI_FLAG_LOCAL_EXTENDED_FEATURES)) { if(btdName != NULL) { hci_set_local_name(btdName); hci_state = HCI_SET_NAME_STATE; @@ -783,8 +898,9 @@ void BTD::HCI_task() { else Notify(PSTR("\r\nConnected to HID device"), 0x80); #endif - hci_authentication_request(); // This will start the pairing with the Wiimote - hci_state = HCI_SCANNING_STATE; + hci_read_remote_extended_features(0); // "Requests the normal LMP features as returned by the HCI_Read_Remote_Supported_Features command" + //hci_read_remote_extended_features(1); // Read page 1 + hci_state = HCI_REMOTE_EXTENDED_FEATURES_STATE; } else { #ifdef DEBUG_USB_HOST Notify(PSTR("\r\nTrying to connect one more time..."), 0x80); @@ -794,6 +910,13 @@ void BTD::HCI_task() { } break; + case HCI_REMOTE_EXTENDED_FEATURES_STATE: + if(hci_check_flag(HCI_FLAG_REMOTE_EXTENDED_FEATURES)) { + hci_authentication_request(); + hci_state = HCI_SCANNING_STATE; + } + break; + case HCI_SCANNING_STATE: if(!connectToWii && !pairWithWii && !connectToHIDDevice && !pairWithHIDDevice) { #ifdef DEBUG_USB_HOST @@ -855,7 +978,7 @@ void BTD::HCI_task() { #endif incomingPS4 = true; } - if(pairWithWii && checkRemoteName) + if((pairWithWii || pairWithHIDDevice) && checkRemoteName) hci_state = HCI_CONNECT_DEVICE_STATE; else { hci_accept_connection(); @@ -999,6 +1122,16 @@ void BTD::hci_read_local_version_information() { HCI_Command(hcibuf, 3); } +void BTD::hci_read_local_extended_features(uint8_t page_number) { + hci_clear_flag(HCI_FLAG_LOCAL_EXTENDED_FEATURES); + hcibuf[0] = 0x04; // HCI OCF = 4 + hcibuf[1] = 0x04 << 2; // HCI OGF = 4 + hcibuf[2] = 0x01; // parameter length = 1 + hcibuf[3] = page_number; + + HCI_Command(hcibuf, 4); +} + void BTD::hci_accept_connection() { hci_clear_flag(HCI_FLAG_CONNECT_COMPLETE); hcibuf[0] = 0x09; // HCI OCF = 9 @@ -1034,6 +1167,18 @@ void BTD::hci_remote_name() { HCI_Command(hcibuf, 13); } +void BTD::hci_read_remote_extended_features(uint8_t page_number) { + hci_clear_flag(HCI_FLAG_REMOTE_EXTENDED_FEATURES); + hcibuf[0] = 0x1C; // HCI OCF = 1C + hcibuf[1] = 0x01 << 2; // HCI OGF = 1 + hcibuf[2] = 0x03; // parameter length = 3 + hcibuf[3] = (uint8_t)(hci_handle & 0xFF); //connection handle - low byte + hcibuf[4] = (uint8_t)((hci_handle >> 8) & 0x0F); //connection handle - high byte + hcibuf[5] = page_number; + + HCI_Command(hcibuf, 6); +} + void BTD::hci_set_local_name(const char* name) { hcibuf[0] = 0x13; // HCI OCF = 13 hcibuf[1] = 0x03 << 2; // HCI OGF = 3 @@ -1158,6 +1303,20 @@ void BTD::hci_link_key_request_negative_reply() { HCI_Command(hcibuf, 9); } +void BTD::hci_user_confirmation_request_reply() { + hcibuf[0] = 0x2C; // HCI OCF = 2C + hcibuf[1] = 0x01 << 2; // HCI OGF = 1 + hcibuf[2] = 0x06; // parameter length 6 + hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr + hcibuf[4] = disc_bdaddr[1]; + hcibuf[5] = disc_bdaddr[2]; + hcibuf[6] = disc_bdaddr[3]; + hcibuf[7] = disc_bdaddr[4]; + hcibuf[8] = disc_bdaddr[5]; + + HCI_Command(hcibuf, 9); +} + void BTD::hci_authentication_request() { hcibuf[0] = 0x11; // HCI OCF = 11 hcibuf[1] = 0x01 << 2; // HCI OGF = 1 diff --git a/BTD.h b/BTD.h index baf39364..e7ab9f96 100644 --- a/BTD.h +++ b/BTD.h @@ -59,6 +59,8 @@ #define HCI_DISABLE_SCAN_STATE 14 #define HCI_DONE_STATE 15 #define HCI_DISCONNECT_STATE 16 +#define HCI_LOCAL_EXTENDED_FEATURES_STATE 17 +#define HCI_REMOTE_EXTENDED_FEATURES_STATE 18 /* HCI event flags*/ #define HCI_FLAG_CMD_COMPLETE (1UL << 0) @@ -70,6 +72,8 @@ #define HCI_FLAG_READ_VERSION (1UL << 6) #define HCI_FLAG_DEVICE_FOUND (1UL << 7) #define HCI_FLAG_CONNECT_EVENT (1UL << 8) +#define HCI_FLAG_LOCAL_EXTENDED_FEATURES (1UL << 9) +#define HCI_FLAG_REMOTE_EXTENDED_FEATURES (1UL << 10) /* Macros for HCI event flag tests */ #define hci_check_flag(flag) (hci_event_flag & (flag)) @@ -99,6 +103,9 @@ #define EV_COMMAND_STATUS 0x0F #define EV_LOOPBACK_COMMAND 0x19 #define EV_PAGE_SCAN_REP_MODE 0x20 +#define EV_READ_REMOTE_EXTENDED_FEATURES_COMPLETE 0x23 +#define EV_USER_CONFIRMATION_REQUEST 0x33 + /* Bluetooth states for the different Bluetooth drivers */ #define L2CAP_WAIT 0 @@ -320,6 +327,8 @@ public: void hci_read_bdaddr(); /** Read the HCI Version of the Bluetooth dongle. */ void hci_read_local_version_information(); + + void hci_read_local_extended_features(uint8_t page_number); /** * Set the local name of the Bluetooth dongle. * @param name Desired name. @@ -331,6 +340,8 @@ public: void hci_write_scan_disable(); /** Read the remote devices name. */ void hci_remote_name(); + + void hci_read_remote_extended_features(uint8_t page_number); /** Accept the connection with the Bluetooth device. */ void hci_accept_connection(); /** @@ -351,6 +362,8 @@ public: * if the Host does not have a stored Link Key for the connection. */ void hci_link_key_request_negative_reply(); + + void hci_user_confirmation_request_reply(); /** Used to try to authenticate with the remote device. */ void hci_authentication_request(); /** Start a HCI inquiry. */ diff --git a/examples/Bluetooth/BTHID/BTHID.ino b/examples/Bluetooth/BTHID/BTHID.ino index 58a2f92f..3d848726 100644 --- a/examples/Bluetooth/BTHID/BTHID.ino +++ b/examples/Bluetooth/BTHID/BTHID.ino @@ -25,7 +25,7 @@ BTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so BTHID bthid(&Btd, PAIR, "0000"); // After that you can simply create the instance like so and then press any button on the device -//BTHID hid(&Btd); +//BTHID bthid(&Btd); KbdRptParser keyboardPrs; MouseRptParser mousePrs; diff --git a/settings.h b/settings.h index 2d176053..63be1770 100644 --- a/settings.h +++ b/settings.h @@ -39,7 +39,7 @@ e-mail : support@circuitsathome.com //////////////////////////////////////////////////////////////////////////////// /* Set this to 1 to activate serial debugging */ -#define ENABLE_UHS_DEBUGGING 0 +#define ENABLE_UHS_DEBUGGING 1 /* This can be used to select which serial port to use for debugging if * multiple serial ports are available.