blob: 64b68f68c6aac5f49f7ec12b6fe284c6e1700262 [file] [log] [blame]
// Copyright 2017 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.
#include "usb-mass-storage.h"
#include <ddk/debug.h>
#include <stdio.h>
#include <string.h>
static void ums_block_queue(void* ctx, block_op_t* op, block_impl_queue_callback completion_cb,
void* cookie) {
ums_block_t* dev = ctx;
ums_txn_t* txn = block_op_to_txn(op);
txn->completion_cb = completion_cb;
txn->cookie = cookie;
switch (op->command & BLOCK_OP_MASK) {
case BLOCK_OP_READ:
case BLOCK_OP_WRITE:
zxlogf(TRACE, "UMS QUEUE %s %u @%zu (%p)\n",
(op->command & BLOCK_OP_MASK) == BLOCK_OP_READ ? "RD" : "WR",
op->rw.length, op->rw.offset_dev, op);
break;
case BLOCK_OP_FLUSH:
zxlogf(TRACE, "UMS QUEUE FLUSH (%p)\n", op);
break;
default:
zxlogf(ERROR, "ums_block_queue: unsupported command %u\n", op->command);
completion_cb(cookie, ZX_ERR_NOT_SUPPORTED, &txn->op);
return;
}
ums_t* ums = block_to_ums(dev);
txn->dev = dev;
mtx_lock(&ums->txn_lock);
list_add_tail(&ums->queued_txns, &txn->node);
mtx_unlock(&ums->txn_lock);
sync_completion_signal(&ums->txn_completion);
}
static void ums_get_info(void* ctx, block_info_t* info) {
ums_block_t* dev = ctx;
ums_t* ums = block_to_ums(dev);
memset(info, 0, sizeof(*info));
info->block_size = dev->block_size;
info->block_count = dev->total_blocks;
info->max_transfer_size = ums->max_transfer;
info->flags = dev->flags;
}
static void ums_block_query(void* ctx, block_info_t* info_out, size_t* block_op_size_out) {
ums_get_info(ctx, info_out);
*block_op_size_out = sizeof(ums_txn_t);
}
static block_impl_protocol_ops_t ums_block_ops = {
.query = ums_block_query,
.queue = ums_block_queue,
};
static zx_status_t ums_block_ioctl(void* ctx, uint32_t op, const void* cmd, size_t cmdlen,
void* reply, size_t max, size_t* out_actual) {
ums_block_t* dev = ctx;
// TODO implement other block ioctls
switch (op) {
case IOCTL_BLOCK_GET_INFO: {
block_info_t* info = reply;
if (max < sizeof(*info))
return ZX_ERR_BUFFER_TOO_SMALL;
ums_get_info(dev, info);
*out_actual = sizeof(*info);
return ZX_OK;
}
case IOCTL_DEVICE_SYNC: {
return ZX_OK;
}
default:
return ZX_ERR_NOT_SUPPORTED;
}
}
static zx_off_t ums_block_get_size(void* ctx) {
ums_block_t* dev = ctx;
return dev->block_size * dev->total_blocks;
}
static zx_protocol_device_t ums_block_proto = {
.version = DEVICE_OPS_VERSION,
.ioctl = ums_block_ioctl,
.get_size = ums_block_get_size,
};
zx_status_t ums_block_add_device(ums_t* ums, ums_block_t* dev) {
char name[16];
snprintf(name, sizeof(name), "lun-%03d", dev->lun);
device_add_args_t args = {
.version = DEVICE_ADD_ARGS_VERSION,
.name = name,
.ctx = dev,
.ops = &ums_block_proto,
.proto_id = ZX_PROTOCOL_BLOCK_IMPL,
.proto_ops = &ums_block_ops,
};
return device_add(ums->zxdev, &args, &dev->zxdev);
}