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