Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 30 Sep 2005 10:44:24 -0400
From:      John Baldwin <jhb@FreeBSD.org>
To:        freebsd-drivers@freebsd.org, rashmi ns <nsrashmi@gmail.com>
Subject:   Re: request:help reqd in using bus_dma functions
Message-ID:  <200509301044.25403.jhb@FreeBSD.org>
In-Reply-To: <9f9993160509292342w6f7fe556l379a6de9f5bdfc24@mail.gmail.com>
References:  <9f9993160509292342w6f7fe556l379a6de9f5bdfc24@mail.gmail.com>

next in thread | previous in thread | raw e-mail | index | archive | help
On Friday 30 September 2005 02:42 am, rashmi ns wrote:
> hello group ,
> I am writing a network driver where in have created tags and maps for
> tx_desc of transmit-q-size by using these functions .
>
>
> 1. bus_dma_tag_create
> 2. bus_dmamap_create
> 3.bus_dmamem_alloc
>
>  struct xxxx_tx_desc {
>
> DWORD data_buff;
>
> DWORD cvbcnxt;
>
> DWORD channel_no;
>
> DWORD pend_desc;
>
> };
>
>  For a packet to be transmitted a packet's address should be placed in
> tx_desc_q 's DWORD data_buff field and update the rest of the fields .Then
> h/w detects the presence of the packet and tranmits the packet
>
> now to test I want to load a buffer like (char buff[50])in tx_q space can
> any one tell me in which order I can use bus_dma functions .I have gone th'
> docs but not very sure as I'm writing n/w drivers for the first time .I'm
> not clear with the dma concepts . I'm getting confused kindly help
>
>  Now should I use bus_dmamap_load(bus_dma_tag_t dmat, bus_dmamap_t map,
> void *buf, bus_size_t buflen, bus_dmamap_callback_t *callback,void
> *callback_arg, int flags);
>
> I'm not getting what callback func should to how to get the mapped address
> and place into tx_desc_q 's
>
>  Thanks and regards,

If your descriptors need to be physically contiguous, you can allocate memory 
for the descriptors using bus_dmamem_alloc().  This function will allocate 
memory and return both the in-kernel pointer as well as an appropriate dma 
map and the physical DMA address to use when talking to the card.

For actual packets, you will need to create a tag that describes the 
limitations of your hardware (for example, does it only support 32-bit 
addresses).  You can then allocate a map for each descriptor based off of 
that tag.  When you want to enqueue a packet for either tx or rx, you call 
bus_dmapmap_load().  It calls your callback function with the list of 
scatter/gather address/length pairs that you can then put into your 
descriptors.  For network drivers thare are some convenience functions that 
work directly with mbufs and mbuf chains: bus_dmamap_load_mbuf() and 
bus_dmamap_load_mbuf_sg().  The sg() variant doesn't call a callback 
function, but returns an array of scatter/gather entries.  Here's some 
stripped-down code from the de(4) driver I recently worked on:

Setting up things in attach:

    /* Allocate tags and such for TX descriptors. */
    maxsize = TULIP_DATA_PER_DESC;
    nsegs = TULIP_MAX_TXSEG;
    ri = &sc->tulip_txinfo;

    /* Allocate a tag for the data buffers. */
    error = bus_dma_tag_create(NULL, 4, 0,
	BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
	maxsize, nsegs, TULIP_DATA_PER_DESC, 0, NULL, NULL, &ri->ri_data_tag);
    if (error) {
	device_printf(dev, "failed to allocate %s buffer dma tag\n", name);
	return (error);
    }

    /* Allocate maps for the data buffers. */
    ri->ri_data_maps = malloc(sizeof(bus_dmamap_t) * count, M_DEVBUF,
	M_WAITOK | M_ZERO);
    for (i = 0; i < count; i++) {
    	error = bus_dmamap_create(ri->ri_data_tag, 0, &ri->ri_data_maps[i]);
	if (error) {
	    device_printf(dev, "failed to create map for %s buffer %d\n",
		name, i);
	    return (error);
	}
    }

Transmitting a packet that can cross multiple descriptors:

    /*
     * ri points to metadata for the TX ring.  It includes an array of
     * metadata about each descriptor along with head and tail pointers
     * into the ring of metadata (nextin and nextout).  Each descriptor
     * info struct contains a pointer to the descriptor (di_desc), a
     * pointer to the associated dma map (di_map), and a pointer to the
     * mbuf associated with this descriptor (di_mbuf).
     */
    bus_dma_segment_t segs[TULIP_MAX_TXSEG];
    int nsegs;

    eop = nextout = ri->ri_nextout;
    segcnt = 0;
    error = bus_dmamap_load_mbuf_sg(ri->ri_data_tag, *eop->di_map, m, segs,
	&nsegs, BUS_DMA_NOWAIT);
    if (error != 0) {
	if (error == EFBIG) {
	    /*
	     * The packet exceeds the number of transmit buffer
	     * entries that we can use for one packet, so we have
	     * to recopy it into one mbuf and then try again.  If
	     * we can't recopy it, try again later.
	     */
	    m0 = m_defrag(m, M_DONTWAIT);
	    if (m0 == NULL) {
		goto finish;
	    }
	    m = m0;
	    error = bus_dmamap_load_mbuf_sg(ri->ri_data_tag, *eop->di_map, m,
		segs, &nsegs, BUS_DMA_NOWAIT);
	}
	if (error != 0) {
	    goto finish;
	}
    }

    /*
     * At this point the list of physical addresses and lengths for the
     * mbuf chain 'm' are in the segs[] array and it has nsegs entries.
     * I start putting the entries into descriptors.  On this card, each
     * descriptor has two s/g entries.
     */
    for (; nsegs - segcnt > 1; segcnt += 2) {
	eop = nextout;
	eop->di_desc->d_flag   &= TULIP_DFLAG_ENDRING|TULIP_DFLAG_CHAIN;
	eop->di_desc->d_status  = d_status;
	eop->di_desc->d_addr1   = segs[segcnt].ds_addr;
	eop->di_desc->d_length1 = segs[segcnt].ds_len;
	eop->di_desc->d_addr2   = segs[segcnt+1].ds_addr;
	eop->di_desc->d_length2 = segs[segcnt+1].ds_len;
	d_status = TULIP_DSTS_OWNER;
	if (++nextout == ri->ri_last)
	    nextout = ri->ri_first;
    }
    if (segcnt < nsegs) {
	eop = nextout;
	eop->di_desc->d_flag   &= TULIP_DFLAG_ENDRING|TULIP_DFLAG_CHAIN;
	eop->di_desc->d_status  = d_status;
	eop->di_desc->d_addr1   = segs[segcnt].ds_addr;
	eop->di_desc->d_length1 = segs[segcnt].ds_len;
	eop->di_desc->d_addr2   = 0;
	eop->di_desc->d_length2 = 0;
	if (++nextout == ri->ri_last)
	    nextout = ri->ri_first;
    }

    /*
     * tulip_tx_intr() harvests the mbuf from the last descriptor in the
     * frame.  We just used the dmamap in the first descriptor for the
     * load operation however.  Thus, to let the tulip_dequeue_mbuf() call
     * in tulip_tx_intr() unload the correct dmamap, we swap the dmamap
     * pointers in the two descriptors if this is a multiple-descriptor
     * packet.
     */
    if (eop != ri->ri_nextout) {
	    map = eop->di_map;
	    eop->di_map = ri->ri_nextout->di_map;
	    ri->ri_nextout->di_map = map;
    }

    /*
     * The descriptors have been filled in.  Now get ready
     * to transmit.
     */
    eop->di_mbuf = m;
    TULIP_TXMAP_PRESYNC(ri, ri->ri_nextout);

When the transmit is finished, this driver cleans up when it harvests the last 
descriptor for the packet:

struct mbuf *
tulip_dequeue_mbuf(tulip_ringinfo_t *ri, tulip_descinfo_t *di, int sync)
{
    struct mbuf *m;

    m = di->di_mbuf;
    if (m != NULL) {
	switch (sync) {
	case SYNC_NONE:
	    break;
	case SYNC_RX:
	    TULIP_RXMAP_POSTSYNC(ri, di);
	    break;
	case SYNC_TX:
	    TULIP_TXMAP_POSTSYNC(ri, di);
	    break;
	default:
	    panic("bad sync flag: %d", sync);
	}
	bus_dmamap_unload(ri->ri_data_tag, *di->di_map);
	di->di_mbuf = NULL;
    }
    return (m);
}

Harvesting a completed packet:

	d_flag = DESC_FLAG(ri->ri_nextin);
	if (d_flag & TULIP_DFLAG_TxLASTSEG) {
	    if (d_flag & TULIP_DFLAG_TxSETUPPKT) {
		...
	    } else {
		const u_int32_t d_status = DESC_STATUS(ri->ri_nextin);

		m = tulip_dequeue_mbuf(ri, ri->ri_nextin, SYNC_TX);
		m_freem(m);
		...
	    }
	}

Hope this helps some.

-- 
John Baldwin <jhb@FreeBSD.org>  <><  http://www.FreeBSD.org/~jhb/
"Power Users Use the Power to Serve"  =  http://www.FreeBSD.org



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200509301044.25403.jhb>