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_sapphire/internal/host/common/byte_buffer.h" 17 18 namespace bt { 19 20 // This file defines classes which provide the interface for constructing HCI 21 // packets and reading/writing them using Emboss 22 // (https://github.com/google/emboss). 23 // 24 // Emboss does not own memory; it provides structured views into user allocated 25 // memory. These views are specified in Emboss source files such as hci.emb in 26 // pw_bluetooth, which implements the HCI protocol packet definitions. 27 // 28 // This file defines two classes: StaticPacket, which provides an Emboss view 29 // over a statically allocated buffer, and DynamicPacket, which is part of a 30 // class hierarchy that provides Emboss views over dynamic memory. 31 // 32 // EXAMPLE: 33 // 34 // Consider the following Emboss definition of the HCI Command packet header and 35 // Inquiry Command. 36 // 37 // [(cpp) namespace: "bt::hci_spec"] 38 // struct CommandHeader: 39 // 0 [+2] OpCodeBits opcode 40 // $next [+1] UInt parameter_total_size 41 // 42 // struct InquiryCommand: 43 // let hdr_size = CommandHeader.$size_in_bytes 44 // 0 [+hdr_size] CommandHeader header 45 // $next [+3] InquiryAccessCode lap 46 // $next [+1] UInt inquiry_length 47 // $next [+1] UInt num_responses 48 // 49 // The Emboss compiler generates two types of view for each struct. In the case 50 // of InquiryCommand, it generates InquiryCommandView (read-only) and 51 // InquiryCommandWriter (read & writable). We can parameterize StaticPacket over 52 // one of these views to read and/or write an Inquiry packet: 53 // 54 // bt::StaticPacket<pw::bluetooth::emboss::InquiryCommandWriter> packet; 55 // auto view = packet.view(); 56 // view.inquiry_length().Write(100); 57 // view.lap().Write(pw::bluetooth::emboss::InquiryAccessCode::GIAC); 58 // cout << "inquiry_length = " << view.inquiry_length().Read(); 59 // 60 // StaticPacket does not currently support packets with variable length. 61 template <typename T> 62 class StaticPacket { 63 public: 64 StaticPacket() = default; 65 66 // Copy this packet from another view. 67 template <typename U> StaticPacket(const U & other)68 explicit StaticPacket(const U& other) { 69 view().CopyFrom(other); 70 } 71 72 // Returns an Emboss view over the buffer. Emboss views consist of two 73 // pointers and a length, so they are cheap to construct on-demand. 74 template <typename... Args> view(Args...args)75 T view(Args... args) { 76 T view(args..., buffer_.mutable_data(), buffer_.size()); 77 BT_ASSERT(view.IsComplete()); 78 return view; 79 } 80 data()81 BufferView data() const { return {buffer_.data(), buffer_.size()}; } mutable_data()82 MutableBufferView mutable_data() { 83 return {buffer_.mutable_data(), buffer_.size()}; 84 } SetToZeros()85 void SetToZeros() { buffer_.SetToZeros(); } 86 87 private: 88 // The intrinsic size of an Emboss struct is the size required to hold all of 89 // its fields. An Emboss view has a static IntrinsicSizeInBytes() accessor if 90 // the struct does not have dynamic length (i.e. not a variable length 91 // packet). 92 StaticByteBuffer<T::IntrinsicSizeInBytes().Read()> buffer_; 93 }; 94 95 // DynamicPacket is the parent class of a two-level class hierarchy that 96 // implements dynamically-allocated HCI packets to which reading/writing is 97 // mediated by Emboss. 98 // 99 // DynamicPacket contains data and methods that are universal across packet 100 // type. Its children are packet type specializations, i.e. Command, Event, ACL, 101 // and Sco packets. These classes provide header-type-specific functionality. 102 // 103 // Instances of DynamicPacket should not be constructed directly. Instead, 104 // packet type specialization classes should provide static factory functions. 105 // 106 // See EmbossCommandPacket in emboss_control_packets.h for an example of a 107 // packet type specialization. 108 class DynamicPacket { 109 public: 110 // Returns an Emboss view over the buffer. Unlike StaticPacket, which ensures 111 // type security as a struct parameterized over a particular Emboss view type, 112 // DynamicPacket is a generic type for all packets, so view() is to be 113 // parameterized over an Emboss view type on each call. 114 template <typename T, typename... Args> view(Args...args)115 T view(Args... args) { 116 T view(args..., buffer_.mutable_data(), size()); 117 BT_ASSERT_MSG( 118 view.IsComplete(), 119 "emboss packet buffer not large enough to hold requested view"); 120 return view; 121 } 122 123 template <typename T, typename... Args> view(Args...args)124 T view(Args... args) const { 125 T view(args..., buffer_.data(), size()); 126 BT_ASSERT_MSG( 127 view.IsComplete(), 128 "emboss packet buffer not large enough to hold requested view"); 129 return view; 130 } 131 132 template <typename T, typename... Args> unchecked_view(Args...args)133 T unchecked_view(Args... args) const { 134 return T(args..., buffer_.data(), size()); 135 } 136 137 // Returns the size of the packet, i.e. payload size + header size. size()138 size_t size() const { return buffer_.size(); } data()139 BufferView data() const { return {buffer_.data(), size()}; } mutable_data()140 MutableBufferView mutable_data() { return {buffer_.mutable_data(), size()}; } release()141 DynamicByteBuffer release() { return std::move(buffer_); } 142 143 protected: 144 // Construct the buffer to hold |packet_size| bytes (payload + header). DynamicPacket(size_t packet_size)145 explicit DynamicPacket(size_t packet_size) : buffer_(packet_size) {} 146 147 private: 148 DynamicByteBuffer buffer_; 149 }; 150 151 } // namespace bt 152