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 
ums_block_queue(void * ctx,block_op_t * op,block_impl_queue_callback completion_cb,void * cookie)12 static void ums_block_queue(void* ctx, block_op_t* op, block_impl_queue_callback completion_cb,
13                             void* cookie) {
14     ums_block_t* dev = ctx;
15     ums_txn_t* txn = block_op_to_txn(op);
16     txn->completion_cb = completion_cb;
17     txn->cookie = cookie;
18 
19     switch (op->command & BLOCK_OP_MASK) {
20     case BLOCK_OP_READ:
21     case BLOCK_OP_WRITE:
22         zxlogf(TRACE, "UMS QUEUE %s %u @%zu (%p)\n",
23                (op->command & BLOCK_OP_MASK) == BLOCK_OP_READ ? "RD" : "WR",
24                op->rw.length, op->rw.offset_dev, op);
25         break;
26     case BLOCK_OP_FLUSH:
27         zxlogf(TRACE, "UMS QUEUE FLUSH (%p)\n", op);
28         break;
29     default:
30         zxlogf(ERROR, "ums_block_queue: unsupported command %u\n", op->command);
31         completion_cb(cookie, ZX_ERR_NOT_SUPPORTED, &txn->op);
32         return;
33     }
34 
35     ums_t* ums = block_to_ums(dev);
36     txn->dev = dev;
37 
38     mtx_lock(&ums->txn_lock);
39     list_add_tail(&ums->queued_txns, &txn->node);
40     mtx_unlock(&ums->txn_lock);
41     sync_completion_signal(&ums->txn_completion);
42 }
43 
ums_get_info(void * ctx,block_info_t * info)44 static void ums_get_info(void* ctx, block_info_t* info) {
45     ums_block_t* dev = ctx;
46     ums_t* ums = block_to_ums(dev);
47     memset(info, 0, sizeof(*info));
48     info->block_size = dev->block_size;
49     info->block_count = dev->total_blocks;
50     info->max_transfer_size = ums->max_transfer;
51     info->flags = dev->flags;
52 }
53 
ums_block_query(void * ctx,block_info_t * info_out,size_t * block_op_size_out)54 static void ums_block_query(void* ctx, block_info_t* info_out, size_t* block_op_size_out) {
55     ums_get_info(ctx, info_out);
56     *block_op_size_out = sizeof(ums_txn_t);
57 }
58 
59 static block_impl_protocol_ops_t ums_block_ops = {
60     .query = ums_block_query,
61     .queue = ums_block_queue,
62 };
63 
ums_block_ioctl(void * ctx,uint32_t op,const void * cmd,size_t cmdlen,void * reply,size_t max,size_t * out_actual)64 static zx_status_t ums_block_ioctl(void* ctx, uint32_t op, const void* cmd, size_t cmdlen,
65                                    void* reply, size_t max, size_t* out_actual) {
66     ums_block_t* dev = ctx;
67 
68     // TODO implement other block ioctls
69     switch (op) {
70     case IOCTL_BLOCK_GET_INFO: {
71         block_info_t* info = reply;
72         if (max < sizeof(*info))
73             return ZX_ERR_BUFFER_TOO_SMALL;
74         ums_get_info(dev, info);
75         *out_actual = sizeof(*info);
76         return ZX_OK;
77     }
78     default:
79         return ZX_ERR_NOT_SUPPORTED;
80     }
81 }
82 
ums_block_get_size(void * ctx)83 static 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 
88 static 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 
ums_block_add_device(ums_t * ums,ums_block_t * dev)94 zx_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