From owner-freebsd-current@FreeBSD.ORG Wed Dec 15 01:44:35 2004 Return-Path: Delivered-To: freebsd-current@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id E4A3D16A4CE for ; Wed, 15 Dec 2004 01:44:34 +0000 (GMT) Received: from pooker.samsco.org (pooker.samsco.org [168.103.85.57]) by mx1.FreeBSD.org (Postfix) with ESMTP id 749FB43D1D for ; Wed, 15 Dec 2004 01:44:34 +0000 (GMT) (envelope-from scottl@freebsd.org) Received: from [192.168.254.11] (junior-wifi.samsco.home [192.168.254.11]) (authenticated bits=0) by pooker.samsco.org (8.12.11/8.12.10) with ESMTP id iBF1lIqG048696; Tue, 14 Dec 2004 18:47:18 -0700 (MST) (envelope-from scottl@freebsd.org) Message-ID: <41BF96E8.2080300@freebsd.org> Date: Tue, 14 Dec 2004 18:44:08 -0700 From: Scott Long User-Agent: Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.7.2) Gecko/20040929 X-Accept-Language: en-us, en MIME-Version: 1.0 To: Julian Elischer References: <41BF8C1A.2090801@elischer.org> In-Reply-To: <41BF8C1A.2090801@elischer.org> X-Enigmail-Version: 0.86.1.0 X-Enigmail-Supports: pgp-inline, pgp-mime Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit X-Spam-Status: No, hits=0.0 required=3.8 tests=none autolearn=no version=2.63 X-Spam-Checker-Version: SpamAssassin 2.63 (2004-01-11) on pooker.samsco.org cc: FreeBSD Current Subject: Re: bus_dma question X-BeenThere: freebsd-current@freebsd.org X-Mailman-Version: 2.1.1 Precedence: list List-Id: Discussions about the use of FreeBSD-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 15 Dec 2004 01:44:35 -0000 Julian Elischer wrote: > > The bus_dma man page is good. but it there documentation of how to > extract the physical page addresses from a map? In other words, if I > defien a tag for a device and then create a map using bus_dmamap_create, > and then load it using bus_dmamap_load, what is the recommended method > of extracting the list of addresses to feed into a DMA scatter_gather > list? Looking at various drivers I'm getting more and more confused.. > There is a lot of home-grown s/g list > generation code out there.. is there a "right" way to do this? > The function pointer that you provide to bus_dmamap_load() is called when the S/G map is ready (it might be deferred due to needing bounce pages, so you cannot assume that it will be called before bus_dmamap_load() returns). The S/G list and length is provided in the arguments to the callback function. So: static void my_start(my_softc, my_data_ptr, my_data_len) { /* Prepare resources for sending data to the hardware */ [...] /* * Hand off to busdma to generate the S/G list. */ error = bus_dmamap_load(my_softc->dmat, my_softc->dmamap, my_data_ptr, my_data_len, my_callback_func, my_softc, 0); if (error == EINPROGRESS) { /* * The callback will be called later. Don't queue up * any more commands until it has so that we don't spin * on busdma. */ my_softc->flags |= FROZEN; } return; } static void my_callack_func(void *arg, bus_dma_segment_t *segs, int nseg, int error) { my_softc_t *my_softc = arg; if (error) { /* Handle error */ [...] return; for (i = 0; i < nsegs; i++) { my_sg[i].phys = segs[i].ds_addr; mg_sg[i].len = segs[i].ds_len; } if (dma is from host memory to the hardware) bus_dmamap_sync(my_dmat, my_dmamap, BUS_DMASYNC_PREWRITE); if (dma is from the hardware to host memory) bus_dmamap_sync(my_dmat, my_dmamap, BUS_DMASYNC_PREREAD); /* * Send S/G list to the hardware and tell the hardware to start * the transaction. */ [...] return; } Note that you cannot pass an error value or anything else from the callback function to the caller since you have no control over when the callback will actually be called. Well, if you use the BUS_DMA_NOWAIT flag for bus_dmamap_load(), you'll be guaranteed to get an error back instead of a deferral, but that should only be used with great care. Also, don't forget to call bus_dmamap_sync() with the POSTREAD and POSTWRITE ops once the transaction is complete. This is very important now that >4GB of memory is easy to aquire; devices like UHCI and ATA can still only do 32bit S/G lists, so bouncing is required on i386 and amd64. You also need to do a bus_dmamap_unload() call to free whatever busdma resources were used for the transaction, so the sequence should be something like: static void my_intr(void *arg) { /* Check and handle interrupt */ [...] /* return busdma resources */ if (dma is from host memory to the hardware) bus_dmamap_sync(my_dmat, my_dmamap, BUS_DMASYNC_POSTWRITE); if (dma is from the hardware to host memory) bus_dmamap_sync(my_dmat, my_dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(my_dmat, my_dmamap); /* Process the data that was DMA'd */ [...] return; } There will likely be changes to the busdma API for 6.0 that will simplify this a bit, make it faster, and make it resemble NetBSD a little more, but I'm still working on them. Scott