USB_Host_Shield_2.0/SPPBase.cpp

185 lines
6.6 KiB
C++
Raw Normal View History

/* Copyright (C) 2014 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 "SPPBase.h"
SPPBase::SPPBase(BTD *p) : pBtd(p) {};
void SPPBase::disconnect() {
connected = false;
// First the two L2CAP channels has to be disconnected and then the HCI connection
if (RFCOMMConnected)
pBtd->l2cap_disconnection_request(hci_handle, ++identifier, rfcomm_scid, rfcomm_dcid);
if (RFCOMMConnected && SDPConnected)
delay(1); // Add delay between commands
if (SDPConnected)
pBtd->l2cap_disconnection_request(hci_handle, ++identifier, sdp_scid, sdp_dcid);
l2cap_sdp_state = L2CAP_DISCONNECT_RESPONSE;
}
void SPPBase::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 (always 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: "), 0x80);
for (i = 0; i < length + 4; i++) {
D_PrintHex<uint8_t > (l2capoutbuf[i], 0x80);
Notify(PSTR(" "), 0x80);
}
#endif
RFCOMM_Command(l2capoutbuf, length + 4);
}
void SPPBase::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: "), 0x80);
for (uint8_t i = 0; i < 5; i++) {
D_PrintHex<uint8_t > (l2capoutbuf[i], 0x80);
Notify(PSTR(" "), 0x80);
}
#endif
RFCOMM_Command(l2capoutbuf, 5);
}
/* CRC on 2 bytes */
uint8_t SPPBase::crc(uint8_t *data) {
return (pgm_read_byte(&rfcomm_crc_table[pgm_read_byte(&rfcomm_crc_table[0xFF ^ data[0]]) ^ data[1]]));
}
/* Calculate FCS */
uint8_t SPPBase::calcFcs(uint8_t *data) {
uint8_t temp = crc(data);
if ((data[1] & 0xEF) == RFCOMM_UIH)
return (0xFF - temp); // FCS on 2 bytes
else
return (0xFF - pgm_read_byte(&rfcomm_crc_table[temp ^ data[2]])); // FCS on 3 bytes
}
/* Check FCS */
bool SPPBase::checkFcs(uint8_t *data, uint8_t fcs) {
uint8_t temp = crc(data);
if ((data[1] & 0xEF) != RFCOMM_UIH)
temp = pgm_read_byte(&rfcomm_crc_table[temp ^ data[2]]); // FCS on 3 bytes
return (pgm_read_byte(&rfcomm_crc_table[temp ^ fcs]) == 0xCF);
}
/* Serial commands */
#if defined(ARDUINO) && ARDUINO >=100
size_t SPPBase::write(uint8_t data) {
return write(&data, 1);
}
#else
void SPPBase::write(uint8_t data) {
write(&data, 1);
}
#endif
#if defined(ARDUINO) && ARDUINO >=100
size_t SPPBase::write(const uint8_t *data, size_t size) {
#else
void SPPBase::write(const uint8_t *data, size_t size) {
#endif
for(uint8_t i = 0; i < size; i++) {
if(sppIndex >= sizeof (sppOutputBuffer) / sizeof (sppOutputBuffer[0]))
send(); // Send the current data in the buffer
sppOutputBuffer[sppIndex++] = data[i]; // All the bytes are put into a buffer and then send using the send() function
}
#if defined(ARDUINO) && ARDUINO >=100
return size;
#endif
}
void SPPBase::send() {
if(!connected || !sppIndex)
return;
uint8_t length; // This is the length of the string we are sending
uint8_t offset = 0; // This is used to keep track of where we are in the string
l2capoutbuf[0] = rfcommChannelConnection | 0 | 0 | extendAddress; // RFCOMM Address
l2capoutbuf[1] = RFCOMM_UIH; // RFCOMM Control
while(sppIndex) { // We will run this while loop until this variable is 0
if(sppIndex > (sizeof (l2capoutbuf) - 4)) // Check if the string is larger than the outgoing buffer
length = sizeof (l2capoutbuf) - 4;
else
length = sppIndex;
l2capoutbuf[2] = length << 1 | 1; // Length
uint8_t i = 0;
for(; i < length; i++)
l2capoutbuf[i + 3] = sppOutputBuffer[i + offset];
l2capoutbuf[i + 3] = calcFcs(l2capoutbuf); // Calculate checksum
RFCOMM_Command(l2capoutbuf, length + 4);
sppIndex -= length;
offset += length; // Increment the offset
}
}
int SPPBase::available(void) {
return rfcommAvailable;
};
void SPPBase::flush(void) {
send();
};
void SPPBase::discard(void) {
rfcommAvailable = 0;
}
int SPPBase::peek(void) {
if(rfcommAvailable == 0) // Don't read if there is nothing in the buffer
return -1;
return rfcommDataBuffer[0];
}
int SPPBase::read(void) {
if(rfcommAvailable == 0) // Don't read if there is nothing in the buffer
return -1;
uint8_t output = rfcommDataBuffer[0];
for(uint8_t i = 1; i < rfcommAvailable; i++)
rfcommDataBuffer[i - 1] = rfcommDataBuffer[i]; // Shift the buffer one left
rfcommAvailable--;
bytesRead++;
if(bytesRead > (sizeof (rfcommDataBuffer) - 5)) { // We will send the command just before it runs out of credit
bytesRead = 0;
sendRfcommCredit(rfcommChannelConnection, rfcommDirection, 0, RFCOMM_UIH, 0x10, sizeof (rfcommDataBuffer)); // Send more credit
#ifdef EXTRADEBUG
Notify(PSTR("\r\nSent "), 0x80);
Notify((uint8_t)sizeof (rfcommDataBuffer), 0x80);
Notify(PSTR(" more credit"), 0x80);
#endif
}
return output;
}