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 <endian.h> 17 18 #include <memory> 19 20 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h" 21 #include "pw_bluetooth_sapphire/internal/host/common/macros.h" 22 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h" 23 #include "pw_bluetooth_sapphire/internal/host/hci-spec/vendor_protocol.h" 24 #include "pw_bluetooth_sapphire/internal/host/transport/error.h" 25 #include "pw_bluetooth_sapphire/internal/host/transport/packet.h" 26 27 namespace bt::hci { 28 29 using CommandPacket = Packet<hci_spec::CommandHeader>; 30 using EventPacket = Packet<hci_spec::EventHeader>; 31 using EventPacketPtr = std::unique_ptr<EventPacket>; 32 33 // Packet template specialization for HCI command packets. 34 template <> 35 class Packet<hci_spec::CommandHeader> 36 : public PacketBase<hci_spec::CommandHeader, CommandPacket> { 37 public: 38 // Slab-allocates a new CommandPacket with the given payload size and 39 // initializes the packet's header field. 40 static std::unique_ptr<CommandPacket> New(hci_spec::OpCode opcode, 41 size_t payload_size = 0u); 42 43 // Returns the HCI command opcode currently in this packet. opcode()44 hci_spec::OpCode opcode() const { return le16toh(view().header().opcode); } 45 46 // Convenience function to get a mutable payload of a packet. 47 template <typename PayloadType> mutable_payload()48 PayloadType* mutable_payload() { 49 return mutable_view()->mutable_payload<PayloadType>(); 50 } 51 52 protected: 53 using PacketBase<hci_spec::CommandHeader, CommandPacket>::PacketBase; 54 55 private: 56 // Writes the given header fields into the underlying buffer. 57 void WriteHeader(hci_spec::OpCode opcode); 58 }; 59 60 // Packet template specialization for HCI event packets. 61 template <> 62 class Packet<hci_spec::EventHeader> 63 : public PacketBase<hci_spec::EventHeader, EventPacket> { 64 public: 65 // Slab-allocates a new EventPacket with the given payload size without 66 // initializing its contents. 67 static std::unique_ptr<EventPacket> New(size_t payload_size); 68 69 // Returns the HCI event code currently in this packet. event_code()70 hci_spec::EventCode event_code() const { return view().header().event_code; } 71 72 // Convenience function to get a parameter payload from a packet 73 template <typename ParamsType> params()74 const ParamsType& params() const { 75 return view().payload<ParamsType>(); 76 } 77 78 // If this is a CommandComplete event packet, this method returns a pointer to 79 // the beginning of the return parameter structure. If the given template type 80 // would exceed the bounds of the packet or if this packet does not represent 81 // a CommandComplete event, this method returns nullptr. 82 template <typename ReturnParams> return_params()83 const ReturnParams* return_params() const { 84 if (event_code() != hci_spec::kCommandCompleteEventCode || 85 sizeof(ReturnParams) > view().payload_size() - 86 sizeof(hci_spec::CommandCompleteEventParams)) 87 return nullptr; 88 return reinterpret_cast<const ReturnParams*>( 89 params<hci_spec::CommandCompleteEventParams>().return_parameters); 90 } 91 92 // If this is a LE Meta Event packet, this method returns a pointer to the 93 // beginning of the subevent parameter structure. If the given template type 94 // would exceed the bounds of the packet or if this packet does not represent 95 // a LE Meta Event, this method returns nullptr. 96 template <typename SubeventParams> subevent_params()97 const SubeventParams* subevent_params() const { 98 if (event_code() != hci_spec::kLEMetaEventCode || 99 sizeof(SubeventParams) > 100 view().payload_size() - sizeof(hci_spec::LEMetaEventParams)) { 101 return nullptr; 102 } 103 104 return reinterpret_cast<const SubeventParams*>( 105 params<hci_spec::LEMetaEventParams>().subevent_parameters); 106 } 107 108 // If this is an event packet with a standard status (See Vol 2, Part D), this 109 // method returns true and populates |out_status| using the status from the 110 // event parameters. 111 // 112 // Not all events contain a status code and not all of those that do are 113 // supported by this method. Returns false for such events and |out_status| is 114 // left unmodified. 115 // 116 // NOTE: Using this method on an unsupported event packet will trigger an 117 // assertion in debug builds. If you intend to use this with a new event code, 118 // make sure to add an entry to the implementation in control_packets.cc. 119 // 120 // TODO(armansito): Add more event entries here as needed. 121 bool ToStatusCode(pw::bluetooth::emboss::StatusCode* out_code) const; 122 123 // Returns a status if this event represents the result of an operation. See 124 // the documentation on ToStatusCode() as the same conditions apply to this 125 // method. Instead of a boolean, this returns a default status of type 126 // HostError::kMalformedPacket. 127 Result<> ToResult() const; 128 129 // Initializes the internal PacketView by reading the header portion of the 130 // underlying buffer. 131 void InitializeFromBuffer(); 132 133 protected: 134 using PacketBase<hci_spec::EventHeader, EventPacket>::PacketBase; 135 }; 136 137 } // namespace bt::hci 138 139 // Convenience macros to check and log any non-Success status of an event. 140 // Evaluate to true if the event status is not success. 141 #define hci_is_error(event, flag, tag, fmt...) \ 142 bt_is_error(event.ToResult(), flag, tag, fmt) 143