First release

This commit is contained in:
Kristian Lauszus 2012-08-04 12:20:47 +02:00
parent b967d610c0
commit a5e1755fa7
9 changed files with 2434 additions and 3355 deletions

970
BTD.cpp Normal file
View file

@ -0,0 +1,970 @@
/* 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)) {
/* The application will work in reduced host mode, so we can save program and data
memory space. After verifying the PID and VID we will use known values for the
configuration values for device, interface, endpoints and HID for the PS3 Controllers */
/* Initialize data structures for endpoints of device */
epInfo[ PS3_OUTPUT_PIPE ].epAddr = 0x02; // PS3 output endpoint
epInfo[ PS3_OUTPUT_PIPE ].epAttribs = EP_INTERRUPT;
epInfo[ PS3_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
epInfo[ PS3_OUTPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
epInfo[ PS3_OUTPUT_PIPE ].bmSndToggle = bmSNDTOG0;
epInfo[ PS3_OUTPUT_PIPE ].bmRcvToggle = bmRCVTOG0;
epInfo[ PS3_INPUT_PIPE ].epAddr = 0x01; // PS3 report endpoint
epInfo[ PS3_INPUT_PIPE ].epAttribs = EP_INTERRUPT;
epInfo[ PS3_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
epInfo[ PS3_INPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
epInfo[ PS3_INPUT_PIPE ].bmSndToggle = bmSNDTOG0;
epInfo[ PS3_INPUT_PIPE ].bmRcvToggle = bmRCVTOG0;
rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);
if( rcode )
goto FailSetDevTblEntry;
delay(200);//Give time for address change
rcode = pUsb->setConf(bAddress, epInfo[ BTD_CONTROL_PIPE ].epAddr, 1);
if( rcode )
goto FailSetConf;
if(PID == PS3_PID || PID == PS3NAVIGATION_PID) {
if(PID == PS3_PID) {
#ifdef DEBUG
Notify(PSTR("\r\nDualshock 3 Controller Connected"));
#endif
} else { // must be a navigation controller
#ifdef DEBUG
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);
}
}
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
rcode = -1;
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++;
return;
}
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_NUMDEVICES; i++)
if (btService[i])
btService[i]->Release(); // Reset both the L2CAP Channel and the HCI Connection
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:
hci_event_flag |= HCI_FLAG_CMD_COMPLETE; // set command complete flag
if (!hcibuf[5]) { // Check if command succeeded
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 commend 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
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;
default:
#ifdef EXTRADEBUG
if(hcibuf[0] != 0x00) {
Notify(PSTR("\r\nUnmanaged HCI Event: "));
PrintHex<uint8_t>(hcibuf[0]);
}
#endif
break;
} // switch
HCI_task();
}
else {
#ifdef EXTRADEBUG
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:
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 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:
if(connectionClaimed) { // Wait until one of the services has claimed the connection before accepting more incoming requests
hci_state = HCI_SCANNING_STATE;
connectionClaimed = 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_NUMDEVICES; i++)
if (btService[i])
btService[i]->ACLData(l2capinbuf);
} else if (rcode != hrNAK) {
#ifdef EXTRADEBUG
Notify(PSTR("\r\nACL data in error: "));
PrintHex<uint8_t>(rcode);
#endif
}
for (uint8_t i=0; i<BTD_NUMDEVICES; i++)
if (btService[i])
btService[i]->Poll();
}
/************************************************************/
/* 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[256];
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) {
#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
return;
}
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
return;
}

244
BTD.h Normal file
View file

@ -0,0 +1,244 @@
/* 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_
#define PS3_CONTROL_PIPE 0
#define PS3_OUTPUT_PIPE 1
#define PS3_INPUT_PIPE 2
#define PS3_CONTROL_PIPE 0
#define PS3_OUTPUT_PIPE 1
#define PS3_INPUT_PIPE 2
#define EP_INTERRUPT 0x03
#define EP_MAXPKTSIZE 64 // max size for data via USB
#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_NUMDEVICES 7 // Max number of Bluetooth devices
class BluetoothService { // All services should include this class
public:
virtual void ACLData(uint8_t* ACLData); // Used to pass acldata to the services
virtual void Poll(); // Used to run the different state machines
virtual void Release(); // Used to disconnect both the L2CAP Channel and the HCI Connection
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_NUMDEVICES; i++)
if (btService[i])
btService[i]->disconnect(); // Disconnect both the L2CAP Channel and the HCI Connection
};
/* Register bluetooth dongle members/services */
uint8_t registerServiceClass(BluetoothService *pService) {
for (uint8_t i=0; i<BTD_NUMDEVICES; i++) {
if (!btService[i]) {
btService[i] = pService;
return 0; // Success
}
}
return 1; // ErrorregisterServiceClass
};
void claimConnection() { connectionClaimed = true; }; // This is used by the service to know when to store the device information
bool connectionClaimed;
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_NUMDEVICES];
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

View file

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

1385
PS3BT.cpp

File diff suppressed because it is too large Load diff

281
PS3BT.h
View file

@ -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_EV_CONTROL_REQUEST 1
#define L2CAP_EV_CONTROL_SUCCESS 2
#define L2CAP_EV_INTERRUPT_SETUP 3
#define L2CAP_EV_INTERRUPT_REQUEST 4
#define L2CAP_EV_INTERRUPT_SUCCESS 5
#define L2CAP_EV_HID_ENABLE_SIXAXIS 6
#define L2CAP_EV_HID_PS3_LED 7
#define L2CAP_EV_DONE 8
#define L2CAP_EV_INTERRUPT_DISCONNECT 9
#define L2CAP_EV_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 Poll(); // Used to run the state maschine
virtual void Release();
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,13 @@ 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 */
/* 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 +273,8 @@ 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* l2capinpointer; // Pointer to l2capinbuf from BTD
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 +285,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

1594
RFCOMM.cpp

File diff suppressed because it is too large Load diff

339
RFCOMM.h
View file

@ -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

766
SPP.cpp Normal file
View file

@ -0,0 +1,766 @@
/* 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;
l2cap_sdp_state = L2CAP_SDP_WAIT;
l2cap_rfcomm_state = L2CAP_RFCOMM_WAIT;
l2cap_event_flag = 0;
/* 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;
}
void SPP::Release() {
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(SDPConnected)
pBtd->l2cap_disconnection_request(hci_handle,0x0B, sdp_scid, sdp_dcid);
l2cap_event_flag = 0; // Reset flags
l2cap_sdp_state = L2CAP_DISCONNECT_RESPONSE;
}
void SPP::ACLData(uint8_t* l2capinbuf) {
if (l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) {
if(((l2capinbuf[12] | (l2capinbuf[13] << 8)) == SDP_PSM) || ((l2capinbuf[12] | (l2capinbuf[13] << 8)) == RFCOMM_PSM)) {
if(!pBtd->connectionClaimed && !connected && !RFCOMMConnected && !SDPConnected) {
pBtd->claimConnection(); // Claim that the incoming connection belongs to this service
hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection
}
}
}
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[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) {
if ((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) { // Success
//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]) {
if ((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) { // Success
//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)
return;
uint8_t length = l2capinbuf[10] >> 1; // Get length
if(rfcommAvailable + length > 256)
return; // Return if the buffer would be full
uint8_t offset = l2capinbuf[4]-length-4; // See if there is credit
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
if(rfcommChannelType != RFCOMM_UIH || rfcommChannel != rfcommChannelConnection)
return;
uint8_t length = l2capinbuf[10] >> 1; // Get length
uint8_t offset = l2capinbuf[4]-length-4; // See if there is credit
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,0xFF); // 255 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
} else if(rfcommChannelType != RFCOMM_DISC) {
#ifdef DEBUG
Notify(PSTR("\r\nUnsupported RFCOMM Data - ChannelType: "));
PrintHex<uint8_t>(rfcommChannelType);
Notify(PSTR(" Command: "));
PrintHex<uint8_t>(l2capinbuf[11]);
#endif
}
}
} else {
#ifdef EXTRADEBUG
Notify(PSTR("\r\nUnsupported L2CAP Data - Channel ID: "));
PrintHex<uint8_t>(l2capinbuf[7]);
Notify(PSTR(" "));
PrintHex<uint8_t>(l2capinbuf[6]);
#endif
}
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::Poll() {
SDP_task();
RFCOMM_task();
}
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_sdp_state = L2CAP_SDP_WAIT;
l2cap_sdp_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 more bytes?
l2capoutbuf[46] = 0x00; // 19 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;
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) {
pBtd->L2CAP_Command(hci_handle,data,nbytes,rfcomm_scid[0],rfcomm_scid[1]);
}
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 char* data) {
l2capoutbuf[0] = rfcommChannelConnection | 0 | 0 | extendAddress;; // RFCOMM Address
l2capoutbuf[1] = RFCOMM_UIH; // RFCOMM Control
l2capoutbuf[2] = strlen(data) << 1 | 1; // Length
uint8_t i = 0;
for(; i < strlen(data); i++)
l2capoutbuf[i+3] = data[i];
l2capoutbuf[i+3] = calcFcs(l2capoutbuf);
RFCOMM_Command(l2capoutbuf,strlen(data)+4);
}
void SPP::print(uint8_t data) {
print(&data,1);
}
void SPP::print(uint8_t* array, uint8_t length) {
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 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);
}
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 > 250) {
bytesRead = 0;
sendRfcommCredit(rfcommChannelConnection,rfcommDirection,0,RFCOMM_UIH,0x10,0xFF); // Send 255 more credit
#ifdef EXTRADEBUG
Notify(PSTR("\r\nSent 255 more credit"));
#endif
}
return output;
}

184
SPP.h Normal file
View file

@ -0,0 +1,184 @@
/* 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 Poll(); // Used to run SDP_task() and RFCOMM_task()
virtual void Release();
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 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
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[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 bytesRead; // Counter to see when it's time to send more credit
/* 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