/*------------------------------------------------------------------------* * usb_bdma_pre_sync * * This function handles DMA synchronisation that must be done before * an USB transfer is started. *------------------------------------------------------------------------*/ void usb_bdma_pre_sync(struct usb_xfer *xfer) { struct usb_page_cache *pc; usb_frcount_t nframes; if (xfer->flags_int.isochronous_xfr) { /* only one frame buffer */ nframes = 1; } else { /* can be multiple frame buffers */ nframes = xfer->nframes; } pc = xfer->frbuffers; while (nframes--) { if (pc->isread) { usb_pc_cpu_invalidate(pc); } else { usb_pc_cpu_flush(pc); } pc++; } }
/*------------------------------------------------------------------------* * usb_pc_alloc_mem - allocate DMA'able memory * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ uint8_t usb_pc_alloc_mem(struct usb_page_cache *pc, struct usb_page *pg, usb_size_t size, usb_size_t align) { void *ptr; uint32_t rem; /* allocate zeroed memory */ if (align != 1) { ptr = usb_malloc(size + align); if (ptr == NULL) goto error; rem = (-((uintptr_t)ptr)) & (align - 1); } else { ptr = usb_malloc(size); if (ptr == NULL) goto error; rem = 0; } /* setup page cache */ pc->buffer = ((uint8_t *)ptr) + rem; pc->page_start = pg; pc->page_offset_buf = 0; pc->page_offset_end = size; pc->map = NULL; pc->tag = ptr; pc->ismultiseg = (align == 1); /* compute physical address */ usb_pc_common_mem_cb(pc, ptr, size); usb_pc_cpu_flush(pc); return (0); error: /* reset most of the page cache */ pc->buffer = NULL; pc->page_start = NULL; pc->page_offset_buf = 0; pc->page_offset_end = 0; pc->map = NULL; pc->tag = NULL; return (1); }
/*------------------------------------------------------------------------* * usb_pc_alloc_mem - allocate DMA'able memory * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ uint8_t usb_pc_alloc_mem(struct usb_page_cache *pc, struct usb_page *pg, usb_size_t size, usb_size_t align) { struct usb_dma_parent_tag *uptag; struct usb_dma_tag *utag; bus_dmamap_t map; void *ptr; int err; uptag = pc->tag_parent; if (align != 1) { /* * The alignment must be greater or equal to the * "size" else the object can be split between two * memory pages and we get a problem! */ while (align < size) { align *= 2; if (align == 0) { goto error; } } #if 1 /* * XXX BUS-DMA workaround - FIXME later: * * We assume that that the aligment at this point of * the code is greater than or equal to the size and * less than two times the size, so that if we double * the size, the size will be greater than the * alignment. * * The bus-dma system has a check for "alignment" * being less than "size". If that check fails we end * up using contigmalloc which is page based even for * small allocations. Try to avoid that to save * memory, hence we sometimes to a large number of * small allocations! */ if (size <= (USB_PAGE_SIZE / 2)) { size *= 2; } #endif } /* get the correct DMA tag */ utag = usb_dma_tag_find(uptag, size, align); if (utag == NULL) { goto error; } /* allocate memory */ if (bus_dmamem_alloc( utag->tag, &ptr, (BUS_DMA_WAITOK | BUS_DMA_COHERENT), &map)) { goto error; } /* setup page cache */ pc->buffer = ptr; pc->page_start = pg; pc->page_offset_buf = 0; pc->page_offset_end = size; pc->map = map; pc->tag = utag->tag; pc->ismultiseg = (align == 1); mtx_lock(uptag->mtx); /* load memory into DMA */ err = bus_dmamap_load( utag->tag, map, ptr, size, &usb_pc_alloc_mem_cb, pc, (BUS_DMA_WAITOK | BUS_DMA_COHERENT)); if (err == EINPROGRESS) { cv_wait(uptag->cv, uptag->mtx); err = 0; } mtx_unlock(uptag->mtx); if (err || uptag->dma_error) { bus_dmamem_free(utag->tag, ptr, map); goto error; } memset(ptr, 0, size); usb_pc_cpu_flush(pc); return (0); error: /* reset most of the page cache */ pc->buffer = NULL; pc->page_start = NULL; pc->page_offset_buf = 0; pc->page_offset_end = 0; pc->map = NULL; pc->tag = NULL; return (1); }
static void usb_bus_mem_flush_all_cb(struct usb_bus *bus, struct usb_page_cache *pc, struct usb_page *pg, usb_size_t size, usb_size_t align) { usb_pc_cpu_flush(pc); }