Add over-current signaling

This adds over-current signaling to the USBs four host controllers.
To signal an OC, use the runtime configuration and set the checkbox (GUI) or text config's parameter to 1.
This pull request also adds USB documentation to user.dbk.
This commit is contained in:
Benjamin David Lunt 2023-03-22 16:05:44 -07:00 committed by GitHub
parent d01e0f6cf8
commit dacc965593
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 241 additions and 56 deletions

View File

@ -166,7 +166,7 @@ void bx_uhci_core_c::reset_uhci(unsigned type)
hub.usb_port[j].resume = 0;
hub.usb_port[j].suspend = 0;
hub.usb_port[j].over_current_change = 0;
hub.usb_port[j].over_current = 1;
hub.usb_port[j].over_current = 0;
hub.usb_port[j].enabled = 0;
hub.usb_port[j].enable_changed = 0;
hub.usb_port[j].status = 0;
@ -564,7 +564,7 @@ void bx_uhci_core_c::write(Bit32u address, Bit32u value, unsigned io_len)
if (hub.usb_port[port].reset) {
hub.usb_port[port].suspend = 0;
hub.usb_port[port].over_current_change = 0;
hub.usb_port[port].over_current = 1;
hub.usb_port[port].over_current = 0;
hub.usb_port[port].resume = 0;
hub.usb_port[port].enabled = 0;
// are we are currently connected/disconnected
@ -645,7 +645,7 @@ void bx_uhci_core_c::uhci_timer(void)
hub.usb_port[i].reset = 0;
hub.usb_port[i].resume = 0;
hub.usb_port[i].status = 0;
hub.usb_port[i].over_current = 1;
hub.usb_port[i].over_current = 0;
hub.usb_port[i].over_current_change = 0;
hub.usb_port[i].suspend = 0;
}
@ -734,10 +734,14 @@ void bx_uhci_core_c::uhci_timer(void)
const int r_actlen = (((td.dword1 & 0x7FF) + 1) & 0x7FF);
const int r_maxlen = (((td.dword2 >> 21) + 1) & 0x7FF);
BX_DEBUG((" r_actlen = %d r_maxlen = %d", r_actlen, r_maxlen));
if (((td.dword2 & 0xFF) == USB_TOKEN_IN) && spd && (queue_addr != 0) && (r_actlen < r_maxlen) && ((td.dword1 & 0x00FF0000) == 0)) {
BX_DEBUG(("Short Packet Detected"));
shortpacket = was_short = 1;
td.dword1 |= (1<<29);
if (((td.dword2 & 0xFF) == USB_TOKEN_IN) && (queue_addr != 0) && (r_actlen < r_maxlen) && ((td.dword1 & 0x00FF0000) == 0)) {
if (spd) {
BX_DEBUG(("Short Packet Detected"));
shortpacket = was_short = 1;
td.dword1 |= (1<<29);
} else {
BX_DEBUG(("A Short Packet was detected, but the SPD bit in DWORD1 was clear"));
}
}
if (td.dword1 & (1<<22)) stalled = was_stall = 1;

View File

@ -168,7 +168,7 @@ typedef struct {
bool status;
} usb_port[USB_UHCI_PORTS];
int max_bandwidth; // standard USB 1.1 is 1280 bytes (VTxxxxx models allowed a few more)
int max_bandwidth; // standard USB 1.1 is 1280 bytes (VTxxxxx models allowed a few less (1023))
int loop_reached; // did we reach our bandwidth loop limit
Bit8u devfunc;
} bx_uhci_core_t;

View File

@ -194,7 +194,7 @@ bx_usb_ehci_c::bx_usb_ehci_c()
bx_usb_ehci_c::~bx_usb_ehci_c()
{
char pname[16];
char pname[32];
int i;
SIM->unregister_runtime_config_handler(rt_conf_id);
@ -209,6 +209,8 @@ bx_usb_ehci_c::~bx_usb_ehci_c()
SIM->get_param_enum(pname, SIM->get_param(BXPN_USB_EHCI))->set_handler(NULL);
sprintf(pname, "port%d.options", i+1);
SIM->get_param_string(pname, SIM->get_param(BXPN_USB_EHCI))->set_enable_handler(NULL);
sprintf(pname, "port%d.over_current", i+1);
SIM->get_param_bool(pname, SIM->get_param(BXPN_USB_EHCI))->set_handler(NULL);
remove_device(i);
}
@ -225,7 +227,14 @@ void bx_usb_ehci_c::init(void)
bx_list_c *ehci, *port;
bx_param_enum_c *device;
bx_param_string_c *options;
bx_param_bool_c *over_current;
Bit8u devfunc;
/* If you wish to set DEBUG=report in the code, instead of
* in the configuration, simply uncomment this line. I use
* it when I am working on this emulation.
*/
//LOG_THIS setonoff(LOGLEV_DEBUG, ACT_REPORT);
// Read in values from config interface
ehci = (bx_list_c*) SIM->get_param(BXPN_USB_EHCI);
@ -283,6 +292,8 @@ void bx_usb_ehci_c::init(void)
device->set_handler(usb_param_handler);
options = (bx_param_string_c*)port->get_by_name("options");
options->set_enable_handler(usb_param_enable_handler);
over_current = (bx_param_bool_c*)port->get_by_name("over_current");
over_current->set_handler(usb_param_oc_handler);
BX_EHCI_THIS hub.usb_port[i].device = NULL;
BX_EHCI_THIS hub.usb_port[i].owner_change = 0;
BX_EHCI_THIS hub.usb_port[i].portsc.ccs = 0;
@ -530,6 +541,7 @@ void bx_usb_ehci_c::init_device(Bit8u port, bx_list_c *portconf)
} else {
((bx_param_enum_c*)portconf->get_by_name("device"))->set_by_name("none");
((bx_param_string_c*)portconf->get_by_name("options"))->set("none");
((bx_param_bool_c*)portconf->get_by_name("over_current"))->set(0);
set_connect_status(port, 0);
}
}
@ -1370,6 +1382,15 @@ int bx_usb_ehci_c::execute(EHCIPacket *p)
int ret;
int endp;
// if we remove the device, or signal an over-current, there is a possibility
// that 'dev' is NULL. simply return that we transfered zero bytes.
// ( we can't return USB_RET_PROCERR, since this code will then reset the HC.
// On an over-current, we don't want the HC to reset... )
if (p->queue->dev == NULL) {
BX_DEBUG(("Attempting to execute a packet with no device attached."));
return 0;
}
BX_ASSERT(p->async == EHCI_ASYNC_NONE ||
p->async == EHCI_ASYNC_INITIALIZED);
@ -2329,6 +2350,32 @@ Bit64s bx_usb_ehci_c::usb_param_handler(bx_param_c *param, bool set, Bit64s val)
return val;
}
// USB runtime parameter handler: over-current
Bit64s bx_usb_ehci_c::usb_param_oc_handler(bx_param_c *param, bool set, Bit64s val)
{
int portnum;
if (set && val) {
portnum = atoi((param->get_parent())->get_name()+4) - 1;
if ((portnum >= 0) && (portnum < USB_EHCI_PORTS)) {
if (BX_EHCI_THIS hub.usb_port[portnum].portsc.ccs) {
// EHCI, section 4.2.5, page 58
BX_EHCI_THIS hub.usb_port[portnum].portsc.occ = 1;
BX_EHCI_THIS hub.usb_port[portnum].portsc.oca = 1;
BX_EHCI_THIS hub.usb_port[portnum].portsc.pec = 1;
BX_EHCI_THIS hub.usb_port[portnum].portsc.ped = 0;
BX_EHCI_THIS hub.usb_port[portnum].portsc.pp = 0; // optional (the HC may leave power on, limiting the current)
BX_DEBUG(("Over-current signaled on port #%d.", portnum + 1));
BX_EHCI_THIS raise_irq(USBSTS_PCD);
}
} else {
BX_ERROR(("Over-current: Bad portnum given: %d", portnum + 1));
}
}
return 0; // clear the indicator for next time
}
// USB runtime parameter enable handler
bool bx_usb_ehci_c::usb_param_enable_handler(bx_param_c *param, bool en)
{

View File

@ -419,6 +419,7 @@ private:
void runtime_config(void);
static Bit64s usb_param_handler(bx_param_c *param, bool set, Bit64s val);
static Bit64s usb_param_oc_handler(bx_param_c *param, bool set, Bit64s val);
static bool usb_param_enable_handler(bx_param_c *param, bool en);
};

View File

@ -254,6 +254,7 @@ bool usb_hub_device_c::init()
bx_list_c *port, *deplist;
bx_param_enum_c *device;
bx_param_string_c *options;
bx_param_bool_c *overcurrent;
// set up config descriptor, status and runtime config for hub.n_ports
bx_hub_config_descriptor[22] = (hub.n_ports + 1 + 7) / 8;
@ -272,8 +273,14 @@ bool usb_hub_device_c::init()
options = new bx_param_string_c(port, "options", "Options", "", "",
BX_PATHNAME_LEN);
options->set_enable_handler(hub_param_enable_handler);
overcurrent = new bx_param_bool_c(port,
"over_current",
"signal over-current",
"signal over-current", 0);
overcurrent->set_handler(hub_param_oc_handler);
deplist = new bx_list_c(NULL);
deplist->add(options);
deplist->add(overcurrent);
device->set_dependent_list(deplist, 1);
device->set_dependent_bitmap(0, 0);
}
@ -601,12 +608,8 @@ void usb_hub_device_c::init_device(Bit8u port, bx_list_c *portconf)
void usb_hub_device_c::remove_device(Bit8u port)
{
// TODO: there is something wrong with 'delete'ing hub.usb_port[port].device
// Bochs crashes and doesn't delete the .lock file when we close Bochs
if (hub.usb_port[port].device != NULL) {
delete hub.usb_port[port].device; // this is the culprit. Somewhere in the destruction of usb_device_c, something happens.
// simply freeing (free(hub.usb_port[port].device)) it, doesn't crash, so it is one of the destructors...
delete hub.usb_port[port].device;
hub.usb_port[port].device = NULL;
}
}
@ -764,6 +767,28 @@ bool usb_hub_device_c::hub_param_enable_handler(bx_param_c *param, bool en)
return en;
}
// USB runtime parameter handler: over-current
Bit64s usb_hub_device_c::hub_param_oc_handler(bx_param_c *param, bool set, Bit64s val)
{
int portnum;
usb_hub_device_c *hub;
bx_list_c *port;
if (set && val) {
port = (bx_list_c *) param->get_parent();
hub = (usb_hub_device_c *) (port->get_parent()->get_device_param());
if (hub != NULL) {
portnum = atoi(port->get_name()+4) - 1;
hub->hub.usb_port[portnum].PortStatus &= ~PORT_STAT_POWER;
hub->hub.usb_port[portnum].PortStatus |= PORT_STAT_OVERCURRENT;
hub->hub.usb_port[portnum].PortChange |= PORT_STAT_C_OVERCURRENT;
BX_DEBUG(("Over-current signaled on port #%d.", portnum + 1));
}
}
return 0; // clear the indicator for next time
}
void usb_hub_restore_handler(void *dev, bx_list_c *conf)
{
if (dev != NULL) {

View File

@ -77,6 +77,7 @@ private:
static Bit64s hub_param_handler(bx_param_c *param, bool set, Bit64s val);
static bool hub_param_enable_handler(bx_param_c *param, bool en);
static Bit64s hub_param_oc_handler(bx_param_c *param, bool set, Bit64s val);
};
#endif

View File

@ -137,7 +137,7 @@ bx_usb_ohci_c::bx_usb_ohci_c()
bx_usb_ohci_c::~bx_usb_ohci_c()
{
char pname[16];
char pname[32];
SIM->unregister_runtime_config_handler(hub.rt_conf_id);
@ -146,6 +146,8 @@ bx_usb_ohci_c::~bx_usb_ohci_c()
SIM->get_param_enum(pname, SIM->get_param(BXPN_USB_OHCI))->set_handler(NULL);
sprintf(pname, "port%d.options", i+1);
SIM->get_param_string(pname, SIM->get_param(BXPN_USB_OHCI))->set_enable_handler(NULL);
sprintf(pname, "port%d.over_current", i+1);
SIM->get_param_bool(pname, SIM->get_param(BXPN_USB_OHCI))->set_handler(NULL);
remove_device(i);
}
@ -162,6 +164,7 @@ void bx_usb_ohci_c::init(void)
bx_list_c *ohci, *port;
bx_param_enum_c *device;
bx_param_string_c *options;
bx_param_bool_c *over_current;
/* If you wish to set DEBUG=report in the code, instead of
* in the configuration, simply uncomment this line. I use
@ -208,6 +211,8 @@ void bx_usb_ohci_c::init(void)
device->set_handler(usb_param_handler);
options = (bx_param_string_c*)port->get_by_name("options");
options->set_enable_handler(usb_param_enable_handler);
over_current = (bx_param_bool_c*)port->get_by_name("over_current");
over_current->set_handler(usb_param_oc_handler);
BX_OHCI_THIS hub.usb_port[i].device = NULL;
BX_OHCI_THIS hub.usb_port[i].HcRhPortStatus.ccs = 0;
BX_OHCI_THIS hub.usb_port[i].HcRhPortStatus.csc = 0;
@ -373,7 +378,7 @@ void bx_usb_ohci_c::reset_hc()
sprintf(pname, "port%d", i+1);
init_device(i, (bx_list_c*)SIM->get_param(pname, SIM->get_param(BXPN_USB_OHCI)));
} else {
usb_set_connect_status(i, 1);
set_connect_status(i, 1);
}
}
@ -498,7 +503,7 @@ void bx_usb_ohci_c::init_device(Bit8u port, bx_list_c *portconf)
char pname[BX_PATHNAME_LEN];
if (DEV_usb_init_device(portconf, BX_OHCI_THIS_PTR, &BX_OHCI_THIS hub.usb_port[port].device)) {
if (usb_set_connect_status(port, 1)) {
if (set_connect_status(port, 1)) {
portconf->get_by_name("options")->set_enabled(0);
sprintf(pname, "usb_ohci.hub.port%d.device", port+1);
bx_list_c *sr_list = (bx_list_c*)SIM->get_param(pname, SIM->get_bochs_root());
@ -506,7 +511,8 @@ void bx_usb_ohci_c::init_device(Bit8u port, bx_list_c *portconf)
} else {
((bx_param_enum_c*)portconf->get_by_name("device"))->set_by_name("none");
((bx_param_string_c*)portconf->get_by_name("options"))->set("none");
usb_set_connect_status(port, 0);
((bx_param_bool_c*)portconf->get_by_name("over_current"))->set(0);
set_connect_status(port, 0);
}
}
}
@ -684,24 +690,21 @@ bool bx_usb_ohci_c::read_handler(bx_phy_address addr, unsigned len, void *data,
#endif
case 0x54: // HcRhPortStatus[0]
p = (offset - 0x54) >> 2;
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);
} else
val = 0;
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);
break;
default:
@ -979,7 +982,7 @@ bool bx_usb_ohci_c::write_handler(bx_phy_address addr, unsigned len, void *data,
if (BX_OHCI_THIS hub.usb_port[p].device != NULL) {
BX_OHCI_THIS hub.usb_port[p].HcRhPortStatus.lsda =
(BX_OHCI_THIS hub.usb_port[p].device->get_speed() == USB_SPEED_LOW);
usb_set_connect_status(p, 1);
set_connect_status(p, 1);
BX_OHCI_THIS hub.usb_port[p].device->usb_send_msg(USB_MSG_RESET);
}
set_interrupt(OHCI_INTR_RHSC);
@ -1433,7 +1436,7 @@ void bx_usb_ohci_c::runtime_config(void)
sprintf(pname, "port%d", i + 1);
init_device(i, (bx_list_c*)SIM->get_param(pname, SIM->get_param(BXPN_USB_OHCI)));
} else {
usb_set_connect_status(i, 0);
set_connect_status(i, 0);
}
BX_OHCI_THIS hub.device_change &= ~(1 << i);
}
@ -1473,7 +1476,7 @@ void bx_usb_ohci_c::pci_write_handler(Bit8u address, Bit32u value, unsigned io_l
}
}
bool bx_usb_ohci_c::usb_set_connect_status(Bit8u port, bool connected)
bool bx_usb_ohci_c::set_connect_status(Bit8u port, bool connected)
{
const bool ccs_org = BX_OHCI_THIS hub.usb_port[port].HcRhPortStatus.ccs;
const bool pes_org = BX_OHCI_THIS hub.usb_port[port].HcRhPortStatus.pes;
@ -1544,6 +1547,42 @@ Bit64s bx_usb_ohci_c::usb_param_handler(bx_param_c *param, bool set, Bit64s val)
return val;
}
// USB runtime parameter handler: over-current
Bit64s bx_usb_ohci_c::usb_param_oc_handler(bx_param_c *param, bool set, Bit64s val)
{
int portnum;
if (set && val) {
if (BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.nocp == 0) {
portnum = atoi((param->get_parent())->get_name()+4) - 1;
if ((portnum >= 0) && (portnum < USB_OHCI_PORTS)) {
if (BX_OHCI_THIS hub.usb_port[portnum].HcRhPortStatus.ccs) {
// is over current reported on a per-port basis?
if (BX_OHCI_THIS hub.op_regs.HcRhDescriptorA.ocpm == 1) {
BX_OHCI_THIS hub.usb_port[portnum].HcRhPortStatus.ocic = 1;
BX_OHCI_THIS hub.usb_port[portnum].HcRhPortStatus.poci = 1;
BX_OHCI_THIS hub.usb_port[portnum].HcRhPortStatus.pes = 0;
BX_OHCI_THIS hub.usb_port[portnum].HcRhPortStatus.pesc = 1;
BX_OHCI_THIS hub.usb_port[portnum].HcRhPortStatus.pps = 0;
BX_DEBUG(("Over-current signaled on port #%d.", portnum + 1));
// else over current is reported globally
} else {
BX_OHCI_THIS hub.op_regs.HcRhStatus.oci = 1;
BX_DEBUG(("Global over-current signaled."));
}
set_interrupt(OHCI_INTR_RHSC);
}
} else {
BX_ERROR(("Over-current: Bad portnum given: %d", portnum + 1));
}
} else {
BX_DEBUG(("Over-current signaled with NOCP set."));
}
}
return 0; // clear the indicator for next time
}
// USB runtime parameter enable handler
bool bx_usb_ohci_c::usb_param_enable_handler(bx_param_c *param, bool en)
{

View File

@ -275,7 +275,7 @@ private:
static void init_device(Bit8u port, bx_list_c *portconf);
static void remove_device(Bit8u port);
static int broadcast_packet(USBPacket *p);
static bool usb_set_connect_status(Bit8u port, bool connected);
static bool set_connect_status(Bit8u port, bool connected);
static void usb_frame_handler(void *);
void usb_frame_timer(void);
@ -298,6 +298,7 @@ private:
void runtime_config(void);
static Bit64s usb_param_handler(bx_param_c *param, bool set, Bit64s val);
static Bit64s usb_param_oc_handler(bx_param_c *param, bool set, Bit64s val);
static bool usb_param_enable_handler(bx_param_c *param, bool en);
};

View File

@ -401,7 +401,8 @@ int usb_msd_device_c::uasp_do_data(UASPRequest *req, USBPacket *p)
req->usb_len = 0;
}
usb_dump_packet(p->data, len, 0, p->devaddr, ((UASP_GET_DIR(req->mode) == USB_TOKEN_IN) ? USB_DIR_IN : USB_DIR_OUT) | p->devep, USB_TRANS_TYPE_BULK, false, true);
// This fills the log.txt file extremely fast, so I comment it out.
//usb_dump_packet(p->data, len, 0, p->devaddr, ((UASP_GET_DIR(req->mode) == USB_TOKEN_IN) ? USB_DIR_IN : USB_DIR_OUT) | p->devep, USB_TRANS_TYPE_BULK, false, true);
return len;
}

View File

@ -102,26 +102,30 @@ PLUGIN_ENTRY_FOR_MODULE(usb_uhci)
bx_usb_uhci_c::bx_usb_uhci_c()
{
put("usb_uhci", "UHCI");
device_change = 0;
rt_conf_id = -1;
}
bx_usb_uhci_c::~bx_usb_uhci_c()
{
char pname[16];
char pname[32];
SIM->unregister_runtime_config_handler(rt_conf_id);
for (int i=0; i<USB_UHCI_PORTS; i++) {
sprintf(pname, "port%d.device", i+1);
SIM->get_param_enum(pname, SIM->get_param(BXPN_USB_UHCI))->set_handler(NULL);
sprintf(pname, "port%d.options", i+1);
SIM->get_param_string(pname, SIM->get_param(BXPN_USB_UHCI))->set_enable_handler(NULL);
sprintf(pname, "port%d.over_current", i+1);
SIM->get_param_bool(pname, SIM->get_param(BXPN_USB_UHCI))->set_handler(NULL);
remove_device(i);
}
SIM->get_bochs_root()->remove("usb_uhci");
bx_list_c *usb_rt = (bx_list_c *) SIM->get_param(BXPN_MENU_RUNTIME_USB);
usb_rt->remove("uhci");
BX_DEBUG(("Exit"));
}
@ -132,6 +136,7 @@ void bx_usb_uhci_c::init(void)
bx_list_c *uhci, *port;
bx_param_enum_c *device;
bx_param_string_c *options;
bx_param_bool_c *over_current;
Bit8u devfunc;
Bit16u devid;
@ -174,6 +179,8 @@ void bx_usb_uhci_c::init(void)
device->set_handler(usb_param_handler);
options = (bx_param_string_c *) port->get_by_name("options");
options->set_enable_handler(usb_param_enable_handler);
over_current = (bx_param_bool_c *) port->get_by_name("over_current");
over_current->set_handler(usb_param_oc_handler);
}
// register handler for correct device connect handling after runtime config
@ -220,6 +227,7 @@ void bx_usb_uhci_c::init_device(Bit8u port, bx_list_c *portconf)
} else {
((bx_param_enum_c *) portconf->get_by_name("device"))->set_by_name("none");
((bx_param_string_c *) portconf->get_by_name("options"))->set("none");
((bx_param_bool_c *) portconf->get_by_name("over_current"))->set(0);
set_connect_status(port, 0);
}
}
@ -287,6 +295,31 @@ Bit64s bx_usb_uhci_c::usb_param_handler(bx_param_c *param, bool set, Bit64s val)
return val;
}
// USB runtime parameter handler: over-current
Bit64s bx_usb_uhci_c::usb_param_oc_handler(bx_param_c *param, bool set, Bit64s val)
{
int portnum;
if (set && val) {
portnum = atoi((param->get_parent())->get_name()+4) - 1;
if ((portnum >= 0) && (portnum < USB_UHCI_PORTS)) {
if (BX_UHCI_THIS hub.usb_port[portnum].status) {
// The UHCI specification does not specify what happens when an over-current
// condition exists. Therefore, we will set the condition and then envoke
// an interrupt. Hopefully the guest will check the port change.
BX_UHCI_THIS hub.usb_port[portnum].over_current_change = 1;
BX_UHCI_THIS hub.usb_port[portnum].over_current = 1;
BX_DEBUG(("Over-current signaled on port #%d.", portnum + 1));
BX_UHCI_THIS update_irq();
}
} else {
BX_ERROR(("Over-current: Bad portnum given: %d", portnum + 1));
}
}
return 0; // clear the indicator for next time
}
// USB runtime parameter enable handler
bool bx_usb_uhci_c::usb_param_enable_handler(bx_param_c *param, bool en)
{

View File

@ -50,6 +50,7 @@ private:
void runtime_config(void);
static Bit64s usb_param_handler(bx_param_c *param, bool set, Bit64s val);
static Bit64s usb_param_oc_handler(bx_param_c *param, bool set, Bit64s val);
static bool usb_param_enable_handler(bx_param_c *param, bool en);
};

View File

@ -148,8 +148,6 @@ static Bit8u ext_caps[EXT_CAPS_SIZE] = {
// builtin configuration handling functions
Bit32s usb_xhci_options_parser(const char *context, int num_params, char *params[])
{
int max_ports;
if (!strcmp(params[0], "usb_xhci")) {
bx_list_c *base = (bx_list_c*) SIM->get_param(BXPN_USB_XHCI);
for (int i = 1; i < num_params; i++) {
@ -163,7 +161,7 @@ Bit32s usb_xhci_options_parser(const char *context, int num_params, char *params
else
BX_PANIC(("%s: unknown parameter '%s' for usb_xhci: model=", context, &params[i][6]));
} else if (!strncmp(params[i], "n_ports=", 8)) {
max_ports = (int) strtol(&params[i][8], NULL, 10);
int max_ports = (int) strtol(&params[i][8], NULL, 10);
if ((max_ports >= 2) && (max_ports <= USB_XHCI_PORTS_MAX) && !(max_ports & 1))
SIM->get_param_num(BXPN_XHCI_N_PORTS)->set(max_ports);
else
@ -225,7 +223,7 @@ bx_usb_xhci_c::bx_usb_xhci_c()
bx_usb_xhci_c::~bx_usb_xhci_c()
{
char pname[16];
char pname[32];
SIM->unregister_runtime_config_handler(rt_conf_id);
@ -234,6 +232,8 @@ bx_usb_xhci_c::~bx_usb_xhci_c()
SIM->get_param_enum(pname, SIM->get_param(BXPN_USB_XHCI))->set_handler(NULL);
sprintf(pname, "port%d.options", i+1);
SIM->get_param_string(pname, SIM->get_param(BXPN_USB_XHCI))->set_enable_handler(NULL);
sprintf(pname, "port%d.over_current", i+1);
SIM->get_param_bool(pname, SIM->get_param(BXPN_USB_XHCI))->set_handler(NULL);
remove_device(i);
}
@ -251,6 +251,7 @@ void bx_usb_xhci_c::init(void)
bx_list_c *xhci, *port;
bx_param_enum_c *device;
bx_param_string_c *options;
bx_param_bool_c *over_current;
struct XHCI_PROTOCOL *protocol;
/* If you wish to set DEBUG=report in the code, instead of
@ -303,7 +304,7 @@ void bx_usb_xhci_c::init(void)
Bit32s n_ports = SIM->get_param_num(BXPN_XHCI_N_PORTS)->get();
if (n_ports > -1) BX_XHCI_THIS hub.n_ports = n_ports;
if ((BX_XHCI_THIS hub.n_ports < 2) || (BX_XHCI_THIS hub.n_ports > USB_XHCI_PORTS_MAX) || (BX_XHCI_THIS hub.n_ports & 1)) {
BX_PANIC(("n_ports (%d) must be at least 2, not more than 10, and must be an even number.", BX_XHCI_THIS hub.n_ports));
BX_PANIC(("n_ports (%d) must be at least 2, not more than %d, and must be an even number.", BX_XHCI_THIS hub.n_ports, USB_XHCI_PORTS_MAX));
return;
}
@ -342,6 +343,8 @@ void bx_usb_xhci_c::init(void)
device->set_handler(usb_param_handler);
options = (bx_param_string_c*)port->get_by_name("options");
options->set_enable_handler(usb_param_enable_handler);
over_current = (bx_param_bool_c*)port->get_by_name("over_current");
over_current->set_handler(usb_param_oc_handler);
BX_XHCI_THIS hub.usb_port[i].device = NULL;
BX_XHCI_THIS hub.usb_port[i].portsc.ccs = 0;
BX_XHCI_THIS hub.usb_port[i].portsc.csc = 0;
@ -648,7 +651,7 @@ void bx_usb_xhci_c::reset_hc()
sprintf(pname, "port%d", i+1);
init_device(i, (bx_list_c *) SIM->get_param(pname, SIM->get_param(BXPN_USB_XHCI)));
} else {
usb_set_connect_status(i, 1);
set_connect_status(i, 1);
}
}
@ -1091,7 +1094,7 @@ void bx_usb_xhci_c::init_device(Bit8u port, bx_list_c *portconf)
char pname[BX_PATHNAME_LEN];
if (DEV_usb_init_device(portconf, BX_XHCI_THIS_PTR, &BX_XHCI_THIS hub.usb_port[port].device)) {
if (usb_set_connect_status(port, 1)) {
if (set_connect_status(port, 1)) {
portconf->get_by_name("options")->set_enabled(0);
sprintf(pname, "usb_xhci.hub.port%d.device", port+1);
bx_list_c *sr_list = (bx_list_c *) SIM->get_param(pname, SIM->get_bochs_root());
@ -1099,7 +1102,8 @@ void bx_usb_xhci_c::init_device(Bit8u port, bx_list_c *portconf)
} else {
((bx_param_enum_c*)portconf->get_by_name("device"))->set_by_name("none");
((bx_param_string_c*)portconf->get_by_name("options"))->set("none");
usb_set_connect_status(port, 0);
((bx_param_bool_c*)portconf->get_by_name("over_current"))->set(0);
set_connect_status(port, 0);
}
}
}
@ -1611,7 +1615,6 @@ bool bx_usb_xhci_c::write_handler(bx_phy_address addr, unsigned len, void *data,
BX_XHCI_THIS hub.op_regs.HcStatus.pcd = (value & (1 << 4)) ? 0 : BX_XHCI_THIS hub.op_regs.HcStatus.pcd;
BX_XHCI_THIS hub.op_regs.HcStatus.eint = (value & (1 << 3)) ? 0 : BX_XHCI_THIS hub.op_regs.HcStatus.eint;
BX_XHCI_THIS hub.op_regs.HcStatus.hse = (value & (1 << 2)) ? 0 : BX_XHCI_THIS hub.op_regs.HcStatus.hse;
//FIXME: should this line go where system software clears the IP bit, or here when it clears the status:eint bit?
if (value & (1 << 3)) // acknowledging the interrupt
DEV_pci_set_irq(BX_XHCI_THIS devfunc, BX_XHCI_THIS pci_conf[0x3d], 0);
break;
@ -3487,6 +3490,12 @@ void bx_usb_xhci_c::xhci_timer(void)
*/
for (port=0; port<BX_XHCI_THIS hub.n_ports; port++) {
new_psceg = get_psceg(port);
// if any bit has transitioned from 0 to 1 (in any port),
// set the pcd bit in the host status register.
if ((new_psceg & BX_XHCI_THIS hub.usb_port[port].psceg) > 0)
BX_XHCI_THIS hub.op_regs.HcStatus.pcd = 1;
// clear any bits that have been cleared by the guest,
// if we are now zero *and* there are any new bits set, send a Change Event.
BX_XHCI_THIS hub.usb_port[port].psceg &= new_psceg;
if ((BX_XHCI_THIS hub.usb_port[port].psceg == 0) && (new_psceg != 0)) {
BX_DEBUG(("Port #%d Status Change Event: (%2Xh)", port + 1, new_psceg));
@ -3536,7 +3545,7 @@ void bx_usb_xhci_c::runtime_config(void)
sprintf(pname, "port%d", i + 1);
init_device(i, (bx_list_c *) SIM->get_param(pname, SIM->get_param(BXPN_USB_XHCI)));
} else {
usb_set_connect_status(i, 0);
set_connect_status(i, 0);
}
BX_XHCI_THIS device_change &= ~(1 << i);
}
@ -3585,7 +3594,7 @@ void bx_usb_xhci_c::pci_write_handler(Bit8u address, Bit32u value, unsigned io_l
}
}
bool bx_usb_xhci_c::usb_set_connect_status(Bit8u port, bool connected)
bool bx_usb_xhci_c::set_connect_status(Bit8u port, bool connected)
{
const bool ccs_org = BX_XHCI_THIS hub.usb_port[port].portsc.ccs;
const bool ped_org = BX_XHCI_THIS hub.usb_port[port].portsc.ped;
@ -3683,6 +3692,28 @@ Bit64s bx_usb_xhci_c::usb_param_handler(bx_param_c *param, bool set, Bit64s val)
return val;
}
// USB runtime parameter handler: over-current
Bit64s bx_usb_xhci_c::usb_param_oc_handler(bx_param_c *param, bool set, Bit64s val)
{
int portnum;
if (set) {
portnum = atoi((param->get_parent())->get_name()+4) - 1;
if ((portnum >= 0) && (portnum < (int) BX_XHCI_THIS hub.n_ports)) {
if (val) {
if (BX_XHCI_THIS hub.usb_port[portnum].portsc.ccs) {
BX_XHCI_THIS hub.usb_port[portnum].portsc.occ = 1;
BX_XHCI_THIS hub.usb_port[portnum].portsc.oca = 1;
BX_DEBUG(("Over-current signaled on port #%d.", portnum + 1));
write_event_TRB(0, ((portnum + 1) << 24), TRB_SET_COMP_CODE(1), TRB_SET_TYPE(PORT_STATUS_CHANGE), 1);
}
}
}
}
return 0; // clear the indicator for next time
}
// USB runtime parameter enable handler
bool bx_usb_xhci_c::usb_param_enable_handler(bx_param_c *param, bool en)
{

View File

@ -620,7 +620,7 @@ private:
static void init_device(Bit8u port, bx_list_c *portconf);
static void remove_device(Bit8u port);
static bool usb_set_connect_status(Bit8u port, bool connected);
static bool set_connect_status(Bit8u port, bool connected);
static int broadcast_speed(const int slot);
static int broadcast_packet(USBPacket *p, const int port);
@ -668,6 +668,7 @@ private:
void runtime_config(void);
static Bit64s usb_param_handler(bx_param_c *param, bool set, Bit64s val);
static Bit64s usb_param_oc_handler(bx_param_c *param, bool set, Bit64s val);
static bool usb_param_enable_handler(bx_param_c *param, bool en);
};