Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
emu10k1_patch.c
Go to the documentation of this file.
1 /*
2  * Patch transfer callback for Emu10k1
3  *
4  * Copyright (C) 2000 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  * All the code for loading in a patch. There is very little that is
22  * chip specific here. Just the actual writing to the board.
23  */
24 
25 #include "emu10k1_synth_local.h"
26 
27 /*
28  */
29 #define BLANK_LOOP_START 4
30 #define BLANK_LOOP_END 8
31 #define BLANK_LOOP_SIZE 12
32 #define BLANK_HEAD_SIZE 32
33 
34 /*
35  * allocate a sample block and copy data from userspace
36  */
37 int
39  struct snd_util_memhdr *hdr,
40  const void __user *data, long count)
41 {
42  int offset;
43  int truesize, size, loopsize, blocksize;
44  int loopend, sampleend;
45  unsigned int start_addr;
46  struct snd_emu10k1 *emu;
47 
48  emu = rec->hw;
49  if (snd_BUG_ON(!sp || !hdr))
50  return -EINVAL;
51 
52  if (sp->v.size == 0) {
53  snd_printd("emu: rom font for sample %d\n", sp->v.sample);
54  return 0;
55  }
56 
57  /* recalculate address offset */
58  sp->v.end -= sp->v.start;
59  sp->v.loopstart -= sp->v.start;
60  sp->v.loopend -= sp->v.start;
61  sp->v.start = 0;
62 
63  /* some samples have invalid data. the addresses are corrected in voice info */
64  sampleend = sp->v.end;
65  if (sampleend > sp->v.size)
66  sampleend = sp->v.size;
67  loopend = sp->v.loopend;
68  if (loopend > sampleend)
69  loopend = sampleend;
70 
71  /* be sure loop points start < end */
72  if (sp->v.loopstart >= sp->v.loopend) {
73  int tmp = sp->v.loopstart;
74  sp->v.loopstart = sp->v.loopend;
75  sp->v.loopend = tmp;
76  }
77 
78  /* compute true data size to be loaded */
79  truesize = sp->v.size + BLANK_HEAD_SIZE;
80  loopsize = 0;
81 #if 0 /* not supported */
83  loopsize = sp->v.loopend - sp->v.loopstart;
84  truesize += loopsize;
85 #endif
86  if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK)
87  truesize += BLANK_LOOP_SIZE;
88 
89  /* try to allocate a memory block */
90  blocksize = truesize;
91  if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
92  blocksize *= 2;
93  sp->block = snd_emu10k1_synth_alloc(emu, blocksize);
94  if (sp->block == NULL) {
95  snd_printd("emu10k1: synth malloc failed (size=%d)\n", blocksize);
96  /* not ENOMEM (for compatibility with OSS) */
97  return -ENOSPC;
98  }
99  /* set the total size */
100  sp->v.truesize = blocksize;
101 
102  /* write blank samples at head */
103  offset = 0;
104  size = BLANK_HEAD_SIZE;
105  if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
106  size *= 2;
107  if (offset + size > blocksize)
108  return -EINVAL;
109  snd_emu10k1_synth_bzero(emu, sp->block, offset, size);
110  offset += size;
111 
112  /* copy start->loopend */
113  size = loopend;
114  if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
115  size *= 2;
116  if (offset + size > blocksize)
117  return -EINVAL;
118  if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
119  snd_emu10k1_synth_free(emu, sp->block);
120  sp->block = NULL;
121  return -EFAULT;
122  }
123  offset += size;
124  data += size;
125 
126 #if 0 /* not suppported yet */
127  /* handle reverse (or bidirectional) loop */
129  /* copy loop in reverse */
130  if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
131  int woffset;
132  unsigned short *wblock = (unsigned short*)block;
133  woffset = offset / 2;
134  if (offset + loopsize * 2 > blocksize)
135  return -EINVAL;
136  for (i = 0; i < loopsize; i++)
137  wblock[woffset + i] = wblock[woffset - i -1];
138  offset += loopsize * 2;
139  } else {
140  if (offset + loopsize > blocksize)
141  return -EINVAL;
142  for (i = 0; i < loopsize; i++)
143  block[offset + i] = block[offset - i -1];
144  offset += loopsize;
145  }
146 
147  /* modify loop pointers */
148  if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) {
149  sp->v.loopend += loopsize;
150  } else {
151  sp->v.loopstart += loopsize;
152  sp->v.loopend += loopsize;
153  }
154  /* add sample pointer */
155  sp->v.end += loopsize;
156  }
157 #endif
158 
159  /* loopend -> sample end */
160  size = sp->v.size - loopend;
161  if (size < 0)
162  return -EINVAL;
163  if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
164  size *= 2;
165  if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
166  snd_emu10k1_synth_free(emu, sp->block);
167  sp->block = NULL;
168  return -EFAULT;
169  }
170  offset += size;
171 
172  /* clear rest of samples (if any) */
173  if (offset < blocksize)
174  snd_emu10k1_synth_bzero(emu, sp->block, offset, blocksize - offset);
175 
176  if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) {
177  /* if no blank loop is attached in the sample, add it */
178  if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) {
179  sp->v.loopstart = sp->v.end + BLANK_LOOP_START;
180  sp->v.loopend = sp->v.end + BLANK_LOOP_END;
181  }
182  }
183 
184 #if 0 /* not supported yet */
185  if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) {
186  /* unsigned -> signed */
187  if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
188  unsigned short *wblock = (unsigned short*)block;
189  for (i = 0; i < truesize; i++)
190  wblock[i] ^= 0x8000;
191  } else {
192  for (i = 0; i < truesize; i++)
193  block[i] ^= 0x80;
194  }
195  }
196 #endif
197 
198  /* recalculate offset */
199  start_addr = BLANK_HEAD_SIZE * 2;
200  if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
201  start_addr >>= 1;
202  sp->v.start += start_addr;
203  sp->v.end += start_addr;
204  sp->v.loopstart += start_addr;
205  sp->v.loopend += start_addr;
206 
207  return 0;
208 }
209 
210 /*
211  * free a sample block
212  */
213 int
215  struct snd_util_memhdr *hdr)
216 {
217  struct snd_emu10k1 *emu;
218 
219  emu = rec->hw;
220  if (snd_BUG_ON(!sp || !hdr))
221  return -EINVAL;
222 
223  if (sp->block) {
224  snd_emu10k1_synth_free(emu, sp->block);
225  sp->block = NULL;
226  }
227  return 0;
228 }
229