/* * 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; }
/* * 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; }