xref: /aosp_15_r20/external/pigweed/pw_bluetooth_proxy/l2cap_signaling_channel.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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/internal/l2cap_signaling_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/h4_packet.h"
21 #include "pw_bluetooth_proxy/internal/l2cap_channel_manager.h"
22 #include "pw_bluetooth_proxy/internal/l2cap_coc_internal.h"
23 #include "pw_log/log.h"
24 #include "pw_status/status.h"
25 #include "pw_status/try.h"
26 
27 namespace pw::bluetooth::proxy {
28 
L2capSignalingChannel(L2capChannelManager & l2cap_channel_manager,uint16_t connection_handle,uint16_t fixed_cid)29 L2capSignalingChannel::L2capSignalingChannel(
30     L2capChannelManager& l2cap_channel_manager,
31     uint16_t connection_handle,
32     uint16_t fixed_cid)
33     : BasicL2capChannel(/*l2cap_channel_manager=*/l2cap_channel_manager,
34                         /*connection_handle=*/connection_handle,
35                         /*local_cid=*/fixed_cid,
36                         /*remote_cid=*/fixed_cid,
37                         /*payload_from_controller_fn=*/nullptr),
38       l2cap_channel_manager_(l2cap_channel_manager) {}
39 
operator =(L2capSignalingChannel && other)40 L2capSignalingChannel& L2capSignalingChannel::operator=(
41     L2capSignalingChannel&& other) {
42   BasicL2capChannel::operator=(std::move(other));
43   return *this;
44 }
45 
HandlePduFromController(pw::span<uint8_t> cframe)46 bool L2capSignalingChannel::HandlePduFromController(pw::span<uint8_t> cframe) {
47   Result<emboss::CFrameView> cframe_view =
48       MakeEmbossView<emboss::CFrameView>(cframe);
49   if (!cframe_view.ok()) {
50     PW_LOG_ERROR(
51         "Buffer is too small for C-frame. So will forward to host without "
52         "processing.");
53     return false;
54   }
55 
56   // TODO: https://pwbug.dev/360929142 - "If a device receives a C-frame that
57   // exceeds its L2CAP_SIG_MTU_SIZE then it shall send an
58   // L2CAP_COMMAND_REJECT_RSP packet containing the supported
59   // L2CAP_SIG_MTU_SIZE." We should consider taking the signaling MTU in the
60   // ProxyHost constructor.
61   return OnCFramePayload(
62       pw::span(cframe_view->payload().BackingStorage().data(),
63                cframe_view->payload().BackingStorage().SizeInBytes()));
64 }
65 
HandlePduFromHost(pw::span<uint8_t>)66 bool L2capSignalingChannel::HandlePduFromHost(pw::span<uint8_t>) {
67   // Forward all to controller.
68   return false;
69 }
70 
HandleL2capSignalingCommand(emboss::L2capSignalingCommandView cmd)71 bool L2capSignalingChannel::HandleL2capSignalingCommand(
72     emboss::L2capSignalingCommandView cmd) {
73   PW_MODIFY_DIAGNOSTICS_PUSH();
74   PW_MODIFY_DIAGNOSTIC(ignored, "-Wswitch-enum");
75   switch (cmd.command_header().code().Read()) {
76     case emboss::L2capSignalingPacketCode::FLOW_CONTROL_CREDIT_IND: {
77       return HandleFlowControlCreditInd(
78           emboss::MakeL2capFlowControlCreditIndView(cmd.BackingStorage().data(),
79                                                     cmd.SizeInBytes()));
80     }
81     default: {
82       return false;
83     }
84   }
85   PW_MODIFY_DIAGNOSTICS_POP();
86 }
87 
HandleFlowControlCreditInd(emboss::L2capFlowControlCreditIndView cmd)88 bool L2capSignalingChannel::HandleFlowControlCreditInd(
89     emboss::L2capFlowControlCreditIndView cmd) {
90   if (!cmd.IsComplete()) {
91     PW_LOG_ERROR(
92         "Buffer is too small for L2CAP_FLOW_CONTROL_CREDIT_IND. So will "
93         "forward to host without processing.");
94     return false;
95   }
96 
97   L2capWriteChannel* found_channel = l2cap_channel_manager_.FindWriteChannel(
98       L2capReadChannel::connection_handle(), cmd.cid().Read());
99   if (found_channel) {
100     // If this L2CAP_FLOW_CONTROL_CREDIT_IND is addressed to a channel managed
101     // by the proxy, it must be an L2CAP connection-oriented channel.
102     // TODO: https://pwbug.dev/360929142 - Validate type in case remote peer
103     // sends indication addressed to wrong CID.
104     L2capCocInternal* coc_ptr = static_cast<L2capCocInternal*>(found_channel);
105     coc_ptr->AddCredits(cmd.credits().Read());
106     return true;
107   }
108 
109   return false;
110 }
111 
SendFlowControlCreditInd(uint16_t cid,uint16_t credits)112 Status L2capSignalingChannel::SendFlowControlCreditInd(uint16_t cid,
113                                                        uint16_t credits) {
114   if (cid == 0) {
115     PW_LOG_ERROR("Tried to send signaling packet on invalid CID 0x0.");
116     return Status::InvalidArgument();
117   }
118 
119   PW_TRY_ASSIGN(H4PacketWithH4 h4_packet,
120                 PopulateTxL2capPacket(
121                     emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes()));
122   PW_TRY_ASSIGN(
123       auto acl,
124       MakeEmbossWriter<emboss::AclDataFrameWriter>(h4_packet.GetHciSpan()));
125   emboss::CFrameWriter cframe = emboss::MakeCFrameView(
126       acl.payload().BackingStorage().data(), acl.payload().SizeInBytes());
127   emboss::L2capFlowControlCreditIndWriter ind =
128       emboss::MakeL2capFlowControlCreditIndView(
129           cframe.payload().BackingStorage().data(),
130           cframe.payload().SizeInBytes());
131   ind.command_header().code().Write(
132       emboss::L2capSignalingPacketCode::FLOW_CONTROL_CREDIT_IND);
133   ind.command_header().data_length().Write(
134       emboss::L2capFlowControlCreditInd::IntrinsicSizeInBytes() -
135       emboss::L2capSignalingCommandHeader::IntrinsicSizeInBytes());
136   ind.cid().Write(cid);
137   ind.credits().Write(credits);
138 
139   return QueuePacket(std::move(h4_packet));
140 }
141 
142 }  // namespace pw::bluetooth::proxy
143