blob: 2c548e38fb1b33bb19379ffcb369eb8260f4f968 [file] [log] [blame]
// Copyright 2022 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.
// Simple RPC server with the transfer service registered. Reads HDLC frames
// with RPC packets through a socket. This server has a single transfer ID that
// is available, and data must be written to the server before data can be read
// from the transfer ID.
#include <cstddef>
#include <string>
#include <thread>
#include "pw_assert/check.h"
#include "pw_log/log.h"
#include "pw_rpc_system_server/rpc_server.h"
#include "pw_rpc_system_server/socket.h"
#include "pw_stream/memory_stream.h"
#include "pw_thread/detached_thread.h"
#include "pw_thread_stl/options.h"
#include "pw_transfer/transfer.h"
namespace pw::transfer {
namespace {
using stream::MemoryReader;
using stream::MemoryWriter;
// TODO(amontanez): These should be configurable.
constexpr size_t kChunkSizeBytes = 256;
constexpr size_t kMaxReceiveSizeBytes = 1024;
std::array<std::byte, kChunkSizeBytes> chunk_buffer;
std::array<std::byte, kChunkSizeBytes> encode_buffer;
transfer::Thread<4, 4> transfer_thread(chunk_buffer, encode_buffer);
TransferService transfer_service(transfer_thread, kMaxReceiveSizeBytes);
class DynamicallyAllocatedRamHandler final : public ReadWriteHandler {
public:
DynamicallyAllocatedRamHandler(TransferService& service,
uint32_t transfer_id,
size_t max_size)
: ReadWriteHandler(transfer_id),
max_size_(max_size),
size_(0),
writer_open_(false),
readers_open_(0),
service_(service),
buffer_(nullptr),
memory_reader_(ConstByteSpan()),
memory_writer_(ByteSpan()) {
buffer_ = new std::byte[max_size];
service_.RegisterHandler(*this);
}
~DynamicallyAllocatedRamHandler() {
service_.UnregisterHandler(*this);
delete[] buffer_;
}
Status PrepareRead() final {
if (writer_open_) {
PW_LOG_ERROR("Failed to open for reading; writer still open");
return Status::Unavailable();
}
if (readers_open_ == 0) {
PW_LOG_DEBUG("Creating new MemoryReader");
memory_reader_ = MemoryReader(ByteSpan(buffer_, size_));
set_reader(memory_reader_);
}
readers_open_++;
PW_LOG_DEBUG("%d readers now open", static_cast<int>(readers_open_));
return OkStatus();
}
void FinalizeRead(Status) final {
PW_CHECK_UINT_GT(readers_open_, 0);
readers_open_--;
PW_LOG_DEBUG("%d readers now open", static_cast<int>(readers_open_));
}
Status PrepareWrite() final {
if (writer_open_) {
PW_LOG_ERROR("Failed to open for writing; writer still open");
return Status::Unavailable();
}
if (readers_open_ > 0) {
PW_LOG_ERROR("Failed to open for writing; %d readers still open",
static_cast<int>(readers_open_));
return Status::Unavailable();
}
memory_writer_ = MemoryWriter(ByteSpan(buffer_, max_size_));
set_writer(memory_writer_);
writer_open_ = true;
return OkStatus();
}
Status FinalizeWrite(Status) final {
PW_CHECK(writer_open_);
size_ = memory_writer_.size();
writer_open_ = false;
return OkStatus();
}
private:
const size_t max_size_;
size_t size_;
bool writer_open_;
size_t readers_open_;
TransferService& service_;
std::byte* buffer_;
MemoryReader memory_reader_;
MemoryWriter memory_writer_;
};
void RunServer(int socket_port, uint32_t transfer_id, size_t max_file_size) {
rpc::system_server::set_socket_port(socket_port);
rpc::system_server::Init();
rpc::system_server::Server().RegisterService(transfer_service);
thread::DetachedThread(thread::stl::Options(), transfer_thread);
// It's fine to allocate this on the stack since this thread doesn't return
// until this process is killed.
DynamicallyAllocatedRamHandler transfer_handler(
transfer_service, transfer_id, max_file_size);
PW_LOG_INFO("Starting pw_rpc server");
PW_CHECK_OK(rpc::system_server::Start());
}
} // namespace
} // namespace pw::transfer
int main(int argc, char* argv[]) {
if (argc != 4) {
PW_LOG_ERROR("Usage: %s PORT TRANSFER_ID MAX_FILE_SIZE", argv[0]);
return 1;
}
int port = std::atoi(argv[1]);
PW_CHECK_UINT_GT(port, 0, "Invalid port!");
int transfer_id = std::atoi(argv[2]);
PW_CHECK_UINT_GT(transfer_id, 0, "Invalid transfer ID!");
long long max_file_size = std::atoll(argv[3]);
PW_CHECK_UINT_GT(max_file_size, 0, "Invalid maximum file size!");
pw::transfer::RunServer(port, transfer_id, max_file_size);
return 0;
}