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