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 #pragma once 15 16 #include <cstddef> 17 #include <cstdint> 18 #include <limits> 19 20 #include "pw_bytes/span.h" 21 #include "pw_sync/mutex.h" 22 #include "pw_transfer/handler.h" 23 #include "pw_transfer/internal/config.h" 24 #include "pw_transfer/internal/server_context.h" 25 #include "pw_transfer/transfer.raw_rpc.pb.h" 26 #include "pw_transfer/transfer_thread.h" 27 28 namespace pw::transfer { 29 namespace internal { 30 31 class Chunk; 32 33 } // namespace internal 34 35 class TransferService : public pw_rpc::raw::Transfer::Service<TransferService> { 36 public: 37 // Initializes a TransferService that can be registered with an RPC server. 38 // 39 // The transfer service runs all of its transfer tasks on the provided 40 // transfer thread. This thread may be shared between a transfer service and 41 // a transfer client. 42 // 43 // `max_window_size_bytes` is the maximum amount of data to ask for at a 44 // time during a write transfer, unless told a more restrictive amount by a 45 // transfer handler. This size should span multiple chunks, and can be set 46 // quite large. The transfer protocol automatically adjusts its window size 47 // as a transfer progresses to attempt to find an optimal configuration for 48 // the connection over which it is running. 49 TransferService( 50 TransferThread& transfer_thread, 51 uint32_t max_window_size_bytes, 52 chrono::SystemClock::duration chunk_timeout = cfg::kDefaultServerTimeout, 53 uint8_t max_retries = cfg::kDefaultMaxServerRetries, 54 uint32_t extend_window_divisor = cfg::kDefaultExtendWindowDivisor, 55 uint32_t max_lifetime_retries = cfg::kDefaultMaxLifetimeRetries) 56 : max_parameters_(max_window_size_bytes, 57 transfer_thread.max_chunk_size(), 58 extend_window_divisor), 59 thread_(transfer_thread), 60 chunk_timeout_(chunk_timeout), 61 max_retries_(max_retries), 62 max_lifetime_retries_(max_lifetime_retries) {} 63 64 TransferService(const TransferService&) = delete; 65 TransferService(TransferService&&) = delete; 66 67 TransferService& operator=(const TransferService&) = delete; 68 TransferService& operator=(TransferService&&) = delete; 69 Read(RawServerReaderWriter & reader_writer)70 void Read(RawServerReaderWriter& reader_writer) { 71 thread_.SetServerReadStream(reader_writer, [this](ConstByteSpan message) { 72 HandleChunk(message, internal::TransferType::kTransmit); 73 }); 74 } 75 Write(RawServerReaderWriter & reader_writer)76 void Write(RawServerReaderWriter& reader_writer) { 77 thread_.SetServerWriteStream(reader_writer, [this](ConstByteSpan message) { 78 HandleChunk(message, internal::TransferType::kReceive); 79 }); 80 } 81 82 void GetResourceStatus(ConstByteSpan request, 83 rpc::RawUnaryResponder& responder); 84 RegisterHandler(Handler & handler)85 bool RegisterHandler(Handler& handler) { 86 return thread_.AddTransferHandler(handler); 87 } 88 UnregisterHandler(Handler & handler)89 bool UnregisterHandler(Handler& handler) { 90 return thread_.RemoveTransferHandler(handler); 91 } 92 93 [[deprecated("Use set_max_window_size_bytes instead")]] set_max_pending_bytes(uint32_t pending_bytes)94 constexpr void set_max_pending_bytes(uint32_t pending_bytes) { 95 set_max_window_size_bytes(pending_bytes); 96 } 97 set_max_window_size_bytes(uint32_t max_window_size_bytes)98 constexpr void set_max_window_size_bytes(uint32_t max_window_size_bytes) { 99 max_parameters_.set_max_window_size_bytes(max_window_size_bytes); 100 } 101 102 // Sets the maximum size for the data in a pw_transfer chunk. Note that the 103 // max chunk size must always fit within the transfer thread's chunk buffer. set_max_chunk_size_bytes(uint32_t max_chunk_size_bytes)104 constexpr void set_max_chunk_size_bytes(uint32_t max_chunk_size_bytes) { 105 max_parameters_.set_max_chunk_size_bytes(max_chunk_size_bytes); 106 } 107 set_chunk_timeout(chrono::SystemClock::duration chunk_timeout)108 constexpr void set_chunk_timeout( 109 chrono::SystemClock::duration chunk_timeout) { 110 chunk_timeout_ = chunk_timeout; 111 } 112 set_max_retries(uint8_t max_retries)113 constexpr void set_max_retries(uint8_t max_retries) { 114 max_retries_ = max_retries; 115 } 116 set_extend_window_divisor(uint32_t extend_window_divisor)117 constexpr Status set_extend_window_divisor(uint32_t extend_window_divisor) { 118 if (extend_window_divisor <= 1) { 119 return Status::InvalidArgument(); 120 } 121 122 max_parameters_.set_extend_window_divisor(extend_window_divisor); 123 return OkStatus(); 124 } 125 126 private: 127 void HandleChunk(ConstByteSpan message, internal::TransferType type); 128 void ResourceStatusCallback(Status status, 129 const internal::ResourceStatus& stats); 130 131 internal::TransferParameters max_parameters_; 132 TransferThread& thread_; 133 134 chrono::SystemClock::duration chunk_timeout_; 135 uint8_t max_retries_; 136 uint32_t max_lifetime_retries_; 137 138 sync::Mutex resource_responder_mutex_; 139 rpc::RawUnaryResponder resource_responder_ 140 PW_GUARDED_BY(resource_responder_mutex_); 141 }; 142 143 } // namespace pw::transfer 144