request:help reqd in using bus_dma functions
John Baldwin
jhb at FreeBSD.org
Fri Sep 30 07:43:25 PDT 2005
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 at FreeBSD.org> <>< http://www.FreeBSD.org/~jhb/
"Power Users Use the Power to Serve" = http://www.FreeBSD.org
More information about the freebsd-drivers
mailing list