pw_transfer: Start docs and proto

- Create the pw_transfer module.
- Start the pw_transfer docs.

Change-Id: I2d3b4f3e719960167ee28ba4ca84eb809bf94bc1
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/49022
Pigweed-Auto-Submit: Wyatt Hepler <hepler@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
Reviewed-by: Alexei Frolov <frolv@google.com>
Commit-Queue: Wyatt Hepler <hepler@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 7b482ff..7631696 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -293,6 +293,7 @@
       "$dir_pw_tokenizer:tests",
       "$dir_pw_trace:tests",
       "$dir_pw_trace_tokenized:tests",
+      "$dir_pw_transfer:tests",
       "$dir_pw_unit_test:tests",
       "$dir_pw_varint:tests",
     ]
diff --git a/docs/BUILD.gn b/docs/BUILD.gn
index 7b0b536..7f5d778 100644
--- a/docs/BUILD.gn
+++ b/docs/BUILD.gn
@@ -141,6 +141,7 @@
     "$dir_pw_toolchain:docs",
     "$dir_pw_trace:docs",
     "$dir_pw_trace_tokenized:docs",
+    "$dir_pw_transfer:docs",
     "$dir_pw_unit_test:docs",
     "$dir_pw_varint:docs",
     "$dir_pw_watch:docs",
diff --git a/modules.gni b/modules.gni
index cc01d95..9b2625e 100644
--- a/modules.gni
+++ b/modules.gni
@@ -40,6 +40,7 @@
   dir_pw_cpu_exception = get_path_info("pw_cpu_exception", "abspath")
   dir_pw_cpu_exception_cortex_m =
       get_path_info("pw_cpu_exception_cortex_m", "abspath")
+  dir_pw_transfer = get_path_info("pw_transfer", "abspath")
   dir_pw_docgen = get_path_info("pw_docgen", "abspath")
   dir_pw_doctor = get_path_info("pw_doctor", "abspath")
   dir_pw_env_setup = get_path_info("pw_env_setup", "abspath")
diff --git a/pw_transfer/BUILD.gn b/pw_transfer/BUILD.gn
new file mode 100644
index 0000000..5fede6f
--- /dev/null
+++ b/pw_transfer/BUILD.gn
@@ -0,0 +1,32 @@
+# 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.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_docgen/docs.gni")
+import("$dir_pw_protobuf_compiler/proto.gni")
+import("$dir_pw_unit_test/test.gni")
+
+pw_proto_library("proto") {
+  sources = [ "transfer.proto" ]
+}
+
+pw_test_group("tests") {
+  tests = []
+}
+
+pw_doc_group("docs") {
+  sources = [ "docs.rst" ]
+  inputs = [ "transfer.proto" ]
+}
diff --git a/pw_transfer/CMakeLists.txt b/pw_transfer/CMakeLists.txt
new file mode 100644
index 0000000..ac100e7
--- /dev/null
+++ b/pw_transfer/CMakeLists.txt
@@ -0,0 +1,23 @@
+# Copyright 2020 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.
+
+include($ENV{PW_ROOT}/pw_build/pigweed.cmake)
+
+pw_auto_add_simple_module(pw_transfer
+  PUBLIC_DEPS
+    pw_assert
+    pw_containers
+    pw_status
+    pw_stream
+)
diff --git a/pw_transfer/docs.rst b/pw_transfer/docs.rst
new file mode 100644
index 0000000..2f31ed6
--- /dev/null
+++ b/pw_transfer/docs.rst
@@ -0,0 +1,154 @@
+.. _module-pw_transfer:
+
+===========
+pw_transfer
+===========
+
+.. attention::
+
+  ``pw_transfer`` is under construction and so is its documentation.
+
+--------
+Protocol
+--------
+
+Protocol buffer definition
+==========================
+.. literalinclude:: transfer.proto
+  :language: protobuf
+  :lines: 14-
+
+Server to client transfer (read)
+================================
+.. seqdiag::
+  :scale: 110
+
+  seqdiag {
+    default_note_color = aliceblue;
+
+    client -> server [
+        label = "set transfer parameters",
+        leftnote = "transfer_id\noffset\npending_bytes\nmax_chunk_size\nchunk_delay"
+    ];
+
+    client <-- server [
+        noactivate,
+        label = "requested bytes\n(zero or more chunks)",
+        rightnote = "offset\ndata\n(remaining_bytes)"
+    ];
+
+    client --> server [
+        noactivate,
+        label = "update transfer parameters\n(as needed)",
+        leftnote = "offset\npending_bytes\n(max_chunk_size)\n(chunk_delay)"
+    ];
+
+    client <- server [
+        noactivate,
+        label = "final chunk",
+        rightnote = "offset\ndata\nremaining_bytes=0"
+    ];
+
+    client -> server [
+        noactivate,
+        label = "received final chunk",
+        leftnote = "pending_bytes=0"
+    ];
+
+    client <- server [
+        label = "done"
+    ];
+  }
+
+Client to server transfer (write)
+=================================
+.. seqdiag::
+  :scale: 110
+
+  seqdiag {
+    default_note_color = aliceblue;
+
+    client -> server [
+        label = "start",
+        leftnote = "transfer_id"
+    ];
+
+    client <- server [
+        noactivate,
+        label = "set transfer parameters",
+        rightnote = "offset\npending_bytes\nmax_chunk_size\nchunk_delay"
+    ];
+
+    client --> server [
+        noactivate,
+        label = "requested bytes\n(zero or more chunks)",
+        leftnote = "offset\ndata\n(remaining_bytes)"
+    ];
+
+    client <-- server [
+        noactivate,
+        label = "update transfer parameters\n(as needed)",
+        rightnote = "offset\npending_bytes\n(max_chunk_size)\n(chunk_delay)"
+    ];
+
+    client -> server [
+        noactivate,
+        label = "final chunk",
+        leftnote = "offset\ndata\nremaining_bytes=0"
+    ];
+
+    client <- server [
+        label = "done"
+    ];
+  }
+
+Transmitter 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])
diff --git a/pw_transfer/transfer.proto b/pw_transfer/transfer.proto
new file mode 100644
index 0000000..427bd17
--- /dev/null
+++ b/pw_transfer/transfer.proto
@@ -0,0 +1,106 @@
+// 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.
+
+syntax = "proto3";
+
+package pw.transfer;
+
+// The transfer RPC service is used to send data between the client and server.
+service Transfer {
+  // Transfer data from the server to the client; a "download" from the client's
+  // perspective.
+  rpc Read(stream Chunk) returns (stream Chunk);
+
+  // Transfer data from the client to the server; an "upload" from the client's
+  // perspective.
+  rpc Write(stream Chunk) returns (stream Chunk);
+}
+
+// Represents a chunk of data sent by the transfer service. Includes fields for
+// configuring the transfer parameters.
+//
+// Notation: (Read|Write) (→|←)
+//   X → Means client sending data to the server.
+//   X ← Means server sending data to the client.
+message Chunk {
+  // Represents the source or destination of the data. May be ephemeral or
+  // stable depending on the implementation. Only ever sent by the client in the
+  // first request.
+  //
+  //  Read → ID of transfer
+  //  Read ← Omitted
+  // Write → ID of transfer
+  // Write ← Omitted
+  uint32 transfer_id = 1;
+
+  // Used by the receiver to indicate how many bytes it can accept. The
+  // transmitter sends this much data, divided into chunks no larger than
+  // max_chunk_size_bytes. The receiver then starts another window by sending
+  // request_bytes again with a new offset.
+  //
+  //  Read → The client requests this many bytes to be sent.
+  //  Read ← N/A
+  // Write → N/A
+  // Write ← The server requests this many bytes to be sent.
+  optional uint32 pending_bytes = 2;
+
+  // Maximum size of an individual chunk. The transmitter may send smaller
+  // chunks if required.
+  //
+  //  Read → Set maximum size for subsequent chunks.
+  //  Read ← N/A
+  // Write → N/A
+  // Write ← Set maximum size for subsequent chunks.
+  optional uint32 max_chunk_size_bytes = 3;
+
+  // Minimum required delay between chunks. The transmitter may delay longer if
+  // desired.
+  //
+  //  Read → Set minimum delay for subsequent chunks.
+  //  Read ← N/A
+  // Write → N/A
+  // Write ← Set minimum delay for subsequent chunks.
+  optional uint32 min_delay_microseconds = 4;
+
+  // On writes, the offset of the data. On reads, the offset at which to read.
+  //
+  //  Read → Read data starting at this offset.
+  //  Read ← Offset of the data.
+  // Write → Offset of the data.
+  // Write ← Write data starting at this offset.
+  uint64 offset = 5;
+
+  // The data that was read or the data to write.
+  //
+  //  Read → N/A
+  //  Read ← Data read
+  // Write → Data to write
+  // Write ← N/A
+  bytes data = 6;
+
+  // Estimated bytes remaining to read/write. Optional except for the last
+  // chunk, for which remaining_bytes must be set to 0.
+  //
+  // The sender can set remaining_bytes at the beginning of a read/write so that
+  // the receiver can track progress or cancel the transaction if the value is
+  // too large.
+  //
+  //  Read → N/A
+  //  Read ← Remaining bytes to read, excluding any data in this chunk. Set to
+  //         0 for the last chunk.
+  // Write → Reamining bytes to write, excluding any data in is chunk. Set to
+  //         0 for the last chunk.
+  // Write ← N/A
+  optional uint64 remaining_bytes = 7;
+}