Implemented USB remote wakeup mechanism.
- Extended USB callback definition to support different event types sent from device to HC. Currently we only have the two events "async completion" and "remote wakeup". - Implemented event handlers in the UHCI, OHCI and EHCI. For the EHCI version we currently have no test case. - The external USB hub now uses this feature at device connect/disconnect. Now the device change on the hub is correctly detected by the guest OS. - TODO #1: remote wakeup and async completion for the xHCI. - TODO #2: event handler for the external hub to make a USB device work on a chain of hubs. - TODO #3: the event handler possibly could be used for the packet copy code.
This commit is contained in:
parent
7b2a8bb340
commit
c3b608a3c9
@ -738,16 +738,33 @@ void bx_uhci_core_c::uhci_timer(void)
|
||||
// However, since we don't do anything, let's not.
|
||||
}
|
||||
|
||||
void uhci_async_complete_packet(USBPacket *packet, void *dev)
|
||||
void uhci_event_handler(int event, USBPacket *packet, void *dev, int port)
|
||||
{
|
||||
((bx_uhci_core_c*)dev)->async_complete_packet(packet);
|
||||
((bx_uhci_core_c*)dev)->event_handler(event, packet, port);
|
||||
}
|
||||
|
||||
void bx_uhci_core_c::async_complete_packet(USBPacket *packet)
|
||||
void bx_uhci_core_c::event_handler(int event, USBPacket *packet, int port)
|
||||
{
|
||||
BX_DEBUG(("Experimental async packet completion"));
|
||||
USBAsync *p = container_of_usb_packet(packet);
|
||||
p->done = 1;
|
||||
if (event == USB_EVENT_ASYNC) {
|
||||
BX_DEBUG(("Experimental async packet completion"));
|
||||
USBAsync *p = container_of_usb_packet(packet);
|
||||
p->done = 1;
|
||||
} else if (event == USB_EVENT_WAKEUP) {
|
||||
if (hub.usb_port[port].suspend && !hub.usb_port[port].resume) {
|
||||
hub.usb_port[port].resume = 1;
|
||||
}
|
||||
// if in suspend state, signal resume
|
||||
if (hub.usb_command.suspend) {
|
||||
hub.usb_command.resume = 1;
|
||||
hub.usb_status.resume = 1;
|
||||
if (hub.usb_enable.resume) {
|
||||
hub.usb_status.interrupt = 1;
|
||||
}
|
||||
update_irq();
|
||||
}
|
||||
} else {
|
||||
BX_ERROR(("unknown/unsupported event (id=%d) on port #%d", event, port+1));
|
||||
}
|
||||
}
|
||||
|
||||
bx_bool bx_uhci_core_c::DoTransfer(Bit32u address, Bit32u queue_num, struct TD *td) {
|
||||
@ -795,7 +812,7 @@ bx_bool bx_uhci_core_c::DoTransfer(Bit32u address, Bit32u queue_num, struct TD *
|
||||
p->packet.pid = pid;
|
||||
p->packet.devaddr = addr;
|
||||
p->packet.devep = endpt;
|
||||
p->packet.complete_cb = uhci_async_complete_packet;
|
||||
p->packet.complete_cb = uhci_event_handler;
|
||||
p->packet.complete_dev = this;
|
||||
switch (pid) {
|
||||
case USB_TOKEN_OUT:
|
||||
@ -1008,11 +1025,13 @@ void bx_uhci_core_c::set_connect_status(Bit8u port, int type, bx_bool connected)
|
||||
if (!device->init()) {
|
||||
set_connect_status(port, type, 0);
|
||||
BX_ERROR(("port #%d: connect failed", port+1));
|
||||
return;
|
||||
} else {
|
||||
BX_INFO(("port #%d: connect: %s", port+1, device->get_info()));
|
||||
device->set_async_mode(1);
|
||||
}
|
||||
}
|
||||
device->set_event_handler(this, uhci_event_handler, port);
|
||||
} else {
|
||||
hub.usb_port[port].status = 0;
|
||||
hub.usb_port[port].connect_changed = 1;
|
||||
|
@ -182,7 +182,7 @@ public:
|
||||
virtual Bit32u pci_read_handler(Bit8u address, unsigned io_len);
|
||||
virtual void pci_write_handler(Bit8u address, Bit32u value, unsigned io_len);
|
||||
|
||||
void async_complete_packet(USBPacket *packet);
|
||||
void event_handler(int event, USBPacket *packet, int port);
|
||||
|
||||
protected:
|
||||
bx_uhci_core_t hub;
|
||||
|
@ -120,7 +120,10 @@
|
||||
|
||||
typedef struct USBPacket USBPacket;
|
||||
|
||||
typedef void USBCallback(USBPacket *packet, void *dev);
|
||||
#define USB_EVENT_WAKEUP 0
|
||||
#define USB_EVENT_ASYNC 1
|
||||
|
||||
typedef void USBCallback(int event, USBPacket *packet, void *dev, int port);
|
||||
|
||||
class usb_device_c;
|
||||
|
||||
@ -191,6 +194,12 @@ public:
|
||||
void set_speed(int speed) {d.speed = speed;}
|
||||
Bit8u get_address() {return d.addr;}
|
||||
void set_async_mode(bx_bool async) {d.async_mode = async;}
|
||||
void set_event_handler(void *dev, USBCallback *cb, int port)
|
||||
{
|
||||
d.event.dev = dev;
|
||||
d.event.cb = cb;
|
||||
d.event.port = port;
|
||||
}
|
||||
void set_debug_mode();
|
||||
|
||||
void usb_send_msg(int msg);
|
||||
@ -222,6 +231,11 @@ protected:
|
||||
int setup_index;
|
||||
bx_bool stall;
|
||||
bx_bool async_mode;
|
||||
struct {
|
||||
USBCallback *cb;
|
||||
void *dev;
|
||||
int port;
|
||||
} event;
|
||||
bx_list_c *sr;
|
||||
} d;
|
||||
|
||||
@ -262,7 +276,7 @@ static BX_CPP_INLINE void usb_cancel_packet(USBPacket *p)
|
||||
|
||||
static BX_CPP_INLINE void usb_packet_complete(USBPacket *p)
|
||||
{
|
||||
p->complete_cb(p, p->complete_dev);
|
||||
p->complete_cb(USB_EVENT_ASYNC, p, p->complete_dev, 0);
|
||||
}
|
||||
|
||||
// Async packet support
|
||||
|
@ -123,6 +123,8 @@ static inline struct EHCIPacket *ehci_container_of_usb_packet(void *ptr)
|
||||
reinterpret_cast<size_t>(&(static_cast<struct EHCIPacket*>(0)->packet)));
|
||||
}
|
||||
|
||||
void ehci_event_handler(int event, USBPacket *packet, void *dev, int port);
|
||||
|
||||
// builtin configuration handling functions
|
||||
|
||||
Bit32s usb_ehci_options_parser(const char *context, int num_params, char *params[])
|
||||
@ -583,11 +585,13 @@ void bx_usb_ehci_c::set_connect_status(Bit8u port, int type, bx_bool connected)
|
||||
if (!device->init()) {
|
||||
set_connect_status(port, type, 0);
|
||||
BX_ERROR(("port #%d: connect failed", port+1));
|
||||
return;
|
||||
} else {
|
||||
BX_INFO(("port #%d: connect: %s", port+1, device->get_info()));
|
||||
device->set_async_mode(1);
|
||||
}
|
||||
}
|
||||
device->set_event_handler(BX_EHCI_THIS_PTR, ehci_event_handler, port);
|
||||
} else { // not connected
|
||||
if (BX_EHCI_THIS hub.usb_port[port].portsc.po) {
|
||||
BX_EHCI_THIS uhci[port >> 1]->set_port_device(port & 1, NULL);
|
||||
@ -1258,26 +1262,35 @@ void bx_usb_ehci_c::finish_transfer(EHCIQueue *q, int status)
|
||||
}
|
||||
}
|
||||
|
||||
void ehci_async_complete_packet(USBPacket *packet, void *dev)
|
||||
void ehci_event_handler(int event, USBPacket *packet, void *dev, int port)
|
||||
{
|
||||
((bx_usb_ehci_c*)dev)->async_complete_packet(packet);
|
||||
((bx_usb_ehci_c*)dev)->event_handler(event, packet, port);
|
||||
}
|
||||
|
||||
void bx_usb_ehci_c::async_complete_packet(USBPacket *packet)
|
||||
void bx_usb_ehci_c::event_handler(int event, USBPacket *packet, int port)
|
||||
{
|
||||
EHCIPacket *p;
|
||||
|
||||
BX_DEBUG(("Experimental async packet completion"));
|
||||
p = ehci_container_of_usb_packet(packet);
|
||||
if (p->pid == USB_TOKEN_IN) {
|
||||
BX_EHCI_THIS transfer(p);
|
||||
}
|
||||
BX_ASSERT(p->async == EHCI_ASYNC_INFLIGHT);
|
||||
p->async = EHCI_ASYNC_FINISHED;
|
||||
p->usb_status = packet->len;
|
||||
if (event == USB_EVENT_ASYNC) {
|
||||
BX_DEBUG(("Experimental async packet completion"));
|
||||
p = ehci_container_of_usb_packet(packet);
|
||||
if (p->pid == USB_TOKEN_IN) {
|
||||
BX_EHCI_THIS transfer(p);
|
||||
}
|
||||
BX_ASSERT(p->async == EHCI_ASYNC_INFLIGHT);
|
||||
p->async = EHCI_ASYNC_FINISHED;
|
||||
p->usb_status = packet->len;
|
||||
|
||||
if (p->queue->async) {
|
||||
BX_EHCI_THIS advance_async_state();
|
||||
if (p->queue->async) {
|
||||
BX_EHCI_THIS advance_async_state();
|
||||
}
|
||||
} else if (event == USB_EVENT_WAKEUP) {
|
||||
if (BX_EHCI_THIS hub.usb_port[port].portsc.sus) {
|
||||
BX_EHCI_THIS hub.usb_port[port].portsc.fpr = 1;
|
||||
raise_irq(USBSTS_PCD);
|
||||
}
|
||||
} else {
|
||||
BX_ERROR(("unknown/unsupported event (id=%d) on port #%d", event, port+1));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1388,7 +1401,7 @@ int bx_usb_ehci_c::execute(EHCIPacket *p)
|
||||
p->packet.pid = p->pid;
|
||||
p->packet.devaddr = p->queue->dev->get_address();
|
||||
p->packet.devep = endp;
|
||||
p->packet.complete_cb = ehci_async_complete_packet;
|
||||
p->packet.complete_cb = ehci_event_handler;
|
||||
p->packet.complete_dev = BX_EHCI_THIS_PTR;
|
||||
|
||||
p->async = EHCI_ASYNC_INITIALIZED;
|
||||
|
@ -333,7 +333,7 @@ public:
|
||||
virtual Bit32u pci_read_handler(Bit8u address, unsigned io_len);
|
||||
virtual void pci_write_handler(Bit8u address, Bit32u value, unsigned io_len);
|
||||
|
||||
void async_complete_packet(USBPacket *packet);
|
||||
void event_handler(int event, USBPacket *packet, int port);
|
||||
|
||||
static const char *usb_param_handler(bx_param_string_c *param, int set,
|
||||
const char *oldval, const char *val, int maxlen);
|
||||
|
@ -570,7 +570,9 @@ void usb_hub_device_c::usb_set_connect_status(Bit8u port, int type, bx_bool conn
|
||||
hub.usb_port[port].PortChange |= PORT_STAT_C_CONNECTION;
|
||||
if (hub.usb_port[port].PortStatus & PORT_STAT_SUSPEND) {
|
||||
hub.usb_port[port].PortChange |= PORT_STAT_C_SUSPEND;
|
||||
// TODO: signal resume to upstream port
|
||||
}
|
||||
if (d.event.dev != NULL) {
|
||||
d.event.cb(USB_EVENT_WAKEUP, NULL, d.event.dev, d.event.port);
|
||||
}
|
||||
if (!device->get_connected()) {
|
||||
if (!device->init()) {
|
||||
@ -581,6 +583,9 @@ void usb_hub_device_c::usb_set_connect_status(Bit8u port, int type, bx_bool conn
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (d.event.dev != NULL) {
|
||||
d.event.cb(USB_EVENT_WAKEUP, NULL, d.event.dev, d.event.port);
|
||||
}
|
||||
hub.usb_port[port].PortStatus &= ~PORT_STAT_CONNECTION;
|
||||
hub.usb_port[port].PortChange |= PORT_STAT_C_CONNECTION;
|
||||
if (hub.usb_port[port].PortStatus & PORT_STAT_ENABLE) {
|
||||
|
@ -271,7 +271,7 @@ void bx_usb_ohci_c::reset_hc()
|
||||
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.hcfs = OHCI_USB_RESET;
|
||||
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;
|
||||
@ -755,9 +755,9 @@ bx_bool bx_usb_ohci_c::write_handler(bx_phy_address addr, unsigned len, void *da
|
||||
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;
|
||||
if (BX_OHCI_THIS hub.op_regs.HcControl.hcfs == 0x02) {
|
||||
if (BX_OHCI_THIS hub.op_regs.HcControl.hcfs == OHCI_USB_OPERATIONAL) {
|
||||
BX_OHCI_THIS hub.op_regs.HcFmRemainingToggle = 0;
|
||||
if (org_state != 2)
|
||||
if (org_state != OHCI_USB_OPERATIONAL)
|
||||
BX_OHCI_THIS hub.use_control_head = BX_OHCI_THIS hub.use_bulk_head = 1;
|
||||
}
|
||||
break;
|
||||
@ -773,7 +773,7 @@ bx_bool bx_usb_ohci_c::write_handler(bx_phy_address addr, unsigned len, void *da
|
||||
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
|
||||
BX_OHCI_THIS hub.op_regs.HcControl.hcfs = OHCI_USB_SUSPEND;
|
||||
for (unsigned i=0; i<USB_OHCI_PORTS; i++)
|
||||
if (BX_OHCI_THIS hub.usb_port[i].HcRhPortStatus.ccs && (BX_OHCI_THIS hub.usb_port[i].device != NULL))
|
||||
DEV_usb_send_msg(BX_OHCI_THIS hub.usb_port[i].device, USB_MSG_RESET);
|
||||
@ -1014,7 +1014,7 @@ Bit32u bx_usb_ohci_c::get_frame_remaining(void)
|
||||
Bit16u bit_time, fr;
|
||||
|
||||
bit_time = (Bit16u)((bx_pc_system.time_usec() - BX_OHCI_THIS hub.sof_time) * 12);
|
||||
if ((BX_OHCI_THIS hub.op_regs.HcControl.hcfs != 2) ||
|
||||
if ((BX_OHCI_THIS hub.op_regs.HcControl.hcfs != OHCI_USB_OPERATIONAL) ||
|
||||
(bit_time > BX_OHCI_THIS hub.op_regs.HcFmInterval.fi)) {
|
||||
fr = 0;
|
||||
} else {
|
||||
@ -1036,7 +1036,7 @@ void bx_usb_ohci_c::usb_frame_timer(void)
|
||||
Bit32u address, ed_address;
|
||||
Bit16u zero = 0;
|
||||
|
||||
if (BX_OHCI_THIS hub.op_regs.HcControl.hcfs == 2) {
|
||||
if (BX_OHCI_THIS hub.op_regs.HcControl.hcfs == OHCI_USB_OPERATIONAL) {
|
||||
// set remaining to the interval amount.
|
||||
BX_OHCI_THIS hub.op_regs.HcFmRemainingToggle = BX_OHCI_THIS hub.op_regs.HcFmInterval.fit;
|
||||
BX_OHCI_THIS hub.sof_time = bx_pc_system.time_usec();
|
||||
@ -1188,17 +1188,34 @@ bx_bool bx_usb_ohci_c::process_ed(struct OHCI_ED *ed, const Bit32u ed_address)
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ohci_async_complete_packet(USBPacket *packet, void *dev)
|
||||
void ohci_event_handler(int event, USBPacket *packet, void *dev, int port)
|
||||
{
|
||||
((bx_usb_ohci_c*)dev)->async_complete_packet(packet);
|
||||
((bx_usb_ohci_c*)dev)->event_handler(event, packet, port);
|
||||
}
|
||||
|
||||
void bx_usb_ohci_c::async_complete_packet(USBPacket *packet)
|
||||
void bx_usb_ohci_c::event_handler(int event, USBPacket *packet, int port)
|
||||
{
|
||||
BX_DEBUG(("Async packet completion"));
|
||||
USBAsync *p = container_of_usb_packet(packet);
|
||||
p->done = 1;
|
||||
BX_OHCI_THIS process_lists();
|
||||
Bit32u intr = 0;
|
||||
|
||||
if (event == USB_EVENT_ASYNC) {
|
||||
BX_DEBUG(("Async packet completion"));
|
||||
USBAsync *p = container_of_usb_packet(packet);
|
||||
p->done = 1;
|
||||
BX_OHCI_THIS process_lists();
|
||||
} else if (event == USB_EVENT_WAKEUP) {
|
||||
if (BX_OHCI_THIS hub.usb_port[port].HcRhPortStatus.pss) {
|
||||
BX_OHCI_THIS hub.usb_port[port].HcRhPortStatus.pss = 0;
|
||||
BX_OHCI_THIS hub.usb_port[port].HcRhPortStatus.pssc = 1;
|
||||
intr = OHCI_INTR_RHSC;
|
||||
}
|
||||
if (BX_OHCI_THIS hub.op_regs.HcControl.hcfs == OHCI_USB_SUSPEND) {
|
||||
BX_OHCI_THIS hub.op_regs.HcControl.hcfs = OHCI_USB_RESUME;
|
||||
intr = OHCI_INTR_RD;
|
||||
}
|
||||
set_interrupt(intr);
|
||||
} else {
|
||||
BX_ERROR(("unknown/unsupported event (id=%d) on port #%d", event, port+1));
|
||||
}
|
||||
}
|
||||
|
||||
bx_bool bx_usb_ohci_c::process_td(struct OHCI_TD *td, struct OHCI_ED *ed)
|
||||
@ -1266,7 +1283,7 @@ bx_bool bx_usb_ohci_c::process_td(struct OHCI_TD *td, struct OHCI_ED *ed)
|
||||
p->packet.pid = pid;
|
||||
p->packet.devaddr = ED_GET_FA(ed);
|
||||
p->packet.devep = ED_GET_EN(ed);
|
||||
p->packet.complete_cb = ohci_async_complete_packet;
|
||||
p->packet.complete_cb = ohci_event_handler;
|
||||
p->packet.complete_dev = this;
|
||||
|
||||
BX_DEBUG((" pid = %s addr = %i endpnt = %i len = %i mps = %i (td->cbp = 0x%08X, td->be = 0x%08X)",
|
||||
@ -1526,11 +1543,13 @@ void bx_usb_ohci_c::usb_set_connect_status(Bit8u port, int type, bx_bool connect
|
||||
if (!device->init()) {
|
||||
usb_set_connect_status(port, type, 0);
|
||||
BX_ERROR(("port #%d: connect failed", port+1));
|
||||
return;
|
||||
} else {
|
||||
BX_INFO(("port #%d: connect: %s", port+1, device->get_info()));
|
||||
device->set_async_mode(1);
|
||||
}
|
||||
}
|
||||
device->set_event_handler(BX_OHCI_THIS_PTR, ohci_event_handler, port);
|
||||
} else { // not connected
|
||||
BX_OHCI_THIS hub.usb_port[port].HcRhPortStatus.ccs = 0;
|
||||
BX_OHCI_THIS hub.usb_port[port].HcRhPortStatus.pes = 0;
|
||||
|
@ -33,6 +33,12 @@
|
||||
|
||||
#define USB_OHCI_PORTS 2
|
||||
|
||||
// HCFS values
|
||||
#define OHCI_USB_RESET 0x00
|
||||
#define OHCI_USB_RESUME 0x01
|
||||
#define OHCI_USB_OPERATIONAL 0x02
|
||||
#define OHCI_USB_SUSPEND 0x03
|
||||
|
||||
#define OHCI_INTR_SO (1<<0) // Scheduling overrun
|
||||
#define OHCI_INTR_WD (1<<1) // HcDoneHead writeback
|
||||
#define OHCI_INTR_SF (1<<2) // Start of frame
|
||||
@ -253,7 +259,7 @@ public:
|
||||
virtual Bit32u pci_read_handler(Bit8u address, unsigned io_len);
|
||||
virtual void pci_write_handler(Bit8u address, Bit32u value, unsigned io_len);
|
||||
|
||||
void async_complete_packet(USBPacket *packet);
|
||||
void event_handler(int event, USBPacket *packet, int port);
|
||||
|
||||
static const char *usb_param_handler(bx_param_string_c *param, int set,
|
||||
const char *oldval, const char *val, int maxlen);
|
||||
|
Loading…
Reference in New Issue
Block a user