I'm working on a device driver that needs to transfer large amounts of
user data to a device. Kiobufs seem perfect for the problem of dma'ing
out of user space, but since my device's DMA engine doesn't support
scatter-gather, I need it to be mapped into contiguous DVMA space.

The attached patch is my first attempt at providing this -- I've made
sure it compiles, but I've not yet been able to try it out -- I have
other problems with DVMA as of yet.

I'm sure the interface needs to be more consistent with
sbus_alloc_consitent(...) and its ilk, but I wanted to get this out and
see if I'm heading in the right direction. It's against 2.3.99-pre2, but
I expect it'll apply cleanly to most recent kernels. Any comments would
be appreciated.

The other problem I have is that I could need to map up to ~2MB of DVMA
space for each transfer. It seems there is less than 1MB available in
the DVMA resource (for consitent, anyway). Should I be trying to allocte
from a different resource, or is there any way we can make that larger?
Or am I all wet in my understanding of how this works?

Thanks for the look-see,
Dave Dillow
[EMAIL PROTECTED]
[EMAIL PROTECTED]
--- linux/include/asm-sparc/sbus.h.orig Sat Apr  1 04:06:31 2000
+++ linux/include/asm-sparc/sbus.h      Sat Apr  1 04:47:06 2000
@@ -105,6 +105,10 @@
 extern void *sbus_alloc_consistent(struct sbus_dev *, long, u32 *dma_addrp);
 extern void sbus_free_consistent(struct sbus_dev *, long, void *, u32);
 
+struct kiobuf;
+extern u32 sbus_map_kiobuf(struct sbus_dev *, struct kiobuf *, const char *);
+extern void sbus_unmap_kiobuf(struct sbus_dev *, struct kiobuf *, u32);
+
 #define SBUS_DMA_BIDIRECTIONAL 0
 #define SBUS_DMA_TODEVICE      1
 #define SBUS_DMA_FROMDEVICE    2
--- ./linux/arch/sparc/kernel/ioport.c.orig     Sat Apr  1 03:02:27 2000
+++ ./linux/arch/sparc/kernel/ioport.c  Sat Apr  1 04:03:48 2000
@@ -35,6 +35,7 @@
 #include <linux/malloc.h>
 #include <linux/pci.h>         /* struct pci_dev */
 #include <linux/proc_fs.h>
+#include <linux/iobuf.h>
 
 #include <asm/io.h>
 #include <asm/vaddrs.h>
@@ -459,6 +460,92 @@
 void sbus_dma_sync_sg(struct sbus_dev *sdev, struct scatterlist *sg, int n, int 
direction)
 {
        printk("sbus_dma_sync_sg: not implemented yet\n");
+}
+
+/* Map a (mapped) kiobuf into DVMA space */
+u32 sbus_map_kiobuf(struct sbus_dev *sdev, struct kiobuf *iobuf,
+                       const char *name)
+{
+       int pageind;
+       u32 dma_addr;
+       struct page *map;
+       struct resource *res;
+       unsigned long len_total = (iobuf->length + PAGE_SIZE-1) & PAGE_MASK;
+       
+       /* make sure iobuf is mapped */
+       if (!iobuf->nr_pages)
+           return 0;
+
+       /* make sure everything in the maplist is really there */
+       for (pageind = 0; pageind < iobuf->nr_pages; pageind++)
+           if (!iobuf->maplist[pageind])
+               return 0;
+
+       if ((res = kmalloc(sizeof(struct resource), GFP_KERNEL)) == NULL) {
+           printk("sbus_map_kiobuf: no core\n");
+           return 0;
+       }
+       memset((char *)res, 0, sizeof(struct resource));
+       res->name = name;
+
+       if (allocate_resource(&_sparc_dvma, res, len_total,
+           _sparc_dvma.start, _sparc_dvma.end, PAGE_SIZE, NULL, NULL) != 0) {
+               printk("sbus_map_single: cannot occupy 0x%lx\n", len_total);
+               kfree(res);
+               return 0;
+       }
+
+       /* walk the kiobuf and map it's pages into our dvma space */
+       dma_addr = res->start;
+       for (pageind = 0; pageind < iobuf->nr_pages; pageind++) {
+           map = iobuf->maplist[pageind];
+           mmu_map_dma_area(page_address(map), dma_addr, PAGE_SIZE);
+           mmu_flush_dma_area(page_address(map), PAGE_SIZE);
+           dma_addr += PAGE_SIZE;
+       }
+
+       return res->start;
+}
+
+void sbus_unmap_kiobuf(struct sbus_dev *sdev, struct kiobuf *iobuf, u32 da)
+{
+       int pageind;
+       struct page *map;
+       struct resource *res;
+       unsigned long len;
+
+       if (!iobuf->nr_pages) {
+           printk("sbus_unmap_kiobuf: passed unmapped kiobuf\n");
+           return;
+       }
+
+       for (pageind = 0; pageind < iobuf->nr_pages; pageind++)
+           if (!iobuf->maplist[pageind]) {
+               printk("sbus_unmap_kiobuf: missing page!\n");
+               return;
+       }
+
+       if ((res = _sparc_find_resource(&_sparc_dvma, da)) == NULL) {
+           printk("sbus_unmap_kiobuf: cannot find 0x%08x\n", (unsigned) da);
+           return;
+       }
+
+       len = (iobuf->length + PAGE_SIZE-1) & PAGE_MASK;
+       if ((res->end-res->start)+1 != len) {
+           printk("sbus_unmap_kiobuf: region 0x%lx asked 0x%lx\n",
+               (long)((res->end-res->start)+1), len);
+           return;
+       }
+
+       for(pageind = 0; pageind < iobuf->nr_pages; pageind++) {
+               map = iobuf->maplist[pageind];
+               mmu_inval_dma_area(page_address(map), PAGE_SIZE);
+               mmu_unmap_dma_area(da, PAGE_SIZE);
+               da += PAGE_SIZE;
+       }
+
+       release_resource(res);
+       kfree(res);
 }
 #endif /* CONFIG_SBUS */
 
--- ./linux/arch/sparc/kernel/sparc_ksyms.c.orig        Sat Apr  1 04:05:47 2000
+++ ./linux/arch/sparc/kernel/sparc_ksyms.c     Sat Apr  1 04:05:28 2000
@@ -185,6 +185,8 @@
 EXPORT_SYMBOL(sbus_unmap_sg);
 EXPORT_SYMBOL(sbus_dma_sync_single);
 EXPORT_SYMBOL(sbus_dma_sync_sg);
+EXPORT_SYMBOL(sbus_map_kiobuf);
+EXPORT_SYMBOL(sbus_unmap_kiobuf);
 EXPORT_SYMBOL(sbus_iounmap);
 EXPORT_SYMBOL(sbus_ioremap);
 #endif

Reply via email to