/////////////////////////////////////////////////////////////////////////
// $Id: pciusb.cc,v 1.3 2003/02/06 19:09:24 vruppert Exp $
/////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2003  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@cybertrails.com) coded most of this usb emulation.
//  I hope to add to this code to make it more functionable.
//

// 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_PCI_SUPPORT && BX_PCI_USB_SUPPORT

#undef LOG_THIS
#define LOG_THIS theUSBDevice->

// Bus Line Status States
#define LO_IDLE  BX_USB_THIS hub[0].usb_port[0].line_dminus = 1; \
				         BX_USB_THIS hub[0].usb_port[0].line_dplus = 0;
#define LO_K     BX_USB_THIS hub[0].usb_port[0].line_dminus = 0; \
				         BX_USB_THIS hub[0].usb_port[0].line_dplus = 1;
#define LO_J     BX_USB_THIS hub[0].usb_port[0].line_dminus = 1; \
				         BX_USB_THIS hub[0].usb_port[0].line_dplus = 0;
#define HI_IDLE  BX_USB_THIS hub[0].usb_port[0].line_dminus = 0; \
				         BX_USB_THIS hub[0].usb_port[0].line_dplus = 1;
#define HI_K     BX_USB_THIS hub[0].usb_port[0].line_dminus = 1; \
				         BX_USB_THIS hub[0].usb_port[0].line_dplus = 0;
#define HI_J     BX_USB_THIS hub[0].usb_port[0].line_dminus = 0; \
				         BX_USB_THIS hub[0].usb_port[0].line_dplus = 1;
#define SE0      BX_USB_THIS hub[0].usb_port[0].line_dminus = 0; \
				         BX_USB_THIS hub[0].usb_port[0].line_dplus = 0;
#define SE1      BX_USB_THIS hub[0].usb_port[0].line_dminus = 1; \
				         BX_USB_THIS hub[0].usb_port[0].line_dplus = 1;
#define LO_SOP   LO_K
#define LO_EOP   SE0 LO_K LO_IDLE


bx_pciusb_c* theUSBDevice = NULL;

  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(void)
{
  put("USB");
  settype(PCIUSBLOG);
}

bx_pciusb_c::~bx_pciusb_c(void)
{
  // nothing for now
  BX_DEBUG(("Exit."));
}


  void
bx_pciusb_c::init(void)
{
  // called once when bochs initializes
  if (!bx_options.usb[0].Oenabled->get()) return;

  Bit16u base_ioaddr = bx_options.usb[0].Oioaddr->get();
  Bit8u irq = bx_options.usb[0].Oirq->get();

  DEV_register_irq(irq, "USB Hub #1");
  BX_USB_THIS hub[0].irq = irq;

  BX_DEBUG(("Init $Id: pciusb.cc,v 1.4 2004/06/03 13:19:31 benlunt Exp $"));

  // 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");

  for (unsigned addr=base_ioaddr; addr<(unsigned)(base_ioaddr+0x14); addr++) {
    BX_DEBUG(("register read/write: 0x%04x", addr));
    DEV_register_ioread_handler(this, read_handler, addr, "USB Hub #1", 7);
    DEV_register_iowrite_handler(this, write_handler, addr, "USB Hub #1", 7);
  }
  BX_USB_THIS hub[0].base_ioaddr = base_ioaddr;

  DEV_register_pci_handlers(this,
                            pci_read_handler,
                            pci_write_handler,
                            DEV_find_free_devfunc(),
                            "Experimental PCI USB");

  for (unsigned i=0; i<256; i++) {
    BX_USB_THIS hub[0].pci_conf[i] = 0x0;
  }

	// reset the data for our USB device
	BX_USB_THIS reset(TRUE);

  BX_INFO(("usb1 at 0x%04x-0x%04x irq %d", base_ioaddr, base_ioaddr+0x13, irq));


	//FIXME: for now, we want a status bar // hub zero, port zero
  BX_USB_THIS hub[0].statusbar_id[0] = bx_gui->register_statusitem("USB0");
}

  void
bx_pciusb_c::reset(bx_bool reset_port)
{
	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, ((bx_options.usb[0].Oioaddr->get() & 0xE0) | 0x01) },
    { 0x21, (bx_options.usb[0].Oioaddr->get() >> 8) },
    { 0x22, 0x00 }, { 0x23, 0x00 },
    { 0x3c, bx_options.usb[0].Oirq->get() }, // IRQ
    { 0x3d, 0x04 },                 // INT
    { 0x6a, 0x01 },                 // USB clock
    { 0xc1, 0x20 }                  // PIRQ enable

  };
*/

  } reset_vals[] = {
    { 0x00, 0x06 }, { 0x01, 0x11 }, // 0x     = vendor
    { 0x02, 0x38 }, { 0x03, 0x30 }, // 0x     = device
    { 0x04, 0x07 }, { 0x05, 0x00 },	// command_io
    { 0x06, 0x10 }, { 0x07, 0x02 },	// status
    { 0x08, 0x1A },                 // revision number
    { 0x09, 0x00 },                 // interface
    { 0x0A, 0x03 },                 // class_sub  USB Host Controller
    { 0x0B, 0x0C },                 // class_base Serial Bus Controller
    { 0x0C, 0x08 },                 // cache line size
    { 0x0D, 0x20 },                 // bus latency
    { 0x0E, 0x00 },                 // header_type_generic
    { 0x0F, 0x00 },                 // Built-in Self Test
    // address space 0x20 - 0x23
    { 0x20, ((bx_options.usb[0].Oioaddr->get() & 0xE0) | 0x01) },
    { 0x21, (bx_options.usb[0].Oioaddr->get() >> 8) },
    { 0x22, 0x00 }, { 0x23, 0x00 },
    { 0x2C, 0x25 },
		{ 0x2D, 0x09 },
		{ 0x2E, 0x34 },
		{ 0x2F, 0x12 },
    { 0x34, 0x80 }, 
    { 0x3C, bx_options.usb[0].Oirq->get() }, // IRQ
    { 0x3D, 0x04 },                 // INT
    { 0x6A, 0x00 },                 // USB clock
    { 0xc1, 0x00 },                 // PIRQ enable

		{ 0x40, 0x00 },
		{ 0x41, 0x12 },
		{ 0x42, 0x03 },
		{ 0x43, 0x00 },
		{ 0x44, 0xC6 },
		{ 0x45, 0x00 },
		{ 0x46, 0x10 },

		{ 0x60, 0x10 },

 		{ 0x80, 0x01 },
		{ 0x81, 0x00 },
		{ 0x82, 0x02 },

		{ 0xC1, 0x20 }
	};

	memset(&BX_USB_THIS hub[0].pci_conf, 0, sizeof(BX_USB_THIS hub[0].pci_conf));
  for (unsigned 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 global_reset = 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.host_reset = 0;
    BX_USB_THIS hub[i].usb_command.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;
		if (reset_port) {
			for (unsigned 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;
			}
		}
  }

	// for our purposes, we are going to set a device on hub 0, port 0
	BX_USB_THIS hub[0].usb_port[0].low_speed = 1;    // all devices we support are low speed
	BX_USB_THIS hub[0].usb_port[0].line_dminus = 1;  //  dminus=1 & dplus=0 = low speed  (at idle time)
	BX_USB_THIS hub[0].usb_port[0].line_dplus = 0;   //  dminus=0 & dplus=1 = high speed (at idle time)
	BX_USB_THIS hub[0].usb_port[0].status = 1;       // we will always have a device attached
	BX_USB_THIS hub[0].usb_port[0].connect_changed = 1;

	// setup devices

/*
	/////// Cyprus mouse
	// the one and only device
	BX_USB_THIS hub[0].device[0].address = 0;
	BX_USB_THIS hub[0].device[0].alt_interface = 0;
	BX_USB_THIS hub[0].device[0].Interface = 0;
	BX_USB_THIS hub[0].device[0].config = 0;
	BX_USB_THIS hub[0].device[0].descriptor = 0;
	BX_USB_THIS hub[0].device[0].functions = 1;
	BX_USB_THIS hub[0].device[0].function[0].direction = 0;
	BX_USB_THIS hub[0].device[0].function[0].device_descr.len = 18;
	BX_USB_THIS hub[0].device[0].function[0].device_descr.type = DEVICE;
	BX_USB_THIS hub[0].device[0].function[0].device_descr.usb_ver = 0x0100;
	BX_USB_THIS hub[0].device[0].function[0].device_descr._class = 0;
	BX_USB_THIS hub[0].device[0].function[0].device_descr.subclass = 0;
	BX_USB_THIS hub[0].device[0].function[0].device_descr.protocol = 0;
	BX_USB_THIS hub[0].device[0].function[0].device_descr.max_packet_size = 8;
	BX_USB_THIS hub[0].device[0].function[0].device_descr.vendorid = 0x0627;
	BX_USB_THIS hub[0].device[0].function[0].device_descr.productid = 0x0001;
	BX_USB_THIS hub[0].device[0].function[0].device_descr.device_rel = 0;
	BX_USB_THIS hub[0].device[0].function[0].device_descr.manuf_indx = 1;
	BX_USB_THIS hub[0].device[0].function[0].device_descr.prod_indx = 1;//2;
	BX_USB_THIS hub[0].device[0].function[0].device_descr.serial_indx = 0;
	BX_USB_THIS hub[0].device[0].function[0].device_descr.configs = 1;
	
	// config descriptor
	BX_USB_THIS hub[0].device[0].function[0].device_config[0].len = 9;
	BX_USB_THIS hub[0].device[0].function[0].device_config[0].type = CONFIG;
	BX_USB_THIS hub[0].device[0].function[0].device_config[0].tot_len = 34;
	BX_USB_THIS hub[0].device[0].function[0].device_config[0].interfaces = 1;
	BX_USB_THIS hub[0].device[0].function[0].device_config[0].config_val = 1;
	BX_USB_THIS hub[0].device[0].function[0].device_config[0].config_indx = 4;
	BX_USB_THIS hub[0].device[0].function[0].device_config[0].attrbs = 0xA0;  // bus powered, etc
	BX_USB_THIS hub[0].device[0].function[0].device_config[0].max_power = 50;
	//memset(BX_USB_THIS hub[0].device[0].function[0].device_config[0].remaining, 0, sizeof(BX_USB_THIS hub[0].device[0].function[0].device_config[0].remaining));
	Bit8u str_0[34] = {	0x09, 0x04, 0x00, 0x00, 0x01, 0x03, 0x01, 0x02, 0x05,
		0x07, 0x05, 0x81, 0x03, 0x03, 0x00, 0x0A, 0x09, 0x21, 0x00, 0x01, 0x00,
		0x01, 0x22, 0x32, 0x00 
	};
	memcpy(&BX_USB_THIS hub[0].device[0].function[0].device_config[0].remaining, str_0, 34);

	// string descriptors
	BX_USB_THIS hub[0].device[0].function[0].str_descriptor.size = 0x04;
	BX_USB_THIS hub[0].device[0].function[0].str_descriptor.type = 0x03;
	BX_USB_THIS hub[0].device[0].function[0].str_descriptor.langid[0] = 0x0409;
	BX_USB_THIS hub[0].device[0].function[0].str_descriptor.langid[1] = 0x0409;
	BX_USB_THIS hub[0].device[0].function[0].str_descriptor.langid[2] = 0x0000;
	BX_USB_THIS hub[0].device[0].function[0].string[0].size = 0x18;
	BX_USB_THIS hub[0].device[0].function[0].string[0].type = 0x03;
	Bit8u str_1[64] = {	0x43, 0x00, 0x79, 0x00, 0x70, 0x00, 0x72, 0x00, 0x65, 
		0x00, 0x73, 0x00, 0x73,	0x00, 0x20, 0x00, 0x53, 0x00, 0x65, 0x00, 0x6D
	};
	memcpy(&BX_USB_THIS hub[0].device[0].function[0].string[0].unicode_str, str_1, 0x18);

	BX_USB_THIS hub[0].device[0].function[0].string[0].size = 0x24;
	BX_USB_THIS hub[0].device[0].function[0].string[0].type = 0x03;
	Bit8u str_2[64] = {	0x43, 0x00, 0x79, 0x00, 0x70, 0x00, 0x72, 0x00, 0x65,
		0x00, 0x73, 0x00, 0x73,	0x00, 0x20, 0x00, 0x55, 0x00, 0x53, 0x00, 0x42,
		0x00, 0x20, 0x00, 0x4D, 0x00, 0x6F, 0x00, 0x75, 0x00, 0x73, 0x00, 0x65		
	};
	memcpy(&BX_USB_THIS hub[0].device[0].function[0].string[0].unicode_str, str_2, 0x24);
*/

	// keypad
	Bit8u keypad[] = { 
		// fillers.  Leave at zero
		0x00,                           // Bit8u  direction;
		0x00,0x00,0x00,0x00,            // Bit8u	*in;
		0x00,0x00,0x00,0x00,            // Bit8u	*out;
		0x00,0x00,                      // Bit16u in_cnt;
		0x00,0x00,                      // Bit16u out_cnt;
		// Descriptor (DEVICE)		
		0x12,                           // Bit8u  len;
		0x01,                           // Bit8u  type;
		0x10,0x01,                      // Bit16u usb_ver;
		0x00,                           // Bit8u  _class;
		0x00,                           // Bit8u  subclass;
		0x00,                           // Bit8u  protocol;
		0x08,                           // Bit8u  max_packet_size;
		0xB4,0x04,                      // Bit16u vendorid;
		0x01,0x01,                      // Bit16u productid;
		0x01,0x00,                      // Bit16u device_rel;
		0x01,  //1                      // Bit8u  manuf_indx;
		0x02,  //2                      // Bit8u  prod_indx;
		0x00,                           // Bit8u  serial_indx;
		0x01,                           // Bit8u  configs;
		// Descriptor (CONFIG) (2 total)
		0x09,                           // Bit8u  len;
		0x02,                           // Bit8u  type;
		0x3B,0x00,                      // Bit16u tot_len;
		0x02,                           // Bit8u  interfaces;
		0x01,                           // Bit8u  config_val;
		0x04,                           // Bit8u  config_indx;
		0xA0,                           // Bit8u  attrbs;
		0x32,                           // Bit8u  max_power;
		0x09, 0x04, 0x00, 0x00, 0x01, 0x03, 0x01,	0x01,
		0x05, 0x09, 0x21, 0x00, 0x01, 0x00, 0x01, 0x22,
		0x41, 0x00, 0x07, 0x05, 0x81,	0x03, 0x08, 0x00,
		0x0A, 0x09, 0x04, 0x01,	0x00, 0x01, 0x03, 0x01,
		0x02, 0x06, 0x09,	0x21, 0x00, 0x01, 0x00, 0x01,
		0x22, 0x32,	0x00, 0x07, 0x05, 0x82, 0x03, 0x05,
		0x00,	0x0A,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,                  		// Bit8u  remaining[247];
		// second CONFIG (empty for this device)
		0x00,                           // Bit8u  len;
		0x00,                           // Bit8u  type;
		0x00,0x00,                      // Bit16u tot_len;
		0x00,                           // Bit8u  interfaces;
		0x00,                           // Bit8u  config_val;
		0x00,                           // Bit8u  config_indx;
		0x00,                           // Bit8u  attrbs;
		0x00,                           // Bit8u  max_power;
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,              		// Bit8u  remaining[247];
		// Descriptor (STRING)
		0x04,                           // Bit8u  size;
		0x03,                           // Bit8u  type;
		0x09,0x04, 0x00,0x00, 0x00,0x00, // Bit16u langid[3];
		// STRINGs (4 total)
		0x12,                           // Bit8u  size;
		0x03,                           // Bit8u  type;
		'K',0,'e',0,'y',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // Bit8u  unicode_str[64];
		// second STRING
		0x12,                           // Bit8u  size;
		0x03,                           // Bit8u  type;
		'K',0,'e',0,'y',0,'B',0,'O',0,'A',0,'R',0,'D',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  // Bit8u  unicode_str[64];
		// Third STRING
		0x12,                           // Bit8u  size;
		0x03,                           // Bit8u  type;
		'K',0,'e',0,'y',0,'B',0,'O',0,'A',0,'R',0,'D',0,'2',0,'2',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  // Bit8u  unicode_str[64];
		// Forth STRING
		0x12,                           // Bit8u  size;
		0x03,                           // Bit8u  type;
		'K',0,'e',0,'y',0,'B',0,'O',0,'A',0,'R',0,'D',0,'3',0,'3',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  // Bit8u  unicode_str[64];
		// Interfaces
		//0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x05, 0x07, 
		0x19, 0xE0, 0x29, 0xe7, 0x15, 0x00, 0x25, 0x01,
    0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x95, 0x01, 0x75, 0x08, 0x81, 0x01, 0x95, 0x03, 0x75, 0x01,
    0x05, 0x08, 0x19, 0x01, 0x29, 0x03, 0x91, 0x02, 0x95, 0x05, 0x75, 0x01, 0x91, 0x01, 0x95, 0x06,
    0x75, 0x08, 0x15, 0x00, 0x26, 0xff, 0x00, 0x05, 0x07, 0x19, 0x00, 0x2A, 0xFF, 0x00, 0x81, 0x00,
    0xC0 
		,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
		,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	};
	memcpy(&BX_USB_THIS hub[0].device[0].function[0], keypad, sizeof(BX_USB_THIS hub[0].device[0].function[0]));
}

  // 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;

  //BX_DEBUG(("register read from address 0x%04x - ", (unsigned) address));

  offset = address - BX_USB_THIS hub[0].base_ioaddr;

  switch (offset) {
    case 0x0C: // Start of Frame Modify
    case 0x11: // port0 (high byte read)
    case 0x13: // port1 (high byte read)
      if (io_len != 1)
        BX_PANIC(("io read from port 0x%04x, bad len=%u", (unsigned) address, (unsigned) io_len));
      break;
    case 0x10: // port0
    case 0x12: // port1
    case 0x14: // port2 non existant, but linux systems check it to see if there are more than 2
      if ((io_len < 1) || (io_len > 2))
        BX_PANIC(("io read from port 0x%04x, bad len=%u", (unsigned) address, (unsigned) io_len));
      break;
    case 0x00: // command register (16-bit)
    case 0x02: // status register (16-bit)
    case 0x04: // interrupt enable register (1-bit)
    case 0x06: // frame number register (16-bit)
      if (io_len != 2)
        BX_PANIC(("io read from port 0x%04x, bad len=%u", (unsigned) address, (unsigned) io_len));
      break;
    case 0x08: // frame base register (32-bit)
      if (io_len != 4)
        BX_PANIC(("io read from port 0x%04x, bad len=%u", (unsigned) address, (unsigned) io_len));
      break;
  }

  switch (offset) {
    case 0x00: // command register (16-bit)
      val = /*BX_USB_THIS hub[0].usb_command.max_packet_size*/ 0 << 7  // always return as 0 (max packet = 32) lowspeed
            | 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: // port2 non existant, but linux systems check it to see if there are more than 2
			BX_INFO(("read from non existant port 0x14 (port 2)"));
			val = 0xFF7F;  //TODO: if non existant, does it return 0xFF7F
			break;

    case 0x10: // port0
    case 0x12: // port1
      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
              | 1 << 8                                         // always return TRUE (lowspeed device)
              | 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;
				//BX_INFO(("read from port%01X register: 0x%04x", port, val));
        break;
      } // else fall through to default
    default:
      val = 0xFF7F; // keep compiler happy
      BX_PANIC(("unsupported io read from address=0x%04x!", (unsigned) address));
      break;
  }

  //BX_DEBUG(("val =  0x%08x", (Bit32u) val));
	//BX_INFO(("register read from address 0x%04X:  0x%08X (%i 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;

	int i, j;

//  BX_DEBUG(("register write to address 0x%04x - ", (unsigned) address));
//  BX_INFO(("register write to  address 0x%04X:  0x%04X  (%i bits)", (unsigned) address, (unsigned) value, io_len * 8));

  offset = address - BX_USB_THIS hub[0].base_ioaddr;

  switch (offset) {
    case 0x0C: // Start of Frame Modify
      if (io_len != 1)
        BX_PANIC(("io write to port 0x%04x, bad len=%u", (unsigned) address, (unsigned) io_len));
      break;
    case 0x00: // command register (16-bit)
    case 0x02: // status register (16-bit)
    case 0x04: // interrupt enable register (1-bit)
    case 0x06: // frame number register (16-bit)
    case 0x10: // port0
    case 0x12: // port1
      if (io_len != 2)
        BX_PANIC(("io write to port 0x%04x, bad len=%u", (unsigned) address, (unsigned) io_len));
      break;
    case 0x08: // frame base register (32-bit)
      if (io_len != 4)
        BX_PANIC(("io write to port 0x%04x, bad len=%u", (unsigned) address, (unsigned) io_len));
      break;
  }

  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));

			// reset, do not reset ports, just host controller
			//  (i.e.: don't send global reset to all USB devices)
      if (BX_USB_THIS hub[0].usb_command.reset && !(value & 0x04))
				BX_USB_THIS reset(FALSE);

      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;

      // If software set the hcreset bit, we need to set reset bit of each port for 10ms.
			// 10ms is a known figure for how long max, a reset should take.  On actual hardware,
			//  this can be considerably less.
      if (BX_USB_THIS hub[0].usb_command.host_reset) {
        BX_USB_THIS global_reset = 10;
				BX_INFO(("Global Reset"));
			}

      // If Run/Stop, identify in log
      if (BX_USB_THIS hub[0].usb_command.schedule)
        BX_INFO(("Software set Schedule bit 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));

      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_INFO(("Host set Enable Interrupt on Short Packet"));
			}
			if (value & 0x04) {
				BX_INFO(("Host set Enable Interrupt on Complete"));
			}

			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: // port2 non existant, but linux systems check it to see if there are more than 2
			BX_DEBUG(("write from non existant port 0x14 (port 2)"));
			break;

    case 0x10: // port0
    case 0x12: // port1
      port = (offset & 0x0F) >> 1;
      if (port < USB_NUM_PORTS) {
        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, value));
        if (!(value & (1<<7)))
          BX_DEBUG(("write to port%d register bit 7 = 0", port));
        if (value & (1<<8))
          BX_DEBUG(("write to bit 8 in port%d register ignored", port));
        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));

        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;
        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].reset = 1;
					BX_USB_THIS hub[0].usb_port[port].low_speed = 1;
					BX_USB_THIS hub[0].usb_port[port].resume = 0;
					BX_USB_THIS hub[0].usb_port[port].line_dminus = 0;
					BX_USB_THIS hub[0].usb_port[port].line_dplus = 0;
					BX_USB_THIS hub[0].usb_port[port].able_changed = 1;
					BX_USB_THIS hub[0].usb_port[port].enabled = 0;
					BX_USB_THIS hub[0].usb_port[port].connect_changed = 1;
					BX_USB_THIS hub[0].usb_port[port].status = 1;
					for (i=0; i<USB_CUR_DEVS; i++) {
						for (j=0; j<BX_USB_THIS hub[0].device[i].functions; j++) {
							BX_USB_THIS hub[0].device[i].address = 0;
							BX_USB_THIS hub[0].device[i].function[j].out = 0;
							BX_USB_THIS hub[0].device[i].function[j].in = 0;
							// need to reset descriptors and the such?
						}
					}
					BX_INFO(("Port Reset"));
				}
        break;
      }
      // else fall through to default
    default:
      BX_PANIC(("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();
}


/*
unsigned temp_status = 0;

unsigned bx_pciusb_c::get_connect_status(unsigned port)
{
	//return (unsigned) BX_USB_THIS hub[0].usb_port[port].status;	
	return temp_status;
}

unsigned bx_pciusb_c::set_connect_status(unsigned port, unsigned value)
{

	//BX_USB_THIS hub[0].usb_port[port].status = value;
	//BX_USB_THIS hub[0].usb_port[port].connect_changed = 1;
	temp_status = value;
 
	return 0;
}
*/

// remember to delete these two globals
//Bit32u xframenum, xaddress;

// Called once every 1ms
void bx_pciusb_c::usb_timer(void)
{
	static bx_bool busy = 0;
	int i;

  // The Frame Number Register is incremented every 1ms
  // Needs more work and investigation on this.
  if (BX_USB_THIS hub[0].usb_command.schedule && !busy) {
		BX_USB_THIS hub[0].usb_frame_num.frame_num++;
		BX_USB_THIS hub[0].usb_frame_num.frame_num &= (1024-1);
	}

  // 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 = 1;
      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;
    }
    BX_USB_THIS global_reset--;
		if (BX_USB_THIS global_reset == 0) {
      BX_USB_THIS reset(TRUE);
		}
  }

  // 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.                                                                      
	//    I am not sure if it will correctly finish something before the 1ms is over, so
	//      that is why the busy flag is used.
  if (BX_USB_THIS hub[0].usb_command.schedule && !busy) {
		busy = 1;
		bx_bool interrupt = 0, shortpacket = 0;
		struct TD td;
		struct HCSTACK stack[64];  // queue stack for this item only
		Bit32s stk = 0, nstk = 0;
		Bit32u item, address, lastvertaddr;
		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);
		BX_MEM_READ_PHYSICAL(frm_addr, 4, &frame);
		if (!(frame & 1)) {
			stack[stk].next = frame & 0xFFFFFFF0;
			stack[stk].d = 0;
			stack[stk].q = (frame & 0x0002) ? 1 : 0;
			stack[stk].t = 0; //(frame & 0x0001) ? 1 : 0;
			while (stk > -1) {
				// 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++;
					BX_MEM_READ_PHYSICAL(address, 4, &item);
					stack[stk].next = item & 0xFFFFFFF0;
					stack[stk].d = HC_HORZ;
					stack[stk].q = (item & 0x0002) ? 1 : 0;
					stack[stk].t = (item & 0x0001) ? 1 : 0;
					// get VERT slot
					stk++;
					BX_MEM_READ_PHYSICAL(lastvertaddr, 4, &item);
					stack[stk].next = item & 0xFFFFFFF0;
					stack[stk].d = HC_VERT;
					stack[stk].q = (item & 0x0002) ? 1 : 0;
					stack[stk].t = (item & 0x0001) ? 1 : 0;
				} else {  // else is a TD
					address = stack[stk].next;
					BX_MEM_READ_PHYSICAL(address, 32, &td);
					bx_bool spd = (td.dword1 & (1<<29)) ? 1 : 0;
					stack[stk].next = td.dword0 & 0xFFFFFFF0;
					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;

					///////// remember to delete these two globals
					//xframenum = BX_USB_THIS hub[0].usb_frame_num.frame_num;
					//xaddress = address;
					//bx_bool was_act = (td.dword1 & (1<<23));

					if (td.dword1 & (1<<24)) interrupt = 1;
					BX_USB_THIS DoTransfer(&td); // pass address for a return value of dev->max_packet_size --v
					BX_MEM_WRITE_PHYSICAL(address+4, 4, &td.dword1);  // write back the status
					
					// issue short packet?
					Bit16u r_actlen = ((td.dword1+1) & 0x7FF);
					Bit16u r_maxlen = (((td.dword2>>21)+1) & 0x7FF);
					if (r_maxlen > 8) r_maxlen = 8;
					if (((td.dword2 & 0xFF) == TOKEN_IN) && spd && stk && (r_actlen < r_maxlen))
						shortpacket = 1;

					//if (was_act)
						//BX_INFO((" %i  %i  %i", r_actlen, r_maxlen, shortpacket));

					// copy pointer for next queue item, in to vert queue head
					if (stk && !shortpacket && (stack[stk].d == HC_VERT)) BX_MEM_WRITE_PHYSICAL(lastvertaddr, 4, &td.dword0);
					else {
						// else, just mark it as done.
						BX_MEM_READ_PHYSICAL(lastvertaddr, 4, &td.dword0);
						td.dword0 |= 1;
						BX_MEM_WRITE_PHYSICAL(lastvertaddr, 4, &td.dword0);
					}
					// if Breadth first, move to next queue.
					if (stk && !depthbreadth) stk--;
					// if last vert in queue, move back one queue
					else if (stack[stk].t) break;
				}
				if (stk < 1) break;  // don't execute the Frame Pointer again, not until next rotation. (1 sec from now)
			}

			// 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_status.interrupt = 1;
				if (BX_USB_THIS hub[0].usb_enable.short_packet) {
					DEV_pic_raise_irq(bx_options.usb[0].Oirq->get());
					//BX_INFO((" [SPD] We wan't it to fire here"));
				}
			}

			// 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_status.interrupt = 1;
				if (BX_USB_THIS hub[0].usb_enable.on_complete) {
					DEV_pic_raise_irq(bx_options.usb[0].Oirq->get());
					//BX_INFO((" [IOC] We wan't it to fire here"));
				}
			}
		
		}
		busy = 0;  // ready to do next frame item
	}  // end run schedule


		// 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.

}

struct USB_DEVICE *g_dev = NULL;
Bit8u g_address = 0;

void bx_pciusb_c::DoTransfer(struct TD *td) {
	
	Bit8u  active = (td->dword1 & (1<<23)) ? 1 : 0;
	Bit16u maxlen = (td->dword2 >> 21);
	Bit8u  addr   = (td->dword2 >> 8) & 0x7F;
	Bit8u  endpt  = (td->dword2 >> 15) & 0x0F;
	Bit8u  pid    =  td->dword2 & 0xFF;
	Bit8u  d      = (td->dword2 & 0x00080000) ? 1 : 0;
	Bit8u  data[64];
	struct REQUEST_PACKET *rp = (struct REQUEST_PACKET *) data;

	// if packed, read in the packet data
	if (pid == TOKEN_SETUP) {
		//LO_SOP
		if (td->dword3) BX_MEM_READ_PHYSICAL(td->dword3, 8, data);
		// the '8' above may need to be maxlen (unless maxlen == 0)
		//LO_EOP
	}

	/*
	// print each TD to the file specified.  for debugging
	if ((pid != TOKEN_OUT) && active && (maxlen < 0x7FF)) {
		FILE *fp = fopen("testusb.txt", "a");
		fprintf(fp, "\n\n Frame: %i, address of TD 0x%08X", xframenum, xaddress);
		fprintf(fp, "\n Frame List Item Found:  (pid = 0x%02X) (maxlen = %i)", pid, maxlen);
		fprintf(fp, "\n %08X  %08X  %08X  %08X", td->dword0, td->dword1, td->dword2, td->dword3);
		if (pid == TOKEN_SETUP)
			fprintf(fp, "\n 0x%02X  0x%02X  0x%04X  0x%04X  %i", data[0], data[1], *((Bit16u *)(data+2)), 
				*((Bit16u *)(data+4)), *((Bit16u *)(data+6)));
		fclose(fp);
	}
	*/

  BX_DEBUG(("TD found:  %08x   %08x   %08x   %08x", td->dword0, td->dword1, td->dword2, td->dword3));
  BX_DEBUG(("           %02x %02x %02x %02x %02x %02x %02x %02x", data[0], data[1], data[2], 
		                                                 data[3], data[4], data[5], data[6], data[7]));

	// check TD to make sure it is valid
	// A max length 0x500 to 0x77E is illegal  (win98se does this to keep the usb "alive" ???)
	if (((td->dword2 >> 21) >= 0x500) && ((td->dword2 >> 21) != 0x7FF)) {
		return;  // error = consistency check failure
	}
  //if (td->dword0 & 0x8) return; // error = reserved bits in dword0 set
	// other error checks here

	if (active) {  // only if active bit in status is set
		// find device address
		struct USB_DEVICE *dev = NULL;
		for (int i=0; i<USB_CUR_DEVS; i++) {
			if (BX_USB_THIS hub[0].device[i].address == addr) {
				dev = &BX_USB_THIS hub[0].device[i];
				break;
			}
		}
		if (dev == NULL) {
			//BX_ERROR(("Device not found for addr: %08X", addr));
			return;  // device not found
		}

		maxlen++;
		maxlen &= 0x7FF;
		if (maxlen > dev->function[endpt].device_descr.max_packet_size)
			maxlen = dev->function[endpt].device_descr.max_packet_size;

		// parse and get command
		Bit16u cnt;

//		FILE *fp;

		switch (pid) {
			case TOKEN_IN:   // Data came from HC to Host
	bx_gui->statusbar_setitem(BX_USB_THIS hub[0].statusbar_id[0], TRUE);
				BX_INFO(("TOKEN_IN: len = %X", maxlen));
				
				if (maxlen == 0) {
					BX_USB_THIS set_status(td, 0, 1, 0, 0, 0, 0, 0, 0, 0x7FF); 

					if (g_dev != NULL) {
						g_dev->address = g_address;
						g_dev = NULL;
					}

					break;
				}

				cnt = (dev->function[endpt].in_cnt < maxlen) ? dev->function[endpt].in_cnt : maxlen;
				//LO_SOP
				BX_MEM_WRITE_PHYSICAL(td->dword3, cnt, dev->function[endpt].in);
				//LO_EOP
				dev->function[endpt].in += cnt;
				dev->function[endpt].in_cnt -= cnt;
				BX_USB_THIS set_status(td, (dev->function[endpt].in_cnt < 1) ? 0 : 1, 1, 0, 0, 0, 0, 0, 0, cnt-1);

				/*
				//// Ben
				fp = fopen("testusb.txt", "a");
				Bit8u data[18];
				BX_MEM_READ_PHYSICAL(td->dword3, 8, data);
				for (int ii=0; ii<cnt; ii++) {
					if (ii==0)
						fprintf(fp, "\n 0x%02X", data[ii]);
					else
						fprintf(fp, " 0x%02X", data[ii]);
				}
				fclose(fp);
				*/
	bx_gui->statusbar_setitem(BX_USB_THIS hub[0].statusbar_id[0], FALSE);




				break;
			case TOKEN_OUT: // data should go from Host to HC
				BX_INFO(("TOKEN_OUT: maxlen = 0x%X  0x%08X", (maxlen-1) & 0x7FF, td->dword0));
				
				if (maxlen == 0) {

					BX_USB_THIS set_status(td, 0, 1, 0, 0, 0, 0, 0, 0, 0x7FF); 

				} else {



				}


				break;
			case TOKEN_SETUP:
				switch (rp->request) {
					case GET_STATUS:
						BX_INFO(("Request: GET_STATUS"));
						
						break;
					case CLEAR_FEATURE:
						BX_INFO(("Request: CLEAR_FEATURE"));
						
						break;
					case SET_FEATURE:
						BX_INFO(("Request: SET_FEATURE"));
						
						break;
					case SET_ADDRESS:
						BX_INFO(("Request: SET_ADDRESS: %04x", rp->value));
						BX_USB_THIS set_status(td, 0, 1, 0, 0, 0, 0, 0, 0, 0x007);
						g_dev = dev;
						g_address = (Bit8u) (rp->value & 0xFF);
						break;
					case GET_DESCRIPTOR:
						BX_INFO(("Request: GET_DESCRIPTOR: %02x  %02x  %i", rp->value >> 8, rp->value & 0xFF, rp->length));
						BX_USB_THIS GetDescriptor(dev, endpt, rp, d);
						BX_USB_THIS set_status(td, 0, 1, 0, 0, 0, 0, 0, 0, 0x007); // an 8 byte packet was received
						break;
					case SET_DESCRIPTOR:
						BX_INFO(("Request: SET_DESCRIPTOR: %02x", rp->value >> 8));
						//dev->descriptor = (Bit8u) rp->value >> 8;
						//td->dword1 = 0x07FF;
						break;
					case GET_CONFIGURATION:
						BX_INFO(("Request: GET_CONFIGURATION"));
						BX_USB_THIS GetDescriptor(dev, endpt, rp, d);
						//td->dword1 = 0x0007;  // an 8 byte packet was received
						break;
					case SET_CONFIGURATION:
						BX_INFO(("Request: SET_CONFIGURATION: %02x", rp->value));
						dev->config = (Bit8u) (rp->value & 0xFF);
						BX_USB_THIS set_status(td, 0, 1, 0, 0, 0, 0, 0, 0, 0x007); // an 8 byte packet was received
						break;
					case GET_INTERFACE:
						//BX_INFO(("Request: GET_INTERFACE*  %02X  %02X  %02X", rp->value >> 8, rp->value & 0xFF, rp->length));
						BX_USB_THIS set_status(td, 0, 1, 0, 0, 0, 0, 0, 0, 0x007); // an 8 byte packet was received
						break;
					case SET_INTERFACE:
						BX_INFO(("Request: SET_INTERFACE: %02x (alt %02x)", rp->index, rp->value));
						dev->Interface = (Bit8u) (rp->index & 0xFF);
						dev->alt_interface = (Bit8u) (rp->value & 0xFF);
						BX_USB_THIS set_status(td, 0, 1, 0, 0, 0, 0, 0, 0, 0x007); // an 8 byte packet was received
						break;
					case SYNCH_FRAME:
						BX_INFO(("Request: SYNCH_FRAME"));
						break;
					default:
						BX_ERROR((" **** illegal or unknown REQUEST sent to Host Controller:  %02x", data[1]));
				}
				break;
			default:
				BX_ERROR(("illegal PID sent to Host Controller:  %02x", pid));
		}
	} else {
		BX_USB_THIS set_status(td, 0, 1, 0, 0, 0, 0, 0, 0, 0x7FF);  /// needed???
	}
}

void bx_pciusb_c::GetDescriptor(struct USB_DEVICE *dev, Bit8u endpt, struct REQUEST_PACKET *packet, Bit8u d) {

	//printf("*****GET DESCRIPTOR  %02X  endpt = %02X\n", packet->value >> 8, endpt);
		
	if (endpt > dev->functions) {
		BX_ERROR(("Function %i on device %i not present", dev->address, endpt));
		return;
	}
	switch (packet->value >> 8) {
		case DEVICE:
			dev->function[endpt].in = (Bit8u *) &dev->function[endpt].device_descr;
			dev->function[endpt].in_cnt = 18;
			break;
		case CONFIG:
			dev->function[endpt].in = (Bit8u *) &dev->function[endpt].device_config[0];
			dev->function[endpt].in_cnt = 59;
			break;
		case STRING:
			switch (packet->value & 0xFF) {
				case 0: // string descriptor table
					dev->function[endpt].in = (Bit8u *) &dev->function[endpt].str_descriptor;
					dev->function[endpt].in_cnt = 4;
					break;
				case 1: // first string
					dev->function[endpt].in = (Bit8u *) &dev->function[endpt].string[0];
					dev->function[endpt].in_cnt = 24;
					break;
				case 2: // second string
					dev->function[endpt].in = (Bit8u *) &dev->function[endpt].string[1];
					dev->function[endpt].in_cnt = 36;
					break;
				default:
					
					BX_ERROR(("STRING:  %i", packet->value & 0xFF));
					/////
					break;
			}
			break;
		case INTERFACE:

			break;
		case ENDPOINT:
			
			break;
		case DEVICE_QUALIFIER:

			break;
		case OTHER_SPEED_CONFIG:
			
			break;
		case INTERFACE_POWER:

			break;
		case 0x22:  // unknown (Win98SE sends it)
			dev->function[endpt].in = (Bit8u *) &dev->function[endpt].interfaces;
			dev->function[endpt].in_cnt = packet->length;
			break;
		default:
			BX_ERROR((" **** illegal or unknown GET_DESCRIPTOR::DEVICE sent to Host Controller:  %02x", packet->value >> 8));
			return;
	}
}

void bx_pciusb_c::set_status(struct TD *td, bx_bool spd, bx_bool ls, 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
	td->dword1 &= 0xDB00F800;
	
	// now set the bits according to the passed param's
	td->dword1 |= spd               ? (1<<29) : 0; // spd
	td->dword1 |= ls                ? (1<<26) : 0; // lowspeed
	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
}
 
  // static pci configuration space read callback handler
  // redirects to non-static class handler to avoid virtual functions

  Bit32u
bx_pciusb_c::pci_read_handler(void *this_ptr, Bit8u address, unsigned io_len)
{
#if !BX_USE_PCIUSB_SMF
  bx_pciusb_c *class_ptr = (bx_pciusb_c *) this_ptr;

  return class_ptr->pci_read(address, io_len);
}


  Bit32u
bx_pciusb_c::pci_read(Bit8u address, unsigned io_len)
{
#else
  UNUSED(this_ptr);
#endif // !BX_USE_PCIUSB_SMF

  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(("Experimental USB PCI read register 0x%02x %svalue 0x%s", address, pszName, szTmp));
//  BX_INFO(("Experimental USB PCI read register 0x%02x %svalue 0x%s", address, pszName, szTmp));
  return value;
}


  // static pci configuration space write callback handler
  // redirects to non-static class handler to avoid virtual functions

  void
bx_pciusb_c::pci_write_handler(void *this_ptr, Bit8u address, Bit32u value, unsigned io_len)
{
#if !BX_USE_PCIUSB_SMF
  bx_pciusb_c *class_ptr = (bx_pciusb_c *) this_ptr;

  class_ptr->pci_write(address, value, io_len);
}

  void
bx_pciusb_c::pci_write(Bit8u address, Bit32u value, unsigned io_len)
{
#else
  UNUSED(this_ptr);
#endif // !BX_USE_PCIUSB_SMF

  if (io_len > 4 || io_len == 0) {
    BX_ERROR(("Experimental USB PCI write register 0x%02x, len=%u !",
             (unsigned) address, (unsigned) io_len));
    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';
  for (unsigned i=0; i<io_len; i++) {
    const Bit8u value8 = (value >> (i*8)) & 0xFF;
    switch (address+i) {
      case 0x20: // Base address
        BX_USB_THIS hub[0].pci_conf[address+i] = (value8 & 0xe0) | 0x01;
        sprintf(szTmp2, "%02x", (value8 & 0xe0) | 0x01);
        break;
      case 0x10: // Reserved
      case 0x11: //
      case 0x12: //
      case 0x13: //
      case 0x14: //
      case 0x15: //
      case 0x16: //
      case 0x17: //
      case 0x18: //
      case 0x19: //
      case 0x1a: //
      case 0x1b: //
      case 0x1c: //
      case 0x1d: //
      case 0x1e: //
      case 0x1f: //
      case 0x22: // Always 0
      case 0x23: //
      case 0x24: // Reserved
      case 0x25: //
      case 0x26: //
      case 0x27: //
      case 0x30: // Oh, no, you're not writing to rom_base!
      case 0x31: //
      case 0x32: //
      case 0x33: //
      case 0x3d: //
      case 0x05: // disallowing write to command hi-byte
      case 0x06: // disallowing write to status lo-byte (is that expected?)
        strcpy(szTmp2, "..");
        break;
      default:
        BX_USB_THIS hub[0].pci_conf[address+i] = value8;
        sprintf(szTmp2, "%02x", value8);
    }
    strrev(szTmp2);
    strcat(szTmp, szTmp2);
  }
  strrev(szTmp);
//  BX_DEBUG(("Experimental USB PCI write register 0x%02x value 0x%s", address, szTmp));
//  BX_INFO(("Experimental USB PCI write register 0x%02x value 0x%08x (%i)", address, value, io_len));
}

#endif // BX_PCI_SUPPORT && BX_PCI_USB_SUPPORT
