• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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