Merge pull request #471 from felis/XBOXONES

Xbox One S controller support
This commit is contained in:
Kristian Sloth Lauszus 2020-11-16 09:31:36 +01:00 committed by GitHub
commit 1399dbf639
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 1144 additions and 58 deletions

View file

@ -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:

256
BTD.cpp
View file

@ -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<uint8_t > (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<uint8_t > (hcibuf[6], 0x80);
Notify(PSTR("\r\nMaximum page number: "), 0x80);
D_PrintHex<uint8_t > (hcibuf[7], 0x80);
Notify(PSTR("\r\nExtended LMP features:"), 0x80);
for(uint8_t i = 0; i < 8; i++) {
Notify(PSTR(" "), 0x80);
D_PrintHex<uint8_t > (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<uint8_t > (hcibuf[2], 0x80);
Notify(PSTR("\r\nNum HCI Command Packets: "), 0x80);
D_PrintHex<uint8_t > (hcibuf[3], 0x80);
Notify(PSTR("\r\nCommand Opcode: "), 0x80);
D_PrintHex<uint8_t > (hcibuf[4], 0x80);
Notify(PSTR(" "), 0x80);
D_PrintHex<uint8_t > (hcibuf[5], 0x80);
#endif
}
break;
@ -465,7 +524,7 @@ void BTD::HCI_event_task() {
D_PrintHex<uint8_t > (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<uint8_t > (hcibuf[8], 0x80);
Notify(PSTR("\r\nOOB data present: "), 0x80);
D_PrintHex<uint8_t > (hcibuf[9], 0x80);
Notify(PSTR("\r\nAuthentication request: "), 0x80);
D_PrintHex<uint8_t > (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<uint8_t > (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<uint8_t > (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<uint8_t > (hcibuf[0], 0x80);
}
#endif
break;
#ifdef EXTRADEBUG
default:
if(hcibuf[0] != 0x00) {
Notify(PSTR("\r\nUnmanaged HCI Event: "), 0x80);
D_PrintHex<uint8_t > (hcibuf[0], 0x80);
Notify(PSTR(", data: "), 0x80);
for(uint16_t i = 0; i < hcibuf[1]; i++) {
D_PrintHex<uint8_t > (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

46
BTD.h
View file

@ -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.

249
BTHID.cpp
View file

@ -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<uint8_t > (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<uint8_t > (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<uint16_t > (uuid, 0x80);
Notify(PSTR("\r\nLength: "), 0x80);
uint16_t length = l2capinbuf[11] << 8 | l2capinbuf[12];
D_PrintHex<uint16_t > (length, 0x80);
Notify(PSTR("\r\nData: "), 0x80);
for(uint8_t i = 0; i < length; i++) {
D_PrintHex<uint8_t > (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<uint8_t > (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 */

10
BTHID.h
View file

@ -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

View file

@ -19,10 +19,10 @@ For more information about the hardware see the [Hardware Manual](https://chome.
# Developed By
* __Oleg Mazurov - <mazurov@gmail.com>
* __Alexei Glushchenko, Circuits@Home__ - <alex-gl@mail.ru>
* __Oleg Mazurov__ - <mazurov@gmail.com>
* __Alexei Glushchenko__ - <alex-gl@mail.ru>
* Developers of the USB Core, HID, FTDI, ADK, ACM, and PL2303 libraries
* __Kristian Lauszus, TKJ Electronics__ - <kristianl@tkjelectronics.com>
* __Kristian Sloth Lauszus__ - <lauszus@gmail.com>
* 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__ - <xxxajk@gmail.com>
* 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:
* <https://github.com/quantus/xbox-one-controller-protocol>
* <https://github.com/torvalds/linux/blob/master/drivers/input/joystick/xpad.c>
* <https://github.com/kylelemons/xbox/blob/master/xbox.go>
#### 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: <https://github.com/felis/USB_Host_Shield_2.0/issues/252#issuecomment-716912362>.
### [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.

View file

@ -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

6
SPP.h
View file

@ -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

101
XBOXONESBT.h Normal file
View file

@ -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<uint8_t > (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

200
XBOXONESParser.cpp Normal file
View file

@ -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<uint8_t > (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<uint8_t > (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

127
XBOXONESParser.h Normal file
View file

@ -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

View file

@ -108,12 +108,14 @@ bool ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::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<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::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<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::ParseDescriptor
theBuffer.valueSize = dscrLen - 2;
valParser.Initialize(&theBuffer);
stateParseDescr = 4;
// fall through
case 4:
switch(dscrType) {
case USB_DESCRIPTOR_CONFIGURATION:

View file

@ -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,

View file

@ -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;

View file

@ -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 <XBOXONESBT.h>
#include <usbhub.h>
// Satisfy the IDE, which needs to see the include statement in the ino too.
#ifdef dobogusinclude
#include <spi4teensy3.h>
#endif
#include <SPI.h>
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"));
}
}

View file

@ -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);

View file

@ -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

View file

@ -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
},
{

View file

@ -1,7 +1,7 @@
name=USB Host Shield Library 2.0
version=1.3.2
author=Oleg Mazurov (Circuits@Home) <mazurov@circuitsathome.com>, Kristian Lauszus (TKJ Electronics) <kristianl@tkjelectronics.com>, Andrew Kroll <xxxajk@gmail.com>, Alexei Glushchenko (Circuits@Home) <alex-gl@mail.ru>
maintainer=Oleg Mazurov (Circuits@Home) <mazurov@circuitsathome.com>, Kristian Lauszus (TKJ Electronics) <kristianl@tkjelectronics.com>, Andrew Kroll <xxxajk@gmail.com>
author=Oleg Mazurov (Circuits@Home) <mazurov@circuitsathome.com>, Kristian Sloth Lauszus <lauszus@gmail.com>, Andrew Kroll <xxxajk@gmail.com>, Alexei Glushchenko (Circuits@Home) <alex-gl@mail.ru>
maintainer=Oleg Mazurov (Circuits@Home) <mazurov@circuitsathome.com>, Kristian Sloth Lauszus <lauszus@gmail.com>, Andrew Kroll <xxxajk@gmail.com>
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

View file

@ -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--) {

View file

@ -79,6 +79,7 @@ public:
case 0:
countDown = bytes_to_skip;
nStage++;
// fall through
case 1:
for(; countDown && (*pcntdn); countDown--, (*pp)++, (*pcntdn)--);

View file

@ -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++);