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 #pragma once 16 #include <pw_bluetooth/hci_common.emb.h> 17 18 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h" 19 #include "pw_bluetooth_sapphire/internal/host/transport/emboss_packet.h" 20 #include "pw_bluetooth_sapphire/internal/host/transport/error.h" 21 22 namespace bt::hci { 23 24 template <class ViewT> 25 class CommandPacketT; 26 27 // CommandPacket is the HCI Command packet specialization of 28 // DynamicPacket. 29 class CommandPacket : public DynamicPacket { 30 public: 31 // Construct an HCI Command packet from an Emboss view T and initialize its 32 // header with the |opcode| and size. 33 template <typename T> New(hci_spec::OpCode opcode)34 static CommandPacketT<T> New(hci_spec::OpCode opcode) { 35 return New<T>(opcode, T::IntrinsicSizeInBytes().Read()); 36 } 37 38 // Construct an HCI Command packet from an Emboss view T of |packet_size| 39 // total bytes (header + payload) and initialize its header with the |opcode| 40 // and size. This constructor is meant for variable size packets, for which 41 // clients must calculate packet size manually. 42 template <typename T> New(hci_spec::OpCode opcode,size_t packet_size)43 static CommandPacketT<T> New(hci_spec::OpCode opcode, size_t packet_size) { 44 CommandPacketT<T> packet(opcode, packet_size); 45 return packet; 46 } 47 48 hci_spec::OpCode opcode() const; 49 // Returns the OGF (OpCode Group Field) which occupies the upper 6-bits of the 50 // opcode. 51 uint8_t ogf() const; 52 // Returns the OCF (OpCode Command Field) which occupies the lower 10-bits of 53 // the opcode. 54 uint16_t ocf() const; 55 56 protected: 57 explicit CommandPacket(hci_spec::OpCode opcode, size_t packet_size); 58 59 private: 60 pw::bluetooth::emboss::CommandHeaderView header_view() const; 61 }; 62 63 // Helper subclass that remembers the view type it was constructed with. It is 64 // safe to slice a CommandPacketT into a CommandPacket. 65 template <class ViewT> 66 class CommandPacketT : public CommandPacket { 67 public: view_t()68 ViewT view_t() { return view<ViewT>(); } 69 70 private: 71 friend class CommandPacket; 72 CommandPacketT(hci_spec::OpCode opcode,size_t packet_size)73 CommandPacketT(hci_spec::OpCode opcode, size_t packet_size) 74 : CommandPacket(opcode, packet_size) {} 75 }; 76 77 template <class ViewT> 78 class EventPacketT; 79 80 // EventPacket is the HCI Event packet specialization of DynamicPacket. 81 class EventPacket : public DynamicPacket { 82 public: 83 // Construct an HCI Event packet of |packet_size| total bytes (header + 84 // payload). New(size_t packet_size)85 static EventPacket New(size_t packet_size) { 86 EventPacket packet(packet_size); 87 return packet; 88 } 89 90 // Construct an HCI Event packet from an Emboss view T and initialize its 91 // header with the |event_code| and size. 92 template <typename T> New(pw::bluetooth::emboss::EventCode event_code)93 static EventPacketT<T> New(pw::bluetooth::emboss::EventCode event_code) { 94 EventPacketT<T> packet(T::IntrinsicSizeInBytes().Read()); 95 auto header = 96 packet.template view<pw::bluetooth::emboss::EventHeaderWriter>(); 97 header.event_code().Write(event_code); 98 header.parameter_total_size().Write( 99 T::IntrinsicSizeInBytes().Read() - 100 pw::bluetooth::emboss::EventHeader::IntrinsicSizeInBytes()); 101 return packet; 102 } 103 104 template <typename T> New(hci_spec::EventCode event_code)105 static EventPacketT<T> New(hci_spec::EventCode event_code) { 106 EventPacketT<T> packet(T::IntrinsicSizeInBytes().Read()); 107 auto header = 108 packet.template view<pw::bluetooth::emboss::EventHeaderWriter>(); 109 header.event_code_uint().Write(event_code); 110 header.parameter_total_size().Write( 111 T::IntrinsicSizeInBytes().Read() - 112 pw::bluetooth::emboss::EventHeader::IntrinsicSizeInBytes()); 113 return packet; 114 } 115 116 // Construct an HCI Event packet from an Emboss view T of |packet_size| total 117 // bytes (header + payload) and initialize its header with the |event_code| 118 // and size. This constructor is meant for variable size packets, for which 119 // clients must calculate packet size manually. 120 template <typename T> 121 // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) New(hci_spec::EventCode event_code,size_t packet_size)122 static EventPacketT<T> New(hci_spec::EventCode event_code, 123 size_t packet_size) { 124 EventPacketT<T> packet(packet_size); 125 auto header = 126 packet.template view<pw::bluetooth::emboss::EventHeaderWriter>(); 127 header.event_code_uint().Write(event_code); 128 header.parameter_total_size().Write( 129 packet_size - 130 pw::bluetooth::emboss::EventHeader::IntrinsicSizeInBytes()); 131 return packet; 132 } 133 134 hci_spec::EventCode event_code() const; 135 136 // If this event packet contains a StatusCode field, this method returns the 137 // status. Not all events contain a StatusCode and not all of those that do 138 // are supported by this method. Returns std::nullopt for such events. 139 // 140 // NOTE: If you intend to use this with a new event code, make sure to add an 141 // entry to the implementation in control_packets.cc. 142 std::optional<pw::bluetooth::emboss::StatusCode> StatusCode() const; 143 144 // Returns a status if this event represents the result of an operation. See 145 // the documentation on StatusCode() as the same conditions apply to this 146 // method. Returns a default status of type HostError::kMalformedPacket on 147 // error. 148 hci::Result<> ToResult() const; 149 150 protected: 151 explicit EventPacket(size_t packet_size); 152 153 private: 154 // From an Emboss view T containing a StatusCode field named "status", returns 155 // the status. Returns std::nullopt on error. 156 template <typename T> StatusCodeFromView()157 std::optional<pw::bluetooth::emboss::StatusCode> StatusCodeFromView() const { 158 // Don't use view(), which asserts on IsComplete(). 159 T packet_view(data().data(), size()); 160 if (!packet_view.status().Ok()) { 161 return std::nullopt; 162 } 163 return packet_view.status().UncheckedRead(); 164 } 165 }; 166 167 // Helper subclass that remembers the view type it was constructed with. It is 168 // safe to slice an EventPacketT into an EventPacket. 169 template <class ViewT> 170 class EventPacketT : public EventPacket { 171 public: 172 template <typename... Args> view_t(Args...args)173 ViewT view_t(Args... args) { 174 return view<ViewT>(args...); 175 } 176 177 template <typename... Args> unchecked_view_t(Args...args)178 ViewT unchecked_view_t(Args... args) { 179 return unchecked_view<ViewT>(args...); 180 } 181 182 private: 183 friend class EventPacket; 184 EventPacketT(size_t packet_size)185 explicit EventPacketT(size_t packet_size) : EventPacket(packet_size) {} 186 }; 187 188 } // namespace bt::hci 189 190 // Convenience macro to check and log any non-Success status of an event. 191 // Evaluate to true if the event status is not success. 192 #define HCI_IS_ERROR(event, flag, tag, /*fmt*/...) \ 193 bt_is_error(event.ToResult(), flag, tag, __VA_ARGS__) 194