2009-01-19 20:18:57 +03:00
|
|
|
/////////////////////////////////////////////////////////////////////////
|
2009-02-26 21:43:11 +03:00
|
|
|
// $Id: usb_ohci.cc,v 1.10 2009-02-26 18:43:10 vruppert Exp $
|
2009-01-19 20:18:57 +03:00
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Copyright (C) 2009 Benjamin D Lunt (fys at frontiernet net)
|
|
|
|
//
|
|
|
|
// 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
|
2009-02-08 12:05:52 +03:00
|
|
|
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
2009-02-08 00:05:31 +03:00
|
|
|
/////////////////////////////////////////////////////////////////////////
|
2009-01-19 20:18:57 +03:00
|
|
|
|
|
|
|
// Experimental USB OHCI adapter
|
|
|
|
|
|
|
|
// Notes: See usb_common.cc
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
2009-02-05 19:53:44 +03:00
|
|
|
|
|
|
|
// TODO:
|
|
|
|
// if not Operational, stop the two timers to free cpu bandwidth,
|
|
|
|
// then start them again when in Operational? (Maybe only if not
|
|
|
|
// in operational for more than 10 frames.
|
|
|
|
|
|
|
|
|
2009-01-19 20:18:57 +03:00
|
|
|
#include "iodev.h"
|
|
|
|
|
|
|
|
#if BX_SUPPORT_PCI && BX_SUPPORT_USB_OHCI
|
|
|
|
|
|
|
|
#include "pci.h"
|
|
|
|
#include "usb_common.h"
|
|
|
|
#include "usb_hid.h"
|
|
|
|
#include "usb_msd.h"
|
|
|
|
#include "usb_ohci.h"
|
|
|
|
|
|
|
|
#define LOG_THIS theUSB_OHCI->
|
|
|
|
|
|
|
|
bx_usb_ohci_c* theUSB_OHCI = NULL;
|
|
|
|
|
2009-02-14 13:06:20 +03:00
|
|
|
const char *usb_ohci_port_name[] = {
|
2009-02-05 19:53:44 +03:00
|
|
|
"HCRevision ",
|
|
|
|
"HCControl ",
|
|
|
|
"HCCommandStatus ",
|
|
|
|
"HCInterruptStatus ",
|
|
|
|
"HCInterruptEnable ",
|
|
|
|
"HCInterruptDisable",
|
|
|
|
"HCHCCA ",
|
|
|
|
"HCPeriodCurrentED ",
|
|
|
|
"HCControlHeadED ",
|
|
|
|
"HCControlCurrentED",
|
|
|
|
"HCBulkHeadED ",
|
|
|
|
"HCBulkCurrentED ",
|
|
|
|
"HCDoneHead ",
|
|
|
|
"HCFmInterval ",
|
|
|
|
"HCFmRemaining ",
|
|
|
|
"HCFmNumber ",
|
|
|
|
"HCPeriodicStart ",
|
|
|
|
"HCLSThreshold ",
|
|
|
|
"HCRhDescriptorA ",
|
|
|
|
"HCRhDescriptorB ",
|
|
|
|
"HCRhStatus ",
|
|
|
|
"HCRhPortStatus0 ",
|
|
|
|
"HCRhPortStatus1 ",
|
|
|
|
"HCRhPortStatus2 ",
|
|
|
|
"HCRhPortStatus3 ",
|
|
|
|
" **unknown** "
|
|
|
|
};
|
|
|
|
|
2009-01-19 20:18:57 +03:00
|
|
|
int libusb_ohci_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
|
|
|
|
{
|
|
|
|
theUSB_OHCI = new bx_usb_ohci_c();
|
|
|
|
bx_devices.pluginUSB_OHCI = theUSB_OHCI;
|
|
|
|
BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theUSB_OHCI, BX_PLUGIN_USB_OHCI);
|
|
|
|
return 0; // Success
|
|
|
|
}
|
|
|
|
|
|
|
|
void libusb_ohci_LTX_plugin_fini(void)
|
|
|
|
{
|
|
|
|
delete theUSB_OHCI;
|
|
|
|
}
|
|
|
|
|
|
|
|
bx_usb_ohci_c::bx_usb_ohci_c()
|
|
|
|
{
|
|
|
|
put("OHCI");
|
|
|
|
memset((void*)&hub, 0, sizeof(bx_usb_ohci_t));
|
|
|
|
device_buffer = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bx_usb_ohci_c::~bx_usb_ohci_c()
|
|
|
|
{
|
|
|
|
if (BX_OHCI_THIS device_buffer != NULL)
|
|
|
|
delete [] BX_OHCI_THIS device_buffer;
|
|
|
|
|
2009-02-25 21:18:57 +03:00
|
|
|
for (int i=0; i<USB_NUM_PORTS; i++) {
|
2009-02-26 21:43:11 +03:00
|
|
|
remove_device(i);
|
2009-01-19 20:18:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
SIM->get_param_string(BXPN_OHCI_PORT1)->set_handler(NULL);
|
|
|
|
SIM->get_param_string(BXPN_OHCI_PORT2)->set_handler(NULL);
|
|
|
|
|
|
|
|
BX_DEBUG(("Exit"));
|
|
|
|
}
|
|
|
|
|
|
|
|
void bx_usb_ohci_c::init(void)
|
|
|
|
{
|
2009-02-25 21:18:57 +03:00
|
|
|
unsigned i;
|
|
|
|
|
2009-01-19 20:18:57 +03:00
|
|
|
BX_OHCI_THIS device_buffer = new Bit8u[65536];
|
|
|
|
|
2009-02-05 19:53:44 +03:00
|
|
|
// Call our frame timer routine every 1mS (1,000uS)
|
2009-01-19 20:18:57 +03:00
|
|
|
// Continuous and active
|
2009-02-05 19:53:44 +03:00
|
|
|
BX_OHCI_THIS hub.frame_index =
|
|
|
|
bx_pc_system.register_timer(this, usb_frame_handler, 1000, 1,1, "ohci.frame_timer");
|
|
|
|
|
|
|
|
// Call our interval timer routine every 1uS
|
|
|
|
// Continuous and active
|
|
|
|
BX_OHCI_THIS hub.interval_index =
|
|
|
|
bx_pc_system.register_timer(this, usb_interval_handler, 1, 1,1, "ohci.interval_timer");
|
2009-01-19 20:18:57 +03:00
|
|
|
|
|
|
|
BX_OHCI_THIS hub.devfunc = 0x00;
|
|
|
|
DEV_register_pci_handlers(this, &BX_OHCI_THIS hub.devfunc, BX_PLUGIN_USB_OHCI,
|
|
|
|
"Experimental USB OHCI");
|
|
|
|
|
2009-02-25 21:18:57 +03:00
|
|
|
for (i=0; i<256; i++)
|
2009-01-19 20:18:57 +03:00
|
|
|
BX_OHCI_THIS hub.pci_conf[i] = 0x0;
|
|
|
|
|
|
|
|
BX_OHCI_THIS hub.base_addr = 0x0;
|
2009-02-05 19:53:44 +03:00
|
|
|
BX_OHCI_THIS hub.ohci_done_count = 7;
|
2009-01-19 20:18:57 +03:00
|
|
|
|
|
|
|
//FIXME: for now, we want a status bar // hub zero, port zero
|
|
|
|
BX_OHCI_THIS hub.statusbar_id[0] = bx_gui->register_statusitem("OHCI");
|
|
|
|
|
|
|
|
SIM->get_param_string(BXPN_OHCI_PORT1)->set_handler(usb_param_handler);
|
|
|
|
SIM->get_param_string(BXPN_OHCI_PORT1)->set_runtime_param(1);
|
|
|
|
SIM->get_param_string(BXPN_OHCI_PORT2)->set_handler(usb_param_handler);
|
|
|
|
SIM->get_param_string(BXPN_OHCI_PORT2)->set_runtime_param(1);
|
|
|
|
|
2009-02-25 21:18:57 +03:00
|
|
|
for (i=0; i<USB_NUM_PORTS; i++) {
|
|
|
|
BX_OHCI_THIS hub.usb_port[i].device = NULL;
|
|
|
|
}
|
|
|
|
BX_OHCI_THIS mousedev = NULL;
|
|
|
|
BX_OHCI_THIS keybdev = NULL;
|
|
|
|
|
2009-01-19 20:18:57 +03:00
|
|
|
//HACK: Turn on debug messages from the start
|
|
|
|
//BX_OHCI_THIS setonoff(LOGLEV_DEBUG, ACT_REPORT);
|
|
|
|
|
|
|
|
BX_INFO(("USB OHCI initialized"));
|
|
|
|
}
|
|
|
|
|
|
|
|
void bx_usb_ohci_c::reset(unsigned type)
|
|
|
|
{
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
if (type == BX_RESET_HARDWARE) {
|
|
|
|
static const struct reset_vals_t {
|
|
|
|
unsigned addr;
|
|
|
|
unsigned char val;
|
|
|
|
} reset_vals[] = {
|
|
|
|
{ 0x00, 0xC1 }, { 0x01, 0x11 }, // 0x11C1 = vendor
|
|
|
|
{ 0x02, 0x03 }, { 0x03, 0x58 }, // 0x5803 = device
|
|
|
|
{ 0x04, 0x06 }, { 0x05, 0x00 }, // command_io
|
|
|
|
{ 0x06, 0x10 }, { 0x07, 0x02 }, // status (bit 4 = 1, has capabilities list.)
|
|
|
|
{ 0x08, 0x11 }, // revision number
|
|
|
|
{ 0x09, 0x10 }, // interface
|
|
|
|
{ 0x0a, 0x03 }, // class_sub USB Host Controller
|
|
|
|
{ 0x0b, 0x0c }, // class_base Serial Bus Controller
|
|
|
|
{ 0x0D, 0x40 }, // bus latency
|
|
|
|
{ 0x0e, 0x00 }, // header_type_generic
|
|
|
|
|
|
|
|
// address space 0x10 - 0x13
|
|
|
|
{ 0x10, 0x00 }, { 0x11, 0x50 }, //
|
|
|
|
{ 0x12, 0x00 }, { 0x13, 0xE1 }, //
|
|
|
|
|
|
|
|
{ 0x2C, 0xC1 }, { 0x2D, 0x11 }, // subsystem vendor ID
|
|
|
|
{ 0x2E, 0x03 }, { 0x2F, 0x58 }, // subsystem ID
|
|
|
|
|
|
|
|
{ 0x34, 0x50 }, // offset of capabilities list within configuration space
|
|
|
|
|
|
|
|
{ 0x3c, 0x0B }, // IRQ
|
|
|
|
{ 0x3d, BX_PCI_INTD }, // INT
|
|
|
|
{ 0x3E, 0x03 }, // minimum time bus master needs PCI bus ownership, in 250ns units
|
|
|
|
{ 0x3F, 0x56 }, // maximum latency, in 250ns units (bus masters only) (read-only)
|
|
|
|
|
|
|
|
// capabilities list:
|
|
|
|
{ 0x50, 0x01 }, //
|
|
|
|
{ 0x51, 0x00 }, //
|
|
|
|
{ 0x52, 0x02 }, //
|
|
|
|
{ 0x53, 0x76 }, //
|
|
|
|
{ 0x54, 0x00 }, //
|
|
|
|
{ 0x55, 0x20 }, //
|
|
|
|
{ 0x56, 0x00 }, //
|
|
|
|
{ 0x57, 0x1F }, //
|
|
|
|
};
|
|
|
|
for (i = 0; i < sizeof(reset_vals) / sizeof(*reset_vals); ++i) {
|
|
|
|
BX_OHCI_THIS hub.pci_conf[reset_vals[i].addr] = reset_vals[i].val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-05 19:53:44 +03:00
|
|
|
BX_OHCI_THIS reset_hc();
|
|
|
|
|
2009-02-25 21:18:57 +03:00
|
|
|
if (BX_OHCI_THIS hub.usb_port[0].device == NULL) {
|
|
|
|
init_device(0, SIM->get_param_string(BXPN_OHCI_PORT1)->getptr());
|
|
|
|
}
|
|
|
|
if (BX_OHCI_THIS hub.usb_port[1].device == NULL) {
|
|
|
|
init_device(1, SIM->get_param_string(BXPN_OHCI_PORT2)->getptr());
|
|
|
|
}
|
2009-02-05 19:53:44 +03:00
|
|
|
}
|
|
|
|
|
2009-02-14 13:06:20 +03:00
|
|
|
void bx_usb_ohci_c::reset_hc()
|
|
|
|
{
|
2009-02-05 19:53:44 +03:00
|
|
|
int i;
|
|
|
|
|
2009-01-19 20:18:57 +03:00
|
|
|
// reset locals
|
2009-02-05 19:53:44 +03:00
|
|
|
BX_OHCI_THIS hub.ohci_done_count = 7;
|
2009-01-19 20:18:57 +03:00
|
|
|
|
|
|
|
// HcRevision
|
2009-02-26 21:43:11 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcRevision = 0x0110;
|
2009-01-19 20:18:57 +03:00
|
|
|
|
|
|
|
// HcControl
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcControl.reserved = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcControl.rwe = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcControl.rwc = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcControl.ir = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcControl.hcfs = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcControl.ble = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcControl.cle = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcControl.ie = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcControl.ple = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcControl.cbsr = 0;
|
|
|
|
|
|
|
|
// HcCommandStatus
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcCommandStatus.reserved0 = 0x000000;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcCommandStatus.soc = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcCommandStatus.reserved1 = 0x000000;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcCommandStatus.ocr = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcCommandStatus.blf = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcCommandStatus.clf = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcCommandStatus.hcr = 0;
|
|
|
|
|
|
|
|
// HcInterruptStatus
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcInterruptStatus = 0x00000000;
|
2009-01-19 20:18:57 +03:00
|
|
|
|
|
|
|
// HcInterruptEnable
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcInterruptEnable = OHCI_INTR_MIE;
|
2009-01-19 20:18:57 +03:00
|
|
|
|
|
|
|
// HcHCCA
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcHCCA = 0x00000000;
|
2009-01-19 20:18:57 +03:00
|
|
|
|
|
|
|
// HcPeriodCurrentED
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcPeriodCurrentED = 0x00000000;
|
2009-01-19 20:18:57 +03:00
|
|
|
|
|
|
|
// HcControlHeadED
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcControlHeadED = 0x00000000;
|
2009-01-19 20:18:57 +03:00
|
|
|
|
|
|
|
// HcControlCurrentED
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcControlCurrentED = 0x00000000;
|
2009-01-19 20:18:57 +03:00
|
|
|
|
|
|
|
// HcBulkHeadED
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcBulkHeadED = 0x00000000;
|
2009-01-19 20:18:57 +03:00
|
|
|
|
|
|
|
// HcBulkCurrentED
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcBulkCurrentED = 0x00000000;
|
2009-01-19 20:18:57 +03:00
|
|
|
|
|
|
|
// HcDoneHead
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcDoneHead = 0x00000000;
|
2009-01-19 20:18:57 +03:00
|
|
|
|
|
|
|
// HcFmInterval
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcFmInterval.fit = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcFmInterval.fsmps = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcFmInterval.reserved = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcFmInterval.fi = 0x2EDF;
|
|
|
|
|
|
|
|
// HcFmRemaining
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcFmRemaining.frt = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcFmRemaining.reserved = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcFmRemaining.fr = 0x0000;
|
|
|
|
|
|
|
|
// HcFmNumber
|
2009-02-26 21:43:11 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcFmNumber = 0x00000000;
|
2009-01-19 20:18:57 +03:00
|
|
|
|
|
|
|
// HcPeriodicStart
|
2009-02-26 21:43:11 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcPeriodicStart = 0x00000000;
|
2009-01-19 20:18:57 +03:00
|
|
|
|
|
|
|
// HcLSThreshold
|
2009-02-26 21:43:11 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcLSThreshold = 0x0628;
|
2009-01-19 20:18:57 +03:00
|
|
|
|
|
|
|
// HcRhDescriptorA
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.potpgt = 0x10;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.reserved = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.nocp = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.ocpm = 1;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.dt = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.nps = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.psm = 1;
|
2009-02-05 19:53:44 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.ndp = USB_NUM_PORTS;
|
2009-01-19 20:18:57 +03:00
|
|
|
|
|
|
|
// HcRhDescriptorB
|
2009-02-05 19:53:44 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcRhDescriptorB.ppcm = 0x0000 | ((USB_NUM_PORTS == 1) ? 2 : 0) | ((USB_NUM_PORTS == 2) ? 4 : 0);
|
2009-01-19 20:18:57 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcRhDescriptorB.dr = 0x0000;
|
|
|
|
|
|
|
|
// HcRhStatus
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcRhStatus.crwe = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcRhStatus.reserved0 = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcRhStatus.ocic = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcRhStatus.lpsc = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcRhStatus.drwe = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcRhStatus.reserved1 = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcRhStatus.oci = 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcRhStatus.lps = 0;
|
|
|
|
|
|
|
|
// HcRhPortStatus[x]
|
2009-02-05 19:53:44 +03:00
|
|
|
for (i=0; i<USB_NUM_PORTS; i++) {
|
|
|
|
reset_port(i);
|
|
|
|
if (BX_OHCI_THIS hub.usb_port[i].device != NULL) {
|
2009-02-25 21:18:57 +03:00
|
|
|
usb_set_connect_status(i, BX_OHCI_THIS hub.usb_port[i].device->get_type(), 1);
|
2009-02-05 19:53:44 +03:00
|
|
|
}
|
2009-01-19 20:18:57 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-14 13:06:20 +03:00
|
|
|
void bx_usb_ohci_c::reset_port(int p)
|
|
|
|
{
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.reserved0 = 0;
|
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.prsc = 0;
|
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.ocic = 0;
|
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.pssc = 0;
|
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.pesc = 0;
|
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.csc = 0;
|
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.reserved1 = 0;
|
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.lsda = 0;
|
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.pps = 0;
|
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.reserved2 = 0;
|
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.prs = 0;
|
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.poci = 0;
|
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.pss = 0;
|
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.pes = 0;
|
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.ccs = 0;
|
2009-02-05 19:53:44 +03:00
|
|
|
}
|
|
|
|
|
2009-01-19 20:18:57 +03:00
|
|
|
void bx_usb_ohci_c::register_state(void)
|
|
|
|
{
|
2009-02-14 13:06:20 +03:00
|
|
|
unsigned i;
|
|
|
|
char portnum[8];
|
2009-02-24 20:15:27 +03:00
|
|
|
bx_list_c *hub, *port, *pstatus;
|
2009-01-19 20:18:57 +03:00
|
|
|
|
2009-02-24 20:15:27 +03:00
|
|
|
// TODO: completion of save/restore support
|
2009-01-19 20:18:57 +03:00
|
|
|
bx_list_c *list = new bx_list_c(SIM->get_bochs_root(), "usb_ohci", "USB OHCI State");
|
2009-02-26 21:43:11 +03:00
|
|
|
hub = new bx_list_c(list, "hub", 15);
|
2009-02-24 20:15:27 +03:00
|
|
|
new bx_shadow_num_c(hub, "HcInterruptStatus", &BX_OHCI_THIS hub.op_regs.HcInterruptStatus, BASE_HEX);
|
|
|
|
new bx_shadow_num_c(hub, "HcInterruptEnable", &BX_OHCI_THIS hub.op_regs.HcInterruptEnable, BASE_HEX);
|
|
|
|
new bx_shadow_num_c(hub, "HcHCCA", &BX_OHCI_THIS hub.op_regs.HcHCCA, BASE_HEX);
|
|
|
|
new bx_shadow_num_c(hub, "HcPeriodCurrentED", &BX_OHCI_THIS hub.op_regs.HcPeriodCurrentED, BASE_HEX);
|
|
|
|
new bx_shadow_num_c(hub, "HcControlHeadED", &BX_OHCI_THIS hub.op_regs.HcControlHeadED, BASE_HEX);
|
|
|
|
new bx_shadow_num_c(hub, "HcControlCurrentED", &BX_OHCI_THIS hub.op_regs.HcControlCurrentED, BASE_HEX);
|
|
|
|
new bx_shadow_num_c(hub, "HcBulkHeadED", &BX_OHCI_THIS hub.op_regs.HcBulkHeadED, BASE_HEX);
|
|
|
|
new bx_shadow_num_c(hub, "HcBulkCurrentED", &BX_OHCI_THIS hub.op_regs.HcBulkCurrentED, BASE_HEX);
|
|
|
|
new bx_shadow_num_c(hub, "HcDoneHead", &BX_OHCI_THIS hub.op_regs.HcDoneHead, BASE_HEX);
|
2009-02-26 21:43:11 +03:00
|
|
|
new bx_shadow_num_c(hub, "HcFmNumber", &BX_OHCI_THIS hub.op_regs.HcFmNumber, BASE_HEX);
|
|
|
|
new bx_shadow_num_c(hub, "HcPeriodicStart", &BX_OHCI_THIS hub.op_regs.HcPeriodicStart, BASE_HEX);
|
2009-02-14 13:06:20 +03:00
|
|
|
for (i=0; i<USB_NUM_PORTS; i++) {
|
|
|
|
sprintf(portnum, "port%d", i+1);
|
|
|
|
port = new bx_list_c(hub, portnum, 2);
|
2009-02-24 20:15:27 +03:00
|
|
|
pstatus = new bx_list_c(port, "HcRhPortStatus", 12);
|
|
|
|
new bx_shadow_bool_c(pstatus, "prsc", &BX_OHCI_THIS hub.usb_port[i].HcRhPortStatus.prsc);
|
|
|
|
new bx_shadow_bool_c(pstatus, "ocic", &BX_OHCI_THIS hub.usb_port[i].HcRhPortStatus.ocic);
|
|
|
|
new bx_shadow_bool_c(pstatus, "pssc", &BX_OHCI_THIS hub.usb_port[i].HcRhPortStatus.pssc);
|
|
|
|
new bx_shadow_bool_c(pstatus, "pesc", &BX_OHCI_THIS hub.usb_port[i].HcRhPortStatus.pesc);
|
|
|
|
new bx_shadow_bool_c(pstatus, "csc", &BX_OHCI_THIS hub.usb_port[i].HcRhPortStatus.csc);
|
|
|
|
new bx_shadow_bool_c(pstatus, "lsda", &BX_OHCI_THIS hub.usb_port[i].HcRhPortStatus.lsda);
|
|
|
|
new bx_shadow_bool_c(pstatus, "pps", &BX_OHCI_THIS hub.usb_port[i].HcRhPortStatus.pps);
|
|
|
|
new bx_shadow_bool_c(pstatus, "prs", &BX_OHCI_THIS hub.usb_port[i].HcRhPortStatus.prs);
|
|
|
|
new bx_shadow_bool_c(pstatus, "poci", &BX_OHCI_THIS hub.usb_port[i].HcRhPortStatus.poci);
|
|
|
|
new bx_shadow_bool_c(pstatus, "pss", &BX_OHCI_THIS hub.usb_port[i].HcRhPortStatus.pss);
|
|
|
|
new bx_shadow_bool_c(pstatus, "pes", &BX_OHCI_THIS hub.usb_port[i].HcRhPortStatus.pes);
|
|
|
|
new bx_shadow_bool_c(pstatus, "ccs", &BX_OHCI_THIS hub.usb_port[i].HcRhPortStatus.ccs);
|
2009-01-19 20:18:57 +03:00
|
|
|
// empty list for USB device state
|
|
|
|
new bx_list_c(port, "device", 20);
|
|
|
|
}
|
2009-02-26 21:43:11 +03:00
|
|
|
new bx_shadow_num_c(hub, "ohci_done_count", &BX_OHCI_THIS hub.ohci_done_count, BASE_DEC);
|
2009-01-19 20:18:57 +03:00
|
|
|
register_pci_state(hub, BX_OHCI_THIS hub.pci_conf);
|
|
|
|
}
|
|
|
|
|
|
|
|
void bx_usb_ohci_c::after_restore_state(void)
|
|
|
|
{
|
|
|
|
if (DEV_pci_set_base_mem(BX_OHCI_THIS_PTR, read_handler, write_handler,
|
|
|
|
&BX_OHCI_THIS hub.base_addr,
|
|
|
|
&BX_OHCI_THIS hub.pci_conf[0x10],
|
|
|
|
4096)) {
|
|
|
|
BX_INFO(("new base address: 0x%04x", BX_OHCI_THIS hub.base_addr));
|
|
|
|
}
|
2009-02-05 19:53:44 +03:00
|
|
|
for (int j=0; j<USB_NUM_PORTS; j++) {
|
2009-01-19 20:18:57 +03:00
|
|
|
if (BX_OHCI_THIS hub.usb_port[j].device != NULL) {
|
|
|
|
BX_OHCI_THIS hub.usb_port[j].device->after_restore_state();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void bx_usb_ohci_c::init_device(Bit8u port, const char *devname)
|
|
|
|
{
|
|
|
|
usbdev_type type = USB_DEV_TYPE_NONE;
|
|
|
|
char pname[BX_PATHNAME_LEN];
|
|
|
|
|
|
|
|
if (!strlen(devname) || !strcmp(devname, "none")) return;
|
|
|
|
|
2009-02-15 17:06:55 +03:00
|
|
|
if (BX_OHCI_THIS hub.usb_port[port].device != NULL) {
|
|
|
|
BX_ERROR(("init_device(): port%d already in use", port+1));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-01-19 20:18:57 +03:00
|
|
|
if (!strcmp(devname, "mouse")) {
|
|
|
|
type = USB_DEV_TYPE_MOUSE;
|
|
|
|
BX_OHCI_THIS hub.usb_port[port].device = new usb_hid_device_c(type);
|
|
|
|
if (BX_OHCI_THIS mousedev == NULL) {
|
|
|
|
BX_OHCI_THIS mousedev = (usb_hid_device_c*)BX_OHCI_THIS hub.usb_port[port].device;
|
|
|
|
}
|
|
|
|
} else if (!strcmp(devname, "tablet")) {
|
|
|
|
type = USB_DEV_TYPE_TABLET;
|
|
|
|
BX_OHCI_THIS hub.usb_port[port].device = new usb_hid_device_c(type);
|
|
|
|
if (BX_OHCI_THIS mousedev == NULL) {
|
|
|
|
BX_OHCI_THIS mousedev = (usb_hid_device_c*)BX_OHCI_THIS hub.usb_port[port].device;
|
|
|
|
}
|
|
|
|
} else if (!strcmp(devname, "keypad")) {
|
|
|
|
type = USB_DEV_TYPE_KEYPAD;
|
|
|
|
BX_OHCI_THIS hub.usb_port[port].device = new usb_hid_device_c(type);
|
|
|
|
if (BX_OHCI_THIS keybdev == NULL) {
|
|
|
|
BX_OHCI_THIS keybdev = (usb_hid_device_c*)BX_OHCI_THIS hub.usb_port[port].device;
|
|
|
|
}
|
|
|
|
} else if (!strncmp(devname, "disk", 4)) {
|
|
|
|
if ((strlen(devname) > 5) && (devname[4] == ':')) {
|
|
|
|
type = USB_DEV_TYPE_DISK;
|
|
|
|
BX_OHCI_THIS hub.usb_port[port].device = new usb_msd_device_c();
|
|
|
|
} else {
|
|
|
|
BX_PANIC(("USB device 'disk' needs a filename separated with a colon"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
BX_PANIC(("unknown USB device: %s", devname));
|
|
|
|
return;
|
|
|
|
}
|
2009-01-20 00:39:03 +03:00
|
|
|
sprintf(pname, "usb_ohci.hub.port%d.device", port+1);
|
2009-01-19 20:18:57 +03:00
|
|
|
bx_list_c *devlist = (bx_list_c*)SIM->get_param(pname, SIM->get_bochs_root());
|
|
|
|
BX_OHCI_THIS hub.usb_port[port].device->register_state(devlist);
|
|
|
|
usb_set_connect_status(port, type, 1);
|
|
|
|
}
|
|
|
|
|
2009-02-26 21:43:11 +03:00
|
|
|
void bx_usb_ohci_c::remove_device(Bit8u port)
|
2009-02-14 13:06:20 +03:00
|
|
|
{
|
2009-02-26 21:43:11 +03:00
|
|
|
char pname[BX_PATHNAME_LEN];
|
|
|
|
int type;
|
|
|
|
|
|
|
|
if (BX_OHCI_THIS hub.usb_port[port].device != NULL) {
|
|
|
|
type = BX_OHCI_THIS hub.usb_port[port].device->get_type();
|
|
|
|
if ((type == USB_DEV_TYPE_MOUSE) ||
|
|
|
|
(type == USB_DEV_TYPE_TABLET)) {
|
|
|
|
if (BX_OHCI_THIS hub.usb_port[port].device == BX_OHCI_THIS mousedev) {
|
|
|
|
BX_OHCI_THIS mousedev = NULL;
|
|
|
|
}
|
|
|
|
} else if (type == USB_DEV_TYPE_KEYPAD) {
|
|
|
|
if (BX_OHCI_THIS hub.usb_port[port].device == BX_OHCI_THIS keybdev) {
|
|
|
|
BX_OHCI_THIS keybdev = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
delete BX_OHCI_THIS hub.usb_port[port].device;
|
|
|
|
BX_OHCI_THIS hub.usb_port[port].device = NULL;
|
|
|
|
sprintf(pname, "usb_ohci.hub.port%d.device", port+1);
|
|
|
|
bx_list_c *devlist = (bx_list_c*)SIM->get_param(pname, SIM->get_bochs_root());
|
|
|
|
devlist->clear();
|
|
|
|
}
|
2009-01-19 20:18:57 +03:00
|
|
|
}
|
|
|
|
|
2009-02-26 21:43:11 +03:00
|
|
|
void bx_usb_ohci_c::update_irq()
|
2009-02-14 13:06:20 +03:00
|
|
|
{
|
|
|
|
bx_bool level = 0;
|
|
|
|
|
2009-02-24 20:15:27 +03:00
|
|
|
if ((BX_OHCI_THIS hub.op_regs.HcInterruptEnable & OHCI_INTR_MIE) &&
|
|
|
|
(BX_OHCI_THIS hub.op_regs.HcInterruptStatus & BX_OHCI_THIS hub.op_regs.HcInterruptEnable)) {
|
2009-02-14 13:06:20 +03:00
|
|
|
level = 1;
|
|
|
|
BX_DEBUG(("Interrupt Fired."));
|
|
|
|
}
|
2009-02-26 21:43:11 +03:00
|
|
|
DEV_pci_set_irq(BX_OHCI_THIS hub.devfunc, BX_OHCI_THIS hub.pci_conf[0x3d], level);
|
|
|
|
}
|
2009-01-19 20:18:57 +03:00
|
|
|
|
2009-02-26 21:43:11 +03:00
|
|
|
void bx_usb_ohci_c::set_interrupt(Bit32u value)
|
|
|
|
{
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcInterruptStatus |= value;
|
|
|
|
update_irq();
|
2009-02-14 13:06:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bx_bool bx_usb_ohci_c::read_handler(bx_phy_address addr, unsigned len, void *data, void *param)
|
|
|
|
{
|
2009-01-19 20:18:57 +03:00
|
|
|
Bit32u val = 0x0;
|
2009-02-05 19:53:44 +03:00
|
|
|
int p = 0;
|
2009-01-19 20:18:57 +03:00
|
|
|
|
|
|
|
if (len != 4) {
|
|
|
|
BX_INFO(("Read at 0x%08X with len != 4 (%i)", addr, len));
|
|
|
|
return 1;
|
|
|
|
}
|
2009-02-26 21:43:11 +03:00
|
|
|
if (addr & 3) {
|
|
|
|
BX_INFO(("Misaligned read at 0x%08X", addr));
|
|
|
|
return 1;
|
|
|
|
}
|
2009-01-19 20:18:57 +03:00
|
|
|
|
|
|
|
Bit32u offset = addr - BX_OHCI_THIS hub.base_addr;
|
|
|
|
switch (offset) {
|
|
|
|
case 0x00: // HcRevision
|
2009-02-26 21:43:11 +03:00
|
|
|
val = BX_OHCI_THIS hub.op_regs.HcRevision;
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x04: // HcControl
|
2009-02-05 19:53:44 +03:00
|
|
|
val = (BX_OHCI_THIS hub.op_regs.HcControl.reserved << 11)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcControl.rwe ? 1 << 10 : 0)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcControl.rwc ? 1 << 9 : 0)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcControl.ir ? 1 << 8 : 0)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcControl.hcfs << 6)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcControl.ble ? 1 << 5 : 0)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcControl.cle ? 1 << 4 : 0)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcControl.ie ? 1 << 3 : 0)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcControl.ple ? 1 << 2 : 0)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcControl.cbsr << 0);
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x08: // HcCommandStatus
|
2009-02-05 19:53:44 +03:00
|
|
|
val = (BX_OHCI_THIS hub.op_regs.HcCommandStatus.reserved0 << 18)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcCommandStatus.soc << 16)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcCommandStatus.reserved1 << 4)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcCommandStatus.ocr ? 1 << 3 : 0)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcCommandStatus.blf ? 1 << 2 : 0)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcCommandStatus.clf ? 1 << 1 : 0)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcCommandStatus.hcr ? 1 << 0 : 0);
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x0C: // HcInterruptStatus
|
2009-02-24 20:15:27 +03:00
|
|
|
val = BX_OHCI_THIS hub.op_regs.HcInterruptStatus;
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x10: // HcInterruptEnable
|
|
|
|
case 0x14: // HcInterruptDisable (reading this one returns that one)
|
2009-02-24 20:15:27 +03:00
|
|
|
val = BX_OHCI_THIS hub.op_regs.HcInterruptEnable;
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x18: // HcHCCA
|
2009-02-24 20:15:27 +03:00
|
|
|
val = BX_OHCI_THIS hub.op_regs.HcHCCA;
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x1C: // HcPeriodCurrentED
|
2009-02-24 20:15:27 +03:00
|
|
|
val = BX_OHCI_THIS hub.op_regs.HcPeriodCurrentED;
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x20: // HcControlHeadED
|
2009-02-24 20:15:27 +03:00
|
|
|
val = BX_OHCI_THIS hub.op_regs.HcControlHeadED;
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x24: // HcControlCurrentED
|
2009-02-24 20:15:27 +03:00
|
|
|
val = BX_OHCI_THIS hub.op_regs.HcControlCurrentED;
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x28: // HcBulkHeadED
|
2009-02-24 20:15:27 +03:00
|
|
|
val = BX_OHCI_THIS hub.op_regs.HcBulkHeadED;
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x2C: // HcBulkCurrentED
|
2009-02-24 20:15:27 +03:00
|
|
|
val = BX_OHCI_THIS hub.op_regs.HcBulkCurrentED;
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x30: // HcDoneHead
|
2009-02-24 20:15:27 +03:00
|
|
|
val = BX_OHCI_THIS hub.op_regs.HcDoneHead;
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x34: // HcFmInterval
|
2009-02-05 19:53:44 +03:00
|
|
|
val = (BX_OHCI_THIS hub.op_regs.HcFmInterval.fit ? 1 << 31 : 0)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcFmInterval.fsmps << 16)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcFmInterval.reserved << 14)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcFmInterval.fi << 0);
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x38: // HcFmRemaining
|
2009-02-05 19:53:44 +03:00
|
|
|
val = (BX_OHCI_THIS hub.op_regs.HcFmRemaining.frt ? 1 << 31 : 0)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcFmRemaining.reserved << 14)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcFmRemaining.fr << 0);
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x3C: // HcFmNumber
|
2009-02-26 21:43:11 +03:00
|
|
|
val = BX_OHCI_THIS hub.op_regs.HcFmNumber;
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x40: // HcPeriodicStart
|
2009-02-26 21:43:11 +03:00
|
|
|
val = BX_OHCI_THIS hub.op_regs.HcPeriodicStart;
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x44: // HcLSThreshold
|
2009-02-26 21:43:11 +03:00
|
|
|
val = BX_OHCI_THIS hub.op_regs.HcLSThreshold;
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x48: // HcRhDescriptorA
|
2009-02-05 19:53:44 +03:00
|
|
|
val = (BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.potpgt << 24)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.reserved << 13)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.nocp ? 1 << 12 : 0)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.ocpm ? 1 << 11 : 0)
|
|
|
|
| 0 //BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.dt << 10
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.nps ? 1 << 9 : 0)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.psm ? 1 << 8 : 0)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.ndp << 0);
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x4C: // HcRhDescriptorB
|
2009-02-05 19:53:44 +03:00
|
|
|
val = (BX_OHCI_THIS hub.op_regs.HcRhDescriptorB.ppcm << 16)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcRhDescriptorB.dr << 0);
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x50: // HcRhStatus
|
2009-02-05 19:53:44 +03:00
|
|
|
val = (BX_OHCI_THIS hub.op_regs.HcRhStatus.crwe ? 1 << 31 : 0)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcRhStatus.reserved0 << 18)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcRhStatus.ocic ? 1 << 17 : 0)
|
|
|
|
| 0 //BX_OHCI_THIS hub.op_regs.HcRhStatus.lpsc << 16
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcRhStatus.drwe ? 1 << 15 : 0)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcRhStatus.reserved1 << 2)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcRhStatus.oci ? 1 << 1 : 0)
|
|
|
|
| (BX_OHCI_THIS hub.op_regs.HcRhStatus.lps ? 1 << 0 : 0);
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x60: // HcRhPortStatus[3]
|
2009-02-05 19:53:44 +03:00
|
|
|
#if (USB_NUM_PORTS < 4)
|
2009-01-19 20:18:57 +03:00
|
|
|
val = 0;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case 0x5C: // HcRhPortStatus[2]
|
2009-02-05 19:53:44 +03:00
|
|
|
#if (USB_NUM_PORTS < 3)
|
2009-01-19 20:18:57 +03:00
|
|
|
val = 0;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case 0x58: // HcRhPortStatus[1]
|
2009-02-05 19:53:44 +03:00
|
|
|
#if (USB_NUM_PORTS < 2)
|
2009-01-19 20:18:57 +03:00
|
|
|
val = 0;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case 0x54: // HcRhPortStatus[0]
|
2009-02-25 21:18:57 +03:00
|
|
|
p = (offset - 0x54) >> 2;
|
2009-02-24 20:15:27 +03:00
|
|
|
if (BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.pps == 1) {
|
|
|
|
val = (BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.reserved0 << 21)
|
|
|
|
| (BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.prsc ? (1 << 20) : 0)
|
|
|
|
| (BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.ocic ? (1 << 19) : 0)
|
|
|
|
| (BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.pssc ? (1 << 18) : 0)
|
|
|
|
| (BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.pesc ? (1 << 17) : 0)
|
|
|
|
| (BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.csc ? (1 << 16) : 0)
|
|
|
|
| (BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.reserved1 << 10)
|
|
|
|
| (BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.lsda ? (1 << 9) : 0)
|
|
|
|
| (BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.pps ? (1 << 8) : 0)
|
|
|
|
| (BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.reserved2 << 5)
|
|
|
|
| (BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.prs ? (1 << 4) : 0)
|
|
|
|
| (BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.poci ? (1 << 3) : 0)
|
|
|
|
| (BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.pss ? (1 << 2) : 0)
|
|
|
|
| (BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.pes ? (1 << 1) : 0)
|
|
|
|
| (BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.ccs ? (1 << 0) : 0);
|
2009-02-05 19:53:44 +03:00
|
|
|
} else
|
|
|
|
val = 0;
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
BX_ERROR(("unsupported read from address=0x%08X!", addr));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-02-05 19:53:44 +03:00
|
|
|
int name = offset >> 2;
|
|
|
|
if (name > (0x60 >> 2))
|
|
|
|
name = 25;
|
2009-02-14 13:06:20 +03:00
|
|
|
// BX_INFO(("register read from address 0x%04X (%s): 0x%08X (len=%i)", (unsigned) addr, usb_ohci_port_name[name], (Bit32u) val, len));
|
2009-01-19 20:18:57 +03:00
|
|
|
*((Bit32u *) data) = val;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2009-02-14 13:06:20 +03:00
|
|
|
bx_bool bx_usb_ohci_c::write_handler(bx_phy_address addr, unsigned len, void *data, void *param)
|
|
|
|
{
|
2009-01-19 20:18:57 +03:00
|
|
|
Bit32u value = *((Bit32u *) data);
|
2009-02-05 19:53:44 +03:00
|
|
|
Bit32u offset = addr - BX_OHCI_THIS hub.base_addr;
|
2009-01-19 20:18:57 +03:00
|
|
|
int p;
|
|
|
|
|
2009-02-05 19:53:44 +03:00
|
|
|
int name = offset >> 2;
|
|
|
|
if (name > (0x60 >> 2))
|
|
|
|
name = 25;
|
2009-02-14 13:06:20 +03:00
|
|
|
// BX_INFO(("register write to address 0x%04X (%s): 0x%08X (len=%i)", (unsigned) addr, usb_ohci_port_name[name], (unsigned) value, len));
|
2009-02-05 19:53:44 +03:00
|
|
|
|
2009-01-19 20:18:57 +03:00
|
|
|
if (len != 4) {
|
|
|
|
BX_INFO(("Write at 0x%08X with len != 4 (%i)", addr, len));
|
|
|
|
return 1;
|
|
|
|
}
|
2009-02-26 21:43:11 +03:00
|
|
|
if (addr & 3) {
|
|
|
|
BX_INFO(("Misaligned write at 0x%08X", addr));
|
|
|
|
return 1;
|
|
|
|
}
|
2009-01-19 20:18:57 +03:00
|
|
|
|
|
|
|
switch (offset) {
|
|
|
|
case 0x00: // HcRevision
|
2009-02-05 19:53:44 +03:00
|
|
|
BX_ERROR(("Write to HcRevision ignored"));
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x04: // HcControl
|
|
|
|
if (value & 0xFFFFF800)
|
|
|
|
BX_ERROR(("Write to reserved field in HcControl"));
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcControl.rwe = (value & (1<<10)) ? 1 : 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcControl.rwc = (value & (1<< 9)) ? 1 : 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcControl.ir = (value & (1<< 8)) ? 1 : 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcControl.hcfs = (value & (3<< 6)) >> 6;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcControl.ble = (value & (1<< 5)) ? 1 : 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcControl.cle = (value & (1<< 4)) ? 1 : 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcControl.ie = (value & (1<< 3)) ? 1 : 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcControl.ple = (value & (1<< 2)) ? 1 : 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcControl.cbsr = (value & (3<< 0)) >> 0;
|
2009-02-05 19:53:44 +03:00
|
|
|
if (BX_OHCI_THIS hub.op_regs.HcControl.hcfs == 0x02) {
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcFmRemaining.fr = BX_OHCI_THIS hub.op_regs.HcFmInterval.fi;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcFmRemaining.frt = 0;
|
|
|
|
}
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x08: // HcCommandStatus
|
|
|
|
if (value & 0xFFFCFFF0)
|
|
|
|
BX_ERROR(("Write to a reserved field in HcCommandStatus"));
|
|
|
|
if (value & (3<<16))
|
|
|
|
BX_ERROR(("Write to R/O field: HcCommandStatus.soc"));
|
2009-02-05 19:53:44 +03:00
|
|
|
if (value & (1<< 3)) BX_OHCI_THIS hub.op_regs.HcCommandStatus.ocr = 1;
|
|
|
|
if (value & (1<< 2)) BX_OHCI_THIS hub.op_regs.HcCommandStatus.blf = 1;
|
|
|
|
if (value & (1<< 1)) BX_OHCI_THIS hub.op_regs.HcCommandStatus.clf = 1;
|
|
|
|
if (value & (1<< 0)) {
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcCommandStatus.hcr = 1;
|
|
|
|
BX_OHCI_THIS reset_hc();
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcControl.hcfs = 3; // suspend state
|
|
|
|
for (unsigned i=0; i<USB_NUM_PORTS; i++)
|
2009-02-24 20:15:27 +03:00
|
|
|
if (BX_OHCI_THIS hub.usb_port[i].HcRhPortStatus.ccs && (BX_OHCI_THIS hub.usb_port[i].device != NULL))
|
2009-02-05 19:53:44 +03:00
|
|
|
BX_OHCI_THIS usb_send_msg(BX_OHCI_THIS hub.usb_port[i].device, USB_MSG_RESET);
|
|
|
|
}
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
2009-02-05 19:53:44 +03:00
|
|
|
case 0x0C: // HcInterruptStatus /// all are WC
|
2009-01-19 20:18:57 +03:00
|
|
|
if (value & 0xBFFFFF80)
|
2009-02-05 19:53:44 +03:00
|
|
|
BX_DEBUG(("Write to a reserved field in HcInterruptStatus"));
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcInterruptStatus &= ~value;
|
2009-02-26 21:43:11 +03:00
|
|
|
update_irq();
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x10: // HcInterruptEnable
|
|
|
|
if (value & 0x3FFFFF80)
|
|
|
|
BX_ERROR(("Write to a reserved field in HcInterruptEnable"));
|
2009-02-25 21:18:57 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcInterruptEnable |= (value & 0xC000007F);
|
2009-02-26 21:43:11 +03:00
|
|
|
update_irq();
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x14: // HcInterruptDisable
|
|
|
|
if (value & 0x3FFFFF80)
|
|
|
|
BX_ERROR(("Write to a reserved field in HcInterruptDisable"));
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcInterruptEnable &= ~value;
|
2009-02-26 21:43:11 +03:00
|
|
|
update_irq();
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x18: // HcHCCA
|
2009-02-05 19:53:44 +03:00
|
|
|
// the HCD can write 0xFFFFFFFF to this register to see what the alignement is
|
|
|
|
// by reading back the amount and seeing how many lower bits are clear.
|
|
|
|
if ((value & 0x000000FF) && (value != 0xFFFFFFFF))
|
2009-01-19 20:18:57 +03:00
|
|
|
BX_ERROR(("Write to lower byte of HcHCCA non zero."));
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcHCCA = (value & 0xFFFFFF00);
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x1C: // HcPeriodCurrentED
|
|
|
|
BX_ERROR(("Write to HcPeriodCurrentED not allowed."));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x20: // HcControlHeadED
|
|
|
|
if (value & 0x0000000F)
|
|
|
|
BX_ERROR(("Write to lower nibble of HcControlHeadED non zero."));
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcControlHeadED = (value & 0xFFFFFFF0);
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x24: // HcControlCurrentED
|
|
|
|
if (value & 0x0000000F)
|
|
|
|
BX_ERROR(("Write to lower nibble of HcControlCurrentED non zero."));
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcControlCurrentED = (value & 0xFFFFFFF0);
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x28: // HcBulkHeadED
|
|
|
|
if (value & 0x0000000F)
|
|
|
|
BX_ERROR(("Write to lower nibble of HcBulkHeadED non zero."));
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcBulkHeadED = (value & 0xFFFFFFF0);
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x2C: // HcBulkCurrentED
|
|
|
|
if (value & 0x0000000F)
|
|
|
|
BX_ERROR(("Write to lower nibble of HcBulkCurrentED non zero."));
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcBulkCurrentED = (value & 0xFFFFFFF0);
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x30: // HcDoneHead
|
|
|
|
BX_ERROR(("Write to HcDoneHead not allowed."));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x34: // HcFmInterval
|
|
|
|
if (value & 0x0000C000)
|
|
|
|
BX_ERROR(("Write to a reserved field in HcFmInterval."));
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcFmInterval.fit = (value & (1<<31)) ? 1 : 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcFmInterval.fsmps = (value & 0x7FFF0000) >> 16;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcFmInterval.fi = (value & 0x00003FFF) >> 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x38: // HcFmRemaining
|
|
|
|
BX_ERROR(("Write to HcFmRemaining not allowed."));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x3C: // HcFmNumber
|
|
|
|
BX_ERROR(("Write to HcFmNumber not allowed."));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x40: // HcPeriodicStart
|
|
|
|
if (value & 0xFFFFC000)
|
|
|
|
BX_ERROR(("Write to a reserved field in HcPeriodicStart."));
|
2009-02-26 21:43:11 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcPeriodicStart = (value & 0x00003FFF);
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x44: // HcLSThreshold
|
2009-02-05 19:53:44 +03:00
|
|
|
BX_ERROR(("Write to HcLSThreshold not allowed."));
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x48: // HcRhDescriptorA
|
|
|
|
if (value & 0x00FFE000)
|
|
|
|
BX_ERROR(("Write to a reserved field in HcRhDescriptorA."));
|
|
|
|
if (value & 0x000000FF)
|
|
|
|
BX_ERROR(("Write to HcRhDescriptorA.ndp not allowed."));
|
|
|
|
if (value & (1<<10))
|
|
|
|
BX_ERROR(("Write to HcRhDescriptorA.dt not allowed."));
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.potpgt = (value & 0xFF000000) >> 24;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.nocp = (value & (1<<12)) ? 1 : 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.ocpm = (value & (1<<11)) ? 1 : 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.nps = (value & (1<< 9)) ? 1 : 0;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.psm = (value & (1<< 8)) ? 1 : 0;
|
2009-02-05 19:53:44 +03:00
|
|
|
if (BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.psm == 0) {
|
|
|
|
|
|
|
|
BX_INFO(("Ben: BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.psm == 0"));
|
|
|
|
// all ports have power, etc.
|
2009-02-24 20:15:27 +03:00
|
|
|
// BX_USB_OHCI_THIS hub.usb_port[p].HcRhPortStatus.pps = 1
|
2009-02-05 19:53:44 +03:00
|
|
|
// Call a routine to set each ports dword (LS, Connected, etc.)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
BX_INFO(("Ben: BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.psm == 1"));
|
|
|
|
// only ports with bit set in rhstatus have power, etc.
|
|
|
|
// Call a routine to set each ports dword (LS, Connected, etc.)
|
|
|
|
|
|
|
|
}
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x4C: // HcRhDescriptorB
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcRhDescriptorB.ppcm = (value & 0xFFFF0000) >> 16;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcRhDescriptorB.dr = (value & 0x0000FFFF) >> 0;
|
|
|
|
break;
|
|
|
|
|
2009-02-05 19:53:44 +03:00
|
|
|
case 0x50: { // HcRhStatus
|
2009-01-19 20:18:57 +03:00
|
|
|
if (value & 0x7FFC7FFC)
|
|
|
|
BX_ERROR(("Write to a reserved field in HcRhStatus."));
|
|
|
|
if (value & (1<<1))
|
|
|
|
BX_ERROR(("Write to HcRhStatus.oci not allowed."));
|
2009-02-05 19:53:44 +03:00
|
|
|
// which one of these two takes presidence?
|
|
|
|
if (value & (1<<31)) BX_OHCI_THIS hub.op_regs.HcRhStatus.drwe = 0;
|
|
|
|
if (value & (1<<15)) BX_OHCI_THIS hub.op_regs.HcRhStatus.drwe = 1;
|
2009-01-19 20:18:57 +03:00
|
|
|
|
2009-02-05 19:53:44 +03:00
|
|
|
if (value & (1<<17)) BX_OHCI_THIS hub.op_regs.HcRhStatus.ocic = 1;
|
2009-01-19 20:18:57 +03:00
|
|
|
if (value & (1<<16)) {
|
|
|
|
if (BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.psm == 0) {
|
2009-02-05 19:53:44 +03:00
|
|
|
for (p=0; p<USB_NUM_PORTS; p++)
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.pps = 1;
|
2009-01-19 20:18:57 +03:00
|
|
|
} else {
|
2009-02-05 19:53:44 +03:00
|
|
|
for (p=0; p<USB_NUM_PORTS; p++)
|
|
|
|
if ((BX_OHCI_THIS hub.op_regs.HcRhDescriptorB.ppcm & (1<<p)) == 0)
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.pps = 1;
|
2009-01-19 20:18:57 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (value & (1<<0)) {
|
|
|
|
if (BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.psm == 0) {
|
2009-02-05 19:53:44 +03:00
|
|
|
for (p=0; p<USB_NUM_PORTS; p++)
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.pps = 0;
|
2009-01-19 20:18:57 +03:00
|
|
|
} else {
|
2009-02-05 19:53:44 +03:00
|
|
|
for (p=0; p<USB_NUM_PORTS; p++)
|
2009-01-19 20:18:57 +03:00
|
|
|
if (!(BX_OHCI_THIS hub.op_regs.HcRhDescriptorB.ppcm & (1<<p)))
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.pps = 0;
|
2009-01-19 20:18:57 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2009-02-05 19:53:44 +03:00
|
|
|
}
|
2009-01-19 20:18:57 +03:00
|
|
|
|
|
|
|
case 0x60: // HcRhPortStatus[3]
|
2009-02-05 19:53:44 +03:00
|
|
|
#if (USB_NUM_PORTS < 4)
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case 0x5C: // HcRhPortStatus[2]
|
2009-02-05 19:53:44 +03:00
|
|
|
#if (USB_NUM_PORTS < 3)
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case 0x58: // HcRhPortStatus[1]
|
2009-02-05 19:53:44 +03:00
|
|
|
#if (USB_NUM_PORTS < 2)
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
#endif
|
2009-02-05 19:53:44 +03:00
|
|
|
case 0x54: { // HcRhPortStatus[0]
|
2009-01-19 20:18:57 +03:00
|
|
|
p = (offset - 0x54) >> 2;
|
|
|
|
if (value & 0xFFE0FCE0)
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_ERROR(("Write to a reserved field in usb_port[%d].HcRhPortStatus", p));
|
2009-01-19 20:18:57 +03:00
|
|
|
if (value & (1<<0))
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.pes = 0;
|
2009-01-19 20:18:57 +03:00
|
|
|
if (value & (1<<1)) {
|
2009-02-24 20:15:27 +03:00
|
|
|
if (BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.ccs == 0)
|
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.csc = 1;
|
2009-01-19 20:18:57 +03:00
|
|
|
else
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.pes = 1;
|
2009-01-19 20:18:57 +03:00
|
|
|
}
|
|
|
|
if (value & (1<<2)) {
|
2009-02-24 20:15:27 +03:00
|
|
|
if (BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.ccs == 0)
|
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.csc = 1;
|
2009-01-19 20:18:57 +03:00
|
|
|
else
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.pss = 1;
|
2009-01-19 20:18:57 +03:00
|
|
|
}
|
|
|
|
// if (value & (1<<3))
|
2009-02-26 21:43:11 +03:00
|
|
|
// if (BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.pss)
|
2009-01-19 20:18:57 +03:00
|
|
|
// ; // do a resume (or test this in the timer code and do the resume there)
|
|
|
|
if (value & (1<<4)) {
|
2009-02-24 20:15:27 +03:00
|
|
|
if (BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.ccs == 0)
|
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.csc = 1;
|
2009-02-05 19:53:44 +03:00
|
|
|
else {
|
|
|
|
reset_port(p);
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.pps = 1;
|
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.pes = 1;
|
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.prsc = 1;
|
2009-02-05 19:53:44 +03:00
|
|
|
// are we are currently connected/disconnected
|
2009-02-14 13:06:20 +03:00
|
|
|
if (BX_OHCI_THIS hub.usb_port[p].device != NULL) {
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.lsda =
|
2009-02-14 13:06:20 +03:00
|
|
|
(BX_OHCI_THIS hub.usb_port[p].device->get_speed() == USB_SPEED_LOW);
|
|
|
|
usb_set_connect_status(p, BX_OHCI_THIS hub.usb_port[p].device->get_type(), 1);
|
|
|
|
BX_OHCI_THIS usb_send_msg(BX_OHCI_THIS hub.usb_port[p].device, USB_MSG_RESET);
|
2009-02-05 19:53:44 +03:00
|
|
|
}
|
2009-02-26 21:43:11 +03:00
|
|
|
set_interrupt(OHCI_INTR_RHSC);
|
2009-02-05 19:53:44 +03:00
|
|
|
}
|
2009-01-19 20:18:57 +03:00
|
|
|
}
|
|
|
|
if (value & (1<<8))
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.pps = 1;
|
2009-01-19 20:18:57 +03:00
|
|
|
if (value & (1<<9))
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.pps = 0;
|
2009-01-19 20:18:57 +03:00
|
|
|
if (value & (1<<16))
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.csc = (value & ((1<<4) | (1<<1) | (1<<2))) ? 1 : 0;
|
2009-01-19 20:18:57 +03:00
|
|
|
if (value & (1<<17))
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.pesc = 0;
|
2009-01-19 20:18:57 +03:00
|
|
|
if (value & (1<<18))
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.pssc = 0;
|
2009-01-19 20:18:57 +03:00
|
|
|
if (value & (1<<19))
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.ocic = 0;
|
2009-01-19 20:18:57 +03:00
|
|
|
if (value & (1<<20))
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.prsc = 0;
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
2009-02-05 19:53:44 +03:00
|
|
|
}
|
2009-01-19 20:18:57 +03:00
|
|
|
|
|
|
|
default:
|
|
|
|
BX_ERROR(("unsupported write to address=0x%08X, val = 0x%08X!", addr, value));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2009-02-05 19:53:44 +03:00
|
|
|
void bx_usb_ohci_c::usb_interval_handler(void *this_ptr)
|
2009-01-19 20:18:57 +03:00
|
|
|
{
|
|
|
|
bx_usb_ohci_c *class_ptr = (bx_usb_ohci_c *) this_ptr;
|
2009-02-05 19:53:44 +03:00
|
|
|
class_ptr->usb_interval_timer();
|
2009-01-19 20:18:57 +03:00
|
|
|
}
|
|
|
|
|
2009-02-05 19:53:44 +03:00
|
|
|
// Called once every 1uS
|
2009-02-14 13:06:20 +03:00
|
|
|
void bx_usb_ohci_c::usb_interval_timer(void)
|
|
|
|
{
|
2009-02-05 19:53:44 +03:00
|
|
|
// if remaining > 0, decrement it
|
|
|
|
//(0x2EDF+1) / 12 = 1000
|
|
|
|
if (BX_OHCI_THIS hub.op_regs.HcFmRemaining.fr > 0)
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcFmRemaining.fr =- 12;
|
|
|
|
}
|
2009-01-19 20:18:57 +03:00
|
|
|
|
2009-02-05 19:53:44 +03:00
|
|
|
void bx_usb_ohci_c::usb_frame_handler(void *this_ptr)
|
|
|
|
{
|
|
|
|
bx_usb_ohci_c *class_ptr = (bx_usb_ohci_c *) this_ptr;
|
|
|
|
class_ptr->usb_frame_timer();
|
|
|
|
}
|
2009-01-19 20:18:57 +03:00
|
|
|
|
2009-02-05 19:53:44 +03:00
|
|
|
// Called once every 1mS
|
2009-02-14 13:06:20 +03:00
|
|
|
void bx_usb_ohci_c::usb_frame_timer(void)
|
|
|
|
{
|
2009-02-05 19:53:44 +03:00
|
|
|
struct OHCI_ED cur_ed;
|
|
|
|
Bit32u address, ed_address;
|
|
|
|
Bit16u zero = 0;
|
2009-01-19 20:18:57 +03:00
|
|
|
|
2009-02-05 19:53:44 +03:00
|
|
|
if (BX_OHCI_THIS hub.op_regs.HcControl.hcfs == 2) {
|
2009-02-14 13:06:20 +03:00
|
|
|
// set remaining to the interval amount.
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcFmRemaining.fr = BX_OHCI_THIS hub.op_regs.HcFmInterval.fi;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcFmRemaining.frt = BX_OHCI_THIS hub.op_regs.HcFmInterval.fit;
|
2009-01-19 20:18:57 +03:00
|
|
|
|
2009-02-05 19:53:44 +03:00
|
|
|
// The Frame Number Register is incremented
|
2009-02-14 13:06:20 +03:00
|
|
|
// every time bit 15 is changed (at 0x8000 or 0x0000), fno is fired.
|
2009-02-26 21:43:11 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcFmNumber++;
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcFmNumber &= 0xffff;
|
|
|
|
DEV_MEM_WRITE_PHYSICAL(BX_OHCI_THIS hub.op_regs.HcHCCA + 0x80, 2, (Bit8u *) &BX_OHCI_THIS hub.op_regs.HcFmNumber);
|
2009-02-24 20:15:27 +03:00
|
|
|
DEV_MEM_WRITE_PHYSICAL(BX_OHCI_THIS hub.op_regs.HcHCCA + 0x82, 2, (Bit8u *) &zero);
|
2009-02-26 21:43:11 +03:00
|
|
|
if ((BX_OHCI_THIS hub.op_regs.HcFmNumber == 0x8000) || (BX_OHCI_THIS hub.op_regs.HcFmNumber == 0x0000)) {
|
|
|
|
set_interrupt(OHCI_INTR_FNO);
|
2009-01-19 20:18:57 +03:00
|
|
|
}
|
|
|
|
|
2009-02-14 13:06:20 +03:00
|
|
|
//
|
2009-02-26 21:43:11 +03:00
|
|
|
set_interrupt(OHCI_INTR_SF);
|
2009-02-05 19:53:44 +03:00
|
|
|
|
|
|
|
// if interrupt delay (done_count) == 0, and status.wdh == 0, then update the donehead fields.
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_DEBUG(("done_count = %i, status.wdh = %i", BX_OHCI_THIS hub.ohci_done_count,
|
|
|
|
((BX_OHCI_THIS hub.op_regs.HcInterruptStatus & OHCI_INTR_WD) > 0)));
|
|
|
|
if ((BX_OHCI_THIS hub.ohci_done_count == 0) && ((BX_OHCI_THIS hub.op_regs.HcInterruptStatus & OHCI_INTR_WD) == 0)) {
|
|
|
|
Bit32u temp = BX_OHCI_THIS hub.op_regs.HcDoneHead;
|
2009-02-26 21:43:11 +03:00
|
|
|
if (BX_OHCI_THIS hub.op_regs.HcInterruptStatus & BX_OHCI_THIS hub.op_regs.HcInterruptEnable)
|
2009-02-05 19:53:44 +03:00
|
|
|
temp |= 1;
|
2009-02-14 13:06:20 +03:00
|
|
|
BX_DEBUG(("Updating the hcca.DoneHead field to 0x%08X and setting the wdh flag", temp));
|
2009-02-24 20:15:27 +03:00
|
|
|
DEV_MEM_WRITE_PHYSICAL(BX_OHCI_THIS hub.op_regs.HcHCCA + 0x84, 4, (Bit8u *) &temp);
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcDoneHead = 0;
|
2009-02-05 19:53:44 +03:00
|
|
|
BX_OHCI_THIS hub.ohci_done_count = 7;
|
2009-02-26 21:43:11 +03:00
|
|
|
set_interrupt(OHCI_INTR_WD);
|
2009-02-05 19:53:44 +03:00
|
|
|
}
|
2009-01-19 20:18:57 +03:00
|
|
|
|
2009-02-05 19:53:44 +03:00
|
|
|
// if (6 >= done_count > 0) then decrement done_count
|
|
|
|
if ((BX_OHCI_THIS hub.ohci_done_count != 7) && (BX_OHCI_THIS hub.ohci_done_count > 0))
|
|
|
|
BX_OHCI_THIS hub.ohci_done_count--;
|
|
|
|
|
2009-02-14 13:06:20 +03:00
|
|
|
// TODO: Rather than just comparing .fr to <8000 here, and <4000 below, see the highlighted
|
|
|
|
// statement on page 45.
|
|
|
|
|
2009-02-05 19:53:44 +03:00
|
|
|
// if the control list is enabled *and* the control list filled bit is set, do a control list ED
|
|
|
|
if (BX_OHCI_THIS hub.op_regs.HcControl.cle && BX_OHCI_THIS hub.op_regs.HcCommandStatus.clf) {
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcCommandStatus.clf = 0;
|
2009-02-24 20:15:27 +03:00
|
|
|
ed_address = BX_OHCI_THIS hub.op_regs.HcControlCurrentED;
|
2009-02-05 19:53:44 +03:00
|
|
|
while (ed_address) {
|
2009-02-14 13:06:20 +03:00
|
|
|
DEV_MEM_READ_PHYSICAL(ed_address, 4, (Bit8u*) &cur_ed.dword0);
|
|
|
|
DEV_MEM_READ_PHYSICAL(ed_address + 4, 4, (Bit8u*) &cur_ed.dword1);
|
|
|
|
DEV_MEM_READ_PHYSICAL(ed_address + 8, 4, (Bit8u*) &cur_ed.dword2);
|
|
|
|
DEV_MEM_READ_PHYSICAL(ed_address + 12, 4, (Bit8u*) &cur_ed.dword3);
|
2009-02-05 19:53:44 +03:00
|
|
|
if (process_ed(&cur_ed, ed_address, 1))
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcCommandStatus.clf = 1;
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcControlCurrentED = ED_GET_NEXTED(&cur_ed);
|
2009-02-05 19:53:44 +03:00
|
|
|
if (BX_OHCI_THIS hub.op_regs.HcFmRemaining.fr < 8000)
|
|
|
|
goto do_bulk_eds;
|
2009-02-14 13:06:20 +03:00
|
|
|
ed_address = ED_GET_NEXTED(&cur_ed);
|
2009-01-19 20:18:57 +03:00
|
|
|
}
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcControlCurrentED = BX_OHCI_THIS hub.op_regs.HcControlHeadED;
|
2009-02-05 19:53:44 +03:00
|
|
|
}
|
2009-01-19 20:18:57 +03:00
|
|
|
|
2009-02-05 19:53:44 +03:00
|
|
|
do_bulk_eds:
|
|
|
|
// if the bulk list is enabled *and* the bulk list filled bit is set, do a bulk list ED
|
|
|
|
if (BX_OHCI_THIS hub.op_regs.HcControl.ble && BX_OHCI_THIS hub.op_regs.HcCommandStatus.blf) {
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcCommandStatus.blf = 0;
|
2009-02-24 20:15:27 +03:00
|
|
|
ed_address = BX_OHCI_THIS hub.op_regs.HcBulkCurrentED;
|
2009-02-05 19:53:44 +03:00
|
|
|
while (ed_address) {
|
2009-02-14 13:06:20 +03:00
|
|
|
DEV_MEM_READ_PHYSICAL(ed_address, 4, (Bit8u*) &cur_ed.dword0);
|
|
|
|
DEV_MEM_READ_PHYSICAL(ed_address + 4, 4, (Bit8u*) &cur_ed.dword1);
|
|
|
|
DEV_MEM_READ_PHYSICAL(ed_address + 8, 4, (Bit8u*) &cur_ed.dword2);
|
|
|
|
DEV_MEM_READ_PHYSICAL(ed_address + 12, 4, (Bit8u*) &cur_ed.dword3);
|
2009-02-05 19:53:44 +03:00
|
|
|
if (process_ed(&cur_ed, ed_address, 1))
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcCommandStatus.blf = 1;
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcBulkCurrentED = ED_GET_NEXTED(&cur_ed);
|
2009-02-05 19:53:44 +03:00
|
|
|
if (BX_OHCI_THIS hub.op_regs.HcFmRemaining.fr < 4000)
|
|
|
|
goto do_iso_eds;
|
2009-02-14 13:06:20 +03:00
|
|
|
ed_address = ED_GET_NEXTED(&cur_ed);
|
2009-01-19 20:18:57 +03:00
|
|
|
}
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.op_regs.HcBulkCurrentED = BX_OHCI_THIS hub.op_regs.HcBulkHeadED;
|
2009-01-19 20:18:57 +03:00
|
|
|
}
|
|
|
|
|
2009-02-05 19:53:44 +03:00
|
|
|
do_iso_eds:
|
|
|
|
// do the ED's in the interrupt table
|
2009-02-26 21:43:11 +03:00
|
|
|
address = BX_OHCI_THIS hub.op_regs.HcHCCA + ((BX_OHCI_THIS hub.op_regs.HcFmNumber & 0x1F) * 4);
|
2009-02-05 19:53:44 +03:00
|
|
|
DEV_MEM_READ_PHYSICAL(address, 4, (Bit8u*) &ed_address);
|
|
|
|
while (ed_address) {
|
2009-02-14 13:06:20 +03:00
|
|
|
DEV_MEM_READ_PHYSICAL(ed_address, 4, (Bit8u*) &cur_ed.dword0);
|
|
|
|
DEV_MEM_READ_PHYSICAL(ed_address + 4, 4, (Bit8u*) &cur_ed.dword1);
|
|
|
|
DEV_MEM_READ_PHYSICAL(ed_address + 8, 4, (Bit8u*) &cur_ed.dword2);
|
|
|
|
DEV_MEM_READ_PHYSICAL(ed_address + 12, 4, (Bit8u*) &cur_ed.dword3);
|
2009-02-05 19:53:44 +03:00
|
|
|
process_ed(&cur_ed, ed_address, BX_OHCI_THIS hub.op_regs.HcControl.ple);
|
2009-02-14 13:06:20 +03:00
|
|
|
ed_address = ED_GET_NEXTED(&cur_ed);
|
2009-01-19 20:18:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
} // end run schedule
|
2009-02-05 19:53:44 +03:00
|
|
|
}
|
2009-01-19 20:18:57 +03:00
|
|
|
|
2009-02-05 19:53:44 +03:00
|
|
|
// see http://www.koders.com/c/fid100DD4B4D99FF9CC7179538BCE26685B577697C4.aspx for more
|
2009-01-19 20:18:57 +03:00
|
|
|
|
2009-02-05 19:53:44 +03:00
|
|
|
bx_bool bx_usb_ohci_c::process_ed(struct OHCI_ED *ed, const Bit32u ed_address, const bx_bool enabled)
|
|
|
|
{
|
|
|
|
bx_bool fnd = 0;
|
|
|
|
struct OHCI_TD cur_td;
|
|
|
|
|
2009-02-14 13:06:20 +03:00
|
|
|
if (!ED_GET_H(ed) && !ED_GET_K(ed) && (ED_GET_HEADP(ed) != ED_GET_TAILP(ed))) {
|
2009-02-05 19:53:44 +03:00
|
|
|
// if the isochronous is enabled and ed is a isochronous, do TD
|
2009-02-14 13:06:20 +03:00
|
|
|
if (ED_GET_F(ed) && BX_OHCI_THIS hub.op_regs.HcControl.ie) {
|
2009-02-05 19:53:44 +03:00
|
|
|
// load and do a isochronous TD list
|
|
|
|
BX_INFO(("Ben: Found a valid ED that points to an isochronous TD"));
|
|
|
|
// we currently ignore ISO TD's
|
|
|
|
}
|
2009-02-14 13:06:20 +03:00
|
|
|
if (!ED_GET_F(ed) && enabled) {
|
2009-02-05 19:53:44 +03:00
|
|
|
BX_INFO(("Ben: Found a valid ED that points to an control/bulk/int TD"));
|
2009-02-14 13:06:20 +03:00
|
|
|
while (ED_GET_HEADP(ed) && (ED_GET_HEADP(ed) != ED_GET_TAILP(ed))) {
|
|
|
|
DEV_MEM_READ_PHYSICAL(ED_GET_HEADP(ed), 4, (Bit8u*) &cur_td.dword0);
|
|
|
|
DEV_MEM_READ_PHYSICAL(ED_GET_HEADP(ed) + 4, 4, (Bit8u*) &cur_td.dword1);
|
|
|
|
DEV_MEM_READ_PHYSICAL(ED_GET_HEADP(ed) + 8, 4, (Bit8u*) &cur_td.dword2);
|
|
|
|
DEV_MEM_READ_PHYSICAL(ED_GET_HEADP(ed) + 12, 4, (Bit8u*) &cur_td.dword3);
|
|
|
|
BX_INFO(("TD: 0x%08X", ED_GET_HEADP(ed)));
|
2009-02-05 19:53:44 +03:00
|
|
|
if (process_td(&cur_td, ed)) {
|
2009-02-14 13:06:20 +03:00
|
|
|
const Bit32u temp = ED_GET_HEADP(ed);
|
|
|
|
ED_SET_HEADP(ed, TD_GET_NEXTTD(&cur_td));
|
2009-02-24 20:15:27 +03:00
|
|
|
TD_SET_NEXTTD(&cur_td, BX_OHCI_THIS hub.op_regs.HcDoneHead);
|
|
|
|
BX_OHCI_THIS hub.op_regs.HcDoneHead = temp;
|
2009-02-14 13:06:20 +03:00
|
|
|
DEV_MEM_WRITE_PHYSICAL(temp, 4, (Bit8u*) &cur_td.dword0);
|
|
|
|
DEV_MEM_WRITE_PHYSICAL(temp + 4, 4, (Bit8u*) &cur_td.dword1);
|
|
|
|
DEV_MEM_WRITE_PHYSICAL(temp + 8, 4, (Bit8u*) &cur_td.dword2);
|
|
|
|
DEV_MEM_WRITE_PHYSICAL(temp + 12, 4, (Bit8u*) &cur_td.dword3);
|
|
|
|
if (TD_GET_DI(&cur_td) < BX_OHCI_THIS hub.ohci_done_count)
|
|
|
|
BX_OHCI_THIS hub.ohci_done_count = TD_GET_DI(&cur_td);
|
2009-02-05 19:53:44 +03:00
|
|
|
fnd = 1;
|
|
|
|
} else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2009-02-14 13:06:20 +03:00
|
|
|
DEV_MEM_WRITE_PHYSICAL(ed_address, 4, (Bit8u*) &ed->dword0);
|
|
|
|
DEV_MEM_WRITE_PHYSICAL(ed_address + 4, 4, (Bit8u*) &ed->dword1);
|
|
|
|
DEV_MEM_WRITE_PHYSICAL(ed_address + 8, 4, (Bit8u*) &ed->dword2);
|
|
|
|
DEV_MEM_WRITE_PHYSICAL(ed_address + 12, 4, (Bit8u*) &ed->dword3);
|
2009-02-05 19:53:44 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return fnd;
|
2009-01-19 20:18:57 +03:00
|
|
|
}
|
|
|
|
|
2009-02-05 19:53:44 +03:00
|
|
|
bx_bool bx_usb_ohci_c::process_td(struct OHCI_TD *td, struct OHCI_ED *ed)
|
2009-01-19 20:18:57 +03:00
|
|
|
{
|
|
|
|
usb_device_c *dev = NULL;
|
2009-02-05 19:53:44 +03:00
|
|
|
unsigned i, pid = 0, len = 0;
|
|
|
|
int ret = 0;
|
2009-01-19 20:18:57 +03:00
|
|
|
|
2009-02-05 19:53:44 +03:00
|
|
|
// The td->cc field should be 111x if it hasn't been processed yet.
|
2009-02-14 13:06:20 +03:00
|
|
|
if (TD_GET_CC(td) < NotAccessed) {
|
2009-02-05 19:53:44 +03:00
|
|
|
BX_ERROR(("Found TD with CC value not 111x"));
|
|
|
|
return 0;
|
2009-01-19 20:18:57 +03:00
|
|
|
}
|
|
|
|
|
2009-02-14 13:06:20 +03:00
|
|
|
if (ED_GET_D(ed) == 1)
|
2009-02-05 19:53:44 +03:00
|
|
|
pid = USB_TOKEN_OUT;
|
2009-02-14 13:06:20 +03:00
|
|
|
else if (ED_GET_D(ed) == 2)
|
2009-02-05 19:53:44 +03:00
|
|
|
pid = USB_TOKEN_IN;
|
|
|
|
else {
|
2009-02-14 13:06:20 +03:00
|
|
|
if (TD_GET_DP(td) == 0)
|
2009-02-05 19:53:44 +03:00
|
|
|
pid = USB_TOKEN_SETUP;
|
2009-02-14 13:06:20 +03:00
|
|
|
else if (TD_GET_DP(td) == 1)
|
2009-02-05 19:53:44 +03:00
|
|
|
pid = USB_TOKEN_OUT;
|
2009-02-14 13:06:20 +03:00
|
|
|
else if (TD_GET_DP(td) == 2)
|
2009-02-05 19:53:44 +03:00
|
|
|
pid = USB_TOKEN_IN;
|
|
|
|
}
|
2009-01-19 20:18:57 +03:00
|
|
|
|
2009-02-05 19:53:44 +03:00
|
|
|
// find the device
|
2009-01-19 20:18:57 +03:00
|
|
|
for (i=0; i<USB_NUM_PORTS; i++) {
|
|
|
|
if (BX_OHCI_THIS hub.usb_port[i].device != NULL) {
|
|
|
|
if (BX_OHCI_THIS hub.usb_port[i].device->get_connected()) {
|
2009-02-14 13:06:20 +03:00
|
|
|
if (BX_OHCI_THIS hub.usb_port[i].device->get_address() == ED_GET_FA(ed)) {
|
2009-01-19 20:18:57 +03:00
|
|
|
dev = BX_OHCI_THIS hub.usb_port[i].device;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (dev == NULL) {
|
2009-02-14 13:06:20 +03:00
|
|
|
if ((pid == USB_TOKEN_OUT) && (ED_GET_MPS(ed) == 0x7FF) && (ED_GET_FA(ed) == 0)) {
|
2009-01-19 20:18:57 +03:00
|
|
|
// This is the "keep awake" packet that Windows sends once a schedule cycle.
|
|
|
|
// For now, let it pass through to the code below.
|
|
|
|
} else {
|
2009-02-14 13:06:20 +03:00
|
|
|
TD_SET_CC(td, DeviceNotResponding);
|
|
|
|
TD_SET_EC(td, 3);
|
|
|
|
BX_PANIC(("Device not found for addr: %i", ED_GET_FA(ed)));
|
2009-01-19 20:18:57 +03:00
|
|
|
return 1; // device not found
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-05 19:53:44 +03:00
|
|
|
// calculate the length of the packet
|
2009-02-14 13:06:20 +03:00
|
|
|
if (TD_GET_CBP(td) && TD_GET_BE(td)) {
|
|
|
|
if ((TD_GET_CBP(td) & 0xFFFFF000) != (TD_GET_BE(td) & 0xFFFFF000))
|
|
|
|
len = (TD_GET_BE(td) & 0xFFF) + 0x1001 - (TD_GET_CBP(td) & 0xFFF);
|
2009-02-05 19:53:44 +03:00
|
|
|
else
|
2009-02-14 13:06:20 +03:00
|
|
|
len = (TD_GET_BE(td) - TD_GET_CBP(td)) + 1;
|
2009-02-05 19:53:44 +03:00
|
|
|
} else
|
|
|
|
len = 0;
|
2009-01-19 20:18:57 +03:00
|
|
|
|
|
|
|
if (dev != NULL) {
|
|
|
|
BX_OHCI_THIS usb_packet.pid = pid;
|
2009-02-14 13:06:20 +03:00
|
|
|
BX_OHCI_THIS usb_packet.devaddr = ED_GET_FA(ed);
|
|
|
|
BX_OHCI_THIS usb_packet.devep = ED_GET_EN(ed);
|
2009-02-05 19:53:44 +03:00
|
|
|
BX_OHCI_THIS usb_packet.data = BX_OHCI_THIS device_buffer;
|
2009-02-14 13:06:20 +03:00
|
|
|
switch (pid) {
|
|
|
|
case USB_TOKEN_SETUP:
|
|
|
|
case USB_TOKEN_OUT:
|
|
|
|
BX_OHCI_THIS usb_packet.len = (len <= ED_GET_MPS(ed)) ? len : ED_GET_MPS(ed);
|
|
|
|
break;
|
|
|
|
case USB_TOKEN_IN:
|
|
|
|
BX_OHCI_THIS usb_packet.len = len;
|
|
|
|
break;
|
|
|
|
}
|
2009-02-05 19:53:44 +03:00
|
|
|
|
2009-02-14 13:06:20 +03:00
|
|
|
BX_INFO((" pid = %i addr = %i endpnt = %i len = %i mps = %i (0x%08X 0x%08X)", pid, ED_GET_FA(ed), ED_GET_EN(ed), len, ED_GET_MPS(ed), TD_GET_CBP(td), TD_GET_BE(td)));
|
|
|
|
BX_INFO((" td->t = %i ed->c = %i td->di = %i td->r = %i", TD_GET_T(td), ED_GET_C(ed), TD_GET_DI(td), TD_GET_R(td)));
|
2009-02-05 19:53:44 +03:00
|
|
|
|
2009-01-19 20:18:57 +03:00
|
|
|
switch (pid) {
|
|
|
|
case USB_TOKEN_SETUP:
|
2009-02-05 19:53:44 +03:00
|
|
|
if (len > 0)
|
2009-02-14 13:06:20 +03:00
|
|
|
DEV_MEM_READ_PHYSICAL_BLOCK(TD_GET_CBP(td), len, device_buffer);
|
2009-02-05 19:53:44 +03:00
|
|
|
// TODO: This is a hack. dev->handle_packet() should return the amount of bytes
|
|
|
|
// it received, not the amount it anticipates on receiving/sending in the next packet.
|
|
|
|
if ((ret = dev->handle_packet(&BX_OHCI_THIS usb_packet)) >= 0)
|
|
|
|
ret = 8;
|
|
|
|
break;
|
|
|
|
case USB_TOKEN_OUT:
|
|
|
|
if (len > 0)
|
2009-02-14 13:06:20 +03:00
|
|
|
DEV_MEM_READ_PHYSICAL_BLOCK(TD_GET_CBP(td), len, device_buffer);
|
2009-01-19 20:18:57 +03:00
|
|
|
ret = dev->handle_packet(&BX_OHCI_THIS usb_packet);
|
|
|
|
break;
|
|
|
|
case USB_TOKEN_IN:
|
|
|
|
ret = dev->handle_packet(&BX_OHCI_THIS usb_packet);
|
|
|
|
if (ret >= 0) {
|
2009-02-14 13:06:20 +03:00
|
|
|
//if (ret > (int) ED_GET_MPS(ed))
|
|
|
|
// ret = USB_RET_BABBLE;
|
2009-02-05 19:53:44 +03:00
|
|
|
if (ret > 0)
|
2009-02-14 13:06:20 +03:00
|
|
|
DEV_MEM_WRITE_PHYSICAL_BLOCK(TD_GET_CBP(td), ret, device_buffer);
|
2009-02-05 19:53:44 +03:00
|
|
|
} else
|
|
|
|
ret = 0;
|
2009-02-14 13:06:20 +03:00
|
|
|
BX_INFO((" %02X %02X %02X %02X %02X %02X %02X %02X", device_buffer[0], device_buffer[1], device_buffer[2],
|
|
|
|
device_buffer[3], device_buffer[4], device_buffer[5], device_buffer[6], device_buffer[7]));
|
2009-01-19 20:18:57 +03:00
|
|
|
break;
|
|
|
|
default:
|
2009-02-14 13:06:20 +03:00
|
|
|
TD_SET_CC(td, UnexpectedPID);
|
|
|
|
TD_SET_EC(td, 3);
|
2009-02-05 19:53:44 +03:00
|
|
|
return 1;
|
2009-01-19 20:18:57 +03:00
|
|
|
}
|
2009-02-05 19:53:44 +03:00
|
|
|
|
2009-02-14 13:06:20 +03:00
|
|
|
if ((ret == (int)len) || ((pid == USB_TOKEN_IN) && (ret >= 0) && TD_GET_R(td))) {
|
2009-02-05 19:53:44 +03:00
|
|
|
if (ret == (int)len)
|
2009-02-14 13:06:20 +03:00
|
|
|
TD_SET_CBP(td, 0);
|
2009-02-05 19:53:44 +03:00
|
|
|
else {
|
2009-02-14 13:06:20 +03:00
|
|
|
TD_SET_CBP(td, TD_GET_CBP(td) + ret);
|
|
|
|
if (((TD_GET_CBP(td) & 0x0FFF) + ret) > 0x0FFF) {
|
|
|
|
TD_SET_CBP(td, TD_GET_CBP(td) & 0x0FFF);
|
|
|
|
TD_SET_CBP(td, TD_GET_CBP(td) | (TD_GET_BE(td) & ~0x0FFF));
|
2009-02-05 19:53:44 +03:00
|
|
|
}
|
|
|
|
}
|
2009-02-14 13:06:20 +03:00
|
|
|
if (TD_GET_T(td) & 2)
|
|
|
|
TD_SET_T(td, TD_GET_T(td) ^ 1);
|
|
|
|
ED_SET_C(ed, (TD_GET_T(td) & 1));
|
|
|
|
TD_SET_CC(td, NoError);
|
|
|
|
TD_SET_EC(td, 0);
|
2009-01-19 20:18:57 +03:00
|
|
|
} else {
|
2009-02-05 19:53:44 +03:00
|
|
|
if (ret >= 0)
|
2009-02-14 13:06:20 +03:00
|
|
|
TD_SET_CC(td, DataUnderrun);
|
2009-02-05 19:53:44 +03:00
|
|
|
else {
|
|
|
|
switch (ret) {
|
2009-02-14 13:06:20 +03:00
|
|
|
case USB_RET_NODEV: // (-1)
|
|
|
|
TD_SET_CC(td, DeviceNotResponding);
|
|
|
|
break;
|
|
|
|
case USB_RET_NAK: // (-2)
|
|
|
|
TD_SET_CC(td, Stall);
|
|
|
|
break;
|
|
|
|
case USB_RET_STALL: // (-3)
|
|
|
|
TD_SET_CC(td, Stall);
|
|
|
|
break;
|
|
|
|
case USB_RET_BABBLE: // (-4)
|
|
|
|
TD_SET_CC(td, BufferOverrun);
|
|
|
|
break;
|
|
|
|
case USB_RET_ASYNC: // (-5)
|
|
|
|
TD_SET_CC(td, BufferOverrun);
|
|
|
|
break;
|
2009-02-05 19:53:44 +03:00
|
|
|
default:
|
2009-02-14 13:06:20 +03:00
|
|
|
BX_ERROR(("Unknown error returned: %i", ret));
|
|
|
|
break;
|
2009-02-05 19:53:44 +03:00
|
|
|
}
|
|
|
|
}
|
2009-02-14 13:06:20 +03:00
|
|
|
TD_SET_EC(td, 3);
|
|
|
|
ED_SET_H(ed, 1);
|
2009-01-19 20:18:57 +03:00
|
|
|
}
|
2009-02-05 19:53:44 +03:00
|
|
|
|
2009-02-14 13:06:20 +03:00
|
|
|
BX_INFO((" td->cbp = 0x%08X ret = %i len = %i td->cc = %i td->ec = %i ed->h = %i", TD_GET_CBP(td), ret, len, TD_GET_CC(td), TD_GET_EC(td), ED_GET_H(ed)));
|
|
|
|
BX_INFO((" td->t = %i ed->c = %i", TD_GET_T(td), ED_GET_C(ed)));
|
2009-02-05 19:53:44 +03:00
|
|
|
|
2009-01-19 20:18:57 +03:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2009-02-05 19:53:44 +03:00
|
|
|
return 0;
|
2009-01-19 20:18:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// pci configuration space read callback handler
|
|
|
|
Bit32u bx_usb_ohci_c::pci_read_handler(Bit8u address, unsigned io_len)
|
|
|
|
{
|
|
|
|
Bit32u value = 0;
|
|
|
|
|
|
|
|
if (io_len > 4 || io_len == 0) {
|
|
|
|
BX_ERROR(("Experimental USB OHCI 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_OHCI_THIS hub.pci_conf[address+i] << (i*8));
|
|
|
|
sprintf(szTmp2, "%02x", (BX_OHCI_THIS hub.pci_conf[address+i]));
|
|
|
|
strrev(szTmp2);
|
|
|
|
strcat(szTmp, szTmp2);
|
|
|
|
}
|
|
|
|
strrev(szTmp);
|
|
|
|
BX_DEBUG(("USB OHCI read register 0x%02x %svalue 0x%s", address, pszName, szTmp));
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// pci configuration space write callback handler
|
|
|
|
void bx_usb_ohci_c::pci_write_handler(Bit8u address, Bit32u value, unsigned io_len)
|
|
|
|
{
|
|
|
|
Bit8u value8, oldval;
|
|
|
|
bx_bool baseaddr_change = 0;
|
2009-02-05 19:53:44 +03:00
|
|
|
|
2009-01-19 20:18:57 +03:00
|
|
|
if (((address >= 0x14) && (address <= 0x34)))
|
|
|
|
return;
|
2009-02-05 19:53:44 +03:00
|
|
|
|
2009-01-19 20:18:57 +03:00
|
|
|
// 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_OHCI_THIS hub.pci_conf[address+i];
|
|
|
|
switch (address+i) {
|
|
|
|
case 0x04:
|
|
|
|
value8 &= 0x06; // (bit 0 is read only for this card) (we don't allow port IO)
|
|
|
|
BX_OHCI_THIS hub.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_OHCI_THIS hub.pci_conf[address+i] = value8;
|
|
|
|
}
|
|
|
|
sprintf(szTmp2, "%02x", value8);
|
|
|
|
break;
|
|
|
|
case 0x10: // low 12 bits of BAR are R/O
|
|
|
|
value8 = 0x00;
|
|
|
|
case 0x11: // low 12 bits of BAR are R/O
|
|
|
|
value8 &= 0xF0;
|
|
|
|
case 0x12:
|
|
|
|
case 0x13:
|
|
|
|
baseaddr_change |= (value8 != oldval);
|
|
|
|
default:
|
|
|
|
BX_OHCI_THIS hub.pci_conf[address+i] = value8;
|
|
|
|
sprintf(szTmp2, "%02x", value8);
|
|
|
|
}
|
|
|
|
strrev(szTmp2);
|
|
|
|
strcat(szTmp, szTmp2);
|
|
|
|
}
|
|
|
|
if (baseaddr_change) {
|
|
|
|
if (DEV_pci_set_base_mem(BX_OHCI_THIS_PTR, read_handler, write_handler,
|
|
|
|
&BX_OHCI_THIS hub.base_addr,
|
|
|
|
&BX_OHCI_THIS hub.pci_conf[0x10],
|
|
|
|
4096)) {
|
|
|
|
BX_INFO(("new base address: 0x%04x", BX_OHCI_THIS hub.base_addr));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
strrev(szTmp);
|
|
|
|
BX_DEBUG(("USB OHCI write register 0x%02x value 0x%s", address, szTmp));
|
|
|
|
}
|
|
|
|
|
|
|
|
bx_bool bx_usb_ohci_c::usb_mouse_enabled_changed(bx_bool enabled)
|
|
|
|
{
|
|
|
|
if (BX_OHCI_THIS mousedev != NULL) {
|
|
|
|
if (enabled) mousedev->handle_reset();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void bx_usb_ohci_c::usb_set_connect_status(Bit8u port, int type, bx_bool connected)
|
|
|
|
{
|
2009-02-05 19:53:44 +03:00
|
|
|
char pname[BX_PATHNAME_LEN];
|
|
|
|
char fname[BX_PATHNAME_LEN];
|
2009-01-19 20:18:57 +03:00
|
|
|
|
|
|
|
if (BX_OHCI_THIS hub.usb_port[port].device != NULL) {
|
|
|
|
if (BX_OHCI_THIS hub.usb_port[port].device->get_type() == type) {
|
|
|
|
if (connected) {
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.usb_port[port].HcRhPortStatus.lsda =
|
2009-02-05 19:53:44 +03:00
|
|
|
(BX_OHCI_THIS hub.usb_port[port].device->get_speed() == USB_SPEED_LOW);
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.usb_port[port].HcRhPortStatus.ccs = 1;
|
|
|
|
//BX_OHCI_THIS hub.usb_port[port].HcRhPortStatus.csc = 1;
|
|
|
|
//BX_OHCI_THIS hub.usb_port[port].HcRhPortStatus.pesc = 1;
|
2009-01-19 20:18:57 +03:00
|
|
|
if ((type == USB_DEV_TYPE_DISK) &&
|
|
|
|
(!BX_OHCI_THIS hub.usb_port[port].device->get_connected())) {
|
|
|
|
if (port == 0) {
|
|
|
|
strcpy(pname, BXPN_OHCI_PORT1);
|
|
|
|
} else {
|
|
|
|
strcpy(pname, BXPN_OHCI_PORT2);
|
|
|
|
}
|
|
|
|
strcpy(fname, SIM->get_param_string(pname)->getptr() + 5);
|
2009-02-05 19:53:44 +03:00
|
|
|
if (!((usb_msd_device_c*)BX_OHCI_THIS hub.usb_port[port].device)->init(fname)) {
|
2009-01-19 20:18:57 +03:00
|
|
|
usb_set_connect_status(port, USB_DEV_TYPE_DISK, 0);
|
|
|
|
} else {
|
|
|
|
BX_INFO(("HD on USB port #%d: '%s'", port+1, fname));
|
|
|
|
}
|
|
|
|
}
|
2009-02-05 19:53:44 +03:00
|
|
|
} else { // not connected
|
2009-02-24 20:15:27 +03:00
|
|
|
BX_OHCI_THIS hub.usb_port[port].HcRhPortStatus.ccs = 0;
|
|
|
|
//BX_OHCI_THIS hub.usb_port[port].HcRhPortStatus.csc = 1;
|
|
|
|
BX_OHCI_THIS hub.usb_port[port].HcRhPortStatus.pes = 0;
|
|
|
|
//BX_OHCI_THIS hub.usb_port[port].HcRhPortStatus.pesc = 1;
|
|
|
|
BX_OHCI_THIS hub.usb_port[port].HcRhPortStatus.lsda = 0;
|
2009-02-26 21:43:11 +03:00
|
|
|
remove_device(port);
|
2009-01-19 20:18:57 +03:00
|
|
|
}
|
|
|
|
}
|
2009-02-05 19:53:44 +03:00
|
|
|
// we changed the value of the port, so show it
|
2009-02-26 21:43:11 +03:00
|
|
|
set_interrupt(OHCI_INTR_RHSC);
|
2009-01-19 20:18:57 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bx_bool bx_usb_ohci_c::usb_mouse_enq(int delta_x, int delta_y, int delta_z, unsigned button_state)
|
|
|
|
{
|
|
|
|
if (BX_OHCI_THIS mousedev != NULL) {
|
|
|
|
mousedev->mouse_enq(delta_x, delta_y, delta_z, button_state);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bx_bool bx_usb_ohci_c::usb_key_enq(Bit8u *scan_code)
|
|
|
|
{
|
|
|
|
if (BX_OHCI_THIS keybdev != NULL) {
|
|
|
|
return keybdev->key_enq(scan_code);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send an internal message to a USB device
|
|
|
|
void bx_usb_ohci_c::usb_send_msg(usb_device_c *dev, int msg)
|
|
|
|
{
|
2009-02-14 13:06:20 +03:00
|
|
|
USBPacket p;
|
|
|
|
memset(&p, 0, sizeof(p));
|
|
|
|
p.pid = msg;
|
|
|
|
dev->handle_packet(&p);
|
2009-01-19 20:18:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// USB runtime parameter handler
|
|
|
|
const char *bx_usb_ohci_c::usb_param_handler(bx_param_string_c *param, int set,
|
|
|
|
const char *oldval, const char *val, int maxlen)
|
|
|
|
{
|
|
|
|
usbdev_type type = USB_DEV_TYPE_NONE;
|
|
|
|
|
|
|
|
// handler for USB runtime parameters
|
|
|
|
if (set) {
|
|
|
|
char pname[BX_PATHNAME_LEN];
|
|
|
|
param->get_param_path(pname, BX_PATHNAME_LEN);
|
|
|
|
if (!strcmp(pname, BXPN_OHCI_PORT1)) {
|
|
|
|
BX_INFO(("USB port #1 experimental device change"));
|
2009-02-24 20:15:27 +03:00
|
|
|
if (!strcmp(val, "none") && BX_OHCI_THIS hub.usb_port[0].HcRhPortStatus.ccs) {
|
2009-01-19 20:18:57 +03:00
|
|
|
if (BX_OHCI_THIS hub.usb_port[0].device != NULL) {
|
|
|
|
type = BX_OHCI_THIS hub.usb_port[0].device->get_type();
|
|
|
|
}
|
|
|
|
usb_set_connect_status(0, type, 0);
|
2009-02-24 20:15:27 +03:00
|
|
|
} else if (strcmp(val, "none") && !BX_OHCI_THIS hub.usb_port[0].HcRhPortStatus.ccs) {
|
2009-01-19 20:18:57 +03:00
|
|
|
init_device(0, val);
|
|
|
|
}
|
|
|
|
} else if (!strcmp(pname, BXPN_OHCI_PORT2)) {
|
|
|
|
BX_INFO(("USB port #2 experimental device change"));
|
2009-02-24 20:15:27 +03:00
|
|
|
if (!strcmp(val, "none") && BX_OHCI_THIS hub.usb_port[1].HcRhPortStatus.ccs) {
|
2009-01-19 20:18:57 +03:00
|
|
|
if (BX_OHCI_THIS hub.usb_port[1].device != NULL) {
|
|
|
|
type = BX_OHCI_THIS hub.usb_port[1].device->get_type();
|
|
|
|
}
|
|
|
|
usb_set_connect_status(1, type, 0);
|
2009-02-24 20:15:27 +03:00
|
|
|
} else if (strcmp(val, "none") && !BX_OHCI_THIS hub.usb_port[1].HcRhPortStatus.ccs) {
|
2009-01-19 20:18:57 +03:00
|
|
|
init_device(1, val);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
BX_PANIC(("usb_param_handler called with unexpected parameter '%s'", pname));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // BX_SUPPORT_PCI && BX_SUPPORT_USB_OHCI
|