| // 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. |
| |
| #pragma once |
| |
| #include <ddk/binding.h> |
| #include <ddk/debug.h> |
| #include <ddk/device.h> |
| #include <ddk/driver.h> |
| #include <ddk/protocol/ethernet.h> |
| |
| #include <ddktl/device.h> |
| #include <ddktl/protocol/empty-protocol.h> |
| #include <ddktl/protocol/ethernet.h> |
| #include <fbl/auto_lock.h> |
| #include <fbl/intrusive_double_list.h> |
| #include <lib/fidl-utils/bind.h> |
| #include <lib/fzl/vmo-mapper.h> |
| #include <lib/zx/fifo.h> |
| |
| #include <fuchsia/hardware/ethernet/c/fidl.h> |
| |
| #include <zircon/assert.h> |
| #include <zircon/device/ethernet.h> |
| #include <zircon/listnode.h> |
| #include <zircon/process.h> |
| #include <zircon/status.h> |
| #include <zircon/syscalls.h> |
| #include <zircon/thread_annotations.h> |
| #include <zircon/types.h> |
| |
| #include <limits.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <threads.h> |
| |
| namespace eth { |
| |
| class EthDev0; |
| class EthDev; |
| |
| struct TransmitInfo { |
| EthDev* edev; |
| uint64_t fifo_cookie; |
| list_node_t node; |
| }; |
| |
| using EthDev0Type = ddk::Device<EthDev0, ddk::Openable, ddk::Unbindable>; |
| |
| class EthDev0 : public EthDev0Type, |
| public ddk::EmptyProtocol<ZX_PROTOCOL_ETHERNET> { |
| |
| public: |
| EthDev0(zx_device_t* parent) |
| : EthDev0Type(parent), mac_(parent) {} |
| |
| EthDev0(const EthDev0&) = delete; |
| EthDev0(EthDev0&&) = delete; |
| EthDev0& operator=(const EthDev0&) = delete; |
| EthDev0& operator=(EthDev0&&) = delete; |
| |
| static zx_status_t EthBind(void* ctx, zx_device_t* dev); |
| zx_status_t DdkOpen(zx_device_t** dev_out, uint32_t flags); |
| void DdkUnbind(); |
| void DdkRelease(); |
| |
| zx_status_t AddDevice(); |
| void SetStatus(uint32_t status); |
| void Recv(const void* data, size_t len, uint32_t flags); |
| void CompleteTx(ethmac_netbuf_t* netbuf, zx_status_t status); |
| |
| private: |
| friend class EthDev; |
| |
| // Buffer typecasting functions. |
| TransmitInfo* NetbufToTransmitInfo(ethmac_netbuf_t* netbuf); |
| ethmac_netbuf_t* TransmitInfoToNetbuf(TransmitInfo* tx_info); |
| // Resend transmitted packets for loopback. |
| void TransmitEcho(const void* data, size_t len); |
| void DestroyAllEthDev(); |
| |
| ddk::EthmacProtocolClient mac_; |
| ethmac_info_t info_ = {}; |
| uint32_t status_ = 0; |
| |
| int32_t promisc_requesters_ = 0; |
| int32_t multicast_promisc_requesters_ = 0; |
| |
| fbl::Mutex ethdev_lock_; |
| |
| // Active and idle instances (EthDev). |
| fbl::DoublyLinkedList<EthDev*> list_active_ __TA_GUARDED(ethdev_lock_); |
| fbl::DoublyLinkedList<EthDev*> list_idle_ __TA_GUARDED(ethdev_lock_); |
| }; |
| |
| using EthDevType = ddk::Device<EthDev, ddk::Openable, ddk::Closable, ddk::Messageable>; |
| |
| class EthDev : public EthDevType, |
| public ddk::EmptyProtocol<ZX_PROTOCOL_ETHERNET>, |
| public fbl::DoublyLinkedListable<EthDev*> { |
| |
| public: |
| EthDev(zx_device_t* parent, EthDev0* edev0) |
| : EthDevType(parent), edev0_(edev0), open_count_(1) {} |
| |
| EthDev(const EthDev&) = delete; |
| EthDev(EthDev&&) = delete; |
| EthDev& operator=(const EthDev&) = delete; |
| EthDev& operator=(EthDev&&) = delete; |
| |
| zx_status_t DdkOpen(zx_device_t** dev_out, uint32_t flags); |
| zx_status_t DdkClose(uint32_t flags); |
| void DdkRelease(); |
| zx_status_t DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn); |
| zx_status_t AddDevice(zx_device_t** out); |
| |
| // These methods are guarded by EthDev0's ethdev_lock_. |
| zx_status_t MsgGetInfoLocked(fidl_txn_t* txn) __TA_REQUIRES(edev0_->ethdev_lock_); |
| zx_status_t MsgGetFifosLocked(fidl_txn_t* txn) __TA_REQUIRES(edev0_->ethdev_lock_); |
| zx_status_t MsgSetIOBufferLocked(zx_handle_t h, fidl_txn_t* txn) |
| __TA_REQUIRES(edev0_->ethdev_lock_); |
| zx_status_t MsgStartLocked(fidl_txn_t* txn) __TA_REQUIRES(edev0_->ethdev_lock_); |
| zx_status_t MsgStopLocked(fidl_txn_t* txn) __TA_REQUIRES(edev0_->ethdev_lock_); |
| zx_status_t MsgListenStartLocked(fidl_txn_t* txn) __TA_REQUIRES(edev0_->ethdev_lock_); |
| zx_status_t MsgListenStopLocked(fidl_txn_t* txn) __TA_REQUIRES(edev0_->ethdev_lock_); |
| zx_status_t MsgSetClientNameLocked(const char* buf, size_t len, |
| fidl_txn_t* txn) __TA_REQUIRES(edev0_->ethdev_lock_); |
| zx_status_t MsgGetStatusLocked(fidl_txn_t* txn) __TA_REQUIRES(edev0_->ethdev_lock_); |
| zx_status_t MsgSetPromiscLocked(bool enabled, fidl_txn_t* txn) |
| __TA_REQUIRES(edev0_->ethdev_lock_); |
| zx_status_t MsgConfigMulticastAddMacLocked(const fuchsia_hardware_ethernet_MacAddress* mac, |
| fidl_txn_t* txn) __TA_REQUIRES(edev0_->ethdev_lock_); |
| zx_status_t |
| MsgConfigMulticastDeleteMacLocked(const fuchsia_hardware_ethernet_MacAddress* mac, |
| fidl_txn_t* txn) __TA_REQUIRES(edev0_->ethdev_lock_); |
| zx_status_t MsgConfigMulticastSetPromiscuousModeLocked(bool enabled, |
| fidl_txn_t* txn) |
| __TA_REQUIRES(edev0_->ethdev_lock_); |
| zx_status_t MsgConfigMulticastTestFilterLocked(fidl_txn_t* txn) |
| __TA_REQUIRES(edev0_->ethdev_lock_); |
| zx_status_t MsgDumpRegistersLocked(fidl_txn_t* txn) |
| __TA_REQUIRES(edev0_->ethdev_lock_); |
| |
| private: |
| friend class EthDev0; |
| |
| // Transmit thread has been created. |
| static constexpr uint32_t kStateTransmitThreadCreated = (1 << 0); |
| // Connected to the ethmac and handling traffic. |
| static constexpr uint32_t kStateRunning = (1 << 1); |
| // Being destroyed. |
| static constexpr uint32_t kStateDead = (1 << 2); |
| // This client should loopback tx packets to rx path. |
| static constexpr uint32_t kStateTransmissionLoopback = (1 << 3); |
| // This client wants to observe loopback tx packets. |
| static constexpr uint32_t kStateTransmissionListen = (1 << 4); |
| // This client has requested promisc mode. |
| static constexpr uint32_t kStatePromiscuous = (1 << 5); |
| // This client has requested multicast promisc mode. |
| static constexpr uint32_t kStateMulticastPromiscuous = (1 << 6); |
| |
| static constexpr uint32_t kFifoDepth = 256; |
| static constexpr uint32_t kFifoEntrySize = sizeof(eth_fifo_entry_t); |
| static constexpr uint32_t kPageMask = PAGE_SIZE - 1; |
| // Ensure that we will not exceed fifo capacity. |
| // Limited to one page - see fifo_create.md. |
| static_assert((kFifoDepth * kFifoEntrySize) <= 4096, ""); |
| // Number of empty fifo entries to read at a time. |
| static constexpr uint32_t kFifoBatchSize = 32; |
| |
| // How many multicast addresses to remember before punting and turning on multicast-promiscuous. |
| // TODO(eventually): enable deleting addresses. |
| // If this value is changed, change the EthernetMulticastPromiscOnOverflow() test in |
| // zircon/system/utest/ethernet/ethernet.cpp. |
| static constexpr uint32_t kMulticastListLimit = 32; |
| |
| static constexpr uint32_t kFailureReportRate = 50; |
| |
| int TransmitThread(); |
| zx_status_t PromiscHelperLogicLocked(bool req_on, uint32_t state_bit, |
| uint32_t param_id, int32_t* requesters_count) |
| __TA_REQUIRES(edev0_->ethdev_lock_); |
| zx_status_t SetPromiscLocked(bool req_on) __TA_REQUIRES(edev0_->ethdev_lock_); |
| zx_status_t SetMulticastPromiscLocked(bool req_on) __TA_REQUIRES(edev0_->ethdev_lock_); |
| zx_status_t RebuildMulticastFilterLocked() __TA_REQUIRES(edev0_->ethdev_lock_); |
| int MulticastAddressIndex(const uint8_t* mac) __TA_REQUIRES(edev0_->ethdev_lock_); |
| zx_status_t AddMulticastAddressLocked(const uint8_t* mac) __TA_REQUIRES(edev0_->ethdev_lock_); |
| zx_status_t DelMulticastAddressLocked(const uint8_t* mac) __TA_REQUIRES(edev0_->ethdev_lock_); |
| zx_status_t TestClearMulticastPromiscLocked() __TA_REQUIRES(edev0_->ethdev_lock_); |
| |
| int TransmitFifoWrite(eth_fifo_entry_t* entries, |
| size_t count); |
| TransmitInfo* GetTransmitInfo(); |
| void PutTransmitInfo(TransmitInfo* tx_info); |
| int Send(eth_fifo_entry_t* entries, size_t count); |
| void StopAndKill(); |
| |
| // These methods are guarded by EthDev0's ethdev_lock_. |
| void RecvLocked(const void* data, size_t len, uint32_t extra) |
| __TA_REQUIRES(edev0_->ethdev_lock_); |
| void KillLocked() __TA_REQUIRES(edev0_->ethdev_lock_); |
| zx_status_t StopLocked() __TA_REQUIRES(edev0_->ethdev_lock_); |
| zx_status_t SetClientNameLocked(const void* in_buf, size_t in_len) |
| __TA_REQUIRES(edev0_->ethdev_lock_); |
| zx_status_t GetStatusLocked(void* out_buf, size_t out_len, |
| size_t* out_actual) __TA_REQUIRES(edev0_->ethdev_lock_); |
| zx_status_t StartLocked() __TA_REQUIRES(edev0_->ethdev_lock_); |
| zx_status_t TransmitListenLocked(bool yes) __TA_REQUIRES(edev0_->ethdev_lock_); |
| zx_status_t GetFifosLocked(struct fuchsia_hardware_ethernet_Fifos* fifos) |
| __TA_REQUIRES(edev0_->ethdev_lock_); |
| zx_status_t SetIObufLocked(zx_handle_t vmo) __TA_REQUIRES(edev0_->ethdev_lock_); |
| |
| // This is used for signaling that TransmitThread() should exit. |
| static const zx_signals_t kSignalFifoTerminate = ZX_USER_SIGNAL_0; |
| |
| EthDev0* edev0_ = nullptr; |
| |
| uint8_t multicast_[kMulticastListLimit][ETH_MAC_SIZE] = {}; |
| uint32_t num_multicast_ = 0; |
| |
| uint32_t state_ = 0; |
| char name_[fuchsia_hardware_ethernet_MAX_CLIENT_NAME_LEN + 1]; |
| |
| // Fifos are named from the perspective of the packet from the client |
| // to the network interface. |
| zx::fifo transmit_fifo_; |
| uint32_t transmit_fifo_depth_ = 0; |
| zx::fifo receive_fifo_; |
| uint32_t receive_fifo_depth_ = 0; |
| eth_fifo_entry_t receive_fifo_entries_[kFifoBatchSize] = {}; |
| size_t receive_fifo_entry_count_ = 0; |
| |
| // io buffer. |
| zx::vmo io_vmo_; |
| fzl::VmoMapper io_buffer_; |
| fbl::unique_ptr<zx_paddr_t[]> paddr_map_ = nullptr; |
| zx::pmt pmt_; |
| |
| // kFifoDepth entries, each |transmit_buffer_size_| large. |
| fbl::unique_ptr<uint8_t[]> all_transmit_buffers_ = nullptr; |
| size_t transmit_buffer_size_ = 0; |
| |
| fbl::Mutex lock_; // Protects free_tx_bufs. |
| list_node_t free_transmit_buffers_ __TA_GUARDED(lock_) = {}; // TransmitInfo elements. |
| uint64_t open_count_ __TA_GUARDED(lock_) = 0; |
| |
| // fifo transmit thread. |
| thrd_t transmit_thread_ = {}; |
| |
| uint32_t fail_receive_read_ = 0; |
| uint32_t fail_receive_write_ = 0; |
| uint32_t ethmac_request_count_ = 0; |
| uint32_t ethmac_response_count_ = 0; |
| }; |
| |
| } // namespace eth |