X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fdevices%2Fusb_storage.c;fp=src%2Fdevices%2Fusb_storage.c;h=a5e3ad1272099cbee47d8e54ed5113ceb1dc990b;hb=b4e9c266d656c6b595cc57920a34937776acc300;hp=0000000000000000000000000000000000000000;hpb=6ffbc2b68c34c2d1e42d5f6bcd8f2b94b82d05d7;p=pintos-anon diff --git a/src/devices/usb_storage.c b/src/devices/usb_storage.c new file mode 100644 index 0000000..a5e3ad1 --- /dev/null +++ b/src/devices/usb_storage.c @@ -0,0 +1,441 @@ +/** + * USB mass storage driver - just like the one Elvis used! + */ + +#include +#include +#include +#include +#include +#include +#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); +}