diff options
Diffstat (limited to 'hw/usb/dev-network.c')
-rw-r--r-- | hw/usb/dev-network.c | 166 |
1 files changed, 94 insertions, 72 deletions
diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index c84892c98d..1c54863452 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -26,10 +26,11 @@ #include "qemu-common.h" #include "hw/usb.h" #include "hw/usb/desc.h" -#include "net.h" -#include "qemu-queue.h" -#include "sysemu.h" -#include "iov.h" +#include "net/net.h" +#include "qemu/queue.h" +#include "qemu/config-file.h" +#include "sysemu/sysemu.h" +#include "qemu/iov.h" /*#define TRAFFIC_DEBUG*/ /* Thanks to NetChip Technologies for donating this product ID. @@ -639,6 +640,8 @@ typedef struct USBNetState { unsigned int in_ptr, in_len; uint8_t in_buf[2048]; + USBEndpoint *intr; + char usbstring_mac[13]; NICState *nic; NICConf conf; @@ -851,6 +854,10 @@ static void *rndis_queue_response(USBNetState *s, unsigned int length) struct rndis_response *r = g_malloc0(sizeof(struct rndis_response) + length); + if (QTAILQ_EMPTY(&s->rndis_resp)) { + usb_wakeup(s->intr); + } + QTAILQ_INSERT_TAIL(&s->rndis_resp, r, entries); r->length = length; @@ -1001,6 +1008,13 @@ static int rndis_keepalive_response(USBNetState *s, return 0; } +/* Prepare to receive the next packet */ +static void usb_net_reset_in_buf(USBNetState *s) +{ + s->in_ptr = s->in_len = 0; + qemu_flush_queued_packets(&s->nic->nc); +} + static int rndis_parse(USBNetState *s, uint8_t *data, int length) { uint32_t msg_type; @@ -1025,7 +1039,8 @@ static int rndis_parse(USBNetState *s, uint8_t *data, int length) case RNDIS_RESET_MSG: rndis_clear_responsequeue(s); - s->out_ptr = s->in_ptr = s->in_len = 0; + s->out_ptr = 0; + usb_net_reset_in_buf(s); return rndis_reset_response(s, (rndis_reset_msg_type *) data); case RNDIS_KEEPALIVE_MSG: @@ -1040,7 +1055,7 @@ static void usb_net_handle_reset(USBDevice *dev) { } -static int usb_net_handle_control(USBDevice *dev, USBPacket *p, +static void usb_net_handle_control(USBDevice *dev, USBPacket *p, int request, int value, int index, int length, uint8_t *data) { USBNetState *s = (USBNetState *) dev; @@ -1048,10 +1063,9 @@ static int usb_net_handle_control(USBDevice *dev, USBPacket *p, ret = usb_desc_handle_control(dev, p, request, value, index, length, data); if (ret >= 0) { - return ret; + return; } - ret = 0; switch(request) { case ClassInterfaceOutRequest | USB_CDC_SEND_ENCAPSULATED_COMMAND: if (!is_rndis(s) || value || index != 0) { @@ -1070,22 +1084,25 @@ static int usb_net_handle_control(USBDevice *dev, USBPacket *p, } #endif ret = rndis_parse(s, data, length); + if (ret < 0) { + p->status = ret; + } break; case ClassInterfaceRequest | USB_CDC_GET_ENCAPSULATED_RESPONSE: if (!is_rndis(s) || value || index != 0) { goto fail; } - ret = rndis_get_response(s, data); - if (!ret) { + p->actual_length = rndis_get_response(s, data); + if (p->actual_length == 0) { data[0] = 0; - ret = 1; + p->actual_length = 1; } #ifdef TRAFFIC_DEBUG { unsigned int i; fprintf(stderr, "GET_ENCAPSULATED_RESPONSE:"); - for (i = 0; i < ret; i++) { + for (i = 0; i < p->actual_length; i++) { if (!(i & 15)) fprintf(stderr, "\n%04x:", i); fprintf(stderr, " %02x", data[i]); @@ -1100,72 +1117,67 @@ static int usb_net_handle_control(USBDevice *dev, USBPacket *p, fprintf(stderr, "usbnet: failed control transaction: " "request 0x%x value 0x%x index 0x%x length 0x%x\n", request, value, index, length); - ret = USB_RET_STALL; + p->status = USB_RET_STALL; break; } - return ret; } -static int usb_net_handle_statusin(USBNetState *s, USBPacket *p) +static void usb_net_handle_statusin(USBNetState *s, USBPacket *p) { le32 buf[2]; - int ret = 8; if (p->iov.size < 8) { - return USB_RET_STALL; + p->status = USB_RET_STALL; + return; } buf[0] = cpu_to_le32(1); buf[1] = cpu_to_le32(0); usb_packet_copy(p, buf, 8); - if (!s->rndis_resp.tqh_first) - ret = USB_RET_NAK; + if (!s->rndis_resp.tqh_first) { + p->status = USB_RET_NAK; + } #ifdef TRAFFIC_DEBUG fprintf(stderr, "usbnet: interrupt poll len %zu return %d", - p->iov.size, ret); - iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", ret); + p->iov.size, p->status); + iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", p->status); #endif - - return ret; } -static int usb_net_handle_datain(USBNetState *s, USBPacket *p) +static void usb_net_handle_datain(USBNetState *s, USBPacket *p) { - int ret = USB_RET_NAK; + int len; if (s->in_ptr > s->in_len) { - s->in_ptr = s->in_len = 0; - ret = USB_RET_NAK; - return ret; + usb_net_reset_in_buf(s); + p->status = USB_RET_NAK; + return; } if (!s->in_len) { - ret = USB_RET_NAK; - return ret; + p->status = USB_RET_NAK; + return; } - ret = s->in_len - s->in_ptr; - if (ret > p->iov.size) { - ret = p->iov.size; + len = s->in_len - s->in_ptr; + if (len > p->iov.size) { + len = p->iov.size; } - usb_packet_copy(p, &s->in_buf[s->in_ptr], ret); - s->in_ptr += ret; + usb_packet_copy(p, &s->in_buf[s->in_ptr], len); + s->in_ptr += 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)) || !len)) { /* no short packet necessary */ - s->in_ptr = s->in_len = 0; + usb_net_reset_in_buf(s); } #ifdef TRAFFIC_DEBUG - fprintf(stderr, "usbnet: data in len %zu return %d", p->iov.size, ret); - iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", ret); + fprintf(stderr, "usbnet: data in len %zu return %d", p->iov.size, len); + iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", len); #endif - - return ret; } -static int usb_net_handle_dataout(USBNetState *s, USBPacket *p) +static void usb_net_handle_dataout(USBNetState *s, USBPacket *p) { - int ret = p->iov.size; int sz = sizeof(s->out_buf) - s->out_ptr; struct rndis_packet_msg_type *msg = (struct rndis_packet_msg_type *) s->out_buf; @@ -1176,21 +1188,23 @@ static int usb_net_handle_dataout(USBNetState *s, USBPacket *p) iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", p->iov.size); #endif - if (sz > ret) - sz = ret; + if (sz > p->iov.size) { + sz = p->iov.size; + } usb_packet_copy(p, &s->out_buf[s->out_ptr], sz); s->out_ptr += sz; if (!is_rndis(s)) { - if (ret < 64) { + if (p->iov.size < 64) { qemu_send_packet(&s->nic->nc, s->out_buf, s->out_ptr); s->out_ptr = 0; } - return ret; + return; } len = le32_to_cpu(msg->MessageLength); - if (s->out_ptr < 8 || s->out_ptr < len) - return ret; + if (s->out_ptr < 8 || s->out_ptr < len) { + return; + } if (le32_to_cpu(msg->MessageType) == RNDIS_PACKET_MSG) { uint32_t offs = 8 + le32_to_cpu(msg->DataOffset); uint32_t size = le32_to_cpu(msg->DataLength); @@ -1199,24 +1213,21 @@ static int usb_net_handle_dataout(USBNetState *s, USBPacket *p) } s->out_ptr -= len; memmove(s->out_buf, &s->out_buf[len], s->out_ptr); - - return ret; } -static int usb_net_handle_data(USBDevice *dev, USBPacket *p) +static void usb_net_handle_data(USBDevice *dev, USBPacket *p) { USBNetState *s = (USBNetState *) dev; - int ret = 0; switch(p->pid) { case USB_TOKEN_IN: switch (p->ep->nr) { case 1: - ret = usb_net_handle_statusin(s, p); + usb_net_handle_statusin(s, p); break; case 2: - ret = usb_net_handle_datain(s, p); + usb_net_handle_datain(s, p); break; default: @@ -1227,7 +1238,7 @@ static int usb_net_handle_data(USBDevice *dev, USBPacket *p) case USB_TOKEN_OUT: switch (p->ep->nr) { case 2: - ret = usb_net_handle_dataout(s, p); + usb_net_handle_dataout(s, p); break; default: @@ -1237,33 +1248,46 @@ static int usb_net_handle_data(USBDevice *dev, USBPacket *p) default: fail: - ret = USB_RET_STALL; + p->status = USB_RET_STALL; break; } - if (ret == USB_RET_STALL) + + if (p->status == USB_RET_STALL) { fprintf(stderr, "usbnet: failed data transaction: " "pid 0x%x ep 0x%x len 0x%zx\n", p->pid, p->ep->nr, p->iov.size); - return ret; + } } static ssize_t usbnet_receive(NetClientState *nc, const uint8_t *buf, size_t size) { USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque; - struct rndis_packet_msg_type *msg; + uint8_t *in_buf = s->in_buf; + size_t total_size = size; if (is_rndis(s)) { - msg = (struct rndis_packet_msg_type *) s->in_buf; if (s->rndis_state != RNDIS_DATA_INITIALIZED) { return -1; } - if (size + sizeof(struct rndis_packet_msg_type) > sizeof(s->in_buf)) - return -1; + total_size += sizeof(struct rndis_packet_msg_type); + } + if (total_size > sizeof(s->in_buf)) { + return -1; + } + + /* Only accept packet if input buffer is empty */ + if (s->in_len > 0) { + return 0; + } + if (is_rndis(s)) { + struct rndis_packet_msg_type *msg; + + msg = (struct rndis_packet_msg_type *)in_buf; memset(msg, 0, sizeof(struct rndis_packet_msg_type)); msg->MessageType = cpu_to_le32(RNDIS_PACKET_MSG); - msg->MessageLength = cpu_to_le32(size + sizeof(struct rndis_packet_msg_type)); - msg->DataOffset = cpu_to_le32(sizeof(struct rndis_packet_msg_type) - 8); + msg->MessageLength = cpu_to_le32(size + sizeof(*msg)); + msg->DataOffset = cpu_to_le32(sizeof(*msg) - 8); msg->DataLength = cpu_to_le32(size); /* msg->OOBDataOffset; * msg->OOBDataLength; @@ -1273,14 +1297,11 @@ static ssize_t usbnet_receive(NetClientState *nc, const uint8_t *buf, size_t siz * msg->VcHandle; * msg->Reserved; */ - memcpy(msg + 1, buf, size); - s->in_len = size + sizeof(struct rndis_packet_msg_type); - } else { - if (size > sizeof(s->in_buf)) - return -1; - memcpy(s->in_buf, buf, size); - s->in_len = size; + in_buf += sizeof(*msg); } + + memcpy(in_buf, buf, size); + s->in_len = total_size; s->in_ptr = 0; return size; } @@ -1335,6 +1356,7 @@ static int usb_net_initfn(USBDevice *dev) s->media_state = 0; /* NDIS_MEDIA_STATE_CONNECTED */; s->filter = 0; s->vendorid = 0x1234; + s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_usbnet_info, &s->conf, |