Added support for multiple async packets on OHCI.

- Moved UHCI async packet functions to usb_common.h for using then on both
  UHCI and OHCI.
- Modified OHCI packet handling based on the UHCI implementation.
- Renamed EHCI helper function to avoid conflicts.
- TODO: async packet support on xHCI.
This commit is contained in:
Volker Ruppert 2016-11-27 12:18:37 +00:00
parent ff3b20be1a
commit a22e66ef13
6 changed files with 101 additions and 112 deletions

View File

@ -59,12 +59,6 @@
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()
@ -170,7 +164,7 @@ void bx_uhci_core_c::reset_uhci(unsigned type)
}
while (packets != NULL) {
usb_cancel_packet(&packets->packet);
remove_async_packet(packets);
remove_async_packet(&packets, packets);
}
}
@ -752,14 +746,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"));
UHCIAsync *p = container_of_usb_packet(packet);
USBAsync *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;
USBAsync *p;
bx_bool completion;
Bit16u maxlen = (td->dword2 >> 21);
@ -770,7 +764,7 @@ 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));
p = find_async_packet(address);
p = find_async_packet(&packets, address);
completion = (p != NULL);
if (completion && !p->done) {
return 0;
@ -797,7 +791,7 @@ bx_bool bx_uhci_core_c::DoTransfer(Bit32u address, Bit32u queue_num, struct TD *
if (completion) {
ret = p->packet.len;
} else {
p = create_async_packet(address, maxlen);
p = create_async_packet(&packets, address, maxlen);
p->packet.pid = pid;
p->packet.devaddr = addr;
p->packet.devep = endpt;
@ -846,7 +840,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
}
remove_async_packet(p);
remove_async_packet(&packets, p);
return 1;
}
@ -1046,56 +1040,4 @@ 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

@ -170,13 +170,6 @@ 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();
@ -196,17 +189,13 @@ protected:
Bit8u global_reset;
bx_bool busy;
UHCIAsync *packets;
USBAsync *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 *);

View File

@ -135,6 +135,13 @@ struct USBPacket {
usb_device_c *dev;
};
typedef struct USBAsync {
USBPacket packet;
Bit32u td_addr;
bx_bool done;
struct USBAsync *next;
} USBAsync;
enum usbdev_type {
USB_DEV_TYPE_NONE=0,
USB_DEV_TYPE_MOUSE,
@ -249,4 +256,62 @@ static BX_CPP_INLINE void usb_packet_complete(USBPacket *p)
p->complete_cb(p, p->complete_dev);
}
// Async packet support
static BX_CPP_INLINE USBAsync* create_async_packet(USBAsync **base, Bit32u addr, int maxlen)
{
USBAsync *p;
p = new USBAsync;
usb_packet_init(&p->packet, maxlen);
p->td_addr = addr;
p->done = 0;
p->next = *base;
*base = p;
return p;
}
static BX_CPP_INLINE void remove_async_packet(USBAsync **base, USBAsync *p)
{
USBAsync *last;
if (*base == p) {
*base = p->next;
} else {
last = *base;
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;
}
static BX_CPP_INLINE USBAsync* find_async_packet(USBAsync **base, Bit32u addr)
{
USBAsync *p = *base;
while (p != NULL) {
if (p->td_addr != addr)
p = p->next;
else
break;
}
return p;
}
static BX_CPP_INLINE struct USBAsync *container_of_usb_packet(void *ptr)
{
return reinterpret_cast<struct USBAsync*>(static_cast<char*>(ptr) -
reinterpret_cast<size_t>(&(static_cast<struct USBAsync*>(0)->packet)));
}
#endif

View File

@ -117,7 +117,7 @@ typedef enum {
*data = val; \
} while(0)
static inline struct EHCIPacket *container_of_usb_packet(void *ptr)
static inline struct EHCIPacket *ehci_container_of_usb_packet(void *ptr)
{
return reinterpret_cast<struct EHCIPacket*>(static_cast<char*>(ptr) -
reinterpret_cast<size_t>(&(static_cast<struct EHCIPacket*>(0)->packet)));
@ -1290,7 +1290,7 @@ void bx_usb_ehci_c::async_complete_packet(USBPacket *packet)
EHCIPacket *p;
BX_DEBUG(("Experimental async packet completion"));
p = container_of_usb_packet(packet);
p = ehci_container_of_usb_packet(packet);
if (p->pid == USB_TOKEN_IN) {
BX_EHCI_THIS transfer(p);
}

View File

@ -367,9 +367,9 @@ void bx_usb_ohci_c::reset_hc()
}
}
if (BX_OHCI_THIS hub.async_td) {
usb_cancel_packet(&BX_OHCI_THIS usb_packet);
BX_OHCI_THIS hub.async_td = 0;
while (BX_OHCI_THIS packets != NULL) {
usb_cancel_packet(&BX_OHCI_THIS packets->packet);
remove_async_packet(&BX_OHCI_THIS packets, BX_OHCI_THIS packets);
}
}
@ -469,8 +469,7 @@ void bx_usb_ohci_c::register_state(void)
BXRS_PARAM_BOOL(hub, use_control_head, BX_OHCI_THIS hub.use_control_head);
BXRS_PARAM_BOOL(hub, use_bulk_head, BX_OHCI_THIS hub.use_bulk_head);
BXRS_DEC_PARAM_FIELD(hub, sof_time, BX_OHCI_THIS hub.sof_time);
BXRS_DEC_PARAM_FIELD(hub, async_td, BX_OHCI_THIS hub.async_td);
BXRS_PARAM_BOOL(hub, async_complete, BX_OHCI_THIS hub.async_complete);
// TODO: handle async packets
register_pci_state(hub);
}
@ -1192,7 +1191,8 @@ void ohci_async_complete_packet(USBPacket *packet, void *dev)
void bx_usb_ohci_c::async_complete_packet(USBPacket *packet)
{
BX_DEBUG(("Async packet completion"));
BX_OHCI_THIS hub.async_complete = 1;
USBAsync *p = container_of_usb_packet(packet);
p->done = 1;
// These hacks are currently required for async completion
BX_OHCI_THIS hub.use_control_head = 1;
BX_OHCI_THIS hub.use_bulk_head = 1;
@ -1207,11 +1207,13 @@ bx_bool bx_usb_ohci_c::process_td(struct OHCI_TD *td, struct OHCI_ED *ed)
int ilen, ret = 0, ret2 = 1;
Bit32u addr;
Bit16u maxlen = 0;
USBAsync *p;
bx_bool completion;
addr = ED_GET_HEADP(ed);
completion = (addr == BX_OHCI_THIS hub.async_td);
if (completion && !BX_OHCI_THIS hub.async_complete) {
p = find_async_packet(&packets, addr);
completion = (p != NULL);
if (completion && !p->done) {
return 0;
}
@ -1249,14 +1251,8 @@ bx_bool bx_usb_ohci_c::process_td(struct OHCI_TD *td, struct OHCI_ED *ed)
len = 0;
if (completion) {
ret = BX_OHCI_THIS usb_packet.len;
BX_OHCI_THIS hub.async_td = 0;
BX_OHCI_THIS hub.async_complete = 0;
ret = p->packet.len;
} else {
if (BX_OHCI_THIS hub.async_td) {
BX_ERROR(("too many pending packets"));
return 0;
}
switch (pid) {
case USB_TOKEN_SETUP:
case USB_TOKEN_OUT:
@ -1266,12 +1262,12 @@ bx_bool bx_usb_ohci_c::process_td(struct OHCI_TD *td, struct OHCI_ED *ed)
maxlen = len;
break;
}
usb_packet_init(&BX_OHCI_THIS usb_packet, maxlen);
BX_OHCI_THIS usb_packet.pid = pid;
BX_OHCI_THIS usb_packet.devaddr = ED_GET_FA(ed);
BX_OHCI_THIS usb_packet.devep = ED_GET_EN(ed);
BX_OHCI_THIS usb_packet.complete_cb = ohci_async_complete_packet;
BX_OHCI_THIS usb_packet.complete_dev = this;
p = create_async_packet(&packets, addr, maxlen);
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_dev = this;
BX_DEBUG((" pid = %s addr = %i endpnt = %i len = %i mps = %i (td->cbp = 0x%08X, td->be = 0x%08X)",
(pid == USB_TOKEN_IN)? "IN" : (pid == USB_TOKEN_OUT) ? "OUT" : (pid == USB_TOKEN_SETUP) ? "SETUP" : "UNKNOWN",
@ -1281,19 +1277,19 @@ bx_bool bx_usb_ohci_c::process_td(struct OHCI_TD *td, struct OHCI_ED *ed)
switch (pid) {
case USB_TOKEN_SETUP:
if (len > 0)
DEV_MEM_READ_PHYSICAL_DMA(TD_GET_CBP(td), len, BX_OHCI_THIS usb_packet.data);
DEV_MEM_READ_PHYSICAL_DMA(TD_GET_CBP(td), len, p->packet.data);
// 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 = BX_OHCI_THIS broadcast_packet(&BX_OHCI_THIS usb_packet)) >= 0)
if ((ret = BX_OHCI_THIS broadcast_packet(&p->packet)) >= 0)
ret = 8;
break;
case USB_TOKEN_OUT:
if (len > 0)
DEV_MEM_READ_PHYSICAL_DMA(TD_GET_CBP(td), maxlen, BX_OHCI_THIS usb_packet.data);
ret = BX_OHCI_THIS broadcast_packet(&BX_OHCI_THIS usb_packet);
DEV_MEM_READ_PHYSICAL_DMA(TD_GET_CBP(td), maxlen, p->packet.data);
ret = BX_OHCI_THIS broadcast_packet(&p->packet);
break;
case USB_TOKEN_IN:
ret = BX_OHCI_THIS broadcast_packet(&BX_OHCI_THIS usb_packet);
ret = BX_OHCI_THIS broadcast_packet(&p->packet);
break;
default:
TD_SET_CC(td, UnexpectedPID);
@ -1302,7 +1298,6 @@ bx_bool bx_usb_ohci_c::process_td(struct OHCI_TD *td, struct OHCI_ED *ed)
}
if (ret == USB_RET_ASYNC) {
BX_OHCI_THIS hub.async_td = addr;
BX_DEBUG(("Async packet deferred"));
return 0;
}
@ -1311,10 +1306,10 @@ bx_bool bx_usb_ohci_c::process_td(struct OHCI_TD *td, struct OHCI_ED *ed)
if (((TD_GET_CBP(td) & 0xfff) + ret) > 0x1000) {
len1 = 0x1000 - (TD_GET_CBP(td) & 0xfff);
len2 = ret - len1;
DEV_MEM_WRITE_PHYSICAL_DMA(TD_GET_CBP(td), len1, BX_OHCI_THIS usb_packet.data);
DEV_MEM_WRITE_PHYSICAL_DMA((TD_GET_BE(td) & ~0xfff), len2, BX_OHCI_THIS usb_packet.data+len1);
DEV_MEM_WRITE_PHYSICAL_DMA(TD_GET_CBP(td), len1, p->packet.data);
DEV_MEM_WRITE_PHYSICAL_DMA((TD_GET_BE(td) & ~0xfff), len2, p->packet.data+len1);
} else {
DEV_MEM_WRITE_PHYSICAL_DMA(TD_GET_CBP(td), ret, BX_OHCI_THIS usb_packet.data);
DEV_MEM_WRITE_PHYSICAL_DMA(TD_GET_CBP(td), ret, p->packet.data);
}
}
if ((ret == (int)len) || ((pid == USB_TOKEN_IN) && (ret >= 0) &&
@ -1369,7 +1364,7 @@ bx_bool bx_usb_ohci_c::process_td(struct OHCI_TD *td, struct OHCI_ED *ed)
BX_DEBUG((" 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_DEBUG((" td->t = %i ed->c = %i", TD_GET_T(td), ED_GET_C(ed)));
usb_packet_cleanup(&BX_OHCI_THIS usb_packet);
remove_async_packet(&packets, p);
return ret2;
}

View File

@ -235,8 +235,6 @@ typedef struct {
bx_bool use_control_head;
bx_bool use_bulk_head;
Bit64u sof_time;
Bit32u async_td;
bx_bool async_complete;
Bit8u device_change;
int rt_conf_id;
@ -264,7 +262,7 @@ private:
bx_usb_ohci_t hub;
USBPacket usb_packet;
USBAsync *packets;
static void reset_hc();
static void reset_port(int);