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