blob: 32ee47c20cf24c4b676f103f7100c0cd057859c7 [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.
#pragma once
#include <inttypes.h>
#include <limits.h>
#include <threads.h>
#include <ddk/protocol/nand.h>
#include <ddktl/device.h>
#include <ddktl/protocol/nand.h>
#include <fbl/macros.h>
#include <fbl/mutex.h>
#include <lib/zx/vmo.h>
#include <lib/sync/completion.h>
#include <zircon/device/ram-nand.h>
#include <zircon/listnode.h>
#include <zircon/thread_annotations.h>
#include <zircon/types.h>
// 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,
NAND_CLASS_FTL, {}}) {}
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;
}
};
class NandDevice;
using DeviceType = ddk::Device<NandDevice, ddk::GetSizable, ddk::Unbindable, ddk::Ioctlable>;
// Provides the bulk of the functionality for a ram-backed NAND device.
class NandDevice : public DeviceType, public ddk::NandProtocol<NandDevice> {
public:
explicit NandDevice(const NandParams& params, zx_device_t* parent = nullptr);
~NandDevice();
zx_status_t Bind(const ram_nand_info_t& info);
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();
zx_status_t DdkIoctl(uint32_t op, const void* in_buf, size_t in_len,
void* out_buf, size_t out_len, size_t* out_actual);
// 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:
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_) = {};
bool dead_ TA_GUARDED(lock_) = false;
bool thread_created_ = false;
sync_completion_t wake_signal_;
thrd_t worker_;
DISALLOW_COPY_ASSIGN_AND_MOVE(NandDevice);
};