static int sis_attach(device_t);
static int sis_detach(device_t);
-static int sis_newbuf(struct sis_softc *, int, struct mbuf *);
-static int sis_encap(struct sis_softc *, struct mbuf *, uint32_t *);
+static int sis_newbuf(struct sis_softc *, int, int);
+static void sis_setup_rxdesc(struct sis_softc *, int);
+static int sis_encap(struct sis_softc *, struct mbuf **, uint32_t *);
static void sis_rxeof(struct sis_softc *);
static void sis_rxeoc(struct sis_softc *);
static void sis_txeof(struct sis_softc *);
static int sis_list_rx_init(struct sis_softc *);
static int sis_list_tx_init(struct sis_softc *);
-static void sis_dma_map_desc_ptr(void *, bus_dma_segment_t *, int, int);
-static void sis_dma_map_desc_next(void *, bus_dma_segment_t *, int, int);
-static void sis_dma_map_ring(void *, bus_dma_segment_t *, int, int);
+static int sis_dma_alloc(device_t dev);
+static void sis_dma_free(device_t dev);
#ifdef DEVICE_POLLING
static poll_handler_t sis_poll;
#endif
#define SIO_CLR(x) \
CSR_WRITE_4(sc, SIS_EECTL, CSR_READ_4(sc, SIS_EECTL) & ~x)
-static void
-sis_dma_map_desc_next(void *arg, bus_dma_segment_t *segs, int nseg, int error)
-{
- struct sis_desc *r;
-
- r = arg;
- r->sis_next = segs->ds_addr;
-}
-
-static void
-sis_dma_map_desc_ptr(void *arg, bus_dma_segment_t *segs, int nseg, int error)
-{
- struct sis_desc *r;
-
- r = arg;
- r->sis_ptr = segs->ds_addr;
-}
-
-static void
-sis_dma_map_ring(void *arg, bus_dma_segment_t *segs, int nseg, int error)
-{
- uint32_t *p;
-
- p = arg;
- *p = segs->ds_addr;
-}
-
/*
* Routine to reverse the bits in a word. Stolen almost
* verbatim from /usr/games/fortune.
callout_init(&sc->sis_timer);
- /*
- * Allocate the parent bus DMA tag appropriate for PCI.
- */
-#define SIS_NSEG_NEW 32
- error = bus_dma_tag_create(NULL, /* parent */
- 1, 0, /* alignment, boundary */
- BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
- BUS_SPACE_MAXADDR, /* highaddr */
- NULL, NULL, /* filter, filterarg */
- MAXBSIZE, SIS_NSEG_NEW, /* maxsize, nsegments */
- BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */
- BUS_DMA_ALLOCNOW, /* flags */
- &sc->sis_parent_tag);
- if (error)
- goto fail;
-
- /*
- * Now allocate a tag for the DMA descriptor lists and a chunk
- * of DMA-able memory based on the tag. Also obtain the physical
- * addresses of the RX and TX ring, which we'll need later.
- * All of our lists are allocated as a contiguous block of memory.
- */
- error = bus_dma_tag_create(sc->sis_parent_tag, /* parent */
- 1, 0, /* alignment, boundary */
- BUS_SPACE_MAXADDR, /* lowaddr */
- BUS_SPACE_MAXADDR, /* highaddr */
- NULL, NULL, /* filter, filterarg */
- SIS_RX_LIST_SZ, 1, /* maxsize, nsegments */
- BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */
- 0, /* flags */
- &sc->sis_ldata.sis_rx_tag);
- if (error)
- goto fail;
-
- error = bus_dmamem_alloc(sc->sis_ldata.sis_rx_tag,
- (void **)&sc->sis_ldata.sis_rx_list,
- BUS_DMA_WAITOK | BUS_DMA_ZERO,
- &sc->sis_ldata.sis_rx_dmamap);
-
- if (error) {
- device_printf(dev, "no memory for rx list buffers!\n");
- bus_dma_tag_destroy(sc->sis_ldata.sis_rx_tag);
- sc->sis_ldata.sis_rx_tag = NULL;
- goto fail;
- }
-
- error = bus_dmamap_load(sc->sis_ldata.sis_rx_tag,
- sc->sis_ldata.sis_rx_dmamap,
- sc->sis_ldata.sis_rx_list,
- sizeof(struct sis_desc), sis_dma_map_ring,
- &sc->sis_ldata.sis_rx_paddr, 0);
-
- if (error) {
- device_printf(dev, "cannot get address of the rx ring!\n");
- bus_dmamem_free(sc->sis_ldata.sis_rx_tag,
- sc->sis_ldata.sis_rx_list,
- sc->sis_ldata.sis_rx_dmamap);
- bus_dma_tag_destroy(sc->sis_ldata.sis_rx_tag);
- sc->sis_ldata.sis_rx_tag = NULL;
- goto fail;
- }
-
- error = bus_dma_tag_create(sc->sis_parent_tag, /* parent */
- 1, 0, /* alignment, boundary */
- BUS_SPACE_MAXADDR, /* lowaddr */
- BUS_SPACE_MAXADDR, /* highaddr */
- NULL, NULL, /* filter, filterarg */
- SIS_TX_LIST_SZ, 1, /* maxsize, nsegments */
- BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */
- 0, /* flags */
- &sc->sis_ldata.sis_tx_tag);
- if (error)
- goto fail;
-
- error = bus_dmamem_alloc(sc->sis_ldata.sis_tx_tag,
- (void **)&sc->sis_ldata.sis_tx_list,
- BUS_DMA_WAITOK | BUS_DMA_ZERO,
- &sc->sis_ldata.sis_tx_dmamap);
-
- if (error) {
- device_printf(dev, "no memory for tx list buffers!\n");
- bus_dma_tag_destroy(sc->sis_ldata.sis_tx_tag);
- sc->sis_ldata.sis_tx_tag = NULL;
- goto fail;
- }
-
- error = bus_dmamap_load(sc->sis_ldata.sis_tx_tag,
- sc->sis_ldata.sis_tx_dmamap,
- sc->sis_ldata.sis_tx_list,
- sizeof(struct sis_desc), sis_dma_map_ring,
- &sc->sis_ldata.sis_tx_paddr, 0);
-
- if (error) {
- device_printf(dev, "cannot get address of the tx ring!\n");
- bus_dmamem_free(sc->sis_ldata.sis_tx_tag,
- sc->sis_ldata.sis_tx_list,
- sc->sis_ldata.sis_tx_dmamap);
- bus_dma_tag_destroy(sc->sis_ldata.sis_tx_tag);
- sc->sis_ldata.sis_tx_tag = NULL;
- goto fail;
- }
-
- error = bus_dma_tag_create(sc->sis_parent_tag, /* parent */
- 1, 0, /* alignment, boundary */
- BUS_SPACE_MAXADDR, /* lowaddr */
- BUS_SPACE_MAXADDR, /* highaddr */
- NULL, NULL, /* filter, filterarg */
- MCLBYTES, 1, /* maxsize, nsegments */
- BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */
- 0, /* flags */
- &sc->sis_tag);
+ error = sis_dma_alloc(dev);
if (error)
goto fail;
if (sc->sis_res)
bus_release_resource(dev, SIS_RES, SIS_RID, sc->sis_res);
- if (sc->sis_ldata.sis_rx_tag) {
- bus_dmamap_unload(sc->sis_ldata.sis_rx_tag,
- sc->sis_ldata.sis_rx_dmamap);
- bus_dmamem_free(sc->sis_ldata.sis_rx_tag,
- sc->sis_ldata.sis_rx_list,
- sc->sis_ldata.sis_rx_dmamap);
- bus_dma_tag_destroy(sc->sis_ldata.sis_rx_tag);
- }
-
- if (sc->sis_ldata.sis_tx_tag) {
- bus_dmamap_unload(sc->sis_ldata.sis_tx_tag,
- sc->sis_ldata.sis_tx_dmamap);
- bus_dmamem_free(sc->sis_ldata.sis_tx_tag,
- sc->sis_ldata.sis_tx_list,
- sc->sis_ldata.sis_tx_dmamap);
- bus_dma_tag_destroy(sc->sis_ldata.sis_tx_tag);
- }
- if (sc->sis_tag)
- bus_dma_tag_destroy(sc->sis_tag);
- if (sc->sis_parent_tag)
- bus_dma_tag_destroy(sc->sis_parent_tag);
+ sis_dma_free(dev);
return(0);
}
static int
sis_list_tx_init(struct sis_softc *sc)
{
- struct sis_list_data *ld;
- struct sis_chain_data *cd;
+ struct sis_list_data *ld = &sc->sis_ldata;
+ struct sis_chain_data *cd = &sc->sis_cdata;
int i, nexti;
- cd = &sc->sis_cdata;
- ld = &sc->sis_ldata;
-
for (i = 0; i < SIS_TX_LIST_CNT; i++) {
+ bus_addr_t paddr;
+
+ /*
+ * Link the TX desc together
+ */
nexti = (i == (SIS_TX_LIST_CNT - 1)) ? 0 : i+1;
- bus_dmamap_load(sc->sis_ldata.sis_tx_tag,
- sc->sis_ldata.sis_tx_dmamap,
- &ld->sis_tx_list[nexti],
- sizeof(struct sis_desc), sis_dma_map_desc_next,
- &ld->sis_tx_list[i], 0);
- ld->sis_tx_list[i].sis_ptr = 0;
- ld->sis_tx_list[i].sis_ctl = 0;
+ paddr = ld->sis_tx_paddr + (nexti * sizeof(struct sis_desc));
+ ld->sis_tx_list[i].sis_next = paddr;
}
-
cd->sis_tx_prod = cd->sis_tx_cons = cd->sis_tx_cnt = 0;
- bus_dmamap_sync(sc->sis_ldata.sis_tx_tag, sc->sis_ldata.sis_tx_dmamap,
- BUS_DMASYNC_PREWRITE);
-
- return(0);
+ return 0;
}
/*
static int
sis_list_rx_init(struct sis_softc *sc)
{
- struct sis_list_data *ld;
- struct sis_chain_data *cd;
- int i, nexti;
-
- ld = &sc->sis_ldata;
- cd = &sc->sis_cdata;
+ struct sis_list_data *ld = &sc->sis_ldata;
+ struct sis_chain_data *cd = &sc->sis_cdata;
+ int i, error;
for (i = 0; i < SIS_RX_LIST_CNT; i++) {
- if (sis_newbuf(sc, i, NULL) == ENOBUFS)
- return(ENOBUFS);
+ bus_addr_t paddr;
+ int nexti;
+ error = sis_newbuf(sc, i, 1);
+ if (error)
+ return error;
+
+ /*
+ * Link the RX desc together
+ */
nexti = (i == (SIS_RX_LIST_CNT - 1)) ? 0 : i+1;
- bus_dmamap_load(sc->sis_ldata.sis_rx_tag,
- sc->sis_ldata.sis_rx_dmamap,
- &ld->sis_rx_list[nexti],
- sizeof(struct sis_desc), sis_dma_map_desc_next,
- &ld->sis_rx_list[i], 0);
+ paddr = ld->sis_rx_paddr + (nexti * sizeof(struct sis_desc));
+ ld->sis_rx_list[i].sis_next = paddr;
}
-
- bus_dmamap_sync(sc->sis_ldata.sis_rx_tag, sc->sis_ldata.sis_rx_dmamap,
- BUS_DMASYNC_PREWRITE);
-
cd->sis_rx_prod = 0;
- return(0);
+ return 0;
}
/*
* Initialize an RX descriptor and attach an MBUF cluster.
*/
static int
-sis_newbuf(struct sis_softc *sc, int idx, struct mbuf *m)
+sis_newbuf(struct sis_softc *sc, int idx, int init)
{
- struct sis_desc *c = &sc->sis_ldata.sis_rx_list[idx];
- struct sis_rx_data *rd = &sc->sis_cdata.sis_rx_data[idx];
+ struct sis_chain_data *cd = &sc->sis_cdata;
+ struct sis_rx_data *rd = &cd->sis_rx_data[idx];
+ bus_dma_segment_t seg;
+ bus_dmamap_t map;
+ struct mbuf *m;
+ int nseg, error;
+ m = m_getcl(init ? MB_WAIT : MB_DONTWAIT, MT_DATA, M_PKTHDR);
if (m == NULL) {
- m = m_getcl(MB_DONTWAIT, MT_DATA, M_PKTHDR);
- if (m == NULL)
- return(ENOBUFS);
- } else {
- m->m_data = m->m_ext.ext_buf;
+ if (init)
+ if_printf(&sc->arpcom.ac_if, "can't alloc RX mbuf\n");
+ return ENOBUFS;
}
+ m->m_len = m->m_pkthdr.len = MCLBYTES;
+ /* Try loading the mbuf into tmp DMA map */
+ error = bus_dmamap_load_mbuf_segment(cd->sis_rxbuf_tag,
+ cd->sis_rx_tmpmap, m, &seg, 1, &nseg, BUS_DMA_NOWAIT);
+ if (error) {
+ m_freem(m);
+ if (init)
+ if_printf(&sc->arpcom.ac_if, "can't load RX mbuf\n");
+ return error;
+ }
+
+ /* Unload the currently loaded mbuf */
+ if (rd->sis_mbuf != NULL) {
+ bus_dmamap_sync(cd->sis_rxbuf_tag, rd->sis_map,
+ BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(cd->sis_rxbuf_tag, rd->sis_map);
+ }
+
+ /* Swap DMA maps */
+ map = cd->sis_rx_tmpmap;
+ cd->sis_rx_tmpmap = rd->sis_map;
+ rd->sis_map = map;
+
+ /* Save necessary information */
rd->sis_mbuf = m;
- c->sis_ctl = SIS_RXLEN;
+ rd->sis_paddr = seg.ds_addr;
- bus_dmamap_create(sc->sis_tag, 0, &rd->sis_map);
- bus_dmamap_load(sc->sis_tag, rd->sis_map, mtod(m, void *), MCLBYTES,
- sis_dma_map_desc_ptr, c, 0);
- bus_dmamap_sync(sc->sis_tag, rd->sis_map, BUS_DMASYNC_PREWRITE);
+ sis_setup_rxdesc(sc, idx);
+ return 0;
+}
- return(0);
+static void
+sis_setup_rxdesc(struct sis_softc *sc, int idx)
+{
+ struct sis_desc *c = &sc->sis_ldata.sis_rx_list[idx];
+
+ /* Setup the RX desc */
+ c->sis_ctl = SIS_RXLEN;
+ c->sis_ptr = sc->sis_cdata.sis_rx_data[idx].sis_paddr;
}
/*
static void
sis_rxeof(struct sis_softc *sc)
{
- struct mbuf *m;
- struct ifnet *ifp;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
int i, total_len = 0;
uint32_t rxstat;
- ifp = &sc->arpcom.ac_if;
i = sc->sis_cdata.sis_rx_prod;
-
- while(SIS_OWNDESC(&sc->sis_ldata.sis_rx_list[i])) {
+ while (SIS_OWNDESC(&sc->sis_ldata.sis_rx_list[i])) {
struct sis_desc *cur_rx;
struct sis_rx_data *rd;
+ struct mbuf *m;
int idx = i;
#ifdef DEVICE_POLLING
rd = &sc->sis_cdata.sis_rx_data[idx];
rxstat = cur_rx->sis_rxstat;
- bus_dmamap_sync(sc->sis_tag, rd->sis_map,
- BUS_DMASYNC_POSTWRITE);
- bus_dmamap_unload(sc->sis_tag, rd->sis_map);
- bus_dmamap_destroy(sc->sis_tag, rd->sis_map);
+ total_len = SIS_RXBYTES(cur_rx);
m = rd->sis_mbuf;
- rd->sis_mbuf = NULL;
-
- total_len = SIS_RXBYTES(cur_rx);
SIS_INC(i, SIS_RX_LIST_CNT);
ifp->if_ierrors++;
if (rxstat & SIS_RXSTAT_COLL)
ifp->if_collisions++;
- sis_newbuf(sc, idx, m);
+ sis_setup_rxdesc(sc, idx);
continue;
}
/* No errors; receive the packet. */
-#ifdef __i386__
- /*
- * On the x86 we do not have alignment problems, so try to
- * allocate a new buffer for the receive ring, and pass up
- * the one where the packet is already, saving the expensive
- * copy done in m_devget().
- * If we are on an architecture with alignment problems, or
- * if the allocation fails, then use m_devget and leave the
- * existing buffer in the receive ring.
- */
- if (sis_newbuf(sc, idx, NULL) == 0)
+ if (sis_newbuf(sc, idx, 0) == 0) {
m->m_pkthdr.len = m->m_len = total_len;
- else
-#endif
- {
- struct mbuf *m0;
- m0 = m_devget(mtod(m, char *) - ETHER_ALIGN,
- total_len + ETHER_ALIGN, 0, ifp, NULL);
- sis_newbuf(sc, idx, m);
- if (m0 == NULL) {
- ifp->if_ierrors++;
- continue;
- }
- m_adj(m0, ETHER_ALIGN);
- m = m0;
+ m->m_pkthdr.rcvif = ifp;
+ } else {
+ ifp->if_ierrors++;
+ sis_setup_rxdesc(sc, idx);
+ continue;
}
ifp->if_ipackets++;
ifp->if_input(ifp, m);
}
-
sc->sis_cdata.sis_rx_prod = i;
}
static void
sis_txeof(struct sis_softc *sc)
{
- struct ifnet *ifp;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct sis_chain_data *cd = &sc->sis_cdata;
uint32_t idx;
- ifp = &sc->arpcom.ac_if;
-
/*
* Go through our tx list and free mbufs for those
* frames that have been transmitted.
struct sis_tx_data *td;
cur_tx = &sc->sis_ldata.sis_tx_list[idx];
- td = &sc->sis_cdata.sis_tx_data[idx];
+ td = &cd->sis_tx_data[idx];
if (SIS_OWNDESC(cur_tx))
break;
ifp->if_opackets++;
if (td->sis_mbuf != NULL) {
+ bus_dmamap_unload(cd->sis_txbuf_tag, td->sis_map);
m_freem(td->sis_mbuf);
td->sis_mbuf = NULL;
- bus_dmamap_unload(sc->sis_tag, td->sis_map);
- bus_dmamap_destroy(sc->sis_tag, td->sis_map);
}
}
if (idx != sc->sis_cdata.sis_tx_cons) {
/* we freed up some buffers */
sc->sis_cdata.sis_tx_cons = idx;
- ifp->if_flags &= ~IFF_OACTIVE;
}
- ifp->if_timer = (sc->sis_cdata.sis_tx_cnt == 0) ? 0 : 5;
+ if (cd->sis_tx_cnt == 0)
+ ifp->if_timer = 0;
+ if (!SIS_IS_OACTIVE(sc))
+ ifp->if_flags &= ~IFF_OACTIVE;
}
static void
* pointers to the fragment pointers.
*/
static int
-sis_encap(struct sis_softc *sc, struct mbuf *m_head, uint32_t *txidx)
+sis_encap(struct sis_softc *sc, struct mbuf **m_head, uint32_t *txidx)
{
- struct sis_desc *f = NULL;
- struct sis_tx_data *td = NULL;
- struct mbuf *m;
- int frag, cur, cnt = 0;
+ struct sis_chain_data *cd = &sc->sis_cdata;
+ struct sis_list_data *ld = &sc->sis_ldata;
+ bus_dma_segment_t segs[SIS_NSEGS];
+ bus_dmamap_t map;
+ int frag, cur, maxsegs, nsegs, error, i;
+
+ maxsegs = SIS_TX_LIST_CNT - SIS_NSEGS_RESERVED - cd->sis_tx_cnt;
+ KASSERT(maxsegs >= 1, ("not enough TX descs\n"));
+ if (maxsegs > SIS_NSEGS)
+ maxsegs = SIS_NSEGS;
+
+ map = cd->sis_tx_data[*txidx].sis_map;
+ error = bus_dmamap_load_mbuf_defrag(cd->sis_txbuf_tag, map, m_head,
+ segs, maxsegs, &nsegs, BUS_DMA_NOWAIT);
+ if (error) {
+ m_freem(*m_head);
+ *m_head = NULL;
+ return error;
+ }
+ bus_dmamap_sync(cd->sis_txbuf_tag, map, BUS_DMASYNC_PREWRITE);
- /*
- * Start packing the mbufs in this chain into
- * the fragment pointers. Stop when we run out
- * of fragments or hit the end of the mbuf chain.
- */
cur = frag = *txidx;
+ for (i = 0; i < nsegs; ++i) {
+ struct sis_desc *f = &ld->sis_tx_list[frag];
- for (m = m_head; m != NULL; m = m->m_next) {
- if (m->m_len != 0) {
- if ((SIS_TX_LIST_CNT -
- (sc->sis_cdata.sis_tx_cnt + cnt)) < 2)
- break;
+ f->sis_ctl = SIS_CMDSTS_MORE | segs[i].ds_len;
+ f->sis_ptr = segs[i].ds_addr;
+ if (i != 0)
+ f->sis_ctl |= SIS_CMDSTS_OWN;
- f = &sc->sis_ldata.sis_tx_list[frag];
- td = &sc->sis_cdata.sis_tx_data[frag];
-
- f->sis_ctl = SIS_CMDSTS_MORE | m->m_len;
- bus_dmamap_create(sc->sis_tag, 0, &td->sis_map);
- bus_dmamap_load(sc->sis_tag, td->sis_map,
- mtod(m, void *), m->m_len,
- sis_dma_map_desc_ptr, f, 0);
- bus_dmamap_sync(sc->sis_tag, td->sis_map,
- BUS_DMASYNC_PREREAD);
- if (cnt != 0)
- f->sis_ctl |= SIS_CMDSTS_OWN;
- cur = frag;
- SIS_INC(frag, SIS_TX_LIST_CNT);
- cnt++;
- }
+ cur = frag;
+ SIS_INC(frag, SIS_TX_LIST_CNT);
}
- /* Caller should make sure that 'm_head' is not excessive fragmented */
- KASSERT(m == NULL, ("too many fragments\n"));
+ ld->sis_tx_list[cur].sis_ctl &= ~SIS_CMDSTS_MORE;
+ ld->sis_tx_list[*txidx].sis_ctl |= SIS_CMDSTS_OWN;
- sc->sis_cdata.sis_tx_data[cur].sis_mbuf = m_head;
+ /* Swap DMA map */
+ cd->sis_tx_data[*txidx].sis_map = cd->sis_tx_data[cur].sis_map;
+ cd->sis_tx_data[cur].sis_map = map;
- sc->sis_ldata.sis_tx_list[cur].sis_ctl &= ~SIS_CMDSTS_MORE;
- sc->sis_ldata.sis_tx_list[*txidx].sis_ctl |= SIS_CMDSTS_OWN;
- sc->sis_cdata.sis_tx_cnt += cnt;
+ cd->sis_tx_data[cur].sis_mbuf = *m_head;
+
+ cd->sis_tx_cnt += nsegs;
*txidx = frag;
- return(0);
+ return 0;
}
/*
static void
sis_start(struct ifnet *ifp)
{
- struct sis_softc *sc;
- struct mbuf *m_head = NULL, *m_defragged;
+ struct sis_softc *sc = ifp->if_softc;
+ int need_trans, error;
uint32_t idx;
- int need_trans;
-
- sc = ifp->if_softc;
if (!sc->sis_link) {
ifq_purge(&ifp->if_snd);
return;
}
- idx = sc->sis_cdata.sis_tx_prod;
-
if ((ifp->if_flags & (IFF_OACTIVE | IFF_RUNNING)) != IFF_RUNNING)
return;
+ idx = sc->sis_cdata.sis_tx_prod;
need_trans = 0;
+
while (sc->sis_cdata.sis_tx_data[idx].sis_mbuf == NULL) {
- struct mbuf *m;
- int cnt;
+ struct mbuf *m_head;
/*
* If there's no way we can send any packets, return now.
*/
- if (SIS_TX_LIST_CNT - sc->sis_cdata.sis_tx_cnt < 2) {
+ if (SIS_IS_OACTIVE(sc)) {
ifp->if_flags |= IFF_OACTIVE;
break;
}
- m_defragged = NULL;
m_head = ifq_dequeue(&ifp->if_snd, NULL);
if (m_head == NULL)
break;
-again:
- cnt = 0;
- for (m = m_head; m != NULL; m = m->m_next)
- ++cnt;
- if ((SIS_TX_LIST_CNT -
- (sc->sis_cdata.sis_tx_cnt + cnt)) < 2) {
- if (m_defragged != NULL) {
- /*
- * Even after defragmentation, there
- * are still too many fragments, so
- * drop this packet.
- */
- m_freem(m_head);
+ error = sis_encap(sc, &m_head, &idx);
+ if (error) {
+ ifp->if_oerrors++;
+ if (sc->sis_cdata.sis_tx_cnt == 0) {
+ continue;
+ } else {
ifp->if_flags |= IFF_OACTIVE;
break;
}
-
- m_defragged = m_defrag(m_head, MB_DONTWAIT);
- if (m_defragged == NULL) {
- m_freem(m_head);
- continue;
- }
- m_head = m_defragged;
-
- /* Recount # of fragments */
- goto again;
}
-
- sis_encap(sc, m_head, &idx);
need_trans = 1;
/*
}
/* Init circular RX list. */
- if (sis_list_rx_init(sc) == ENOBUFS) {
+ if (sis_list_rx_init(sc)) {
if_printf(ifp, "initialization failed: "
"no memory for rx buffers\n");
sis_stop(sc);
static void
sis_stop(struct sis_softc *sc)
{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct sis_list_data *ld = &sc->sis_ldata;
+ struct sis_chain_data *cd = &sc->sis_cdata;
int i;
- struct ifnet *ifp;
-
- ifp = &sc->arpcom.ac_if;
- ifp->if_timer = 0;
callout_stop(&sc->sis_timer);
ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+ ifp->if_timer = 0;
+
CSR_WRITE_4(sc, SIS_IER, 0);
CSR_WRITE_4(sc, SIS_IMR, 0);
SIS_SETBIT(sc, SIS_CSR, SIS_CSR_TX_DISABLE|SIS_CSR_RX_DISABLE);
* Free data in the RX lists.
*/
for (i = 0; i < SIS_RX_LIST_CNT; i++) {
- if (sc->sis_cdata.sis_rx_data[i].sis_mbuf != NULL) {
- bus_dmamap_unload(sc->sis_tag,
- sc->sis_cdata.sis_rx_data[i].sis_map);
- bus_dmamap_destroy(sc->sis_tag,
- sc->sis_cdata.sis_rx_data[i].sis_map);
- m_freem(sc->sis_cdata.sis_rx_data[i].sis_mbuf);
- sc->sis_cdata.sis_rx_data[i].sis_mbuf = NULL;
+ struct sis_rx_data *rd = &cd->sis_rx_data[i];
+
+ if (rd->sis_mbuf != NULL) {
+ bus_dmamap_unload(cd->sis_rxbuf_tag, rd->sis_map);
+ m_freem(rd->sis_mbuf);
+ rd->sis_mbuf = NULL;
}
}
- bzero(sc->sis_ldata.sis_rx_list, sizeof(sc->sis_ldata.sis_rx_list));
+ bzero(ld->sis_rx_list, SIS_RX_LIST_SZ);
/*
* Free the TX list buffers.
*/
for (i = 0; i < SIS_TX_LIST_CNT; i++) {
- if (sc->sis_cdata.sis_tx_data[i].sis_mbuf != NULL) {
- bus_dmamap_unload(sc->sis_tag,
- sc->sis_cdata.sis_tx_data[i].sis_map);
- bus_dmamap_destroy(sc->sis_tag,
- sc->sis_cdata.sis_tx_data[i].sis_map);
- m_freem(sc->sis_cdata.sis_tx_data[i].sis_mbuf);
- sc->sis_cdata.sis_tx_data[i].sis_mbuf = NULL;
+ struct sis_tx_data *td = &cd->sis_tx_data[i];
+
+ if (td->sis_mbuf != NULL) {
+ bus_dmamap_unload(cd->sis_txbuf_tag, td->sis_map);
+ m_freem(td->sis_mbuf);
+ td->sis_mbuf = NULL;
}
}
- bzero(sc->sis_ldata.sis_tx_list, sizeof(sc->sis_ldata.sis_tx_list));
+ bzero(ld->sis_tx_list, SIS_TX_LIST_SZ);
}
/*
lwkt_serialize_exit(ifp->if_serializer);
}
+static int
+sis_dma_alloc(device_t dev)
+{
+ struct sis_softc *sc = device_get_softc(dev);
+ struct sis_chain_data *cd = &sc->sis_cdata;
+ struct sis_list_data *ld = &sc->sis_ldata;
+ int i, error;
+
+ /* Create top level DMA tag */
+ error = bus_dma_tag_create(NULL, /* parent */
+ 1, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ BUS_SPACE_MAXSIZE_32BIT,/* maxsize */
+ 0, /* nsegments */
+ BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */
+ 0, /* flags */
+ &sc->sis_parent_tag);
+ if (error) {
+ device_printf(dev, "could not create parent DMA tag\n");
+ return error;
+ }
+
+ /* Allocate RX ring */
+ ld->sis_rx_list = bus_dmamem_coherent_any(sc->sis_parent_tag,
+ SIS_RING_ALIGN, SIS_RX_LIST_SZ,
+ BUS_DMA_WAITOK | BUS_DMA_ZERO,
+ &ld->sis_rx_tag, &ld->sis_rx_dmamap,
+ &ld->sis_rx_paddr);
+ if (ld->sis_rx_list == NULL) {
+ device_printf(dev, "could not allocate RX ring\n");
+ return ENOMEM;
+ }
+
+ /* Allocate TX ring */
+ ld->sis_tx_list = bus_dmamem_coherent_any(sc->sis_parent_tag,
+ SIS_RING_ALIGN, SIS_TX_LIST_SZ,
+ BUS_DMA_WAITOK | BUS_DMA_ZERO,
+ &ld->sis_tx_tag, &ld->sis_tx_dmamap,
+ &ld->sis_tx_paddr);
+ if (ld->sis_tx_list == NULL) {
+ device_printf(dev, "could not allocate TX ring\n");
+ return ENOMEM;
+ }
+
+ /* Create DMA tag for TX mbuf */
+ error = bus_dma_tag_create(sc->sis_parent_tag,/* parent */
+ 1, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ MCLBYTES, /* maxsize */
+ SIS_NSEGS, /* nsegments */
+ MCLBYTES, /* maxsegsize */
+ BUS_DMA_ALLOCNOW | BUS_DMA_WAITOK,/* flags */
+ &cd->sis_txbuf_tag);
+ if (error) {
+ device_printf(dev, "could not create TX buf DMA tag\n");
+ return error;
+ }
+
+ /* Create DMA maps for TX mbufs */
+ for (i = 0; i < SIS_TX_LIST_CNT; ++i) {
+ error = bus_dmamap_create(cd->sis_txbuf_tag, BUS_DMA_WAITOK,
+ &cd->sis_tx_data[i].sis_map);
+ if (error) {
+ int j;
+
+ for (j = 0; j < i; ++j) {
+ bus_dmamap_destroy(cd->sis_txbuf_tag,
+ cd->sis_tx_data[j].sis_map);
+ }
+ bus_dma_tag_destroy(cd->sis_txbuf_tag);
+ cd->sis_txbuf_tag = NULL;
+
+ device_printf(dev, "could not create %dth "
+ "TX buf DMA map\n", i);
+ return error;
+ }
+ }
+
+ /* Create DMA tag for RX mbuf */
+ error = bus_dma_tag_create(sc->sis_parent_tag,/* parent */
+ SIS_RXBUF_ALIGN, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ MCLBYTES, /* maxsize */
+ 1, /* nsegments */
+ MCLBYTES, /* maxsegsize */
+ BUS_DMA_ALLOCNOW | BUS_DMA_WAITOK |
+ BUS_DMA_ALIGNED, /* flags */
+ &cd->sis_rxbuf_tag);
+ if (error) {
+ device_printf(dev, "could not create RX buf DMA tag\n");
+ return error;
+ }
+
+ /* Create tmp DMA map for loading RX mbuf */
+ error = bus_dmamap_create(cd->sis_rxbuf_tag, BUS_DMA_WAITOK,
+ &cd->sis_rx_tmpmap);
+ if (error) {
+ device_printf(dev, "could not create RX buf tmp DMA map\n");
+ bus_dma_tag_destroy(cd->sis_rxbuf_tag);
+ cd->sis_rxbuf_tag = NULL;
+ return error;
+ }
+
+ /* Create DMA maps for RX mbufs */
+ for (i = 0; i < SIS_RX_LIST_CNT; ++i) {
+ error = bus_dmamap_create(cd->sis_rxbuf_tag, BUS_DMA_WAITOK,
+ &cd->sis_rx_data[i].sis_map);
+ if (error) {
+ int j;
+
+ for (j = 0; j < i; ++j) {
+ bus_dmamap_destroy(cd->sis_rxbuf_tag,
+ cd->sis_rx_data[j].sis_map);
+ }
+ bus_dmamap_destroy(cd->sis_rxbuf_tag,
+ cd->sis_rx_tmpmap);
+ bus_dma_tag_destroy(cd->sis_rxbuf_tag);
+ cd->sis_rxbuf_tag = NULL;
+
+ device_printf(dev, "could not create %dth "
+ "RX buf DMA map\n", i);
+ return error;
+ }
+ }
+ return 0;
+}
+
+static void
+sis_dma_free(device_t dev)
+{
+ struct sis_softc *sc = device_get_softc(dev);
+ struct sis_list_data *ld = &sc->sis_ldata;
+ struct sis_chain_data *cd = &sc->sis_cdata;
+ int i;
+
+ /* Free TX ring */
+ if (ld->sis_tx_list != NULL) {
+ bus_dmamap_unload(ld->sis_tx_tag, ld->sis_tx_dmamap);
+ bus_dmamem_free(ld->sis_tx_tag, ld->sis_tx_list,
+ ld->sis_tx_dmamap);
+ bus_dma_tag_destroy(ld->sis_tx_tag);
+ }
+
+ /* Free RX ring */
+ if (ld->sis_rx_list != NULL) {
+ bus_dmamap_unload(ld->sis_rx_tag, ld->sis_rx_dmamap);
+ bus_dmamem_free(ld->sis_rx_tag, ld->sis_rx_list,
+ ld->sis_rx_dmamap);
+ bus_dma_tag_destroy(ld->sis_rx_tag);
+ }
+
+ /* Destroy DMA stuffs for TX mbufs */
+ if (cd->sis_txbuf_tag != NULL) {
+ for (i = 0; i < SIS_TX_LIST_CNT; ++i) {
+ KKASSERT(cd->sis_tx_data[i].sis_mbuf == NULL);
+ bus_dmamap_destroy(cd->sis_txbuf_tag,
+ cd->sis_tx_data[i].sis_map);
+ }
+ bus_dma_tag_destroy(cd->sis_txbuf_tag);
+ }
+
+ /* Destroy DMA stuffs for RX mbufs */
+ if (cd->sis_rxbuf_tag != NULL) {
+ for (i = 0; i < SIS_RX_LIST_CNT; ++i) {
+ KKASSERT(cd->sis_rx_data[i].sis_mbuf == NULL);
+ bus_dmamap_destroy(cd->sis_rxbuf_tag,
+ cd->sis_rx_data[i].sis_map);
+ }
+ bus_dmamap_destroy(cd->sis_rxbuf_tag, cd->sis_rx_tmpmap);
+ bus_dma_tag_destroy(cd->sis_rxbuf_tag);
+ }
+}