Added support for multiple async packets on UHCI (TODO: other HCs).

This commit is contained in:
Volker Ruppert 2016-11-22 18:06:44 +00:00
parent 666780dab5
commit f82bb13f88
2 changed files with 92 additions and 30 deletions

View File

@ -59,6 +59,12 @@
const Bit8u uhci_iomask[32] = {2, 1, 2, 1, 2, 1, 2, 0, 4, 0, 0, 0, 1, 0, 0, 0,
3, 1, 3, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
static inline struct UHCIAsync *container_of_usb_packet(void *ptr)
{
return reinterpret_cast<struct UHCIAsync*>(static_cast<char*>(ptr) -
reinterpret_cast<size_t>(&(static_cast<struct UHCIAsync*>(0)->packet)));
}
// the device object
bx_uhci_core_c::bx_uhci_core_c()
@ -162,9 +168,9 @@ void bx_uhci_core_c::reset_uhci(unsigned type)
set_connect_status(j, hub.usb_port[j].device->get_type(), 1);
}
}
if (hub.async_td) {
usb_cancel_packet(&usb_packet);
hub.async_td = 0;
while (packets != NULL) {
usb_cancel_packet(&packets->packet);
remove_async_packet(packets);
}
}
@ -217,8 +223,7 @@ void bx_uhci_core_c::register_state(bx_list_c *parent)
// empty list for USB device state
new bx_list_c(port, "device");
}
BXRS_DEC_PARAM_FIELD(hub1, async_td, hub.async_td);
BXRS_PARAM_BOOL(hub1, async_complete, hub.async_complete);
// TODO: handle async packets
register_pci_state(hub1);
BXRS_PARAM_BOOL(list, busy, busy);
@ -747,12 +752,14 @@ void uhci_async_complete_packet(USBPacket *packet, void *dev)
void bx_uhci_core_c::async_complete_packet(USBPacket *packet)
{
BX_DEBUG(("Experimental async packet completion"));
hub.async_complete = 1;
UHCIAsync *p = container_of_usb_packet(packet);
p->done = 1;
}
bx_bool bx_uhci_core_c::DoTransfer(Bit32u address, Bit32u queue_num, struct TD *td) {
int len = 0, ret = 0;
UHCIAsync *p;
bx_bool completion;
Bit16u maxlen = (td->dword2 >> 21);
@ -763,8 +770,9 @@ bx_bool bx_uhci_core_c::DoTransfer(Bit32u address, Bit32u queue_num, struct TD *
BX_DEBUG(("QH%03i:TD found at address: 0x%08X", queue_num, address));
BX_DEBUG((" %08X %08X %08X %08X", td->dword0, td->dword1, td->dword2, td->dword3));
completion = (address == hub.async_td);
if (completion && !hub.async_complete) {
p = find_async_packet(address);
completion = (p != NULL);
if (completion && !p->done) {
return 0;
}
@ -787,31 +795,25 @@ bx_bool bx_uhci_core_c::DoTransfer(Bit32u address, Bit32u queue_num, struct TD *
maxlen &= 0x7FF;
if (completion) {
ret = usb_packet.len;
hub.async_td = 0;
hub.async_complete = 0;
ret = p->packet.len;
} else {
if (hub.async_td) {
BX_ERROR(("too many pending packets"));
return 0;
}
usb_packet_init(&usb_packet, maxlen);
usb_packet.pid = pid;
usb_packet.devaddr = addr;
usb_packet.devep = endpt;
usb_packet.complete_cb = uhci_async_complete_packet;
usb_packet.complete_dev = this;
p = create_async_packet(address, maxlen);
p->packet.pid = pid;
p->packet.devaddr = addr;
p->packet.devep = endpt;
p->packet.complete_cb = uhci_async_complete_packet;
p->packet.complete_dev = this;
switch (pid) {
case USB_TOKEN_OUT:
case USB_TOKEN_SETUP:
if (maxlen > 0) {
DEV_MEM_READ_PHYSICAL_DMA(td->dword3, maxlen, usb_packet.data);
DEV_MEM_READ_PHYSICAL_DMA(td->dword3, maxlen, p->packet.data);
}
ret = broadcast_packet(&usb_packet);
ret = broadcast_packet(&p->packet);
len = maxlen;
break;
case USB_TOKEN_IN:
ret = broadcast_packet(&usb_packet);
ret = broadcast_packet(&p->packet);
break;
default:
hub.usb_status.host_error = 1;
@ -819,7 +821,6 @@ bx_bool bx_uhci_core_c::DoTransfer(Bit32u address, Bit32u queue_num, struct TD *
return 0;
}
if (ret == USB_RET_ASYNC) {
hub.async_td = address;
BX_DEBUG(("Async packet deferred"));
return 0;
}
@ -832,7 +833,7 @@ bx_bool bx_uhci_core_c::DoTransfer(Bit32u address, Bit32u queue_num, struct TD *
ret = USB_RET_BABBLE;
}
if (len > 0) {
DEV_MEM_WRITE_PHYSICAL_DMA(td->dword3, len, usb_packet.data);
DEV_MEM_WRITE_PHYSICAL_DMA(td->dword3, len, p->packet.data);
}
} else {
len = 0;
@ -845,7 +846,7 @@ bx_bool bx_uhci_core_c::DoTransfer(Bit32u address, Bit32u queue_num, struct TD *
} else {
set_status(td, 1, 0, 0, 0, 0, 0, 0x007); // stalled
}
usb_packet_cleanup(&usb_packet);
remove_async_packet(p);
return 1;
}
@ -1045,4 +1046,56 @@ void bx_uhci_core_c::set_port_device(int port, usb_device_c *dev)
}
}
// Async packet support
UHCIAsync* bx_uhci_core_c::create_async_packet(Bit32u addr, int maxlen)
{
UHCIAsync *p;
p = new UHCIAsync;
usb_packet_init(&p->packet, maxlen);
p->td_addr = addr;
p->done = 0;
p->next = packets;
packets = p;
return p;
}
void bx_uhci_core_c::remove_async_packet(UHCIAsync *p)
{
UHCIAsync *last;
if (packets == p) {
packets = p->next;
} else {
last = packets;
while (last != NULL) {
if (last->next != p)
last = last->next;
else
break;
}
if (last) {
last->next = p->next;
} else {
return;
}
}
usb_packet_cleanup(&p->packet);
delete p;
}
UHCIAsync* bx_uhci_core_c::find_async_packet(Bit32u addr)
{
UHCIAsync *p = packets;
while (p != NULL) {
if (p->td_addr != addr)
p = p->next;
else
break;
}
return p;
}
#endif // BX_SUPPORT_PCI && BX_SUPPORT_USB_UHCI

View File

@ -149,8 +149,6 @@ typedef struct {
} usb_port[USB_UHCI_PORTS];
Bit8u devfunc;
Bit32u async_td;
bx_bool async_complete;
} bx_uhci_core_t;
#pragma pack (push, 1)
@ -172,6 +170,13 @@ struct HCSTACK {
bx_bool t;
};
typedef struct UHCIAsync {
USBPacket packet;
Bit32u td_addr;
bx_bool done;
struct UHCIAsync *next;
} UHCIAsync;
class bx_uhci_core_c : public bx_devmodel_c, public bx_pci_device_stub_c {
public:
bx_uhci_core_c();
@ -191,13 +196,17 @@ protected:
Bit8u global_reset;
bx_bool busy;
USBPacket usb_packet;
UHCIAsync *packets;
void update_irq(void);
int broadcast_packet(USBPacket *p);
void set_connect_status(Bit8u port, int type, bx_bool connected);
UHCIAsync* create_async_packet(Bit32u addr, int maxlen);
void remove_async_packet(UHCIAsync *packet);
UHCIAsync* find_async_packet(Bit32u addr);
static void uhci_timer_handler(void *);
void uhci_timer(void);
bx_bool DoTransfer(Bit32u address, Bit32u queue_num, struct TD *);