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:
parent
d0ff81b871
commit
36dfe324fd
21
hw/usb.h
21
hw/usb.h
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user