blob: 0f5cebe895aaacecea38f092768c64926f735410 [file] [log] [blame]
// Copyright 2019 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 <lib/operation/operation.h>
#include <ddk/protocol/nand.h>
// namespace nand is used because this library will inevitably move to
// dev/lib/nand. In an effort to reduce dependencies (make doesn't support
// transitive deps), it's currently in the operation lib.
// TODO(surajmalhotra): Move to dev/lib/nand.
namespace nand {
// Usage notes:
//
// nand::Operation is a c++ wrapper around the nand_operation_t object. It provides
// capabilites to interact with a nand_op buffer which is used to traverse the
// nand stack. On deletion, it will automatically free itself.
//
// nand::UnownedOperation provides an unowned variant of nand::Operation. It adds
// functionality to store and call a complete callback which isn't present in
// nand::Operation. In addition, it will call the completion on destruction if it
// wasn't already triggered.
//
// nand::OperationPool provides pooling functionality for nand::Operation reuse.
//
// nand::OperationQueue provides a queue interface for tracking nand::Operation and
// nand::UnownedOperation objects.
//
// Available methods for both Operation and UnownedOperation include:
//
// nand_operation_t* operation(); // accessor for inner type.
//
// // Takes ownership of inner type. Should only be used when transferring
// // ownership to another driver.
// nand_operation_t* take();
//
// Available to Operation and UnownedOperation if they templatize of Storage:
//
// Storage* private_storage(); // accessor for private storage.
//
// Available to Operation:
//
// void Release(); // Frees the inner type.
//
// Available to UnownedOperation:
//
// void Complete(zx_status_t); // Completes the operation.
//
///////////////////////////////////////////////////////////////////////////////
// Example: Basic allocation with a pool:
//
// nand::OperationPool<> pool;
//
// const size_t op_size = nand::Operation<>::OperationSize(parent_op_size);
// for (int i = 0; i < kNumRequest; i++) {
// std::optional<nand::Operation> request;
// request = nand::Operation::Alloc(op_size, parent_op_size);
//
// if (!request) return ZX_ERR_NO_MEMORY;
// pool.add(*std::move(request));
// }
//
///////////////////////////////////////////////////////////////////////////////
// Example: Enqueue incoming operation into a nand::OperationQueue:
//
// class Driver {
// public:
// <...>
// private:
// nand::UnownedOperationQueue<> operations_;
// const size_t parent_op_size_;
// };
//
// void Driver::NandQueue(nand_operation_t* op, nand_queue_callback completion_cb, void* cookie) {
// operations_.push(nand::UnownedOperation<>(op, cb, parent_req_size_));
// }
//
///////////////////////////////////////////////////////////////////////////////
// Example: Using private context only visible to your driver:
//
// struct PrivateStorage {
// bool valid;
// size_t count_metric;
// }
//
// using NandOperation = nand::UnownedOperation<PrivateStorage>;
//
// void Driver::NandQueue(nand_operation_t* op, nand_queue_callback completion_cb, void* cookie) {
// NandOperation nand_op(op, cb, parent_req_size_));
// ZX_DEBUG_ASSERT(nand_op.operation()->command == NAND_ERASE);
// nand_op.private_storage()->valid = true;
// nand_op.private_storage()->count_metric += 1;
// <...>
// }
//
struct OperationTraits {
using OperationType = nand_operation_t;
static OperationType* Alloc(size_t op_size) {
fbl::AllocChecker ac;
fbl::unique_ptr<uint8_t[]> raw;
if constexpr (alignof(OperationType) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
raw = fbl::unique_ptr<uint8_t[]>(
new (static_cast<std::align_val_t>(alignof(OperationType)), &ac) uint8_t[op_size]);
} else {
raw = fbl::unique_ptr<uint8_t[]>(new (&ac) uint8_t[op_size]);
}
if (!ac.check()) {
return nullptr;
}
return reinterpret_cast<OperationType*>(raw.release());
}
static void Free(OperationType* op) {
delete[] reinterpret_cast<uint8_t*>(op);
}
};
struct CallbackTraits {
using CallbackType = void(void*, zx_status_t, nand_operation_t*);
static std::tuple<zx_status_t> AutoCompleteArgs() {
return std::make_tuple(ZX_ERR_INTERNAL);
}
static void Callback(CallbackType* callback, void* cookie, nand_operation_t* op,
zx_status_t status) {
callback(cookie, status, op);
}
};
template <typename Storage = void>
class Operation : public operation::Operation<Operation<Storage>, OperationTraits, Storage> {
public:
using BaseClass = operation::Operation<Operation<Storage>, OperationTraits, Storage>;
using BaseClass::BaseClass;
};
template <typename Storage = void>
class UnownedOperation : public operation::UnownedOperation<UnownedOperation<Storage>,
OperationTraits, CallbackTraits,
Storage> {
public:
using BaseClass = operation::UnownedOperation<UnownedOperation<Storage>, OperationTraits,
CallbackTraits, Storage>;
using BaseClass::BaseClass;
};
template <typename Storage = void>
using OperationQueue = operation::OperationQueue<Operation<Storage>, OperationTraits, Storage>;
template <typename Storage = void>
using UnownedOperationQueue = operation::UnownedOperationQueue<UnownedOperation<Storage>,
OperationTraits, CallbackTraits,
Storage>;
} // namespace nand