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