2007-11-22 20:51:00 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* __________ __ ___.
|
|
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
* \/ \/ \/ \/ \/
|
2008-04-18 16:42:50 +00:00
|
|
|
* $Id$
|
2007-11-22 20:51:00 +00:00
|
|
|
*
|
2008-05-05 10:32:46 +00:00
|
|
|
* Copyright (C) 2007 by Björn Stenberg
|
2007-11-22 20:51:00 +00:00
|
|
|
*
|
2008-06-28 18:10:04 +00:00
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
2007-11-22 20:51:00 +00:00
|
|
|
*
|
|
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
|
|
* KIND, either express or implied.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
#include "string.h"
|
|
|
|
#include "system.h"
|
|
|
|
#include "usb_core.h"
|
|
|
|
#include "usb_drv.h"
|
2008-10-03 22:49:36 +00:00
|
|
|
//#define LOGF_ENABLE
|
2007-11-22 20:51:00 +00:00
|
|
|
#include "logf.h"
|
2008-11-01 16:14:28 +00:00
|
|
|
#include "storage.h"
|
2007-11-22 20:51:00 +00:00
|
|
|
#include "hotswap.h"
|
2008-02-11 14:26:25 +00:00
|
|
|
#include "disk.h"
|
2008-02-27 20:25:32 +00:00
|
|
|
/* Needed to get at the audio buffer */
|
|
|
|
#include "audio.h"
|
2008-05-03 07:10:21 +00:00
|
|
|
#include "usb_storage.h"
|
2007-11-22 20:51:00 +00:00
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
|
2008-02-20 22:54:26 +00:00
|
|
|
#ifdef USB_STORAGE
|
|
|
|
|
2008-07-09 19:56:09 +00:00
|
|
|
/* The SD card driver on Sansa c200 and e200 can cause write corruption,
|
|
|
|
* often triggered by simultaneous USB activity. This can be largely avoided
|
2008-11-01 16:14:28 +00:00
|
|
|
* by not overlapping storage_write_sector() with USB transfers. This does reduce
|
2008-07-09 19:56:09 +00:00
|
|
|
* write performance, so we only do it for the affected DAPs
|
|
|
|
*/
|
2008-10-31 21:25:04 +00:00
|
|
|
#if (CONFIG_STORAGE & STORAGE_SD)
|
2008-07-09 19:56:09 +00:00
|
|
|
#define SERIALIZE_WRITES
|
|
|
|
#endif
|
2008-02-20 22:54:26 +00:00
|
|
|
/* 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
|
|
|
|
|
2008-10-20 21:49:59 +00:00
|
|
|
#ifdef USB_USE_RAMDISK
|
|
|
|
#define RAMDISK_SIZE 2048
|
|
|
|
#endif
|
|
|
|
|
2007-11-22 20:51:00 +00:00
|
|
|
#define SECTOR_SIZE 512
|
|
|
|
|
2008-02-20 22:54:26 +00:00
|
|
|
/* 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
|
|
|
|
|
2007-11-22 20:51:00 +00:00
|
|
|
/* bulk-only class specific requests */
|
|
|
|
#define USB_BULK_RESET_REQUEST 0xff
|
|
|
|
#define USB_BULK_GET_MAX_LUN 0xfe
|
|
|
|
|
|
|
|
#define DIRECT_ACCESS_DEVICE 0x00 /* disks */
|
|
|
|
#define DEVICE_REMOVABLE 0x80
|
|
|
|
|
|
|
|
#define CBW_SIGNATURE 0x43425355
|
|
|
|
#define CSW_SIGNATURE 0x53425355
|
|
|
|
|
|
|
|
#define SCSI_TEST_UNIT_READY 0x00
|
|
|
|
#define SCSI_INQUIRY 0x12
|
2008-02-20 22:54:26 +00:00
|
|
|
#define SCSI_MODE_SENSE_6 0x1a
|
|
|
|
#define SCSI_MODE_SENSE_10 0x5a
|
2008-02-11 14:26:25 +00:00
|
|
|
#define SCSI_REQUEST_SENSE 0x03
|
2007-11-22 20:51:00 +00:00
|
|
|
#define SCSI_ALLOW_MEDIUM_REMOVAL 0x1e
|
|
|
|
#define SCSI_READ_CAPACITY 0x25
|
2008-02-11 14:26:25 +00:00
|
|
|
#define SCSI_READ_FORMAT_CAPACITY 0x23
|
2007-11-22 20:51:00 +00:00
|
|
|
#define SCSI_READ_10 0x28
|
|
|
|
#define SCSI_WRITE_10 0x2a
|
2008-02-11 14:26:25 +00:00
|
|
|
#define SCSI_START_STOP_UNIT 0x1b
|
2008-02-20 22:54:26 +00:00
|
|
|
#define SCSI_REPORT_LUNS 0xa0
|
2007-11-22 20:51:00 +00:00
|
|
|
|
2008-02-28 22:35:23 +00:00
|
|
|
#define UMS_STATUS_GOOD 0x00
|
|
|
|
#define UMS_STATUS_FAIL 0x01
|
2007-11-22 20:51:00 +00:00
|
|
|
|
2008-02-20 22:54:26 +00:00
|
|
|
#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
|
2008-03-02 00:15:02 +00:00
|
|
|
#define ASC_NOT_READY 0x04
|
|
|
|
#define ASCQ_BECOMING_READY 0x01
|
2008-02-20 22:54:26 +00:00
|
|
|
|
2008-02-11 14:26:25 +00:00
|
|
|
#define SCSI_FORMAT_CAPACITY_FORMATTED_MEDIA 0x02000000
|
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
/* storage interface */
|
|
|
|
|
|
|
|
#define USB_SC_SCSI 0x06 /* Transparent */
|
|
|
|
#define USB_PROT_BULK 0x50 /* bulk only */
|
|
|
|
|
|
|
|
static struct usb_interface_descriptor __attribute__((aligned(2)))
|
|
|
|
interface_descriptor =
|
|
|
|
{
|
|
|
|
.bLength = sizeof(struct usb_interface_descriptor),
|
|
|
|
.bDescriptorType = USB_DT_INTERFACE,
|
|
|
|
.bInterfaceNumber = 0,
|
|
|
|
.bAlternateSetting = 0,
|
|
|
|
.bNumEndpoints = 2,
|
|
|
|
.bInterfaceClass = USB_CLASS_MASS_STORAGE,
|
|
|
|
.bInterfaceSubClass = USB_SC_SCSI,
|
|
|
|
.bInterfaceProtocol = USB_PROT_BULK,
|
|
|
|
.iInterface = 0
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct usb_endpoint_descriptor __attribute__((aligned(2)))
|
|
|
|
endpoint_descriptor =
|
|
|
|
{
|
|
|
|
.bLength = sizeof(struct usb_endpoint_descriptor),
|
|
|
|
.bDescriptorType = USB_DT_ENDPOINT,
|
|
|
|
.bEndpointAddress = 0,
|
|
|
|
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
|
|
|
.wMaxPacketSize = 0,
|
|
|
|
.bInterval = 0
|
|
|
|
};
|
2007-11-22 20:51:00 +00:00
|
|
|
|
|
|
|
struct inquiry_data {
|
|
|
|
unsigned char DeviceType;
|
|
|
|
unsigned char DeviceTypeModifier;
|
|
|
|
unsigned char Versions;
|
|
|
|
unsigned char Format;
|
|
|
|
unsigned char AdditionalLength;
|
|
|
|
unsigned char Reserved[2];
|
|
|
|
unsigned char Capability;
|
|
|
|
unsigned char VendorId[8];
|
|
|
|
unsigned char ProductId[16];
|
|
|
|
unsigned char ProductRevisionLevel[4];
|
|
|
|
} __attribute__ ((packed));
|
|
|
|
|
2008-02-20 22:54:26 +00:00
|
|
|
struct report_lun_data {
|
|
|
|
unsigned int lun_list_length;
|
|
|
|
unsigned int reserved1;
|
2008-11-01 16:14:28 +00:00
|
|
|
// TODO this should be cleaned up with the VOLUMES vs DRIVES mess
|
|
|
|
unsigned char luns[NUM_VOLUMES][8];
|
2008-02-20 22:54:26 +00:00
|
|
|
} __attribute__ ((packed));
|
|
|
|
|
2008-02-11 14:26:25 +00:00
|
|
|
struct sense_data {
|
|
|
|
unsigned char ResponseCode;
|
|
|
|
unsigned char Obsolete;
|
2008-03-06 21:25:09 +00:00
|
|
|
unsigned char fei_sensekey;
|
2008-02-11 14:26:25 +00:00
|
|
|
unsigned int Information;
|
|
|
|
unsigned char AdditionalSenseLength;
|
|
|
|
unsigned int CommandSpecificInformation;
|
|
|
|
unsigned char AdditionalSenseCode;
|
|
|
|
unsigned char AdditionalSenseCodeQualifier;
|
|
|
|
unsigned char FieldReplaceableUnitCode;
|
|
|
|
unsigned char SKSV;
|
|
|
|
unsigned short SenseKeySpecific;
|
|
|
|
} __attribute__ ((packed));
|
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
struct mode_sense_bdesc_longlba {
|
|
|
|
unsigned char num_blocks[8];
|
2008-02-29 19:25:14 +00:00
|
|
|
unsigned char reserved[4];
|
|
|
|
unsigned char block_size[4];
|
|
|
|
} __attribute__ ((packed));
|
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
struct mode_sense_bdesc_shortlba {
|
2008-02-29 19:25:14 +00:00
|
|
|
unsigned char density_code;
|
2008-03-06 21:25:09 +00:00
|
|
|
unsigned char num_blocks[3];
|
2008-02-29 19:25:14 +00:00
|
|
|
unsigned char reserved;
|
|
|
|
unsigned char block_size[3];
|
|
|
|
} __attribute__ ((packed));
|
|
|
|
|
|
|
|
struct mode_sense_data_10 {
|
2008-02-20 22:54:26 +00:00
|
|
|
unsigned short mode_data_length;
|
|
|
|
unsigned char medium_type;
|
|
|
|
unsigned char device_specific;
|
2008-02-29 19:25:14 +00:00
|
|
|
unsigned char longlba;
|
|
|
|
unsigned char reserved;
|
2008-02-20 22:54:26 +00:00
|
|
|
unsigned short block_descriptor_length;
|
2008-03-06 21:25:09 +00:00
|
|
|
struct mode_sense_bdesc_longlba block_descriptor;
|
2008-02-20 22:54:26 +00:00
|
|
|
} __attribute__ ((packed));
|
|
|
|
|
2008-02-29 19:25:14 +00:00
|
|
|
struct mode_sense_data_6 {
|
2008-02-20 22:54:26 +00:00
|
|
|
unsigned char mode_data_length;
|
|
|
|
unsigned char medium_type;
|
|
|
|
unsigned char device_specific;
|
|
|
|
unsigned char block_descriptor_length;
|
2008-03-06 21:25:09 +00:00
|
|
|
struct mode_sense_bdesc_shortlba block_descriptor;
|
2008-02-20 22:54:26 +00:00
|
|
|
} __attribute__ ((packed));
|
|
|
|
|
2007-11-22 20:51:00 +00:00
|
|
|
struct command_block_wrapper {
|
|
|
|
unsigned int signature;
|
|
|
|
unsigned int tag;
|
|
|
|
unsigned int data_transfer_length;
|
|
|
|
unsigned char flags;
|
|
|
|
unsigned char lun;
|
|
|
|
unsigned char command_length;
|
|
|
|
unsigned char command_block[16];
|
|
|
|
} __attribute__ ((packed));
|
|
|
|
|
|
|
|
struct command_status_wrapper {
|
|
|
|
unsigned int signature;
|
|
|
|
unsigned int tag;
|
|
|
|
unsigned int data_residue;
|
|
|
|
unsigned char status;
|
|
|
|
} __attribute__ ((packed));
|
|
|
|
|
|
|
|
struct capacity {
|
|
|
|
unsigned int block_count;
|
|
|
|
unsigned int block_size;
|
|
|
|
} __attribute__ ((packed));
|
|
|
|
|
2008-02-11 14:26:25 +00:00
|
|
|
struct format_capacity {
|
|
|
|
unsigned int following_length;
|
|
|
|
unsigned int block_count;
|
|
|
|
unsigned int block_size;
|
|
|
|
} __attribute__ ((packed));
|
|
|
|
|
2008-02-29 21:10:31 +00:00
|
|
|
|
|
|
|
static union {
|
|
|
|
unsigned char* transfer_buffer;
|
|
|
|
struct inquiry_data* inquiry;
|
|
|
|
struct capacity* capacity_data;
|
|
|
|
struct format_capacity* format_capacity_data;
|
|
|
|
struct sense_data *sense_data;
|
2008-03-06 21:25:09 +00:00
|
|
|
struct mode_sense_data_6 *ms_data_6;
|
|
|
|
struct mode_sense_data_10 *ms_data_10;
|
2008-02-29 21:10:31 +00:00
|
|
|
struct report_lun_data *lun_data;
|
|
|
|
struct command_status_wrapper* csw;
|
|
|
|
char *max_lun;
|
|
|
|
} tb;
|
2007-11-22 20:51:00 +00:00
|
|
|
|
|
|
|
static struct {
|
|
|
|
unsigned int sector;
|
|
|
|
unsigned int count;
|
|
|
|
unsigned int tag;
|
2008-02-11 14:26:25 +00:00
|
|
|
unsigned int lun;
|
2008-02-20 22:54:26 +00:00
|
|
|
unsigned char *data[2];
|
|
|
|
unsigned char data_select;
|
|
|
|
unsigned int last_result;
|
2008-03-06 21:25:09 +00:00
|
|
|
} cur_cmd;
|
2007-11-22 20:51:00 +00:00
|
|
|
|
2008-02-20 22:54:26 +00:00
|
|
|
static struct {
|
|
|
|
unsigned char sense_key;
|
|
|
|
unsigned char information;
|
|
|
|
unsigned char asc;
|
2008-03-02 00:15:02 +00:00
|
|
|
unsigned char ascq;
|
2008-02-20 22:54:26 +00:00
|
|
|
} cur_sense_data;
|
|
|
|
|
2008-02-11 14:26:25 +00:00
|
|
|
static void handle_scsi(struct command_block_wrapper* cbw);
|
2008-02-20 22:54:26 +00:00
|
|
|
static void send_csw(int status);
|
|
|
|
static void send_command_result(void *data,int size);
|
2008-04-01 20:36:51 +00:00
|
|
|
static void send_command_failed_result(void);
|
2008-02-20 22:54:26 +00:00
|
|
|
static void send_block_data(void *data,int size);
|
|
|
|
static void receive_block_data(void *data,int size);
|
2008-11-01 16:14:28 +00:00
|
|
|
static void fill_inquiry(IF_MV_NONVOID(int lun));
|
2008-02-20 22:54:26 +00:00
|
|
|
static void send_and_read_next(void);
|
2008-03-03 22:53:25 +00:00
|
|
|
static bool ejected[NUM_VOLUMES];
|
2007-11-22 20:51:00 +00:00
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
static int usb_interface;
|
2008-10-03 22:43:16 +00:00
|
|
|
static int ep_in, ep_out;
|
2008-03-06 21:25:09 +00:00
|
|
|
|
2008-10-20 21:49:59 +00:00
|
|
|
#ifdef USB_USE_RAMDISK
|
|
|
|
static unsigned char* ramdisk_buffer;
|
|
|
|
#endif
|
|
|
|
|
2007-11-22 20:51:00 +00:00
|
|
|
static enum {
|
2008-02-20 22:54:26 +00:00
|
|
|
WAITING_FOR_COMMAND,
|
|
|
|
SENDING_BLOCKS,
|
|
|
|
SENDING_RESULT,
|
2008-04-01 20:36:51 +00:00
|
|
|
SENDING_FAILED_RESULT,
|
2008-02-20 22:54:26 +00:00
|
|
|
RECEIVING_BLOCKS,
|
|
|
|
SENDING_CSW
|
|
|
|
} state = WAITING_FOR_COMMAND;
|
2007-11-22 20:51:00 +00:00
|
|
|
|
2008-03-10 21:28:44 +00:00
|
|
|
static bool check_disk_present(IF_MV_NONVOID(int volume))
|
2008-03-10 20:55:24 +00:00
|
|
|
{
|
2008-10-20 21:49:59 +00:00
|
|
|
#ifdef USB_USE_RAMDISK
|
|
|
|
return true;
|
|
|
|
#else
|
2008-03-10 20:55:24 +00:00
|
|
|
unsigned char sector[512];
|
2008-11-01 16:14:28 +00:00
|
|
|
return storage_read_sectors(IF_MV2(volume,)0,1,sector) == 0;
|
2008-10-20 21:49:59 +00:00
|
|
|
#endif
|
2008-03-10 20:55:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void try_release_ata(void)
|
|
|
|
{
|
|
|
|
/* Check if there is a connected drive left. If not,
|
|
|
|
release excusive access */
|
|
|
|
bool canrelease=true;
|
|
|
|
int i;
|
|
|
|
for(i=0;i<NUM_VOLUMES;i++) {
|
|
|
|
if(ejected[i]==false){
|
|
|
|
canrelease=false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(canrelease) {
|
|
|
|
logf("scsi release ata");
|
|
|
|
usb_release_exclusive_ata();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_HOTSWAP
|
|
|
|
void usb_storage_notify_hotswap(int volume,bool inserted)
|
|
|
|
{
|
|
|
|
logf("notify %d",inserted);
|
2008-03-10 21:16:31 +00:00
|
|
|
if(inserted && check_disk_present(IF_MV(volume))) {
|
2008-03-10 20:55:24 +00:00
|
|
|
ejected[volume] = false;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ejected[volume] = true;
|
|
|
|
try_release_ata();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void usb_storage_reconnect(void)
|
|
|
|
{
|
|
|
|
int i;
|
2008-04-13 17:16:50 +00:00
|
|
|
if(usb_core_driver_enabled(USB_DRIVER_MASS_STORAGE)
|
|
|
|
&& usb_inserted()) {
|
|
|
|
for(i=0;i<NUM_VOLUMES;i++)
|
|
|
|
ejected[i] = !check_disk_present(IF_MV(i));
|
2008-10-03 22:43:16 +00:00
|
|
|
logf("%s", __func__);
|
2008-04-13 17:16:50 +00:00
|
|
|
usb_request_exclusive_ata();
|
|
|
|
}
|
2008-03-10 20:55:24 +00:00
|
|
|
}
|
|
|
|
|
2007-11-22 20:51:00 +00:00
|
|
|
/* called by usb_code_init() */
|
|
|
|
void usb_storage_init(void)
|
|
|
|
{
|
2008-03-03 22:53:25 +00:00
|
|
|
int i;
|
2008-03-10 20:55:24 +00:00
|
|
|
for(i=0;i<NUM_VOLUMES;i++) {
|
2008-03-10 21:16:31 +00:00
|
|
|
ejected[i] = !check_disk_present(IF_MV(i));
|
2008-03-10 20:55:24 +00:00
|
|
|
}
|
2008-02-11 14:26:25 +00:00
|
|
|
logf("usb_storage_init done");
|
2007-11-22 20:51:00 +00:00
|
|
|
}
|
|
|
|
|
2008-10-03 22:43:16 +00:00
|
|
|
int usb_storage_request_endpoints(struct usb_class_driver *drv)
|
2008-03-06 21:25:09 +00:00
|
|
|
{
|
2008-10-03 22:43:16 +00:00
|
|
|
ep_in = usb_core_request_endpoint(USB_DIR_IN, drv);
|
|
|
|
|
|
|
|
if (ep_in < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ep_out = usb_core_request_endpoint(USB_DIR_OUT, drv);
|
|
|
|
|
|
|
|
if (ep_out < 0) {
|
|
|
|
usb_core_release_endpoint(ep_in);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2008-04-26 19:02:16 +00:00
|
|
|
}
|
2008-10-03 22:43:16 +00:00
|
|
|
|
2008-04-26 19:02:16 +00:00
|
|
|
int usb_storage_set_first_interface(int interface)
|
|
|
|
{
|
|
|
|
usb_interface = interface;
|
|
|
|
return interface + 1;
|
|
|
|
}
|
2008-03-06 21:25:09 +00:00
|
|
|
|
2008-04-26 19:02:16 +00:00
|
|
|
int usb_storage_get_config_descriptor(unsigned char *dest,int max_packet_size)
|
|
|
|
{
|
|
|
|
endpoint_descriptor.wMaxPacketSize=max_packet_size;
|
|
|
|
interface_descriptor.bInterfaceNumber=usb_interface;
|
2008-03-06 21:25:09 +00:00
|
|
|
|
|
|
|
memcpy(dest,&interface_descriptor,
|
|
|
|
sizeof(struct usb_interface_descriptor));
|
|
|
|
dest+=sizeof(struct usb_interface_descriptor);
|
|
|
|
|
2008-10-03 22:43:16 +00:00
|
|
|
endpoint_descriptor.bEndpointAddress = ep_in;
|
2008-03-06 21:25:09 +00:00
|
|
|
memcpy(dest,&endpoint_descriptor,
|
|
|
|
sizeof(struct usb_endpoint_descriptor));
|
|
|
|
dest+=sizeof(struct usb_endpoint_descriptor);
|
|
|
|
|
2008-10-03 22:43:16 +00:00
|
|
|
endpoint_descriptor.bEndpointAddress = ep_out;
|
2008-03-06 21:25:09 +00:00
|
|
|
memcpy(dest,&endpoint_descriptor,
|
|
|
|
sizeof(struct usb_endpoint_descriptor));
|
|
|
|
|
|
|
|
return sizeof(struct usb_interface_descriptor) +
|
|
|
|
2*sizeof(struct usb_endpoint_descriptor);
|
|
|
|
}
|
|
|
|
|
2008-04-26 19:02:16 +00:00
|
|
|
void usb_storage_init_connection(void)
|
2008-03-06 21:25:09 +00:00
|
|
|
{
|
|
|
|
logf("ums: set config");
|
|
|
|
/* prime rx endpoint. We only need room for commands */
|
|
|
|
state = WAITING_FOR_COMMAND;
|
|
|
|
|
2008-10-03 22:43:16 +00:00
|
|
|
#if CONFIG_CPU == IMX31L || CONFIG_USBOTG == USBOTG_ISP1583 || \
|
|
|
|
defined(CPU_TCC77X) || defined(CPU_TCC780X)
|
2008-04-18 16:42:50 +00:00
|
|
|
static unsigned char _transfer_buffer[BUFFER_SIZE*2]
|
|
|
|
USBDEVBSS_ATTR __attribute__((aligned(32)));
|
|
|
|
tb.transfer_buffer = (void *)_transfer_buffer;
|
|
|
|
#else
|
2008-03-06 21:25:09 +00:00
|
|
|
/* TODO : check if bufsize is at least 32K ? */
|
2008-04-18 16:42:50 +00:00
|
|
|
size_t bufsize;
|
|
|
|
unsigned char * audio_buffer;
|
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
audio_buffer = audio_get_buffer(false,&bufsize);
|
|
|
|
tb.transfer_buffer =
|
2008-08-31 15:11:04 +00:00
|
|
|
(void *)UNCACHED_ADDR((unsigned int)(audio_buffer + 31) & 0xffffffe0);
|
2008-04-18 16:42:50 +00:00
|
|
|
invalidate_icache();
|
2008-10-20 21:49:59 +00:00
|
|
|
#ifdef USB_USE_RAMDISK
|
|
|
|
ramdisk_buffer = tb.transfer_buffer + BUFFER_SIZE*2;
|
|
|
|
#endif
|
2008-04-18 16:42:50 +00:00
|
|
|
#endif
|
2008-10-03 22:43:16 +00:00
|
|
|
usb_drv_recv(ep_out, tb.transfer_buffer, 1024);
|
2008-03-06 21:25:09 +00:00
|
|
|
}
|
|
|
|
|
2007-11-22 20:51:00 +00:00
|
|
|
/* called by usb_core_transfer_complete() */
|
2008-10-03 22:43:16 +00:00
|
|
|
void usb_storage_transfer_complete(int ep,int dir,int status,int length)
|
2007-11-22 20:51:00 +00:00
|
|
|
{
|
2008-04-26 19:02:16 +00:00
|
|
|
(void)ep;
|
2008-02-29 21:10:31 +00:00
|
|
|
struct command_block_wrapper* cbw = (void*)tb.transfer_buffer;
|
2007-11-22 20:51:00 +00:00
|
|
|
|
2008-02-20 22:54:26 +00:00
|
|
|
//logf("transfer result %X %d", status, length);
|
|
|
|
switch(state) {
|
|
|
|
case RECEIVING_BLOCKS:
|
2008-10-03 22:43:16 +00:00
|
|
|
if(dir==USB_DIR_IN) {
|
2008-02-20 22:54:26 +00:00
|
|
|
logf("IN received in RECEIVING");
|
|
|
|
}
|
2008-03-06 21:25:09 +00:00
|
|
|
logf("scsi write %d %d", cur_cmd.sector, cur_cmd.count);
|
2008-02-20 22:54:26 +00:00
|
|
|
if(status==0) {
|
2008-03-06 21:25:09 +00:00
|
|
|
if((unsigned int)length!=(SECTOR_SIZE*cur_cmd.count)
|
2008-02-20 22:54:26 +00:00
|
|
|
&& (unsigned int)length!=BUFFER_SIZE) {
|
|
|
|
logf("unexpected length :%d",length);
|
2008-02-11 14:26:25 +00:00
|
|
|
}
|
2008-02-20 22:54:26 +00:00
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
unsigned int next_sector = cur_cmd.sector +
|
|
|
|
(BUFFER_SIZE/SECTOR_SIZE);
|
|
|
|
unsigned int next_count = cur_cmd.count -
|
|
|
|
MIN(cur_cmd.count,BUFFER_SIZE/SECTOR_SIZE);
|
2008-07-09 19:56:09 +00:00
|
|
|
int next_select = !cur_cmd.data_select;
|
2008-02-20 22:54:26 +00:00
|
|
|
|
2008-07-09 19:56:09 +00:00
|
|
|
#ifndef SERIALIZE_WRITES
|
2008-02-20 22:54:26 +00:00
|
|
|
if(next_count!=0) {
|
|
|
|
/* Ask the host to send more, to the other buffer */
|
2008-07-09 19:56:09 +00:00
|
|
|
receive_block_data(cur_cmd.data[next_select],
|
2008-02-20 22:54:26 +00:00
|
|
|
MIN(BUFFER_SIZE,next_count*SECTOR_SIZE));
|
2008-02-11 14:26:25 +00:00
|
|
|
}
|
2008-07-09 19:56:09 +00:00
|
|
|
#endif
|
2008-02-20 22:54:26 +00:00
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
/* Now write the data that just came in, while the host is
|
|
|
|
sending the next bit */
|
2008-10-20 21:49:59 +00:00
|
|
|
#ifdef USB_USE_RAMDISK
|
|
|
|
memcpy(ramdisk_buffer + cur_cmd.sector*SECTOR_SIZE,
|
|
|
|
cur_cmd.data[cur_cmd.data_select],
|
|
|
|
MIN(BUFFER_SIZE/SECTOR_SIZE, cur_cmd.count)*SECTOR_SIZE);
|
|
|
|
#else
|
2008-11-01 16:14:28 +00:00
|
|
|
int result = storage_write_sectors(IF_MV2(cur_cmd.lun,)
|
2008-03-06 21:25:09 +00:00
|
|
|
cur_cmd.sector,
|
|
|
|
MIN(BUFFER_SIZE/SECTOR_SIZE,
|
2008-03-10 20:55:24 +00:00
|
|
|
cur_cmd.count),
|
2008-08-31 15:11:04 +00:00
|
|
|
cur_cmd.data[cur_cmd.data_select]);
|
2008-02-20 22:54:26 +00:00
|
|
|
if(result != 0) {
|
2008-02-28 22:35:23 +00:00
|
|
|
send_csw(UMS_STATUS_FAIL);
|
2008-02-20 22:54:26 +00:00
|
|
|
cur_sense_data.sense_key=SENSE_MEDIUM_ERROR;
|
|
|
|
cur_sense_data.asc=ASC_WRITE_ERROR;
|
2008-03-02 00:15:02 +00:00
|
|
|
cur_sense_data.ascq=0;
|
2008-02-20 22:54:26 +00:00
|
|
|
break;
|
|
|
|
}
|
2008-10-20 21:49:59 +00:00
|
|
|
#endif
|
2008-07-09 19:56:09 +00:00
|
|
|
#ifdef SERIALIZE_WRITES
|
|
|
|
if(next_count!=0) {
|
|
|
|
/* Ask the host to send more, to the other buffer */
|
|
|
|
receive_block_data(cur_cmd.data[next_select],
|
|
|
|
MIN(BUFFER_SIZE,next_count*SECTOR_SIZE));
|
|
|
|
}
|
|
|
|
#endif
|
2008-02-20 22:54:26 +00:00
|
|
|
|
|
|
|
if(next_count==0) {
|
2008-02-28 22:35:23 +00:00
|
|
|
send_csw(UMS_STATUS_GOOD);
|
2008-02-20 22:54:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Switch buffers for the next one */
|
2008-03-06 21:25:09 +00:00
|
|
|
cur_cmd.data_select=!cur_cmd.data_select;
|
2008-02-20 22:54:26 +00:00
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
cur_cmd.sector = next_sector;
|
|
|
|
cur_cmd.count = next_count;
|
2008-02-20 22:54:26 +00:00
|
|
|
|
2007-11-22 20:51:00 +00:00
|
|
|
}
|
2008-02-20 22:54:26 +00:00
|
|
|
else {
|
|
|
|
logf("Transfer failed %X",status);
|
2008-02-28 22:35:23 +00:00
|
|
|
send_csw(UMS_STATUS_FAIL);
|
2008-02-20 22:54:26 +00:00
|
|
|
/* TODO fill in cur_sense_data */
|
|
|
|
cur_sense_data.sense_key=0;
|
|
|
|
cur_sense_data.information=0;
|
|
|
|
cur_sense_data.asc=0;
|
2008-03-02 00:15:02 +00:00
|
|
|
cur_sense_data.ascq=0;
|
2008-02-11 14:26:25 +00:00
|
|
|
}
|
2007-11-22 20:51:00 +00:00
|
|
|
break;
|
2008-02-20 22:54:26 +00:00
|
|
|
case WAITING_FOR_COMMAND:
|
2008-10-03 22:43:16 +00:00
|
|
|
if(dir==USB_DIR_IN) {
|
2008-02-20 22:54:26 +00:00
|
|
|
logf("IN received in WAITING_FOR_COMMAND");
|
|
|
|
}
|
|
|
|
//logf("command received");
|
2008-02-28 22:35:23 +00:00
|
|
|
if(letoh32(cbw->signature) == CBW_SIGNATURE){
|
|
|
|
handle_scsi(cbw);
|
|
|
|
}
|
|
|
|
else {
|
2008-10-03 22:43:16 +00:00
|
|
|
usb_drv_stall(ep_in, true,true);
|
|
|
|
usb_drv_stall(ep_out, true,false);
|
2008-02-28 22:35:23 +00:00
|
|
|
}
|
2008-02-20 22:54:26 +00:00
|
|
|
break;
|
|
|
|
case SENDING_CSW:
|
2008-10-03 22:43:16 +00:00
|
|
|
if(dir==USB_DIR_OUT) {
|
2008-02-20 22:54:26 +00:00
|
|
|
logf("OUT received in SENDING_CSW");
|
|
|
|
}
|
|
|
|
//logf("csw sent, now go back to idle");
|
|
|
|
state = WAITING_FOR_COMMAND;
|
2008-10-03 22:43:16 +00:00
|
|
|
usb_drv_recv(ep_out, tb.transfer_buffer, 1024);
|
2008-02-20 22:54:26 +00:00
|
|
|
break;
|
|
|
|
case SENDING_RESULT:
|
2008-10-03 22:43:16 +00:00
|
|
|
if(dir==USB_DIR_OUT) {
|
2008-02-20 22:54:26 +00:00
|
|
|
logf("OUT received in SENDING");
|
|
|
|
}
|
|
|
|
if(status==0) {
|
|
|
|
//logf("data sent, now send csw");
|
2008-02-28 22:35:23 +00:00
|
|
|
send_csw(UMS_STATUS_GOOD);
|
2008-02-20 22:54:26 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
logf("Transfer failed %X",status);
|
2008-02-28 22:35:23 +00:00
|
|
|
send_csw(UMS_STATUS_FAIL);
|
2008-02-20 22:54:26 +00:00
|
|
|
/* TODO fill in cur_sense_data */
|
|
|
|
cur_sense_data.sense_key=0;
|
|
|
|
cur_sense_data.information=0;
|
|
|
|
cur_sense_data.asc=0;
|
2008-03-02 00:15:02 +00:00
|
|
|
cur_sense_data.ascq=0;
|
2008-02-20 22:54:26 +00:00
|
|
|
}
|
|
|
|
break;
|
2008-04-01 20:36:51 +00:00
|
|
|
case SENDING_FAILED_RESULT:
|
2008-10-03 22:43:16 +00:00
|
|
|
if(dir==USB_DIR_OUT) {
|
2008-04-01 20:36:51 +00:00
|
|
|
logf("OUT received in SENDING");
|
|
|
|
}
|
|
|
|
send_csw(UMS_STATUS_FAIL);
|
|
|
|
break;
|
2008-02-20 22:54:26 +00:00
|
|
|
case SENDING_BLOCKS:
|
2008-10-03 22:43:16 +00:00
|
|
|
if(dir==USB_DIR_OUT) {
|
2008-02-20 22:54:26 +00:00
|
|
|
logf("OUT received in SENDING");
|
|
|
|
}
|
|
|
|
if(status==0) {
|
2008-03-06 21:25:09 +00:00
|
|
|
if(cur_cmd.count==0) {
|
2008-02-20 22:54:26 +00:00
|
|
|
//logf("data sent, now send csw");
|
2008-02-28 22:35:23 +00:00
|
|
|
send_csw(UMS_STATUS_GOOD);
|
2008-02-20 22:54:26 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
send_and_read_next();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
logf("Transfer failed %X",status);
|
2008-02-28 22:35:23 +00:00
|
|
|
send_csw(UMS_STATUS_FAIL);
|
2008-02-20 22:54:26 +00:00
|
|
|
/* TODO fill in cur_sense_data */
|
|
|
|
cur_sense_data.sense_key=0;
|
|
|
|
cur_sense_data.information=0;
|
|
|
|
cur_sense_data.asc=0;
|
2008-03-02 00:15:02 +00:00
|
|
|
cur_sense_data.ascq=0;
|
2008-02-11 14:26:25 +00:00
|
|
|
}
|
2007-11-22 20:51:00 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* called by usb_core_control_request() */
|
|
|
|
bool usb_storage_control_request(struct usb_ctrlrequest* req)
|
|
|
|
{
|
|
|
|
bool handled = false;
|
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
|
2007-11-22 20:51:00 +00:00
|
|
|
switch (req->bRequest) {
|
|
|
|
case USB_BULK_GET_MAX_LUN: {
|
2008-02-20 22:54:26 +00:00
|
|
|
#ifdef ONLY_EXPOSE_CARD_SLOT
|
2008-02-29 21:10:31 +00:00
|
|
|
*tb.max_lun = 0;
|
2008-02-20 22:54:26 +00:00
|
|
|
#else
|
2008-02-29 21:10:31 +00:00
|
|
|
*tb.max_lun = NUM_VOLUMES - 1;
|
2008-02-20 22:54:26 +00:00
|
|
|
#endif
|
2007-11-22 20:51:00 +00:00
|
|
|
logf("ums: getmaxlun");
|
2008-04-18 16:42:50 +00:00
|
|
|
usb_drv_send(EP_CONTROL, tb.max_lun, 1);
|
2007-11-22 20:51:00 +00:00
|
|
|
usb_drv_recv(EP_CONTROL, NULL, 0); /* ack */
|
|
|
|
handled = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case USB_BULK_RESET_REQUEST:
|
|
|
|
logf("ums: bulk reset");
|
2008-02-28 22:35:23 +00:00
|
|
|
state = WAITING_FOR_COMMAND;
|
|
|
|
/* UMS BOT 3.1 says The device shall preserve the value of its bulk
|
2008-03-06 21:25:09 +00:00
|
|
|
data toggle bits and endpoint STALL conditions despite
|
|
|
|
the Bulk-Only Mass Storage Reset. */
|
2008-02-28 22:35:23 +00:00
|
|
|
#if 0
|
2008-10-03 22:43:16 +00:00
|
|
|
usb_drv_reset_endpoint(ep_in, false);
|
|
|
|
usb_drv_reset_endpoint(ep_out, true);
|
2008-02-28 22:35:23 +00:00
|
|
|
#endif
|
|
|
|
|
2007-11-22 20:51:00 +00:00
|
|
|
usb_drv_send(EP_CONTROL, NULL, 0); /* ack */
|
|
|
|
handled = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return handled;
|
|
|
|
}
|
|
|
|
|
2008-02-20 22:54:26 +00:00
|
|
|
static void send_and_read_next(void)
|
|
|
|
{
|
2008-03-06 21:25:09 +00:00
|
|
|
if(cur_cmd.last_result!=0) {
|
2008-02-20 22:54:26 +00:00
|
|
|
/* The last read failed. */
|
2008-02-28 22:35:23 +00:00
|
|
|
send_csw(UMS_STATUS_FAIL);
|
2008-02-20 22:54:26 +00:00
|
|
|
cur_sense_data.sense_key=SENSE_MEDIUM_ERROR;
|
|
|
|
cur_sense_data.asc=ASC_READ_ERROR;
|
2008-03-02 00:15:02 +00:00
|
|
|
cur_sense_data.ascq=0;
|
2008-02-20 22:54:26 +00:00
|
|
|
return;
|
|
|
|
}
|
2008-03-06 21:25:09 +00:00
|
|
|
send_block_data(cur_cmd.data[cur_cmd.data_select],
|
|
|
|
MIN(BUFFER_SIZE,cur_cmd.count*SECTOR_SIZE));
|
2008-02-20 22:54:26 +00:00
|
|
|
|
|
|
|
/* Switch buffers for the next one */
|
2008-03-06 21:25:09 +00:00
|
|
|
cur_cmd.data_select=!cur_cmd.data_select;
|
2008-02-20 22:54:26 +00:00
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
cur_cmd.sector+=(BUFFER_SIZE/SECTOR_SIZE);
|
|
|
|
cur_cmd.count-=MIN(cur_cmd.count,BUFFER_SIZE/SECTOR_SIZE);
|
2008-02-20 22:54:26 +00:00
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
if(cur_cmd.count!=0){
|
2008-02-20 22:54:26 +00:00
|
|
|
/* already read the next bit, so we can send it out immediately when the
|
|
|
|
* current transfer completes. */
|
2008-10-20 21:49:59 +00:00
|
|
|
#ifdef USB_USE_RAMDISK
|
|
|
|
memcpy(cur_cmd.data[cur_cmd.data_select],
|
|
|
|
ramdisk_buffer + cur_cmd.sector*SECTOR_SIZE,
|
|
|
|
MIN(BUFFER_SIZE/SECTOR_SIZE, cur_cmd.count)*SECTOR_SIZE);
|
|
|
|
#else
|
2008-11-01 16:14:28 +00:00
|
|
|
cur_cmd.last_result = storage_read_sectors(IF_MV2(cur_cmd.lun,)
|
2008-03-06 21:25:09 +00:00
|
|
|
cur_cmd.sector,
|
|
|
|
MIN(BUFFER_SIZE/SECTOR_SIZE,
|
2008-03-10 20:55:24 +00:00
|
|
|
cur_cmd.count),
|
2008-03-06 21:25:09 +00:00
|
|
|
cur_cmd.data[cur_cmd.data_select]);
|
2008-10-20 21:49:59 +00:00
|
|
|
#endif
|
2008-02-20 22:54:26 +00:00
|
|
|
}
|
|
|
|
}
|
2007-11-22 20:51:00 +00:00
|
|
|
/****************************************************************************/
|
|
|
|
|
2008-02-11 14:26:25 +00:00
|
|
|
static void handle_scsi(struct command_block_wrapper* cbw)
|
2007-11-22 20:51:00 +00:00
|
|
|
{
|
|
|
|
/* USB Mass Storage assumes LBA capability.
|
|
|
|
TODO: support 48-bit LBA */
|
|
|
|
|
2008-11-01 16:14:28 +00:00
|
|
|
struct storage_info info;
|
2007-11-22 20:51:00 +00:00
|
|
|
unsigned int length = cbw->data_transfer_length;
|
2008-03-10 20:55:24 +00:00
|
|
|
unsigned int block_size = 0;
|
|
|
|
unsigned int block_count = 0;
|
2008-02-20 22:54:26 +00:00
|
|
|
bool lun_present=true;
|
|
|
|
#ifdef ONLY_EXPOSE_CARD_SLOT
|
|
|
|
unsigned char lun = cbw->lun+1;
|
|
|
|
#else
|
2008-02-11 14:26:25 +00:00
|
|
|
unsigned char lun = cbw->lun;
|
2008-02-20 22:54:26 +00:00
|
|
|
#endif
|
2008-02-11 14:26:25 +00:00
|
|
|
unsigned int block_size_mult = 1;
|
2008-11-01 16:14:28 +00:00
|
|
|
storage_get_info(IF_MV2(lun,)&info);
|
2008-10-20 21:49:59 +00:00
|
|
|
#ifdef USB_USE_RAMDISK
|
|
|
|
block_size = SECTOR_SIZE;
|
|
|
|
block_count = RAMDISK_SIZE;
|
|
|
|
#else
|
2008-11-01 16:14:28 +00:00
|
|
|
block_size=info.sector_size;
|
|
|
|
block_count=info.num_sectors;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_HOTSWAP
|
|
|
|
if(storage_removable(IF_MV(lun)) && !storage_present(IF_MV(lun))) {
|
2008-03-10 20:55:24 +00:00
|
|
|
ejected[lun] = true;
|
|
|
|
try_release_ata();
|
2008-02-11 14:26:25 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-03-03 22:53:25 +00:00
|
|
|
if(ejected[lun])
|
|
|
|
lun_present = false;
|
|
|
|
|
2008-02-11 14:26:25 +00:00
|
|
|
#ifdef MAX_LOG_SECTOR_SIZE
|
|
|
|
block_size_mult = disk_sector_multiplier;
|
|
|
|
#endif
|
2007-11-22 20:51:00 +00:00
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
cur_cmd.tag = cbw->tag;
|
|
|
|
cur_cmd.lun = lun;
|
2008-02-20 22:54:26 +00:00
|
|
|
|
2007-11-22 20:51:00 +00:00
|
|
|
switch (cbw->command_block[0]) {
|
|
|
|
case SCSI_TEST_UNIT_READY:
|
2008-02-11 14:26:25 +00:00
|
|
|
logf("scsi test_unit_ready %d",lun);
|
2008-03-02 00:15:02 +00:00
|
|
|
if(!usb_exclusive_ata()) {
|
|
|
|
send_csw(UMS_STATUS_FAIL);
|
|
|
|
cur_sense_data.sense_key=SENSE_NOT_READY;
|
2008-03-10 20:55:24 +00:00
|
|
|
cur_sense_data.asc=ASC_MEDIUM_NOT_PRESENT;
|
|
|
|
cur_sense_data.ascq=0;
|
2008-03-02 00:15:02 +00:00
|
|
|
break;
|
|
|
|
}
|
2008-02-28 22:35:23 +00:00
|
|
|
if(lun_present) {
|
|
|
|
send_csw(UMS_STATUS_GOOD);
|
|
|
|
}
|
2008-02-20 22:54:26 +00:00
|
|
|
else {
|
2008-02-28 22:35:23 +00:00
|
|
|
send_csw(UMS_STATUS_FAIL);
|
2008-02-20 22:54:26 +00:00
|
|
|
cur_sense_data.sense_key=SENSE_NOT_READY;
|
|
|
|
cur_sense_data.asc=ASC_MEDIUM_NOT_PRESENT;
|
2008-03-02 00:15:02 +00:00
|
|
|
cur_sense_data.ascq=0;
|
2008-02-20 22:54:26 +00:00
|
|
|
}
|
2007-11-22 20:51:00 +00:00
|
|
|
break;
|
|
|
|
|
2008-02-20 22:54:26 +00:00
|
|
|
case SCSI_REPORT_LUNS: {
|
|
|
|
logf("scsi inquiry %d",lun);
|
|
|
|
int allocation_length=0;
|
2008-11-01 16:14:28 +00:00
|
|
|
int i;
|
2008-02-20 22:54:26 +00:00
|
|
|
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]);
|
2008-02-29 21:10:31 +00:00
|
|
|
memset(tb.lun_data,0,sizeof(struct report_lun_data));
|
2008-11-01 16:14:28 +00:00
|
|
|
tb.lun_data->lun_list_length=htobe32(8*NUM_VOLUMES);
|
|
|
|
for(i=0;i<NUM_VOLUMES;i++)
|
|
|
|
{
|
2008-02-20 22:54:26 +00:00
|
|
|
#ifdef HAVE_HOTSWAP
|
2008-11-01 16:14:28 +00:00
|
|
|
if(storage_removable(IF_MV(i)))
|
|
|
|
tb.lun_data->luns[i][1]=1;
|
|
|
|
else
|
2008-02-20 22:54:26 +00:00
|
|
|
#endif
|
2008-11-01 16:14:28 +00:00
|
|
|
tb.lun_data->luns[i][1]=0;
|
|
|
|
}
|
2008-03-06 21:25:09 +00:00
|
|
|
send_command_result(tb.lun_data,
|
|
|
|
MIN(sizeof(struct report_lun_data), length));
|
2008-02-20 22:54:26 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2007-11-22 20:51:00 +00:00
|
|
|
case SCSI_INQUIRY:
|
2008-02-11 14:26:25 +00:00
|
|
|
logf("scsi inquiry %d",lun);
|
2008-11-01 16:14:28 +00:00
|
|
|
fill_inquiry(IF_MV(lun));
|
2007-11-22 20:51:00 +00:00
|
|
|
length = MIN(length, cbw->command_block[4]);
|
2008-03-06 21:25:09 +00:00
|
|
|
send_command_result(tb.inquiry,
|
|
|
|
MIN(sizeof(struct inquiry_data), length));
|
2007-11-22 20:51:00 +00:00
|
|
|
break;
|
|
|
|
|
2008-02-11 14:26:25 +00:00
|
|
|
case SCSI_REQUEST_SENSE: {
|
2008-02-29 21:10:31 +00:00
|
|
|
tb.sense_data->ResponseCode=0x70;/*current error*/
|
2008-04-01 20:36:51 +00:00
|
|
|
tb.sense_data->Obsolete=0;
|
2008-03-06 21:25:09 +00:00
|
|
|
tb.sense_data->fei_sensekey=cur_sense_data.sense_key&0x0f;
|
2008-02-29 21:10:31 +00:00
|
|
|
tb.sense_data->Information=cur_sense_data.information;
|
|
|
|
tb.sense_data->AdditionalSenseLength=10;
|
|
|
|
tb.sense_data->CommandSpecificInformation=0;
|
|
|
|
tb.sense_data->AdditionalSenseCode=cur_sense_data.asc;
|
2008-03-02 00:15:02 +00:00
|
|
|
tb.sense_data->AdditionalSenseCodeQualifier=cur_sense_data.ascq;
|
2008-02-29 21:10:31 +00:00
|
|
|
tb.sense_data->FieldReplaceableUnitCode=0;
|
|
|
|
tb.sense_data->SKSV=0;
|
|
|
|
tb.sense_data->SenseKeySpecific=0;
|
2008-02-11 14:26:25 +00:00
|
|
|
logf("scsi request_sense %d",lun);
|
2008-02-29 21:10:31 +00:00
|
|
|
send_command_result(tb.sense_data, sizeof(struct sense_data));
|
2008-02-11 14:26:25 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-02-20 22:54:26 +00:00
|
|
|
case SCSI_MODE_SENSE_10: {
|
2008-02-28 22:35:23 +00:00
|
|
|
if(! lun_present) {
|
2008-04-01 20:36:51 +00:00
|
|
|
send_command_failed_result();
|
2008-02-28 22:35:23 +00:00
|
|
|
cur_sense_data.sense_key=SENSE_NOT_READY;
|
|
|
|
cur_sense_data.asc=ASC_MEDIUM_NOT_PRESENT;
|
2008-03-02 00:15:02 +00:00
|
|
|
cur_sense_data.ascq=0;
|
2008-02-28 22:35:23 +00:00
|
|
|
break;
|
|
|
|
}
|
2008-02-20 22:54:26 +00:00
|
|
|
/*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:
|
2008-03-06 21:25:09 +00:00
|
|
|
tb.ms_data_10->mode_data_length =
|
|
|
|
htobe16(sizeof(struct mode_sense_data_10)-2);
|
|
|
|
tb.ms_data_10->medium_type = 0;
|
|
|
|
tb.ms_data_10->device_specific = 0;
|
|
|
|
tb.ms_data_10->reserved = 0;
|
|
|
|
tb.ms_data_10->longlba = 1;
|
|
|
|
tb.ms_data_10->block_descriptor_length =
|
|
|
|
htobe16(sizeof(struct mode_sense_bdesc_longlba));
|
|
|
|
|
|
|
|
memset(tb.ms_data_10->block_descriptor.reserved,0,4);
|
|
|
|
memset(tb.ms_data_10->block_descriptor.num_blocks,0,8);
|
|
|
|
|
|
|
|
tb.ms_data_10->block_descriptor.num_blocks[4] =
|
|
|
|
((block_count/block_size_mult) & 0xff000000)>>24;
|
|
|
|
tb.ms_data_10->block_descriptor.num_blocks[5] =
|
|
|
|
((block_count/block_size_mult) & 0x00ff0000)>>16;
|
|
|
|
tb.ms_data_10->block_descriptor.num_blocks[6] =
|
|
|
|
((block_count/block_size_mult) & 0x0000ff00)>>8;
|
|
|
|
tb.ms_data_10->block_descriptor.num_blocks[7] =
|
|
|
|
((block_count/block_size_mult) & 0x000000ff);
|
|
|
|
|
|
|
|
tb.ms_data_10->block_descriptor.block_size[0] =
|
|
|
|
((block_size*block_size_mult) & 0xff000000)>>24;
|
|
|
|
tb.ms_data_10->block_descriptor.block_size[1] =
|
|
|
|
((block_size*block_size_mult) & 0x00ff0000)>>16;
|
|
|
|
tb.ms_data_10->block_descriptor.block_size[2] =
|
|
|
|
((block_size*block_size_mult) & 0x0000ff00)>>8;
|
|
|
|
tb.ms_data_10->block_descriptor.block_size[3] =
|
|
|
|
((block_size*block_size_mult) & 0x000000ff);
|
|
|
|
send_command_result(tb.ms_data_10,
|
2008-02-29 19:25:14 +00:00
|
|
|
MIN(sizeof(struct mode_sense_data_10), length));
|
2008-02-20 22:54:26 +00:00
|
|
|
break;
|
|
|
|
default:
|
2008-04-01 20:36:51 +00:00
|
|
|
send_command_failed_result();
|
2008-02-20 22:54:26 +00:00
|
|
|
cur_sense_data.sense_key=SENSE_ILLEGAL_REQUEST;
|
|
|
|
cur_sense_data.asc=ASC_INVALID_FIELD_IN_CBD;
|
2008-03-02 00:15:02 +00:00
|
|
|
cur_sense_data.ascq=0;
|
2008-02-20 22:54:26 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SCSI_MODE_SENSE_6: {
|
2008-02-28 22:35:23 +00:00
|
|
|
if(! lun_present) {
|
2008-04-01 20:36:51 +00:00
|
|
|
send_command_failed_result();
|
2008-02-28 22:35:23 +00:00
|
|
|
cur_sense_data.sense_key=SENSE_NOT_READY;
|
|
|
|
cur_sense_data.asc=ASC_MEDIUM_NOT_PRESENT;
|
2008-03-02 00:15:02 +00:00
|
|
|
cur_sense_data.ascq=0;
|
2008-02-28 22:35:23 +00:00
|
|
|
break;
|
|
|
|
}
|
2008-02-20 22:54:26 +00:00
|
|
|
/*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:
|
2008-03-06 21:25:09 +00:00
|
|
|
/* All supported pages. */
|
|
|
|
tb.ms_data_6->mode_data_length =
|
|
|
|
sizeof(struct mode_sense_data_6)-1;
|
|
|
|
tb.ms_data_6->medium_type = 0;
|
|
|
|
tb.ms_data_6->device_specific = 0;
|
|
|
|
tb.ms_data_6->block_descriptor_length =
|
|
|
|
sizeof(struct mode_sense_bdesc_shortlba);
|
|
|
|
tb.ms_data_6->block_descriptor.density_code = 0;
|
|
|
|
tb.ms_data_6->block_descriptor.reserved = 0;
|
2008-02-29 19:25:14 +00:00
|
|
|
if(block_count/block_size_mult > 0xffffff){
|
2008-03-06 21:25:09 +00:00
|
|
|
tb.ms_data_6->block_descriptor.num_blocks[0] = 0xff;
|
|
|
|
tb.ms_data_6->block_descriptor.num_blocks[1] = 0xff;
|
|
|
|
tb.ms_data_6->block_descriptor.num_blocks[2] = 0xff;
|
2008-02-29 19:25:14 +00:00
|
|
|
}
|
|
|
|
else {
|
2008-03-06 21:25:09 +00:00
|
|
|
tb.ms_data_6->block_descriptor.num_blocks[0] =
|
|
|
|
((block_count/block_size_mult) & 0xff0000)>>16;
|
|
|
|
tb.ms_data_6->block_descriptor.num_blocks[1] =
|
|
|
|
((block_count/block_size_mult) & 0x00ff00)>>8;
|
|
|
|
tb.ms_data_6->block_descriptor.num_blocks[2] =
|
|
|
|
((block_count/block_size_mult) & 0x0000ff);
|
2008-02-29 19:25:14 +00:00
|
|
|
}
|
2008-03-06 21:25:09 +00:00
|
|
|
tb.ms_data_6->block_descriptor.block_size[0] =
|
|
|
|
((block_size*block_size_mult) & 0xff0000)>>16;
|
|
|
|
tb.ms_data_6->block_descriptor.block_size[1] =
|
|
|
|
((block_size*block_size_mult) & 0x00ff00)>>8;
|
|
|
|
tb.ms_data_6->block_descriptor.block_size[2] =
|
|
|
|
((block_size*block_size_mult) & 0x0000ff);
|
|
|
|
send_command_result(tb.ms_data_6,
|
2008-02-29 19:25:14 +00:00
|
|
|
MIN(sizeof(struct mode_sense_data_6), length));
|
2008-02-20 22:54:26 +00:00
|
|
|
break;
|
|
|
|
default:
|
2008-04-01 20:36:51 +00:00
|
|
|
send_command_failed_result();
|
2008-02-20 22:54:26 +00:00
|
|
|
cur_sense_data.sense_key=SENSE_ILLEGAL_REQUEST;
|
|
|
|
cur_sense_data.asc=ASC_INVALID_FIELD_IN_CBD;
|
2008-03-02 00:15:02 +00:00
|
|
|
cur_sense_data.ascq=0;
|
2008-02-20 22:54:26 +00:00
|
|
|
break;
|
|
|
|
}
|
2007-11-22 20:51:00 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-02-11 14:26:25 +00:00
|
|
|
case SCSI_START_STOP_UNIT:
|
|
|
|
logf("scsi start_stop unit %d",lun);
|
2008-03-10 20:55:24 +00:00
|
|
|
if((cbw->command_block[4] & 0xf0) == 0) /*load/eject bit is valid*/
|
2008-03-06 21:25:09 +00:00
|
|
|
{ /* Process start and eject bits */
|
2008-03-10 20:55:24 +00:00
|
|
|
logf("scsi load/eject");
|
|
|
|
if((cbw->command_block[4] & 0x01) == 0) /* Don't start */
|
2008-03-03 22:53:25 +00:00
|
|
|
{
|
2008-03-10 20:55:24 +00:00
|
|
|
if((cbw->command_block[4] & 0x02) != 0) /* eject */
|
|
|
|
{
|
|
|
|
logf("scsi eject");
|
|
|
|
ejected[lun]=true;
|
|
|
|
try_release_ata();
|
|
|
|
}
|
2008-03-03 22:53:25 +00:00
|
|
|
}
|
|
|
|
}
|
2008-02-28 22:35:23 +00:00
|
|
|
send_csw(UMS_STATUS_GOOD);
|
2008-02-11 14:26:25 +00:00
|
|
|
break;
|
|
|
|
|
2007-11-22 20:51:00 +00:00
|
|
|
case SCSI_ALLOW_MEDIUM_REMOVAL:
|
2008-02-11 14:26:25 +00:00
|
|
|
logf("scsi allow_medium_removal %d",lun);
|
2008-02-20 22:54:26 +00:00
|
|
|
/* TODO: use this to show the connect screen ? */
|
2008-02-28 22:35:23 +00:00
|
|
|
send_csw(UMS_STATUS_GOOD);
|
2008-02-11 14:26:25 +00:00
|
|
|
break;
|
|
|
|
case SCSI_READ_FORMAT_CAPACITY: {
|
|
|
|
logf("scsi read_format_capacity %d",lun);
|
2008-02-28 22:35:23 +00:00
|
|
|
if(lun_present) {
|
2008-02-29 21:10:31 +00:00
|
|
|
tb.format_capacity_data->following_length=htobe32(8);
|
2008-03-06 21:25:09 +00:00
|
|
|
/* "block count" actually means "number of last block" */
|
|
|
|
tb.format_capacity_data->block_count =
|
|
|
|
htobe32(block_count/block_size_mult - 1);
|
|
|
|
tb.format_capacity_data->block_size =
|
|
|
|
htobe32(block_size*block_size_mult);
|
|
|
|
tb.format_capacity_data->block_size |=
|
2008-03-18 10:16:27 +00:00
|
|
|
htobe32(SCSI_FORMAT_CAPACITY_FORMATTED_MEDIA);
|
2008-02-28 22:35:23 +00:00
|
|
|
|
2008-02-29 21:10:31 +00:00
|
|
|
send_command_result(tb.format_capacity_data,
|
2008-02-28 22:35:23 +00:00
|
|
|
MIN(sizeof(struct format_capacity), length));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-04-01 20:36:51 +00:00
|
|
|
send_command_failed_result();
|
2008-02-28 22:35:23 +00:00
|
|
|
cur_sense_data.sense_key=SENSE_NOT_READY;
|
|
|
|
cur_sense_data.asc=ASC_MEDIUM_NOT_PRESENT;
|
2008-03-02 00:15:02 +00:00
|
|
|
cur_sense_data.ascq=0;
|
2008-02-28 22:35:23 +00:00
|
|
|
}
|
2007-11-22 20:51:00 +00:00
|
|
|
break;
|
2008-02-11 14:26:25 +00:00
|
|
|
}
|
2007-11-22 20:51:00 +00:00
|
|
|
case SCSI_READ_CAPACITY: {
|
2008-02-11 14:26:25 +00:00
|
|
|
logf("scsi read_capacity %d",lun);
|
2008-02-20 22:54:26 +00:00
|
|
|
|
2008-02-28 22:35:23 +00:00
|
|
|
if(lun_present) {
|
2008-03-06 21:25:09 +00:00
|
|
|
/* "block count" actually means "number of last block" */
|
|
|
|
tb.capacity_data->block_count =
|
|
|
|
htobe32(block_count/block_size_mult - 1);
|
|
|
|
tb.capacity_data->block_size =
|
|
|
|
htobe32(block_size*block_size_mult);
|
|
|
|
|
|
|
|
send_command_result(tb.capacity_data,
|
|
|
|
MIN(sizeof(struct capacity), length));
|
2008-02-28 22:35:23 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-04-01 20:36:51 +00:00
|
|
|
send_command_failed_result();
|
2008-02-28 22:35:23 +00:00
|
|
|
cur_sense_data.sense_key=SENSE_NOT_READY;
|
|
|
|
cur_sense_data.asc=ASC_MEDIUM_NOT_PRESENT;
|
2008-03-02 00:15:02 +00:00
|
|
|
cur_sense_data.ascq=0;
|
2008-02-28 22:35:23 +00:00
|
|
|
}
|
2007-11-22 20:51:00 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SCSI_READ_10:
|
2008-02-20 22:54:26 +00:00
|
|
|
logf("scsi read10 %d",lun);
|
|
|
|
if(! lun_present) {
|
2008-04-01 20:36:51 +00:00
|
|
|
send_command_failed_result();
|
2008-02-20 22:54:26 +00:00
|
|
|
cur_sense_data.sense_key=SENSE_NOT_READY;
|
|
|
|
cur_sense_data.asc=ASC_MEDIUM_NOT_PRESENT;
|
2008-03-02 00:15:02 +00:00
|
|
|
cur_sense_data.ascq=0;
|
2008-02-20 22:54:26 +00:00
|
|
|
break;
|
|
|
|
}
|
2008-08-31 15:11:04 +00:00
|
|
|
cur_cmd.data[0] = tb.transfer_buffer;
|
|
|
|
cur_cmd.data[1] = &tb.transfer_buffer[BUFFER_SIZE];
|
2008-03-06 21:25:09 +00:00
|
|
|
cur_cmd.data_select=0;
|
|
|
|
cur_cmd.sector = block_size_mult *
|
2008-02-11 14:26:25 +00:00
|
|
|
(cbw->command_block[2] << 24 |
|
2007-11-22 20:51:00 +00:00
|
|
|
cbw->command_block[3] << 16 |
|
|
|
|
cbw->command_block[4] << 8 |
|
2008-02-11 14:26:25 +00:00
|
|
|
cbw->command_block[5] );
|
2008-03-06 21:25:09 +00:00
|
|
|
cur_cmd.count = block_size_mult *
|
2008-03-03 22:17:21 +00:00
|
|
|
(cbw->command_block[7] << 8 |
|
2008-02-11 14:26:25 +00:00
|
|
|
cbw->command_block[8]);
|
2007-11-22 20:51:00 +00:00
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
//logf("scsi read %d %d", cur_cmd.sector, cur_cmd.count);
|
2007-11-22 20:51:00 +00:00
|
|
|
|
2008-03-06 21:25:09 +00:00
|
|
|
if((cur_cmd.sector + cur_cmd.count) > block_count) {
|
2008-02-28 22:35:23 +00:00
|
|
|
send_csw(UMS_STATUS_FAIL);
|
2008-02-20 22:54:26 +00:00
|
|
|
cur_sense_data.sense_key=SENSE_ILLEGAL_REQUEST;
|
|
|
|
cur_sense_data.asc=ASC_LBA_OUT_OF_RANGE;
|
2008-03-02 00:15:02 +00:00
|
|
|
cur_sense_data.ascq=0;
|
2008-02-11 14:26:25 +00:00
|
|
|
}
|
|
|
|
else {
|
2008-10-20 21:49:59 +00:00
|
|
|
#ifdef USB_USE_RAMDISK
|
|
|
|
memcpy(cur_cmd.data[cur_cmd.data_select],
|
|
|
|
ramdisk_buffer + cur_cmd.sector*SECTOR_SIZE,
|
|
|
|
MIN(BUFFER_SIZE/SECTOR_SIZE, cur_cmd.count)*SECTOR_SIZE);
|
|
|
|
#else
|
2008-11-01 16:14:28 +00:00
|
|
|
cur_cmd.last_result = storage_read_sectors(IF_MV2(cur_cmd.lun,)
|
2008-03-06 21:25:09 +00:00
|
|
|
cur_cmd.sector,
|
|
|
|
MIN(BUFFER_SIZE/SECTOR_SIZE,
|
2008-03-10 20:55:24 +00:00
|
|
|
cur_cmd.count),
|
2008-03-06 21:25:09 +00:00
|
|
|
cur_cmd.data[cur_cmd.data_select]);
|
2008-10-20 21:49:59 +00:00
|
|
|
#endif
|
2008-02-20 22:54:26 +00:00
|
|
|
send_and_read_next();
|
2008-02-11 14:26:25 +00:00
|
|
|
}
|
2007-11-22 20:51:00 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SCSI_WRITE_10:
|
2008-02-20 22:54:26 +00:00
|
|
|
logf("scsi write10 %d",lun);
|
|
|
|
if(! lun_present) {
|
2008-02-28 22:35:23 +00:00
|
|
|
send_csw(UMS_STATUS_FAIL);
|
2008-02-20 22:54:26 +00:00
|
|
|
cur_sense_data.sense_key=SENSE_NOT_READY;
|
|
|
|
cur_sense_data.asc=ASC_MEDIUM_NOT_PRESENT;
|
2008-03-02 00:15:02 +00:00
|
|
|
cur_sense_data.ascq=0;
|
2008-02-20 22:54:26 +00:00
|
|
|
break;
|
|
|
|
}
|
2008-08-31 15:11:04 +00:00
|
|
|
cur_cmd.data[0] = tb.transfer_buffer;
|
|
|
|
cur_cmd.data[1] = &tb.transfer_buffer[BUFFER_SIZE];
|
2008-03-06 21:25:09 +00:00
|
|
|
cur_cmd.data_select=0;
|
|
|
|
cur_cmd.sector = block_size_mult *
|
2008-02-11 14:26:25 +00:00
|
|
|
(cbw->command_block[2] << 24 |
|
|
|
|
cbw->command_block[3] << 16 |
|
|
|
|
cbw->command_block[4] << 8 |
|
|
|
|
cbw->command_block[5] );
|
2008-03-06 21:25:09 +00:00
|
|
|
cur_cmd.count = block_size_mult *
|
2008-03-03 22:17:21 +00:00
|
|
|
(cbw->command_block[7] << 8 |
|
2008-02-11 14:26:25 +00:00
|
|
|
cbw->command_block[8]);
|
|
|
|
/* expect data */
|
2008-03-06 21:25:09 +00:00
|
|
|
if((cur_cmd.sector + cur_cmd.count) > block_count) {
|
2008-02-28 22:35:23 +00:00
|
|
|
send_csw(UMS_STATUS_FAIL);
|
2008-02-20 22:54:26 +00:00
|
|
|
cur_sense_data.sense_key=SENSE_ILLEGAL_REQUEST;
|
|
|
|
cur_sense_data.asc=ASC_LBA_OUT_OF_RANGE;
|
2008-03-02 00:15:02 +00:00
|
|
|
cur_sense_data.ascq=0;
|
2008-02-11 14:26:25 +00:00
|
|
|
}
|
|
|
|
else {
|
2008-03-06 21:25:09 +00:00
|
|
|
receive_block_data(cur_cmd.data[0],
|
|
|
|
MIN(BUFFER_SIZE,
|
|
|
|
cur_cmd.count*SECTOR_SIZE));
|
2008-02-11 14:26:25 +00:00
|
|
|
}
|
|
|
|
|
2007-11-22 20:51:00 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2008-02-11 14:26:25 +00:00
|
|
|
logf("scsi unknown cmd %x",cbw->command_block[0x0]);
|
2008-10-03 22:43:16 +00:00
|
|
|
usb_drv_stall(ep_in, true,true);
|
2008-02-28 22:35:23 +00:00
|
|
|
send_csw(UMS_STATUS_FAIL);
|
2007-11-22 20:51:00 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-02-20 22:54:26 +00:00
|
|
|
static void send_block_data(void *data,int size)
|
|
|
|
{
|
2008-10-03 22:43:16 +00:00
|
|
|
usb_drv_send_nonblocking(ep_in, data,size);
|
2008-02-20 22:54:26 +00:00
|
|
|
state = SENDING_BLOCKS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void send_command_result(void *data,int size)
|
|
|
|
{
|
2008-10-03 22:43:16 +00:00
|
|
|
usb_drv_send_nonblocking(ep_in, data,size);
|
2008-02-20 22:54:26 +00:00
|
|
|
state = SENDING_RESULT;
|
|
|
|
}
|
|
|
|
|
2008-04-01 20:36:51 +00:00
|
|
|
static void send_command_failed_result(void)
|
|
|
|
{
|
2008-10-03 22:43:16 +00:00
|
|
|
usb_drv_send_nonblocking(ep_in, NULL, 0);
|
2008-04-01 20:36:51 +00:00
|
|
|
state = SENDING_FAILED_RESULT;
|
|
|
|
}
|
|
|
|
|
2008-02-20 22:54:26 +00:00
|
|
|
static void receive_block_data(void *data,int size)
|
2007-11-22 20:51:00 +00:00
|
|
|
{
|
2008-10-03 22:43:16 +00:00
|
|
|
usb_drv_recv(ep_out, data, size);
|
2008-02-20 22:54:26 +00:00
|
|
|
state = RECEIVING_BLOCKS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void send_csw(int status)
|
|
|
|
{
|
2008-02-29 21:10:31 +00:00
|
|
|
tb.csw->signature = htole32(CSW_SIGNATURE);
|
2008-03-06 21:25:09 +00:00
|
|
|
tb.csw->tag = cur_cmd.tag;
|
2008-02-29 21:10:31 +00:00
|
|
|
tb.csw->data_residue = 0;
|
|
|
|
tb.csw->status = status;
|
2007-11-22 20:51:00 +00:00
|
|
|
|
2008-10-03 22:43:16 +00:00
|
|
|
usb_drv_send_nonblocking(ep_in, tb.csw,
|
2008-03-06 21:25:09 +00:00
|
|
|
sizeof(struct command_status_wrapper));
|
2008-02-20 22:54:26 +00:00
|
|
|
state = SENDING_CSW;
|
2008-02-28 22:35:23 +00:00
|
|
|
//logf("CSW: %X",status);
|
2008-02-20 22:54:26 +00:00
|
|
|
|
2008-02-28 22:35:23 +00:00
|
|
|
if(status == UMS_STATUS_GOOD) {
|
2008-02-20 22:54:26 +00:00
|
|
|
cur_sense_data.sense_key=0;
|
|
|
|
cur_sense_data.information=0;
|
|
|
|
cur_sense_data.asc=0;
|
2008-03-02 00:15:02 +00:00
|
|
|
cur_sense_data.ascq=0;
|
2008-02-20 22:54:26 +00:00
|
|
|
}
|
2007-11-22 20:51:00 +00:00
|
|
|
}
|
|
|
|
|
2008-11-01 16:14:28 +00:00
|
|
|
static void copy_padded(char *dest, char *src, int len)
|
2007-11-22 20:51:00 +00:00
|
|
|
{
|
2008-11-01 16:14:28 +00:00
|
|
|
int i=0;
|
|
|
|
while(src[i]!=0 && i<len)
|
|
|
|
{
|
|
|
|
dest[i]=src[i];
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
while(i<len)
|
|
|
|
{
|
|
|
|
dest[i]=' ';
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
2007-11-22 20:51:00 +00:00
|
|
|
|
2008-11-01 16:14:28 +00:00
|
|
|
/* build SCSI INQUIRY */
|
|
|
|
static void fill_inquiry(IF_MV_NONVOID(int lun))
|
|
|
|
{
|
|
|
|
memset(tb.inquiry, 0, sizeof(struct inquiry_data));
|
|
|
|
struct storage_info info;
|
|
|
|
storage_get_info(IF_MV2(lun,)&info);
|
|
|
|
copy_padded(tb.inquiry->VendorId,info.vendor,sizeof(tb.inquiry->VendorId));
|
|
|
|
copy_padded(tb.inquiry->ProductId,info.product,sizeof(tb.inquiry->ProductId));
|
|
|
|
copy_padded(tb.inquiry->ProductRevisionLevel,info.revision,sizeof(tb.inquiry->ProductRevisionLevel));
|
2007-11-22 20:51:00 +00:00
|
|
|
|
2008-02-29 21:10:31 +00:00
|
|
|
tb.inquiry->DeviceType = DIRECT_ACCESS_DEVICE;
|
|
|
|
tb.inquiry->AdditionalLength = 0x1f;
|
2008-04-01 20:36:51 +00:00
|
|
|
memset(tb.inquiry->Reserved, 0, 3);
|
2008-02-29 21:10:31 +00:00
|
|
|
tb.inquiry->Versions = 4; /* SPC-2 */
|
|
|
|
tb.inquiry->Format = 2; /* SPC-2/3 inquiry format */
|
2008-02-11 14:26:25 +00:00
|
|
|
|
2008-04-18 16:42:50 +00:00
|
|
|
#ifdef TOSHIBA_GIGABEAT_S
|
|
|
|
tb.inquiry->DeviceTypeModifier = 0;
|
|
|
|
#else
|
2008-03-03 22:19:38 +00:00
|
|
|
tb.inquiry->DeviceTypeModifier = DEVICE_REMOVABLE;
|
2008-04-18 16:42:50 +00:00
|
|
|
#endif
|
2007-11-22 20:51:00 +00:00
|
|
|
}
|
|
|
|
|
2008-02-20 22:54:26 +00:00
|
|
|
#endif /* USB_STORAGE */
|