mirror of
https://github.com/felis/USB_Host_Shield_2.0.git
synced 2024-03-22 11:31:26 +01:00
Initial commit for BTHID library
To see incoming data uncomment PRINTREPORT in BTHID.cpp
This commit is contained in:
parent
4512f0ee0c
commit
f783b97cb9
8 changed files with 749 additions and 63 deletions
130
BTD.cpp
130
BTD.cpp
|
@ -27,6 +27,8 @@ const uint8_t BTD::BTD_DATAOUT_PIPE = 3;
|
||||||
BTD::BTD(USB *p) :
|
BTD::BTD(USB *p) :
|
||||||
connectToWii(false),
|
connectToWii(false),
|
||||||
pairWithWii(false),
|
pairWithWii(false),
|
||||||
|
connectToHIDDevice(false),
|
||||||
|
pairWithHIDDevice(false),
|
||||||
pUsb(p), // Pointer to USB class instance - mandatory
|
pUsb(p), // Pointer to USB class instance - mandatory
|
||||||
bAddress(0), // Device address - mandatory
|
bAddress(0), // Device address - mandatory
|
||||||
bNumEP(1), // If config descriptor needs to be parsed
|
bNumEP(1), // If config descriptor needs to be parsed
|
||||||
|
@ -303,6 +305,8 @@ void BTD::clearAllVariables() {
|
||||||
|
|
||||||
connectToWii = false;
|
connectToWii = false;
|
||||||
incomingWii = false;
|
incomingWii = false;
|
||||||
|
connectToHIDDevice = false;
|
||||||
|
incomingHIDDevice = false;
|
||||||
bAddress = 0; // Clear device address
|
bAddress = 0; // Clear device address
|
||||||
bNumEP = 1; // Must have to be reset to 1
|
bNumEP = 1; // Must have to be reset to 1
|
||||||
qNextPollTime = 0; // Reset next poll time
|
qNextPollTime = 0; // Reset next poll time
|
||||||
|
@ -411,13 +415,18 @@ void BTD::HCI_event_task() {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EV_INQUIRY_COMPLETE:
|
case EV_INQUIRY_COMPLETE:
|
||||||
if (inquiry_counter >= 5 && pairWithWii) {
|
if (inquiry_counter >= 5 && (pairWithWii || pairWithHIDDevice)) {
|
||||||
inquiry_counter = 0;
|
inquiry_counter = 0;
|
||||||
#ifdef DEBUG_USB_HOST
|
#ifdef DEBUG_USB_HOST
|
||||||
|
if (pairWithWii)
|
||||||
Notify(PSTR("\r\nCouldn't find Wiimote"), 0x80);
|
Notify(PSTR("\r\nCouldn't find Wiimote"), 0x80);
|
||||||
|
else
|
||||||
|
Notify(PSTR("\r\nCouldn't find HID device"), 0x80);
|
||||||
#endif
|
#endif
|
||||||
connectToWii = false;
|
connectToWii = false;
|
||||||
pairWithWii = false;
|
pairWithWii = false;
|
||||||
|
connectToHIDDevice = false;
|
||||||
|
pairWithHIDDevice = false;
|
||||||
hci_state = HCI_SCANNING_STATE;
|
hci_state = HCI_SCANNING_STATE;
|
||||||
}
|
}
|
||||||
inquiry_counter++;
|
inquiry_counter++;
|
||||||
|
@ -430,28 +439,44 @@ void BTD::HCI_event_task() {
|
||||||
Notify(hcibuf[2], 0x80);
|
Notify(hcibuf[2], 0x80);
|
||||||
#endif
|
#endif
|
||||||
for (uint8_t i = 0; i < hcibuf[2]; i++) {
|
for (uint8_t i = 0; i < hcibuf[2]; i++) {
|
||||||
if ((hcibuf[4 + 8 * hcibuf[2] + 3 * i] == 0x04 && hcibuf[5 + 8 * hcibuf[2] + 3 * i] == 0x25 && hcibuf[6 + 8 * hcibuf[2] + 3 * i] == 0x00) || (hcibuf[4 + 8 * hcibuf[2] + 3 * i] == 0x08 && hcibuf[5 + 8 * hcibuf[2] + 3 * i] == 0x05 && hcibuf[6 + 8 * hcibuf[2] + 3 * i] == 0x00)) { // See http://bluetooth-pentest.narod.ru/software/bluetooth_class_of_device-service_generator.html and http://wiibrew.org/wiki/Wiimote#SDP_information
|
uint8_t offset = 8 * hcibuf[2] + 3 * i;
|
||||||
if (hcibuf[4 + 8 * hcibuf[2] + 3 * i] == 0x08) // Check if it's the new Wiimote with motion plus inside that was detected
|
uint8_t classOfDevice[3];
|
||||||
|
|
||||||
|
for (uint8_t j = 0; j < 3; j++)
|
||||||
|
classOfDevice[j] = hcibuf[j + 4 + offset];
|
||||||
|
|
||||||
|
if (pairWithWii && classOfDevice[2] == 0x00 && (classOfDevice[1] & 0x05) && (classOfDevice[0] & 0x0C)) { // See http://bluetooth-pentest.narod.ru/software/bluetooth_class_of_device-service_generator.html and http://wiibrew.org/wiki/Wiimote#SDP_information
|
||||||
|
if (classOfDevice[0] & 0x08) // Check if it's the new Wiimote with motion plus inside that was detected
|
||||||
motionPlusInside = true;
|
motionPlusInside = true;
|
||||||
else
|
else
|
||||||
motionPlusInside = false;
|
motionPlusInside = false;
|
||||||
disc_bdaddr[0] = hcibuf[3 + 6 * i];
|
|
||||||
disc_bdaddr[1] = hcibuf[4 + 6 * i];
|
for (uint8_t j = 0; j < 6; j++)
|
||||||
disc_bdaddr[2] = hcibuf[5 + 6 * i];
|
disc_bdaddr[j] = hcibuf[j + 3 + 6 * i];
|
||||||
disc_bdaddr[3] = hcibuf[6 + 6 * i];
|
|
||||||
disc_bdaddr[4] = hcibuf[7 + 6 * i];
|
hci_event_flag |= HCI_FLAG_DEVICE_FOUND;
|
||||||
disc_bdaddr[5] = hcibuf[8 + 6 * i];
|
|
||||||
hci_event_flag |= HCI_FLAG_WII_FOUND;
|
|
||||||
break;
|
break;
|
||||||
|
} else if (pairWithHIDDevice && (classOfDevice[1] & 0x05) && (classOfDevice[0] & 0xC0)) { // Check if it is a mouse or keyboard
|
||||||
|
#ifdef DEBUG_USB_HOST
|
||||||
|
if (classOfDevice[0] & 0x80)
|
||||||
|
Notify(PSTR("\r\nMouse found"), 0x80);
|
||||||
|
if (classOfDevice[0] & 0x40)
|
||||||
|
Notify(PSTR("\r\nKeyboard found"), 0x80);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (uint8_t j = 0; j < 6; j++)
|
||||||
|
disc_bdaddr[j] = hcibuf[j + 3 + 6 * i];
|
||||||
|
|
||||||
|
hci_event_flag |= HCI_FLAG_DEVICE_FOUND;
|
||||||
}
|
}
|
||||||
#ifdef EXTRADEBUG
|
#ifdef EXTRADEBUG
|
||||||
else {
|
else {
|
||||||
Notify(PSTR("\r\nClass of device: "), 0x80);
|
Notify(PSTR("\r\nClass of device: "), 0x80);
|
||||||
D_PrintHex<uint8_t > (hcibuf[6 + 8 * hcibuf[2] + 3 * i], 0x80);
|
D_PrintHex<uint8_t > (classOfDevice[2], 0x80);
|
||||||
Notify(PSTR(" "), 0x80);
|
Notify(PSTR(" "), 0x80);
|
||||||
D_PrintHex<uint8_t > (hcibuf[5 + 8 * hcibuf[2] + 3 * i], 0x80);
|
D_PrintHex<uint8_t > (classOfDevice[1], 0x80);
|
||||||
Notify(PSTR(" "), 0x80);
|
Notify(PSTR(" "), 0x80);
|
||||||
D_PrintHex<uint8_t > (hcibuf[4 + 8 * hcibuf[2] + 3 * i], 0x80);
|
D_PrintHex<uint8_t > (classOfDevice[0], 0x80);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -467,7 +492,7 @@ void BTD::HCI_event_task() {
|
||||||
hci_handle = hcibuf[3] | ((hcibuf[4] & 0x0F) << 8); // store the handle for the ACL connection
|
hci_handle = hcibuf[3] | ((hcibuf[4] & 0x0F) << 8); // store the handle for the ACL connection
|
||||||
hci_event_flag |= HCI_FLAG_CONN_COMPLETE; // set connection complete flag
|
hci_event_flag |= HCI_FLAG_CONN_COMPLETE; // set connection complete flag
|
||||||
} else {
|
} else {
|
||||||
hci_state = HCI_CHECK_WII_SERVICE;
|
hci_state = HCI_CHECK_DEVICE_SERVICE;
|
||||||
#ifdef DEBUG_USB_HOST
|
#ifdef DEBUG_USB_HOST
|
||||||
Notify(PSTR("\r\nConnection Failed: "), 0x80);
|
Notify(PSTR("\r\nConnection Failed: "), 0x80);
|
||||||
D_PrintHex<uint8_t > (hcibuf[2], 0x80);
|
D_PrintHex<uint8_t > (hcibuf[2], 0x80);
|
||||||
|
@ -491,12 +516,19 @@ void BTD::HCI_event_task() {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EV_INCOMING_CONNECT:
|
case EV_INCOMING_CONNECT:
|
||||||
disc_bdaddr[0] = hcibuf[2];
|
for (uint8_t i = 0; i < 6; i++)
|
||||||
disc_bdaddr[1] = hcibuf[3];
|
disc_bdaddr[i] = hcibuf[i + 2];
|
||||||
disc_bdaddr[2] = hcibuf[4];
|
|
||||||
disc_bdaddr[3] = hcibuf[5];
|
if ((hcibuf[9] & 0x05) && (hcibuf[8] & 0xC0)) { // Check if it is a mouse or keyboard
|
||||||
disc_bdaddr[4] = hcibuf[6];
|
#ifdef DEBUG_USB_HOST
|
||||||
disc_bdaddr[5] = hcibuf[7];
|
if (hcibuf[8] & 0x80)
|
||||||
|
Notify(PSTR("\r\nMouse is connecting"), 0x80);
|
||||||
|
if (hcibuf[8] & 0x40)
|
||||||
|
Notify(PSTR("\r\nKeyboard is connecting"), 0x80);
|
||||||
|
#endif
|
||||||
|
incomingHIDDevice = true;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef EXTRADEBUG
|
#ifdef EXTRADEBUG
|
||||||
Notify(PSTR("\r\nClass of device: "), 0x80);
|
Notify(PSTR("\r\nClass of device: "), 0x80);
|
||||||
D_PrintHex<uint8_t > (hcibuf[10], 0x80);
|
D_PrintHex<uint8_t > (hcibuf[10], 0x80);
|
||||||
|
@ -538,9 +570,14 @@ void BTD::HCI_event_task() {
|
||||||
case EV_AUTHENTICATION_COMPLETE:
|
case EV_AUTHENTICATION_COMPLETE:
|
||||||
if (pairWithWii && !connectToWii) {
|
if (pairWithWii && !connectToWii) {
|
||||||
#ifdef DEBUG_USB_HOST
|
#ifdef DEBUG_USB_HOST
|
||||||
Notify(PSTR("\r\nPairing successful"), 0x80);
|
Notify(PSTR("\r\nPairing successful with Wiimote"), 0x80);
|
||||||
#endif
|
#endif
|
||||||
connectToWii = true; // Only send the ACL data to the Wii service
|
connectToWii = true; // Only send the ACL data to the Wii service
|
||||||
|
} else if (pairWithHIDDevice && !connectToHIDDevice) {
|
||||||
|
#ifdef DEBUG_USB_HOST
|
||||||
|
Notify(PSTR("\r\nPairing successful with HID device"), 0x80);
|
||||||
|
#endif
|
||||||
|
connectToHIDDevice = true; // Only send the ACL data to the Wii service
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
/* We will just ignore the following events */
|
/* We will just ignore the following events */
|
||||||
|
@ -639,7 +676,7 @@ void BTD::HCI_task() {
|
||||||
hci_set_local_name(btdName);
|
hci_set_local_name(btdName);
|
||||||
hci_state = HCI_SET_NAME_STATE;
|
hci_state = HCI_SET_NAME_STATE;
|
||||||
} else
|
} else
|
||||||
hci_state = HCI_CHECK_WII_SERVICE;
|
hci_state = HCI_CHECK_DEVICE_SERVICE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -649,14 +686,17 @@ void BTD::HCI_task() {
|
||||||
Notify(PSTR("\r\nThe name is set to: "), 0x80);
|
Notify(PSTR("\r\nThe name is set to: "), 0x80);
|
||||||
NotifyStr(btdName, 0x80);
|
NotifyStr(btdName, 0x80);
|
||||||
#endif
|
#endif
|
||||||
hci_state = HCI_CHECK_WII_SERVICE;
|
hci_state = HCI_CHECK_DEVICE_SERVICE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HCI_CHECK_WII_SERVICE:
|
case HCI_CHECK_DEVICE_SERVICE:
|
||||||
if (pairWithWii) { // Check if it should try to connect to a wiimote
|
if (pairWithHIDDevice || pairWithWii) { // Check if it should try to connect to a wiimote
|
||||||
#ifdef DEBUG_USB_HOST
|
#ifdef DEBUG_USB_HOST
|
||||||
|
if (pairWithWii)
|
||||||
Notify(PSTR("\r\nStarting inquiry\r\nPress 1 & 2 on the Wiimote\r\nOr press sync if you are using a Wii U Pro Controller"), 0x80);
|
Notify(PSTR("\r\nStarting inquiry\r\nPress 1 & 2 on the Wiimote\r\nOr press sync if you are using a Wii U Pro Controller"), 0x80);
|
||||||
|
else
|
||||||
|
Notify(PSTR("\r\nPlease enable discovery of your device"), 0x80);
|
||||||
#endif
|
#endif
|
||||||
hci_inquiry();
|
hci_inquiry();
|
||||||
hci_state = HCI_INQUIRY_STATE;
|
hci_state = HCI_INQUIRY_STATE;
|
||||||
|
@ -665,37 +705,55 @@ void BTD::HCI_task() {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HCI_INQUIRY_STATE:
|
case HCI_INQUIRY_STATE:
|
||||||
if (hci_wii_found) {
|
if (hci_device_found) {
|
||||||
hci_inquiry_cancel(); // Stop inquiry
|
hci_inquiry_cancel(); // Stop inquiry
|
||||||
#ifdef DEBUG_USB_HOST
|
#ifdef DEBUG_USB_HOST
|
||||||
|
if (pairWithWii)
|
||||||
Notify(PSTR("\r\nWiimote found"), 0x80);
|
Notify(PSTR("\r\nWiimote found"), 0x80);
|
||||||
|
else
|
||||||
|
Notify(PSTR("\r\nHID device found"), 0x80);
|
||||||
|
|
||||||
Notify(PSTR("\r\nNow just create the instance like so:"), 0x80);
|
Notify(PSTR("\r\nNow just create the instance like so:"), 0x80);
|
||||||
|
if (pairWithWii)
|
||||||
Notify(PSTR("\r\nWII Wii(&Btd);"), 0x80);
|
Notify(PSTR("\r\nWII Wii(&Btd);"), 0x80);
|
||||||
Notify(PSTR("\r\nAnd then press any button on the Wiimote"), 0x80);
|
else
|
||||||
|
Notify(PSTR("\r\nBTHID hid(&Btd);"), 0x80);
|
||||||
|
|
||||||
|
Notify(PSTR("\r\nAnd then press any button on the "), 0x80);
|
||||||
|
if (pairWithWii)
|
||||||
|
Notify(PSTR("Wiimote"), 0x80);
|
||||||
|
else
|
||||||
|
Notify(PSTR("device"), 0x80);
|
||||||
#endif
|
#endif
|
||||||
if (motionPlusInside) {
|
if (motionPlusInside) {
|
||||||
hci_remote_name(); // We need to know the name to distinguish between a Wiimote and a Wii U Pro Controller
|
hci_remote_name(); // We need to know the name to distinguish between a Wiimote and a Wii U Pro Controller
|
||||||
hci_state = HCI_REMOTE_NAME_STATE;
|
hci_state = HCI_REMOTE_NAME_STATE;
|
||||||
} else
|
} else
|
||||||
hci_state = HCI_CONNECT_WII_STATE;
|
hci_state = HCI_CONNECT_DEVICE_STATE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HCI_CONNECT_WII_STATE:
|
case HCI_CONNECT_DEVICE_STATE:
|
||||||
if (hci_cmd_complete) {
|
if (hci_cmd_complete) {
|
||||||
#ifdef DEBUG_USB_HOST
|
#ifdef DEBUG_USB_HOST
|
||||||
|
if (pairWithWii)
|
||||||
Notify(PSTR("\r\nConnecting to Wiimote"), 0x80);
|
Notify(PSTR("\r\nConnecting to Wiimote"), 0x80);
|
||||||
|
else
|
||||||
|
Notify(PSTR("\r\nConnecting to HID device"), 0x80);
|
||||||
#endif
|
#endif
|
||||||
hci_connect();
|
hci_connect();
|
||||||
hci_state = HCI_CONNECTED_WII_STATE;
|
hci_state = HCI_CONNECTED_DEVICE_STATE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HCI_CONNECTED_WII_STATE:
|
case HCI_CONNECTED_DEVICE_STATE:
|
||||||
if (hci_connect_event) {
|
if (hci_connect_event) {
|
||||||
if (hci_connect_complete) {
|
if (hci_connect_complete) {
|
||||||
#ifdef DEBUG_USB_HOST
|
#ifdef DEBUG_USB_HOST
|
||||||
|
if (pairWithWii)
|
||||||
Notify(PSTR("\r\nConnected to Wiimote"), 0x80);
|
Notify(PSTR("\r\nConnected to Wiimote"), 0x80);
|
||||||
|
else
|
||||||
|
Notify(PSTR("\r\nConnected to HID device"), 0x80);
|
||||||
#endif
|
#endif
|
||||||
hci_authentication_request(); // This will start the pairing with the wiimote
|
hci_authentication_request(); // This will start the pairing with the wiimote
|
||||||
hci_state = HCI_SCANNING_STATE;
|
hci_state = HCI_SCANNING_STATE;
|
||||||
|
@ -709,7 +767,7 @@ void BTD::HCI_task() {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HCI_SCANNING_STATE:
|
case HCI_SCANNING_STATE:
|
||||||
if (!connectToWii && !pairWithWii) {
|
if (!connectToWii && !pairWithWii && !connectToHIDDevice && !pairWithHIDDevice) {
|
||||||
#ifdef DEBUG_USB_HOST
|
#ifdef DEBUG_USB_HOST
|
||||||
Notify(PSTR("\r\nWait For Incoming Connection Request"), 0x80);
|
Notify(PSTR("\r\nWait For Incoming Connection Request"), 0x80);
|
||||||
#endif
|
#endif
|
||||||
|
@ -763,7 +821,7 @@ void BTD::HCI_task() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (pairWithWii && motionPlusInside)
|
if (pairWithWii && motionPlusInside)
|
||||||
hci_state = HCI_CONNECT_WII_STATE;
|
hci_state = HCI_CONNECT_DEVICE_STATE;
|
||||||
else {
|
else {
|
||||||
hci_accept_connection();
|
hci_accept_connection();
|
||||||
hci_state = HCI_CONNECTED_STATE;
|
hci_state = HCI_CONNECTED_STATE;
|
||||||
|
@ -816,6 +874,10 @@ void BTD::HCI_task() {
|
||||||
incomingWii = false;
|
incomingWii = false;
|
||||||
pairWithWii = false;
|
pairWithWii = false;
|
||||||
|
|
||||||
|
connectToHIDDevice = false;
|
||||||
|
incomingHIDDevice = false;
|
||||||
|
pairWithHIDDevice = false;
|
||||||
|
|
||||||
hci_state = HCI_SCANNING_STATE;
|
hci_state = HCI_SCANNING_STATE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -947,7 +1009,7 @@ void BTD::hci_set_local_name(const char* name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void BTD::hci_inquiry() {
|
void BTD::hci_inquiry() {
|
||||||
hci_event_flag &= ~HCI_FLAG_WII_FOUND;
|
hci_event_flag &= ~HCI_FLAG_DEVICE_FOUND;
|
||||||
hcibuf[0] = 0x01;
|
hcibuf[0] = 0x01;
|
||||||
hcibuf[1] = 0x01 << 2; // HCI OGF = 1
|
hcibuf[1] = 0x01 << 2; // HCI OGF = 1
|
||||||
hcibuf[2] = 0x05; // Parameter Total Length = 5
|
hcibuf[2] = 0x05; // Parameter Total Length = 5
|
||||||
|
|
28
BTD.h
28
BTD.h
|
@ -45,11 +45,11 @@
|
||||||
#define HCI_BDADDR_STATE 3
|
#define HCI_BDADDR_STATE 3
|
||||||
#define HCI_LOCAL_VERSION_STATE 4
|
#define HCI_LOCAL_VERSION_STATE 4
|
||||||
#define HCI_SET_NAME_STATE 5
|
#define HCI_SET_NAME_STATE 5
|
||||||
#define HCI_CHECK_WII_SERVICE 6
|
#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 Wii controller
|
#define HCI_INQUIRY_STATE 7 // These three states are only used if it should pair and connect to a Wii controller
|
||||||
#define HCI_CONNECT_WII_STATE 8
|
#define HCI_CONNECT_DEVICE_STATE 8
|
||||||
#define HCI_CONNECTED_WII_STATE 9
|
#define HCI_CONNECTED_DEVICE_STATE 9
|
||||||
|
|
||||||
#define HCI_SCANNING_STATE 10
|
#define HCI_SCANNING_STATE 10
|
||||||
#define HCI_CONNECT_IN_STATE 11
|
#define HCI_CONNECT_IN_STATE 11
|
||||||
|
@ -67,7 +67,7 @@
|
||||||
#define HCI_FLAG_INCOMING_REQUEST 0x10
|
#define HCI_FLAG_INCOMING_REQUEST 0x10
|
||||||
#define HCI_FLAG_READ_BDADDR 0x20
|
#define HCI_FLAG_READ_BDADDR 0x20
|
||||||
#define HCI_FLAG_READ_VERSION 0x40
|
#define HCI_FLAG_READ_VERSION 0x40
|
||||||
#define HCI_FLAG_WII_FOUND 0x80
|
#define HCI_FLAG_DEVICE_FOUND 0x80
|
||||||
#define HCI_FLAG_CONNECT_EVENT 0x100
|
#define HCI_FLAG_CONNECT_EVENT 0x100
|
||||||
|
|
||||||
/*Macros for HCI event flag tests */
|
/*Macros for HCI event flag tests */
|
||||||
|
@ -78,7 +78,7 @@
|
||||||
#define hci_incoming_connect_request (hci_event_flag & HCI_FLAG_INCOMING_REQUEST)
|
#define hci_incoming_connect_request (hci_event_flag & HCI_FLAG_INCOMING_REQUEST)
|
||||||
#define hci_read_bdaddr_complete (hci_event_flag & HCI_FLAG_READ_BDADDR)
|
#define hci_read_bdaddr_complete (hci_event_flag & HCI_FLAG_READ_BDADDR)
|
||||||
#define hci_read_version_complete (hci_event_flag & HCI_FLAG_READ_VERSION)
|
#define hci_read_version_complete (hci_event_flag & HCI_FLAG_READ_VERSION)
|
||||||
#define hci_wii_found (hci_event_flag & HCI_FLAG_WII_FOUND)
|
#define hci_device_found (hci_event_flag & HCI_FLAG_DEVICE_FOUND)
|
||||||
#define hci_connect_event (hci_event_flag & HCI_FLAG_CONNECT_EVENT)
|
#define hci_connect_event (hci_event_flag & HCI_FLAG_CONNECT_EVENT)
|
||||||
|
|
||||||
/* HCI Events managed */
|
/* HCI Events managed */
|
||||||
|
@ -133,6 +133,8 @@
|
||||||
#define BTD_MAX_ENDPOINTS 4
|
#define BTD_MAX_ENDPOINTS 4
|
||||||
#define BTD_NUMSERVICES 4 // Max number of Bluetooth services - if you need more than four simply increase this number
|
#define BTD_NUMSERVICES 4 // Max number of Bluetooth services - if you need more than four simply increase this number
|
||||||
|
|
||||||
|
#define PAIR 1
|
||||||
|
|
||||||
/** All Bluetooth services should include this class. */
|
/** All Bluetooth services should include this class. */
|
||||||
class BluetoothService {
|
class BluetoothService {
|
||||||
public:
|
public:
|
||||||
|
@ -422,19 +424,31 @@ public:
|
||||||
/** Call this function to pair with a Wiimote */
|
/** Call this function to pair with a Wiimote */
|
||||||
void pairWithWiimote() {
|
void pairWithWiimote() {
|
||||||
pairWithWii = true;
|
pairWithWii = true;
|
||||||
hci_state = HCI_CHECK_WII_SERVICE;
|
hci_state = HCI_CHECK_DEVICE_SERVICE;
|
||||||
};
|
};
|
||||||
/** Used to only send the ACL data to the wiimote. */
|
/** Used to only send the ACL data to the wiimote. */
|
||||||
bool connectToWii;
|
bool connectToWii;
|
||||||
/** True if a Wiimote is connecting. */
|
/** True if a Wiimote is connecting. */
|
||||||
bool incomingWii;
|
bool incomingWii;
|
||||||
/** True when it should pair with the incoming Wiimote. */
|
/** True when it should pair with a Wiimote. */
|
||||||
bool pairWithWii;
|
bool pairWithWii;
|
||||||
/** True if it's the new Wiimote with the Motion Plus Inside or a Wii U Pro Controller. */
|
/** True if it's the new Wiimote with the Motion Plus Inside or a Wii U Pro Controller. */
|
||||||
bool motionPlusInside;
|
bool motionPlusInside;
|
||||||
/** True if it's a Wii U Pro Controller. */
|
/** True if it's a Wii U Pro Controller. */
|
||||||
bool wiiUProController;
|
bool wiiUProController;
|
||||||
|
|
||||||
|
/** Call this function to pair with a Wiimote */
|
||||||
|
void pairWithHID() {
|
||||||
|
pairWithHIDDevice = true;
|
||||||
|
hci_state = HCI_CHECK_DEVICE_SERVICE;
|
||||||
|
};
|
||||||
|
/** Used to only send the ACL data to the wiimote. */
|
||||||
|
bool connectToHIDDevice;
|
||||||
|
/** True if a Wiimote is connecting. */
|
||||||
|
bool incomingHIDDevice;
|
||||||
|
/** True when it should pair with a device like a mouse or keyboard. */
|
||||||
|
bool pairWithHIDDevice;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read the poll interval taken from the endpoint descriptors.
|
* Read the poll interval taken from the endpoint descriptors.
|
||||||
* @return The poll interval in ms.
|
* @return The poll interval in ms.
|
||||||
|
|
410
BTHID.cpp
Normal file
410
BTHID.cpp
Normal file
|
@ -0,0 +1,410 @@
|
||||||
|
/* Copyright (C) 2013 Kristian Lauszus, TKJ Electronics. 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 Lauszus, TKJ Electronics
|
||||||
|
Web : http://www.tkjelectronics.com
|
||||||
|
e-mail : kristianl@tkjelectronics.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "BTHID.h"
|
||||||
|
// To enable serial debugging see "settings.h"
|
||||||
|
//#define EXTRADEBUG // Uncomment to get even more debugging data
|
||||||
|
//#define PRINTREPORT // Uncomment to print the report send by the Wii controllers
|
||||||
|
|
||||||
|
const uint8_t BUTTONS[] PROGMEM = { // Mouse buttons
|
||||||
|
0x04, // MIDDLE
|
||||||
|
0x02, // RIGHT
|
||||||
|
0, // Skip
|
||||||
|
0x01, // LEFT
|
||||||
|
};
|
||||||
|
|
||||||
|
BTHID::BTHID(BTD *p, bool pair, const char *pin) :
|
||||||
|
pBtd(p) // pointer to USB class instance - mandatory
|
||||||
|
{
|
||||||
|
if (pBtd)
|
||||||
|
pBtd->registerServiceClass(this); // Register it as a Bluetooth service
|
||||||
|
|
||||||
|
pBtd->pairWithHIDDevice = pair;
|
||||||
|
|
||||||
|
if (pair)
|
||||||
|
pBtd->btdPin= pin;
|
||||||
|
|
||||||
|
/* Set device cid for the control and intterrupt channelse - LSB */
|
||||||
|
control_dcid[0] = 0x70; // 0x0070
|
||||||
|
control_dcid[1] = 0x00;
|
||||||
|
interrupt_dcid[0] = 0x71; // 0x0071
|
||||||
|
interrupt_dcid[1] = 0x00;
|
||||||
|
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BTHID::Reset() {
|
||||||
|
connected = false;
|
||||||
|
activeConnection = false;
|
||||||
|
l2cap_event_flag = 0; // Reset flags
|
||||||
|
l2cap_state = L2CAP_WAIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BTHID::disconnect() { // Use this void to disconnect any of the controllers
|
||||||
|
// 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, 0x0A, interrupt_scid, interrupt_dcid);
|
||||||
|
Reset();
|
||||||
|
l2cap_state = L2CAP_INTERRUPT_DISCONNECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BTHID::ACLData(uint8_t* l2capinbuf) {
|
||||||
|
if (!pBtd->l2capConnectionClaimed && pBtd->incomingHIDDevice && !connected && !activeConnection) {
|
||||||
|
if (l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) {
|
||||||
|
if ((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) {
|
||||||
|
pBtd->incomingHIDDevice = false;
|
||||||
|
pBtd->l2capConnectionClaimed = true; // Claim that the incoming connection belongs to this service
|
||||||
|
activeConnection = true;
|
||||||
|
hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection
|
||||||
|
l2cap_state = L2CAP_WAIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((l2capinbuf[0] | (l2capinbuf[1] << 8)) == (hci_handle | 0x2000)) { // acl_handle_ok or it's a new connection
|
||||||
|
if ((l2capinbuf[6] | (l2capinbuf[7] << 8)) == 0x0001) { //l2cap_control - Channel ID for ACL-U
|
||||||
|
if (l2capinbuf[8] == L2CAP_CMD_COMMAND_REJECT) {
|
||||||
|
#ifdef DEBUG_USB_HOST
|
||||||
|
Notify(PSTR("\r\nL2CAP Command Rejected - Reason: "), 0x80);
|
||||||
|
D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);
|
||||||
|
Notify(PSTR(" "), 0x80);
|
||||||
|
D_PrintHex<uint8_t > (l2capinbuf[12], 0x80);
|
||||||
|
Notify(PSTR(" "), 0x80);
|
||||||
|
D_PrintHex<uint8_t > (l2capinbuf[17], 0x80);
|
||||||
|
Notify(PSTR(" "), 0x80);
|
||||||
|
D_PrintHex<uint8_t > (l2capinbuf[16], 0x80);
|
||||||
|
Notify(PSTR(" "), 0x80);
|
||||||
|
D_PrintHex<uint8_t > (l2capinbuf[15], 0x80);
|
||||||
|
Notify(PSTR(" "), 0x80);
|
||||||
|
D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);
|
||||||
|
#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]) { // Success
|
||||||
|
//Notify(PSTR("\r\nHID Control Connection Complete"), 0x80);
|
||||||
|
identifier = l2capinbuf[9];
|
||||||
|
control_scid[0] = l2capinbuf[12];
|
||||||
|
control_scid[1] = l2capinbuf[13];
|
||||||
|
l2cap_event_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);
|
||||||
|
identifier = l2capinbuf[9];
|
||||||
|
interrupt_scid[0] = l2capinbuf[12];
|
||||||
|
interrupt_scid[1] = l2capinbuf[13];
|
||||||
|
l2cap_event_flag |= L2CAP_FLAG_INTERRUPT_CONNECTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) {
|
||||||
|
#ifdef EXTRADEBUG
|
||||||
|
Notify(PSTR("\r\nL2CAP Connection Request - PSM: "), 0x80);
|
||||||
|
D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);
|
||||||
|
Notify(PSTR(" "), 0x80);
|
||||||
|
D_PrintHex<uint8_t > (l2capinbuf[12], 0x80);
|
||||||
|
Notify(PSTR(" SCID: "), 0x80);
|
||||||
|
D_PrintHex<uint8_t > (l2capinbuf[15], 0x80);
|
||||||
|
Notify(PSTR(" "), 0x80);
|
||||||
|
D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);
|
||||||
|
Notify(PSTR(" Identifier: "), 0x80);
|
||||||
|
D_PrintHex<uint8_t > (l2capinbuf[9], 0x80);
|
||||||
|
#endif
|
||||||
|
if ((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) {
|
||||||
|
identifier = l2capinbuf[9];
|
||||||
|
control_scid[0] = l2capinbuf[14];
|
||||||
|
control_scid[1] = l2capinbuf[15];
|
||||||
|
l2cap_event_flag |= L2CAP_FLAG_CONNECTION_CONTROL_REQUEST;
|
||||||
|
} else if ((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_INTR_PSM) {
|
||||||
|
identifier = l2capinbuf[9];
|
||||||
|
interrupt_scid[0] = l2capinbuf[14];
|
||||||
|
interrupt_scid[1] = l2capinbuf[15];
|
||||||
|
l2cap_event_flag |= L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST;
|
||||||
|
}
|
||||||
|
} 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);
|
||||||
|
identifier = l2capinbuf[9];
|
||||||
|
l2cap_event_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);
|
||||||
|
identifier = l2capinbuf[9];
|
||||||
|
l2cap_event_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);
|
||||||
|
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);
|
||||||
|
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]) {
|
||||||
|
#ifdef DEBUG_USB_HOST
|
||||||
|
Notify(PSTR("\r\nDisconnect Request: Control Channel"), 0x80);
|
||||||
|
#endif
|
||||||
|
identifier = l2capinbuf[9];
|
||||||
|
pBtd->l2cap_disconnection_response(hci_handle, identifier, control_dcid, control_scid);
|
||||||
|
Reset();
|
||||||
|
} else if (l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {
|
||||||
|
#ifdef DEBUG_USB_HOST
|
||||||
|
Notify(PSTR("\r\nDisconnect Request: Interrupt Channel"), 0x80);
|
||||||
|
#endif
|
||||||
|
identifier = l2capinbuf[9];
|
||||||
|
pBtd->l2cap_disconnection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid);
|
||||||
|
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);
|
||||||
|
identifier = l2capinbuf[9];
|
||||||
|
l2cap_event_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);
|
||||||
|
identifier = l2capinbuf[9];
|
||||||
|
l2cap_event_flag |= L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifdef EXTRADEBUG
|
||||||
|
else {
|
||||||
|
identifier = l2capinbuf[9];
|
||||||
|
Notify(PSTR("\r\nL2CAP Unknown Signaling Command: "), 0x80);
|
||||||
|
D_PrintHex<uint8_t > (l2capinbuf[8], 0x80);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} else if (l2capinbuf[6] == interrupt_dcid[0] && l2capinbuf[7] == interrupt_dcid[1]) { // l2cap_interrupt
|
||||||
|
//Notify(PSTR("\r\n\r\nL2CAP Interrupt: "), 0x80);
|
||||||
|
#ifdef PRINTREPORT
|
||||||
|
Notify(PSTR("\r\n"), 0x80);
|
||||||
|
for (uint16_t i = 0; i < ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]); i++) {
|
||||||
|
D_PrintHex<uint8_t > (l2capinbuf[i + 8], 0x80);
|
||||||
|
Notifyc(' ', 0x80);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (l2capinbuf[8] == 0xA1) { // HID_THDR_DATA_INPUT
|
||||||
|
switch (l2capinbuf[9]) {
|
||||||
|
case 0x01: // Keyboard events
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x02: // Mouse events
|
||||||
|
case 0x1A:
|
||||||
|
ButtonState = l2capinbuf[10];
|
||||||
|
/*xAxis = l2capinbuf[11] | ((int16_t)l2capinbuf[12] << 8);
|
||||||
|
yAxis = l2capinbuf[13] | ((int16_t)l2capinbuf[14] << 8);
|
||||||
|
scroll = l2capinbuf[15] | ((int16_t)l2capinbuf[16] << 8);*/
|
||||||
|
|
||||||
|
if (ButtonState != OldButtonState) {
|
||||||
|
ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable
|
||||||
|
OldButtonState = ButtonState;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x03:
|
||||||
|
#ifdef DEBUG_USB_HOST
|
||||||
|
Notify(PSTR("\r\nChange mode event: "), 0x80);
|
||||||
|
D_PrintHex<uint8_t > (l2capinbuf[11], 0x80);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
#ifdef DEBUG_USB_HOST
|
||||||
|
default:
|
||||||
|
Notify(PSTR("\r\nUnknown Report type: "), 0x80);
|
||||||
|
D_PrintHex<uint8_t > (l2capinbuf[9], 0x80);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifdef EXTRADEBUG
|
||||||
|
else {
|
||||||
|
Notify(PSTR("\r\nUnsupported L2CAP Data - Channel ID: "), 0x80);
|
||||||
|
D_PrintHex<uint8_t > (l2capinbuf[7], 0x80);
|
||||||
|
Notify(PSTR(" "), 0x80);
|
||||||
|
D_PrintHex<uint8_t > (l2capinbuf[6], 0x80);
|
||||||
|
|
||||||
|
Notify(PSTR("\r\nData: "), 0x80);
|
||||||
|
Notify(PSTR("\r\n"), 0x80);
|
||||||
|
for (uint16_t i = 0; i < ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]); i++) {
|
||||||
|
D_PrintHex<uint8_t > (l2capinbuf[i + 8], 0x80);
|
||||||
|
Notifyc(' ', 0x80);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
L2CAP_task();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BTHID::L2CAP_task() {
|
||||||
|
switch (l2cap_state) {
|
||||||
|
/* These states are used if the Wiimote is the host */
|
||||||
|
case L2CAP_CONTROL_SUCCESS:
|
||||||
|
if (l2cap_config_success_control_flag) {
|
||||||
|
#ifdef DEBUG_USB_HOST
|
||||||
|
Notify(PSTR("\r\nHID Control Successfully Configured"), 0x80);
|
||||||
|
#endif
|
||||||
|
l2cap_state = L2CAP_INTERRUPT_SETUP;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L2CAP_INTERRUPT_SETUP:
|
||||||
|
if (l2cap_connection_request_interrupt_flag) {
|
||||||
|
#ifdef DEBUG_USB_HOST
|
||||||
|
Notify(PSTR("\r\nHID Interrupt Incoming Connection Request"), 0x80);
|
||||||
|
#endif
|
||||||
|
pBtd->l2cap_connection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid, PENDING);
|
||||||
|
delay(1);
|
||||||
|
pBtd->l2cap_connection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid, SUCCESSFUL);
|
||||||
|
identifier++;
|
||||||
|
delay(1);
|
||||||
|
pBtd->l2cap_config_request(hci_handle, identifier, interrupt_scid);
|
||||||
|
|
||||||
|
l2cap_state = L2CAP_INTERRUPT_CONFIG_REQUEST;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* These states are used if the Arduino is the host */
|
||||||
|
case L2CAP_CONTROL_CONNECT_REQUEST:
|
||||||
|
if (l2cap_connected_control_flag) {
|
||||||
|
#ifdef DEBUG_USB_HOST
|
||||||
|
Notify(PSTR("\r\nSend HID Control Config Request"), 0x80);
|
||||||
|
#endif
|
||||||
|
identifier++;
|
||||||
|
pBtd->l2cap_config_request(hci_handle, identifier, control_scid);
|
||||||
|
l2cap_state = L2CAP_CONTROL_CONFIG_REQUEST;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L2CAP_CONTROL_CONFIG_REQUEST:
|
||||||
|
if (l2cap_config_success_control_flag) {
|
||||||
|
#ifdef DEBUG_USB_HOST
|
||||||
|
Notify(PSTR("\r\nSend HID Interrupt Connection Request"), 0x80);
|
||||||
|
#endif
|
||||||
|
identifier++;
|
||||||
|
pBtd->l2cap_connection_request(hci_handle, identifier, interrupt_dcid, HID_INTR_PSM);
|
||||||
|
l2cap_state = L2CAP_INTERRUPT_CONNECT_REQUEST;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L2CAP_INTERRUPT_CONNECT_REQUEST:
|
||||||
|
if (l2cap_connected_interrupt_flag) {
|
||||||
|
#ifdef DEBUG_USB_HOST
|
||||||
|
Notify(PSTR("\r\nSend HID Interrupt Config Request"), 0x80);
|
||||||
|
#endif
|
||||||
|
identifier++;
|
||||||
|
pBtd->l2cap_config_request(hci_handle, identifier, interrupt_scid);
|
||||||
|
l2cap_state = L2CAP_INTERRUPT_CONFIG_REQUEST;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L2CAP_INTERRUPT_CONFIG_REQUEST:
|
||||||
|
if (l2cap_config_success_interrupt_flag) { // Now the HID channels is established
|
||||||
|
#ifdef DEBUG_USB_HOST
|
||||||
|
Notify(PSTR("\r\nHID Channels Established"), 0x80);
|
||||||
|
#endif
|
||||||
|
pBtd->connectToHIDDevice = false;
|
||||||
|
pBtd->pairWithHIDDevice = false;
|
||||||
|
connected = true;
|
||||||
|
setProtocol();
|
||||||
|
onInit();
|
||||||
|
l2cap_state = L2CAP_DONE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L2CAP_DONE:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L2CAP_INTERRUPT_DISCONNECT:
|
||||||
|
if (l2cap_disconnect_response_interrupt_flag) {
|
||||||
|
#ifdef DEBUG_USB_HOST
|
||||||
|
Notify(PSTR("\r\nDisconnected Interrupt Channel"), 0x80);
|
||||||
|
#endif
|
||||||
|
identifier++;
|
||||||
|
pBtd->l2cap_disconnection_request(hci_handle, identifier, control_scid, control_dcid);
|
||||||
|
l2cap_state = L2CAP_CONTROL_DISCONNECT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L2CAP_CONTROL_DISCONNECT:
|
||||||
|
if (l2cap_disconnect_response_control_flag) {
|
||||||
|
#ifdef DEBUG_USB_HOST
|
||||||
|
Notify(PSTR("\r\nDisconnected Control Channel"), 0x80);
|
||||||
|
#endif
|
||||||
|
pBtd->hci_disconnect(hci_handle);
|
||||||
|
hci_handle = -1; // Reset handle
|
||||||
|
l2cap_event_flag = 0; // Reset flags
|
||||||
|
l2cap_state = L2CAP_WAIT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BTHID::Run() {
|
||||||
|
switch (l2cap_state) {
|
||||||
|
case L2CAP_WAIT:
|
||||||
|
if (pBtd->connectToHIDDevice && !pBtd->l2capConnectionClaimed && !connected && !activeConnection) {
|
||||||
|
pBtd->l2capConnectionClaimed = true;
|
||||||
|
activeConnection = true;
|
||||||
|
#ifdef DEBUG_USB_HOST
|
||||||
|
Notify(PSTR("\r\nSend HID Control Connection Request"), 0x80);
|
||||||
|
#endif
|
||||||
|
hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection
|
||||||
|
l2cap_event_flag = 0; // Reset flags
|
||||||
|
identifier = 0;
|
||||||
|
pBtd->l2cap_connection_request(hci_handle, identifier, control_dcid, HID_CTRL_PSM);
|
||||||
|
l2cap_state = L2CAP_CONTROL_CONNECT_REQUEST;
|
||||||
|
} else if (l2cap_connection_request_control_flag) {
|
||||||
|
#ifdef DEBUG_USB_HOST
|
||||||
|
Notify(PSTR("\r\nHID Control Incoming Connection Request"), 0x80);
|
||||||
|
#endif
|
||||||
|
pBtd->l2cap_connection_response(hci_handle, identifier, control_dcid, control_scid, PENDING);
|
||||||
|
delay(1);
|
||||||
|
pBtd->l2cap_connection_response(hci_handle, identifier, control_dcid, control_scid, SUCCESSFUL);
|
||||||
|
identifier++;
|
||||||
|
delay(1);
|
||||||
|
pBtd->l2cap_config_request(hci_handle, identifier, control_scid);
|
||||||
|
l2cap_state = L2CAP_CONTROL_SUCCESS;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************/
|
||||||
|
/* HID Commands */
|
||||||
|
/************************************************************/
|
||||||
|
void BTHID::setProtocol() {
|
||||||
|
uint8_t command = 0x71; // Set Protocol to "Report Protocol Mode", see HID specs page 33
|
||||||
|
pBtd->L2CAP_Command(hci_handle, &command, 1, control_scid[0], control_scid[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************/
|
||||||
|
/* BT HID Commands */
|
||||||
|
/************************************************************/
|
||||||
|
|
||||||
|
bool BTHID::getButtonPress(Button b) { // Return true when a button is pressed
|
||||||
|
return (bool)((ButtonState & pgm_read_byte(&BUTTONS[(uint8_t)b])));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BTHID::getButtonClick(Button b) { // Only return true when a button is clicked
|
||||||
|
uint8_t button = pgm_read_byte(&BUTTONS[(uint8_t)b]);
|
||||||
|
bool click = (ButtonClickState & button);
|
||||||
|
ButtonClickState &= ~button; // Clear "click" event
|
||||||
|
return click;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BTHID::onInit() {
|
||||||
|
if (pFuncOnInit)
|
||||||
|
pFuncOnInit(); // Call the user function
|
||||||
|
else {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
}
|
162
BTHID.h
Normal file
162
BTHID.h
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
/* Copyright (C) 2013 Kristian Lauszus, TKJ Electronics. 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 Lauszus, TKJ Electronics
|
||||||
|
Web : http://www.tkjelectronics.com
|
||||||
|
e-mail : kristianl@tkjelectronics.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _bthid_h_
|
||||||
|
#define _bthid_h_
|
||||||
|
|
||||||
|
#include "BTD.h"
|
||||||
|
#include "controllerEnums.h"
|
||||||
|
|
||||||
|
/* Bluetooth L2CAP states for L2CAP_task() */
|
||||||
|
#define L2CAP_WAIT 0
|
||||||
|
|
||||||
|
// These states are used if the device is the host
|
||||||
|
#define L2CAP_CONTROL_SUCCESS 1
|
||||||
|
#define L2CAP_INTERRUPT_SETUP 2
|
||||||
|
|
||||||
|
// These states are used if the Arduino is the host
|
||||||
|
#define L2CAP_CONTROL_CONNECT_REQUEST 3
|
||||||
|
#define L2CAP_CONTROL_CONFIG_REQUEST 4
|
||||||
|
#define L2CAP_INTERRUPT_CONNECT_REQUEST 5
|
||||||
|
|
||||||
|
#define L2CAP_INTERRUPT_CONFIG_REQUEST 6
|
||||||
|
#define L2CAP_DONE 7
|
||||||
|
|
||||||
|
#define L2CAP_INTERRUPT_DISCONNECT 8
|
||||||
|
#define L2CAP_CONTROL_DISCONNECT 9
|
||||||
|
|
||||||
|
/* L2CAP event flags */
|
||||||
|
#define L2CAP_FLAG_CONTROL_CONNECTED 0x01
|
||||||
|
#define L2CAP_FLAG_INTERRUPT_CONNECTED 0x02
|
||||||
|
#define L2CAP_FLAG_CONFIG_CONTROL_SUCCESS 0x04
|
||||||
|
#define L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS 0x08
|
||||||
|
#define L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE 0x10
|
||||||
|
#define L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE 0x20
|
||||||
|
#define L2CAP_FLAG_CONNECTION_CONTROL_REQUEST 0x40
|
||||||
|
#define L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST 0x80
|
||||||
|
|
||||||
|
/* Macros for L2CAP event flag tests */
|
||||||
|
#define l2cap_connected_control_flag (l2cap_event_flag & L2CAP_FLAG_CONTROL_CONNECTED)
|
||||||
|
#define l2cap_connected_interrupt_flag (l2cap_event_flag & L2CAP_FLAG_INTERRUPT_CONNECTED)
|
||||||
|
#define l2cap_config_success_control_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_CONTROL_SUCCESS)
|
||||||
|
#define l2cap_config_success_interrupt_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS)
|
||||||
|
#define l2cap_disconnect_response_control_flag (l2cap_event_flag & L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE)
|
||||||
|
#define l2cap_disconnect_response_interrupt_flag (l2cap_event_flag & L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE)
|
||||||
|
#define l2cap_connection_request_control_flag (l2cap_event_flag & L2CAP_FLAG_CONNECTION_CONTROL_REQUEST)
|
||||||
|
#define l2cap_connection_request_interrupt_flag (l2cap_event_flag & L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST)
|
||||||
|
|
||||||
|
/** This BluetoothService class implements support for the HID keyboard and mice. */
|
||||||
|
class BTHID : public BluetoothService {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor for the BTHID 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.
|
||||||
|
* @param pin Write the pin to BTD#btdPin. If argument is omitted, then "1234" will be used.
|
||||||
|
*/
|
||||||
|
BTHID(BTD *p, bool pair = false, const char *pin = "1234");
|
||||||
|
|
||||||
|
/** @name BluetoothService implementation */
|
||||||
|
/**
|
||||||
|
* Used to pass acldata to the services.
|
||||||
|
* @param ACLData Incoming acldata.
|
||||||
|
*/
|
||||||
|
virtual void ACLData(uint8_t* ACLData);
|
||||||
|
/** Used to run part of the state maschine. */
|
||||||
|
virtual void Run();
|
||||||
|
/** Use this to reset the service. */
|
||||||
|
virtual void Reset();
|
||||||
|
/** Used this to disconnect any of the controllers. */
|
||||||
|
virtual void disconnect();
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/** True if a device is connected */
|
||||||
|
bool connected;
|
||||||
|
|
||||||
|
/** @name HID mouse functions */
|
||||||
|
/**
|
||||||
|
* getButtonPress(Button b) will return true as long as the button is held down.
|
||||||
|
*
|
||||||
|
* While getButtonClick(Button b) will only return it once.
|
||||||
|
*
|
||||||
|
* So you instance if you need to increase a variable once you would use getButtonClick(Button b),
|
||||||
|
* but if you need to drive a robot forward you would use getButtonPress(Button b).
|
||||||
|
*/
|
||||||
|
bool getButtonPress(Button b);
|
||||||
|
bool getButtonClick(Button b);
|
||||||
|
/**@}*/
|
||||||
|
/** @name HID mouse functions */
|
||||||
|
/*int16_t getXaxis() {
|
||||||
|
return xAxis;
|
||||||
|
}
|
||||||
|
int16_t getYaxis() {
|
||||||
|
return yAxis;
|
||||||
|
}
|
||||||
|
int16_t getScroll() {
|
||||||
|
return scroll;
|
||||||
|
}*/
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/** Call this to start the paring sequence with a controller */
|
||||||
|
void pair(void) {
|
||||||
|
if (pBtd)
|
||||||
|
pBtd->pairWithHID();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to call your own function when the controller is successfully initialized.
|
||||||
|
* @param funcOnInit Function to call.
|
||||||
|
*/
|
||||||
|
void attachOnInit(void (*funcOnInit)(void)) {
|
||||||
|
pFuncOnInit = funcOnInit;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
BTD *pBtd; // Pointer to BTD instance
|
||||||
|
|
||||||
|
/** Set report protocol. */
|
||||||
|
void setProtocol();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the controller 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.
|
||||||
|
*/
|
||||||
|
void onInit();
|
||||||
|
void (*pFuncOnInit)(void); // Pointer to function called in onInit()
|
||||||
|
|
||||||
|
void L2CAP_task(); // L2CAP state machine
|
||||||
|
|
||||||
|
/* Variables filled from HCI event management */
|
||||||
|
uint16_t hci_handle;
|
||||||
|
bool activeConnection; // Used to indicate if it's already has established a connection
|
||||||
|
|
||||||
|
/* Variables used by high level L2CAP task */
|
||||||
|
uint8_t l2cap_state;
|
||||||
|
uint8_t l2cap_event_flag; // l2cap flags of received Bluetooth events
|
||||||
|
|
||||||
|
uint8_t ButtonState, OldButtonState, ButtonClickState;
|
||||||
|
int16_t xAxis, yAxis, scroll;
|
||||||
|
|
||||||
|
/* L2CAP Channels */
|
||||||
|
uint8_t control_scid[2]; // L2CAP source CID for HID_Control
|
||||||
|
uint8_t control_dcid[2]; // 0x0070
|
||||||
|
uint8_t interrupt_scid[2]; // L2CAP source CID for HID_Interrupt
|
||||||
|
uint8_t interrupt_dcid[2]; // 0x0071
|
||||||
|
uint8_t identifier; // Identifier for connection
|
||||||
|
};
|
||||||
|
#endif
|
2
Wii.h
2
Wii.h
|
@ -76,8 +76,6 @@
|
||||||
#define motion_plus_connected_flag (l2cap_event_flag & WII_FLAG_MOTION_PLUS_CONNECTED)
|
#define motion_plus_connected_flag (l2cap_event_flag & WII_FLAG_MOTION_PLUS_CONNECTED)
|
||||||
#define nunchuck_connected_flag (l2cap_event_flag & WII_FLAG_NUNCHUCK_CONNECTED)
|
#define nunchuck_connected_flag (l2cap_event_flag & WII_FLAG_NUNCHUCK_CONNECTED)
|
||||||
|
|
||||||
#define PAIR 1
|
|
||||||
|
|
||||||
/** Enum used to read the joystick on the Nunchuck. */
|
/** Enum used to read the joystick on the Nunchuck. */
|
||||||
enum Hat {
|
enum Hat {
|
||||||
/** Read the x-axis on the Nunchuck joystick. */
|
/** Read the x-axis on the Nunchuck joystick. */
|
||||||
|
|
|
@ -103,6 +103,8 @@ enum Button {
|
||||||
BLACK = 8, // Available on the original Xbox controller
|
BLACK = 8, // Available on the original Xbox controller
|
||||||
WHITE = 9, // Available on the original Xbox controller
|
WHITE = 9, // Available on the original Xbox controller
|
||||||
/**@}*/
|
/**@}*/
|
||||||
|
|
||||||
|
MIDDLE = 0, // Middle mouse button
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Joysticks on the PS3 and Xbox controllers. */
|
/** Joysticks on the PS3 and Xbox controllers. */
|
||||||
|
|
37
examples/Bluetooth/BTHID/BTHID.ino
Normal file
37
examples/Bluetooth/BTHID/BTHID.ino
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
Example sketch for the HID Bluetooth library - developed by Kristian Lauszus
|
||||||
|
For more information visit my blog: http://blog.tkjelectronics.dk/ or
|
||||||
|
send me an e-mail: kristianl@tkjelectronics.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <BTHID.h>
|
||||||
|
#include <usbhub.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 class in two ways */
|
||||||
|
BTHID hid(&Btd, PAIR, "0000"); // This will start an inquiry and then pair with your Wiimote - you only have to do this once
|
||||||
|
//BTHID hid(&Btd); // After that you can simply create the instance like so and then press any button on the Wiimote
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection
|
||||||
|
if (Usb.Init() == -1) {
|
||||||
|
Serial.print(F("\r\nOSC did not start"));
|
||||||
|
while (1); //halt
|
||||||
|
}
|
||||||
|
Serial.print(F("\r\nHID Bluetooth Library Started"));
|
||||||
|
}
|
||||||
|
void loop() {
|
||||||
|
Usb.Task();
|
||||||
|
if (hid.connected) {
|
||||||
|
if (hid.getButtonClick(LEFT)) // Print mouse buttons
|
||||||
|
Serial.println(F("Left"));
|
||||||
|
if (hid.getButtonClick(RIGHT))
|
||||||
|
Serial.println(F("Right"));
|
||||||
|
if (hid.getButtonClick(MIDDLE))
|
||||||
|
Serial.println(F("Middle"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -97,6 +97,7 @@ L3 LITERAL1
|
||||||
R3 LITERAL1
|
R3 LITERAL1
|
||||||
START LITERAL1
|
START LITERAL1
|
||||||
UP LITERAL1
|
UP LITERAL1
|
||||||
|
MIDDLE LITERAL1
|
||||||
RIGHT LITERAL1
|
RIGHT LITERAL1
|
||||||
DOWN LITERAL1
|
DOWN LITERAL1
|
||||||
LEFT LITERAL1
|
LEFT LITERAL1
|
||||||
|
|
Loading…
Reference in a new issue