blob: b64edac1369601c76dd77cb7d566a8b1e0ee89e2 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fuchsia/hardware/network/device/cpp/banjo.h>
#include <fuchsia/hardware/network/llcpp/fidl.h>
#include <fuchsia/hardware/serial/cpp/banjo.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fidl/llcpp/vector_view.h>
#include <lib/fit/function.h>
#include <lib/fit/result.h>
#include <lib/zx/channel.h>
#include <lib/zx/port.h>
#include <zircon/compiler.h>
#include <zircon/types.h>
#include <array>
#include <cstddef>
#include <mutex>
#include <queue>
#include <thread>
#include <utility>
#include <ddktl/device.h>
#include <ddktl/protocol/empty-protocol.h>
#include <fbl/auto_lock.h>
#include <fbl/span.h>
#include "lib/common/ppp.h"
#include "lib/hdlc/frame.h"
#include "src/lib/vmo_store/vmo_store.h"
namespace ppp {
class SerialPpp;
using DeviceType = ddk::Device<SerialPpp>;
class SerialPpp final : public DeviceType,
public ddk::NetworkDeviceImplProtocol<SerialPpp>,
public ddk::EmptyProtocol<ZX_PROTOCOL_NETWORK_DEVICE_IMPL> {
static constexpr zx::duration kSerialTimeout = zx::msec(10);
// FIFO depth is abitrarily low since PPP is quite slow. There's no point in having many buffers.
static constexpr uint16_t kFifoDepth = 64;
explicit SerialPpp(zx_device_t* parent);
SerialPpp(zx_device_t* parent, ddk::SerialProtocolClient serial);
static zx_status_t Create(void* ctx, zx_device_t* parent);
// ddk::Releasable
void DdkRelease();
// ddk::Unbindable
void DdkUnbind(ddk::UnbindTxn txn);
// ddk::NetworkDeviceImplProtocol
zx_status_t NetworkDeviceImplInit(const network_device_ifc_protocol_t* iface);
void NetworkDeviceImplStart(network_device_impl_start_callback callback, void* cookie);
void NetworkDeviceImplStop(network_device_impl_stop_callback callback, void* cookie);
void NetworkDeviceImplGetInfo(device_info_t* out_info);
void NetworkDeviceImplGetStatus(status_t* out_status);
void NetworkDeviceImplQueueTx(const tx_buffer_t* buf_list, size_t buf_count);
void NetworkDeviceImplQueueRxSpace(const rx_space_buffer_t* buf_list, size_t buf_count);
void NetworkDeviceImplPrepareVmo(uint8_t vmo_id, zx::vmo vmo);
void NetworkDeviceImplReleaseVmo(uint8_t vmo_id);
void NetworkDeviceImplSetSnoop(bool snoop);
// Used in tests:
void set_enable_rx_timeout(bool enable) { enable_rx_timeout_ = enable; }
// Synchronously shuts down |WorkerLoop| thread if one is running and disposes of serial
// connection. Does nothing otherwise.
void Shutdown();
static constexpr uint16_t kDefaultMtu = 1500;
// Max buffer size selected to fit body of two messages plus single header/footer padding.
static constexpr size_t kMaxBufferSize = 2 * kDefaultMtu + 8;
void WorkerLoop();
// Reads data from the |serial_| socket, finds frame boundaries and publishes parsed frames to the
// network device interface.
// If |fetch_from_socket| is trued, more data will be read from the |serial_| socket. Only local
// |rx_frame_buffer_| is considered otherwise.
// Returns |true| if we should wait for more |serial_| to become readable.
bool ConsumeSerial(bool fetch_from_socket);
zx_status_t WriteFramed(FrameView frame);
ddk::SerialProtocolClient serial_protocol_;
ddk::NetworkDeviceIfcProtocolClient netdevice_protocol_;
struct PendingBuffer {
uint32_t id;
fbl::Span<uint8_t> data;
uint8_t type;
bool enable_rx_timeout_ = true;
// State lock must be acquired for lifecycle events.
fbl::Mutex state_lock_;
std::thread thread_ __TA_GUARDED(state_lock_);
// NB: |serial_| and |port_| are not marked as guarded so they can be accessed without locking
// |state_lock_| in |WorkerLoop|. Safety comes from |serial_| and |port_| only being read from the
// |WorkerLoop| thread and only being modified after the thread is joined in lifecycle events.
zx::socket serial_;
zx::port port_;
fbl::Mutex rx_lock_;
std::queue<PendingBuffer> rx_space_ __TA_GUARDED(rx_lock_);
fbl::Mutex tx_lock_;
std::queue<PendingBuffer> pending_tx_ __TA_GUARDED(tx_lock_);
vmo_store::VmoStore<vmo_store::SlabStorage<uint8_t>> vmos_;
// Buffer to accumulate rx frames, to be used only in |WorkerLoop| thread.
std::array<uint8_t, kMaxBufferSize> rx_frame_buffer_;
decltype(rx_frame_buffer_)::iterator rx_frame_buffer_offset_;
} // namespace ppp