xref: /aosp_15_r20/external/pigweed/pw_transfer/transfer.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 <mutex>
16 #define PW_LOG_MODULE_NAME "TRN"
17 #define PW_LOG_LEVEL PW_TRANSFER_CONFIG_LOG_LEVEL
18 
19 #include "public/pw_transfer/transfer.h"
20 #include "pw_assert/check.h"
21 #include "pw_log/log.h"
22 #include "pw_status/try.h"
23 #include "pw_transfer/internal/chunk.h"
24 #include "pw_transfer/internal/config.h"
25 #include "pw_transfer/transfer.h"
26 
27 namespace pw::transfer {
28 
HandleChunk(ConstByteSpan message,internal::TransferType type)29 void TransferService::HandleChunk(ConstByteSpan message,
30                                   internal::TransferType type) {
31   Result<internal::Chunk> chunk = internal::Chunk::Parse(message);
32   if (!chunk.ok()) {
33     PW_LOG_ERROR("Failed to decode transfer chunk: %d", chunk.status().code());
34     return;
35   }
36 
37   if (chunk->IsInitialChunk()) {
38     uint32_t resource_id =
39         chunk->is_legacy() ? chunk->session_id() : chunk->resource_id().value();
40 
41     uint32_t session_id;
42     if (chunk->is_legacy()) {
43       session_id = chunk->session_id();
44     } else if (chunk->desired_session_id().has_value()) {
45       session_id = chunk->desired_session_id().value();
46     } else {
47       // Non-legacy start chunks are required to use desired_session_id.
48       thread_.SendServerStatus(type,
49                                chunk->session_id(),
50                                chunk->protocol_version(),
51                                Status::DataLoss());
52       return;
53     }
54 
55     uint32_t initial_offset;
56 
57     if (chunk->is_legacy()) {
58       initial_offset = 0;
59     } else {
60       initial_offset = chunk->initial_offset();
61     }
62 
63     thread_.StartServerTransfer(type,
64                                 chunk->protocol_version(),
65                                 session_id,
66                                 resource_id,
67                                 message,
68                                 max_parameters_,
69                                 chunk_timeout_,
70                                 max_retries_,
71                                 max_lifetime_retries_,
72                                 initial_offset);
73   } else {
74     thread_.ProcessServerChunk(message);
75   }
76 }
77 
GetResourceStatus(pw::ConstByteSpan request,pw::rpc::RawUnaryResponder & responder)78 void TransferService::GetResourceStatus(pw::ConstByteSpan request,
79                                         pw::rpc::RawUnaryResponder& responder) {
80   uint32_t resource_id;
81   Status status;
82   std::array<std::byte, pwpb::ResourceStatus::kMaxEncodedSizeBytes> buffer = {};
83   pwpb::ResourceStatus::MemoryEncoder encoder(buffer);
84 
85   protobuf::Decoder decoder(request);
86   if (status = decoder.Next(); status.IsOutOfRange()) {
87     resource_id = 0;
88   } else if (!status.ok()) {
89     responder.Finish({}, Status::DataLoss()).IgnoreError();
90     return;
91   } else if (static_cast<pwpb::ResourceStatusRequest::Fields>(
92                  decoder.FieldNumber()) ==
93              pwpb::ResourceStatusRequest::Fields::kResourceId) {
94     if (status = decoder.ReadUint32(&resource_id); !status.ok()) {
95       responder.Finish({}, Status::DataLoss()).IgnoreError();
96       return;
97     }
98   } else {
99     responder.Finish({}, Status::DataLoss()).IgnoreError();
100     return;
101   }
102 
103   encoder.WriteResourceId(resource_id).IgnoreError();
104 
105   {
106     std::lock_guard lock(resource_responder_mutex_);
107     if (TransferService::resource_responder_.active()) {
108       PW_LOG_ERROR("Previous GetResourceStatus still being handled!");
109       responder.Finish(ConstByteSpan(encoder), Status::Unavailable())
110           .IgnoreError();
111       return;
112     }
113 
114     TransferService::resource_responder_ = std::move(responder);
115   }
116 
117   thread_.EnqueueResourceEvent(
118       resource_id,
119       [this](Status call_status, const internal::ResourceStatus stats) {
120         this->ResourceStatusCallback(call_status, stats);
121       });
122 }
123 
ResourceStatusCallback(Status status,const internal::ResourceStatus & stats)124 void TransferService::ResourceStatusCallback(
125     Status status, const internal::ResourceStatus& stats) {
126   std::lock_guard lock(resource_responder_mutex_);
127 
128   if (!resource_responder_.active()) {
129     PW_LOG_ERROR("ResourceStatusCallback invoked without an active responder");
130     return;
131   }
132 
133   std::array<std::byte, pwpb::ResourceStatus::kMaxEncodedSizeBytes> buffer = {};
134   pwpb::ResourceStatus::MemoryEncoder encoder(buffer);
135 
136   encoder.WriteResourceId(stats.resource_id).IgnoreError();
137   encoder.WriteStatus(status.code()).IgnoreError();
138 
139   if (!status.ok()) {
140     resource_responder_.Finish(ConstByteSpan(encoder), status).IgnoreError();
141     return;
142   }
143 
144   encoder.WriteReadableOffset(stats.readable_offset).IgnoreError();
145   encoder.WriteReadChecksum(stats.read_checksum).IgnoreError();
146   encoder.WriteWriteableOffset(stats.writeable_offset).IgnoreError();
147   encoder.WriteWriteChecksum(stats.write_checksum).IgnoreError();
148 
149   if (!encoder.status().ok()) {
150     resource_responder_.Finish(ConstByteSpan(encoder), encoder.status())
151         .IgnoreError();
152     return;
153   }
154 
155   resource_responder_.Finish(ConstByteSpan(encoder), status).IgnoreError();
156 }
157 
158 }  // namespace pw::transfer
159