From owner-freebsd-current@FreeBSD.ORG Wed Dec 15 22:59:11 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 7F7FB16A4CF; Wed, 15 Dec 2004 22:59:11 +0000 (GMT) Received: from mail.vicor-nb.com (bigwoop.vicor-nb.com [208.206.78.2]) by mx1.FreeBSD.org (Postfix) with ESMTP id 2CF1B43D55; Wed, 15 Dec 2004 22:59:11 +0000 (GMT) (envelope-from julian@elischer.org) Received: from elischer.org (julian.vicor-nb.com [208.206.78.97]) by mail.vicor-nb.com (Postfix) with ESMTP id F315F7A44E; Wed, 15 Dec 2004 14:59:10 -0800 (PST) Message-ID: <41C0C1BE.8070801@elischer.org> Date: Wed, 15 Dec 2004 14:59:10 -0800 From: Julian Elischer User-Agent: Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.3.1) Gecko/20030516 X-Accept-Language: en, hu MIME-Version: 1.0 To: Scott Long References: <41BF8C1A.2090801@elischer.org> <41BF96E8.2080300@freebsd.org> In-Reply-To: <41BF96E8.2080300@freebsd.org> Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit 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 22:59:11 -0000 Scott this is just what I was looking for. This should be added to the busdma man page I think.. Scott Long wrote: > 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