/////////////////////////////////////////////////////////////////////////
// $Id: pciusb.cc,v 1.40 2006/08/20 09:19:59 vruppert Exp $
/////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2004  MandrakeSoft S.A.
//
//    MandrakeSoft S.A.
//    43, rue d'Aboukir
//    75002 Paris - France
//    http://www.linux-mandrake.com/
//    http://www.mandrakesoft.com/
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA

// Experimental PCI USB adapter
// Benjamin D Lunt (fys at frontiernet net) coded most of this usb emulation.

/* Notes:
   - I have coded this to be able to use more than one HUB and each
     device to have more than one function.  *However*, at the moment,
     not all of the code will work with multiple hubs and/or functions.
     I thought of removing this functionallity to make it easier, but
     thought that later when multiple hubs and/or functions were desired,
     it would already be half way there.
   - Currently, this code is quite messy.  This is for all of the debugging
     I have been doing.  Many BX_INFO()'s here and there.
   - My purpose of coding this emulation was/is to learn about the USB.
     It has been a challenge, but I have learned a lot.
   - If I forget, there are a lot of BX_INFO's that can be changed to BX_DEBUG's.
   - 31 July 2006:
     I now have a Beagle USB Protocol Analyzer from Total Phase for my research.
     (http://www.totalphase.com/products/beagle/usb/)
     With this device, I plan on doing a lot of research and development to get this
     code to a state where it is actually very useful.  I plan on adding support
     of many "plug-in" type modules so that you can simply add a plug-in for your
     specific device without having to modify the root code.
     I hope to have some working code to upload to the CVS as soon as possible.
     Thanks to Total Phase for their help in my research and the development of
     this project.
  */

// Define BX_PLUGGABLE in files that can be compiled into plugins.  For
// platforms that require a special tag on exported symbols, BX_PLUGGABLE 
// is used to know when we are exporting symbols and when we are importing.
#define BX_PLUGGABLE

#include "iodev.h"
#if BX_SUPPORT_PCI && BX_SUPPORT_PCIUSB

#define LOG_THIS theUSBDevice->

bx_pciusb_c* theUSBDevice = NULL;

const Bit8u usb_iomask[32] = {2, 1, 2, 1, 2, 1, 2, 0, 4, 0, 0, 0, 1, 0, 0, 0,
                              3, 1, 3, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

int libpciusb_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
{
  theUSBDevice = new bx_pciusb_c ();
  bx_devices.pluginPciUSBAdapter = theUSBDevice;
  BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theUSBDevice, BX_PLUGIN_PCIUSB);
  return 0; // Success
}

void libpciusb_LTX_plugin_fini(void)
{
}

bx_pciusb_c::bx_pciusb_c()
{
  put("USB");
  settype(PCIUSBLOG);
}

bx_pciusb_c::~bx_pciusb_c()
{
  //TODO:  free  BX_USB_THIS device_buffer

  // close any open handles
  for (int i=0; i<USB_CUR_DEVS; i++)
    if (BX_USB_THIS hub[0].device[i].fd > -1) 
      ::close(BX_USB_THIS hub[0].device[i].fd);

  BX_DEBUG(("Exit."));
}

void bx_pciusb_c::init(void)
{
  // called once when bochs initializes

  if (!SIM->get_param_bool(BXPN_USB1_ENABLED)->get()) return;

  BX_USB_THIS device_buffer = new Bit8u[65536];

  // Call our timer routine every 1mS (1,000uS)
  // Continuous and active
  BX_USB_THIS hub[0].timer_index =
                   bx_pc_system.register_timer(this, usb_timer_handler, 1000, 1,1, "usb.timer");

  BX_USB_THIS hub[0].devfunc = BX_PCI_DEVICE(1,2);
  DEV_register_pci_handlers(this, &BX_USB_THIS hub[0].devfunc, BX_PLUGIN_PCIUSB,
                            "Experimental PCI USB");

  for (unsigned i=0; i<256; i++) {
    BX_USB_THIS hub[0].pci_conf[i] = 0x0;
  }

  BX_USB_THIS hub[0].base_ioaddr = 0x0;

  BX_INFO(("usb1 initialized - I/O base and IRQ assigned by PCI BIOS"));

  //FIXME: for now, we want a status bar // hub zero, port zero
  BX_USB_THIS hub[0].statusbar_id[0] = bx_gui->register_statusitem("USB");

  SIM->get_param_string(BXPN_USB1_PORT1)->set_handler(usb_param_handler);
  SIM->get_param_string(BXPN_USB1_PORT1)->set_runtime_param(1);
  SIM->get_param_string(BXPN_USB1_OPTION1)->set_handler(usb_param_handler);
  SIM->get_param_string(BXPN_USB1_OPTION1)->set_runtime_param(1);
  SIM->get_param_string(BXPN_USB1_PORT2)->set_handler(usb_param_handler);
  SIM->get_param_string(BXPN_USB1_PORT2)->set_runtime_param(1);
  SIM->get_param_string(BXPN_USB1_OPTION2)->set_handler(usb_param_handler);
  SIM->get_param_string(BXPN_USB1_OPTION2)->set_runtime_param(1);

  //HACK: Turn on debug messages from the start
  //BX_USB_THIS setonoff(LOGLEV_DEBUG, ACT_REPORT);
}

void bx_pciusb_c::reset(unsigned type)
{
  unsigned i, j;

  if (!SIM->get_param_bool(BXPN_USB1_ENABLED)->get()) return;

  if (type == BX_RESET_HARDWARE) {
    static const struct reset_vals_t {
      unsigned      addr;
      unsigned char val;
    } reset_vals[] = {
      { 0x00, 0x86 }, { 0x01, 0x80 }, // 0x8086 = vendor
      { 0x02, 0x20 }, { 0x03, 0x70 }, // 0x7020 = device
      { 0x04, 0x05 }, { 0x05, 0x00 }, // command_io
      { 0x06, 0x80 }, { 0x07, 0x02 }, // status
      { 0x08, 0x01 },                 // revision number
      { 0x09, 0x00 },                 // interface
      { 0x0a, 0x03 },                 // class_sub  USB Host Controller
      { 0x0b, 0x0c },                 // class_base Serial Bus Controller
      { 0x0D, 0x20 },                 // bus latency
      { 0x0e, 0x00 },                 // header_type_generic
      // address space 0x20 - 0x23
      { 0x20, 0x01 }, { 0x21, 0x00 },
      { 0x22, 0x00 }, { 0x23, 0x00 },
      { 0x3c, 0x00 },                 // IRQ
      { 0x3d, BX_PCI_INTD },          // INT
      { 0x60, 0x10 },                 // USB revision 1.0
      { 0x6a, 0x01 },                 // USB clock
      { 0xc1, 0x20 }                  // PIRQ enable

    };
    for (i = 0; i < sizeof(reset_vals) / sizeof(*reset_vals); ++i) {
        BX_USB_THIS hub[0].pci_conf[reset_vals[i].addr] = reset_vals[i].val;
    }
  }

  // reset locals
  BX_USB_THIS busy = 0;
  BX_USB_THIS last_connect = 0xFF;
  BX_USB_THIS global_reset = 0;
  BX_USB_THIS set_address_stk = 0;
  memset(BX_USB_THIS saved_key, 0, 8);
  memset(BX_USB_THIS key_pad_packet, 0, 8);

  // mouse packet stuff
  BX_USB_THIS mouse_delayed_dx = 0;
  BX_USB_THIS mouse_delayed_dy = 0;
  BX_USB_THIS mouse_delayed_dz = 0;
  BX_USB_THIS button_state = 0;

  // Put the USB registers into their RESET state
  for (i=0; i<BX_USB_CONFDEV; i++) {
    BX_USB_THIS hub[i].usb_command.max_packet_size = 0;
    BX_USB_THIS hub[i].usb_command.configured = 0;
    BX_USB_THIS hub[i].usb_command.debug = 0;
    BX_USB_THIS hub[i].usb_command.resume = 0;
    BX_USB_THIS hub[i].usb_command.suspend = 0;
    BX_USB_THIS hub[i].usb_command.reset = 0;
    BX_USB_THIS hub[i].usb_command.host_reset = 0;
    BX_USB_THIS hub[i].usb_command.schedule = 0;
    BX_USB_THIS hub[i].usb_status.error_interrupt = 0;
    BX_USB_THIS hub[i].usb_status.host_error = 0;
    BX_USB_THIS hub[i].usb_status.host_halted = 0;
    BX_USB_THIS hub[i].usb_status.interrupt = 0;
    BX_USB_THIS hub[i].usb_status.pci_error = 0;
    BX_USB_THIS hub[i].usb_status.resume = 0;
    BX_USB_THIS hub[i].usb_enable.short_packet = 0;
    BX_USB_THIS hub[i].usb_enable.on_complete = 0;
    BX_USB_THIS hub[i].usb_enable.resume = 0;
    BX_USB_THIS hub[i].usb_enable.timeout_crc = 0;
    BX_USB_THIS hub[i].usb_frame_num.frame_num = 0x0000;
    BX_USB_THIS hub[i].usb_frame_base.frame_base = 0x00000000;
    BX_USB_THIS hub[i].usb_sof.sof_timing = 0x40;
    for (j=0; j<USB_NUM_PORTS; j++) {
      BX_USB_THIS hub[i].usb_port[j].connect_changed = 0;
      BX_USB_THIS hub[i].usb_port[j].line_dminus = 0;
      BX_USB_THIS hub[i].usb_port[j].line_dplus = 0;
      BX_USB_THIS hub[i].usb_port[j].low_speed = 0;
      BX_USB_THIS hub[i].usb_port[j].reset = 0;
      BX_USB_THIS hub[i].usb_port[j].resume = 0;
      BX_USB_THIS hub[i].usb_port[j].suspend = 0;
      BX_USB_THIS hub[i].usb_port[j].enabled = 0;
      BX_USB_THIS hub[i].usb_port[j].able_changed = 0;
      BX_USB_THIS hub[i].usb_port[j].status = 0;
      BX_USB_THIS hub[i].usb_port[j].device_num = -1;
    }
  }

  // First, clear out the device(s)
  for (i=0; i<BX_USB_CONFDEV; i++)
    for (j=0; j<USB_CUR_DEVS; j++) {
      memset(&BX_USB_THIS hub[i].device[j], 0, sizeof(USB_DEVICE));
      BX_USB_THIS hub[i].device[j].fd = -1;
    }

  BX_USB_THIS keyboard_connected = 0;
  BX_USB_THIS mouse_connected = 0;
  BX_USB_THIS flash_connected = 0;

  // include the device(s) initialize code
  #include "pciusb_devs.h"

  init_device(0, SIM->get_param_string(BXPN_USB1_PORT1)->getptr());
  init_device(1, SIM->get_param_string(BXPN_USB1_PORT2)->getptr());
}

#if BX_SUPPORT_SAVE_RESTORE
void bx_pciusb_c::register_state(void)
{
  unsigned i, j, n;
  char hubnum[8], portnum[8], name[6];
  bx_list_c *hub, *usb_cmd, *usb_st, *usb_en, *port;

  bx_list_c *list = new bx_list_c(SIM->get_sr_root(), "pciusb", "PCI USB Controller State", BX_USB_CONFDEV + 15);
  for (i=0; i<BX_USB_CONFDEV; i++) {
    sprintf(hubnum, "hub%d", i+1);
    hub = new bx_list_c(list, hubnum, USB_NUM_PORTS + 7);
    usb_cmd = new bx_list_c(hub, "usb_command", 8);
    new bx_shadow_bool_c(usb_cmd, "max_packet_size", &BX_USB_THIS hub[i].usb_command.max_packet_size);
    new bx_shadow_bool_c(usb_cmd, "configured", &BX_USB_THIS hub[i].usb_command.configured);
    new bx_shadow_bool_c(usb_cmd, "debug", &BX_USB_THIS hub[i].usb_command.debug);
    new bx_shadow_bool_c(usb_cmd, "resume", &BX_USB_THIS hub[i].usb_command.resume);
    new bx_shadow_bool_c(usb_cmd, "suspend", &BX_USB_THIS hub[i].usb_command.suspend);
    new bx_shadow_bool_c(usb_cmd, "reset", &BX_USB_THIS hub[i].usb_command.reset);
    new bx_shadow_bool_c(usb_cmd, "host_reset", &BX_USB_THIS hub[i].usb_command.host_reset);
    new bx_shadow_bool_c(usb_cmd, "schedule", &BX_USB_THIS hub[i].usb_command.schedule);
    usb_st = new bx_list_c(hub, "usb_status");
    new bx_shadow_bool_c(usb_st, "host_halted", &BX_USB_THIS hub[i].usb_status.host_halted);
    new bx_shadow_bool_c(usb_st, "host_error", &BX_USB_THIS hub[i].usb_status.host_error);
    new bx_shadow_bool_c(usb_st, "pci_error", &BX_USB_THIS hub[i].usb_status.pci_error);
    new bx_shadow_bool_c(usb_st, "resume", &BX_USB_THIS hub[i].usb_status.resume);
    new bx_shadow_bool_c(usb_st, "error_interrupt", &BX_USB_THIS hub[i].usb_status.error_interrupt);
    new bx_shadow_bool_c(usb_st, "interrupt", &BX_USB_THIS hub[i].usb_status.interrupt);
    usb_en = new bx_list_c(hub, "usb_enable");
    new bx_shadow_bool_c(usb_en, "short_packet", &BX_USB_THIS hub[i].usb_enable.short_packet);
    new bx_shadow_bool_c(usb_en, "on_complete", &BX_USB_THIS hub[i].usb_enable.on_complete);
    new bx_shadow_bool_c(usb_en, "resume", &BX_USB_THIS hub[i].usb_enable.resume);
    new bx_shadow_bool_c(usb_en, "timeout_crc", &BX_USB_THIS hub[i].usb_enable.timeout_crc);
    new bx_shadow_num_c(hub, "frame_num", &BX_USB_THIS hub[i].usb_frame_num.frame_num, BASE_HEX);
    new bx_shadow_num_c(hub, "frame_base", &BX_USB_THIS hub[i].usb_frame_base.frame_base, BASE_HEX);
    new bx_shadow_num_c(hub, "sof_timing", &BX_USB_THIS hub[i].usb_sof.sof_timing, BASE_HEX);
    for (j=0; j<USB_NUM_PORTS; j++) {
      sprintf(portnum, "port%d", j+1);
      port = new bx_list_c(hub, portnum, 10);
      new bx_shadow_bool_c(port, "suspend", &BX_USB_THIS hub[i].usb_port[j].suspend);
      new bx_shadow_bool_c(port, "reset", &BX_USB_THIS hub[i].usb_port[j].reset);
      new bx_shadow_bool_c(port, "low_speed", &BX_USB_THIS hub[i].usb_port[j].low_speed);
      new bx_shadow_bool_c(port, "resume", &BX_USB_THIS hub[i].usb_port[j].resume);
      new bx_shadow_bool_c(port, "line_dminus", &BX_USB_THIS hub[i].usb_port[j].line_dminus);
      new bx_shadow_bool_c(port, "line_dplus", &BX_USB_THIS hub[i].usb_port[j].line_dplus);
      new bx_shadow_bool_c(port, "able_changed", &BX_USB_THIS hub[i].usb_port[j].able_changed);
      new bx_shadow_bool_c(port, "enabled", &BX_USB_THIS hub[i].usb_port[j].enabled);
      new bx_shadow_bool_c(port, "connect_changed", &BX_USB_THIS hub[i].usb_port[j].connect_changed);
      new bx_shadow_bool_c(port, "status", &BX_USB_THIS hub[i].usb_port[j].status);
    }
    bx_list_c *pci_conf = new bx_list_c(hub, "pci_conf", 256);
    for (n=0; n<256; n++) {
      sprintf(name, "0x%02x", n);
      new bx_shadow_num_c(pci_conf, name, &BX_USB_THIS hub[i].pci_conf[n], BASE_HEX);
    }
  }
  new bx_shadow_bool_c(list, "busy", &BX_USB_THIS busy);
  new bx_shadow_num_c(list, "global_reset", &BX_USB_THIS global_reset);
  new bx_shadow_num_c(list, "mouse_delayed_dx", &BX_USB_THIS mouse_delayed_dx);
  new bx_shadow_num_c(list, "mouse_delayed_dy", &BX_USB_THIS mouse_delayed_dy);
  new bx_shadow_num_c(list, "mouse_delayed_dz", &BX_USB_THIS mouse_delayed_dz);
  new bx_shadow_num_c(list, "button_state", &BX_USB_THIS button_state);
  new bx_shadow_num_c(list, "mouse_x", &BX_USB_THIS mouse_x);
  new bx_shadow_num_c(list, "mouse_y", &BX_USB_THIS mouse_y);
  new bx_shadow_num_c(list, "mouse_z", &BX_USB_THIS mouse_z);
  new bx_shadow_num_c(list, "b_state", &BX_USB_THIS b_state);
  bx_list_c *svkey = new bx_list_c(list, "saved_key", 8);
  for (i=0; i<8; i++) {
    sprintf(name, "%d", i);
    new bx_shadow_num_c(svkey, name, &BX_USB_THIS saved_key[i]);
  }
  bx_list_c *kppkt = new bx_list_c(list, "key_pad_packet", 8);
  for (i=0; i<8; i++) {
    sprintf(name, "%d", i);
    new bx_shadow_num_c(kppkt, name, &BX_USB_THIS key_pad_packet[i]);
  }
  new bx_shadow_data_c(list, "device_buffer", BX_USB_THIS device_buffer, 65536);
  new bx_shadow_num_c(list, "set_address_stk", &BX_USB_THIS set_address_stk);
  bx_list_c *setaddr = new bx_list_c(list, "set_address", 128);
  for (i=0; i<128; i++) {
    sprintf(name, "0x%02x", i);
    new bx_shadow_num_c(setaddr, name, &BX_USB_THIS set_address[i], BASE_HEX);
  }
}

void bx_pciusb_c::after_restore_state(void)
{
  if (DEV_pci_set_base_io(BX_USB_THIS_PTR, read_handler, write_handler,
                         &BX_USB_THIS hub[0].base_ioaddr,
                         &BX_USB_THIS hub[0].pci_conf[0x20],
                         32, &usb_iomask[0], "USB Hub #1"))
  {
     BX_INFO(("new base address: 0x%04x", BX_USB_THIS hub[0].base_ioaddr));
  }
}
#endif

void bx_pciusb_c::init_device(Bit8u port, char *devname)
{
  Bit8u type = USB_DEV_TYPE_NONE;
  bx_bool connected = 0;

  if (!strlen(devname)) return;

  if (!strcmp(devname, "mouse")) {
    type = USB_DEV_TYPE_MOUSE;
    connected = SIM->get_param_bool(BXPN_MOUSE_ENABLED)->get();
    if (SIM->get_param_enum(BXPN_MOUSE_TYPE)->get() == BX_MOUSE_TYPE_USB) {
      BX_USB_THIS mouse_connected = connected;
    } else if (connected) {
      BX_ERROR(("USB mouse connect ignored, since other mouse type is configured"));
      connected = 0;
    }
  } else if (!strcmp(devname, "keypad")) {
    type = USB_DEV_TYPE_KEYPAD;
    connected = 1;
    BX_USB_THIS keyboard_connected = 1;
//} else if (!strcmp(devname, "flash")) {
//  type = USB_DEV_TYPE_FLASH;
//  connected = 1;
//  BX_USB_THIS flash_connected = 1;
  } else {
    BX_PANIC(("unknown USB device: %s", devname));
    return;
  }
  for (int i=0; i<USB_CUR_DEVS; i++) {
    if (BX_USB_THIS hub[0].device[i].dev_type == type) {
      BX_USB_THIS hub[0].usb_port[port].device_num = i;
      BX_USB_THIS hub[0].device[i].stall_once &= ~0x80; // clear out the "stall once bit has happened"
    }
  }
  usb_set_connect_status(port, type, connected);
}

void bx_pciusb_c::set_irq_level(bx_bool level)
{
  DEV_pci_set_irq(BX_USB_THIS hub[0].devfunc, BX_USB_THIS hub[0].pci_conf[0x3d], level);
}

// static IO port read callback handler
// redirects to non-static class handler to avoid virtual functions

Bit32u bx_pciusb_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
{
#if !BX_USE_PCIUSB_SMF
  bx_pciusb_c *class_ptr = (bx_pciusb_c *) this_ptr;
  return class_ptr->read(address, io_len);
}

Bit32u bx_pciusb_c::read(Bit32u address, unsigned io_len)
{
#else
  UNUSED(this_ptr);
#endif // !BX_USE_PCIUSB_SMF
  Bit32u val = 0x0;
  Bit8u  offset,port;

  offset = address - BX_USB_THIS hub[0].base_ioaddr;

  switch (offset) {
    case 0x00: // command register (16-bit)
      val =   BX_USB_THIS hub[0].usb_command.max_packet_size << 7
            | BX_USB_THIS hub[0].usb_command.configured << 6
            | BX_USB_THIS hub[0].usb_command.debug << 5
            | BX_USB_THIS hub[0].usb_command.resume << 4
            | BX_USB_THIS hub[0].usb_command.suspend << 3
            | BX_USB_THIS hub[0].usb_command.reset << 2
            | BX_USB_THIS hub[0].usb_command.host_reset << 1
            | BX_USB_THIS hub[0].usb_command.schedule;
      break;

    case 0x02: // status register (16-bit)
      val = BX_USB_THIS hub[0].usb_status.host_halted << 5
            | BX_USB_THIS hub[0].usb_status.host_error << 4
            | BX_USB_THIS hub[0].usb_status.pci_error << 3
            | BX_USB_THIS hub[0].usb_status.resume << 2
            | BX_USB_THIS hub[0].usb_status.error_interrupt << 1
            | BX_USB_THIS hub[0].usb_status.interrupt;
      break;

    case 0x04: // interrupt enable register (16-bit)
      val = BX_USB_THIS hub[0].usb_enable.short_packet << 3
            | BX_USB_THIS hub[0].usb_enable.on_complete << 2
            | BX_USB_THIS hub[0].usb_enable.resume << 1
            | BX_USB_THIS hub[0].usb_enable.timeout_crc;
      break;

    case 0x06: // frame number register (16-bit)
      val = BX_USB_THIS hub[0].usb_frame_num.frame_num;
      break;

    case 0x08: // frame base register (32-bit)
      val = BX_USB_THIS hub[0].usb_frame_base.frame_base;
      break;

    case 0x0C: // start of Frame Modify register (8-bit)
      val = BX_USB_THIS hub[0].usb_sof.sof_timing;
      break;

    case 0x14: // port #3 non existant, but linux systems check it to see if there are more than 2
      BX_ERROR(("read from non existant offset 0x14 (port #3)"));
      val = 0xFF7F;
      break;

    case 0x10: // port #1
    case 0x11:
    case 0x12: // port #2
    case 0x13:
      port = (offset & 0x0F) >> 1;
      if (port < USB_NUM_PORTS) {
        val = BX_USB_THIS hub[0].usb_port[port].suspend << 12
              |                                       1 << 10  // some Root Hubs have bit 10 set ?????
              | BX_USB_THIS hub[0].usb_port[port].reset << 9
              | BX_USB_THIS hub[0].usb_port[port].low_speed << 8
              | 1 << 7
              | BX_USB_THIS hub[0].usb_port[port].resume << 6
              | BX_USB_THIS hub[0].usb_port[port].line_dminus << 5
              | BX_USB_THIS hub[0].usb_port[port].line_dplus << 4
              | BX_USB_THIS hub[0].usb_port[port].able_changed << 3
              | BX_USB_THIS hub[0].usb_port[port].enabled << 2
              | BX_USB_THIS hub[0].usb_port[port].connect_changed << 1
              | BX_USB_THIS hub[0].usb_port[port].status;
        if (offset & 1) val >>= 8;
        break;
      } // else fall through to default
    default:
      val = 0xFF7F; // keep compiler happy
      BX_ERROR(("unsupported io read from address=0x%04x!", (unsigned) address));
      break;
  }

  BX_DEBUG(("register read from address 0x%04X:  0x%08X (%2i bits)", (unsigned) address, (Bit32u) val, io_len * 8));

  return(val);
}

// static IO port write callback handler
// redirects to non-static class handler to avoid virtual functions

void bx_pciusb_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
{
#if !BX_USE_PCIUSB_SMF
  bx_pciusb_c *class_ptr = (bx_pciusb_c *) this_ptr;
  class_ptr->write(address, value, io_len);
}

void bx_pciusb_c::write(Bit32u address, Bit32u value, unsigned io_len)
{
#else
  UNUSED(this_ptr);
#endif // !BX_USE_PCIUSB_SMF
  Bit8u  offset,port;

  BX_DEBUG(("register write to  address 0x%04X:  0x%08X (%2i bits)", (unsigned) address, (unsigned) value, io_len * 8));

  offset = address - BX_USB_THIS hub[0].base_ioaddr;

  switch (offset) {
    case 0x00: // command register (16-bit) (R/W)
      if (value & 0xFF00)
        BX_DEBUG(("write to command register with bits 15:8 not zero: 0x%04x", value));

      BX_USB_THIS hub[0].usb_command.max_packet_size = (value & 0x80) ? 1: 0;
      BX_USB_THIS hub[0].usb_command.configured = (value & 0x40) ? 1: 0;
      BX_USB_THIS hub[0].usb_command.debug = (value & 0x20) ? 1: 0;
      BX_USB_THIS hub[0].usb_command.resume = (value & 0x10) ? 1: 0;
      BX_USB_THIS hub[0].usb_command.suspend = (value & 0x08) ? 1: 0;
      BX_USB_THIS hub[0].usb_command.reset = (value & 0x04) ? 1: 0;
      BX_USB_THIS hub[0].usb_command.host_reset = (value & 0x02) ? 1: 0;
      BX_USB_THIS hub[0].usb_command.schedule = (value & 0x01) ? 1: 0;

      // HCRESET 
      if (BX_USB_THIS hub[0].usb_command.host_reset) {
        BX_USB_THIS reset(0);
        for (unsigned i=0; i<USB_NUM_PORTS; i++) {
          if (BX_USB_THIS hub[0].usb_port[i].status)
            usb_set_connect_status(i, BX_USB_THIS hub[0].device[BX_USB_THIS hub[0].usb_port[i].device_num].dev_type,
              BX_USB_THIS hub[0].device[BX_USB_THIS hub[0].usb_port[i].device_num].connect_status);
          BX_USB_THIS hub[0].usb_port[i].connect_changed = 1;
          BX_USB_THIS hub[0].usb_port[i].enabled = 0;
          BX_USB_THIS hub[0].usb_port[i].able_changed = 1;
        }
      }

      // If software set the GRESET bit, we need to send the reset to all USB.
      // The software should guarentee that the reset is for at least 10ms.
      // We hold the reset until software resets this bit
      if (BX_USB_THIS hub[0].usb_command.reset) {
        BX_USB_THIS global_reset = 1;
        BX_DEBUG(("Global Reset"));
      } else {
        // if software cleared the reset, then we need to reset the usb registers.
        if (BX_USB_THIS global_reset) {
          BX_USB_THIS global_reset = 0;
          unsigned int running = BX_USB_THIS hub[0].usb_command.schedule;
          BX_USB_THIS reset(0);
          BX_USB_THIS hub[0].usb_status.host_halted = (running) ? 1 : 0;
        }
      }

      // If Run/Stop, identify in log
      if (BX_USB_THIS hub[0].usb_command.schedule) {
        BX_USB_THIS hub[0].usb_status.host_halted = 0;
        BX_DEBUG(("Schedule bit set in Command register"));
      } else {
        //BX_USB_THIS hub[0].usb_status.host_halted = 1;
        BX_DEBUG(("Schedule bit clear in Command register"));
      }

      // If Debug mode set, panic.  Not implemented
      if (BX_USB_THIS hub[0].usb_command.debug)
        BX_PANIC(("Software set DEBUG bit in Command register. Not implemented"));

      break;

    case 0x02: // status register (16-bit) (R/WC)
      if (value & 0xFFC0)
        BX_DEBUG(("write to status register with bits 15:6 not zero: 0x%04x", value));

      // host_halted, even though not specified in the specs, is read only
      //BX_USB_THIS hub[0].usb_status.host_halted = (value & 0x20) ? 0: BX_USB_THIS hub[0].usb_status.host_halted;
      BX_USB_THIS hub[0].usb_status.host_error = (value & 0x10) ? 0: BX_USB_THIS hub[0].usb_status.host_error;
      BX_USB_THIS hub[0].usb_status.pci_error = (value & 0x08) ? 0: BX_USB_THIS hub[0].usb_status.pci_error;
      BX_USB_THIS hub[0].usb_status.resume = (value & 0x04) ? 0: BX_USB_THIS hub[0].usb_status.resume;
      BX_USB_THIS hub[0].usb_status.error_interrupt = (value & 0x02) ? 0: BX_USB_THIS hub[0].usb_status.error_interrupt;
      BX_USB_THIS hub[0].usb_status.interrupt = (value & 0x01) ? 0: BX_USB_THIS hub[0].usb_status.interrupt;
      break;

    case 0x04: // interrupt enable register (16-bit)
      if (value & 0xFFF0)
        BX_DEBUG(("write to interrupt enable register with bits 15:4 not zero: 0x%04x", value));

      BX_USB_THIS hub[0].usb_enable.short_packet  = (value & 0x08) ? 1: 0;
      BX_USB_THIS hub[0].usb_enable.on_complete  = (value & 0x04) ? 1: 0;
      BX_USB_THIS hub[0].usb_enable.resume  = (value & 0x02) ? 1: 0;
      BX_USB_THIS hub[0].usb_enable.timeout_crc = (value & 0x01) ? 1: 0;

      if (value & 0x08) {
        BX_DEBUG(("Host set Enable Interrupt on Short Packet"));
      }
      if (value & 0x04) {
        BX_DEBUG(("Host set Enable Interrupt on Complete"));
      }
      if (value & 0x02) {
        BX_DEBUG(("Host set Enable Interrupt on Resume"));
      }
      break;

    case 0x06: // frame number register (16-bit)
      if (value & 0xF800)
        BX_DEBUG(("write to frame number register with bits 15:11 not zero: 0x%04x", value));

      if (BX_USB_THIS hub[0].usb_status.host_halted)
        BX_USB_THIS hub[0].usb_frame_num.frame_num = value;
      else
        // ignored by the hardward, but lets report it anyway
        BX_DEBUG(("write to frame number register with STATUS.HALTED == 0"));
      break;

    case 0x08: // frame base register (32-bit)
      if (value & 0xFFF)
        BX_PANIC(("write to frame base register with bits 11:0 not zero: 0x%08x", value));

      BX_USB_THIS hub[0].usb_frame_base.frame_base = value;
      break;

    case 0x0C: // start of Frame Modify register (8-bit)
      if (value & 0x80)
        BX_DEBUG(("write to SOF Modify register with bit 7 not zero: 0x%04x", value));

       BX_USB_THIS hub[0].usb_sof.sof_timing = value;
       break;

    case 0x14: // port #3 non existant, but linux systems check it to see if there are more than 2
      BX_ERROR(("write to non existant offset 0x14 (port #3)"));
      break;

    case 0x10: // port #1
    case 0x12: // port #2
      port = (offset & 0x0F) >> 1;
      if ((port < USB_NUM_PORTS) && (io_len == 2)) {
        // If the ports reset bit is set, don't allow any writes unless the new write will clear the reset bit
        if (BX_USB_THIS hub[0].usb_port[port].reset & (value & (1<<9)))
          break;
        if (value & ((1<<5) | (1<<4) | (1<<0)))
          BX_DEBUG(("write to one or more read-only bits in port #%d register: 0x%04x", port+1, value));
        if (!(value & (1<<7)))
          BX_DEBUG(("write to port #%d register bit 7 = 0", port+1));
        if (value & (1<<8))
          BX_DEBUG(("write to bit 8 in port #%d register ignored", port+1));
        if ((value & (1<<12)) && BX_USB_THIS hub[0].usb_command.suspend)
          BX_DEBUG(("write to port #%d register bit 12 when in Global-Suspend", port+1));

        BX_USB_THIS hub[0].usb_port[port].suspend = (value & (1<<12)) ? 1 : 0;
        BX_USB_THIS hub[0].usb_port[port].reset = (value & (1<<9)) ? 1 : 0;
        BX_USB_THIS hub[0].usb_port[port].resume = (value & (1<<6)) ? 1 : 0;
        if (!BX_USB_THIS hub[0].usb_port[port].enabled && (value & (1<<2)))
          BX_USB_THIS hub[0].usb_port[port].able_changed = 0;
        else
          BX_USB_THIS hub[0].usb_port[port].able_changed = (value & (1<<3)) ? 0 : BX_USB_THIS hub[0].usb_port[port].able_changed;
        BX_USB_THIS hub[0].usb_port[port].enabled = (value & (1<<2)) ? 1 : 0;
        BX_USB_THIS hub[0].usb_port[port].connect_changed = (value & (1<<1)) ? 0 : BX_USB_THIS hub[0].usb_port[port].connect_changed;

        // if port reset, reset function(s)
        //TODO: only reset items on the downstream...
        // for now, reset the one and only
        // TODO: descriptors, etc....
        if (BX_USB_THIS hub[0].usb_port[port].reset) {
          BX_USB_THIS hub[0].usb_port[port].suspend = 0;
          BX_USB_THIS hub[0].usb_port[port].resume = 0;
          BX_USB_THIS hub[0].usb_port[port].enabled = 0;
          // are we are currently connected/disconnected
          if (BX_USB_THIS hub[0].usb_port[port].status) {
            BX_USB_THIS hub[0].device[BX_USB_THIS hub[0].usb_port[port].device_num].connect_status = 0;
            usb_set_connect_status(port, BX_USB_THIS hub[0].device[BX_USB_THIS hub[0].usb_port[port].device_num].dev_type, 1);
          }
          BX_INFO(("Port%d: Reset", port+1));
        }
        break;
      }
      // else fall through to default
    default:
      BX_ERROR(("unsupported io write to address=0x%04x!", (unsigned) address));
      break;
  }
}

void bx_pciusb_c::usb_timer_handler(void *this_ptr)
{
  bx_pciusb_c *class_ptr = (bx_pciusb_c *) this_ptr;
  class_ptr->usb_timer();
}

// Called once every 1ms
#define USB_STACK_SIZE  256
void bx_pciusb_c::usb_timer(void)
{
  int i;

  // If the "global reset" bit was set by software
  if (BX_USB_THIS global_reset) {
    for (i=0; i<USB_NUM_PORTS; i++) {
      BX_USB_THIS hub[0].usb_port[i].able_changed = 0;
      BX_USB_THIS hub[0].usb_port[i].connect_changed = 0;
      BX_USB_THIS hub[0].usb_port[i].enabled = 0;
      BX_USB_THIS hub[0].usb_port[i].line_dminus = 0;
      BX_USB_THIS hub[0].usb_port[i].line_dplus = 0;
      BX_USB_THIS hub[0].usb_port[i].low_speed = 0;
      BX_USB_THIS hub[0].usb_port[i].reset = 0;
      BX_USB_THIS hub[0].usb_port[i].resume = 0;
      BX_USB_THIS hub[0].usb_port[i].status = 0;
      BX_USB_THIS hub[0].usb_port[i].suspend = 0;
    }
    return;
  }

  // If command.schedule = 1, then run schedule
  //  *** This assumes that we can complete the frame within the 1ms time allowed ***
  // Actually, not complete, but reach the end of the frame.  This means that there may still 
  //  be TDs and QHs that were BREADTH defined and will be executed on the next cycle/iteration.

  if (BX_USB_THIS busy) {
    BX_PANIC(("Did not complete last frame before the 1ms was over. Starting next frame."));
    BX_USB_THIS busy = 0;
  }
  if (BX_USB_THIS hub[0].usb_command.schedule) {
    struct USB_DEVICE *dev = NULL;
    BX_USB_THIS busy = 1;
    bx_bool fire_int = 0;
    set_irq_level(0);  // make sure it is low
    bx_bool interrupt = 0, shortpacket = 0, stalled = 0;
    struct TD td;
    struct HCSTACK stack[USB_STACK_SIZE+1];  // queue stack for this item only
    Bit32s stk = 0;
    Bit32u item, address, lastvertaddr = 0, queue_num = 0;
    Bit32u frame, frm_addr = BX_USB_THIS hub[0].usb_frame_base.frame_base + 
                                (BX_USB_THIS hub[0].usb_frame_num.frame_num << 2);
    DEV_MEM_READ_PHYSICAL(frm_addr, 4, &frame);
    if ((frame & 1) == 0) {
      stack[stk].next = (frame & ~0xF);
      stack[stk].d = 0;
      stack[stk].q = (frame & 0x0002) ? 1 : 0;
      stack[stk].t = 0;
      while (stk > -1) {

        // Linux seems to just loop a few queues together and wait for the 1ms to end.
        // We will just count the stack and exit when we get to a good point to stop.
        if (stk >= USB_STACK_SIZE) break;

        // check to make sure we are not done before continue-ing on
        if ((stack[stk].d == HC_VERT) && stack[stk].t) { stk--; continue; }
        if ((stack[stk].d == HC_HORZ) && stack[stk].t) break;
        if (stack[stk].q) { // is a queue
          address = stack[stk].next;
          lastvertaddr = address + 4;
          // get HORZ slot
          stk++;
          DEV_MEM_READ_PHYSICAL(address, 4, &item);
          stack[stk].next = item & ~0xF;
          stack[stk].d = HC_HORZ;
          stack[stk].q = (item & 0x0002) ? 1 : 0;
          stack[stk].t = (item & 0x0001) ? 1 : 0;
          // get VERT slot
          stk++;
          DEV_MEM_READ_PHYSICAL(lastvertaddr, 4, &item);
          stack[stk].next = item & ~0xF;
          stack[stk].d = HC_VERT;
          stack[stk].q = (item & 0x0002) ? 1 : 0;
          stack[stk].t = (item & 0x0001) ? 1 : 0;
          BX_DEBUG(("Queue %3i: 0x%08X %i %i  0x%08X %i %i", queue_num, 
            stack[stk-1].next, stack[stk-1].q, stack[stk-1].t,
            stack[stk].next, stack[stk].q, stack[stk].t));
          queue_num++;
        } else {  // else is a TD
          address = stack[stk].next;
          DEV_MEM_READ_PHYSICAL(address, 32, &td);
          bx_bool spd = (td.dword1 & (1<<29)) ? 1 : 0;
          stack[stk].next = td.dword0 & ~0xF;
          bx_bool depthbreadth = (td.dword0 & 0x0004) ? 1 : 0;     // 1 = depth first, 0 = breadth first
          stack[stk].q = (td.dword0 & 0x0002) ? 1 : 0;
          stack[stk].t = (td.dword0 & 0x0001) ? 1 : 0;
          if (td.dword1 & (1<<24)) interrupt = 1;
          if (td.dword1 & (1<<23)) {  // is it an active TD
            BX_DEBUG(("Frame: %04i (0x%04X)", BX_USB_THIS hub[0].usb_frame_num.frame_num, BX_USB_THIS hub[0].usb_frame_num.frame_num));
            if (BX_USB_THIS DoTransfer(address, queue_num, &td)) {
              // issue short packet?
              Bit16u r_actlen = (((td.dword1 & 0x7FF)+1) & 0x7FF);
              Bit16u r_maxlen = (((td.dword2>>21)+1) & 0x7FF);
              BX_DEBUG((" r_actlen = 0x%04X r_maxlen = 0x%04X", r_actlen, r_maxlen));
              if (((td.dword2 & 0xFF) == TOKEN_IN) && spd && stk && (r_actlen < r_maxlen) && ((td.dword1 & 0x00FF0000) == 0)) {
                shortpacket = 1;
                td.dword1 |= (1<<29);
              }
              if (td.dword1 & (1<<22)) stalled = 1;

              DEV_MEM_WRITE_PHYSICAL(address+4, 4, &td.dword1);  // write back the status
              // copy pointer for next queue item, in to vert queue head
              if ((stk > 0) && !shortpacket && (stack[stk].d == HC_VERT))
                DEV_MEM_WRITE_PHYSICAL(lastvertaddr, 4, &td.dword0);
            }
          }

          if (stk > 0) {
            // if last TD in HORZ queue pointer, then we are done.
            if (stack[stk].t && (stack[stk].d == HC_HORZ)) break;
            // if Breadth first or last item in queue, move to next queue.
            if (!depthbreadth || stack[stk].t) {
              if (stack[stk].d == HC_HORZ) queue_num--;  // <-- really, this should never happen until we
              stk--;                                     //           support bandwidth reclamation...
            }
            if (stk < 1) break;
          } else {
            if (stack[stk].t) break;
          }

        }
      }

      // set the status register bit:0 to 1 if SPD is enabled
      // and if interrupts not masked via interrupt register, raise irq interrupt.
      if (shortpacket && BX_USB_THIS hub[0].usb_enable.short_packet) {
        fire_int = 1;
        BX_DEBUG((" [SPD] We want it to fire here (Frame: %04i)", BX_USB_THIS hub[0].usb_frame_num.frame_num));
      }

      // if one of the TD's in this frame had the ioc bit set, we need to
      //   raise an interrupt, if interrupts are not masked via interrupt register.
      //   always set the status register if IOC.
      if (interrupt && BX_USB_THIS hub[0].usb_enable.on_complete) {
        fire_int = 1;
        BX_DEBUG((" [IOC] We want it to fire here (Frame: %04i)", BX_USB_THIS hub[0].usb_frame_num.frame_num));
      }

      if (stalled && BX_USB_THIS hub[0].usb_enable.timeout_crc) {
        fire_int = 1;
        BX_DEBUG((" [stalled] We want it to fire here (Frame: %04i)", BX_USB_THIS hub[0].usb_frame_num.frame_num));
      }
    }

    // The Frame Number Register is incremented every 1ms
    BX_USB_THIS hub[0].usb_frame_num.frame_num++;
    BX_USB_THIS hub[0].usb_frame_num.frame_num &= (1024-1);

    // if we needed to fire an interrupt now, lets do it *after* we increment the frame_num register
    if (fire_int) {
      BX_USB_THIS hub[0].usb_status.interrupt = 1 | ((stalled) ? 2 : 0);
      set_irq_level(1);
    }

    BX_USB_THIS busy = 0;  // ready to do next frame item
  }  // end run schedule

  // if host turned off the schedule, set the halted bit in the status register
  // Note: Can not use an else from the if() above since the host can changed this bit
  //  while we are processing a frame.
  if (BX_USB_THIS hub[0].usb_command.schedule == 0)
    BX_USB_THIS hub[0].usb_status.host_halted = 1;

  // TODO:
  //  If in Global_Suspend mode and any of usb_port[i] bits 6,3, or 1 are set,
  //    we need to issue a Global_Resume (set the global resume bit).
  //    However, since we don't do anything, let's not.
}

bx_bool bx_pciusb_c::DoTransfer(Bit32u address, Bit32u queue_num, struct TD *td) {
  
  int i, j;
  Bit8u protocol = 0;
  bx_bool fnd;

  Bit16u maxlen = (td->dword2 >> 21);
  Bit8u  addr   = (td->dword2 >> 8) & 0x7F;
  Bit8u  endpt  = (td->dword2 >> 15) & 0x0F;
  Bit8u  pid    =  td->dword2 & 0xFF;
  Bit8u  data[64];
  struct REQUEST_PACKET *rp = (struct REQUEST_PACKET *) data;

  // if packet, read in the packet data
  if (pid == TOKEN_SETUP) {
    if (td->dword3) DEV_MEM_READ_PHYSICAL(td->dword3, 8, data);
    // the '8' above may need to be maxlen (unless maxlen == 0)
  }

  BX_DEBUG(("QH%03i:TD found at address: 0x%08X", queue_num, address));
  BX_DEBUG(("  %08X   %08X   %08X   %08X", td->dword0, td->dword1, td->dword2, td->dword3));

  // check TD to make sure it is valid
  // A max length 0x500 to 0x77E is illegal
  if (((td->dword2 >> 21) >= 0x500) && ((td->dword2 >> 21) != 0x7FF)) {
    BX_ERROR(("error at 11111111111"));
    return 1;  // error = consistency check failure
  }

  //if (td->dword0 & 0x8) return 1; // error = reserved bits in dword0 set
  // other error checks here

  // find device address
  bx_bool at_least_one = 0;
  struct USB_DEVICE *dev = NULL;
  for (i=0; i<USB_CUR_DEVS; i++) {
    if (BX_USB_THIS hub[0].device[i].connect_status) at_least_one = 1;
    if ((BX_USB_THIS hub[0].device[i].address == addr) && BX_USB_THIS hub[0].device[i].connect_status) {
      dev = &BX_USB_THIS hub[0].device[i];
      break;
    }
  }
  if (!at_least_one) {
    BX_USB_THIS set_status(td, 1, 0, 0, 0, (pid==TOKEN_SETUP)?1:0, 0, 0x007); // an 8 byte packet was received, but stalled
    return 1;
  }
  if (dev == NULL) {
    if ((pid == TOKEN_OUT) && (maxlen == 0x7FF) && (addr == 0)) {
      // This is the "keep awake" packet that Windows sends once a schedule cycle.
      // For now, let it pass through to the code below.
    } else {
      BX_PANIC(("Device not found for addr: %i", addr));
      BX_USB_THIS set_status(td, 1, 0, 0, 0, (pid==TOKEN_SETUP)?1:0, 0, 0x007); // an 8 byte packet was received, but stalled
      return 1;  // device not found
    }
  }

  // the device should remain in a stall state until the next setup packet is recieved
  // For some reason, this doesn't work yet.
  //if (dev && dev->in_stall && (pid != TOKEN_SETUP))
  //  return FALSE;

  maxlen++;
  maxlen &= 0x7FF;
  if (dev) {
    if (maxlen > dev->function.device_descr.max_packet_size)
      maxlen = dev->function.device_descr.max_packet_size;
  } else
    maxlen = 0;

  // parse and get command
  Bit16u cnt;
  switch (pid) {
    case TOKEN_IN:   // Data came from HC to Host
      // if an interrupt in / bulk in, do the protocol packet.
      if (endpt > 0) {
        fnd = 0;
        for (i=0; (i<dev->function.device_config[dev->config].interfaces) && !fnd; i++) {
          for (j=0; j<dev->function.device_config[dev->config].Interface[i].num_endpts; j++) {
            if (endpt == (dev->function.device_config[dev->config].Interface[i].endpts[j].endpt & 0x7F)) {
              protocol = dev->function.device_config[dev->config].Interface[i].protocol;
              fnd = 1;
              break;
            }
          }
        }
        if (fnd) {
          cnt = dev->function.device_config[dev->config].Interface[dev->Interface].endpts[0].max_size;
          switch (protocol) {
            case 1:  // keypad

              memcpy(device_buffer, BX_USB_THIS key_pad_packet, 8);

              DEV_MEM_WRITE_PHYSICAL(td->dword3, cnt, device_buffer);
              BX_USB_THIS set_status(td, 0, 0, 0, 0, 0, 0, cnt-1);
              break;

            case 2:  // mouse
              if (!BX_USB_THIS mouse_x && !BX_USB_THIS mouse_y) {
                // if there's no new movement, handle delayed one
                BX_USB_THIS usb_mouse_enq(0, 0, BX_USB_THIS mouse_z, BX_USB_THIS b_state);
              }
              device_buffer[0] = (Bit8u) BX_USB_THIS b_state;
              device_buffer[1] = (Bit8s) BX_USB_THIS mouse_x;
              device_buffer[2] = (Bit8s) BX_USB_THIS mouse_y;
              device_buffer[3] = (Bit8s) BX_USB_THIS mouse_z;  // if wheel mouse
              BX_USB_THIS b_state = 0;
              BX_USB_THIS mouse_x = 0;
              BX_USB_THIS mouse_y = 0;
              BX_USB_THIS mouse_z = 0;
              DEV_MEM_WRITE_PHYSICAL(td->dword3, cnt, device_buffer);
              BX_USB_THIS set_status(td, 0, 0, 0, 0, 0, 0, cnt-1);
              break;

            case 0x50: // USB Mass Storage ????
              Bit8u bulk_int_packet[1024];
              if (flash_stick(bulk_int_packet, maxlen, 0)) {

              } else {

              }
              break;

            default:
              BX_PANIC(("Unknown/unsupported endpt protocol issued an Interrupt In / Bulk packet.  protocol %i", protocol));
          }       
        } else {
          BX_PANIC(("Unknown endpt issued an Interrupt In / Bulk packet.  ID 0x%04x Interface %i  endpt %i", i, endpt, dev->function.device_descr.vendorid));
        }
      } else {
        BX_DEBUG(("TOKEN_IN: len = %i", maxlen));
        BX_DEBUG(("          address = 0x%08X, Depth = %i, QH = %i, Terminate = %i\n"
                  "                              spd=%i, c_err=%i, LS=%i, IOS=%i, IOC=%i, status=0x%02X, act_len=0x%03X\n"
                  "                              max_len=0x%03X, D=%i, EndPt=%i, addr=%i, PID=0x%02X\n"
                  "                              buffer address = 0x%08X",
        td->dword0 & ~0x0000000F, (td->dword0 & 0x00000004)?1:0, (td->dword0 & 0x00000002)?1:0, (td->dword0 & 0x00000001)?1:0,
        (td->dword1 & 0x20000000)?1:0, (td->dword1 & 0x18000000)>>27, (td->dword1 & 0x04000000)?1:0, (td->dword1 & 0x02000000)?1:0, (td->dword1 & 0x01000000)?1:0, (td->dword1 & 0x00FF0000)>>16, td->dword1 & 0x000007FF,
        (td->dword2 & 0xFFE00000)>>21, (td->dword2 & 0x00080000)?1:0, (td->dword2 & 0x00078000)>>15, (td->dword2 & 0x00007F00)>>8, td->dword2 & 0x000000FF,
        td->dword3));
        // After a set address, the Host will send an empty IN packet for the status stage.
        //  The address on the IN packet will still be zero (0).
        //  So we need to set the address for this device *after* the in packet.
        if (maxlen == 0) {
          BX_USB_THIS set_status(td, 0, 0, 0, 0, 0, 0, 0x7FF);
          if (BX_USB_THIS set_address_stk && (addr == 0)) {
            BX_USB_THIS set_address_stk--;
            dev->address = BX_USB_THIS set_address[BX_USB_THIS set_address_stk];
            dev->state = STATE_ADDRESS;
          }
          break;
        }

        if (dev->function.in_cnt > 0) {
          bx_gui->statusbar_setitem(BX_USB_THIS hub[0].statusbar_id[0], 1);
          cnt = (dev->function.in_cnt < maxlen) ? dev->function.in_cnt : maxlen;
          DEV_MEM_WRITE_PHYSICAL(td->dword3, cnt, dev->function.in);
          dump_packet(dev->function.in, cnt);
          dev->function.in += cnt;
          dev->function.in_cnt -= cnt;
          BX_USB_THIS set_status(td, 0, 0, 0, 0, 0, 0, cnt-1);
        } else
          BX_USB_THIS set_status(td, 0, 0, 0, 0, 0, 0, 0x7FF);
        bx_gui->statusbar_setitem(BX_USB_THIS hub[0].statusbar_id[0], 0);
      }
      break;
    case TOKEN_OUT: // data should go from Host to HC
      // (remember that we will get an out once in a while with dev==NULL)
      if ((addr > 0) && (endpt > 0)) {
        fnd = 0;
        for (i=0; (i<dev->function.device_config[dev->config].interfaces) && !fnd; i++) {
          for (j=0; j<dev->function.device_config[dev->config].Interface[i].num_endpts; j++) {
            if (endpt == (dev->function.device_config[dev->config].Interface[i].endpts[j].endpt & 0x7F)) {
              protocol = dev->function.device_config[dev->config].Interface[i].protocol;
              fnd = 1;
              break;
            }
          }
        }
        if (fnd) {
          // Read in the packet
          Bit8u bulk_int_packet[1024];
          DEV_MEM_READ_PHYSICAL(td->dword3, maxlen, bulk_int_packet);

          // now do the task
          switch (protocol) {
            case 1:  // keypad
              BX_PANIC(("Keyboard received and OUT packet!"));
              break;

            case 2:  // mouse
              BX_PANIC(("Mouse received and OUT packet!"));
              break;

            case 0x50: // USB Mass Storage ????
              if (flash_stick(bulk_int_packet, maxlen, 1)) {

              } else {

              }
              break;
              
            default:
              BX_PANIC(("Unknown/unsupported endpt protocol issued an Interrupt Out / Bulk packet.  protocol %i", protocol));
          }       
        } else {
          BX_PANIC(("Unknown endpt issued an Interrupt Out / Bulk packet.  ID 0x%04x Interface %i  endpt %i", i, endpt, dev->function.device_descr.vendorid));
        }
      } else {
        BX_DEBUG(("TOKEN_OUT: maxlen = 0x%03X", maxlen));
        BX_DEBUG(("          address = 0x%08X, Depth = %i, QH = %i, Terminate = %i\n"
                  "                              spd=%i, c_err=%i, LS=%i, IOS=%i, IOC=%i, status=0x%02X, act_len=0x%03X\n"
                  "                              max_len=0x%03X, D=%i, EndPt=%i, addr=%i, PID=0x%02X\n"
                  "                              buffer address = 0x%08X",
        td->dword0 & ~0x0000000F, (td->dword0 & 0x00000004)?1:0, (td->dword0 & 0x00000002)?1:0, (td->dword0 & 0x00000001)?1:0,
        (td->dword1 & 0x20000000)?1:0, (td->dword1 & 0x18000000)>>27, (td->dword1 & 0x04000000)?1:0, (td->dword1 & 0x02000000)?1:0, (td->dword1 & 0x01000000)?1:0, (td->dword1 & 0x00FF0000)>>16, td->dword1 & 0x000007FF,
        (td->dword2 & 0xFFE00000)>>21, (td->dword2 & 0x00080000)?1:0, (td->dword2 & 0x00078000)>>15, (td->dword2 & 0x00007F00)>>8, td->dword2 & 0x000000FF,
        td->dword3));

        if (maxlen == 0) {
          BX_USB_THIS set_status(td, 0, 0, 0, 0, 0, 0, 0x7FF);
        } else {
          BX_ERROR(("Got to this position and shouldn't have...000"));
        }
      }
      bx_gui->statusbar_setitem(BX_USB_THIS hub[0].statusbar_id[0], 0);
      break;
    case TOKEN_SETUP:
      if (dev) dev->in_stall = 0;

      // as a debug check, stall on the first setup packet after boot up.
      if (dev && (dev->stall_once & 0x01) && !(dev->stall_once & 0x80)) {
        dev->stall_once |= 0x80;
        BX_USB_THIS set_status(td, 1, 0, 0, 0, 1, 0, 0x007);
        BX_INFO((" Stalling once one first setup packet after bootup"));
        break;
      }

      BX_DEBUG((" SETUP packet: %02X %02X %02X %02X %02X %02X %02X %02X", 
        data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]));
      switch (rp->request) {
        case GET_STATUS:
          switch (dev->state) {
            case STATE_DEFAULT:
              BX_USB_THIS set_status(td, 1, 0, 0, 0, (pid==TOKEN_SETUP)?1:0, 0, 0x007); // an 8 byte packet was received, but stalled
              BX_ERROR(("Request: GET_STATUS returned an error"));
              break;
            case STATE_ADDRESS:
              if (rp->index > 0) {
                BX_USB_THIS set_status(td, 1, 0, 0, 0, (pid==TOKEN_SETUP)?1:0, 0, 0x007); // an 8 byte packet was received, but stalled
                BX_ERROR(("Request: GET_STATUS returned an error"));
                break;
              } // else fall through
            case STATE_CONFIGURED:

              BX_PANIC(("Request: GET_STATUS not implemented yet"));

              // If the interface or endpoint does not exist, return error

              BX_DEBUG(("Request: GET_STATUS"));
              BX_USB_THIS set_status(td, 0, 0, 0, 0, 0, 0, 0x007); // an 8 byte packet was received
          }
          break;
        case CLEAR_FEATURE:
          switch (dev->state) {
            case STATE_DEFAULT:
              BX_USB_THIS set_status(td, 1, 0, 0, 0, (pid==TOKEN_SETUP)?1:0, 0, 0x007); // an 8 byte packet was received, but stalled
              BX_ERROR(("Request: CLEAR_FEATURE returned an error"));
              break;
            case STATE_ADDRESS:
              if (rp->index > 0) {
                BX_USB_THIS set_status(td, 1, 0, 0, 0, (pid==TOKEN_SETUP)?1:0, 0, 0x007); // an 8 byte packet was received, but stalled
                BX_ERROR(("Request: CLEAR_FEATURE returned an error"));
                break;
              } // else fall through
            case STATE_CONFIGURED:
              if (rp->length) {
                BX_USB_THIS set_status(td, 1, 0, 0, 0, (pid==TOKEN_SETUP)?1:0, 0, 0x007); // an 8 byte packet was received, but stalled
                BX_ERROR(("Request: CLEAR_FEATURE returned an error:  length > 0"));
                break;
              }
              // At this point, we don't implement features.  So until we do, there isn't anything
              //  to clear.  Just log an error and continue.
              BX_ERROR(("Request: CLEAR_FEATURE:  Not implemented yet."));
              BX_USB_THIS set_status(td, 0, 0, 0, 0, 0, 0, 0x007); // an 8 byte packet was received
              break;
          }
          break;
        case SET_FEATURE:
          switch (dev->state) {
            case STATE_DEFAULT:
              if (((rp->index >> 8) == SET_FEATURE_TEST_MODE) && (rp->request_type == 0) && ((rp->index & 0xFF) == 0)) {
                BX_DEBUG(("Request: SET_FEATURE in TEST MODE"));
              } else {
                BX_USB_THIS set_status(td, 1, 0, 0, 0, (pid==TOKEN_SETUP)?1:0, 0, 0x007); // an 8 byte packet was received, but stalled
                BX_ERROR(("Request: SET_FEATURE returned an error"));
              }
              break;
            case STATE_ADDRESS:
              if ((rp->index & 0xFF) > 0) {
                BX_USB_THIS set_status(td, 1, 0, 0, 0, (pid==TOKEN_SETUP)?1:0, 0, 0x007); // an 8 byte packet was received, but stalled
                BX_ERROR(("Request: SET_FEATURE returned an error"));
                break;
              } // else fall through
            case STATE_CONFIGURED:

              BX_PANIC(("Request: SET_FEATURE not implemented yet"));

              BX_DEBUG(("Request: SET_FEATURE"));
              BX_USB_THIS set_status(td, 0, 0, 0, 0, 0, 0, 0x007); // an 8 byte packet was received
          }
          break;
        case SET_ADDRESS:
          BX_DEBUG(("Request: SET_ADDRESS: %04x", rp->value));
          BX_DEBUG(("          address = 0x%08X, Depth = %i, QH = %i, Terminate = %i\n"
                    "                              spd=%i, c_err=%i, LS=%i, IOS=%i, IOC=%i, status=0x%02X, act_len=0x%03X\n"
                    "                              max_len=0x%03X, D=%i, EndPt=%i, addr=%i, PID=0x%02X\n"
                    "                              buffer address = 0x%08X",
          td->dword0 & ~0x0000000F, (td->dword0 & 0x00000004)?1:0, (td->dword0 & 0x00000002)?1:0, (td->dword0 & 0x00000001)?1:0,
          (td->dword1 & 0x20000000)?1:0, (td->dword1 & 0x18000000)>>27, (td->dword1 & 0x04000000)?1:0, (td->dword1 & 0x02000000)?1:0, (td->dword1 & 0x01000000)?1:0, (td->dword1 & 0x00FF0000)>>16, td->dword1 & 0x000007FF,
          (td->dword2 & 0xFFE00000)>>21, (td->dword2 & 0x00080000)?1:0, (td->dword2 & 0x00078000)>>15, (td->dword2 & 0x00007F00)>>8, td->dword2 & 0x000000FF,
          td->dword3));
          if ((rp->value > 127) || rp->index || rp->length) {
            BX_USB_THIS set_status(td, 1, 0, 0, 0, (pid==TOKEN_SETUP)?1:0, 0, 0x007); // an 8 byte packet was received, but stalled
            BX_ERROR(("Request: SET_ADDRESS returned an error"));
          } else {
            switch (dev->state) {
              case STATE_DEFAULT:
                BX_USB_THIS set_address[BX_USB_THIS set_address_stk] = (Bit8u) rp->value;
                BX_USB_THIS set_address_stk++;
                BX_USB_THIS set_status(td, 0, 0, 0, 0, 0, 0, 0x007);
                break;
              case STATE_ADDRESS:
                if (rp->value > 0) {
                  BX_USB_THIS set_address[BX_USB_THIS set_address_stk] = (Bit8u) rp->value;
                  BX_USB_THIS set_address_stk++;
                } else {
                  BX_USB_THIS set_address[BX_USB_THIS set_address_stk] = 0;
                  BX_USB_THIS set_address_stk++;
                  dev->state = STATE_DEFAULT;
                }
                BX_USB_THIS set_status(td, 0, 0, 0, 0, 0, 0, 0x007);
                break;
              case STATE_CONFIGURED:
                BX_USB_THIS set_status(td, 1, 0, 0, 0, (pid==TOKEN_SETUP)?1:0, 0, 0x007); // an 8 byte packet was received, but stalled
                BX_ERROR(("Request: SET_ADDRESS returned an error"));
            }
          }
          break;
        case GET_DESCRIPTOR:
          BX_DEBUG(("Request: GET_DESCRIPTOR: 0x%02x  0x%02x  %i", rp->value >> 8, rp->value & 0xFF, rp->length));
          BX_DEBUG(("          address = 0x%08X, Depth = %i, QH = %i, Terminate = %i\n"
                    "                              spd=%i, c_err=%i, LS=%i, IOS=%i, IOC=%i, status=0x%02X, act_len=0x%03X\n"
                    "                              max_len=0x%03X, D=%i, EndPt=%i, addr=%i, PID=0x%02X\n"
                    "                              buffer address = 0x%08X",
          td->dword0 & ~0x0000000F, (td->dword0 & 0x00000004)?1:0, (td->dword0 & 0x00000002)?1:0, (td->dword0 & 0x00000001)?1:0,
          (td->dword1 & 0x20000000)?1:0, (td->dword1 & 0x18000000)>>27, (td->dword1 & 0x04000000)?1:0, (td->dword1 & 0x02000000)?1:0, (td->dword1 & 0x01000000)?1:0, (td->dword1 & 0x00FF0000)>>16, td->dword1 & 0x000007FF,
          (td->dword2 & 0xFFE00000)>>21, (td->dword2 & 0x00080000)?1:0, (td->dword2 & 0x00078000)>>15, (td->dword2 & 0x00007F00)>>8, td->dword2 & 0x000000FF,
          td->dword3));

          switch (dev->state) {
            case STATE_DEFAULT:
            case STATE_ADDRESS:
            case STATE_CONFIGURED:
              unsigned ret;
              ret = BX_USB_THIS GetDescriptor(dev, rp);
              BX_USB_THIS set_status(td, ret, 0, 0, 0, (ret && pid==TOKEN_SETUP)?1:0, 0, 0x007);
          }
          break;
        case SET_DESCRIPTOR:
          switch (dev->state) {
            case STATE_DEFAULT:
              BX_ERROR(("Request: SET_DESCRIPTOR returned an error"));
              BX_USB_THIS set_status(td, 1, 0, 0, 0, (pid==TOKEN_SETUP)?1:0, 0, 0x007); // an 8 byte packet was received, but stalled
              break;
            case STATE_ADDRESS:
            case STATE_CONFIGURED:
              
              BX_PANIC(("Request: SET_DESCRIPTOR not implemented yet"));

              //dev->descriptor = (Bit8u) rp->value >> 8;

              BX_DEBUG(("Request: SET_DESCRIPTOR: %02x", rp->value >> 8));
              BX_USB_THIS set_status(td, 0, 0, 0, 0, 0, 0, 0x007); // an 8 byte packet was received
          }
          break;
        case GET_CONFIGURATION:
          BX_DEBUG(("Request: GET_CONFIGURATION"));
          BX_DEBUG(("          address = 0x%08X, Depth = %i, QH = %i, Terminate = %i\n"
                    "                              spd=%i, c_err=%i, LS=%i, IOS=%i, IOC=%i, status=0x%02X, act_len=0x%03X\n"
                    "                              max_len=0x%03X, D=%i, EndPt=%i, addr=%i, PID=0x%02X\n"
                    "                              buffer address = 0x%08X",
          td->dword0 & ~0x0000000F, (td->dword0 & 0x00000004)?1:0, (td->dword0 & 0x00000002)?1:0, (td->dword0 & 0x00000001)?1:0,
          (td->dword1 & 0x20000000)?1:0, (td->dword1 & 0x18000000)>>27, (td->dword1 & 0x04000000)?1:0, (td->dword1 & 0x02000000)?1:0, (td->dword1 & 0x01000000)?1:0, (td->dword1 & 0x00FF0000)>>16, td->dword1 & 0x000007FF,
          (td->dword2 & 0xFFE00000)>>21, (td->dword2 & 0x00080000)?1:0, (td->dword2 & 0x00078000)>>15, (td->dword2 & 0x00007F00)>>8, td->dword2 & 0x000000FF,
          td->dword3));
          switch (dev->state) {
            case STATE_DEFAULT:
              BX_ERROR(("Request: GET_CONFIGURATION returned an error"));
              BX_USB_THIS set_status(td, 1, 0, 0, 0, (pid==TOKEN_SETUP)?1:0, 0, 0x007); // an 8 byte packet was received, but stalled
              break;
            case STATE_ADDRESS:
              // must return zero if in the address state
              *device_buffer = 0;
              break;
            case STATE_CONFIGURED:
              *device_buffer = dev->function.device_config[dev->config].config_val;
          }
          dev->function.in = device_buffer;
          dev->function.in_cnt = 1;
          BX_DEBUG(("Request: GET_CONFIGURATION"));
          BX_USB_THIS set_status(td, 0, 0, 0, 0, 0, 0, 0x007); // an 8 byte packet was received
          break;
        case SET_CONFIGURATION:
          switch (dev->state) {
            case STATE_DEFAULT:
              BX_USB_THIS set_status(td, 1, 0, 0, 0, (pid==TOKEN_SETUP)?1:0, 0, 0x007); // an 8 byte packet was received, but stalled
              BX_ERROR(("Request: SET_CONFIGURATION: returned an error"));
              break;
            case STATE_CONFIGURED:
              if ((rp->value & 0xFF) == 0) {
                dev->state = STATE_DEFAULT;
                BX_DEBUG(("Request: SET_CONFIGURATION = 0 (set to default state)"));
                BX_USB_THIS set_status(td, 0, 0, 0, 0, 0, 0, 0x007); // an 8 byte packet was received
                break;
              } // else fall through ????
            case STATE_ADDRESS:
              dev->config = 0xFF;
              unsigned configs;
              for (configs=0; configs<dev->function.configs; configs++) {
                if ((rp->value & 0xFF) == dev->function.device_config[configs].config_val) {
                  dev->config = configs;
                  dev->state = STATE_CONFIGURED;
                  break;
                }
              }
              if (dev->config == 0xFF) {
                dev->config = 0;
                BX_USB_THIS set_status(td, 1, 0, 0, 0, (pid==TOKEN_SETUP)?1:0, 0, 0x007); // an 8 byte packet was received, but stalled
                BX_ERROR(("Request: SET_CONFIGURATION: returned an error"));
              } else {
                BX_USB_THIS set_status(td, 0, 0, 0, 0, 0, 0, 0x007); // an 8 byte packet was received
                BX_DEBUG(("Request: SET_CONFIGURATION: %02x", rp->value));
              }
              break;
          }
          break;
        case GET_INTERFACE:
          switch (dev->state) {
            case STATE_DEFAULT:
            case STATE_ADDRESS:
              BX_USB_THIS set_status(td, 1, 0, 0, 0, (pid==TOKEN_SETUP)?1:0, 0, 0x007); // an 8 byte packet was received, but stalled
              BX_ERROR(("Request: GET_INTERFACE returned and error: not in configured state"));
              break;
            case STATE_CONFIGURED:
              if ((unsigned)(rp->index + 1) > dev->function.device_config[dev->config].interfaces) {
                BX_PANIC(("GET_INTERFACE: wanted interface number is greater than interface count."));
                BX_USB_THIS set_status(td, 1, 0, 0, 0, (pid==TOKEN_SETUP)?1:0, 0, 0x007); // an 8 byte packet was received, but stalled
              }
              // The USB specs say that I can return a STALL if only one interface is used. (ie, no alternate)
              // However, Win98SE doesn't like it at all....
              //if (dev->function.device_config[dev->config].interfaces > 1) {
                *device_buffer = dev->function.device_config[dev->config].Interface[rp->value & 0xFF].alternate;
                dev->function.in = device_buffer;
                dev->function.in_cnt = 1;
                if (dev->function.in_cnt > rp->length)
                  dev->function.in_cnt = rp->length;
                BX_DEBUG(("Request: GET_INTERFACE  %02X  %02X  %02X", rp->value >> 8, rp->value & 0xFF, rp->length));
                BX_USB_THIS set_status(td, 0, 0, 0, 0, 0, 0, 0x007); // an 8 byte packet was received
              //} else {
              //  BX_DEBUG(("Request: GET_INTERFACE  %02X  %02X  %02X (Only 1 interface, returning STALL)", rp->value >> 8, rp->value & 0xFF, rp->length));
              //  BX_USB_THIS set_status(td, 1, 0, 0, 0, (pid==TOKEN_SETUP)?1:0, 0, 0x007); // an 8 byte packet was received, but stalled
              //}
              break;
          }
          break;
        case SET_INTERFACE:
          switch (dev->state) {
            case STATE_DEFAULT:
            case STATE_ADDRESS:
              BX_ERROR(("Request: SET_INTERFACE returned and error: not in configured state"));
              BX_USB_THIS set_status(td, 1, 0, 0, 0, (pid==TOKEN_SETUP)?1:0, 0, 0x007); // an 8 byte packet was received, but stalled
              break;
            case STATE_CONFIGURED:
              if ((unsigned)(rp->index + 1) > dev->function.device_config[dev->config].interfaces) {
                BX_PANIC(("SET_INTERFACE: wanted interface number is greater than interface count."));
                BX_USB_THIS set_status(td, 1, 0, 0, 0, (pid==TOKEN_SETUP)?1:0, 0, 0x007); // an 8 byte packet was received, but stalled
              }
              dev->Interface = (Bit8u) rp->index;
              dev->alt_interface = (Bit8u) rp->value;
              BX_DEBUG(("Request: SET_INTERFACE: %02x (alt %02x)", rp->index, rp->value));
              BX_USB_THIS set_status(td, 0, 0, 0, 0, 0, 0, 0x007); // an 8 byte packet was received
          }
          break;
        case SYNCH_FRAME:
          switch (dev->state) {
            case STATE_DEFAULT:
            case STATE_ADDRESS:
              BX_ERROR(("Request: SYNCH_FRAME returned and error: not in configured state"));
              BX_USB_THIS set_status(td, 1, 0, 0, 0, (pid==TOKEN_SETUP)?1:0, 0, 0x007); // an 8 byte packet was received, but stalled
              break;
            case STATE_CONFIGURED:

              BX_PANIC(("Request: SYNCH_FRAME not implemented yet"));

              BX_DEBUG(("Request: SYNCH_FRAME"));
              BX_USB_THIS set_status(td, 0, 0, 0, 0, 0, 0, 0x007); // an 8 byte packet was received
          }
          break;
        case 0xFE:
          // The Kingston driver sends the control packet.
          // At the moment, I haven't a clue to what it is.
          //   are we ready.???
          
          dev->scratch = 0;
          dev->function.in = (Bit8u *) &dev->scratch;
          dev->function.in_cnt = 1;
          
          BX_USB_THIS set_status(td, 1, 0, 0, 0, (pid==TOKEN_SETUP)?1:0, 0, 0x007); // an 8 byte packet was received, but stalled
          break;
        default:
          BX_PANIC((" **** illegal or unknown REQUEST sent to Host Controller:  %02x", data[1]));
      }
      break;
    default:
      BX_PANIC(("illegal PID sent to Host Controller:  %02x", pid));
  }
  // did it stall?, then set the "in_stall" state
  if (td->dword1 & (1<<22))
    if (dev) dev->in_stall = 1;
  
  return 1;
}

unsigned bx_pciusb_c::GetDescriptor(struct USB_DEVICE *dev, struct REQUEST_PACKET *packet)
{
  Bit8u *p = device_buffer;
  unsigned i, j, fnd, ret = 0; // ret = 0 = no error

  BX_DEBUG(("GET DESCRIPTOR  0x%02X 0x%02X  value=0x%04X  len=%i index=%i", 
    packet->request, packet->request_type, packet->value, packet->length, packet->index));
    
  switch (packet->value >> 8) {
    case DEVICE:
      dev->function.in = (Bit8u *) &dev->function.device_descr;
      dev->function.in_cnt = dev->function.device_descr.len;
      ret = 0;
      break;
    case CONFIG:
      memcpy(p, &dev->function.device_config[dev->config], 9); p += 9;  // config descriptor
      for (i=0; i<dev->function.device_config[dev->config].interfaces; i++) {
        memcpy(p, &dev->function.device_config[dev->config].Interface[i], 9); p += 9;
        for (j=0; j<dev->function.device_config[dev->config].Interface[i].num_endpts; j++) {
          memcpy(p, &dev->function.device_config[dev->config].Interface[i].endpts[j], 7); p += 7;
        }
        memcpy(p, &dev->function.device_config[dev->config].Interface[i].dev_hid_descript, 6); p += 6;
        for (j=0; j<dev->function.device_config[dev->config].Interface[i].dev_hid_descript.num_descriptors; j++) {
          memcpy(p, &dev->function.device_config[dev->config].Interface[i].dev_hid_descript.descriptor[j], 3); p += 3;
        }
      }
      dev->function.in = device_buffer;
      dev->function.in_cnt = (p - device_buffer);
      ret = 0;
      break;
    case STRING:
      switch (packet->value & 0xFF) {
        case 0: // string descriptor table
          dev->function.in = (Bit8u *) &dev->function.str_descriptor;
          dev->function.in_cnt = dev->function.str_descriptor.size;
          ret = 0;
          break;
        case 1: case 2: case 3:
        case 4: case 5: case 6:
          dev->function.in = (Bit8u *) &dev->function.string[(packet->value & 0xFF)-1];
          dev->function.in_cnt = dev->function.string[(packet->value & 0xFF)-1].size;
          break;
          ret = 0;
        default:
          BX_ERROR(("STRING:  %i", packet->value & 0xFF));
          ret = 1;
      }
      break;
    case INTERFACE:
      BX_PANIC(("GET_DESCRIPTOR: INTERFACE not implemented yet."));
      ret = 1;
      break;
    case ENDPOINT:
      BX_PANIC(("GET_DESCRIPTOR: ENDPOINT not implemented yet."));
      ret = 1;
      break;
    case DEVICE_QUALIFIER:
      BX_PANIC(("GET_DESCRIPTOR: DEVICE_QUALIFIER not implemented yet."));
      ret = 1;
      break;
    case OTHER_SPEED_CONFIG:
      BX_PANIC(("GET_DESCRIPTOR: OTHER_SPEED_CONFIG not implemented yet."));
      ret = 1;
      break;
    case INTERFACE_POWER:
      BX_PANIC(("GET_DESCRIPTOR: INTERFACE_POWER not implemented yet."));
      ret = 1;
      break;
    case 0x21:  // (HID device descriptor)
      dev->function.in = (Bit8u *) &dev->function.device_config[dev->config].Interface[packet->index-1].dev_hid_descript;
      dev->function.in_cnt = dev->function.device_config[dev->config].Interface[packet->index-1].dev_hid_descript.size;
      ret = 0;
      break;
    case 0x22:  // (HID report descriptor)
      // Win98SE passes packet->index as endpoint number (packet->request_type = 0x82)
      // SuSE Linux passes packet->index as interface number (packet->request_type = 0x81)
      switch (packet->request_type & 0x1F) {
        case 2: // endpoint
          fnd = 0;
          for (i=0; (i<dev->function.device_config[dev->config].interfaces) && !fnd; i++) {
            for (j=0; (j<dev->function.device_config[dev->config].Interface[i].num_endpts) && !fnd; j++) {
              if ((dev->function.device_config[dev->config].Interface[i].endpts[j].endpt & ~0x80) == packet->index) {
                packet->index = i;
                fnd = 1;
              }
            }
          }
          if (!fnd) {
            BX_ERROR(("Get Descriptor:  HID Report:  index = endpint.  did not find endpoint"));
            ret = 1;
            break;
          } // else fall through to interface
        case 1: // interface
          for (i=0; i<dev->function.device_config[dev->config].Interface[packet->index].dev_hid_descript.num_descriptors; i++) {
            if (dev->function.device_config[dev->config].Interface[packet->index].dev_hid_descript.descriptor[i].type == 0x22) {
              dev->function.in = dev->function.device_config[dev->config].Interface[packet->index].dev_hid_descript.descriptor[i].dev_hid_descript_report;
              dev->function.in_cnt = dev->function.device_config[dev->config].Interface[packet->index].dev_hid_descript.descriptor[i].len;
              break;
            }
          }
          ret = 0;
          break;
        case 0:  // device
        case 3:  // other
        default: // reserved
          BX_ERROR(("Get Descriptor:  HID Report:  index = reserved.  Should be Interface or Endpoint"));
          ret = 1;
          break;
      }
      break;
    default:
      BX_PANIC((" **** illegal or unknown GET_DESCRIPTOR::DEVICE sent to Host Controller:  %02x", packet->value >> 8));
      return 1;
  }
  if (dev->function.in_cnt > packet->length)
    dev->function.in_cnt = packet->length;
  
  return ret;
}

// If the request fails, set the stall bit ????
void bx_pciusb_c::set_status(struct TD *td, bx_bool stalled, bx_bool data_buffer_error, bx_bool babble, 
                             bx_bool nak, bx_bool crc_time_out, bx_bool bitstuff_error, Bit16u act_len)
{
  // clear out the bits we can modify and/or want zero
  td->dword1 &= 0xDF00F800;

  // now set the bits according to the passed param's
  td->dword1 |= stalled           ? (1<<22) : 0; // stalled
  td->dword1 |= data_buffer_error ? (1<<21) : 0; // data buffer error
  td->dword1 |= babble            ? (1<<20) : 0; // babble
  td->dword1 |= nak               ? (1<<19) : 0; // nak
  td->dword1 |= crc_time_out      ? (1<<18) : 0; // crc/timeout
  td->dword1 |= bitstuff_error    ? (1<<17) : 0; // bitstuff error
  td->dword1 |= (act_len & 0x7FF);               // actual length
  if (stalled || data_buffer_error || babble || nak || crc_time_out || bitstuff_error)
    td->dword1 &= ~((1<<28) | (1<<27));  // clear the c_err field in there was an error
}
 
// pci configuration space read callback handler
Bit32u bx_pciusb_c::pci_read_handler(Bit8u address, unsigned io_len)
{
  Bit32u value = 0;

  if (io_len > 4 || io_len == 0) {
    BX_ERROR(("Experimental USB PCI read register 0x%02x, len=%u !",
             (unsigned) address, (unsigned) io_len));
    return 0xffffffff;
  }

  const char* pszName = "                  ";
  switch (address) {
    case 0x00: if (io_len == 2) {
                 pszName = "(vendor id)       ";
               } else if (io_len == 4) {
                 pszName = "(vendor + device) ";
               }
      break;
    case 0x04: if (io_len == 2) {
                 pszName = "(command)         ";
               } else if (io_len == 4) {
                 pszName = "(command+status)  ";
               }
      break;
    case 0x08: if (io_len == 1) {
                 pszName = "(revision id)     ";
               } else if (io_len == 4) {
                 pszName = "(rev.+class code) ";
               }
      break;
    case 0x0c: pszName = "(cache line size) "; break;
    case 0x20: pszName = "(base address)    "; break;
    case 0x28: pszName = "(cardbus cis)     "; break;
    case 0x2c: pszName = "(subsys. vendor+) "; break;
    case 0x30: pszName = "(rom base)        "; break;
    case 0x3c: pszName = "(interrupt line+) "; break;
    case 0x3d: pszName = "(interrupt pin)   "; break;
  }

  // This odd code is to display only what bytes actually were read.
  char szTmp[9];
  char szTmp2[3];
  szTmp[0] = '\0';
  szTmp2[0] = '\0';
  for (unsigned i=0; i<io_len; i++) {
    value |= (BX_USB_THIS hub[0].pci_conf[address+i] << (i*8));
    sprintf(szTmp2, "%02x", (BX_USB_THIS hub[0].pci_conf[address+i]));
    strrev(szTmp2);
    strcat(szTmp, szTmp2);
  }
  strrev(szTmp);
  BX_DEBUG(("USB PCI read  register 0x%02x %svalue 0x%s", address, pszName, szTmp));
  return value;
}


// pci configuration space write callback handler
void bx_pciusb_c::pci_write_handler(Bit8u address, Bit32u value, unsigned io_len)
{
  Bit8u value8, oldval;
  bx_bool baseaddr_change = 0;

  if (((address >= 0x10) && (address < 0x20)) ||
      ((address > 0x23) && (address < 0x34)))
    return;

  // This odd code is to display only what bytes actually were written.
  char szTmp[9];
  char szTmp2[3];
  szTmp[0] = '\0';
  szTmp2[0] = '\0';
  if (io_len <= 4) {
    for (unsigned i=0; i<io_len; i++) {
      value8 = (value >> (i*8)) & 0xFF;
      oldval = BX_USB_THIS hub[0].pci_conf[address+i];
      switch (address+i) {
        case 0x04:
          value8 &= 0x05;
          BX_USB_THIS hub[0].pci_conf[address+i] = value8;
          sprintf(szTmp2, "%02x", value8);
          break;
        case 0x3d: //
        case 0x3e: //
        case 0x3f: //
        case 0x05: // disallowing write to command hi-byte
        case 0x06: // disallowing write to status lo-byte (is that expected?)
          strcpy(szTmp2, "..");
          break;
        case 0x3c:
          if (value8 != oldval) {
            BX_INFO(("new irq line = %d", value8));
            BX_USB_THIS hub[0].pci_conf[address+i] = value8;
          }
          sprintf(szTmp2, "%02x", value8);
          break;
        case 0x20:
          value8 = (value8 & 0xfc) | 0x01;
        case 0x21:
        case 0x22:
        case 0x23:
          baseaddr_change |= (value8 != oldval);
        default:
          BX_USB_THIS hub[0].pci_conf[address+i] = value8;
          sprintf(szTmp2, "%02x", value8);
      }
      strrev(szTmp2);
      strcat(szTmp, szTmp2);
    }
    if (baseaddr_change) {
      if (DEV_pci_set_base_io(BX_USB_THIS_PTR, read_handler, write_handler,
                             &BX_USB_THIS hub[0].base_ioaddr,
                             &BX_USB_THIS hub[0].pci_conf[0x20],
                             32, &usb_iomask[0], "USB Hub #1")) {
         BX_INFO(("new base address: 0x%04x", BX_USB_THIS hub[0].base_ioaddr));
      }
    }
  }
  strrev(szTmp);
  BX_DEBUG(("USB PCI write register 0x%02x                   value 0x%s", address, szTmp));
}

void bx_pciusb_c::usb_mouse_enable(bx_bool enable)
{
  BX_USB_THIS mouse_connected = enable;
  if (BX_USB_THIS last_connect != enable) {
    for (int i=0; i<USB_NUM_PORTS; i++)
      usb_set_connect_status(i, USB_DEV_TYPE_MOUSE, enable);
    BX_USB_THIS last_connect = enable;
  }
}

void bx_pciusb_c::usb_set_connect_status(Bit8u port, int type, bx_bool connected)
{
  if (BX_USB_THIS hub[0].usb_port[port].device_num > -1) {
    if (BX_USB_THIS hub[0].device[BX_USB_THIS hub[0].usb_port[port].device_num].dev_type == type) {
      if (connected) {
        if (!BX_USB_THIS hub[0].device[BX_USB_THIS hub[0].usb_port[port].device_num].connect_status) {
          BX_USB_THIS hub[0].device[BX_USB_THIS hub[0].usb_port[port].device_num].state = STATE_DEFAULT;
          BX_USB_THIS hub[0].device[BX_USB_THIS hub[0].usb_port[port].device_num].address = 0;
          BX_USB_THIS hub[0].device[BX_USB_THIS hub[0].usb_port[port].device_num].alt_interface = 0;
          BX_USB_THIS hub[0].device[BX_USB_THIS hub[0].usb_port[port].device_num].Interface = 0;
          BX_USB_THIS hub[0].device[BX_USB_THIS hub[0].usb_port[port].device_num].config = 0;
          BX_USB_THIS hub[0].usb_port[port].low_speed = 
            BX_USB_THIS hub[0].device[BX_USB_THIS hub[0].usb_port[port].device_num].low_speed;
        }
        if (BX_USB_THIS hub[0].usb_port[port].low_speed) {
          BX_USB_THIS hub[0].usb_port[port].line_dminus = 1;  //  dminus=1 & dplus=0 = low speed  (at idle time)
          BX_USB_THIS hub[0].usb_port[port].line_dplus = 0;   //  dminus=0 & dplus=1 = high speed (at idle time)
        } else {
          BX_USB_THIS hub[0].usb_port[port].line_dminus = 0;  //  dminus=1 & dplus=0 = low speed  (at idle time)
          BX_USB_THIS hub[0].usb_port[port].line_dplus = 1;   //  dminus=0 & dplus=1 = high speed (at idle time)
        }
        BX_USB_THIS hub[0].usb_port[port].status = 1;       // 
        BX_USB_THIS hub[0].usb_port[port].connect_changed = 1;
        BX_USB_THIS hub[0].usb_port[port].able_changed = 1;
        BX_USB_THIS hub[0].device[BX_USB_THIS hub[0].usb_port[port].device_num].connect_status = 1;

        // if in suspend state, signal resume
        if (BX_USB_THIS hub[0].usb_command.suspend) {
          BX_USB_THIS hub[0].usb_port[port].resume = 1;
          BX_USB_THIS hub[0].usb_status.resume = 1;
          if (BX_USB_THIS hub[0].usb_enable.resume) {
            BX_USB_THIS hub[0].usb_status.interrupt = 1;
            set_irq_level(1);
          }
        }

        // If the type is a flash stick, we need to close, then re open the file connected with it.
        // **** We current assume the flash stick is on port1 ****
        /*
        if (type == USB_DEV_TYPE_FLASH) {
          if (BX_USB_THIS hub[0].device[BX_USB_THIS hub[0].usb_port[port].device_num].fd > -1) 
            ::close(BX_USB_THIS hub[0].device[BX_USB_THIS hub[0].usb_port[port].device_num].fd);
          BX_USB_THIS hub[0].device[BX_USB_THIS hub[0].usb_port[port].device_num].fd = 
            ::open(bx_options.usb[0].Opath1->getptr(), O_RDWR | O_BINARY);
          if (BX_USB_THIS hub[0].device[BX_USB_THIS hub[0].usb_port[port].device_num].fd < 0)
            BX_PANIC(("Could not open file for USB flash stick: %s", bx_options.usb[0].Opath1->getptr()));
        
          // look at size of image file to calculate disk geometry
          struct stat stat_buf;
          int ret = fstat(BX_USB_THIS hub[0].device[BX_USB_THIS hub[0].usb_port[port].device_num].fd, &stat_buf);
          if (ret) BX_PANIC(("fstat() returns error!"));
          if (stat_buf.st_size != (USB_FLASH_SIZE * 0x00100000))
            BX_PANIC(("size of disk image must be %i Megs", USB_FLASH_SIZE));
        }
        */
      } else {
        BX_USB_THIS hub[0].usb_port[port].status = 0;
        BX_USB_THIS hub[0].usb_port[port].connect_changed = 1;
        BX_USB_THIS hub[0].usb_port[port].enabled = 0;
        BX_USB_THIS hub[0].usb_port[port].able_changed = 1;
        BX_USB_THIS hub[0].usb_port[port].low_speed = 0;
        BX_USB_THIS hub[0].usb_port[port].line_dminus = 0;  //  dminus=1 & dplus=0 = low speed  (at idle time)
        BX_USB_THIS hub[0].usb_port[port].line_dplus = 0;   //  dminus=0 & dplus=1 = high speed (at idle time)
      }
    }
  }
}

void bx_pciusb_c::usb_mouse_enq(int delta_x, int delta_y, int delta_z, unsigned button_state)
{
  // scale down the motion
  if ( (delta_x < -1) || (delta_x > 1) )
    delta_x /= 2;
  if ( (delta_y < -1) || (delta_y > 1) )
    delta_y /= 2;

  if(delta_x>127) delta_x=127;
  if(delta_y>127) delta_y=127;
  if(delta_x<-128) delta_x=-128;
  if(delta_y<-128) delta_y=-128;

  BX_USB_THIS mouse_delayed_dx+=delta_x;
  BX_USB_THIS mouse_delayed_dy-=delta_y;

  if (BX_USB_THIS mouse_delayed_dx > 127) {
    delta_x = 127;
    BX_USB_THIS mouse_delayed_dx -= 127;
  } else if (BX_USB_THIS mouse_delayed_dx < -128) {
    delta_x = -128;
    BX_USB_THIS mouse_delayed_dx += 128;
  } else {
    delta_x = BX_USB_THIS mouse_delayed_dx;
    BX_USB_THIS mouse_delayed_dx = 0;
  }
  if (BX_USB_THIS mouse_delayed_dy > 127) {
    delta_y = 127;
    BX_USB_THIS mouse_delayed_dy -= 127;
  } else if (BX_USB_THIS mouse_delayed_dy < -128) {
    delta_y = -128;
    BX_USB_THIS mouse_delayed_dy += 128;
  } else {
    delta_y = BX_USB_THIS mouse_delayed_dy;
    BX_USB_THIS mouse_delayed_dy = 0;
  }

  BX_USB_THIS mouse_x = (Bit8s) delta_x;
  BX_USB_THIS mouse_y = (Bit8s) delta_y;
  BX_USB_THIS mouse_z = (Bit8s) delta_z;
  BX_USB_THIS b_state = (Bit8u) button_state;
}

bx_bool bx_pciusb_c::usb_key_enq(Bit8u *scan_code)
{
  bx_bool is_break_code = 0;
  Bit8u our_scan_code[8];

  memset(our_scan_code, 0, 8);
  int os = 0;
  for (int s=0; s<8; s++) {
    if ((scan_code[s] == 0xF0) && ((s == 0) || ((s == 1) && (scan_code[0] == 0xE0)))) {
      is_break_code = 1;
    } else {
      if (!(our_scan_code[os++] = scan_code[s])) break;
    }
  }
  
  // if it is the break code of the saved key, then clear our packet key.
  if (is_break_code && !memcmp(BX_USB_THIS saved_key, our_scan_code, 8)) {
    memset(BX_USB_THIS saved_key, 0, 8);
    memset(BX_USB_THIS key_pad_packet, 0, 8);
    return 1; // tell the keyboard handler that we used it, and to return with out processing key
  }

  // find an attached keyboard device and one that has a conversion table filled.
  // we assume the (bochs) user has only one device attached with these specs.
  bx_bool fnd = 0;
  struct USB_DEVICE *dev;
  for (int i=0; i<BX_USB_CONFDEV && !fnd; i++) {
    for (int j=0; j<USB_NUM_PORTS && !fnd; j++) {
      // is there something plugged in to this port?
      if (BX_USB_THIS hub[i].usb_port[j].status) {
        if (BX_USB_THIS hub[i].usb_port[j].device_num < 0)
          BX_PANIC(("USB internal error at line %i", __LINE__));
        dev = &BX_USB_THIS hub[i].device[BX_USB_THIS hub[i].usb_port[j].device_num];
        for (int k=0; k<dev->function.device_descr.configs && !fnd; k++) {
          for (int l=0; l<dev->function.device_config[k].interfaces && !fnd; l++) {
            if ((dev->function.device_config[k].Interface[l].protocol == 1) 
              && dev->function.device_config[k].Interface[l].lookup_cnt) {
              // Only do this if the keypad is in the configured state
              if (dev->state == STATE_CONFIGURED) {
                for (int m=0; m<dev->function.device_config[k].Interface[l].lookup_cnt; m++) {
                  if (!memcmp(dev->function.device_config[k].Interface[l].lookup[m].scan_code, our_scan_code, 8)) {
                    memcpy(BX_USB_THIS key_pad_packet, dev->function.device_config[k].Interface[l].lookup[m].keypad_packet, 8);
                    fnd = 1;
                    break;
                  }
                }
              } else {
                memset(BX_USB_THIS saved_key, 0, 8);
                return 0;  // if keypad is not configured, it can't send scan codes. 
              }
            }
          }
        }
      }
    }
  }
  
  if (!fnd) {
    memset(BX_USB_THIS key_pad_packet, 0, 8);
    memset(BX_USB_THIS saved_key, 0, 8);
  } else {
    memcpy(BX_USB_THIS saved_key, our_scan_code, 8);
    // print a debug line to the log file
    char bx_debug_code[128] = "";
    char value[8];
    for (unsigned i=0; i<strlen((char *) our_scan_code); i++) {
      sprintf(value, "0x%02x", our_scan_code[i]);
      if (i) strcat(bx_debug_code, "  ");
      strcat(bx_debug_code, value);
    }
    BX_DEBUG(("Re-routing scan code (%s) to USB keypad", bx_debug_code));
  }

  // tell the keyboard handler whether we used it or not.  (0 = no, 1 = yes and keyboard.cc ignores keystoke)
  return fnd;
}

bx_bool bx_pciusb_c::usb_keyboard_connected()
{
  return BX_USB_THIS keyboard_connected;
}

bx_bool bx_pciusb_c::usb_mouse_connected()
{
  return BX_USB_THIS mouse_connected;
}

// Dumps the contents of a buffer to the log file
void bx_pciusb_c::dump_packet(Bit8u *data, unsigned size)
{
  char the_packet[256], str[16];
  strcpy(the_packet, "Packet contents (in hex):");
  unsigned offset = 0;
  for (unsigned p=0; p<size; p++) {
    if (!(p & 0x0F)) {
      BX_DEBUG(("%s", the_packet));
      sprintf(the_packet, "  0x%04X ", offset);
      offset += 16;
    }
    sprintf(str, " %02X", data[p]);
    strcat(the_packet, str);
  }
  if (strlen(the_packet))
    BX_DEBUG(("%s", the_packet));
}

// usb flash stick support
// packet is the data coming in/going out
// size is the size of the data
// out = 1 if Host to Device, 0 it Device to Host

bx_bool bx_pciusb_c::flash_stick(Bit8u *packet, Bit16u size, bx_bool out)
{
  // packet contains the SCSI interface command
  dump_packet(packet, size);
  
  if (out) {

      // TODO:

      // From this point on, it is simply a SCSI command interface.
      // We could set up the image so that we could use the existing
      //  harddrv.cc and cdrom.cc code and our flash.img file.
  
      // At this point, I don't have the time to do this, but if someone
      //  else would like to, please do.

  } else {
    
    // For now, I will not implement the write part.
    
  }
  
  return 1;
}

const char *bx_pciusb_c::usb_param_handler(bx_param_string_c *param, int set, const char *val, int maxlen)
{
  // handler for USB runtime parameters
  if (set) {
    char pname[BX_PATHNAME_LEN];
    param->get_param_path(pname, BX_PATHNAME_LEN);
    if (!strcmp(pname, BXPN_USB1_PORT1)) {
      BX_ERROR(("USB port #1 device change not implemented yet"));
    } else if (!strcmp(pname, BXPN_USB1_OPTION1)) {
      BX_ERROR(("USB port #1 option change not implemented yet"));
    } else if (!strcmp(pname, BXPN_USB1_PORT2)) {
      BX_ERROR(("USB port #2 device change not implemented yet"));
    } else if (!strcmp(pname, BXPN_USB1_OPTION2)) {
      BX_ERROR(("USB port #2 option change not implemented yet"));
    } else {
      BX_PANIC(("usb_param_handler called with unexpected parameter '%s'", pname));
    }
  }
  return val;
}

#endif // BX_SUPPORT_PCI && BX_SUPPORT_PCIUSB
