usb mass storage class

This commit is contained in:
NIIBE Yutaka 2011-12-08 11:00:26 +09:00
parent b113e6fa7b
commit 696de23b52
7 changed files with 876 additions and 9 deletions

View File

@ -28,7 +28,7 @@
#define USB_INTERFACE_DESCRIPTOR_TYPE 0x04
#define USB_ENDPOINT_DESCRIPTOR_TYPE 0x05
#define VIRTUAL_COM_PORT_DATA_SIZE 64
#define VIRTUAL_COM_PORT_DATA_SIZE 16
#define VIRTUAL_COM_PORT_INT_SIZE 8
#define VIRTUAL_COM_PORT_SIZ_DEVICE_DESC 18

View File

@ -439,6 +439,13 @@ main (int argc, char **argv)
"Hello world\r\n\r\n", 35+21+15);
}
#endif
if (msc_recv_cbw () == 0)
{
int r = msc_handle_cbw ();
if (r != 1)
msc_handle_err (r);
}
}
return 0;

View File

@ -28,9 +28,14 @@
/* EP3 */
#define ENDP3_TXADDR (0x140)
/* EP4 */
#define ENDP4_TXADDR (0x180)
#define ENDP4_TXADDR (0x150)
/* EP5 */
#define ENDP5_RXADDR (0x190)
#define ENDP5_RXADDR (0x160)
/* EP6 */
#define ENDP6_TXADDR (0x180)
/* EP7 */
#define ENDP7_RXADDR (0x1c0)
#define IMR_MSK (CNTR_CTRM | CNTR_SOFM | CNTR_RESETM )

View File

@ -29,14 +29,29 @@ static const uint8_t gnukDeviceDescriptor[] = {
0x01 /* bNumConfigurations */
};
#define ICC_TOTAL_LENGTH (9+9+54+7+7)
#define ICC_NUM_INTERFACES 1
#ifdef ENABLE_VIRTUAL_COM_PORT
#define W_TOTAL_LENGTH (9+9+54+7+7+9+5+5+4+5+7+9+7+7)
#define NUM_INTERFACES 3 /* two for CDC, one for GPG */
#define VCOM_TOTAL_LENGTH (9+5+5+4+5+7+9+7+7)
#define VCOM_NUM_INTERFACES 2
#else
#define W_TOTAL_LENGTH (9+9+54+7+7)
#define NUM_INTERFACES 1 /* GPG only */
#define VCOM_TOTAL_LENGTH 0
#define VCOM_NUM_INTERFACES 0
#endif
#ifdef PINPAD_DND_SUPPORT
#define MSC_TOTAL_LENGTH (9+7+7)
#define MSC_NUM_INTERFACES 1
#else
#define MSC_TOTAL_LENGTH 0
#define MSC_NUM_INTERFACES 0
#endif
#define W_TOTAL_LENGTH (ICC_TOTAL_LENGTH+VCOM_TOTAL_LENGTH+MSC_TOTAL_LENGTH)
#define NUM_INTERFACES (ICC_NUM_INTERFACES+VCOM_NUM_INTERFACES+MSC_NUM_INTERFACES)
/* Configuation Descriptor */
static const uint8_t gnukConfigDescriptor[] = {
9, /* bLength: Configuation Descriptor size */
@ -105,7 +120,7 @@ static const uint8_t gnukConfigDescriptor[] = {
0xff, /* bClassEnvelope: */
0, 0, /* wLCDLayout: FIXED VALUE */
#if defined(PINPAD_SUPPORT)
#if defined(PINPAD_CIR_SUPPORT)
#if defined(PINPAD_CIR_SUPPORT) || defined(PINPAD_DND_SUPPORT)
1, /* bPinSupport: with PIN pad (verify) */
#elif defined(PINPAD_DIAL_SUPPORT)
3, /* bPinSupport: with PIN pad (verify, modify) */
@ -193,7 +208,40 @@ static const uint8_t gnukConfigDescriptor[] = {
0x83, /* bEndpointAddress: (IN3) */
0x02, /* bmAttributes: Bulk */
VIRTUAL_COM_PORT_DATA_SIZE, 0x00, /* wMaxPacketSize: */
0x00 /* bInterval */
0x00, /* bInterval */
#endif
#ifdef PINPAD_DND_SUPPORT
/* Interface Descriptor.*/
9, /* bLength: Interface Descriptor size */
USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType: Interface */
#ifdef ENABLE_VIRTUAL_COM_PORT
0x03, /* bInterfaceNumber. */
#else
0x01, /* bInterfaceNumber. */
#endif
0x00, /* bAlternateSetting. */
0x02, /* bNumEndpoints. */
0x08, /* bInterfaceClass (Mass Stprage). */
0x06, /* bInterfaceSubClass (SCSI
transparent command set, MSCO
chapter 2). */
0x50, /* bInterfaceProtocol (Bulk-Only
Mass Storage, MSCO chapter 3). */
0x00, /* iInterface. */
/* Endpoint Descriptor.*/
7, /* bLength: Endpoint Descriptor size */
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */
0x86, /* bEndpointAddress: (IN6) */
0x02, /* bmAttributes (Bulk). */
0x40, 0x00, /* wMaxPacketSize. */
0x00, /* bInterval (ignored for bulk). */
/* Endpoint Descriptor.*/
7, /* bLength: Endpoint Descriptor size */
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */
0x07, /* bEndpointAddress: (OUT7) */
0x02, /* bmAttributes (Bulk). */
0x40, 0x00, /* wMaxPacketSize. */
0x00, /* bInterval (ignored for bulk). */
#endif
};

491
src/usb_msc.c Normal file
View File

@ -0,0 +1,491 @@
#include "usb_lib.h"
#include "config.h"
#include "ch.h"
#include "gnuk.h"
#include "usb_msc.h"
struct usb_endp_in {
const uint8_t *txbuf; /* Pointer to the transmission buffer. */
size_t txsize; /* Transmit transfer size remained. */
size_t txcnt; /* Transmitted bytes so far. */
};
struct usb_endp_out {
uint8_t *rxbuf; /* Pointer to the receive buffer. */
size_t rxsize; /* Requested receive transfer size. */
size_t rxcnt; /* Received bytes so far. */
};
static struct usb_endp_in ep6_in;
static struct usb_endp_out ep7_out;
static Thread *the_thread;
#define ENDP_MAX_SIZE 64
static uint8_t msc_state;
static void usb_stall_transmit (void)
{
SetEPTxStatus (ENDP6, EP_TX_STALL);
}
static void usb_start_transmit (const uint8_t *p, size_t n)
{
size_t pkt_len = n > ENDP_MAX_SIZE ? ENDP_MAX_SIZE : n;
ep6_in.txbuf = p;
ep6_in.txsize = n;
ep6_in.txcnt = 0;
USB_SIL_Write (EP6_IN, (uint8_t *)ep6_in.txbuf, pkt_len);
SetEPTxValid (ENDP6);
}
/* "Data Transmitted" callback */
void EP6_IN_Callback (void)
{
size_t n = (size_t)GetEPTxCount (ENDP6);
ep6_in.txbuf += n;
ep6_in.txcnt += n;
ep6_in.txsize -= n;
if (ep6_in.txsize > 0) /* More data to be sent */
{
if (ep6_in.txsize > ENDP_MAX_SIZE)
n = ENDP_MAX_SIZE;
else
n = ep6_in.txsize;
USB_SIL_Write (EP6_IN, (uint8_t *)ep6_in.txbuf, n);
SetEPTxValid (ENDP6);
return;
}
/* Transmit has been completed, notify the waiting thread */
switch (msc_state)
{
case MSC_SENDING_CSW:
case MSC_DATA_IN:
if (the_thread != NULL) {
Thread *tp;
chSysLockFromIsr ();
tp = the_thread;
the_thread = NULL;
tp->p_u.rdymsg = RDY_OK;
chSchReadyI (tp);
chSysUnlockFromIsr ();
}
break;
default:
break;
}
}
static void usb_stall_receive (void)
{
SetEPRxStatus (ENDP7, EP_RX_STALL);
}
static void usb_start_receive (uint8_t *p, size_t n)
{
ep7_out.rxbuf = p;
ep7_out.rxsize = n;
ep7_out.rxcnt = 0;
SetEPRxValid (ENDP7);
}
/* "Data Received" call back */
void EP7_OUT_Callback (void)
{
size_t n = (size_t)GetEPRxCount (ENDP7);
int err = 0;
if (n > ep7_out.rxsize)
{ /* buffer overflow */
err = 1;
SetEPRxCount (ENDP7, ep7_out.rxsize);
}
n = USB_SIL_Read (EP7_OUT, ep7_out.rxbuf);
ep7_out.rxbuf += n;
ep7_out.rxcnt += n;
ep7_out.rxsize -= n;
if (n == ENDP_MAX_SIZE && ep7_out.rxsize != 0)
{ /* More data to be received */
SetEPRxValid (ENDP7);
return;
}
/* Receiving has been completed, notify the waiting thread */
switch (msc_state)
{
case MSC_IDLE:
case MSC_DATA_OUT:
if (the_thread != NULL) {
Thread *tp;
chSysLockFromIsr ();
tp = the_thread;
the_thread = NULL;
tp->p_u.rdymsg = err? RDY_OK : RDY_RESET;
chSchReadyI (tp);
chSysUnlockFromIsr ();
}
break;
default:
break;
}
}
static const uint8_t lun_buf[4] = {0, 0, 0, 0}; /* One drives: 0 */
static const uint8_t scsi_inquiry_data_00[] = { 0, 0, 0, 0, 0 };
static const uint8_t scsi_inquiry_data[] = {
0x00, /* Direct Access Device. */
0x80, /* RMB = 1: Removable Medium. */
0x05, /* Version: SPC-3. */
0x02, /* Response format: SPC-3. */
36 - 4, /* Additional Length. */
0x00,
0x00,
0x00,
/* Vendor Identification */
'F', 'S', 'I', 'J', ' ', ' ', ' ', ' ',
/* Product Identification */
'V', 'i', 'r', 't', 'u', 'a', 'l', ' ',
'D', 'i', 's', 'k', ' ', ' ', ' ', ' ',
/* Product Revision Level */
'1', '.', '0', ' '
};
static uint8_t scsi_sense_data_desc[] = {
0x72, /* Response Code: descriptor, current */
0x02, /* Sense Key */
0x3a, /* ASC (additional sense code) */
0x00, /* ASCQ (additional sense code qualifier) */
0x00, 0x00, 0x00,
0x00, /* Additional Sense Length */
};
static uint8_t scsi_sense_data_fixed[] = {
0x70, /* Response Code: fixed, current */
0x00,
0x02, /* Sense Key */
0x00, 0x00, 0x00, 0x00,
0x0a, /* Additional Sense Length */
0x00, 0x00, 0x00, 0x00,
0x3a, /* ASC (additional sense code) */
0x00, /* ASCQ (additional sense code qualifier) */
0x00,
0x00, 0x00, 0x00,
};
static void set_scsi_sense_data(uint8_t sense_key, uint8_t asc) {
scsi_sense_data_desc[1] = scsi_sense_data_fixed[2] = sense_key;
scsi_sense_data_desc[2] = scsi_sense_data_fixed[12] = asc;
}
static uint8_t buf[512];
static uint8_t media_available;
static uint8_t media_changed;
void msc_media_insert_change (int available)
{
media_available = available;
media_changed = 1;
if (available)
set_scsi_sense_data (0x06, 0x28); /* UNIT_ATTENTION */
else
set_scsi_sense_data (0x02, 0x3a); /* NOT_READY */
}
static uint8_t scsi_read_format_capacities (uint32_t *nblocks,
uint32_t *secsize)
{
*nblocks = 68;
*secsize = 512;
if (media_available)
return 2; /* Formatted Media.*/
else
return 3; /* No Media.*/
}
static struct CBW CBW;
static struct CSW CSW;
int msc_recv_cbw (void)
{
msg_t msg;
chSysLock();
msc_state = MSC_IDLE;
the_thread = chThdSelf ();
usb_start_receive ((uint8_t *)&CBW, sizeof CBW);
chSchGoSleepTimeoutS (THD_STATE_SUSPENDED, MS2ST (1000));
msg = chThdSelf ()->p_u.rdymsg;
chSysUnlock ();
if (msg == RDY_OK)
return 0;
else
return -1;
}
static int msc_recv_data (void)
{
msg_t msg;
chSysLock ();
msc_state = MSC_DATA_OUT;
the_thread = chThdSelf ();
usb_start_receive (buf, 512);
chSchGoSleepS (THD_STATE_SUSPENDED);
msg = chThdSelf ()->p_u.rdymsg;
chSysUnlock ();
return 0;
}
static void msc_send_data (const uint8_t *p, size_t n)
{
msg_t msg;
chSysLock ();
msc_state = MSC_DATA_IN;
the_thread = chThdSelf ();
usb_start_transmit (p, n);
chSchGoSleepS (THD_STATE_SUSPENDED);
msg = chThdSelf ()->p_u.rdymsg;
CSW.dCSWDataResidue -= (uint32_t)n;
chSysUnlock();
}
static void msc_send_result (const uint8_t *p, size_t n)
{
msg_t msg;
if (p != NULL)
{
if (n > CBW.dCBWDataTransferLength)
n = CBW.dCBWDataTransferLength;
CSW.dCSWDataResidue = CBW.dCBWDataTransferLength;
msc_send_data (p, n);
CSW.bCSWStatus = MSC_CSW_STATUS_PASSED;
}
chSysLock ();
msc_state = MSC_SENDING_CSW;
the_thread = chThdSelf ();
usb_start_transmit ((uint8_t *)&CSW, sizeof CSW);
chSchGoSleepS (THD_STATE_SUSPENDED);
msg = chThdSelf ()->p_u.rdymsg;
chSysUnlock ();
}
void msc_handle_err (int err)
{
if (err == 0)
{
msc_state = MSC_ERROR;
chSysLock ();
usb_stall_receive ();
chSysUnlock ();
}
else
{
msc_state = MSC_ERROR;
chSysLock ();
usb_stall_transmit ();
usb_stall_receive ();
chSysUnlock ();
}
}
int msc_handle_cbw (void)
{
size_t n;
uint32_t nblocks, secsize;
uint8_t lun;
uint32_t lba;
n = ep7_out.rxcnt;
if ((n != sizeof (struct CBW)) || (CBW.dCBWSignature != MSC_CBW_SIGNATURE))
return 0;
CSW.dCSWTag = CBW.dCBWTag;
switch (CBW.CBWCB[0]) {
case SCSI_REQUEST_SENSE:
if (CBW.CBWCB[1] & 0x01) /* DESC */
msc_send_result ((uint8_t *)&scsi_sense_data_desc,
sizeof scsi_sense_data_desc);
else
msc_send_result ((uint8_t *)&scsi_sense_data_fixed,
sizeof scsi_sense_data_fixed);
if (media_changed && media_available)
{
media_changed = 0;
set_scsi_sense_data (0x00, 0x00);
}
return 1;
case SCSI_INQUIRY:
if (CBW.CBWCB[1] & 0x01) /* EVPD */
/* assume page 00 */
msc_send_result ((uint8_t *)&scsi_inquiry_data_00,
sizeof scsi_inquiry_data_00);
else
msc_send_result ((uint8_t *)&scsi_inquiry_data,
sizeof scsi_inquiry_data);
return 1;
case SCSI_READ_FORMAT_CAPACITIES:
buf[8] = scsi_read_format_capacities (&nblocks, &secsize);
buf[0] = buf[1] = buf[2] = 0;
buf[3] = 8;
buf[4] = (uint8_t)(nblocks >> 24);
buf[5] = (uint8_t)(nblocks >> 16);
buf[6] = (uint8_t)(nblocks >> 8);
buf[7] = (uint8_t)(nblocks >> 0);
buf[9] = (uint8_t)(secsize >> 16);
buf[10] = (uint8_t)(secsize >> 8);
buf[11] = (uint8_t)(secsize >> 0);
msc_send_result (buf, 12);
return 1;
case SCSI_TEST_UNIT_READY:
if (media_available == 0 || media_changed)
{
CSW.bCSWStatus = MSC_CSW_STATUS_FAILED;
CSW.dCSWDataResidue = 0;
msc_send_result (NULL, 0);
return 1;
}
/* fall through */
case SCSI_SYNCHRONIZE_CACHE:
case SCSI_VERIFY10:
case SCSI_START_STOP_UNIT:
case SCSI_ALLOW_MEDIUM_REMOVAL:
CSW.bCSWStatus = MSC_CSW_STATUS_PASSED;
CSW.dCSWDataResidue = CBW.dCBWDataTransferLength;
msc_send_result (NULL, 0);
return 1;
case SCSI_MODE_SENSE6:
buf[0] = 0x03;
buf[1] = buf[2] = buf[3] = 0;
msc_send_result (buf, 4);
return 1;
case SCSI_READ_CAPACITY10:
scsi_read_format_capacities (&nblocks, &secsize);
buf[0] = (uint8_t)((nblocks - 1) >> 24);
buf[1] = (uint8_t)((nblocks - 1) >> 16);
buf[2] = (uint8_t)((nblocks - 1) >> 8);
buf[3] = (uint8_t)((nblocks - 1) >> 0);
buf[4] = (uint8_t)(secsize >> 24);
buf[5] = (uint8_t)(secsize >> 16);
buf[6] = (uint8_t)(secsize >> 8);
buf[7] = (uint8_t)(secsize >> 0);
msc_send_result (buf, 8);
return 1;
case SCSI_READ10:
case SCSI_WRITE10:
break;
default:
if (CBW.dCBWDataTransferLength == 0)
{
CSW.bCSWStatus = MSC_CSW_STATUS_FAILED;
CSW.dCSWDataResidue = 0;
msc_send_result (NULL, 0);
return 1;
}
return -1;
}
lun = CBW.bCBWLUN;
lba = (CBW.CBWCB[2] << 24) | (CBW.CBWCB[3] << 16)
| (CBW.CBWCB[4] << 8) | CBW.CBWCB[5];
/* Transfer direction.*/
if (CBW.bmCBWFlags & 0x80)
{
/* IN, Device to Host.*/
msc_state = MSC_DATA_IN;
if (CBW.CBWCB[0] == SCSI_READ10)
{
const uint8_t *p;
CSW.dCSWDataResidue = 0;
while (1)
{
if (CBW.CBWCB[7] == 0 && CBW.CBWCB[8] == 0)
{
CSW.bCSWStatus = MSC_CSW_STATUS_PASSED;
break;
}
if ((p = msc_scsi_read (lun, lba)))
{
msc_send_data (p, 512);
if (++CBW.CBWCB[5] == 0)
if (++CBW.CBWCB[4] == 0)
if (++CBW.CBWCB[3] == 0)
++CBW.CBWCB[2];
if (CBW.CBWCB[8]-- == 0)
CBW.CBWCB[7]--;
CSW.dCSWDataResidue += 512;
}
else
{
CSW.bCSWStatus = MSC_CSW_STATUS_FAILED;
break;
}
}
msc_send_result (NULL, 0);
}
}
else
{
/* OUT, Host to Device.*/
if (CBW.CBWCB[0] == SCSI_WRITE10)
{
CSW.dCSWDataResidue = CBW.dCBWDataTransferLength;
while (1)
{
if (CBW.CBWCB[8] == 0 && CBW.CBWCB[7] == 0)
{
CSW.bCSWStatus = MSC_CSW_STATUS_PASSED;
break;
}
msc_recv_data ();
if (msc_scsi_write (lun, lba, buf, 512))
{
if (++CBW.CBWCB[5] == 0)
if (++CBW.CBWCB[4] == 0)
if (++CBW.CBWCB[3] == 0)
++CBW.CBWCB[2];
if (CBW.CBWCB[8]-- == 0)
CBW.CBWCB[7]--;
CSW.dCSWDataResidue -= 512;
}
else
{
CSW.bCSWStatus = MSC_CSW_STATUS_FAILED;
break;
}
}
msc_send_result (NULL, 0);
}
}
return 1;
}

45
src/usb_msc.h Normal file
View File

@ -0,0 +1,45 @@
#define MSC_CBW_SIGNATURE 0x43425355
#define MSC_CSW_SIGNATURE 0x53425355
#define MSC_GET_MAX_LUN_COMMAND 0xFE
#define MSC_MASS_STORAGE_RESET_COMMAND 0xFF
#define MSC_CSW_STATUS_PASSED 0
#define MSC_CSW_STATUS_FAILED 1
#define SCSI_INQUIRY 0x12
#define SCSI_MODE_SENSE6 0x1A
#define SCSI_ALLOW_MEDIUM_REMOVAL 0x1E
#define SCSI_READ10 0x28
#define SCSI_READ_CAPACITY10 0x25
#define SCSI_REQUEST_SENSE 0x03
#define SCSI_START_STOP_UNIT 0x1B
#define SCSI_TEST_UNIT_READY 0x00
#define SCSI_WRITE10 0x2A
#define SCSI_VERIFY10 0x2F
#define SCSI_READ_FORMAT_CAPACITIES 0x23
#define SCSI_SYNCHRONIZE_CACHE 0x35
#define MSC_IDLE 0
#define MSC_DATA_OUT 1
#define MSC_DATA_IN 2
#define MSC_SENDING_CSW 3
#define MSC_ERROR 4
struct CBW {
uint32_t dCBWSignature;
uint32_t dCBWTag;
uint32_t dCBWDataTransferLength;
uint8_t bmCBWFlags;
uint8_t bCBWLUN;
uint8_t bCBWCBLength;
uint8_t CBWCB[16];
} __attribute__((packed));
struct CSW {
uint32_t dCSWSignature;
uint32_t dCSWTag;
uint32_t dCSWDataResidue;
uint8_t bCSWStatus;
} __attribute__((packed));

271
src/virtual_block_device.c Normal file
View File

@ -0,0 +1,271 @@
#include <stdint.h>
#include "config.h"
#include "ch.h"
#include "board.h"
extern Thread *main_thread;
/*
blk=0: master boot record sector
blk=1: fat0
blk=2: fat1
blk=3: root directory
blk=4: fat cluster #2
...
blk=4+63: fat cluster #2+63
*/
static const uint8_t d0_0_sector[512] = {
0xeb, 0x3c, /* Jump instruction */
0x90, /* NOP */
0x6d, 0x6b, 0x64, 0x6f, 0x73, 0x66, 0x73, 0x20, /* "mkdosfs " */
0x00, 0x02, /* Bytes per sector: 512 */
0x01, /* sectors per cluster: 1 */
0x01, 0x00, /* reserved sector count: 1 */
0x02, /* Number of FATs: 2 */
0x10, 0x00, /* Max. root directory entries: 16 (1 sector) */
0x44, 0x00, /* total sectors: 68 */
0xf8, /* media descriptor: fixed disk */
0x01, 0x00, /* sectors per FAT: 1 */
0x04, 0x00, /* sectors per track: 4 */
0x01, 0x00, /* number of heads: 1 */
0x00, 0x00, 0x00, 0x00, /* hidden sectors: 0 */
0x00, 0x00, 0x00, 0x00, /* total sectors (long) */
0x00, /* drive number */
0x00, /* reserved */
0x29, /* extended boot signature */
0xbf, 0x86, 0x75, 0xea, /* Volume ID (serial number) (Little endian) */
/* Volume label */
'G', 'O', 'N', 'E', 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, /* FAT12 */
0x0e, 0x1f,
0xbe, 0x5b, 0x7c, 0xac, 0x22, 0xc0, 0x74, 0x0b,
0x56, 0xb4, 0x0e, 0xbb, 0x07, 0x00, 0xcd, 0x10,
0x5e, 0xeb, 0xf0, 0x32, 0xe4, 0xcd, 0x16, 0xcd,
0x19,
0xeb, 0xfe, /* loop: jmp loop */
/* "Thisis not a bootable disk... \r\n" */
0x54, 0x68, 0x69, 0x73, 0x20,
0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61,
0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c,
0x65, 0x20, 0x64, 0x69, 0x73, 0x6b, 0x2e, 0x20,
0x20, 0x50, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20,
0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x61,
0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c,
0x65, 0x20, 0x66, 0x6c, 0x6f, 0x70, 0x70, 0x79,
0x20, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x70, 0x72,
0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x79, 0x20,
0x6b, 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x74,
0x72, 0x79, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6e,
0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x0d, 0x0a, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa
};
static const uint8_t d0_fat0_sector[512] = {
0xf8, 0xff, 0xff, /* Media descriptor: fixed disk */ /* EOC */
0xff, 0xff, 0xff, /* cluster 2: used */ /* cluster 3: used */
0xff, 0xff, 0xff, /* cluster 4: used */ /* cluster 5: used */
0xff, 0xff, 0xff, /* cluster 6: used */ /* cluster 7: used */
0xff, 0x0f, 0x00, /* cluster 8: used */ /* cluster 9: free */
0x00,
};
static const uint8_t zero_sector[512] = {
0x00,
};
static uint8_t the_sector[512];
struct folder {
uint8_t parent;
uint8_t children[7];
};
static struct folder folders[8] = { { 0, { 1, 2, 3, 4, 5, 6, 7 } }, };
#define FOLDER_INDEX_TO_CLUSTER_NO(i) (i+1)
#define CLUSTER_NO_TO_FOLDER_INDEX(n) (n-1)
#define FOLDER_INDEX_TO_LBA(i) (i+3)
#define LBA_TO_FOLDER_INDEX(lba) (lba-3)
#define FOLDER_INDEX_TO_DIRCHAR(i) ('A'+i-1)
#define DIRCHAR_TO_FOLDER_INDEX(c) (c - 'A' + 1)
static uint8_t *fill_file_entry (uint8_t *p, const uint8_t *filename,
uint16_t cluster_no)
{
memcpy (p, filename, 8+3);
p += 11;
*p++ = 0x10; /* directory */
*p++ = 0x00; /* reserved */
memcpy (p, "\x64\x3b\xa7\x61\x3f", 5); /* create time */
p += 5;
memcpy (p, "\x61\x3f", 2); /* last access */
p += 2;
*p++ = 0x00; *p++ = 0x00; /* ea-index */
memcpy (p, "\x3b\xa7\x61\x3f", 4); /* last modified */
p += 4;
memcpy (p, &cluster_no, 2); /* cluster # */
p += 2;
*p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; /* file size */
return p;
}
static const uint8_t *build_directory_sector (uint8_t *p, uint8_t index)
{
uint16_t cluster_no = FOLDER_INDEX_TO_CLUSTER_NO (index);
int i;
uint8_t filename[11] = { 0x2e, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20 };
uint8_t child;
uint8_t *p_orig = p;
memset (p, 0, 512);
if (index != 0)
{
p = fill_file_entry (p, filename, cluster_no);
filename[1] = 0x2e;
p = fill_file_entry (p, filename, 0);
filename[1] = 0x20;
}
for (i = 0; i < 7; i++)
if ((child = folders[index].children[i]) != 0)
{
filename[0] = FOLDER_INDEX_TO_DIRCHAR (child);
p = fill_file_entry (p, filename, FOLDER_INDEX_TO_CLUSTER_NO (child));
}
else
break;
return p_orig;
}
const uint8_t *
msc_scsi_read (uint8_t lun, uint32_t lba)
{
switch (lba)
{
case 0:
return d0_0_sector;
case 1:
case 2:
return d0_fat0_sector;
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
return build_directory_sector (the_sector, LBA_TO_FOLDER_INDEX (lba));
default:
return zero_sector;
}
}
uint8_t datetime_string[29];
static uint8_t *dts = datetime_string;
static void parse_directory_sector (const uint8_t *p, uint8_t index)
{
uint16_t cluster_no = FOLDER_INDEX_TO_CLUSTER_NO (index);
int i;
uint8_t filename[11] = { 0x2e, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20 };
uint8_t child;
uint8_t *p_orig = p;
if (index != 0)
{
uint16_t cluster_no;
uint8_t dest_index;
p += 32; /* skip "." */
/* ".." */
cluster_no = p[26] | (p[27] << 8);
dest_index = CLUSTER_NO_TO_FOLDER_INDEX (cluster_no);
if (dest_index >= 1 && dest_index <= 7)
dts += sprintf (dts, "%c%c ", FOLDER_INDEX_TO_DIRCHAR (index),
FOLDER_INDEX_TO_DIRCHAR (dest_index));
else
; /* can be 255 : root_dir */
p += 32;
}
for (i = 0; i < 7; i++)
if (*p)
{
child = DIRCHAR_TO_FOLDER_INDEX (*p);
folders[index].children[i] = child;
p += 32;
}
else
break;
}
int
msc_scsi_write (uint8_t lun, uint32_t lba, const uint8_t *buf, size_t size)
{
if (lba <= 2)
return 1; /* error */
else
{
uint8_t index = LBA_TO_FOLDER_INDEX (lba);
parse_directory_sector (buf, index);
}
return 1;
}