usb: use iovecs in USBPacket

Zap data pointer from USBPacket, add a QEMUIOVector instead.
Add a bunch of helper functions to manage USBPacket data.
Switch over users to the new interface.

Note that USBPacket->len was used for two purposes:  First to
pass in the buffer size and second to return the number of
transfered bytes or the status code on async transfers.  There
is a new result variable for the latter.  A new status code
was added to catch uninitialized result.

Nobody creates iovecs with more than one element (yet).
Some users are (temporarely) limited to iovecs with a single
element to keep the patch size as small as possible.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
Gerd Hoffmann 2011-07-12 15:22:25 +02:00
parent d35bf9ade5
commit 4f4321c11f
21 changed files with 338 additions and 222 deletions

View File

@ -172,6 +172,7 @@ user-obj-y += cutils.o cache-utils.o
hw-obj-y = hw-obj-y =
hw-obj-y += vl.o loader.o hw-obj-y += vl.o loader.o
hw-obj-$(CONFIG_VIRTIO) += virtio-console.o hw-obj-$(CONFIG_VIRTIO) += virtio-console.o
hw-obj-y += usb-libhw.o
hw-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o hw-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
hw-obj-y += fw_cfg.o hw-obj-y += fw_cfg.o
hw-obj-$(CONFIG_PCI) += pci.o pci_bridge.o hw-obj-$(CONFIG_PCI) += pci.o pci_bridge.o

View File

@ -127,11 +127,11 @@ static int bt_hid_out(struct bt_hid_device_s *s)
USBPacket p; USBPacket p;
if (s->data_type == BT_DATA_OUTPUT) { if (s->data_type == BT_DATA_OUTPUT) {
p.pid = USB_TOKEN_OUT; usb_packet_init(&p);
p.devep = 1; usb_packet_setup(&p, USB_TOKEN_OUT, 0, 1);
p.data = s->dataout.buffer; usb_packet_addbuf(&p, s->dataout.buffer, s->dataout.len);
p.len = s->dataout.len;
s->dataout.len = s->usbdev->info->handle_data(s->usbdev, &p); s->dataout.len = s->usbdev->info->handle_data(s->usbdev, &p);
usb_packet_cleanup(&p);
return s->dataout.len; return s->dataout.len;
} }
@ -150,11 +150,11 @@ static int bt_hid_in(struct bt_hid_device_s *s)
{ {
USBPacket p; USBPacket p;
p.pid = USB_TOKEN_IN; usb_packet_init(&p);
p.devep = 1; usb_packet_setup(&p, USB_TOKEN_IN, 0, 1);
p.data = s->datain.buffer; usb_packet_addbuf(&p, s->dataout.buffer, sizeof(s->datain.buffer));
p.len = sizeof(s->datain.buffer);
s->datain.len = s->usbdev->info->handle_data(s->usbdev, &p); s->datain.len = s->usbdev->info->handle_data(s->usbdev, &p);
usb_packet_cleanup(&p);
return s->datain.len; return s->datain.len;
} }

View File

@ -234,11 +234,11 @@ static void softusb_usbdev_datain(void *opaque)
USBPacket p; USBPacket p;
p.pid = USB_TOKEN_IN; usb_packet_init(&p);
p.devep = 1; usb_packet_setup(&p, USB_TOKEN_IN, 0, 1);
p.data = s->kbd_usb_buffer; usb_packet_addbuf(&p, s->kbd_usb_buffer, sizeof(s->kbd_usb_buffer));
p.len = sizeof(s->kbd_usb_buffer);
s->usbdev->info->handle_data(s->usbdev, &p); s->usbdev->info->handle_data(s->usbdev, &p);
usb_packet_cleanup(&p);
softusb_kbd_changed(s); softusb_kbd_changed(s);
} }

View File

@ -294,9 +294,9 @@ static inline int usb_bt_fifo_dequeue(struct usb_hci_in_fifo_s *fifo,
if (likely(!fifo->len)) if (likely(!fifo->len))
return USB_RET_STALL; return USB_RET_STALL;
len = MIN(p->len, fifo->fifo[fifo->start].len); len = MIN(p->iov.size, fifo->fifo[fifo->start].len);
memcpy(p->data, fifo->fifo[fifo->start].data, len); usb_packet_copy(p, fifo->fifo[fifo->start].data, len);
if (len == p->len) { if (len == p->iov.size) {
fifo->fifo[fifo->start].len -= len; fifo->fifo[fifo->start].len -= len;
fifo->fifo[fifo->start].data += len; fifo->fifo[fifo->start].data += len;
} else { } else {
@ -319,20 +319,13 @@ static inline void usb_bt_fifo_out_enqueue(struct USBBtState *s,
struct usb_hci_out_fifo_s *fifo, struct usb_hci_out_fifo_s *fifo,
void (*send)(struct HCIInfo *, const uint8_t *, int), void (*send)(struct HCIInfo *, const uint8_t *, int),
int (*complete)(const uint8_t *, int), int (*complete)(const uint8_t *, int),
const uint8_t *data, int len) USBPacket *p)
{ {
if (fifo->len) { usb_packet_copy(p, fifo->data + fifo->len, p->iov.size);
memcpy(fifo->data + fifo->len, data, len); fifo->len += p->iov.size;
fifo->len += len; if (complete(fifo->data, fifo->len)) {
if (complete(fifo->data, fifo->len)) { send(s->hci, fifo->data, fifo->len);
send(s->hci, fifo->data, fifo->len); fifo->len = 0;
fifo->len = 0;
}
} else if (complete(data, len))
send(s->hci, data, len);
else {
memcpy(fifo->data, data, len);
fifo->len = len;
} }
/* TODO: do we need to loop? */ /* TODO: do we need to loop? */
@ -432,7 +425,7 @@ static int usb_bt_handle_control(USBDevice *dev, USBPacket *p,
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE) << 8): case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE) << 8):
if (s->config) if (s->config)
usb_bt_fifo_out_enqueue(s, &s->outcmd, s->hci->cmd_send, usb_bt_fifo_out_enqueue(s, &s->outcmd, s->hci->cmd_send,
usb_bt_hci_cmd_complete, data, length); usb_bt_hci_cmd_complete, p);
break; break;
default: default:
fail: fail:
@ -474,12 +467,12 @@ static int usb_bt_handle_data(USBDevice *dev, USBPacket *p)
switch (p->devep & 0xf) { switch (p->devep & 0xf) {
case USB_ACL_EP: case USB_ACL_EP:
usb_bt_fifo_out_enqueue(s, &s->outacl, s->hci->acl_send, usb_bt_fifo_out_enqueue(s, &s->outacl, s->hci->acl_send,
usb_bt_hci_acl_complete, p->data, p->len); usb_bt_hci_acl_complete, p);
break; break;
case USB_SCO_EP: case USB_SCO_EP:
usb_bt_fifo_out_enqueue(s, &s->outsco, s->hci->sco_send, usb_bt_fifo_out_enqueue(s, &s->outsco, s->hci->sco_send,
usb_bt_hci_sco_complete, p->data, p->len); usb_bt_hci_sco_complete, p);
break; break;
default: default:

View File

@ -934,16 +934,16 @@ static int ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
{ {
CCID_Header *ccid_header; CCID_Header *ccid_header;
if (p->len + s->bulk_out_pos > BULK_OUT_DATA_SIZE) { if (p->iov.size + s->bulk_out_pos > BULK_OUT_DATA_SIZE) {
return USB_RET_STALL; return USB_RET_STALL;
} }
ccid_header = (CCID_Header *)s->bulk_out_data; ccid_header = (CCID_Header *)s->bulk_out_data;
memcpy(s->bulk_out_data + s->bulk_out_pos, p->data, p->len); usb_packet_copy(p, s->bulk_out_data + s->bulk_out_pos, p->iov.size);
s->bulk_out_pos += p->len; s->bulk_out_pos += p->iov.size;
if (p->len == CCID_MAX_PACKET_SIZE) { if (p->iov.size == CCID_MAX_PACKET_SIZE) {
DPRINTF(s, D_VERBOSE, DPRINTF(s, D_VERBOSE,
"usb-ccid: bulk_in: expecting more packets (%d/%d)\n", "usb-ccid: bulk_in: expecting more packets (%zd/%d)\n",
p->len, ccid_header->dwLength); p->iov.size, ccid_header->dwLength);
return 0; return 0;
} }
if (s->bulk_out_pos < 10) { if (s->bulk_out_pos < 10) {
@ -1006,15 +1006,17 @@ static int ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
return 0; return 0;
} }
static int ccid_bulk_in_copy_to_guest(USBCCIDState *s, uint8_t *data, int len) static int ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p)
{ {
int ret = 0; int ret = 0;
assert(len > 0); assert(p->iov.size > 0);
ccid_bulk_in_get(s); ccid_bulk_in_get(s);
if (s->current_bulk_in != NULL) { if (s->current_bulk_in != NULL) {
ret = MIN(s->current_bulk_in->len - s->current_bulk_in->pos, len); ret = MIN(s->current_bulk_in->len - s->current_bulk_in->pos,
memcpy(data, s->current_bulk_in->data + s->current_bulk_in->pos, ret); p->iov.size);
usb_packet_copy(p, s->current_bulk_in->data +
s->current_bulk_in->pos, ret);
s->current_bulk_in->pos += ret; s->current_bulk_in->pos += ret;
if (s->current_bulk_in->pos == s->current_bulk_in->len) { if (s->current_bulk_in->pos == s->current_bulk_in->len) {
ccid_bulk_in_release(s); ccid_bulk_in_release(s);
@ -1025,11 +1027,13 @@ static int ccid_bulk_in_copy_to_guest(USBCCIDState *s, uint8_t *data, int len)
} }
if (ret > 0) { if (ret > 0) {
DPRINTF(s, D_MORE_INFO, DPRINTF(s, D_MORE_INFO,
"%s: %d/%d req/act to guest (BULK_IN)\n", __func__, len, ret); "%s: %zd/%d req/act to guest (BULK_IN)\n",
__func__, p->iov.size, ret);
} }
if (ret != USB_RET_NAK && ret < len) { if (ret != USB_RET_NAK && ret < p->iov.size) {
DPRINTF(s, 1, DPRINTF(s, 1,
"%s: returning short (EREMOTEIO) %d < %d\n", __func__, ret, len); "%s: returning short (EREMOTEIO) %d < %zd\n",
__func__, ret, p->iov.size);
} }
return ret; return ret;
} }
@ -1038,8 +1042,7 @@ static int ccid_handle_data(USBDevice *dev, USBPacket *p)
{ {
USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev); USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
int ret = 0; int ret = 0;
uint8_t *data = p->data; uint8_t buf[2];
int len = p->len;
switch (p->pid) { switch (p->pid) {
case USB_TOKEN_OUT: case USB_TOKEN_OUT:
@ -1049,24 +1052,25 @@ static int ccid_handle_data(USBDevice *dev, USBPacket *p)
case USB_TOKEN_IN: case USB_TOKEN_IN:
switch (p->devep & 0xf) { switch (p->devep & 0xf) {
case CCID_BULK_IN_EP: case CCID_BULK_IN_EP:
if (!len) { if (!p->iov.size) {
ret = USB_RET_NAK; ret = USB_RET_NAK;
} else { } else {
ret = ccid_bulk_in_copy_to_guest(s, data, len); ret = ccid_bulk_in_copy_to_guest(s, p);
} }
break; break;
case CCID_INT_IN_EP: case CCID_INT_IN_EP:
if (s->notify_slot_change) { if (s->notify_slot_change) {
/* page 56, RDR_to_PC_NotifySlotChange */ /* page 56, RDR_to_PC_NotifySlotChange */
data[0] = CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange; buf[0] = CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange;
data[1] = s->bmSlotICCState; buf[1] = s->bmSlotICCState;
usb_packet_copy(p, buf, 2);
ret = 2; ret = 2;
s->notify_slot_change = false; s->notify_slot_change = false;
s->bmSlotICCState &= ~SLOT_0_CHANGED_MASK; s->bmSlotICCState &= ~SLOT_0_CHANGED_MASK;
DPRINTF(s, D_INFO, DPRINTF(s, D_INFO,
"handle_data: int_in: notify_slot_change %X, " "handle_data: int_in: notify_slot_change %X, "
"requested len %d\n", "requested len %zd\n",
s->bmSlotICCState, len); s->bmSlotICCState, p->iov.size);
} }
break; break;
default: default:

View File

@ -1235,7 +1235,7 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
trace_usb_ehci_queue_action(q, "wakeup"); trace_usb_ehci_queue_action(q, "wakeup");
assert(q->async == EHCI_ASYNC_INFLIGHT); assert(q->async == EHCI_ASYNC_INFLIGHT);
q->async = EHCI_ASYNC_FINISHED; q->async = EHCI_ASYNC_FINISHED;
q->usb_status = packet->len; q->usb_status = packet->result;
} }
static void ehci_execute_complete(EHCIQueue *q) static void ehci_execute_complete(EHCIQueue *q)
@ -1367,17 +1367,15 @@ static int ehci_execute(EHCIQueue *q)
continue; continue;
} }
q->packet.pid = q->pid; usb_packet_setup(&q->packet, q->pid, devadr, endp);
q->packet.devaddr = devadr; usb_packet_addbuf(&q->packet, q->buffer, q->tbytes);
q->packet.devep = endp;
q->packet.data = q->buffer;
q->packet.len = q->tbytes;
ret = usb_handle_packet(dev, &q->packet); ret = usb_handle_packet(dev, &q->packet);
DPRINTF("submit: qh %x next %x qtd %x pid %x len %d (total %d) endp %x ret %d\n", DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd "
"(total %d) endp %x ret %d\n",
q->qhaddr, q->qh.next, q->qtdaddr, q->pid, q->qhaddr, q->qh.next, q->qtdaddr, q->pid,
q->packet.len, q->tbytes, endp, ret); q->packet.iov.size, q->tbytes, endp, ret);
if (ret != USB_RET_NODEV) { if (ret != USB_RET_NODEV) {
break; break;
@ -1457,11 +1455,8 @@ static int ehci_process_itd(EHCIState *ehci,
continue; continue;
} }
ehci->ipacket.pid = pid; usb_packet_setup(&ehci->ipacket, pid, devaddr, endp);
ehci->ipacket.devaddr = devaddr; usb_packet_addbuf(&ehci->ipacket, ehci->ibuffer, len);
ehci->ipacket.devep = endp;
ehci->ipacket.data = ehci->ibuffer;
ehci->ipacket.len = len;
ret = usb_handle_packet(dev, &ehci->ipacket); ret = usb_handle_packet(dev, &ehci->ipacket);

View File

@ -816,6 +816,7 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) static int usb_hid_handle_data(USBDevice *dev, USBPacket *p)
{ {
USBHIDState *s = (USBHIDState *)dev; USBHIDState *s = (USBHIDState *)dev;
uint8_t buf[p->iov.size];
int ret = 0; int ret = 0;
switch(p->pid) { switch(p->pid) {
@ -826,11 +827,12 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p)
return USB_RET_NAK; return USB_RET_NAK;
usb_hid_set_next_idle(s, curtime); usb_hid_set_next_idle(s, curtime);
if (s->kind == USB_MOUSE || s->kind == USB_TABLET) { if (s->kind == USB_MOUSE || s->kind == USB_TABLET) {
ret = usb_pointer_poll(s, p->data, p->len); ret = usb_pointer_poll(s, buf, p->iov.size);
} }
else if (s->kind == USB_KEYBOARD) { else if (s->kind == USB_KEYBOARD) {
ret = usb_keyboard_poll(s, p->data, p->len); ret = usb_keyboard_poll(s, buf, p->iov.size);
} }
usb_packet_copy(p, buf, ret);
s->changed = s->n > 0; s->changed = s->n > 0;
} else { } else {
goto fail; goto fail;

View File

@ -394,11 +394,12 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p)
if (p->devep == 1) { if (p->devep == 1) {
USBHubPort *port; USBHubPort *port;
unsigned int status; unsigned int status;
uint8_t buf[4];
int i, n; int i, n;
n = (NUM_PORTS + 1 + 7) / 8; n = (NUM_PORTS + 1 + 7) / 8;
if (p->len == 1) { /* FreeBSD workaround */ if (p->iov.size == 1) { /* FreeBSD workaround */
n = 1; n = 1;
} else if (n > p->len) { } else if (n > p->iov.size) {
return USB_RET_BABBLE; return USB_RET_BABBLE;
} }
status = 0; status = 0;
@ -409,8 +410,9 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p)
} }
if (status != 0) { if (status != 0) {
for(i = 0; i < n; i++) { for(i = 0; i < n; i++) {
p->data[i] = status >> (8 * i); buf[i] = status >> (8 * i);
} }
usb_packet_copy(p, buf, n);
ret = n; ret = n;
} else { } else {
ret = USB_RET_NAK; /* usb11 11.13.1 */ ret = USB_RET_NAK; /* usb11 11.13.1 */

63
hw/usb-libhw.c Normal file
View File

@ -0,0 +1,63 @@
/*
* QEMU USB emulation, libhw bits.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu-common.h"
#include "cpu-common.h"
#include "usb.h"
#include "dma.h"
int usb_packet_map(USBPacket *p, QEMUSGList *sgl)
{
int is_write = (p->pid == USB_TOKEN_IN);
target_phys_addr_t len;
void *mem;
int i;
for (i = 0; i < sgl->nsg; i++) {
len = sgl->sg[i].len;
mem = cpu_physical_memory_map(sgl->sg[i].base, &len,
is_write);
if (!mem) {
goto err;
}
qemu_iovec_add(&p->iov, mem, len);
if (len != sgl->sg[i].len) {
goto err;
}
}
return 0;
err:
usb_packet_unmap(p);
return -1;
}
void usb_packet_unmap(USBPacket *p)
{
int is_write = (p->pid == USB_TOKEN_IN);
int i;
for (i = 0; i < p->iov.niov; i++) {
cpu_physical_memory_unmap(p->iov.iov[i].iov_base,
p->iov.iov[i].iov_len, is_write,
p->iov.iov[i].iov_len);
}
}

View File

@ -207,8 +207,9 @@ static void usb_msd_send_status(MSDState *s, USBPacket *p)
csw.residue = s->residue; csw.residue = s->residue;
csw.status = s->result; csw.status = s->result;
len = MIN(sizeof(csw), p->len); len = MIN(sizeof(csw), p->iov.size);
memcpy(p->data, &csw, len); usb_packet_copy(p, &csw, len);
p->result = len;
} }
static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
@ -222,6 +223,7 @@ static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
if (p) { if (p) {
usb_msd_copy_data(s); usb_msd_copy_data(s);
if (s->packet && s->usb_len == 0) { if (s->packet && s->usb_len == 0) {
p->result = p->iov.size;
/* Set s->packet to NULL before calling usb_packet_complete /* Set s->packet to NULL before calling usb_packet_complete
because another request may be issued before because another request may be issued before
usb_packet_complete returns. */ usb_packet_complete returns. */
@ -257,6 +259,7 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status)
if (s->data_len == 0) { if (s->data_len == 0) {
s->mode = USB_MSDM_CSW; s->mode = USB_MSDM_CSW;
} }
p->result = p->iov.size;
} }
s->packet = NULL; s->packet = NULL;
usb_packet_complete(&s->dev, p); usb_packet_complete(&s->dev, p);
@ -342,9 +345,10 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
int ret = 0; int ret = 0;
struct usb_msd_cbw cbw; struct usb_msd_cbw cbw;
uint8_t devep = p->devep; uint8_t devep = p->devep;
uint8_t *data = p->data; uint8_t *data = p->iov.iov[0].iov_base;
int len = p->len; int len = p->iov.iov[0].iov_len;
assert(p->iov.niov == 1); /* temporary */
switch (p->pid) { switch (p->pid) {
case USB_TOKEN_OUT: case USB_TOKEN_OUT:
if (devep != 2) if (devep != 2)

View File

@ -365,6 +365,8 @@ struct MUSBState *musb_init(qemu_irq *irqs)
s->ep[i].maxp[1] = 0x40; s->ep[i].maxp[1] = 0x40;
s->ep[i].musb = s; s->ep[i].musb = s;
s->ep[i].epnum = i; s->ep[i].epnum = i;
usb_packet_init(&s->ep[i].packey[0].p);
usb_packet_init(&s->ep[i].packey[1].p);
} }
usb_bus_new(&s->bus, &musb_bus_ops, NULL /* FIXME */); usb_bus_new(&s->bus, &musb_bus_ops, NULL /* FIXME */);
@ -605,12 +607,10 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
ep->interrupt[dir] = ttype == USB_ENDPOINT_XFER_INT; ep->interrupt[dir] = ttype == USB_ENDPOINT_XFER_INT;
ep->delayed_cb[dir] = cb; ep->delayed_cb[dir] = cb;
ep->packey[dir].p.pid = pid;
/* A wild guess on the FADDR semantics... */ /* A wild guess on the FADDR semantics... */
ep->packey[dir].p.devaddr = ep->faddr[idx]; usb_packet_setup(&ep->packey[dir].p, pid, ep->faddr[idx],
ep->packey[dir].p.devep = ep->type[idx] & 0xf; ep->type[idx] & 0xf);
ep->packey[dir].p.data = (void *) ep->buf[idx]; usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len);
ep->packey[dir].p.len = len;
ep->packey[dir].ep = ep; ep->packey[dir].ep = ep;
ep->packey[dir].dir = dir; ep->packey[dir].dir = dir;
@ -738,7 +738,7 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque)
if (ep->status[1] == USB_RET_STALL) { if (ep->status[1] == USB_RET_STALL) {
ep->status[1] = 0; ep->status[1] = 0;
packey->len = 0; packey->result = 0;
ep->csr[1] |= MGC_M_RXCSR_H_RXSTALL; ep->csr[1] |= MGC_M_RXCSR_H_RXSTALL;
if (!epnum) if (!epnum)
@ -752,7 +752,7 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque)
* Data-errors in Isochronous. */ * Data-errors in Isochronous. */
if (ep->interrupt[1]) if (ep->interrupt[1])
return musb_packet(s, ep, epnum, USB_TOKEN_IN, return musb_packet(s, ep, epnum, USB_TOKEN_IN,
packey->len, musb_rx_packet_complete, 1); packey->iov.size, musb_rx_packet_complete, 1);
ep->csr[1] |= MGC_M_RXCSR_DATAERROR; ep->csr[1] |= MGC_M_RXCSR_DATAERROR;
if (!epnum) if (!epnum)
@ -777,14 +777,14 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque)
/* TODO: check len for over/underruns of an OUT packet? */ /* TODO: check len for over/underruns of an OUT packet? */
/* TODO: perhaps make use of e->ext_size[1] here. */ /* TODO: perhaps make use of e->ext_size[1] here. */
packey->len = ep->status[1]; packey->result = ep->status[1];
if (!(ep->csr[1] & (MGC_M_RXCSR_H_RXSTALL | MGC_M_RXCSR_DATAERROR))) { if (!(ep->csr[1] & (MGC_M_RXCSR_H_RXSTALL | MGC_M_RXCSR_DATAERROR))) {
ep->csr[1] |= MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY; ep->csr[1] |= MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY;
if (!epnum) if (!epnum)
ep->csr[0] |= MGC_M_CSR0_RXPKTRDY; ep->csr[0] |= MGC_M_CSR0_RXPKTRDY;
ep->rxcount = packey->len; /* XXX: MIN(packey->len, ep->maxp[1]); */ ep->rxcount = packey->result; /* XXX: MIN(packey->len, ep->maxp[1]); */
/* In DMA mode: assert DMA request for this EP */ /* In DMA mode: assert DMA request for this EP */
} }
@ -856,12 +856,12 @@ static void musb_rx_req(MUSBState *s, int epnum)
* 64 bytes of the FIFO, only move the FIFO start and return. (Obsolete) */ * 64 bytes of the FIFO, only move the FIFO start and return. (Obsolete) */
if (ep->packey[1].p.pid == USB_TOKEN_IN && ep->status[1] >= 0 && if (ep->packey[1].p.pid == USB_TOKEN_IN && ep->status[1] >= 0 &&
(ep->fifostart[1]) + ep->rxcount < (ep->fifostart[1]) + ep->rxcount <
ep->packey[1].p.len) { ep->packey[1].p.iov.size) {
TRACE("0x%08x, %d", ep->fifostart[1], ep->rxcount ); TRACE("0x%08x, %d", ep->fifostart[1], ep->rxcount );
ep->fifostart[1] += ep->rxcount; ep->fifostart[1] += ep->rxcount;
ep->fifolen[1] = 0; ep->fifolen[1] = 0;
ep->rxcount = MIN(ep->packey[0].p.len - (ep->fifostart[1]), ep->rxcount = MIN(ep->packey[0].p.iov.size - (ep->fifostart[1]),
ep->maxp[1]); ep->maxp[1]);
ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT; ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT;

View File

@ -29,6 +29,7 @@
#include "net.h" #include "net.h"
#include "qemu-queue.h" #include "qemu-queue.h"
#include "sysemu.h" #include "sysemu.h"
#include "iov.h"
/*#define TRAFFIC_DEBUG*/ /*#define TRAFFIC_DEBUG*/
/* Thanks to NetChip Technologies for donating this product ID. /* Thanks to NetChip Technologies for donating this product ID.
@ -1121,28 +1122,23 @@ static int usb_net_handle_control(USBDevice *dev, USBPacket *p,
static int usb_net_handle_statusin(USBNetState *s, USBPacket *p) static int usb_net_handle_statusin(USBNetState *s, USBPacket *p)
{ {
le32 buf[2];
int ret = 8; int ret = 8;
if (p->len < 8) if (p->iov.size < 8) {
return USB_RET_STALL; return USB_RET_STALL;
}
((le32 *) p->data)[0] = cpu_to_le32(1); buf[0] = cpu_to_le32(1);
((le32 *) p->data)[1] = cpu_to_le32(0); buf[1] = cpu_to_le32(0);
usb_packet_copy(p, buf, 8);
if (!s->rndis_resp.tqh_first) if (!s->rndis_resp.tqh_first)
ret = USB_RET_NAK; ret = USB_RET_NAK;
#ifdef TRAFFIC_DEBUG #ifdef TRAFFIC_DEBUG
fprintf(stderr, "usbnet: interrupt poll len %u return %d", p->len, ret); fprintf(stderr, "usbnet: interrupt poll len %zu return %d",
{ p->iov.size, ret);
int i; iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", ret);
fprintf(stderr, ":");
for (i = 0; i < ret; i++) {
if (!(i & 15))
fprintf(stderr, "\n%04x:", i);
fprintf(stderr, " %02x", p->data[i]);
}
fprintf(stderr, "\n\n");
}
#endif #endif
return ret; return ret;
@ -1162,9 +1158,10 @@ static int usb_net_handle_datain(USBNetState *s, USBPacket *p)
return ret; return ret;
} }
ret = s->in_len - s->in_ptr; ret = s->in_len - s->in_ptr;
if (ret > p->len) if (ret > p->iov.size) {
ret = p->len; ret = p->iov.size;
memcpy(p->data, &s->in_buf[s->in_ptr], ret); }
usb_packet_copy(p, &s->in_buf[s->in_ptr], ret);
s->in_ptr += ret; s->in_ptr += ret;
if (s->in_ptr >= s->in_len && if (s->in_ptr >= s->in_len &&
(is_rndis(s) || (s->in_len & (64 - 1)) || !ret)) { (is_rndis(s) || (s->in_len & (64 - 1)) || !ret)) {
@ -1173,17 +1170,8 @@ static int usb_net_handle_datain(USBNetState *s, USBPacket *p)
} }
#ifdef TRAFFIC_DEBUG #ifdef TRAFFIC_DEBUG
fprintf(stderr, "usbnet: data in len %u return %d", p->len, ret); fprintf(stderr, "usbnet: data in len %zu return %d", p->iov.size, ret);
{ iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", ret);
int i;
fprintf(stderr, ":");
for (i = 0; i < ret; i++) {
if (!(i & 15))
fprintf(stderr, "\n%04x:", i);
fprintf(stderr, " %02x", p->data[i]);
}
fprintf(stderr, "\n\n");
}
#endif #endif
return ret; return ret;
@ -1191,29 +1179,20 @@ static int usb_net_handle_datain(USBNetState *s, USBPacket *p)
static int usb_net_handle_dataout(USBNetState *s, USBPacket *p) static int usb_net_handle_dataout(USBNetState *s, USBPacket *p)
{ {
int ret = p->len; int ret = p->iov.size;
int sz = sizeof(s->out_buf) - s->out_ptr; int sz = sizeof(s->out_buf) - s->out_ptr;
struct rndis_packet_msg_type *msg = struct rndis_packet_msg_type *msg =
(struct rndis_packet_msg_type *) s->out_buf; (struct rndis_packet_msg_type *) s->out_buf;
uint32_t len; uint32_t len;
#ifdef TRAFFIC_DEBUG #ifdef TRAFFIC_DEBUG
fprintf(stderr, "usbnet: data out len %u\n", p->len); fprintf(stderr, "usbnet: data out len %zu\n", p->iov.size);
{ iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", p->iov.size);
int i;
fprintf(stderr, ":");
for (i = 0; i < p->len; i++) {
if (!(i & 15))
fprintf(stderr, "\n%04x:", i);
fprintf(stderr, " %02x", p->data[i]);
}
fprintf(stderr, "\n\n");
}
#endif #endif
if (sz > ret) if (sz > ret)
sz = ret; sz = ret;
memcpy(&s->out_buf[s->out_ptr], p->data, sz); usb_packet_copy(p, &s->out_buf[s->out_ptr], sz);
s->out_ptr += sz; s->out_ptr += sz;
if (!is_rndis(s)) { if (!is_rndis(s)) {
@ -1277,8 +1256,8 @@ static int usb_net_handle_data(USBDevice *dev, USBPacket *p)
} }
if (ret == USB_RET_STALL) if (ret == USB_RET_STALL)
fprintf(stderr, "usbnet: failed data transaction: " fprintf(stderr, "usbnet: failed data transaction: "
"pid 0x%x ep 0x%x len 0x%x\n", "pid 0x%x ep 0x%x len 0x%zx\n",
p->pid, p->devep, p->len); p->pid, p->devep, p->iov.size);
return ret; return ret;
} }

View File

@ -777,18 +777,17 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
} }
if (completion) { if (completion) {
ret = ohci->usb_packet.len; ret = ohci->usb_packet.result;
} else { } else {
ret = USB_RET_NODEV; ret = USB_RET_NODEV;
for (i = 0; i < ohci->num_ports; i++) { for (i = 0; i < ohci->num_ports; i++) {
dev = ohci->rhport[i].port.dev; dev = ohci->rhport[i].port.dev;
if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0) if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0)
continue; continue;
ohci->usb_packet.pid = pid; usb_packet_setup(&ohci->usb_packet, pid,
ohci->usb_packet.devaddr = OHCI_BM(ed->flags, ED_FA); OHCI_BM(ed->flags, ED_FA),
ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN); OHCI_BM(ed->flags, ED_EN));
ohci->usb_packet.data = ohci->usb_buf; usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len);
ohci->usb_packet.len = len;
ret = usb_handle_packet(dev, &ohci->usb_packet); ret = usb_handle_packet(dev, &ohci->usb_packet);
if (ret != USB_RET_NODEV) if (ret != USB_RET_NODEV)
break; break;
@ -959,7 +958,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
} }
#endif #endif
if (completion) { if (completion) {
ret = ohci->usb_packet.len; ret = ohci->usb_packet.result;
ohci->async_td = 0; ohci->async_td = 0;
ohci->async_complete = 0; ohci->async_complete = 0;
} else { } else {
@ -980,11 +979,10 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
#endif #endif
return 1; return 1;
} }
ohci->usb_packet.pid = pid; usb_packet_setup(&ohci->usb_packet, pid,
ohci->usb_packet.devaddr = OHCI_BM(ed->flags, ED_FA); OHCI_BM(ed->flags, ED_FA),
ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN); OHCI_BM(ed->flags, ED_EN));
ohci->usb_packet.data = ohci->usb_buf; usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len);
ohci->usb_packet.len = len;
ret = usb_handle_packet(dev, &ohci->usb_packet); ret = usb_handle_packet(dev, &ohci->usb_packet);
if (ret != USB_RET_NODEV) if (ret != USB_RET_NODEV)
break; break;
@ -1761,6 +1759,7 @@ static int usb_ohci_init(OHCIState *ohci, DeviceState *dev,
ohci->localmem_base = localmem_base; ohci->localmem_base = localmem_base;
ohci->name = dev->info->name; ohci->name = dev->info->name;
usb_packet_init(&ohci->usb_packet);
ohci->async_td = 0; ohci->async_td = 0;
qemu_register_reset(ohci_reset, ohci); qemu_register_reset(ohci_reset, ohci);

View File

@ -361,10 +361,11 @@ static int usb_serial_handle_data(USBDevice *dev, USBPacket *p)
USBSerialState *s = (USBSerialState *)dev; USBSerialState *s = (USBSerialState *)dev;
int ret = 0; int ret = 0;
uint8_t devep = p->devep; uint8_t devep = p->devep;
uint8_t *data = p->data; uint8_t *data = p->iov.iov[0].iov_base;
int len = p->len; int len = p->iov.iov[0].iov_len;
int first_len; int first_len;
assert(p->iov.niov == 1); /* temporary */
switch (p->pid) { switch (p->pid) {
case USB_TOKEN_OUT: case USB_TOKEN_OUT:
if (devep != 2) if (devep != 2)

View File

@ -30,6 +30,7 @@
#include "pci.h" #include "pci.h"
#include "qemu-timer.h" #include "qemu-timer.h"
#include "usb-uhci.h" #include "usb-uhci.h"
#include "iov.h"
//#define DEBUG //#define DEBUG
//#define DEBUG_DUMP_DATA //#define DEBUG_DUMP_DATA
@ -93,17 +94,12 @@ static const char *pid2str(int pid)
#endif #endif
#ifdef DEBUG_DUMP_DATA #ifdef DEBUG_DUMP_DATA
static void dump_data(const uint8_t *data, int len) static void dump_data(USBPacket *p, int ret)
{ {
int i; iov_hexdump(p->iov.iov, p->iov.niov, stderr, "uhci", ret);
printf("uhci: data: ");
for(i = 0; i < len; i++)
printf(" %02x", data[i]);
printf("\n");
} }
#else #else
static void dump_data(const uint8_t *data, int len) {} static void dump_data(USBPacket *p, int ret) {}
#endif #endif
typedef struct UHCIState UHCIState; typedef struct UHCIState UHCIState;
@ -179,12 +175,14 @@ static UHCIAsync *uhci_async_alloc(UHCIState *s)
async->token = 0; async->token = 0;
async->done = 0; async->done = 0;
async->isoc = 0; async->isoc = 0;
usb_packet_init(&async->packet);
return async; return async;
} }
static void uhci_async_free(UHCIState *s, UHCIAsync *async) static void uhci_async_free(UHCIState *s, UHCIAsync *async)
{ {
usb_packet_cleanup(&async->packet);
qemu_free(async); qemu_free(async);
} }
@ -648,10 +646,10 @@ static int uhci_broadcast_packet(UHCIState *s, USBPacket *p)
{ {
int i, ret; int i, ret;
DPRINTF("uhci: packet enter. pid %s addr 0x%02x ep %d len %d\n", DPRINTF("uhci: packet enter. pid %s addr 0x%02x ep %d len %zd\n",
pid2str(p->pid), p->devaddr, p->devep, p->len); pid2str(p->pid), p->devaddr, p->devep, p->iov.size);
if (p->pid == USB_TOKEN_OUT || p->pid == USB_TOKEN_SETUP) if (p->pid == USB_TOKEN_OUT || p->pid == USB_TOKEN_SETUP)
dump_data(p->data, p->len); dump_data(p, 0);
ret = USB_RET_NODEV; ret = USB_RET_NODEV;
for (i = 0; i < NB_PORTS && ret == USB_RET_NODEV; i++) { for (i = 0; i < NB_PORTS && ret == USB_RET_NODEV; i++) {
@ -662,9 +660,9 @@ static int uhci_broadcast_packet(UHCIState *s, USBPacket *p)
ret = usb_handle_packet(dev, p); ret = usb_handle_packet(dev, p);
} }
DPRINTF("uhci: packet exit. ret %d len %d\n", ret, p->len); DPRINTF("uhci: packet exit. ret %d len %zd\n", ret, p->iov.size);
if (p->pid == USB_TOKEN_IN && ret > 0) if (p->pid == USB_TOKEN_IN && ret > 0)
dump_data(p->data, ret); dump_data(p, ret);
return ret; return ret;
} }
@ -684,7 +682,7 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_
max_len = ((td->token >> 21) + 1) & 0x7ff; max_len = ((td->token >> 21) + 1) & 0x7ff;
pid = td->token & 0xff; pid = td->token & 0xff;
ret = async->packet.len; ret = async->packet.result;
if (td->ctrl & TD_CTRL_IOS) if (td->ctrl & TD_CTRL_IOS)
td->ctrl &= ~TD_CTRL_ACTIVE; td->ctrl &= ~TD_CTRL_ACTIVE;
@ -692,7 +690,7 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_
if (ret < 0) if (ret < 0)
goto out; goto out;
len = async->packet.len; len = async->packet.result;
td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff); td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff);
/* The NAK bit may have been set by a previous frame, so clear it /* The NAK bit may have been set by a previous frame, so clear it
@ -827,11 +825,9 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in
max_len = ((td->token >> 21) + 1) & 0x7ff; max_len = ((td->token >> 21) + 1) & 0x7ff;
pid = td->token & 0xff; pid = td->token & 0xff;
async->packet.pid = pid; usb_packet_setup(&async->packet, pid, (td->token >> 8) & 0x7f,
async->packet.devaddr = (td->token >> 8) & 0x7f; (td->token >> 15) & 0xf);
async->packet.devep = (td->token >> 15) & 0xf; usb_packet_addbuf(&async->packet, async->buffer, max_len);
async->packet.data = async->buffer;
async->packet.len = max_len;
switch(pid) { switch(pid) {
case USB_TOKEN_OUT: case USB_TOKEN_OUT:
@ -859,7 +855,7 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in
return 2; return 2;
} }
async->packet.len = len; async->packet.result = len;
done: done:
len = uhci_complete_td(s, td, async, int_mask); len = uhci_complete_td(s, td, async, int_mask);

View File

@ -308,6 +308,7 @@ static int usb_wacom_handle_control(USBDevice *dev, USBPacket *p,
static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p) static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p)
{ {
USBWacomState *s = (USBWacomState *) dev; USBWacomState *s = (USBWacomState *) dev;
uint8_t buf[p->iov.size];
int ret = 0; int ret = 0;
switch (p->pid) { switch (p->pid) {
@ -317,9 +318,10 @@ static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p)
return USB_RET_NAK; return USB_RET_NAK;
s->changed = 0; s->changed = 0;
if (s->mode == WACOM_MODE_HID) if (s->mode == WACOM_MODE_HID)
ret = usb_mouse_poll(s, p->data, p->len); ret = usb_mouse_poll(s, buf, p->iov.size);
else if (s->mode == WACOM_MODE_WACOM) else if (s->mode == WACOM_MODE_WACOM)
ret = usb_wacom_poll(s, p->data, p->len); ret = usb_wacom_poll(s, buf, p->iov.size);
usb_packet_copy(p, buf, ret);
break; break;
} }
/* Fall through. */ /* Fall through. */

View File

@ -25,6 +25,7 @@
*/ */
#include "qemu-common.h" #include "qemu-common.h"
#include "usb.h" #include "usb.h"
#include "iov.h"
void usb_attach(USBPort *port, USBDevice *dev) void usb_attach(USBPort *port, USBDevice *dev)
{ {
@ -72,10 +73,11 @@ static int do_token_setup(USBDevice *s, USBPacket *p)
int request, value, index; int request, value, index;
int ret = 0; int ret = 0;
if (p->len != 8) if (p->iov.size != 8) {
return USB_RET_STALL; return USB_RET_STALL;
}
memcpy(s->setup_buf, p->data, 8);
usb_packet_copy(p, s->setup_buf, p->iov.size);
s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6];
s->setup_index = 0; s->setup_index = 0;
@ -144,9 +146,10 @@ static int do_token_in(USBDevice *s, USBPacket *p)
case SETUP_STATE_DATA: case SETUP_STATE_DATA:
if (s->setup_buf[0] & USB_DIR_IN) { if (s->setup_buf[0] & USB_DIR_IN) {
int len = s->setup_len - s->setup_index; int len = s->setup_len - s->setup_index;
if (len > p->len) if (len > p->iov.size) {
len = p->len; len = p->iov.size;
memcpy(p->data, s->data_buf + s->setup_index, len); }
usb_packet_copy(p, s->data_buf + s->setup_index, len);
s->setup_index += len; s->setup_index += len;
if (s->setup_index >= s->setup_len) if (s->setup_index >= s->setup_len)
s->setup_state = SETUP_STATE_ACK; s->setup_state = SETUP_STATE_ACK;
@ -179,9 +182,10 @@ static int do_token_out(USBDevice *s, USBPacket *p)
case SETUP_STATE_DATA: case SETUP_STATE_DATA:
if (!(s->setup_buf[0] & USB_DIR_IN)) { if (!(s->setup_buf[0] & USB_DIR_IN)) {
int len = s->setup_len - s->setup_index; int len = s->setup_len - s->setup_index;
if (len > p->len) if (len > p->iov.size) {
len = p->len; len = p->iov.size;
memcpy(s->data_buf + s->setup_index, p->data, len); }
usb_packet_copy(p, s->data_buf + s->setup_index, len);
s->setup_index += len; s->setup_index += len;
if (s->setup_index >= s->setup_len) if (s->setup_index >= s->setup_len)
s->setup_state = SETUP_STATE_ACK; s->setup_state = SETUP_STATE_ACK;
@ -251,22 +255,22 @@ int usb_generic_handle_packet(USBDevice *s, USBPacket *p)
usb_packet_complete to complete their async control packets. */ usb_packet_complete to complete their async control packets. */
void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p) void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p)
{ {
if (p->len < 0) { if (p->result < 0) {
s->setup_state = SETUP_STATE_IDLE; s->setup_state = SETUP_STATE_IDLE;
} }
switch (s->setup_state) { switch (s->setup_state) {
case SETUP_STATE_SETUP: case SETUP_STATE_SETUP:
if (p->len < s->setup_len) { if (p->result < s->setup_len) {
s->setup_len = p->len; s->setup_len = p->result;
} }
s->setup_state = SETUP_STATE_DATA; s->setup_state = SETUP_STATE_DATA;
p->len = 8; p->result = 8;
break; break;
case SETUP_STATE_ACK: case SETUP_STATE_ACK:
s->setup_state = SETUP_STATE_IDLE; s->setup_state = SETUP_STATE_IDLE;
p->len = 0; p->result = 0;
break; break;
default: default:
@ -347,3 +351,57 @@ void usb_cancel_packet(USBPacket * p)
p->owner->info->cancel_packet(p->owner, p); p->owner->info->cancel_packet(p->owner, p);
p->owner = NULL; p->owner = NULL;
} }
void usb_packet_init(USBPacket *p)
{
qemu_iovec_init(&p->iov, 1);
}
void usb_packet_setup(USBPacket *p, int pid, uint8_t addr, uint8_t ep)
{
p->pid = pid;
p->devaddr = addr;
p->devep = ep;
p->result = 0;
qemu_iovec_reset(&p->iov);
}
void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len)
{
qemu_iovec_add(&p->iov, ptr, len);
}
void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes)
{
assert(p->result >= 0);
assert(p->result + bytes <= p->iov.size);
switch (p->pid) {
case USB_TOKEN_SETUP:
case USB_TOKEN_OUT:
iov_to_buf(p->iov.iov, p->iov.niov, ptr, p->result, bytes);
break;
case USB_TOKEN_IN:
iov_from_buf(p->iov.iov, p->iov.niov, ptr, p->result, bytes);
break;
default:
fprintf(stderr, "%s: invalid pid: %x\n", __func__, p->pid);
abort();
}
p->result += bytes;
}
void usb_packet_skip(USBPacket *p, size_t bytes)
{
assert(p->result >= 0);
assert(p->result + bytes <= p->iov.size);
if (p->pid == USB_TOKEN_IN) {
iov_clear(p->iov.iov, p->iov.niov, p->result, bytes);
}
p->result += bytes;
}
void usb_packet_cleanup(USBPacket *p)
{
qemu_iovec_destroy(&p->iov);
}

View File

@ -285,12 +285,21 @@ struct USBPacket {
int pid; int pid;
uint8_t devaddr; uint8_t devaddr;
uint8_t devep; uint8_t devep;
uint8_t *data; QEMUIOVector iov;
int len; int result; /* transfer length or USB_RET_* status code */
/* Internal use by the USB layer. */ /* Internal use by the USB layer. */
USBDevice *owner; USBDevice *owner;
}; };
void usb_packet_init(USBPacket *p);
void usb_packet_setup(USBPacket *p, int pid, uint8_t addr, uint8_t ep);
void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len);
int usb_packet_map(USBPacket *p, QEMUSGList *sgl);
void usb_packet_unmap(USBPacket *p);
void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes);
void usb_packet_skip(USBPacket *p, size_t bytes);
void usb_packet_cleanup(USBPacket *p);
int usb_handle_packet(USBDevice *dev, USBPacket *p); int usb_handle_packet(USBDevice *dev, USBPacket *p);
void usb_packet_complete(USBDevice *dev, USBPacket *p); void usb_packet_complete(USBDevice *dev, USBPacket *p);
void usb_cancel_packet(USBPacket * p); void usb_cancel_packet(USBPacket * p);

View File

@ -253,9 +253,9 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
} }
if (p->pid == USB_TOKEN_IN) if (p->pid == USB_TOKEN_IN)
ret = read(fd, p->data, p->len); ret = readv(fd, p->iov.iov, p->iov.niov);
else else
ret = write(fd, p->data, p->len); ret = writev(fd, p->iov.iov, p->iov.niov);
sigprocmask(SIG_SETMASK, &old_mask, NULL); sigprocmask(SIG_SETMASK, &old_mask, NULL);

View File

@ -341,16 +341,16 @@ static void async_complete(void *opaque)
if (p) { if (p) {
switch (aurb->urb.status) { switch (aurb->urb.status) {
case 0: case 0:
p->len += aurb->urb.actual_length; p->result += aurb->urb.actual_length;
break; break;
case -EPIPE: case -EPIPE:
set_halt(s, p->devep); set_halt(s, p->devep);
p->len = USB_RET_STALL; p->result = USB_RET_STALL;
break; break;
default: default:
p->len = USB_RET_NAK; p->result = USB_RET_NAK;
break; break;
} }
@ -604,6 +604,7 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
{ {
AsyncURB *aurb; AsyncURB *aurb;
int i, j, ret, max_packet_size, offset, len = 0; int i, j, ret, max_packet_size, offset, len = 0;
uint8_t *buf;
max_packet_size = get_max_packet_size(s, p->devep); max_packet_size = get_max_packet_size(s, p->devep);
if (max_packet_size == 0) if (max_packet_size == 0)
@ -628,19 +629,19 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
len = urb_status_to_usb_ret( len = urb_status_to_usb_ret(
aurb[i].urb.iso_frame_desc[j].status); aurb[i].urb.iso_frame_desc[j].status);
/* Check the frame fits */ /* Check the frame fits */
} else if (aurb[i].urb.iso_frame_desc[j].actual_length > p->len) { } else if (aurb[i].urb.iso_frame_desc[j].actual_length
> p->iov.size) {
printf("husb: received iso data is larger then packet\n"); printf("husb: received iso data is larger then packet\n");
len = USB_RET_NAK; len = USB_RET_NAK;
/* All good copy data over */ /* All good copy data over */
} else { } else {
len = aurb[i].urb.iso_frame_desc[j].actual_length; len = aurb[i].urb.iso_frame_desc[j].actual_length;
memcpy(p->data, buf = aurb[i].urb.buffer +
aurb[i].urb.buffer + j * aurb[i].urb.iso_frame_desc[0].length;
j * aurb[i].urb.iso_frame_desc[0].length, usb_packet_copy(p, buf, len);
len);
} }
} else { } else {
len = p->len; len = p->iov.size;
offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->devep); offset = (j == 0) ? 0 : get_iso_buffer_used(s, p->devep);
/* Check the frame fits */ /* Check the frame fits */
@ -650,7 +651,7 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
} }
/* All good copy data over */ /* All good copy data over */
memcpy(aurb[i].urb.buffer + offset, p->data, len); usb_packet_copy(p, aurb[i].urb.buffer + offset, len);
aurb[i].urb.iso_frame_desc[j].length = len; aurb[i].urb.iso_frame_desc[j].length = len;
offset += len; offset += len;
set_iso_buffer_used(s, p->devep, offset); set_iso_buffer_used(s, p->devep, offset);
@ -734,9 +735,9 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN); return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN);
} }
rem = p->len; assert(p->iov.niov == 1); /* temporary */
pbuf = p->data; rem = p->iov.iov[0].iov_len;
p->len = 0; pbuf = p->iov.iov[0].iov_base;
while (rem) { while (rem) {
aurb = async_alloc(s); aurb = async_alloc(s);
aurb->packet = p; aurb->packet = p;

View File

@ -365,12 +365,12 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
} }
len = isop->len; len = isop->len;
if (len > p->len) { if (len > p->iov.size) {
ERROR("received iso data is larger then packet ep %02X\n", ep); ERROR("received iso data is larger then packet ep %02X\n", ep);
bufp_free(dev, isop, ep); bufp_free(dev, isop, ep);
return USB_RET_NAK; return USB_RET_NAK;
} }
memcpy(p->data, isop->data, len); usb_packet_copy(p, isop->data, len);
bufp_free(dev, isop, ep); bufp_free(dev, isop, ep);
return len; return len;
} else { } else {
@ -379,18 +379,20 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
if (dev->endpoint[EP2I(ep)].iso_started) { if (dev->endpoint[EP2I(ep)].iso_started) {
struct usb_redir_iso_packet_header iso_packet = { struct usb_redir_iso_packet_header iso_packet = {
.endpoint = ep, .endpoint = ep,
.length = p->len .length = p->iov.size
}; };
uint8_t buf[p->iov.size];
/* No id, we look at the ep when receiving a status back */ /* No id, we look at the ep when receiving a status back */
usb_packet_copy(p, buf, p->iov.size);
usbredirparser_send_iso_packet(dev->parser, 0, &iso_packet, usbredirparser_send_iso_packet(dev->parser, 0, &iso_packet,
p->data, p->len); buf, p->iov.size);
usbredirparser_do_write(dev->parser); usbredirparser_do_write(dev->parser);
} }
status = dev->endpoint[EP2I(ep)].iso_error; status = dev->endpoint[EP2I(ep)].iso_error;
dev->endpoint[EP2I(ep)].iso_error = 0; dev->endpoint[EP2I(ep)].iso_error = 0;
DPRINTF2("iso-token-out ep %02X status %d len %d\n", ep, status, DPRINTF2("iso-token-out ep %02X status %d len %zd\n", ep, status,
p->len); p->iov.size);
return usbredir_handle_status(dev, status, p->len); return usbredir_handle_status(dev, status, p->iov.size);
} }
} }
@ -413,10 +415,11 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
AsyncURB *aurb = async_alloc(dev, p); AsyncURB *aurb = async_alloc(dev, p);
struct usb_redir_bulk_packet_header bulk_packet; struct usb_redir_bulk_packet_header bulk_packet;
DPRINTF("bulk-out ep %02X len %d id %u\n", ep, p->len, aurb->packet_id); DPRINTF("bulk-out ep %02X len %zd id %u\n", ep,
p->iov.size, aurb->packet_id);
bulk_packet.endpoint = ep; bulk_packet.endpoint = ep;
bulk_packet.length = p->len; bulk_packet.length = p->iov.size;
bulk_packet.stream_id = 0; bulk_packet.stream_id = 0;
aurb->bulk_packet = bulk_packet; aurb->bulk_packet = bulk_packet;
@ -424,9 +427,11 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id, usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id,
&bulk_packet, NULL, 0); &bulk_packet, NULL, 0);
} else { } else {
usbredir_log_data(dev, "bulk data out:", p->data, p->len); uint8_t buf[p->iov.size];
usb_packet_copy(p, buf, p->iov.size);
usbredir_log_data(dev, "bulk data out:", buf, p->iov.size);
usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id, usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id,
&bulk_packet, p->data, p->len); &bulk_packet, buf, p->iov.size);
} }
usbredirparser_do_write(dev->parser); usbredirparser_do_write(dev->parser);
return USB_RET_ASYNC; return USB_RET_ASYNC;
@ -471,29 +476,31 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev,
} }
len = intp->len; len = intp->len;
if (len > p->len) { if (len > p->iov.size) {
ERROR("received int data is larger then packet ep %02X\n", ep); ERROR("received int data is larger then packet ep %02X\n", ep);
bufp_free(dev, intp, ep); bufp_free(dev, intp, ep);
return USB_RET_NAK; return USB_RET_NAK;
} }
memcpy(p->data, intp->data, len); usb_packet_copy(p, intp->data, len);
bufp_free(dev, intp, ep); bufp_free(dev, intp, ep);
return len; return len;
} else { } else {
/* Output interrupt endpoint, normal async operation */ /* Output interrupt endpoint, normal async operation */
AsyncURB *aurb = async_alloc(dev, p); AsyncURB *aurb = async_alloc(dev, p);
struct usb_redir_interrupt_packet_header interrupt_packet; struct usb_redir_interrupt_packet_header interrupt_packet;
uint8_t buf[p->iov.size];
DPRINTF("interrupt-out ep %02X len %d id %u\n", ep, p->len, DPRINTF("interrupt-out ep %02X len %zd id %u\n", ep, p->iov.size,
aurb->packet_id); aurb->packet_id);
interrupt_packet.endpoint = ep; interrupt_packet.endpoint = ep;
interrupt_packet.length = p->len; interrupt_packet.length = p->iov.size;
aurb->interrupt_packet = interrupt_packet; aurb->interrupt_packet = interrupt_packet;
usbredir_log_data(dev, "interrupt data out:", p->data, p->len); usb_packet_copy(p, buf, p->iov.size);
usbredir_log_data(dev, "interrupt data out:", buf, p->iov.size);
usbredirparser_send_interrupt_packet(dev->parser, aurb->packet_id, usbredirparser_send_interrupt_packet(dev->parser, aurb->packet_id,
&interrupt_packet, p->data, p->len); &interrupt_packet, buf, p->iov.size);
usbredirparser_do_write(dev->parser); usbredirparser_do_write(dev->parser);
return USB_RET_ASYNC; return USB_RET_ASYNC;
} }
@ -959,7 +966,7 @@ static void usbredir_configuration_status(void *priv, uint32_t id,
dev->dev.data_buf[0] = config_status->configuration; dev->dev.data_buf[0] = config_status->configuration;
len = 1; len = 1;
} }
aurb->packet->len = aurb->packet->result =
usbredir_handle_status(dev, config_status->status, len); usbredir_handle_status(dev, config_status->status, len);
usb_generic_async_ctrl_complete(&dev->dev, aurb->packet); usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
} }
@ -987,7 +994,7 @@ static void usbredir_alt_setting_status(void *priv, uint32_t id,
dev->dev.data_buf[0] = alt_setting_status->alt; dev->dev.data_buf[0] = alt_setting_status->alt;
len = 1; len = 1;
} }
aurb->packet->len = aurb->packet->result =
usbredir_handle_status(dev, alt_setting_status->status, len); usbredir_handle_status(dev, alt_setting_status->status, len);
usb_generic_async_ctrl_complete(&dev->dev, aurb->packet); usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
} }
@ -1070,7 +1077,7 @@ static void usbredir_control_packet(void *priv, uint32_t id,
len = USB_RET_STALL; len = USB_RET_STALL;
} }
} }
aurb->packet->len = len; aurb->packet->result = len;
usb_generic_async_ctrl_complete(&dev->dev, aurb->packet); usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
} }
async_free(dev, aurb); async_free(dev, aurb);
@ -1105,15 +1112,15 @@ static void usbredir_bulk_packet(void *priv, uint32_t id,
len = usbredir_handle_status(dev, bulk_packet->status, len); len = usbredir_handle_status(dev, bulk_packet->status, len);
if (len > 0) { if (len > 0) {
usbredir_log_data(dev, "bulk data in:", data, data_len); usbredir_log_data(dev, "bulk data in:", data, data_len);
if (data_len <= aurb->packet->len) { if (data_len <= aurb->packet->iov.size) {
memcpy(aurb->packet->data, data, data_len); usb_packet_copy(aurb->packet, data, data_len);
} else { } else {
ERROR("bulk buffer too small (%d > %d)\n", data_len, ERROR("bulk buffer too small (%d > %zd)\n", data_len,
aurb->packet->len); aurb->packet->iov.size);
len = USB_RET_STALL; len = USB_RET_STALL;
} }
} }
aurb->packet->len = len; aurb->packet->result = len;
usb_packet_complete(&dev->dev, aurb->packet); usb_packet_complete(&dev->dev, aurb->packet);
} }
async_free(dev, aurb); async_free(dev, aurb);
@ -1185,7 +1192,7 @@ static void usbredir_interrupt_packet(void *priv, uint32_t id,
} }
if (aurb->packet) { if (aurb->packet) {
aurb->packet->len = usbredir_handle_status(dev, aurb->packet->result = usbredir_handle_status(dev,
interrupt_packet->status, len); interrupt_packet->status, len);
usb_packet_complete(&dev->dev, aurb->packet); usb_packet_complete(&dev->dev, aurb->packet);
} }