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