diff --git a/BTD.cpp b/BTD.cpp index 216ffcc4..0495689c 100755 --- a/BTD.cpp +++ b/BTD.cpp @@ -27,6 +27,8 @@ const uint8_t BTD::BTD_DATAOUT_PIPE = 3; BTD::BTD(USB *p) : connectToWii(false), pairWithWii(false), +connectToHIDDevice(false), +pairWithHIDDevice(false), pUsb(p), // Pointer to USB class instance - mandatory bAddress(0), // Device address - mandatory 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++) 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 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; 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 #ifdef EXTRADEBUG @@ -289,7 +291,7 @@ Fail: return rcode; } -void BTD::clearAllVariables() { +void BTD::Initialize() { uint8_t i; for (i = 0; i < BTD_MAX_ENDPOINTS; i++) { epInfo[i].epAddr = 0; @@ -304,6 +306,8 @@ void BTD::clearAllVariables() { connectToWii = false; incomingWii = false; + connectToHIDDevice = false; + incomingHIDDevice = false; bAddress = 0; // Clear device address bNumEP = 1; // Must have to be reset to 1 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 */ 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); return 0; } @@ -412,13 +416,18 @@ void BTD::HCI_event_task() { break; case EV_INQUIRY_COMPLETE: - if (inquiry_counter >= 5 && pairWithWii) { + if (inquiry_counter >= 5 && (pairWithWii || pairWithHIDDevice)) { inquiry_counter = 0; #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 connectToWii = false; pairWithWii = false; + connectToHIDDevice = false; + pairWithHIDDevice = false; hci_state = HCI_SCANNING_STATE; } inquiry_counter++; @@ -431,28 +440,44 @@ void BTD::HCI_event_task() { Notify(hcibuf[2], 0x80); #endif 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 - 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 offset = 8 * hcibuf[2] + 3 * i; + 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; else motionPlusInside = false; - disc_bdaddr[0] = hcibuf[3 + 6 * i]; - disc_bdaddr[1] = hcibuf[4 + 6 * i]; - disc_bdaddr[2] = hcibuf[5 + 6 * i]; - disc_bdaddr[3] = hcibuf[6 + 6 * i]; - disc_bdaddr[4] = hcibuf[7 + 6 * i]; - disc_bdaddr[5] = hcibuf[8 + 6 * i]; - hci_event_flag |= HCI_FLAG_WII_FOUND; + + for (uint8_t j = 0; j < 6; j++) + disc_bdaddr[j] = hcibuf[j + 3 + 6 * i]; + + hci_event_flag |= HCI_FLAG_DEVICE_FOUND; 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 else { Notify(PSTR("\r\nClass of device: "), 0x80); - D_PrintHex (hcibuf[6 + 8 * hcibuf[2] + 3 * i], 0x80); + D_PrintHex (classOfDevice[2], 0x80); Notify(PSTR(" "), 0x80); - D_PrintHex (hcibuf[5 + 8 * hcibuf[2] + 3 * i], 0x80); + D_PrintHex (classOfDevice[1], 0x80); Notify(PSTR(" "), 0x80); - D_PrintHex (hcibuf[4 + 8 * hcibuf[2] + 3 * i], 0x80); + D_PrintHex (classOfDevice[0], 0x80); } #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_event_flag |= HCI_FLAG_CONN_COMPLETE; // set connection complete flag } else { - hci_state = HCI_CHECK_WII_SERVICE; + hci_state = HCI_CHECK_DEVICE_SERVICE; #ifdef DEBUG_USB_HOST Notify(PSTR("\r\nConnection Failed: "), 0x80); D_PrintHex (hcibuf[2], 0x80); @@ -492,12 +517,19 @@ void BTD::HCI_event_task() { break; case EV_INCOMING_CONNECT: - disc_bdaddr[0] = hcibuf[2]; - disc_bdaddr[1] = hcibuf[3]; - disc_bdaddr[2] = hcibuf[4]; - disc_bdaddr[3] = hcibuf[5]; - disc_bdaddr[4] = hcibuf[6]; - disc_bdaddr[5] = hcibuf[7]; + for (uint8_t i = 0; i < 6; i++) + disc_bdaddr[i] = hcibuf[i + 2]; + + if ((hcibuf[9] & 0x05) && (hcibuf[8] & 0xC0)) { // Check if it is a mouse or keyboard +#ifdef DEBUG_USB_HOST + 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 Notify(PSTR("\r\nClass of device: "), 0x80); D_PrintHex (hcibuf[10], 0x80); @@ -539,9 +571,14 @@ void BTD::HCI_event_task() { case EV_AUTHENTICATION_COMPLETE: if (pairWithWii && !connectToWii) { #ifdef DEBUG_USB_HOST - Notify(PSTR("\r\nPairing successful"), 0x80); + Notify(PSTR("\r\nPairing successful with Wiimote"), 0x80); #endif 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; /* We will just ignore the following events */ @@ -640,7 +677,7 @@ void BTD::HCI_task() { hci_set_local_name(btdName); hci_state = HCI_SET_NAME_STATE; } else - hci_state = HCI_CHECK_WII_SERVICE; + hci_state = HCI_CHECK_DEVICE_SERVICE; } break; @@ -650,14 +687,17 @@ void BTD::HCI_task() { Notify(PSTR("\r\nThe name is set to: "), 0x80); NotifyStr(btdName, 0x80); #endif - hci_state = HCI_CHECK_WII_SERVICE; + hci_state = HCI_CHECK_DEVICE_SERVICE; } break; - case HCI_CHECK_WII_SERVICE: - if (pairWithWii) { // Check if it should try to connect to a wiimote + case HCI_CHECK_DEVICE_SERVICE: + if (pairWithHIDDevice || pairWithWii) { // Check if it should try to connect to a wiimote #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 hci_inquiry(); hci_state = HCI_INQUIRY_STATE; @@ -666,37 +706,55 @@ void BTD::HCI_task() { break; case HCI_INQUIRY_STATE: - if (hci_wii_found) { + if (hci_device_found) { hci_inquiry_cancel(); // Stop inquiry #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\nWII Wii(&Btd);"), 0x80); - Notify(PSTR("\r\nAnd then press any button on the Wiimote"), 0x80); + if (pairWithWii) + 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 if (motionPlusInside) { 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; } else - hci_state = HCI_CONNECT_WII_STATE; + hci_state = HCI_CONNECT_DEVICE_STATE; } break; - case HCI_CONNECT_WII_STATE: + case HCI_CONNECT_DEVICE_STATE: if (hci_cmd_complete) { #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 hci_connect(); - hci_state = HCI_CONNECTED_WII_STATE; + hci_state = HCI_CONNECTED_DEVICE_STATE; } break; - case HCI_CONNECTED_WII_STATE: + case HCI_CONNECTED_DEVICE_STATE: if (hci_connect_event) { if (hci_connect_complete) { #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 hci_authentication_request(); // This will start the pairing with the wiimote hci_state = HCI_SCANNING_STATE; @@ -710,7 +768,7 @@ void BTD::HCI_task() { break; case HCI_SCANNING_STATE: - if (!connectToWii && !pairWithWii) { + if (!connectToWii && !pairWithWii && !connectToHIDDevice && !pairWithHIDDevice) { #ifdef DEBUG_USB_HOST Notify(PSTR("\r\nWait For Incoming Connection Request"), 0x80); #endif @@ -764,7 +822,7 @@ void BTD::HCI_task() { } } if (pairWithWii && motionPlusInside) - hci_state = HCI_CONNECT_WII_STATE; + hci_state = HCI_CONNECT_DEVICE_STATE; else { hci_accept_connection(); hci_state = HCI_CONNECTED_STATE; @@ -817,6 +875,10 @@ void BTD::HCI_task() { incomingWii = false; pairWithWii = false; + connectToHIDDevice = false; + incomingHIDDevice = false; + pairWithHIDDevice = false; + hci_state = HCI_SCANNING_STATE; } break; @@ -948,7 +1010,7 @@ void BTD::hci_set_local_name(const char* name) { } void BTD::hci_inquiry() { - hci_event_flag &= ~HCI_FLAG_WII_FOUND; + hci_event_flag &= ~HCI_FLAG_DEVICE_FOUND; hcibuf[0] = 0x01; hcibuf[1] = 0x01 << 2; // HCI OGF = 1 hcibuf[2] = 0x05; // Parameter Total Length = 5 diff --git a/BTD.h b/BTD.h index 27b3794e..2bf5e7ba 100755 --- a/BTD.h +++ b/BTD.h @@ -39,25 +39,25 @@ #define HID_REQUEST_SET_REPORT 0x09 /* Bluetooth HCI states for hci_task() */ -#define HCI_INIT_STATE 0 -#define HCI_RESET_STATE 1 -#define HCI_CLASS_STATE 2 -#define HCI_BDADDR_STATE 3 -#define HCI_LOCAL_VERSION_STATE 4 -#define HCI_SET_NAME_STATE 5 -#define HCI_CHECK_WII_SERVICE 6 +#define HCI_INIT_STATE 0 +#define HCI_RESET_STATE 1 +#define HCI_CLASS_STATE 2 +#define HCI_BDADDR_STATE 3 +#define HCI_LOCAL_VERSION_STATE 4 +#define HCI_SET_NAME_STATE 5 +#define HCI_CHECK_DEVICE_SERVICE 6 -#define HCI_INQUIRY_STATE 7 // These three states are only used if it should pair and connect to a Wii controller -#define HCI_CONNECT_WII_STATE 8 -#define HCI_CONNECTED_WII_STATE 9 +#define HCI_INQUIRY_STATE 7 // These three states are only used if it should pair and connect to a Wii controller +#define HCI_CONNECT_DEVICE_STATE 8 +#define HCI_CONNECTED_DEVICE_STATE 9 -#define HCI_SCANNING_STATE 10 -#define HCI_CONNECT_IN_STATE 11 -#define HCI_REMOTE_NAME_STATE 12 -#define HCI_CONNECTED_STATE 13 -#define HCI_DISABLE_SCAN_STATE 14 -#define HCI_DONE_STATE 15 -#define HCI_DISCONNECT_STATE 16 +#define HCI_SCANNING_STATE 10 +#define HCI_CONNECT_IN_STATE 11 +#define HCI_REMOTE_NAME_STATE 12 +#define HCI_CONNECTED_STATE 13 +#define HCI_DISABLE_SCAN_STATE 14 +#define HCI_DONE_STATE 15 +#define HCI_DISCONNECT_STATE 16 /* HCI event flags*/ #define HCI_FLAG_CMD_COMPLETE 0x01 @@ -67,7 +67,7 @@ #define HCI_FLAG_INCOMING_REQUEST 0x10 #define HCI_FLAG_READ_BDADDR 0x20 #define HCI_FLAG_READ_VERSION 0x40 -#define HCI_FLAG_WII_FOUND 0x80 +#define HCI_FLAG_DEVICE_FOUND 0x80 #define HCI_FLAG_CONNECT_EVENT 0x100 /*Macros for HCI event flag tests */ @@ -78,7 +78,7 @@ #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_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) /* HCI Events managed */ @@ -133,6 +133,8 @@ #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 PAIR 1 + /** All Bluetooth services should include this class. */ class BluetoothService { public: @@ -422,19 +424,31 @@ public: /** Call this function to pair with a Wiimote */ void pairWithWiimote() { pairWithWii = true; - hci_state = HCI_CHECK_WII_SERVICE; + hci_state = HCI_CHECK_DEVICE_SERVICE; }; /** Used to only send the ACL data to the wiimote. */ bool connectToWii; /** True if a Wiimote is connecting. */ bool incomingWii; - /** True when it should pair with the incoming Wiimote. */ + /** True when it should pair with a Wiimote. */ bool pairWithWii; /** True if it's the new Wiimote with the Motion Plus Inside or a Wii U Pro Controller. */ bool motionPlusInside; /** True if it's a Wii U Pro Controller. */ 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. * @return The poll interval in ms. @@ -474,7 +488,7 @@ protected: void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr); 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]; uint16_t PID, VID; // PID and VID of device connected diff --git a/BTHID.cpp b/BTHID.cpp new file mode 100644 index 00000000..3e626b18 --- /dev/null +++ b/BTHID.cpp @@ -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 (l2capinbuf[13], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[12], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[17], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[16], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[15], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (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 (l2capinbuf[13], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[12], 0x80); + Notify(PSTR(" SCID: "), 0x80); + D_PrintHex (l2capinbuf[15], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (l2capinbuf[14], 0x80); + Notify(PSTR(" Identifier: "), 0x80); + D_PrintHex (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 (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 (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 (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 (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 (l2capinbuf[11], 0x80); +#endif + break; +#ifdef DEBUG_USB_HOST + default: + Notify(PSTR("\r\nUnknown Report type: "), 0x80); + D_PrintHex (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 (l2capinbuf[i + 8], 0x80); + Notify(PSTR(" "), 0x80); + } +#endif + } +#ifdef EXTRADEBUG + else { + Notify(PSTR("\r\nUnsupported L2CAP Data - Channel ID: "), 0x80); + D_PrintHex (l2capinbuf[7], 0x80); + Notify(PSTR(" "), 0x80); + D_PrintHex (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 (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 (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]); +} \ No newline at end of file diff --git a/BTHID.h b/BTHID.h new file mode 100644 index 00000000..7eae9494 --- /dev/null +++ b/BTHID.h @@ -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 \ No newline at end of file diff --git a/PS3USB.h b/PS3USB.h index ae5314f4..82559acd 100644 --- a/PS3USB.h +++ b/PS3USB.h @@ -140,7 +140,7 @@ public: void getMoveBdaddr(uint8_t *bdaddr); /** * 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); diff --git a/README.md b/README.md index 361caf1a..59c98caf 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ For more information about the hardware see the [Hardware Manual](http://www.cir * __Alexei Glushchenko, Circuits@Home__ - * Developers of the USB Core, HID, FTDI, ADK, ACM, and PL2303 libraries * __Kristian Lauszus, TKJ Electronics__ - - * 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__ - * 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.) * 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: . You should then add ```#include ``` to your .ino file. * Balanduino * Sanguino * 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). The BTD library will also make it possible to use multiple services at once, the following example sketch is an example of this: - +. + +### [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: for more information. ### [SPP library](SPP.cpp) diff --git a/SPP.cpp b/SPP.cpp index 24911da9..0c133830 100644 --- a/SPP.cpp +++ b/SPP.cpp @@ -801,7 +801,7 @@ int SPP::available(void) { return rfcommAvailable; }; -void SPP::flush(void) { +void SPP::discard(void) { rfcommAvailable = 0; } diff --git a/SPP.h b/SPP.h index 558f16bc..81b83829 100644 --- a/SPP.h +++ b/SPP.h @@ -89,16 +89,19 @@ #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 { public: /** * Constructor for the SPP class. * @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 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. @@ -130,8 +133,10 @@ public: * @return Return the number of bytes ready to be read. */ virtual int available(void); - /** Discard all the bytes in the buffer. */ - virtual void flush(void); + /** Send out all bytes in the buffer. */ + virtual void flush(void) { + send(); + }; /** * 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. @@ -157,6 +162,8 @@ public: virtual size_t write(const uint8_t* data, size_t size); /** Pull in write(const char *str) from Print */ using Print::write; + /** Discard all the bytes in the buffer. */ + void discard(void); /** * This will send all the bytes in the buffer. * This is called whenever Usb.Task() is called, diff --git a/Wii.cpp b/Wii.cpp index 2e8669db..d55d4e79 100755 --- a/Wii.cpp +++ b/Wii.cpp @@ -163,7 +163,7 @@ void WII::ACLData(uint8_t* l2capinbuf) { #endif } else if (l2capinbuf[8] == L2CAP_CMD_CONNECTION_RESPONSE) { if (((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) && ((l2capinbuf[18] | (l2capinbuf[19] << 8)) == SUCCESSFUL)) { // Success - if (l2capinbuf[14] == control_dcid[0] && l2capinbuf[15] == control_dcid[1]) { // 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]; diff --git a/Wii.h b/Wii.h index f8d206ef..397b837f 100755 --- a/Wii.h +++ b/Wii.h @@ -76,8 +76,6 @@ #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 PAIR 1 - /** Enum used to read the joystick on the Nunchuck. */ enum Hat { /** Read the x-axis on the Nunchuck joystick. */ diff --git a/examples/Bluetooth/BTHID/BTHID.ino b/examples/Bluetooth/BTHID/BTHID.ino new file mode 100644 index 00000000..fecc4b50 --- /dev/null +++ b/examples/Bluetooth/BTHID/BTHID.ino @@ -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 +#include +#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(); +} diff --git a/examples/Bluetooth/BTHID/KeyboardParser.h b/examples/Bluetooth/BTHID/KeyboardParser.h new file mode 100644 index 00000000..b7ab08e9 --- /dev/null +++ b/examples/Bluetooth/BTHID/KeyboardParser.h @@ -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 (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(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 diff --git a/examples/Bluetooth/BTHID/MouseParser.h b/examples/Bluetooth/BTHID/MouseParser.h new file mode 100644 index 00000000..a9245ded --- /dev/null +++ b/examples/Bluetooth/BTHID/MouseParser.h @@ -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 diff --git a/examples/Bluetooth/PS3SPP/PS3SPP.ino b/examples/Bluetooth/PS3SPP/PS3SPP.ino index 5c0a0d5a..ed346d90 100644 --- a/examples/Bluetooth/PS3SPP/PS3SPP.ino +++ b/examples/Bluetooth/PS3SPP/PS3SPP.ino @@ -23,7 +23,7 @@ USB Usb; 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 */ -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 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 diff --git a/examples/Bluetooth/SPPMulti/SPPMulti.ino b/examples/Bluetooth/SPPMulti/SPPMulti.ino index 23bb6ed6..262a2927 100644 --- a/examples/Bluetooth/SPPMulti/SPPMulti.ino +++ b/examples/Bluetooth/SPPMulti/SPPMulti.ino @@ -22,7 +22,7 @@ uint8_t buffer[50]; void setup() { 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); while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection diff --git a/hidboot.h b/hidboot.h index 30d00ae8..57f6862e 100644 --- a/hidboot.h +++ b/hidboot.h @@ -495,10 +495,14 @@ uint8_t HIDBoot::Poll() { if(pRptParser[i]) pRptParser[i]->Parse((HID*)this, 0, (uint8_t) read, buf); - //for (uint8_t i=0; i(buf[i]); - //if (read) - // USB_HOST_SERIAL.println(""); +#if 0 // Set this to 1 to print the incoming data + for (uint8_t i=0; i < read; i++) { + PrintHex (buf[i], 0x80); + USB_HOST_SERIAL.write(' '); + } + if (read) + USB_HOST_SERIAL.println(); +#endif } else { if(rcode != hrNAK) { USBTRACE2("Poll:", rcode); diff --git a/keywords.txt b/keywords.txt index 6847bb09..c4f5bd87 100644 --- a/keywords.txt +++ b/keywords.txt @@ -19,6 +19,11 @@ USBHub KEYWORD1 BTD KEYWORD1 +#################################################### +# Methods and Functions (KEYWORD2) +#################################################### +Task KEYWORD2 + #################################################### # Syntax Coloring Map For PS3 Bluetooth/USB Library #################################################### @@ -223,8 +228,7 @@ SPP KEYWORD1 #################################################### connected KEYWORD2 -printNumber KEYWORD2 -printNumberln KEYWORD2 +discard KEYWORD2 #################################################### # Syntax Coloring Map For Wiimote Library @@ -292,4 +296,20 @@ getIRy3 KEYWORD2 getIRs3 KEYWORD2 getIRx4 KEYWORD2 getIRy4 KEYWORD2 -getIRs4 KEYWORD2 \ No newline at end of file +getIRs4 KEYWORD2 + +#################################################### +# Syntax Coloring Map For RFCOMM/SPP Library +#################################################### + +#################################################### +# Datatypes (KEYWORD1) +#################################################### + +BTHID KEYWORD1 + +#################################################### +# Methods and Functions (KEYWORD2) +#################################################### +SetReportParser KEYWORD2 +setProtocolMode KEYWORD2 \ No newline at end of file