Implement bus_dmamap_load_mbuf/bus_dmamap_load_uio.

Tested load_mbuf with GEM ethernet driver.

Submitted by: Hiten Pandya <hiten@unixdaemons.com> (modified by grehan)
This commit is contained in:
Peter Grehan 2003-01-08 11:46:53 +00:00
parent a5550be852
commit dedc660361

View File

@ -44,9 +44,12 @@ static const char rcsid[] =
#include <sys/lock.h>
#include <sys/proc.h>
#include <sys/mutex.h>
#include <sys/mbuf.h>
#include <sys/uio.h>
#include <vm/vm.h>
#include <vm/vm_page.h>
#include <vm/vm_map.h>
#include <machine/bus.h>
@ -226,7 +229,7 @@ bus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags,
}
/*
* Free a piece of memory and it's allociated dmamap, that was allocated
* Free a piece of memory and it's allocated dmamap, that was allocated
* via bus_dmamem_alloc. Make the same choice for free/contigfree.
*/
void
@ -294,22 +297,214 @@ bus_dmamap_load(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf,
vaddr += size;
nextpaddr = paddr + size;
buflen -= size;
} while (buflen > 0);
if (buflen != 0) {
printf("bus_dmamap_load: Too many segs! buf_len = 0x%lx\n",
(u_long)buflen);
error = EFBIG;
}
(*callback)(callback_arg, dm_segments, seg, error);
} while (buflen > 0);
return (0);
if (buflen != 0) {
printf("bus_dmamap_load: Too many segs! buf_len = 0x%lx\n",
(u_long)buflen);
error = EFBIG;
}
(*callback)(callback_arg, dm_segments, seg, error);
return (0);
}
/*
* Release the mapping held by map.
* Utility function to load a linear buffer. lastaddrp holds state
* between invocations (for multiple-buffer loads). segp contains
* the starting segment on entrance, and the ending segment on exit.
* first indicates if this is the first invocation of this function.
*/
static int
bus_dmamap_load_buffer(bus_dma_tag_t dmat, bus_dma_segment_t segs[],
void *buf, bus_size_t buflen, struct thread *td,
int flags, vm_offset_t *lastaddrp, int *segp,
int first)
{
bus_size_t sgsize;
bus_addr_t curaddr, lastaddr, baddr, bmask;
vm_offset_t vaddr = (vm_offset_t)buf;
int seg;
pmap_t pmap;
if (td != NULL)
pmap = vmspace_pmap(td->td_proc->p_vmspace);
else
pmap = NULL;
lastaddr = *lastaddrp;
bmask = ~(dmat->boundary - 1);
for (seg = *segp; buflen > 0 ; ) {
/*
* Get the physical address for this segment.
*/
if (pmap)
curaddr = pmap_extract(pmap, vaddr);
else
curaddr = pmap_kextract(vaddr);
/*
* Compute the segment size, and adjust counts.
*/
sgsize = PAGE_SIZE - ((u_long)curaddr & PAGE_MASK);
if (buflen < sgsize)
sgsize = buflen;
/*
* Make sure we don't cross any boundaries.
*/
if (dmat->boundary > 0) {
baddr = (curaddr + dmat->boundary) & bmask;
if (sgsize > (baddr - curaddr))
sgsize = (baddr - curaddr);
}
/*
* Insert chunk into a segment, coalescing with
* the previous segment if possible.
*/
if (first) {
segs[seg].ds_addr = curaddr;
segs[seg].ds_len = sgsize;
first = 0;
} else {
if (curaddr == lastaddr &&
(segs[seg].ds_len + sgsize) <= dmat->maxsegsz &&
(dmat->boundary == 0 ||
(segs[seg].ds_addr & bmask) == (curaddr & bmask)))
segs[seg].ds_len += sgsize;
else {
if (++seg >= dmat->nsegments)
break;
segs[seg].ds_addr = curaddr;
segs[seg].ds_len = sgsize;
}
}
lastaddr = curaddr + sgsize;
vaddr += sgsize;
buflen -= sgsize;
}
*segp = seg;
*lastaddrp = lastaddr;
/*
* Did we fit?
*/
return (buflen != 0 ? EFBIG : 0); /* XXX better return value here? */
}
/*
* Like bus_dmamap_load(), but for mbufs.
*/
int
bus_dmamap_load_mbuf(bus_dma_tag_t dmat, bus_dmamap_t map, struct mbuf *m0,
bus_dmamap_callback2_t *callback, void *callback_arg,
int flags)
{
#ifdef __GNUC__
bus_dma_segment_t dm_segments[dmat->nsegments];
#else
bus_dma_segment_t dm_segments[BUS_DMAMAP_NSEGS];
#endif
int nsegs = 0, error = 0;
KASSERT(m0->m_flags & M_PKTHDR,
("bus_dmamap_load_mbuf: no packet header"));
if (m0->m_pkthdr.len <= dmat->maxsize) {
int first = 1;
vm_offset_t lastaddr = 0;
struct mbuf *m;
for (m = m0; m != NULL && error == 0; m = m->m_next) {
error = bus_dmamap_load_buffer(dmat, dm_segments,
m->m_data, m->m_len, NULL, flags,
&lastaddr, &nsegs, first);
first = 0;
}
} else {
error = EINVAL;
}
if (error) {
/*
* force "no valid mappings" on error in callback.
*/
(*callback)(callback_arg, dm_segments, 0, 0, error);
} else {
(*callback)(callback_arg, dm_segments, nsegs+1,
m0->m_pkthdr.len, error);
}
return (error);
}
/*
* Like bus_dmamap_load(), but for uios.
*/
int
bus_dmamap_load_uio(bus_dma_tag_t dmat, bus_dmamap_t map, struct uio *uio,
bus_dmamap_callback2_t *callback, void *callback_arg,
int flags)
{
vm_offset_t lastaddr;
#ifdef __GNUC__
bus_dma_segment_t dm_segments[dmat->nsegments];
#else
bus_dma_segment_t dm_segments[BUS_DMAMAP_NSEGS];
#endif
int nsegs, i, error, first;
bus_size_t resid;
struct iovec *iov;
struct proc *td = NULL;
resid = uio->uio_resid;
iov = uio->uio_iov;
if (uio->uio_segflg == UIO_USERSPACE) {
td = uio->uio_td;
KASSERT(td != NULL,
("bus_dmamap_load_uio: USERSPACE but no proc"));
}
first = 1;
nsegs = error = 0;
for (i = 0; i < uio->uio_iovcnt && resid != 0 && !error; i++) {
/*
* Now at the first iovec to load. Load each iovec
* until we have exhausted the residual count.
*/
bus_size_t minlen =
resid < iov[i].iov_len ? resid : iov[i].iov_len;
caddr_t addr = (caddr_t) iov[i].iov_base;
error = bus_dmamap_load_buffer(dmat, dm_segments, addr,
minlen, td, flags, &lastaddr, &nsegs, first);
first = 0;
resid -= minlen;
}
if (error) {
/*
* force "no valid mappings" on error in callback.
*/
(*callback)(callback_arg, dm_segments, 0, 0, error);
} else {
(*callback)(callback_arg, dm_segments, nsegs+1,
uio->uio_resid, error);
}
return (error);
}
/*
* Release the mapping held by map. A no-op on PowerPC.
*/
void
bus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map)
@ -318,8 +513,3 @@ bus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map)
void
bus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op)
{}