usb-bug-fixes.patch (applied cleanly) blp-usb
authorBen Pfaff <blp@cs.stanford.edu>
Fri, 12 Dec 2008 05:08:14 +0000 (21:08 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Fri, 12 Dec 2008 05:08:14 +0000 (21:08 -0800)
src/devices/usb.c
src/devices/usb.h
src/devices/usb_storage.c
src/devices/usb_uhci.c
src/threads/malloc.c

index 7cdb2f07f8b5e77363bc940ee682f13dc01fe9e9..5eb670de78886f429e4024efa3ed6dfc4bb51f97 100644 (file)
@@ -4,6 +4,7 @@
 #include "threads/malloc.h"
 #include "devices/timer.h"
 #include <kernel/bitmap.h>
+#include <ctype.h>
 #include <string.h>
 #include <stdio.h>
 
@@ -136,10 +137,7 @@ static char *usb_get_string (struct usb_dev *d, int ndx);
 static struct class *usb_get_class_by_id (int id);
 static void usb_setup_dev_addr (struct usb_dev *dev);
 static void usb_config_dev (struct usb_dev *dev, int config_val);
-static int usb_load_config (struct usb_dev *dev, int idx, void *data,
-                           int dsz);
-static int usb_tx_all (struct usb_endpoint *eop, void *buf,
-                      int max_bytes, int bailout, bool in);
+static int usb_load_config (struct usb_dev *dev, int idx);
 static void usb_attach_interfaces (struct usb_dev *dev);
 static void usb_apply_class_to_interfaces (struct class *c);
 static size_t wchar_to_ascii (char *dst, const char *src);
@@ -241,16 +239,16 @@ usb_configure_default (struct host *h)
 {
   struct usb_dev *dev;
   struct usb_setup_pkt sp;
-  char data[256];
-  struct device_descriptor *dd;
+  struct device_descriptor dd;
   host_dev_info hi;
   host_eop_info cfg_eop;
   bool ignore_device = false;
-  int err, sz, txed;
+  int err;
   int config_val;
+  size_t size;
 
   hi = h->dev->create_dev_channel (h->info, ADDR_DEFAULT, USB_VERSION_1_1);
-  cfg_eop = h->dev->create_eop (hi, 0, 64);
+  cfg_eop = h->dev->create_eop (hi, 0, 8);
 
   /* determine device descriptor */
   sp.recipient = USB_SETUP_RECIP_DEV;
@@ -259,53 +257,36 @@ usb_configure_default (struct host *h)
   sp.request = REQ_STD_GET_DESC;
   sp.value = SETUP_DESC_DEVICE << 8;
   sp.index = 0;
-  sp.length = sizeof (struct device_descriptor);
+  sp.length = 8;
 
-  err =
-    h->dev->tx_pkt (cfg_eop, USB_TOKEN_SETUP, &sp, 0, sizeof (sp), NULL,
-                   true);
-  /* did it timeout? - no device */
-  if (err != USB_HOST_ERR_NONE)
+  size = sp.length;
+  err = h->dev->dev_control (cfg_eop, &sp, &dd, &size); 
+  if (err)
     {
-      h->dev->remove_eop (cfg_eop);
-      h->dev->remove_dev_channel (hi);
+      /* FIXME: free memory. */
+      printf ("err=%d: no more devices\n", err);
       return NULL;
     }
 
-  dd = (void *) &data;
-  memset (dd, 0, sizeof (data));
-  txed = 0;
-  h->dev->tx_pkt (cfg_eop, USB_TOKEN_IN, data, 0, sizeof (data) - txed, &sz,
-                 true);
-
-  /* some devices only send 16 bytes for the device descriptor - 
-   * they wind up with a NAK, waiting for another command to be issued
-   *
-  * if it's a 16 byte max packet device, we just take the first 16 bytes */
-
-  if (dd->usb_spec == USB_VERSION_1_0)
+  if (dd.usb_spec == USB_VERSION_1_0)
     {
       /* USB 1.0 devices have strict schedule requirements not yet supported */
       printf ("USB 1.0 device detected - skipping\n");
     }
-  else if (sz == 8)
-    {
-      /* read in the full descriptor */
-      txed = sz;
-      while (txed <= 16)
-       {
-         if (h->dev->
-             tx_pkt (cfg_eop, USB_TOKEN_IN, data + txed, 0,
-                     sizeof (data) - txed, &sz, true) != USB_HOST_ERR_NONE)
-           break;
-         txed += sz;
-       }
-    }
-  if (dd->num_configs == 0)
+  if (dd.num_configs == 0)
     ignore_device = true;
 
   h->dev->remove_eop (cfg_eop);
-  cfg_eop = h->dev->create_eop (hi, 0, dd->max_pktsz);
+  cfg_eop = h->dev->create_eop (hi, 0, dd.max_pktsz);
+
+  sp.length = sizeof dd;
+  size = sp.length;
+  err = h->dev->dev_control (cfg_eop, &sp, &dd, &size);
+  if (err)
+    {
+      /* FIXME: free memory. */
+      return NULL;
+    }
 
   /* device exists - create device structure */
   dev = malloc (sizeof (struct usb_dev));
@@ -318,37 +299,38 @@ usb_configure_default (struct host *h)
 
   dev->cfg_eop.h_eop = cfg_eop;
 
-  dev->usb_version = dd->usb_spec;
-  dev->default_iface.class_id = dd->dev_class;
-  dev->default_iface.subclass_id = dd->dev_subclass;
+  dev->usb_version = dd.usb_spec;
+  dev->default_iface.class_id = dd.dev_class;
+  dev->default_iface.subclass_id = dd.dev_subclass;
   dev->default_iface.iface_num = 0;
-  dev->default_iface.proto = dd->dev_proto;
+  dev->default_iface.proto = dd.dev_proto;
   dev->default_iface.class = NULL;
   dev->default_iface.c_info = NULL;
   dev->default_iface.dev = dev;
 
   dev->cfg_eop.iface = &dev->default_iface;
-  dev->cfg_eop.max_pkt = dd->max_pktsz;
+  dev->cfg_eop.max_pkt = dd.max_pktsz;
   dev->cfg_eop.eop = 0;
 
   dev->host = h;
 
-  dev->vendor_id = dd->vendor_id;
-  dev->product_id = dd->product_id;
-  dev->device_id = dd->device_id;
+  dev->vendor_id = dd.vendor_id;
+  dev->product_id = dd.product_id;
+  dev->device_id = dd.device_id;
 
   if (ignore_device == false)
     {
-      dev->product = usb_get_string (dev, dd->product);
-      dev->manufacturer = usb_get_string (dev, dd->manufacturer);
+      dev->product = usb_get_string (dev, dd.product);
+      dev->manufacturer = usb_get_string (dev, dd.manufacturer);
+      printf ("product=%s manufacturer=%s\n", dev->product, dev->manufacturer);
     }
 
   config_val = -123;
   /* read in configuration data if there are configurations available */
   /* (which there should be...) */
-  if (dd->num_configs > 0 && ignore_device == false)
+  if (dd.num_configs > 0 && ignore_device == false)
     {
-      config_val = usb_load_config (dev, 0, data, sizeof (data));
+      config_val = usb_load_config (dev, 0);
       if (config_val < 0)
        {
          printf
@@ -370,14 +352,16 @@ usb_configure_default (struct host *h)
  * XXX support multiple configurations
  */
 static int
-usb_load_config (struct usb_dev *dev, int idx, void *data, int dsz)
+usb_load_config (struct usb_dev *dev, int idx)
 {
   struct usb_setup_pkt sp;
   struct config_descriptor *cd;
   struct host *h;
   host_eop_info cfg;
   void *ptr;
-  int config_val, err, sz;
+  int config_val, err;
+  size_t size;
+  uint8_t data[256];
   int i;
 
   h = dev->host;
@@ -389,34 +373,29 @@ usb_load_config (struct usb_dev *dev, int idx, void *data, int dsz)
   sp.request = REQ_STD_GET_DESC;
   sp.value = SETUP_DESC_CONFIG << 8 | idx;
   sp.index = 0;
-  sp.length = dsz;
-  cd = data;
+  sp.length = sizeof data;
+  cd = (struct config_descriptor *) data;
 
-  err = h->dev->tx_pkt (cfg, USB_TOKEN_SETUP, &sp, 0, sizeof (sp), &sz, true);
+  size = sp.length;
+  err = h->dev->dev_control (cfg, &sp, data, &size);
   if (err != USB_HOST_ERR_NONE)
     {
       printf ("USB: Could not setup GET descriptor\n");
       return -err;
     }
 
-  sz = usb_tx_all (&dev->cfg_eop, cd, dsz,
-                  sizeof (struct config_descriptor), true);
-  if (sz < sizeof (struct config_descriptor))
+  if (size < sizeof *cd || cd->hdr.type != SETUP_DESC_CONFIG)
     {
-      printf ("USB: Did not rx GET descriptor (%d bytes, expected %d)\n", sz,
-             sizeof (struct config_descriptor));
-      return -err;
+      printf ("USB: Invalid descriptor\n");
+      return -1;
     }
 
-  if (sz == 0 || cd->hdr.type != SETUP_DESC_CONFIG)
+  if (size < cd->total_length) 
     {
-      printf ("USB: Invalid descriptor\n");
+      printf ("USB: configuration data too long\n");
       return -1;
     }
 
-  if (sz < cd->total_length)
-    sz += usb_tx_all (&dev->cfg_eop, data+sz, dsz, cd->total_length - sz, true);
-
   dev->pwr = cd->max_power;
 
   /* interface information comes right after config data */
@@ -499,13 +478,9 @@ usb_config_dev (struct usb_dev *dev, int config_val)
   sp.value = config_val;
   sp.index = 0;
   sp.length = 0;
-  err = h->dev->tx_pkt (cfg, USB_TOKEN_SETUP, &sp, 0, sizeof (sp), NULL, true);
-  if (err != USB_HOST_ERR_NONE)
-    PANIC ("USB: Config setup packet did not tx\n");
-
-  err = h->dev->tx_pkt (cfg, USB_TOKEN_IN, NULL, 0, 0, NULL, true);
+  err = h->dev->dev_control (cfg, &sp, 0, NULL);
   if (err != USB_HOST_ERR_NONE)
-    PANIC ("USB: Could not configure device!\n");
+    PANIC ("USB: Could not configure device (err=%d)", err);
 }
 
 /**
@@ -533,13 +508,7 @@ usb_setup_dev_addr (struct usb_dev *dev)
   sp.value = dev->addr;
   sp.index = 0;
   sp.length = 0;
-  err =
-    h->dev->tx_pkt (cfg, USB_TOKEN_SETUP, &sp, 0, sizeof (sp), NULL, true);
-  if (err != USB_HOST_ERR_NONE)
-    {
-      PANIC ("USB: WHOOPS!!!!!!!\n");
-    }
-  err = h->dev->tx_pkt (cfg, USB_TOKEN_IN, NULL, 0, 0, NULL, true);
+  err = h->dev->dev_control (cfg, &sp, 0, NULL);
   if (err != USB_HOST_ERR_NONE)
     {
       PANIC ("USB: Error on setting device address (err = %d)\n", err);
@@ -554,9 +523,10 @@ static char *
 usb_get_string (struct usb_dev *udev, int ndx)
 {
   struct usb_setup_pkt sp;
-  char str[MAX_USB_STR];
+  char str[MAX_USB_STR + 1];
   char *ret;
-  int sz;
+  size_t size;
+  int err;
 
   sp.recipient = USB_SETUP_RECIP_DEV;
   sp.type = USB_SETUP_TYPE_STD;
@@ -564,26 +534,20 @@ usb_get_string (struct usb_dev *udev, int ndx)
   sp.request = REQ_STD_GET_DESC;
   sp.value = (SETUP_DESC_STRING << 8) | ndx;
   sp.index = 0;
-  sp.length = MAX_USB_STR;
-  udev->host->dev->tx_pkt (udev->h_cfg_eop, USB_TOKEN_SETUP,
-                          &sp, 0, sizeof (sp), NULL, false);
-  sz = usb_tx_all (&udev->cfg_eop, &str, MAX_USB_STR, 2, true);
-  sz +=
-    usb_tx_all (&udev->cfg_eop, str + sz, (uint8_t) (str[0]) - sz, 0, true);
-
-  /* string failed to tx? */
-  if (sz == 0)
+  size = sp.length = MAX_USB_STR;
+  err = udev->host->dev->dev_control (udev->h_cfg_eop, &sp, str, &size);
+  if (err)
     return NULL;
 
   /* some devices don't respect the string descriptor length value (str[0]) 
    * and just send any old value they want, so we can't use it */
-
-  str[(sz < (MAX_USB_STR - 1)) ? (sz) : (MAX_USB_STR - 1)] = '\0';
+  str[size] = '\0';
 
   /* usb uses wchars for strings, convert to ASCII */
   wchar_to_ascii (str, str + 2);
   ret = malloc (strlen (str) + 1);
-  strlcpy (ret, str, MAX_USB_STR);
+  if (ret != NULL)
+    strlcpy (ret, str, MAX_USB_STR);
   return ret;
 }
 
@@ -679,47 +643,31 @@ usb_apply_class_to_interfaces (struct class *c)
 }
 
 int
-usb_dev_bulk (struct usb_endpoint *eop, void *buf, int sz, int *tx)
+usb_dev_control (struct usb_endpoint *eop, struct usb_setup_pkt *setup,
+                 void *data, size_t *size) 
 {
   struct host *h;
   int err;
-  int token;
 
-  ASSERT (eop != NULL);
   h = eop->iface->dev->host;
-
-  if (eop->direction == 0)
-    token = USB_TOKEN_OUT;
-  else
-    token = USB_TOKEN_IN;
-
-  err = h->dev->tx_pkt (eop->h_eop, token, buf, sz, sz, tx, true);
-
+  err = h->dev->dev_control (eop->h_eop, setup, data, size);
   return err;
 }
 
 int
-usb_dev_setup (struct usb_endpoint *eop, bool in,
-              struct usb_setup_pkt *s, void *buf, int sz)
+usb_dev_bulk (struct usb_endpoint *eop, void *buf, int sz, int *tx)
 {
   struct host *h;
+  size_t size;
   int err;
 
-  ASSERT (eop != NULL);
   h = eop->iface->dev->host;
 
-  err = h->dev->tx_pkt (eop->h_eop, USB_TOKEN_SETUP, s,
-                       0, sizeof (struct usb_setup_pkt), NULL, true);
-  if (err != USB_HOST_ERR_NONE)
-    {
-      printf ("usb_dev_setup: failed\n");
-      return 0;
-    }
-
-  err = h->dev->tx_pkt (eop->h_eop, (in) ? USB_TOKEN_IN : USB_TOKEN_OUT,
-                       buf, 0, sz, &sz, true);
-
-  return sz;
+  size = sz;
+  err = h->dev->dev_bulk (eop->h_eop, eop->direction == 0, buf, &size);
+  *tx = size;
+  printf ("usb_dev_bulk=%d\n", err);
+  return err;
 }
 
 /** convert a wchar string to ascii in place */
@@ -731,46 +679,8 @@ wchar_to_ascii (char *dst, const char *src)
     {
       dst[sz] = src[sz * 2];
     }
+  while (sz > 0 && isspace (dst[sz - 1]))
+    sz--;
   dst[sz] = '\0';
   return sz;
 }
-
-/* this is used for variable sized transfers where a normal bulk transfer would 
-   probably fail, since it expects some minimum size - we just want to 
-   read/write as much to the pipe as we can
- */
-static int
-usb_tx_all (struct usb_endpoint *eop, void *buf,
-           int max_bytes, int bailout, bool in)
-{
-  int txed;
-  int token;
-  int prev_sz = 0;
-  struct host *h;
-
-  if (max_bytes <= 0)
-    return 0;
-
-  if (bailout == 0)
-    bailout = 512;
-
-  txed = 0;
-  token = (in) ? USB_TOKEN_IN : USB_TOKEN_OUT;
-  h = eop->iface->dev->host;
-  while (txed < max_bytes && txed < bailout)
-    {
-      int sz, err;
-      sz = 0;
-      err = h->dev->tx_pkt (eop->h_eop, token,
-                           buf + txed, 0, max_bytes - txed, &sz, true);
-      if (prev_sz == 0)
-       prev_sz = sz;
-      txed += sz;
-      /* this should probably be using short packet detection */
-      if (err != USB_HOST_ERR_NONE || sz != prev_sz || sz == 0)
-       {
-         return txed;
-       }
-    }
-  return txed;
-}
index b15978e1e15b8ce26a7d036ebc443047d92c5383..30b400a647a030dbf277beb73760e70c1847ed6d 100644 (file)
@@ -13,6 +13,7 @@
 #define USB_HOST_ERR_BUFFER    5
 #define USB_HOST_ERR_STALL     6
 #define USB_HOST_ERR_NODEV     7
+#define USB_HOST_ERR_NOMEM      8
 
 #define make_usb_pid(x)         ((x) | ((~(x)) << 4))
 
@@ -57,6 +58,7 @@
 
 struct class;
 struct host;
+struct usb_setup_pkt;
 typedef void *host_info;
 typedef void *host_eop_info;
 typedef void *host_dev_info;
@@ -88,6 +90,9 @@ struct usb_host
 {
   const char *name;
   int (*detect_change) (host_info);
+  int (*dev_control) (host_eop_info, struct usb_setup_pkt *,
+                      void *data, size_t *size);
+  int (*dev_bulk) (host_eop_info, bool out, void *data, size_t *size);
   int (*tx_pkt) (host_eop_info, int pid, void *pkt, 
                 int min_sz, int max_sz, int *in_sz, bool wait);
 
@@ -189,9 +194,11 @@ int usb_unregister_host (struct usb_host *, host_info info);
 int usb_register_class (struct usb_class *);
 int usb_unregister_class (struct usb_class *);
 
+int usb_dev_control (struct usb_endpoint *, struct usb_setup_pkt *,
+                     void *data, size_t *size);
 int usb_dev_bulk (struct usb_endpoint *eop, void *buf, int sz, int *tx);
-int usb_dev_setup (struct usb_endpoint *eop, bool in,
-                  struct usb_setup_pkt *s, void *buf, int sz);
 int usb_dev_wait_int (struct usb_dev *);
 
+void usb_storage_init (void);
+
 #endif
index 25114393ab29109fe2d24bbe7937de62013103cd..56c940aa7de640bb7119c2195f0ff4bbd4525ae7 100644 (file)
@@ -285,43 +285,47 @@ 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_capacity10 cap;
   struct scsi_cdb10 *cdb;
-  struct msc_csw *csw;
+  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)]);
-
+  memset (&cbw, 0, sizeof cbw);
   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);
+  usb_dev_bulk (mci->eop_out, &cbw, sizeof cbw, &tx);
+  if (tx != sizeof cbw)
+    PANIC ("send cbw");
+  usb_dev_bulk (mci->eop_in, &cap, sizeof cap, &tx);
+  if (tx != sizeof cap)
+    PANIC ("recv cap");
+  usb_dev_bulk (mci->eop_in, &csw, sizeof csw, &tx);
+  if (tx != sizeof csw)
+    PANIC ("recv csw");
 
-  mci->blk_count = be32_to_machine (cap->blocks) + 1;
-  mci->blk_size = be32_to_machine (cap->block_len);
+  mci->blk_count = be32_to_machine (cap.blocks) + 1;
+  mci->blk_size = be32_to_machine (cap.block_len);
 
+#if 0
   /* did CSW stall?  */
   if (tx == sizeof (struct scsi_capacity10))
     {
+      PANIC ("foo");
       msc_reset_endpoint (mci->eop_in);
       usb_dev_bulk (mci->eop_in, csw, sizeof (*csw), &tx);
     }
+#endif
 
-  ASSERT (csw->sig == CSW_SIG_MAGIC);
+  ASSERT (csw.sig == CSW_SIG_MAGIC);
 
-  if (csw->status != CSW_STATUS_PASSED)
+  if (csw.status != CSW_STATUS_PASSED)
     {
       PANIC ("USB storage geometry read failure!\n");
     }
@@ -408,6 +412,7 @@ msc_reset_endpoint (struct usb_endpoint *eop)
 {
   struct usb_setup_pkt sp;
 
+  printf ("*** reset endpoint\n");
   sp.recipient = USB_SETUP_RECIP_ENDPT;
   sp.type = USB_SETUP_TYPE_STD;
   sp.direction = 0;
@@ -415,7 +420,7 @@ msc_reset_endpoint (struct usb_endpoint *eop)
   sp.value = 0;                        /* 0 is ENDPOINT_HALT */
   sp.index = eop->eop;
   sp.length = 0;
-  usb_dev_setup (eop, true, &sp, NULL, 0);
+  usb_dev_control (eop, &sp, NULL, NULL);
 }
 
 static void
@@ -437,5 +442,5 @@ static void msc_bulk_reset(struct msc_class_info* mci)
   sp.value = 0;                 
   sp.index = mci->ui->iface_num;
   sp.length = 0;
-  usb_dev_setup (&mci->ui->dev->cfg_eop, true, &sp, NULL, 0);
+  usb_dev_control (&mci->ui->dev->cfg_eop, &sp, NULL, NULL);
 }
index 1e5a64a152040fe2d546746e4a03b5ace3873996..f92dd0e8e76cc444cb317632578b95f9ee0bc065 100644 (file)
@@ -8,7 +8,6 @@
 #include <round.h>
 #include <stdio.h>
 #include <string.h>
-#include <kernel/bitmap.h>
 #include "threads/pte.h"
 #include "threads/malloc.h"
 #include "threads/palloc.h"
 #define ptr_to_flp(x)  (((uintptr_t)x) >> 4)
 #define flp_to_ptr(x)  (uintptr_t)(((uintptr_t)x) << 4)
 
-/* frame structures */
+#define UHCI_LP_POINTER         0xfffffff0 /* Pointer field. */
+#define UHCI_LP_TERMINATE       0x00000001 /* 1=pointer not valid */
+#define UHCI_LP_QUEUE_HEAD      0x00000002 /* 1=points to queue head,
+                                              0=points to tx descriptor. */
+#define UHCI_LP_DEPTH           0x00000004 /* Tx descriptor only:
+                                              1=execute depth first,
+                                              0=execute breadth first. */
 #pragma pack(1)
-struct frame_list_ptr
-{
-  uint32_t terminate:1;
-  uint32_t qh_select:1;
-  uint32_t depth_select:1;     /* only for TD */
-  uint32_t resv:1;             /* zero */
-  uint32_t flp:28;             /* frame list pointer */
-};
-
 struct td_token
 {
   uint32_t pid:8;              /* packet id */
@@ -126,20 +122,24 @@ struct td_control
 
 struct tx_descriptor
 {
-  struct frame_list_ptr flp;
+  uint32_t next_td;
   struct td_control control;
   struct td_token token;
   uint32_t buf_ptr;
 
-
-  struct frame_list_ptr *head; /* for fast removal */
   uint32_t flags;
+  struct list_elem td_elem;     /* Element in queue_head's td_list. */
+  size_t offset;
 };
 
 struct queue_head
 {
-  struct frame_list_ptr qhlp;  /* queue head link pointer */
-  struct frame_list_ptr qelp;  /* queue elem link pointer */
+  uint32_t next_qh;             /* Queue head link pointer. */
+  uint32_t first_td;            /* Queue element link pointer. */
+
+  struct list_elem qh_elem;     /* Element in uhci_info's qh_list. */
+  struct list td_list;          /* List of tx_descriptor structures. */
+  struct semaphore completion;  /* Upped by interrupt on completion. */
 };
 #pragma pack()
 
@@ -148,21 +148,12 @@ struct uhci_info
   struct pci_dev *dev;
   struct pci_io *io;           /* pci io space */
   struct lock lock;
-  struct frame_list_ptr *frame_list;   /* page aligned frame list */
-
-  struct tx_descriptor *td_pool;
-  struct bitmap *td_used;
-  struct queue_head *qh_pool;
-  struct bitmap *qh_used;
+  uint32_t *frame_list;         /* Page-aligned list of 1024 frame pointers. */
+  struct list qh_list;          /* List of queue_head structuress. */
+  struct queue_head *term_qh;
 
   uint8_t num_ports;
   uint8_t attached_ports;
-
-  int timeouts;                        /* number of timeouts */
-
-  struct semaphore td_sem;
-  struct list devices;         /* devices on host */
-  struct list waiting;         /* threads waiting */
 };
 
 struct usb_wait
@@ -181,7 +172,6 @@ struct uhci_dev_info
   int errors;                  /* aggregate errors */
   struct list_elem peers;      /* next dev on host */
   struct lock lock;
-  struct queue_head *qh;
 };
 
 struct uhci_eop_info
@@ -200,29 +190,19 @@ struct uhci_eop_info
 
 static int token_to_pid (int token);
 
-static int uhci_tx_pkt (host_eop_info eop, int token, void *pkt,
-                       int min_sz, int max_sz, int *in_sz, bool wait);
-
+static int uhci_dev_control (host_eop_info, struct usb_setup_pkt *,
+                             void *data, size_t *size);
+static int uhci_dev_bulk (host_eop_info, bool out, void *data, size_t *size);
 static int uhci_detect_change (host_info);
 
 
-static int uhci_tx_pkt_now (struct uhci_eop_info *ue, int token, void *pkt,
-                           int sz);
-static int uhci_tx_pkt_wait (struct uhci_eop_info *ue, int token, void *pkt,
-                            int max_sz, int *in_sz);
-static int uhci_tx_pkt_bulk (struct uhci_eop_info *ue, int token, void *buf,
-                            int sz, int *tx);
-
+static struct queue_head *allocate_queue_head (void);
+static struct tx_descriptor *allocate_transfer_descriptor (struct queue_head *);
 
-static int uhci_process_completed (struct uhci_info *ui);
 
-static struct tx_descriptor *uhci_acquire_td (struct uhci_info *);
-static void uhci_release_td (struct uhci_info *, struct tx_descriptor *);
-static void uhci_remove_qh (struct uhci_info *ui, struct queue_head *qh);
+static void uhci_process_completed (struct uhci_info *ui);
 
 
-static void qh_free (struct uhci_info *ui, struct queue_head *qh);
-static struct queue_head *qh_alloc (struct uhci_info *ui);
 
 static struct uhci_info *uhci_create_info (struct pci_io *io);
 static void uhci_destroy_info (struct uhci_info *ui);
@@ -232,31 +212,16 @@ static host_dev_info uhci_create_chan (host_info hi, int dev_addr, int ver);
 static void uhci_destroy_chan (host_dev_info);
 static void uhci_modify_chan (host_dev_info, int dev_addr, int ver);
 
-static struct tx_descriptor *td_from_pool (struct uhci_info *ui, int idx);
-
 static int check_and_flip_change (struct uhci_info *ui, int reg);
-static void uhci_stop (struct uhci_info *ui);
-static void uhci_run (struct uhci_info *ui);
-static void uhci_stop_unlocked (struct uhci_info *ui);
-static void uhci_run_unlocked (struct uhci_info *ui);
 #define uhci_is_stopped(x)     (pci_reg_read16((x)->io, UHCI_REG_USBSTS) \
                                & USB_STATUS_HALTED)
 #define uhci_port_enabled(x, y) (pci_reg_read16((x)->io, (y)) & USB_PORT_ENABLE)
-static void uhci_add_td_to_qh (struct queue_head *qh,
-                              struct tx_descriptor *td);
-static void uhci_remove_error_td (struct tx_descriptor *td);
 static void uhci_setup_td (struct tx_descriptor *td, int dev_addr, int token,
                           int eop, void *pkt, int sz, int toggle, bool ls);
 static int uhci_enable_port (struct uhci_info *ui, int port);
 static void uhci_irq (void *uhci_data);
 static void uhci_detect_ports (struct uhci_info *ui);
 
-static int uhci_remove_stalled (struct uhci_info *ui);
-static void uhci_stall_watchdog (struct uhci_info *ui);
-
-static void dump_all_qh (struct uhci_info *ui);
-static void dump_qh (struct queue_head *qh);
-
 static void dump_regs (struct uhci_info *ui);
 
 void uhci_init (void);
@@ -264,7 +229,8 @@ void uhci_init (void);
 
 static struct usb_host uhci_host = {
   .name = "UHCI",
-  .tx_pkt = uhci_tx_pkt,
+  .dev_control = uhci_dev_control,
+  .dev_bulk = uhci_dev_bulk,
   .detect_change = uhci_detect_change,
   .create_dev_channel = uhci_create_chan,
   .remove_dev_channel = uhci_destroy_chan,
@@ -273,6 +239,14 @@ static struct usb_host uhci_host = {
   .remove_eop = uhci_remove_eop
 };
 
+static uint32_t
+make_lp (void *p) 
+{
+  uint32_t q = vtop (p);
+  ASSERT ((q & UHCI_LP_POINTER) == q);
+  return q;
+}
+
 void
 uhci_init (void)
 {
@@ -285,7 +259,6 @@ uhci_init (void)
     {
       struct pci_io *io;
       struct uhci_info *ui;
-      uint8_t sof;
       int i;
 
       dev_num++;
@@ -305,49 +278,44 @@ uhci_init (void)
       ui = uhci_create_info (io);
       ui->dev = pd;
 
-      sof = pci_reg_read8 (ui->io, UHCI_REG_SOFMOD);
-      uhci_detect_ports (ui);
+      //dump_regs (ui);
 
-      /* reset devices */
-      pci_reg_write16 (ui->io, UHCI_REG_USBCMD, USB_CMD_GRESET);
-      timer_msleep (50);
-      pci_reg_write16 (ui->io, UHCI_REG_USBCMD, 0);
-      timer_msleep (1);
+      uhci_detect_ports (ui);
 
-      /* reset controller */
+      pci_write_config16 (ui->dev, UHCI_REG_LEGSUP, 0x8f00);
       pci_reg_write16 (ui->io, UHCI_REG_USBCMD, USB_CMD_HCRESET);
-      timer_msleep (1);
+      asm volatile ("mfence":::"memory");
+      timer_usleep (5);
       if (pci_reg_read16 (ui->io, UHCI_REG_USBCMD) & USB_CMD_HCRESET)
-       {
-         printf ("UHCI: Reset timed out\n");
-         uhci_destroy_info (ui);
-         continue;
-       }
+        printf ("reset failed!\n");
       pci_reg_write16 (ui->io, UHCI_REG_USBINTR, 0);
       pci_reg_write16 (ui->io, UHCI_REG_USBCMD, 0);
 
-      for (i = 0; i < ui->num_ports; i++)
-       pci_reg_write16 (ui->io, UHCI_REG_PORTSC1 + i * 2, 0);
-
-      timer_msleep (100);
-      printf ("UHCI: Enabling %d root ports\n", ui->num_ports);
-      ui->attached_ports = 0;
-      for (i = 0; i < ui->num_ports; i++)
-       ui->attached_ports += uhci_enable_port (ui, i);
+      pci_reg_write16 (ui->io, UHCI_REG_PORTSC1, 0);
+      pci_reg_write16 (ui->io, UHCI_REG_PORTSC2, 0);
 
-      pci_reg_write8 (ui->io, UHCI_REG_SOFMOD, sof);
+      pci_reg_write8 (ui->io, UHCI_REG_SOFMOD, 64);
+      pci_reg_write32 (ui->io, UHCI_REG_FLBASEADD, make_lp (ui->frame_list));
       pci_reg_write16 (ui->io, UHCI_REG_FRNUM, 0);
-      pci_reg_write32 (ui->io, UHCI_REG_FLBASEADD, vtop (ui->frame_list));
+      asm volatile ("mfence":::"memory");
+
+      /* deactivate SMM junk, only enable IRQ */
+      pci_write_config16 (ui->dev, UHCI_REG_LEGSUP, 0x2000);
+
+      asm volatile ("mfence":::"memory");
+
+      pci_reg_write16 (ui->io, UHCI_REG_USBCMD,
+                       USB_CMD_RS | USB_CMD_CONFIGURE | USB_CMD_MAX_PACKET);
       pci_reg_write16 (ui->io, UHCI_REG_USBINTR,
                       USB_INTR_SHORT | USB_INTR_IOC |
                       USB_INTR_TIMEOUT | USB_INTR_RESUME);
 
-      /* deactivate SMM junk, only enable IRQ */
-      pci_write_config16 (ui->dev, UHCI_REG_LEGSUP, 0x2000);
+      asm volatile ("mfence":::"memory");
 
-      uhci_lock (ui);
-      uhci_run (ui);
-      uhci_unlock (ui);
+      printf ("UHCI: Enabling %d root ports\n", ui->num_ports);
+      ui->attached_ports = 0;
+      for (i = 0; i < ui->num_ports; i++)
+       ui->attached_ports += uhci_enable_port (ui, i);
 
       pci_register_irq (pd, uhci_irq, ui);
 
@@ -357,42 +325,75 @@ uhci_init (void)
 
 #define UHCI_PORT_TIMEOUT      1000
 static int
-uhci_enable_port (struct uhci_info *ui, int port)
+uhci_enable_port (struct uhci_info *ui, int idx)
 {
   uint16_t status;
+  int time, stable_since;
   int count;
+  int port;
 
-  /* individual ports must be reset for QEMU to go into USB_STATE_DEFAULT */
-  pci_reg_write16 (ui->io, UHCI_REG_PORTSC1 + port * 2, USB_PORT_RESET);
-  timer_msleep (50);
+  port = UHCI_REG_PORTSC1 + idx * 2;
 
-  /* ACK disconnect from reset so we can see if port is connected */
-  pci_reg_write16 (ui->io, UHCI_REG_PORTSC1 + port * 2,
-                  USB_PORT_ENABLE | USB_PORT_CONNECTCHG);
-  timer_msleep (10);
-  status = pci_reg_read16 (ui->io, UHCI_REG_PORTSC1 + port * 2);
-  if (!(status & USB_PORT_CONNECTSTATUS))
+  status = 0xffff;
+  stable_since = 0;
+  for (time = 0; ; time += 25) 
     {
-      return 0;
+      uint16_t new_status;
+      new_status = pci_reg_read16 (ui->io, port);
+      if (status != (new_status & USB_PORT_CONNECTSTATUS)
+          || new_status & USB_PORT_CONNECTCHG) 
+        {
+          if (new_status & USB_PORT_CONNECTCHG)
+            pci_reg_write16 (ui->io, port, (new_status & ~0xe80a) | USB_PORT_CONNECTCHG);
+          stable_since = time;
+          status = new_status & USB_PORT_CONNECTSTATUS;
+        }
+      else if (time - stable_since >= 100)
+        break;
+      else if (time >= 1500)
+        return 0;
+      timer_msleep (25);
     }
+  //printf ("time=%d stable_since=%d\n", time, stable_since);
+
+  if (!(status & USB_PORT_CONNECTSTATUS))
+    return 0;
 
-  /* ACK CONNECTCHG status so port can be enabled */
-  pci_reg_write16 (ui->io, UHCI_REG_PORTSC1 + port * 2,
-                  USB_PORT_ENABLE | USB_PORT_CONNECTCHG);
-  for (count = 0; count < UHCI_PORT_TIMEOUT; count++)
+  for (count = 0; count < 3; count++) 
     {
-      if (uhci_port_enabled (ui, UHCI_REG_PORTSC1 + port * 2))
-       {
-         pci_reg_write16 (ui->io, UHCI_REG_PORTSC1 + port * 2,
-                          USB_PORT_ENABLE | USB_PORT_CONNECTCHG);
-         return 1;
-       }
+      status = pci_reg_read16 (ui->io, port) & ~0xe80a;
+      //printf ("read %x, write %x, ", status, status | USB_PORT_RESET);
+      pci_reg_write16 (ui->io, port, status | USB_PORT_RESET);
+      timer_msleep (50);
+
+      status = pci_reg_read16 (ui->io, port) & ~0xe80a;
+      //printf ("read %x, write %x, ", status, status & ~USB_PORT_RESET);
+      pci_reg_write16 (ui->io, port, status & ~USB_PORT_RESET);
+      timer_usleep (10);
+
+      status = pci_reg_read16 (ui->io, port) & ~0xe80a;
+      //printf ("read %x, write %x, ", status, status & ~(USB_PORT_CONNECTCHG | USB_PORT_CHANGE));
+      pci_reg_write16 (ui->io, port, status & ~(USB_PORT_CONNECTCHG | USB_PORT_CHANGE));
+
+      status = pci_reg_read16 (ui->io, port) & ~0xe80a;
+      //printf ("read %x, write %x, ", status, status | USB_PORT_ENABLE);
+      pci_reg_write16 (ui->io, port, status | USB_PORT_ENABLE);
+
+      status = pci_reg_read16 (ui->io, port);
+      //printf ("read %x\n", status);
+      if (status & USB_PORT_ENABLE)
+        break;
     }
 
-  printf ("UHCI: Port %d enable timed out\n", port);
-  dump_regs (ui);
+  if (!(status & USB_PORT_CONNECTSTATUS))
+    {
+      //printf ("port %d not connected\n", idx);
+      pci_reg_write16 (ui->io, port, 0);
+      return 0;
+    }
 
-  return 0;
+  //dump_regs (ui);
+  return 1;
 }
 
 
@@ -404,14 +405,16 @@ dump_regs (struct uhci_info *ui)
   char *name[] =
     { "cmd", "sts", "intr", "frnum", "base", "sofmod", "portsc1", "portsc2" };
   int i;
-  printf ("UHCI registers:\n");
+  printf ("UHCI registers: ");
   for (i = 0; i < 8; i++)
     {
-      printf ("%s: %x\n", name[i], (sz[i] == 2) ?
+      if (i)
+        printf (", ");
+      printf ("%s: %x", name[i], (sz[i] == 2) ?
              pci_reg_read16 (ui->io, regs[i]) :
              pci_reg_read32 (ui->io, regs[i]));
-
     }
+  printf (", legsup=%x\n", pci_read_config16 (ui->dev, UHCI_REG_LEGSUP));
 }
 
 
@@ -419,10 +422,6 @@ static void
 uhci_destroy_info (struct uhci_info *ui)
 {
   palloc_free_page (ui->frame_list);
-  palloc_free_page (ui->td_pool);
-  palloc_free_page (ui->qh_pool);
-  bitmap_destroy (ui->qh_used);
-  bitmap_destroy (ui->td_used);
   free (ui);
 }
 
@@ -430,6 +429,8 @@ static struct uhci_info *
 uhci_create_info (struct pci_io *io)
 {
   struct uhci_info *ui;
+  struct queue_head *qh1, *qh2;
+  struct tx_descriptor *td;
   int i;
 
   ui = malloc (sizeof (struct uhci_info));
@@ -437,116 +438,329 @@ uhci_create_info (struct pci_io *io)
   ui->io = io;
   lock_init (&ui->lock);
 
-  /* create an empty schedule */
+  /* Create initial queue head and put on qh list. */
+  list_init (&ui->qh_list);
+
+  qh1 = allocate_queue_head ();
+  list_push_back (&ui->qh_list, &qh1->qh_elem);
+
+  ui->term_qh = qh2 = allocate_queue_head ();
+  qh1->next_qh = make_lp (qh2) | UHCI_LP_QUEUE_HEAD;
+
+  td = allocate_transfer_descriptor (qh2);
+  td->token.maxlen = 0x7ff;
+  td->token.dev_addr = 0x7f;
+  td->token.pid = USB_TOKEN_IN;
+
+  /* Create an empty schedule */
   ui->frame_list = palloc_get_page (PAL_ASSERT | PAL_NOCACHE);
-  memset (ui->frame_list, 0, PGSIZE);
   for (i = 0; i < FRAME_LIST_ENTRIES; i++)
-    ui->frame_list[i].terminate = 1;
+    ui->frame_list[i] = make_lp (qh1) | UHCI_LP_QUEUE_HEAD;
+  //printf ("frame_list=%p: %08x\n", ui->frame_list, ui->frame_list[0]);
 
-  /* permit 3 timeouts */
-  ui->timeouts = 3;
 //  thread_create ("uhci watchdog", PRI_MIN,
 //              (thread_func *) uhci_stall_watchdog, ui);
-  ui->td_pool = palloc_get_page (PAL_ASSERT | PAL_NOCACHE);
-  ui->td_used = bitmap_create (TD_ENTRIES);
-  ui->qh_pool = palloc_get_page (PAL_ASSERT | PAL_NOCACHE);
-  ui->qh_used = bitmap_create (QH_ENTRIES);
-  sema_init (&ui->td_sem, TD_ENTRIES);
-
-  list_init (&ui->devices);
-  list_init (&ui->waiting);
 
   return ui;
 }
 
+static void
+print_td_list (struct queue_head *qh) 
+{
+  struct list_elem *e;
+  
+  for (e = list_begin (&qh->td_list); e != list_end (&qh->td_list);
+       e = list_next (e)) 
+    {
+      struct tx_descriptor *td = list_entry (e, struct tx_descriptor, td_elem);
+      printf ("  %08x: next_td=%08x control=%08x %d,%d,%d,%d,%x buf_ptr=%08x\n",
+              vtop (td), td->next_td, *(uint32_t *) &td->control,
+              (td->token.maxlen + 1) & 0x7ff, td->token.data_toggle,
+              td->token.end_point, td->token.dev_addr, td->token.pid,
+              td->buf_ptr);
+      hex_dump (0, ptov (td->buf_ptr), (td->token.maxlen + 1) & 0x7ff, true);
+    }
+}
+
+static void
+print_qh_list (struct uhci_info *ui) 
+{
+  struct list_elem *e;
+  
+  for (e = list_begin (&ui->qh_list); e != list_end (&ui->qh_list);
+       e = list_next (e)) 
+    {
+      struct queue_head *qh = list_entry (e, struct queue_head, qh_elem);
+      printf ("%08x: next_qh=%08x first_td=%08x\n",
+              vtop (qh), qh->next_qh, qh->first_td);
+      print_td_list (qh);
+    }
+}
+
+static struct tx_descriptor *
+execute_qh (struct uhci_info *ui, struct queue_head *qh) 
+{
+  struct queue_head *prev_qh;
+  struct tx_descriptor *last_td;
+  enum intr_level old_level;
+
+  /* Mark the final tx descriptor to interrupt us when it's
+     done. */
+  last_td = list_entry (list_back (&qh->td_list), struct tx_descriptor, td_elem);
+  last_td->control.ioc = 1;
+  
+  qh->next_qh = make_lp (ui->term_qh) | UHCI_LP_QUEUE_HEAD;
+
+  /* Add the queue head to the list of queue heads.
+     qh_list is accessed from uhci_irq so we need to disable
+     interrupts. */
+  old_level = intr_disable ();
+  list_push_back (&ui->qh_list, &qh->qh_elem);
+  prev_qh = list_entry (list_prev (&qh->qh_elem), struct queue_head, qh_elem);
+  prev_qh->next_qh = make_lp (qh) | UHCI_LP_QUEUE_HEAD;
+  print_qh_list (ui);
+  //dump_regs (ui);
+  intr_set_level (old_level);
+
+  /* Wait until the queue has been processed. */
+  //printf ("down...");
+  sema_down (&qh->completion);
+  //printf ("up\n");
+  
+
+  /* Return the descriptor that failed, or a null pointer on
+     success. */
+  return (qh->first_td & UHCI_LP_TERMINATE
+          ? NULL
+          : ptov (qh->first_td & UHCI_LP_POINTER));
+}
 
 static int
-uhci_tx_pkt (host_eop_info hei, int token, void *pkt, int min_sz,
-            int max_sz, int *in_sz, bool wait)
+uhci_dev_control (host_eop_info hei, struct usb_setup_pkt *setup,
+                  void *data, size_t *size) 
 {
   struct uhci_eop_info *ue;
   struct uhci_dev_info *ud;
-
-  ASSERT (min_sz <= max_sz);
-
-  /* can't have page overlap */
-  if (pkt != NULL)
-    {
-      ASSERT (max_sz > 0);
-      ASSERT (pg_no (pkt + max_sz - 1) == pg_no (pkt));
-    }
+  struct queue_head *qh;
+  struct tx_descriptor *td;
+  size_t size_alloced;
+  int status_token;
+  int err;
 
   ue = hei;
   ud = ue->ud;
 
   /* don't bother if ports are down */
   if (ud->ui->attached_ports == 0)
+    return USB_HOST_ERR_NODEV;
+
+  qh = allocate_queue_head ();
+  if (qh == NULL)
+    return USB_HOST_ERR_NOMEM;
+
+  /* Setup stage: SETUP packet. */
+  td = allocate_transfer_descriptor (qh);
+  if (td == NULL)
+    return USB_HOST_ERR_NOMEM;
+  ue->toggle = 0;
+  uhci_setup_td (td, ud->dev_addr, USB_TOKEN_SETUP, ue->eop,
+                 setup, sizeof *setup, ue->toggle, ud->low_speed);
+
+  /* Data stage: IN/OUT packets. */
+  if (data != NULL) 
     {
-      return USB_HOST_ERR_NODEV;
+      size_alloced = 0;
+      while (size_alloced < *size) 
+        {
+          int packet = *size - size_alloced;
+          if (packet > ue->maxpkt)
+            packet = ue->maxpkt;
+
+          td = allocate_transfer_descriptor (qh);
+          if (td == NULL)
+            return USB_HOST_ERR_NOMEM;
+
+          ue->toggle = !ue->toggle;
+          uhci_setup_td (td, ud->dev_addr,
+                         setup->direction ? USB_TOKEN_IN : USB_TOKEN_OUT,
+                         ue->eop, data + size_alloced, packet,
+                         ue->toggle, ud->low_speed);
+          td->control.spd = 1;
+          td->offset = size_alloced;
+
+          size_alloced += packet;
+        }
     }
+  
+  /* Status stage: OUT/IN packet. */
+  td = allocate_transfer_descriptor (qh);
+  if (td == NULL)
+    return USB_HOST_ERR_NOMEM;
+  ue->toggle = 1;
+  status_token = setup->direction ? USB_TOKEN_OUT : USB_TOKEN_IN;
+  uhci_setup_td (td, ud->dev_addr, status_token, ue->eop,
+                 NULL, 0, ue->toggle, ud->low_speed);
+
+  /* Execute. */
+  td = execute_qh (ud->ui, qh);
+  if (td != NULL)
+    ue->toggle = td->token.data_toggle;
+  //printf ("execution done, td=%p\n", td);
+  if (td != NULL
+      && setup->direction
+      && list_front (&qh->td_list) != &td->td_elem
+      && list_back (&qh->td_list) != &td->td_elem
+      && !td->control.active
+      && !td->control.stalled
+      && !td->control.buffer_error
+      && !td->control.babble
+      && !td->control.nak
+      && !td->control.timeout
+      && !td->control.bitstuff
+      && (((td->control.actual_len + 1) & 0x7ff)
+          < ((td->token.maxlen + 1) & 0x7ff))) 
+    {
+      /* Short packet detected in stream, which indicates that
+         the input data is shorter than the maximum that we
+         expected.
 
-  /* setup token acts to synchronize data toggle */
-  if (token == USB_TOKEN_SETUP)
-    ue->toggle = 0;
+         Re-queue the Status stage. */
 
-  if (min_sz != 0)
-    {
-      return uhci_tx_pkt_bulk (ue, token, pkt, max_sz, in_sz);
+      *size = td->offset + ((td->control.actual_len + 1) & 0x7ff);
+
+      //delayed_free_qh (qh);
+
+      qh = allocate_queue_head ();
+      if (qh == NULL)
+        return USB_HOST_ERR_NOMEM;
+
+      td = allocate_transfer_descriptor (qh);
+      if (td == NULL)
+        return USB_HOST_ERR_NOMEM;
+      ue->toggle = 1;
+      uhci_setup_td (td, ud->dev_addr, status_token, ue->eop,
+                     NULL, 0, ue->toggle, ud->low_speed);
+
+      //printf ("short packet, requeuing status\n");
+      td = execute_qh (ud->ui, qh);
+      if (td != NULL)
+        ue->toggle = td->token.data_toggle;
     }
-  else
+      
+  if (td != NULL) 
     {
-      if (wait == false)
-       {
-         if (in_sz != NULL)
-           *in_sz = max_sz;
-         return uhci_tx_pkt_now (ue, token, pkt, max_sz);
-       }
+      if (td->control.bitstuff)
+        err = USB_HOST_ERR_BITSTUFF;
+      else if (td->control.timeout)
+        err = USB_HOST_ERR_TIMEOUT;
+      else if (td->control.nak)
+        err = USB_HOST_ERR_NAK;
+      else if (td->control.babble)
+        err = USB_HOST_ERR_BABBLE;
+      else if (td->control.buffer_error)
+        err = USB_HOST_ERR_BUFFER;
+      else if (td->control.stalled)
+        err = USB_HOST_ERR_STALL;
       else
-       {
-         return uhci_tx_pkt_wait (ue, token, pkt, max_sz, in_sz);
-       }
+        PANIC ("unknown USB error");
     }
+  else
+    err = USB_HOST_ERR_NONE;
 
-  return 0;
+  //delayed_free_qh (qh);
+  
+  //printf ("err=%d\n", err);
+  if (err == 0 && data != NULL)
+    hex_dump (0, data, *size, true);
+  return err;
 }
 
 static int
-uhci_tx_pkt_bulk (struct uhci_eop_info *ue, int token, void *buf,
-                 int sz, int *tx)
+uhci_dev_bulk (host_eop_info hei, bool out, void *data, size_t *size) 
 {
-  /* XXX this can be made to use async packets */
-  int bytes = 0;
-  int txed = 0;
+  struct uhci_eop_info *ue;
+  struct uhci_dev_info *ud;
+  struct queue_head *qh;
+  struct tx_descriptor *td;
+  size_t size_alloced;
+  int err;
 
-  /* send data in max_pkt sized chunks */
-  while (bytes < sz)
+  ue = hei;
+  ud = ue->ud;
+
+  /* don't bother if ports are down */
+  if (ud->ui->attached_ports == 0)
+    return USB_HOST_ERR_NODEV;
+
+  if (!out)
+    memset (data, 0xcc, *size);
+
+  qh = allocate_queue_head ();
+  if (qh == NULL)
+    return USB_HOST_ERR_NOMEM;
+
+  /* Data stage: IN/OUT packets. */
+  size_alloced = 0;
+  while (size_alloced < *size)
     {
-      int to_tx, pkt_txed;
-      int left;
-      int err;
-      bool wait_on_pkt;
-
-      left = sz - bytes;
-      to_tx = (left > ue->maxpkt) ? ue->maxpkt : left;
-      wait_on_pkt = (left <= to_tx) ? true : false;
-
-      pkt_txed = 0;
-      err = uhci_tx_pkt (ue, token, buf + bytes, 0, to_tx, &pkt_txed, 
-                        wait_on_pkt);
-      if (err)
-       {
-         if (tx != NULL)
-           *tx = txed;
-         return err;
-       }
-      txed += pkt_txed;
-      bytes += pkt_txed;
+      int packet = *size - size_alloced;
+      if (packet > ue->maxpkt)
+        packet = ue->maxpkt;
+
+      td = allocate_transfer_descriptor (qh);
+      if (td == NULL)
+        return USB_HOST_ERR_NOMEM;
+
+      uhci_setup_td (td, ud->dev_addr,
+                     out ? USB_TOKEN_OUT : USB_TOKEN_IN,
+                     ue->eop, data + size_alloced, packet,
+                     ue->toggle, ud->low_speed);
+      ue->toggle = !ue->toggle;
+      td->control.spd = 1;
+      td->offset = size_alloced;
+
+      size_alloced += packet;
+    }
+  
+  /* Execute. */
+  td = execute_qh (ud->ui, qh);
+  if (td != NULL) 
+    {
+      ue->toggle = !td->token.data_toggle;
+      if (!out) 
+        *size = td->offset + ((td->control.actual_len + 1) & 0x7ff);
+      
+      if (td->control.bitstuff)
+        err = USB_HOST_ERR_BITSTUFF;
+      else if (td->control.timeout)
+        err = USB_HOST_ERR_TIMEOUT;
+      else if (td->control.nak)
+        err = USB_HOST_ERR_NAK;
+      else if (td->control.babble)
+        err = USB_HOST_ERR_BABBLE;
+      else if (td->control.buffer_error)
+        err = USB_HOST_ERR_BUFFER;
+      else if (td->control.stalled)
+        err = USB_HOST_ERR_STALL;
+      else if (!out)
+        {
+          /* Just a short packet. */
+          printf ("short packet\n");
+          err = USB_HOST_ERR_NONE;
+        }
+      else
+        PANIC ("unknown USB error");
     }
+  else
+    err = USB_HOST_ERR_NONE;
 
-  if (tx != NULL)
-    *tx = txed;
+  //delayed_free_qh (qh);
 
-  return USB_HOST_ERR_NONE;
+  if (err)
+    PANIC ("err=%d\n", err);
+  if (err == 0 && data != NULL && !out)
+    hex_dump (0, data, *size, true);
+  return err;
 }
 
 static int
@@ -567,7 +781,7 @@ token_to_pid (int token)
 
 static void
 uhci_setup_td (struct tx_descriptor *td, int dev_addr, int token,
-              int eop, void *pkt, int sz, int toggle, bool ls)
+              int eop, void *pkt, int sz, int toggle, bool ls UNUSED)
 {
   td->buf_ptr = (sz == 0) ? 0 : vtop (pkt);
 
@@ -580,263 +794,100 @@ uhci_setup_td (struct tx_descriptor *td, int dev_addr, int token,
 
   td->control.actual_len = 0;
   td->control.active = 1;
-  td->flp.qh_select = 0;
-  td->flp.depth_select = 0;
+  td->next_td = UHCI_LP_TERMINATE;
 
   /* kill packet if too many errors */
   td->control.error_limit = 3;
 }
 
-static int
-uhci_tx_pkt_now (struct uhci_eop_info *ue, int token, void *pkt, int sz)
-{
-  struct tx_descriptor *td;
-  struct uhci_dev_info *ud;
-
-  ud = ue->ud;
-
-  uhci_lock (ud->ui);
-
-  td = uhci_acquire_td (ud->ui);
-  memset (td, 0, sizeof (struct tx_descriptor));
-  uhci_setup_td (td, ud->dev_addr, token, ue->eop, pkt, sz, ue->toggle,
-                ud->low_speed);
-  td->control.ioc = 1;
-
-  uhci_stop (ud->ui);
-
-  uhci_add_td_to_qh (ud->qh, td);
-  td->flags = TD_FL_ASYNC | TD_FL_USED;
-
-  uhci_run (ud->ui);
-  uhci_unlock (ud->ui);
-
-  ue->toggle ^= 1;
-  return USB_HOST_ERR_NONE;
-}
-
-static int
-uhci_tx_pkt_wait (struct uhci_eop_info *ue, int token, void *pkt,
-                 int max_sz, int *in_sz)
-{
-  enum intr_level old_lvl;
-  struct tx_descriptor *td;
-  struct usb_wait w;
-  int err;
-  struct uhci_dev_info *ud;
-
-  ud = ue->ud;
-
-  uhci_lock (ud->ui);
-
-  td = uhci_acquire_td (ud->ui);
-  memset (td, 0, sizeof (struct tx_descriptor));
-
-  uhci_setup_td (td, ud->dev_addr, token, ue->eop, pkt, max_sz, ue->toggle,
-                ud->low_speed);
-  td->control.ioc = 1;
-
-  w.td = td;
-  w.ud = ud;
-  sema_init (&w.sem, 0);
-
-  uhci_stop (ud->ui);
-
-  /* put into device's queue and add to waiting packet list */
-  uhci_add_td_to_qh (ud->qh, td);
-  td->flags = TD_FL_USED;
-
-  list_push_back (&ud->ui->waiting, &w.peers);
-
-  /* reactivate controller and wait */
-  old_lvl = intr_disable ();
-  uhci_run (ud->ui);
-  uhci_unlock (ud->ui);
-  sema_down (&w.sem);
-  intr_set_level (old_lvl);
-
-  if (in_sz != NULL)
-    {
-      if (w.td->control.actual_len == 0x7ff)
-       *in_sz = 0;
-      else
-       *in_sz = w.td->control.actual_len + 1;
-    }
-
-  if (w.td->control.bitstuff)
-    err = USB_HOST_ERR_BITSTUFF;
-  else if (w.td->control.timeout)
-    err = USB_HOST_ERR_TIMEOUT;
-  else if (w.td->control.nak)
-    err = USB_HOST_ERR_NAK;
-  else if (w.td->control.babble)
-    err = USB_HOST_ERR_BABBLE;
-  else if (w.td->control.buffer_error)
-    err = USB_HOST_ERR_BUFFER;
-  else if (w.td->control.stalled)
-    err = USB_HOST_ERR_STALL;
-  else
-    {
-      err = USB_HOST_ERR_NONE;
-      ue->toggle ^= 1;
-    }
-
-  uhci_release_td (ud->ui, td);
-
-  return err;
-}
-
-static void
-uhci_add_td_to_qh (struct queue_head *qh, struct tx_descriptor *td)
-{
-  struct frame_list_ptr *fp;
-
-  ASSERT (td != NULL);
-
-  td->head = &qh->qelp;
-  if (qh->qelp.terminate == 1)
-    {
-      /* queue is empty */
-      td->flp.terminate = 1;
-      barrier ();
-      td->flp.flp = 0;
-      qh->qelp.flp = ptr_to_flp (vtop (td));
-      qh->qelp.terminate = 0;
-    }
-  else
-    {
-      /* find the last element in the queue */
-      fp = ptov (flp_to_ptr (qh->qelp.flp));
-      ASSERT (qh->qelp.terminate == 0);
-      while (!fp->terminate)
-       {
-         fp = ptov (flp_to_ptr (fp->flp));
-       }
-
-      /* set TD to terminated ptr */
-      td->flp = *fp;
-
-      fp->qh_select = 0;
-      fp->depth_select = 0;
-      fp->flp = ptr_to_flp (vtop (td));
-      barrier ();
-      fp->terminate = 0;
-    }
-}
-
 static void
 uhci_irq (void *uhci_data)
 {
   struct uhci_info *ui;
   uint16_t status;
 
+  //printf ("uhci_irq\n");
   ui = uhci_data;
   status = pci_reg_read16 (ui->io, UHCI_REG_USBSTS);
   if (status & USB_STATUS_PROCESSERR)
     {
-      dump_all_qh (ui);
+      //dump_all_qh (ui);
       dump_regs (ui);
       PANIC ("UHCI: Malformed schedule");
     }
   else if (status & USB_STATUS_HOSTERR)
     {
-      dump_all_qh (ui);
+      //dump_all_qh (ui);
       dump_regs (ui);
       PANIC ("UHCI: Host system error");
     }
-  else if (status & USB_STATUS_INTERR)
+  else if (status & (USB_STATUS_INTERR | USB_STATUS_USBINT))
     {
       /* errors */
-      pci_reg_write16 (ui->io, UHCI_REG_USBSTS, USB_STATUS_INTERR);
-    }
-
-  if (status & USB_STATUS_USBINT)
-    {
-      /* turn off interrupt */
-      uhci_stop_unlocked (ui);
-      pci_reg_write16 (ui->io, UHCI_REG_USBSTS, USB_STATUS_USBINT);
+      //pci_reg_write16 (ui->io, UHCI_REG_USBINTR, 0);
+      pci_reg_write16 (ui->io, UHCI_REG_USBSTS,
+                       status & (USB_STATUS_INTERR | USB_STATUS_USBINT));
+      if (status & USB_STATUS_INTERR) 
+        {
+          printf ("USB_STATUS_INTERR\n");
+          print_qh_list (ui);
+          dump_regs (ui); 
+        }
+      //if (status & USB_STATUS_USBINT)
+      //printf ("USB_STATUS_USBINT\n");
+      barrier ();
       uhci_process_completed (ui);
-      uhci_run_unlocked (ui);
     }
+  else
+    printf ("nothing?\n");
 }
 
-static int
-uhci_process_completed (struct uhci_info *ui)
+/* Queue processing finished.
+   Remove from lists, wake up waiter. */
+static void
+finish_qh (struct queue_head *qh)
 {
-  struct list_elem *li;
-  int completed = 0;
-  size_t start = 0;
-
-  li = list_begin (&ui->waiting);
-  while (li != list_end (&ui->waiting))
-    {
-      struct usb_wait *uw;
-      struct list_elem *next;
-
-      next = list_next (li);
-      uw = list_entry (li, struct usb_wait, peers);
-
-      if (!uw->td->control.active)
-       {
-         list_remove (li);
-         if (uw->td->control.error_limit == 0 || uw->td->control.stalled)
-           {
-             uhci_remove_error_td (uw->td);
-           }
-         uw->td->flags = 0;
-         sema_up (&uw->sem);
-         completed++;
-       }
-      li = next;
-    }
-
-  /* must be a completed async TD.. */
-  /* is this too time consuming? I hope not */
-  if (completed != 0)
-    return completed;
-
-  while (start < TD_ENTRIES)
-    {
-      struct tx_descriptor *td;
+  struct list_elem *p = list_prev (&qh->qh_elem);
+  struct queue_head *prev_qh = list_entry (p, struct queue_head, qh_elem);
 
-      start = bitmap_scan (ui->td_used, start, 1, true);
-      if (start == BITMAP_ERROR)
-       break;
-
-      td = td_from_pool (ui, start);
-
-      if (!td->control.active && (td->flags & TD_FL_ASYNC) &&
-         (td->flags & TD_FL_USED))
-       {
-         if (td->control.error_limit == 0 || td->control.stalled)
-           {
-             uhci_remove_error_td (td);
-           }
-         uhci_release_td (ui, td);
-         completed++;
-       }
-      start++;
-    }
-
-  return completed;
+  //printf ("finish_qh\n");
+  prev_qh->next_qh = qh->next_qh;
+  list_remove (&qh->qh_elem);
+  sema_up (&qh->completion);
 }
 
 static void
-uhci_remove_error_td (struct tx_descriptor *td)
+uhci_process_completed (struct uhci_info *ui)
 {
-  struct frame_list_ptr *fp;
-  uint32_t td_flp;
+  struct list_elem *e;
 
-  ASSERT (td->head != NULL);
+  if (list_empty (&ui->qh_list))
+    return;
 
-  fp = td->head;
-  td_flp = ptr_to_flp (vtop (td));
-  while (fp->flp != td_flp)
+  /* Note that we skip the first element in the list, which is an
+     empty queue head pointed to by every frame list pointer. */
+  for (e = list_next (list_begin (&ui->qh_list));
+       e != list_end (&ui->qh_list); ) 
     {
-      ASSERT (fp->terminate == 0);
-      fp = ptov (flp_to_ptr (fp->flp));
+      struct queue_head *qh = list_entry (e, struct queue_head, qh_elem);
+      uint32_t first_td = qh->first_td;
+      bool delete = false;
+
+      barrier ();
+      //printf ("first_td=%08x\n", first_td);
+      if ((first_td & UHCI_LP_POINTER) == 0)
+        delete = true;
+      else 
+        {
+          struct tx_descriptor *td = ptov (first_td & UHCI_LP_POINTER);
+          if (!td->control.active) 
+            delete = true;
+        }
+
+      e = list_next (e);
+      if (delete)
+        finish_qh (qh);
     }
-  *fp = td->flp;
 }
 
 static int
@@ -877,206 +928,72 @@ check_and_flip_change (struct uhci_info *ui, int port)
   return 0;
 }
 
-static host_dev_info
-uhci_create_chan (host_info hi, int dev_addr, int ver)
+static struct queue_head *
+allocate_queue_head (void) 
 {
-  struct uhci_info *ui;
-  struct uhci_dev_info *ud;
-  int i;
-
-  ASSERT (dev_addr <= 127 && dev_addr >= 0);
-
-  ui = hi;
-
-  ud = malloc (sizeof (struct uhci_dev_info));
-  ud->dev_addr = dev_addr;
-  ud->low_speed = (ver == USB_VERSION_1_0) ? true : false;
-
-  ud->errors = 0;
-  ud->ui = ui;
-  lock_init (&ud->lock);
-
-  uhci_lock (ui);
-
-  ud->qh = qh_alloc (ud->ui);
-
-  /* queue data */
-  memset (ud->qh, 0, sizeof (*ud->qh));
-  ud->qh->qelp.terminate = 1;
-  barrier ();
-  ud->qh->qelp.flp = 0;
-  ud->qh->qelp.qh_select = 0;
-  ud->qh->qhlp.qh_select = 1;
-
-  uhci_stop (ui);
-
-  /* add to queues in frame list */
-  ud->qh->qhlp.flp = ui->frame_list[0].flp;
-  ud->qh->qhlp.terminate = ui->frame_list[0].terminate;
-  for (i = 0; i < FRAME_LIST_ENTRIES; i++)
+  size_t qh_size = sizeof *allocate_queue_head ();
+  struct queue_head *qh = calloc (1, qh_size > 16 ? qh_size : 16);
+  if (qh != NULL) 
     {
-      ui->frame_list[i].flp = ptr_to_flp (vtop (ud->qh));
-      ui->frame_list[i].qh_select = 1;
-      ui->frame_list[i].terminate = 0;
+      qh->next_qh = UHCI_LP_TERMINATE;
+      qh->first_td = UHCI_LP_TERMINATE;
+      list_init (&qh->td_list);
+      sema_init (&qh->completion, 0); 
     }
-
-  /* add to device list */
-  list_push_back (&ui->devices, &ud->peers);
-
-  uhci_run (ui);
-  uhci_unlock (ui);
-
-  return ud;
+  return qh;
 }
 
-static void
-uhci_destroy_chan (host_dev_info hd)
+static struct tx_descriptor *
+allocate_transfer_descriptor (struct queue_head *qh) 
 {
-  struct uhci_dev_info *ud;
-  struct list_elem *li;
-
-  ud = hd;
-  uhci_lock (ud->ui);
-
-  uhci_stop (ud->ui);
-
-  uhci_remove_qh (ud->ui, ud->qh);
-
-  /* wake up all waiting */
-  li = list_begin (&ud->ui->waiting);
-  while (li != list_end (&ud->ui->waiting))
+  struct tx_descriptor *td = calloc (1, sizeof *td);
+  if (td == NULL) 
     {
-      struct usb_wait *w;
-      w = list_entry (li, struct usb_wait, peers);
-      if (w->ud == ud)
-       {
-         sema_up (&w->sem);
-         list_remove (li);
-       }
-      li = list_next (li);
+      /* FIXME: free the whole queue head. */
+      return NULL; 
     }
 
-  list_remove (&ud->peers);
-
-  uhci_run (ud->ui);
-
-  qh_free (ud->ui, ud->qh);
-
-  uhci_unlock (ud->ui);
-
-  free (ud);
-}
-
-/**
- * Remove a queue from the UHCI schedule
- */
-static void
-uhci_remove_qh (struct uhci_info *ui, struct queue_head *qh)
-{
-  uintptr_t qh_flp;
-
-  ASSERT (lock_held_by_current_thread (&ui->lock));
-  ASSERT (uhci_is_stopped (ui));
-  ASSERT (qh != NULL);
-
-  qh_flp = ptr_to_flp (vtop (qh));
-  /* remove from host queue */
-  if (ui->frame_list[0].flp == qh_flp)
-    {
-      int i;
-      /* up top */
-      for (i = 0; i < FRAME_LIST_ENTRIES; i++)
-       {
-         ui->frame_list[i] = qh->qhlp;
-       }
-    }
+  if (list_empty (&qh->td_list))
+    qh->first_td = make_lp (td);
   else
     {
-      /* in the middle */
-      struct frame_list_ptr *fp;
-      struct frame_list_ptr *prev;
-
-      fp = ptov (flp_to_ptr (ui->frame_list[0].flp));
-      ASSERT (!fp->terminate);
-      do
-       {
-         prev = fp;
-         fp = ptov (flp_to_ptr (fp->flp));
-       }
-      while (!fp->terminate && fp->flp != qh_flp);
-      *prev = qh->qhlp;
+      struct list_elem *p = list_back (&qh->td_list);
+      struct tx_descriptor *prev_td = list_entry (p, struct tx_descriptor,
+                                                  td_elem);
+      prev_td->next_td = make_lp (td);
     }
+  list_push_back (&qh->td_list, &td->td_elem);
+  td->next_td = UHCI_LP_TERMINATE;
+  return td;
 }
 
-/**
- * Put UHCI into stop state
- * Wait until status register reflects setting
- */
-static void
-uhci_stop (struct uhci_info *ui)
-{
-  ASSERT (intr_get_level () != INTR_OFF);
-  ASSERT (lock_held_by_current_thread (&ui->lock));
-
-  uhci_stop_unlocked (ui);
-}
-
-static void
-uhci_stop_unlocked (struct uhci_info *ui)
+static host_dev_info
+uhci_create_chan (host_info hi, int dev_addr, int ver)
 {
-  uint16_t cmd;
-  int i;
-
-  cmd = pci_reg_read16 (ui->io, UHCI_REG_USBCMD);
-  cmd = cmd & ~USB_CMD_RS;
+  struct uhci_info *ui;
+  struct uhci_dev_info *ud;
 
-  pci_reg_write16 (ui->io, UHCI_REG_USBCMD, cmd);
+  ASSERT (dev_addr <= 127 && dev_addr >= 0);
 
-  /* wait for execution schedule to finish up */
-  for (i = 0; i < 1000; i++)
-    {
-      if (uhci_is_stopped (ui))
-       return;
-    }
+  ui = hi;
 
-  PANIC ("UHCI: Controller did not halt\n");
+  ud = malloc (sizeof (struct uhci_dev_info));
+  ud->dev_addr = dev_addr;
+  ud->low_speed = (ver == USB_VERSION_1_0) ? true : false;
 
-}
+  ud->errors = 0;
+  ud->ui = ui;
+  lock_init (&ud->lock);
 
-static void
-uhci_run_unlocked (struct uhci_info *ui)
-{
-  uint16_t cmd;
-  cmd = pci_reg_read16 (ui->io, UHCI_REG_USBCMD);
-  cmd = cmd | USB_CMD_RS | USB_CMD_MAX_PACKET;
-  pci_reg_write16 (ui->io, UHCI_REG_USBCMD, cmd);
+  return ud;
 }
 
-/**
- * Put UHCI into 'Run' State 
- */
 static void
-uhci_run (struct uhci_info *ui)
+uhci_destroy_chan (host_dev_info hd UNUSED)
 {
-  ASSERT (lock_held_by_current_thread (&ui->lock));
-  uhci_run_unlocked (ui);
-}
-
-static struct tx_descriptor *
-uhci_acquire_td (struct uhci_info *ui)
-{
-  size_t td_idx;
-  struct tx_descriptor *td;
-
-  ASSERT (lock_held_by_current_thread (&ui->lock));
-  ASSERT (!uhci_is_stopped (ui));
-
-  sema_down (&ui->td_sem);
-  td_idx = bitmap_scan_and_flip (ui->td_used, 0, 1, false);
-  ASSERT (td_idx != BITMAP_ERROR);
-  td = td_from_pool (ui, td_idx);
-
-  return td;
+  /* FIXME */
+  /* FIXME: wake up all waiting queue heads. */
+  /* FIXME: arrange for qhs to be freed. */
 }
 
 static void
@@ -1089,44 +1006,6 @@ uhci_modify_chan (host_dev_info hd, int dev_addr, int ver)
   ud->low_speed = (ver == USB_VERSION_1_0) ? true : false;
 }
 
-static void
-dump_all_qh (struct uhci_info *ui)
-{
-  struct list_elem *li;
-
-  printf ("schedule: %x...", vtop (ui->frame_list));
-  printf ("%x", *((uint32_t *) ui->frame_list));
-  li = list_begin (&ui->devices);
-  while (li != list_end (&ui->devices))
-    {
-      struct uhci_dev_info *ud;
-      ud = list_entry (li, struct uhci_dev_info, peers);
-      dump_qh (ud->qh);
-      li = list_next (li);
-    }
-}
-
-static void
-dump_qh (struct queue_head *qh)
-{
-  struct frame_list_ptr *fp;
-  printf ("qh: %p %x\n", qh, vtop (qh));
-  fp = &qh->qelp;
-  while (!fp->terminate)
-    {
-      printf ("%x %x\n", *((uint32_t *) fp), *(uint32_t *) (fp + 1));
-      fp = ptov (flp_to_ptr (fp->flp));
-    }
-  printf ("%x %x\n\n", *(uint32_t *) fp, *(uint32_t *) (fp + 1));
-}
-
-static struct tx_descriptor *
-td_from_pool (struct uhci_info *ui, int idx)
-{
-  ASSERT (idx >= 0 && idx < TD_ENTRIES);
-  return (((void *) ui->td_pool) + idx * 32);
-}
-
 static void
 uhci_detect_ports (struct uhci_info *ui)
 {
@@ -1142,75 +1021,6 @@ uhci_detect_ports (struct uhci_info *ui)
     }
 }
 
-static void
-uhci_stall_watchdog (struct uhci_info *ui)
-{
-  while (1)
-    {
-      int rmved;
-      timer_msleep (1000);
-      printf ("watchdog\n");
-      uhci_lock (ui);
-      uhci_stop (ui);
-      rmved = uhci_remove_stalled (ui);
-      if (rmved > 0)
-       printf ("removed stalled packet in watchdog\n");
-      uhci_run (ui);
-      uhci_unlock (ui);
-    }
-}
-
-static int
-uhci_remove_stalled (struct uhci_info *ui)
-{
-  struct list_elem *li;
-  int rmved;
-
-  rmved = 0;
-  li = list_begin (&ui->waiting);
-
-  intr_disable ();
-
-  while (li != list_end (&ui->waiting))
-    {
-      struct usb_wait *uw;
-      struct list_elem *next;
-      uint32_t ctrl;
-
-      next = list_next (li);
-      uw = list_entry (li, struct usb_wait, peers);
-
-      if ((!uw->td->control.active && uw->td->control.stalled) ||
-         (uw->td->control.nak))
-       {
-         memcpy (&ctrl, &uw->td->control, 4);
-         printf ("CTRL: %x\n", ctrl);
-         list_remove (li);
-         uhci_remove_error_td (uw->td);
-         sema_up (&uw->sem);
-         rmved++;
-       }
-      li = next;
-    }
-
-  intr_enable ();
-
-  return rmved;
-}
-
-static void
-uhci_release_td (struct uhci_info *ui, struct tx_descriptor *td)
-{
-  int ofs = (uintptr_t) td - (uintptr_t) ui->td_pool;
-  int entry = ofs / 32;
-
-  ASSERT (entry < TD_ENTRIES);
-
-  td->flags = 0;
-  bitmap_reset (ui->td_used, entry);
-  sema_up (&ui->td_sem);
-}
-
 static host_eop_info
 uhci_create_eop (host_dev_info hd, int eop, int maxpkt)
 {
@@ -1233,31 +1043,3 @@ uhci_remove_eop (host_eop_info hei)
 {
   free (hei);
 }
-
-static struct queue_head *
-qh_alloc (struct uhci_info *ui)
-{
-  size_t qh_idx;
-  struct queue_head *qh;
-
-  ASSERT (lock_held_by_current_thread (&ui->lock));
-
-  qh_idx = bitmap_scan_and_flip (ui->qh_used, 0, 1, false);
-  if (qh_idx == BITMAP_ERROR)
-    {
-      PANIC ("UHCI: Too many queue heads in use-- runaway USB stack?\n");
-    }
-  qh = (void *) (((intptr_t) ui->qh_pool) + qh_idx * 16);
-
-  return qh;
-}
-
-static void
-qh_free (struct uhci_info *ui, struct queue_head *qh)
-{
-  size_t entry;
-  ASSERT (lock_held_by_current_thread (&ui->lock));
-
-  entry = ((intptr_t) qh - (intptr_t) ui->qh_pool) / 16;
-  bitmap_reset (ui->qh_used, entry);
-}
index f6f803b9b6dc437a25876928679ab393176c086d..e41baa72bcda6424c536066b0cef78fb306f105a 100644 (file)
    Otherwise, a new page of memory, called an "arena", is
    obtained from the page allocator (if none is available,
    malloc() returns a null pointer).  The new arena is divided
-   into blocks, all of which are added to the descriptor's free
-   list.  Then we return one of the new blocks.
+   into blocks.  The first block in the page is reserved for
+   arena bookkeeping data (struct arena).  The remaining blocks
+   are added to the descriptor's free list.  Then we return one
+   of the new blocks.
 
    When we free a block, we add it to its descriptor's free list.
    But if the arena that the block was in now has no in-use
@@ -61,7 +63,7 @@ struct block
   };
 
 /* Our set of descriptors. */
-static struct desc descs[10];   /* Descriptors. */
+static struct desc descs[11];   /* Descriptors. */
 static size_t desc_cnt;         /* Number of descriptors. */
 
 static struct arena *block_to_arena (struct block *);
@@ -73,19 +75,25 @@ malloc_init (void)
 {
   size_t block_size;
 
-  for (block_size = 16; block_size < PGSIZE / 2; block_size *= 2)
+  for (block_size = 16; block_size <= PGSIZE / 2; block_size *= 2)
     {
       struct desc *d = &descs[desc_cnt++];
       ASSERT (desc_cnt <= sizeof descs / sizeof *descs);
+      ASSERT (block_size >= sizeof (struct arena));
       d->block_size = block_size;
-      d->blocks_per_arena = (PGSIZE - sizeof (struct arena)) / block_size;
+      d->blocks_per_arena = (PGSIZE / block_size) - 1;
       list_init (&d->free_list);
       lock_init (&d->lock);
     }
 }
 
 /* Obtains and returns a new block of at least SIZE bytes.
-   Returns a null pointer if memory is not available. */
+   Returns a null pointer if memory is not available.
+
+   If SIZE is PGSIZE / 2 or less, the returned block is
+   guaranteed to be aligned on the least power-of-2 boundary
+   greater than or equal to SIZE.  (Hence, such a block never
+   crosses the boundary between two physical pages.) */
 void *
 malloc (size_t size) 
 {
@@ -174,7 +182,9 @@ calloc (size_t a, size_t b)
   return p;
 }
 
-/* Returns the number of bytes allocated for BLOCK. */
+/* Returns the number of bytes allocated for BLOCK.  If the
+   return value is PGSIZE / 2 or less, the block is also
+   guaranteed to be aligned to that power-of-2 boundary. */
 static size_t
 block_size (void *block) 
 {
@@ -274,9 +284,10 @@ block_to_arena (struct block *b)
   ASSERT (a->magic == ARENA_MAGIC);
 
   /* Check that the block is properly aligned for the arena. */
-  ASSERT (a->desc == NULL
-          || (pg_ofs (b) - sizeof *a) % a->desc->block_size == 0);
-  ASSERT (a->desc != NULL || pg_ofs (b) == sizeof *a);
+  ASSERT (a->desc != NULL
+          ? pg_ofs (b) % a->desc->block_size == 0
+          : pg_ofs (b) == sizeof *a);
+  ASSERT (pg_ofs (b) != 0);
 
   return a;
 }
@@ -288,7 +299,5 @@ arena_to_block (struct arena *a, size_t idx)
   ASSERT (a != NULL);
   ASSERT (a->magic == ARENA_MAGIC);
   ASSERT (idx < a->desc->blocks_per_arena);
-  return (struct block *) ((uint8_t *) a
-                           + sizeof *a
-                           + idx * a->desc->block_size);
+  return (struct block *) ((uint8_t *) a + (idx + 1) * a->desc->block_size);
 }