diff --git a/Virtual_COM_Port/usb_desc.h b/Virtual_COM_Port/usb_desc.h index df0476a..bc3cc57 100644 --- a/Virtual_COM_Port/usb_desc.h +++ b/Virtual_COM_Port/usb_desc.h @@ -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 diff --git a/src/main.c b/src/main.c index 655aa59..edd62d7 100644 --- a/src/main.c +++ b/src/main.c @@ -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; diff --git a/src/usb_conf.h b/src/usb_conf.h index 34a9b36..17d738d 100644 --- a/src/usb_conf.h +++ b/src/usb_conf.h @@ -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 ) diff --git a/src/usb_desc.c b/src/usb_desc.c index 50ab1c3..00b0f17 100644 --- a/src/usb_desc.c +++ b/src/usb_desc.c @@ -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 }; diff --git a/src/usb_msc.c b/src/usb_msc.c new file mode 100644 index 0000000..cd8cac7 --- /dev/null +++ b/src/usb_msc.c @@ -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; +} diff --git a/src/usb_msc.h b/src/usb_msc.h new file mode 100644 index 0000000..3a117e0 --- /dev/null +++ b/src/usb_msc.h @@ -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)); diff --git a/src/virtual_block_device.c b/src/virtual_block_device.c new file mode 100644 index 0000000..51cadca --- /dev/null +++ b/src/virtual_block_device.c @@ -0,0 +1,271 @@ +#include + +#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; +}