366 lines
9.9 KiB
C
366 lines
9.9 KiB
C
#include <usb-msc-bulk.h>
|
|
#include <usb-api.h>
|
|
#include <usb-core.h>
|
|
#include <sys/process.h>
|
|
#include <stdio.h>
|
|
#include <rbc_const.h>
|
|
#include <rbc_struct.h>
|
|
#include <string.h>
|
|
|
|
#ifdef DEBUG
|
|
#define PRINTF(...) printf(__VA_ARGS__)
|
|
#else
|
|
#define PRINTF(...)
|
|
#endif
|
|
|
|
#ifndef USB_RBC_NUM_BLOCKS
|
|
#define USB_RBC_NUM_BLOCKS 32
|
|
#endif
|
|
|
|
static struct spc2_sense_data sense_data =
|
|
{
|
|
SCSI_SENSE_CURRENT_ERROR,
|
|
0,
|
|
0,
|
|
{0},
|
|
(sizeof(struct spc2_sense_data)
|
|
- offsetof(struct spc2_sense_data, command_specific))
|
|
};
|
|
|
|
static void
|
|
scsi_error(unsigned int sense_key, unsigned int asc, int32_t info)
|
|
{
|
|
sense_data.response_code = SCSI_SENSE_INFORMATION_VALID | SCSI_SENSE_CURRENT_ERROR;
|
|
sense_data.information[0] = (info >> 24) & 0xff;
|
|
sense_data.information[1] = (info >> 16) & 0xff;
|
|
sense_data.information[2] = (info >> 8) & 0xff;
|
|
sense_data.information[3] = info & 0xff;
|
|
sense_data.sense_key = sense_key;
|
|
sense_data.asc = (asc >> 8) & 0xff;
|
|
sense_data.ascq = asc & 0xff;
|
|
}
|
|
|
|
static void
|
|
scsi_ok()
|
|
{
|
|
sense_data.response_code = SCSI_SENSE_CURRENT_ERROR;
|
|
sense_data.sense_key = SCSI_SENSE_KEY_NO_SENSE;
|
|
sense_data.asc = 0x00;
|
|
sense_data.ascq = 0x00;
|
|
};
|
|
|
|
static const struct spc2_std_inquiry_data std_inquiry_data =
|
|
{
|
|
SCSI_STD_INQUIRY_CONNECTED | SCSI_STD_INQUIRY_TYPE_RBC,
|
|
0,
|
|
SCSI_STD_INQUIRY_VERSION_SPC2,
|
|
0,
|
|
(sizeof(struct spc2_std_inquiry_data)
|
|
- offsetof(struct spc2_std_inquiry_data, flags3)),
|
|
0,
|
|
0,
|
|
0,
|
|
{'F','l','u','f','w','a','r','e'},
|
|
{'T','e','s','t',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
|
|
{'0','.','1',' '}
|
|
};
|
|
|
|
#define UNIT_NAME {'F','l','u','f','f','w','a','r','e',' ', \
|
|
'P','s','e','u','d','o',' ','D','i','s','k'}
|
|
#define UNIT_NAME_LENGTH 21
|
|
|
|
static const struct
|
|
{
|
|
struct spc2_vital_product_data_head head;
|
|
struct {
|
|
struct spc2_vital_product_data_head head;
|
|
char unit_name[UNIT_NAME_LENGTH];
|
|
} descriptor;
|
|
} CC_BYTE_ALIGNED device_identification_data =
|
|
{
|
|
{
|
|
SCSI_STD_INQUIRY_CONNECTED | SCSI_STD_INQUIRY_TYPE_RBC,
|
|
SCSI_PAGE_DEVICE_IDENTIFICATION,
|
|
0,
|
|
sizeof(device_identification_data.descriptor),
|
|
},
|
|
{
|
|
{
|
|
SCSI_CODE_SET_ACSII,
|
|
SCSI_IDENTIFIER_TYPE_NON_UNIQUE,
|
|
0,
|
|
sizeof(device_identification_data.descriptor.unit_name)
|
|
},
|
|
UNIT_NAME
|
|
}
|
|
};
|
|
|
|
|
|
static const struct
|
|
{
|
|
struct spc2_vital_product_data_head head;
|
|
uint8_t supported[3];
|
|
} CC_BYTE_ALIGNED supported_pages_data =
|
|
{
|
|
{
|
|
SCSI_STD_INQUIRY_CONNECTED | SCSI_STD_INQUIRY_TYPE_RBC,
|
|
SCSI_PAGE_SUPPORTED_PAGES,
|
|
0,
|
|
sizeof(supported_pages_data.supported),
|
|
},
|
|
{SCSI_PAGE_SUPPORTED_PAGES, SCSI_PAGE_UNIT_SERIAL_NUMBER,
|
|
SCSI_PAGE_DEVICE_IDENTIFICATION}
|
|
};
|
|
|
|
static const struct
|
|
{
|
|
struct spc2_vital_product_data_head head;
|
|
uint8_t serial_number[8];
|
|
} CC_BYTE_ALIGNED unit_serial_number_data = {
|
|
{
|
|
SCSI_STD_INQUIRY_CONNECTED | SCSI_STD_INQUIRY_TYPE_RBC,
|
|
SCSI_PAGE_SUPPORTED_PAGES,
|
|
0,
|
|
sizeof(unit_serial_number_data.serial_number)
|
|
},
|
|
{'1','2','3','4','5','6','7','8'}
|
|
};
|
|
|
|
static usb_msc_handler_status
|
|
handle_inquiry_cmd(struct usb_msc_command_state *state)
|
|
{
|
|
struct spc2_inquiry_cmd *cmd = (struct spc2_inquiry_cmd*)state->command;
|
|
if (cmd->flags & SCSI_INQUIRY_FLAG_CMDDT) {
|
|
scsi_error(SCSI_SENSE_KEY_ILLEGAL_REQUEST,SCSI_ASC_INVALID_FIELD_IN_CDB,
|
|
cmd->allocation_length);
|
|
return USB_MSC_HANDLER_FAILED;
|
|
}
|
|
if (cmd->flags & SCSI_INQUIRY_FLAG_EVPD) {
|
|
PRINTF("Requested page %02x\n", cmd->page);
|
|
switch (cmd->page) {
|
|
case SCSI_PAGE_SUPPORTED_PAGES:
|
|
usb_msc_send_data((uint8_t *)&supported_pages_data,
|
|
sizeof(supported_pages_data),
|
|
USB_MSC_DATA_SEND | USB_MSC_DATA_LAST);
|
|
break;
|
|
case SCSI_PAGE_DEVICE_IDENTIFICATION:
|
|
usb_msc_send_data((uint8_t *)&device_identification_data,
|
|
sizeof(device_identification_data),
|
|
USB_MSC_DATA_SEND | USB_MSC_DATA_LAST);
|
|
break;
|
|
case SCSI_PAGE_UNIT_SERIAL_NUMBER:
|
|
usb_msc_send_data((uint8_t *)&unit_serial_number_data,
|
|
sizeof(unit_serial_number_data),
|
|
USB_MSC_DATA_SEND | USB_MSC_DATA_LAST);
|
|
break;
|
|
default:
|
|
scsi_error(SCSI_SENSE_KEY_ILLEGAL_REQUEST,SCSI_ASC_INVALID_FIELD_IN_CDB,
|
|
cmd->allocation_length);
|
|
return USB_MSC_HANDLER_FAILED;
|
|
}
|
|
return USB_MSC_HANDLER_OK;
|
|
} else {
|
|
if (cmd->page != 0) {
|
|
scsi_error(SCSI_SENSE_KEY_ILLEGAL_REQUEST,SCSI_ASC_INVALID_FIELD_IN_CDB,
|
|
cmd->allocation_length);
|
|
return USB_MSC_HANDLER_FAILED;
|
|
}
|
|
usb_msc_send_data((uint8_t *)&std_inquiry_data,
|
|
sizeof(std_inquiry_data),
|
|
USB_MSC_DATA_SEND | USB_MSC_DATA_LAST);
|
|
}
|
|
return USB_MSC_HANDLER_OK;
|
|
}
|
|
|
|
static usb_msc_handler_status
|
|
handle_request_sense_cmd(struct usb_msc_command_state *state)
|
|
{
|
|
usb_msc_send_data((uint8_t *)&sense_data,
|
|
sizeof(sense_data),
|
|
USB_MSC_DATA_SEND | USB_MSC_DATA_LAST);
|
|
return USB_MSC_HANDLER_OK;
|
|
}
|
|
|
|
static usb_msc_handler_status
|
|
handle_test_unit_ready_cmd(struct usb_msc_command_state *state)
|
|
{
|
|
scsi_ok();
|
|
return USB_MSC_HANDLER_OK;
|
|
}
|
|
|
|
static const struct rbc_read_capacity_data read_capacity_data =
|
|
{
|
|
HOST32_TO_BE_BYTES(USB_RBC_NUM_BLOCKS-1),
|
|
HOST32_TO_BE_BYTES(512)
|
|
};
|
|
|
|
static usb_msc_handler_status
|
|
handle_read_capacity(struct usb_msc_command_state *state)
|
|
{
|
|
usb_msc_send_data((uint8_t *)&read_capacity_data,
|
|
sizeof(read_capacity_data),
|
|
USB_MSC_DATA_SEND | USB_MSC_DATA_LAST);
|
|
return USB_MSC_HANDLER_OK;
|
|
}
|
|
|
|
static const struct mode_sense_data {
|
|
struct spc2_mode_parameter_header_6 header;
|
|
struct rbc_device_parameters_page page;
|
|
} CC_BYTE_ALIGNED mode_sense_data =
|
|
{
|
|
{
|
|
(sizeof(mode_sense_data)
|
|
- offsetof(struct mode_sense_data, header.medium_type)),
|
|
0,0,0
|
|
},
|
|
{
|
|
{SCSI_MODE_RBC_DEVICE_PAGE | SCSI_MODE_PAGE_SP,
|
|
sizeof(mode_sense_data) - offsetof(struct mode_sense_data, page.flags1)},
|
|
SCSI_MODE_WCD,
|
|
HOST16_TO_BE_BYTES(512),
|
|
HOST40_TO_BE_BYTES((long long)USB_RBC_NUM_BLOCKS),
|
|
0x80,
|
|
(SCSI_MODE_FORMATD | SCSI_MODE_LOCKD),
|
|
0
|
|
}
|
|
};
|
|
|
|
static usb_msc_handler_status
|
|
handle_mode_sense(struct usb_msc_command_state *state)
|
|
{
|
|
struct spc2_mode_sence_6_cmd *cmd =
|
|
(struct spc2_mode_sence_6_cmd*)state->command;
|
|
PRINTF("%ld - %ld - %ld\n", sizeof(struct mode_sense_data), offsetof(struct mode_sense_data, page.flags1),offsetof(struct mode_sense_data, page.reserved));
|
|
switch(cmd->page_code) {
|
|
case SCSI_MODE_RBC_DEVICE_PAGE:
|
|
case SCSI_MODE_SENSE_ALL_PAGES:
|
|
usb_msc_send_data((uint8_t *)&mode_sense_data,
|
|
sizeof(mode_sense_data),
|
|
USB_MSC_DATA_SEND | USB_MSC_DATA_LAST);
|
|
break;
|
|
default:
|
|
scsi_error(SCSI_SENSE_KEY_ILLEGAL_REQUEST,SCSI_ASC_INVALID_FIELD_IN_CDB,
|
|
cmd->allocation_length);
|
|
return USB_MSC_HANDLER_FAILED;
|
|
}
|
|
return USB_MSC_HANDLER_OK;
|
|
}
|
|
|
|
static usb_msc_handler_status
|
|
handle_mode_select(struct usb_msc_command_state *state)
|
|
{
|
|
/* Can't change anything */
|
|
return USB_MSC_HANDLER_OK;
|
|
}
|
|
static uint8_t disk_blocks[USB_RBC_NUM_BLOCKS][512];
|
|
|
|
static usb_msc_handler_status
|
|
handle_read(struct usb_msc_command_state *state)
|
|
{
|
|
struct rbc_read_cmd *cmd = (struct rbc_read_cmd*)state->command;
|
|
unsigned long lba = be32_to_host(cmd->logical_block_address);
|
|
unsigned long blocks = be16_to_host(cmd->transfer_length);
|
|
PRINTF("Requested %ld blocks at %ld\n", blocks, lba);
|
|
if (lba >= USB_RBC_NUM_BLOCKS || lba + blocks > USB_RBC_NUM_BLOCKS) {
|
|
scsi_error(SCSI_SENSE_KEY_ILLEGAL_REQUEST,SCSI_ASC_INVALID_FIELD_IN_CDB,
|
|
blocks);
|
|
return USB_MSC_HANDLER_FAILED;
|
|
}
|
|
usb_msc_send_data((uint8_t *)&disk_blocks[lba], blocks * 512,
|
|
USB_MSC_DATA_SEND | USB_MSC_DATA_LAST);
|
|
scsi_ok();
|
|
return USB_MSC_HANDLER_OK;
|
|
}
|
|
|
|
static void
|
|
handle_write_done(struct usb_msc_command_state *state)
|
|
{
|
|
PRINTF("Wrote data\n");
|
|
state->status = MASS_BULK_CSW_STATUS_PASSED;
|
|
scsi_ok();
|
|
}
|
|
|
|
static usb_msc_handler_status
|
|
handle_write(struct usb_msc_command_state *state)
|
|
{
|
|
struct rbc_write_cmd *cmd = (struct rbc_write_cmd*)state->command;
|
|
unsigned long lba = be32_to_host(cmd->logical_block_address);
|
|
unsigned long blocks = be16_to_host(cmd->transfer_length);
|
|
if (lba >= USB_RBC_NUM_BLOCKS || lba + blocks > USB_RBC_NUM_BLOCKS) {
|
|
scsi_error(SCSI_SENSE_KEY_ILLEGAL_REQUEST,SCSI_ASC_INVALID_FIELD_IN_CDB,
|
|
blocks);
|
|
return USB_MSC_HANDLER_FAILED;
|
|
}
|
|
PRINTF("Writing %ld blocks at %ld\n", blocks, lba);
|
|
usb_msc_receive_data(disk_blocks[lba], blocks * 512,
|
|
USB_MSC_DATA_RECEIVE | USB_MSC_DATA_LAST
|
|
| USB_MSC_DATA_DO_CALLBACK);
|
|
state->data_cb = handle_write_done;
|
|
return USB_MSC_HANDLER_DELAYED;
|
|
}
|
|
|
|
static usb_msc_handler_status
|
|
handle_start_stop_unit(struct usb_msc_command_state *state)
|
|
{
|
|
scsi_ok();
|
|
return USB_MSC_HANDLER_OK;
|
|
}
|
|
|
|
static usb_msc_handler_status
|
|
handle_verify(struct usb_msc_command_state *state)
|
|
{
|
|
scsi_ok();
|
|
return USB_MSC_HANDLER_OK;
|
|
}
|
|
|
|
usb_msc_handler_status
|
|
usb_msc_handle_command(struct usb_msc_command_state *state)
|
|
{
|
|
|
|
usb_msc_handler_status ret;
|
|
PRINTF("Got CBW %02x\n", state->command[0]);
|
|
switch(state->command[0]) {
|
|
case SCSI_CMD_INQUIRY:
|
|
ret = handle_inquiry_cmd(state);
|
|
break;
|
|
case SCSI_CMD_REQUEST_SENSE:
|
|
ret = handle_request_sense_cmd(state);
|
|
break;
|
|
case SCSI_CMD_TEST_UNIT_READY:
|
|
ret = handle_test_unit_ready_cmd(state);
|
|
break;
|
|
case SCSI_CMD_READ_CAPACITY:
|
|
ret = handle_read_capacity(state);
|
|
break;
|
|
case SCSI_CMD_MODE_SENSE_6:
|
|
ret = handle_mode_sense(state);
|
|
break;
|
|
case SCSI_CMD_MODE_SELECT_6:
|
|
ret = handle_mode_select(state);
|
|
break;
|
|
case SCSI_CMD_READ_10:
|
|
ret = handle_read(state);
|
|
break;
|
|
case SCSI_CMD_WRITE_10:
|
|
ret = handle_write(state);
|
|
break;
|
|
case SCSI_CMD_VERIFY_10:
|
|
ret = handle_verify(state);
|
|
break;
|
|
case SCSI_CMD_START_STOP_UNIT:
|
|
ret = handle_start_stop_unit(state);
|
|
break;
|
|
default:
|
|
printf("Unhandled request: %02x\n", state->command[0]);
|
|
scsi_error(SCSI_SENSE_KEY_ILLEGAL_REQUEST,
|
|
SCSI_ASC_INVALID_COMMAND_OPERATION,0);
|
|
return USB_MSC_HANDLER_FAILED;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
usb_msc_command_handler_init()
|
|
{
|
|
}
|