From owner-p4-projects@FreeBSD.ORG Fri Jan 11 00:45:56 2008 Return-Path: Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id 1FB1B16A4CA; Fri, 11 Jan 2008 00:45:56 +0000 (UTC) Delivered-To: perforce@FreeBSD.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 890FF16A46D for ; Fri, 11 Jan 2008 00:45:55 +0000 (UTC) (envelope-from hselasky@FreeBSD.org) Received: from repoman.freebsd.org (repoman.freebsd.org [IPv6:2001:4f8:fff6::29]) by mx1.freebsd.org (Postfix) with ESMTP id 9B03413C46B for ; Fri, 11 Jan 2008 00:45:55 +0000 (UTC) (envelope-from hselasky@FreeBSD.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.14.1/8.14.1) with ESMTP id m0B0jtDa049298 for ; Fri, 11 Jan 2008 00:45:55 GMT (envelope-from hselasky@FreeBSD.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.14.1/8.14.1/Submit) id m0B0jt5Y049295 for perforce@freebsd.org; Fri, 11 Jan 2008 00:45:55 GMT (envelope-from hselasky@FreeBSD.org) Date: Fri, 11 Jan 2008 00:45:55 GMT Message-Id: <200801110045.m0B0jt5Y049295@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to hselasky@FreeBSD.org using -f From: Hans Petter Selasky To: Perforce Change Reviews Cc: Subject: PERFORCE change 133006 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 11 Jan 2008 00:45:56 -0000 http://perforce.freebsd.org/chv.cgi?CH=133006 Change 133006 by hselasky@hselasky_laptop001 on 2008/01/11 00:45:51 This commit only affects code that compiles on NetBSD platforms. Update BUS-DMA code for NetBSD. Affected files ... .. //depot/projects/usb/src/sys/dev/usb/usb_subr.c#85 edit Differences ... ==== //depot/projects/usb/src/sys/dev/usb/usb_subr.c#85 (text+ko) ==== @@ -2522,97 +2522,260 @@ #ifdef __NetBSD__ -bus_dma_tag_t -usbd_dma_tag_alloc(bus_dma_tag_t parent, uint32_t seg_size, - uint32_t alignment, uint32_t max_size, uint8_t single_seg) +/*------------------------------------------------------------------------* + * usbd_dma_tag_create - allocate a DMA tag + * + * NOTE: If the "align" parameter has a value of 1 the DMA-tag will + * allow multi-segment mappings. Else all mappings are single-segment. + *------------------------------------------------------------------------*/ +void +usbd_dma_tag_create(bus_dma_tag_t tag_parent, struct usbd_dma_tag *udt, + uint32_t size, uint32_t align) { - /* FreeBSD specific */ - return (parent); + uint32_t nseg; + + if (align == 1) { + nseg = (2 + (size / USB_PAGE_SIZE)); + } else { + nseg = 1; + } + + udt->p_seg = malloc(nseg * sizeof(*(udt->p_seg)), + M_USB, M_WAITOK | M_ZERO); + + if (udt->p_seg == NULL) { + return; + } + udt->tag = tag_parent; + udt->n_seg = nseg; + return; } +/*------------------------------------------------------------------------* + * usbd_dma_tag_free - free a DMA tag + *------------------------------------------------------------------------*/ void -usbd_dma_tag_free(bus_dma_tag_t tag) +usbd_dma_tag_destroy(struct usbd_dma_tag *udt) +{ + free(udt->p_seg, M_USB); + return; +} + +/*------------------------------------------------------------------------* + * usbd_pc_alloc_mem_cb + *------------------------------------------------------------------------*/ +static void +usbd_pc_alloc_mem_cb(struct usbd_page_cache *pc, bus_dma_segment_t *segs, + int nseg, int error) { - /* FreeBSD specific */ + struct usbd_xfer *xfer; + struct usbd_page *pg; + uint32_t rem; + uint8_t owned; + + xfer = pc->xfer; + + /* + * XXX There is sometimes recursive locking here. + * XXX We should try to find a better solution. + * XXX Until further the "owned" variable does + * XXX the trick. + */ + + if (error) { + if (xfer) { + owned = mtx_owned(xfer->priv_mtx); + if (!owned) + mtx_lock(xfer->priv_mtx); + xfer->usb_root->dma_error = 1; + usbd_bdma_done_event(xfer->usb_root); + if (!owned) + mtx_unlock(xfer->priv_mtx); + } + return; + } + pg = pc->page_start; + pg->physaddr = segs->ds_addr & ~(USB_PAGE_SIZE - 1); + rem = segs->ds_addr & (USB_PAGE_SIZE - 1); + pc->page_offset_buf = rem; + pc->page_offset_end += rem; + nseg--; + + while (nseg > 0) { + nseg--; + segs++; + pg++; + pg->physaddr = segs->ds_addr & ~(USB_PAGE_SIZE - 1); + } + + if (xfer) { + owned = mtx_owned(xfer->priv_mtx); + if (!owned) + mtx_lock(xfer->priv_mtx); + usbd_bdma_done_event(xfer->usb_root); + if (!owned) + mtx_unlock(xfer->priv_mtx); + } return; } -void * -usbd_mem_alloc_sub(bus_dma_tag_t tag, struct usbd_page *page, - uint32_t size, uint32_t alignment) +/*------------------------------------------------------------------------* + * usbd_pc_alloc_mem - allocate DMA'able memory + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +uint8_t +usbd_pc_alloc_mem(bus_dma_tag_t parent_tag, struct usbd_dma_tag *utag, + struct usbd_page_cache *pc, struct usbd_page *pg, uint32_t size, + uint32_t align, uint8_t utag_max) { caddr_t ptr = NULL; + bus_dma_tag_t tag; + bus_dmamap_t map; + int seg_count; - page->tag = tag; - page->seg_count = 1; + 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 done_5; + } + } + } + /* get the correct DMA tag */ + utag = usbd_dma_tag_setup(parent_tag, utag, size, align, utag_max); + if (utag == NULL) { + goto done_5; + } + /* get the DMA tag */ + tag = utag->tag; - if (bus_dmamem_alloc(page->tag, size, alignment, 0, - &page->seg, 1, - &page->seg_count, BUS_DMA_WAITOK)) { + if (bus_dmamem_alloc(tag, size, align, 0, utag->p_seg, + utag->n_seg, &seg_count, BUS_DMA_WAITOK)) { goto done_4; } - if (bus_dmamem_map(page->tag, &page->seg, page->seg_count, size, + if (bus_dmamem_map(tag, utag->p_seg, seg_count, size, &ptr, BUS_DMA_WAITOK | BUS_DMA_COHERENT)) { goto done_3; } - if (bus_dmamap_create(page->tag, size, 1, size, - 0, BUS_DMA_WAITOK, &page->map)) { + if (bus_dmamap_create(tag, size, utag->n_seg, USB_PAGE_SIZE, + 0, BUS_DMA_WAITOK, &map)) { goto done_2; } - if (bus_dmamap_load(page->tag, page->map, ptr, size, NULL, + if (bus_dmamap_load(tag, map, ptr, size, NULL, BUS_DMA_WAITOK)) { goto done_1; } - page->physaddr = page->map->dm_segs[0].ds_addr; - page->buffer = ptr; - page->length = size; + pc->p_seg = malloc(seg_count * sizeof(*(pc->p_seg)), + M_USB, M_WAITOK | M_ZERO); + if (pc->p_seg == NULL) { + goto done_0; + } + /* store number if actual segments used */ + pc->n_seg = seg_count; + + /* make a copy of the segments */ + bcopy(utag->p_seg, pc->p_seg, + seg_count * sizeof(*(pc->p_seg))); + + /* 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 = tag; + + usbd_pc_alloc_mem_cb(pc, utag->p_seg, seg_count, 0); - usbd_page_cpu_invalidate(page); bzero(ptr, size); - usbd_page_cpu_flush(page); + + usbd_pc_cpu_flush(pc); -#ifdef USB_DEBUG - if (usbdebug > 14) { - printf("%s: %p, %d bytes, phys=%p\n", - __FUNCTION__, ptr, size, - ((char *)0) + page->physaddr); - } -#endif - return (ptr); + return (0); +done_0: + bus_dmamap_unload(tag, map); done_1: - bus_dmamap_destroy(page->tag, page->map); - + bus_dmamap_destroy(tag, map); done_2: - bus_dmamem_unmap(page->tag, ptr, size); - + bus_dmamem_unmap(tag, ptr, size); done_3: - bus_dmamem_free(page->tag, &page->seg, page->seg_count); + bus_dmamem_free(tag, utag->p_seg, seg_count); +done_4: + /* utag is destroyed later */ +done_5: + /* 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; + pc->n_seg = 0; + pc->p_seg = NULL; + return (1); +} -done_4: - return (NULL); +/*------------------------------------------------------------------------* + * usbd_pc_free_mem - free DMA memory + * + * This function is NULL safe. + *------------------------------------------------------------------------*/ +void +usbd_pc_free_mem(struct usbd_page_cache *pc) +{ + if (pc && pc->buffer) { + bus_dmamap_unload(pc->tag, pc->map); + bus_dmamap_destroy(pc->tag, pc->map); + bus_dmamem_unmap(pc->tag, pc->buffer, + pc->page_offset_end - pc->page_offset_buf); + bus_dmamem_free(pc->tag, pc->p_seg, pc->n_seg); + free(pc->p_seg, M_USB); + pc->buffer = NULL; + } + return; } +/*------------------------------------------------------------------------* + * usbd_pc_load_mem - load virtual memory into DMA + *------------------------------------------------------------------------*/ void -usbd_mem_free_sub(struct usbd_page *page) +usbd_pc_load_mem(struct usbd_page_cache *pc, uint32_t size) { - /* - * NOTE: make a copy of "tag", "map", and "buffer" in case "page" is - * part of the allocated memory: - */ - struct usbd_page temp = *page; + int error; + + /* sanity check */ + if (pc->xfer == NULL) { + panic("This page cache is not loadable!\n"); + return; + } + /* setup page cache */ + pc->page_offset_buf = 0; + pc->page_offset_end = size; + + if (size > 0) { + + pc->xfer->usb_root->dma_refcount++; - bus_dmamap_unload(temp.tag, temp.map); - bus_dmamap_destroy(temp.tag, temp.map); - bus_dmamem_unmap(temp.tag, temp.buffer, temp.length); - bus_dmamem_free(temp.tag, &temp.seg, temp.seg_count); + /* try to load memory into DMA */ + if (bus_dmamap_load(pc->tag, pc->map, pc->buffer, + size, NULL, BUS_DMA_NOWAIT)) { + error = ENOMEM; + } else { + error = 0; + } -#ifdef USB_DEBUG - if (usbdebug > 14) { - printf("%s: %p\n", - __FUNCTION__, temp.buffer); + usbd_pc_alloc_mem_cb(pc, pc->map->dm_segs, + pc->map->dm_nsegs, error); } -#endif return; } @@ -2641,11 +2804,70 @@ len = pc->page_offset_end - pc->page_offset_buf; - bus_dmamap_sync(page->tag, page->map, 0, len, + bus_dmamap_sync(pc->tag, pc->map, 0, len, BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); return; } +/*------------------------------------------------------------------------* + * usbd_pc_dmamap_create + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +uint8_t +usbd_pc_dmamap_create(struct usbd_page_cache *pc, uint32_t size) +{ + struct usbd_memory_info *info; + struct usbd_dma_tag *utag; + bus_dma_tag_t tag; + + /* sanity check */ + if (pc->xfer == NULL) { + goto error; + } + info = pc->xfer->usb_root; + tag = pc->xfer->udev->bus->dma_tag_parent; + + utag = usbd_dma_tag_setup(tag, info->dma_tag_p, + size, 1, info->dma_tag_max); + if (utag == NULL) { + goto error; + } + if (bus_dmamap_create(utag->tag, size, utag->n_seg, + USB_PAGE_SIZE, 0, BUS_DMA_WAITOK, &(pc->map))) { + goto error; + } + pc->tag = utag->tag; + pc->p_seg = utag->p_seg; + pc->n_seg = utag->n_seg; + return 0; /* success */ + +error: + pc->map = NULL; + pc->tag = NULL; + pc->p_seg = NULL; + pc->n_seg = 0; + return 1; /* failure */ +} + +/*------------------------------------------------------------------------* + * usbd_pc_dmamap_destroy + * + * This function is NULL safe. + *------------------------------------------------------------------------*/ +void +usbd_pc_dmamap_destroy(struct usbd_page_cache *pc) +{ + if (pc && pc->tag) { + bus_dmamap_destroy(pc->tag, pc->map); + pc->tag = NULL; + pc->map = NULL; + } + return; +} + #endif /*------------------------------------------------------------------------*