summaryrefslogtreecommitdiff
path: root/firmware/usbstack/usb_storage.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/usbstack/usb_storage.c')
-rw-r--r--firmware/usbstack/usb_storage.c604
1 files changed, 436 insertions, 168 deletions
diff --git a/firmware/usbstack/usb_storage.c b/firmware/usbstack/usb_storage.c
index a1faf3d..0904e17 100644
--- a/firmware/usbstack/usb_storage.c
+++ b/firmware/usbstack/usb_storage.c
@@ -26,8 +26,22 @@
#include "hotswap.h"
#include "disk.h"
+#ifdef USB_STORAGE
+
+/* Enable the following define to export only the SD card slot. This
+ * is useful for USBCV MSC tests, as those are destructive.
+ * This won't work right if the device doesn't have a card slot.
+ */
+//#define ONLY_EXPOSE_CARD_SLOT
+
#define SECTOR_SIZE 512
+/* We can currently use up to 20k buffer size. More than that requires
+ * transfer chaining in the driver. Tests on sansa c200 show that the 16k
+ * limitation causes no more than 2% slowdown.
+ */
+#define BUFFER_SIZE 16384
+
/* bulk-only class specific requests */
#define USB_BULK_RESET_REQUEST 0xff
#define USB_BULK_GET_MAX_LUN 0xfe
@@ -40,7 +54,8 @@
#define SCSI_TEST_UNIT_READY 0x00
#define SCSI_INQUIRY 0x12
-#define SCSI_MODE_SENSE 0x1a
+#define SCSI_MODE_SENSE_6 0x1a
+#define SCSI_MODE_SENSE_10 0x5a
#define SCSI_REQUEST_SENSE 0x03
#define SCSI_ALLOW_MEDIUM_REMOVAL 0x1e
#define SCSI_READ_CAPACITY 0x25
@@ -48,11 +63,23 @@
#define SCSI_READ_10 0x28
#define SCSI_WRITE_10 0x2a
#define SCSI_START_STOP_UNIT 0x1b
+#define SCSI_REPORT_LUNS 0xa0
#define SCSI_STATUS_GOOD 0x00
#define SCSI_STATUS_FAIL 0x01
#define SCSI_STATUS_CHECK_CONDITION 0x02
+#define SENSE_NOT_READY 0x02
+#define SENSE_MEDIUM_ERROR 0x03
+#define SENSE_ILLEGAL_REQUEST 0x05
+#define SENSE_UNIT_ATTENTION 0x06
+
+#define ASC_MEDIUM_NOT_PRESENT 0x3a
+#define ASC_INVALID_FIELD_IN_CBD 0x24
+#define ASC_LBA_OUT_OF_RANGE 0x21
+#define ASC_WRITE_ERROR 0x0C
+#define ASC_READ_ERROR 0x11
+
#define SCSI_FORMAT_CAPACITY_FORMATTED_MEDIA 0x02000000
@@ -69,6 +96,15 @@ struct inquiry_data {
unsigned char ProductRevisionLevel[4];
} __attribute__ ((packed));
+struct report_lun_data {
+ unsigned int lun_list_length;
+ unsigned int reserved1;
+ unsigned char lun0[8];
+#ifdef HAVE_HOTSWAP
+ unsigned char lun1[8];
+#endif
+} __attribute__ ((packed));
+
struct sense_data {
unsigned char ResponseCode;
unsigned char Obsolete;
@@ -83,6 +119,21 @@ struct sense_data {
unsigned short SenseKeySpecific;
} __attribute__ ((packed));
+struct mode_sense_header_10 {
+ unsigned short mode_data_length;
+ unsigned char medium_type;
+ unsigned char device_specific;
+ unsigned char reserved1[2];
+ unsigned short block_descriptor_length;
+} __attribute__ ((packed));
+
+struct mode_sense_header_6 {
+ unsigned char mode_data_length;
+ unsigned char medium_type;
+ unsigned char device_specific;
+ unsigned char block_descriptor_length;
+} __attribute__ ((packed));
+
struct command_block_wrapper {
unsigned int signature;
unsigned int tag;
@@ -111,93 +162,195 @@ struct format_capacity {
unsigned int block_size;
} __attribute__ ((packed));
-/* the ARC USB controller can at most buffer 16KB unaligned data */
-static unsigned char _transfer_buffer[16384*8] __attribute((aligned (4096)));
+static unsigned char _transfer_buffer[2*BUFFER_SIZE] __attribute((aligned (4096)));
static unsigned char* transfer_buffer;
-static struct inquiry_data _inquiry CACHEALIGN_ATTR;
+
static struct inquiry_data* inquiry;
-static struct capacity _capacity_data CACHEALIGN_ATTR;
+static unsigned char __inquiry[CACHEALIGN_UP(sizeof(struct inquiry_data))] CACHEALIGN_ATTR;
+
static struct capacity* capacity_data;
-static struct format_capacity _format_capacity_data CACHEALIGN_ATTR;
+static unsigned char __capacity_data[CACHEALIGN_UP(sizeof(struct capacity))] CACHEALIGN_ATTR;
+
static struct format_capacity* format_capacity_data;
-static struct sense_data _sense_data CACHEALIGN_ATTR;
+static unsigned char __format_capacity_data[CACHEALIGN_UP(sizeof(struct format_capacity))] CACHEALIGN_ATTR;
+
static struct sense_data *sense_data;
+static unsigned char __sense_data[CACHEALIGN_UP(sizeof(struct sense_data))] CACHEALIGN_ATTR;
+
+static struct mode_sense_header_6 *mode_sense_data_6;
+static unsigned char __mode_sense_data_6[CACHEALIGN_UP(sizeof(struct mode_sense_header_6))] CACHEALIGN_ATTR;
+
+static struct mode_sense_header_10 *mode_sense_data_10;
+static unsigned char __mode_sense_data_10[CACHEALIGN_UP(sizeof(struct mode_sense_header_10))] CACHEALIGN_ATTR;
+
+static struct report_lun_data *lun_data;
+static unsigned char __lun_data[CACHEALIGN_UP(sizeof(struct report_lun_data))] CACHEALIGN_ATTR;
+
+static struct command_status_wrapper* csw;
+static unsigned char __csw[CACHEALIGN_UP(sizeof(struct command_status_wrapper))] CACHEALIGN_ATTR;
+
+static char *max_lun;
+static unsigned char __max_lun[CACHEALIGN_UP(1)] CACHEALIGN_ATTR;
static struct {
unsigned int sector;
unsigned int count;
unsigned int tag;
unsigned int lun;
+ unsigned char *data[2];
+ unsigned char data_select;
+ unsigned int last_result;
} current_cmd;
+static struct {
+ unsigned char sense_key;
+ unsigned char information;
+ unsigned char asc;
+} cur_sense_data;
+
static void handle_scsi(struct command_block_wrapper* cbw);
-static void send_csw(unsigned int tag, int status);
+static void send_csw(int status);
+static void send_command_result(void *data,int size);
+static void send_block_data(void *data,int size);
+static void receive_block_data(void *data,int size);
static void identify2inquiry(int lun);
+static void send_and_read_next(void);
static enum {
- IDLE,
- SENDING,
- RECEIVING
-} state = IDLE;
+ WAITING_FOR_COMMAND,
+ SENDING_BLOCKS,
+ SENDING_RESULT,
+ RECEIVING_BLOCKS,
+ SENDING_CSW
+} state = WAITING_FOR_COMMAND;
/* called by usb_code_init() */
void usb_storage_init(void)
{
- inquiry = (void*)UNCACHED_ADDR(&_inquiry);
transfer_buffer = (void*)UNCACHED_ADDR(&_transfer_buffer);
- capacity_data = (void*)UNCACHED_ADDR(&_capacity_data);
- format_capacity_data = (void*)UNCACHED_ADDR(&_format_capacity_data);
- sense_data = (void*)UNCACHED_ADDR(&_sense_data);
- state = IDLE;
+ inquiry = (void*)UNCACHED_ADDR(&__inquiry);
+ capacity_data = (void*)UNCACHED_ADDR(&__capacity_data);
+ format_capacity_data = (void*)UNCACHED_ADDR(&__format_capacity_data);
+ sense_data = (void*)UNCACHED_ADDR(&__sense_data);
+ mode_sense_data_6 = (void*)UNCACHED_ADDR(&__mode_sense_data_6);
+ mode_sense_data_10 = (void*)UNCACHED_ADDR(&__mode_sense_data_10);
+ lun_data = (void*)UNCACHED_ADDR(&__lun_data);
+ max_lun = (void*)UNCACHED_ADDR(&__max_lun);
+ csw = (void*)UNCACHED_ADDR(&__csw);
logf("usb_storage_init done");
}
/* called by usb_core_transfer_complete() */
-void usb_storage_transfer_complete(int endpoint)
+void usb_storage_transfer_complete(bool in,int status,int length)
{
struct command_block_wrapper* cbw = (void*)transfer_buffer;
- switch (endpoint) {
- case EP_RX:
- //logf("ums: %d bytes in", length);
- if(state == RECEIVING)
- {
- int receive_count=usb_drv_get_last_transfer_length();
- logf("scsi write %d %d", current_cmd.sector, current_cmd.count);
- if(usb_drv_get_last_transfer_status()==0)
- {
- if((unsigned int)receive_count!=(SECTOR_SIZE*current_cmd.count))
- {
- logf("%d >= %d",SECTOR_SIZE*current_cmd.count,receive_count);
- }
- ata_write_sectors(IF_MV2(current_cmd.lun,)
- current_cmd.sector, current_cmd.count,
- transfer_buffer);
- send_csw(current_cmd.tag, SCSI_STATUS_GOOD);
+ //logf("transfer result %X %d", status, length);
+ switch(state) {
+ case RECEIVING_BLOCKS:
+ if(in==true) {
+ logf("IN received in RECEIVING");
+ }
+ logf("scsi write %d %d", current_cmd.sector, current_cmd.count);
+ if(status==0) {
+ if((unsigned int)length!=(SECTOR_SIZE*current_cmd.count)
+ && (unsigned int)length!=BUFFER_SIZE) {
+ logf("unexpected length :%d",length);
}
- else
- {
- logf("Transfer failed %X",usb_drv_get_last_transfer_status());
- send_csw(current_cmd.tag, SCSI_STATUS_CHECK_CONDITION);
+
+ unsigned int next_sector = current_cmd.sector + (BUFFER_SIZE/SECTOR_SIZE);
+ unsigned int next_count = current_cmd.count - MIN(current_cmd.count,BUFFER_SIZE/SECTOR_SIZE);
+
+ if(next_count!=0) {
+ /* Ask the host to send more, to the other buffer */
+ receive_block_data(current_cmd.data[!current_cmd.data_select],
+ MIN(BUFFER_SIZE,next_count*SECTOR_SIZE));
}
+
+ /* Now write the data that just came in, while the host is sending the next bit */
+ int result = ata_write_sectors(IF_MV2(current_cmd.lun,)
+ current_cmd.sector, MIN(BUFFER_SIZE/SECTOR_SIZE,current_cmd.count),
+ current_cmd.data[current_cmd.data_select]);
+ if(result != 0) {
+ send_csw(SCSI_STATUS_CHECK_CONDITION);
+ cur_sense_data.sense_key=SENSE_MEDIUM_ERROR;
+ cur_sense_data.asc=ASC_WRITE_ERROR;
+ break;
+ }
+
+ if(next_count==0) {
+ send_csw(SCSI_STATUS_GOOD);
+ }
+
+ /* Switch buffers for the next one */
+ current_cmd.data_select=!current_cmd.data_select;
+
+ current_cmd.sector = next_sector;
+ current_cmd.count = next_count;
+
}
- else
- {
- state = SENDING;
- handle_scsi(cbw);
+ else {
+ logf("Transfer failed %X",status);
+ send_csw(SCSI_STATUS_CHECK_CONDITION);
+ /* TODO fill in cur_sense_data */
+ cur_sense_data.sense_key=0;
+ cur_sense_data.information=0;
+ cur_sense_data.asc=0;
}
-
break;
-
- case EP_TX:
- //logf("ums: out complete");
- if(state != IDLE)
- {
- /* re-prime endpoint. We only need room for commands */
- state = IDLE;
- usb_drv_recv(EP_RX, transfer_buffer, 1024);
+ case WAITING_FOR_COMMAND:
+ if(in==true) {
+ logf("IN received in WAITING_FOR_COMMAND");
+ }
+ //logf("command received");
+ handle_scsi(cbw);
+ break;
+ case SENDING_CSW:
+ if(in==false) {
+ logf("OUT received in SENDING_CSW");
+ }
+ //logf("csw sent, now go back to idle");
+ state = WAITING_FOR_COMMAND;
+ usb_drv_recv(EP_MASS_STORAGE, transfer_buffer, 1024);
+ break;
+ case SENDING_RESULT:
+ if(in==false) {
+ logf("OUT received in SENDING");
+ }
+ if(status==0) {
+ //logf("data sent, now send csw");
+ send_csw(SCSI_STATUS_GOOD);
+ }
+ else {
+ logf("Transfer failed %X",status);
+ send_csw(SCSI_STATUS_CHECK_CONDITION);
+ /* TODO fill in cur_sense_data */
+ cur_sense_data.sense_key=0;
+ cur_sense_data.information=0;
+ cur_sense_data.asc=0;
+ }
+ break;
+ case SENDING_BLOCKS:
+ if(in==false) {
+ logf("OUT received in SENDING");
+ }
+ if(status==0) {
+ if(current_cmd.count==0) {
+ //logf("data sent, now send csw");
+ send_csw(SCSI_STATUS_GOOD);
+ }
+ else {
+ send_and_read_next();
+ }
+ }
+ else {
+ logf("Transfer failed %X",status);
+ send_csw(SCSI_STATUS_CHECK_CONDITION);
+ /* TODO fill in cur_sense_data */
+ cur_sense_data.sense_key=0;
+ cur_sense_data.information=0;
+ cur_sense_data.asc=0;
}
-
break;
}
}
@@ -205,15 +358,24 @@ void usb_storage_transfer_complete(int endpoint)
/* called by usb_core_control_request() */
bool usb_storage_control_request(struct usb_ctrlrequest* req)
{
- /* note: interrupt context */
-
bool handled = false;
switch (req->bRequest) {
case USB_BULK_GET_MAX_LUN: {
- static char maxlun = NUM_VOLUMES - 1;
+#ifdef ONLY_EXPOSE_CARD_SLOT
+ *max_lun = 0;
+#else
+ *max_lun = NUM_VOLUMES - 1;
+#endif
+#ifdef HAVE_HOTSWAP
+ /* Workaround until we find out how to do removable devices properly */
+ tCardInfo* cinfo = card_get_info(1);
+ if(cinfo->initialized==0) {
+ *max_lun=0;
+ }
+#endif
logf("ums: getmaxlun");
- usb_drv_send(EP_CONTROL, UNCACHED_ADDR(&maxlun), 1);
+ usb_drv_send(EP_CONTROL, UNCACHED_ADDR(max_lun), 1);
usb_drv_recv(EP_CONTROL, NULL, 0); /* ack */
handled = true;
break;
@@ -221,8 +383,8 @@ bool usb_storage_control_request(struct usb_ctrlrequest* req)
case USB_BULK_RESET_REQUEST:
logf("ums: bulk reset");
- usb_drv_reset_endpoint(EP_RX, false);
- usb_drv_reset_endpoint(EP_TX, true);
+ usb_drv_reset_endpoint(EP_MASS_STORAGE, false);
+ usb_drv_reset_endpoint(EP_MASS_STORAGE, true);
usb_drv_send(EP_CONTROL, NULL, 0); /* ack */
handled = true;
break;
@@ -230,8 +392,8 @@ bool usb_storage_control_request(struct usb_ctrlrequest* req)
case USB_REQ_SET_CONFIGURATION:
logf("ums: set config");
/* prime rx endpoint. We only need room for commands */
- state = IDLE;
- usb_drv_recv(EP_RX, transfer_buffer, 1024);
+ state = WAITING_FOR_COMMAND;
+ usb_drv_recv(EP_MASS_STORAGE, transfer_buffer, 1024);
handled = true;
break;
}
@@ -239,6 +401,32 @@ bool usb_storage_control_request(struct usb_ctrlrequest* req)
return handled;
}
+static void send_and_read_next(void)
+{
+ if(current_cmd.last_result!=0) {
+ /* The last read failed. */
+ send_csw(SCSI_STATUS_CHECK_CONDITION);
+ cur_sense_data.sense_key=SENSE_MEDIUM_ERROR;
+ cur_sense_data.asc=ASC_READ_ERROR;
+ return;
+ }
+ send_block_data(current_cmd.data[current_cmd.data_select],
+ MIN(BUFFER_SIZE,current_cmd.count*SECTOR_SIZE));
+
+ /* Switch buffers for the next one */
+ current_cmd.data_select=!current_cmd.data_select;
+
+ current_cmd.sector+=(BUFFER_SIZE/SECTOR_SIZE);
+ current_cmd.count-=MIN(current_cmd.count,BUFFER_SIZE/SECTOR_SIZE);
+
+ if(current_cmd.count!=0){
+ /* already read the next bit, so we can send it out immediately when the
+ * current transfer completes. */
+ current_cmd.last_result = ata_read_sectors(IF_MV2(current_cmd.lun,) current_cmd.sector,
+ MIN(BUFFER_SIZE/SECTOR_SIZE,current_cmd.count),
+ current_cmd.data[current_cmd.data_select]);
+ }
+}
/****************************************************************************/
static void handle_scsi(struct command_block_wrapper* cbw)
@@ -246,141 +434,196 @@ static void handle_scsi(struct command_block_wrapper* cbw)
/* USB Mass Storage assumes LBA capability.
TODO: support 48-bit LBA */
- unsigned int sectors_per_transfer=0;
unsigned int length = cbw->data_transfer_length;
unsigned int block_size;
+ unsigned int block_count;
+ bool lun_present=true;
+#ifdef ONLY_EXPOSE_CARD_SLOT
+ unsigned char lun = cbw->lun+1;
+#else
unsigned char lun = cbw->lun;
+#endif
unsigned int block_size_mult = 1;
#ifdef HAVE_HOTSWAP
tCardInfo* cinfo = card_get_info(lun);
- block_size = cinfo->blocksize;
- if(cinfo->initialized==1)
- {
- sectors_per_transfer=(sizeof _transfer_buffer/ block_size);
+ if(cinfo->initialized==1) {
+ block_size = cinfo->blocksize;
+ block_count = cinfo->numblocks;
+ }
+ else {
+ lun_present=false;
+ block_size = 0;
+ block_count = 0;
}
#else
+ unsigned short* identify = ata_get_identify();
block_size = SECTOR_SIZE;
- sectors_per_transfer=(sizeof _transfer_buffer/ block_size);
+ block_count = (identify[61] << 16 | identify[60]);
#endif
#ifdef MAX_LOG_SECTOR_SIZE
block_size_mult = disk_sector_multiplier;
#endif
+ current_cmd.tag = cbw->tag;
+ current_cmd.lun = lun;
+
switch (cbw->command_block[0]) {
case SCSI_TEST_UNIT_READY:
logf("scsi test_unit_ready %d",lun);
#ifdef HAVE_HOTSWAP
if(cinfo->initialized==1)
- send_csw(cbw->tag, SCSI_STATUS_GOOD);
- else
- send_csw(cbw->tag, SCSI_STATUS_FAIL);
+ send_csw(SCSI_STATUS_GOOD);
+ else {
+ send_csw(SCSI_STATUS_FAIL);
+ cur_sense_data.sense_key=SENSE_NOT_READY;
+ cur_sense_data.asc=ASC_MEDIUM_NOT_PRESENT;
+ }
#else
- send_csw(cbw->tag, SCSI_STATUS_GOOD);
+ send_csw(SCSI_STATUS_GOOD);
#endif
break;
+ case SCSI_REPORT_LUNS: {
+ logf("scsi inquiry %d",lun);
+ int allocation_length=0;
+ allocation_length|=(cbw->command_block[6]<<24);
+ allocation_length|=(cbw->command_block[7]<<16);
+ allocation_length|=(cbw->command_block[8]<<8);
+ allocation_length|=(cbw->command_block[9]);
+ memset(lun_data,0,sizeof(struct report_lun_data));
+#ifdef HAVE_HOTSWAP
+ lun_data->lun_list_length=htobe32(16);
+ lun_data->lun1[1]=1;
+#else
+ lun_data->lun_list_length=htobe32(8);
+#endif
+ lun_data->lun0[1]=0;
+
+ send_command_result(lun_data, MIN(sizeof(struct report_lun_data), length));
+ break;
+ }
+
case SCSI_INQUIRY:
logf("scsi inquiry %d",lun);
identify2inquiry(lun);
length = MIN(length, cbw->command_block[4]);
- usb_drv_send(EP_TX, inquiry, MIN(sizeof _inquiry, length));
- send_csw(cbw->tag, SCSI_STATUS_GOOD);
+ send_command_result(inquiry, MIN(sizeof(struct inquiry_data), length));
break;
case SCSI_REQUEST_SENSE: {
- sense_data->ResponseCode=0x70;
- sense_data->filemark_eom_ili_sensekey=2;
- sense_data->Information=2;
+ sense_data->ResponseCode=0x70;/*current error*/
+ sense_data->filemark_eom_ili_sensekey=cur_sense_data.sense_key&0x0f;
+ sense_data->Information=cur_sense_data.information;
sense_data->AdditionalSenseLength=10;
sense_data->CommandSpecificInformation=0;
- sense_data->AdditionalSenseCode=0x3a;
+ sense_data->AdditionalSenseCode=cur_sense_data.asc;
sense_data->AdditionalSenseCodeQualifier=0;
sense_data->FieldReplaceableUnitCode=0;
sense_data->SKSV=0;
sense_data->SenseKeySpecific=0;
logf("scsi request_sense %d",lun);
- usb_drv_send(EP_TX, sense_data,
- sizeof(_sense_data));
- send_csw(cbw->tag, SCSI_STATUS_GOOD);
+ send_command_result(sense_data, sizeof(struct sense_data));
break;
}
- case SCSI_MODE_SENSE: {
- static unsigned char sense_data[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
- logf("scsi mode_sense %d",lun);
- usb_drv_send(EP_TX, UNCACHED_ADDR(&sense_data),
- MIN(sizeof sense_data, length));
- send_csw(cbw->tag, SCSI_STATUS_GOOD);
+ case SCSI_MODE_SENSE_10: {
+ /*unsigned char pc = (cbw->command_block[2] & 0xc0) >>6;*/
+ unsigned char page_code = cbw->command_block[2] & 0x3f;
+ logf("scsi mode_sense_10 %d %X",lun,page_code);
+ switch(page_code) {
+ case 0x3f:
+ default:
+ mode_sense_data_10->mode_data_length=0;
+ mode_sense_data_10->medium_type=0;
+ mode_sense_data_10->device_specific=0;
+ mode_sense_data_10->block_descriptor_length=0;
+ send_command_result(mode_sense_data_10,
+ MIN(sizeof(struct mode_sense_header_10), length));
+ break;
+#if 0
+ default:
+ send_csw(SCSI_STATUS_CHECK_CONDITION);
+ cur_sense_data.sense_key=SENSE_ILLEGAL_REQUEST;
+ cur_sense_data.asc=ASC_INVALID_FIELD_IN_CBD;
+ break;
+#endif
+ }
+ break;
+ }
+ case SCSI_MODE_SENSE_6: {
+ /*unsigned char pc = (cbw->command_block[2] & 0xc0) >>6;*/
+ unsigned char page_code = cbw->command_block[2] & 0x3f;
+ logf("scsi mode_sense_6 %d %X",lun,page_code);
+ switch(page_code) {
+ case 0x3f:
+ default:
+ /* All supported pages Since we support only one this is easy*/
+ mode_sense_data_6->mode_data_length=0;
+ mode_sense_data_6->medium_type=0;
+ mode_sense_data_6->device_specific=0;
+ mode_sense_data_6->block_descriptor_length=0;
+ send_command_result(mode_sense_data_6,
+ MIN(sizeof(struct mode_sense_header_6), length));
+ break;
+#if 0
+ default:
+ send_csw(SCSI_STATUS_CHECK_CONDITION);
+ cur_sense_data.sense_key=SENSE_ILLEGAL_REQUEST;
+ cur_sense_data.asc=ASC_INVALID_FIELD_IN_CBD;
+ break;
+#endif
+ }
break;
}
case SCSI_START_STOP_UNIT:
logf("scsi start_stop unit %d",lun);
- send_csw(cbw->tag, SCSI_STATUS_GOOD);
+ send_csw(SCSI_STATUS_GOOD);
break;
case SCSI_ALLOW_MEDIUM_REMOVAL:
logf("scsi allow_medium_removal %d",lun);
- send_csw(cbw->tag, SCSI_STATUS_GOOD);
+ /* TODO: use this to show the connect screen ? */
+ send_csw(SCSI_STATUS_GOOD);
break;
case SCSI_READ_FORMAT_CAPACITY: {
logf("scsi read_format_capacity %d",lun);
format_capacity_data->following_length=htobe32(8);
-#ifdef HAVE_HOTSWAP
/* Careful: "block count" actually means "number of last block" */
- if(cinfo->initialized==1)
- {
- format_capacity_data->block_count = htobe32(cinfo->numblocks - 1);
- format_capacity_data->block_size = htobe32(cinfo->blocksize);
- }
- else
- {
- format_capacity_data->block_count = htobe32(0);
- format_capacity_data->block_size = htobe32(0);
- }
-#else
- unsigned short* identify = ata_get_identify();
- /* Careful: "block count" actually means "number of last block" */
- format_capacity_data->block_count = htobe32((identify[61] << 16 | identify[60]) / block_size_mult - 1);
- format_capacity_data->block_size = htobe32(block_size * block_size_mult);
-#endif
+ format_capacity_data->block_count = htobe32(block_count/block_size_mult - 1);
+ format_capacity_data->block_size = htobe32(block_size*block_size_mult);
format_capacity_data->block_size |= SCSI_FORMAT_CAPACITY_FORMATTED_MEDIA;
- usb_drv_send(EP_TX, format_capacity_data,
- MIN(sizeof _format_capacity_data, length));
- send_csw(cbw->tag, SCSI_STATUS_GOOD);
+ send_command_result(format_capacity_data,
+ MIN(sizeof(struct format_capacity), length));
+
break;
}
case SCSI_READ_CAPACITY: {
logf("scsi read_capacity %d",lun);
-#ifdef HAVE_HOTSWAP
/* Careful: "block count" actually means "number of last block" */
- if(cinfo->initialized==1)
- {
- capacity_data->block_count = htobe32(cinfo->numblocks - 1);
- capacity_data->block_size = htobe32(cinfo->blocksize);
- }
- else
- {
- capacity_data->block_count = htobe32(0);
- capacity_data->block_size = htobe32(0);
- }
-#else
- unsigned short* identify = ata_get_identify();
- /* Careful : "block count" actually means the number of the last block */
- capacity_data->block_count = htobe32((identify[61] << 16 | identify[60]) / block_size_mult - 1);
- capacity_data->block_size = htobe32(block_size * block_size_mult);
-#endif
- usb_drv_send(EP_TX, capacity_data,
- MIN(sizeof _capacity_data, length));
- send_csw(cbw->tag, SCSI_STATUS_GOOD);
+ capacity_data->block_count = htobe32(block_count/block_size_mult - 1);
+ capacity_data->block_size = htobe32(block_size*block_size_mult);
+
+ send_command_result(capacity_data, MIN(sizeof(struct capacity), length));
break;
}
case SCSI_READ_10:
+ logf("scsi read10 %d",lun);
+ if(! lun_present) {
+ send_csw(SCSI_STATUS_CHECK_CONDITION);
+ cur_sense_data.sense_key=SENSE_NOT_READY;
+ cur_sense_data.asc=ASC_MEDIUM_NOT_PRESENT;
+ break;
+ }
+ trigger_cpu_boost();
+ current_cmd.data[0] = transfer_buffer;
+ current_cmd.data[1] = &transfer_buffer[BUFFER_SIZE];
+ current_cmd.data_select=0;
current_cmd.sector = block_size_mult *
(cbw->command_block[2] << 24 |
cbw->command_block[3] << 16 |
@@ -389,32 +632,35 @@ static void handle_scsi(struct command_block_wrapper* cbw)
current_cmd.count = block_size_mult *
(cbw->command_block[7] << 16 |
cbw->command_block[8]);
- current_cmd.tag = cbw->tag;
- current_cmd.lun = cbw->lun;
- //logf("scsi read %d %d", current_cmd.sector, current_cmd.count);
+ logf("scsi read %d %d", current_cmd.sector, current_cmd.count);
- //logf("Asked for %d sectors",current_cmd.count);
- if(current_cmd.count > sectors_per_transfer)
- {
- current_cmd.count = sectors_per_transfer;
- }
- //logf("Sending %d sectors",current_cmd.count);
-
- if(current_cmd.count*block_size > sizeof(_transfer_buffer)) {
- send_csw(current_cmd.tag, SCSI_STATUS_CHECK_CONDITION);
+ if((current_cmd.sector + current_cmd.count) * block_size_mult > block_count) {
+ send_csw(SCSI_STATUS_CHECK_CONDITION);
+ cur_sense_data.sense_key=SENSE_ILLEGAL_REQUEST;
+ cur_sense_data.asc=ASC_LBA_OUT_OF_RANGE;
}
else {
- ata_read_sectors(IF_MV2(lun,) current_cmd.sector,
- current_cmd.count, transfer_buffer);
- usb_drv_send(EP_TX, transfer_buffer,
- current_cmd.count*block_size);
- send_csw(current_cmd.tag, SCSI_STATUS_GOOD);
+ /* TODO: any way to do this nonblocking ? */
+ current_cmd.last_result = ata_read_sectors(IF_MV2(current_cmd.lun,) current_cmd.sector,
+ MIN(BUFFER_SIZE/SECTOR_SIZE,current_cmd.count),
+ current_cmd.data[current_cmd.data_select]);
+ send_and_read_next();
}
break;
case SCSI_WRITE_10:
- //logf("scsi write10");
+ logf("scsi write10 %d",lun);
+ if(! lun_present) {
+ send_csw(SCSI_STATUS_CHECK_CONDITION);
+ cur_sense_data.sense_key=SENSE_NOT_READY;
+ cur_sense_data.asc=ASC_MEDIUM_NOT_PRESENT;
+ break;
+ }
+ trigger_cpu_boost();
+ current_cmd.data[0] = transfer_buffer;
+ current_cmd.data[1] = &transfer_buffer[BUFFER_SIZE];
+ current_cmd.data_select=0;
current_cmd.sector = block_size_mult *
(cbw->command_block[2] << 24 |
cbw->command_block[3] << 16 |
@@ -423,53 +669,74 @@ static void handle_scsi(struct command_block_wrapper* cbw)
current_cmd.count = block_size_mult *
(cbw->command_block[7] << 16 |
cbw->command_block[8]);
- current_cmd.tag = cbw->tag;
- current_cmd.lun = cbw->lun;
/* expect data */
- if(current_cmd.count*block_size > sizeof(_transfer_buffer)) {
- send_csw(current_cmd.tag, SCSI_STATUS_CHECK_CONDITION);
+ if((current_cmd.sector + current_cmd.count) * block_size_mult > block_count) {
+ send_csw(SCSI_STATUS_CHECK_CONDITION);
+ cur_sense_data.sense_key=SENSE_ILLEGAL_REQUEST;
+ cur_sense_data.asc=ASC_LBA_OUT_OF_RANGE;
}
else {
- usb_drv_recv(EP_RX, transfer_buffer,
- current_cmd.count*block_size);
- state = RECEIVING;
+ receive_block_data(current_cmd.data[0],
+ MIN(BUFFER_SIZE,current_cmd.count*SECTOR_SIZE));
}
break;
default:
logf("scsi unknown cmd %x",cbw->command_block[0x0]);
- usb_drv_stall(EP_TX, true);
- send_csw(current_cmd.tag, SCSI_STATUS_GOOD);
+ usb_drv_stall(EP_MASS_STORAGE, true,true);
+ send_csw(SCSI_STATUS_GOOD);
break;
}
}
-static void send_csw(unsigned int tag, int status)
+static void send_block_data(void *data,int size)
+{
+ usb_drv_send_nonblocking(EP_MASS_STORAGE, data,size);
+ state = SENDING_BLOCKS;
+}
+
+static void send_command_result(void *data,int size)
+{
+ usb_drv_send_nonblocking(EP_MASS_STORAGE, data,size);
+ state = SENDING_RESULT;
+}
+
+static void receive_block_data(void *data,int size)
{
- static struct command_status_wrapper _csw;
- struct command_status_wrapper* csw = UNCACHED_ADDR(&_csw);
+ usb_drv_recv(EP_MASS_STORAGE, data, size);
+ state = RECEIVING_BLOCKS;
+}
+
+static void send_csw(int status)
+{
+ cancel_cpu_boost();
csw->signature = CSW_SIGNATURE;
- csw->tag = tag;
+ csw->tag = current_cmd.tag;
csw->data_residue = 0;
csw->status = status;
- //logf("csw %x %x", csw->tag, csw->signature);
- usb_drv_send(EP_TX, csw, sizeof _csw);
+ usb_drv_send_nonblocking(EP_MASS_STORAGE, csw, sizeof(struct command_status_wrapper));
+ state = SENDING_CSW;
+ logf("CSW: %X",status);
+
+ if(status == SCSI_STATUS_GOOD) {
+ cur_sense_data.sense_key=0;
+ cur_sense_data.information=0;
+ cur_sense_data.asc=0;
+ }
}
/* convert ATA IDENTIFY to SCSI INQUIRY */
static void identify2inquiry(int lun)
{
#ifdef HAVE_FLASH_STORAGE
- if(lun==0)
- {
+ if(lun==0) {
memcpy(&inquiry->VendorId,"Rockbox ",8);
memcpy(&inquiry->ProductId,"Internal Storage",16);
memcpy(&inquiry->ProductRevisionLevel,"0.00",4);
}
- else
- {
+ else {
memcpy(&inquiry->VendorId,"Rockbox ",8);
memcpy(&inquiry->ProductId,"SD Card Slot ",16);
memcpy(&inquiry->ProductRevisionLevel,"0.00",4);
@@ -480,7 +747,7 @@ static void identify2inquiry(int lun)
unsigned short* src;
unsigned short* identify = ata_get_identify();
(void)lun;
- memset(inquiry, 0, sizeof _inquiry);
+ memset(inquiry, 0, sizeof(struct inquiry_data));
if (identify[82] & 4)
inquiry->DeviceTypeModifier = DEVICE_REMOVABLE;
@@ -501,8 +768,8 @@ static void identify2inquiry(int lun)
inquiry->DeviceType = DIRECT_ACCESS_DEVICE;
inquiry->AdditionalLength = 0x1f;
- inquiry->Versions = 3; /* ANSI SCSI level 2 */
- inquiry->Format = 3; /* ANSI SCSI level 2 INQUIRY format */
+ inquiry->Versions = 4; /* SPC-2 */
+ inquiry->Format = 2; /* SPC-2/3 inquiry format */
#ifdef HAVE_HOTSWAP
inquiry->DeviceTypeModifier = DEVICE_REMOVABLE;
@@ -510,3 +777,4 @@ static void identify2inquiry(int lun)
}
+#endif /* USB_STORAGE */