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 EmbossCommandPacketT; 26 27 // EmbossCommandPacket is the HCI Command packet specialization of 28 // DynamicPacket. 29 class EmbossCommandPacket : 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 EmbossCommandPacketT<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 EmbossCommandPacketT<T> New(hci_spec::OpCode opcode, 44 size_t packet_size) { 45 EmbossCommandPacketT<T> packet(opcode, packet_size); 46 return packet; 47 } 48 49 hci_spec::OpCode opcode() const; 50 // Returns the OGF (OpCode Group Field) which occupies the upper 6-bits of the 51 // opcode. 52 uint8_t ogf() const; 53 // Returns the OCF (OpCode Command Field) which occupies the lower 10-bits of 54 // the opcode. 55 uint16_t ocf() const; 56 57 protected: 58 explicit EmbossCommandPacket(hci_spec::OpCode opcode, size_t packet_size); 59 60 private: 61 pw::bluetooth::emboss::CommandHeaderView header_view() const; 62 }; 63 64 // Helper subclass that remembers the view type it was constructed with. It is 65 // safe to slice an EmbossCommandPacketT into an EmbossCommandPacket. 66 template <class ViewT> 67 class EmbossCommandPacketT : public EmbossCommandPacket { 68 public: view_t()69 ViewT view_t() { return view<ViewT>(); } 70 71 private: 72 friend class EmbossCommandPacket; 73 EmbossCommandPacketT(hci_spec::OpCode opcode,size_t packet_size)74 EmbossCommandPacketT(hci_spec::OpCode opcode, size_t packet_size) 75 : EmbossCommandPacket(opcode, packet_size) {} 76 }; 77 78 template <class ViewT> 79 class EmbossEventPacketT; 80 81 // EmbossEventPacket is the HCI Event packet specialization of DynamicPacket. 82 class EmbossEventPacket : public DynamicPacket { 83 public: 84 // Construct an HCI Event packet of |packet_size| total bytes (header + 85 // payload). New(size_t packet_size)86 static EmbossEventPacket New(size_t packet_size) { 87 EmbossEventPacket packet(packet_size); 88 return packet; 89 } 90 91 // Construct an HCI Event packet from an Emboss view T and initialize its 92 // header with the |event_code| and size. 93 template <typename T> New(hci_spec::EventCode event_code)94 static EmbossEventPacketT<T> New(hci_spec::EventCode event_code) { 95 EmbossEventPacketT<T> packet(T::IntrinsicSizeInBytes().Read()); 96 auto header = 97 packet.template view<pw::bluetooth::emboss::EventHeaderWriter>(); 98 header.event_code().Write(event_code); 99 header.parameter_total_size().Write( 100 T::IntrinsicSizeInBytes().Read() - 101 pw::bluetooth::emboss::EventHeader::IntrinsicSizeInBytes()); 102 return packet; 103 } 104 105 // Construct an HCI Event packet from an Emboss view T of |packet_size| total 106 // bytes (header + payload) and initialize its header with the |event_code| 107 // and size. This constructor is meant for variable size packets, for which 108 // clients must calculate packet size manually. 109 template <typename T> 110 // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) New(hci_spec::EventCode event_code,size_t packet_size)111 static EmbossEventPacketT<T> New(hci_spec::EventCode event_code, 112 size_t packet_size) { 113 EmbossEventPacketT<T> packet(packet_size); 114 auto header = 115 packet.template view<pw::bluetooth::emboss::EventHeaderWriter>(); 116 header.event_code().Write(event_code); 117 header.parameter_total_size().Write( 118 packet_size - 119 pw::bluetooth::emboss::EventHeader::IntrinsicSizeInBytes()); 120 return packet; 121 } 122 123 hci_spec::EventCode event_code() const; 124 125 // If this event packet contains a StatusCode field, this method returns the 126 // status. Not all events contain a StatusCode and not all of those that do 127 // are supported by this method. Returns std::nullopt for such events. 128 // 129 // NOTE: If you intend to use this with a new event code, make sure to add an 130 // entry to the implementation in emboss_control_packets.cc. 131 std::optional<pw::bluetooth::emboss::StatusCode> StatusCode() const; 132 133 // Returns a status if this event represents the result of an operation. See 134 // the documentation on StatusCode() as the same conditions apply to this 135 // method. Returns a default status of type HostError::kMalformedPacket on 136 // error. 137 hci::Result<> ToResult() const; 138 139 protected: 140 explicit EmbossEventPacket(size_t packet_size); 141 142 private: 143 // From an Emboss view T containing a StatusCode field named "status", returns 144 // the status. Returns std::nullopt on error. 145 template <typename T> StatusCodeFromView()146 std::optional<pw::bluetooth::emboss::StatusCode> StatusCodeFromView() const { 147 // Don't use view(), which asserts on IsComplete(). 148 T packet_view(data().data(), size()); 149 if (!packet_view.status().Ok()) { 150 return std::nullopt; 151 } 152 return packet_view.status().UncheckedRead(); 153 } 154 }; 155 156 // Helper subclass that remembers the view type it was constructed with. It is 157 // safe to slice an EmbossEventPacketT into an EmbossEventPacket. 158 template <class ViewT> 159 class EmbossEventPacketT : public EmbossEventPacket { 160 public: 161 template <typename... Args> view_t(Args...args)162 ViewT view_t(Args... args) { 163 return view<ViewT>(args...); 164 } 165 166 private: 167 friend class EmbossEventPacket; 168 EmbossEventPacketT(size_t packet_size)169 explicit EmbossEventPacketT(size_t packet_size) 170 : EmbossEventPacket(packet_size) {} 171 }; 172 173 } // namespace bt::hci 174