mirror of
https://github.com/felis/USB_Host_Shield_2.0.git
synced 2024-03-22 11:31:26 +01:00
BTD
This commit is contained in:
commit
67bfceb240
14 changed files with 2453 additions and 3835 deletions
949
BTD.cpp
Normal file
949
BTD.cpp
Normal file
|
@ -0,0 +1,949 @@
|
|||
/* Copyright (C) 2012 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 "BTD.h"
|
||||
#define DEBUG // Uncomment to print data for debugging
|
||||
//#define EXTRADEBUG // Uncomment to get even more debugging data
|
||||
|
||||
const uint8_t BTD::BTD_EVENT_PIPE = 1;
|
||||
const uint8_t BTD::BTD_DATAIN_PIPE = 2;
|
||||
const uint8_t BTD::BTD_DATAOUT_PIPE = 3;
|
||||
|
||||
BTD::BTD(USB *p):
|
||||
pUsb(p), // Pointer to USB class instance - mandatory
|
||||
bAddress(0), // Device address - mandatory
|
||||
bNumEP(1), // If config descriptor needs to be parsed
|
||||
qNextPollTime(0), // Reset NextPollTime
|
||||
bPollEnable(false) // Don't start polling before dongle is connected
|
||||
{
|
||||
for(uint8_t i=0; i<BTD_MAX_ENDPOINTS; i++) {
|
||||
epInfo[i].epAddr = 0;
|
||||
epInfo[i].maxPktSize = (i) ? 0 : 8;
|
||||
epInfo[i].epAttribs = 0;
|
||||
epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
|
||||
}
|
||||
|
||||
if (pUsb) // register in USB subsystem
|
||||
pUsb->RegisterDeviceClass(this); //set devConfig[] entry
|
||||
}
|
||||
|
||||
uint8_t BTD::Init(uint8_t parent, uint8_t port, bool lowspeed) {
|
||||
uint8_t buf[sizeof(USB_DEVICE_DESCRIPTOR)];
|
||||
uint8_t rcode;
|
||||
UsbDevice *p = NULL;
|
||||
EpInfo *oldep_ptr = NULL;
|
||||
uint8_t num_of_conf; // number of configurations
|
||||
uint16_t PID;
|
||||
uint16_t VID;
|
||||
|
||||
// get memory address of USB device address pool
|
||||
AddressPool &addrPool = pUsb->GetAddressPool();
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR("\r\nBTD Init"));
|
||||
#endif
|
||||
// check if address has already been assigned to an instance
|
||||
if (bAddress) {
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nAddress in use"));
|
||||
#endif
|
||||
return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
|
||||
}
|
||||
|
||||
// Get pointer to pseudo device with address 0 assigned
|
||||
p = addrPool.GetUsbDevicePtr(0);
|
||||
|
||||
if (!p) {
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nAddress not found"));
|
||||
#endif
|
||||
return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
|
||||
}
|
||||
|
||||
if (!p->epinfo) {
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nepinfo is null"));
|
||||
#endif
|
||||
return USB_ERROR_EPINFO_IS_NULL;
|
||||
}
|
||||
|
||||
// Save old pointer to EP_RECORD of address 0
|
||||
oldep_ptr = p->epinfo;
|
||||
|
||||
// Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
|
||||
p->epinfo = epInfo;
|
||||
|
||||
p->lowspeed = lowspeed;
|
||||
|
||||
// Get device descriptor
|
||||
rcode = pUsb->getDevDescr(0, 0, sizeof(USB_DEVICE_DESCRIPTOR), (uint8_t*)buf);// Get device descriptor - addr, ep, nbytes, data
|
||||
|
||||
// Restore p->epinfo
|
||||
p->epinfo = oldep_ptr;
|
||||
|
||||
if(rcode)
|
||||
goto FailGetDevDescr;
|
||||
|
||||
// Allocate new address according to device class
|
||||
bAddress = addrPool.AllocAddress(parent, false, port);
|
||||
|
||||
if (!bAddress)
|
||||
return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
|
||||
|
||||
// Extract Max Packet Size from device descriptor
|
||||
epInfo[0].maxPktSize = (uint8_t)((USB_DEVICE_DESCRIPTOR*)buf)->bMaxPacketSize0;
|
||||
|
||||
// Assign new address to the device
|
||||
rcode = pUsb->setAddr( 0, 0, bAddress );
|
||||
if (rcode) {
|
||||
p->lowspeed = false;
|
||||
addrPool.FreeAddress(bAddress);
|
||||
bAddress = 0;
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nsetAddr: "));
|
||||
#endif
|
||||
PrintHex<uint8_t>(rcode);
|
||||
return rcode;
|
||||
}
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR("\r\nAddr: "));
|
||||
PrintHex<uint8_t>(bAddress);
|
||||
#endif
|
||||
p->lowspeed = false;
|
||||
|
||||
//get pointer to assigned address record
|
||||
p = addrPool.GetUsbDevicePtr(bAddress);
|
||||
if (!p)
|
||||
return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
|
||||
|
||||
p->lowspeed = lowspeed;
|
||||
|
||||
// Assign epInfo to epinfo pointer - only EP0 is known
|
||||
rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
|
||||
if (rcode)
|
||||
goto FailSetDevTblEntry;
|
||||
VID = ((USB_DEVICE_DESCRIPTOR*)buf)->idVendor;
|
||||
PID = ((USB_DEVICE_DESCRIPTOR*)buf)->idProduct;
|
||||
|
||||
if(VID == PS3_VID && (PID == PS3_PID || PID == PS3NAVIGATION_PID || PID == PS3MOVE_PID)) {
|
||||
/* We only need the Control endpoint, so we don't have to initialize the other endpoints of device */
|
||||
rcode = pUsb->setConf(bAddress, epInfo[ BTD_CONTROL_PIPE ].epAddr, 1);
|
||||
if( rcode )
|
||||
goto FailSetConf;
|
||||
|
||||
if(PID == PS3_PID || PID == PS3NAVIGATION_PID) {
|
||||
#ifdef DEBUG
|
||||
if(PID == PS3_PID)
|
||||
Notify(PSTR("\r\nDualshock 3 Controller Connected"));
|
||||
else // must be a navigation controller
|
||||
Notify(PSTR("\r\nNavigation Controller Connected"));
|
||||
#endif
|
||||
/* Set internal bluetooth address */
|
||||
setBdaddr(my_bdaddr);
|
||||
}
|
||||
else { // must be a Motion controller
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nMotion Controller Connected"));
|
||||
#endif
|
||||
setMoveBdaddr(my_bdaddr);
|
||||
}
|
||||
rcode = pUsb->setConf(bAddress, epInfo[ BTD_CONTROL_PIPE ].epAddr, 0); // Reset configuration value
|
||||
pUsb->setAddr(bAddress, 0, 0); // Reset address
|
||||
Release(); // Release device
|
||||
return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED; // return
|
||||
}
|
||||
else {
|
||||
num_of_conf = ((USB_DEVICE_DESCRIPTOR*)buf)->bNumConfigurations;
|
||||
|
||||
// check if attached device is a Bluetooth dongle and fill endpoint data structure
|
||||
// first interface in the configuration must have Bluetooth assigned Class/Subclass/Protocol
|
||||
// and 3 endpoints - interrupt-IN, bulk-IN, bulk-OUT,
|
||||
// not necessarily in this order
|
||||
for (uint8_t i=0; i<num_of_conf; i++) {
|
||||
ConfigDescParser<USB_CLASS_WIRELESS_CTRL, WI_SUBCLASS_RF, WI_PROTOCOL_BT, CP_MASK_COMPARE_ALL> confDescrParser(this);
|
||||
rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser);
|
||||
if(rcode)
|
||||
goto FailGetConfDescr;
|
||||
if(bNumEP >= BTD_MAX_ENDPOINTS) // All endpoints extracted
|
||||
break;
|
||||
}
|
||||
|
||||
if (bNumEP < BTD_MAX_ENDPOINTS)
|
||||
goto FailUnknownDevice;
|
||||
|
||||
// Assign epInfo to epinfo pointer - this time all 3 endpoins
|
||||
rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo);
|
||||
if(rcode)
|
||||
goto FailSetDevTblEntry;
|
||||
|
||||
delay(200); // Give time for address change
|
||||
|
||||
// Set Configuration Value
|
||||
rcode = pUsb->setConf(bAddress, epInfo[ BTD_CONTROL_PIPE ].epAddr, bConfNum);
|
||||
if(rcode)
|
||||
goto FailSetConf;
|
||||
|
||||
hci_num_reset_loops = 100; // only loop 100 times before trying to send the hci reset command
|
||||
hci_counter = 0;
|
||||
hci_state = HCI_INIT_STATE;
|
||||
watingForConnection = false;
|
||||
bPollEnable = true;
|
||||
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nBluetooth Dongle Initialized"));
|
||||
#endif
|
||||
}
|
||||
return 0; // Successful configuration
|
||||
|
||||
/* diagnostic messages */
|
||||
FailGetDevDescr:
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\ngetDevDescr"));
|
||||
#endif
|
||||
goto Fail;
|
||||
FailSetDevTblEntry:
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nsetDevTblEn"));
|
||||
#endif
|
||||
goto Fail;
|
||||
FailGetConfDescr:
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\ngetConf"));
|
||||
#endif
|
||||
goto Fail;
|
||||
FailSetConf:
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nsetConf"));
|
||||
#endif
|
||||
goto Fail;
|
||||
FailUnknownDevice:
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nUnknown Device Connected - VID: "));
|
||||
PrintHex<uint16_t>(VID);
|
||||
Notify(PSTR(" PID: "));
|
||||
PrintHex<uint16_t>(PID);
|
||||
#endif
|
||||
pUsb->setAddr(bAddress, 0, 0); // Reset address
|
||||
rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
|
||||
goto Fail;
|
||||
Fail:
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nBTD Init Failed, error code: "));
|
||||
Serial.print(rcode);
|
||||
#endif
|
||||
Release();
|
||||
return rcode;
|
||||
}
|
||||
/* Extracts interrupt-IN, bulk-IN, bulk-OUT endpoint information from config descriptor */
|
||||
void BTD::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *pep) {
|
||||
//ErrorMessage<uint8_t>(PSTR("Conf.Val"),conf);
|
||||
//ErrorMessage<uint8_t>(PSTR("Iface Num"),iface);
|
||||
//ErrorMessage<uint8_t>(PSTR("Alt.Set"),alt);
|
||||
|
||||
if(alt) // wrong interface - by BT spec, no alt setting
|
||||
return;
|
||||
|
||||
bConfNum = conf;
|
||||
uint8_t index;
|
||||
|
||||
if ((pep->bmAttributes & 0x03) == 3 && (pep->bEndpointAddress & 0x80) == 0x80) // Interrupt In endpoint found
|
||||
index = BTD_EVENT_PIPE;
|
||||
|
||||
else {
|
||||
if ((pep->bmAttributes & 0x02) == 2) // bulk endpoint found
|
||||
index = ((pep->bEndpointAddress & 0x80) == 0x80) ? BTD_DATAIN_PIPE : BTD_DATAOUT_PIPE;
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
// Fill the rest of endpoint data structure
|
||||
epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F);
|
||||
epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize;
|
||||
#ifdef EXTRADEBUG
|
||||
PrintEndpointDescriptor(pep);
|
||||
#endif
|
||||
if(pollInterval < pep->bInterval) // Set the polling interval as the largest polling interval obtained from endpoints
|
||||
pollInterval = pep->bInterval;
|
||||
bNumEP++;
|
||||
}
|
||||
void BTD::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr) {
|
||||
Notify(PSTR("\r\nEndpoint descriptor:"));
|
||||
Notify(PSTR("\r\nLength:\t\t"));
|
||||
PrintHex<uint8_t>(ep_ptr->bLength);
|
||||
Notify(PSTR("\r\nType:\t\t"));
|
||||
PrintHex<uint8_t>(ep_ptr->bDescriptorType);
|
||||
Notify(PSTR("\r\nAddress:\t"));
|
||||
PrintHex<uint8_t>(ep_ptr->bEndpointAddress);
|
||||
Notify(PSTR("\r\nAttributes:\t"));
|
||||
PrintHex<uint8_t>(ep_ptr->bmAttributes);
|
||||
Notify(PSTR("\r\nMaxPktSize:\t"));
|
||||
PrintHex<uint16_t>(ep_ptr->wMaxPacketSize);
|
||||
Notify(PSTR("\r\nPoll Intrv:\t"));
|
||||
PrintHex<uint8_t>(ep_ptr->bInterval);
|
||||
}
|
||||
|
||||
/* Performs a cleanup after failed Init() attempt */
|
||||
uint8_t BTD::Release() {
|
||||
for (uint8_t i=0; i<BTD_NUMSERVICES; i++)
|
||||
if (btService[i])
|
||||
btService[i]->Reset(); // Reset all Bluetooth services
|
||||
pUsb->GetAddressPool().FreeAddress(bAddress);
|
||||
bAddress = 0;
|
||||
bPollEnable = false;
|
||||
bNumEP = 1; // must have to be reset to 1
|
||||
return 0;
|
||||
}
|
||||
uint8_t BTD::Poll() {
|
||||
if (!bPollEnable)
|
||||
return 0;
|
||||
if (qNextPollTime <= millis()) { // Don't poll if shorter than polling interval
|
||||
qNextPollTime = millis() + pollInterval; // Set new poll time
|
||||
HCI_event_task(); // poll the HCI event pipe
|
||||
ACL_event_task(); // start polling the ACL input pipe too, though discard data until connected
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void BTD::HCI_event_task() {
|
||||
/* check the event pipe*/
|
||||
uint16_t MAX_BUFFER_SIZE = BULK_MAXPKTSIZE; // Request more than 16 bytes anyway, the inTransfer routine will take care of this
|
||||
uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[ BTD_EVENT_PIPE ].epAddr, &MAX_BUFFER_SIZE, hcibuf); // input on endpoint 1
|
||||
if(!rcode || rcode == hrNAK) // Check for errors
|
||||
{
|
||||
switch (hcibuf[0]) //switch on event type
|
||||
{
|
||||
case EV_COMMAND_COMPLETE:
|
||||
if (!hcibuf[5]) { // Check if command succeeded
|
||||
hci_event_flag |= HCI_FLAG_CMD_COMPLETE; // set command complete flag
|
||||
if((hcibuf[3] == 0x01) && (hcibuf[4] == 0x10)) { // parameters from read local version information
|
||||
hci_version = hcibuf[6]; // Used to check if it supports 2.0+EDR - see http://www.bluetooth.org/Technical/AssignedNumbers/hci.htm
|
||||
hci_event_flag |= HCI_FLAG_READ_VERSION;
|
||||
} else if((hcibuf[3] == 0x09) && (hcibuf[4] == 0x10)) { // parameters from read local bluetooth address
|
||||
for (uint8_t i = 0; i < 6; i++)
|
||||
my_bdaddr[i] = hcibuf[6 + i];
|
||||
hci_event_flag |= HCI_FLAG_READ_BDADDR;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case EV_COMMAND_STATUS:
|
||||
if(hcibuf[2]) { // show status on serial if not OK
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nHCI Command Failed: "));
|
||||
PrintHex<uint8_t>(hcibuf[2]);
|
||||
Notify(PSTR(" "));
|
||||
PrintHex<uint8_t>(hcibuf[4]);
|
||||
Notify(PSTR(" "));
|
||||
PrintHex<uint8_t>(hcibuf[5]);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case EV_CONNECT_COMPLETE:
|
||||
if (!hcibuf[2]) { // check if connected OK
|
||||
hci_handle = hcibuf[3] | hcibuf[4] << 8; // store the handle for the ACL connection
|
||||
hci_event_flag |= HCI_FLAG_CONN_COMPLETE; // set connection complete flag
|
||||
}
|
||||
break;
|
||||
|
||||
case EV_DISCONNECT_COMPLETE:
|
||||
if (!hcibuf[2]) { // check if disconnected OK
|
||||
hci_event_flag |= HCI_FLAG_DISCONN_COMPLETE; // set disconnect command complete flag
|
||||
hci_event_flag &= ~HCI_FLAG_CONN_COMPLETE; // clear connection complete flag
|
||||
}
|
||||
break;
|
||||
|
||||
case EV_REMOTE_NAME_COMPLETE:
|
||||
if (!hcibuf[2]) { // check if reading is OK
|
||||
for (uint8_t i = 0; i < 30; i++)
|
||||
remote_name[i] = hcibuf[9 + i]; //store first 30 bytes
|
||||
hci_event_flag |= HCI_FLAG_REMOTE_NAME_COMPLETE;
|
||||
}
|
||||
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];
|
||||
hci_event_flag |= HCI_FLAG_INCOMING_REQUEST;
|
||||
break;
|
||||
|
||||
case EV_PIN_CODE_REQUEST:
|
||||
if(btdPin != NULL) {
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nBluetooth pin is set too: "));
|
||||
Serial.print(btdPin);
|
||||
#endif
|
||||
hci_pin_code_request_reply(btdPin);
|
||||
}
|
||||
else {
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nNo pin was set"));
|
||||
#endif
|
||||
hci_pin_code_negative_request_reply();
|
||||
}
|
||||
break;
|
||||
|
||||
case EV_LINK_KEY_REQUEST:
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nReceived Key Request"));
|
||||
#endif
|
||||
hci_link_key_request_negative_reply();
|
||||
break;
|
||||
|
||||
/* We will just ignore the following events */
|
||||
case EV_NUM_COMPLETE_PKT:
|
||||
case EV_ROLE_CHANGED:
|
||||
case EV_PAGE_SCAN_REP_MODE:
|
||||
case EV_LOOPBACK_COMMAND:
|
||||
case EV_DATA_BUFFER_OVERFLOW:
|
||||
case EV_CHANGE_CONNECTION_LINK:
|
||||
case EV_AUTHENTICATION_COMPLETE:
|
||||
case EV_MAX_SLOTS_CHANGE:
|
||||
case EV_QOS_SETUP_COMPLETE:
|
||||
case EV_LINK_KEY_NOTIFICATION:
|
||||
case EV_ENCRYPTION_CHANGE:
|
||||
case EV_READ_REMOTE_VERSION_INFORMATION_COMPLETE:
|
||||
break;
|
||||
#ifdef EXTRADEBUG
|
||||
default:
|
||||
if(hcibuf[0] != 0x00) {
|
||||
Notify(PSTR("\r\nUnmanaged HCI Event: "));
|
||||
PrintHex<uint8_t>(hcibuf[0]);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
} // switch
|
||||
HCI_task();
|
||||
}
|
||||
#ifdef EXTRADEBUG
|
||||
else {
|
||||
Notify(PSTR("\r\nHCI event error: "));
|
||||
PrintHex<uint8_t>(rcode);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Poll Bluetooth and print result */
|
||||
void BTD::HCI_task() {
|
||||
switch (hci_state){
|
||||
case HCI_INIT_STATE:
|
||||
hci_counter++;
|
||||
if (hci_counter > hci_num_reset_loops) { // wait until we have looped x times to clear any old events
|
||||
hci_reset();
|
||||
hci_state = HCI_RESET_STATE;
|
||||
hci_counter = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case HCI_RESET_STATE:
|
||||
hci_counter++;
|
||||
if (hci_cmd_complete) {
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nHCI Reset complete"));
|
||||
#endif
|
||||
hci_state = HCI_BDADDR_STATE;
|
||||
hci_read_bdaddr();
|
||||
}
|
||||
else if (hci_counter > hci_num_reset_loops) {
|
||||
hci_num_reset_loops *= 10;
|
||||
if(hci_num_reset_loops > 2000)
|
||||
hci_num_reset_loops = 2000;
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nNo response to HCI Reset"));
|
||||
#endif
|
||||
hci_state = HCI_INIT_STATE;
|
||||
hci_counter = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case HCI_BDADDR_STATE:
|
||||
if (hci_read_bdaddr_complete) {
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nLocal Bluetooth Address: "));
|
||||
for(int8_t i = 5; i > 0;i--) {
|
||||
PrintHex<uint8_t>(my_bdaddr[i]);
|
||||
Serial.print(":");
|
||||
}
|
||||
PrintHex<uint8_t>(my_bdaddr[0]);
|
||||
#endif
|
||||
hci_read_local_version_information();
|
||||
hci_state = HCI_LOCAL_VERSION_STATE;
|
||||
}
|
||||
break;
|
||||
|
||||
case HCI_LOCAL_VERSION_STATE: // The local version is used by the PS3BT class
|
||||
if (hci_read_version_complete) {
|
||||
if(btdName != NULL) {
|
||||
hci_set_local_name(btdName);
|
||||
hci_state = HCI_SET_NAME_STATE;
|
||||
} else
|
||||
hci_state = HCI_SCANNING_STATE;
|
||||
}
|
||||
break;
|
||||
|
||||
case HCI_SET_NAME_STATE:
|
||||
if (hci_cmd_complete) {
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nThe name is set to: "));
|
||||
Serial.print(btdName);
|
||||
#endif
|
||||
hci_state = HCI_SCANNING_STATE;
|
||||
}
|
||||
break;
|
||||
|
||||
case HCI_SCANNING_STATE:
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nWait For Incoming Connection Request"));
|
||||
#endif
|
||||
hci_write_scan_enable();
|
||||
watingForConnection = true;
|
||||
hci_state = HCI_CONNECT_IN_STATE;
|
||||
break;
|
||||
|
||||
case HCI_CONNECT_IN_STATE:
|
||||
if(hci_incoming_connect_request) {
|
||||
watingForConnection = false;
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nIncoming Connection Request"));
|
||||
#endif
|
||||
hci_remote_name();
|
||||
hci_state = HCI_REMOTE_NAME_STATE;
|
||||
} else if (hci_disconnect_complete)
|
||||
hci_state = HCI_DISCONNECT_STATE;
|
||||
break;
|
||||
|
||||
case HCI_REMOTE_NAME_STATE:
|
||||
if(hci_remote_name_complete) {
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nRemote Name: "));
|
||||
for (uint8_t i = 0; i < 30; i++)
|
||||
{
|
||||
if(remote_name[i] == NULL)
|
||||
break;
|
||||
Serial.write(remote_name[i]);
|
||||
}
|
||||
#endif
|
||||
hci_accept_connection();
|
||||
hci_state = HCI_CONNECTED_STATE;
|
||||
}
|
||||
break;
|
||||
|
||||
case HCI_CONNECTED_STATE:
|
||||
if (hci_connect_complete) {
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nConnected to Device: "));
|
||||
for(int8_t i = 5; i>0;i--)
|
||||
{
|
||||
PrintHex<uint8_t>(disc_bdaddr[i]);
|
||||
Serial.print(":");
|
||||
}
|
||||
PrintHex<uint8_t>(disc_bdaddr[0]);
|
||||
#endif
|
||||
hci_write_scan_disable();
|
||||
hci_state = HCI_DISABLE_SCAN_STATE;
|
||||
}
|
||||
break;
|
||||
|
||||
case HCI_DISABLE_SCAN_STATE:
|
||||
if (hci_cmd_complete) {
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nScan Disabled"));
|
||||
#endif
|
||||
hci_event_flag = 0;
|
||||
hci_state = HCI_DONE_STATE;
|
||||
}
|
||||
break;
|
||||
|
||||
case HCI_DONE_STATE:
|
||||
hci_counter++;
|
||||
if (hci_counter > 250) { // Wait until we have looped 250 times to make sure that the L2CAP connection has been started
|
||||
hci_state = HCI_SCANNING_STATE;
|
||||
l2capConnectionClaimed = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case HCI_DISCONNECT_STATE:
|
||||
if (hci_disconnect_complete) {
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nHCI Disconnected from Device"));
|
||||
#endif
|
||||
hci_event_flag = 0; // Clear all flags
|
||||
|
||||
// Reset all buffers
|
||||
for (uint8_t i = 0; i < BULK_MAXPKTSIZE; i++)
|
||||
hcibuf[i] = 0;
|
||||
for (uint8_t i = 0; i < BULK_MAXPKTSIZE; i++)
|
||||
l2capinbuf[i] = 0;
|
||||
|
||||
hci_state = HCI_SCANNING_STATE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void BTD::ACL_event_task() {
|
||||
uint16_t MAX_BUFFER_SIZE = BULK_MAXPKTSIZE;
|
||||
uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[ BTD_DATAIN_PIPE ].epAddr, &MAX_BUFFER_SIZE, l2capinbuf); // input on endpoint 2
|
||||
if(!rcode) { // Check for errors
|
||||
for (uint8_t i=0; i<BTD_NUMSERVICES; i++)
|
||||
if (btService[i])
|
||||
btService[i]->ACLData(l2capinbuf);
|
||||
}
|
||||
#ifdef EXTRADEBUG
|
||||
else if (rcode != hrNAK) {
|
||||
Notify(PSTR("\r\nACL data in error: "));
|
||||
PrintHex<uint8_t>(rcode);
|
||||
}
|
||||
#endif
|
||||
for (uint8_t i=0; i<BTD_NUMSERVICES; i++)
|
||||
if (btService[i])
|
||||
btService[i]->Run();
|
||||
}
|
||||
|
||||
/************************************************************/
|
||||
/* HCI Commands */
|
||||
/************************************************************/
|
||||
void BTD::HCI_Command(uint8_t* data, uint16_t nbytes) {
|
||||
hci_event_flag &= ~HCI_FLAG_CMD_COMPLETE;
|
||||
pUsb->ctrlReq(bAddress, epInfo[ BTD_CONTROL_PIPE ].epAddr, bmREQ_HCI_OUT, 0x00, 0x00, 0x00 ,0x00, nbytes, nbytes, data, NULL);
|
||||
}
|
||||
void BTD::hci_reset() {
|
||||
hci_event_flag = 0; // Clear all the flags
|
||||
hcibuf[0] = 0x03; // HCI OCF = 3
|
||||
hcibuf[1] = 0x03 << 2; // HCI OGF = 3
|
||||
hcibuf[2] = 0x00;
|
||||
HCI_Command(hcibuf, 3);
|
||||
}
|
||||
void BTD::hci_write_scan_enable() {
|
||||
hci_event_flag &= ~HCI_FLAG_INCOMING_REQUEST;
|
||||
hcibuf[0] = 0x1A; // HCI OCF = 1A
|
||||
hcibuf[1] = 0x03 << 2; // HCI OGF = 3
|
||||
hcibuf[2] = 0x01; // parameter length = 1
|
||||
if(btdName != NULL)
|
||||
hcibuf[3] = 0x03; // Inquiry Scan enabled. Page Scan enabled.
|
||||
else
|
||||
hcibuf[3] = 0x02; // Inquiry Scan disabled. Page Scan enabled.
|
||||
HCI_Command(hcibuf, 4);
|
||||
}
|
||||
void BTD::hci_write_scan_disable() {
|
||||
hcibuf[0] = 0x1A; // HCI OCF = 1A
|
||||
hcibuf[1] = 0x03 << 2; // HCI OGF = 3
|
||||
hcibuf[2] = 0x01; // parameter length = 1
|
||||
hcibuf[3] = 0x00; // Inquiry Scan disabled. Page Scan disabled.
|
||||
HCI_Command(hcibuf, 4);
|
||||
}
|
||||
void BTD::hci_read_bdaddr() {
|
||||
hcibuf[0] = 0x09; // HCI OCF = 9
|
||||
hcibuf[1] = 0x04 << 2; // HCI OGF = 4
|
||||
hcibuf[2] = 0x00;
|
||||
HCI_Command(hcibuf, 3);
|
||||
}
|
||||
void BTD::hci_read_local_version_information() {
|
||||
hcibuf[0] = 0x01; // HCI OCF = 1
|
||||
hcibuf[1] = 0x04 << 2; // HCI OGF = 4
|
||||
hcibuf[2] = 0x00;
|
||||
HCI_Command(hcibuf, 3);
|
||||
}
|
||||
void BTD::hci_accept_connection() {
|
||||
hcibuf[0] = 0x09; // HCI OCF = 9
|
||||
hcibuf[1] = 0x01 << 2; // HCI OGF = 1
|
||||
hcibuf[2] = 0x07; // parameter length 7
|
||||
hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr
|
||||
hcibuf[4] = disc_bdaddr[1];
|
||||
hcibuf[5] = disc_bdaddr[2];
|
||||
hcibuf[6] = disc_bdaddr[3];
|
||||
hcibuf[7] = disc_bdaddr[4];
|
||||
hcibuf[8] = disc_bdaddr[5];
|
||||
hcibuf[9] = 0x00; //switch role to master
|
||||
|
||||
HCI_Command(hcibuf, 10);
|
||||
}
|
||||
void BTD::hci_remote_name() {
|
||||
hci_event_flag &= ~HCI_FLAG_REMOTE_NAME_COMPLETE;
|
||||
hcibuf[0] = 0x19; // HCI OCF = 19
|
||||
hcibuf[1] = 0x01 << 2; // HCI OGF = 1
|
||||
hcibuf[2] = 0x0A; // parameter length = 10
|
||||
hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr
|
||||
hcibuf[4] = disc_bdaddr[1];
|
||||
hcibuf[5] = disc_bdaddr[2];
|
||||
hcibuf[6] = disc_bdaddr[3];
|
||||
hcibuf[7] = disc_bdaddr[4];
|
||||
hcibuf[8] = disc_bdaddr[5];
|
||||
hcibuf[9] = 0x01; //Page Scan Repetition Mode
|
||||
hcibuf[10] = 0x00; //Reserved
|
||||
hcibuf[11] = 0x00; //Clock offset - low byte
|
||||
hcibuf[12] = 0x00; //Clock offset - high byte
|
||||
|
||||
HCI_Command(hcibuf, 13);
|
||||
}
|
||||
void BTD::hci_set_local_name(const char* name) {
|
||||
hcibuf[0] = 0x13; // HCI OCF = 13
|
||||
hcibuf[1] = 0x03 << 2; // HCI OGF = 3
|
||||
hcibuf[2] = strlen(name)+1; // parameter length = the length of the string + end byte
|
||||
uint8_t i;
|
||||
for(i = 0; i < strlen(name); i++)
|
||||
hcibuf[i+3] = name[i];
|
||||
hcibuf[i+3] = 0x00; // End of string
|
||||
|
||||
HCI_Command(hcibuf, 4+strlen(name));
|
||||
}
|
||||
void BTD::hci_pin_code_request_reply(const char* key) {
|
||||
hcibuf[0] = 0x0D; // HCI OCF = 0D
|
||||
hcibuf[1] = 0x01 << 2; // HCI OGF = 1
|
||||
hcibuf[2] = 0x17; // parameter length 23
|
||||
hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr
|
||||
hcibuf[4] = disc_bdaddr[1];
|
||||
hcibuf[5] = disc_bdaddr[2];
|
||||
hcibuf[6] = disc_bdaddr[3];
|
||||
hcibuf[7] = disc_bdaddr[4];
|
||||
hcibuf[8] = disc_bdaddr[5];
|
||||
hcibuf[9] = strlen(key); // Length of key
|
||||
uint8_t i;
|
||||
for(i = 0; i < strlen(key); i++) // The maximum size of the key is 16
|
||||
hcibuf[i+10] = key[i];
|
||||
for(;i < 16; i++)
|
||||
hcibuf[i+10] = 0x00; // The rest should be 0
|
||||
|
||||
HCI_Command(hcibuf, 26);
|
||||
}
|
||||
void BTD::hci_pin_code_negative_request_reply() {
|
||||
hcibuf[0] = 0x0E; // HCI OCF = 0E
|
||||
hcibuf[1] = 0x01 << 2; // HCI OGF = 1
|
||||
hcibuf[2] = 0x06; // parameter length 6
|
||||
hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr
|
||||
hcibuf[4] = disc_bdaddr[1];
|
||||
hcibuf[5] = disc_bdaddr[2];
|
||||
hcibuf[6] = disc_bdaddr[3];
|
||||
hcibuf[7] = disc_bdaddr[4];
|
||||
hcibuf[8] = disc_bdaddr[5];
|
||||
|
||||
HCI_Command(hcibuf, 9);
|
||||
}
|
||||
void BTD::hci_link_key_request_negative_reply() {
|
||||
hcibuf[0] = 0x0C; // HCI OCF = 0C
|
||||
hcibuf[1] = 0x01 << 2; // HCI OGF = 1
|
||||
hcibuf[2] = 0x06; // parameter length 6
|
||||
hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr
|
||||
hcibuf[4] = disc_bdaddr[1];
|
||||
hcibuf[5] = disc_bdaddr[2];
|
||||
hcibuf[6] = disc_bdaddr[3];
|
||||
hcibuf[7] = disc_bdaddr[4];
|
||||
hcibuf[8] = disc_bdaddr[5];
|
||||
|
||||
HCI_Command(hcibuf, 9);
|
||||
}
|
||||
void BTD::hci_disconnect(uint16_t handle) { // This is called by the different services
|
||||
hci_event_flag &= ~HCI_FLAG_DISCONN_COMPLETE;
|
||||
hcibuf[0] = 0x06; // HCI OCF = 6
|
||||
hcibuf[1] = 0x01 << 2; // HCI OGF = 1
|
||||
hcibuf[2] = 0x03; // parameter length = 3
|
||||
hcibuf[3] = (uint8_t)(handle & 0xFF);//connection handle - low byte
|
||||
hcibuf[4] = (uint8_t)((handle >> 8) & 0x0F);//connection handle - high byte
|
||||
hcibuf[5] = 0x13; // reason
|
||||
|
||||
HCI_Command(hcibuf, 6);
|
||||
}
|
||||
/*******************************************************************
|
||||
* *
|
||||
* HCI ACL Data Packet *
|
||||
* *
|
||||
* buf[0] buf[1] buf[2] buf[3]
|
||||
* 0 4 8 11 12 16 24 31 MSB
|
||||
* .-+-+-+-+-+-+-+-|-+-+-+-|-+-|-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-.
|
||||
* | HCI Handle |PB |BC | Data Total Length | HCI ACL Data Packet
|
||||
* .-+-+-+-+-+-+-+-|-+-+-+-|-+-|-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-.
|
||||
*
|
||||
* buf[4] buf[5] buf[6] buf[7]
|
||||
* 0 8 16 31 MSB
|
||||
* .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-.
|
||||
* | Length | Channel ID | Basic L2CAP header
|
||||
* .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-.
|
||||
*
|
||||
* buf[8] buf[9] buf[10] buf[11]
|
||||
* 0 8 16 31 MSB
|
||||
* .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-.
|
||||
* | Code | Identifier | Length | Control frame (C-frame)
|
||||
* .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. (signaling packet format)
|
||||
*/
|
||||
/************************************************************/
|
||||
/* L2CAP Commands */
|
||||
/************************************************************/
|
||||
void BTD::L2CAP_Command(uint16_t handle, uint8_t* data, uint8_t nbytes, uint8_t channelLow, uint8_t channelHigh) {
|
||||
uint8_t buf[8+nbytes];
|
||||
buf[0] = (uint8_t)(handle & 0xff); // HCI handle with PB,BC flag
|
||||
buf[1] = (uint8_t)(((handle >> 8) & 0x0f) | 0x20);
|
||||
buf[2] = (uint8_t)((4 + nbytes) & 0xff); // HCI ACL total data length
|
||||
buf[3] = (uint8_t)((4 + nbytes) >> 8);
|
||||
buf[4] = (uint8_t)(nbytes & 0xff); // L2CAP header: Length
|
||||
buf[5] = (uint8_t)(nbytes >> 8);
|
||||
buf[6] = channelLow;
|
||||
buf[7] = channelHigh;
|
||||
|
||||
for (uint16_t i = 0; i < nbytes; i++) // L2CAP C-frame
|
||||
buf[8 + i] = data[i];
|
||||
|
||||
uint8_t rcode = pUsb->outTransfer(bAddress, epInfo[ BTD_DATAOUT_PIPE ].epAddr, (8 + nbytes), buf);
|
||||
if(rcode) {
|
||||
delay(100); // This small delay prevents it from overflowing if it fails
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nError sending L2CAP message: 0x"));
|
||||
PrintHex<uint8_t>(rcode);
|
||||
Notify(PSTR(" - Channel ID: "));
|
||||
Serial.print(channelHigh);
|
||||
Notify(PSTR(" "));
|
||||
Serial.print(channelLow);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
void BTD::l2cap_connection_response(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid, uint8_t result) {
|
||||
l2capoutbuf[0] = L2CAP_CMD_CONNECTION_RESPONSE; // Code
|
||||
l2capoutbuf[1] = rxid; // Identifier
|
||||
l2capoutbuf[2] = 0x08; // Length
|
||||
l2capoutbuf[3] = 0x00;
|
||||
l2capoutbuf[4] = dcid[0]; // Destination CID
|
||||
l2capoutbuf[5] = dcid[1];
|
||||
l2capoutbuf[6] = scid[0]; // Source CID
|
||||
l2capoutbuf[7] = scid[1];
|
||||
l2capoutbuf[8] = result; // Result: Pending or Success
|
||||
l2capoutbuf[9] = 0x00;
|
||||
l2capoutbuf[10] = 0x00; // No further information
|
||||
l2capoutbuf[11] = 0x00;
|
||||
|
||||
L2CAP_Command(handle, l2capoutbuf, 12);
|
||||
}
|
||||
void BTD::l2cap_config_request(uint16_t handle, uint8_t rxid, uint8_t* dcid) {
|
||||
l2capoutbuf[0] = L2CAP_CMD_CONFIG_REQUEST; // Code
|
||||
l2capoutbuf[1] = rxid; // Identifier
|
||||
l2capoutbuf[2] = 0x08; // Length
|
||||
l2capoutbuf[3] = 0x00;
|
||||
l2capoutbuf[4] = dcid[0]; // Destination CID
|
||||
l2capoutbuf[5] = dcid[1];
|
||||
l2capoutbuf[6] = 0x00; // Flags
|
||||
l2capoutbuf[7] = 0x00;
|
||||
l2capoutbuf[8] = 0x01; // Config Opt: type = MTU (Maximum Transmission Unit) - Hint
|
||||
l2capoutbuf[9] = 0x02; // Config Opt: length
|
||||
l2capoutbuf[10] = 0xFF; // MTU
|
||||
l2capoutbuf[11] = 0xFF;
|
||||
|
||||
L2CAP_Command(handle, l2capoutbuf, 12);
|
||||
}
|
||||
void BTD::l2cap_config_response(uint16_t handle, uint8_t rxid, uint8_t* scid) {
|
||||
l2capoutbuf[0] = L2CAP_CMD_CONFIG_RESPONSE; // Code
|
||||
l2capoutbuf[1] = rxid; // Identifier
|
||||
l2capoutbuf[2] = 0x0A; // Length
|
||||
l2capoutbuf[3] = 0x00;
|
||||
l2capoutbuf[4] = scid[0]; // Source CID
|
||||
l2capoutbuf[5] = scid[1];
|
||||
l2capoutbuf[6] = 0x00; // Flag
|
||||
l2capoutbuf[7] = 0x00;
|
||||
l2capoutbuf[8] = 0x00; // Result
|
||||
l2capoutbuf[9] = 0x00;
|
||||
l2capoutbuf[10] = 0x01; // Config
|
||||
l2capoutbuf[11] = 0x02;
|
||||
l2capoutbuf[12] = 0xA0;
|
||||
l2capoutbuf[13] = 0x02;
|
||||
|
||||
L2CAP_Command(handle, l2capoutbuf, 14);
|
||||
}
|
||||
void BTD::l2cap_disconnection_request(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid) {
|
||||
l2capoutbuf[0] = L2CAP_CMD_DISCONNECT_REQUEST; // Code
|
||||
l2capoutbuf[1] = rxid; // Identifier
|
||||
l2capoutbuf[2] = 0x04; // Length
|
||||
l2capoutbuf[3] = 0x00;
|
||||
l2capoutbuf[4] = dcid[0];
|
||||
l2capoutbuf[5] = dcid[1];
|
||||
l2capoutbuf[6] = scid[0];
|
||||
l2capoutbuf[7] = scid[1];
|
||||
L2CAP_Command(handle, l2capoutbuf, 8);
|
||||
}
|
||||
void BTD::l2cap_disconnection_response(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid) {
|
||||
l2capoutbuf[0] = L2CAP_CMD_DISCONNECT_RESPONSE; // Code
|
||||
l2capoutbuf[1] = rxid; // Identifier
|
||||
l2capoutbuf[2] = 0x04; // Length
|
||||
l2capoutbuf[3] = 0x00;
|
||||
l2capoutbuf[4] = dcid[0];
|
||||
l2capoutbuf[5] = dcid[1];
|
||||
l2capoutbuf[6] = scid[0];
|
||||
l2capoutbuf[7] = scid[1];
|
||||
L2CAP_Command(handle, l2capoutbuf, 8);
|
||||
}
|
||||
void BTD::l2cap_information_response(uint16_t handle, uint8_t rxid, uint8_t infoTypeLow, uint8_t infoTypeHigh) {
|
||||
l2capoutbuf[0] = L2CAP_CMD_INFORMATION_RESPONSE; // Code
|
||||
l2capoutbuf[1] = rxid; // Identifier
|
||||
l2capoutbuf[2] = 0x08; // Length
|
||||
l2capoutbuf[3] = 0x00;
|
||||
l2capoutbuf[4] = infoTypeLow;
|
||||
l2capoutbuf[5] = infoTypeHigh;
|
||||
l2capoutbuf[6] = 0x00; // Result = success
|
||||
l2capoutbuf[7] = 0x00; // Result = success
|
||||
l2capoutbuf[8] = 0x00;
|
||||
l2capoutbuf[9] = 0x00;
|
||||
l2capoutbuf[10] = 0x00;
|
||||
l2capoutbuf[11] = 0x00;
|
||||
L2CAP_Command(handle, l2capoutbuf, 12);
|
||||
}
|
||||
|
||||
/* PS3 Commands - only set Bluetooth address is implemented */
|
||||
void BTD::setBdaddr(uint8_t* BDADDR) {
|
||||
/* Set the internal bluetooth address */
|
||||
uint8_t buf[8];
|
||||
buf[0] = 0x01;
|
||||
buf[1] = 0x00;
|
||||
for (uint8_t i = 0; i < 6; i++)
|
||||
buf[i+2] = BDADDR[5 - i];//Copy into buffer, has to be written reversed
|
||||
|
||||
//bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0xF5), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data)
|
||||
pUsb->ctrlReq(bAddress,epInfo[BTD_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0xF5, 0x03, 0x00, 8, 8, buf, NULL);
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nBluetooth Address was set to: "));
|
||||
for(int8_t i = 5; i > 0; i--)
|
||||
{
|
||||
PrintHex<uint8_t>(my_bdaddr[i]);
|
||||
Serial.print(":");
|
||||
}
|
||||
PrintHex<uint8_t>(my_bdaddr[0]);
|
||||
#endif
|
||||
}
|
||||
void BTD::setMoveBdaddr(uint8_t* BDADDR) {
|
||||
/* Set the internal bluetooth address */
|
||||
uint8_t buf[11];
|
||||
buf[0] = 0x05;
|
||||
buf[7] = 0x10;
|
||||
buf[8] = 0x01;
|
||||
buf[9] = 0x02;
|
||||
buf[10] = 0x12;
|
||||
|
||||
for (uint8_t i = 0; i < 6; i++)
|
||||
buf[i + 1] = BDADDR[i];
|
||||
|
||||
//bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x05), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data)
|
||||
pUsb->ctrlReq(bAddress,epInfo[BTD_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x05, 0x03, 0x00,11,11, buf, NULL);
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nBluetooth Address was set to: "));
|
||||
for(int8_t i = 5; i > 0; i--)
|
||||
{
|
||||
PrintHex<uint8_t>(my_bdaddr[i]);
|
||||
Serial.print(":");
|
||||
}
|
||||
PrintHex<uint8_t>(my_bdaddr[0]);
|
||||
#endif
|
||||
}
|
232
BTD.h
Normal file
232
BTD.h
Normal file
|
@ -0,0 +1,232 @@
|
|||
/* Copyright (C) 2012 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 _btd_h_
|
||||
#define _btd_h_
|
||||
|
||||
#include "Usb.h"
|
||||
#include "confdescparser.h"
|
||||
|
||||
//PID and VID of the Sony PS3 devices
|
||||
#define PS3_VID 0x054C // Sony Corporation
|
||||
#define PS3_PID 0x0268 // PS3 Controller DualShock 3
|
||||
#define PS3NAVIGATION_PID 0x042F // Navigation controller
|
||||
#define PS3MOVE_PID 0x03D5 // Motion controller
|
||||
|
||||
/* Bluetooth dongle data taken from descriptors */
|
||||
#define BULK_MAXPKTSIZE 64 // max size for ACL data
|
||||
|
||||
// Used in control endpoint header for HCI Commands
|
||||
#define bmREQ_HCI_OUT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE
|
||||
// Used in control endpoint header for HID Commands
|
||||
#define bmREQ_HID_OUT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE
|
||||
#define HID_REQUEST_SET_REPORT 0x09
|
||||
|
||||
/* Bluetooth HCI states for hci_task() */
|
||||
#define HCI_INIT_STATE 0
|
||||
#define HCI_RESET_STATE 1
|
||||
#define HCI_BDADDR_STATE 2
|
||||
#define HCI_LOCAL_VERSION_STATE 3
|
||||
#define HCI_SET_NAME_STATE 4
|
||||
#define HCI_SCANNING_STATE 5
|
||||
#define HCI_CONNECT_IN_STATE 6
|
||||
#define HCI_REMOTE_NAME_STATE 7
|
||||
#define HCI_CONNECTED_STATE 8
|
||||
#define HCI_DISABLE_SCAN_STATE 9
|
||||
#define HCI_DONE_STATE 10
|
||||
#define HCI_DISCONNECT_STATE 11
|
||||
|
||||
/* HCI event flags*/
|
||||
#define HCI_FLAG_CMD_COMPLETE 0x01
|
||||
#define HCI_FLAG_CONN_COMPLETE 0x02
|
||||
#define HCI_FLAG_DISCONN_COMPLETE 0x04
|
||||
#define HCI_FLAG_REMOTE_NAME_COMPLETE 0x08
|
||||
#define HCI_FLAG_INCOMING_REQUEST 0x10
|
||||
#define HCI_FLAG_READ_BDADDR 0x20
|
||||
#define HCI_FLAG_READ_VERSION 0x40
|
||||
|
||||
/*Macros for HCI event flag tests */
|
||||
#define hci_cmd_complete (hci_event_flag & HCI_FLAG_CMD_COMPLETE)
|
||||
#define hci_connect_complete (hci_event_flag & HCI_FLAG_CONN_COMPLETE)
|
||||
#define hci_disconnect_complete (hci_event_flag & HCI_FLAG_DISCONN_COMPLETE)
|
||||
#define hci_remote_name_complete (hci_event_flag & HCI_FLAG_REMOTE_NAME_COMPLETE)
|
||||
#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)
|
||||
|
||||
/* HCI Events managed */
|
||||
#define EV_CONNECT_COMPLETE 0x03
|
||||
#define EV_INCOMING_CONNECT 0x04
|
||||
#define EV_DISCONNECT_COMPLETE 0x05
|
||||
#define EV_AUTHENTICATION_COMPLETE 0x06
|
||||
#define EV_REMOTE_NAME_COMPLETE 0x07
|
||||
#define EV_ENCRYPTION_CHANGE 0x08
|
||||
#define EV_CHANGE_CONNECTION_LINK 0x09
|
||||
#define EV_ROLE_CHANGED 0x12
|
||||
#define EV_NUM_COMPLETE_PKT 0x13
|
||||
#define EV_PIN_CODE_REQUEST 0x16
|
||||
#define EV_LINK_KEY_REQUEST 0x17
|
||||
#define EV_LINK_KEY_NOTIFICATION 0x18
|
||||
#define EV_DATA_BUFFER_OVERFLOW 0x1A
|
||||
#define EV_MAX_SLOTS_CHANGE 0x1B
|
||||
#define EV_READ_REMOTE_VERSION_INFORMATION_COMPLETE 0x0C
|
||||
#define EV_QOS_SETUP_COMPLETE 0x0D
|
||||
#define EV_COMMAND_COMPLETE 0x0E
|
||||
#define EV_COMMAND_STATUS 0x0F
|
||||
#define EV_LOOPBACK_COMMAND 0x19
|
||||
#define EV_PAGE_SCAN_REP_MODE 0x20
|
||||
|
||||
/* L2CAP signaling commands */
|
||||
#define L2CAP_CMD_COMMAND_REJECT 0x01
|
||||
#define L2CAP_CMD_CONNECTION_REQUEST 0x02
|
||||
#define L2CAP_CMD_CONNECTION_RESPONSE 0x03
|
||||
#define L2CAP_CMD_CONFIG_REQUEST 0x04
|
||||
#define L2CAP_CMD_CONFIG_RESPONSE 0x05
|
||||
#define L2CAP_CMD_DISCONNECT_REQUEST 0x06
|
||||
#define L2CAP_CMD_DISCONNECT_RESPONSE 0x07
|
||||
#define L2CAP_CMD_INFORMATION_REQUEST 0x0A
|
||||
#define L2CAP_CMD_INFORMATION_RESPONSE 0x0B
|
||||
|
||||
// Used For Connection Response - Remember to Include High Byte
|
||||
#define PENDING 0x01
|
||||
#define SUCCESSFUL 0x00
|
||||
|
||||
// Used to determine if it is a Bluetooth dongle
|
||||
#define WI_SUBCLASS_RF 0x01 // RF Controller
|
||||
#define WI_PROTOCOL_BT 0x01 // Bluetooth Programming Interface
|
||||
|
||||
#define BTD_MAX_ENDPOINTS 4
|
||||
#define BTD_NUMSERVICES 4 // Max number of Bluetooth services
|
||||
|
||||
class BluetoothService { // All services should include this class
|
||||
public:
|
||||
virtual void ACLData(uint8_t* ACLData); // Used to pass acldata to the services
|
||||
virtual void Run(); // Used to run the different state machines
|
||||
virtual void Reset(); // Used to reset the services
|
||||
virtual void disconnect(); // Used to disconnect both the L2CAP Channel and the HCI Connection
|
||||
};
|
||||
|
||||
class BTD : public USBDeviceConfig, public UsbConfigXtracter {
|
||||
public:
|
||||
BTD(USB *p); // Constructor
|
||||
|
||||
// USBDeviceConfig implementation
|
||||
virtual uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);
|
||||
virtual uint8_t Release();
|
||||
virtual uint8_t Poll();
|
||||
virtual uint8_t GetAddress() { return bAddress; };
|
||||
virtual bool isReady() { return bPollEnable; };
|
||||
|
||||
// UsbConfigXtracter implementation, used to extract endpoint information
|
||||
virtual void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);
|
||||
|
||||
bool watingForConnection; // Use this to see if it is waiting for a incoming connection
|
||||
void disconnect() { // Used this void to disconnect all services
|
||||
for (uint8_t i=0; i<BTD_NUMSERVICES; i++)
|
||||
if (btService[i])
|
||||
btService[i]->disconnect(); // Disconnect both the L2CAP Channel and the HCI Connection
|
||||
};
|
||||
|
||||
/* Register bluetooth dongle members/services */
|
||||
int8_t registerServiceClass(BluetoothService *pService) {
|
||||
for (uint8_t i=0; i<BTD_NUMSERVICES; i++) {
|
||||
if (!btService[i]) {
|
||||
btService[i] = pService;
|
||||
return i; // Return ID
|
||||
}
|
||||
}
|
||||
return -1; // ErrorregisterServiceClass
|
||||
};
|
||||
|
||||
bool l2capConnectionClaimed; // This is used by the service to know when to store the device information
|
||||
|
||||
const char* btdName; // These are set by the SPP library
|
||||
const char* btdPin;
|
||||
|
||||
uint8_t my_bdaddr[6]; // The bluetooth dongles Bluetooth address
|
||||
uint16_t hci_handle; // HCI handle for the last connection
|
||||
uint8_t disc_bdaddr[6]; // Last incoming devices Bluetooth address
|
||||
uint8_t remote_name[30]; // First 30 chars of last remote name
|
||||
uint8_t hci_version;
|
||||
|
||||
/* HCI Commands */
|
||||
void HCI_Command(uint8_t* data, uint16_t nbytes);
|
||||
void hci_reset();
|
||||
void hci_read_bdaddr();
|
||||
void hci_read_local_version_information();
|
||||
void hci_set_local_name(const char* name);
|
||||
void hci_write_scan_enable();
|
||||
void hci_remote_name();
|
||||
void hci_accept_connection();
|
||||
void hci_write_scan_disable();
|
||||
void hci_disconnect(uint16_t handle);
|
||||
void hci_pin_code_request_reply(const char* key);
|
||||
void hci_pin_code_negative_request_reply();
|
||||
void hci_link_key_request_negative_reply();
|
||||
|
||||
/* L2CAP Commands */
|
||||
void L2CAP_Command(uint16_t handle, uint8_t* data, uint8_t nbytes, uint8_t channelLow = 0x01, uint8_t channelHigh = 0x00); // Standard L2CAP header: Channel ID (0x01) for ACL-U
|
||||
void l2cap_connection_response(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid, uint8_t result);
|
||||
void l2cap_config_request(uint16_t handle, uint8_t rxid, uint8_t* dcid);
|
||||
void l2cap_config_response(uint16_t handle, uint8_t rxid, uint8_t* scid);
|
||||
void l2cap_disconnection_request(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid);
|
||||
void l2cap_disconnection_response(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid);
|
||||
void l2cap_information_response(uint16_t handle, uint8_t rxid, uint8_t infoTypeLow, uint8_t infoTypeHigh);
|
||||
|
||||
protected:
|
||||
/* Mandatory USB members */
|
||||
USB *pUsb;
|
||||
uint8_t bAddress; // Device address
|
||||
EpInfo epInfo[BTD_MAX_ENDPOINTS]; // Endpoint info structure
|
||||
|
||||
uint8_t bConfNum; // Configuration number
|
||||
uint8_t bNumEP; // Total number of endpoints in the configuration
|
||||
uint32_t qNextPollTime; // Next poll time
|
||||
|
||||
#define BTD_CONTROL_PIPE 0 // Bluetooth dongles control endpoint
|
||||
static const uint8_t BTD_EVENT_PIPE; // HCI event endpoint index
|
||||
static const uint8_t BTD_DATAIN_PIPE; // ACL In endpoint index
|
||||
static const uint8_t BTD_DATAOUT_PIPE; // ACL Out endpoint index
|
||||
|
||||
void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr);
|
||||
|
||||
private:
|
||||
BluetoothService* btService[BTD_NUMSERVICES];
|
||||
|
||||
bool bPollEnable;
|
||||
uint8_t pollInterval;
|
||||
|
||||
/* variables used by high level HCI task */
|
||||
uint8_t hci_state; //current state of bluetooth hci connection
|
||||
uint16_t hci_counter; // counter used for bluetooth hci reset loops
|
||||
uint8_t hci_num_reset_loops; // this value indicate how many times it should read before trying to reset
|
||||
uint16_t hci_event_flag; // hci flags of received bluetooth events
|
||||
|
||||
uint8_t hcibuf[BULK_MAXPKTSIZE];//General purpose buffer for hci data
|
||||
uint8_t l2capinbuf[BULK_MAXPKTSIZE];//General purpose buffer for l2cap in data
|
||||
uint8_t l2capoutbuf[BULK_MAXPKTSIZE];//General purpose buffer for l2cap out data
|
||||
|
||||
/* State machines */
|
||||
void HCI_event_task(); // Poll the HCI event pipe
|
||||
void HCI_task(); // HCI state machine
|
||||
void ACL_event_task(); // ACL input pipe
|
||||
|
||||
/* Used to set the Bluetooth Address internally to the PS3 Controllers */
|
||||
void setBdaddr(uint8_t* BDADDR);
|
||||
void setMoveBdaddr(uint8_t* BDADDR);
|
||||
};
|
||||
#endif
|
|
@ -1,4 +1,4 @@
|
|||
The PS3BT.cpp, PS3BT.h, PS3USB.cpp, PS3USB.h, XBOXUSB.cpp, XBOXUSB.h, RFCOMM.cpp, and RFCOMM.h is developed by Kristian Lauszus
|
||||
The BTD.cpp, BTD.h, SPP.cpp, SPP.h, PS3BT.cpp, PS3BT.h, PS3USB.cpp, PS3USB.h, XBOXUSB.cpp, and XBOXUSB.h is developed by Kristian Lauszus
|
||||
|
||||
For more information regarding the PS3 protocol etc. visit my blog at: http://blog.tkjelectronics.dk/ or send me an email at kristianl at tkjelectronics dot dk.
|
||||
You could also visit the official wiki: https://github.com/TKJElectronics/USB_Host_Shield_2.0/wiki for information.
|
||||
|
|
284
PS3BT.h
284
PS3BT.h
|
@ -18,153 +18,49 @@
|
|||
#ifndef _ps3bt_h_
|
||||
#define _ps3bt_h_
|
||||
|
||||
#if defined(ARDUINO) && ARDUINO >= 100
|
||||
#include "Arduino.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#endif
|
||||
|
||||
#include "Usb.h"
|
||||
#include "confdescparser.h"
|
||||
|
||||
/* CSR Bluetooth data taken from descriptors */
|
||||
#define INT_MAXPKTSIZE 16 // max size for HCI data
|
||||
#define BULK_MAXPKTSIZE 64 // max size for ACL data
|
||||
|
||||
/* PS3 data taken from descriptors */
|
||||
#define EP_MAXPKTSIZE 64 // max size for data via USB
|
||||
|
||||
/* Endpoint types */
|
||||
#define EP_INTERRUPT 0x03
|
||||
|
||||
/* Names we give to the 3 ps3 pipes - this is only used for setting the bluetooth address into the ps3 controllers */
|
||||
#define PS3_CONTROL_PIPE 0
|
||||
#define PS3_OUTPUT_PIPE 1
|
||||
#define PS3_INPUT_PIPE 2
|
||||
|
||||
//PID and VID of the different devices
|
||||
#define PS3_VID 0x054C // Sony Corporation
|
||||
#define PS3_PID 0x0268 // PS3 Controller DualShock 3
|
||||
#define PS3NAVIGATION_PID 0x042F // Navigation controller
|
||||
#define PS3MOVE_PID 0x03D5 // Motion controller
|
||||
#include "BTD.h"
|
||||
|
||||
#define HID_BUFFERSIZE 50 // size of the buffer for the Playstation Motion Controller
|
||||
#define OUTPUT_REPORT_BUFFER_SIZE 48 //Size of the output report buffer for the controllers
|
||||
|
||||
// used in control endpoint header for HCI Commands
|
||||
#define bmREQ_HCI_OUT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE
|
||||
|
||||
// used in control endpoint header for HID Commands
|
||||
#define bmREQ_HID_OUT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE
|
||||
#define HID_REQUEST_SET_REPORT 0x09
|
||||
|
||||
/* Bluetooth HCI states for hci_task() */
|
||||
#define HCI_INIT_STATE 0
|
||||
#define HCI_RESET_STATE 1
|
||||
#define HCI_BDADDR_STATE 2
|
||||
#define HCI_LOCAL_VERSION_STATE 3
|
||||
#define HCI_SCANNING_STATE 4
|
||||
#define HCI_CONNECT_IN_STATE 5
|
||||
#define HCI_REMOTE_NAME_STATE 6
|
||||
#define HCI_CONNECTED_STATE 7
|
||||
#define HCI_DISABLE_SCAN 8
|
||||
#define HCI_DONE_STATE 9
|
||||
#define HCI_DISCONNECT_STATE 10
|
||||
|
||||
/* HCI event flags*/
|
||||
#define HCI_FLAG_CMD_COMPLETE 0x01
|
||||
#define HCI_FLAG_CONN_COMPLETE 0x02
|
||||
#define HCI_FLAG_DISCONN_COMPLETE 0x04
|
||||
#define HCI_FLAG_REMOTE_NAME_COMPLETE 0x08
|
||||
#define HCI_FLAG_INCOMING_REQUEST 0x10
|
||||
#define HCI_FLAG_READ_BDADDR 0x20
|
||||
#define HCI_FLAG_READ_VERSION 0x40
|
||||
|
||||
/*Macros for HCI event flag tests */
|
||||
#define hci_cmd_complete (hci_event_flag & HCI_FLAG_CMD_COMPLETE)
|
||||
#define hci_connect_complete (hci_event_flag & HCI_FLAG_CONN_COMPLETE)
|
||||
#define hci_disconnect_complete (hci_event_flag & HCI_FLAG_DISCONN_COMPLETE)
|
||||
#define hci_remote_name_complete (hci_event_flag & HCI_FLAG_REMOTE_NAME_COMPLETE)
|
||||
#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)
|
||||
|
||||
/* HCI Events managed */
|
||||
#define EV_COMMAND_COMPLETE 0x0E
|
||||
#define EV_COMMAND_STATUS 0x0F
|
||||
#define EV_CONNECT_COMPLETE 0x03
|
||||
#define EV_DISCONNECT_COMPLETE 0x05
|
||||
#define EV_NUM_COMPLETE_PKT 0x13
|
||||
#define EV_INQUIRY_COMPLETE 0x01
|
||||
#define EV_INQUIRY_RESULT 0x02
|
||||
#define EV_REMOTE_NAME_COMPLETE 0x07
|
||||
#define EV_INCOMING_CONNECT 0x04
|
||||
#define EV_ROLE_CHANGED 0x12
|
||||
#define EV_PAGE_SCAN_REP_MODE 0x20
|
||||
#define EV_DATA_BUFFER_OVERFLOW 0x1A
|
||||
#define EV_LOOPBACK_COMMAND 0x19
|
||||
#define EV_CHANGE_CONNECTION_LINK 0x09
|
||||
#define EV_AUTHENTICATION_COMPLETE 0x06
|
||||
|
||||
/* Bluetooth L2CAP states for L2CAP_task() */
|
||||
#define L2CAP_EV_WAIT 0
|
||||
#define L2CAP_EV_CONTROL_SETUP 1
|
||||
#define L2CAP_EV_CONTROL_REQUEST 2
|
||||
#define L2CAP_EV_CONTROL_SUCCESS 3
|
||||
#define L2CAP_EV_INTERRUPT_SETUP 4
|
||||
#define L2CAP_EV_INTERRUPT_REQUEST 5
|
||||
#define L2CAP_EV_INTERRUPT_SUCCESS 6
|
||||
#define L2CAP_EV_HID_ENABLE_SIXAXIS 7
|
||||
#define L2CAP_EV_HID_PS3_LED 8
|
||||
#define L2CAP_EV_L2CAP_DONE 9
|
||||
#define L2CAP_EV_INTERRUPT_DISCONNECT 10
|
||||
#define L2CAP_EV_CONTROL_DISCONNECT 11
|
||||
#define L2CAP_WAIT 0
|
||||
#define L2CAP_CONTROL_REQUEST 1
|
||||
#define L2CAP_CONTROL_SUCCESS 2
|
||||
#define L2CAP_INTERRUPT_SETUP 3
|
||||
#define L2CAP_INTERRUPT_REQUEST 4
|
||||
#define L2CAP_INTERRUPT_SUCCESS 5
|
||||
#define L2CAP_HID_ENABLE_SIXAXIS 6
|
||||
#define L2CAP_HID_PS3_LED 7
|
||||
#define L2CAP_DONE 8
|
||||
#define L2CAP_INTERRUPT_DISCONNECT 9
|
||||
#define L2CAP_CONTROL_DISCONNECT 10
|
||||
|
||||
/* L2CAP event flags */
|
||||
#define L2CAP_EV_CONTROL_CONNECTION_REQUEST 0x01
|
||||
#define L2CAP_EV_CONTROL_CONFIG_REQUEST 0x02
|
||||
#define L2CAP_EV_CONTROL_CONFIG_SUCCESS 0x04
|
||||
#define L2CAP_EV_INTERRUPT_CONNECTION_REQUEST 0x08
|
||||
#define L2CAP_EV_INTERRUPT_CONFIG_REQUEST 0x10
|
||||
#define L2CAP_EV_INTERRUPT_CONFIG_SUCCESS 0x20
|
||||
#define L2CAP_EV_CONTROL_DISCONNECT_RESPONSE 0x40
|
||||
#define L2CAP_EV_INTERRUPT_DISCONNECT_RESPONSE 0x80
|
||||
#define L2CAP_FLAG_CONNECTION_CONTROL_REQUEST 0x01
|
||||
#define L2CAP_FLAG_CONFIG_CONTROL_REQUEST 0x02
|
||||
#define L2CAP_FLAG_CONFIG_CONTROL_SUCCESS 0x04
|
||||
#define L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST 0x08
|
||||
#define L2CAP_FLAG_CONFIG_INTERRUPT_REQUEST 0x10
|
||||
#define L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS 0x20
|
||||
#define L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE 0x40
|
||||
#define L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE 0x80
|
||||
|
||||
/*Macros for L2CAP event flag tests */
|
||||
#define l2cap_control_connection_request (l2cap_event_flag & L2CAP_EV_CONTROL_CONNECTION_REQUEST)
|
||||
#define l2cap_control_config_request (l2cap_event_flag & L2CAP_EV_CONTROL_CONFIG_REQUEST)
|
||||
#define l2cap_control_config_success (l2cap_event_flag & L2CAP_EV_CONTROL_CONFIG_SUCCESS)
|
||||
#define l2cap_interrupt_connection_request (l2cap_event_flag & L2CAP_EV_INTERRUPT_CONNECTION_REQUEST)
|
||||
#define l2cap_interrupt_config_request (l2cap_event_flag & L2CAP_EV_INTERRUPT_CONFIG_REQUEST)
|
||||
#define l2cap_interrupt_config_success (l2cap_event_flag & L2CAP_EV_INTERRUPT_CONFIG_SUCCESS)
|
||||
#define l2cap_control_disconnect_response (l2cap_event_flag & L2CAP_EV_CONTROL_DISCONNECT_RESPONSE)
|
||||
#define l2cap_interrupt_disconnect_response (l2cap_event_flag & L2CAP_EV_INTERRUPT_DISCONNECT_RESPONSE)
|
||||
|
||||
/* L2CAP signaling commands */
|
||||
#define L2CAP_CMD_COMMAND_REJECT 0x01
|
||||
#define L2CAP_CMD_CONNECTION_REQUEST 0x02
|
||||
#define L2CAP_CMD_CONNECTION_RESPONSE 0x03
|
||||
#define L2CAP_CMD_CONFIG_REQUEST 0x04
|
||||
#define L2CAP_CMD_CONFIG_RESPONSE 0x05
|
||||
#define L2CAP_CMD_DISCONNECT_REQUEST 0x06
|
||||
#define L2CAP_CMD_DISCONNECT_RESPONSE 0x07
|
||||
#define l2cap_connection_request_control_flag (l2cap_event_flag & L2CAP_FLAG_CONNECTION_CONTROL_REQUEST)
|
||||
#define l2cap_config_request_control_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_CONTROL_REQUEST)
|
||||
#define l2cap_config_success_control_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_CONTROL_SUCCESS)
|
||||
#define l2cap_connection_request_interrupt_flag (l2cap_event_flag & L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST)
|
||||
#define l2cap_config_request_interrupt_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_INTERRUPT_REQUEST)
|
||||
#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)
|
||||
|
||||
/* Bluetooth L2CAP PSM */
|
||||
#define L2CAP_PSM_HID_CTRL 0x11 // HID_Control
|
||||
#define L2CAP_PSM_HID_INTR 0x13 // HID_Interrupt
|
||||
#define HID_CTRL_PSM 0x11 // HID_Control
|
||||
#define HID_INTR_PSM 0x13 // HID_Interrupt
|
||||
|
||||
// Used For Connection Response - Remember to Include High Byte
|
||||
#define PENDING 0x01
|
||||
#define SUCCESSFUL 0x00
|
||||
|
||||
// Used to determine if it is a Bluetooth dongle
|
||||
#define WI_SUBCLASS_RF 0x01
|
||||
#define WI_PROTOCOL_BT 0x01
|
||||
|
||||
#define PS3_MAX_ENDPOINTS 4
|
||||
|
||||
enum LED
|
||||
{
|
||||
enum LED {
|
||||
LED1 = 0x01,
|
||||
LED2 = 0x02,
|
||||
LED3 = 0x04,
|
||||
|
@ -177,8 +73,7 @@ enum LED
|
|||
LED9 = 0x0E,
|
||||
LED10 = 0x0F,
|
||||
};
|
||||
enum Colors
|
||||
{
|
||||
enum Colors {
|
||||
// Used to set the colors of the move controller
|
||||
Red = 0xFF0000, // r = 255, g = 0, b = 0
|
||||
Green = 0xFF00, // r = 0, g = 255, b = 0
|
||||
|
@ -192,8 +87,7 @@ enum Colors
|
|||
Off = 0x00, // r = 0, g = 0, b = 0
|
||||
};
|
||||
|
||||
enum Button
|
||||
{
|
||||
enum Button {
|
||||
// byte location | bit location
|
||||
|
||||
// Sixaxis Dualshcock 3 & Navigation controller
|
||||
|
@ -237,8 +131,7 @@ enum Button
|
|||
T_MOVE = (12 << 8) | 0x10, // covers 12 bits - we only need to read the top 8
|
||||
*/
|
||||
};
|
||||
enum AnalogButton
|
||||
{
|
||||
enum AnalogButton {
|
||||
//Sixaxis Dualshcock 3 & Navigation controller
|
||||
UP_ANALOG = 23,
|
||||
RIGHT_ANALOG = 24,
|
||||
|
@ -257,15 +150,13 @@ enum AnalogButton
|
|||
//Playstation Move Controller
|
||||
T_ANALOG = 15, // Both at byte 14 (last reading) and byte 15 (current reading)
|
||||
};
|
||||
enum AnalogHat
|
||||
{
|
||||
enum AnalogHat {
|
||||
LeftHatX = 15,
|
||||
LeftHatY = 16,
|
||||
RightHatX = 17,
|
||||
RightHatY = 18,
|
||||
};
|
||||
enum Sensor
|
||||
{
|
||||
enum Sensor {
|
||||
//Sensors inside the Sixaxis Dualshock 3 controller
|
||||
aX = 50,
|
||||
aY = 52,
|
||||
|
@ -287,13 +178,11 @@ enum Sensor
|
|||
mZmove = 49,
|
||||
mYmove = 50,
|
||||
};
|
||||
enum Angle
|
||||
{
|
||||
enum Angle {
|
||||
Pitch = 0x01,
|
||||
Roll = 0x02,
|
||||
};
|
||||
enum Status
|
||||
{
|
||||
enum Status {
|
||||
// byte location | bit location
|
||||
Plugged = (38 << 8) | 0x02,
|
||||
Unplugged = (38 << 8) | 0x03,
|
||||
|
@ -319,31 +208,20 @@ enum Status
|
|||
BluetoothRumble = (40 << 8) | 0x14,//Opperating by bluetooth and rumble is turned on
|
||||
Bluetooth = (40 << 8) | 0x16,//Opperating by bluetooth and rumble is turned off
|
||||
};
|
||||
enum Rumble
|
||||
{
|
||||
enum Rumble {
|
||||
RumbleHigh = 0x10,
|
||||
RumbleLow = 0x20,
|
||||
};
|
||||
|
||||
class PS3BT : public USBDeviceConfig, public UsbConfigXtracter
|
||||
{
|
||||
class PS3BT : public BluetoothService {
|
||||
public:
|
||||
PS3BT(USB *pUsb, uint8_t btadr5=0, uint8_t btadr4=0, uint8_t btadr3=0, uint8_t btadr2=0, uint8_t btadr1=0, uint8_t btadr0=0);
|
||||
PS3BT(BTD *pBtd, uint8_t btadr5=0, uint8_t btadr4=0, uint8_t btadr3=0, uint8_t btadr2=0, uint8_t btadr1=0, uint8_t btadr0=0);
|
||||
|
||||
// USBDeviceConfig implementation
|
||||
virtual uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);
|
||||
virtual uint8_t Release();
|
||||
virtual uint8_t Poll();
|
||||
virtual uint8_t GetAddress() { return bAddress; };
|
||||
virtual bool isReady() { return bPollEnable; };
|
||||
|
||||
// UsbConfigXtracter implementation
|
||||
virtual void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);
|
||||
|
||||
bool isWatingForConnection() { return watingForConnection; }; // Use this to indicate when it is ready for a incoming connection
|
||||
|
||||
void setBdaddr(uint8_t* BDADDR);
|
||||
void setMoveBdaddr(uint8_t* BDADDR);
|
||||
// BluetoothService implementation
|
||||
virtual void ACLData(uint8_t* ACLData); // Used to pass acldata to the services
|
||||
virtual void Run(); // Used to run part of the state maschine
|
||||
virtual void Reset(); // Use this to reset the service
|
||||
virtual void disconnect(); // Use this void to disconnect any of the controllers
|
||||
|
||||
/* PS3 Controller Commands */
|
||||
bool getButton(Button b);
|
||||
|
@ -354,7 +232,6 @@ public:
|
|||
bool getStatus(Status c);
|
||||
String getStatusString();
|
||||
String getTemperature();
|
||||
void disconnect(); // use this void to disconnect any of the controllers
|
||||
|
||||
/* HID Commands */
|
||||
/* Commands for Dualshock 3 and Navigation controller */
|
||||
|
@ -364,6 +241,7 @@ public:
|
|||
void setLedOff(LED a);
|
||||
void setLedOn(LED a);
|
||||
void setLedToggle(LED a);
|
||||
|
||||
/* Commands for Motion controller only */
|
||||
void moveSetBulb(uint8_t r, uint8_t g, uint8_t b);//Use this to set the Color using RGB values
|
||||
void moveSetBulb(Colors color);//Use this to set the Color using the predefined colors in "enum Colors"
|
||||
|
@ -376,39 +254,15 @@ public:
|
|||
bool buttonPressed;//Indicate if a button has been pressed
|
||||
bool buttonReleased;//Indicate if a button has been released
|
||||
|
||||
protected:
|
||||
/* mandatory members */
|
||||
USB *pUsb;
|
||||
uint8_t bAddress; // device address
|
||||
EpInfo epInfo[PS3_MAX_ENDPOINTS]; //endpoint info structure
|
||||
|
||||
uint8_t bConfNum; // configuration number
|
||||
uint8_t bNumEP; // total number of endpoints in the configuration
|
||||
uint32_t qNextPollTime; // next poll time
|
||||
|
||||
#define BTD_CONTROL_PIPE 0 // Bluetooth dongles control endpoint
|
||||
static const uint8_t BTD_EVENT_PIPE; // HCI event endpoint index
|
||||
static const uint8_t BTD_DATAIN_PIPE; // ACL In endpoint index
|
||||
static const uint8_t BTD_DATAOUT_PIPE; // ACL Out endpoint index
|
||||
|
||||
void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr);
|
||||
|
||||
private:
|
||||
bool bPollEnable;
|
||||
uint8_t pollInterval;
|
||||
bool watingForConnection;
|
||||
/* mandatory members */
|
||||
BTD *pBtd;
|
||||
|
||||
/*variables filled from HCI event management */
|
||||
void L2CAP_task(); // L2CAP state machine
|
||||
|
||||
/* Variables filled from HCI event management */
|
||||
int16_t hci_handle;
|
||||
uint8_t disc_bdaddr[6]; // the bluetooth address is always 6 bytes
|
||||
uint8_t remote_name[30]; // first 30 chars of remote name
|
||||
uint8_t hci_version;
|
||||
|
||||
/* variables used by high level HCI task */
|
||||
uint8_t hci_state; //current state of bluetooth hci connection
|
||||
uint16_t hci_counter; // counter used for bluetooth hci reset loops
|
||||
uint8_t hci_num_reset_loops; // this value indicate how many times it should read before trying to reset
|
||||
uint16_t hci_event_flag;// hci flags of received bluetooth events
|
||||
|
||||
/* variables used by high level L2CAP task */
|
||||
uint8_t l2cap_state;
|
||||
|
@ -421,10 +275,7 @@ private:
|
|||
uint32_t timerHID;// timer used see if there has to be a delay before a new HID command
|
||||
uint32_t timerBulbRumble;// used to continuously set PS3 Move controller Bulb and rumble values
|
||||
|
||||
uint8_t my_bdaddr[6]; // Change to your dongles Bluetooth address in the constructor
|
||||
uint8_t hcibuf[BULK_MAXPKTSIZE];//General purpose buffer for hci data
|
||||
uint8_t l2capinbuf[BULK_MAXPKTSIZE]; // General purpose buffer for l2cap in data
|
||||
uint8_t l2capoutbuf[BULK_MAXPKTSIZE];//General purpose buffer for l2cap out data
|
||||
uint8_t HIDBuffer[HID_BUFFERSIZE];// Used to store HID commands
|
||||
uint8_t HIDMoveBuffer[HID_BUFFERSIZE];// Used to store HID commands for the Move controller
|
||||
|
||||
|
@ -435,36 +286,9 @@ private:
|
|||
uint8_t interrupt_dcid[2];//0x0041
|
||||
uint8_t identifier;//Identifier for connection
|
||||
|
||||
void HCI_event_task(); //poll the HCI event pipe
|
||||
void HCI_task(); // HCI state machine
|
||||
void ACL_event_task(); // start polling the ACL input pipe too, though discard data until connected
|
||||
void L2CAP_task(); // L2CAP state machine
|
||||
|
||||
void readReport(); // read incoming data
|
||||
void printReport(); // print incoming date - Uncomment for debugging
|
||||
|
||||
/* HCI Commands */
|
||||
void HCI_Command(uint8_t* data, uint16_t nbytes);
|
||||
void hci_reset();
|
||||
void hci_write_scan_enable();
|
||||
void hci_write_scan_disable();
|
||||
void hci_read_bdaddr();
|
||||
void hci_read_local_version_information();
|
||||
void hci_accept_connection();
|
||||
void hci_remote_name();
|
||||
void hci_disconnect();
|
||||
|
||||
/* L2CAP Commands */
|
||||
void L2CAP_Command(uint8_t* data, uint16_t nbytes);
|
||||
void l2cap_connection_response(uint8_t rxid, uint8_t dcid[], uint8_t scid[], uint8_t result);
|
||||
void l2cap_config_request(uint8_t rxid, uint8_t dcid[]);
|
||||
void l2cap_config_response(uint8_t rxid, uint8_t scid[]);
|
||||
void l2cap_disconnection_request(uint8_t rxid, uint8_t dcid[], uint8_t scid[]);
|
||||
void l2cap_disconnection_response(uint8_t rxid, uint8_t dcid[], uint8_t scid[]);
|
||||
|
||||
/* HID Commands */
|
||||
void HID_Command(uint8_t* data, uint16_t nbytes);
|
||||
void HIDMove_Command(uint8_t* data, uint16_t nbytes);
|
||||
void HID_Command(uint8_t* data, uint8_t nbytes);
|
||||
void HIDMove_Command(uint8_t* data, uint8_t nbytes);
|
||||
void enable_sixaxis();//Command used to enable the Dualshock 3 and Navigation controller to send data via USB
|
||||
};
|
||||
#endif
|
1596
RFCOMM.cpp
1596
RFCOMM.cpp
File diff suppressed because it is too large
Load diff
339
RFCOMM.h
339
RFCOMM.h
|
@ -1,339 +0,0 @@
|
|||
/* Copyright (C) 2012 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 _rfcomm_h_
|
||||
#define _rfcomm_h_
|
||||
|
||||
#if defined(ARDUINO) && ARDUINO >= 100
|
||||
#include "Arduino.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#endif
|
||||
|
||||
#include "Usb.h"
|
||||
#include "confdescparser.h"
|
||||
|
||||
/* CSR Bluetooth data taken from descriptors */
|
||||
#define BULK_MAXPKTSIZE 64 // max size for ACL data
|
||||
|
||||
// used in control endpoint header for HCI Commands
|
||||
#define bmREQ_HCI_OUT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE
|
||||
|
||||
/* Bluetooth HCI states for hci_task() */
|
||||
#define HCI_INIT_STATE 0
|
||||
#define HCI_RESET_STATE 1
|
||||
#define HCI_BDADDR_STATE 2
|
||||
#define HCI_SET_NAME_STATE 3
|
||||
#define HCI_SCANNING_STATE 4
|
||||
#define HCI_CONNECT_IN_STATE 5
|
||||
#define HCI_REMOTE_NAME_STATE 6
|
||||
#define HCI_CONNECTED_STATE 7
|
||||
#define HCI_DISABLE_SCAN 8
|
||||
#define HCI_DONE_STATE 9
|
||||
#define HCI_DISCONNECT_STATE 10
|
||||
|
||||
/* HCI event flags*/
|
||||
#define HCI_FLAG_CMD_COMPLETE 0x01
|
||||
#define HCI_FLAG_CONN_COMPLETE 0x02
|
||||
#define HCI_FLAG_DISCONN_COMPLETE 0x04
|
||||
#define HCI_FLAG_REMOTE_NAME_COMPLETE 0x08
|
||||
#define HCI_FLAG_INCOMING_REQUEST 0x10
|
||||
#define HCI_FLAG_READ_BDADDR 0x20
|
||||
|
||||
/*Macros for HCI event flag tests */
|
||||
#define hci_cmd_complete (hci_event_flag & HCI_FLAG_CMD_COMPLETE)
|
||||
#define hci_connect_complete (hci_event_flag & HCI_FLAG_CONN_COMPLETE)
|
||||
#define hci_disconnect_complete (hci_event_flag & HCI_FLAG_DISCONN_COMPLETE)
|
||||
#define hci_remote_name_complete (hci_event_flag & HCI_FLAG_REMOTE_NAME_COMPLETE)
|
||||
#define hci_incoming_connect_request (hci_event_flag & HCI_FLAG_INCOMING_REQUEST)
|
||||
#define hci_read_bdaddr_complete (hci_event_flag & HCI_FLAG_READ_BDADDR)
|
||||
|
||||
/* HCI Events managed */
|
||||
#define EV_COMMAND_COMPLETE 0x0E
|
||||
#define EV_COMMAND_STATUS 0x0F
|
||||
#define EV_CONNECT_COMPLETE 0x03
|
||||
#define EV_DISCONNECT_COMPLETE 0x05
|
||||
#define EV_NUM_COMPLETE_PKT 0x13
|
||||
#define EV_INQUIRY_COMPLETE 0x01
|
||||
#define EV_INQUIRY_RESULT 0x02
|
||||
#define EV_REMOTE_NAME_COMPLETE 0x07
|
||||
#define EV_INCOMING_CONNECT 0x04
|
||||
#define EV_ROLE_CHANGED 0x12
|
||||
#define EV_PAGE_SCAN_REP_MODE 0x20
|
||||
#define EV_DATA_BUFFER_OVERFLOW 0x1A
|
||||
#define EV_LOOPBACK_COMMAND 0x19
|
||||
#define EV_CHANGE_CONNECTION_LINK 0x09
|
||||
#define EV_AUTHENTICATION_COMPLETE 0x06
|
||||
#define EV_MAX_SLOTS_CHANGE 0x1B
|
||||
#define EV_PIN_CODE_REQUEST 0x16
|
||||
#define EV_LINK_KEY_REQUEST 0x17
|
||||
#define EV_QOS_SETUP_COMPLETE 0x0D
|
||||
#define EV_LINK_KEY_NOTIFICATION 0x18
|
||||
#define EV_ENCRYPTION_CHANGE 0x08
|
||||
#define EV_READ_REMOTE_VERSION_INFORMATION_COMPLETE 0x0C
|
||||
|
||||
/* Bluetooth L2CAP states for SDP_task() and RFCOMM_task() */
|
||||
#define L2CAP_SDP_WAIT 0
|
||||
#define L2CAP_SDP_SETUP 1
|
||||
#define L2CAP_SDP_REQUEST 2
|
||||
#define L2CAP_SDP_SUCCESS 3
|
||||
#define L2CAP_SDP_DONE 4
|
||||
#define L2CAP_RFCOMM_WAIT 5
|
||||
#define L2CAP_RFCOMM_SETUP 6
|
||||
#define L2CAP_RFCOMM_REQUEST 7
|
||||
#define L2CAP_RFCOMM_SUCCESS 8
|
||||
#define L2CAP_RFCOMM_DONE 9
|
||||
#define L2CAP_DISCONNECT_RESPONSE 10
|
||||
|
||||
/* L2CAP event flags */
|
||||
#define L2CAP_FLAG_CONNECTION_SDP_REQUEST 0x001
|
||||
#define L2CAP_FLAG_CONNECTION_RFCOMM_REQUEST 0x002
|
||||
#define L2CAP_FLAG_CONFIG_SDP_REQUEST 0x004
|
||||
#define L2CAP_FLAG_CONFIG_RFCOMM_REQUEST 0x008
|
||||
#define L2CAP_FLAG_CONFIG_SDP_SUCCESS 0x010
|
||||
#define L2CAP_FLAG_CONFIG_RFCOMM_SUCCESS 0x020
|
||||
#define L2CAP_FLAG_DISCONNECT_SDP_REQUEST 0x040
|
||||
#define L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST 0x080
|
||||
#define L2CAP_FLAG_DISCONNECT_RESPONSE 0x100
|
||||
|
||||
/* Macros for L2CAP event flag tests */
|
||||
#define l2cap_connection_request_sdp_flag (l2cap_event_flag & L2CAP_FLAG_CONNECTION_SDP_REQUEST)
|
||||
#define l2cap_connection_request_rfcomm_flag (l2cap_event_flag & L2CAP_FLAG_CONNECTION_RFCOMM_REQUEST)
|
||||
#define l2cap_config_request_sdp_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_SDP_REQUEST)
|
||||
#define l2cap_config_request_rfcomm_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_RFCOMM_REQUEST)
|
||||
#define l2cap_config_success_sdp_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_SDP_SUCCESS)
|
||||
#define l2cap_config_success_rfcomm_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_RFCOMM_SUCCESS)
|
||||
#define l2cap_disconnect_request_sdp_flag (l2cap_event_flag & L2CAP_FLAG_DISCONNECT_SDP_REQUEST)
|
||||
#define l2cap_disconnect_request_rfcomm_flag (l2cap_event_flag & L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST)
|
||||
#define l2cap_disconnect_response_flag (l2cap_event_flag & L2CAP_FLAG_DISCONNECT_RESPONSE)
|
||||
|
||||
/* L2CAP signaling commands */
|
||||
#define L2CAP_CMD_COMMAND_REJECT 0x01
|
||||
#define L2CAP_CMD_CONNECTION_REQUEST 0x02
|
||||
#define L2CAP_CMD_CONNECTION_RESPONSE 0x03
|
||||
#define L2CAP_CMD_CONFIG_REQUEST 0x04
|
||||
#define L2CAP_CMD_CONFIG_RESPONSE 0x05
|
||||
#define L2CAP_CMD_DISCONNECT_REQUEST 0x06
|
||||
#define L2CAP_CMD_DISCONNECT_RESPONSE 0x07
|
||||
#define L2CAP_CMD_INFORMATION_REQUEST 0x0A
|
||||
#define L2CAP_CMD_INFORMATION_RESPONSE 0x0B
|
||||
|
||||
/* Bluetooth L2CAP PSM */
|
||||
#define SDP_PSM 0x01 // Service Discovery Protocol PSM Value
|
||||
#define RFCOMM_PSM 0x03 // RFCOMM PSM Value
|
||||
|
||||
// Used For Connection Response - Remember to Include High Byte
|
||||
#define PENDING 0x01
|
||||
#define SUCCESSFUL 0x00
|
||||
|
||||
// Used to determine if it is a Bluetooth dongle
|
||||
#define WI_SUBCLASS_RF 0x01 // RF Controller
|
||||
#define WI_PROTOCOL_BT 0x01 // Bluetooth Programming Interface
|
||||
|
||||
#define BTD_MAX_ENDPOINTS 4
|
||||
|
||||
/* Used for SDP */
|
||||
#define SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST_PDU 0x06 // See the RFCOMM specs
|
||||
#define SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU 0x07 // See the RFCOMM specs
|
||||
#define SERIALPORT_UUID 0x1101 // See http://www.bluetooth.org/Technical/AssignedNumbers/service_discovery.htm
|
||||
#define L2CAP_UUID 0x0100
|
||||
|
||||
/* Used for RFCOMM */
|
||||
#define RFCOMM_SABM 0x2F
|
||||
#define RFCOMM_UA 0x63
|
||||
#define RFCOMM_UIH 0xEF
|
||||
//#define RFCOMM_DM 0x0F
|
||||
#define RFCOMM_DISC 0x43
|
||||
|
||||
#define extendAddress 0x01 // Allways 1
|
||||
|
||||
// Multiplexer message types
|
||||
#define BT_RFCOMM_PN_CMD 0x83
|
||||
#define BT_RFCOMM_PN_RSP 0x81
|
||||
#define BT_RFCOMM_MSC_CMD 0xE3
|
||||
#define BT_RFCOMM_MSC_RSP 0xE1
|
||||
#define BT_RFCOMM_RPN_CMD 0x93
|
||||
#define BT_RFCOMM_RPN_RSP 0x91
|
||||
/*
|
||||
#define BT_RFCOMM_TEST_CMD 0x23
|
||||
#define BT_RFCOMM_TEST_RSP 0x21
|
||||
#define BT_RFCOMM_FCON_CMD 0xA3
|
||||
#define BT_RFCOMM_FCON_RSP 0xA1
|
||||
#define BT_RFCOMM_FCOFF_CMD 0x63
|
||||
#define BT_RFCOMM_FCOFF_RSP 0x61
|
||||
#define BT_RFCOMM_RLS_CMD 0x53
|
||||
#define BT_RFCOMM_RLS_RSP 0x51
|
||||
#define BT_RFCOMM_NSC_RSP 0x11
|
||||
*/
|
||||
|
||||
class RFCOMM : public USBDeviceConfig, public UsbConfigXtracter {
|
||||
public:
|
||||
RFCOMM(USB *p, const char* name = "Arduino", const char* pin = "1234");
|
||||
|
||||
// USBDeviceConfig implementation
|
||||
virtual uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);
|
||||
virtual uint8_t Release();
|
||||
virtual uint8_t Poll();
|
||||
virtual uint8_t GetAddress() { return bAddress; };
|
||||
virtual bool isReady() { return bPollEnable; };
|
||||
|
||||
// UsbConfigXtracter implementation
|
||||
virtual void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);
|
||||
|
||||
bool isWatingForConnection() { return watingForConnection; }; // Use this to indicate when it is ready for a incoming connection
|
||||
|
||||
void disconnect(); // Used this void to disconnect the virtual serial port
|
||||
bool connected;// Variable used to indicate if the connection is established
|
||||
|
||||
/* Serial commands currently supported */
|
||||
void print(const char* data); // Used to send strings
|
||||
void print(uint8_t data); // Used to send single bytes
|
||||
void print(uint8_t* array, uint8_t length); // Used to send arrays
|
||||
void print(const __FlashStringHelper *); // Used to print strings stored in flash
|
||||
|
||||
void println(const char* data); // Include newline and carriage return
|
||||
void println(uint8_t data); // Include newline and carriage return
|
||||
void println(uint8_t* array, uint8_t length); // Include newline and carriage return
|
||||
void println(const __FlashStringHelper *); // Include newline and carriage return
|
||||
|
||||
uint8_t available() { return rfcommAvailable; }; // Get the bytes waiting to be read
|
||||
uint8_t read(); // Used to read the buffer
|
||||
|
||||
protected:
|
||||
/* mandatory members */
|
||||
USB *pUsb;
|
||||
uint8_t bAddress; // device address
|
||||
EpInfo epInfo[BTD_MAX_ENDPOINTS]; //endpoint info structure
|
||||
|
||||
uint8_t bConfNum; // configuration number
|
||||
uint8_t bNumEP; // total number of endpoints in the configuration
|
||||
uint32_t qNextPollTime; // next poll time
|
||||
|
||||
#define BTD_CONTROL_PIPE 0 // Bluetooth dongles control endpoint
|
||||
static const uint8_t BTD_EVENT_PIPE; // HCI event endpoint index
|
||||
static const uint8_t BTD_DATAIN_PIPE; // ACL In endpoint index
|
||||
static const uint8_t BTD_DATAOUT_PIPE; // ACL Out endpoint index
|
||||
|
||||
void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr);
|
||||
|
||||
private:
|
||||
const char* btdName;
|
||||
const char* btdPin;
|
||||
|
||||
bool bPollEnable;
|
||||
uint8_t pollInterval;
|
||||
bool watingForConnection;
|
||||
|
||||
/* Set true when a channel is created */
|
||||
bool SDPConnected;
|
||||
bool RFCOMMConnected;
|
||||
|
||||
/*variables filled from HCI event management */
|
||||
uint16_t hci_handle;
|
||||
uint8_t my_bdaddr[6]; // The bluetooth dongles Bluetooth address
|
||||
uint8_t disc_bdaddr[6]; // the bluetooth address is always 6 bytes
|
||||
uint8_t remote_name[30]; // first 30 chars of remote name
|
||||
|
||||
/* variables used by high level HCI task */
|
||||
uint8_t hci_state; //current state of bluetooth hci connection
|
||||
uint16_t hci_counter; // counter used for bluetooth hci reset loops
|
||||
uint8_t hci_num_reset_loops; // this value indicate how many times it should read before trying to reset
|
||||
uint16_t hci_event_flag;// hci flags of received bluetooth events
|
||||
|
||||
/* variables used by high level L2CAP task */
|
||||
uint8_t l2cap_sdp_state;
|
||||
uint8_t l2cap_rfcomm_state;
|
||||
uint16_t l2cap_event_flag;// l2cap flags of received bluetooth events
|
||||
|
||||
uint8_t hcibuf[BULK_MAXPKTSIZE];//General purpose buffer for hci data
|
||||
uint8_t l2capinbuf[BULK_MAXPKTSIZE];//General purpose buffer for l2cap in data
|
||||
uint8_t l2capoutbuf[BULK_MAXPKTSIZE];//General purpose buffer for l2cap out data
|
||||
uint8_t rfcommbuf[BULK_MAXPKTSIZE]; // Buffer for RFCOMM Data
|
||||
|
||||
/* L2CAP Channels */
|
||||
uint8_t sdp_scid[2]; // L2CAP source CID for SDP
|
||||
uint8_t sdp_dcid[2]; // 0x0050
|
||||
uint8_t rfcomm_scid[2]; // L2CAP source CID for RFCOMM
|
||||
uint8_t rfcomm_dcid[2]; // 0x0051
|
||||
uint8_t identifier; // Identifier for command
|
||||
|
||||
/* RFCOMM Variables */
|
||||
uint8_t rfcommChannel;
|
||||
uint8_t rfcommChannelPermanent;
|
||||
uint8_t rfcommDirection;
|
||||
uint8_t rfcommCommandResponse;
|
||||
uint8_t rfcommChannelType;
|
||||
uint8_t rfcommPfBit;
|
||||
|
||||
unsigned long timer;
|
||||
bool waitForLastCommand;
|
||||
bool creditSent;
|
||||
|
||||
uint8_t rfcommDataBuffer[256]; // Create a 256 sized buffer for incoming data
|
||||
uint8_t rfcommAvailable;
|
||||
|
||||
bool firstMessage; // Used to see if it's the first SDP request received
|
||||
uint8_t bytesReceived; // Counter to see when it's time to send more credit
|
||||
|
||||
/* State machines */
|
||||
void HCI_event_task(); //poll the HCI event pipe
|
||||
void HCI_task(); // HCI state machine
|
||||
void ACL_event_task(); // start polling the ACL input pipe too, though discard data until connected
|
||||
void SDP_task(); // SDP state machine
|
||||
void RFCOMM_task(); // RFCOMM state machine
|
||||
|
||||
void readReport(); // read incoming data
|
||||
void printReport(); // print incoming date - Uncomment "#define PRINTREPORT" to print incoming data debugging
|
||||
|
||||
/* HCI Commands */
|
||||
void HCI_Command(uint8_t* data, uint16_t nbytes);
|
||||
void hci_reset();
|
||||
void hci_write_scan_enable();
|
||||
void hci_write_scan_disable();
|
||||
void hci_read_bdaddr();
|
||||
void hci_accept_connection();
|
||||
void hci_remote_name();
|
||||
void hci_set_local_name(const char* name);
|
||||
void hci_pin_code_request_reply(const char* key);
|
||||
void hci_link_key_request_negative_reply();
|
||||
void hci_disconnect();
|
||||
|
||||
/* L2CAP Commands */
|
||||
void L2CAP_Command(uint8_t* data, uint8_t nbytes, uint8_t channelLow = 0x01, uint8_t channelHigh = 0x00); // Standard L2CAP header: Channel ID (0x01) for ACL-U
|
||||
void l2cap_connection_response(uint8_t rxid, uint8_t* dcid, uint8_t* scid, uint8_t result);
|
||||
void l2cap_config_request(uint8_t rxid, uint8_t* dcid);
|
||||
void l2cap_config_response(uint8_t rxid, uint8_t* scid);
|
||||
void l2cap_disconnection_request(uint8_t rxid, uint8_t* dcid, uint8_t* scid);
|
||||
void l2cap_disconnection_response(uint8_t rxid, uint8_t* dcid, uint8_t* scid);
|
||||
void l2cap_information_response(uint8_t rxid, uint8_t infoTypeLow, uint8_t infoTypeHigh);
|
||||
|
||||
/* SDP Commands */
|
||||
void SDP_Command(uint8_t* data, uint8_t nbytes);
|
||||
void serviceNotSupported(uint8_t transactionIDHigh, uint8_t transactionIDLow);
|
||||
void serialPortResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow);
|
||||
void serialPortResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow);
|
||||
void l2capResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow);
|
||||
void l2capResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow);
|
||||
|
||||
/* RFCOMM Commands */
|
||||
void RFCOMM_Command(uint8_t* data, uint8_t nbytes);
|
||||
void sendRfcomm(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t* data, uint8_t length);
|
||||
void sendRfcommCredit(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t credit);
|
||||
uint8_t calcFcs(uint8_t *data);
|
||||
uint8_t __crc(uint8_t* data);
|
||||
};
|
||||
#endif
|
837
SPP.cpp
Normal file
837
SPP.cpp
Normal file
|
@ -0,0 +1,837 @@
|
|||
/* Copyright (C) 2012 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 "SPP.h"
|
||||
#define DEBUG // Uncomment to print data for debugging
|
||||
//#define EXTRADEBUG // Uncomment to get even more debugging data
|
||||
//#define PRINTREPORT // Uncomment to print the report sent to the Arduino
|
||||
|
||||
/*
|
||||
* CRC (reversed crc) lookup table as calculated by the table generator in ETSI TS 101 369 V6.3.0.
|
||||
*/
|
||||
const uint8_t rfcomm_crc_table[256] PROGMEM = { /* reversed, 8-bit, poly=0x07 */
|
||||
0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B,
|
||||
0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67,
|
||||
0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43,
|
||||
0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F,
|
||||
0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, 0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B,
|
||||
0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17,
|
||||
0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33,
|
||||
0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, 0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F,
|
||||
0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95, 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B,
|
||||
0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, 0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87,
|
||||
0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD, 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3,
|
||||
0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF,
|
||||
0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB,
|
||||
0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, 0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7,
|
||||
0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3,
|
||||
0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF
|
||||
};
|
||||
|
||||
SPP::SPP(BTD *p, const char* name, const char* pin):
|
||||
pBtd(p) // Pointer to BTD class instance - mandatory
|
||||
{
|
||||
if (pBtd)
|
||||
pBtd->registerServiceClass(this); // Register it as a Bluetooth service
|
||||
|
||||
pBtd->btdName = name;
|
||||
pBtd->btdPin = pin;
|
||||
|
||||
/* Set device cid for the SDP and RFCOMM channelse */
|
||||
sdp_dcid[0] = 0x50; // 0x0050
|
||||
sdp_dcid[1] = 0x00;
|
||||
rfcomm_dcid[0] = 0x51; // 0x0051
|
||||
rfcomm_dcid[1] = 0x00;
|
||||
|
||||
Reset();
|
||||
}
|
||||
void SPP::Reset() {
|
||||
connected = false;
|
||||
RFCOMMConnected = false;
|
||||
SDPConnected = false;
|
||||
l2cap_sdp_state = L2CAP_SDP_WAIT;
|
||||
l2cap_rfcomm_state = L2CAP_RFCOMM_WAIT;
|
||||
l2cap_event_flag = 0;
|
||||
}
|
||||
void SPP::disconnect(){
|
||||
connected = false;
|
||||
// First the two L2CAP channels has to be disconencted and then the HCI connection
|
||||
if(RFCOMMConnected)
|
||||
pBtd->l2cap_disconnection_request(hci_handle,0x0A, rfcomm_scid, rfcomm_dcid);
|
||||
if(RFCOMMConnected && SDPConnected)
|
||||
delay(1); // Add delay between commands
|
||||
if(SDPConnected)
|
||||
pBtd->l2cap_disconnection_request(hci_handle,0x0B, sdp_scid, sdp_dcid);
|
||||
l2cap_sdp_state = L2CAP_DISCONNECT_RESPONSE;
|
||||
}
|
||||
void SPP::ACLData(uint8_t* l2capinbuf) {
|
||||
if(!pBtd->l2capConnectionClaimed && !connected && !RFCOMMConnected && !SDPConnected) {
|
||||
if (l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) {
|
||||
if(((l2capinbuf[12] | (l2capinbuf[13] << 8)) == SDP_PSM) || ((l2capinbuf[12] | (l2capinbuf[13] << 8)) == RFCOMM_PSM)) {
|
||||
pBtd->l2capConnectionClaimed = true; // Claim that the incoming connection belongs to this service
|
||||
hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection
|
||||
l2cap_sdp_state = L2CAP_SDP_WAIT; // Reset state
|
||||
l2cap_rfcomm_state = L2CAP_RFCOMM_WAIT; // Reset state
|
||||
}
|
||||
}
|
||||
}
|
||||
if (((l2capinbuf[0] | (l2capinbuf[1] << 8)) == (hci_handle | 0x2000))) { // acl_handle_ok
|
||||
if ((l2capinbuf[6] | (l2capinbuf[7] << 8)) == 0x0001) { //l2cap_control - Channel ID for ACL-U
|
||||
if (l2capinbuf[8] == L2CAP_CMD_COMMAND_REJECT) {
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nL2CAP Command Rejected - Reason: "));
|
||||
PrintHex<uint8_t>(l2capinbuf[13]);
|
||||
Notify(PSTR(" "));
|
||||
PrintHex<uint8_t>(l2capinbuf[12]);
|
||||
Notify(PSTR(" Data: "));
|
||||
PrintHex<uint8_t>(l2capinbuf[17]);
|
||||
Notify(PSTR(" "));
|
||||
PrintHex<uint8_t>(l2capinbuf[16]);
|
||||
Notify(PSTR(" "));
|
||||
PrintHex<uint8_t>(l2capinbuf[15]);
|
||||
Notify(PSTR(" "));
|
||||
PrintHex<uint8_t>(l2capinbuf[14]);
|
||||
#endif
|
||||
} else if (l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) {
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR("\r\nL2CAP Connection Request - PSM: "));
|
||||
PrintHex<uint8_t>(l2capinbuf[13]);
|
||||
Notify(PSTR(" "));
|
||||
PrintHex<uint8_t>(l2capinbuf[12]);
|
||||
Notify(PSTR(" SCID: "));
|
||||
PrintHex<uint8_t>(l2capinbuf[15]);
|
||||
Notify(PSTR(" "));
|
||||
PrintHex<uint8_t>(l2capinbuf[14]);
|
||||
Notify(PSTR(" Identifier: "));
|
||||
PrintHex<uint8_t>(l2capinbuf[9]);
|
||||
#endif
|
||||
if ((l2capinbuf[12] | (l2capinbuf[13] << 8)) == SDP_PSM) { // It doesn't matter if it receives another reqeust, since it waits for the channel to disconnect in the L2CAP_SDP_DONE state, and the l2cap_event_flag will be cleared if so
|
||||
identifier = l2capinbuf[9];
|
||||
sdp_scid[0] = l2capinbuf[14];
|
||||
sdp_scid[1] = l2capinbuf[15];
|
||||
l2cap_event_flag |= L2CAP_FLAG_CONNECTION_SDP_REQUEST;
|
||||
} else if ((l2capinbuf[12] | (l2capinbuf[13] << 8)) == RFCOMM_PSM) { // ----- || -----
|
||||
identifier = l2capinbuf[9];
|
||||
rfcomm_scid[0] = l2capinbuf[14];
|
||||
rfcomm_scid[1] = l2capinbuf[15];
|
||||
l2cap_event_flag |= L2CAP_FLAG_CONNECTION_RFCOMM_REQUEST;
|
||||
}
|
||||
} else if (l2capinbuf[8] == L2CAP_CMD_CONFIG_RESPONSE) {
|
||||
if ((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) { // Success
|
||||
if (l2capinbuf[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) {
|
||||
//Serial.print("\r\nSDP Configuration Complete");
|
||||
l2cap_event_flag |= L2CAP_FLAG_CONFIG_SDP_SUCCESS;
|
||||
}
|
||||
else if (l2capinbuf[12] == rfcomm_dcid[0] && l2capinbuf[13] == rfcomm_dcid[1]) {
|
||||
//Serial.print("\r\nRFCOMM Configuration Complete");
|
||||
l2cap_event_flag |= L2CAP_FLAG_CONFIG_RFCOMM_SUCCESS;
|
||||
}
|
||||
}
|
||||
} else if (l2capinbuf[8] == L2CAP_CMD_CONFIG_REQUEST) {
|
||||
if (l2capinbuf[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) {
|
||||
//Serial.print("\r\nSDP Configuration Request");
|
||||
identifier = l2capinbuf[9];
|
||||
l2cap_event_flag |= L2CAP_FLAG_CONFIG_SDP_REQUEST;
|
||||
}
|
||||
else if (l2capinbuf[12] == rfcomm_dcid[0] && l2capinbuf[13] == rfcomm_dcid[1]) {
|
||||
//Serial.print("\r\nRFCOMM Configuration Request");
|
||||
identifier = l2capinbuf[9];
|
||||
l2cap_event_flag |= L2CAP_FLAG_CONFIG_RFCOMM_REQUEST;
|
||||
}
|
||||
} else if (l2capinbuf[8] == L2CAP_CMD_DISCONNECT_REQUEST) {
|
||||
if (l2capinbuf[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) {
|
||||
//Notify(PSTR("\r\nDisconnect Request: SDP Channel"));
|
||||
identifier = l2capinbuf[9];
|
||||
l2cap_event_flag |= L2CAP_FLAG_DISCONNECT_SDP_REQUEST;
|
||||
} else if (l2capinbuf[12] == rfcomm_dcid[0] && l2capinbuf[13] == rfcomm_dcid[1]) {
|
||||
//Notify(PSTR("\r\nDisconnect Request: RFCOMM Channel"));
|
||||
identifier = l2capinbuf[9];
|
||||
l2cap_event_flag |= L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST;
|
||||
}
|
||||
} else if (l2capinbuf[8] == L2CAP_CMD_DISCONNECT_RESPONSE) {
|
||||
if (l2capinbuf[12] == sdp_scid[0] && l2capinbuf[13] == sdp_scid[1]) {
|
||||
//Serial.print("\r\nDisconnect Response: SDP Channel");
|
||||
identifier = l2capinbuf[9];
|
||||
l2cap_event_flag |= L2CAP_FLAG_DISCONNECT_RESPONSE;
|
||||
} else if (l2capinbuf[12] == rfcomm_scid[0] && l2capinbuf[13] == rfcomm_scid[1]) {
|
||||
//Serial.print("\r\nDisconnect Response: RFCOMM Channel");
|
||||
identifier = l2capinbuf[9];
|
||||
l2cap_event_flag |= L2CAP_FLAG_DISCONNECT_RESPONSE;
|
||||
}
|
||||
} else if (l2capinbuf[8] == L2CAP_CMD_INFORMATION_REQUEST) {
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nInformation request"));
|
||||
#endif
|
||||
identifier = l2capinbuf[9];
|
||||
pBtd->l2cap_information_response(hci_handle,identifier,l2capinbuf[12],l2capinbuf[13]);
|
||||
}
|
||||
#ifdef EXTRADEBUG
|
||||
else {
|
||||
Notify(PSTR("\r\nL2CAP Unknown Signaling Command: "));
|
||||
PrintHex<uint8_t>(l2capinbuf[8]);
|
||||
}
|
||||
#endif
|
||||
} else if (l2capinbuf[6] == sdp_dcid[0] && l2capinbuf[7] == sdp_dcid[1]) { // SDP
|
||||
if(l2capinbuf[8] == SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST_PDU) {
|
||||
/*
|
||||
Serial.print("\r\nUUID: 0x");
|
||||
Serial.print(l2capinbuf[16],HEX);
|
||||
Serial.print(" ");
|
||||
Serial.print(l2capinbuf[17],HEX);
|
||||
*/
|
||||
if ((l2capinbuf[16] << 8 | l2capinbuf[17]) == SERIALPORT_UUID) {
|
||||
if(firstMessage) {
|
||||
serialPortResponse1(l2capinbuf[9],l2capinbuf[10]);
|
||||
firstMessage = false;
|
||||
} else {
|
||||
serialPortResponse2(l2capinbuf[9],l2capinbuf[10]); // Serialport continuation state
|
||||
firstMessage = true;
|
||||
}
|
||||
} else if ((l2capinbuf[16] << 8 | l2capinbuf[17]) == L2CAP_UUID) {
|
||||
if(firstMessage) {
|
||||
l2capResponse1(l2capinbuf[9],l2capinbuf[10]);
|
||||
firstMessage = false;
|
||||
} else {
|
||||
l2capResponse2(l2capinbuf[9],l2capinbuf[10]); // L2CAP continuation state
|
||||
firstMessage = true;
|
||||
}
|
||||
} else
|
||||
serviceNotSupported(l2capinbuf[9],l2capinbuf[10]); // The service is not supported
|
||||
}
|
||||
} else if (l2capinbuf[6] == rfcomm_dcid[0] && l2capinbuf[7] == rfcomm_dcid[1]) { // RFCOMM
|
||||
rfcommChannel = l2capinbuf[8] & 0xF8;
|
||||
rfcommDirection = l2capinbuf[8] & 0x04;
|
||||
rfcommCommandResponse = l2capinbuf[8] & 0x02;
|
||||
rfcommChannelType = l2capinbuf[9] & 0xEF;
|
||||
rfcommPfBit = l2capinbuf[9] & 0x10;
|
||||
|
||||
if(rfcommChannel>>3 != 0x00)
|
||||
rfcommChannelConnection = rfcommChannel;
|
||||
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR("\r\nRFCOMM Channel: "));
|
||||
Serial.print(rfcommChannel>>3,HEX);
|
||||
Notify(PSTR(" Direction: "));
|
||||
Serial.print(rfcommDirection>>2,HEX);
|
||||
Notify(PSTR(" CommandResponse: "));
|
||||
Serial.print(rfcommCommandResponse>>1,HEX);
|
||||
Notify(PSTR(" ChannelType: "));
|
||||
Serial.print(rfcommChannelType,HEX);
|
||||
Notify(PSTR(" PF_BIT: "));
|
||||
Serial.print(rfcommPfBit,HEX);
|
||||
#endif
|
||||
if (rfcommChannelType == RFCOMM_DISC) {
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nReceived Disconnect RFCOMM Command on channel: "));
|
||||
Serial.print(rfcommChannel>>3,HEX);
|
||||
#endif
|
||||
connected = false;
|
||||
sendRfcomm(rfcommChannel,rfcommDirection,rfcommCommandResponse,RFCOMM_UA,rfcommPfBit,rfcommbuf,0x00); // UA Command
|
||||
}
|
||||
if(connected) {
|
||||
/* Read the incoming message */
|
||||
if(rfcommChannelType == RFCOMM_UIH && rfcommChannel == rfcommChannelConnection) {
|
||||
uint8_t length = l2capinbuf[10] >> 1; // Get length
|
||||
uint8_t offset = l2capinbuf[4]-length-4; // See if there is credit
|
||||
if(rfcommAvailable + length <= sizeof(rfcommDataBuffer)) { // Don't add data to buffer if it would be full
|
||||
for(uint8_t i = 0; i < length; i++)
|
||||
rfcommDataBuffer[rfcommAvailable+i] = l2capinbuf[11+i+offset];
|
||||
rfcommAvailable += length;
|
||||
}
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR("\r\nRFCOMM Data Available: "));
|
||||
Serial.print(rfcommAvailable);
|
||||
if (offset) {
|
||||
Notify(PSTR(" - Credit: 0x"));
|
||||
Serial.print(l2capinbuf[11],HEX);
|
||||
}
|
||||
#endif
|
||||
#ifdef PRINTREPORT // Uncomment "#define PRINTREPORT" to print the report send to the Arduino via Bluetooth
|
||||
for(uint8_t i = 0; i < length; i++)
|
||||
Serial.write(l2capinbuf[i+11+offset]);
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
if(rfcommChannelType == RFCOMM_SABM) { // SABM Command - this is sent twice: once for channel 0 and then for the channel to establish
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nReceived SABM Command"));
|
||||
#endif
|
||||
sendRfcomm(rfcommChannel,rfcommDirection,rfcommCommandResponse,RFCOMM_UA,rfcommPfBit,rfcommbuf,0x00); // UA Command
|
||||
} else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_PN_CMD) { // UIH Parameter Negotiation Command
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nReceived UIH Parameter Negotiation Command"));
|
||||
#endif
|
||||
rfcommbuf[0] = BT_RFCOMM_PN_RSP; // UIH Parameter Negotiation Response
|
||||
rfcommbuf[1] = l2capinbuf[12]; // Length and shiftet like so: length << 1 | 1
|
||||
rfcommbuf[2] = l2capinbuf[13]; // Channel: channel << 1 | 1
|
||||
rfcommbuf[3] = 0xE0; // Pre difined for Bluetooth, see 5.5.3 of TS 07.10 Adaption for RFCOMM
|
||||
rfcommbuf[4] = 0x00; // Priority
|
||||
rfcommbuf[5] = 0x00; // Timer
|
||||
rfcommbuf[6] = 0x40; // Max Fram Size LSB - we will just set this to 64
|
||||
rfcommbuf[7] = 0x00; // Max Fram Size MSB
|
||||
rfcommbuf[8] = 0x00; // MaxRatransm.
|
||||
rfcommbuf[9] = 0x00; // Number of Frames
|
||||
sendRfcomm(rfcommChannel,rfcommDirection,0,RFCOMM_UIH,rfcommPfBit,rfcommbuf,0x0A);
|
||||
} else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_MSC_CMD) { // UIH Modem Status Command
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nSend UIH Modem Status Response"));
|
||||
#endif
|
||||
rfcommbuf[0] = BT_RFCOMM_MSC_RSP; // UIH Modem Status Response
|
||||
rfcommbuf[1] = 2 << 1 | 1; // Length and shiftet like so: length << 1 | 1
|
||||
rfcommbuf[2] = l2capinbuf[13]; // Channel: (1 << 0) | (1 << 1) | (0 << 2) | (channel << 3)
|
||||
rfcommbuf[3] = l2capinbuf[14];
|
||||
sendRfcomm(rfcommChannel,rfcommDirection,0,RFCOMM_UIH,rfcommPfBit,rfcommbuf,0x04);
|
||||
|
||||
delay(1);
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nSend UIH Modem Status Command"));
|
||||
#endif
|
||||
rfcommbuf[0] = BT_RFCOMM_MSC_CMD; // UIH Modem Status Command
|
||||
rfcommbuf[1] = 2 << 1 | 1; // Length and shiftet like so: length << 1 | 1
|
||||
rfcommbuf[2] = l2capinbuf[13]; // Channel: (1 << 0) | (1 << 1) | (0 << 2) | (channel << 3)
|
||||
rfcommbuf[3] = 0x8D; // Can receive frames (YES), Ready to Communicate (YES), Ready to Receive (YES), Incomig Call (NO), Data is Value (YES)
|
||||
|
||||
sendRfcomm(rfcommChannel,rfcommDirection,0,RFCOMM_UIH,rfcommPfBit,rfcommbuf,0x04);
|
||||
} else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_MSC_RSP) { // UIH Modem Status Response
|
||||
if(!creditSent) {
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nSend UIH Command with credit"));
|
||||
#endif
|
||||
sendRfcommCredit(rfcommChannelConnection,rfcommDirection,0,RFCOMM_UIH,0x10,sizeof(rfcommDataBuffer)); // Send credit
|
||||
creditSent = true;
|
||||
timer = millis();
|
||||
waitForLastCommand = true;
|
||||
}
|
||||
} else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[10] == 0x01) { // UIH Command with credit
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nReceived UIH Command with credit"));
|
||||
#endif
|
||||
} else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_RPN_CMD) { // UIH Remote Port Negotiation Command
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nReceived UIH Remote Port Negotiation Command"));
|
||||
#endif
|
||||
rfcommbuf[0] = BT_RFCOMM_RPN_RSP; // Command
|
||||
rfcommbuf[1] = l2capinbuf[12]; // Length and shiftet like so: length << 1 | 1
|
||||
rfcommbuf[2] = l2capinbuf[13]; // Channel: channel << 1 | 1
|
||||
rfcommbuf[3] = l2capinbuf[14]; // Pre difined for Bluetooth, see 5.5.3 of TS 07.10 Adaption for RFCOMM
|
||||
rfcommbuf[4] = l2capinbuf[15]; // Priority
|
||||
rfcommbuf[5] = l2capinbuf[16]; // Timer
|
||||
rfcommbuf[6] = l2capinbuf[17]; // Max Fram Size LSB
|
||||
rfcommbuf[7] = l2capinbuf[18]; // Max Fram Size MSB
|
||||
rfcommbuf[8] = l2capinbuf[19]; // MaxRatransm.
|
||||
rfcommbuf[9] = l2capinbuf[20]; // Number of Frames
|
||||
sendRfcomm(rfcommChannel,rfcommDirection,0,RFCOMM_UIH,rfcommPfBit,rfcommbuf,0x0A); // UIH Remote Port Negotiation Response
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nRFCOMM Connection is now established\r\n"));
|
||||
#endif
|
||||
waitForLastCommand = false;
|
||||
creditSent = false;
|
||||
connected = true; // The RFCOMM channel is now established
|
||||
}
|
||||
#ifdef DEBUG
|
||||
else if(rfcommChannelType != RFCOMM_DISC) {
|
||||
Notify(PSTR("\r\nUnsupported RFCOMM Data - ChannelType: "));
|
||||
PrintHex<uint8_t>(rfcommChannelType);
|
||||
Notify(PSTR(" Command: "));
|
||||
PrintHex<uint8_t>(l2capinbuf[11]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#ifdef EXTRADEBUG
|
||||
else {
|
||||
Notify(PSTR("\r\nUnsupported L2CAP Data - Channel ID: "));
|
||||
PrintHex<uint8_t>(l2capinbuf[7]);
|
||||
Notify(PSTR(" "));
|
||||
PrintHex<uint8_t>(l2capinbuf[6]);
|
||||
}
|
||||
#endif
|
||||
SDP_task();
|
||||
RFCOMM_task();
|
||||
}
|
||||
}
|
||||
void SPP::Run() {
|
||||
if(waitForLastCommand && (millis() - timer) > 100) { // We will only wait 100ms and see if the UIH Remote Port Negotiation Command is send, as some deviced don't send it
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nRFCOMM Connection is now established - Automatic\r\n"));
|
||||
#endif
|
||||
creditSent = false;
|
||||
waitForLastCommand = false;
|
||||
connected = true; // The RFCOMM channel is now established
|
||||
}
|
||||
}
|
||||
void SPP::SDP_task() {
|
||||
switch (l2cap_sdp_state)
|
||||
{
|
||||
case L2CAP_SDP_WAIT:
|
||||
if (l2cap_connection_request_sdp_flag) {
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nSDP Incoming Connection Request"));
|
||||
#endif
|
||||
pBtd->l2cap_connection_response(hci_handle,identifier, sdp_dcid, sdp_scid, PENDING);
|
||||
delay(1);
|
||||
pBtd->l2cap_connection_response(hci_handle,identifier, sdp_dcid, sdp_scid, SUCCESSFUL);
|
||||
identifier++;
|
||||
delay(1);
|
||||
pBtd->l2cap_config_request(hci_handle,identifier, sdp_scid);
|
||||
l2cap_sdp_state = L2CAP_SDP_REQUEST;
|
||||
}
|
||||
break;
|
||||
case L2CAP_SDP_REQUEST:
|
||||
if (l2cap_config_request_sdp_flag) {
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nSDP Configuration Request"));
|
||||
#endif
|
||||
pBtd->l2cap_config_response(hci_handle,identifier, sdp_scid);
|
||||
l2cap_sdp_state = L2CAP_SDP_SUCCESS;
|
||||
}
|
||||
break;
|
||||
case L2CAP_SDP_SUCCESS:
|
||||
if (l2cap_config_success_sdp_flag) {
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nSDP Successfully Configured"));
|
||||
#endif
|
||||
firstMessage = true; // Reset bool
|
||||
SDPConnected = true;
|
||||
l2cap_sdp_state = L2CAP_SDP_DONE;
|
||||
}
|
||||
break;
|
||||
case L2CAP_SDP_DONE:
|
||||
if(l2cap_disconnect_request_sdp_flag) {
|
||||
SDPConnected = false;
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nDisconnected SDP Channel"));
|
||||
#endif
|
||||
pBtd->l2cap_disconnection_response(hci_handle,identifier,sdp_dcid,sdp_scid);
|
||||
l2cap_event_flag = 0; // Reset flags
|
||||
l2cap_sdp_state = L2CAP_SDP_WAIT;
|
||||
}
|
||||
break;
|
||||
case L2CAP_DISCONNECT_RESPONSE: // This is for both disconnection response from the RFCOMM and SDP channel if they were connected
|
||||
if (l2cap_disconnect_response_flag) {
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nDisconnected L2CAP Connection"));
|
||||
#endif
|
||||
RFCOMMConnected = false;
|
||||
SDPConnected = false;
|
||||
pBtd->hci_disconnect(hci_handle);
|
||||
l2cap_event_flag = 0; // Reset flags
|
||||
l2cap_sdp_state = L2CAP_SDP_WAIT;
|
||||
l2cap_rfcomm_state = L2CAP_RFCOMM_WAIT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
void SPP::RFCOMM_task()
|
||||
{
|
||||
switch (l2cap_rfcomm_state)
|
||||
{
|
||||
case L2CAP_RFCOMM_WAIT:
|
||||
if(l2cap_connection_request_rfcomm_flag) {
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nRFCOMM Incoming Connection Request"));
|
||||
#endif
|
||||
pBtd->l2cap_connection_response(hci_handle,identifier, rfcomm_dcid, rfcomm_scid, PENDING);
|
||||
delay(1);
|
||||
pBtd->l2cap_connection_response(hci_handle,identifier, rfcomm_dcid, rfcomm_scid, SUCCESSFUL);
|
||||
identifier++;
|
||||
delay(1);
|
||||
pBtd->l2cap_config_request(hci_handle,identifier, rfcomm_scid);
|
||||
l2cap_rfcomm_state = L2CAP_RFCOMM_REQUEST;
|
||||
}
|
||||
break;
|
||||
case L2CAP_RFCOMM_REQUEST:
|
||||
if (l2cap_config_request_rfcomm_flag) {
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nRFCOMM Configuration Request"));
|
||||
#endif
|
||||
pBtd->l2cap_config_response(hci_handle,identifier, rfcomm_scid);
|
||||
l2cap_rfcomm_state = L2CAP_RFCOMM_SUCCESS;
|
||||
}
|
||||
break;
|
||||
case L2CAP_RFCOMM_SUCCESS:
|
||||
if (l2cap_config_success_rfcomm_flag) {
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nRFCOMM Successfully Configured"));
|
||||
#endif
|
||||
rfcommAvailable = 0; // Reset number of bytes available
|
||||
bytesRead = 0; // Reset number of bytes received
|
||||
RFCOMMConnected = true;
|
||||
l2cap_rfcomm_state = L2CAP_RFCOMM_DONE;
|
||||
}
|
||||
break;
|
||||
case L2CAP_RFCOMM_DONE:
|
||||
if(l2cap_disconnect_request_rfcomm_flag) {
|
||||
RFCOMMConnected = false;
|
||||
connected = false;
|
||||
#ifdef DEBUG
|
||||
Notify(PSTR("\r\nDisconnected RFCOMM Channel"));
|
||||
#endif
|
||||
pBtd->l2cap_disconnection_response(hci_handle,identifier,rfcomm_dcid,rfcomm_scid);
|
||||
l2cap_event_flag = 0; // Reset flags
|
||||
l2cap_rfcomm_state = L2CAP_RFCOMM_WAIT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
/************************************************************/
|
||||
/* SDP Commands */
|
||||
/************************************************************/
|
||||
void SPP::SDP_Command(uint8_t* data, uint8_t nbytes) { // See page 223 in the Bluetooth specs
|
||||
pBtd->L2CAP_Command(hci_handle,data,nbytes,sdp_scid[0],sdp_scid[1]);
|
||||
}
|
||||
void SPP::serviceNotSupported(uint8_t transactionIDHigh, uint8_t transactionIDLow) { // See page 235 in the Bluetooth specs
|
||||
l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU;
|
||||
l2capoutbuf[1] = transactionIDHigh;
|
||||
l2capoutbuf[2] = transactionIDLow;
|
||||
l2capoutbuf[3] = 0x00; // Parameter Length
|
||||
l2capoutbuf[4] = 0x05; // Parameter Length
|
||||
l2capoutbuf[5] = 0x00; // AttributeListsByteCount
|
||||
l2capoutbuf[6] = 0x02; // AttributeListsByteCount
|
||||
|
||||
/* Attribute ID/Value Sequence: */
|
||||
l2capoutbuf[7] = 0x35;
|
||||
l2capoutbuf[8] = 0x00;
|
||||
l2capoutbuf[9] = 0x00;
|
||||
|
||||
SDP_Command(l2capoutbuf,10);
|
||||
}
|
||||
void SPP::serialPortResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow) {
|
||||
l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU;
|
||||
l2capoutbuf[1] = transactionIDHigh;
|
||||
l2capoutbuf[2] = transactionIDLow;
|
||||
l2capoutbuf[3] = 0x00; // Parameter Length
|
||||
l2capoutbuf[4] = 0x2B; // Parameter Length
|
||||
l2capoutbuf[5] = 0x00; // AttributeListsByteCount
|
||||
l2capoutbuf[6] = 0x26; // AttributeListsByteCount
|
||||
|
||||
/* Attribute ID/Value Sequence: */
|
||||
l2capoutbuf[7] = 0x36;
|
||||
l2capoutbuf[8] = 0x00;
|
||||
l2capoutbuf[9] = 0x3C;
|
||||
l2capoutbuf[10] = 0x36;
|
||||
l2capoutbuf[11] = 0x00;
|
||||
|
||||
l2capoutbuf[12] = 0x39;
|
||||
l2capoutbuf[13] = 0x09;
|
||||
l2capoutbuf[14] = 0x00;
|
||||
l2capoutbuf[15] = 0x00;
|
||||
l2capoutbuf[16] = 0x0A;
|
||||
l2capoutbuf[17] = 0x00;
|
||||
l2capoutbuf[18] = 0x01;
|
||||
l2capoutbuf[19] = 0x00;
|
||||
l2capoutbuf[20] = 0x06;
|
||||
l2capoutbuf[21] = 0x09;
|
||||
l2capoutbuf[22] = 0x00;
|
||||
l2capoutbuf[23] = 0x01;
|
||||
l2capoutbuf[24] = 0x35;
|
||||
l2capoutbuf[25] = 0x03;
|
||||
l2capoutbuf[26] = 0x19;
|
||||
l2capoutbuf[27] = 0x11;
|
||||
|
||||
l2capoutbuf[28] = 0x01;
|
||||
l2capoutbuf[29] = 0x09;
|
||||
l2capoutbuf[30] = 0x00;
|
||||
l2capoutbuf[31] = 0x04;
|
||||
l2capoutbuf[32] = 0x35;
|
||||
l2capoutbuf[33] = 0x0C;
|
||||
l2capoutbuf[34] = 0x35;
|
||||
l2capoutbuf[35] = 0x03;
|
||||
l2capoutbuf[36] = 0x19;
|
||||
l2capoutbuf[37] = 0x01;
|
||||
l2capoutbuf[38] = 0x00;
|
||||
l2capoutbuf[39] = 0x35;
|
||||
l2capoutbuf[40] = 0x05;
|
||||
l2capoutbuf[41] = 0x19;
|
||||
l2capoutbuf[42] = 0x00;
|
||||
l2capoutbuf[43] = 0x03;
|
||||
|
||||
l2capoutbuf[44] = 0x08;
|
||||
l2capoutbuf[45] = 0x02; // Two extra bytes
|
||||
l2capoutbuf[46] = 0x00; // 25 (0x19) more bytes to come
|
||||
l2capoutbuf[47] = 0x19;
|
||||
|
||||
SDP_Command(l2capoutbuf,48);
|
||||
}
|
||||
void SPP::serialPortResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow) {
|
||||
l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU;
|
||||
l2capoutbuf[1] = transactionIDHigh;
|
||||
l2capoutbuf[2] = transactionIDLow;
|
||||
l2capoutbuf[3] = 0x00; // Parameter Length
|
||||
l2capoutbuf[4] = 0x1C; // Parameter Length
|
||||
l2capoutbuf[5] = 0x00; // AttributeListsByteCount
|
||||
l2capoutbuf[6] = 0x19; // AttributeListsByteCount
|
||||
|
||||
/* Attribute ID/Value Sequence: */
|
||||
l2capoutbuf[7] = 0x01;
|
||||
l2capoutbuf[8] = 0x09;
|
||||
l2capoutbuf[9] = 0x00;
|
||||
l2capoutbuf[10] = 0x06;
|
||||
l2capoutbuf[11] = 0x35;
|
||||
|
||||
l2capoutbuf[12] = 0x09;
|
||||
l2capoutbuf[13] = 0x09;
|
||||
l2capoutbuf[14] = 0x65;
|
||||
l2capoutbuf[15] = 0x6E;
|
||||
l2capoutbuf[16] = 0x09;
|
||||
l2capoutbuf[17] = 0x00;
|
||||
l2capoutbuf[18] = 0x6A;
|
||||
l2capoutbuf[19] = 0x09;
|
||||
l2capoutbuf[20] = 0x01;
|
||||
l2capoutbuf[21] = 0x00;
|
||||
l2capoutbuf[22] = 0x09;
|
||||
l2capoutbuf[23] = 0x01;
|
||||
l2capoutbuf[24] = 0x00;
|
||||
l2capoutbuf[25] = 0x25;
|
||||
|
||||
l2capoutbuf[26] = 0x05; // Name length
|
||||
l2capoutbuf[27] = 'T';
|
||||
l2capoutbuf[28] = 'K';
|
||||
l2capoutbuf[29] = 'J';
|
||||
l2capoutbuf[30] = 'S';
|
||||
l2capoutbuf[31] = 'P';
|
||||
l2capoutbuf[32] = 0x00; // No more data
|
||||
|
||||
SDP_Command(l2capoutbuf,33);
|
||||
}
|
||||
void SPP::l2capResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow) {
|
||||
serialPortResponse1(transactionIDHigh,transactionIDLow); // These has to send all the supported functions, since it only supports virtual serialport it just sends the message again
|
||||
}
|
||||
void SPP::l2capResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow) {
|
||||
serialPortResponse2(transactionIDHigh,transactionIDLow); // Same data as serialPortResponse2
|
||||
}
|
||||
/************************************************************/
|
||||
/* RFCOMM Commands */
|
||||
/************************************************************/
|
||||
void SPP::RFCOMM_Command(uint8_t* data, uint8_t nbytes) {
|
||||
if ((millis() - printTimer) < 10)// Check if is has been more than 10ms since last command
|
||||
delay((uint32_t)(10 - (millis() - printTimer))); // There have to be a delay between commands
|
||||
pBtd->L2CAP_Command(hci_handle,data,nbytes,rfcomm_scid[0],rfcomm_scid[1]);
|
||||
printTimer = millis();
|
||||
}
|
||||
|
||||
void SPP::sendRfcomm(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t* data, uint8_t length) {
|
||||
l2capoutbuf[0] = channel | direction | CR | extendAddress; // RFCOMM Address
|
||||
l2capoutbuf[1] = channelType | pfBit; // RFCOMM Control
|
||||
l2capoutbuf[2] = length << 1 | 0x01; // Length and format (allways 0x01 bytes format)
|
||||
uint8_t i = 0;
|
||||
for(; i < length; i++)
|
||||
l2capoutbuf[i+3] = data[i];
|
||||
l2capoutbuf[i+3] = calcFcs(l2capoutbuf);
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR(" - RFCOMM Data: "));
|
||||
for(i = 0; i < length+4; i++) {
|
||||
Serial.print(l2capoutbuf[i],HEX);
|
||||
Notify(PSTR(" "));
|
||||
}
|
||||
#endif
|
||||
RFCOMM_Command(l2capoutbuf,length+4);
|
||||
}
|
||||
|
||||
void SPP::sendRfcommCredit(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t credit) {
|
||||
l2capoutbuf[0] = channel | direction | CR | extendAddress; // RFCOMM Address
|
||||
l2capoutbuf[1] = channelType | pfBit; // RFCOMM Control
|
||||
l2capoutbuf[2] = 0x01; // Length = 0
|
||||
l2capoutbuf[3] = credit; // Credit
|
||||
l2capoutbuf[4] = calcFcs(l2capoutbuf);
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR(" - RFCOMM Credit Data: "));
|
||||
for(uint8_t i = 0; i < 5; i++) {
|
||||
Serial.print(l2capoutbuf[i],HEX);
|
||||
Notify(PSTR(" "));
|
||||
}
|
||||
#endif
|
||||
RFCOMM_Command(l2capoutbuf,5);
|
||||
}
|
||||
|
||||
/* CRC on 2 bytes */
|
||||
uint8_t SPP::__crc(uint8_t* data) {
|
||||
return(pgm_read_byte(&rfcomm_crc_table[pgm_read_byte(&rfcomm_crc_table[0xff ^ data[0]]) ^ data[1]]));
|
||||
}
|
||||
|
||||
/* Calculate FCS - we never actually check if the host sends correct FCS to the Arduino */
|
||||
uint8_t SPP::calcFcs(uint8_t *data) {
|
||||
if((data[1] & 0xEF) == RFCOMM_UIH)
|
||||
return (0xff - __crc(data)); // FCS on 2 bytes
|
||||
else
|
||||
return (0xff - pgm_read_byte(&rfcomm_crc_table[__crc(data) ^ data[2]])); // FCS on 3 bytes
|
||||
}
|
||||
|
||||
/* Serial commands */
|
||||
void SPP::print(const String &str) {
|
||||
uint8_t length = str.length();
|
||||
if(length > (sizeof(l2capoutbuf)-4))
|
||||
length = sizeof(l2capoutbuf)-4;
|
||||
l2capoutbuf[0] = rfcommChannelConnection | 0 | 0 | extendAddress;; // RFCOMM Address
|
||||
l2capoutbuf[1] = RFCOMM_UIH; // RFCOMM Control
|
||||
l2capoutbuf[2] = length << 1 | 1; // Length
|
||||
uint8_t i = 0;
|
||||
for(; i < length; i++)
|
||||
l2capoutbuf[i+3] = str[i];
|
||||
l2capoutbuf[i+3] = calcFcs(l2capoutbuf);
|
||||
|
||||
RFCOMM_Command(l2capoutbuf,length+4);
|
||||
}
|
||||
void SPP::print(const char* data) {
|
||||
uint8_t length = strlen(data);
|
||||
if(length > (sizeof(l2capoutbuf)-4))
|
||||
length = sizeof(l2capoutbuf)-4;
|
||||
l2capoutbuf[0] = rfcommChannelConnection | 0 | 0 | extendAddress;; // RFCOMM Address
|
||||
l2capoutbuf[1] = RFCOMM_UIH; // RFCOMM Control
|
||||
l2capoutbuf[2] = length << 1 | 1; // Length
|
||||
uint8_t i = 0;
|
||||
for(; i < length; i++)
|
||||
l2capoutbuf[i+3] = data[i];
|
||||
l2capoutbuf[i+3] = calcFcs(l2capoutbuf);
|
||||
|
||||
RFCOMM_Command(l2capoutbuf,length+4);
|
||||
}
|
||||
void SPP::print(uint8_t data) {
|
||||
print(&data,1);
|
||||
}
|
||||
void SPP::print(uint8_t* array, uint8_t length) {
|
||||
if(length > (sizeof(l2capoutbuf)-4))
|
||||
length = sizeof(l2capoutbuf)-4;
|
||||
l2capoutbuf[0] = rfcommChannelConnection | 0 | 0 | extendAddress;; // RFCOMM Address
|
||||
l2capoutbuf[1] = RFCOMM_UIH; // RFCOMM Control
|
||||
l2capoutbuf[2] = length << 1 | 1; // Length
|
||||
uint8_t i = 0;
|
||||
for(; i < length; i++)
|
||||
l2capoutbuf[i+3] = array[i];
|
||||
l2capoutbuf[i+3] = calcFcs(l2capoutbuf);
|
||||
|
||||
RFCOMM_Command(l2capoutbuf,length+4);
|
||||
}
|
||||
void SPP::print(const __FlashStringHelper *ifsh) {
|
||||
const char PROGMEM *p = (const char PROGMEM *)ifsh;
|
||||
size_t size = 0;
|
||||
while (1) { // Calculate the size of the string
|
||||
uint8_t c = pgm_read_byte(p+size);
|
||||
if (c == 0)
|
||||
break;
|
||||
size++;
|
||||
}
|
||||
uint8_t buf[size];
|
||||
|
||||
for(uint8_t i = 0; i < size; i++)
|
||||
buf[i] = pgm_read_byte(p++);
|
||||
|
||||
print(buf,size);
|
||||
}
|
||||
void SPP::println(const String &str) {
|
||||
String output = str + "\r\n";
|
||||
print(output);
|
||||
}
|
||||
void SPP::println(const char* data) {
|
||||
char output[strlen(data)+2];
|
||||
strcpy(output,data);
|
||||
strcat(output,"\r\n");
|
||||
print(output);
|
||||
}
|
||||
void SPP::println(uint8_t data) {
|
||||
uint8_t buf[3] = {data, '\r', '\n'};
|
||||
print(buf,3);
|
||||
}
|
||||
void SPP::println(uint8_t* array, uint8_t length) {
|
||||
uint8_t buf[length+2];
|
||||
memcpy(buf,array,length);
|
||||
buf[length] = '\r';
|
||||
buf[length+1] = '\n';
|
||||
print(buf,length+2);
|
||||
}
|
||||
void SPP::println(const __FlashStringHelper *ifsh) {
|
||||
const char PROGMEM *p = (const char PROGMEM *)ifsh;
|
||||
size_t size = 0;
|
||||
while (1) { // Calculate the size of the string
|
||||
uint8_t c = pgm_read_byte(p+size);
|
||||
if (c == 0)
|
||||
break;
|
||||
size++;
|
||||
}
|
||||
uint8_t buf[size+2];
|
||||
|
||||
for(uint8_t i = 0; i < size; i++)
|
||||
buf[i] = pgm_read_byte(p++);
|
||||
|
||||
buf[size] = '\r';
|
||||
buf[size+1] = '\n';
|
||||
print(buf,size+2);
|
||||
}
|
||||
void SPP::println(void) {
|
||||
uint8_t buf[2] = {'\r','\n'};
|
||||
print(buf,2);
|
||||
}
|
||||
|
||||
/* These must be used to print numbers */
|
||||
void SPP::printNumber(uint16_t n) {
|
||||
uint16_t temp = n;
|
||||
uint8_t digits = 0;
|
||||
while (temp) {
|
||||
temp /= 10;
|
||||
digits++;
|
||||
}
|
||||
if(digits == 0)
|
||||
print("0");
|
||||
else {
|
||||
uint8_t buf[digits];
|
||||
for(uint8_t i = 1; i < digits+1; i++) {
|
||||
buf[digits-i] = n%10; // Get number and convert to ASCII Character
|
||||
buf[digits-i] += 48;
|
||||
n /= 10;
|
||||
}
|
||||
print(buf,digits);
|
||||
}
|
||||
}
|
||||
void SPP::printNumberln(uint16_t n) {
|
||||
uint16_t temp = n;
|
||||
uint8_t digits = 0;
|
||||
while (temp) {
|
||||
temp /= 10;
|
||||
digits++;
|
||||
}
|
||||
if(digits == 0)
|
||||
print("0\r\n");
|
||||
else {
|
||||
uint8_t buf[digits+2];
|
||||
for(uint8_t i = 1; i < digits+1; i++) {
|
||||
buf[digits-i] = n%10; // Get number and convert to ASCII Character
|
||||
buf[digits-i] += 48;
|
||||
n /= 10;
|
||||
}
|
||||
buf[digits] = '\r';
|
||||
buf[digits+1] = '\n';
|
||||
print(buf,digits+2);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t SPP::read() {
|
||||
uint8_t output = rfcommDataBuffer[0];
|
||||
for(uint8_t i = 1; i < rfcommAvailable; i++)
|
||||
rfcommDataBuffer[i-1] = rfcommDataBuffer[i]; // Shift the buffer one left
|
||||
rfcommAvailable--;
|
||||
bytesRead++;
|
||||
if(bytesRead > (sizeof(rfcommDataBuffer)-5)) { // We will send the command just before it runs out of credit
|
||||
bytesRead = 0;
|
||||
sendRfcommCredit(rfcommChannelConnection,rfcommDirection,0,RFCOMM_UIH,0x10,sizeof(rfcommDataBuffer)); // Send more credit
|
||||
#ifdef EXTRADEBUG
|
||||
Notify(PSTR("\r\nSent "));
|
||||
Serial.print(sizeof(rfcommDataBuffer));
|
||||
Notify(PSTR(" more credit"));
|
||||
#endif
|
||||
}
|
||||
return output;
|
||||
}
|
192
SPP.h
Normal file
192
SPP.h
Normal file
|
@ -0,0 +1,192 @@
|
|||
/* Copyright (C) 2012 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 _spp_h_
|
||||
#define _spp_h_
|
||||
|
||||
#include "BTD.h"
|
||||
|
||||
/* Bluetooth L2CAP states for SDP_task() */
|
||||
#define L2CAP_SDP_WAIT 0
|
||||
#define L2CAP_SDP_REQUEST 1
|
||||
#define L2CAP_SDP_SUCCESS 2
|
||||
#define L2CAP_SDP_DONE 3
|
||||
#define L2CAP_DISCONNECT_RESPONSE 4
|
||||
|
||||
/* Bluetooth L2CAP states for RFCOMM_task() */
|
||||
#define L2CAP_RFCOMM_WAIT 0
|
||||
#define L2CAP_RFCOMM_REQUEST 1
|
||||
#define L2CAP_RFCOMM_SUCCESS 2
|
||||
#define L2CAP_RFCOMM_DONE 3
|
||||
|
||||
/* L2CAP event flags */
|
||||
#define L2CAP_FLAG_CONNECTION_SDP_REQUEST 0x001
|
||||
#define L2CAP_FLAG_CONNECTION_RFCOMM_REQUEST 0x002
|
||||
#define L2CAP_FLAG_CONFIG_SDP_REQUEST 0x004
|
||||
#define L2CAP_FLAG_CONFIG_RFCOMM_REQUEST 0x008
|
||||
#define L2CAP_FLAG_CONFIG_SDP_SUCCESS 0x010
|
||||
#define L2CAP_FLAG_CONFIG_RFCOMM_SUCCESS 0x020
|
||||
#define L2CAP_FLAG_DISCONNECT_SDP_REQUEST 0x040
|
||||
#define L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST 0x080
|
||||
#define L2CAP_FLAG_DISCONNECT_RESPONSE 0x100
|
||||
|
||||
/* Macros for L2CAP event flag tests */
|
||||
#define l2cap_connection_request_sdp_flag (l2cap_event_flag & L2CAP_FLAG_CONNECTION_SDP_REQUEST)
|
||||
#define l2cap_connection_request_rfcomm_flag (l2cap_event_flag & L2CAP_FLAG_CONNECTION_RFCOMM_REQUEST)
|
||||
#define l2cap_config_request_sdp_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_SDP_REQUEST)
|
||||
#define l2cap_config_request_rfcomm_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_RFCOMM_REQUEST)
|
||||
#define l2cap_config_success_sdp_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_SDP_SUCCESS)
|
||||
#define l2cap_config_success_rfcomm_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_RFCOMM_SUCCESS)
|
||||
#define l2cap_disconnect_request_sdp_flag (l2cap_event_flag & L2CAP_FLAG_DISCONNECT_SDP_REQUEST)
|
||||
#define l2cap_disconnect_request_rfcomm_flag (l2cap_event_flag & L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST)
|
||||
#define l2cap_disconnect_response_flag (l2cap_event_flag & L2CAP_FLAG_DISCONNECT_RESPONSE)
|
||||
|
||||
/* Bluetooth L2CAP PSM */
|
||||
#define SDP_PSM 0x01 // Service Discovery Protocol PSM Value
|
||||
#define RFCOMM_PSM 0x03 // RFCOMM PSM Value
|
||||
|
||||
/* Used for SDP */
|
||||
#define SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST_PDU 0x06 // See the RFCOMM specs
|
||||
#define SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU 0x07 // See the RFCOMM specs
|
||||
#define SERIALPORT_UUID 0x1101 // See http://www.bluetooth.org/Technical/AssignedNumbers/service_discovery.htm
|
||||
#define L2CAP_UUID 0x0100
|
||||
|
||||
/* Used for RFCOMM */
|
||||
#define RFCOMM_SABM 0x2F
|
||||
#define RFCOMM_UA 0x63
|
||||
#define RFCOMM_UIH 0xEF
|
||||
//#define RFCOMM_DM 0x0F
|
||||
#define RFCOMM_DISC 0x43
|
||||
|
||||
#define extendAddress 0x01 // Allways 1
|
||||
|
||||
// Multiplexer message types
|
||||
#define BT_RFCOMM_PN_CMD 0x83
|
||||
#define BT_RFCOMM_PN_RSP 0x81
|
||||
#define BT_RFCOMM_MSC_CMD 0xE3
|
||||
#define BT_RFCOMM_MSC_RSP 0xE1
|
||||
#define BT_RFCOMM_RPN_CMD 0x93
|
||||
#define BT_RFCOMM_RPN_RSP 0x91
|
||||
/*
|
||||
#define BT_RFCOMM_TEST_CMD 0x23
|
||||
#define BT_RFCOMM_TEST_RSP 0x21
|
||||
#define BT_RFCOMM_FCON_CMD 0xA3
|
||||
#define BT_RFCOMM_FCON_RSP 0xA1
|
||||
#define BT_RFCOMM_FCOFF_CMD 0x63
|
||||
#define BT_RFCOMM_FCOFF_RSP 0x61
|
||||
#define BT_RFCOMM_RLS_CMD 0x53
|
||||
#define BT_RFCOMM_RLS_RSP 0x51
|
||||
#define BT_RFCOMM_NSC_RSP 0x11
|
||||
*/
|
||||
|
||||
class SPP : public BluetoothService {
|
||||
public:
|
||||
SPP(BTD *p, const char* name = "Arduino", const char* pin = "1234");
|
||||
|
||||
// BluetoothService implementation
|
||||
virtual void ACLData(uint8_t* ACLData); // Used to pass acldata to the services
|
||||
virtual void Run(); // Used to establish the connection automatically
|
||||
virtual void Reset(); // Use this to reset the service
|
||||
virtual void disconnect(); // Used this void to disconnect the virtual serial port
|
||||
|
||||
bool connected;// Variable used to indicate if the connection is established
|
||||
|
||||
/* Serial port profile (SPP) commands */
|
||||
void print(const String &); // Used to send strings
|
||||
void print(const char* data); // Used to send strings
|
||||
void print(uint8_t data); // Used to send single bytes
|
||||
void print(uint8_t* array, uint8_t length); // Used to send arrays
|
||||
void print(const __FlashStringHelper *); // Used to print strings stored in flash
|
||||
|
||||
void println(const String &); // Include newline and carriage return
|
||||
void println(const char* data); // Include newline and carriage return
|
||||
void println(uint8_t data); // Include newline and carriage return
|
||||
void println(uint8_t* array, uint8_t length); // Include newline and carriage return
|
||||
void println(const __FlashStringHelper *); // Include newline and carriage return
|
||||
void println(void); // Use this to print newline and carriage return
|
||||
|
||||
void printNumber(uint16_t n); // These must be used to print numbers
|
||||
void printNumberln(uint16_t n); // This will include newline and carriage return
|
||||
|
||||
uint8_t available() { return rfcommAvailable; }; // Get the bytes waiting to be read
|
||||
uint8_t read(); // Used to read the buffer
|
||||
|
||||
private:
|
||||
/* Bluetooth dongle library pointer */
|
||||
BTD *pBtd;
|
||||
|
||||
/* Set true when a channel is created */
|
||||
bool SDPConnected;
|
||||
bool RFCOMMConnected;
|
||||
|
||||
uint16_t hci_handle; // The HCI Handle for the connection
|
||||
|
||||
/* Variables used by L2CAP state maschines */
|
||||
uint8_t l2cap_sdp_state;
|
||||
uint8_t l2cap_rfcomm_state;
|
||||
uint16_t l2cap_event_flag; // l2cap flags of received bluetooth events
|
||||
|
||||
uint8_t l2capoutbuf[BULK_MAXPKTSIZE]; // General purpose buffer for l2cap out data
|
||||
uint8_t rfcommbuf[10]; // Buffer for RFCOMM Commands
|
||||
|
||||
/* L2CAP Channels */
|
||||
uint8_t sdp_scid[2]; // L2CAP source CID for SDP
|
||||
uint8_t sdp_dcid[2]; // 0x0050
|
||||
uint8_t rfcomm_scid[2]; // L2CAP source CID for RFCOMM
|
||||
uint8_t rfcomm_dcid[2]; // 0x0051
|
||||
uint8_t identifier; // Identifier for command
|
||||
|
||||
/* RFCOMM Variables */
|
||||
uint8_t rfcommChannel;
|
||||
uint8_t rfcommChannelConnection; // This is the channel the SPP chanel will be running at
|
||||
uint8_t rfcommDirection;
|
||||
uint8_t rfcommCommandResponse;
|
||||
uint8_t rfcommChannelType;
|
||||
uint8_t rfcommPfBit;
|
||||
|
||||
unsigned long timer;
|
||||
bool waitForLastCommand;
|
||||
bool creditSent;
|
||||
|
||||
uint8_t rfcommDataBuffer[100]; // Create a 100 sized buffer for incoming data
|
||||
uint8_t rfcommAvailable;
|
||||
|
||||
bool firstMessage; // Used to see if it's the first SDP request received
|
||||
uint8_t bytesRead; // Counter to see when it's time to send more credit
|
||||
|
||||
unsigned long printTimer; // Used to set a delay, so it doesn't try to print too fast
|
||||
|
||||
/* State machines */
|
||||
void SDP_task(); // SDP state machine
|
||||
void RFCOMM_task(); // RFCOMM state machine
|
||||
|
||||
/* SDP Commands */
|
||||
void SDP_Command(uint8_t* data, uint8_t nbytes);
|
||||
void serviceNotSupported(uint8_t transactionIDHigh, uint8_t transactionIDLow);
|
||||
void serialPortResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow);
|
||||
void serialPortResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow);
|
||||
void l2capResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow);
|
||||
void l2capResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow);
|
||||
|
||||
/* RFCOMM Commands */
|
||||
void RFCOMM_Command(uint8_t* data, uint8_t nbytes);
|
||||
void sendRfcomm(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t* data, uint8_t length);
|
||||
void sendRfcommCredit(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t credit);
|
||||
uint8_t calcFcs(uint8_t *data);
|
||||
uint8_t __crc(uint8_t* data);
|
||||
};
|
||||
#endif
|
|
@ -6,15 +6,15 @@
|
|||
|
||||
#include <PS3BT.h>
|
||||
USB Usb;
|
||||
BTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so
|
||||
/* You can create the instance of the class in two ways */
|
||||
PS3BT PS3(&Usb); // This will just create the instance
|
||||
//PS3BT PS3(&Usb,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); // 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
|
||||
|
||||
boolean printTemperature;
|
||||
boolean printAngle;
|
||||
|
||||
void setup()
|
||||
{
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
if (Usb.Init() == -1) {
|
||||
|
@ -23,8 +23,7 @@ void setup()
|
|||
}
|
||||
Serial.print(F("\r\nPS3 Bluetooth Library Started"));
|
||||
}
|
||||
void loop()
|
||||
{
|
||||
void loop() {
|
||||
Usb.Task();
|
||||
|
||||
if(PS3.PS3Connected || PS3.PS3NavigationConnected) {
|
||||
|
@ -193,5 +192,4 @@ void loop()
|
|||
Serial.println(PS3.getTemperature());
|
||||
}
|
||||
}
|
||||
delay(1);
|
||||
}
|
167
examples/Bluetooth/PS3SPP/PS3SPP.ino
Normal file
167
examples/Bluetooth/PS3SPP/PS3SPP.ino
Normal file
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
Example sketch for the 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
|
||||
*/
|
||||
|
||||
/*
|
||||
Note:
|
||||
You will need a Arduino Mega 1280/2560 to run this sketch,
|
||||
As a normal Arduino (Uno, Duemilanove etc.) doesn't have enough SRAM and FLASH
|
||||
*/
|
||||
|
||||
#include <PS3BT.h>
|
||||
#include <SPP.h>
|
||||
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 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
|
||||
|
||||
boolean firstMessage = true;
|
||||
|
||||
String analogOutput; // We will store the data in these string so we doesn't overflow the dongle
|
||||
String digitalOutput;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200); // This wil lprint the debugging from the libraries
|
||||
if (Usb.Init() == -1) {
|
||||
Serial.print(F("\r\nOSC did not start"));
|
||||
while(1); //halt
|
||||
}
|
||||
Serial.print(F("\r\nBluetooth Library Started"));
|
||||
}
|
||||
void loop() {
|
||||
Usb.Task();
|
||||
|
||||
if(SerialBT.connected) {
|
||||
if(firstMessage) {
|
||||
firstMessage = false;
|
||||
SerialBT.println(F("Hello from Arduino")); // Send welcome message
|
||||
}
|
||||
if(Serial.available())
|
||||
SerialBT.print(Serial.read());
|
||||
if(SerialBT.available())
|
||||
Serial.write(SerialBT.read());
|
||||
}
|
||||
else
|
||||
firstMessage = true;
|
||||
|
||||
if(PS3.PS3Connected || PS3.PS3NavigationConnected) {
|
||||
analogOutput = ""; // Reset analog output string
|
||||
if(PS3.getAnalogHat(LeftHatX) > 137 || PS3.getAnalogHat(LeftHatX) < 117 || PS3.getAnalogHat(LeftHatY) > 137 || PS3.getAnalogHat(LeftHatY) < 117 || PS3.getAnalogHat(RightHatX) > 137 || PS3.getAnalogHat(RightHatX) < 117 || PS3.getAnalogHat(RightHatY) > 137 || PS3.getAnalogHat(RightHatY) < 117) {
|
||||
if(PS3.getAnalogHat(LeftHatX) > 137 || PS3.getAnalogHat(LeftHatX) < 117) {
|
||||
analogOutput += "LeftHatX: ";
|
||||
analogOutput += PS3.getAnalogHat(LeftHatX);
|
||||
analogOutput += "\t";
|
||||
}
|
||||
if(PS3.getAnalogHat(LeftHatY) > 137 || PS3.getAnalogHat(LeftHatY) < 117) {
|
||||
analogOutput += "LeftHatY: ";
|
||||
analogOutput += PS3.getAnalogHat(LeftHatY);
|
||||
analogOutput += "\t";
|
||||
}
|
||||
if(PS3.getAnalogHat(RightHatX) > 137 || PS3.getAnalogHat(RightHatX) < 117) {
|
||||
analogOutput += "RightHatX: ";
|
||||
analogOutput += PS3.getAnalogHat(RightHatX);
|
||||
analogOutput += "\t";
|
||||
}
|
||||
if(PS3.getAnalogHat(RightHatY) > 137 || PS3.getAnalogHat(RightHatY) < 117) {
|
||||
analogOutput += "RightHatY: ";
|
||||
analogOutput += PS3.getAnalogHat(RightHatY);
|
||||
analogOutput += "\t";
|
||||
}
|
||||
}
|
||||
//Analog button values can be read from almost all buttons
|
||||
if(PS3.getAnalogButton(L2_ANALOG) || PS3.getAnalogButton(R2_ANALOG)) {
|
||||
if(analogOutput != "")
|
||||
analogOutput += "\r\n";
|
||||
if(PS3.getAnalogButton(L2_ANALOG)) {
|
||||
analogOutput += "L2: ";
|
||||
analogOutput += PS3.getAnalogButton(L2_ANALOG);
|
||||
analogOutput += "\t";
|
||||
}
|
||||
if(PS3.getAnalogButton(R2_ANALOG)) {
|
||||
analogOutput += "R2: ";
|
||||
analogOutput += PS3.getAnalogButton(R2_ANALOG);
|
||||
analogOutput += "\t";
|
||||
}
|
||||
}
|
||||
if(analogOutput != "") {
|
||||
Serial.println(analogOutput);
|
||||
if(SerialBT.connected)
|
||||
SerialBT.println(analogOutput);
|
||||
}
|
||||
|
||||
if(PS3.buttonPressed) {
|
||||
digitalOutput = "PS3 Controller";
|
||||
if(PS3.getButton(PS)) {
|
||||
digitalOutput += " - PS";
|
||||
PS3.disconnect();
|
||||
}
|
||||
else {
|
||||
if(PS3.getButton(TRIANGLE))
|
||||
digitalOutput += " - Traingle";
|
||||
if(PS3.getButton(CIRCLE))
|
||||
digitalOutput += " - Circle";
|
||||
if(PS3.getButton(CROSS))
|
||||
digitalOutput += " - Cross";
|
||||
if(PS3.getButton(SQUARE))
|
||||
digitalOutput += " - Square";
|
||||
|
||||
if(PS3.getButton(UP)) {
|
||||
digitalOutput += " - UP";
|
||||
if(PS3.PS3Connected) {
|
||||
PS3.setAllOff();
|
||||
PS3.setLedOn(LED4);
|
||||
}
|
||||
}
|
||||
if(PS3.getButton(RIGHT)) {
|
||||
digitalOutput += " - Right";
|
||||
if(PS3.PS3Connected) {
|
||||
PS3.setAllOff();
|
||||
PS3.setLedOn(LED1);
|
||||
}
|
||||
}
|
||||
if(PS3.getButton(DOWN)) {
|
||||
digitalOutput += " - Down";
|
||||
if(PS3.PS3Connected) {
|
||||
PS3.setAllOff();
|
||||
PS3.setLedOn(LED2);
|
||||
}
|
||||
}
|
||||
if(PS3.getButton(LEFT)) {
|
||||
digitalOutput += " - Left";
|
||||
if(PS3.PS3Connected) {
|
||||
PS3.setAllOff();
|
||||
PS3.setLedOn(LED3);
|
||||
}
|
||||
}
|
||||
|
||||
if(PS3.getButton(L1))
|
||||
digitalOutput += " - L1";
|
||||
//if(PS3.getButton(L2))
|
||||
//digitalOutput += " - L2";
|
||||
if(PS3.getButton(L3))
|
||||
digitalOutput += " - L3";
|
||||
if(PS3.getButton(R1))
|
||||
digitalOutput += " - R1";
|
||||
//if(PS3.getButton(R2))
|
||||
//digitalOutput += " - R2";
|
||||
if(PS3.getButton(R3))
|
||||
digitalOutput += " - R3";
|
||||
|
||||
if(PS3.getButton(SELECT))
|
||||
digitalOutput += " - Select";
|
||||
if(PS3.getButton(START))
|
||||
digitalOutput += " - Start";
|
||||
|
||||
Serial.println(digitalOutput);
|
||||
if(SerialBT.connected)
|
||||
SerialBT.println(digitalOutput);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +1,15 @@
|
|||
/*
|
||||
Example sketch for the RFCOMM Bluetooth library - developed by Kristian Lauszus
|
||||
Example sketch for the RFCOMM/SPP 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 <RFCOMM.h>
|
||||
#include <SPP.h>
|
||||
USB Usb;
|
||||
BTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so
|
||||
/* You can create the instance of the class in two ways */
|
||||
RFCOMM SerialBT(&Usb); // This will set the name to the defaults: "Arduino" and the pin to "1234"
|
||||
//RFCOMM SerialBT(&Usb, "Lauszus' Arduino","0000"); // You can also set the name and pin like so
|
||||
SPP SerialBT(&Btd); // This will set the name to the defaults: "Arduino" and the pin to "1234"
|
||||
//SPP SerialBT(&Btd, "Lauszus's Arduino","0000"); // You can also set the name and pin like so
|
||||
|
||||
boolean firstMessage = true;
|
||||
|
||||
|
@ -18,7 +19,7 @@ void setup() {
|
|||
Serial.print(F("\r\nOSC did not start"));
|
||||
while(1); //halt
|
||||
}
|
||||
Serial.print(F("\r\nRFCOMM Bluetooth Library Started"));
|
||||
Serial.print(F("\r\nSPP Bluetooth Library Started"));
|
||||
}
|
||||
void loop() {
|
||||
Usb.Task();
|
||||
|
@ -34,5 +35,4 @@ void loop() {
|
|||
}
|
||||
else
|
||||
firstMessage = true;
|
||||
delay(5);
|
||||
}
|
|
@ -41,7 +41,7 @@ buttonChanged KEYWORD2
|
|||
buttonPressed KEYWORD2
|
||||
buttonReleased KEYWORD2
|
||||
|
||||
isWatingForConnection KEYWORD2
|
||||
watingForConnection KEYWORD2
|
||||
|
||||
####################################################
|
||||
# Constants and enums (LITERAL1)
|
||||
|
@ -186,17 +186,19 @@ X LITERAL1
|
|||
Y LITERAL1
|
||||
|
||||
####################################################
|
||||
# Syntax Coloring Map For RFCOMM Library
|
||||
# Syntax Coloring Map For RFCOMM/SPP Library
|
||||
####################################################
|
||||
|
||||
####################################################
|
||||
# Datatypes (KEYWORD1)
|
||||
####################################################
|
||||
|
||||
RFCOMM KEYWORD1
|
||||
SPP KEYWORD1
|
||||
|
||||
####################################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
####################################################
|
||||
|
||||
connected KEYWORD2
|
||||
printNumber KEYWORD2
|
||||
printNumberln KEYWORD2
|
Loading…
Reference in a new issue