• 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/transport/acl_data_packet.h"
16 
17 #include <pw_assert/check.h>
18 #include <pw_bytes/endian.h>
19 
20 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
21 #include "pw_bluetooth_sapphire/internal/host/transport/slab_allocators.h"
22 
23 namespace bt::hci {
24 namespace {
25 
26 // Type containing both a fixed packet storage buffer and a ACLDataPacket
27 // interface to the buffer. Limit to 3 template instantiations: small, medium,
28 // and large.
29 using SmallACLDataPacket =
30     allocators::internal::FixedSizePacket<hci_spec::ACLDataHeader,
31                                           allocators::kSmallACLDataPacketSize>;
32 using MediumACLDataPacket =
33     allocators::internal::FixedSizePacket<hci_spec::ACLDataHeader,
34                                           allocators::kMediumACLDataPacketSize>;
35 using LargeACLDataPacket =
36     allocators::internal::FixedSizePacket<hci_spec::ACLDataHeader,
37                                           allocators::kLargeACLDataPacketSize>;
38 
NewACLDataPacket(size_t payload_size)39 ACLDataPacketPtr NewACLDataPacket(size_t payload_size) {
40   PW_CHECK(payload_size <= allocators::kLargeACLDataPayloadSize,
41            "payload size %zu too large (allowed = %zu)",
42            payload_size,
43            allocators::kLargeACLDataPayloadSize);
44 
45   if (payload_size <= allocators::kSmallACLDataPayloadSize) {
46     return std::make_unique<SmallACLDataPacket>(payload_size);
47   }
48 
49   if (payload_size <= allocators::kMediumACLDataPayloadSize) {
50     return std::make_unique<MediumACLDataPacket>(payload_size);
51   }
52 
53   return std::make_unique<LargeACLDataPacket>(payload_size);
54 }
55 
56 }  // namespace
57 
58 // static
New(uint16_t payload_size)59 ACLDataPacketPtr ACLDataPacket::New(uint16_t payload_size) {
60   return NewACLDataPacket(payload_size);
61 }
62 
63 // static
New(hci_spec::ConnectionHandle connection_handle,hci_spec::ACLPacketBoundaryFlag packet_boundary_flag,hci_spec::ACLBroadcastFlag broadcast_flag,uint16_t payload_size)64 ACLDataPacketPtr ACLDataPacket::New(
65     hci_spec::ConnectionHandle connection_handle,
66     hci_spec::ACLPacketBoundaryFlag packet_boundary_flag,
67     hci_spec::ACLBroadcastFlag broadcast_flag,
68     uint16_t payload_size) {
69   auto packet = NewACLDataPacket(payload_size);
70   if (!packet)
71     return nullptr;
72 
73   packet->WriteHeader(connection_handle, packet_boundary_flag, broadcast_flag);
74   return packet;
75 }
76 
connection_handle() const77 hci_spec::ConnectionHandle ACLDataPacket::connection_handle() const {
78   // Return the lower 12-bits of the first two octets.
79   return pw::bytes::ConvertOrderFrom(
80              cpp20::endian::little,
81              ACLDataPacket::view().header().handle_and_flags) &
82          0x0FFF;
83 }
84 
packet_boundary_flag() const85 hci_spec::ACLPacketBoundaryFlag ACLDataPacket::packet_boundary_flag() const {
86   // Return bits 4-5 in the higher octet of |handle_and_flags| or
87   // "0b00xx000000000000".
88   return static_cast<hci_spec::ACLPacketBoundaryFlag>(
89       (pw::bytes::ConvertOrderFrom(
90            cpp20::endian::little,
91            ACLDataPacket::view().header().handle_and_flags) >>
92        12) &
93       0x0003);
94 }
95 
broadcast_flag() const96 hci_spec::ACLBroadcastFlag ACLDataPacket::broadcast_flag() const {
97   // Return bits 6-7 in the higher octet of |handle_and_flags| or
98   // "0bxx00000000000000".
99   return static_cast<hci_spec::ACLBroadcastFlag>(
100       pw::bytes::ConvertOrderFrom(cpp20::endian::little,
101                                   view().header().handle_and_flags) >>
102       14);
103 }
104 
InitializeFromBuffer()105 void ACLDataPacket::InitializeFromBuffer() {
106   mutable_view()->Resize(pw::bytes::ConvertOrderFrom(
107       cpp20::endian::little, view().header().data_total_length));
108 }
109 
WriteHeader(hci_spec::ConnectionHandle connection_handle,hci_spec::ACLPacketBoundaryFlag packet_boundary_flag,hci_spec::ACLBroadcastFlag broadcast_flag)110 void ACLDataPacket::WriteHeader(
111     hci_spec::ConnectionHandle connection_handle,
112     hci_spec::ACLPacketBoundaryFlag packet_boundary_flag,
113     hci_spec::ACLBroadcastFlag broadcast_flag) {
114   // Must fit inside 12-bits.
115   PW_DCHECK(connection_handle <= 0x0FFF);
116 
117   // Must fit inside 2-bits.
118   PW_DCHECK(static_cast<uint8_t>(packet_boundary_flag) <= 0x03);
119   PW_DCHECK(static_cast<uint8_t>(broadcast_flag) <= 0x03);
120 
121   // Bitwise OR causes int promotion, so the result must be explicitly casted.
122   uint16_t handle_and_flags = static_cast<uint16_t>(
123       connection_handle | (static_cast<uint16_t>(packet_boundary_flag) << 12) |
124       (static_cast<uint16_t>(broadcast_flag) << 14));
125   mutable_view()->mutable_header()->handle_and_flags =
126       pw::bytes::ConvertOrderTo(cpp20::endian::little, handle_and_flags);
127   mutable_view()->mutable_header()->data_total_length = static_cast<uint16_t>(
128       pw::bytes::ConvertOrderTo(cpp20::endian::little, view().payload_size()));
129 }
130 
131 }  // namespace bt::hci
132