usb.patch, with conflicts and some warnings fixed
authorBen Pfaff <blp@cs.stanford.edu>
Fri, 12 Dec 2008 05:03:36 +0000 (21:03 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Fri, 12 Dec 2008 05:03:36 +0000 (21:03 -0800)
20 files changed:
src/Makefile.build
src/devices/pci.c [new file with mode: 0644]
src/devices/pci.h [new file with mode: 0644]
src/devices/pci_lookup.h [new file with mode: 0644]
src/devices/usb.c [new file with mode: 0644]
src/devices/usb.h [new file with mode: 0644]
src/devices/usb_ehci.c [new file with mode: 0644]
src/devices/usb_hub.c [new file with mode: 0644]
src/devices/usb_hub.h [new file with mode: 0644]
src/devices/usb_storage.c [new file with mode: 0644]
src/devices/usb_storage.h [new file with mode: 0644]
src/devices/usb_uhci.c [new file with mode: 0644]
src/lib/endian.h [new file with mode: 0644]
src/threads/init.c
src/threads/interrupt.c
src/threads/interrupt.h
src/threads/palloc.c
src/threads/palloc.h
src/threads/pte.h
src/userprog/pagedir.c

index e997d2787543cb4860edff352660dea9ae350bcf..763f0aff8fd22d7ee0805e87078a7e797da3daa4 100644 (file)
@@ -36,6 +36,12 @@ devices_SRC += devices/intq.c                # Interrupt queue.
 devices_SRC += devices/rtc.c           # Real-time clock.
 devices_SRC += devices/shutdown.c      # Reboot and power off.
 devices_SRC += devices/speaker.c       # PC speaker.
+devices_SRC += devices/pci.c           # PCI bus.
+devices_SRC += devices/usb.c           # USB layer.
+devices_SRC += devices/usb_uhci.c      # UHCI device.
+devices_SRC += devices/usb_ehci.c      # EHCI device.
+devices_SRC += devices/usb_storage.c   # USB mass storage class driver.
+devices_SRC += devices/usb_hub.c       # USB hub class driver.
 
 # Library code shared between kernel and user programs.
 lib_SRC  = lib/debug.c                 # Debug helpers.
diff --git a/src/devices/pci.c b/src/devices/pci.c
new file mode 100644 (file)
index 0000000..2df8e2a
--- /dev/null
@@ -0,0 +1,812 @@
+#define PCI_TRANSLATION_ENABLE 1
+
+#include "devices/pci.h"
+#include "threads/init.h"
+#include "threads/malloc.h"
+#include "threads/interrupt.h"
+#include "threads/pte.h"
+#include "threads/io.h"
+#include "pci_lookup.h"
+#include <round.h>
+#include <list.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+extern uint32_t *base_page_dir;
+
+#define PCI_REG_ADDR   0xcf8
+#define PCI_REG_DATA   0xcfc
+#define pci_config_offset(bus, dev, func, reg) (0x80000000 | ((bus) << 16) | ((dev) << 11) | ((func) << 8) | (reg & (~3)))
+
+#define PCI_MAX_DEV_PER_BUS    32
+#define PCI_MAX_FUNC_PER_DEV   8
+
+#define PCI_HEADER_SZ          256
+
+#define PCI_CMD_IO             0x001   /* enable io response */
+#define PCI_CMD_MEMORY         0x002   /* enable memory response */
+#define PCI_CMD_MASTER         0x004   /* enable bus mastering */
+#define PCI_CMD_SPECIAL                0x008   /* enable special cycles */
+#define PCI_CMD_INVALIDATE     0x010   /* memory write + invalidate */
+#define PCI_CMD_PALETTE                0x020   /* palette snooping */
+#define PCI_CMD_PARITY         0x040   /* parity checking */
+#define PCI_CMD_WAIT           0x080   /* address/data stepping */
+#define PCI_CMD_SERR           0x100   /* serr */
+#define PCI_CMD_FASTBACK       0x200   /* back-to-back writing */
+#define PCI_CMD_INTX_DISABLE   0x400   /* emulation disable */
+
+#define PCI_STATUS_CAPLIST     0x10    /* capability list */
+#define PCI_STATUS_66MHZ       0x20    /* 66mhz pci 2.1 bus */
+#define PCI_STATUS_UDF         0x40    /* user definable features */
+#define PCI_STATUS_FASTBACK    0x80    /* fast back to back */
+#define PCI_STATUS_PARITY      0x100   /* parity error detected */
+#define PCI_STATUS_DEVSEL      0x600   /* devsel mask */
+#define PCI_STATUS_DEVSEL_FAST 0
+#define PCI_STATUS_DEVSEL_MED  0x200
+#define PCI_STATUS_DEVSEL_SLOW 0x400
+#define PCI_STATUS_SIG_ABORT   0x0800  /* set on target abort */
+#define PCI_STATUS_REC_ABORT   0x1000  /* master ack of abort */
+#define PCI_STATUS_REC_ABORT_M 0x2000  /* set on master abort */
+#define PCI_STATUS_SERR                0x4000  /* system error */
+#define PCI_STATUS_PARITY2     0x8000  /* set on parity err */
+
+#define PCI_HEADER_NORMAL      0
+#define PCI_HEADER_BRIDGE      1
+#define PCI_HEADER_CARDBUS     2
+
+#define PCI_HEADER_MASK                0x7f
+#define PCI_HEADER_MULTIFUNC   0x80
+
+#define pci_get_reg_offset(x)  (&(((pci_config_header*)(NULL))->x))
+
+#define PCI_VENDOR_INVALID     0xffff
+
+#define PCI_BASEADDR_IO                0x00000001
+#define PCI_BASEADDR_TYPEMASK  0x00000006
+#define PCI_BASEADDR_32BIT     0x00000000
+#define PCI_BASEADDR_64BIT     0x00000004
+#define PCI_BASEADDR_PREFETCH  0x00000008
+#define PCI_BASEADDR_ADDR32    0xfffffff0
+#define PCI_BASEADDR_ADDR64    0xfffffffffffffff0
+#define PCI_BASEADDR_IOPORT    0xfffffffc
+
+#pragma pack(1)
+
+struct pci_config_header
+{
+  uint16_t pci_vendor_id;
+  uint16_t pci_device_id;
+
+  uint16_t pci_command;
+  uint16_t pci_status;
+
+  uint8_t pci_revision;
+
+  /* pci class */
+  uint8_t pci_interface;
+  uint8_t pci_minor;
+  uint8_t pci_major;
+
+  uint8_t pci_cachelinesz;
+  uint8_t pci_latency;
+  uint8_t pci_header;          /* header type */
+  uint8_t pci_bist;            /* self test */
+
+  uint32_t pci_base_reg[6];
+  uint32_t pci_cis;            /* cardbus cis pointer */
+  uint16_t pci_sub_vendor_id;
+  uint16_t pci_sub_id;
+  uint32_t pci_rom_addr;
+  uint8_t pci_capabilities;
+  uint8_t pci_resv[7];
+  uint8_t pci_int_line;
+  uint8_t pci_int_pin;
+  uint8_t pci_min_gnt;
+  uint8_t pci_max_latency;
+};
+
+#pragma pack()
+
+#define PCI_BASE_COUNT         6
+struct pci_dev
+{
+  struct pci_config_header pch;
+  uint8_t bus, dev, func;
+  int base_reg_size[PCI_BASE_COUNT];
+
+  pci_handler_func *irq_handler;
+  void *irq_handler_aux;
+
+  struct list io_ranges;
+  struct list_elem peer;
+  struct list_elem int_peer;
+};
+
+enum pci_io_type
+{ PCI_IO_MEM, PCI_IO_PORT };
+
+/* represents a PCI IO range */
+struct pci_io
+{
+  struct pci_dev *dev;
+  enum pci_io_type type;
+  size_t size; /* bytes in range */
+  union
+  {
+    void *ptr; /* virtual memory address */
+    int port;  /* io port */
+  } addr;
+  struct list_elem peer; /* linkage */
+};
+
+
+static void pci_write_config (int bus, int dev, int func, int reg,
+                             int size, uint32_t data);
+static uint32_t pci_read_config (int bus, int dev, int func, int reg,
+                                int size);
+static void pci_read_all_config (int bus, int dev, int func,
+                                struct pci_config_header *pch);
+static int pci_scan_bus (int bus);
+static int pci_probe (int bus, int dev, int func,
+                     struct pci_config_header *ph);
+static int pci_pci_bridge (struct pci_dev *pd);
+static void pci_power_on (struct pci_dev *pd);
+static void pci_setup_io (struct pci_dev *pd);
+static void pci_interrupt (struct intr_frame *);
+static void pci_print_dev_info (struct pci_dev *pd);
+static void *pci_alloc_mem (void *phys_ptr, int pages);
+
+static struct list devices;
+static struct list int_devices;
+
+/* number of pages that have been allocated to pci devices in the pci zone */
+static int num_pci_pages;
+
+void
+pci_init (void)
+{
+  list_init (&devices);
+  list_init (&int_devices);
+
+  num_pci_pages = 0;
+
+  pci_scan_bus (0);
+  pci_print_stats ();
+}
+
+struct pci_dev *
+pci_get_device (int vendor, int device, int func, int n)
+{
+  struct list_elem *e;
+  int count;
+
+  count = 0;
+  e = list_begin (&devices);
+  while (e != list_end (&devices))
+    {
+      struct pci_dev *pd;
+
+      pd = list_entry (e, struct pci_dev, peer);
+      if (pd->pch.pci_vendor_id == vendor && pd->pch.pci_device_id == device)
+       if (pd->func == func)
+         {
+           if (count == n)
+             return pd;
+           count++;
+         }
+
+      e = list_next (e);
+    }
+
+  return NULL;
+}
+
+struct pci_dev *
+pci_get_dev_by_class (int major, int minor, int iface, int n)
+{
+  struct list_elem *e;
+  int count;
+
+  count = 0;
+  e = list_begin (&devices);
+  while (e != list_end (&devices))
+    {
+      struct pci_dev *pd;
+
+      pd = list_entry (e, struct pci_dev, peer);
+      if (pd->pch.pci_major == major && pd->pch.pci_minor == minor &&
+         pd->pch.pci_interface == iface)
+       {
+         if (count == n)
+           return pd;
+         count++;
+       }
+
+      e = list_next (e);
+    }
+
+  return NULL;
+
+}
+
+struct pci_io *
+pci_io_enum (struct pci_dev *pio, struct pci_io *last)
+{
+  struct list_elem *e;
+
+  if (last != NULL)
+    e = list_next (&last->peer);
+  else
+    e = list_begin (&pio->io_ranges);
+
+  if (e == list_end (&pio->io_ranges))
+    return NULL;
+
+  return list_entry (e, struct pci_io, peer);
+}
+
+void
+pci_register_irq (struct pci_dev *pd, pci_handler_func * f, void *aux)
+{
+  int int_vec;
+  ASSERT (pd != NULL);
+  ASSERT (pd->irq_handler == NULL);
+
+  pd->irq_handler_aux = aux;
+  pd->irq_handler = f;
+  int_vec = pd->pch.pci_int_line + 0x20;
+
+  list_push_back (&int_devices, &pd->int_peer);
+
+  /* ensure that pci interrupt is hooked */
+  if (!intr_is_registered (int_vec))
+    intr_register_ext (int_vec, pci_interrupt, "PCI");
+}
+
+void
+pci_unregister_irq (struct pci_dev *pd)
+{
+  ASSERT (pd != NULL);
+
+  intr_disable ();
+  list_remove (&pd->int_peer);
+  intr_enable ();
+
+  pd->irq_handler = NULL;
+  pd->irq_handler_aux = NULL;
+}
+
+size_t
+pci_io_size (struct pci_io *pio)
+{
+  ASSERT (pio != NULL);
+
+  return pio->size;
+}
+
+void
+pci_reg_write32 (struct pci_io *pio, int reg, uint32_t data)
+{
+  ASSERT (pio != NULL);
+  ASSERT ((unsigned) reg < pio->size);
+
+  if (pio->type == PCI_IO_MEM)
+    {
+      *((uint32_t *) (pio->addr.ptr + reg)) = data;
+    }
+  else if (pio->type == PCI_IO_PORT)
+    {
+      outl (pio->addr.port + reg, data);
+    }
+  else
+    PANIC ("pci: Invalid IO type\n");
+}
+
+void
+pci_reg_write16 (struct pci_io *pio, int reg, uint16_t data)
+{
+  ASSERT (pio != NULL);
+  ASSERT ((unsigned) reg < pio->size);
+
+  if (pio->type == PCI_IO_MEM)
+    {
+      *((uint16_t *) (pio->addr.ptr + reg)) = data;
+    }
+  else if (pio->type == PCI_IO_PORT)
+    {
+      outw (pio->addr.port + reg, data);
+    }
+  else
+    PANIC ("pci: Invalid IO type\n");
+}
+
+void
+pci_reg_write8 (struct pci_io *pio, int reg, uint8_t data)
+{
+  ASSERT (pio != NULL);
+  ASSERT ((unsigned) reg < pio->size);
+
+  if (pio->type == PCI_IO_MEM)
+    {
+      ((uint8_t *) pio->addr.ptr)[reg] = data;
+    }
+  else if (pio->type == PCI_IO_PORT)
+    {
+      outb (pio->addr.port + reg, data);
+    }
+  else
+    PANIC ("pci: Invalid IO type\n");
+}
+
+uint32_t
+pci_reg_read32 (struct pci_io *pio, int reg)
+{
+  uint32_t ret;
+
+  ASSERT (pio != NULL);
+  ASSERT ((unsigned) reg < pio->size);
+
+  if (pio->type == PCI_IO_MEM)
+    {
+      ret = ((uint32_t *) pio->addr.ptr)[reg];
+    }
+  else if (pio->type == PCI_IO_PORT)
+    {
+      ret = inl (pio->addr.port + reg);
+    }
+  else
+    PANIC ("pci: Invalid IO type\n");
+
+  return ret;
+}
+
+uint16_t
+pci_reg_read16 (struct pci_io * pio, int reg)
+{
+  uint16_t ret;
+
+  ASSERT (pio != NULL);
+  ASSERT ((unsigned) reg < pio->size);
+
+  ret = 0;
+  if (pio->type == PCI_IO_MEM)
+    {
+      ret = ((uint16_t *) pio->addr.ptr)[reg];
+    }
+  else if (pio->type == PCI_IO_PORT)
+    {
+      ret = inw (pio->addr.port + reg);
+    }
+  else
+    PANIC ("pci: Invalid IO type\n");
+
+  return ret;
+
+}
+
+uint8_t
+pci_reg_read8 (struct pci_io * pio, int reg)
+{
+  uint8_t ret;
+
+  ASSERT (pio != NULL);
+  ASSERT ((unsigned) reg < pio->size);
+
+  if (pio->type == PCI_IO_MEM)
+    {
+      ret = ((uint8_t *) pio->addr.ptr)[reg];
+    }
+  else if (pio->type == PCI_IO_PORT)
+    {
+      ret = inb (pio->addr.port + reg);
+    }
+  else
+    PANIC ("pci: Invalid IO type\n");
+
+  return ret;
+}
+
+void
+pci_read_in (struct pci_io *pio UNUSED, int off UNUSED, size_t size UNUSED,
+            void *buf UNUSED)
+{
+  PANIC ("pci_read_in: STUB");
+}
+
+void
+pci_write_out (struct pci_io *pio UNUSED, int off UNUSED, size_t size UNUSED,
+              const void *buf UNUSED)
+{
+  PANIC ("pci_write_out: STUB");
+}
+
+static void
+pci_write_config (int bus, int dev, int func, int reg,
+                 int size, uint32_t data)
+{
+  int config_offset;
+
+  config_offset = pci_config_offset (bus, dev, func, reg);
+
+  outl (PCI_REG_ADDR, config_offset);
+
+  switch (size)
+    {
+    case 1:
+      outb (PCI_REG_DATA + (reg & 3), data);
+      break;
+
+    case 2:
+      outw (PCI_REG_DATA + (reg & 3), data);
+      break;
+
+    case 4:
+      outl (PCI_REG_DATA, data);
+      break;
+    }
+}
+
+static uint32_t
+pci_read_config (int bus, int dev, int func, int reg, int size)
+{
+  uint32_t ret;
+  int config_offset;
+
+  config_offset = pci_config_offset (bus, dev, func, reg);
+
+  outl (PCI_REG_ADDR, config_offset);
+
+  switch (size)
+    {
+    case 1:
+      ret = inb (PCI_REG_DATA);
+      break;
+    case 2:
+      ret = inw (PCI_REG_DATA);
+      break;
+    case 4:
+      ret = inl (PCI_REG_DATA);
+      break;
+    default:
+      PANIC ("pci: Strange config read size\n");
+    }
+
+  return ret;
+}
+
+/* read entire configuration header into memory */
+static void
+pci_read_all_config (int bus, int dev, int func,
+                    struct pci_config_header *pch)
+{
+  unsigned int i;
+  for (i = 0; i < ((sizeof (struct pci_config_header) + 3) & ~3) / 4; i++)
+    {
+      ((uint32_t *) pch)[i] = pci_read_config (bus, dev, func, i * 4, 4);
+    }
+}
+
+
+/** scan PCI bus for all devices */
+static int
+pci_scan_bus (int bus)
+{
+  int dev;
+  int max_bus;
+
+  max_bus = 0;
+
+  for (dev = 0; dev < PCI_MAX_DEV_PER_BUS; dev++)
+    {
+      struct pci_config_header pch;
+      int func_cnt, func;
+
+      pci_read_all_config (bus, dev, 0, &pch);
+
+      if (pch.pci_vendor_id == PCI_VENDOR_INVALID)
+       continue;
+
+      func_cnt = 8;
+      if (!(pch.pci_header & PCI_HEADER_MULTIFUNC))
+       {
+         func_cnt = 1;
+       }
+
+      for (func = 0; func < func_cnt; func++)
+       {
+         int retbus;
+         retbus = pci_probe (bus, dev, func, &pch);
+         if (retbus > max_bus)
+           max_bus = retbus;
+       }
+    }
+
+  return max_bus;
+}
+
+/* get all information for a PCI device given bus/dev/func 
+   add pci device to device list if applicable 
+   return a new bus number if new bus is found
+ */
+static int
+pci_probe (int bus, int dev, int func, struct pci_config_header *ph)
+{
+  struct pci_dev *pd;
+
+  if (func != 0)
+    {
+      pci_read_all_config (bus, dev, func, ph);
+      if (ph->pci_vendor_id == PCI_VENDOR_INVALID)
+       return bus;
+    }
+
+  pd = malloc (sizeof (struct pci_dev));
+  memcpy (&pd->pch, ph, sizeof (struct pci_config_header));
+  pd->irq_handler = NULL;
+  pd->irq_handler_aux = NULL;
+  pd->bus = bus;
+  pd->dev = dev;
+  pd->func = func;
+
+  list_init (&pd->io_ranges);
+  list_push_back (&devices, &pd->peer);
+
+
+  if (ph->pci_major == PCI_MAJOR_BRIDGE)
+    {
+      if (ph->pci_minor == PCI_MINOR_PCI)
+       return pci_pci_bridge (pd);
+    }
+
+  pci_setup_io (pd);
+  pci_power_on (pd);
+
+  return bus;
+}
+
+static void
+pci_setup_io (struct pci_dev *pd)
+{
+  int i;
+  for (i = 0; i < PCI_BASE_COUNT; i++)
+    {
+      uint32_t tmp;
+      struct pci_io *pio;
+
+      if (pd->pch.pci_base_reg[i] == 0)
+       {
+         continue;
+       }
+
+      /* determine io granularity.. */
+      pci_write_config (pd->bus, pd->dev, pd->func,
+                       offsetof (struct pci_config_header, pci_base_reg[i]),
+                       4, ~0);
+
+      tmp =
+       pci_read_config (pd->bus, pd->dev, pd->func,
+                        offsetof (struct pci_config_header, pci_base_reg[i]),
+                        4);
+
+      /* configure BAR to the default */
+      pci_write_config (pd->bus, pd->dev, pd->func,
+                       offsetof (struct pci_config_header, pci_base_reg[i]),
+                       4, pd->pch.pci_base_reg[i]);
+
+      pio = malloc (sizeof (struct pci_io));
+      pio->dev = pd;
+
+      if (tmp & PCI_BASEADDR_IO)
+       {
+         pio->type = PCI_IO_PORT;
+         pio->size = (uint16_t) ((~tmp + 1) & 0xffff) + 1;
+         pio->addr.port = pd->pch.pci_base_reg[i] & ~1;
+       }
+      else
+       {
+         uint32_t ofs;
+
+         pio->type = PCI_IO_MEM;
+         pio->size = ROUND_UP ((~tmp + 1), PGSIZE);
+         ofs = (pd->pch.pci_base_reg[i] & 0xfffffff0 & PGMASK);
+         pio->addr.ptr = pci_alloc_mem ((void *) pd->pch.pci_base_reg[i],
+                                        pio->size / PGSIZE);
+         if (pio->addr.ptr == NULL)
+           {
+             printf ("PCI: %d pages for %d:%d.%d failed - may not work\n",
+                     pio->size / PGSIZE, pd->bus, pd->dev, pd->func);
+             free (pio);
+             pio = NULL;
+           }
+         else
+           {
+             pio->addr.ptr = (void *) ((uintptr_t) pio->addr.ptr + ofs);
+           }
+       }
+
+      /* add IO struct to device, if valid */
+      if (pio != NULL)
+       list_push_back (&pd->io_ranges, &pio->peer);
+    }
+
+}
+
+static void
+pci_power_on (struct pci_dev *pd UNUSED)
+{
+  /* STUB */
+}
+
+static int
+pci_pci_bridge (struct pci_dev *pd)
+{
+  int max_bus;
+  uint16_t command;
+
+  /* put bus into offline mode */
+  command = pd->pch.pci_command;
+  command &= ~0x3;
+  pci_write_config (pd->bus, pd->dev, pd->func,
+                   offsetof (struct pci_config_header, pci_command),
+                   2, command);
+  pd->pch.pci_command = command;
+
+  /* set up primary bus */
+  pci_write_config (pd->bus, pd->dev, pd->func, 0x18, 1, pd->bus);
+  /* secondary bus */
+  pci_write_config (pd->bus, pd->dev, pd->func, 0x19, 1, pd->bus + 1);
+
+  /* disable subordinates */
+  pci_write_config (pd->bus, pd->dev, pd->func, 0x1a, 1, 0xff);
+
+  /* scan this new bus */
+  max_bus = pci_scan_bus (pd->bus + 1);
+
+  /* set subordinate to the actual last bus */
+  pci_write_config (pd->bus, pd->dev, pd->func, 0x1a, 1, max_bus);
+
+  /* put online */
+  command |= 0x03;
+  pci_write_config (pd->bus, pd->dev, pd->func,
+                   offsetof (struct pci_config_header, pci_command),
+                   2, command);
+  pd->pch.pci_command = command;
+
+  return max_bus;
+}
+
+/* alert all PCI devices waiting on interrupt line that IRQ fired */
+static void
+pci_interrupt (struct intr_frame *frame)
+{
+  struct list_elem *e;
+  int int_line;
+
+  int_line = frame->vec_no - 0x20;
+  e = list_begin (&int_devices);
+  while (e != list_end (&int_devices))
+    {
+      struct pci_dev *pd;
+
+      pd = list_entry (e, struct pci_dev, int_peer);
+      if (pd->pch.pci_int_line == int_line)
+       pd->irq_handler (pd->irq_handler_aux);
+      e = list_next (e);
+    }
+}
+
+/* display information on all USB devices */
+void
+pci_print_stats (void)
+{
+  struct list_elem *e;
+
+  e = list_begin (&devices);
+  while (e != list_end (&devices))
+    {
+      struct pci_dev *pd;
+
+      pd = list_entry (e, struct pci_dev, peer);
+      pci_print_dev_info (pd);
+
+      e = list_next (e);
+    }
+}
+
+static void
+pci_print_dev_info (struct pci_dev *pd)
+{
+  printf ("PCI Device %d:%d.%d (%x,%x): %s - %s (%s) IRQ %d\n",
+         pd->bus, pd->dev, pd->func,
+         pd->pch.pci_vendor_id,
+         pd->pch.pci_device_id,
+         pci_lookup_vendor (pd->pch.pci_vendor_id),
+         pci_lookup_device (pd->pch.pci_vendor_id, pd->pch.pci_device_id),
+         pci_lookup_class (pd->pch.pci_major, pd->pch.pci_minor,
+                           pd->pch.pci_interface), pd->pch.pci_int_line);
+}
+
+void
+pci_mask_irq (struct pci_dev *pd)
+{
+  intr_irq_mask (pd->pch.pci_int_line);
+}
+
+void
+pci_unmask_irq (struct pci_dev *pd)
+{
+  intr_irq_unmask (pd->pch.pci_int_line);
+}
+
+void
+pci_write_config16 (struct pci_dev *pd, int off, uint16_t data)
+{
+  pci_write_config (pd->bus, pd->dev, pd->func, off, 2, data);
+}
+
+void
+pci_write_config32 (struct pci_dev *pd, int off, uint32_t data)
+{
+  pci_write_config (pd->bus, pd->dev, pd->func, off, 4, data);
+}
+
+void
+pci_write_config8 (struct pci_dev *pd, int off, uint8_t data)
+{
+  pci_write_config (pd->bus, pd->dev, pd->func, off, 1, data);
+}
+
+uint8_t
+pci_read_config8 (struct pci_dev *pd, int off)
+{
+  return pci_read_config (pd->bus, pd->dev, pd->func, off, 1);
+}
+
+uint16_t
+pci_read_config16 (struct pci_dev * pd, int off)
+{
+  return pci_read_config (pd->bus, pd->dev, pd->func, off, 2);
+}
+
+uint32_t
+pci_read_config32 (struct pci_dev * pd, int off)
+{
+  return pci_read_config (pd->bus, pd->dev, pd->func, off, 4);
+}
+
+
+/** allocate PCI memory pages for PCI devices */
+static void *
+pci_alloc_mem (void *phys_ptr, int pages)
+{
+  void *vaddr;
+  int i;
+
+  phys_ptr = (void *) ((uintptr_t) phys_ptr & ~PGMASK);
+
+  /* not enough space to allocate? */
+  if ((unsigned) (num_pci_pages + pages) >= (unsigned) PCI_ADDR_ZONE_PAGES)
+    {
+      return NULL;
+    }
+
+  /* insert into PCI_ZONE */
+  for (i = 0; i < pages; i++)
+    {
+      uint32_t pte_idx = (num_pci_pages + i) % 1024;
+      uint32_t pde_idx = (num_pci_pages + i) / 1024;
+      uint32_t *pt;
+      uint32_t pte;
+
+      pde_idx += pd_no ((void *) PCI_ADDR_ZONE_BEGIN);
+      pte = ((uint32_t) phys_ptr + (i * PGSIZE)) | PTE_P | PTE_W | PTE_CD;
+      pt = (uint32_t *) (ptov (init_page_dir[pde_idx] & ~PGMASK));
+      pt[pte_idx] = pte;
+    }
+
+  vaddr = (void *) (PCI_ADDR_ZONE_BEGIN + (num_pci_pages * PGSIZE));
+  num_pci_pages += pages;
+
+  return vaddr;
+}
diff --git a/src/devices/pci.h b/src/devices/pci.h
new file mode 100644 (file)
index 0000000..258c022
--- /dev/null
@@ -0,0 +1,112 @@
+#ifndef DEVICES_PCI_H
+#define DEVICES_PCI_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+/**************************/
+/* some major/minor pairs */
+/**************************/
+#define PCI_MAJOR_EARLY                        0
+#define PCI_MINOR_VGA                  1
+
+#define PCI_MAJOR_MASS_STORAGE         1       /* mass storage controller */
+#define PCI_MINOR_SCSI                 0
+#define PCI_MINOR_IDE                  1
+#define PCI_MINOR_FLOPPY               2
+#define PCI_MINOR_IPI                  3
+#define PCI_MINOR_RAID                 4
+#define PCI_MINOR_MS_OTHER             0x80
+
+#define PCI_MAJOR_NETWORK              2       /* network controller */
+#define PCI_MINOR_ETHERNET             0
+#define PCI_MINOR_TOKENRING            1
+#define PCI_MINOR_FDDI                 2
+#define PCI_MINOR_ATM                  3
+#define PCI_MINOR_ISDN                 4
+#define PCI_MINOR_NET_OTHER            0x80
+
+#define PCI_MAJOR_DISPLAY              3       /* display controller */
+#define PCI_MAJOR_MULTIMEDIA           4       /* multimedia device */
+#define PCI_MAJOR_MEMORY               5       /* memory controller */
+
+#define PCI_MAJOR_BRIDGE               6       /* bus bridge controller */
+#define PCI_MINOR_HOST                 0
+#define PCI_MINOR_ISA                  1
+#define PCI_MINOR_EISA                 2
+#define PCI_MINOR_MCA                  3
+#define PCI_MINOR_PCI                  4
+#define PCI_MINOR_PCMCIA               5
+#define PCI_MINOR_NUBUS                        6
+#define PCI_MINOR_CARDBUS              7
+#define PCI_MINOR_RACEWAY              8
+
+#define PCI_MAJOR_SIMPLE_COMM          7
+
+#define PCI_MAJOR_BASE_PERIPHERAL      8
+#define PCI_MINOR_PIC                  0
+#define PCI_MINOR_DMA                  1
+#define PCI_MINOR_TIMER                        2
+#define PCI_MINOR_RTC                  3
+#define PCI_MINOR_HOTPLUG              4
+
+
+#define        PCI_MAJOR_INPUT                 9
+#define PCI_MAJOR_DOCK                 10
+#define PCI_MAJOR_PROCESSOR            11
+#define PCI_MINOR_386                  0
+#define PCI_MINOR_486                  1
+#define PCI_MINOR_PENTIUM              2
+#define PCI_MINOR_ALPHA                        0x10
+#define PCI_MINOR_PPC                  0x20
+#define PCI_MINOR_MIPS                 0x30
+#define PCI_MINOR_COPROC               0x40
+
+#define PCI_MAJOR_SERIALBUS            12
+#define PCI_MINOR_FIREWIRE             0
+#define PCI_MINOR_ACCESS               1
+#define PCI_MINOR_SSA                  2
+#define PCI_MINOR_USB                  3
+#define PCI_USB_IFACE_UHCI             0
+#define PCI_USB_IFACE_OHCI             0x10
+#define PCI_USB_IFACE_EHCI             0x20
+#define PCI_MINOR_FIBRE                        4
+#define PCI_MAJOR_UNDEF                        0xff
+
+typedef void pci_handler_func (void *AUX);
+
+/* used for io range for a pci device */
+struct pci_io;
+
+/* structure representing a specific pci device/function */
+struct pci_dev;
+
+void pci_init (void);
+struct pci_dev *pci_get_device (int vendor, int device, int func, int n);
+struct pci_dev *pci_get_dev_by_class (int major, int minor, int iface, int n);
+struct pci_io *pci_io_enum (struct pci_dev *, struct pci_io *last);
+void pci_register_irq (struct pci_dev *, pci_handler_func *, void *AUX);
+void pci_unregister_irq (struct pci_dev *);
+size_t pci_io_size (struct pci_io *);
+
+void pci_write_config8 (struct pci_dev *, int reg, uint8_t);
+void pci_write_config16 (struct pci_dev *, int reg, uint16_t);
+void pci_write_config32 (struct pci_dev *, int reg, uint32_t);
+uint8_t pci_read_config8 (struct pci_dev *, int reg);
+uint16_t pci_read_config16 (struct pci_dev *, int reg);
+uint32_t pci_read_config32 (struct pci_dev *, int reg);
+
+void pci_reg_write32 (struct pci_io *, int reg, uint32_t);
+void pci_reg_write16 (struct pci_io *, int reg, uint16_t);
+void pci_reg_write8 (struct pci_io *, int reg, uint8_t);
+uint32_t pci_reg_read32 (struct pci_io *, int reg);
+uint16_t pci_reg_read16 (struct pci_io *, int reg);
+uint8_t pci_reg_read8 (struct pci_io *, int reg);
+
+void pci_read_in (struct pci_io *, int off, size_t sz, void *buf);
+void pci_write_out (struct pci_io *, int off, size_t sz, const void *buf);
+void pci_print_stats (void);
+void pci_mask_irq (struct pci_dev *);
+void pci_unmask_irq (struct pci_dev *);
+
+#endif
diff --git a/src/devices/pci_lookup.h b/src/devices/pci_lookup.h
new file mode 100644 (file)
index 0000000..355f24b
--- /dev/null
@@ -0,0 +1,138 @@
+#ifndef PCILOOKUP_H
+#define PCILOOKUP_H
+
+#ifdef PCI_TRANSLATION_ENABLE
+
+struct pci_vendor
+{
+  uint16_t vendor_id;
+  char *vendor;
+};
+
+struct pci_vendor pci_vendor_table[] = {
+  {0x1013, "Cirrus Logic"},
+  {0x10de, "nVidia"},
+  {0x10EC, "Realtek"},
+  {0x11c1, "Agere Systems"},
+  {0x8086, "Intel"}
+};
+
+struct pci_device
+{
+  uint16_t vendor_id;
+  uint16_t device_id;
+  char *device;
+};
+
+struct pci_device pci_device_table[] = {
+  {0x1013, 0x00b8, "CL-GD5446"},
+  {0x10ec, 0x8029, "RTL8029 - NE2K Compatible"},
+  {0x10ec, 0x8139, "RTL8139"},
+  {0x8086, 0x1237, "82441FX"},
+  {0x8086, 0x7000, "82371SB_ISA"},
+  {0x8086, 0x7010, "82371SB_IDE"},
+  {0x8086, 0x7020, "82371SB_USB"},
+  {0x8086, 0x7113, "82371AB/EB/MB_ACPI"}
+};
+
+
+#define PCI_DEVICE_TABLE_SZ    (sizeof (pci_device_table) / sizeof (struct pci_device ))
+#define PCI_VENDOR_TABLE_SZ    (sizeof (pci_vendor_table) / sizeof (struct pci_vendor))
+
+/** Too lazy to to a binary search... */
+
+const char *pci_lookup_vendor (uint16_t vendor);
+const char *pci_lookup_device (uint16_t vendor, uint16_t device);
+
+const char *
+pci_lookup_vendor (uint16_t vendor)
+{
+  unsigned int i;
+  for (i = 0; i < PCI_VENDOR_TABLE_SZ; i++)
+    {
+      if (pci_vendor_table[i].vendor_id > vendor)
+       break;
+      if (pci_vendor_table[i].vendor_id == vendor)
+       return pci_vendor_table[i].vendor;
+    }
+
+  return "Unknown Vendor";
+}
+
+const char *
+pci_lookup_device (uint16_t vendor, uint16_t device)
+{
+  unsigned int i;
+  for (i = 0; i < PCI_DEVICE_TABLE_SZ; i++)
+    {
+      if (pci_device_table[i].vendor_id > vendor)
+       break;
+      if (pci_device_table[i].vendor_id == vendor &&
+         pci_device_table[i].device_id == device)
+       return pci_device_table[i].device;
+    }
+
+  return "Unknown Device";
+}
+
+#else
+
+#define pci_lookup_vendor(x)   "Unknown Vendor"
+#define pci_lookup_device(x,y) "Unknown Device"
+
+#endif
+
+struct pci_class
+{
+  uint8_t major;
+  uint8_t minor;
+  uint8_t iface;
+  char *name;
+};
+
+struct pci_class pci_class_table[] = {
+  {0, 0, 0, "Pre-PCI 2.0 Non-VGA Device"},
+  {0, 1, 0, "Pre-PCI 2.0 VGA Device"},
+  {1, 0, 0, "SCSI Controller"},
+  {1, 1, 0, "IDE Controller"},
+  {1, 2, 0, "Floppy Disk Controller"},
+  {2, 0, 0, "Ethernet"},
+  {3, 0, 0, "VGA Controller"},
+  {3, 1, 0, "XGA Controller"},
+  {5, 0, 0, "Memory Controller - RAM"},
+  {5, 1, 0, "Memory Controller - Flash"},
+  {6, 0, 0, "PCI Host"},
+  {6, 1, 0, "PCI-ISA Bridge"},
+  {6, 2, 0, "PCI-EISA Bridge"},
+  {6, 4, 0, "PCI-PCI Bridge"},
+  {6, 5, 0, "PCI-PCMCIA Bridge"},
+  {12, 0, 0, "Firewire Adapter"},
+  {12, 3, 0, "USB 1.1 Controller (UHCI)"},
+  {12, 3, 0x10, "USB 1.1 Controller (OHCI)"},
+  {12, 3, 0x20, "USB 2.0 Controller (EHCI)"}
+};
+
+#define PCI_CLASS_TABLE_SZ (sizeof(pci_class_table) / sizeof(struct pci_class))
+
+const char *pci_lookup_class (uint8_t major, uint8_t minor, uint8_t iface);
+const char *
+pci_lookup_class (uint8_t major, uint8_t minor, uint8_t iface)
+{
+  unsigned int i;
+  for (i = 0; i < PCI_CLASS_TABLE_SZ; i++)
+    {
+      if (pci_class_table[i].major > major)
+       break;
+      if (pci_class_table[i].major != major)
+       continue;
+      if (pci_class_table[i].minor != minor)
+       continue;
+      if (pci_class_table[i].iface != iface)
+       continue;
+      return pci_class_table[i].name;
+    }
+
+  return "Unknown Type";
+}
+
+#endif
diff --git a/src/devices/usb.c b/src/devices/usb.c
new file mode 100644 (file)
index 0000000..7cdb2f0
--- /dev/null
@@ -0,0 +1,776 @@
+#include "devices/usb.h"
+#include "devices/usb_hub.h"
+#include "threads/synch.h"
+#include "threads/malloc.h"
+#include "devices/timer.h"
+#include <kernel/bitmap.h>
+#include <string.h>
+#include <stdio.h>
+
+#define ADDRS_PER_HOST         127     /* number of configurable addresses */
+#define ADDR_DEFAULT           0       /* default configuration address */
+#define ADDR_FIRST             1       /* first configurable address */
+
+/** sometimes used for ''value' */
+/* used in desc_header */
+#define SETUP_DESC_DEVICE      1
+#define SETUP_DESC_CONFIG      2
+#define SETUP_DESC_STRING      3
+#define SETUP_DESC_IFACE       4
+#define SETUP_DESC_ENDPOINT    5
+#define SETUP_DESC_DEVQUAL     6
+#define SETUP_DESC_SPDCONFIG   7
+#define SETUP_DESC_IFACEPWR    8
+
+#pragma pack(1)
+struct desc_header
+{
+  uint8_t length;
+  uint8_t type;
+};
+
+/**
+ * More details can be found in chapter 9 of the usb1.1 spec
+ */
+
+struct device_descriptor
+{
+  struct desc_header hdr;
+  uint16_t usb_spec;
+  uint8_t dev_class;
+  uint8_t dev_subclass;
+  uint8_t dev_proto;
+  uint8_t max_pktsz;
+  uint16_t vendor_id;
+  uint16_t product_id;
+  uint16_t device_id;
+  uint8_t manufacturer;
+  uint8_t product;
+  uint8_t serial;
+  uint8_t num_configs;
+};
+
+struct device_qualifier
+{
+  uint16_t usb_spec;
+  uint8_t dev_class;
+  uint8_t dev_subclass;
+  uint8_t dev_proto;
+  uint8_t max_pktsz;
+  uint8_t num_configs;
+  uint8_t resv;
+};
+
+struct config_descriptor
+{
+  struct desc_header hdr;
+  uint16_t total_length;
+  uint8_t num_ifaces;
+  uint8_t config_val;
+  uint8_t config_desc;
+  uint8_t attributes;
+  uint8_t max_power;
+};
+
+struct interface_descriptor
+{
+  struct desc_header hdr;
+  uint8_t iface_num;
+  uint8_t alt_setting;
+  uint8_t num_endpoints;
+  uint8_t iface_class;
+  uint8_t iface_subclass;
+  uint8_t iface_proto;
+  uint8_t iface_desc;
+};
+
+struct endpoint_descriptor
+{
+  struct desc_header hdr;
+  /* end point address */
+  uint8_t endpoint_num:4;
+  uint8_t resv1:3;
+  uint8_t direction:1;
+
+  /* attributes */
+  uint8_t transfer:2;
+  uint8_t synch:2;
+  uint8_t usage:2;
+  uint8_t resv2:2;
+
+  uint16_t max_pktsz;
+  uint8_t interval;
+
+};
+
+#pragma pack()
+
+#define USB_CLASS_HUB          0x09
+struct host
+{
+  struct usb_host *dev;                /* host driver */
+  host_info info;              /* private to host */
+  struct bitmap *usb_dev_addrs;        /* addrs used on device */
+  struct list host_usb_devs;   /* usb devices on host */
+  struct list_elem peers;      /* other hosts on system */
+};
+
+struct class
+{
+  struct usb_class *dev;       /* class driver */
+  struct list usb_ifaces;      /* usb devices on class */
+  struct list_elem peers;
+};
+
+static struct list usb_dev_list;       /* list of all usb devices */
+static struct list class_list; /* list of all classes */
+static struct list host_list;  /* list of all hosts on system */
+static struct lock usb_sys_lock;       /* big usb lock */
+
+#define usb_lock()             lock_acquire(&usb_sys_lock)
+#define usb_unlock()           lock_release(&usb_sys_lock)
+
+static void usb_scan_devices (struct host *h);
+static struct usb_dev *usb_configure_default (struct host *h);
+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 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);
+
+extern void uhci_init (void);
+extern void ehci_init (void);
+
+void
+usb_init (void)
+{
+  list_init (&host_list);
+  list_init (&class_list);
+  list_init (&usb_dev_list);
+  lock_init (&usb_sys_lock);
+
+  usb_hub_init ();
+
+  /* add usb hosts */
+  printf ("Initializing EHCI\n");
+  ehci_init ();
+  printf ("Initializing UHCI\n");
+  uhci_init ();
+}
+
+/* add host controller device to usb layer */
+void
+usb_register_host (struct usb_host *uh, host_info info)
+{
+  struct host *h;
+
+  h = malloc (sizeof (struct host));
+  h->dev = uh;
+  h->info = info;
+  h->usb_dev_addrs = bitmap_create (ADDRS_PER_HOST);
+  list_init (&h->host_usb_devs);
+
+  usb_lock ();
+  list_push_back (&host_list, &h->peers);
+  usb_unlock ();
+
+  usb_scan_devices (h);
+}
+
+int
+usb_register_class (struct usb_class *uc)
+{
+  struct class *c;
+
+  usb_lock ();
+
+  /* check to make sure class is not in list */
+  if (usb_get_class_by_id (uc->class_id) != NULL)
+    {
+      usb_unlock ();
+      return -1;
+    }
+
+
+  /* add class to class list */
+  c = malloc (sizeof (struct class));
+  c->dev = uc;
+  list_init (&c->usb_ifaces);
+  list_push_back (&class_list, &c->peers);
+
+  usb_apply_class_to_interfaces (c);
+
+  usb_unlock ();
+
+  return 0;
+}
+
+static void
+usb_scan_devices (struct host *h)
+{
+  /* scan until there all devices using default pipe are configured */
+
+  printf ("USB: scanning devices...\n");
+  while (1)
+    {
+      struct usb_dev *dev;
+
+      dev = usb_configure_default (h);
+      if (dev == NULL)
+       break;
+
+      if (dev->ignore_device == false)
+       printf ("USB Device %d: %s (%s)\n",
+               dev->addr, dev->product, dev->manufacturer);
+
+      list_push_back (&h->host_usb_devs, &dev->host_peers);
+      list_push_back (&usb_dev_list, &dev->sys_peers);
+      usb_attach_interfaces (dev);
+    }
+}
+
+
+static struct usb_dev *
+usb_configure_default (struct host *h)
+{
+  struct usb_dev *dev;
+  struct usb_setup_pkt sp;
+  char data[256];
+  struct device_descriptor *dd;
+  host_dev_info hi;
+  host_eop_info cfg_eop;
+  bool ignore_device = false;
+  int err, sz, txed;
+  int config_val;
+
+  hi = h->dev->create_dev_channel (h->info, ADDR_DEFAULT, USB_VERSION_1_1);
+  cfg_eop = h->dev->create_eop (hi, 0, 64);
+
+  /* determine device descriptor */
+  sp.recipient = USB_SETUP_RECIP_DEV;
+  sp.type = USB_SETUP_TYPE_STD;
+  sp.direction = 1;
+  sp.request = REQ_STD_GET_DESC;
+  sp.value = SETUP_DESC_DEVICE << 8;
+  sp.index = 0;
+  sp.length = sizeof (struct device_descriptor);
+
+  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)
+    {
+      h->dev->remove_eop (cfg_eop);
+      h->dev->remove_dev_channel (hi);
+      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)
+    {
+      /* 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)
+    ignore_device = true;
+
+  h->dev->remove_eop (cfg_eop);
+  cfg_eop = h->dev->create_eop (hi, 0, dd->max_pktsz);
+
+  /* device exists - create device structure */
+  dev = malloc (sizeof (struct usb_dev));
+  memset (dev, 0, sizeof (struct usb_dev));
+  list_init (&dev->interfaces);
+
+  dev->ignore_device = ignore_device;
+  dev->h_dev = hi;
+  dev->h_cfg_eop = cfg_eop;
+
+  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->default_iface.iface_num = 0;
+  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.eop = 0;
+
+  dev->host = h;
+
+  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);
+    }
+
+  config_val = -123;
+  /* read in configuration data if there are configurations available */
+  /* (which there should be...) */
+  if (dd->num_configs > 0 && ignore_device == false)
+    {
+      config_val = usb_load_config (dev, 0, data, sizeof (data));
+      if (config_val < 0)
+       {
+         printf
+           ("USB: Invalid configuration value %d on '%s (%s)' (%x,%x,%x)\n",
+            config_val, dev->product, dev->manufacturer, dev->vendor_id,
+            dev->product_id, dev->device_id);
+       }
+    }
+
+  usb_setup_dev_addr (dev);
+
+  usb_config_dev (dev, (config_val < 0) ? 1 : config_val);
+
+  return dev;
+}
+
+/**
+ * Load in data for 'idx' configuration into device structure
+ * XXX support multiple configurations
+ */
+static int
+usb_load_config (struct usb_dev *dev, int idx, void *data, int dsz)
+{
+  struct usb_setup_pkt sp;
+  struct config_descriptor *cd;
+  struct host *h;
+  host_eop_info cfg;
+  void *ptr;
+  int config_val, err, sz;
+  int i;
+
+  h = dev->host;
+  cfg = dev->h_cfg_eop;
+
+  sp.recipient = USB_SETUP_RECIP_DEV;
+  sp.type = USB_SETUP_TYPE_STD;
+  sp.direction = 1;
+  sp.request = REQ_STD_GET_DESC;
+  sp.value = SETUP_DESC_CONFIG << 8 | idx;
+  sp.index = 0;
+  sp.length = dsz;
+  cd = data;
+
+  err = h->dev->tx_pkt (cfg, USB_TOKEN_SETUP, &sp, 0, sizeof (sp), &sz, true);
+  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))
+    {
+      printf ("USB: Did not rx GET descriptor (%d bytes, expected %d)\n", sz,
+             sizeof (struct config_descriptor));
+      return -err;
+    }
+
+  if (sz == 0 || cd->hdr.type != SETUP_DESC_CONFIG)
+    {
+      printf ("USB: Invalid descriptor\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 */
+  /* scan interfaces */
+  ptr = data + sizeof (struct config_descriptor);
+
+  for (i = 0; i < cd->num_ifaces; i++)
+    {
+      struct interface_descriptor *iface;
+      struct usb_iface *ui;
+      int j;
+
+      iface = ptr;
+      if (iface->hdr.type != SETUP_DESC_IFACE)
+       {
+         hex_dump (0, iface, 64, false);
+         PANIC ("Expected %d, found %d\n", SETUP_DESC_IFACE,
+                iface->hdr.type);
+       }
+
+      ui = malloc (sizeof (struct usb_iface));
+      ui->class_id = iface->iface_class;
+      ui->subclass_id = iface->iface_subclass;
+      ui->iface_num = iface->iface_num;
+      ui->proto = iface->iface_proto;
+      ui->class = NULL;
+      ui->c_info = NULL;
+      ui->dev = dev;
+      list_init (&ui->endpoints);
+
+      /* endpoint data comes after interfaces */
+      /* scan endpoints */
+      ptr += sizeof (struct interface_descriptor);
+      for (j = 0; j < iface->num_endpoints;
+          j++, ptr += sizeof (struct endpoint_descriptor))
+       {
+         struct usb_endpoint *ue;
+         struct endpoint_descriptor *ed;
+
+         ed = ptr;
+
+         ue = malloc (sizeof (struct usb_endpoint));
+         ue->eop = ed->endpoint_num;
+         ue->direction = ed->direction;
+         ue->attr = ed->transfer;
+         ue->max_pkt = ed->max_pktsz;
+         ue->interval = ed->interval;
+         ue->iface = ui;
+         ue->h_eop = h->dev->create_eop (dev->h_dev, ue->eop, ue->max_pkt);
+
+         list_push_back (&ui->endpoints, &ue->peers);
+       }
+
+      list_push_back (&dev->interfaces, &ui->peers);
+    }
+
+  config_val = cd->config_val;
+
+  return config_val;
+}
+
+/**
+ * Set USB device configuration to desired configuration value
+ */
+static void
+usb_config_dev (struct usb_dev *dev, int config_val)
+{
+  struct usb_setup_pkt sp;
+  struct host *h;
+  host_eop_info cfg;
+  int err;
+
+  h = dev->host;
+  cfg = dev->h_cfg_eop;
+
+  sp.recipient = USB_SETUP_RECIP_DEV;
+  sp.type = USB_SETUP_TYPE_STD;
+  sp.direction = 0;
+  sp.request = REQ_STD_SET_CONFIG;
+  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);
+  if (err != USB_HOST_ERR_NONE)
+    PANIC ("USB: Could not configure device!\n");
+}
+
+/**
+ * Set a device address to something other than the default pipe 
+ */
+static void
+usb_setup_dev_addr (struct usb_dev *dev)
+{
+  struct usb_setup_pkt sp;
+  struct host *h;
+  host_eop_info cfg;
+  int err;
+
+  ASSERT (dev->addr == 0);
+
+  h = dev->host;
+  cfg = dev->h_cfg_eop;
+
+  dev->addr = bitmap_scan_and_flip (h->usb_dev_addrs, 1, 1, false);
+
+  sp.recipient = USB_SETUP_RECIP_DEV;
+  sp.type = USB_SETUP_TYPE_STD;
+  sp.direction = 0;
+  sp.request = REQ_STD_SET_ADDRESS;
+  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);
+  if (err != USB_HOST_ERR_NONE)
+    {
+      PANIC ("USB: Error on setting device address (err = %d)\n", err);
+    }
+
+  h->dev->modify_dev_channel (dev->h_dev, dev->addr, dev->usb_version);
+}
+
+#define MAX_USB_STR    128
+/* read string by string descriptor index from usb device */
+static char *
+usb_get_string (struct usb_dev *udev, int ndx)
+{
+  struct usb_setup_pkt sp;
+  char str[MAX_USB_STR];
+  char *ret;
+  int sz;
+
+  sp.recipient = USB_SETUP_RECIP_DEV;
+  sp.type = USB_SETUP_TYPE_STD;
+  sp.direction = 1;
+  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)
+    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';
+
+  /* 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);
+  return ret;
+}
+
+static struct class *
+usb_get_class_by_id (int id)
+{
+  struct list_elem *li;
+
+  li = list_begin (&class_list);
+  while (li != list_end (&class_list))
+    {
+      struct class *c;
+      c = list_entry (li, struct class, peers);
+      if (c->dev->class_id == id)
+       return c;
+      li = list_next (li);
+    }
+
+  return NULL;
+}
+
+/**
+ * Asssociate interfaces on a device with their respective device classes
+ */
+static void
+usb_attach_interfaces (struct usb_dev *dev)
+{
+
+  struct list_elem *li;
+  li = list_begin (&dev->interfaces);
+  while (li != list_end (&dev->interfaces))
+    {
+      struct class *cl;
+      struct usb_iface *ui;
+
+      ui = list_entry (li, struct usb_iface, peers);
+      li = list_next (li);
+      cl = usb_get_class_by_id (ui->class_id);
+
+      /* no matching class? try next interface */
+      if (cl == NULL)
+       continue;
+
+      ui->c_info = cl->dev->attached (ui);
+      /* did class driver initialize it OK? */
+      if (ui->c_info != NULL)
+       {
+         /* yes, add to list of usable interfaces */
+         list_push_back (&cl->usb_ifaces, &ui->class_peers);
+         ui->class = cl;
+       }
+    }
+}
+
+/**
+ * Scan all interfaces for interfaces that match class's class id
+ * Attach interfaces that match
+ */
+static void
+usb_apply_class_to_interfaces (struct class *c)
+{
+  struct list_elem *li;
+
+  li = list_begin (&usb_dev_list);
+  while (li != list_end (&usb_dev_list))
+    {
+      struct usb_dev *ud;
+      struct list_elem *lii;
+
+      ud = list_entry (li, struct usb_dev, sys_peers);
+
+      lii = list_begin (&ud->interfaces);
+      while (lii != list_end (&ud->interfaces))
+       {
+         struct usb_iface *ui;
+
+         ui = list_entry (lii, struct usb_iface, peers);
+
+         lii = list_next (lii);
+         if (ui->class_id != c->dev->class_id)
+           continue;
+
+         ui->c_info = c->dev->attached (ui);
+         if (ui->c_info == NULL)
+           continue;
+
+         list_push_back (&c->usb_ifaces, &ui->class_peers);
+         ui->class = c;
+       }
+
+      li = list_next (li);
+    }
+}
+
+int
+usb_dev_bulk (struct usb_endpoint *eop, void *buf, int sz, int *tx)
+{
+  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);
+
+  return err;
+}
+
+int
+usb_dev_setup (struct usb_endpoint *eop, bool in,
+              struct usb_setup_pkt *s, void *buf, int sz)
+{
+  struct host *h;
+  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;
+}
+
+/** convert a wchar string to ascii in place */
+static size_t
+wchar_to_ascii (char *dst, const char *src)
+{
+  size_t sz = 0;
+  for (sz = 0; src[sz * 2] != '\0'; sz++)
+    {
+      dst[sz] = src[sz * 2];
+    }
+  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;
+}
diff --git a/src/devices/usb.h b/src/devices/usb.h
new file mode 100644 (file)
index 0000000..b15978e
--- /dev/null
@@ -0,0 +1,197 @@
+#ifndef USB_H
+#define USB_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <kernel/list.h>
+
+#define USB_HOST_ERR_NONE      0
+#define USB_HOST_ERR_BITSTUFF  1
+#define USB_HOST_ERR_TIMEOUT   2
+#define USB_HOST_ERR_NAK       3
+#define USB_HOST_ERR_BABBLE    4
+#define USB_HOST_ERR_BUFFER    5
+#define USB_HOST_ERR_STALL     6
+#define USB_HOST_ERR_NODEV     7
+
+#define make_usb_pid(x)         ((x) | ((~(x)) << 4))
+
+/* token packets... */
+#define USB_PID_OUT     make_usb_pid(1)
+#define USB_PID_IN      make_usb_pid(9)
+#define USB_PID_SOF     make_usb_pid(5)
+#define USB_PID_SETUP   make_usb_pid(13)
+/* data packets... */
+#define USB_PID_DATA0   make_usb_pid(3)
+#define USB_PID_DATA1   make_usb_pid(11)
+#define USB_PID_DATA2   make_usb_pid(7)
+#define USB_PID_MDATA   make_usb_pid(15)
+/* handshake packets.. */
+#define USB_PID_ACK     make_usb_pid(2)
+#define USB_PID_NAK     make_usb_pid(10)
+#define USB_PID_STALL   make_usb_pid(14)
+#define USB_PID_NYET    make_usb_pid(6)
+/* special */
+#define USB_PID_PRE     make_usb_pid(12)
+#define USB_PID_ERR     make_usb_pid(12)
+#define USB_PID_SPLIT   make_usb_pid(8)
+#define USB_PID_PING    make_usb_pid(4)
+
+/* the standard setup requests */
+#define REQ_STD_GET_STATUS      0
+#define REQ_STD_CLR_FEAT        1
+#define REQ_STD_SET_FEAT        3
+#define REQ_STD_SET_ADDRESS     5
+#define REQ_STD_GET_DESC        6
+#define REQ_STD_SET_DESC        7
+#define REQ_STD_GET_CONFIG      8
+#define REQ_STD_SET_CONFIG      9
+#define REQ_STD_GET_IFACE       10
+#define REQ_STD_SET_IFACE       11
+#define REQ_STD_SYNCH_FRAME     12
+
+
+#define USB_TOKEN_SETUP                0x00
+#define USB_TOKEN_IN           0x80
+#define USB_TOKEN_OUT          0x90
+
+struct class;
+struct host;
+typedef void *host_info;
+typedef void *host_eop_info;
+typedef void *host_dev_info;
+typedef void *class_info;
+
+
+struct usb_iface
+{
+  int iface_num;
+  int class_id, subclass_id;
+  int proto;
+
+  struct usb_dev *dev;
+
+  struct class *class;
+  class_info c_info;
+
+  struct list_elem class_peers;        /* peers on class */
+  struct list endpoints;
+
+  struct list_elem peers;      /* peers on device */
+};
+
+#define USB_SPEED_1            0
+#define USB_SPEED_1_1          1
+#define USB_SPEED_2            2
+
+struct usb_host
+{
+  const char *name;
+  int (*detect_change) (host_info);
+  int (*tx_pkt) (host_eop_info, int pid, void *pkt, 
+                int min_sz, int max_sz, int *in_sz, bool wait);
+
+  host_eop_info (*create_eop)(host_dev_info, int eop, int maxpkt);
+  void (*remove_eop)(host_eop_info);
+
+  host_dev_info (*create_dev_channel) (host_info, int dev_addr, int ver);
+  void (*modify_dev_channel) (host_dev_info, int dev_addr, int ver);
+  void (*remove_dev_channel) (host_dev_info);
+};
+
+struct usb_dev;
+struct usb_class
+{
+  const int class_id;
+  const char *name;
+
+  /* when a device of this class is attached, the device is passed in */
+  /* returns private info on device */
+  void *(*attached) (struct usb_iface *);
+
+  /* device is detached -> detached(dev_info) */
+  void (*detached) (class_info info);
+};
+
+#define USB_VERSION_1_0        0x100
+#define USB_VERSION_1_1        0x110
+#define USB_VERSION_2  0x200
+
+#define USB_EOP_ATTR_CTL       0       /* control */
+#define USB_EOP_ATTR_ISO       1       /* isochronous */
+#define USB_EOP_ATTR_BULK      2       /* bulk */
+#define USB_EOP_ATTR_INT       3       /* interrupt */
+struct usb_endpoint
+{
+  int eop;                     /* end point address */
+  int attr;
+  int direction;               /* 0 = host->dev, 1=dev->host */
+  int max_pkt;
+  int interval;
+  struct usb_iface *iface;
+  host_eop_info h_eop;
+  struct list_elem peers;
+};
+
+struct usb_dev
+{
+  uint8_t addr;
+  int usb_version;
+  int class_id, subclass_id;
+  uint16_t product_id, vendor_id, device_id;
+  int interface;
+  int max_pkt_len;
+  int int_period;
+  char *manufacturer;
+  char *product;
+  int pwr;                     /* power draw for this config */
+  bool ignore_device;
+  struct list interfaces;
+
+  struct list_elem host_peers; /* peers on host */
+  struct list_elem sys_peers;  /* list for all devices */
+
+  struct usb_iface default_iface;
+  struct usb_endpoint cfg_eop;
+
+  host_dev_info h_dev;
+  host_eop_info h_cfg_eop;     /* configuration EOP */
+  struct host *host;
+};
+
+/* pg276 usb_20.pdf */
+#define USB_SETUP_TYPE_STD     0
+#define USB_SETUP_TYPE_CLASS   1
+#define USB_SETUP_TYPE_VENDOR  2
+
+#define USB_SETUP_RECIP_DEV    0
+#define USB_SETUP_RECIP_IFACE  1
+#define USB_SETUP_RECIP_ENDPT  2
+#define USB_SETUP_RECIP_OTHER  3
+
+#pragma pack(1)
+struct usb_setup_pkt
+{
+  uint8_t recipient:5;
+  uint8_t type:2;
+  uint8_t direction:1;         /* 0 = host->dev, 1 = dev->host */
+  uint8_t request;
+  uint16_t value;
+  uint16_t index;
+  uint16_t length;
+};
+#pragma pack()
+
+void usb_init (void);
+
+void usb_register_host (struct usb_host *, host_info info);
+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_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 *);
+
+#endif
diff --git a/src/devices/usb_ehci.c b/src/devices/usb_ehci.c
new file mode 100644 (file)
index 0000000..34f328b
--- /dev/null
@@ -0,0 +1,52 @@
+/**
+ *
+ * Actual EHCI will be implemented later
+ *
+ * For now, we just deactivate the EHCI controller and routing circuitry
+ * so that any USB2.0 devices activated by the BIOS will show up on the 
+ * USB1.1 controllers instead of being routed to EHCI and therefore "invisible"
+ * to the system.
+ *
+ */
+
+#include "devices/pci.h"
+#include "devices/usb.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+/* capability registers */
+#define EHCI_REG_CAPLENGTH     0x00
+
+/* operational regisers - must be offset by op_base */
+#define EHCI_REG_CONFIGFLAG    0x40
+
+void ehci_init (void);
+
+
+void
+ehci_init (void)
+{
+  struct pci_dev *pd;
+  int dev_num;
+
+  dev_num = 0;
+  while ((pd = pci_get_dev_by_class (PCI_MAJOR_SERIALBUS, PCI_MINOR_USB,
+                                    PCI_USB_IFACE_EHCI, dev_num)) != NULL)
+    {
+      struct pci_io *io;
+      uint8_t op_base;
+
+      dev_num++;
+      io = pci_io_enum (pd, NULL);
+      if (io == NULL)
+       {
+         printf ("IO not found on EHCI device?\n");
+         continue;
+       }
+      printf ("Disabling the EHCI controller #%d\n", dev_num - 1);
+      op_base = pci_reg_read8 (io, EHCI_REG_CAPLENGTH);
+
+      /* turn off EHCI routing */
+      pci_reg_write32 (io, EHCI_REG_CONFIGFLAG + op_base, 0);
+    }
+}
diff --git a/src/devices/usb_hub.c b/src/devices/usb_hub.c
new file mode 100644 (file)
index 0000000..01bbfb9
--- /dev/null
@@ -0,0 +1,54 @@
+#include "devices/usb.h"
+#include "devices/usb_hub.h"
+#include <lib/debug.h>
+
+#define USB_CLASS_HUB  0x09
+
+#define REQ_HUB_GET_STATUS     0
+#define REQ_HUB_CLEAR_FEATURE  1
+#define REQ_HUB_SET_FEATURE    2
+#define REQ_HUB_GET_DESC       6
+#define REQ_HUB_SET_DESC       7
+#define REQ_HUB_CLEAR_TT_BUF   8
+#define REQ_HUB_RESET_TT       9
+#define REQ_HUB_GET_TT_STATE   10
+#define REQ_HUB_STOP_TT                11
+
+#define HUB_SEL_HUB_POWER              0
+#define HUB_SEL_OVER_CURRENT           1
+#define HUB_SEL_PORT_CONN              0
+#define HUB_SEL_PORT_ENABLE            1
+#define HUB_SEL_PORT_SUSPEND           2
+#define HUB_SEL_PORT_OVER_CURRENT      3
+#define HUB_SEL_PORT_RESET             4
+#define HUB_SEL_PORT_POWER             8
+#define HUB_SEL_PORT_LOW_SPEED         9
+
+#define SETUP_DESC_HUB                 0x29
+
+static void* hub_attached(struct usb_iface*);
+static void hub_detached(class_info);
+
+static struct usb_class hub_class = {
+               .attached = hub_attached,
+               .detached = hub_detached,
+               .class_id = USB_CLASS_HUB,
+               .name = "hub"
+               };
+
+
+void usb_hub_init(void)
+{
+       usb_register_class(&hub_class);
+}
+
+
+static void* hub_attached(struct usb_iface* ui UNUSED)
+{
+       return NULL;
+}
+
+static void hub_detached(class_info info UNUSED)
+{
+}
+
diff --git a/src/devices/usb_hub.h b/src/devices/usb_hub.h
new file mode 100644 (file)
index 0000000..d4399d7
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef HUB_H
+#define HUB_H
+
+void usb_hub_init(void);
+
+
+#endif
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);
+}
diff --git a/src/devices/usb_storage.h b/src/devices/usb_storage.h
new file mode 100644 (file)
index 0000000..ed0a493
--- /dev/null
@@ -0,0 +1 @@
+void usb_storage_init (void);
diff --git a/src/devices/usb_uhci.c b/src/devices/usb_uhci.c
new file mode 100644 (file)
index 0000000..1e5a64a
--- /dev/null
@@ -0,0 +1,1263 @@
+/**
+ * Universal Host Controller Interface driver
+ * TODO:
+ *     Stall timeouts
+ *     Better (any) root hub handling
+ */
+
+#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"
+#include "threads/synch.h"
+#include "threads/interrupt.h"
+#include "threads/thread.h"
+#include "devices/pci.h"
+#include "devices/usb.h"
+#include "devices/timer.h"
+
+#define UHCI_MAX_PORTS         8
+
+#define FRAME_LIST_ENTRIES     1024
+#define TD_ENTRIES             (4096/32)       /* number of entries allocated */
+#define QH_ENTRIES             (4096/16)
+
+/* uhci pci registers */
+#define UHCI_REG_USBCMD                0x00    /* Command */
+#define UHCI_REG_USBSTS                0x02    /* Status */
+#define UHCI_REG_USBINTR       0x04    /* interrupt enable */
+#define UHCI_REG_FRNUM         0x06    /* frame number */
+#define UHCI_REG_FLBASEADD     0x08    /* frame list base address */
+#define UHCI_REG_SOFMOD                0x0c    /* start of frame modify */
+#define UHCI_REG_PORTSC1       0x10    /* port 1 status/control */
+#define UHCI_REG_PORTSC2       0x12    /* port 2 status/control */
+#define UHCI_REGSZ             0x20    /* register iospace size */
+
+/* in PCI config space for some reason */
+#define UHCI_REG_LEGSUP                0xC0
+
+
+/* command register */
+#define USB_CMD_MAX_PACKET     (1 << 7)
+#define USB_CMD_CONFIGURE      (1 << 6)
+#define USB_CMD_FGR            (1 << 4)        /* force global resume */
+#define USB_CMD_EGSM           (1 << 3)        /* global suspend mode */
+#define USB_CMD_GRESET         (1 << 2)        /* global reset */
+#define USB_CMD_HCRESET                (1 << 1)        /* host controller reset */
+#define USB_CMD_RS             (1 << 0)        /* run/stop */
+
+/* status register */
+#define USB_STATUS_HALTED      (1 << 5)
+#define USB_STATUS_PROCESSERR  (1 << 4)
+#define USB_STATUS_HOSTERR     (1 << 3)
+#define USB_STATUS_RESUME      (1 << 2)
+#define USB_STATUS_INTERR      (1 << 1)
+#define USB_STATUS_USBINT      (1 << 0)
+
+/* interrupt enable register */
+#define USB_INTR_SHORT         (1 << 3)        /* enable short packets */
+#define USB_INTR_IOC           (1 << 2)        /* interrupt on complete */
+#define USB_INTR_RESUME                (1 << 1)        /* resume interrupt enable */
+#define USB_INTR_TIMEOUT       (1 << 0)        /* timeout int enable */
+
+/* port control register */
+#define USB_PORT_SUSPEND       (1 << 12)
+#define USB_PORT_RESET         (1 << 9)
+#define USB_PORT_LOWSPEED      (1 << 8)
+#define USB_PORT_RESUMED       (1 << 6)        /* resume detected */
+#define USB_PORT_CHANGE                (1 << 3)        /* enable change */
+#define USB_PORT_ENABLE                (1 << 2)        /* enable the port */
+#define USB_PORT_CONNECTCHG    (1 << 1)        /* connect status changed */
+#define USB_PORT_CONNECTSTATUS (1 << 0)        /* device is connected */
+
+#define ptr_to_flp(x)  (((uintptr_t)x) >> 4)
+#define flp_to_ptr(x)  (uintptr_t)(((uintptr_t)x) << 4)
+
+/* frame structures */
+#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 */
+  uint32_t dev_addr:7;         /* device address */
+  uint32_t end_point:4;
+  uint32_t data_toggle:1;
+  uint32_t resv:1;
+  uint32_t maxlen:11;          /* maximum pkt length */
+};
+
+struct td_control
+{
+  uint32_t actual_len:11;
+  uint32_t resv1:5;
+
+  /* status information */
+  uint32_t resv2:1;
+  uint32_t bitstuff:1;
+  uint32_t timeout:1;
+  uint32_t nak:1;
+  uint32_t babble:1;
+  uint32_t buffer_error:1;
+  uint32_t stalled:1;
+  uint32_t active:1;
+
+  /* config data */
+  uint32_t ioc:1;              /* issue int on complete */
+  uint32_t ios:1;              /* isochronous select */
+  uint32_t ls:1;               /* low speed device */
+  uint32_t error_limit:2;      /* errors before interrupt */
+  uint32_t spd:1;              /* short packet detect */
+  uint32_t resv3:2;
+};
+
+#define TD_FL_ASYNC    1
+#define TD_FL_USED     0x80000000
+
+struct tx_descriptor
+{
+  struct frame_list_ptr flp;
+  struct td_control control;
+  struct td_token token;
+  uint32_t buf_ptr;
+
+
+  struct frame_list_ptr *head; /* for fast removal */
+  uint32_t flags;
+};
+
+struct queue_head
+{
+  struct frame_list_ptr qhlp;  /* queue head link pointer */
+  struct frame_list_ptr qelp;  /* queue elem link pointer */
+};
+#pragma pack()
+
+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;
+
+  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
+{
+  struct tx_descriptor *td;
+  struct uhci_dev_info *ud;
+  struct semaphore sem;
+  struct list_elem peers;
+};
+
+struct uhci_dev_info
+{
+  struct uhci_info *ui;                /* owner */
+  bool low_speed;              /* whether device is low speed */
+  int dev_addr;                        /* device address */
+  int errors;                  /* aggregate errors */
+  struct list_elem peers;      /* next dev on host */
+  struct lock lock;
+  struct queue_head *qh;
+};
+
+struct uhci_eop_info
+{
+  struct uhci_dev_info *ud;
+  int eop;
+  int maxpkt;                  /* max packet size */
+  int toggle;                  /* data toggle bit for bulk transfers */
+};
+
+#define uhci_lock(x)   lock_acquire(&(x)->lock)
+#define uhci_unlock(x) lock_release(&(x)->lock)
+#define dev_lock(x)    lock_acquire(&(x)->lock)
+#define dev_unlock(x)  lock_release(&(x)->lock)
+
+
+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_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 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 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);
+static host_eop_info uhci_create_eop (host_dev_info hd, int eop, int maxpkt);
+static void uhci_remove_eop (host_eop_info hei);
+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);
+
+
+static struct usb_host uhci_host = {
+  .name = "UHCI",
+  .tx_pkt = uhci_tx_pkt,
+  .detect_change = uhci_detect_change,
+  .create_dev_channel = uhci_create_chan,
+  .remove_dev_channel = uhci_destroy_chan,
+  .modify_dev_channel = uhci_modify_chan,
+  .create_eop = uhci_create_eop,
+  .remove_eop = uhci_remove_eop
+};
+
+void
+uhci_init (void)
+{
+  struct pci_dev *pd;
+  int dev_num;
+
+  dev_num = 0;
+  while ((pd = pci_get_dev_by_class (PCI_MAJOR_SERIALBUS, PCI_MINOR_USB,
+                                    PCI_USB_IFACE_UHCI, dev_num)) != NULL)
+    {
+      struct pci_io *io;
+      struct uhci_info *ui;
+      uint8_t sof;
+      int i;
+
+      dev_num++;
+
+      /* find IO space */
+      io = NULL;
+      while ((io = pci_io_enum (pd, io)) != NULL)
+       {
+         if (pci_io_size (io) == UHCI_REGSZ)
+           break;
+       }
+
+      /* not found, next PCI */
+      if (io == NULL)
+       continue;
+
+      ui = uhci_create_info (io);
+      ui->dev = pd;
+
+      sof = pci_reg_read8 (ui->io, UHCI_REG_SOFMOD);
+      uhci_detect_ports (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);
+
+      /* reset controller */
+      pci_reg_write16 (ui->io, UHCI_REG_USBCMD, USB_CMD_HCRESET);
+      timer_msleep (1);
+      if (pci_reg_read16 (ui->io, UHCI_REG_USBCMD) & USB_CMD_HCRESET)
+       {
+         printf ("UHCI: Reset timed out\n");
+         uhci_destroy_info (ui);
+         continue;
+       }
+      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_write8 (ui->io, UHCI_REG_SOFMOD, sof);
+      pci_reg_write16 (ui->io, UHCI_REG_FRNUM, 0);
+      pci_reg_write32 (ui->io, UHCI_REG_FLBASEADD, vtop (ui->frame_list));
+      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);
+
+      uhci_lock (ui);
+      uhci_run (ui);
+      uhci_unlock (ui);
+
+      pci_register_irq (pd, uhci_irq, ui);
+
+      usb_register_host (&uhci_host, ui);
+    }
+}
+
+#define UHCI_PORT_TIMEOUT      1000
+static int
+uhci_enable_port (struct uhci_info *ui, int port)
+{
+  uint16_t status;
+  int count;
+
+  /* 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);
+
+  /* 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))
+    {
+      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++)
+    {
+      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;
+       }
+    }
+
+  printf ("UHCI: Port %d enable timed out\n", port);
+  dump_regs (ui);
+
+  return 0;
+}
+
+
+static void
+dump_regs (struct uhci_info *ui)
+{
+  int regs[] = { 0, 2, 4, 6, 8, 0xc, 0x10, 0x12 };
+  int sz[] = { 2, 2, 2, 2, 4, 2, 2, 2 };
+  char *name[] =
+    { "cmd", "sts", "intr", "frnum", "base", "sofmod", "portsc1", "portsc2" };
+  int i;
+  printf ("UHCI registers:\n");
+  for (i = 0; i < 8; i++)
+    {
+      printf ("%s: %x\n", name[i], (sz[i] == 2) ?
+             pci_reg_read16 (ui->io, regs[i]) :
+             pci_reg_read32 (ui->io, regs[i]));
+
+    }
+}
+
+
+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);
+}
+
+static struct uhci_info *
+uhci_create_info (struct pci_io *io)
+{
+  struct uhci_info *ui;
+  int i;
+
+  ui = malloc (sizeof (struct uhci_info));
+
+  ui->io = io;
+  lock_init (&ui->lock);
+
+  /* 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;
+
+  /* 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 int
+uhci_tx_pkt (host_eop_info hei, int token, void *pkt, int min_sz,
+            int max_sz, int *in_sz, bool wait)
+{
+  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));
+    }
+
+  ue = hei;
+  ud = ue->ud;
+
+  /* don't bother if ports are down */
+  if (ud->ui->attached_ports == 0)
+    {
+      return USB_HOST_ERR_NODEV;
+    }
+
+  /* setup token acts to synchronize data toggle */
+  if (token == USB_TOKEN_SETUP)
+    ue->toggle = 0;
+
+  if (min_sz != 0)
+    {
+      return uhci_tx_pkt_bulk (ue, token, pkt, max_sz, in_sz);
+    }
+  else
+    {
+      if (wait == false)
+       {
+         if (in_sz != NULL)
+           *in_sz = max_sz;
+         return uhci_tx_pkt_now (ue, token, pkt, max_sz);
+       }
+      else
+       {
+         return uhci_tx_pkt_wait (ue, token, pkt, max_sz, in_sz);
+       }
+    }
+
+  return 0;
+}
+
+static int
+uhci_tx_pkt_bulk (struct uhci_eop_info *ue, int token, void *buf,
+                 int sz, int *tx)
+{
+  /* XXX this can be made to use async packets */
+  int bytes = 0;
+  int txed = 0;
+
+  /* send data in max_pkt sized chunks */
+  while (bytes < sz)
+    {
+      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;
+    }
+
+  if (tx != NULL)
+    *tx = txed;
+
+  return USB_HOST_ERR_NONE;
+}
+
+static int
+token_to_pid (int token)
+{
+  switch (token)
+    {
+    case USB_TOKEN_SETUP:
+      return USB_PID_SETUP;
+    case USB_TOKEN_IN:
+      return USB_PID_IN;
+    case USB_TOKEN_OUT:
+      return USB_PID_OUT;
+    default:
+      PANIC ("Unknown USB token\n");
+    }
+}
+
+static void
+uhci_setup_td (struct tx_descriptor *td, int dev_addr, int token,
+              int eop, void *pkt, int sz, int toggle, bool ls)
+{
+  td->buf_ptr = (sz == 0) ? 0 : vtop (pkt);
+
+  td->token.pid = token_to_pid (token);
+  td->token.dev_addr = dev_addr;
+  td->token.end_point = eop;
+  td->token.data_toggle = toggle;
+  td->token.maxlen = sz - 1;
+//  td->control.ls = ls;
+
+  td->control.actual_len = 0;
+  td->control.active = 1;
+  td->flp.qh_select = 0;
+  td->flp.depth_select = 0;
+
+  /* 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;
+
+  ui = uhci_data;
+  status = pci_reg_read16 (ui->io, UHCI_REG_USBSTS);
+  if (status & USB_STATUS_PROCESSERR)
+    {
+      dump_all_qh (ui);
+      dump_regs (ui);
+      PANIC ("UHCI: Malformed schedule");
+    }
+  else if (status & USB_STATUS_HOSTERR)
+    {
+      dump_all_qh (ui);
+      dump_regs (ui);
+      PANIC ("UHCI: Host system error");
+    }
+  else if (status & USB_STATUS_INTERR)
+    {
+      /* 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);
+      uhci_process_completed (ui);
+      uhci_run_unlocked (ui);
+    }
+}
+
+static int
+uhci_process_completed (struct uhci_info *ui)
+{
+  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;
+
+      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;
+}
+
+static void
+uhci_remove_error_td (struct tx_descriptor *td)
+{
+  struct frame_list_ptr *fp;
+  uint32_t td_flp;
+
+  ASSERT (td->head != NULL);
+
+  fp = td->head;
+  td_flp = ptr_to_flp (vtop (td));
+  while (fp->flp != td_flp)
+    {
+      ASSERT (fp->terminate == 0);
+      fp = ptov (flp_to_ptr (fp->flp));
+    }
+  *fp = td->flp;
+}
+
+static int
+uhci_detect_change (host_info hi)
+{
+  struct uhci_info *ui;
+  int change;
+  int i;
+
+  ui = hi;
+  change = 0;
+  uhci_lock (ui);
+  for (i = 0; i < ui->num_ports; i++)
+    {
+      change = check_and_flip_change (ui, i);
+      if (change != 0)
+       break;
+    }
+  uhci_unlock (ui);
+
+  return change;
+}
+
+static int
+check_and_flip_change (struct uhci_info *ui, int port)
+{
+  int val;
+  int reg;
+
+  reg = UHCI_REG_PORTSC1 + port * 2;
+  val = pci_reg_read16 (ui->io, reg);
+  if (val & USB_PORT_CHANGE)
+    {
+      pci_reg_write16 (ui->io, reg, val & ~(USB_PORT_CHANGE));
+      return 1;
+    }
+
+  return 0;
+}
+
+static host_dev_info
+uhci_create_chan (host_info hi, int dev_addr, int ver)
+{
+  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++)
+    {
+      ui->frame_list[i].flp = ptr_to_flp (vtop (ud->qh));
+      ui->frame_list[i].qh_select = 1;
+      ui->frame_list[i].terminate = 0;
+    }
+
+  /* add to device list */
+  list_push_back (&ui->devices, &ud->peers);
+
+  uhci_run (ui);
+  uhci_unlock (ui);
+
+  return ud;
+}
+
+static void
+uhci_destroy_chan (host_dev_info hd)
+{
+  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 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);
+    }
+
+  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;
+       }
+    }
+  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;
+    }
+}
+
+/**
+ * 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)
+{
+  uint16_t cmd;
+  int i;
+
+  cmd = pci_reg_read16 (ui->io, UHCI_REG_USBCMD);
+  cmd = cmd & ~USB_CMD_RS;
+
+  pci_reg_write16 (ui->io, UHCI_REG_USBCMD, cmd);
+
+  /* wait for execution schedule to finish up */
+  for (i = 0; i < 1000; i++)
+    {
+      if (uhci_is_stopped (ui))
+       return;
+    }
+
+  PANIC ("UHCI: Controller did not halt\n");
+
+}
+
+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);
+}
+
+/**
+ * Put UHCI into 'Run' State 
+ */
+static void
+uhci_run (struct uhci_info *ui)
+{
+  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;
+}
+
+static void
+uhci_modify_chan (host_dev_info hd, int dev_addr, int ver)
+{
+  struct uhci_dev_info *ud;
+
+  ud = hd;
+  ud->dev_addr = dev_addr;
+  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)
+{
+  int i;
+  ui->num_ports = 0;
+  for (i = 0; i < UHCI_MAX_PORTS; i++)
+    {
+      uint16_t status;
+      status = pci_reg_read16 (ui->io, UHCI_REG_PORTSC1 + i * 2);
+      if (!(status & 0x0080) || status == 0xffff)
+       return;
+      ui->num_ports++;
+    }
+}
+
+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)
+{
+  struct uhci_dev_info *ud;
+  struct uhci_eop_info *e;
+
+  ud = hd;
+
+  e = malloc (sizeof (struct uhci_eop_info));
+  e->eop = eop;
+  e->ud = ud;
+  e->maxpkt = maxpkt;
+  e->toggle = 0;
+
+  return e;
+}
+
+static void
+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);
+}
diff --git a/src/lib/endian.h b/src/lib/endian.h
new file mode 100644 (file)
index 0000000..7e5fc32
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef __LIB_ENDIAN_H
+#define __LIB_ENDIAN_H
+
+
+#define endian_swap32(x)       ((((x) & 0xff000000) >> 24) | \
+                                  (((x) & 0x00ff0000) >> 8)  | \
+                                  (((x) & 0x0000ff00) << 8)  | \
+                                  (((x) & 0x000000ff) << 24))
+#define endian_swap16(x)        ((((x) & 0xff00)>> 8) | (((x) & 0x00ff) << 8))
+#define endian_swap24(x)       (((x) >> 16) & 0xff) | (((x) & 0xff) << 16) | ((x) & 0xff00)
+
+#define be32_to_machine(x)     endian_swap32(x)
+#define be16_to_machine(x)     endian_swap16(x)
+#define be24_to_machine(x)     endian_swap24(x)
+#define machine_to_be32(x)     endian_swap32(x)
+#define machine_to_be16(x)     endian_swap16(x)
+#define machine_to_be24(x)     endian_swap24(x)
+
+#endif
index cebec2c817f5954b9accf9558dbd36822c1fb8f4..dbb41da71c93e9dfb22623b8e569ada51d7ce28a 100644 (file)
 #include "devices/input.h"
 #include "devices/serial.h"
 #include "devices/shutdown.h"
+#include "devices/pci.h"
 #include "devices/timer.h"
 #include "devices/vga.h"
 #include "devices/rtc.h"
+#include "devices/usb.h"
 #include "threads/interrupt.h"
 #include "threads/io.h"
 #include "threads/loader.h"
@@ -40,6 +42,7 @@
 
 /* Page directory with kernel mappings only. */
 uint32_t *init_page_dir;
+bool init_page_dir_initialized;
 
 #ifdef FILESYS
 /* -f: Format the file system? */
@@ -59,6 +62,7 @@ static size_t user_page_limit = SIZE_MAX;
 
 static void bss_init (void);
 static void paging_init (void);
+static void pci_zone_init (void);
 
 static char **read_command_line (void);
 static char **parse_options (char **argv);
@@ -109,6 +113,7 @@ main (void)
   intr_init ();
   timer_init ();
   kbd_init ();
+  pci_init ();
   input_init ();
 #ifdef USERPROG
   exception_init ();
@@ -119,9 +124,11 @@ main (void)
   thread_start ();
   serial_init_queue ();
   timer_calibrate ();
+  usb_init ();
 
 #ifdef FILESYS
   /* Initialize file system. */
+  usb_storage_init ();
   ide_init ();
   locate_block_devices ();
   filesys_init (format_filesys);
@@ -174,18 +181,39 @@ paging_init (void)
       if (pd[pde_idx] == 0)
         {
           pt = palloc_get_page (PAL_ASSERT | PAL_ZERO);
-          pd[pde_idx] = pde_create (pt);
+          pd[pde_idx] = pde_create_kernel (pt);
         }
 
       pt[pte_idx] = pte_create_kernel (vaddr, !in_kernel_text);
     }
 
+  pci_zone_init ();
+
   /* Store the physical address of the page directory into CR3
      aka PDBR (page directory base register).  This activates our
      new page tables immediately.  See [IA32-v2a] "MOV--Move
      to/from Control Registers" and [IA32-v3a] 3.7.5 "Base Address
      of the Page Directory". */
   asm volatile ("movl %0, %%cr3" : : "r" (vtop (init_page_dir)));
+
+  init_page_dir_initialized = true;
+}
+
+/* initialize PCI zone at PCI_ADDR_ZONE_BEGIN - PCI_ADDR_ZONE_END*/
+static void
+pci_zone_init (void)
+{
+  int i;
+  for (i = 0; i < PCI_ADDR_ZONE_PDES; i++)
+    {
+      size_t pde_idx = pd_no ((void *) PCI_ADDR_ZONE_BEGIN) + i;
+      uint32_t pde;
+      void *pt;
+
+      pt = palloc_get_page (PAL_ASSERT | PAL_ZERO);
+      pde = pde_create_kernel (pt);
+      init_page_dir[pde_idx] = pde;
+    }
 }
 
 /* Breaks the kernel command line into words and returns them as
index 9ffd873540b803ca3414b83f10c6da4e8c4b2f54..27f470861a22de11b725475b731d2a11afdfafcc 100644 (file)
@@ -17,6 +17,8 @@
 #define PIC0_DATA      0x21    /* Master PIC data register address. */
 #define PIC1_CTRL      0xa0    /* Slave PIC control register address. */
 #define PIC1_DATA      0xa1    /* Slave PIC data register address. */
+#define IRQ_CASCADE0   2
+#define IRQ_CASCADE1   9
 
 /* Number of x86 interrupts. */
 #define INTR_CNT 256
@@ -33,6 +35,9 @@ static intr_handler_func *intr_handlers[INTR_CNT];
 /* Names for each interrupt, for debugging purposes. */
 static const char *intr_names[INTR_CNT];
 
+/* cached values for PIC */
+static uint8_t pic_mask[2];
+
 /* External interrupts are those generated by devices outside the
    CPU, such as the timer.  External interrupts run with
    interrupts turned off, so they never nest, nor are they ever
@@ -251,6 +256,8 @@ pic_init (void)
   /* Unmask all interrupts. */
   outb (PIC0_DATA, 0x00);
   outb (PIC1_DATA, 0x00);
+  pic_mask[0] = 0;
+  pic_mask[1] = 0;
 }
 
 /* Sends an end-of-interrupt signal to the PIC for the given IRQ.
@@ -418,3 +425,44 @@ intr_name (uint8_t vec)
 {
   return intr_names[vec];
 }
+
+/** masks a given IRQ */
+void intr_irq_mask(int irq)
+{
+       if(irq < 8){
+               pic_mask[0] |= 1 << irq;
+               outb (PIC0_DATA, pic_mask[0]);
+       }else{  
+               pic_mask[1] |= 1 << (irq - 8);
+               outb (PIC1_DATA, pic_mask[1]);
+       }
+}
+
+/** unmasks a given IRQ */
+void intr_irq_unmask(int irq)
+{
+       if(irq >= 8){
+               /* enable cascade if not enabled for pic2 */
+               if(pic_mask[1] & (1 << (IRQ_CASCADE1 - 8)))
+                       pic_mask[1] &= ~(1 << (IRQ_CASCADE1 - 8));
+
+               pic_mask[1] &= ~(1 << (irq - 8));
+               outb(PIC1_DATA, pic_mask[1]);
+
+               /* enable cascade if not enabled for pic1 */
+               if(pic_mask[0] & (1 << IRQ_CASCADE0))
+                       irq = IRQ_CASCADE0;
+       }
+
+       if(irq < 8){
+               pic_mask[0] &= ~(1 << irq);
+               outb (PIC0_DATA, pic_mask[0]);
+       }
+
+}
+
+/* return whether an interrupt vector is registered */
+bool intr_is_registered(uint8_t vec_no)
+{
+       return (intr_handlers[vec_no] != NULL);
+}
index d43e06d90831f76d54e04c4693765565f96ded7c..79d63e71282db18aaa5a11ff7997c7a1d5fc514a 100644 (file)
@@ -67,4 +67,9 @@ void intr_yield_on_return (void);
 void intr_dump_frame (const struct intr_frame *);
 const char *intr_name (uint8_t vec);
 
+void intr_irq_mask(int irq);
+void intr_irq_unmask(int irq);
+
+bool intr_is_registered ( uint8_t vec );
+
 #endif /* threads/interrupt.h */
index 4fc8394c8f078a20c5a48722c36e3e022658620e..fbd74fc57917a356f9532078754e1d3a4e37f9e1 100644 (file)
@@ -39,6 +39,7 @@ static struct pool kernel_pool, user_pool;
 static void init_pool (struct pool *, void *base, size_t page_cnt,
                        const char *name);
 static bool page_from_pool (const struct pool *, void *page);
+static void page_set_cache(void* page, bool enable_cache);
 
 /* Initializes the page allocator.  At most USER_PAGE_LIMIT
    pages are put into the user pool. */
index cb3b70e94c93b6642ae35b74eee636da594c2d8d..adfb3ca67e7ecb0cd7e87c631b85f1c8023c44ff 100644 (file)
@@ -6,11 +6,14 @@
 /* How to allocate pages. */
 enum palloc_flags
   {
-    PAL_ASSERT = 001,           /* Panic on failure. */
-    PAL_ZERO = 002,             /* Zero page contents. */
-    PAL_USER = 004              /* User page. */
+    PAL_ASSERT = 1 << 0,   /* Panic on failure. */
+    PAL_ZERO = 1 << 1,     /* Zero page contents. */
+    PAL_USER = 1 << 2,     /* User page. */
+    PAL_NOCACHE = 1 << 3   /* Disable memory caching for page. */
   };
 
+extern void *zero_page;
+
 void palloc_init (size_t user_page_limit);
 void *palloc_get_page (enum palloc_flags);
 void *palloc_get_multiple (enum palloc_flags, size_t page_cnt);
index 1660727108385418a2cf07edba939f2a77ed9399..9133c45a2dc66ee963ca9642db8a0916ccdcaec1 100644 (file)
 #define PDBITS  10                         /* Number of page dir bits. */
 #define PDMASK  BITMASK(PDSHIFT, PDBITS)   /* Page directory bits (22:31). */
 
+/* memory dedicated to PCI - make sure this is 4MB aligned */
+#define PCI_ADDR_ZONE_BEGIN    0xe0000000
+#define PCI_ADDR_ZONE_END      0xe0800000
+#define PCI_ADDR_ZONE_PDES     2
+#define PCI_ADDR_ZONE_PAGES    (PCI_ADDR_ZONE_END-PCI_ADDR_ZONE_BEGIN)/PGSIZE
+
+
+
 /* Obtains page table index from a virtual address. */
 static inline unsigned pt_no (const void *va) {
   return ((uintptr_t) va & PTMASK) >> PTSHIFT;
@@ -64,15 +72,24 @@ static inline uintptr_t pd_no (const void *va) {
 #define PTE_P 0x1               /* 1=present, 0=not present. */
 #define PTE_W 0x2               /* 1=read/write, 0=read-only. */
 #define PTE_U 0x4               /* 1=user/kernel, 0=kernel only. */
+#define PTE_WT (1 << 3)         /* 1=write-through, 0=write-back */
+#define PTE_CD (1 << 4)         /* 1=cache disabled, 0=cache enabled. */
 #define PTE_A 0x20              /* 1=accessed, 0=not acccessed. */
 #define PTE_D 0x40              /* 1=dirty, 0=not dirty (PTEs only). */
+#define PTE_G (1 << 8)          /* 1=global page, do not flush */
 
 /* Returns a PDE that points to page table PT. */
-static inline uint32_t pde_create (uint32_t *pt) {
+static inline uint32_t pde_create_user (uint32_t *pt) {
   ASSERT (pg_ofs (pt) == 0);
   return vtop (pt) | PTE_U | PTE_P | PTE_W;
 }
 
+/* Returns a PDE that points to page table PT. */
+static inline uint32_t pde_create_kernel (uint32_t *pt) {
+  ASSERT (pg_ofs (pt) == 0);
+  return vtop (pt) | PTE_P | PTE_W | PTE_G;
+}
+
 /* Returns a pointer to the page table that page directory entry
    PDE, which must "present", points to. */
 static inline uint32_t *pde_get_pt (uint32_t pde) {
@@ -86,7 +103,7 @@ static inline uint32_t *pde_get_pt (uint32_t pde) {
    The page will be usable only by ring 0 code (the kernel). */
 static inline uint32_t pte_create_kernel (void *page, bool writable) {
   ASSERT (pg_ofs (page) == 0);
-  return vtop (page) | PTE_P | (writable ? PTE_W : 0);
+  return vtop (page) | PTE_P | (writable ? PTE_W : 0) | PTE_G;
 }
 
 /* Returns a PTE that points to PAGE.
@@ -94,7 +111,8 @@ static inline uint32_t pte_create_kernel (void *page, bool writable) {
    If WRITABLE is true then it will be writable as well.
    The page will be usable by both user and kernel code. */
 static inline uint32_t pte_create_user (void *page, bool writable) {
-  return pte_create_kernel (page, writable) | PTE_U;
+  ASSERT (pg_ofs (page) == 0);
+  return vtop (page) | PTE_P | (writable ? PTE_W : 0) | PTE_U;
 }
 
 /* Returns a pointer to the page that page table entry PTE points
index a6a87b827ba16a12157d94df6f2a7233b7c59e8e..83af90d67fffcb2994d1e6fa67ee16de1da998ca 100644 (file)
@@ -74,7 +74,7 @@ lookup_page (uint32_t *pd, const void *vaddr, bool create)
           if (pt == NULL) 
             return NULL; 
       
-          *pde = pde_create (pt);
+          *pde = pde_create_user (pt);
         }
       else
         return NULL;