xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/l2cap/pdu.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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