1 // Copyright 2023 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 15 #pragma once 16 17 #include <cstdint> 18 19 #include "pw_bluetooth_sapphire/internal/host/common/macros.h" 20 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h" 21 #include "pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h" 22 #include "pw_bluetooth_sapphire/internal/host/l2cap/pdu.h" 23 #include "pw_bluetooth_sapphire/internal/host/transport/acl_data_packet.h" 24 25 namespace bt::l2cap { 26 27 // A Recombiner can be used to obtain complete L2CAP frames from received 28 // fragments. Incoming ACL data packets can be accumulated in a Recombiner. 29 // 30 // Each instance of Recombiner is intended to be used over a unique logical 31 // link. ACL data packets with different connection handles should not be added 32 // to the same Recombiner (the code will assert this in debug-mode). 33 // 34 // THREAD-SAFETY: 35 // 36 // This class is not thread-safe. External locking should be provided if an 37 // instance will be accessed on multiple threads. 38 class Recombiner final { 39 public: 40 explicit Recombiner(hci_spec::ConnectionHandle handle); 41 42 // Consumes an ACL data fragment. This function may return a complete L2CAP 43 // PDU if |fragment| completes a sequence or constitutes a complete fragment 44 // on its own. The |frames_dropped| flag is set to true if a sequence was 45 // dropped due to a recombination error. The most likely causes for an error 46 // are: 47 // 48 // 1. |fragment| contains a malformed L2CAP frame. A packet is treated as 49 // malformed if: 50 // a. Its suspected to be the first fragment in a new recombination 51 // sequence and does not contain a complete L2CAP basic header. b. After 52 // a recombination sequence is considered complete, the length of the 53 // frame does not match the length that was obtained from the L2CAP basic 54 // header. 55 // 56 // 2. |fragment| begins a new sequence when a prior incomplete sequence was 57 // in progress, in which case the incomplete sequence is dropped but 58 // |fragment| is retained UNLESS |fragment| itself constitues a malformed 59 // PDU (as in #1); 60 // 61 // 3. |fragment| is a continuing fragment that leaves the sequence in 62 // progress in a malformed state, in which case the sequence and |fragment| 63 // are dropped; 64 // 65 // A "true" |frames_dropped| value does not imply that the supplied input 66 // |fragment| itself was in error and it is possible for |frames_dropped| to 67 // be true alongside a valid |pdu| value. The caller can resume calling 68 // ConsumeFragment as normal, as the Recombiner can internally recover from a 69 // recombination error. 70 // 71 // This function panics if |fragment| is not built for the connection handle 72 // that this Recombiner was assigned to. 73 struct Result { 74 std::optional<PDU> pdu; 75 bool frames_dropped; 76 }; 77 Result ConsumeFragment(hci::ACLDataPacketPtr fragment); 78 79 private: 80 // Handles a new ACL data fragment received when a recombination is not in 81 // progress. This may deliver |fragment| as is if it constitutes a complete 82 // PDU, drop it if it's malformed, or initiate a new recombination if it's 83 // partial. 84 Result ProcessFirstFragment(hci::ACLDataPacketPtr fragment); 85 86 // Clears the current recombination. 87 void ClearRecombination(); 88 89 // Begins a trace for a new queued fragment, tracking a single new trace ID in 90 // |trace_ids_|. 91 void BeginTrace(); 92 93 // Ends the traces for all queued fragments. This gets called by 94 // ClearRecombination() when a pending recombination ends (either successfully 95 // or in error). 96 void EndTraces(); 97 98 struct Recombination { 99 PDU pdu; 100 size_t expected_frame_length = 0u; 101 size_t accumulated_length = 0u; 102 }; 103 std::optional<Recombination> recombination_; 104 105 // The handle for the logical link this Recombiner operates on. This field is 106 // here purely to enforce that this Recombiner is used with ACL fragments from 107 // the correct link. 108 const hci_spec::ConnectionHandle handle_; 109 110 // Trace flow IDs for the fragments being recombined into a single PDU. 111 // Flows track from AddFragment to Release, only when there is fragmentation. 112 // (PDUs are expected to be released immediately when there is no recombining) 113 std::vector<trace_flow_id_t> trace_ids_; 114 115 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(Recombiner); 116 }; 117 118 } // namespace bt::l2cap 119