• 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 <lib/fit/defer.h>
17 
18 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
19 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
20 #include "pw_bluetooth_sapphire/internal/host/transport/command_channel.h"
21 #include "pw_bluetooth_sapphire/internal/host/transport/error.h"
22 
23 namespace bt::hci {
24 
25 // CommandHandler is a wrapper around CommandChannel that abstracts serializing
26 // & deserializing of command and event packets. Command and event types must
27 // implement methods and fields documented above each method.
28 // TODO(fxbug.dev/42135598): Types should match PDL generated packet
29 // definitions.
30 //
31 // This class does not track state regarding commands and events, so it may be
32 // used as either a temporary or saved object.
33 class CommandHandler {
34  public:
CommandHandler(CommandChannel::WeakPtr channel)35   explicit CommandHandler(CommandChannel::WeakPtr channel)
36       : channel_(std::move(channel)) {}
37 
38   // Wrapper around CommandChannel::SendCommand that sends a CommandT and
39   // completes on CommandT::EventT.
40   //
41   // If an event status field indicates an error, that error will be returned
42   // instead of the event.
43   //
44   // The status event of async commands will be ignored unless it is an error.
45   //
46   // CommandT must implement:
47   // std::unique_ptr<CommandPacket> Encode();
48   // using EventT = ...;
49   // static OpCode opcode();
50   //
51   // EventT must implement:
52   // static fit::result<bt::Error<>, EventT> Decode(const EventPacket& packet);
53   // static constexpr uint8_t kEventCode = ...;
54   template <typename CommandT>
SendCommand(CommandT command,ResultCallback<typename CommandT::EventT> event_cb)55   CommandChannel::TransactionId SendCommand(
56       CommandT command, ResultCallback<typename CommandT::EventT> event_cb) {
57     // EventT should be the command complete event code. Use
58     // SendCommandFinishOnStatus to only handle the command status event.
59     static_assert(CommandT::EventT::kEventCode !=
60                   hci_spec::kCommandStatusEventCode);
61     BT_ASSERT(event_cb);
62 
63     auto encoded = command.Encode();
64     auto event_packet_cb = [event_cb = std::move(event_cb)](
65                                auto id,
66                                const EventPacket& event_packet) mutable {
67       BT_ASSERT_MSG(event_cb,
68                     "SendCommand event callback already called (opcode: %#.4x)",
69                     CommandT::opcode());
70 
71       auto status = event_packet.ToResult();
72       if (status.is_error()) {
73         event_cb(status.take_error());
74         return;
75       }
76 
77       // Ignore success status event if it is not the expected completion event.
78       if (event_packet.event_code() == hci_spec::kCommandStatusEventCode &&
79           CommandT::EventT::kEventCode != hci_spec::kCommandStatusEventCode) {
80         bt_log(TRACE,
81                "hci",
82                "received success command status event (opcode: %#.4x)",
83                CommandT::opcode());
84         return;
85       }
86 
87       BT_ASSERT(event_packet.event_code() == CommandT::EventT::kEventCode);
88 
89       auto event_result = CommandT::EventT::Decode(event_packet);
90       if (event_result.is_error()) {
91         bt_log(WARN,
92                "hci",
93                "Error decoding event packet (event: %#.2x, error: %s)",
94                event_packet.event_code(),
95                bt_str(event_result.error_value()));
96         event_cb(event_result.take_error());
97         return;
98       }
99       event_cb(event_result.take_value());
100     };
101     return channel_->SendCommand(std::move(encoded),
102                                  std::move(event_packet_cb),
103                                  CommandT::EventT::kEventCode);
104   }
105 
106   // Same as SendCommand, but completes on the command status event.
107   // The complete event WILL BE IGNORED if no event handler is registered.
108   //
109   // This is useful when the command complete event is already handled by an
110   // event handler, and you only need to handle command errors.
111   //
112   // Example:
113   // handler.AddEventHandler(fit::bind_member<&BrEdrConnectionManager::OnConnectionComplete>(this));
114   //
115   // handler.SendCommandFinishOnStatus(
116   //  CreateConnectionCommand{...},
117   //  [](auto result) {
118   //    if (result.is_error()) {
119   //      // Handle error
120   //      return;
121   //    }
122   // });
123   template <typename CommandT>
SendCommandFinishOnStatus(CommandT command,hci::ResultCallback<> status_cb)124   CommandChannel::TransactionId SendCommandFinishOnStatus(
125       CommandT command, hci::ResultCallback<> status_cb) {
126     BT_ASSERT(status_cb);
127 
128     auto encoded = command.Encode();
129     auto event_packet_cb = [status_cb = std::move(status_cb)](
130                                auto id,
131                                const EventPacket& event_packet) mutable {
132       BT_ASSERT(event_packet.event_code() == hci_spec::kCommandStatusEventCode);
133 
134       status_cb(event_packet.ToResult());
135     };
136     return channel_->SendCommand(std::move(encoded),
137                                  std::move(event_packet_cb),
138                                  hci_spec::kCommandStatusEventCode);
139   }
140 
141   // Wrapper around CommandChannel::AddEventHandler that calls |handler| with an
142   // EventT.
143   //
144   // EventT must implement:
145   // static fit::result<bt::Error<>, EventT> Decode(const EventPacket& packet);
146   // static constexpr uint8_t kEventCode = ...;
147   template <typename EventT>
AddEventHandler(fit::function<CommandChannel::EventCallbackResult (EventT)> handler)148   CommandChannel::EventHandlerId AddEventHandler(
149       fit::function<CommandChannel::EventCallbackResult(EventT)> handler) {
150     BT_ASSERT(handler);
151 
152     auto event_packet_cb =
153         [handler = std::move(handler)](const EventPacket& event_packet) {
154           auto event_result = EventT::Decode(event_packet);
155           if (event_result.is_error()) {
156             bt_log(WARN,
157                    "hci",
158                    "Error decoding event packet (event: %#.2x, error: %s)",
159                    event_packet.event_code(),
160                    bt_str(event_result.error_value()));
161             return CommandChannel::EventCallbackResult::kContinue;
162           }
163           return handler(std::move(event_result).value());
164         };
165     return channel_->AddEventHandler(EventT::kEventCode,
166                                      std::move(event_packet_cb));
167   }
168 
169  private:
170   CommandChannel::WeakPtr channel_;
171 };
172 
173 }  // namespace bt::hci
174