• 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/control_packets.h"
16 
17 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
18 #include "pw_bluetooth_sapphire/internal/host/transport/emboss_control_packets.h"
19 #include "pw_bluetooth_sapphire/internal/host/transport/error.h"
20 #include "pw_bluetooth_sapphire/internal/host/transport/slab_allocators.h"
21 
22 namespace bt::hci {
23 namespace {
24 
25 // Limit CommandPacket template instantiations to 2 (small and large):
26 using SmallCommandPacket =
27     allocators::internal::FixedSizePacket<hci_spec::CommandHeader,
28                                           allocators::kSmallControlPacketSize>;
29 using LargeCommandPacket =
30     allocators::internal::FixedSizePacket<hci_spec::CommandHeader,
31                                           allocators::kLargeControlPacketSize>;
32 
33 using EventFixedSizedPacket =
34     allocators::internal::FixedSizePacket<hci_spec::EventHeader,
35                                           allocators::kLargeControlPacketSize>;
36 
37 // TODO(fxbug.dev/42058160): Use Pigweed's slab allocator
NewCommandPacket(size_t payload_size)38 std::unique_ptr<CommandPacket> NewCommandPacket(size_t payload_size) {
39   BT_DEBUG_ASSERT(payload_size <= allocators::kLargeControlPayloadSize);
40 
41   if (payload_size <= allocators::kSmallControlPayloadSize) {
42     return std::make_unique<SmallCommandPacket>(payload_size);
43   }
44   return std::make_unique<LargeCommandPacket>(payload_size);
45 }
46 
47 // Returns true and populates the |out_code| field with the status parameter.
48 // Returns false if |event|'s payload is too small to hold a T. T must have a
49 // |status| member of type pw::bluetooth::emboss::StatusCode for this to
50 // compile.
51 template <typename T>
StatusCodeFromEvent(const EventPacket & event,pw::bluetooth::emboss::StatusCode * out_code)52 bool StatusCodeFromEvent(const EventPacket& event,
53                          pw::bluetooth::emboss::StatusCode* out_code) {
54   BT_DEBUG_ASSERT(out_code);
55 
56   if (event.view().payload_size() < sizeof(T))
57     return false;
58 
59   *out_code = event.params<T>().status;
60   return true;
61 }
62 
63 // For packet definitions that have been migrated to Emboss, parameterize this
64 // method over the Emboss view.
65 template <typename T>
StatusCodeFromEmbossEvent(const EventPacket & event,pw::bluetooth::emboss::StatusCode * out_code)66 bool StatusCodeFromEmbossEvent(const EventPacket& event,
67                                pw::bluetooth::emboss::StatusCode* out_code) {
68   BT_DEBUG_ASSERT(out_code);
69 
70   auto emboss_packet = EmbossEventPacket::New<T>(event.event_code());
71   bt::MutableBufferView dest = emboss_packet.mutable_data();
72   event.view().data().Copy(&dest);
73 
74   if (event.view().payload_size() <
75       T::IntrinsicSizeInBytes().Read() -
76           pw::bluetooth::emboss::EventHeader::IntrinsicSizeInBytes()) {
77     return false;
78   }
79 
80   *out_code = emboss_packet.view_t().status().Read();
81   return true;
82 }
83 
84 // As pw::bluetooth::emboss::StatusCodeFromEvent, but for LEMetaEvent subevents.
85 // Returns true and populates the |out_code| field with the subevent status
86 // parameter. Returns false if |event|'s payload is too small to hold a
87 // LEMetaEvent containing a T. T must have a |status| member of type
88 // pw::bluetooth::emboss::StatusCode for this to compile.
89 template <typename T>
StatusCodeFromSubevent(const EventPacket & event,pw::bluetooth::emboss::StatusCode * out_code)90 bool StatusCodeFromSubevent(const EventPacket& event,
91                             pw::bluetooth::emboss::StatusCode* out_code) {
92   BT_ASSERT(out_code);
93 
94   if (event.view().payload_size() <
95       sizeof(hci_spec::LEMetaEventParams) + sizeof(T))
96     return false;
97 
98   *out_code = event.subevent_params<T>()->status;
99   return true;
100 }
101 
102 // Specialization for the CommandComplete event.
103 template <>
StatusCodeFromEvent(const EventPacket & event,pw::bluetooth::emboss::StatusCode * out_code)104 bool StatusCodeFromEvent<hci_spec::CommandCompleteEventParams>(
105     const EventPacket& event, pw::bluetooth::emboss::StatusCode* out_code) {
106   BT_DEBUG_ASSERT(out_code);
107 
108   const auto* params = event.return_params<hci_spec::SimpleReturnParams>();
109   if (!params)
110     return false;
111 
112   *out_code = params->status;
113   return true;
114 }
115 
116 }  // namespace
117 
118 namespace hci_android = bt::hci_spec::vendor::android;
119 
120 // static
New(hci_spec::OpCode opcode,size_t payload_size)121 std::unique_ptr<CommandPacket> CommandPacket::New(hci_spec::OpCode opcode,
122                                                   size_t payload_size) {
123   auto packet = NewCommandPacket(payload_size);
124   if (!packet)
125     return nullptr;
126 
127   packet->WriteHeader(opcode);
128   return packet;
129 }
130 
WriteHeader(hci_spec::OpCode opcode)131 void CommandPacket::WriteHeader(hci_spec::OpCode opcode) {
132   mutable_view()->mutable_header()->opcode = htole16(opcode);
133   BT_ASSERT(view().payload_size() < std::numeric_limits<uint8_t>::max());
134   mutable_view()->mutable_header()->parameter_total_size =
135       static_cast<uint8_t>(view().payload_size());
136 }
137 
138 // static
New(size_t payload_size)139 std::unique_ptr<EventPacket> EventPacket::New(size_t payload_size) {
140   // TODO(fxbug.dev/42058160): Use Pigweed's slab allocator
141   return std::make_unique<EventFixedSizedPacket>(payload_size);
142 }
143 
ToStatusCode(pw::bluetooth::emboss::StatusCode * out_code) const144 bool EventPacket::ToStatusCode(
145     pw::bluetooth::emboss::StatusCode* out_code) const {
146 #define CASE_EVENT_STATUS(event_name)                                    \
147   case hci_spec::k##event_name##EventCode:                               \
148     return StatusCodeFromEvent<hci_spec::event_name##EventParams>(*this, \
149                                                                   out_code)
150 
151 #define CASE_EMBOSS_EVENT_STATUS(event_name) \
152   case hci_spec::k##event_name##EventCode:   \
153     return StatusCodeFromEmbossEvent<        \
154         pw::bluetooth::emboss::event_name##EventView>(*this, out_code)
155 
156 #define CASE_SUBEVENT_STATUS(subevent_name)                                 \
157   case hci_spec::k##subevent_name##SubeventCode:                            \
158     return StatusCodeFromSubevent<hci_spec::subevent_name##SubeventParams>( \
159         *this, out_code)
160 
161   switch (event_code()) {
162     CASE_EMBOSS_EVENT_STATUS(AuthenticationComplete);
163     CASE_EVENT_STATUS(ChangeConnectionLinkKeyComplete);
164     CASE_EVENT_STATUS(CommandComplete);
165     CASE_EVENT_STATUS(CommandStatus);
166     CASE_EMBOSS_EVENT_STATUS(ConnectionComplete);
167     CASE_EMBOSS_EVENT_STATUS(DisconnectionComplete);
168     CASE_EMBOSS_EVENT_STATUS(RemoteNameRequestComplete);
169     CASE_EVENT_STATUS(ReadRemoteSupportedFeaturesComplete);
170     CASE_EVENT_STATUS(SimplePairingComplete);
171     CASE_EMBOSS_EVENT_STATUS(InquiryComplete);
172     case hci_spec::kEncryptionChangeEventCode:
173       return StatusCodeFromEmbossEvent<
174           pw::bluetooth::emboss::EncryptionChangeEventV1View>(*this, out_code);
175     case hci_spec::kLEMetaEventCode: {
176       auto subevent_code = params<hci_spec::LEMetaEventParams>().subevent_code;
177       switch (subevent_code) {
178         CASE_SUBEVENT_STATUS(LEAdvertisingSetTerminated);
179         default:
180           BT_PANIC("LE subevent (%#.2x) not implemented!", subevent_code);
181           break;
182       }
183     }
184 
185       // TODO(armansito): Complete this list.
186 
187     default:
188       BT_PANIC("event (%#.2x) not implemented!", event_code());
189       break;
190   }
191   return false;
192 
193 #undef CASE_EVENT_STATUS
194 }
195 
ToResult() const196 hci::Result<> EventPacket::ToResult() const {
197   pw::bluetooth::emboss::StatusCode code;
198   if (!ToStatusCode(&code)) {
199     return bt::ToResult(HostError::kPacketMalformed);
200   }
201   return bt::ToResult(code);
202 }
203 
InitializeFromBuffer()204 void EventPacket::InitializeFromBuffer() {
205   mutable_view()->Resize(view().header().parameter_total_size);
206 }
207 
208 }  // namespace bt::hci
209