From owner-freebsd-drivers@FreeBSD.ORG Fri Sep 30 14:43:25 2005 Return-Path: X-Original-To: freebsd-drivers@freebsd.org Delivered-To: freebsd-drivers@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 803D516A41F for ; Fri, 30 Sep 2005 14:43:25 +0000 (GMT) (envelope-from jhb@FreeBSD.org) Received: from mv.twc.weather.com (mv.twc.weather.com [65.212.71.225]) by mx1.FreeBSD.org (Postfix) with ESMTP id A7C4B43D49 for ; Fri, 30 Sep 2005 14:43:24 +0000 (GMT) (envelope-from jhb@FreeBSD.org) Received: from [10.50.41.233] (Not Verified[10.50.41.233]) by mv.twc.weather.com with NetIQ MailMarshal (v6, 0, 3, 8) id ; Fri, 30 Sep 2005 10:59:26 -0400 From: John Baldwin To: freebsd-drivers@freebsd.org, rashmi ns Date: Fri, 30 Sep 2005 10:44:24 -0400 User-Agent: KMail/1.8 References: <9f9993160509292342w6f7fe556l379a6de9f5bdfc24@mail.gmail.com> In-Reply-To: <9f9993160509292342w6f7fe556l379a6de9f5bdfc24@mail.gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: 7bit Content-Disposition: inline Message-Id: <200509301044.25403.jhb@FreeBSD.org> Cc: Subject: Re: request:help reqd in using bus_dma functions X-BeenThere: freebsd-drivers@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Writing device drivers for FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 30 Sep 2005 14:43:25 -0000 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 <>< http://www.FreeBSD.org/~jhb/ "Power Users Use the Power to Serve" = http://www.FreeBSD.org