usb: Add USB_RET_ADD_TO_QUEUE packet result code

This can be used by usb-device code which wishes to process an entire endpoint
queue at once, to do this the usb-device code returns USB_RET_ADD_TO_QUEUE
from its handle_data class method and defines a flush_ep_queue class method
to call when the hcd is done queuing up packets.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
Hans de Goede 2012-10-24 18:14:07 +02:00 committed by Gerd Hoffmann
parent d0ff81b871
commit 36dfe324fd
8 changed files with 51 additions and 11 deletions

View File

@ -38,12 +38,13 @@
#define USB_TOKEN_IN 0x69 /* device -> host */
#define USB_TOKEN_OUT 0xe1 /* host -> device */
#define USB_RET_NODEV (-1)
#define USB_RET_NAK (-2)
#define USB_RET_STALL (-3)
#define USB_RET_BABBLE (-4)
#define USB_RET_IOERROR (-5)
#define USB_RET_ASYNC (-6)
#define USB_RET_NODEV (-1)
#define USB_RET_NAK (-2)
#define USB_RET_STALL (-3)
#define USB_RET_BABBLE (-4)
#define USB_RET_IOERROR (-5)
#define USB_RET_ASYNC (-6)
#define USB_RET_ADD_TO_QUEUE (-7)
#define USB_SPEED_LOW 0
#define USB_SPEED_FULL 1
@ -293,6 +294,12 @@ typedef struct USBDeviceClass {
void (*set_interface)(USBDevice *dev, int interface,
int alt_old, int alt_new);
/*
* Called when the hcd is done queuing packets for an endpoint, only
* necessary for devices which can return USB_RET_ADD_TO_QUEUE.
*/
void (*flush_ep_queue)(USBDevice *dev, USBEndpoint *ep);
const char *product_desc;
const USBDesc *usb_desc;
} USBDeviceClass;
@ -507,6 +514,8 @@ int usb_device_handle_data(USBDevice *dev, USBPacket *p);
void usb_device_set_interface(USBDevice *dev, int interface,
int alt_old, int alt_new);
void usb_device_flush_ep_queue(USBDevice *dev, USBEndpoint *ep);
const char *usb_device_get_product_desc(USBDevice *dev);
const USBDesc *usb_device_get_usb_desc(USBDevice *dev);

View File

@ -181,6 +181,14 @@ void usb_device_set_interface(USBDevice *dev, int interface,
}
}
void usb_device_flush_ep_queue(USBDevice *dev, USBEndpoint *ep)
{
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
if (klass->flush_ep_queue) {
klass->flush_ep_queue(dev, ep);
}
}
static int usb_qdev_init(DeviceState *qdev)
{
USBDevice *dev = USB_DEVICE(qdev);

View File

@ -393,6 +393,10 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p)
if (ret == USB_RET_ASYNC) {
usb_packet_set_state(p, USB_PACKET_ASYNC);
QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
} else if (ret == USB_RET_ADD_TO_QUEUE) {
usb_packet_set_state(p, USB_PACKET_QUEUED);
QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
ret = USB_RET_ASYNC;
} else {
/*
* When pipelining is enabled usb-devices must always return async,

View File

@ -2072,6 +2072,7 @@ static int ehci_state_horizqh(EHCIQueue *q)
static int ehci_fill_queue(EHCIPacket *p)
{
USBEndpoint *ep = p->packet.ep;
EHCIQueue *q = p->queue;
EHCIqtd qtd = p->qtd;
uint32_t qtdaddr, start_addr = p->qtdaddr;
@ -2107,6 +2108,9 @@ static int ehci_fill_queue(EHCIPacket *p)
assert(p->usb_status == USB_RET_ASYNC);
p->async = EHCI_ASYNC_INFLIGHT;
}
if (p->usb_status != USB_RET_PROCERR) {
usb_device_flush_ep_queue(ep->dev, ep);
}
return p->usb_status;
}

View File

@ -635,6 +635,7 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
ret = usb_handle_packet(dev, &ep->packey[dir].p);
if (ret == USB_RET_ASYNC) {
usb_device_flush_ep_queue(dev, uep);
ep->status[dir] = len;
return;
}

View File

@ -816,6 +816,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len);
ret = usb_handle_packet(dev, &ohci->usb_packet);
if (ret == USB_RET_ASYNC) {
usb_device_flush_ep_queue(dev, ep);
return 1;
}
}
@ -1018,6 +1019,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
DPRINTF("ret=%d\n", ret);
#endif
if (ret == USB_RET_ASYNC) {
usb_device_flush_ep_queue(dev, ep);
ohci->async_td = addr;
return 1;
}

View File

@ -818,7 +818,8 @@ out:
}
static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td,
uint32_t *int_mask, bool queuing)
uint32_t *int_mask, bool queuing,
struct USBEndpoint **ep_ret)
{
UHCIAsync *async;
int len = 0, max_len;
@ -870,6 +871,9 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td,
dev = uhci_find_device(s, (td->token >> 8) & 0x7f);
ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf);
if (ep_ret) {
*ep_ret = ep;
}
usb_packet_setup(&async->packet, pid, ep, addr);
qemu_sglist_add(&async->sgl, td->buffer, max_len);
usb_packet_map(&async->packet, &async->sgl);
@ -983,7 +987,7 @@ static int qhdb_insert(QhDb *db, uint32_t addr)
return 0;
}
static void uhci_fill_queue(UHCIState *s, UHCI_TD *td)
static void uhci_fill_queue(UHCIState *s, UHCI_TD *td, struct USBEndpoint *ep)
{
uint32_t int_mask = 0;
uint32_t plink = td->link;
@ -1005,7 +1009,7 @@ static void uhci_fill_queue(UHCIState *s, UHCI_TD *td)
break;
}
trace_usb_uhci_td_queue(plink & ~0xf, ptd.ctrl, ptd.token);
ret = uhci_handle_td(s, plink, &ptd, &int_mask, true);
ret = uhci_handle_td(s, plink, &ptd, &int_mask, true, NULL);
if (ret == TD_RESULT_ASYNC_CONT) {
break;
}
@ -1013,12 +1017,14 @@ static void uhci_fill_queue(UHCIState *s, UHCI_TD *td)
assert(int_mask == 0);
plink = ptd.link;
}
usb_device_flush_ep_queue(ep->dev, ep);
}
static void uhci_process_frame(UHCIState *s)
{
uint32_t frame_addr, link, old_td_ctrl, val, int_mask;
uint32_t curr_qh, td_count = 0;
struct USBEndpoint *curr_ep;
int cnt, ret;
UHCI_TD td;
UHCI_QH qh;
@ -1089,7 +1095,7 @@ static void uhci_process_frame(UHCIState *s)
trace_usb_uhci_td_load(curr_qh & ~0xf, link & ~0xf, td.ctrl, td.token);
old_td_ctrl = td.ctrl;
ret = uhci_handle_td(s, link, &td, &int_mask, false);
ret = uhci_handle_td(s, link, &td, &int_mask, false, &curr_ep);
if (old_td_ctrl != td.ctrl) {
/* update the status bits of the TD */
val = cpu_to_le32(td.ctrl);
@ -1108,7 +1114,7 @@ static void uhci_process_frame(UHCIState *s)
case TD_RESULT_ASYNC_START:
trace_usb_uhci_td_async(curr_qh & ~0xf, link & ~0xf);
uhci_fill_queue(s, &td);
uhci_fill_queue(s, &td, curr_ep);
link = curr_qh ? qh.link : td.link;
continue;

View File

@ -1652,6 +1652,7 @@ static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext
static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid)
{
XHCIEPContext *epctx;
USBEndpoint *ep = NULL;
uint64_t mfindex;
int length;
int i;
@ -1745,12 +1746,14 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
if (epid == 1) {
if (xhci_fire_ctl_transfer(xhci, xfer) >= 0) {
epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE;
ep = xfer->packet.ep;
} else {
fprintf(stderr, "xhci: error firing CTL transfer\n");
}
} else {
if (xhci_fire_transfer(xhci, xfer, epctx) >= 0) {
epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE;
ep = xfer->packet.ep;
} else {
if (!xfer->iso_xfer) {
fprintf(stderr, "xhci: error firing data transfer\n");
@ -1767,6 +1770,9 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
break;
}
}
if (ep) {
usb_device_flush_ep_queue(ep->dev, ep);
}
}
static TRBCCode xhci_enable_slot(XHCIState *xhci, unsigned int slotid)