usb mass storage class
authorNIIBE Yutaka <gniibe@fsij.org>
Thu, 8 Dec 2011 02:00:26 +0000 (11:00 +0900)
committerNIIBE Yutaka <gniibe@fsij.org>
Thu, 8 Dec 2011 02:00:26 +0000 (11:00 +0900)
Virtual_COM_Port/usb_desc.h
src/main.c
src/usb_conf.h
src/usb_desc.c
src/usb_msc.c [new file with mode: 0644]
src/usb_msc.h [new file with mode: 0644]
src/virtual_block_device.c [new file with mode: 0644]

index df0476a..bc3cc57 100644 (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
index 655aa59..edd62d7 100644 (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;
index 34a9b36..17d738d 100644 (file)
 /* 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 )
 
index 50ab1c3..00b0f17 100644 (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 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 W_TOTAL_LENGTH (9+9+54+7+7)
-#define NUM_INTERFACES 1       /* GPG only */
+#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 (file)
index 0000000..cd8cac7
--- /dev/null
@@ -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 (file)
index 0000000..3a117e0
--- /dev/null
@@ -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 (file)
index 0000000..51cadca
--- /dev/null
@@ -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;
+}