blob: 66d83bf85af5df4afc05eaf998b310594ba1b98d [file] [log] [blame]
// Copyright 2023 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_DEVICES_BLOCK_DRIVERS_UMS_FUNCTION_UMS_FUNCTION_H_
#define SRC_DEVICES_BLOCK_DRIVERS_UMS_FUNCTION_UMS_FUNCTION_H_
#include <fuchsia/hardware/usb/function/cpp/banjo.h>
#include <ddktl/device.h>
#include <fbl/condition_variable.h>
#include <fbl/mutex.h>
#include <usb/request-cpp.h>
#include <usb/ums.h>
#include <usb/usb-request.h>
namespace ums {
class UmsFunction;
using DeviceType = ddk::Device<UmsFunction, ddk::Initializable, ddk::Unbindable>;
class UmsFunction : public DeviceType, public ddk::UsbFunctionInterfaceProtocol<UmsFunction> {
public:
static constexpr char kDriverName[] = "usb-ums-function";
static constexpr uint32_t kBlockSize = 512;
static constexpr size_t kStorageSize = 4L * 1024L * 1024L * 1024L;
static constexpr uint64_t kBlockCount = kStorageSize / kBlockSize;
static constexpr size_t kDataReqSize = 16384;
static constexpr uint16_t kBulkMaxPacket = 512;
UmsFunction(zx_device_t* parent, ddk::UsbFunctionProtocolClient function)
: DeviceType(parent), function_(function) {}
~UmsFunction() = default;
static zx_status_t Bind(void* ctx, zx_device_t* parent);
zx_status_t AddDevice(zx_device_t* parent);
void DdkInit(ddk::InitTxn txn);
void DdkUnbind(ddk::UnbindTxn txn);
void DdkRelease();
// ddk::UsbFunctionInterfaceProtocol implementations.
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);
private:
enum DataState {
DATA_STATE_NONE,
DATA_STATE_READ,
DATA_STATE_WRITE,
DATA_STATE_UNMAP,
DATA_STATE_FAILED
};
void RequestQueue(usb::Request<>* req, const usb_request_complete_callback_t* completion);
static void CompletionCallback(void* ctx, usb_request_t* req);
// Main driver initialization.
zx_status_t Init();
void QueueData(usb::Request<>* req);
void QueueCsw(uint8_t status);
void ContinueTransfer();
void StartTransfer(DataState state, uint32_t transfer_bytes, uint64_t lba = 0);
void HandleInquiry(ums_cbw_t* cbw);
void HandleTestUnitReady(ums_cbw_t* cbw);
void HandleRequestSense(ums_cbw_t* cbw);
void HandleReadCapacity10(ums_cbw_t* cbw);
void HandleReadCapacity16(ums_cbw_t* cbw);
void HandleModeSense6(ums_cbw_t* cbw);
void HandleRead10(ums_cbw_t* cbw);
void HandleRead12(ums_cbw_t* cbw);
void HandleRead16(ums_cbw_t* cbw);
void HandleWrite10(ums_cbw_t* cbw);
void HandleWrite12(ums_cbw_t* cbw);
void HandleWrite16(ums_cbw_t* cbw);
void HandleUnmap(ums_cbw_t* cbw);
void HandleCbw(ums_cbw_t* cbw);
void CbwComplete(usb::Request<>* req);
void DataComplete(usb::Request<>* req);
int WorkerLoop();
ddk::UsbFunctionProtocolClient function_;
std::optional<usb::Request<>> cbw_req_;
bool cbw_req_complete_ = false;
std::optional<usb::Request<>> data_req_;
bool data_req_complete_ = false;
std::optional<usb::Request<>> csw_req_;
bool csw_req_complete_ = false;
// vmo for backing storage
static zx::vmo vmo_;
void* storage_;
// command we are currently handling
ums_cbw_t current_cbw_ = {};
// data transferred for the current command
uint32_t data_length_ = 0;
// state for data transfers
DataState data_state_;
// state for reads and writes
zx_off_t data_offset_ = 0;
size_t data_remaining_ = 0;
uint8_t bulk_out_addr_;
uint8_t bulk_in_addr_;
size_t parent_req_size_;
thrd_t thread_;
bool active_;
fbl::Mutex mtx_;
fbl::ConditionVariable condvar_ __TA_GUARDED(mtx_);
std::atomic_int pending_request_count_;
};
} // namespace ums
#endif // SRC_DEVICES_BLOCK_DRIVERS_UMS_FUNCTION_UMS_FUNCTION_H_