blob: 7ef84f93cbd62cc6ba15cb5b26f00a93b263674e [file] [log] [blame]
// Copyright 2021 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.
#ifndef SRC_CONNECTIVITY_NETWORK_DRIVERS_NETWORK_DEVICE_DEVICE_PORT_WATCHER_H_
#define SRC_CONNECTIVITY_NETWORK_DRIVERS_NETWORK_DEVICE_DEVICE_PORT_WATCHER_H_
#include <fidl/fuchsia.hardware.network/cpp/wire.h>
#include <lib/async/dispatcher.h>
#include <lib/fidl/cpp/wire/server.h>
#include <fbl/intrusive_double_list.h>
#include <fbl/mutex.h>
#include "definitions.h"
namespace network::internal {
class PortWatcher : public fbl::DoublyLinkedListable<std::unique_ptr<PortWatcher>>,
public fidl::WireServer<netdev::PortWatcher> {
public:
// Maximum number of port events that can be queued before the channel is closed.
static constexpr size_t kMaximumQueuedEvents = netdev::kMaxPorts * 2ul;
using List = fbl::SizedDoublyLinkedList<std::unique_ptr<PortWatcher>>;
using ClosedCallback = fit::callback<void(PortWatcher&)>;
// Binds the watcher to |dispatcher| serving on |channel|.
// |existing_ports| contains the port identifiers to be included in the watcher's existing ports
// list.
// |closed_callback| is called when the watcher is closed by the peer or by a call to |Unbind|.
zx_status_t Bind(async_dispatcher_t* dispatcher,
cpp20::span<const netdev::wire::PortId> existing_ports,
fidl::ServerEnd<netdev::PortWatcher> channel, ClosedCallback closed_callback);
// Unbinds the port watcher if currently bound.
void Unbind();
// Notifies peer of port addition.
void PortAdded(netdev::wire::PortId port_id);
// Notifies peer of port removal.
void PortRemoved(netdev::wire::PortId port_id);
// FIDL protocol implementation.
void Watch(WatchCompleter::Sync& completer) override;
private:
// Helper class to provide owned FIDL port event union and intrusive linked list.
class Event : public fbl::DoublyLinkedListable<std::unique_ptr<Event>> {
public:
Event() = default;
Event(Event&& other) = delete;
Event(const Event& other);
void SetExisting(netdev::wire::PortId port_id);
void SetAdded(netdev::wire::PortId port_id);
void SetRemoved(netdev::wire::PortId port_id);
void SetIdle();
netdev::wire::DevicePortEvent event() const { return event_; }
private:
netdev::wire::DevicePortEvent event_;
netdev::wire::Empty empty_;
netdev::wire::PortId port_id_;
};
// Queues an event in the internal queue.
// Returns |ZX_ERR_NO_MEMORY| if it can't allocate queue space.
// Returns |ZX_ERR_CANCELED| if too many events are already enqueued.
[[nodiscard]] zx_status_t QueueEvent(const Event& event) __TA_REQUIRES(lock_);
// Processes a single event, firing a pending FIDL response if one exists or enqueuing it for
// later consumption.
//
// Closes the channel with an epitaph and unbinds on queueing errors.
void ProcessEvent(const Event& event) __TA_REQUIRES(lock_);
fbl::Mutex lock_;
ClosedCallback closed_cb_;
std::optional<WatchCompleter ::Async> pending_txn_ __TA_GUARDED(lock_);
fbl::DoublyLinkedList<std::unique_ptr<Event>, fbl::DefaultObjectTag, fbl::SizeOrder::Constant>
event_queue_ __TA_GUARDED(lock_);
std::optional<fidl::ServerBindingRef<netdev::PortWatcher>> binding_ __TA_GUARDED(lock_);
};
} // namespace network::internal
#endif // SRC_CONNECTIVITY_NETWORK_DRIVERS_NETWORK_DEVICE_DEVICE_PORT_WATCHER_H_