blob: fed98501660a86eb54c7a499de5ac7e12142e666 [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 <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/fit/function.h>
#include <lib/operation/ethernet.h>
#include <zircon/hw/usb/cdc.h>
#include <array>
#include <queue>
#include <thread>
#include <ddk/protocol/usb.h>
#include <ddktl/device.h>
#include <ddktl/protocol/ethernet.h>
#include <ddktl/protocol/usb/function.h>
#include <fbl/mutex.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;
using RndisFunctionType = ddk::Device<RndisFunction, ddk::Unbindable, ddk::Suspendable>;
class RndisFunction : public RndisFunctionType,
public ddk::UsbFunctionInterfaceProtocol<RndisFunction>,
public ddk::EthernetImplProtocol<RndisFunction, ddk::base_protocol> {
public:
explicit RndisFunction(zx_device_t* parent)
: RndisFunctionType(parent),
loop_(&kAsyncLoopConfigNoAttachToCurrentThread),
function_(parent) {}
void DdkUnbind(ddk::UnbindTxn txn);
void DdkSuspend(ddk::SuspendTxn txn);
void DdkRelease();
size_t UsbFunctionInterfaceGetDescriptorsSize();
void UsbFunctionInterfaceGetDescriptors(void* out_descriptors_buffer, size_t descriptors_size,
size_t* out_descriptors_actual);
zx_status_t UsbFunctionInterfaceControl(const usb_setup_t* setup, const void* write_buffer,
size_t write_size, void* 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 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 void* data,
size_t data_size);
void EthernetImplGetBti(zx::bti* bti) { bti->reset(); }
static zx_status_t Create(void* ctx, zx_device_t* dev);
zx_status_t Bind();
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();
void ShutdownComplete() __TA_REQUIRES(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.bEndpointAddress; }
uint8_t BulkInAddress() { return descriptors_.in_ep.bEndpointAddress; }
uint8_t BulkOutAddress() { return descriptors_.out_ep.bEndpointAddress; }
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_;
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<fit::function<void()>> shutdown_callback_;
usb_request_complete_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_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_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_;
};
#endif // SRC_CONNECTIVITY_ETHERNET_DRIVERS_RNDIS_FUNCTION_RNDIS_FUNCTION_H_