PERFORCE change 133006 for review

Hans Petter Selasky hselasky at FreeBSD.org
Thu Jan 10 16:45:56 PST 2008


http://perforce.freebsd.org/chv.cgi?CH=133006

Change 133006 by hselasky at 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
 
 /*------------------------------------------------------------------------*


More information about the p4-projects mailing list