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 #include "pw_bluetooth_sapphire/internal/host/l2cap/pdu.h"
16
17 #include <pw_assert/check.h>
18
19 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
20 #include "pw_bluetooth_sapphire/internal/host/transport/acl_data_packet.h"
21
22 namespace bt::l2cap {
23
24 // NOTE: The order in which these are initialized matters, as
25 // other.ReleaseFragments() resets |other.fragment_count_|.
PDU(PDU && other)26 PDU::PDU(PDU&& other) : fragments_(other.ReleaseFragments()) {}
27
operator =(PDU && other)28 PDU& PDU::operator=(PDU&& other) {
29 // NOTE: The order in which these are initialized matters, as
30 // other.ReleaseFragments() resets |other.fragment_count_|.
31 fragments_ = other.ReleaseFragments();
32 return *this;
33 }
34
Copy(MutableByteBuffer * out_buffer,size_t pos,size_t size) const35 size_t PDU::Copy(MutableByteBuffer* out_buffer, size_t pos, size_t size) const {
36 PW_DCHECK(out_buffer);
37 PW_DCHECK(pos <= length());
38 PW_DCHECK(is_valid());
39
40 size_t remaining = std::min(size, length() - pos);
41 PW_DCHECK(out_buffer->size() >= remaining);
42 if (!remaining) {
43 return 0;
44 }
45
46 bool found = false;
47 size_t offset = 0u;
48 for (auto iter = fragments_.begin(); iter != fragments_.end() && remaining;
49 ++iter) {
50 auto payload = (*iter)->view().payload_data();
51
52 // Skip the Basic L2CAP header for the first fragment.
53 if (iter == fragments_.begin()) {
54 payload = payload.view(sizeof(BasicHeader));
55 }
56
57 // We first find the beginning fragment based on |pos|.
58 if (!found) {
59 size_t fragment_size = payload.size();
60 if (pos >= fragment_size) {
61 pos -= fragment_size;
62 continue;
63 }
64
65 // The beginning fragment has been found.
66 found = true;
67 }
68
69 // Calculate how much to read from the current fragment
70 size_t write_size = std::min(payload.size() - pos, remaining);
71
72 // Read the fragment into out_buffer->mutable_data() + offset.
73 out_buffer->Write(payload.data() + pos, write_size, offset);
74
75 // Clear |pos| after using it on the first fragment as all successive
76 // fragments are read from the beginning.
77 if (pos)
78 pos = 0u;
79
80 offset += write_size;
81 remaining -= write_size;
82 }
83
84 return offset;
85 }
86
ReleaseFragments()87 PDU::FragmentList PDU::ReleaseFragments() {
88 auto out_list = std::move(fragments_);
89
90 PW_DCHECK(!is_valid());
91 return out_list;
92 }
93
basic_header() const94 const BasicHeader& PDU::basic_header() const {
95 PW_DCHECK(!fragments_.empty());
96 const auto& fragment = *fragments_.begin();
97
98 PW_DCHECK(fragment->packet_boundary_flag() !=
99 hci_spec::ACLPacketBoundaryFlag::kContinuingFragment);
100 return fragment->view().payload<BasicHeader>();
101 }
102
AppendFragment(hci::ACLDataPacketPtr fragment)103 void PDU::AppendFragment(hci::ACLDataPacketPtr fragment) {
104 PW_DCHECK(fragment);
105 PW_DCHECK(!is_valid() || (*fragments_.begin())->connection_handle() ==
106 fragment->connection_handle());
107 fragments_.push_back(std::move(fragment));
108 }
109
110 } // namespace bt::l2cap
111