usb.patch, with conflicts and some warnings fixed
[pintos-anon] / src / devices / usb_storage.c
diff --git a/src/devices/usb_storage.c b/src/devices/usb_storage.c
new file mode 100644 (file)
index 0000000..a5e3ad1
--- /dev/null
@@ -0,0 +1,441 @@
+/**
+ * USB mass storage driver - just like the one Elvis used!
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <kernel/list.h>
+#include <endian.h>
+#include <debug.h>
+#include "threads/malloc.h"
+#include "threads/palloc.h"
+#include "threads/thread.h"
+#include "devices/timer.h"
+#include "threads/synch.h"
+#include "threads/pte.h"
+#include "devices/usb.h"
+#include "devices/block.h"
+#include "devices/partition.h"
+#include "devices/usb_storage.h"
+
+//http://www.usb.org/developers/defined_class
+#define USB_CLASS_MASS_STORAGE 0x08
+
+#define USB_SUBCLASS_RBC       0x01    /* reduced block commands */
+#define USB_SUBCLASS_ATAPI     0x02    /* ATAPI - CD's */
+#define USB_SUBCLASS_TAPE      0x03    
+#define USB_SUBCLASS_UFI       0x04    /* floppy disk */
+#define USB_SUBCLASS_SFF8070   0x05    
+#define USB_SUBCLASS_SCSI      0x06    /* scsi transparent command set */
+
+#define USB_PROTO_COMPLETE     0x00
+#define USB_PROTO_NO_COMPLETE  0x01
+#define USB_PROTO_BULK         0x50
+
+#pragma pack(1)
+
+#define CBW_SIG_MAGIC  0x43425355
+
+#define CBW_FL_IN              (1 << 7)
+#define CBW_FL_OUT             0
+
+/** usbmassbulk_10.pdf - pg 13 */
+/* command block wrapper */
+struct msc_cbw
+{
+  uint32_t sig;
+  uint32_t tag;
+  uint32_t tx_len;
+  uint8_t flags;
+  uint8_t lun;
+  uint8_t cb_len;              /* command block length */
+  uint8_t cb[16];              /* command block */
+};
+
+#define CSW_SIG_MAGIC          0x53425355
+#define CSW_STATUS_PASSED      0
+#define CSW_STATUS_FAILED      1
+#define CSW_STATUS_PHASE       2       /* phase error */
+/* command status wrapper */
+struct msc_csw
+{
+  uint32_t sig;
+  uint32_t tag;
+  uint32_t residue;
+  uint8_t status;
+};
+
+struct scsi_cdb6
+{
+  uint8_t op;
+  uint8_t lba[3];
+  uint8_t len;
+  uint8_t control;
+};
+
+struct scsi_cdb10
+{
+  uint8_t op;
+  uint8_t action;
+  uint32_t lba;
+  uint8_t resv;
+  uint16_t len;
+  uint8_t control;
+};
+
+struct scsi_cdb12
+{
+  uint8_t op;
+  uint8_t action;
+  uint32_t lba;
+  uint32_t len;
+  uint8_t resv;
+  uint8_t control;
+};
+
+struct scsi_cdb16
+{
+  uint8_t op;
+  uint8_t action;
+  uint32_t lba;
+  uint32_t data;
+  uint32_t len;
+  uint8_t resv;
+  uint8_t control;
+};
+
+struct scsi_capacity10
+{
+  uint32_t blocks;
+  uint32_t block_len;
+};
+
+#define SCSI_OP_WRITE6                 0xa
+#define SCSI_OP_READ6                  0x8
+#define SCSI_OP_TEST_READY             0x00
+#define SCSI_OP_SEEK8                  0x0b
+#define SCSI_OP_MODE_SENSE8            0x1a
+#define SCSI_OP_MODE_SELECT8           0x15
+#define SCSI_OP_READ_CAPACITY10                0x25
+#define SCSI_OP_READ_CAPACITY16                0x9e
+#define SCSI_OP_WRITE10                        0x2a
+#define SCSI_OP_READ10                 0x28
+#define SCSI_OP_SEEK10                 0x2b
+#define SCSI_OP_MODE_SENSE10           0x5a
+#define SCSI_OP_MODE_SELECT10          0x55
+
+
+#pragma pack()
+
+static void *msc_attached (struct usb_iface *);
+static void msc_detached (class_info);
+
+static void msc_reset_endpoint (struct usb_endpoint *eop);
+
+static struct usb_class storage_class = {
+  .attached = msc_attached,
+  .detached = msc_detached,
+  .name = "Mass Storage",
+  .class_id = USB_CLASS_MASS_STORAGE
+};
+
+#define mci_lock(x)            lock_acquire(&(x)->lock)
+#define mci_unlock(x)          lock_release(&(x)->lock)
+
+struct msc_class_info
+{
+  int refs;
+  struct lock lock;
+  struct usb_iface *ui;
+  int blk_size;
+  int blk_count;
+  bool retrying;
+  uint32_t tag;
+  void *bounce_buffer;
+
+  struct usb_endpoint *eop_in;
+  struct usb_endpoint *eop_out;
+  struct list_elem peers;
+};
+static void msc_get_geometry (struct msc_class_info *);
+static void msc_io (struct msc_class_info *, block_sector_t, void *buf, bool wr);
+static void msc_reset_recovery(struct msc_class_info* mci);
+static void msc_bulk_reset(struct msc_class_info* mci);
+
+struct msc_blk_info
+{
+  struct msc_class_info *mci;
+};
+
+static struct list device_list;
+static struct block_operations msc_operations;
+
+void usb_storage_init (void);
+
+void
+usb_storage_init (void)
+{
+  list_init (&device_list);
+  usb_register_class (&storage_class);
+}
+
+
+static class_info
+msc_attached (struct usb_iface *ui)
+{
+  static int dev_no;
+
+  struct msc_class_info *mci;
+  struct msc_blk_info *mbi;
+  struct list_elem *li;
+  struct block *block;
+  char name[16];
+
+  if (ui->subclass_id != USB_SUBCLASS_SCSI)
+    {
+      printf ("usb_storage: Only support SCSI-type devices\n");
+      return NULL;
+    }
+
+  mci = malloc (sizeof (struct msc_class_info));
+
+  mci->refs = 0;
+  mci->retrying = false;
+  lock_init (&mci->lock);
+  mci->ui = ui;
+  list_push_back (&device_list, &mci->peers);
+
+  /* grab endpoints */
+  li = list_begin (&ui->endpoints);
+  mci->eop_in = NULL;
+  mci->eop_out = NULL;
+  while (li != list_end (&ui->endpoints))
+    {
+      struct usb_endpoint *ue;
+
+      ue = list_entry (li, struct usb_endpoint, peers);
+      li = list_next (li);
+
+      if (ue->attr == USB_EOP_ATTR_BULK)
+       {
+         if (ue->direction == 0)
+           mci->eop_out = ue;
+         else
+           mci->eop_in = ue;
+       }
+    }
+
+  ASSERT (mci->eop_in != NULL && mci->eop_out != NULL);
+
+  msc_get_geometry (mci);
+
+  if (mci->blk_size != 512)
+    {
+      printf ("ignoring device with %d-byte sectors\n", mci->blk_size);
+      return NULL;
+    }
+
+  mci->bounce_buffer = palloc_get_multiple (PAL_ASSERT,
+                                           DIV_ROUND_UP (mci->blk_size,
+                                                         PGSIZE));
+  mbi = malloc (sizeof (struct msc_blk_info));
+  mbi->mci = mci;
+  snprintf (name, sizeof name, "ud%c", 'a' + dev_no++);
+  block = block_register (name, BLOCK_RAW, "USB", mci->blk_count,
+                          &msc_operations, mbi);
+  partition_scan (block);
+                          
+  return mci;
+}
+
+static void
+msc_detached (class_info ci UNUSED)
+{
+  PANIC ("msc_detached: STUB");
+}
+
+static void
+msc_read (void *mbi_, block_sector_t sector, void *buffer) 
+{
+  struct msc_blk_info *mbi = mbi_;
+
+  mci_lock (mbi->mci);
+  msc_io (mbi->mci, sector, buffer, false);
+  mci_unlock (mbi->mci);
+}
+
+static void
+msc_write (void *mbi_, block_sector_t sector, const void *buffer)
+{
+  struct msc_blk_info *mbi = mbi_;
+
+  mci_lock (mbi->mci);
+  msc_io (mbi->mci, sector, (void *) buffer, true);
+  mci_unlock (mbi->mci);
+}
+
+static struct block_operations msc_operations = 
+  {
+    msc_read,
+    msc_write,
+  };
+
+static void
+msc_get_geometry (struct msc_class_info *mci)
+{
+  struct msc_cbw cbw;
+  struct scsi_capacity10 *cap;
+  uint8_t buf[sizeof (struct msc_csw) + sizeof (struct scsi_capacity10)];
+  struct scsi_cdb10 *cdb;
+  struct msc_csw *csw;
+  int tx;
+
+  /* cap + csw must be read in one shot, combine into a single buffer */
+  cap = (struct scsi_capacity10 *) (buf);
+  csw = (struct msc_csw *) (&buf[sizeof (struct scsi_capacity10)]);
+
+  cbw.sig = CBW_SIG_MAGIC;
+  cbw.tag = mci->tag++;
+  cbw.tx_len = sizeof (struct scsi_capacity10);
+  cbw.flags = CBW_FL_IN;
+  cbw.lun = 0;
+  cbw.cb_len = sizeof (struct scsi_cdb10);
+
+  cdb = (void *) (&cbw.cb);
+  memset (cdb, 0, sizeof (struct scsi_cdb10));
+  cdb->op = SCSI_OP_READ_CAPACITY10;
+
+  usb_dev_bulk (mci->eop_out, &cbw, sizeof (cbw), &tx);
+  usb_dev_bulk (mci->eop_in, &buf, sizeof (buf), &tx);
+
+  mci->blk_count = be32_to_machine (cap->blocks);
+  mci->blk_size = be32_to_machine (cap->block_len);
+
+  /* did CSW stall?  */
+  if (tx == sizeof (struct scsi_capacity10))
+    {
+      msc_reset_endpoint (mci->eop_in);
+      usb_dev_bulk (mci->eop_in, csw, sizeof (*csw), &tx);
+    }
+
+  ASSERT (csw->sig == CSW_SIG_MAGIC);
+
+  if (csw->status != CSW_STATUS_PASSED)
+    {
+      PANIC ("USB storage geometry read failure!\n");
+    }
+
+}
+
+static void
+msc_io (struct msc_class_info *mci, block_sector_t bn, void *buf, bool wr)
+{
+  struct msc_cbw cbw;
+  struct msc_csw csw;
+  struct scsi_cdb10 *cdb;
+  int tx;
+  int err;
+
+  if (wr)
+    memcpy (mci->bounce_buffer, buf, mci->blk_size);
+
+  memset (&cbw, 0, sizeof (cbw));
+  cbw.sig = CBW_SIG_MAGIC;
+  cbw.tag = mci->tag++;
+  cbw.tx_len = mci->blk_size;
+  cbw.flags = (wr) ? CBW_FL_OUT : CBW_FL_IN;
+  cbw.lun = 0;
+  cbw.cb_len = sizeof (struct scsi_cdb10);
+
+  cdb = (void *) (&cbw.cb);
+  cdb->op = (wr) ? SCSI_OP_WRITE10 : SCSI_OP_READ10;
+  *((uint32_t *) ((uint8_t *) (&cdb->lba) + 1)) = machine_to_be24 (bn);
+  cdb->len = machine_to_be16 (1);
+
+//  msc_reset_endpoint (mci->eop_in);
+//  msc_reset_endpoint (mci->eop_out);
+
+  /* set it up */
+  err = usb_dev_bulk (mci->eop_out, &cbw, sizeof (cbw), &tx);
+  if (err != 0)
+    {
+      msc_reset_endpoint (mci->eop_out);
+      err = usb_dev_bulk (mci->eop_out, &cbw, sizeof (cbw), &tx);
+    }
+
+  /* do storage io */
+  err = usb_dev_bulk ((wr) ? mci->eop_out : mci->eop_in,
+                     mci->bounce_buffer, mci->blk_size, &tx);
+  memset (&csw, 0, sizeof (csw));
+  ASSERT (tx == mci->blk_size);
+
+
+  /* get command status */
+  err = usb_dev_bulk (mci->eop_in, &csw, sizeof (csw), &tx);
+  if (err != 0)
+    {
+      msc_reset_endpoint (mci->eop_in);
+      msc_reset_endpoint (mci->eop_out);
+      err = usb_dev_bulk (mci->eop_in, &csw, sizeof (csw), &tx);
+      if (err != 0)
+       PANIC ("msc_io: error %d\n", err);
+    }
+
+  if (csw.sig != CSW_SIG_MAGIC)
+    {
+      if (mci->retrying == true)
+        PANIC ("usb_msd: CSW still missing. Bail out\n");
+      printf ("usb_msd: no command status, resetting. Buggy device?\n");
+      msc_reset_recovery(mci);
+      printf ("reset complete\n");
+      mci->retrying = true;
+      msc_io (mci, bn, buf, wr);
+      return;
+    }
+  mci->retrying = false;
+
+  if (csw.status != CSW_STATUS_PASSED)
+    {
+      PANIC ("USB storage IO failure! - error %d\n", csw.status);
+    }
+  if (!wr)
+    memcpy (buf, mci->bounce_buffer, mci->blk_size);
+}
+
+static void
+msc_reset_endpoint (struct usb_endpoint *eop)
+{
+  struct usb_setup_pkt sp;
+
+  sp.recipient = USB_SETUP_RECIP_ENDPT;
+  sp.type = USB_SETUP_TYPE_STD;
+  sp.direction = 0;
+  sp.request = REQ_STD_CLR_FEAT;
+  sp.value = 0;                        /* 0 is ENDPOINT_HALT */
+  sp.index = eop->eop;
+  sp.length = 0;
+  usb_dev_setup (eop, true, &sp, NULL, 0);
+}
+
+static void
+msc_reset_recovery(struct msc_class_info* mci)
+{
+  msc_bulk_reset (mci);
+  msc_reset_endpoint (mci->eop_in);
+  msc_reset_endpoint (mci->eop_out);
+}
+
+static void msc_bulk_reset(struct msc_class_info* mci)
+{
+  struct usb_setup_pkt sp;
+
+  sp.recipient = USB_SETUP_RECIP_DEV;
+  sp.type = USB_SETUP_TYPE_CLASS;
+  sp.direction = 0;
+  sp.request = 0xff;
+  sp.value = 0;                 
+  sp.index = mci->ui->iface_num;
+  sp.length = 0;
+  usb_dev_setup (&mci->ui->dev->cfg_eop, true, &sp, NULL, 0);
+}