blob: b2cb35e5f9863051737efa1e4323df65239ea06d [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 <fuchsia/hardware/nand/c/fidl.h>
#include <inttypes.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 <ddk/metadata/nand.h>
#include <ddk/protocol/nand.h>
#include <ddktl/device.h>
#include <ddktl/protocol/nand.h>
#include <fbl/array.h>
#include <fbl/macros.h>
#include <fbl/mutex.h>
// Wrapper for fuchsia_hardware_nand_Info. It simplifies initialization of NandDevice.
struct NandParams : public fuchsia_hardware_nand_Info {
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(fuchsia_hardware_nand_Info{page_size,
pages_per_block,
num_blocks,
ecc_bits,
oob_size,
fuchsia_hardware_nand_Class_FTL,
{}}) {}
NandParams(const fuchsia_hardware_nand_Info& 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; }
};
class NandDevice;
using DeviceType =
ddk::Device<NandDevice, ddk::GetSizable, ddk::Initializable, ddk::Unbindable, ddk::Messageable>;
// Provides the bulk of the functionality for a ram-backed NAND device.
class NandDevice : public DeviceType, public ddk::NandProtocol<NandDevice, ddk::base_protocol> {
public:
explicit NandDevice(const NandParams& params, zx_device_t* parent = nullptr);
~NandDevice();
zx_status_t Bind(const fuchsia_hardware_nand_RamNandInfo& info);
void DdkInit(ddk::InitTxn txn);
void DdkRelease() { delete this; }
// Performs the object initialization, returning the required data to create
// an actual device (to call device_add()). The provided callback will be
// called when this device must be removed from the system.
zx_status_t Init(char name[NAME_MAX], zx::vmo vmo);
// Device protocol implementation.
zx_off_t DdkGetSize() { return params_.GetSize(); }
void DdkUnbind(ddk::UnbindTxn txn);
zx_status_t DdkMessage(fidl_incoming_msg_t* msg, fidl_txn_t* txn);
// Fidl RamNand implementation.
zx_status_t Unlink();
// NAND protocol implementation.
void NandQuery(fuchsia_hardware_nand_Info* 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:
void Kill();
bool AddToList(nand_operation_t* operation, nand_queue_callback completion_cb, void* cookie);
bool RemoveFromList(nand_operation_t** operation);
int WorkerThread();
static int WorkerThreadStub(void* arg);
uint32_t MainDataSize() const { return params_.NumPages() * params_.page_size; }
// Implementation of the actual commands.
zx_status_t ReadWriteData(nand_operation_t* operation);
zx_status_t ReadWriteOob(nand_operation_t* operation);
zx_status_t Erase(nand_operation_t* operation);
uintptr_t mapped_addr_ = 0;
zx::vmo vmo_;
NandParams params_;
fbl::Mutex lock_;
list_node_t txn_list_ TA_GUARDED(lock_) = LIST_INITIAL_VALUE(txn_list_);
bool dead_ TA_GUARDED(lock_) = false;
bool thread_created_ = false;
sync_completion_t wake_signal_;
thrd_t worker_;
std::optional<nand_config_t> export_nand_config_;
fbl::Array<char> export_partition_map_;
DISALLOW_COPY_ASSIGN_AND_MOVE(NandDevice);
};
#endif // SRC_DEVICES_NAND_DRIVERS_RAM_NAND_RAM_NAND_H_