1// Copyright 2017 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 "usb-mass-storage.h"
6
7#include <ddk/debug.h>
8
9#include <stdio.h>
10#include <string.h>
11
12static void ums_block_queue(void* ctx, block_op_t* op) {
13    ums_block_t* dev = ctx;
14    ums_txn_t* txn = block_op_to_txn(op);
15
16    switch (op->command & BLOCK_OP_MASK) {
17    case BLOCK_OP_READ:
18    case BLOCK_OP_WRITE:
19        zxlogf(TRACE, "UMS QUEUE %s %u @%zu (%p)\n",
20               (op->command & BLOCK_OP_MASK) == BLOCK_OP_READ ? "RD" : "WR",
21               op->rw.length, op->rw.offset_dev, op);
22        break;
23    case BLOCK_OP_FLUSH:
24        zxlogf(TRACE, "UMS QUEUE FLUSH (%p)\n", op);
25        break;
26    default:
27        zxlogf(ERROR, "ums_block_queue: unsupported command %u\n", op->command);
28        op->completion_cb(&txn->op, ZX_ERR_NOT_SUPPORTED);
29        return;
30    }
31
32    ums_t* ums = block_to_ums(dev);
33    txn->dev = dev;
34
35    mtx_lock(&ums->txn_lock);
36    list_add_tail(&ums->queued_txns, &txn->node);
37    mtx_unlock(&ums->txn_lock);
38    sync_completion_signal(&ums->txn_completion);
39}
40
41static void ums_get_info(void* ctx, block_info_t* info) {
42    ums_block_t* dev = ctx;
43    ums_t* ums = block_to_ums(dev);
44    memset(info, 0, sizeof(*info));
45    info->block_size = dev->block_size;
46    info->block_count = dev->total_blocks;
47    info->max_transfer_size = ums->max_transfer;
48    info->flags = dev->flags;
49}
50
51static void ums_block_query(void* ctx, block_info_t* info_out, size_t* block_op_size_out) {
52    ums_get_info(ctx, info_out);
53    *block_op_size_out = sizeof(ums_txn_t);
54}
55
56static block_protocol_ops_t ums_block_ops = {
57    .query = ums_block_query,
58    .queue = ums_block_queue,
59};
60
61static zx_status_t ums_block_ioctl(void* ctx, uint32_t op, const void* cmd, size_t cmdlen,
62                                   void* reply, size_t max, size_t* out_actual) {
63    ums_block_t* dev = ctx;
64
65    // TODO implement other block ioctls
66    switch (op) {
67    case IOCTL_BLOCK_GET_INFO: {
68        block_info_t* info = reply;
69        if (max < sizeof(*info))
70            return ZX_ERR_BUFFER_TOO_SMALL;
71        ums_get_info(dev, info);
72        *out_actual = sizeof(*info);
73        return ZX_OK;
74    }
75    case IOCTL_DEVICE_SYNC: {
76        return ZX_OK;
77    }
78    default:
79        return ZX_ERR_NOT_SUPPORTED;
80    }
81}
82
83static zx_off_t ums_block_get_size(void* ctx) {
84    ums_block_t* dev = ctx;
85    return dev->block_size * dev->total_blocks;
86}
87
88static zx_protocol_device_t ums_block_proto = {
89    .version = DEVICE_OPS_VERSION,
90    .ioctl = ums_block_ioctl,
91    .get_size = ums_block_get_size,
92};
93
94zx_status_t ums_block_add_device(ums_t* ums, ums_block_t* dev) {
95    char name[16];
96    snprintf(name, sizeof(name), "lun-%03d", dev->lun);
97
98    device_add_args_t args = {
99        .version = DEVICE_ADD_ARGS_VERSION,
100        .name = name,
101        .ctx = dev,
102        .ops = &ums_block_proto,
103        .proto_id = ZX_PROTOCOL_BLOCK_IMPL,
104        .proto_ops = &ums_block_ops,
105    };
106
107    return device_add(ums->zxdev, &args, &dev->zxdev);
108}
109