示例#1
0
static void
ffec_rxfinish_locked(struct ffec_softc *sc)
{
	struct ffec_hwdesc *desc;
	int len;
	boolean_t produced_empty_buffer;

	FFEC_ASSERT_LOCKED(sc);

	produced_empty_buffer = false;
	for (;;) {
		desc = &sc->rxdesc_ring[sc->rx_idx];
		if (desc->flags_len & FEC_RXDESC_EMPTY)
			break;
		produced_empty_buffer = true;
		len = (desc->flags_len & FEC_RXDESC_LEN_MASK);
		if (len < 64) {
			/*
			 * Just recycle the descriptor and continue.           .
			 */
			ffec_setup_rxdesc(sc, sc->rx_idx,
			    sc->rxdesc_ring[sc->rx_idx].buf_paddr);
		} else if ((desc->flags_len & FEC_RXDESC_L) == 0) {
			/*
			 * The entire frame is not in this buffer.  Impossible.
			 * Recycle the descriptor and continue.
			 *
			 * XXX what's the right way to handle this? Probably we
			 * should stop/init the hardware because this should
			 * just really never happen when we have buffers bigger
			 * than the maximum frame size.
			 */
			device_printf(sc->dev, 
			    "fec_rxfinish: received frame without LAST bit set");
			ffec_setup_rxdesc(sc, sc->rx_idx, 
			    sc->rxdesc_ring[sc->rx_idx].buf_paddr);
		} else if (desc->flags_len & FEC_RXDESC_ERROR_BITS) {
			/*
			 *  Something went wrong with receiving the frame, we
			 *  don't care what (the hardware has counted the error
			 *  in the stats registers already), we just reuse the
			 *  same mbuf, which is still dma-mapped, by resetting
			 *  the rx descriptor.
			 */
			ffec_setup_rxdesc(sc, sc->rx_idx, 
			    sc->rxdesc_ring[sc->rx_idx].buf_paddr);
		} else {
			/*
			 *  Normal case: a good frame all in one buffer.
			 */
			ffec_rxfinish_onebuf(sc, len);
		}
		sc->rx_idx = next_rxidx(sc, sc->rx_idx);
	}

	if (produced_empty_buffer) {
		WR4(sc, FEC_RDAR_REG, FEC_RDAR_RDAR);
	}
}
示例#2
0
static int
ffec_setup_rxbuf(struct ffec_softc *sc, int idx, struct mbuf * m)
{
	int error, nsegs;
	struct bus_dma_segment seg;

	/*
	 * We need to leave at least ETHER_ALIGN bytes free at the beginning of
	 * the buffer to allow the data to be re-aligned after receiving it (by
	 * copying it backwards ETHER_ALIGN bytes in the same buffer).  We also
	 * have to ensure that the beginning of the buffer is aligned to the
	 * hardware's requirements.
	 */
	m_adj(m, roundup(ETHER_ALIGN, FEC_RXBUF_ALIGN));

	error = bus_dmamap_load_mbuf_sg(sc->rxbuf_tag, sc->rxbuf_map[idx].map,
	    m, &seg, &nsegs, 0);
	if (error != 0) {
		return (error);
	}

	bus_dmamap_sync(sc->rxbuf_tag, sc->rxbuf_map[idx].map, 
	    BUS_DMASYNC_PREREAD);

	sc->rxbuf_map[idx].mbuf = m;
	ffec_setup_rxdesc(sc, idx, seg.ds_addr);
	
	return (0);
}
示例#3
0
static void
ffec_rxfinish_onebuf(struct ffec_softc *sc, int len)
{
	struct mbuf *m, *newmbuf;
	struct ffec_bufmap *bmap;
	uint8_t *dst, *src;
	int error;

	/*
	 *  First try to get a new mbuf to plug into this slot in the rx ring.
	 *  If that fails, drop the current packet and recycle the current
	 *  mbuf, which is still mapped and loaded.
	 */
	if ((newmbuf = ffec_alloc_mbufcl(sc)) == NULL) {
		if_inc_counter(sc->ifp, IFCOUNTER_IQDROPS, 1);
		ffec_setup_rxdesc(sc, sc->rx_idx, 
		    sc->rxdesc_ring[sc->rx_idx].buf_paddr);
		return;
	}

	/*
	 *  Unfortunately, the protocol headers need to be aligned on a 32-bit
	 *  boundary for the upper layers.  The hardware requires receive
	 *  buffers to be 16-byte aligned.  The ethernet header is 14 bytes,
	 *  leaving the protocol header unaligned.  We used m_adj() after
	 *  allocating the buffer to leave empty space at the start of the
	 *  buffer, now we'll use the alignment agnostic bcopy() routine to
	 *  shuffle all the data backwards 2 bytes and adjust m_data.
	 *
	 *  XXX imx6 hardware is able to do this 2-byte alignment by setting the
	 *  SHIFT16 bit in the RACC register.  Older hardware doesn't have that
	 *  feature, but for them could we speed this up by copying just the
	 *  protocol headers into their own small mbuf then chaining the cluster
	 *  to it?  That way we'd only need to copy like 64 bytes or whatever
	 *  the biggest header is, instead of the whole 1530ish-byte frame.
	 */

	FFEC_UNLOCK(sc);

	bmap = &sc->rxbuf_map[sc->rx_idx];
	len -= ETHER_CRC_LEN;
	bus_dmamap_sync(sc->rxbuf_tag, bmap->map, BUS_DMASYNC_POSTREAD);
	bus_dmamap_unload(sc->rxbuf_tag, bmap->map);
	m = bmap->mbuf;
	bmap->mbuf = NULL;
	m->m_len = len;
	m->m_pkthdr.len = len;
	m->m_pkthdr.rcvif = sc->ifp;

	src = mtod(m, uint8_t*);
	dst = src - ETHER_ALIGN;
	bcopy(src, dst, len);
	m->m_data = dst;
	sc->ifp->if_input(sc->ifp, m);

	FFEC_LOCK(sc);

	if ((error = ffec_setup_rxbuf(sc, sc->rx_idx, newmbuf)) != 0) {
		device_printf(sc->dev, "ffec_setup_rxbuf error %d\n", error);
		/* XXX Now what?  We've got a hole in the rx ring. */
	}

}