1// Copyright 2018 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "broker.h" 6 7#include <stdio.h> 8#include <string.h> 9 10#include <ddk/binding.h> 11#include <ddk/debug.h> 12#include <ddk/protocol/nand.h> 13#include <ddktl/device.h> 14#include <ddktl/protocol/nand.h> 15#include <lib/sync/completion.h> 16#include <fbl/alloc_checker.h> 17#include <fbl/unique_ptr.h> 18#include <zircon/assert.h> 19#include <zircon/device/nand-broker.h> 20#include <zircon/types.h> 21 22 23namespace { 24 25// Wrapper for a nand_op_t. 26class Operation { 27 public: 28 explicit Operation(size_t op_size) { 29 raw_buffer_.reset(new uint8_t[op_size]); 30 31 memset(raw_buffer_.get(), 0, op_size); 32 nand_op_t* op = reinterpret_cast<nand_op_t*>(raw_buffer_.get()); 33 op->completion_cb = OnCompletion; 34 op->cookie = this; 35 } 36 ~Operation() {} 37 38 nand_op_t* GetOperation() { return reinterpret_cast<nand_op_t*>(raw_buffer_.get()); } 39 40 // Waits for the operation to complete and returns the operation's status. 41 zx_status_t Wait() { 42 zx_status_t status = sync_completion_wait(&event_, ZX_TIME_INFINITE); 43 sync_completion_reset(&event_); 44 return status != ZX_OK ? status : status_; 45 } 46 47 private: 48 static void OnCompletion(nand_op_t* op, zx_status_t status) { 49 Operation* operation = reinterpret_cast<Operation*>(op->cookie); 50 operation->status_ = status; 51 sync_completion_signal(&operation->event_); 52 } 53 54 sync_completion_t event_; 55 zx_status_t status_ = ZX_ERR_INTERNAL; 56 fbl::unique_ptr<uint8_t[]> raw_buffer_; 57}; 58 59class Broker; 60using DeviceType = ddk::Device<Broker, ddk::Unbindable, ddk::Ioctlable>; 61 62// Exposes a control device (nand-broker) for a nand protocol device. 63class Broker : public DeviceType { 64 public: 65 explicit Broker(zx_device_t* parent) : DeviceType(parent) {} 66 ~Broker() {} 67 68 zx_status_t Bind(); 69 void DdkRelease() { delete this; } 70 71 // Device protocol implementation. 72 void DdkUnbind() { DdkRemove(); } 73 zx_status_t DdkIoctl(uint32_t op, const void* in_buf, size_t in_len, 74 void* out_buf, size_t out_len, size_t* out_actual); 75 76 private: 77 zx_status_t Query(nand_info_t* info); 78 zx_status_t Queue(uint32_t command, const nand_broker_request_t& request, 79 nand_broker_response_t* response); 80 81 nand_protocol_t nand_protocol_; 82 size_t op_size_ = 0; 83}; 84 85zx_status_t Broker::Bind() { 86 if (device_get_protocol(parent(), ZX_PROTOCOL_NAND, &nand_protocol_) != ZX_OK) { 87 zxlogf(ERROR, "nand-broker: device '%s' does not support nand protocol\n", 88 device_get_name(parent())); 89 return ZX_ERR_NOT_SUPPORTED; 90 } 91 92 nand_info_t info; 93 Query(&info); 94 if (!op_size_) { 95 zxlogf(ERROR, "nand-broker: unable to query the nand driver\n"); 96 return ZX_ERR_NOT_SUPPORTED; 97 } 98 zxlogf(INFO, "nand-broker: %d blocks of %d pages each. Page size: %d\n", info.num_blocks, 99 info.pages_per_block, info.page_size); 100 101 return DdkAdd("broker"); 102} 103 104zx_status_t Broker::DdkIoctl(uint32_t op, const void* in_buf, size_t in_len, 105 void* out_buf, size_t out_len, size_t* out_actual) { 106 switch (op) { 107 case IOCTL_NAND_BROKER_UNLINK: 108 DdkUnbind(); 109 return ZX_OK; 110 111 case IOCTL_NAND_BROKER_GET_INFO: 112 if (out_len < sizeof(nand_info_t)) { 113 return ZX_ERR_INVALID_ARGS; 114 } 115 *out_actual = sizeof(nand_info_t); 116 return Query(reinterpret_cast<nand_info_t*>(out_buf)); 117 118 case IOCTL_NAND_BROKER_READ: 119 case IOCTL_NAND_BROKER_WRITE: 120 case IOCTL_NAND_BROKER_ERASE: 121 if (in_len != sizeof(nand_broker_request_t)) { 122 return ZX_ERR_INVALID_ARGS; 123 } 124 if (out_len < sizeof(nand_broker_response_t)) { 125 return ZX_ERR_INVALID_ARGS; 126 } 127 *out_actual = sizeof(nand_broker_response_t); 128 return Queue(op, *reinterpret_cast<const nand_broker_request_t*>(in_buf), 129 reinterpret_cast<nand_broker_response_t*>(out_buf)); 130 131 default: 132 return ZX_ERR_NOT_SUPPORTED; 133 } 134} 135 136zx_status_t Broker::Query(nand_info_t* info) { 137 ddk::NandProtocolProxy proxy(&nand_protocol_); 138 proxy.Query(info, &op_size_); 139 return ZX_OK; 140} 141 142zx_status_t Broker::Queue(uint32_t command, const nand_broker_request_t& request, 143 nand_broker_response_t* response) { 144 Operation operation(op_size_); 145 nand_op_t* op = operation.GetOperation(); 146 *response = {}; 147 148 switch (command) { 149 case IOCTL_NAND_BROKER_READ: 150 case IOCTL_NAND_BROKER_WRITE: 151 op->rw.command = (command == IOCTL_NAND_BROKER_READ) ? NAND_OP_READ : NAND_OP_WRITE; 152 op->rw.length = request.length; 153 op->rw.offset_nand = request.offset_nand; 154 op->rw.offset_data_vmo = request.offset_data_vmo; 155 op->rw.offset_oob_vmo = request.offset_oob_vmo; 156 op->rw.data_vmo = request.data_vmo ? request.vmo : ZX_HANDLE_INVALID; 157 op->rw.oob_vmo = request.oob_vmo ? request.vmo : ZX_HANDLE_INVALID; 158 break; 159 case IOCTL_NAND_BROKER_ERASE: 160 op->erase.command = NAND_OP_ERASE; 161 op->erase.first_block = request.offset_nand; 162 op->erase.num_blocks = request.length; 163 break; 164 default: 165 ZX_DEBUG_ASSERT(false); 166 } 167 168 ddk::NandProtocolProxy proxy(&nand_protocol_); 169 proxy.Queue(op); 170 171 response->status = operation.Wait(); 172 173 if (command == IOCTL_NAND_BROKER_READ) { 174 response->corrected_bit_flips = op->rw.corrected_bit_flips; 175 } 176 177 if ((command == IOCTL_NAND_BROKER_READ || command == IOCTL_NAND_BROKER_WRITE) && 178 request.vmo != ZX_HANDLE_INVALID) { 179 zx_handle_close(request.vmo); 180 } 181 182 return ZX_OK; 183} 184 185} // namespace 186 187zx_status_t nand_broker_bind(void* ctx, zx_device_t* parent) { 188 zxlogf(INFO, "nand-broker: binding\n"); 189 fbl::AllocChecker checker; 190 fbl::unique_ptr<Broker> device(new (&checker) Broker(parent)); 191 if (!checker.check()) { 192 return ZX_ERR_NO_MEMORY; 193 } 194 195 zx_status_t status = device->Bind(); 196 if (status == ZX_OK) { 197 // devmgr is now in charge of the device. 198 __UNUSED Broker* dummy = device.release(); 199 } 200 return status; 201} 202