[Linux1394-cvslog] rev 560 - branches/rawiso
Brought to you by:
aeb,
bencollins
From: SVN U. <dm...@li...> - 2002-08-28 07:27:53
|
Author: dmaas Date: 2002-08-28 12:20:08 -0400 (Wed, 28 Aug 2002) New Revision: 560 Added: branches/rawiso/dma.c branches/rawiso/dma.h Modified: branches/rawiso/Makefile branches/rawiso/Makefile.in Log: added dma.c and dma.h (DMA region bookkeeping routines) Modified: branches/rawiso/Makefile.in ============================================================================== --- branches/rawiso/Makefile.in (original) +++ branches/rawiso/Makefile.in 2002-08-28 12:20:09.000000000 -0400 @@ -1,10 +1,10 @@ # # Makefile for the Linux IEEE 1394 implementation -expsyms(ieee1394_core.o ohci1394.o cmp.o) +expsyms(ieee1394_core.o ohci1394.o cmp.o dma.o) objlink(ieee1394.o ieee1394_core.o ieee1394_transactions.o hosts.o highlevel.o - csr.o nodemgr.o) + csr.o nodemgr.o dma.o) extra_cflags(sbp2.o $(src_includelist /drivers/scsi)) Modified: branches/rawiso/Makefile ============================================================================== --- branches/rawiso/Makefile (original) +++ branches/rawiso/Makefile 2002-08-28 12:20:09.000000000 -0400 @@ -4,11 +4,11 @@ O_TARGET := ieee1394drv.o -export-objs := ieee1394_core.o ohci1394.o cmp.o +export-objs := ieee1394_core.o ohci1394.o cmp.o dma.o list-multi := ieee1394.o ieee1394-objs := ieee1394_core.o ieee1394_transactions.o hosts.o \ - highlevel.o csr.o nodemgr.o + highlevel.o csr.o nodemgr.o dma.o obj-$(CONFIG_IEEE1394) += ieee1394.o obj-$(CONFIG_IEEE1394_PCILYNX) += pcilynx.o Added: branches/rawiso/dma.c ============================================================================== --- branches/rawiso/dma.c (original) +++ branches/rawiso/dma.c 2002-08-28 12:20:09.000000000 -0400 @@ -0,0 +1,203 @@ +/* + * DMA region bookkeeping routines + * + * Copyright (C) 2002 Maas Digital LLC + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. + */ + +#include <linux/module.h> +#include "dma.h" + +void dma_prog_region_init(struct dma_prog_region *prog) +{ + prog->kvirt = NULL; + prog->dev = NULL; + prog->n_pages = 0; + prog->bus_addr = 0; +} + +int dma_prog_region_alloc(struct dma_prog_region *prog, unsigned long n_bytes, struct pci_dev *dev) +{ + /* round up to page size */ + if(n_bytes % PAGE_SIZE) + n_bytes += PAGE_SIZE - (n_bytes & PAGE_SIZE); + + prog->n_pages = n_bytes / PAGE_SIZE; + + prog->kvirt = pci_alloc_consistent(dev, prog->n_pages * PAGE_SIZE, &prog->bus_addr); + if(!prog->kvirt) { + printk(KERN_ERR "dma_prog_region_alloc: pci_alloc_consistent() failed\n"); + dma_prog_region_free(prog); + return -ENOMEM; + } + + prog->dev = dev; + + return 0; +} + +void dma_prog_region_free(struct dma_prog_region *prog) +{ + if(prog->kvirt) { + pci_free_consistent(prog->dev, prog->n_pages * PAGE_SIZE, prog->kvirt, prog->bus_addr); + } + + prog->kvirt = NULL; + prog->dev = NULL; + prog->n_pages = 0; + prog->bus_addr = 0; +} + + + + +void dma_region_init(struct dma_region *dma) +{ + dma->kvirt = NULL; + dma->dev = NULL; + dma->n_pages = 0; + dma->n_dma_pages = 0; + dma->sglist = NULL; +} + +int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes, struct pci_dev *dev, int direction) +{ + unsigned int i, n_pages; + + /* round up to page size */ + if(n_bytes % PAGE_SIZE) + n_bytes += PAGE_SIZE - (n_bytes & PAGE_SIZE); + + n_pages = n_bytes / PAGE_SIZE; + + dma->kvirt = vmalloc_32(n_pages * PAGE_SIZE); + if(!dma->kvirt) { + printk(KERN_ERR "dma_region_alloc: vmalloc_32() failed\n"); + goto err; + } + + dma->n_pages = n_pages; + + /* Clear the ram out, no junk to the user */ + memset(dma->kvirt, 0, n_pages * PAGE_SIZE); + + dma->sglist = kmalloc(dma->n_pages * sizeof(struct scatterlist), GFP_KERNEL); + if(!dma->sglist) { + printk(KERN_ERR "dma_region_alloc: kmalloc(sglist) failed\n"); + goto err; + } + + /* just to be safe - this will become unnecessary once sglist->address goes away */ + memset(dma->sglist, 0, dma->n_pages * sizeof(struct scatterlist)); + + for(i = 0; i < dma->n_pages; i++) { + unsigned long va = (unsigned long) dma->kvirt + i * PAGE_SIZE; + + dma->sglist[i].page = vmalloc_to_page((void *)va); + dma->sglist[i].length = PAGE_SIZE; + } + + dma->n_dma_pages = pci_map_sg(dev, &dma->sglist[0], dma->n_pages, direction); + + if(dma->n_dma_pages == 0) { + printk(KERN_ERR "dma_region_alloc: pci_map_sg() failed\n"); + goto err; + } + + dma->dev = dev; + dma->direction = direction; + + return 0; + +err: + dma_region_free(dma); + return -ENOMEM; +} + +void dma_region_free(struct dma_region *dma) +{ + if(dma->n_dma_pages) { + pci_unmap_sg(dma->dev, dma->sglist, dma->n_pages, dma->direction); + dma->n_dma_pages = 0; + dma->dev = NULL; + } + + if(dma->sglist) { + kfree(dma->sglist); + dma->sglist = NULL; + } + + if(dma->kvirt) { + vfree(dma->kvirt); + dma->kvirt = NULL; + dma->n_pages = 0; + } +} + +void dma_region_sync(struct dma_region *dma) +{ + pci_dma_sync_sg(dma->dev, &dma->sglist[0], dma->n_dma_pages, dma->direction); +} + +static struct page* +dma_region_pagefault(struct vm_area_struct *area, unsigned long address, int write_access) +{ + unsigned long offset; + unsigned long kernel_virt_addr; + struct page *ret = NOPAGE_SIGBUS; + + struct dma_region *dma = (struct dma_region*) area->vm_private_data; + + if(!dma->kvirt) + goto out; + + if( (address < (unsigned long) area->vm_start) || + (address > (unsigned long) area->vm_start + (PAGE_SIZE * dma->n_pages)) ) + goto out; + + offset = address - area->vm_start; + kernel_virt_addr = (unsigned long) dma->kvirt + offset; + ret = vmalloc_to_page((void*) kernel_virt_addr); + get_page(ret); +out: + return ret; +} + +static struct vm_operations_struct dma_region_vm_ops = { + nopage: dma_region_pagefault, +}; + +int dma_region_mmap(struct dma_region *dma, struct file *file, struct vm_area_struct *vma) +{ + unsigned long size; + + if(!dma->kvirt) + return -EINVAL; + + /* must be page-aligned */ + if(vma->vm_pgoff != 0) + return -EINVAL; + + /* check the length */ + size = vma->vm_end - vma->vm_start; + if(size > (PAGE_SIZE * dma->n_pages)) + return -EINVAL; + + vma->vm_ops = &dma_region_vm_ops; + vma->vm_private_data = dma; + vma->vm_file = file; + vma->vm_flags |= VM_RESERVED; + + return 0; +} + +EXPORT_SYMBOL(dma_prog_region_init); +EXPORT_SYMBOL(dma_prog_region_alloc); +EXPORT_SYMBOL(dma_prog_region_free); +EXPORT_SYMBOL(dma_region_init); +EXPORT_SYMBOL(dma_region_alloc); +EXPORT_SYMBOL(dma_region_free); +EXPORT_SYMBOL(dma_region_sync); +EXPORT_SYMBOL(dma_region_mmap); Added: branches/rawiso/dma.h ============================================================================== --- branches/rawiso/dma.h (original) +++ branches/rawiso/dma.h 2002-08-28 12:20:09.000000000 -0400 @@ -0,0 +1,86 @@ +/* + * DMA region bookkeeping routines + * + * Copyright (C) 2002 Maas Digital LLC + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. + */ + +#ifndef IEEE1394_DMA_H +#define IEEE1394_DMA_H + +#include <linux/pci.h> +#include <asm/scatterlist.h> + +/* structure for bookkeeping of a small, physically-contiguous DMA buffer + with random-access, synchronous usage characteristics */ + +struct dma_prog_region { + unsigned char *kvirt; /* kernel virtual address */ + struct pci_dev *dev; /* PCI device */ + unsigned int n_pages; /* # of kernel pages */ + dma_addr_t bus_addr; /* base bus address */ +}; + +/* clear out all fields but do not allocate any memory */ +void dma_prog_region_init(struct dma_prog_region *prog); +int dma_prog_region_alloc(struct dma_prog_region *prog, unsigned long n_bytes, struct pci_dev *dev); +void dma_prog_region_free(struct dma_prog_region *prog); + +static inline dma_addr_t dma_prog_region_offset_to_bus(struct dma_prog_region *prog, unsigned long offset) +{ + return prog->bus_addr + offset; +} + + + +/* structure for bookkeeping of a large, non-physically-contiguous DMA buffer + with streaming, asynchronous usage characteristics */ + +struct dma_region { + unsigned char *kvirt; /* kernel virtual address */ + struct pci_dev *dev; /* PCI device */ + unsigned int n_pages; /* # of kernel pages */ + unsigned int n_dma_pages; /* # of IOMMU pages */ + struct scatterlist *sglist; + int direction; /* PCI_DMA_TODEVICE, etc */ +}; + +/* clear out all fields but do not allocate anything */ +void dma_region_init(struct dma_region *dma); + +/* allocate the buffer and map it to the IOMMU */ +int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes, struct pci_dev *dev, int direction); + +/* unmap and free the buffer */ +void dma_region_free(struct dma_region *dma); + +/* sync the IO bus' view of the buffer with the CPU's view */ +/* XXXDJM add addr, len arguments */ +void dma_region_sync(struct dma_region *dma); + +/* map the buffer into a user space process */ +int dma_region_mmap(struct dma_region *dma, struct file *file, struct vm_area_struct *vma); + +/* macro to index into a DMA region */ +#define dma_region_i(_dma, _type, _index) ( ((_type*) ((_dma)->kvirt)) + (_index) ) + +/* return the DMA bus address of the byte with the given offset + relative to the beginning of the dma_region */ + +static inline dma_addr_t dma_region_offset_to_bus(struct dma_region *dma, unsigned long offset) +{ + int i; + struct scatterlist *sg; + + for(i = 0, sg = &dma->sglist[0]; i < dma->n_dma_pages; i++, sg++) { + if(offset < sg_dma_len(sg)) { + return sg_dma_address(sg) + offset; + } + offset -= sg_dma_len(sg); + } + return 0; +} + +#endif /* IEEE1394_DMA_H */ |