diff --git a/.travis.yml b/.travis.yml index cc13e6c2..1b1f1378 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: python python: - - "2.7" + - "3.9" # Cache PlatformIO packages using Travis CI container-based infrastructure sudo: false @@ -31,7 +31,7 @@ env: - PLATFORMIO_CI_SRC=examples/Bluetooth/PS4BT - PLATFORMIO_CI_SRC=examples/Bluetooth/SPP - PLATFORMIO_CI_SRC=examples/Bluetooth/SPPMulti - - PLATFORMIO_CI_SRC=examples/Bluetooth/Wii + - PLATFORMIO_CI_SRC=examples/Bluetooth/Wii SKIP_UNO=true - PLATFORMIO_CI_SRC=examples/Bluetooth/WiiBalanceBoard SKIP_UNO=true - PLATFORMIO_CI_SRC=examples/Bluetooth/WiiIRCamera PLATFORMIO_BUILD_FLAGS="-DWIICAMERA" SKIP_UNO=true - PLATFORMIO_CI_SRC=examples/Bluetooth/WiiMulti SKIP_UNO=true @@ -62,13 +62,15 @@ env: - PLATFORMIO_CI_SRC=examples/PSBuzz # - PLATFORMIO_CI_SRC=examples/testusbhostFAT - PLATFORMIO_CI_SRC=examples/USB_desc - - PLATFORMIO_CI_SRC=examples/USBH_MIDI/bidirectional_converter + # See: https://travis-ci.org/github/felis/USB_Host_Shield_2.0/jobs/743787235 + - PLATFORMIO_CI_SRC=examples/USBH_MIDI/bidirectional_converter SKIP_TEENSY35=true SKIP_TEENSY36=true - PLATFORMIO_CI_SRC=examples/USBH_MIDI/eVY1_sample - PLATFORMIO_CI_SRC=examples/USBH_MIDI/USB_MIDI_converter - PLATFORMIO_CI_SRC=examples/USBH_MIDI/USB_MIDI_converter_multi - PLATFORMIO_CI_SRC=examples/USBH_MIDI/USBH_MIDI_dump - PLATFORMIO_CI_SRC=examples/Xbox/XBOXOLD - PLATFORMIO_CI_SRC=examples/Xbox/XBOXONE + - PLATFORMIO_CI_SRC=examples/Xbox/XBOXONESBT - PLATFORMIO_CI_SRC=examples/Xbox/XBOXRECV - PLATFORMIO_CI_SRC=examples/Xbox/XBOXUSB @@ -86,7 +88,9 @@ install: script: - if [[ -z "$SKIP_UNO" ]]; then UNO="--board=uno"; fi - - platformio ci --lib="." $UNO --board=genuino101 --board=teensy30 --board=teensy31 --board=teensy35 --board=teensy36 --board=teensylc --board=esp12e --board=nodemcu --board=esp32dev + - if [[ -z "$SKIP_TEENSY35" ]]; then TEENSY35="--board=teensy35"; fi + - if [[ -z "$SKIP_TEENSY36" ]]; then TEENSY36="--board=teensy36"; fi + - platformio ci --lib="." $UNO --board=genuino101 --board=teensy30 --board=teensy31 $TEENSY35 $TEENSY36 --board=teensylc --board=esp12e --board=nodemcu --board=esp32dev - platformio ci --lib="." --board=due --project-option="build_flags=-Wno-misleading-indentation" # Workaround https://travis-ci.org/felis/USB_Host_Shield_2.0/jobs/569237654 and https://github.com/arduino/ArduinoCore-sam/issues/69 before_deploy: diff --git a/BTD.cpp b/BTD.cpp index 536aa84e..32efeb86 100644 --- a/BTD.cpp +++ b/BTD.cpp @@ -29,11 +29,13 @@ connectToWii(false), pairWithWii(false), connectToHIDDevice(false), pairWithHIDDevice(false), +useSimplePairing(false), pUsb(p), // Pointer to USB class instance - mandatory bAddress(0), // Device address - mandatory bNumEP(1), // If config descriptor needs to be parsed qNextPollTime(0), // Reset NextPollTime pollInterval(0), +simple_pairing_supported(false), bPollEnable(false) // Don't start polling before dongle is connected { for(uint8_t i = 0; i < BTD_NUM_SERVICES; i++) @@ -321,6 +323,7 @@ void BTD::Initialize() { qNextPollTime = 0; // Reset next poll time pollInterval = 0; bPollEnable = false; // Don't start polling before dongle is connected + simple_pairing_supported = false; } /* Extracts interrupt-IN, bulk-IN, bulk-OUT endpoint information from config descriptor */ @@ -408,7 +411,57 @@ 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 + if(hcibuf[6] == 0) { // Page 0 +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nDongle "), 0x80); +#endif + if(hcibuf[8 + 6] & (1U << 3)) { + simple_pairing_supported = true; +#ifdef DEBUG_USB_HOST + Notify(PSTR("supports"), 0x80); +#endif + } else { + simple_pairing_supported = false; +#ifdef DEBUG_USB_HOST + Notify(PSTR("does NOT support"), 0x80); +#endif + } +#ifdef DEBUG_USB_HOST + Notify(PSTR(" secure simple pairing (controller support)"), 0x80); +#endif + } else if(hcibuf[6] == 1) { // Page 1 +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nDongle "), 0x80); + if(hcibuf[8 + 0] & (1U << 0)) + Notify(PSTR("supports"), 0x80); + else + Notify(PSTR("does NOT support"), 0x80); + Notify(PSTR(" secure simple pairing (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]; @@ -422,6 +475,12 @@ void BTD::HCI_event_task() { #ifdef DEBUG_USB_HOST Notify(PSTR("\r\nHCI Command Failed: "), 0x80); D_PrintHex (hcibuf[2], 0x80); + Notify(PSTR("\r\nNum HCI Command Packets: "), 0x80); + D_PrintHex (hcibuf[3], 0x80); + Notify(PSTR("\r\nCommand Opcode: "), 0x80); + D_PrintHex (hcibuf[4], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (hcibuf[5], 0x80); #endif } break; @@ -465,7 +524,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 +532,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) @@ -482,7 +543,6 @@ void BTD::HCI_event_task() { if(classOfDevice[0] & 0x08) Notify(PSTR("\r\nGamepad found"), 0x80); #endif - for(uint8_t j = 0; j < 6; j++) disc_bdaddr[j] = hcibuf[j + 3 + 6 * i]; @@ -524,7 +584,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 +596,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); @@ -598,6 +658,10 @@ void BTD::HCI_event_task() { Notify(PSTR("\r\nPairing successful with HID device"), 0x80); #endif connectToHIDDevice = true; // Used to indicate to the BTHID service, that it should connect to this device + } else { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nPairing was successful"), 0x80); +#endif } } else { #ifdef DEBUG_USB_HOST @@ -608,24 +672,82 @@ void BTD::HCI_event_task() { hci_state = HCI_DISCONNECT_STATE; } break; + + case EV_IO_CAPABILITY_REQUEST: +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nReceived IO Capability Request"), 0x80); +#endif + hci_io_capability_request_reply(); + break; + + case EV_IO_CAPABILITY_RESPONSE: +#ifdef EXTRADEBUG + Notify(PSTR("\r\nReceived IO Capability Response: "), 0x80); + Notify(PSTR("\r\nIO capability: "), 0x80); + D_PrintHex (hcibuf[8], 0x80); + Notify(PSTR("\r\nOOB data present: "), 0x80); + D_PrintHex (hcibuf[9], 0x80); + Notify(PSTR("\r\nAuthentication request: "), 0x80); + D_PrintHex (hcibuf[10], 0x80); +#endif + break; + + case EV_USER_CONFIRMATION_REQUEST: +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nUser confirmation Request"), 0x80); +#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 +#endif + // Simply confirm the connection, as the host has no "NoInputNoOutput" capabilities + hci_user_confirmation_request_reply(); + break; + + case EV_SIMPLE_PAIRING_COMPLETE: +#ifdef EXTRADEBUG + if(!hcibuf[2]) { // Check if connected OK + Notify(PSTR("\r\nSimple Pairing succeeded"), 0x80); + } else { + Notify(PSTR("\r\nSimple Pairing failed: "), 0x80); + D_PrintHex (hcibuf[2], 0x80); + } +#endif + break; + /* We will just ignore the following events */ + case EV_MAX_SLOTS_CHANGE: case EV_NUM_COMPLETE_PKT: + break; 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: if(hcibuf[0] != 0x00) { Notify(PSTR("\r\nUnmanaged HCI Event: "), 0x80); D_PrintHex (hcibuf[0], 0x80); + Notify(PSTR(", data: "), 0x80); + for(uint16_t i = 0; i < hcibuf[1]; i++) { + D_PrintHex (hcibuf[2 + i], 0x80); + Notify(PSTR(" "), 0x80); + } } break; #endif @@ -700,18 +822,56 @@ 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)) { if(btdName != NULL) { - hci_set_local_name(btdName); - hci_state = HCI_SET_NAME_STATE; + hci_write_local_name(btdName); + hci_state = HCI_WRITE_NAME_STATE; + } else if(useSimplePairing) { + 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; } else hci_state = HCI_CHECK_DEVICE_SERVICE; } break; - case HCI_SET_NAME_STATE: + case HCI_WRITE_NAME_STATE: if(hci_check_flag(HCI_FLAG_CMD_COMPLETE)) { #ifdef DEBUG_USB_HOST - Notify(PSTR("\r\nThe name is set to: "), 0x80); + Notify(PSTR("\r\nThe name was set to: "), 0x80); NotifyStr(btdName, 0x80); +#endif + if(useSimplePairing) { + 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; + } else + hci_state = HCI_CHECK_DEVICE_SERVICE; + } + break; + + case HCI_LOCAL_EXTENDED_FEATURES_STATE: + if(hci_check_flag(HCI_FLAG_LOCAL_EXTENDED_FEATURES)) { + if(simple_pairing_supported) { + hci_write_simple_pairing_mode(true); + hci_state = HCI_WRITE_SIMPLE_PAIRING_STATE; + } else + hci_state = HCI_CHECK_DEVICE_SERVICE; + } + break; + + case HCI_WRITE_SIMPLE_PAIRING_STATE: + if(hci_check_flag(HCI_FLAG_CMD_COMPLETE)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nSimple pairing was enabled"), 0x80); +#endif + hci_set_event_mask(); + hci_state = HCI_SET_EVENT_MASK_STATE; + } + break; + + case HCI_SET_EVENT_MASK_STATE: + if(hci_check_flag(HCI_FLAG_CMD_COMPLETE)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nSet event mask completed"), 0x80); #endif hci_state = HCI_CHECK_DEVICE_SERVICE; } @@ -783,7 +943,7 @@ 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_authentication_request(); // This will start the pairing with the device hci_state = HCI_SCANNING_STATE; } else { #ifdef DEBUG_USB_HOST @@ -855,7 +1015,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 +1159,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,7 +1204,7 @@ void BTD::hci_remote_name() { HCI_Command(hcibuf, 13); } -void BTD::hci_set_local_name(const char* name) { +void BTD::hci_write_local_name(const char* name) { hcibuf[0] = 0x13; // HCI OCF = 13 hcibuf[1] = 0x03 << 2; // HCI OGF = 3 hcibuf[2] = strlen(name) + 1; // parameter length = the length of the string + end byte @@ -1046,6 +1216,33 @@ void BTD::hci_set_local_name(const char* name) { HCI_Command(hcibuf, 4 + strlen(name)); } +void BTD::hci_set_event_mask() { + hcibuf[0] = 0x01; // HCI OCF = 01 + hcibuf[1] = 0x03 << 2; // HCI OGF = 3 + hcibuf[2] = 0x08; + // The first 6 bytes are the default of 1FFF FFFF FFFF + // However we need to set bits 48-55 for simple pairing to work + hcibuf[3] = 0xFF; + hcibuf[4] = 0xFF; + hcibuf[5] = 0xFF; + hcibuf[6] = 0xFF; + hcibuf[7] = 0xFF; + hcibuf[8] = 0x1F; + hcibuf[9] = 0xFF; // Enable bits 48-55 used for simple pairing + hcibuf[10] = 0x00; + + HCI_Command(hcibuf, 11); +} + +void BTD::hci_write_simple_pairing_mode(bool enable) { + hcibuf[0] = 0x56; // HCI OCF = 56 + hcibuf[1] = 0x03 << 2; // HCI OGF = 3 + hcibuf[2] = 1; // parameter length = 1 + hcibuf[3] = enable ? 1 : 0; + + HCI_Command(hcibuf, 4); +} + void BTD::hci_inquiry() { hci_clear_flag(HCI_FLAG_DEVICE_FOUND); hcibuf[0] = 0x01; @@ -1074,7 +1271,7 @@ void BTD::hci_connect() { void BTD::hci_connect(uint8_t *bdaddr) { hci_clear_flag(HCI_FLAG_CONNECT_COMPLETE | HCI_FLAG_CONNECT_EVENT); - hcibuf[0] = 0x05; + hcibuf[0] = 0x05; // HCI OCF = 5 hcibuf[1] = 0x01 << 2; // HCI OGF = 1 hcibuf[2] = 0x0D; // parameter Total Length = 13 hcibuf[3] = bdaddr[0]; // 6 octet bdaddr (LSB) @@ -1158,6 +1355,37 @@ void BTD::hci_link_key_request_negative_reply() { HCI_Command(hcibuf, 9); } +void BTD::hci_io_capability_request_reply() { + hcibuf[0] = 0x2B; // HCI OCF = 2B + hcibuf[1] = 0x01 << 2; // HCI OGF = 1 + hcibuf[2] = 0x09; + 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]; + hcibuf[9] = 0x03; // NoInputNoOutput + hcibuf[10] = 0x00; // OOB authentication data not present + hcibuf[11] = 0x00; // MITM Protection Not Required – No Bonding. Numeric comparison with automatic accept allowed + + HCI_Command(hcibuf, 12); +} + +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..5337dea9 100644 --- a/BTD.h +++ b/BTD.h @@ -45,7 +45,7 @@ #define HCI_CLASS_STATE 2 #define HCI_BDADDR_STATE 3 #define HCI_LOCAL_VERSION_STATE 4 -#define HCI_SET_NAME_STATE 5 +#define HCI_WRITE_NAME_STATE 5 #define HCI_CHECK_DEVICE_SERVICE 6 #define HCI_INQUIRY_STATE 7 // These three states are only used if it should pair and connect to a device @@ -59,6 +59,9 @@ #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_WRITE_SIMPLE_PAIRING_STATE 18 +#define HCI_SET_EVENT_MASK_STATE 19 /* HCI event flags*/ #define HCI_FLAG_CMD_COMPLETE (1UL << 0) @@ -70,6 +73,7 @@ #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) /* Macros for HCI event flag tests */ #define hci_check_flag(flag) (hci_event_flag & (flag)) @@ -86,6 +90,10 @@ #define EV_REMOTE_NAME_COMPLETE 0x07 #define EV_ENCRYPTION_CHANGE 0x08 #define EV_CHANGE_CONNECTION_LINK 0x09 +#define EV_READ_REMOTE_VERSION_INFORMATION_COMPLETE 0x0C +#define EV_QOS_SETUP_COMPLETE 0x0D +#define EV_COMMAND_COMPLETE 0x0E +#define EV_COMMAND_STATUS 0x0F #define EV_ROLE_CHANGED 0x12 #define EV_NUM_COMPLETE_PKT 0x13 #define EV_PIN_CODE_REQUEST 0x16 @@ -93,12 +101,13 @@ #define EV_LINK_KEY_NOTIFICATION 0x18 #define EV_DATA_BUFFER_OVERFLOW 0x1A #define EV_MAX_SLOTS_CHANGE 0x1B -#define EV_READ_REMOTE_VERSION_INFORMATION_COMPLETE 0x0C -#define EV_QOS_SETUP_COMPLETE 0x0D -#define EV_COMMAND_COMPLETE 0x0E -#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_IO_CAPABILITY_REQUEST 0x31 +#define EV_IO_CAPABILITY_RESPONSE 0x32 +#define EV_USER_CONFIRMATION_REQUEST 0x33 +#define EV_SIMPLE_PAIRING_COMPLETE 0x36 /* Bluetooth states for the different Bluetooth drivers */ #define L2CAP_WAIT 0 @@ -183,6 +192,17 @@ #define HID_CTRL_PSM 0x11 // HID_Control PSM Value #define HID_INTR_PSM 0x13 // HID_Interrupt PSM Value +/* Used for SDP */ +#define SDP_SERVICE_SEARCH_REQUEST 0x02 +#define SDP_SERVICE_SEARCH_RESPONSE 0x03 +#define SDP_SERVICE_ATTRIBUTE_REQUEST 0x04 +#define SDP_SERVICE_ATTRIBUTE_RESPONSE 0x05 +#define SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST 0x06 // See the RFCOMM specs +#define SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE 0x07 // See the RFCOMM specs +#define PNP_INFORMATION_UUID 0x1200 +#define SERIALPORT_UUID 0x1101 // See http://www.bluetooth.org/Technical/AssignedNumbers/service_discovery.htm +#define L2CAP_UUID 0x0100 + // Used to determine if it is a Bluetooth dongle #define WI_SUBCLASS_RF 0x01 // RF Controller #define WI_PROTOCOL_BT 0x01 // Bluetooth Programming Interface @@ -320,11 +340,17 @@ public: void hci_read_bdaddr(); /** Read the HCI Version of the Bluetooth dongle. */ void hci_read_local_version_information(); + /** Used to check if the dongle supports simple paring */ + void hci_read_local_extended_features(uint8_t page_number); /** * Set the local name of the Bluetooth dongle. * @param name Desired name. */ - void hci_set_local_name(const char* name); + void hci_write_local_name(const char* name); + /** Used to enable simply paring if the dongle supports it */ + void hci_write_simple_pairing_mode(bool enable); + /** Used to enable events related to simple paring */ + void hci_set_event_mask(); /** Enable visibility to other Bluetooth devices. */ void hci_write_scan_enable(); /** Disable visibility to other Bluetooth devices. */ @@ -351,6 +377,8 @@ public: * if the Host does not have a stored Link Key for the connection. */ void hci_link_key_request_negative_reply(); + /** Used to during simple paring to confirm that the we want to connect */ + void hci_user_confirmation_request_reply(); /** Used to try to authenticate with the remote device. */ void hci_authentication_request(); /** Start a HCI inquiry. */ @@ -359,6 +387,8 @@ public: void hci_inquiry_cancel(); /** Connect to last device communicated with. */ void hci_connect(); + /** Used during simple paring to reply to a IO capability request */ + void hci_io_capability_request_reply(); /** * Connect to device. * @param bdaddr Bluetooth address of the device. @@ -500,6 +530,9 @@ public: return pollInterval; }; + /** Used by the drivers to enable simple pairing */ + bool useSimplePairing; + protected: /** Pointer to USB class instance. */ USB *pUsb; @@ -537,6 +570,7 @@ private: uint16_t PID, VID; // PID and VID of device connected uint8_t pollInterval; + bool simple_pairing_supported; bool bPollEnable; bool pairWiiUsingSync; // True if pairing was done using the Wii SYNC button. diff --git a/BTHID.cpp b/BTHID.cpp index d32e9aa6..dc5ab207 100644 --- a/BTHID.cpp +++ b/BTHID.cpp @@ -30,6 +30,8 @@ protocolMode(USB_HID_BOOT_PROTOCOL) { pBtd->btdPin = pin; /* Set device cid for the control and intterrupt channelse - LSB */ + sdp_dcid[0] = 0x50; // 0x0050 + sdp_dcid[1] = 0x00; control_dcid[0] = 0x70; // 0x0070 control_dcid[1] = 0x00; interrupt_dcid[0] = 0x71; // 0x0071 @@ -41,19 +43,34 @@ protocolMode(USB_HID_BOOT_PROTOCOL) { void BTHID::Reset() { connected = false; activeConnection = false; + SDPConnected = false; l2cap_event_flag = 0; // Reset flags + l2cap_sdp_state = L2CAP_SDP_WAIT; l2cap_state = L2CAP_WAIT; ResetBTHID(); } void BTHID::disconnect() { // Use this void to disconnect the device + if(SDPConnected) + pBtd->l2cap_disconnection_request(hci_handle, ++identifier, sdp_scid, sdp_dcid); // First the HID interrupt channel has to be disconnected, then the HID control channel and finally the HCI connection pBtd->l2cap_disconnection_request(hci_handle, ++identifier, interrupt_scid, interrupt_dcid); Reset(); + l2cap_sdp_state = L2CAP_DISCONNECT_RESPONSE; l2cap_state = L2CAP_INTERRUPT_DISCONNECT; } void BTHID::ACLData(uint8_t* l2capinbuf) { + if(!connected) { + if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) { + if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == SDP_PSM && !pBtd->sdpConnectionClaimed) { + pBtd->sdpConnectionClaimed = true; + hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection + l2cap_sdp_state = L2CAP_SDP_WAIT; // Reset state + } + } + } + if(!pBtd->l2capConnectionClaimed && pBtd->incomingHIDDevice && !connected && !activeConnection) { if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) { if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) { @@ -85,14 +102,30 @@ void BTHID::ACLData(uint8_t* l2capinbuf) { #endif } else if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_RESPONSE) { if(((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) && ((l2capinbuf[18] | (l2capinbuf[19] << 8)) == SUCCESSFUL)) { // Success - if(l2capinbuf[14] == control_dcid[0] && l2capinbuf[15] == control_dcid[1]) { - //Notify(PSTR("\r\nHID Control Connection Complete"), 0x80); + if(l2capinbuf[14] == sdp_dcid[0] && l2capinbuf[15] == sdp_dcid[1]) { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nSDP Connection Complete"), 0x80); +#endif + identifier = l2capinbuf[9]; + sdp_scid[0] = l2capinbuf[12]; + sdp_scid[1] = l2capinbuf[13]; +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nSend SDP Config Request"), 0x80); +#endif + identifier++; + pBtd->l2cap_config_request(hci_handle, identifier, sdp_scid); + } else if(l2capinbuf[14] == control_dcid[0] && l2capinbuf[15] == control_dcid[1]) { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nHID Control Connection Complete"), 0x80); +#endif identifier = l2capinbuf[9]; control_scid[0] = l2capinbuf[12]; control_scid[1] = l2capinbuf[13]; l2cap_set_flag(L2CAP_FLAG_CONTROL_CONNECTED); } else if(l2capinbuf[14] == interrupt_dcid[0] && l2capinbuf[15] == interrupt_dcid[1]) { - //Notify(PSTR("\r\nHID Interrupt Connection Complete"), 0x80); +#ifdef EXTRADEBUG + Notify(PSTR("\r\nHID Interrupt Connection Complete"), 0x80); +#endif identifier = l2capinbuf[9]; interrupt_scid[0] = l2capinbuf[12]; interrupt_scid[1] = l2capinbuf[13]; @@ -112,7 +145,12 @@ void BTHID::ACLData(uint8_t* l2capinbuf) { Notify(PSTR(" Identifier: "), 0x80); D_PrintHex (l2capinbuf[9], 0x80); #endif - if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) { + if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == SDP_PSM) { + identifier = l2capinbuf[9]; + sdp_scid[0] = l2capinbuf[14]; + sdp_scid[1] = l2capinbuf[15]; + l2cap_set_flag(L2CAP_FLAG_CONNECTION_SDP_REQUEST); + } else if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) { identifier = l2capinbuf[9]; control_scid[0] = l2capinbuf[14]; control_scid[1] = l2capinbuf[15]; @@ -125,26 +163,51 @@ void BTHID::ACLData(uint8_t* l2capinbuf) { } } else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_RESPONSE) { if((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) { // Success - if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) { - //Notify(PSTR("\r\nHID Control Configuration Complete"), 0x80); + if(l2capinbuf[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nSDP Configuration Complete"), 0x80); +#endif + identifier = l2capinbuf[9]; + l2cap_set_flag(L2CAP_FLAG_CONFIG_SDP_SUCCESS); + } else if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nHID Control Configuration Complete"), 0x80); +#endif identifier = l2capinbuf[9]; l2cap_set_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS); } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) { - //Notify(PSTR("\r\nHID Interrupt Configuration Complete"), 0x80); +#ifdef EXTRADEBUG + Notify(PSTR("\r\nHID Interrupt Configuration Complete"), 0x80); +#endif identifier = l2capinbuf[9]; l2cap_set_flag(L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS); } } } else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_REQUEST) { - if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) { - //Notify(PSTR("\r\nHID Control Configuration Request"), 0x80); + if(l2capinbuf[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nSDP Configuration Request"), 0x80); +#endif + pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], sdp_scid); + } else if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nHID Control Configuration Request"), 0x80); +#endif pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], control_scid); } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) { - //Notify(PSTR("\r\nHID Interrupt Configuration Request"), 0x80); +#ifdef EXTRADEBUG + Notify(PSTR("\r\nHID Interrupt Configuration Request"), 0x80); +#endif pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], interrupt_scid); } } else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_REQUEST) { - if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) { + if(l2capinbuf[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nDisconnect Request: SDP Channel"), 0x80); +#endif + identifier = l2capinbuf[9]; + l2cap_set_flag(L2CAP_FLAG_DISCONNECT_SDP_REQUEST); + } else if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) { #ifdef DEBUG_USB_HOST Notify(PSTR("\r\nDisconnect Request: Control Channel"), 0x80); #endif @@ -160,15 +223,31 @@ void BTHID::ACLData(uint8_t* l2capinbuf) { Reset(); } } else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_RESPONSE) { - if(l2capinbuf[12] == control_scid[0] && l2capinbuf[13] == control_scid[1]) { - //Notify(PSTR("\r\nDisconnect Response: Control Channel"), 0x80); + if(l2capinbuf[12] == sdp_scid[0] && l2capinbuf[13] == sdp_scid[1]) { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nDisconnect Response: SDP Channel"), 0x80); +#endif + identifier = l2capinbuf[9]; + l2cap_set_flag(L2CAP_FLAG_DISCONNECT_RESPONSE); + } else if(l2capinbuf[12] == control_scid[0] && l2capinbuf[13] == control_scid[1]) { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nDisconnect Response: Control Channel"), 0x80); +#endif identifier = l2capinbuf[9]; l2cap_set_flag(L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE); } else if(l2capinbuf[12] == interrupt_scid[0] && l2capinbuf[13] == interrupt_scid[1]) { - //Notify(PSTR("\r\nDisconnect Response: Interrupt Channel"), 0x80); +#ifdef EXTRADEBUG + Notify(PSTR("\r\nDisconnect Response: Interrupt Channel"), 0x80); +#endif identifier = l2capinbuf[9]; l2cap_set_flag(L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE); } + } else if(l2capinbuf[8] == L2CAP_CMD_INFORMATION_REQUEST) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nInformation request"), 0x80); +#endif + identifier = l2capinbuf[9]; + pBtd->l2cap_information_response(hci_handle, identifier, l2capinbuf[12], l2capinbuf[13]); } #ifdef EXTRADEBUG else { @@ -176,6 +255,78 @@ void BTHID::ACLData(uint8_t* l2capinbuf) { Notify(PSTR("\r\nL2CAP Unknown Signaling Command: "), 0x80); D_PrintHex (l2capinbuf[8], 0x80); } +#endif + } else if(l2capinbuf[6] == sdp_dcid[0] && l2capinbuf[7] == sdp_dcid[1]) { // SDP + if(l2capinbuf[8] == SDP_SERVICE_SEARCH_REQUEST) { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nSDP_SERVICE_SEARCH_REQUEST"), 0x80); +#endif + // Send response + l2capoutbuf[0] = SDP_SERVICE_SEARCH_RESPONSE; + l2capoutbuf[1] = l2capinbuf[9];//transactionIDHigh; + l2capoutbuf[2] = l2capinbuf[10];//transactionIDLow; + + l2capoutbuf[3] = 0x00; // MSB Parameter Length + l2capoutbuf[4] = 0x05; // LSB Parameter Length = 5 + + l2capoutbuf[5] = 0x00; // MSB TotalServiceRecordCount + l2capoutbuf[6] = 0x00; // LSB TotalServiceRecordCount = 0 + + l2capoutbuf[7] = 0x00; // MSB CurrentServiceRecordCount + l2capoutbuf[8] = 0x00; // LSB CurrentServiceRecordCount = 0 + + l2capoutbuf[9] = 0x00; // No continuation state + + SDP_Command(l2capoutbuf, 10); + } else if(l2capinbuf[8] == SDP_SERVICE_ATTRIBUTE_REQUEST) { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nSDP_SERVICE_ATTRIBUTE_REQUEST"), 0x80); +#endif + // Send response + l2capoutbuf[0] = SDP_SERVICE_ATTRIBUTE_RESPONSE; + l2capoutbuf[1] = l2capinbuf[9];//transactionIDHigh; + l2capoutbuf[2] = l2capinbuf[10];//transactionIDLow; + + l2capoutbuf[3] = 0x00; // MSB Parameter Length + l2capoutbuf[4] = 0x05; // LSB Parameter Length = 5 + + l2capoutbuf[5] = 0x00; // MSB AttributeListByteCount + l2capoutbuf[6] = 0x02; // LSB AttributeListByteCount = 2 + + // TODO: What to send? + l2capoutbuf[7] = 0x35; // Data element sequence - length in next byte + l2capoutbuf[8] = 0x00; // Length = 0 + + l2capoutbuf[9] = 0x00; // No continuation state + + SDP_Command(l2capoutbuf, 10); + } else if(l2capinbuf[8] == SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST) { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nSDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST"), 0x80); + Notify(PSTR("\r\nUUID: "), 0x80); + uint16_t uuid; + if((l2capinbuf[16] << 8 | l2capinbuf[17]) == 0x0000) // Check if it's sending the UUID as a 128-bit UUID + uuid = (l2capinbuf[18] << 8 | l2capinbuf[19]); + else // Short UUID + uuid = (l2capinbuf[16] << 8 | l2capinbuf[17]); + D_PrintHex (uuid, 0x80); + + Notify(PSTR("\r\nLength: "), 0x80); + uint16_t length = l2capinbuf[11] << 8 | l2capinbuf[12]; + D_PrintHex (length, 0x80); + Notify(PSTR("\r\nData: "), 0x80); + for(uint8_t i = 0; i < length; i++) { + D_PrintHex (l2capinbuf[13 + i], 0x80); + Notify(PSTR(" "), 0x80); + } +#endif + serviceNotSupported(l2capinbuf[9], l2capinbuf[10]); // The service is not supported + } +#ifdef EXTRADEBUG + else { + Notify(PSTR("\r\nUnknown PDU: "), 0x80); + D_PrintHex (l2capinbuf[8], 0x80); + } #endif } else if(l2capinbuf[6] == interrupt_dcid[0] && l2capinbuf[7] == interrupt_dcid[1]) { // l2cap_interrupt #ifdef PRINTREPORT @@ -231,10 +382,59 @@ void BTHID::ACLData(uint8_t* l2capinbuf) { } } #endif + SDP_task(); L2CAP_task(); } } +void BTHID::SDP_task() { + switch(l2cap_sdp_state) { + case L2CAP_SDP_WAIT: + if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_SDP_REQUEST)) { + l2cap_clear_flag(L2CAP_FLAG_CONNECTION_SDP_REQUEST); // Clear flag +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nSDP Incoming Connection Request"), 0x80); +#endif + pBtd->l2cap_connection_response(hci_handle, identifier, sdp_dcid, sdp_scid, PENDING); + delay(1); + pBtd->l2cap_connection_response(hci_handle, identifier, sdp_dcid, sdp_scid, SUCCESSFUL); + identifier++; + delay(1); + pBtd->l2cap_config_request(hci_handle, identifier, sdp_scid); + l2cap_sdp_state = L2CAP_SDP_SUCCESS; + } else if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_SDP_REQUEST)) { + l2cap_clear_flag(L2CAP_FLAG_DISCONNECT_SDP_REQUEST); // Clear flag + SDPConnected = false; +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nDisconnected SDP Channel"), 0x80); +#endif + pBtd->l2cap_disconnection_response(hci_handle, identifier, sdp_dcid, sdp_scid); + } + break; + case L2CAP_SDP_SUCCESS: + if(l2cap_check_flag(L2CAP_FLAG_CONFIG_SDP_SUCCESS)) { + l2cap_clear_flag(L2CAP_FLAG_CONFIG_SDP_SUCCESS); // Clear flag +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nSDP Successfully Configured"), 0x80); +#endif + SDPConnected = true; + l2cap_sdp_state = L2CAP_SDP_WAIT; + } + break; + + case L2CAP_DISCONNECT_RESPONSE: // This is for both disconnection response from the RFCOMM and SDP channel if they were connected + if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_RESPONSE)) { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nDisconnected L2CAP Connection"), 0x80); +#endif + pBtd->hci_disconnect(hci_handle); + hci_handle = -1; // Reset handle + Reset(); + } + break; + } +} + void BTHID::L2CAP_task() { switch(l2cap_state) { /* These states are used if the HID device is the host */ @@ -371,6 +571,27 @@ void BTHID::Run() { } } +void BTHID::SDP_Command(uint8_t* data, uint8_t nbytes) { // See page 223 in the Bluetooth specs + pBtd->L2CAP_Command(hci_handle, data, nbytes, sdp_scid[0], sdp_scid[1]); +} + +void BTHID::serviceNotSupported(uint8_t transactionIDHigh, uint8_t transactionIDLow) { // See page 235 in the Bluetooth specs + l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE; + l2capoutbuf[1] = transactionIDHigh; + l2capoutbuf[2] = transactionIDLow; + l2capoutbuf[3] = 0x00; // MSB Parameter Length + l2capoutbuf[4] = 0x05; // LSB Parameter Length = 5 + l2capoutbuf[5] = 0x00; // MSB AttributeListsByteCount + l2capoutbuf[6] = 0x02; // LSB AttributeListsByteCount = 2 + + /* Attribute ID/Value Sequence: */ + l2capoutbuf[7] = 0x35; // Data element sequence - length in next byte + l2capoutbuf[8] = 0x00; // Length = 0 + l2capoutbuf[9] = 0x00; // No continuation state + + SDP_Command(l2capoutbuf, 10); +} + /************************************************************/ /* HID Commands */ diff --git a/BTHID.h b/BTHID.h index 7d1d1116..b15649b5 100644 --- a/BTHID.h +++ b/BTHID.h @@ -141,20 +141,30 @@ protected: /** L2CAP source CID for HID_Interrupt */ uint8_t interrupt_scid[2]; + uint8_t l2cap_sdp_state; + uint8_t sdp_scid[2]; // L2CAP source CID for SDP + private: HIDReportParser *pRptParser[NUM_PARSERS]; // Pointer to HIDReportParsers. + uint8_t l2capoutbuf[BULK_MAXPKTSIZE]; // General purpose buffer for l2cap out data + void SDP_Command(uint8_t* data, uint8_t nbytes); + void serviceNotSupported(uint8_t transactionIDHigh, uint8_t transactionIDLow); + /** Set report protocol. */ void setProtocol(); uint8_t protocolMode; + void SDP_task(); void L2CAP_task(); // L2CAP state machine bool activeConnection; // Used to indicate if it already has established a connection + bool SDPConnected; /* Variables used for L2CAP communication */ uint8_t control_dcid[2]; // L2CAP device CID for HID_Control - Always 0x0070 uint8_t interrupt_dcid[2]; // L2CAP device CID for HID_Interrupt - Always 0x0071 + uint8_t sdp_dcid[2]; uint8_t l2cap_state; }; #endif diff --git a/README.md b/README.md index f7d7c5b8..cac71f6e 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,10 @@ For more information about the hardware see the [Hardware Manual](https://chome. # Developed By -* __Oleg Mazurov - -* __Alexei Glushchenko, Circuits@Home__ - +* __Oleg Mazurov__ - +* __Alexei Glushchenko__ - * Developers of the USB Core, HID, FTDI, ADK, ACM, and PL2303 libraries -* __Kristian Lauszus, TKJ Electronics__ - +* __Kristian Sloth Lauszus__ - * Developer of the [BTD](#bluetooth-libraries), [BTHID](#bthid-library), [SPP](#spp-library), [PS4](#ps4-library), [PS3](#ps3-library), [Wii](#wii-library), [Xbox](#xbox-library), and [PSBuzz](#ps-buzz-library) libraries * __Andrew Kroll__ - * Major contributor to mass storage code @@ -50,6 +50,7 @@ For more information about the hardware see the [Hardware Manual](https://chome. * [Xbox library](#xbox-library) * [Xbox 360 Library](#xbox-360-library) * [Xbox ONE Library](#xbox-one-library) + * [Xbox ONE S Library](#xbox-one-s-library) * [Wii library](#wii-library) * [PS Buzz Library](#ps-buzz-library) * [HID Libraries](#hid-libraries) @@ -257,12 +258,18 @@ All the information regarding the Xbox 360 controller protocol are form these si #### Xbox ONE Library -An Xbox ONE controller is supported via USB in the [XBOXONE](XBOXONE.cpp) class. It is heavily based on the 360 library above. In addition to cross referencing the above, information on the protocol was found at: +A Xbox ONE controller is supported via USB in the [XBOXONE](XBOXONE.cpp) class. It is heavily based on the 360 library above. In addition to cross referencing the above, information on the protocol was found at: * * * +#### Xbox ONE S Library + +A Xbox ONE controller is supported via Bluetooth in the [XBOXONESBT](XBOXONESBT.cpp) class. + +Special thanks to [HisashiKato](https://github.com/HisashiKato) for his help: . + ### [Wii library](Wii.cpp) The [Wii](Wii.cpp) library support the Wiimote, but also the Nunchuch and Motion Plus extensions via Bluetooth. The Wii U Pro Controller and Wii Balance Board are also supported via Bluetooth. diff --git a/SPP.cpp b/SPP.cpp index 009ea7bc..910f7db8 100644 --- a/SPP.cpp +++ b/SPP.cpp @@ -189,7 +189,7 @@ void SPP::ACLData(uint8_t* l2capinbuf) { } #endif } else if(l2capinbuf[6] == sdp_dcid[0] && l2capinbuf[7] == sdp_dcid[1]) { // SDP - if(l2capinbuf[8] == SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST_PDU) { + if(l2capinbuf[8] == SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST) { if(((l2capinbuf[16] << 8 | l2capinbuf[17]) == SERIALPORT_UUID) || ((l2capinbuf[16] << 8 | l2capinbuf[17]) == 0x0000 && (l2capinbuf[18] << 8 | l2capinbuf[19]) == SERIALPORT_UUID)) { // Check if it's sending the full UUID, see: https://www.bluetooth.org/Technical/AssignedNumbers/service_discovery.htm, we will just check the first four bytes if(firstMessage) { serialPortResponse1(l2capinbuf[9], l2capinbuf[10]); @@ -536,7 +536,7 @@ void SPP::SDP_Command(uint8_t* data, uint8_t nbytes) { // See page 223 in the Bl } void SPP::serviceNotSupported(uint8_t transactionIDHigh, uint8_t transactionIDLow) { // See page 235 in the Bluetooth specs - l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU; + l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE; l2capoutbuf[1] = transactionIDHigh; l2capoutbuf[2] = transactionIDLow; l2capoutbuf[3] = 0x00; // MSB Parameter Length @@ -553,7 +553,7 @@ void SPP::serviceNotSupported(uint8_t transactionIDHigh, uint8_t transactionIDLo } void SPP::serialPortResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow) { - l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU; + l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE; l2capoutbuf[1] = transactionIDHigh; l2capoutbuf[2] = transactionIDLow; l2capoutbuf[3] = 0x00; // MSB Parameter Length @@ -615,7 +615,7 @@ void SPP::serialPortResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLo } void SPP::serialPortResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow) { - l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU; + l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE; l2capoutbuf[1] = transactionIDHigh; l2capoutbuf[2] = transactionIDLow; l2capoutbuf[3] = 0x00; // MSB Parameter Length diff --git a/SPP.h b/SPP.h index bb3027b4..116296ca 100644 --- a/SPP.h +++ b/SPP.h @@ -20,12 +20,6 @@ #include "BTD.h" -/* Used for SDP */ -#define SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST_PDU 0x06 // See the RFCOMM specs -#define SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU 0x07 // See the RFCOMM specs -#define SERIALPORT_UUID 0x1101 // See http://www.bluetooth.org/Technical/AssignedNumbers/service_discovery.htm -#define L2CAP_UUID 0x0100 - /* Used for RFCOMM */ #define RFCOMM_SABM 0x2F #define RFCOMM_UA 0x63 diff --git a/XBOXONESBT.h b/XBOXONESBT.h new file mode 100644 index 00000000..58395790 --- /dev/null +++ b/XBOXONESBT.h @@ -0,0 +1,101 @@ +/* Copyright (C) 2020 Kristian Sloth Lauszus. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Sloth Lauszus + Web : https://lauszus.com + e-mail : lauszus@gmail.com + */ + +#ifndef _xboxonesbt_h_ +#define _xboxonesbt_h_ + +#include "BTHID.h" +#include "XBOXONESParser.h" + +/** + * This class implements support for the Xbox One S controller via Bluetooth. + * It uses the BTHID class for all the Bluetooth communication. + */ +class XBOXONESBT : public BTHID, public XBOXONESParser { +public: + /** + * Constructor for the XBOXONESBT class. + * @param p Pointer to the BTD class instance. + * @param pair Set this to true in order to pair with the device. If the argument is omitted then it will not pair with it. One can use ::PAIR to set it to true. + */ + XBOXONESBT(BTD *p, bool pair = false) : + BTHID(p, pair) { + XBOXONESParser::Reset(); + pBtd->useSimplePairing = true; // The Xbox One S controller only works via simple pairing + }; + + /** + * Used to check if a Xbox One S controller is connected. + * @return Returns true if it is connected. + */ + bool connected() { + return BTHID::connected; + }; + +protected: + /** @name BTHID implementation */ + /** + * Used to parse Bluetooth HID data. + * @param len The length of the incoming data. + * @param buf Pointer to the data buffer. + */ + virtual void ParseBTHIDData(uint8_t len, uint8_t *buf) { + XBOXONESParser::Parse(len, buf); + }; + + /** + * Called when a device is successfully initialized. + * Use attachOnInit(void (*funcOnInit)(void)) to call your own function. + * This is useful for instance if you want to set the LEDs in a specific way. + */ + virtual void OnInitBTHID() { + XBOXONESParser::Reset(); + if (pFuncOnInit) + pFuncOnInit(); // Call the user function + }; + + /** Used to reset the different buffers to there default values */ + virtual void ResetBTHID() { + XBOXONESParser::Reset(); + }; + /**@}*/ + +#if 0 + /** @name XBOXONESParser implementation */ + virtual void sendOutputReport(uint8_t *data, uint8_t nbytes) { + // See: https://lore.kernel.org/patchwork/patch/973394/ + uint8_t buf[nbytes + 2]; + + buf[0] = 0x52; // HID BT Set_report (0x50) | Report Type (Output 0x02) + buf[1] = 0x03; // Report ID + + memcpy(buf + 2, data, nbytes); + + Notify(PSTR("\r\n"), 0x80); + for (uint8_t i = 0; i < sizeof(buf); i++) { + D_PrintHex (buf[i], 0x80); + Notify(PSTR(" "), 0x80); + } + Notify(PSTR("\r\n"), 0x80); + + //pBtd->L2CAP_Command(hci_handle, data, nbytes, interrupt_scid[0], interrupt_scid[1]); + pBtd->L2CAP_Command(hci_handle, buf, sizeof(buf), control_scid[0], control_scid[1]); + }; + /**@}*/ +#endif +}; +#endif diff --git a/XBOXONESParser.cpp b/XBOXONESParser.cpp new file mode 100644 index 00000000..e8f0064f --- /dev/null +++ b/XBOXONESParser.cpp @@ -0,0 +1,200 @@ +/* Copyright (C) 2020 Kristian Sloth Lauszus. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Sloth Lauszus + Web : https://lauszus.com + e-mail : lauszus@gmail.com + */ + +#include "XBOXONESParser.h" + +// To enable serial debugging see "settings.h" +//#define PRINTREPORT // Uncomment to print the report send by the Xbox One S Controller + +/** Buttons on the controller */ +const uint8_t XBOX_ONE_S_BUTTONS[] PROGMEM = { + UP, // UP + RIGHT, // RIGHT + DOWN, // DOWN + LEFT, // LEFT + + 0x0E, // VIEW + 0x0F, // MENU + 0x10, // L3 + 0x11, // R3 + + 0, 0, // Skip L2 and R2 as these are analog buttons + 0x0C, // L1 + 0x0D, // R1 + + 0x09, // B + 0x08, // A + 0x0A, // X + 0x0B, // Y +}; + +enum DPADEnum { + DPAD_OFF = 0x0, + DPAD_UP = 0x1, + DPAD_UP_RIGHT = 0x2, + DPAD_RIGHT = 0x3, + DPAD_RIGHT_DOWN = 0x4, + DPAD_DOWN = 0x5, + DPAD_DOWN_LEFT = 0x6, + DPAD_LEFT = 0x7, + DPAD_LEFT_UP = 0x8, +}; + +bool XBOXONESParser::checkDpad(ButtonEnum b) { + switch (b) { + case UP: + return xboxOneSData.btn.dpad == DPAD_LEFT_UP || xboxOneSData.btn.dpad == DPAD_UP || xboxOneSData.btn.dpad == DPAD_UP_RIGHT; + case RIGHT: + return xboxOneSData.btn.dpad == DPAD_UP_RIGHT || xboxOneSData.btn.dpad == DPAD_RIGHT || xboxOneSData.btn.dpad == DPAD_RIGHT_DOWN; + case DOWN: + return xboxOneSData.btn.dpad == DPAD_RIGHT_DOWN || xboxOneSData.btn.dpad == DPAD_DOWN || xboxOneSData.btn.dpad == DPAD_DOWN_LEFT; + case LEFT: + return xboxOneSData.btn.dpad == DPAD_DOWN_LEFT || xboxOneSData.btn.dpad == DPAD_LEFT || xboxOneSData.btn.dpad == DPAD_LEFT_UP; + default: + return false; + } +} + +uint16_t XBOXONESParser::getButtonPress(ButtonEnum b) { + if (b == L2) + return xboxOneSData.trigger[0]; + else if (b == R2) + return xboxOneSData.trigger[1]; + else if (b <= LEFT) // Dpad + return checkDpad(b); + else if (b == XBOX) + return xboxButtonState; + return xboxOneSData.btn.val & (1UL << pgm_read_byte(&XBOX_ONE_S_BUTTONS[(uint8_t)b])); +} + +bool XBOXONESParser::getButtonClick(ButtonEnum b) { + if(b == L2) { + if(L2Clicked) { + L2Clicked = false; + return true; + } + return false; + } else if(b == R2) { + if(R2Clicked) { + R2Clicked = false; + return true; + } + return false; + } else if (b == XBOX) { + bool click = xboxbuttonClickState; + xboxbuttonClickState = 0; // Clear "click" event + return click; + } + uint32_t mask = 1UL << pgm_read_byte(&XBOX_ONE_S_BUTTONS[(uint8_t)b]); + bool click = buttonClickState.val & mask; + buttonClickState.val &= ~mask; // Clear "click" event + return click; +} + +int16_t XBOXONESParser::getAnalogHat(AnalogHatEnum a) { + return xboxOneSData.hatValue[(uint8_t)a] - 32768; // Convert to signed integer +} + +void XBOXONESParser::Parse(uint8_t len, uint8_t *buf) { + if (len > 1 && buf) { +#ifdef PRINTREPORT + Notify(PSTR("\r\n"), 0x80); + for (uint8_t i = 0; i < len; i++) { + D_PrintHex (buf[i], 0x80); + Notify(PSTR(" "), 0x80); + } +#endif + + if (buf[0] == 0x01) // Check report ID + memcpy(&xboxOneSData, buf + 1, min((uint8_t)(len - 1), MFK_CASTUINT8T sizeof(xboxOneSData))); + else if (buf[0] == 0x02) { // This report contains the Xbox button + xboxButtonState = buf[1]; + if(xboxButtonState != xboxOldButtonState) { + xboxbuttonClickState = xboxButtonState & ~xboxOldButtonState; // Update click state variable + xboxOldButtonState = xboxButtonState; + } + return; + } else if (buf[0] == 0x04) // Heartbeat + return; + else { +#ifdef DEBUG_USB_HOST + Notify(PSTR("\r\nUnknown report id: "), 0x80); + D_PrintHex (buf[0], 0x80); +#endif + return; + } + + if (xboxOneSData.btn.val != oldButtonState.val) { // Check if anything has changed + buttonClickState.val = xboxOneSData.btn.val & ~oldButtonState.val; // Update click state variable + oldButtonState.val = xboxOneSData.btn.val; + + // The DPAD buttons does not set the different bits, but set a value corresponding to the buttons pressed, we will simply set the bits ourself + uint8_t newDpad = 0; + if (checkDpad(UP)) + newDpad |= 1 << UP; + if (checkDpad(RIGHT)) + newDpad |= 1 << RIGHT; + if (checkDpad(DOWN)) + newDpad |= 1 << DOWN; + if (checkDpad(LEFT)) + newDpad |= 1 << LEFT; + if (newDpad != oldDpad) { + buttonClickState.dpad = newDpad & ~oldDpad; // Override values + oldDpad = newDpad; + } + } + + // Handle click detection for triggers + if(xboxOneSData.trigger[0] != 0 && triggerOld[0] == 0) + L2Clicked = true; + triggerOld[0] = xboxOneSData.trigger[0]; + if(xboxOneSData.trigger[1] != 0 && triggerOld[1] == 0) + R2Clicked = true; + triggerOld[1] = xboxOneSData.trigger[1]; + } +} + +void XBOXONESParser::Reset() { + uint8_t i; + for (i = 0; i < sizeof(xboxOneSData.hatValue) / sizeof(xboxOneSData.hatValue[0]); i++) + xboxOneSData.hatValue[i] = 32768; // Center value + xboxOneSData.btn.val = 0; + oldButtonState.val = 0; + for (i = 0; i < sizeof(xboxOneSData.trigger) / sizeof(xboxOneSData.trigger[0]); i++) + xboxOneSData.trigger[i] = 0; + + xboxOneSData.btn.dpad = DPAD_OFF; + oldButtonState.dpad = DPAD_OFF; + buttonClickState.dpad = 0; + oldDpad = 0; +}; + +#if 0 +void XBOXONESParser::setRumbleOn(uint8_t leftTrigger, uint8_t rightTrigger, uint8_t leftMotor, uint8_t rightMotor) { + // See: https://lore.kernel.org/patchwork/patch/973394/ + uint8_t buf[8]; + buf[0] = 0x0F;//1 << 1 | 1 << 0; // Enable weak and strong feedback + buf[1] = leftTrigger; + buf[2] = rightTrigger; + buf[3] = leftMotor; + buf[4] = rightMotor; + buf[5] = 255; // Duration of effect in 10 ms + buf[6] = 0; // Start delay in 10 ms + buf[7] = 255; // Loop count + sendOutputReport(buf, sizeof(buf)); +} +#endif diff --git a/XBOXONESParser.h b/XBOXONESParser.h new file mode 100644 index 00000000..41dc30b2 --- /dev/null +++ b/XBOXONESParser.h @@ -0,0 +1,127 @@ +/* Copyright (C) 2020 Kristian Sloth Lauszus. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Sloth Lauszus + Web : https://lauszus.com + e-mail : lauszus@gmail.com + */ + +#ifndef _xboxonesparser_h_ +#define _xboxonesparser_h_ + +#include "Usb.h" +#include "controllerEnums.h" + +union XboxOneSButtons { + struct { + uint8_t dpad : 4; + uint8_t reserved : 4; + + uint8_t a : 1; + uint8_t b : 1; + uint8_t x : 1; + uint8_t y : 1; + + uint8_t l1 : 1; + uint8_t r1 : 1; + uint8_t view : 1; + uint8_t menu : 1; + + uint8_t l3 : 1; + uint8_t r3 : 1; + uint8_t reserved2 : 6; + } __attribute__((packed)); + uint32_t val : 24; +} __attribute__((packed)); + +struct XboxOneSData { + /* Button and joystick values */ + uint16_t hatValue[4]; + uint16_t trigger[2]; + XboxOneSButtons btn; +} __attribute__((packed)); + +/** This class parses all the data sent by the Xbox One S controller */ +class XBOXONESParser { +public: + /** Constructor for the XBOXONESParser class. */ + XBOXONESParser() { + Reset(); + }; + + /** @name Xbox One S Controller functions */ + /** + * getButtonPress(ButtonEnum b) will return true as long as the button is held down. + * + * While getButtonClick(ButtonEnum b) will only return it once. + * + * So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b), + * but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b). + * @param b ::ButtonEnum to read. + * @return getButtonPress(ButtonEnum b) will return a true as long as a button is held down, while getButtonClick(ButtonEnum b) will return true once for each button press. + */ + uint16_t getButtonPress(ButtonEnum b); + bool getButtonClick(ButtonEnum b); + /**@}*/ + + /** + * Used to read the analog joystick. + * @param a ::LeftHatX, ::LeftHatY, ::RightHatX, and ::RightHatY. + * @return Return the analog value as a 16-bit signed integer. + */ + int16_t getAnalogHat(AnalogHatEnum a); + + /** Used to set the rumble off. */ + //void setRumbleOff(); + + /** + * Used to turn on rumble continuously. + * @param leftTrigger Left trigger force. + * @param rightTrigger Right trigger force. + * @param leftMotor Left motor force. + * @param rightMotor Right motor force. + */ + //void setRumbleOn(uint8_t leftTrigger, uint8_t rightTrigger, uint8_t leftMotor, uint8_t rightMotor); + +protected: + /** + * Used to parse data sent from the Xbox One S controller. + * @param len Length of the data. + * @param buf Pointer to the data buffer. + */ + void Parse(uint8_t len, uint8_t *buf); + + /** Used to reset the different buffers to their default values */ + void Reset(); + + /** + * Send the output to the Xbox One S controller. This is implemented in XBOXONESBT.h. + * @param output Pointer to data buffer. + * @param nbytes Bytes to send. + */ + //virtual void sendOutputReport(uint8_t *data, uint8_t nbytes) = 0; + +private: + bool checkDpad(ButtonEnum b); // Used to check Xbox One S DPAD buttons + + XboxOneSData xboxOneSData; + XboxOneSButtons oldButtonState, buttonClickState; + uint8_t oldDpad; + + // The Xbox button is sent in a separate report + uint8_t xboxButtonState, xboxOldButtonState, xboxbuttonClickState; + + uint16_t triggerOld[2]; + bool L2Clicked; // These buttons are analog, so we use we use these bools to check if they where clicked or not + bool R2Clicked; +}; +#endif diff --git a/confdescparser.h b/confdescparser.h index 54053545..5991182a 100644 --- a/confdescparser.h +++ b/confdescparser.h @@ -108,12 +108,14 @@ bool ConfigDescParser::ParseDescriptor theBuffer.valueSize = 2; valParser.Initialize(&theBuffer); stateParseDescr = 1; + // fall through case 1: if(!valParser.Parse(pp, pcntdn)) return false; dscrLen = *((uint8_t*)theBuffer.pValue); dscrType = *((uint8_t*)theBuffer.pValue + 1); stateParseDescr = 2; + // fall through case 2: // This is a sort of hack. Assuming that two bytes are all ready in the buffer // the pointer is positioned two bytes ahead in order for the rest of descriptor @@ -122,6 +124,7 @@ bool ConfigDescParser::ParseDescriptor // in the buffer. theBuffer.pValue = varBuffer + 2; stateParseDescr = 3; + // fall through case 3: switch(dscrType) { case USB_DESCRIPTOR_INTERFACE: @@ -135,6 +138,7 @@ bool ConfigDescParser::ParseDescriptor theBuffer.valueSize = dscrLen - 2; valParser.Initialize(&theBuffer); stateParseDescr = 4; + // fall through case 4: switch(dscrType) { case USB_DESCRIPTOR_CONFIGURATION: diff --git a/controllerEnums.h b/controllerEnums.h index 631825b2..c1a5a497 100644 --- a/controllerEnums.h +++ b/controllerEnums.h @@ -144,6 +144,12 @@ enum ButtonEnum { WHITE = 9, // Available on the original Xbox controller /**@}*/ + /**@{*/ + /** Xbox One S buttons */ + VIEW = 4, + MENU = 5, + /**@}*/ + /** PS Buzz controllers */ RED = 0, YELLOW = 1, 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/examples/Xbox/XBOXONESBT/XBOXONESBT.ino b/examples/Xbox/XBOXONESBT/XBOXONESBT.ino new file mode 100644 index 00000000..c691e9bf --- /dev/null +++ b/examples/Xbox/XBOXONESBT/XBOXONESBT.ino @@ -0,0 +1,135 @@ +/* + Example sketch for the Xbox One S Bluetooth library - developed by Kristian Sloth Lauszus + For more information visit the Github repository: github.com/felis/USB_Host_Shield_2.0 or + send me an e-mail: lauszus@gmail.com + */ + +#include +#include + +// Satisfy the IDE, which needs to see the include statement in the ino too. +#ifdef dobogusinclude +#include +#endif +#include + +USB Usb; +//USBHub Hub1(&Usb); // Some dongles have a hub inside +BTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so + +/* You can create the instance of the XBOXONESBT class in two ways */ +// This will start an inquiry and then pair with the Xbox One S controller - you only have to do this once +// You will need to hold down the Sync and Xbox button at the same time, the Xbox One S controller will then start to blink rapidly indicating that it is in pairing mode +XBOXONESBT Xbox(&Btd, PAIR); + +// After that you can simply create the instance like so and then press the Xbox button on the device +//XBOXONESBT Xbox(&Btd); + +void setup() { + Serial.begin(115200); +#if !defined(__MIPSEL__) + while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection +#endif + if (Usb.Init() == -1) { + Serial.print(F("\r\nOSC did not start")); + while (1); //halt + } + Serial.print(F("\r\nXbox One S Bluetooth Library Started")); +} +void loop() { + Usb.Task(); + + if (Xbox.connected()) { + if (Xbox.getAnalogHat(LeftHatX) > 7500 || Xbox.getAnalogHat(LeftHatX) < -7500 || Xbox.getAnalogHat(LeftHatY) > 7500 || Xbox.getAnalogHat(LeftHatY) < -7500 || Xbox.getAnalogHat(RightHatX) > 7500 || Xbox.getAnalogHat(RightHatX) < -7500 || Xbox.getAnalogHat(RightHatY) > 7500 || Xbox.getAnalogHat(RightHatY) < -7500) { + if (Xbox.getAnalogHat(LeftHatX) > 7500 || Xbox.getAnalogHat(LeftHatX) < -7500) { + Serial.print(F("LeftHatX: ")); + Serial.print(Xbox.getAnalogHat(LeftHatX)); + Serial.print("\t"); + } + if (Xbox.getAnalogHat(LeftHatY) > 7500 || Xbox.getAnalogHat(LeftHatY) < -7500) { + Serial.print(F("LeftHatY: ")); + Serial.print(Xbox.getAnalogHat(LeftHatY)); + Serial.print("\t"); + } + if (Xbox.getAnalogHat(RightHatX) > 7500 || Xbox.getAnalogHat(RightHatX) < -7500) { + Serial.print(F("RightHatX: ")); + Serial.print(Xbox.getAnalogHat(RightHatX)); + Serial.print("\t"); + } + if (Xbox.getAnalogHat(RightHatY) > 7500 || Xbox.getAnalogHat(RightHatY) < -7500) { + Serial.print(F("RightHatY: ")); + Serial.print(Xbox.getAnalogHat(RightHatY)); + } + Serial.println(); + } + + if (Xbox.getButtonPress(L2) > 0 || Xbox.getButtonPress(R2) > 0) { + if (Xbox.getButtonPress(L2) > 0) { + Serial.print(F("L2: ")); + Serial.print(Xbox.getButtonPress(L2)); + Serial.print("\t"); + } + if (Xbox.getButtonPress(R2) > 0) { + Serial.print(F("R2: ")); + Serial.print(Xbox.getButtonPress(R2)); + Serial.print("\t"); + } + Serial.println(); + } + +#if 0 // This is currently not working + // Set rumble effect + static uint16_t oldL2Value, oldR2Value; + if (Xbox.getButtonPress(L2) != oldL2Value || Xbox.getButtonPress(R2) != oldR2Value) { + oldL2Value = Xbox.getButtonPress(L2); + oldR2Value = Xbox.getButtonPress(R2); + uint8_t leftRumble = map(oldL2Value, 0, 1023, 0, 255); // Map the trigger values into a byte + uint8_t rightRumble = map(oldR2Value, 0, 1023, 0, 255); + if (leftRumble > 0 || rightRumble > 0) + Xbox.setRumbleOn(leftRumble, rightRumble, leftRumble, rightRumble); + else + Xbox.setRumbleOff(); + } +#endif + + if (Xbox.getButtonClick(UP)) + Serial.println(F("Up")); + if (Xbox.getButtonClick(DOWN)) + Serial.println(F("Down")); + if (Xbox.getButtonClick(LEFT)) + Serial.println(F("Left")); + if (Xbox.getButtonClick(RIGHT)) + Serial.println(F("Right")); + + if (Xbox.getButtonClick(VIEW)) + Serial.println(F("View")); + if (Xbox.getButtonClick(MENU)) + Serial.println(F("Menu")); + if (Xbox.getButtonClick(XBOX)) { + Serial.println(F("Xbox")); + Xbox.disconnect(); + } + + if (Xbox.getButtonClick(L1)) + Serial.println(F("L1")); + if (Xbox.getButtonClick(R1)) + Serial.println(F("R1")); + if (Xbox.getButtonClick(L2)) + Serial.println(F("L2")); + if (Xbox.getButtonClick(R2)) + Serial.println(F("R2")); + if (Xbox.getButtonClick(L3)) + Serial.println(F("L3")); + if (Xbox.getButtonClick(R3)) + Serial.println(F("R3")); + + if (Xbox.getButtonClick(A)) + Serial.println(F("A")); + if (Xbox.getButtonClick(B)) + Serial.println(F("B")); + if (Xbox.getButtonClick(X)) + Serial.println(F("X")); + if (Xbox.getButtonClick(Y)) + Serial.println(F("Y")); + } +} diff --git a/hidescriptorparser.cpp b/hidescriptorparser.cpp index 43e3f7d6..42e3103b 100644 --- a/hidescriptorparser.cpp +++ b/hidescriptorparser.cpp @@ -1113,16 +1113,19 @@ uint8_t ReportDescParserBase::ParseItem(uint8_t **pp, uint16_t *pcntdn) { if(!pcntdn) return enErrorIncomplete; + // fall through case 1: //USBTRACE2("\r\niSz:",itemSize); theBuffer.valueSize = itemSize; valParser.Initialize(&theBuffer); itemParseState = 2; + // fall through case 2: if(!valParser.Parse(pp, pcntdn)) return enErrorIncomplete; itemParseState = 3; + // fall through case 3: { uint8_t data = *((uint8_t*)varBuffer); @@ -1448,14 +1451,17 @@ uint8_t ReportDescParser2::ParseItem(uint8_t **pp, uint16_t *pcntdn) { if(!pcntdn) return enErrorIncomplete; + // fall through case 1: theBuffer.valueSize = itemSize; valParser.Initialize(&theBuffer); itemParseState = 2; + // fall through case 2: if(!valParser.Parse(pp, pcntdn)) return enErrorIncomplete; itemParseState = 3; + // fall through case 3: { uint8_t data = *((uint8_t*)varBuffer); diff --git a/keywords.txt b/keywords.txt index eed0eb5c..2513e98e 100644 --- a/keywords.txt +++ b/keywords.txt @@ -196,6 +196,7 @@ XBOXUSB KEYWORD1 XBOXONE KEYWORD1 XBOXOLD KEYWORD1 XBOXRECV KEYWORD1 +XBOXONESBT KEYWORD1 #################################################### # Methods and Functions (KEYWORD2) @@ -227,6 +228,9 @@ BACK LITERAL1 XBOX LITERAL1 SYNC LITERAL1 +VIEW LITERAL1 +MENU LITERAL1 + BLACK LITERAL1 WHITE LITERAL1 diff --git a/library.json b/library.json index 5baffe60..76e1069f 100644 --- a/library.json +++ b/library.json @@ -15,9 +15,9 @@ "email": "alex-gl@mail.ru" }, { - "name": "Kristian Lauszus", - "email": "kristianl@tkjelectronics.com", - "url": "http://tkjelectronics.com", + "name": "Kristian Sloth Lauszus", + "email": "lauszus@gmail.com", + "url": "https://lauszus.com", "maintainer": true }, { diff --git a/library.properties b/library.properties index 2dd60a44..179a515c 100644 --- a/library.properties +++ b/library.properties @@ -1,7 +1,7 @@ name=USB Host Shield Library 2.0 version=1.3.2 -author=Oleg Mazurov (Circuits@Home) , Kristian Lauszus (TKJ Electronics) , Andrew Kroll , Alexei Glushchenko (Circuits@Home) -maintainer=Oleg Mazurov (Circuits@Home) , Kristian Lauszus (TKJ Electronics) , Andrew Kroll +author=Oleg Mazurov (Circuits@Home) , Kristian Sloth Lauszus , Andrew Kroll , Alexei Glushchenko (Circuits@Home) +maintainer=Oleg Mazurov (Circuits@Home) , Kristian Sloth Lauszus , Andrew Kroll sentence=Revision 2.0 of MAX3421E-based USB Host Shield Library. paragraph=Supports HID devices, FTDI, ADK, ACM, PL2303, Bluetooth HID devices, SPP communication and mass storage devices. Furthermore it supports PS3, PS4, PS Buzz, Wii and Xbox controllers. category=Other diff --git a/parsetools.cpp b/parsetools.cpp index 1b5efaf3..47c0d2a1 100644 --- a/parsetools.cpp +++ b/parsetools.cpp @@ -44,6 +44,7 @@ bool PTPListParser::Parse(uint8_t **pp, uint16_t *pcntdn, PTP_ARRAY_EL_FUNC pf, pBuf->valueSize = lenSize; theParser.Initialize(pBuf); nStage = 1; + // fall through case 1: if(!theParser.Parse(pp, pcntdn)) @@ -53,11 +54,13 @@ bool PTPListParser::Parse(uint8_t **pp, uint16_t *pcntdn, PTP_ARRAY_EL_FUNC pf, arLen = (pBuf->valueSize >= 4) ? *((uint32_t*)pBuf->pValue) : (uint32_t)(*((uint16_t*)pBuf->pValue)); arLenCntdn = arLen; nStage = 2; + // fall through case 2: pBuf->valueSize = valSize; theParser.Initialize(pBuf); nStage = 3; + // fall through case 3: for(; arLenCntdn; arLenCntdn--) { diff --git a/parsetools.h b/parsetools.h index f7525369..7812e938 100644 --- a/parsetools.h +++ b/parsetools.h @@ -79,6 +79,7 @@ public: case 0: countDown = bytes_to_skip; nStage++; + // fall through case 1: for(; countDown && (*pcntdn); countDown--, (*pp)++, (*pcntdn)--); diff --git a/usbh_midi.cpp b/usbh_midi.cpp index 104eb816..a9f0f719 100644 --- a/usbh_midi.cpp +++ b/usbh_midi.cpp @@ -589,6 +589,7 @@ uint8_t USBH_MIDI::SendSysEx(uint8_t *dataptr, uint16_t datasize, uint8_t nCable break; case 3 : buf[wptr] = (nCable << 4) | 0x7; //x7 SysEx ends with following three bytes. + // fall through default : wptr++; buf[wptr++] = *(dataptr++);