mirror of
https://github.com/felis/USB_Host_Shield_2.0.git
synced 2024-03-22 11:31:26 +01:00
Merge branch 'master' into xxxajk
This commit is contained in:
commit
75637bab31
17 changed files with 964 additions and 87 deletions
150
BTD.cpp
150
BTD.cpp
|
@ -27,6 +27,8 @@ const uint8_t BTD::BTD_DATAOUT_PIPE = 3;
|
||||||
BTD::BTD(USB *p) :
|
BTD::BTD(USB *p) :
|
||||||
connectToWii(false),
|
connectToWii(false),
|
||||||
pairWithWii(false),
|
pairWithWii(false),
|
||||||
|
connectToHIDDevice(false),
|
||||||
|
pairWithHIDDevice(false),
|
||||||
pUsb(p), // Pointer to USB class instance - mandatory
|
pUsb(p), // Pointer to USB class instance - mandatory
|
||||||
bAddress(0), // Device address - mandatory
|
bAddress(0), // Device address - mandatory
|
||||||
bNumEP(1), // If config descriptor needs to be parsed
|
bNumEP(1), // If config descriptor needs to be parsed
|
||||||
|
@ -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
58
BTD.h
|
@ -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
404
BTHID.cpp
Normal 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
161
BTHID.h
Normal 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
|
2
PS3USB.h
2
PS3USB.h
|
@ -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);
|
||||||
|
|
||||||
|
|
13
README.md
13
README.md
|
@ -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)
|
||||||
|
|
||||||
|
|
2
SPP.cpp
2
SPP.cpp
|
@ -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
17
SPP.h
|
@ -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,
|
||||||
|
|
2
Wii.cpp
2
Wii.cpp
|
@ -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
2
Wii.h
|
@ -76,8 +76,6 @@
|
||||||
#define motion_plus_connected_flag (l2cap_event_flag & WII_FLAG_MOTION_PLUS_CONNECTED)
|
#define motion_plus_connected_flag (l2cap_event_flag & WII_FLAG_MOTION_PLUS_CONNECTED)
|
||||||
#define nunchuck_connected_flag (l2cap_event_flag & WII_FLAG_NUNCHUCK_CONNECTED)
|
#define nunchuck_connected_flag (l2cap_event_flag & WII_FLAG_NUNCHUCK_CONNECTED)
|
||||||
|
|
||||||
#define PAIR 1
|
|
||||||
|
|
||||||
/** Enum used to read the joystick on the Nunchuck. */
|
/** Enum used to read the joystick on the Nunchuck. */
|
||||||
enum Hat {
|
enum Hat {
|
||||||
/** Read the x-axis on the Nunchuck joystick. */
|
/** Read the x-axis on the Nunchuck joystick. */
|
||||||
|
|
47
examples/Bluetooth/BTHID/BTHID.ino
Normal file
47
examples/Bluetooth/BTHID/BTHID.ino
Normal 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();
|
||||||
|
}
|
105
examples/Bluetooth/BTHID/KeyboardParser.h
Normal file
105
examples/Bluetooth/BTHID/KeyboardParser.h
Normal 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
|
46
examples/Bluetooth/BTHID/MouseParser.h
Normal file
46
examples/Bluetooth/BTHID/MouseParser.h
Normal 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
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
12
hidboot.h
12
hidboot.h
|
@ -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);
|
||||||
|
|
24
keywords.txt
24
keywords.txt
|
@ -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
|
Loading…
Reference in a new issue