• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 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 #include "pw_bluetooth_proxy/basic_l2cap_channel.h"
16 
17 #include "pw_bluetooth/emboss_util.h"
18 #include "pw_bluetooth/hci_data.emb.h"
19 #include "pw_bluetooth/l2cap_frames.emb.h"
20 #include "pw_bluetooth_proxy/l2cap_channel_common.h"
21 #include "pw_log/log.h"
22 #include "pw_status/try.h"
23 
24 namespace pw::bluetooth::proxy {
25 
Create(L2capChannelManager & l2cap_channel_manager,multibuf::MultiBufAllocator * rx_multibuf_allocator,uint16_t connection_handle,AclTransportType transport,uint16_t local_cid,uint16_t remote_cid,OptionalPayloadReceiveCallback && payload_from_controller_fn,OptionalPayloadReceiveCallback && payload_from_host_fn,ChannelEventCallback && event_fn)26 pw::Result<BasicL2capChannel> BasicL2capChannel::Create(
27     L2capChannelManager& l2cap_channel_manager,
28     multibuf::MultiBufAllocator* rx_multibuf_allocator,
29     uint16_t connection_handle,
30     AclTransportType transport,
31     uint16_t local_cid,
32     uint16_t remote_cid,
33     OptionalPayloadReceiveCallback&& payload_from_controller_fn,
34     OptionalPayloadReceiveCallback&& payload_from_host_fn,
35     ChannelEventCallback&& event_fn) {
36   if (!AreValidParameters(/*connection_handle=*/connection_handle,
37                           /*local_cid=*/local_cid,
38                           /*remote_cid=*/remote_cid)) {
39     return pw::Status::InvalidArgument();
40   }
41 
42   return BasicL2capChannel(
43       l2cap_channel_manager,
44       rx_multibuf_allocator,
45       /*connection_handle=*/connection_handle,
46       /*transport=*/transport,
47       /*local_cid=*/local_cid,
48       /*remote_cid=*/remote_cid,
49       /*payload_from_controller_fn=*/std::move(payload_from_controller_fn),
50       /*payload_from_host_fn=*/std::move(payload_from_host_fn),
51       /*event_fn=*/std::move(event_fn));
52 }
53 
Write(multibuf::MultiBuf && payload)54 StatusWithMultiBuf BasicL2capChannel::Write(multibuf::MultiBuf&& payload) {
55   if (!IsOkL2capDataLength(payload.size())) {
56     PW_LOG_WARN("Payload (%zu bytes) is too large. So will not process.",
57                 payload.size());
58     return {Status::InvalidArgument(), std::move(payload)};
59   }
60 
61   return L2capChannel::Write(std::move(payload));
62 }
63 
GenerateNextTxPacket()64 std::optional<H4PacketWithH4> BasicL2capChannel::GenerateNextTxPacket() {
65   if (state() != State::kRunning || PayloadQueueEmpty()) {
66     return std::nullopt;
67   }
68 
69   ConstByteSpan payload = GetFrontPayloadSpan();
70 
71   pw::Result<H4PacketWithH4> result =
72       PopulateTxL2capPacket(payload.size_bytes());
73   if (!result.ok()) {
74     return std::nullopt;
75   }
76   H4PacketWithH4 h4_packet = std::move(result.value());
77 
78   Result<emboss::AclDataFrameWriter> result2 =
79       MakeEmbossWriter<emboss::AclDataFrameWriter>(h4_packet.GetHciSpan());
80   PW_CHECK(result2.ok());
81   emboss::AclDataFrameWriter acl = result2.value();
82 
83   // At this point we assume we can return a PDU with the payload.
84   PopFrontPayload();
85 
86   emboss::BFrameWriter bframe = emboss::MakeBFrameView(
87       acl.payload().BackingStorage().data(), acl.payload().SizeInBytes());
88   PW_CHECK(bframe.IsComplete());
89 
90   PW_CHECK(TryToCopyToEmbossStruct(bframe.payload(), payload));
91 
92   PW_CHECK(acl.Ok());
93   PW_CHECK(bframe.Ok());
94 
95   return h4_packet;
96 }
97 
BasicL2capChannel(L2capChannelManager & l2cap_channel_manager,multibuf::MultiBufAllocator * rx_multibuf_allocator,uint16_t connection_handle,AclTransportType transport,uint16_t local_cid,uint16_t remote_cid,OptionalPayloadReceiveCallback && payload_from_controller_fn,OptionalPayloadReceiveCallback && payload_from_host_fn,ChannelEventCallback && event_fn)98 BasicL2capChannel::BasicL2capChannel(
99     L2capChannelManager& l2cap_channel_manager,
100     multibuf::MultiBufAllocator* rx_multibuf_allocator,
101     uint16_t connection_handle,
102     AclTransportType transport,
103     uint16_t local_cid,
104     uint16_t remote_cid,
105     OptionalPayloadReceiveCallback&& payload_from_controller_fn,
106     OptionalPayloadReceiveCallback&& payload_from_host_fn,
107     ChannelEventCallback&& event_fn)
108     : L2capChannel(
109           l2cap_channel_manager,
110           rx_multibuf_allocator,
111           /*connection_handle=*/connection_handle,
112           /*transport=*/transport,
113           /*local_cid=*/local_cid,
114           /*remote_cid=*/remote_cid,
115           /*payload_from_controller_fn=*/std::move(payload_from_controller_fn),
116           /*payload_from_host_fn=*/std::move(payload_from_host_fn),
117           /*event_fn=*/std::move(event_fn)) {
118   PW_LOG_INFO("btproxy: BasicL2capChannel ctor");
119 }
120 
~BasicL2capChannel()121 BasicL2capChannel::~BasicL2capChannel() {
122   // Don't log dtor of moved-from channels.
123   if (state() != State::kUndefined) {
124     PW_LOG_INFO("btproxy: BasicL2capChannel dtor");
125   }
126 }
127 
DoHandlePduFromController(pw::span<uint8_t> bframe)128 bool BasicL2capChannel::DoHandlePduFromController(pw::span<uint8_t> bframe) {
129   Result<emboss::BFrameWriter> bframe_view =
130       MakeEmbossWriter<emboss::BFrameWriter>(bframe);
131 
132   if (!bframe_view.ok()) {
133     // TODO: https://pwbug.dev/360929142 - Stop channel on error.
134     PW_LOG_ERROR("(CID: 0x%X) Received invalid B-frame. So will drop.",
135                  local_cid());
136     return true;
137   }
138 
139   return SendPayloadFromControllerToClient(
140       span(bframe_view->payload().BackingStorage().data(),
141            bframe_view->payload().SizeInBytes()));
142 }
143 
HandlePduFromHost(pw::span<uint8_t> bframe)144 bool BasicL2capChannel::HandlePduFromHost(pw::span<uint8_t> bframe) {
145   Result<emboss::BFrameWriter> bframe_view =
146       MakeEmbossWriter<emboss::BFrameWriter>(bframe);
147 
148   if (!bframe_view.ok()) {
149     // TODO: https://pwbug.dev/360929142 - Stop channel on error.
150     PW_LOG_ERROR("(CID: 0x%X) Host transmitted invalid B-frame. So will drop.",
151                  local_cid());
152     return true;
153   }
154 
155   return SendPayloadFromHostToClient(
156       span(bframe_view->payload().BackingStorage().data(),
157            bframe_view->payload().SizeInBytes()));
158 }
159 
160 }  // namespace pw::bluetooth::proxy
161