blob: 15d59046d9543ae733a85759105f9a430bcda33b [file] [log] [blame]
/*
* Copyright 2017, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the BSD 2-Clause license. Note that NO WARRANTY is provided.
* See "LICENSE_BSD2.txt" for details.
*
* @TAG(DATA61_BSD)
*/
/**
* @brief UFI Command implementation for USB Mass Storage Class
* @see USB Mass Storage Class UFI Command Spec
*/
#include <platsupport/delay.h>
#include <string.h>
#include <usb/drivers/ufi.h>
#include "storage.h"
/* Operation Code */
#define TEST_UNIT_READY 0x00
#define REZERO_UNIT 0x01
#define REQUEST_SENSE 0x03
#define FORMAT_UNIT 0x04
#define INQUIRY 0x12
#define START_STOP 0x1B
#define SEND_DIAGNOSTIC 0x1D
#define ALLOW_REMOVAL 0x1E
#define READ_FORMAT_CAP 0x23
#define READ_CAPACITY 0x25
#define READ_10 0x28
#define WRITE_10 0x2A
#define SEEK 0x2B
#define WRITE_VERIFY 0x2E
#define VERIFY 0x2F
#define MODE_SELECT 0x55
#define MODE_SENSE 0x5A
#define READ_12 0xA8
#define WRITE_12 0xAA
#define UFI_OUTPUT 0
#define UFI_INPUT 1
#define UFI_LBA_SHF 16
#define UFI_SENSE_LEN 18
#define UFI_INQ_LEN 36
#define UFI_MODE_SENSE_LEN 192
#define UFI_MODE_PAGE_ALL 0x3F
#define UFI_BLK_SIZE 512
/* Command Descriptor Block */
struct ufi_cdb {
uint8_t opcode;
uint8_t lun;
uint32_t lba;
uint32_t length;
uint16_t reserved;
} __attribute__((packed));
static void ufi_format_unit()
{
ZF_LOGF("Not implemented\n");
}
static void ufi_test_unit_ready(usb_dev_t *udev)
{
int err;
struct ufi_cdb cdb;
memset(&cdb, 0, sizeof(struct ufi_cdb));
/* Fill in the command */
cdb.opcode = TEST_UNIT_READY;
err = usb_storage_xfer(udev, &cdb, sizeof(struct ufi_cdb),
NULL, 0, UFI_OUTPUT);
if (err) {
ZF_LOGF("Transfer error\n");
}
}
static void ufi_request_sense(usb_dev_t *udev)
{
int err;
struct ufi_cdb cdb;
struct xact data;
memset(&cdb, 0, sizeof(struct ufi_cdb));
/* Fill in the command */
cdb.opcode = REQUEST_SENSE;
cdb.lba = UFI_SENSE_LEN << UFI_LBA_SHF;
data.type = PID_IN;
data.len = UFI_SENSE_LEN;
err = usb_alloc_xact(udev->dman, &data, 1);
if (err) {
ZF_LOGF("Out of DMA memory\n");
}
err = usb_storage_xfer(udev, &cdb, sizeof(struct ufi_cdb),
&data, 1, UFI_INPUT);
if (err) {
ZF_LOGF("Transfer error\n");
}
usb_destroy_xact(udev->dman, &data, 1);
}
static void ufi_inquiry(usb_dev_t *udev)
{
int err;
struct ufi_cdb cdb;
struct xact data;
memset(&cdb, 0, sizeof(struct ufi_cdb));
/* Inquiry UFI disk */
cdb.opcode = INQUIRY;
cdb.lba = UFI_INQ_LEN << UFI_LBA_SHF;
data.type = PID_IN;
data.len = UFI_INQ_LEN;
err = usb_alloc_xact(udev->dman, &data, 1);
if (err) {
ZF_LOGF("Out of DMA memory\n");
}
err = usb_storage_xfer(udev, &cdb, sizeof(struct ufi_cdb),
&data, 1, UFI_INPUT);
if (err) {
ZF_LOGF("Transfer error\n");
}
usb_destroy_xact(udev->dman, &data, 1);
}
static void ufi_prevent_allow_medium_removal(usb_dev_t *udev, int enable)
{
int err;
struct ufi_cdb cdb;
memset(&cdb, 0, sizeof(struct ufi_cdb));
cdb.opcode = ALLOW_REMOVAL;
cdb.lba = enable << 8;
err = usb_storage_xfer(udev, &cdb, sizeof(struct ufi_cdb),
NULL, 0, UFI_OUTPUT);
if (err) {
ZF_LOGF("Transfer error\n");
}
}
uint32_t ufi_read_capacity(usb_dev_t *udev)
{
int err;
uint32_t ret;
struct ufi_cdb cdb;
struct xact data;
memset(&cdb, 0, sizeof(struct ufi_cdb));
cdb.opcode = READ_CAPACITY;
data.type = PID_IN;
data.len = 8;
err = usb_alloc_xact(udev->dman, &data, 1);
if (err) {
ZF_LOGF("Out of DMA memory\n");
}
err = usb_storage_xfer(udev, &cdb, sizeof(struct ufi_cdb),
&data, 1, UFI_INPUT);
if (err) {
ZF_LOGF("Transfer error\n");
}
ret = *(uint32_t*)data.vaddr;
usb_destroy_xact(udev->dman, &data, 1);
return ret;
}
static void ufi_mode_sense(usb_dev_t *udev)
{
int err;
struct ufi_cdb cdb;
struct xact data;
memset(&cdb, 0, sizeof(struct ufi_cdb));
cdb.opcode = MODE_SENSE;
cdb.lba = UFI_MODE_PAGE_ALL;
cdb.length = UFI_MODE_SENSE_LEN << UFI_LBA_SHF;
data.type = PID_IN;
data.len = UFI_MODE_SENSE_LEN;
err = usb_alloc_xact(udev->dman, &data, 1);
if (err) {
ZF_LOGF("Out of DMA memory\n");
}
err = usb_storage_xfer(udev, &cdb, sizeof(struct ufi_cdb),
&data, 1, UFI_INPUT);
if (err) {
ZF_LOGF("Transfer error\n");
}
usb_destroy_xact(udev->dman, &data, 1);
}
static void ufi_read10(usb_dev_t *udev, uint32_t lba, uint16_t count)
{
int err;
struct ufi_cdb cdb;
struct xact data;
memset(&cdb, 0, sizeof(struct ufi_cdb));
cdb.opcode = READ_10;
cdb.lba = __builtin_bswap32(lba);
cdb.length = __builtin_bswap16(count) << 8;
data.type = PID_IN;
data.len = UFI_BLK_SIZE * count;
err = usb_alloc_xact(udev->dman, &data, 1);
if (err) {
ZF_LOGF("Out of DMA memory\n");
}
err = usb_storage_xfer(udev, &cdb, sizeof(struct ufi_cdb),
&data, 1, UFI_INPUT);
if (err) {
ZF_LOGF("Transfer error\n");
}
usb_destroy_xact(udev->dman, &data, 1);
}
static void ufi_read12(usb_dev_t *udev, uint32_t lba, uint32_t count)
{
int err;
struct ufi_cdb cdb;
struct xact data;
memset(&cdb, 0, sizeof(struct ufi_cdb));
cdb.opcode = READ_12;
cdb.lba = __builtin_bswap32(lba);
cdb.length = __builtin_bswap16(count);
data.type = PID_IN;
data.len = UFI_BLK_SIZE * count;
err = usb_alloc_xact(udev->dman, &data, 1);
if (err) {
ZF_LOGF("Out of DMA memory\n");
}
err = usb_storage_xfer(udev, &cdb, sizeof(struct ufi_cdb),
&data, 1, UFI_INPUT);
if (err) {
ZF_LOGF("Transfer error\n");
}
usb_destroy_xact(udev->dman, &data, 1);
}
int
ufi_init_disk(usb_dev_t *udev)
{
usb_storage_bind(udev);
ufi_inquiry(udev);
ufi_test_unit_ready(udev);
ufi_request_sense(udev);
ufi_test_unit_ready(udev);
ufi_read_capacity(udev);
ufi_mode_sense(udev);
ufi_test_unit_ready(udev);
ufi_prevent_allow_medium_removal(udev, 0);
ufi_request_sense(udev);
ufi_test_unit_ready(udev);
return 0;
}