blob: 6c8c085422de38af42e3b3158038c88b82f5982d [file] [log] [blame]
// Copyright 2020 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_RNDIS_FUNCTION_RNDIS_FUNCTION_H_
#define SRC_CONNECTIVITY_ETHERNET_DRIVERS_RNDIS_FUNCTION_RNDIS_FUNCTION_H_
#include <fuchsia/hardware/ethernet/cpp/banjo.h>
#include <fuchsia/hardware/usb/c/banjo.h>
#include <fuchsia/hardware/usb/function/cpp/banjo.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/async-loop/loop.h>
#include <lib/async/cpp/task.h>
#include <lib/driver/compat/cpp/compat.h>
#include <lib/driver/component/cpp/driver_base.h>
#include <lib/driver/component/cpp/node_add_args.h>
#include <lib/fit/function.h>
#include <lib/operation/ethernet.h>
#include <array>
#include <queue>
#include <thread>
#include <fbl/mutex.h>
#include <usb/cdc.h>
#include <usb/request-cpp.h>
#include <usb/usb-request.h>
#include <usb/usb.h>
#include "src/connectivity/ethernet/lib/rndis/rndis.h"
class RndisFunction : public fdf::DriverBase,
public ddk::UsbFunctionInterfaceProtocol<RndisFunction>,
public ddk::EthernetImplProtocol<RndisFunction> {
public:
static constexpr std::string_view kDriverName = "rndis-function";
static constexpr std::string_view kChildNodeName = "rndis-function";
RndisFunction(fdf::DriverStartArgs start_args,
fdf::UnownedSynchronizedDispatcher driver_dispatcher)
: DriverBase(kDriverName, std::move(start_args), std::move(driver_dispatcher)) {}
zx::result<> Start() override;
void PrepareStop(fdf::PrepareStopCompleter completer) override;
// ddk::UsbFunctionInterfaceProtocol<RndisFunction> implementation.
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);
// ddk::EthernetImplProtocol<RndisFunction> implementation.
zx_status_t EthernetImplQuery(uint32_t options, ethernet_info_t* 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 completion_cb, void* cookie);
zx_status_t EthernetImplSetParam(uint32_t param, int32_t value, const uint8_t* data,
size_t data_size);
void EthernetImplGetBti(zx::bti* bti) { bti->reset(); }
private:
zx_status_t HandleCommand(const void* buffer, size_t size);
zx_status_t HandleResponse(void* buffer, size_t size, size_t* actual);
zx_status_t Halt();
void Reset();
std::optional<std::vector<uint8_t>> QueryOid(uint32_t oid, void* input, size_t length);
zx_status_t SetOid(uint32_t oid, const uint8_t* input, size_t length);
void Shutdown();
// The driver framework may call DdkRelease at any point after we trigger the shutdown callback,
// so we should not hold the lock when executing the callback or we might get a use-after-free
// when the lock is released.
void ShutdownComplete() __TA_EXCLUDES(lock_);
void ReadComplete(usb_request_t* request);
void WriteComplete(usb_request_t* request);
void NotificationComplete(usb_request_t* request);
void ReceiveLocked(usb::Request<>& request) __TA_REQUIRES(lock_);
void NotifyLocked() __TA_REQUIRES(lock_);
void IndicateConnectionStatus(bool connected);
uint8_t NotificationAddress() { return descriptors_.notification_ep.b_endpoint_address; }
uint8_t BulkInAddress() { return descriptors_.in_ep.b_endpoint_address; }
uint8_t BulkOutAddress() { return descriptors_.out_ep.b_endpoint_address; }
bool Online() const __TA_REQUIRES(lock_) { return ifc_.is_valid() && rndis_ready_; }
static constexpr size_t kNotificationMaxPacketSize = 8;
static constexpr size_t kRequestPoolSize = 8;
static constexpr size_t kMtu = RNDIS_MAX_XFER_SIZE;
static constexpr uint32_t kVendorId = 0x44070b00;
static constexpr char kVendorDescription[] = "Google";
static constexpr uint16_t kVendorDriverVersionMajor = 1;
static constexpr uint16_t kVendorDriverVersionMinor = 0;
async::Loop loop_{&kAsyncLoopConfigNoAttachToCurrentThread};
ddk::EthernetIfcProtocolClient ifc_ __TA_GUARDED(lock_);
ddk::UsbFunctionProtocolClient function_;
size_t usb_request_size_;
fbl::Mutex lock_;
bool rndis_ready_ __TA_GUARDED(lock_) = false;
bool shutting_down_ __TA_GUARDED(lock_) = false;
uint32_t link_speed_ __TA_GUARDED(lock_) = 0;
std::array<uint8_t, ETH_MAC_SIZE> mac_addr_;
// Stats.
std::atomic<uint32_t> transmit_ok_ = 0;
std::atomic<uint32_t> receive_ok_ = 0;
std::atomic<uint32_t> transmit_errors_ = 0;
std::atomic<uint32_t> receive_errors_ = 0;
std::atomic<uint32_t> transmit_no_buffer_ = 0;
std::queue<std::vector<uint8_t>> control_responses_ __TA_GUARDED(lock_);
usb::RequestPool<> free_notify_pool_ __TA_GUARDED(lock_);
usb::RequestPool<> free_read_pool_ __TA_GUARDED(lock_);
usb::RequestPool<> free_write_pool_ __TA_GUARDED(lock_);
size_t pending_requests_ __TA_GUARDED(lock_) = 0;
std::optional<fdf::PrepareStopCompleter> prepare_stop_completer_;
usb_request_complete_callback_t read_request_complete_ = {
.callback =
[](void* ctx, usb_request_t* request) {
auto rndis = reinterpret_cast<RndisFunction*>(ctx);
async::PostTask(rndis->loop_.dispatcher(),
[rndis, request]() { rndis->ReadComplete(request); });
},
.ctx = this,
};
usb_request_complete_callback_t write_request_complete_ = {
.callback =
[](void* ctx, usb_request_t* request) {
auto rndis = reinterpret_cast<RndisFunction*>(ctx);
async::PostTask(rndis->loop_.dispatcher(),
[rndis, request]() { rndis->WriteComplete(request); });
},
.ctx = this,
};
usb_request_complete_callback_t notification_request_complete_ = {
.callback =
[](void* ctx, usb_request_t* request) {
auto rndis = reinterpret_cast<RndisFunction*>(ctx);
async::PostTask(rndis->loop_.dispatcher(),
[rndis, request]() { rndis->NotificationComplete(request); });
},
.ctx = this,
};
struct {
usb_interface_assoc_descriptor_t assoc;
usb_interface_descriptor_t communication_interface;
usb_cs_header_interface_descriptor_t cdc_header;
usb_cs_call_mgmt_interface_descriptor_t call_mgmt;
usb_cs_abstract_ctrl_mgmt_interface_descriptor_t acm;
usb_cs_union_interface_descriptor_1_t cdc_union;
usb_endpoint_descriptor_t notification_ep;
usb_interface_descriptor_t data_interface;
usb_endpoint_descriptor_t out_ep;
usb_endpoint_descriptor_t in_ep;
} __PACKED descriptors_;
compat::SyncInitializedDeviceServer compat_server_;
fidl::ClientEnd<fuchsia_driver_framework::NodeController> child_;
compat::BanjoServer usb_function_interface_banjo_server_{ZX_PROTOCOL_USB_FUNCTION, this,
&usb_function_interface_protocol_ops_};
compat::BanjoServer ethernet_impl_banjo_server_{ZX_PROTOCOL_ETHERNET_IMPL, this,
&ethernet_impl_protocol_ops_};
};
#endif // SRC_CONNECTIVITY_ETHERNET_DRIVERS_RNDIS_FUNCTION_RNDIS_FUNCTION_H_