Merge branch 'master' into xxxajk

This commit is contained in:
Kristian Lauszus 2013-12-05 07:26:41 +01:00
commit 75637bab31
17 changed files with 964 additions and 87 deletions

150
BTD.cpp
View file

@ -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
@ -37,7 +39,7 @@ bPollEnable(false) // Don't start polling before dongle is connected
for (uint8_t i = 0; i < BTD_NUMSERVICES; i++) for (uint8_t i = 0; i < BTD_NUMSERVICES; i++)
btService[i] = NULL; btService[i] = NULL;
clearAllVariables(); // Set all variables, endpoint structs etc. to default values Initialize(); // Set all variables, endpoint structs etc. to default values
if (pUsb) // Register in USB subsystem if (pUsb) // Register in USB subsystem
pUsb->RegisterDeviceClass(this); // Set devConfig[] entry pUsb->RegisterDeviceClass(this); // Set devConfig[] entry
@ -51,7 +53,7 @@ uint8_t BTD::ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed) {
UsbDevice *p = NULL; UsbDevice *p = NULL;
EpInfo *oldep_ptr = NULL; EpInfo *oldep_ptr = NULL;
clearAllVariables(); // Set all variables, endpoint structs etc. to default values Initialize(); // Set all variables, endpoint structs etc. to default values
AddressPool &addrPool = pUsb->GetAddressPool(); // Get memory address of USB device address pool AddressPool &addrPool = pUsb->GetAddressPool(); // Get memory address of USB device address pool
#ifdef EXTRADEBUG #ifdef EXTRADEBUG
@ -289,7 +291,7 @@ Fail:
return rcode; return rcode;
} }
void BTD::clearAllVariables() { void BTD::Initialize() {
uint8_t i; uint8_t i;
for (i = 0; i < BTD_MAX_ENDPOINTS; i++) { for (i = 0; i < BTD_MAX_ENDPOINTS; i++) {
epInfo[i].epAddr = 0; epInfo[i].epAddr = 0;
@ -304,6 +306,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
@ -364,7 +368,7 @@ void BTD::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr) {
/* Performs a cleanup after failed Init() attempt */ /* Performs a cleanup after failed Init() attempt */
uint8_t BTD::Release() { uint8_t BTD::Release() {
clearAllVariables(); // Set all variables, endpoint structs etc. to default values Initialize(); // Set all variables, endpoint structs etc. to default values
pUsb->GetAddressPool().FreeAddress(bAddress); pUsb->GetAddressPool().FreeAddress(bAddress);
return 0; return 0;
} }
@ -412,13 +416,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
Notify(PSTR("\r\nCouldn't find Wiimote"), 0x80); if (pairWithWii)
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++;
@ -431,28 +440,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
} }
@ -468,7 +493,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);
@ -492,12 +517,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);
@ -539,9 +571,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 */
@ -640,7 +677,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;
@ -650,14 +687,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
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); 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);
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;
@ -666,37 +706,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
Notify(PSTR("\r\nWiimote found"), 0x80); if (pairWithWii)
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);
Notify(PSTR("\r\nWII Wii(&Btd);"), 0x80); if (pairWithWii)
Notify(PSTR("\r\nAnd then press any button on the Wiimote"), 0x80); Notify(PSTR("\r\nWII Wii(&Btd);"), 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
Notify(PSTR("\r\nConnecting to Wiimote"), 0x80); if (pairWithWii)
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
Notify(PSTR("\r\nConnected to Wiimote"), 0x80); if (pairWithWii)
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;
@ -710,7 +768,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
@ -764,7 +822,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;
@ -817,6 +875,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;
@ -948,7 +1010,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

58
BTD.h
View file

@ -39,25 +39,25 @@
#define HID_REQUEST_SET_REPORT 0x09 #define HID_REQUEST_SET_REPORT 0x09
/* Bluetooth HCI states for hci_task() */ /* Bluetooth HCI states for hci_task() */
#define HCI_INIT_STATE 0 #define HCI_INIT_STATE 0
#define HCI_RESET_STATE 1 #define HCI_RESET_STATE 1
#define HCI_CLASS_STATE 2 #define HCI_CLASS_STATE 2
#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
#define HCI_REMOTE_NAME_STATE 12 #define HCI_REMOTE_NAME_STATE 12
#define HCI_CONNECTED_STATE 13 #define HCI_CONNECTED_STATE 13
#define HCI_DISABLE_SCAN_STATE 14 #define HCI_DISABLE_SCAN_STATE 14
#define HCI_DONE_STATE 15 #define HCI_DONE_STATE 15
#define HCI_DISCONNECT_STATE 16 #define HCI_DISCONNECT_STATE 16
/* HCI event flags*/ /* HCI event flags*/
#define HCI_FLAG_CMD_COMPLETE 0x01 #define HCI_FLAG_CMD_COMPLETE 0x01
@ -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.
@ -474,7 +488,7 @@ protected:
void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr); void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr);
private: private:
void clearAllVariables(); // Set all variables, endpoint structs etc. to default values void Initialize(); // Set all variables, endpoint structs etc. to default values
BluetoothService* btService[BTD_NUMSERVICES]; BluetoothService* btService[BTD_NUMSERVICES];
uint16_t PID, VID; // PID and VID of device connected uint16_t PID, VID; // PID and VID of device connected

404
BTHID.cpp Normal file
View file

@ -0,0 +1,404 @@
/* 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 HID device
BTHID::BTHID(BTD *p, bool pair, const char *pin) :
pBtd(p), // pointer to USB class instance - mandatory
protocolMode(HID_BOOT_PROTOCOL)
{
for (uint8_t i = 0; i < epMUL; i++)
pRptParser[i] = NULL;
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]) {
//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
#ifdef PRINTREPORT
Notify(PSTR("\r\nL2CAP Interrupt: "), 0x80);
for (uint16_t i = 0; i < ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]); i++) {
D_PrintHex<uint8_t > (l2capinbuf[i + 8], 0x80);
Notify(PSTR(" "), 0x80);
}
#endif
if (l2capinbuf[8] == 0xA1) { // HID_THDR_DATA_INPUT
switch (l2capinbuf[9]) {
case 0x01: // Keyboard events
if (pRptParser[KEYBOARD_PARSER_ID]) {
uint16_t length = ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]);
pRptParser[KEYBOARD_PARSER_ID]->Parse(reinterpret_cast<HID *> (this), 0, (uint8_t) length, &l2capinbuf[10]); // Use reinterpret_cast again to extract the instance
}
break;
case 0x02: // Mouse events
if (pRptParser[MOUSE_PARSER_ID]) {
uint16_t length = ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]);
pRptParser[MOUSE_PARSER_ID]->Parse(reinterpret_cast<HID *> (this), 0, (uint8_t) length, &l2capinbuf[10]); // Use reinterpret_cast again to extract the instance
}
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
}
}
} else if (l2capinbuf[6] == control_dcid[0] && l2capinbuf[7] == control_dcid[1]) { // l2cap_control
#ifdef PRINTREPORT
Notify(PSTR("\r\nL2CAP Control: "), 0x80);
for (uint16_t i = 0; i < ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]); i++) {
D_PrintHex<uint8_t > (l2capinbuf[i + 8], 0x80);
Notify(PSTR(" "), 0x80);
}
#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);
Notify(PSTR(" "), 0x80);
}
}
#endif
L2CAP_task();
}
}
void BTHID::L2CAP_task() {
switch (l2cap_state) {
/* These states are used if the HID device 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
setProtocol(); // Set protocol before establishing HID interrupt channel
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) {
setProtocol(); // Set protocol before establishing HID interrupt channel
delay(1); // Short delay between commands - just to be sure
#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;
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() {
#ifdef DEBUG_USB_HOST
Notify(PSTR("\r\nSet protocol mode: "), 0x80);
D_PrintHex<uint8_t > (protocolMode, 0x80);
#endif
uint8_t command = 0x70 | protocolMode; // Set Protocol, see HID specs page 33
pBtd->L2CAP_Command(hci_handle, &command, 1, control_scid[0], control_scid[1]);
}
void BTHID::setLeds(uint8_t data) {
uint8_t buf[3];
buf[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02)
buf[1] = 0x01; // Report ID
buf[2] = data;
pBtd->L2CAP_Command(hci_handle, buf, 3, interrupt_scid[0], interrupt_scid[1]);
}

161
BTHID.h Normal file
View file

@ -0,0 +1,161 @@
/* 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 "hidboot.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)
#define KEYBOARD_PARSER_ID 0
#define MOUSE_PARSER_ID 1
#define epMUL 2
/** 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 "0000" will be used.
*/
BTHID(BTD *p, bool pair = false, const char *pin = "0000");
/** @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();
/**@}*/
HIDReportParser *GetReportParser(uint8_t id) {
return pRptParser[id];
};
bool SetReportParser(uint8_t id, HIDReportParser *prs) {
pRptParser[id] = prs;
return true;
};
void setProtocolMode(uint8_t mode) {
protocolMode = mode;
};
/** Used to set the leds on a keyboard */
void setLeds(uint8_t data);
/** True if a device is connected */
bool connected;
/** 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
HIDReportParser *pRptParser[epMUL];
/** Set report protocol. */
void setProtocol();
uint8_t protocolMode;
/**
* 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() {
if (pFuncOnInit)
pFuncOnInit(); // Call the user function
}
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
/* 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

View file

@ -140,7 +140,7 @@ public:
void getMoveBdaddr(uint8_t *bdaddr); void getMoveBdaddr(uint8_t *bdaddr);
/** /**
* Used to get the calibration data inside the Move controller. * Used to get the calibration data inside the Move controller.
* @param bdaddr Buffer to store data in. Must be at least 147 bytes * @param data Buffer to store data in. Must be at least 147 bytes
*/ */
void getMoveCalibration(uint8_t *data); void getMoveCalibration(uint8_t *data);

View file

@ -22,7 +22,7 @@ For more information about the hardware see the [Hardware Manual](http://www.cir
* __Alexei Glushchenko, Circuits@Home__ - <alex-gl@mail.ru> * __Alexei Glushchenko, Circuits@Home__ - <alex-gl@mail.ru>
* Developers of the USB Core, HID, FTDI, ADK, ACM, and PL2303 libraries * Developers of the USB Core, HID, FTDI, ADK, ACM, and PL2303 libraries
* __Kristian Lauszus, TKJ Electronics__ - <kristianl@tkjelectronics.com> * __Kristian Lauszus, TKJ Electronics__ - <kristianl@tkjelectronics.com>
* Developer of the [BTD](#bluetooth-libraries), [SPP](#spp-library), [PS3](#ps3-library), [Wii](#wii-library), and [Xbox](#xbox-library) libraries * Developer of the [BTD](#bluetooth-libraries), [BTHID](#bthid-library), [SPP](#spp-library), [PS3](#ps3-library), [Wii](#wii-library), and [Xbox](#xbox-library) libraries
* __Andrew Kroll__ - <xxxajk@gmail.com> * __Andrew Kroll__ - <xxxajk@gmail.com>
* Major contributor to mass storage code * Major contributor to mass storage code
@ -67,6 +67,7 @@ Currently the following boards are supported by the library:
* All official Arduino AVR boards (Uno, Duemilanove, Mega, Mega 2560, Mega ADK, Leonardo etc.) * All official Arduino AVR boards (Uno, Duemilanove, Mega, Mega 2560, Mega ADK, Leonardo etc.)
* Teensy (Teensy++ 1.0, Teensy 2.0, Teensy++ 2.0, and Teensy 3.0) * Teensy (Teensy++ 1.0, Teensy 2.0, Teensy++ 2.0, and Teensy 3.0)
* Note if you are using the Teensy 3.0 you should download this SPI library as well: <https://github.com/xxxajk/spi4teensy3>. You should then add ```#include <spi4teensy3.h>``` to your .ino file.
* Balanduino * Balanduino
* Sanguino * Sanguino
* Black Widdow * Black Widdow
@ -86,7 +87,15 @@ This library make it easy to add support for different Bluetooth services like a
Some different examples can be found in the [example directory](examples/Bluetooth). Some different examples can be found in the [example directory](examples/Bluetooth).
The BTD library will also make it possible to use multiple services at once, the following example sketch is an example of this: The BTD library will also make it possible to use multiple services at once, the following example sketch is an example of this:
<https://github.com/felis/USB_Host_Shield_2.0/blob/master/examples/Bluetooth/PS3SPP/PS3SPP.ino> <https://github.com/felis/USB_Host_Shield_2.0/blob/master/examples/Bluetooth/PS3SPP/PS3SPP.ino>.
### [BTHID library](BTHID.cpp)
The [Bluetooth HID library](BTHID.cpp) allows you to connect HID devices via Bluetooth to the USB Host Shield.
Currently HID mice and keyboards are supported.
It uses the standard Boot protocol by default, but it is also able to use the Report protocol as well. You would simply have to call ```setProtocolMode()``` and then parse ```HID_RPT_PROTOCOL``` as an argument. You will then have to modify the parser for your device. See the example: <https://github.com/felis/USB_Host_Shield_2.0/blob/master/examples/Bluetooth/BTHID/BTHID.ino> for more information.
### [SPP library](SPP.cpp) ### [SPP library](SPP.cpp)

View file

@ -801,7 +801,7 @@ int SPP::available(void) {
return rfcommAvailable; return rfcommAvailable;
}; };
void SPP::flush(void) { void SPP::discard(void) {
rfcommAvailable = 0; rfcommAvailable = 0;
} }

17
SPP.h
View file

@ -89,16 +89,19 @@
#define BT_RFCOMM_NSC_RSP 0x11 #define BT_RFCOMM_NSC_RSP 0x11
*/ */
/** This BluetoothService class implements the Serial Port Protocol (SPP). */ /**
* This BluetoothService class implements the Serial Port Protocol (SPP).
* It inherits the Arduino Stream class. This allows it to use all the standard Arduino print functions.
*/
class SPP : public BluetoothService, public Stream { class SPP : public BluetoothService, public Stream {
public: public:
/** /**
* Constructor for the SPP class. * Constructor for the SPP class.
* @param p Pointer to BTD class instance. * @param p Pointer to BTD class instance.
* @param name Set the name to BTD#btdName. If argument is omitted, then "Arduino" will be used. * @param name Set the name to BTD#btdName. If argument is omitted, then "Arduino" will be used.
* @param pin Write the pin to BTD#btdPin. If argument is omitted, then "1234" will be used. * @param pin Write the pin to BTD#btdPin. If argument is omitted, then "0000" will be used.
*/ */
SPP(BTD *p, const char* name = "Arduino", const char* pin = "1234"); SPP(BTD *p, const char *name = "Arduino", const char *pin = "0000");
/** /**
* Used to provide Boolean tests for the class. * Used to provide Boolean tests for the class.
@ -130,8 +133,10 @@ public:
* @return Return the number of bytes ready to be read. * @return Return the number of bytes ready to be read.
*/ */
virtual int available(void); virtual int available(void);
/** Discard all the bytes in the buffer. */ /** Send out all bytes in the buffer. */
virtual void flush(void); virtual void flush(void) {
send();
};
/** /**
* Used to read the next value in the buffer without advancing to the next one. * Used to read the next value in the buffer without advancing to the next one.
* @return Return the byte. Will return -1 if no bytes are available. * @return Return the byte. Will return -1 if no bytes are available.
@ -157,6 +162,8 @@ public:
virtual size_t write(const uint8_t* data, size_t size); virtual size_t write(const uint8_t* data, size_t size);
/** Pull in write(const char *str) from Print */ /** Pull in write(const char *str) from Print */
using Print::write; using Print::write;
/** Discard all the bytes in the buffer. */
void discard(void);
/** /**
* This will send all the bytes in the buffer. * This will send all the bytes in the buffer.
* This is called whenever Usb.Task() is called, * This is called whenever Usb.Task() is called,

View file

@ -163,7 +163,7 @@ void WII::ACLData(uint8_t* l2capinbuf) {
#endif #endif
} else if (l2capinbuf[8] == L2CAP_CMD_CONNECTION_RESPONSE) { } else if (l2capinbuf[8] == L2CAP_CMD_CONNECTION_RESPONSE) {
if (((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) && ((l2capinbuf[18] | (l2capinbuf[19] << 8)) == SUCCESSFUL)) { // Success 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 if (l2capinbuf[14] == control_dcid[0] && l2capinbuf[15] == control_dcid[1]) {
//Notify(PSTR("\r\nHID Control Connection Complete"), 0x80); //Notify(PSTR("\r\nHID Control Connection Complete"), 0x80);
identifier = l2capinbuf[9]; identifier = l2capinbuf[9];
control_scid[0] = l2capinbuf[12]; control_scid[0] = l2capinbuf[12];

2
Wii.h
View file

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

View file

@ -0,0 +1,47 @@
/*
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>
#include "KeyboardParser.h"
#include "MouseParser.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 */
// This will start an inquiry and then pair with your device - you only have to do this once
// If you are using a Bluetooth keyboard, then you should type in the password on the keypad and then press enter
BTHID hid(&Btd, PAIR, "0000");
// After that you can simply create the instance like so and then press any button on the device
//BTHID hid(&Btd);
KbdRptParser keyboardPrs;
MouseRptParser mousePrs;
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
}
hid.SetReportParser(KEYBOARD_PARSER_ID, (HIDReportParser*)&keyboardPrs);
hid.SetReportParser(MOUSE_PARSER_ID, (HIDReportParser*)&mousePrs);
// If "Boot Protocol Mode" does not work, then try "Report Protocol Mode"
// If that does not work either, then uncomment PRINTREPORT in BTHID.cpp to see the raw report
hid.setProtocolMode(HID_BOOT_PROTOCOL); // Boot Protocol Mode
//hid.setProtocolMode(HID_RPT_PROTOCOL); // Report Protocol Mode
Serial.print(F("\r\nHID Bluetooth Library Started"));
}
void loop() {
Usb.Task();
}

View file

@ -0,0 +1,105 @@
#ifndef __kbdrptparser_h_
#define __kbdrptparser_h_
class KbdRptParser : public KeyboardReportParser {
protected:
virtual uint8_t HandleLockingKeys(HID *hid, uint8_t key);
virtual void OnControlKeysChanged(uint8_t before, uint8_t after);
virtual void OnKeyDown(uint8_t mod, uint8_t key);
virtual void OnKeyUp(uint8_t mod, uint8_t key);
virtual void OnKeyPressed(uint8_t key);
private:
void PrintKey(uint8_t mod, uint8_t key);
};
uint8_t KbdRptParser::HandleLockingKeys(HID *hid, uint8_t key) {
uint8_t old_keys = kbdLockingKeys.bLeds;
switch (key) {
case KEY_NUM_LOCK:
Serial.println(F("Num lock"));
kbdLockingKeys.kbdLeds.bmNumLock = ~kbdLockingKeys.kbdLeds.bmNumLock;
break;
case KEY_CAPS_LOCK:
Serial.println(F("Caps lock"));
kbdLockingKeys.kbdLeds.bmCapsLock = ~kbdLockingKeys.kbdLeds.bmCapsLock;
break;
case KEY_SCROLL_LOCK:
Serial.println(F("Scroll lock"));
kbdLockingKeys.kbdLeds.bmScrollLock = ~kbdLockingKeys.kbdLeds.bmScrollLock;
break;
}
if (old_keys != kbdLockingKeys.bLeds && hid) {
BTHID *pBTHID = reinterpret_cast<BTHID *> (hid); // A cast the other way around is done in BTHID.cpp
pBTHID->setLeds(kbdLockingKeys.bLeds); // Update the LEDs on the keyboard
}
return 0;
};
void KbdRptParser::PrintKey(uint8_t m, uint8_t key) {
MODIFIERKEYS mod;
*((uint8_t*)&mod) = m;
Serial.print((mod.bmLeftCtrl == 1) ? F("C") : F(" "));
Serial.print((mod.bmLeftShift == 1) ? F("S") : F(" "));
Serial.print((mod.bmLeftAlt == 1) ? F("A") : F(" "));
Serial.print((mod.bmLeftGUI == 1) ? F("G") : F(" "));
Serial.print(F(" >"));
PrintHex<uint8_t>(key, 0x80);
Serial.print(F("< "));
Serial.print((mod.bmRightCtrl == 1) ? F("C") : F(" "));
Serial.print((mod.bmRightShift == 1) ? F("S") : F(" "));
Serial.print((mod.bmRightAlt == 1) ? F("A") : F(" "));
Serial.println((mod.bmRightGUI == 1) ? F("G") : F(" "));
};
void KbdRptParser::OnKeyDown(uint8_t mod, uint8_t key) {
Serial.print(F("DN "));
PrintKey(mod, key);
uint8_t c = OemToAscii(mod, key);
if (c)
OnKeyPressed(c);
};
void KbdRptParser::OnControlKeysChanged(uint8_t before, uint8_t after) {
MODIFIERKEYS beforeMod;
*((uint8_t*)&beforeMod) = before;
MODIFIERKEYS afterMod;
*((uint8_t*)&afterMod) = after;
if (beforeMod.bmLeftCtrl != afterMod.bmLeftCtrl)
Serial.println(F("LeftCtrl changed"));
if (beforeMod.bmLeftShift != afterMod.bmLeftShift)
Serial.println(F("LeftShift changed"));
if (beforeMod.bmLeftAlt != afterMod.bmLeftAlt)
Serial.println(F("LeftAlt changed"));
if (beforeMod.bmLeftGUI != afterMod.bmLeftGUI)
Serial.println(F("LeftGUI changed"));
if (beforeMod.bmRightCtrl != afterMod.bmRightCtrl)
Serial.println(F("RightCtrl changed"));
if (beforeMod.bmRightShift != afterMod.bmRightShift)
Serial.println(F("RightShift changed"));
if (beforeMod.bmRightAlt != afterMod.bmRightAlt)
Serial.println(F("RightAlt changed"));
if (beforeMod.bmRightGUI != afterMod.bmRightGUI)
Serial.println(F("RightGUI changed"));
};
void KbdRptParser::OnKeyUp(uint8_t mod, uint8_t key) {
Serial.print(F("UP "));
PrintKey(mod, key);
};
void KbdRptParser::OnKeyPressed(uint8_t key) {
Serial.print(F("ASCII: "));
Serial.println((char)key);
};
#endif

View file

@ -0,0 +1,46 @@
#ifndef __mouserptparser_h__
#define __mouserptparser_h__
class MouseRptParser : public MouseReportParser {
protected:
virtual void OnMouseMove(MOUSEINFO *mi);
virtual void OnLeftButtonUp(MOUSEINFO *mi);
virtual void OnLeftButtonDown(MOUSEINFO *mi);
virtual void OnRightButtonUp(MOUSEINFO *mi);
virtual void OnRightButtonDown(MOUSEINFO *mi);
virtual void OnMiddleButtonUp(MOUSEINFO *mi);
virtual void OnMiddleButtonDown(MOUSEINFO *mi);
};
void MouseRptParser::OnMouseMove(MOUSEINFO *mi) {
Serial.print(F("dx="));
Serial.print(mi->dX, DEC);
Serial.print(F(" dy="));
Serial.println(mi->dY, DEC);
};
void MouseRptParser::OnLeftButtonUp(MOUSEINFO *mi) {
Serial.println(F("L Butt Up"));
};
void MouseRptParser::OnLeftButtonDown(MOUSEINFO *mi) {
Serial.println(F("L Butt Dn"));
};
void MouseRptParser::OnRightButtonUp(MOUSEINFO *mi) {
Serial.println(F("R Butt Up"));
};
void MouseRptParser::OnRightButtonDown(MOUSEINFO *mi) {
Serial.println(F("R Butt Dn"));
};
void MouseRptParser::OnMiddleButtonUp(MOUSEINFO *mi) {
Serial.println(F("M Butt Up"));
};
void MouseRptParser::OnMiddleButtonDown(MOUSEINFO *mi) {
Serial.println(F("M Butt Dn"));
};
#endif

View file

@ -23,7 +23,7 @@ USB Usb;
BTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so BTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so
/* You can create the instances of the bluetooth services in two ways */ /* You can create the instances of the bluetooth services in two ways */
SPP SerialBT(&Btd); // This will set the name to the defaults: "Arduino" and the pin to "1234" SPP SerialBT(&Btd); // This will set the name to the defaults: "Arduino" and the pin to "0000"
//SPP SerialBTBT(&Btd,"Lauszus's Arduino","0000"); // You can also set the name and pin like so //SPP SerialBTBT(&Btd,"Lauszus's Arduino","0000"); // You can also set the name and pin like so
PS3BT PS3(&Btd); // This will just create the instance PS3BT PS3(&Btd); // This will just create the instance
//PS3BT PS3(&Btd,0x00,0x15,0x83,0x3D,0x0A,0x57); // This will also store the bluetooth address - this can be obtained from the dongle when running the sketch //PS3BT PS3(&Btd,0x00,0x15,0x83,0x3D,0x0A,0x57); // This will also store the bluetooth address - this can be obtained from the dongle when running the sketch

View file

@ -22,7 +22,7 @@ uint8_t buffer[50];
void setup() { void setup() {
for (uint8_t i = 0; i < length; i++) for (uint8_t i = 0; i < length; i++)
SerialBT[i] = new SPP(&Btd); // This will set the name to the default: "Arduino" and the pin to "1234" for all connections SerialBT[i] = new SPP(&Btd); // This will set the name to the default: "Arduino" and the pin to "0000" for all connections
Serial.begin(115200); 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 while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection

View file

@ -495,10 +495,14 @@ uint8_t HIDBoot<BOOT_PROTOCOL>::Poll() {
if(pRptParser[i]) if(pRptParser[i])
pRptParser[i]->Parse((HID*)this, 0, (uint8_t) read, buf); pRptParser[i]->Parse((HID*)this, 0, (uint8_t) read, buf);
//for (uint8_t i=0; i<read; i++) #if 0 // Set this to 1 to print the incoming data
// PrintHex<uint8_t>(buf[i]); for (uint8_t i=0; i < read; i++) {
//if (read) PrintHex<uint8_t > (buf[i], 0x80);
// USB_HOST_SERIAL.println(""); USB_HOST_SERIAL.write(' ');
}
if (read)
USB_HOST_SERIAL.println();
#endif
} else { } else {
if(rcode != hrNAK) { if(rcode != hrNAK) {
USBTRACE2("Poll:", rcode); USBTRACE2("Poll:", rcode);

View file

@ -19,6 +19,11 @@ USBHub KEYWORD1
BTD KEYWORD1 BTD KEYWORD1
####################################################
# Methods and Functions (KEYWORD2)
####################################################
Task KEYWORD2
#################################################### ####################################################
# Syntax Coloring Map For PS3 Bluetooth/USB Library # Syntax Coloring Map For PS3 Bluetooth/USB Library
#################################################### ####################################################
@ -223,8 +228,7 @@ SPP KEYWORD1
#################################################### ####################################################
connected KEYWORD2 connected KEYWORD2
printNumber KEYWORD2 discard KEYWORD2
printNumberln KEYWORD2
#################################################### ####################################################
# Syntax Coloring Map For Wiimote Library # Syntax Coloring Map For Wiimote Library
@ -293,3 +297,19 @@ getIRs3 KEYWORD2
getIRx4 KEYWORD2 getIRx4 KEYWORD2
getIRy4 KEYWORD2 getIRy4 KEYWORD2
getIRs4 KEYWORD2 getIRs4 KEYWORD2
####################################################
# Syntax Coloring Map For RFCOMM/SPP Library
####################################################
####################################################
# Datatypes (KEYWORD1)
####################################################
BTHID KEYWORD1
####################################################
# Methods and Functions (KEYWORD2)
####################################################
SetReportParser KEYWORD2
setProtocolMode KEYWORD2