| // Copyright 2021 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| |
| #define PW_LOG_MODULE_NAME "TRN" |
| |
| #include "pw_transfer/transfer.h" |
| |
| #include "pw_assert/check.h" |
| #include "pw_log/log.h" |
| #include "pw_status/try.h" |
| #include "pw_transfer_private/chunk.h" |
| |
| namespace pw::transfer { |
| |
| void TransferService::HandleChunk(ConstByteSpan message, |
| internal::TransferType type) { |
| // All incoming chunks in a client read transfer are transfer parameter |
| // updates, except for the final chunk, which is an acknowledgement of |
| // completion. |
| // |
| // Transfer parameters may contain the following fields: |
| // |
| // - transfer_id (required) |
| // - pending_bytes (required) |
| // - offset (required) |
| // - max_chunk_size_bytes |
| // - min_delay_microseconds (not yet supported) |
| // |
| internal::Chunk chunk; |
| |
| if (Status status = internal::DecodeChunk(message, chunk); !status.ok()) { |
| // No special handling required here. The client will retransmit the chunk |
| // when no response is received. |
| PW_LOG_ERROR("Failed to decode incoming transfer chunk"); |
| return; |
| } |
| |
| internal::ServerContextPool& pool = |
| type == internal::kRead ? read_transfers_ : write_transfers_; |
| |
| Result<internal::ServerContext*> result = |
| chunk.IsInitialChunk() ? pool.StartTransfer(chunk.transfer_id) |
| : pool.GetPendingTransfer(chunk.transfer_id); |
| if (!result.ok()) { |
| client_.SendStatusChunk(type, chunk.transfer_id, result.status()); |
| PW_LOG_ERROR("Error handling chunk for transfer %u: %d", |
| static_cast<unsigned>(chunk.transfer_id), |
| result.status().code()); |
| return; |
| } |
| |
| internal::ServerContext& transfer = *result.value(); |
| |
| if (chunk.status.has_value()) { |
| // Transfer has been terminated (successfully or not). |
| const Status status = chunk.status.value(); |
| if (!status.ok()) { |
| PW_LOG_ERROR("Receiver terminated transfer %u with status %d", |
| static_cast<unsigned>(chunk.transfer_id), |
| static_cast<int>(status.code())); |
| } |
| transfer.Finish(status).IgnoreError(); |
| return; |
| } |
| |
| if (type == internal::kRead) { |
| transfer.HandleReadChunk(client_, chunk); |
| } else { |
| transfer.HandleWriteChunk(client_, chunk); |
| } |
| } |
| |
| } // namespace pw::transfer |