Skip to content

Commit b721120

Browse files
authored
Merge pull request #401 from swnf/fix-acl-parser
Rewrite handleAclDataPkt to correctly handle fragments
2 parents 7e24147 + f6c5238 commit b721120

File tree

2 files changed

+105
-55
lines changed

2 files changed

+105
-55
lines changed

src/utility/HCI.cpp

Lines changed: 103 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ String commandToString(LE_COMMAND command){
101101
HCIClass::HCIClass() :
102102
_debug(NULL),
103103
_recvIndex(0),
104-
_pendingPkt(0)
104+
_pendingPkt(0),
105+
_l2CapPduBufferSize(0)
105106
{
106107
}
107108

@@ -728,87 +729,135 @@ int HCIClass::sendCommand(uint16_t opcode, uint8_t plen, void* parameters)
728729
void HCIClass::handleAclDataPkt(uint8_t /*plen*/, uint8_t pdata[])
729730
{
730731
struct __attribute__ ((packed)) HCIACLHdr {
731-
uint16_t handle;
732-
uint16_t dlen;
733-
uint16_t len;
734-
uint16_t cid;
735-
} *aclHdr = (HCIACLHdr*)pdata;
732+
uint16_t connectionHandleWithFlags;
733+
uint16_t dlen; // dlen + 4 = plen (dlen is the size of the ACL SDU)
734+
} *aclHeader = (HCIACLHdr*)pdata;
736735

736+
uint8_t bcFlag = (aclHeader->connectionHandleWithFlags & 0xc000) >> 14;
737+
uint8_t pbFlag = (aclHeader->connectionHandleWithFlags & 0x3000) >> 12;
738+
uint16_t connectionHandle = aclHeader->connectionHandleWithFlags & 0x0fff;
737739

738-
uint16_t aclFlags = (aclHdr->handle & 0xf000) >> 12;
740+
uint8_t *aclSdu = &pdata[sizeof(HCIACLHdr)];
739741

740-
if ((aclHdr->dlen - 4) != aclHdr->len) {
741-
// packet is fragmented
742-
if (aclFlags != 0x01) {
743-
// copy into ACL buffer
744-
memcpy(_aclPktBuffer, &_recvBuffer[1], sizeof(HCIACLHdr) + aclHdr->dlen - 4);
745-
} else {
746-
// copy next chunk into the buffer
747-
HCIACLHdr* aclBufferHeader = (HCIACLHdr*)_aclPktBuffer;
742+
#ifdef _BLE_TRACE_
743+
Serial.print("Acl packet bcFlag = ");
744+
Serial.print(bcFlag, BIN);
745+
Serial.print(" pbFlag = ");
746+
Serial.print(pbFlag, BIN);
747+
Serial.print(" connectionHandle = ");
748+
Serial.print(connectionHandle, HEX);
749+
Serial.print(" dlen = ");
750+
Serial.println(aclHeader->dlen, DEC);
751+
#endif
748752

749-
memcpy(&_aclPktBuffer[sizeof(HCIACLHdr) + aclBufferHeader->dlen - 4], &_recvBuffer[1 + sizeof(aclHdr->handle) + sizeof(aclHdr->dlen)], aclHdr->dlen);
753+
// Pointer to the L2CAP PDU (might be reconstructed from multiple fragments)
754+
uint8_t *l2CapPdu;
755+
uint8_t l2CapPduSize;
750756

751-
aclBufferHeader->dlen += aclHdr->dlen;
752-
aclHdr = aclBufferHeader;
753-
}
754-
}
757+
if (pbFlag == 0b10) {
758+
// "First automatically flushable packet" = Start of our L2CAP PDU
759+
760+
l2CapPdu = aclSdu;
761+
l2CapPduSize = aclHeader->dlen;
762+
} else if (pbFlag == 0b01) {
763+
// "Continuing Fragment" = Continued L2CAP PDU
764+
#ifdef _BLE_TRACE_
765+
Serial.print("Continued packet. Appending to L2CAP PDU buffer (previously ");
766+
Serial.print(_l2CapPduBufferSize, DEC);
767+
Serial.println(" bytes in buffer)");
768+
#endif
769+
// If we receive a fragment, we always need to append it to the L2CAP PDU buffer
770+
memcpy(&_l2CapPduBuffer[_l2CapPduBufferSize], aclSdu, aclHeader->dlen);
771+
_l2CapPduBufferSize += aclHeader->dlen;
755772

756-
if ((aclHdr->dlen - 4) != aclHdr->len) {
773+
l2CapPdu = _l2CapPduBuffer;
774+
l2CapPduSize = _l2CapPduBufferSize;
775+
} else {
776+
// I don't think other values are allowed for BLE
757777
#ifdef _BLE_TRACE_
758-
Serial.println("Don't have full packet yet");
759-
Serial.print("Handle: ");
760-
btct.printBytes((uint8_t*)&aclHdr->handle,2);
761-
Serial.print("dlen: ");
762-
btct.printBytes((uint8_t*)&aclHdr->dlen,2);
763-
Serial.print("len: ");
764-
btct.printBytes((uint8_t*)&aclHdr->len,2);
765-
Serial.print("cid: ");
766-
btct.printBytes((uint8_t*)&aclHdr->cid,2);
778+
Serial.println("Invalid pbFlag, discarding packet");
767779
#endif
768-
// don't have the full packet yet
769780
return;
770781
}
771782

772-
if (aclHdr->cid == ATT_CID) {
773-
if (aclFlags == 0x01) {
774-
// use buffered packet
775-
ATT.handleData(aclHdr->handle & 0x0fff, aclHdr->len, &_aclPktBuffer[sizeof(HCIACLHdr)]);
776-
} else {
777-
// use the recv buffer
778-
ATT.handleData(aclHdr->handle & 0x0fff, aclHdr->len, &_recvBuffer[1 + sizeof(HCIACLHdr)]);
779-
}
780-
} else if (aclHdr->cid == SIGNALING_CID) {
783+
// We now have a valid L2CAP header in l2CapPdu and can parse the headers
784+
struct __attribute__ ((packed)) HCIL2CapHdr {
785+
uint16_t len; // size of the L2CAP SDU
786+
uint16_t cid;
787+
} *l2CapHeader = (HCIL2CapHdr*)l2CapPdu;
788+
781789
#ifdef _BLE_TRACE_
782-
Serial.println("Signaling");
790+
Serial.print("Received ");
791+
Serial.print(l2CapPduSize - 4, DEC);
792+
Serial.print("B/");
793+
Serial.print(l2CapHeader->len, DEC);
794+
Serial.print("B of the L2CAP SDU. CID = ");
795+
Serial.println(l2CapHeader->cid, HEX);
783796
#endif
784-
L2CAPSignaling.handleData(aclHdr->handle & 0x0fff, aclHdr->len, &_recvBuffer[1 + sizeof(HCIACLHdr)]);
785-
} else if (aclHdr->cid == SECURITY_CID){
786-
// Security manager
797+
798+
// -4 because the buffer is the L2CAP PDU (with L2CAP header). The len field is only the L2CAP SDU (without L2CAP header).
799+
if (l2CapPduSize - 4 != l2CapHeader->len) {
787800
#ifdef _BLE_TRACE_
788-
Serial.println("Security data");
801+
Serial.println("L2CAP SDU incomplete");
789802
#endif
790-
if (aclFlags == 0x1){
791-
L2CAPSignaling.handleSecurityData(aclHdr->handle & 0x0fff, aclHdr->len, &_aclPktBuffer[sizeof(HCIACLHdr)]);
792-
}else{
793-
L2CAPSignaling.handleSecurityData(aclHdr->handle & 0x0fff, aclHdr->len, &_recvBuffer[1 + sizeof(HCIACLHdr)]);
803+
804+
// If this is a first packet, we have not copied it into the buffer yet
805+
if (pbFlag == 0b10) {
806+
#ifdef _BLE_TRACE_
807+
Serial.println("Storing first packet to L2CAP PDU buffer");
808+
if (_l2CapPduBufferSize != 0) {
809+
Serial.print("Warning: Discarding ");
810+
Serial.print(_l2CapPduBufferSize, DEC);
811+
Serial.println(" bytes from buffer");
812+
}
813+
#endif
814+
815+
memcpy(_l2CapPduBuffer, l2CapPdu, l2CapPduSize);
816+
_l2CapPduBufferSize = l2CapPduSize;
794817
}
795818

796-
}else {
819+
// We need to wait for the missing parts of the L2CAP SDU
820+
return;
821+
}
822+
823+
#ifdef _BLE_TRACE_
824+
Serial.println("L2CAP SDU complete");
825+
#endif
826+
827+
if (l2CapHeader->cid == ATT_CID) {
828+
#ifdef _BLE_TRACE_
829+
Serial.println("CID: ATT");
830+
#endif
831+
ATT.handleData(connectionHandle, l2CapHeader->len, &l2CapPdu[sizeof(HCIL2CapHdr)]);
832+
} else if (l2CapHeader->cid == SIGNALING_CID) {
833+
#ifdef _BLE_TRACE_
834+
Serial.println("CID: SIGNALING");
835+
#endif
836+
L2CAPSignaling.handleData(connectionHandle, l2CapHeader->len, &l2CapPdu[sizeof(HCIL2CapHdr)]);
837+
} else if (l2CapHeader->cid == SECURITY_CID) {
838+
// Security manager
839+
#ifdef _BLE_TRACE_
840+
Serial.println("CID: SECURITY");
841+
#endif
842+
L2CAPSignaling.handleSecurityData(connectionHandle, l2CapHeader->len, &l2CapPdu[sizeof(HCIL2CapHdr)]);
843+
} else {
797844
struct __attribute__ ((packed)) {
798845
uint8_t op;
799846
uint8_t id;
800847
uint16_t length;
801848
uint16_t reason;
802849
uint16_t localCid;
803850
uint16_t remoteCid;
804-
} l2capRejectCid= { 0x01, 0x00, 0x006, 0x0002, aclHdr->cid, 0x0000 };
851+
} l2capRejectCid= { 0x01, 0x00, 0x006, 0x0002, l2CapHeader->cid, 0x0000 };
805852
#ifdef _BLE_TRACE_
806-
Serial.print("rejecting packet cid: 0x");
807-
Serial.println(aclHdr->cid,HEX);
853+
Serial.println("Rejecting packet cid");
808854
#endif
809855

810-
sendAclPkt(aclHdr->handle & 0x0fff, 0x0005, sizeof(l2capRejectCid), &l2capRejectCid);
856+
sendAclPkt(connectionHandle, 0x0005, sizeof(l2capRejectCid), &l2capRejectCid);
811857
}
858+
859+
// We have processed everything in the buffer. Discard the contents.
860+
_l2CapPduBufferSize = 0;
812861
}
813862

814863
void HCIClass::handleNumCompPkts(uint16_t /*handle*/, uint16_t numPkts)

src/utility/HCI.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,8 @@ class HCIClass {
159159
uint8_t _maxPkt;
160160
uint8_t _pendingPkt;
161161

162-
uint8_t _aclPktBuffer[255];
162+
uint8_t _l2CapPduBuffer[255];
163+
uint8_t _l2CapPduBufferSize;
163164
};
164165

165166
extern HCIClass& HCI;

0 commit comments

Comments
 (0)