mirror of
https://fuchsia.googlesource.com/third_party/pigweed.googlesource.com/pigweed/pigweed
synced 2024-09-20 05:41:06 +00:00
08956abf0d
- Attempt to seek on read transfers if an out-of-order offset is requested. - Remap prepare status codes to standard codes for pw_transfer. - Update status documentation. Change-Id: I708c8598fee7d87d5badb1ffa0aff8f46596107b Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/65080 Pigweed-Auto-Submit: Wyatt Hepler <hepler@google.com> Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com> Reviewed-by: Ewout van Bekkum <ewout@google.com>
239 lines
8.5 KiB
ReStructuredText
239 lines
8.5 KiB
ReStructuredText
.. _module-pw_transfer:
|
|
|
|
===========
|
|
pw_transfer
|
|
===========
|
|
|
|
.. attention::
|
|
|
|
``pw_transfer`` is under construction and so is its documentation.
|
|
|
|
-----
|
|
Usage
|
|
-----
|
|
|
|
C++
|
|
===
|
|
The transfer service is defined and registered with an RPC server like any other
|
|
RPC service.
|
|
|
|
To know how to read data from or write data to device, a ``TransferHandler``
|
|
interface is defined (``pw_transfer/public/pw_transfer/handler.h``). Transfer
|
|
handlers wrap a stream reader and/or writer with initialization and completion
|
|
code. Custom transfer handler implementations should derive from
|
|
``ReadOnlyHandler``, ``WriteOnlyHandler``, or ``ReadWriteHandler`` as
|
|
appropriate and override Prepare and Finalize methods if necessary.
|
|
|
|
A transfer handler should be implemented and instantiated for each unique data
|
|
transfer to or from a device. These handlers are then registered with the
|
|
transfer service using their transfer IDs.
|
|
|
|
**Example**
|
|
|
|
.. code-block:: cpp
|
|
|
|
#include "pw_transfer/transfer.h"
|
|
|
|
namespace {
|
|
|
|
// Simple transfer handler which reads data from an in-memory buffer.
|
|
class SimpleBufferReadHandler : public pw::transfer::ReadOnlyHandler {
|
|
public:
|
|
SimpleReadTransfer(uint32_t transfer_id, pw::ConstByteSpan data)
|
|
: ReadOnlyHandler(transfer_id), reader_(data) {
|
|
set_reader(reader_);
|
|
}
|
|
|
|
private:
|
|
pw::stream::MemoryReader reader_;
|
|
};
|
|
|
|
// The maximum amount of data that can be sent in a single chunk, excluding
|
|
// transport layer overhead.
|
|
constexpr size_t kMaxChunkSizeBytes = 256;
|
|
|
|
// In a write transfer, the maximum number of bytes to receive at one time,
|
|
// (potentially across multiple chunks), unless specified otherwise by the
|
|
// transfer handler's stream::Writer.
|
|
constexpr size_t kDefaultMaxBytesToReceive = 1024;
|
|
|
|
// Instantiate a static transfer service.
|
|
pw::transfer::TransferService transfer_service(
|
|
kMaxChunkSizeBytes, kDefaultMaxBytesToReceive);
|
|
|
|
// Instantiate a handler for the the data to be transferred.
|
|
constexpr uint32_t kBufferTransferId = 1;
|
|
char buffer_to_transfer[256] = { /* ... */ };
|
|
SimpleBufferReadHandler buffer_handler(kBufferTransferId, buffer_to_transfer);
|
|
|
|
} // namespace
|
|
|
|
void InitTransfer() {
|
|
// Register the handler with the transfer service, then the transfer service
|
|
// with an RPC server.
|
|
transfer_service.RegisterHandler(buffer_handler);
|
|
GetSystemRpcServer().RegisterService(transfer_service);
|
|
}
|
|
|
|
Python
|
|
======
|
|
.. automodule:: pw_transfer
|
|
:members: ProgressStats, Manager, Error
|
|
|
|
**Example**
|
|
|
|
.. code-block:: python
|
|
|
|
import pw_transfer
|
|
|
|
# Initialize a Pigweed RPC client; see pw_rpc docs for more info.
|
|
rpc_client = CustomRpcClient()
|
|
rpcs = rpc_client.channel(1).rpcs
|
|
|
|
transfer_service = rpcs.pw.transfer.Transfer
|
|
transfer_manager = pw_transfer.Manager(transfer_service)
|
|
|
|
try:
|
|
# Read transfer_id 3 from the server.
|
|
data = transfer_manager.read(3)
|
|
except pw_transfer.Error as err:
|
|
print('Failed to read:', err.status)
|
|
|
|
try:
|
|
# Send some data to the server. The transfer manager does not have to be
|
|
# reinitialized.
|
|
transfer_manager.write(2, b'hello, world')
|
|
except pw_transfer.Error as err:
|
|
print('Failed to write:', err.status)
|
|
|
|
--------
|
|
Protocol
|
|
--------
|
|
|
|
Protocol buffer definition
|
|
==========================
|
|
.. literalinclude:: transfer.proto
|
|
:language: protobuf
|
|
:lines: 14-
|
|
|
|
Server to client transfer (read)
|
|
================================
|
|
.. image:: read.svg
|
|
|
|
Client to server transfer (write)
|
|
=================================
|
|
.. image:: write.svg
|
|
|
|
Errors
|
|
======
|
|
At any point, either the client or server may terminate the transfer with a
|
|
status code. The transfer chunk with the status is the final chunk of the
|
|
transfer.
|
|
|
|
The following table describes the meaning of each status code when sent by the
|
|
sender or the receiver (see `Transfer roles`_).
|
|
|
|
.. cpp:namespace-push:: pw::stream
|
|
|
|
+-------------------------+-------------------------+-------------------------+
|
|
| Status | Sent by sender | Sent by receiver |
|
|
+=========================+=========================+=========================+
|
|
| ``OK`` | (not sent) | All data was received |
|
|
| | | and handled |
|
|
| | | successfully. |
|
|
+-------------------------+-------------------------+-------------------------+
|
|
| ``ABORTED`` | The service aborted the transfer because the |
|
|
| | client restarted it. This status is passed to the |
|
|
| | transfer handler, but not sent to the client |
|
|
| | because it restarted the transfer. |
|
|
+-------------------------+---------------------------------------------------+
|
|
| ``CANCELLED`` | The client cancelled the transfer. |
|
|
+-------------------------+-------------------------+-------------------------+
|
|
| ``DATA_LOSS`` | Failed to read the data | Failed to write the |
|
|
| | to send. The | received data. The |
|
|
| | :cpp:class:`Reader` | :cpp:class:`Writer` |
|
|
| | returned an error. | returned an error. |
|
|
+-------------------------+-------------------------+-------------------------+
|
|
| ``FAILED_PRECONDITION`` | Received chunk for transfer that is not active. |
|
|
+-------------------------+-------------------------+-------------------------+
|
|
| ``INVALID_ARGUMENT`` | Received a malformed packet. |
|
|
+-------------------------+-------------------------+-------------------------+
|
|
| ``INTERNAL`` | An assumption of the protocol was violated. |
|
|
| | Encountering ``INTERNAL`` indicates that there is |
|
|
| | a bug in the service or client implementation. |
|
|
+-------------------------+-------------------------+-------------------------+
|
|
| ``PERMISSION_DENIED`` | The transfer does not support the requested |
|
|
| | operation (either reading or writing). |
|
|
+-------------------------+-------------------------+-------------------------+
|
|
| ``RESOURCE_EXHAUSTED`` | (not sent) | Storage is full. |
|
|
+-------------------------+-------------------------+-------------------------+
|
|
| ``UNAVAILABLE`` | The service is busy with other transfers and |
|
|
| | cannot begin a new transfer at this time. |
|
|
+-------------------------+-------------------------+-------------------------+
|
|
| ``UNIMPLEMENTED`` | Out-of-order chunk was | (not sent) |
|
|
| | requested, but seeking | |
|
|
| | is not supported. | |
|
|
+-------------------------+-------------------------+-------------------------+
|
|
|
|
.. cpp:namespace-pop::
|
|
|
|
Transfer roles
|
|
==============
|
|
Every transfer has two participants: the sender and the receiver. The sender
|
|
transmits data to the receiver. The receiver controls how the data is
|
|
transferred and sends the final status when the transfer is complete.
|
|
|
|
In read transfers, the client is the receiver and the service is the sender. In
|
|
write transfers, the client is the sender and the service is the receiver.
|
|
|
|
Sender flow
|
|
-----------
|
|
.. mermaid::
|
|
|
|
graph TD
|
|
start([Client initiates<br>transfer]) -->data_request
|
|
data_request[Receive transfer<br>parameters]-->send_chunk
|
|
|
|
send_chunk[Send chunk]-->sent_all
|
|
|
|
sent_all{Sent final<br>chunk?} -->|yes|wait
|
|
sent_all-->|no|sent_requested
|
|
|
|
sent_requested{Sent all<br>pending?}-->|yes|data_request
|
|
sent_requested-->|no|send_chunk
|
|
|
|
wait[Wait for receiver]-->is_done
|
|
|
|
is_done{Received<br>final chunk?}-->|yes|done
|
|
is_done-->|no|data_request
|
|
|
|
done([Transfer complete])
|
|
|
|
Receiver flow
|
|
-------------
|
|
.. mermaid::
|
|
|
|
graph TD
|
|
start([Client initiates<br>transfer]) -->request_bytes
|
|
request_bytes[Set transfer<br>parameters]-->wait
|
|
|
|
wait[Wait for chunk]-->received_chunk
|
|
|
|
received_chunk{Received<br>chunk by<br>deadline?}-->|no|request_bytes
|
|
received_chunk-->|yes|check_chunk
|
|
|
|
check_chunk{Correct<br>offset?} -->|yes|process_chunk
|
|
check_chunk --> |no|request_bytes
|
|
|
|
process_chunk[Process chunk]-->final_chunk
|
|
|
|
final_chunk{Final<br>chunk?}-->|yes|signal_completion
|
|
final_chunk{Final<br>chunk?}-->|no|received_requested
|
|
|
|
received_requested{Received all<br>pending?}-->|yes|request_bytes
|
|
received_requested-->|no|wait
|
|
|
|
signal_completion[Signal completion]-->done
|
|
|
|
done([Transfer complete])
|