• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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