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/gatt_notify_channel.h"
16
17 #include "pw_assert/check.h"
18 #include "pw_bluetooth/att.emb.h"
19 #include "pw_bluetooth/emboss_util.h"
20 #include "pw_bluetooth/l2cap_frames.emb.h"
21 #include "pw_log/log.h"
22 #include "pw_status/try.h"
23
24 namespace pw::bluetooth::proxy {
Write(pw::span<const uint8_t> attribute_value)25 pw::Status GattNotifyChannel::Write(pw::span<const uint8_t> attribute_value) {
26 std::optional<uint16_t> max_l2cap_payload_size = MaxL2capPayloadSize();
27 if (!max_l2cap_payload_size) {
28 PW_LOG_ERROR("Tried to write before LE_Read_Buffer_Size processed.");
29 return Status::FailedPrecondition();
30 }
31 if (*max_l2cap_payload_size <= emboss::AttHandleValueNtf::MinSizeInBytes()) {
32 PW_LOG_ERROR("LE ACL data packet size limit does not support writing.");
33 return Status::FailedPrecondition();
34 }
35 const uint16_t max_attribute_size =
36 *max_l2cap_payload_size - emboss::AttHandleValueNtf::MinSizeInBytes();
37 if (attribute_value.size() > max_attribute_size) {
38 PW_LOG_ERROR("Attribute too large (%zu > %d). So will not process.",
39 attribute_value.size(),
40 max_attribute_size);
41 return pw::Status::InvalidArgument();
42 }
43
44 size_t att_size =
45 emboss::AttHandleValueNtf::MinSizeInBytes() + attribute_value.size();
46 pw::Result<H4PacketWithH4> h4_result =
47 PopulateTxL2capPacketDuringWrite(att_size);
48 if (!h4_result.ok()) {
49 // This can fail as a result of the L2CAP PDU not fitting in an H4 buffer
50 // or if all buffers are occupied.
51 // TODO: https://pwbug.dev/379337260 - Once we support ACL fragmentation,
52 // this function will not fail due to the L2CAP PDU size not fitting.
53 return h4_result.status();
54 }
55 H4PacketWithH4 h4_packet = std::move(*h4_result);
56
57 // Write ATT PDU.
58 PW_TRY_ASSIGN(
59 auto acl,
60 MakeEmbossWriter<emboss::AclDataFrameWriter>(h4_packet.GetHciSpan()));
61 PW_TRY_ASSIGN(auto l2cap,
62 MakeEmbossWriter<emboss::BFrameWriter>(
63 acl.payload().BackingStorage().data(),
64 acl.payload().BackingStorage().SizeInBytes()));
65 PW_TRY_ASSIGN(auto att_notify,
66 MakeEmbossWriter<emboss::AttHandleValueNtfWriter>(
67 attribute_value.size(),
68 l2cap.payload().BackingStorage().data(),
69 att_size));
70 att_notify.attribute_opcode().Write(emboss::AttOpcode::ATT_HANDLE_VALUE_NTF);
71 att_notify.attribute_handle().Write(attribute_handle_);
72
73 PW_CHECK(
74 TryToCopyToEmbossStruct(att_notify.attribute_value(), attribute_value));
75
76 return QueuePacket(std::move(h4_packet));
77 }
78
Create(L2capChannelManager & l2cap_channel_manager,uint16_t connection_handle,uint16_t attribute_handle,ChannelEventCallback && event_fn)79 pw::Result<GattNotifyChannel> GattNotifyChannel::Create(
80 L2capChannelManager& l2cap_channel_manager,
81 uint16_t connection_handle,
82 uint16_t attribute_handle,
83 ChannelEventCallback&& event_fn) {
84 if (!AreValidParameters(/*connection_handle=*/connection_handle,
85 /*local_cid=*/kAttributeProtocolCID,
86 /*remote_cid=*/kAttributeProtocolCID)) {
87 return pw::Status::InvalidArgument();
88 }
89 if (attribute_handle == 0) {
90 PW_LOG_ERROR("Attribute handle cannot be 0.");
91 return pw::Status::InvalidArgument();
92 }
93 return GattNotifyChannel(/*l2cap_channel_manager=*/l2cap_channel_manager,
94 /*connection_handle=*/connection_handle,
95 /*attribute_handle=*/attribute_handle,
96 std::move(event_fn));
97 }
98
GattNotifyChannel(L2capChannelManager & l2cap_channel_manager,uint16_t connection_handle,uint16_t attribute_handle,ChannelEventCallback && event_fn)99 GattNotifyChannel::GattNotifyChannel(L2capChannelManager& l2cap_channel_manager,
100 uint16_t connection_handle,
101 uint16_t attribute_handle,
102 ChannelEventCallback&& event_fn)
103 : L2capChannel(/*l2cap_channel_manager=*/l2cap_channel_manager,
104 /*rx_multibuf_allocator*/ nullptr,
105 /*connection_handle=*/connection_handle,
106 /*transport=*/AclTransportType::kLe,
107 /*local_cid=*/kAttributeProtocolCID,
108 /*remote_cid=*/kAttributeProtocolCID,
109 /*payload_from_controller_fn=*/nullptr,
110 /*payload_from_host_fn=*/nullptr,
111 /*event_fn=*/std::move(event_fn)),
112 attribute_handle_(attribute_handle) {
113 PW_LOG_INFO("btproxy: GattNotifyChannel ctor - attribute_handle: %u",
114 attribute_handle_);
115 }
116
~GattNotifyChannel()117 GattNotifyChannel::~GattNotifyChannel() {
118 PW_LOG_INFO("btproxy: GattNotifyChannel dtor - attribute_handle: %u",
119 attribute_handle_);
120 }
121
122 } // namespace pw::bluetooth::proxy
123