blob: 66e6783138138beb7b8541d8e53c4950b3e343d1 [file] [log] [blame]
// Copyright 2018 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_NAND_DRIVERS_RAM_NAND_RAM_NAND_H_
#define SRC_DEVICES_NAND_DRIVERS_RAM_NAND_RAM_NAND_H_
#include <fidl/fuchsia.boot.metadata/cpp/fidl.h>
#include <fidl/fuchsia.driver.framework/cpp/fidl.h>
#include <fidl/fuchsia.hardware.nand/cpp/fidl.h>
#include <fuchsia/hardware/nand/c/banjo.h>
#include <fuchsia/hardware/nand/cpp/banjo.h>
#include <lib/driver/compat/cpp/banjo_server.h>
#include <lib/driver/compat/cpp/device_server.h>
#include <lib/driver/devfs/cpp/connector.h>
#include <lib/driver/metadata/cpp/metadata_server.h>
#include <lib/fzl/vmo-mapper.h>
#include <lib/sync/completion.h>
#include <lib/zircon-internal/thread_annotations.h>
#include <lib/zx/vmo.h>
#include <limits.h>
#include <threads.h>
#include <zircon/listnode.h>
#include <zircon/types.h>
#include <optional>
#include <fbl/array.h>
#include <fbl/macros.h>
#include <fbl/mutex.h>
namespace ram_nand {
// Wrapper for nand_info_t. It simplifies initialization of NandDevice.
struct NandParams : public nand_info_t {
NandParams() : NandParams(0, 0, 0, 0, 0) {}
NandParams(uint32_t page_size, uint32_t pages_per_block, uint32_t num_blocks, uint32_t ecc_bits,
uint32_t oob_size)
: NandParams(nand_info_t{page_size,
pages_per_block,
num_blocks,
ecc_bits,
oob_size,
static_cast<nand_class_t>(fuchsia_hardware_nand::wire::Class::kFtl),
{}}) {}
explicit NandParams(const nand_info_t& base) {
// NandParams has no data members.
*this = *reinterpret_cast<const NandParams*>(&base);
}
uint64_t GetSize() const { return static_cast<uint64_t>(page_size + oob_size) * NumPages(); }
uint32_t NumPages() const { return pages_per_block * num_blocks; }
};
// Provides the bulk of the functionality for a ram-backed NAND device.
class NandDevice : public ddk::NandProtocol<NandDevice>,
public fidl::WireServer<fuchsia_hardware_nand::RamNand> {
public:
using Id = size_t;
// Called after responding to an Unlink FIDL request.
using OnUnlink = fit::callback<void()>;
explicit NandDevice(const NandParams& params, async_dispatcher_t* dispatcher, Id id,
OnUnlink on_unlink)
: params_(params),
on_unlink_(std::move(on_unlink)),
device_name_(std::format("ram-nand-{}", id)),
banjo_server_(ZX_PROTOCOL_NAND, this, &nand_protocol_ops_),
dispatcher_(dispatcher) {}
~NandDevice() override;
// Perform object initialization, and return a unique name of this device.
// If `vmo` is not provided or is not valid, the device will create its own buffer, and
// initialize it to be empty (all 1s).
zx::result<std::string> Init(fuchsia_hardware_nand::wire::RamNandInfo& info,
fidl::UnownedClientEnd<fuchsia_driver_framework::Node> parent,
const std::shared_ptr<fdf::Namespace>& incoming,
const std::shared_ptr<fdf::OutgoingDirectory>& outgoing,
const std::optional<std::string>& node_name);
// fidl::WireServer<fuchsia_hardware_nand::RamNand> implementation.
void Unlink(UnlinkCompleter::Sync& completer) override;
// NAND protocol implementation.
void NandQuery(nand_info_t* info_out, size_t* nand_op_size_out);
void NandQueue(nand_operation_t* operation, nand_queue_callback completion_cb, void* cookie);
zx_status_t NandGetFactoryBadBlockList(uint32_t* bad_blocks, size_t bad_block_len,
size_t* num_bad_blocks);
private:
struct RamNandOp {
nand_operation_t* op;
nand_queue_callback completion_cb;
void* cookie;
// ftl::NandOperation::CreateOperation() requires that the size of the nand operation be at
// least `sizeof(nand_operation_t)`.
uint8_t padding_[24];
};
static_assert(sizeof(RamNandOp) >= sizeof(nand_operation_t));
void DevfsConnect(fidl::ServerEnd<fuchsia_hardware_nand::RamNand> server);
zx::result<> AddPendingOperation(nand_operation_t* operation, nand_queue_callback completion_cb,
void* cookie);
void PerformOperations();
std::pair<bool, std::vector<RamNandOp>> TakePendingOperations();
void PerformOperation(RamNandOp& operation);
uint32_t MainDataSize() const { return params_.NumPages() * params_.page_size; }
// Implementation of the actual commands.
zx_status_t ReadWriteData(nand_operation_t* operation, bool bytes);
zx_status_t ReadWriteOob(nand_operation_t* operation);
zx_status_t Erase(nand_operation_t* operation);
uintptr_t mapped_addr_ = 0;
zx::vmo vmo_;
// The mapping for the wear info vmo. May be empty, check for nullptr before use.
fzl::VmoMapper wear_info_;
NandParams params_;
fbl::Mutex lock_;
// Use `AddPendingOperation()` and `TakePendingOperation()` in order to manipulate
// `pending_operations_`.
std::vector<RamNandOp> pending_operations_ TA_GUARDED(lock_);
bool dead_ TA_GUARDED(lock_) = false;
// Triggered when operations have been added to `pending_operations_` or when `dead_` is set to
// true.
sync_completion_t operations_pending_;
std::optional<std::thread> operation_performer_;
// If non-zero, the driver will fail writes once the write-count reaches this value.
uint64_t fail_after_ = 0;
// The number of bytes written.
uint64_t write_count_ = 0;
// Called after responding to an Unlink FIDL request.
OnUnlink on_unlink_;
// Name of the ram-nand device.
std::string device_name_;
compat::BanjoServer banjo_server_;
compat::SyncInitializedDeviceServer compat_server_;
driver_devfs::Connector<fuchsia_hardware_nand::RamNand> devfs_connector_{
fit::bind_member<&NandDevice::DevfsConnect>(this)};
fidl::ClientEnd<fuchsia_driver_framework::NodeController> child_;
async_dispatcher_t* dispatcher_;
fidl::ServerBindingGroup<fuchsia_hardware_nand::RamNand> bindings_;
fdf_metadata::MetadataServer<fuchsia_boot_metadata::PartitionMap> partition_map_metadata_server_{
device_name_};
DISALLOW_COPY_ASSIGN_AND_MOVE(NandDevice);
};
} // namespace ram_nand
#endif // SRC_DEVICES_NAND_DRIVERS_RAM_NAND_RAM_NAND_H_