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