blob: a5d912cd7a79f6061c5f38d05014d8f9be04a6b7 [file] [log] [blame]
// Copyright 2024 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_ETHERNET_DRIVERS_USB_CDC_FUNCTION_USB_CDC_FUNCTION_H_
#define SRC_CONNECTIVITY_ETHERNET_DRIVERS_USB_CDC_FUNCTION_USB_CDC_FUNCTION_H_
#include <endian.h>
#include <fidl/fuchsia.hardware.usb.endpoint/cpp/fidl.h>
#include <fuchsia/hardware/ethernet/cpp/banjo.h>
#include <fuchsia/hardware/usb/function/cpp/banjo.h>
#include <lib/ddk/device.h>
#include <lib/fdf/cpp/dispatcher.h>
#include <lib/sync/completion.h>
#include <zircon/listnode.h>
#include <array>
#include <mutex>
#include <optional>
#include <thread>
#include <ddktl/device.h>
#include <usb-endpoint/usb-endpoint-client.h>
#include <usb/cdc.h>
#include <usb/request-fidl.h>
#include <usb/usb.h>
namespace usb_cdc_function {
#define BULK_REQ_SIZE 2048
#define BULK_TX_COUNT 16
#define BULK_RX_COUNT 16
#define INTR_COUNT 8
#define BULK_MAX_PACKET 512 // FIXME(voydanoff) USB 3.0 support
#define INTR_MAX_PACKET sizeof(usb_cdc_speed_change_notification_t)
// Total Ethernet MTU is 1514 bytes. 1500 bytes for IP and 14 bytes for Ethernet
// header.
#define ETH_MTU 1514
class UsbCdcFunction;
using UsbCdcFunctionType = ddk::Device<UsbCdcFunction, ddk::Initializable, ddk::Unbindable>;
class UsbCdcFunction : public UsbCdcFunctionType,
public ddk::EthernetImplProtocol<UsbCdcFunction, ddk::base_protocol>,
public ddk::UsbFunctionInterfaceProtocol<UsbCdcFunction> {
public:
explicit UsbCdcFunction(zx_device_t* parent) : UsbCdcFunctionType(parent), function_(parent) {}
// Driver bind method.
static zx_status_t Bind(void* ctx, zx_device_t* parent);
void DdkInit(ddk::InitTxn txn);
void DdkRelease();
void DdkSuspend(ddk::SuspendTxn txn);
void DdkUnbind(ddk::UnbindTxn txn);
// EthernetImpl methods.
zx_status_t EthernetImplQuery(uint32_t options, ethernet_info_t* out_info);
void EthernetImplStop();
zx_status_t EthernetImplStart(const ethernet_ifc_protocol_t* ifc);
void EthernetImplQueueTx(uint32_t options, ethernet_netbuf_t* netbuf,
ethernet_impl_queue_tx_callback callback, void* cookie);
zx_status_t EthernetImplSetParam(uint32_t param, int32_t value, const uint8_t* data_buffer,
size_t data_size);
void EthernetImplGetBti(zx::bti* out_bti) { ZX_ASSERT(false); }
// UsbFunctionInterface methods.
size_t UsbFunctionInterfaceGetDescriptorsSize();
void UsbFunctionInterfaceGetDescriptors(uint8_t* out_descriptors_buffer, size_t descriptors_size,
size_t* out_descriptors_actual);
zx_status_t UsbFunctionInterfaceControl(const usb_setup_t* setup, const uint8_t* write_buffer,
size_t write_size, uint8_t* out_read_buffer,
size_t read_size, size_t* out_read_actual);
zx_status_t UsbFunctionInterfaceSetConfigured(bool configured, usb_speed_t speed);
zx_status_t UsbFunctionInterfaceSetInterface(uint8_t interface, uint8_t alt_setting);
zx_status_t insert_usb_request(usb::FidlRequest&& req, usb::EndpointClient<UsbCdcFunction>& ep);
void usb_request_queue(usb::FidlRequest&& req, usb::EndpointClient<UsbCdcFunction>& ep);
zx_status_t cdc_generate_mac_address();
zx_status_t cdc_send_locked(ethernet_netbuf_t* netbuf) __TA_REQUIRES(tx_mutex_);
void cdc_intr_complete(fuchsia_hardware_usb_endpoint::Completion completion);
void cdc_send_notifications();
void cdc_rx_complete(fuchsia_hardware_usb_endpoint::Completion completion);
void cdc_tx_complete(fuchsia_hardware_usb_endpoint::Completion completion);
ddk::UsbFunctionProtocolClient function_;
fdf::SynchronizedDispatcher dispatcher_;
// In-direction (TX to host).
usb::EndpointClient<UsbCdcFunction> intr_ep_{usb::EndpointType::INTERRUPT, this,
std::mem_fn(&UsbCdcFunction::cdc_intr_complete)};
// Out-direction (RX from host).
usb::EndpointClient<UsbCdcFunction> bulk_out_ep_{usb::EndpointType::BULK, this,
std::mem_fn(&UsbCdcFunction::cdc_rx_complete)};
// In-direction (TX to host).
usb::EndpointClient<UsbCdcFunction> bulk_in_ep_{usb::EndpointType::BULK, this,
std::mem_fn(&UsbCdcFunction::cdc_tx_complete)};
list_node_t tx_pending_infos_ __TA_GUARDED(tx_mutex_) = {}; // list of ethernet_netbuf_t
// Use this method to access the request lists defined above. These have correct thread
// annotations and will ensure that any error in locking are caught during compilation.
list_node_t* tx_pending_infos() __TA_REQUIRES(tx_mutex_) { return &tx_pending_infos_; }
void DiscardPendingTxInfos(zx_status_t status) __TA_EXCLUDES(tx_mutex_);
std::atomic_bool unbound_ = false; // set to true when device is going away.
// Device attributes
std::array<uint8_t, ETH_MAC_SIZE> mac_addr_;
// Ethernet lock -- must be acquired after tx_mutex
// when both locks are held.
std::mutex ethernet_mutex_ __TA_ACQUIRED_AFTER(tx_mutex_);
ddk::EthernetIfcProtocolClient ethernet_ifc_ __TA_GUARDED(ethernet_mutex_);
bool online_ __TA_GUARDED(ethernet_mutex_) = false;
bool configured_ = false;
usb_speed_t speed_ = 0;
// TX lock -- Must be acquired before ethernet_mutex
// when both locks are held.
std::mutex& tx_mutex_ = bulk_in_ep_.mutex();
std::mutex& rx_mutex_ = bulk_out_ep_.mutex();
std::mutex& intr_mutex_ = intr_ep_.mutex();
uint8_t bulk_out_addr_ = 0;
uint8_t bulk_in_addr_ = 0;
uint8_t intr_addr_ = 0;
std::optional<std::thread> suspend_thread_;
std::optional<ddk::SuspendTxn> suspend_txn_;
struct {
usb_interface_descriptor_t comm_intf;
usb_cs_header_interface_descriptor_t cdc_header;
usb_cs_union_interface_descriptor_1_t cdc_union;
usb_cs_ethernet_interface_descriptor_t cdc_eth;
usb_endpoint_descriptor_t intr_ep;
usb_interface_descriptor_t cdc_intf_0;
usb_interface_descriptor_t cdc_intf_1;
usb_endpoint_descriptor_t bulk_out_ep;
usb_endpoint_descriptor_t bulk_in_ep;
} descriptors_ = {
.comm_intf =
{
.b_length = sizeof(usb_interface_descriptor_t),
.b_descriptor_type = USB_DT_INTERFACE,
.b_interface_number = 0, // set later
.b_alternate_setting = 0,
.b_num_endpoints = 1,
.b_interface_class = USB_CLASS_COMM,
.b_interface_sub_class = USB_CDC_SUBCLASS_ETHERNET,
.b_interface_protocol = 0,
.i_interface = 0,
},
.cdc_header =
{
.bLength = sizeof(usb_cs_header_interface_descriptor_t),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_DST_HEADER,
.bcdCDC = 0x120,
},
.cdc_union =
{
.bLength = sizeof(usb_cs_union_interface_descriptor_1_t),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_DST_UNION,
.bControlInterface = 0, // set later
.bSubordinateInterface = 0, // set later
},
.cdc_eth =
{
.bLength = sizeof(usb_cs_ethernet_interface_descriptor_t),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_DST_ETHERNET,
.iMACAddress = 0, // set later
.bmEthernetStatistics = 0,
.wMaxSegmentSize = ETH_MTU,
.wNumberMCFilters = 0,
.bNumberPowerFilters = 0,
},
.intr_ep =
{
.b_length = sizeof(usb_endpoint_descriptor_t),
.b_descriptor_type = USB_DT_ENDPOINT,
.b_endpoint_address = 0, // set later
.bm_attributes = USB_ENDPOINT_INTERRUPT,
.w_max_packet_size = htole16(INTR_MAX_PACKET),
.b_interval = 8,
},
.cdc_intf_0 =
{
.b_length = sizeof(usb_interface_descriptor_t),
.b_descriptor_type = USB_DT_INTERFACE,
.b_interface_number = 0, // set later
.b_alternate_setting = 0,
.b_num_endpoints = 0,
.b_interface_class = USB_CLASS_CDC,
.b_interface_sub_class = 0,
.b_interface_protocol = 0,
.i_interface = 0,
},
.cdc_intf_1 =
{
.b_length = sizeof(usb_interface_descriptor_t),
.b_descriptor_type = USB_DT_INTERFACE,
.b_interface_number = 0, // set later
.b_alternate_setting = 1,
.b_num_endpoints = 2,
.b_interface_class = USB_CLASS_CDC,
.b_interface_sub_class = 0,
.b_interface_protocol = 0,
.i_interface = 0,
},
.bulk_out_ep =
{
.b_length = sizeof(usb_endpoint_descriptor_t),
.b_descriptor_type = USB_DT_ENDPOINT,
.b_endpoint_address = 0, // set later
.bm_attributes = USB_ENDPOINT_BULK,
.w_max_packet_size = htole16(BULK_MAX_PACKET),
.b_interval = 0,
},
.bulk_in_ep =
{
.b_length = sizeof(usb_endpoint_descriptor_t),
.b_descriptor_type = USB_DT_ENDPOINT,
.b_endpoint_address = 0, // set later
.bm_attributes = USB_ENDPOINT_BULK,
.w_max_packet_size = htole16(BULK_MAX_PACKET),
.b_interval = 0,
},
};
};
} // namespace usb_cdc_function
#endif // SRC_CONNECTIVITY_ETHERNET_DRIVERS_USB_CDC_FUNCTION_USB_CDC_FUNCTION_H_