Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
sgbuf.c
Go to the documentation of this file.
1 /*
2  * Scatter-Gather buffer
3  *
4  * Copyright (c) by Takashi Iwai <[email protected]>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  */
21 
22 #include <linux/slab.h>
23 #include <linux/mm.h>
24 #include <linux/vmalloc.h>
25 #include <linux/export.h>
26 #include <sound/memalloc.h>
27 
28 
29 /* table entries are align to 32 */
30 #define SGBUF_TBL_ALIGN 32
31 #define sgbuf_align_table(tbl) ALIGN((tbl), SGBUF_TBL_ALIGN)
32 
34 {
35  struct snd_sg_buf *sgbuf = dmab->private_data;
36  struct snd_dma_buffer tmpb;
37  int i;
38 
39  if (! sgbuf)
40  return -EINVAL;
41 
42  if (dmab->area)
43  vunmap(dmab->area);
44  dmab->area = NULL;
45 
46  tmpb.dev.type = SNDRV_DMA_TYPE_DEV;
47  tmpb.dev.dev = sgbuf->dev;
48  for (i = 0; i < sgbuf->pages; i++) {
49  if (!(sgbuf->table[i].addr & ~PAGE_MASK))
50  continue; /* continuous pages */
51  tmpb.area = sgbuf->table[i].buf;
52  tmpb.addr = sgbuf->table[i].addr & PAGE_MASK;
53  tmpb.bytes = (sgbuf->table[i].addr & ~PAGE_MASK) << PAGE_SHIFT;
54  snd_dma_free_pages(&tmpb);
55  }
56 
57  kfree(sgbuf->table);
58  kfree(sgbuf->page_table);
59  kfree(sgbuf);
60  dmab->private_data = NULL;
61 
62  return 0;
63 }
64 
65 #define MAX_ALLOC_PAGES 32
66 
68  size_t size, struct snd_dma_buffer *dmab,
69  size_t *res_size)
70 {
71  struct snd_sg_buf *sgbuf;
72  unsigned int i, pages, chunk, maxpages;
73  struct snd_dma_buffer tmpb;
74  struct snd_sg_page *table;
75  struct page **pgtable;
76 
77  dmab->area = NULL;
78  dmab->addr = 0;
79  dmab->private_data = sgbuf = kzalloc(sizeof(*sgbuf), GFP_KERNEL);
80  if (! sgbuf)
81  return NULL;
82  sgbuf->dev = device;
83  pages = snd_sgbuf_aligned_pages(size);
84  sgbuf->tblsize = sgbuf_align_table(pages);
85  table = kcalloc(sgbuf->tblsize, sizeof(*table), GFP_KERNEL);
86  if (!table)
87  goto _failed;
88  sgbuf->table = table;
89  pgtable = kcalloc(sgbuf->tblsize, sizeof(*pgtable), GFP_KERNEL);
90  if (!pgtable)
91  goto _failed;
92  sgbuf->page_table = pgtable;
93 
94  /* allocate pages */
95  maxpages = MAX_ALLOC_PAGES;
96  while (pages > 0) {
97  chunk = pages;
98  /* don't be too eager to take a huge chunk */
99  if (chunk > maxpages)
100  chunk = maxpages;
101  chunk <<= PAGE_SHIFT;
103  chunk, &tmpb) < 0) {
104  if (!sgbuf->pages)
105  goto _failed;
106  if (!res_size)
107  goto _failed;
108  size = sgbuf->pages * PAGE_SIZE;
109  break;
110  }
111  chunk = tmpb.bytes >> PAGE_SHIFT;
112  for (i = 0; i < chunk; i++) {
113  table->buf = tmpb.area;
114  table->addr = tmpb.addr;
115  if (!i)
116  table->addr |= chunk; /* mark head */
117  table++;
118  *pgtable++ = virt_to_page(tmpb.area);
119  tmpb.area += PAGE_SIZE;
120  tmpb.addr += PAGE_SIZE;
121  }
122  sgbuf->pages += chunk;
123  pages -= chunk;
124  if (chunk < maxpages)
125  maxpages = chunk;
126  }
127 
128  sgbuf->size = size;
129  dmab->area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, PAGE_KERNEL);
130  if (! dmab->area)
131  goto _failed;
132  if (res_size)
133  *res_size = sgbuf->size;
134  return dmab->area;
135 
136  _failed:
137  snd_free_sgbuf_pages(dmab); /* free the table */
138  return NULL;
139 }
140 
141 /*
142  * compute the max chunk size with continuous pages on sg-buffer
143  */
144 unsigned int snd_sgbuf_get_chunk_size(struct snd_dma_buffer *dmab,
145  unsigned int ofs, unsigned int size)
146 {
147  struct snd_sg_buf *sg = dmab->private_data;
148  unsigned int start, end, pg;
149 
150  start = ofs >> PAGE_SHIFT;
151  end = (ofs + size - 1) >> PAGE_SHIFT;
152  /* check page continuity */
153  pg = sg->table[start].addr >> PAGE_SHIFT;
154  for (;;) {
155  start++;
156  if (start > end)
157  break;
158  pg++;
159  if ((sg->table[start].addr >> PAGE_SHIFT) != pg)
160  return (start << PAGE_SHIFT) - ofs;
161  }
162  /* ok, all on continuous pages */
163  return size;
164 }