Index: uspace/drv/nic/virtio-net/virtio-net.c
===================================================================
--- uspace/drv/nic/virtio-net/virtio-net.c	(revision fe96085bcf6c380399cf9a2766b34890d9f087a3)
+++ uspace/drv/nic/virtio-net/virtio-net.c	(revision 3d135e92dfa5eada208677984fcd67d69711f95b)
@@ -112,8 +112,17 @@
 {
 	for (unsigned i = 0; i < size; i++) {
-		virtio_virtq_set_desc(vdev, num, i, 0, 0,
+		virtio_virtq_desc_set(vdev, num, i, 0, 0,
 		    VIRTQ_DESC_F_NEXT, (i + 1 == size) ? -1U : i + 1);
 	}
 	*head = 0;
+}
+
+static uint16_t virtio_net_alloc_buf(virtio_dev_t *vdev, uint16_t num,
+    uint16_t *head)
+{
+	uint16_t descno = *head;
+	if (descno != (uint16_t) -1U)
+		*head = virtio_virtq_desc_get_next(vdev, num, descno);
+	return descno;
 }
 
@@ -261,5 +270,5 @@
 		 * flags.
 		 */
-		virtio_virtq_set_desc(vdev, RX_QUEUE_1, i,
+		virtio_virtq_desc_set(vdev, RX_QUEUE_1, i,
 		    virtio_net->rx_buf_p[i], RX_BUF_SIZE, VIRTQ_DESC_F_WRITE,
 		    0);
@@ -335,5 +344,34 @@
 static void virtio_net_send(nic_t *nic, void *data, size_t size)
 {
-	// TODO
+	virtio_net_t *virtio_net = nic_get_specific(nic);
+	virtio_dev_t *vdev = &virtio_net->virtio_dev;
+
+	if (size > sizeof(virtio_net) + TX_BUF_SIZE) {
+		ddf_msg(LVL_WARN, "TX data too big, frame dropped");
+		return;
+	}
+
+	uint16_t descno = virtio_net_alloc_buf(vdev, TX_QUEUE_1,
+	    &virtio_net->tx_free_head);
+	if (descno == (uint16_t) -1U) {
+		ddf_msg(LVL_WARN, "No TX buffers available, frame dropped");
+		return;
+	}
+	assert(descno < TX_BUFFERS);
+
+	/* Setup the packed header */
+	virtio_net_hdr_t *hdr = (virtio_net_hdr_t *) virtio_net->tx_buf[descno];
+	memset(hdr, 0, sizeof(virtio_net_hdr_t));
+	hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE;
+
+	/* Copy packet data into the buffer just past the header */ 
+	memcpy(&hdr[1], data, size);
+
+	/*
+	 * Set the descriptor, put it into the virtqueue and notify the device
+	 */
+	virtio_virtq_desc_set(vdev, TX_QUEUE_1, descno,
+	    virtio_net->tx_buf_p[descno], TX_BUF_SIZE, 0, 0);
+	virtio_virtq_produce_available(vdev, TX_QUEUE_1, descno);
 }
 
Index: uspace/drv/nic/virtio-net/virtio-net.h
===================================================================
--- uspace/drv/nic/virtio-net/virtio-net.h	(revision fe96085bcf6c380399cf9a2766b34890d9f087a3)
+++ uspace/drv/nic/virtio-net/virtio-net.h	(revision 3d135e92dfa5eada208677984fcd67d69711f95b)
@@ -46,4 +46,15 @@
 #define VIRTIO_NET_F_CTRL_VQ		(1U << 17)
 
+#define VIRTIO_NET_HDR_GSO_NONE 0
+typedef struct {
+	uint8_t flags;
+	uint8_t gso_type;
+	uint16_t hdr_len;
+	uint16_t gso_size;
+	uint16_t csum_start;
+	uint16_t csum_offset;
+	uint16_t num_buffers;
+} virtio_net_hdr_t;
+
 typedef struct {
 	uint8_t mac[6];
