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_bluetooth/att.emb.h"
18 #include "pw_bluetooth/emboss_util.h"
19 #include "pw_bluetooth/l2cap_frames.emb.h"
20 #include "pw_log/log.h"
21 #include "pw_status/try.h"
22
23 namespace pw::bluetooth::proxy {
Write(pw::span<const uint8_t> attribute_value)24 pw::Status GattNotifyChannel::Write(pw::span<const uint8_t> attribute_value) {
25 const uint16_t max_attribute_size =
26 MaxL2capPayloadSize() - emboss::AttHandleValueNtf::MinSizeInBytes();
27 if (attribute_value.size() > max_attribute_size) {
28 PW_LOG_ERROR("Attribute too large (%zu > %d). So will not process.",
29 attribute_value.size(),
30 max_attribute_size);
31 return pw::Status::InvalidArgument();
32 }
33
34 size_t att_size =
35 emboss::AttHandleValueNtf::MinSizeInBytes() + attribute_value.size();
36 pw::Result<H4PacketWithH4> h4_result = PopulateTxL2capPacket(att_size);
37 if (!h4_result.ok()) {
38 // This can fail as a result of the L2CAP PDU not fitting in an H4 buffer
39 // or if all buffers are occupied.
40 // TODO: https://pwbug.dev/365179076 - Once we support ACL fragmentation,
41 // this function will not fail due to the L2CAP PDU size not fitting.
42 return h4_result.status();
43 }
44 H4PacketWithH4 h4_packet = std::move(*h4_result);
45
46 // Write ATT PDU.
47 PW_TRY_ASSIGN(
48 auto acl,
49 MakeEmbossWriter<emboss::AclDataFrameWriter>(h4_packet.GetHciSpan()));
50 PW_TRY_ASSIGN(auto l2cap,
51 MakeEmbossWriter<emboss::BFrameWriter>(
52 acl.payload().BackingStorage().data(),
53 acl.payload().BackingStorage().SizeInBytes()));
54 PW_TRY_ASSIGN(auto att_notify,
55 MakeEmbossWriter<emboss::AttHandleValueNtfWriter>(
56 attribute_value.size(),
57 l2cap.payload().BackingStorage().data(),
58 att_size));
59 att_notify.attribute_opcode().Write(emboss::AttOpcode::ATT_HANDLE_VALUE_NTF);
60 att_notify.attribute_handle().Write(attribute_handle_);
61 std::memcpy(att_notify.attribute_value().BackingStorage().data(),
62 attribute_value.data(),
63 attribute_value.size());
64
65 return QueuePacket(std::move(h4_packet));
66 }
67
Create(L2capChannelManager & l2cap_channel_manager,uint16_t connection_handle,uint16_t attribute_handle)68 pw::Result<GattNotifyChannel> GattNotifyChannel::Create(
69 L2capChannelManager& l2cap_channel_manager,
70 uint16_t connection_handle,
71 uint16_t attribute_handle) {
72 if (!L2capWriteChannel::AreValidParameters(connection_handle,
73 kAttributeProtocolCID)) {
74 return pw::Status::InvalidArgument();
75 }
76 if (attribute_handle == 0) {
77 PW_LOG_ERROR("Attribute handle cannot be 0.");
78 return pw::Status::InvalidArgument();
79 }
80 return GattNotifyChannel(
81 l2cap_channel_manager, connection_handle, attribute_handle);
82 }
83
GattNotifyChannel(L2capChannelManager & l2cap_channel_manager,uint16_t connection_handle,uint16_t attribute_handle)84 GattNotifyChannel::GattNotifyChannel(L2capChannelManager& l2cap_channel_manager,
85 uint16_t connection_handle,
86 uint16_t attribute_handle)
87 : L2capWriteChannel(l2cap_channel_manager,
88 connection_handle,
89 AclTransportType::kLe,
90 kAttributeProtocolCID),
91 attribute_handle_(attribute_handle) {}
92
93 } // namespace pw::bluetooth::proxy
94