Subject: Re: bus_dmamem question
To: None <tech-kern@netbsd.org>
From: Hans Petter Selasky <hselasky@c2i.net>
List: tech-kern
Date: 02/26/2006 21:58:21
Hi,

I managed to get it working, after some hard debugging.

On Sunday 19 February 2006 19:32, Hans Petter Selasky wrote:
> Hi,
>
> Can someone here help get the following BUS-DMA code right? Currently
> "usb_alloc_mem()" works, but "usb_free_mem()" will cause a "uvm_fault()":
>
> uvm_fault(0xc07b08a0, 0xcb6bd000, 0, 1) -> 0xe
> kernel: page fault trap, code=0
> Stopped in pid 1257.1 (modload) at      0xcbbf146c:     movl   
> 0(%ebx),%eax
>
>
> struct usb_dma {
>     bus_dma_tag_t     tag;
>     bus_dmamap_t      map;
>     bus_dma_segment_t seg;
>     int               seg_count;
> } __packed;
>
> void *
> usb_alloc_mem(bus_dma_tag_t dma_tag, u_int32_t size, u_int8_t align_power)
> {
>     caddr_t ptr = NULL;
>     struct usb_dma temp;
>
>     bzero(&temp, sizeof(temp));
>
>     temp.tag = dma_tag;
>     temp.seg_count = 1;
>
>     size += sizeof(struct usb_dma);
>
>     if(bus_dmamem_alloc(temp.tag, size, (1 << align_power), 0,
>                         &temp.seg, 1,
>                         &temp.seg_count, BUS_DMA_NOWAIT))
>     {
>         goto done_4;
>     }
>
>     if(bus_dmamem_map(temp.tag, &temp.seg, temp.seg_count, size,
>                       &ptr, BUS_DMA_NOWAIT|BUS_DMA_COHERENT))
>     {
>         goto done_3;
>     }
>
>     if(bus_dmamap_create(temp.tag, size, 1, size,
>                          0, BUS_DMA_NOWAIT, &temp.map))
>     {
>         goto done_2;
>     }
>
>     if(bus_dmamap_load(temp.tag, temp.map, ptr, size, NULL,
>                        BUS_DMA_NOWAIT))
>     {
>         goto done_1;
>     }
>
>     size -= sizeof(temp);
>
>     bcopy(&temp, ((u_int8_t *)ptr) + size, sizeof(temp));
>
>     return ptr;
>
>  done_1:
>     bus_dmamap_destroy(temp.tag, temp.map);
>
>  done_2:
>     bus_dmamem_unmap(temp.tag, ptr, size);
>
>  done_3:
>     bus_dmamem_free(temp.tag, &temp.seg, temp.seg_count);
>
>  done_4:
>     return NULL;
> }
>
> u_int32_t
> usb_vtophys(void *ptr, u_int32_t size)
> {
>     struct usb_dma *arg = (void *)(((u_int8_t *)ptr) + size);
>     return arg->seg.ds_addr;
> }

Should be:

u_int32_t
usb_vtophys(void *ptr, u_int32_t size)
{
    struct usb_dma *arg = (void *)(((u_int8_t *)ptr) + size);
#if 1
    return arg->map->dm_segs[0].ds_addr;
#else
    return arg->seg.ds_addr;
#endif
}

> void
> usb_free_mem(void *ptr, u_int32_t size)
> {
>     struct usb_dma *arg = (void *)(((u_int8_t *)ptr) + size);
>
>     bus_dmamap_unload(arg->tag, arg->map);
>     bus_dmamap_destroy(arg->tag, arg->map);
>     bus_dmamem_unmap(arg->tag, ptr, size);
>     bus_dmamem_free(arg->tag, &arg->seg, arg->seg_count);
>
>     return;
> }

Should be:

void
usb_free_mem(void *ptr, u_int32_t size)
{
    struct usb_dma *arg = (void *)(((u_int8_t *)ptr) + size);
    struct usb_dma temp = *arg; /* make a copy ! */

    bus_dmamap_unload(temp.tag, temp.map);
    bus_dmamap_destroy(temp.tag, temp.map);
    bus_dmamem_unmap(temp.tag, ptr, size);
    bus_dmamem_free(temp.tag, &temp.seg, temp.seg_count);
    return;
}

--HPS