Blame view

drivers/ieee1394/dma.c 6.16 KB
1da177e4c   Linus Torvalds   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   Stefan Richter   [PATCH] ieee1394:...
9
  #include <linux/mm.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
  #include <linux/module.h>
de4394f13   Stefan Richter   [PATCH] ieee1394:...
11
  #include <linux/pci.h>
de4394f13   Stefan Richter   [PATCH] ieee1394:...
12
  #include <linux/vmalloc.h>
117636092   Ralf Baechle   [PATCH] Fix break...
13
  #include <linux/scatterlist.h>
de4394f13   Stefan Richter   [PATCH] ieee1394:...
14

1da177e4c   Linus Torvalds   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   Jens-Michael Hoffmann   ieee1394/dma: LIn...
26
27
  int dma_prog_region_alloc(struct dma_prog_region *prog, unsigned long n_bytes,
  			  struct pci_dev *dev)
1da177e4c   Linus Torvalds   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   Jens-Michael Hoffmann   ieee1394/dma: LIn...
36
37
38
  		printk(KERN_ERR
  		       "dma_prog_region_alloc: pci_alloc_consistent() failed
  ");
1da177e4c   Linus Torvalds   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   Jens-Michael Hoffmann   ieee1394/dma: LIn...
51
52
  		pci_free_consistent(prog->dev, prog->n_pages << PAGE_SHIFT,
  				    prog->kvirt, prog->bus_addr);
1da177e4c   Linus Torvalds   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   Stefan Richter   ieee1394: move so...
62
63
64
  /**
   * dma_region_init - clear out all fields but do not allocate anything
   */
1da177e4c   Linus Torvalds   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   Stefan Richter   ieee1394: move so...
73
74
75
  /**
   * dma_region_alloc - allocate the buffer and map it to the IOMMU
   */
6649e92d7   Jens-Michael Hoffmann   ieee1394/dma: LIn...
76
77
  int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes,
  		     struct pci_dev *dev, int direction)
1da177e4c   Linus Torvalds   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   Jens Axboe   ieee1394: iso and...
103
  	sg_init_table(dma->sglist, dma->n_pages);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
104
105
106
  
  	/* fill scatter/gather list with pages */
  	for (i = 0; i < dma->n_pages; i++) {
6649e92d7   Jens-Michael Hoffmann   ieee1394/dma: LIn...
107
108
  		unsigned long va =
  		    (unsigned long)dma->kvirt + (i << PAGE_SHIFT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109

642f14903   Jens Axboe   SG: Change sg_set...
110
111
  		sg_set_page(&dma->sglist[i], vmalloc_to_page((void *)va),
  				PAGE_SIZE, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
112
113
114
  	}
  
  	/* map sglist to the IOMMU */
6649e92d7   Jens-Michael Hoffmann   ieee1394/dma: LIn...
115
116
  	dma->n_dma_pages =
  	    pci_map_sg(dev, dma->sglist, dma->n_pages, direction);
1da177e4c   Linus Torvalds   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   Jens-Michael Hoffmann   ieee1394/dma: LIn...
128
        err:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
130
131
  	dma_region_free(dma);
  	return -ENOMEM;
  }
afd6546d8   Stefan Richter   ieee1394: move so...
132
133
134
  /**
   * dma_region_free - unmap and free the buffer
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
136
137
  void dma_region_free(struct dma_region *dma)
  {
  	if (dma->n_dma_pages) {
6649e92d7   Jens-Michael Hoffmann   ieee1394/dma: LIn...
138
139
  		pci_unmap_sg(dma->dev, dma->sglist, dma->n_pages,
  			     dma->direction);
1da177e4c   Linus Torvalds   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   Jens-Michael Hoffmann   ieee1394/dma: LIn...
154
  static inline int dma_region_find(struct dma_region *dma, unsigned long offset,
9bb2bcdb4   Ben Collins   ieee1394: speed u...
155
  				  unsigned int start, unsigned long *rem)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
157
158
  {
  	int i;
  	unsigned long off = offset;
9bb2bcdb4   Ben Collins   ieee1394: speed u...
159
  	for (i = start; i < dma->n_dma_pages; i++) {
1da177e4c   Linus Torvalds   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   Stefan Richter   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   Jens-Michael Hoffmann   ieee1394/dma: LIn...
178
179
  dma_addr_t dma_region_offset_to_bus(struct dma_region * dma,
  				    unsigned long offset)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
  {
1934b8b65   Ben Collins   [PATCH] Sync up i...
181
  	unsigned long rem = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
182

6649e92d7   Jens-Michael Hoffmann   ieee1394/dma: LIn...
183
  	struct scatterlist *sg =
9bb2bcdb4   Ben Collins   ieee1394: speed u...
184
  	    &dma->sglist[dma_region_find(dma, offset, 0, &rem)];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
186
  	return sg_dma_address(sg) + rem;
  }
afd6546d8   Stefan Richter   ieee1394: move so...
187
188
189
  /**
   * dma_region_sync_for_cpu - sync the CPU's view of the buffer
   */
6649e92d7   Jens-Michael Hoffmann   ieee1394/dma: LIn...
190
191
  void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset,
  			     unsigned long len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
193
  {
  	int first, last;
9bb2bcdb4   Ben Collins   ieee1394: speed u...
194
  	unsigned long rem = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195
196
197
  
  	if (!len)
  		len = 1;
9bb2bcdb4   Ben Collins   ieee1394: speed u...
198
199
  	first = dma_region_find(dma, offset, 0, &rem);
  	last = dma_region_find(dma, rem + len - 1, first, &rem);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200

6649e92d7   Jens-Michael Hoffmann   ieee1394/dma: LIn...
201
202
  	pci_dma_sync_sg_for_cpu(dma->dev, &dma->sglist[first], last - first + 1,
  				dma->direction);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
  }
afd6546d8   Stefan Richter   ieee1394: move so...
204
205
206
  /**
   * dma_region_sync_for_device - sync the IO bus' view of the buffer
   */
6649e92d7   Jens-Michael Hoffmann   ieee1394/dma: LIn...
207
208
  void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset,
  				unsigned long len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
210
  {
  	int first, last;
9bb2bcdb4   Ben Collins   ieee1394: speed u...
211
  	unsigned long rem = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
212
213
214
  
  	if (!len)
  		len = 1;
9bb2bcdb4   Ben Collins   ieee1394: speed u...
215
216
  	first = dma_region_find(dma, offset, 0, &rem);
  	last = dma_region_find(dma, rem + len - 1, first, &rem);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217

6649e92d7   Jens-Michael Hoffmann   ieee1394/dma: LIn...
218
219
  	pci_dma_sync_sg_for_device(dma->dev, &dma->sglist[first],
  				   last - first + 1, dma->direction);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
220
221
222
  }
  
  #ifdef CONFIG_MMU
61db81214   Nick Piggin   ieee1394: nopage
223
  static int dma_region_pagefault(struct vm_area_struct *vma,
c7ea990f8   Stefan Richter   ieee1394: small c...
224
  				struct vm_fault *vmf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
225
  {
61db81214   Nick Piggin   ieee1394: nopage
226
  	struct dma_region *dma = (struct dma_region *)vma->vm_private_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
227
228
  
  	if (!dma->kvirt)
c7ea990f8   Stefan Richter   ieee1394: small c...
229
  		return VM_FAULT_SIGBUS;
61db81214   Nick Piggin   ieee1394: nopage
230
231
  
  	if (vmf->pgoff >= dma->n_pages)
c7ea990f8   Stefan Richter   ieee1394: small c...
232
  		return VM_FAULT_SIGBUS;
61db81214   Nick Piggin   ieee1394: nopage
233

c7ea990f8   Stefan Richter   ieee1394: small c...
234
  	vmf->page = vmalloc_to_page(dma->kvirt + (vmf->pgoff << PAGE_SHIFT));
61db81214   Nick Piggin   ieee1394: nopage
235
236
  	get_page(vmf->page);
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237
  }
f0f37e2f7   Alexey Dobriyan   const: mark struc...
238
  static const struct vm_operations_struct dma_region_vm_ops = {
61db81214   Nick Piggin   ieee1394: nopage
239
  	.fault = dma_region_pagefault,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
  };
afd6546d8   Stefan Richter   ieee1394: move so...
241
242
243
  /**
   * dma_region_mmap - map the buffer into a user space process
   */
6649e92d7   Jens-Michael Hoffmann   ieee1394/dma: LIn...
244
245
  int dma_region_mmap(struct dma_region *dma, struct file *file,
  		    struct vm_area_struct *vma)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246
247
248
249
250
  {
  	unsigned long size;
  
  	if (!dma->kvirt)
  		return -EINVAL;
61db81214   Nick Piggin   ieee1394: nopage
251
  	/* must be page-aligned (XXX: comment is wrong, we could allow pgoff) */
1da177e4c   Linus Torvalds   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   Philippe De Muyter   ieee1394: dump mm...
263
  	vma->vm_flags |= VM_RESERVED | VM_ALWAYSDUMP;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
265
266
  
  	return 0;
  }
6649e92d7   Jens-Michael Hoffmann   ieee1394/dma: LIn...
267
  #else				/* CONFIG_MMU */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
268

6649e92d7   Jens-Michael Hoffmann   ieee1394/dma: LIn...
269
270
  int dma_region_mmap(struct dma_region *dma, struct file *file,
  		    struct vm_area_struct *vma)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271
272
273
  {
  	return -EINVAL;
  }
6649e92d7   Jens-Michael Hoffmann   ieee1394/dma: LIn...
274
  #endif				/* CONFIG_MMU */