Exemple #1
0
/*
 * Try to receive a packet.
 */
static ssize_t
e1000_recv(struct netdriver_data * data, size_t max)
{
	e1000_t *e;
	e1000_rx_desc_t *desc;
	unsigned int head, tail, cur;
	char *ptr;
	size_t size;

	e = &e1000_state;

	/* If the queue head and tail are equal, the queue is empty. */
	head = e1000_reg_read(e, E1000_REG_RDH);
	tail = e1000_reg_read(e, E1000_REG_RDT);

	E1000_DEBUG(4, ("%s: head=%u, tail=%u\n", e->name, head, tail));

	if (head == tail)
		return SUSPEND;

	/* Has a packet been received? */
	cur = (tail + 1) % e->rx_desc_count;
	desc = &e->rx_desc[cur];

	if (!(desc->status & E1000_RX_STATUS_DONE))
		return SUSPEND;

	/*
	 * HACK: we expect all packets to fit in a single receive buffer.
	 * Eventually, some sort of support to deal with packets spanning
	 * multiple receive descriptors should be added.  For now, we panic,
	 * so that we can continue after the restart; this is already an
	 * improvement over freezing (the old behavior of this driver).
	 */
	size = desc->length;

	if (!(desc->status & E1000_RX_STATUS_EOP))
		panic("received packet too large");

	/* Copy the packet to the caller. */
	ptr = e->rx_buffer + cur * E1000_IOBUF_SIZE;

	if (size > max)
		size = max;

	netdriver_copyout(data, 0, ptr, size);

	/* Reset the descriptor. */
	desc->status = 0;

	/* Increment tail. */
	e1000_reg_write(e, E1000_REG_RDT, cur);

	/* Return the size of the received packet. */
	return size;
}
Exemple #2
0
/*
 * Put a packet receive from the RX queue into a user buffer, and return the
 * packet length.  If there are no received packets, return SUSPEND.
 */
static ssize_t
virtio_net_recv(struct netdriver_data * data, size_t max)
{
	struct packet *p;
	ssize_t len;

	/* Get the first received packet, if any. */
	if (STAILQ_EMPTY(&recv_list))
		return SUSPEND;

	p = STAILQ_FIRST(&recv_list);
	STAILQ_REMOVE_HEAD(&recv_list, next);

	/* Copy out the packet contents. */
	len = p->len - sizeof(struct virtio_net_hdr);
	if (len > max)
		len = max;

	/*
	 * HACK: due to lack of padding, received packets may in fact be
	 * smaller than the minimum ethernet packet size.  Inet will accept the
	 * packets just fine if we increase the length to its minimum.  We
	 * already zeroed out the rest of the packet data, so this is safe.
	 */
	if (len < ETH_MIN_PACK_SIZE)
		len = ETH_MIN_PACK_SIZE;

	netdriver_copyout(data, 0, p->vdata, len);

	/* Clean the packet. */
	memset(p->vhdr, 0, sizeof(*p->vhdr));
	memset(p->vdata, 0, MAX_PACK_SIZE);
	STAILQ_INSERT_HEAD(&free_list, p, next);

	/* Readd packets to the receive queue as necessary. */
	virtio_net_refill_rx_queue();

	return len;
}
/*============================================================================*
 *				lan8710a_recv				      *
 *============================================================================*/
static ssize_t
lan8710a_recv(struct netdriver_data * data, size_t max)
{
	lan8710a_t *e = &lan8710a_state;
	lan8710a_desc_t *p_rx_desc;
	u32_t flags;
	u8_t *buf;
	size_t off, size, chunk;

	/*
	 * Only handle one packet at a time.
	 */
	p_rx_desc = &(e->rx_desc[e->rx_desc_idx]);
	/* find next OWN descriptor with SOP flag */
	while ((0 == (LAN8710A_DESC_FLAG_SOP & p_rx_desc->pkt_len_flags)) &&
	    (0 == (LAN8710A_DESC_FLAG_OWN & p_rx_desc->pkt_len_flags))) {
		p_rx_desc->buffer_length_off = LAN8710A_IOBUF_SIZE;
		/* set ownership of current descriptor to EMAC */
		p_rx_desc->pkt_len_flags = LAN8710A_DESC_FLAG_OWN;

		e->rx_desc_idx++;
		if (LAN8710A_NUM_RX_DESC == e->rx_desc_idx)
			e->rx_desc_idx = 0;
		p_rx_desc = &(e->rx_desc[e->rx_desc_idx]);
	}

	if (0 == (LAN8710A_DESC_FLAG_SOP & p_rx_desc->pkt_len_flags)) {
		/* SOP was not found */
		return SUSPEND;
	}

	/*
	 * Copy data from descriptors, from SOP to EOP inclusive.
	 * TODO: make sure that the presence of a SOP slot implies the presence
	 * of an EOP slot, because we are not checking for ownership below..
	 */
	size = 0;
	off = 0;

	for (;;) {
		buf = e->p_rx_buf + e->rx_desc_idx * LAN8710A_IOBUF_SIZE + off;
		chunk = p_rx_desc->buffer_length_off & 0xFFFF;

		/* Truncate packets that are too large. */
		if (chunk > max - size)
			chunk = max - size;

		if (chunk > 0) {
			netdriver_copyout(data, size, buf, chunk);

			size += chunk;
		}

		flags = p_rx_desc->pkt_len_flags;

		/* Whole buffer move to the next descriptor */
		p_rx_desc->buffer_length_off = LAN8710A_IOBUF_SIZE;
		/* set ownership of current desc to EMAC */
		p_rx_desc->pkt_len_flags = LAN8710A_DESC_FLAG_OWN;

		e->rx_desc_idx++;
		if (LAN8710A_NUM_RX_DESC == e->rx_desc_idx)
			e->rx_desc_idx = 0;
		p_rx_desc = &(e->rx_desc[e->rx_desc_idx]);

		/* if EOP flag is set -> stop processing */
		if (flags & LAN8710A_DESC_FLAG_EOP)
			break;

		/*
		 * TODO: the upper 16 bits of buffer_length_off are used *only*
		 * for descriptors *after* the first one; I'm retaining this
		 * behavior because I don't have the chip's spec, but it may be
		 * better to simplify/correct this behavior. --David
		 */
		off = p_rx_desc->buffer_length_off >> 16;
	}

	return size;
}