• 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/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