Blame view
drivers/ieee1394/dma.c
6.16 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 3 4 5 6 7 8 |
/* * 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. */ |
de4394f13 [PATCH] ieee1394:... |
9 |
#include <linux/mm.h> |
1da177e4c Linux-2.6.12-rc2 |
10 |
#include <linux/module.h> |
de4394f13 [PATCH] ieee1394:... |
11 |
#include <linux/pci.h> |
de4394f13 [PATCH] ieee1394:... |
12 |
#include <linux/vmalloc.h> |
117636092 [PATCH] Fix break... |
13 |
#include <linux/scatterlist.h> |
de4394f13 [PATCH] ieee1394:... |
14 |
|
1da177e4c Linux-2.6.12-rc2 |
15 16 17 18 19 20 21 22 23 24 25 |
#include "dma.h" /* dma_prog_region */ void dma_prog_region_init(struct dma_prog_region *prog) { prog->kvirt = NULL; prog->dev = NULL; prog->n_pages = 0; prog->bus_addr = 0; } |
6649e92d7 ieee1394/dma: LIn... |
26 27 |
int dma_prog_region_alloc(struct dma_prog_region *prog, unsigned long n_bytes, struct pci_dev *dev) |
1da177e4c Linux-2.6.12-rc2 |
28 29 30 31 32 33 34 35 |
{ /* round up to page size */ n_bytes = PAGE_ALIGN(n_bytes); prog->n_pages = n_bytes >> PAGE_SHIFT; prog->kvirt = pci_alloc_consistent(dev, n_bytes, &prog->bus_addr); if (!prog->kvirt) { |
6649e92d7 ieee1394/dma: LIn... |
36 37 38 |
printk(KERN_ERR "dma_prog_region_alloc: pci_alloc_consistent() failed "); |
1da177e4c Linux-2.6.12-rc2 |
39 40 41 42 43 44 45 46 47 48 49 50 |
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) { |
6649e92d7 ieee1394/dma: LIn... |
51 52 |
pci_free_consistent(prog->dev, prog->n_pages << PAGE_SHIFT, prog->kvirt, prog->bus_addr); |
1da177e4c Linux-2.6.12-rc2 |
53 54 55 56 57 58 59 60 61 |
} prog->kvirt = NULL; prog->dev = NULL; prog->n_pages = 0; prog->bus_addr = 0; } /* dma_region */ |
afd6546d8 ieee1394: move so... |
62 63 64 |
/** * dma_region_init - clear out all fields but do not allocate anything */ |
1da177e4c Linux-2.6.12-rc2 |
65 66 67 68 69 70 71 72 |
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; } |
afd6546d8 ieee1394: move so... |
73 74 75 |
/** * dma_region_alloc - allocate the buffer and map it to the IOMMU */ |
6649e92d7 ieee1394/dma: LIn... |
76 77 |
int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes, struct pci_dev *dev, int direction) |
1da177e4c Linux-2.6.12-rc2 |
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
{ unsigned int i; /* round up to page size */ n_bytes = PAGE_ALIGN(n_bytes); dma->n_pages = n_bytes >> PAGE_SHIFT; dma->kvirt = vmalloc_32(n_bytes); if (!dma->kvirt) { printk(KERN_ERR "dma_region_alloc: vmalloc_32() failed "); goto err; } /* Clear the ram out, no junk to the user */ memset(dma->kvirt, 0, n_bytes); /* allocate scatter/gather list */ dma->sglist = vmalloc(dma->n_pages * sizeof(*dma->sglist)); if (!dma->sglist) { printk(KERN_ERR "dma_region_alloc: vmalloc(sglist) failed "); goto err; } |
9e66269d4 ieee1394: iso and... |
103 |
sg_init_table(dma->sglist, dma->n_pages); |
1da177e4c Linux-2.6.12-rc2 |
104 105 106 |
/* fill scatter/gather list with pages */ for (i = 0; i < dma->n_pages; i++) { |
6649e92d7 ieee1394/dma: LIn... |
107 108 |
unsigned long va = (unsigned long)dma->kvirt + (i << PAGE_SHIFT); |
1da177e4c Linux-2.6.12-rc2 |
109 |
|
642f14903 SG: Change sg_set... |
110 111 |
sg_set_page(&dma->sglist[i], vmalloc_to_page((void *)va), PAGE_SIZE, 0); |
1da177e4c Linux-2.6.12-rc2 |
112 113 114 |
} /* map sglist to the IOMMU */ |
6649e92d7 ieee1394/dma: LIn... |
115 116 |
dma->n_dma_pages = pci_map_sg(dev, dma->sglist, dma->n_pages, direction); |
1da177e4c Linux-2.6.12-rc2 |
117 118 119 120 121 122 123 124 125 126 127 |
if (dma->n_dma_pages == 0) { printk(KERN_ERR "dma_region_alloc: pci_map_sg() failed "); goto err; } dma->dev = dev; dma->direction = direction; return 0; |
6649e92d7 ieee1394/dma: LIn... |
128 |
err: |
1da177e4c Linux-2.6.12-rc2 |
129 130 131 |
dma_region_free(dma); return -ENOMEM; } |
afd6546d8 ieee1394: move so... |
132 133 134 |
/** * dma_region_free - unmap and free the buffer */ |
1da177e4c Linux-2.6.12-rc2 |
135 136 137 |
void dma_region_free(struct dma_region *dma) { if (dma->n_dma_pages) { |
6649e92d7 ieee1394/dma: LIn... |
138 139 |
pci_unmap_sg(dma->dev, dma->sglist, dma->n_pages, dma->direction); |
1da177e4c Linux-2.6.12-rc2 |
140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
dma->n_dma_pages = 0; dma->dev = NULL; } vfree(dma->sglist); dma->sglist = NULL; vfree(dma->kvirt); dma->kvirt = NULL; dma->n_pages = 0; } /* find the scatterlist index and remaining offset corresponding to a given offset from the beginning of the buffer */ |
6649e92d7 ieee1394/dma: LIn... |
154 |
static inline int dma_region_find(struct dma_region *dma, unsigned long offset, |
9bb2bcdb4 ieee1394: speed u... |
155 |
unsigned int start, unsigned long *rem) |
1da177e4c Linux-2.6.12-rc2 |
156 157 158 |
{ int i; unsigned long off = offset; |
9bb2bcdb4 ieee1394: speed u... |
159 |
for (i = start; i < dma->n_dma_pages; i++) { |
1da177e4c Linux-2.6.12-rc2 |
160 161 162 163 164 165 166 167 168 169 170 171 |
if (off < sg_dma_len(&dma->sglist[i])) { *rem = off; break; } off -= sg_dma_len(&dma->sglist[i]); } BUG_ON(i >= dma->n_dma_pages); return i; } |
afd6546d8 ieee1394: move so... |
172 173 174 175 176 177 |
/** * dma_region_offset_to_bus - get bus address of an offset within a DMA region * * Returns the DMA bus address of the byte with the given @offset relative to * the beginning of the @dma. */ |
6649e92d7 ieee1394/dma: LIn... |
178 179 |
dma_addr_t dma_region_offset_to_bus(struct dma_region * dma, unsigned long offset) |
1da177e4c Linux-2.6.12-rc2 |
180 |
{ |
1934b8b65 [PATCH] Sync up i... |
181 |
unsigned long rem = 0; |
1da177e4c Linux-2.6.12-rc2 |
182 |
|
6649e92d7 ieee1394/dma: LIn... |
183 |
struct scatterlist *sg = |
9bb2bcdb4 ieee1394: speed u... |
184 |
&dma->sglist[dma_region_find(dma, offset, 0, &rem)]; |
1da177e4c Linux-2.6.12-rc2 |
185 186 |
return sg_dma_address(sg) + rem; } |
afd6546d8 ieee1394: move so... |
187 188 189 |
/** * dma_region_sync_for_cpu - sync the CPU's view of the buffer */ |
6649e92d7 ieee1394/dma: LIn... |
190 191 |
void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset, unsigned long len) |
1da177e4c Linux-2.6.12-rc2 |
192 193 |
{ int first, last; |
9bb2bcdb4 ieee1394: speed u... |
194 |
unsigned long rem = 0; |
1da177e4c Linux-2.6.12-rc2 |
195 196 197 |
if (!len) len = 1; |
9bb2bcdb4 ieee1394: speed u... |
198 199 |
first = dma_region_find(dma, offset, 0, &rem); last = dma_region_find(dma, rem + len - 1, first, &rem); |
1da177e4c Linux-2.6.12-rc2 |
200 |
|
6649e92d7 ieee1394/dma: LIn... |
201 202 |
pci_dma_sync_sg_for_cpu(dma->dev, &dma->sglist[first], last - first + 1, dma->direction); |
1da177e4c Linux-2.6.12-rc2 |
203 |
} |
afd6546d8 ieee1394: move so... |
204 205 206 |
/** * dma_region_sync_for_device - sync the IO bus' view of the buffer */ |
6649e92d7 ieee1394/dma: LIn... |
207 208 |
void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset, unsigned long len) |
1da177e4c Linux-2.6.12-rc2 |
209 210 |
{ int first, last; |
9bb2bcdb4 ieee1394: speed u... |
211 |
unsigned long rem = 0; |
1da177e4c Linux-2.6.12-rc2 |
212 213 214 |
if (!len) len = 1; |
9bb2bcdb4 ieee1394: speed u... |
215 216 |
first = dma_region_find(dma, offset, 0, &rem); last = dma_region_find(dma, rem + len - 1, first, &rem); |
1da177e4c Linux-2.6.12-rc2 |
217 |
|
6649e92d7 ieee1394/dma: LIn... |
218 219 |
pci_dma_sync_sg_for_device(dma->dev, &dma->sglist[first], last - first + 1, dma->direction); |
1da177e4c Linux-2.6.12-rc2 |
220 221 222 |
} #ifdef CONFIG_MMU |
61db81214 ieee1394: nopage |
223 |
static int dma_region_pagefault(struct vm_area_struct *vma, |
c7ea990f8 ieee1394: small c... |
224 |
struct vm_fault *vmf) |
1da177e4c Linux-2.6.12-rc2 |
225 |
{ |
61db81214 ieee1394: nopage |
226 |
struct dma_region *dma = (struct dma_region *)vma->vm_private_data; |
1da177e4c Linux-2.6.12-rc2 |
227 228 |
if (!dma->kvirt) |
c7ea990f8 ieee1394: small c... |
229 |
return VM_FAULT_SIGBUS; |
61db81214 ieee1394: nopage |
230 231 |
if (vmf->pgoff >= dma->n_pages) |
c7ea990f8 ieee1394: small c... |
232 |
return VM_FAULT_SIGBUS; |
61db81214 ieee1394: nopage |
233 |
|
c7ea990f8 ieee1394: small c... |
234 |
vmf->page = vmalloc_to_page(dma->kvirt + (vmf->pgoff << PAGE_SHIFT)); |
61db81214 ieee1394: nopage |
235 236 |
get_page(vmf->page); return 0; |
1da177e4c Linux-2.6.12-rc2 |
237 |
} |
f0f37e2f7 const: mark struc... |
238 |
static const struct vm_operations_struct dma_region_vm_ops = { |
61db81214 ieee1394: nopage |
239 |
.fault = dma_region_pagefault, |
1da177e4c Linux-2.6.12-rc2 |
240 |
}; |
afd6546d8 ieee1394: move so... |
241 242 243 |
/** * dma_region_mmap - map the buffer into a user space process */ |
6649e92d7 ieee1394/dma: LIn... |
244 245 |
int dma_region_mmap(struct dma_region *dma, struct file *file, struct vm_area_struct *vma) |
1da177e4c Linux-2.6.12-rc2 |
246 247 248 249 250 |
{ unsigned long size; if (!dma->kvirt) return -EINVAL; |
61db81214 ieee1394: nopage |
251 |
/* must be page-aligned (XXX: comment is wrong, we could allow pgoff) */ |
1da177e4c Linux-2.6.12-rc2 |
252 253 254 255 256 257 258 259 260 261 262 |
if (vma->vm_pgoff != 0) return -EINVAL; /* check the length */ size = vma->vm_end - vma->vm_start; if (size > (dma->n_pages << PAGE_SHIFT)) return -EINVAL; vma->vm_ops = &dma_region_vm_ops; vma->vm_private_data = dma; vma->vm_file = file; |
435f97269 ieee1394: dump mm... |
263 |
vma->vm_flags |= VM_RESERVED | VM_ALWAYSDUMP; |
1da177e4c Linux-2.6.12-rc2 |
264 265 266 |
return 0; } |
6649e92d7 ieee1394/dma: LIn... |
267 |
#else /* CONFIG_MMU */ |
1da177e4c Linux-2.6.12-rc2 |
268 |
|
6649e92d7 ieee1394/dma: LIn... |
269 270 |
int dma_region_mmap(struct dma_region *dma, struct file *file, struct vm_area_struct *vma) |
1da177e4c Linux-2.6.12-rc2 |
271 272 273 |
{ return -EINVAL; } |
6649e92d7 ieee1394/dma: LIn... |
274 |
#endif /* CONFIG_MMU */ |