• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 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_transfer/handler.h"
22 #include "pw_transfer/internal/config.h"
23 #include "pw_transfer/internal/server_context.h"
24 #include "pw_transfer/transfer.raw_rpc.pb.h"
25 #include "pw_transfer/transfer_thread.h"
26 
27 namespace pw::transfer {
28 namespace internal {
29 
30 class Chunk;
31 
32 }  // namespace internal
33 
34 class TransferService : public pw_rpc::raw::Transfer::Service<TransferService> {
35  public:
36   // Initializes a TransferService that can be registered with an RPC server.
37   //
38   // The transfer service requires a work queue to perform deferred tasks, such
39   // as handling transfer timeouts and retries. This work queue does not need to
40   // be unique to the transfer service; it may be shared with other parts of the
41   // system.
42   //
43   // The provided buffer is used to stage data from transfer chunks before it is
44   // written out to the writer. The size of this buffer is the largest amount of
45   // data that can be sent in a single transfer chunk, excluding any transport
46   // layer overhead.
47   //
48   // max_pending_bytes is the maximum amount of data to ask for at a
49   // time during a write transfer, unless told a more restrictive amount by a
50   // transfer handler. This size can span multiple chunks. A larger value
51   // generally increases the efficiency of write transfers when sent over a
52   // reliable transport. However, if the underlying transport is unreliable,
53   // larger values could slow down a transfer in the event of repeated packet
54   // loss.
55   TransferService(
56       TransferThread& transfer_thread,
57       uint32_t max_pending_bytes,
58       chrono::SystemClock::duration chunk_timeout = cfg::kDefaultChunkTimeout,
59       uint8_t max_retries = cfg::kDefaultMaxRetries,
60       uint32_t extend_window_divisor = cfg::kDefaultExtendWindowDivisor,
61       uint32_t max_lifetime_retries = cfg::kDefaultMaxLifetimeRetries)
62       : max_parameters_(max_pending_bytes,
63                         transfer_thread.max_chunk_size(),
64                         extend_window_divisor),
65         thread_(transfer_thread),
66         chunk_timeout_(chunk_timeout),
67         max_retries_(max_retries),
68         max_lifetime_retries_(max_lifetime_retries),
69         next_session_id_(1) {}
70 
71   TransferService(const TransferService&) = delete;
72   TransferService(TransferService&&) = delete;
73 
74   TransferService& operator=(const TransferService&) = delete;
75   TransferService& operator=(TransferService&&) = delete;
76 
Read(RawServerReaderWriter & reader_writer)77   void Read(RawServerReaderWriter& reader_writer) {
78     reader_writer.set_on_next([this](ConstByteSpan message) {
79       HandleChunk(message, internal::TransferType::kTransmit);
80     });
81     thread_.SetServerReadStream(reader_writer);
82   }
83 
Write(RawServerReaderWriter & reader_writer)84   void Write(RawServerReaderWriter& reader_writer) {
85     reader_writer.set_on_next([this](ConstByteSpan message) {
86       HandleChunk(message, internal::TransferType::kReceive);
87     });
88     thread_.SetServerWriteStream(reader_writer);
89   }
90 
RegisterHandler(Handler & handler)91   void RegisterHandler(Handler& handler) {
92     thread_.AddTransferHandler(handler);
93   }
94 
UnregisterHandler(Handler & handler)95   void UnregisterHandler(Handler& handler) {
96     thread_.RemoveTransferHandler(handler);
97   }
98 
set_max_pending_bytes(uint32_t max_pending_bytes)99   void set_max_pending_bytes(uint32_t max_pending_bytes) {
100     max_parameters_.set_pending_bytes(max_pending_bytes);
101   }
102 
103   // Sets the maximum size for the data in a pw_transfer chunk. Note that the
104   // max chunk size must always fit within the transfer thread's chunk buffer.
set_max_chunk_size_bytes(uint32_t max_chunk_size_bytes)105   void set_max_chunk_size_bytes(uint32_t max_chunk_size_bytes) {
106     max_parameters_.set_max_chunk_size_bytes(max_chunk_size_bytes);
107   }
108 
set_chunk_timeout(chrono::SystemClock::duration chunk_timeout)109   void set_chunk_timeout(chrono::SystemClock::duration chunk_timeout) {
110     chunk_timeout_ = chunk_timeout;
111   }
112 
set_max_retries(uint8_t max_retries)113   void set_max_retries(uint8_t max_retries) { max_retries_ = max_retries; }
114 
set_extend_window_divisor(uint32_t extend_window_divisor)115   Status set_extend_window_divisor(uint32_t extend_window_divisor) {
116     if (extend_window_divisor <= 1) {
117       return Status::InvalidArgument();
118     }
119 
120     max_parameters_.set_extend_window_divisor(extend_window_divisor);
121     return OkStatus();
122   }
123 
124  private:
125   void HandleChunk(ConstByteSpan message, internal::TransferType type);
126 
127   // TODO(frolv): This could be more sophisticated and less predictable.
GenerateNewSessionId()128   uint32_t GenerateNewSessionId() { return next_session_id_++; }
129 
130   internal::TransferParameters max_parameters_;
131   TransferThread& thread_;
132 
133   chrono::SystemClock::duration chunk_timeout_;
134   uint8_t max_retries_;
135   uint32_t max_lifetime_retries_;
136 
137   uint32_t next_session_id_;
138 };
139 
140 }  // namespace pw::transfer
141