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