blob: 86ac9ae9460e6210d7f444c0484c14383cb01bf9 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Digital Audio (PCM) abstract layer
Jaroslav Kyselac1017a42007-10-15 09:50:19 +02003 * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 * Abramo Bagnara <abramo@alsa-project.org>
5 *
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#include <linux/slab.h>
24#include <linux/time.h>
25#include <sound/core.h>
26#include <sound/control.h>
27#include <sound/info.h>
28#include <sound/pcm.h>
29#include <sound/pcm_params.h>
30#include <sound/timer.h>
31
32/*
33 * fill ring buffer with silence
34 * runtime->silence_start: starting pointer to silence area
35 * runtime->silence_filled: size filled with silence
36 * runtime->silence_threshold: threshold from application
37 * runtime->silence_size: maximal size from application
38 *
39 * when runtime->silence_size >= runtime->boundary - fill processed area with silence immediately
40 */
Takashi Iwai877211f2005-11-17 13:59:38 +010041void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_uframes_t new_hw_ptr)
Linus Torvalds1da177e2005-04-16 15:20:36 -070042{
Takashi Iwai877211f2005-11-17 13:59:38 +010043 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -070044 snd_pcm_uframes_t frames, ofs, transfer;
45
46 if (runtime->silence_size < runtime->boundary) {
47 snd_pcm_sframes_t noise_dist, n;
48 if (runtime->silence_start != runtime->control->appl_ptr) {
49 n = runtime->control->appl_ptr - runtime->silence_start;
50 if (n < 0)
51 n += runtime->boundary;
52 if ((snd_pcm_uframes_t)n < runtime->silence_filled)
53 runtime->silence_filled -= n;
54 else
55 runtime->silence_filled = 0;
56 runtime->silence_start = runtime->control->appl_ptr;
57 }
Takashi Iwai235475c2005-12-07 15:28:07 +010058 if (runtime->silence_filled >= runtime->buffer_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -070059 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -070060 noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled;
61 if (noise_dist >= (snd_pcm_sframes_t) runtime->silence_threshold)
62 return;
63 frames = runtime->silence_threshold - noise_dist;
64 if (frames > runtime->silence_size)
65 frames = runtime->silence_size;
66 } else {
67 if (new_hw_ptr == ULONG_MAX) { /* initialization */
68 snd_pcm_sframes_t avail = snd_pcm_playback_hw_avail(runtime);
69 runtime->silence_filled = avail > 0 ? avail : 0;
70 runtime->silence_start = (runtime->status->hw_ptr +
71 runtime->silence_filled) %
72 runtime->boundary;
73 } else {
74 ofs = runtime->status->hw_ptr;
75 frames = new_hw_ptr - ofs;
76 if ((snd_pcm_sframes_t)frames < 0)
77 frames += runtime->boundary;
78 runtime->silence_filled -= frames;
79 if ((snd_pcm_sframes_t)runtime->silence_filled < 0) {
80 runtime->silence_filled = 0;
Clemens Ladisch9a826dd2006-10-23 16:26:57 +020081 runtime->silence_start = new_hw_ptr;
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 } else {
Clemens Ladisch9a826dd2006-10-23 16:26:57 +020083 runtime->silence_start = ofs;
Linus Torvalds1da177e2005-04-16 15:20:36 -070084 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070085 }
86 frames = runtime->buffer_size - runtime->silence_filled;
87 }
Takashi Iwai7eaa9432008-08-08 17:09:09 +020088 if (snd_BUG_ON(frames > runtime->buffer_size))
89 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -070090 if (frames == 0)
91 return;
Clemens Ladisch9a826dd2006-10-23 16:26:57 +020092 ofs = runtime->silence_start % runtime->buffer_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 while (frames > 0) {
94 transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames;
95 if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ||
96 runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) {
97 if (substream->ops->silence) {
98 int err;
99 err = substream->ops->silence(substream, -1, ofs, transfer);
Takashi Iwai7eaa9432008-08-08 17:09:09 +0200100 snd_BUG_ON(err < 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 } else {
102 char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs);
103 snd_pcm_format_set_silence(runtime->format, hwbuf, transfer * runtime->channels);
104 }
105 } else {
106 unsigned int c;
107 unsigned int channels = runtime->channels;
108 if (substream->ops->silence) {
109 for (c = 0; c < channels; ++c) {
110 int err;
111 err = substream->ops->silence(substream, c, ofs, transfer);
Takashi Iwai7eaa9432008-08-08 17:09:09 +0200112 snd_BUG_ON(err < 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 }
114 } else {
115 size_t dma_csize = runtime->dma_bytes / channels;
116 for (c = 0; c < channels; ++c) {
117 char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, ofs);
118 snd_pcm_format_set_silence(runtime->format, hwbuf, transfer);
119 }
120 }
121 }
122 runtime->silence_filled += transfer;
123 frames -= transfer;
124 ofs = 0;
125 }
126}
127
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100128#ifdef CONFIG_SND_PCM_XRUN_DEBUG
129#define xrun_debug(substream) ((substream)->pstr->xrun_debug)
130#else
131#define xrun_debug(substream) 0
132#endif
133
134#define dump_stack_on_xrun(substream) do { \
135 if (xrun_debug(substream) > 1) \
136 dump_stack(); \
137 } while (0)
138
Takashi Iwai877211f2005-11-17 13:59:38 +0100139static void xrun(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140{
141 snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100142 if (xrun_debug(substream)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n",
144 substream->pcm->card->number,
145 substream->pcm->device,
146 substream->stream ? 'c' : 'p');
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100147 dump_stack_on_xrun(substream);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149}
150
Takashi Iwai877211f2005-11-17 13:59:38 +0100151static inline snd_pcm_uframes_t snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream,
152 struct snd_pcm_runtime *runtime)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153{
154 snd_pcm_uframes_t pos;
155
Jaroslav Kysela8c121582008-01-11 08:45:08 +0100156 if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
157 snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 pos = substream->ops->pointer(substream);
159 if (pos == SNDRV_PCM_POS_XRUN)
160 return pos; /* XRUN */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161#ifdef CONFIG_SND_DEBUG
162 if (pos >= runtime->buffer_size) {
163 snd_printk(KERN_ERR "BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size);
Takashi Iwai7c22f1a2005-10-10 11:46:31 +0200164 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 pos -= pos % runtime->min_align;
167 return pos;
168}
169
Takashi Iwai877211f2005-11-17 13:59:38 +0100170static inline int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream,
171 struct snd_pcm_runtime *runtime)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172{
173 snd_pcm_uframes_t avail;
174
175 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
176 avail = snd_pcm_playback_avail(runtime);
177 else
178 avail = snd_pcm_capture_avail(runtime);
179 if (avail > runtime->avail_max)
180 runtime->avail_max = avail;
181 if (avail >= runtime->stop_threshold) {
182 if (substream->runtime->status->state == SNDRV_PCM_STATE_DRAINING)
183 snd_pcm_drain_done(substream);
184 else
185 xrun(substream);
186 return -EPIPE;
187 }
188 if (avail >= runtime->control->avail_min)
189 wake_up(&runtime->sleep);
190 return 0;
191}
192
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100193#define hw_ptr_error(substream, fmt, args...) \
194 do { \
195 if (xrun_debug(substream)) { \
196 if (printk_ratelimit()) { \
197 snd_printd("hda_codec: " fmt, ##args); \
198 } \
199 dump_stack_on_xrun(substream); \
200 } \
201 } while (0)
202
Takashi Iwai877211f2005-11-17 13:59:38 +0100203static inline int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204{
Takashi Iwai877211f2005-11-17 13:59:38 +0100205 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206 snd_pcm_uframes_t pos;
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100207 snd_pcm_uframes_t new_hw_ptr, hw_ptr_interrupt, hw_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208 snd_pcm_sframes_t delta;
209
210 pos = snd_pcm_update_hw_ptr_pos(substream, runtime);
211 if (pos == SNDRV_PCM_POS_XRUN) {
212 xrun(substream);
213 return -EPIPE;
214 }
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100215 hw_base = runtime->hw_ptr_base;
216 new_hw_ptr = hw_base + pos;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size;
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100218 delta = new_hw_ptr - hw_ptr_interrupt;
219 if (hw_ptr_interrupt == runtime->boundary)
220 hw_ptr_interrupt = 0;
221 if (delta < 0) {
222 delta += runtime->buffer_size;
223 if (delta < 0) {
224 hw_ptr_error(substream,
225 "Unexpected hw_pointer value "
226 "(stream=%i, pos=%ld, intr_ptr=%ld)\n",
227 substream->stream, (long)pos,
228 (long)hw_ptr_interrupt);
229 /* rebase to interrupt position */
230 hw_base = new_hw_ptr = hw_ptr_interrupt;
231 delta = 0;
232 } else {
233 hw_base += runtime->buffer_size;
234 if (hw_base == runtime->boundary)
235 hw_base = 0;
236 new_hw_ptr = hw_base + pos;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 }
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100239 if (delta > runtime->period_size) {
240 hw_ptr_error(substream,
241 "Lost interrupts? "
242 "(stream=%i, delta=%ld, intr_ptr=%ld)\n",
243 substream->stream, (long)delta,
244 (long)hw_ptr_interrupt);
245 /* rebase hw_ptr_interrupt */
246 hw_ptr_interrupt =
247 new_hw_ptr - new_hw_ptr % runtime->period_size;
248 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
250 runtime->silence_size > 0)
251 snd_pcm_playback_silence(substream, new_hw_ptr);
252
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100253 runtime->hw_ptr_base = hw_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 runtime->status->hw_ptr = new_hw_ptr;
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100255 runtime->hw_ptr_interrupt = hw_ptr_interrupt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256
257 return snd_pcm_update_hw_ptr_post(substream, runtime);
258}
259
260/* CAUTION: call it with irq disabled */
Takashi Iwai877211f2005-11-17 13:59:38 +0100261int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262{
Takashi Iwai877211f2005-11-17 13:59:38 +0100263 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 snd_pcm_uframes_t pos;
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100265 snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 snd_pcm_sframes_t delta;
267
268 old_hw_ptr = runtime->status->hw_ptr;
269 pos = snd_pcm_update_hw_ptr_pos(substream, runtime);
270 if (pos == SNDRV_PCM_POS_XRUN) {
271 xrun(substream);
272 return -EPIPE;
273 }
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100274 hw_base = runtime->hw_ptr_base;
275 new_hw_ptr = hw_base + pos;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100277 delta = new_hw_ptr - old_hw_ptr;
278 if (delta < 0) {
279 delta += runtime->buffer_size;
280 if (delta < 0) {
281 hw_ptr_error(substream,
282 "Unexpected hw_pointer value [2] "
283 "(stream=%i, pos=%ld, old_ptr=%ld)\n",
284 substream->stream, (long)pos,
285 (long)old_hw_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 return 0;
287 }
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100288 hw_base += runtime->buffer_size;
289 if (hw_base == runtime->boundary)
290 hw_base = 0;
291 new_hw_ptr = hw_base + pos;
292 }
293 if (delta > runtime->period_size && runtime->periods > 1) {
294 hw_ptr_error(substream,
295 "hw_ptr skipping! "
296 "(pos=%ld, delta=%ld, period=%ld)\n",
297 (long)pos, (long)delta,
298 (long)runtime->period_size);
299 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 }
301 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
302 runtime->silence_size > 0)
303 snd_pcm_playback_silence(substream, new_hw_ptr);
304
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100305 runtime->hw_ptr_base = hw_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 runtime->status->hw_ptr = new_hw_ptr;
307
308 return snd_pcm_update_hw_ptr_post(substream, runtime);
309}
310
311/**
312 * snd_pcm_set_ops - set the PCM operators
313 * @pcm: the pcm instance
314 * @direction: stream direction, SNDRV_PCM_STREAM_XXX
315 * @ops: the operator table
316 *
317 * Sets the given PCM operators to the pcm instance.
318 */
Takashi Iwai877211f2005-11-17 13:59:38 +0100319void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, struct snd_pcm_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320{
Takashi Iwai877211f2005-11-17 13:59:38 +0100321 struct snd_pcm_str *stream = &pcm->streams[direction];
322 struct snd_pcm_substream *substream;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323
324 for (substream = stream->substream; substream != NULL; substream = substream->next)
325 substream->ops = ops;
326}
327
Takashi Iwaie88e8ae62006-04-28 15:13:40 +0200328EXPORT_SYMBOL(snd_pcm_set_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329
330/**
331 * snd_pcm_sync - set the PCM sync id
332 * @substream: the pcm substream
333 *
334 * Sets the PCM sync identifier for the card.
335 */
Takashi Iwai877211f2005-11-17 13:59:38 +0100336void snd_pcm_set_sync(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337{
Takashi Iwai877211f2005-11-17 13:59:38 +0100338 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339
340 runtime->sync.id32[0] = substream->pcm->card->number;
341 runtime->sync.id32[1] = -1;
342 runtime->sync.id32[2] = -1;
343 runtime->sync.id32[3] = -1;
344}
345
Takashi Iwaie88e8ae62006-04-28 15:13:40 +0200346EXPORT_SYMBOL(snd_pcm_set_sync);
347
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348/*
349 * Standard ioctl routine
350 */
351
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352static inline unsigned int div32(unsigned int a, unsigned int b,
353 unsigned int *r)
354{
355 if (b == 0) {
356 *r = 0;
357 return UINT_MAX;
358 }
359 *r = a % b;
360 return a / b;
361}
362
363static inline unsigned int div_down(unsigned int a, unsigned int b)
364{
365 if (b == 0)
366 return UINT_MAX;
367 return a / b;
368}
369
370static inline unsigned int div_up(unsigned int a, unsigned int b)
371{
372 unsigned int r;
373 unsigned int q;
374 if (b == 0)
375 return UINT_MAX;
376 q = div32(a, b, &r);
377 if (r)
378 ++q;
379 return q;
380}
381
382static inline unsigned int mul(unsigned int a, unsigned int b)
383{
384 if (a == 0)
385 return 0;
386 if (div_down(UINT_MAX, a) < b)
387 return UINT_MAX;
388 return a * b;
389}
390
391static inline unsigned int muldiv32(unsigned int a, unsigned int b,
392 unsigned int c, unsigned int *r)
393{
394 u_int64_t n = (u_int64_t) a * b;
395 if (c == 0) {
Takashi Iwai7eaa9432008-08-08 17:09:09 +0200396 snd_BUG_ON(!n);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 *r = 0;
398 return UINT_MAX;
399 }
400 div64_32(&n, c, r);
401 if (n >= UINT_MAX) {
402 *r = 0;
403 return UINT_MAX;
404 }
405 return n;
406}
407
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408/**
409 * snd_interval_refine - refine the interval value of configurator
410 * @i: the interval value to refine
411 * @v: the interval value to refer to
412 *
413 * Refines the interval value with the reference value.
414 * The interval is changed to the range satisfying both intervals.
415 * The interval status (min, max, integer, etc.) are evaluated.
416 *
417 * Returns non-zero if the value is changed, zero if not changed.
418 */
Takashi Iwai877211f2005-11-17 13:59:38 +0100419int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420{
421 int changed = 0;
Takashi Iwai7eaa9432008-08-08 17:09:09 +0200422 if (snd_BUG_ON(snd_interval_empty(i)))
423 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 if (i->min < v->min) {
425 i->min = v->min;
426 i->openmin = v->openmin;
427 changed = 1;
428 } else if (i->min == v->min && !i->openmin && v->openmin) {
429 i->openmin = 1;
430 changed = 1;
431 }
432 if (i->max > v->max) {
433 i->max = v->max;
434 i->openmax = v->openmax;
435 changed = 1;
436 } else if (i->max == v->max && !i->openmax && v->openmax) {
437 i->openmax = 1;
438 changed = 1;
439 }
440 if (!i->integer && v->integer) {
441 i->integer = 1;
442 changed = 1;
443 }
444 if (i->integer) {
445 if (i->openmin) {
446 i->min++;
447 i->openmin = 0;
448 }
449 if (i->openmax) {
450 i->max--;
451 i->openmax = 0;
452 }
453 } else if (!i->openmin && !i->openmax && i->min == i->max)
454 i->integer = 1;
455 if (snd_interval_checkempty(i)) {
456 snd_interval_none(i);
457 return -EINVAL;
458 }
459 return changed;
460}
461
Takashi Iwaie88e8ae62006-04-28 15:13:40 +0200462EXPORT_SYMBOL(snd_interval_refine);
463
Takashi Iwai877211f2005-11-17 13:59:38 +0100464static int snd_interval_refine_first(struct snd_interval *i)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465{
Takashi Iwai7eaa9432008-08-08 17:09:09 +0200466 if (snd_BUG_ON(snd_interval_empty(i)))
467 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 if (snd_interval_single(i))
469 return 0;
470 i->max = i->min;
471 i->openmax = i->openmin;
472 if (i->openmax)
473 i->max++;
474 return 1;
475}
476
Takashi Iwai877211f2005-11-17 13:59:38 +0100477static int snd_interval_refine_last(struct snd_interval *i)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478{
Takashi Iwai7eaa9432008-08-08 17:09:09 +0200479 if (snd_BUG_ON(snd_interval_empty(i)))
480 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 if (snd_interval_single(i))
482 return 0;
483 i->min = i->max;
484 i->openmin = i->openmax;
485 if (i->openmin)
486 i->min--;
487 return 1;
488}
489
Takashi Iwai877211f2005-11-17 13:59:38 +0100490void snd_interval_mul(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491{
492 if (a->empty || b->empty) {
493 snd_interval_none(c);
494 return;
495 }
496 c->empty = 0;
497 c->min = mul(a->min, b->min);
498 c->openmin = (a->openmin || b->openmin);
499 c->max = mul(a->max, b->max);
500 c->openmax = (a->openmax || b->openmax);
501 c->integer = (a->integer && b->integer);
502}
503
504/**
505 * snd_interval_div - refine the interval value with division
Takashi Iwaidf8db932005-09-07 13:38:19 +0200506 * @a: dividend
507 * @b: divisor
508 * @c: quotient
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 *
510 * c = a / b
511 *
512 * Returns non-zero if the value is changed, zero if not changed.
513 */
Takashi Iwai877211f2005-11-17 13:59:38 +0100514void snd_interval_div(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515{
516 unsigned int r;
517 if (a->empty || b->empty) {
518 snd_interval_none(c);
519 return;
520 }
521 c->empty = 0;
522 c->min = div32(a->min, b->max, &r);
523 c->openmin = (r || a->openmin || b->openmax);
524 if (b->min > 0) {
525 c->max = div32(a->max, b->min, &r);
526 if (r) {
527 c->max++;
528 c->openmax = 1;
529 } else
530 c->openmax = (a->openmax || b->openmin);
531 } else {
532 c->max = UINT_MAX;
533 c->openmax = 0;
534 }
535 c->integer = 0;
536}
537
538/**
539 * snd_interval_muldivk - refine the interval value
Takashi Iwaidf8db932005-09-07 13:38:19 +0200540 * @a: dividend 1
541 * @b: dividend 2
542 * @k: divisor (as integer)
543 * @c: result
544 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 * c = a * b / k
546 *
547 * Returns non-zero if the value is changed, zero if not changed.
548 */
Takashi Iwai877211f2005-11-17 13:59:38 +0100549void snd_interval_muldivk(const struct snd_interval *a, const struct snd_interval *b,
550 unsigned int k, struct snd_interval *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551{
552 unsigned int r;
553 if (a->empty || b->empty) {
554 snd_interval_none(c);
555 return;
556 }
557 c->empty = 0;
558 c->min = muldiv32(a->min, b->min, k, &r);
559 c->openmin = (r || a->openmin || b->openmin);
560 c->max = muldiv32(a->max, b->max, k, &r);
561 if (r) {
562 c->max++;
563 c->openmax = 1;
564 } else
565 c->openmax = (a->openmax || b->openmax);
566 c->integer = 0;
567}
568
569/**
570 * snd_interval_mulkdiv - refine the interval value
Takashi Iwaidf8db932005-09-07 13:38:19 +0200571 * @a: dividend 1
572 * @k: dividend 2 (as integer)
573 * @b: divisor
574 * @c: result
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 *
576 * c = a * k / b
577 *
578 * Returns non-zero if the value is changed, zero if not changed.
579 */
Takashi Iwai877211f2005-11-17 13:59:38 +0100580void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k,
581 const struct snd_interval *b, struct snd_interval *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582{
583 unsigned int r;
584 if (a->empty || b->empty) {
585 snd_interval_none(c);
586 return;
587 }
588 c->empty = 0;
589 c->min = muldiv32(a->min, k, b->max, &r);
590 c->openmin = (r || a->openmin || b->openmax);
591 if (b->min > 0) {
592 c->max = muldiv32(a->max, k, b->min, &r);
593 if (r) {
594 c->max++;
595 c->openmax = 1;
596 } else
597 c->openmax = (a->openmax || b->openmin);
598 } else {
599 c->max = UINT_MAX;
600 c->openmax = 0;
601 }
602 c->integer = 0;
603}
604
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605/* ---- */
606
607
608/**
609 * snd_interval_ratnum - refine the interval value
Takashi Iwaidf8db932005-09-07 13:38:19 +0200610 * @i: interval to refine
611 * @rats_count: number of ratnum_t
612 * @rats: ratnum_t array
613 * @nump: pointer to store the resultant numerator
614 * @denp: pointer to store the resultant denominator
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615 *
616 * Returns non-zero if the value is changed, zero if not changed.
617 */
Takashi Iwai877211f2005-11-17 13:59:38 +0100618int snd_interval_ratnum(struct snd_interval *i,
619 unsigned int rats_count, struct snd_ratnum *rats,
620 unsigned int *nump, unsigned int *denp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621{
622 unsigned int best_num, best_diff, best_den;
623 unsigned int k;
Takashi Iwai877211f2005-11-17 13:59:38 +0100624 struct snd_interval t;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 int err;
626
627 best_num = best_den = best_diff = 0;
628 for (k = 0; k < rats_count; ++k) {
629 unsigned int num = rats[k].num;
630 unsigned int den;
631 unsigned int q = i->min;
632 int diff;
633 if (q == 0)
634 q = 1;
635 den = div_down(num, q);
636 if (den < rats[k].den_min)
637 continue;
638 if (den > rats[k].den_max)
639 den = rats[k].den_max;
640 else {
641 unsigned int r;
642 r = (den - rats[k].den_min) % rats[k].den_step;
643 if (r != 0)
644 den -= r;
645 }
646 diff = num - q * den;
647 if (best_num == 0 ||
648 diff * best_den < best_diff * den) {
649 best_diff = diff;
650 best_den = den;
651 best_num = num;
652 }
653 }
654 if (best_den == 0) {
655 i->empty = 1;
656 return -EINVAL;
657 }
658 t.min = div_down(best_num, best_den);
659 t.openmin = !!(best_num % best_den);
660
661 best_num = best_den = best_diff = 0;
662 for (k = 0; k < rats_count; ++k) {
663 unsigned int num = rats[k].num;
664 unsigned int den;
665 unsigned int q = i->max;
666 int diff;
667 if (q == 0) {
668 i->empty = 1;
669 return -EINVAL;
670 }
671 den = div_up(num, q);
672 if (den > rats[k].den_max)
673 continue;
674 if (den < rats[k].den_min)
675 den = rats[k].den_min;
676 else {
677 unsigned int r;
678 r = (den - rats[k].den_min) % rats[k].den_step;
679 if (r != 0)
680 den += rats[k].den_step - r;
681 }
682 diff = q * den - num;
683 if (best_num == 0 ||
684 diff * best_den < best_diff * den) {
685 best_diff = diff;
686 best_den = den;
687 best_num = num;
688 }
689 }
690 if (best_den == 0) {
691 i->empty = 1;
692 return -EINVAL;
693 }
694 t.max = div_up(best_num, best_den);
695 t.openmax = !!(best_num % best_den);
696 t.integer = 0;
697 err = snd_interval_refine(i, &t);
698 if (err < 0)
699 return err;
700
701 if (snd_interval_single(i)) {
702 if (nump)
703 *nump = best_num;
704 if (denp)
705 *denp = best_den;
706 }
707 return err;
708}
709
Takashi Iwaie88e8ae62006-04-28 15:13:40 +0200710EXPORT_SYMBOL(snd_interval_ratnum);
711
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712/**
713 * snd_interval_ratden - refine the interval value
Takashi Iwaidf8db932005-09-07 13:38:19 +0200714 * @i: interval to refine
Takashi Iwai877211f2005-11-17 13:59:38 +0100715 * @rats_count: number of struct ratden
716 * @rats: struct ratden array
Takashi Iwaidf8db932005-09-07 13:38:19 +0200717 * @nump: pointer to store the resultant numerator
718 * @denp: pointer to store the resultant denominator
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719 *
720 * Returns non-zero if the value is changed, zero if not changed.
721 */
Takashi Iwai877211f2005-11-17 13:59:38 +0100722static int snd_interval_ratden(struct snd_interval *i,
723 unsigned int rats_count, struct snd_ratden *rats,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 unsigned int *nump, unsigned int *denp)
725{
726 unsigned int best_num, best_diff, best_den;
727 unsigned int k;
Takashi Iwai877211f2005-11-17 13:59:38 +0100728 struct snd_interval t;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 int err;
730
731 best_num = best_den = best_diff = 0;
732 for (k = 0; k < rats_count; ++k) {
733 unsigned int num;
734 unsigned int den = rats[k].den;
735 unsigned int q = i->min;
736 int diff;
737 num = mul(q, den);
738 if (num > rats[k].num_max)
739 continue;
740 if (num < rats[k].num_min)
741 num = rats[k].num_max;
742 else {
743 unsigned int r;
744 r = (num - rats[k].num_min) % rats[k].num_step;
745 if (r != 0)
746 num += rats[k].num_step - r;
747 }
748 diff = num - q * den;
749 if (best_num == 0 ||
750 diff * best_den < best_diff * den) {
751 best_diff = diff;
752 best_den = den;
753 best_num = num;
754 }
755 }
756 if (best_den == 0) {
757 i->empty = 1;
758 return -EINVAL;
759 }
760 t.min = div_down(best_num, best_den);
761 t.openmin = !!(best_num % best_den);
762
763 best_num = best_den = best_diff = 0;
764 for (k = 0; k < rats_count; ++k) {
765 unsigned int num;
766 unsigned int den = rats[k].den;
767 unsigned int q = i->max;
768 int diff;
769 num = mul(q, den);
770 if (num < rats[k].num_min)
771 continue;
772 if (num > rats[k].num_max)
773 num = rats[k].num_max;
774 else {
775 unsigned int r;
776 r = (num - rats[k].num_min) % rats[k].num_step;
777 if (r != 0)
778 num -= r;
779 }
780 diff = q * den - num;
781 if (best_num == 0 ||
782 diff * best_den < best_diff * den) {
783 best_diff = diff;
784 best_den = den;
785 best_num = num;
786 }
787 }
788 if (best_den == 0) {
789 i->empty = 1;
790 return -EINVAL;
791 }
792 t.max = div_up(best_num, best_den);
793 t.openmax = !!(best_num % best_den);
794 t.integer = 0;
795 err = snd_interval_refine(i, &t);
796 if (err < 0)
797 return err;
798
799 if (snd_interval_single(i)) {
800 if (nump)
801 *nump = best_num;
802 if (denp)
803 *denp = best_den;
804 }
805 return err;
806}
807
808/**
809 * snd_interval_list - refine the interval value from the list
810 * @i: the interval value to refine
811 * @count: the number of elements in the list
812 * @list: the value list
813 * @mask: the bit-mask to evaluate
814 *
815 * Refines the interval value from the list.
816 * When mask is non-zero, only the elements corresponding to bit 1 are
817 * evaluated.
818 *
819 * Returns non-zero if the value is changed, zero if not changed.
820 */
Takashi Iwai877211f2005-11-17 13:59:38 +0100821int snd_interval_list(struct snd_interval *i, unsigned int count, unsigned int *list, unsigned int mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822{
823 unsigned int k;
824 int changed = 0;
Takashi Iwai0981a262007-02-01 14:53:49 +0100825
826 if (!count) {
827 i->empty = 1;
828 return -EINVAL;
829 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 for (k = 0; k < count; k++) {
831 if (mask && !(mask & (1 << k)))
832 continue;
833 if (i->min == list[k] && !i->openmin)
834 goto _l1;
835 if (i->min < list[k]) {
836 i->min = list[k];
837 i->openmin = 0;
838 changed = 1;
839 goto _l1;
840 }
841 }
842 i->empty = 1;
843 return -EINVAL;
844 _l1:
845 for (k = count; k-- > 0;) {
846 if (mask && !(mask & (1 << k)))
847 continue;
848 if (i->max == list[k] && !i->openmax)
849 goto _l2;
850 if (i->max > list[k]) {
851 i->max = list[k];
852 i->openmax = 0;
853 changed = 1;
854 goto _l2;
855 }
856 }
857 i->empty = 1;
858 return -EINVAL;
859 _l2:
860 if (snd_interval_checkempty(i)) {
861 i->empty = 1;
862 return -EINVAL;
863 }
864 return changed;
865}
866
Takashi Iwaie88e8ae62006-04-28 15:13:40 +0200867EXPORT_SYMBOL(snd_interval_list);
868
Takashi Iwai877211f2005-11-17 13:59:38 +0100869static int snd_interval_step(struct snd_interval *i, unsigned int min, unsigned int step)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870{
871 unsigned int n;
872 int changed = 0;
873 n = (i->min - min) % step;
874 if (n != 0 || i->openmin) {
875 i->min += step - n;
876 changed = 1;
877 }
878 n = (i->max - min) % step;
879 if (n != 0 || i->openmax) {
880 i->max -= n;
881 changed = 1;
882 }
883 if (snd_interval_checkempty(i)) {
884 i->empty = 1;
885 return -EINVAL;
886 }
887 return changed;
888}
889
890/* Info constraints helpers */
891
892/**
893 * snd_pcm_hw_rule_add - add the hw-constraint rule
894 * @runtime: the pcm runtime instance
895 * @cond: condition bits
896 * @var: the variable to evaluate
897 * @func: the evaluation function
898 * @private: the private data pointer passed to function
899 * @dep: the dependent variables
900 *
901 * Returns zero if successful, or a negative error code on failure.
902 */
Takashi Iwai877211f2005-11-17 13:59:38 +0100903int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904 int var,
905 snd_pcm_hw_rule_func_t func, void *private,
906 int dep, ...)
907{
Takashi Iwai877211f2005-11-17 13:59:38 +0100908 struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;
909 struct snd_pcm_hw_rule *c;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910 unsigned int k;
911 va_list args;
912 va_start(args, dep);
913 if (constrs->rules_num >= constrs->rules_all) {
Takashi Iwai877211f2005-11-17 13:59:38 +0100914 struct snd_pcm_hw_rule *new;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915 unsigned int new_rules = constrs->rules_all + 16;
916 new = kcalloc(new_rules, sizeof(*c), GFP_KERNEL);
917 if (!new)
918 return -ENOMEM;
919 if (constrs->rules) {
920 memcpy(new, constrs->rules,
921 constrs->rules_num * sizeof(*c));
922 kfree(constrs->rules);
923 }
924 constrs->rules = new;
925 constrs->rules_all = new_rules;
926 }
927 c = &constrs->rules[constrs->rules_num];
928 c->cond = cond;
929 c->func = func;
930 c->var = var;
931 c->private = private;
932 k = 0;
933 while (1) {
Takashi Iwai7eaa9432008-08-08 17:09:09 +0200934 if (snd_BUG_ON(k >= ARRAY_SIZE(c->deps)))
935 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 c->deps[k++] = dep;
937 if (dep < 0)
938 break;
939 dep = va_arg(args, int);
940 }
941 constrs->rules_num++;
942 va_end(args);
943 return 0;
944}
945
Takashi Iwaie88e8ae62006-04-28 15:13:40 +0200946EXPORT_SYMBOL(snd_pcm_hw_rule_add);
947
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -0700949 * snd_pcm_hw_constraint_mask - apply the given bitmap mask constraint
Takashi Iwaidf8db932005-09-07 13:38:19 +0200950 * @runtime: PCM runtime instance
951 * @var: hw_params variable to apply the mask
952 * @mask: the bitmap mask
953 *
Randy Dunlap1c85cc62008-10-15 14:38:40 -0700954 * Apply the constraint of the given bitmap mask to a 32-bit mask parameter.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955 */
Takashi Iwai877211f2005-11-17 13:59:38 +0100956int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957 u_int32_t mask)
958{
Takashi Iwai877211f2005-11-17 13:59:38 +0100959 struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;
960 struct snd_mask *maskp = constrs_mask(constrs, var);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961 *maskp->bits &= mask;
962 memset(maskp->bits + 1, 0, (SNDRV_MASK_MAX-32) / 8); /* clear rest */
963 if (*maskp->bits == 0)
964 return -EINVAL;
965 return 0;
966}
967
968/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -0700969 * snd_pcm_hw_constraint_mask64 - apply the given bitmap mask constraint
Takashi Iwaidf8db932005-09-07 13:38:19 +0200970 * @runtime: PCM runtime instance
971 * @var: hw_params variable to apply the mask
972 * @mask: the 64bit bitmap mask
973 *
Randy Dunlap1c85cc62008-10-15 14:38:40 -0700974 * Apply the constraint of the given bitmap mask to a 64-bit mask parameter.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975 */
Takashi Iwai877211f2005-11-17 13:59:38 +0100976int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 u_int64_t mask)
978{
Takashi Iwai877211f2005-11-17 13:59:38 +0100979 struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;
980 struct snd_mask *maskp = constrs_mask(constrs, var);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 maskp->bits[0] &= (u_int32_t)mask;
982 maskp->bits[1] &= (u_int32_t)(mask >> 32);
983 memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */
984 if (! maskp->bits[0] && ! maskp->bits[1])
985 return -EINVAL;
986 return 0;
987}
988
989/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -0700990 * snd_pcm_hw_constraint_integer - apply an integer constraint to an interval
Takashi Iwaidf8db932005-09-07 13:38:19 +0200991 * @runtime: PCM runtime instance
992 * @var: hw_params variable to apply the integer constraint
993 *
994 * Apply the constraint of integer to an interval parameter.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995 */
Takashi Iwai877211f2005-11-17 13:59:38 +0100996int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997{
Takashi Iwai877211f2005-11-17 13:59:38 +0100998 struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 return snd_interval_setinteger(constrs_interval(constrs, var));
1000}
1001
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001002EXPORT_SYMBOL(snd_pcm_hw_constraint_integer);
1003
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001005 * snd_pcm_hw_constraint_minmax - apply a min/max range constraint to an interval
Takashi Iwaidf8db932005-09-07 13:38:19 +02001006 * @runtime: PCM runtime instance
1007 * @var: hw_params variable to apply the range
1008 * @min: the minimal value
1009 * @max: the maximal value
1010 *
1011 * Apply the min/max range constraint to an interval parameter.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012 */
Takashi Iwai877211f2005-11-17 13:59:38 +01001013int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014 unsigned int min, unsigned int max)
1015{
Takashi Iwai877211f2005-11-17 13:59:38 +01001016 struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;
1017 struct snd_interval t;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018 t.min = min;
1019 t.max = max;
1020 t.openmin = t.openmax = 0;
1021 t.integer = 0;
1022 return snd_interval_refine(constrs_interval(constrs, var), &t);
1023}
1024
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001025EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax);
1026
Takashi Iwai877211f2005-11-17 13:59:38 +01001027static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params,
1028 struct snd_pcm_hw_rule *rule)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029{
Takashi Iwai877211f2005-11-17 13:59:38 +01001030 struct snd_pcm_hw_constraint_list *list = rule->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 return snd_interval_list(hw_param_interval(params, rule->var), list->count, list->list, list->mask);
1032}
1033
1034
1035/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001036 * snd_pcm_hw_constraint_list - apply a list of constraints to a parameter
Takashi Iwaidf8db932005-09-07 13:38:19 +02001037 * @runtime: PCM runtime instance
1038 * @cond: condition bits
1039 * @var: hw_params variable to apply the list constraint
1040 * @l: list
1041 *
1042 * Apply the list of constraints to an interval parameter.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043 */
Takashi Iwai877211f2005-11-17 13:59:38 +01001044int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045 unsigned int cond,
1046 snd_pcm_hw_param_t var,
Takashi Iwai877211f2005-11-17 13:59:38 +01001047 struct snd_pcm_hw_constraint_list *l)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048{
1049 return snd_pcm_hw_rule_add(runtime, cond, var,
1050 snd_pcm_hw_rule_list, l,
1051 var, -1);
1052}
1053
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001054EXPORT_SYMBOL(snd_pcm_hw_constraint_list);
1055
Takashi Iwai877211f2005-11-17 13:59:38 +01001056static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params,
1057 struct snd_pcm_hw_rule *rule)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058{
Takashi Iwai877211f2005-11-17 13:59:38 +01001059 struct snd_pcm_hw_constraint_ratnums *r = rule->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 unsigned int num = 0, den = 0;
1061 int err;
1062 err = snd_interval_ratnum(hw_param_interval(params, rule->var),
1063 r->nrats, r->rats, &num, &den);
1064 if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) {
1065 params->rate_num = num;
1066 params->rate_den = den;
1067 }
1068 return err;
1069}
1070
1071/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001072 * snd_pcm_hw_constraint_ratnums - apply ratnums constraint to a parameter
Takashi Iwaidf8db932005-09-07 13:38:19 +02001073 * @runtime: PCM runtime instance
1074 * @cond: condition bits
1075 * @var: hw_params variable to apply the ratnums constraint
Takashi Iwai877211f2005-11-17 13:59:38 +01001076 * @r: struct snd_ratnums constriants
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 */
Takashi Iwai877211f2005-11-17 13:59:38 +01001078int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 unsigned int cond,
1080 snd_pcm_hw_param_t var,
Takashi Iwai877211f2005-11-17 13:59:38 +01001081 struct snd_pcm_hw_constraint_ratnums *r)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082{
1083 return snd_pcm_hw_rule_add(runtime, cond, var,
1084 snd_pcm_hw_rule_ratnums, r,
1085 var, -1);
1086}
1087
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001088EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums);
1089
Takashi Iwai877211f2005-11-17 13:59:38 +01001090static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params,
1091 struct snd_pcm_hw_rule *rule)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092{
Takashi Iwai877211f2005-11-17 13:59:38 +01001093 struct snd_pcm_hw_constraint_ratdens *r = rule->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094 unsigned int num = 0, den = 0;
1095 int err = snd_interval_ratden(hw_param_interval(params, rule->var),
1096 r->nrats, r->rats, &num, &den);
1097 if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) {
1098 params->rate_num = num;
1099 params->rate_den = den;
1100 }
1101 return err;
1102}
1103
1104/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001105 * snd_pcm_hw_constraint_ratdens - apply ratdens constraint to a parameter
Takashi Iwaidf8db932005-09-07 13:38:19 +02001106 * @runtime: PCM runtime instance
1107 * @cond: condition bits
1108 * @var: hw_params variable to apply the ratdens constraint
Takashi Iwai877211f2005-11-17 13:59:38 +01001109 * @r: struct snd_ratdens constriants
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 */
Takashi Iwai877211f2005-11-17 13:59:38 +01001111int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112 unsigned int cond,
1113 snd_pcm_hw_param_t var,
Takashi Iwai877211f2005-11-17 13:59:38 +01001114 struct snd_pcm_hw_constraint_ratdens *r)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115{
1116 return snd_pcm_hw_rule_add(runtime, cond, var,
1117 snd_pcm_hw_rule_ratdens, r,
1118 var, -1);
1119}
1120
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001121EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens);
1122
Takashi Iwai877211f2005-11-17 13:59:38 +01001123static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params,
1124 struct snd_pcm_hw_rule *rule)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125{
1126 unsigned int l = (unsigned long) rule->private;
1127 int width = l & 0xffff;
1128 unsigned int msbits = l >> 16;
Takashi Iwai877211f2005-11-17 13:59:38 +01001129 struct snd_interval *i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130 if (snd_interval_single(i) && snd_interval_value(i) == width)
1131 params->msbits = msbits;
1132 return 0;
1133}
1134
1135/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001136 * snd_pcm_hw_constraint_msbits - add a hw constraint msbits rule
Takashi Iwaidf8db932005-09-07 13:38:19 +02001137 * @runtime: PCM runtime instance
1138 * @cond: condition bits
1139 * @width: sample bits width
1140 * @msbits: msbits width
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 */
Takashi Iwai877211f2005-11-17 13:59:38 +01001142int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143 unsigned int cond,
1144 unsigned int width,
1145 unsigned int msbits)
1146{
1147 unsigned long l = (msbits << 16) | width;
1148 return snd_pcm_hw_rule_add(runtime, cond, -1,
1149 snd_pcm_hw_rule_msbits,
1150 (void*) l,
1151 SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
1152}
1153
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001154EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits);
1155
Takashi Iwai877211f2005-11-17 13:59:38 +01001156static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params,
1157 struct snd_pcm_hw_rule *rule)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158{
1159 unsigned long step = (unsigned long) rule->private;
1160 return snd_interval_step(hw_param_interval(params, rule->var), 0, step);
1161}
1162
1163/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001164 * snd_pcm_hw_constraint_step - add a hw constraint step rule
Takashi Iwaidf8db932005-09-07 13:38:19 +02001165 * @runtime: PCM runtime instance
1166 * @cond: condition bits
1167 * @var: hw_params variable to apply the step constraint
1168 * @step: step size
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 */
Takashi Iwai877211f2005-11-17 13:59:38 +01001170int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171 unsigned int cond,
1172 snd_pcm_hw_param_t var,
1173 unsigned long step)
1174{
1175 return snd_pcm_hw_rule_add(runtime, cond, var,
1176 snd_pcm_hw_rule_step, (void *) step,
1177 var, -1);
1178}
1179
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001180EXPORT_SYMBOL(snd_pcm_hw_constraint_step);
1181
Takashi Iwai877211f2005-11-17 13:59:38 +01001182static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183{
Marcin Åšlusarz67c39312007-12-14 12:53:21 +01001184 static unsigned int pow2_sizes[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185 1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7,
1186 1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15,
1187 1<<16, 1<<17, 1<<18, 1<<19, 1<<20, 1<<21, 1<<22, 1<<23,
1188 1<<24, 1<<25, 1<<26, 1<<27, 1<<28, 1<<29, 1<<30
1189 };
1190 return snd_interval_list(hw_param_interval(params, rule->var),
1191 ARRAY_SIZE(pow2_sizes), pow2_sizes, 0);
1192}
1193
1194/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001195 * snd_pcm_hw_constraint_pow2 - add a hw constraint power-of-2 rule
Takashi Iwaidf8db932005-09-07 13:38:19 +02001196 * @runtime: PCM runtime instance
1197 * @cond: condition bits
1198 * @var: hw_params variable to apply the power-of-2 constraint
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 */
Takashi Iwai877211f2005-11-17 13:59:38 +01001200int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201 unsigned int cond,
1202 snd_pcm_hw_param_t var)
1203{
1204 return snd_pcm_hw_rule_add(runtime, cond, var,
1205 snd_pcm_hw_rule_pow2, NULL,
1206 var, -1);
1207}
1208
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001209EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2);
1210
Takashi Iwai877211f2005-11-17 13:59:38 +01001211static void _snd_pcm_hw_param_any(struct snd_pcm_hw_params *params,
Adrian Bunk123992f2005-05-18 18:02:04 +02001212 snd_pcm_hw_param_t var)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213{
1214 if (hw_is_mask(var)) {
1215 snd_mask_any(hw_param_mask(params, var));
1216 params->cmask |= 1 << var;
1217 params->rmask |= 1 << var;
1218 return;
1219 }
1220 if (hw_is_interval(var)) {
1221 snd_interval_any(hw_param_interval(params, var));
1222 params->cmask |= 1 << var;
1223 params->rmask |= 1 << var;
1224 return;
1225 }
1226 snd_BUG();
1227}
1228
Takashi Iwai877211f2005-11-17 13:59:38 +01001229void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230{
1231 unsigned int k;
1232 memset(params, 0, sizeof(*params));
1233 for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++)
1234 _snd_pcm_hw_param_any(params, k);
1235 for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++)
1236 _snd_pcm_hw_param_any(params, k);
1237 params->info = ~0U;
1238}
1239
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001240EXPORT_SYMBOL(_snd_pcm_hw_params_any);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241
1242/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001243 * snd_pcm_hw_param_value - return @params field @var value
Takashi Iwaidf8db932005-09-07 13:38:19 +02001244 * @params: the hw_params instance
1245 * @var: parameter to retrieve
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001246 * @dir: pointer to the direction (-1,0,1) or %NULL
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247 *
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001248 * Return the value for field @var if it's fixed in configuration space
1249 * defined by @params. Return -%EINVAL otherwise.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250 */
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001251int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params,
1252 snd_pcm_hw_param_t var, int *dir)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253{
1254 if (hw_is_mask(var)) {
Takashi Iwai877211f2005-11-17 13:59:38 +01001255 const struct snd_mask *mask = hw_param_mask_c(params, var);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001256 if (!snd_mask_single(mask))
1257 return -EINVAL;
1258 if (dir)
1259 *dir = 0;
1260 return snd_mask_value(mask);
1261 }
1262 if (hw_is_interval(var)) {
Takashi Iwai877211f2005-11-17 13:59:38 +01001263 const struct snd_interval *i = hw_param_interval_c(params, var);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264 if (!snd_interval_single(i))
1265 return -EINVAL;
1266 if (dir)
1267 *dir = i->openmin;
1268 return snd_interval_value(i);
1269 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270 return -EINVAL;
1271}
1272
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001273EXPORT_SYMBOL(snd_pcm_hw_param_value);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274
Takashi Iwai877211f2005-11-17 13:59:38 +01001275void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276 snd_pcm_hw_param_t var)
1277{
1278 if (hw_is_mask(var)) {
1279 snd_mask_none(hw_param_mask(params, var));
1280 params->cmask |= 1 << var;
1281 params->rmask |= 1 << var;
1282 } else if (hw_is_interval(var)) {
1283 snd_interval_none(hw_param_interval(params, var));
1284 params->cmask |= 1 << var;
1285 params->rmask |= 1 << var;
1286 } else {
1287 snd_BUG();
1288 }
1289}
1290
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001291EXPORT_SYMBOL(_snd_pcm_hw_param_setempty);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292
Takashi Iwai877211f2005-11-17 13:59:38 +01001293static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params,
Adrian Bunk123992f2005-05-18 18:02:04 +02001294 snd_pcm_hw_param_t var)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295{
1296 int changed;
1297 if (hw_is_mask(var))
1298 changed = snd_mask_refine_first(hw_param_mask(params, var));
1299 else if (hw_is_interval(var))
1300 changed = snd_interval_refine_first(hw_param_interval(params, var));
Takashi Iwai2f4ca8e2006-04-28 15:13:40 +02001301 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303 if (changed) {
1304 params->cmask |= 1 << var;
1305 params->rmask |= 1 << var;
1306 }
1307 return changed;
1308}
1309
1310
1311/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001312 * snd_pcm_hw_param_first - refine config space and return minimum value
Takashi Iwaidf8db932005-09-07 13:38:19 +02001313 * @pcm: PCM instance
1314 * @params: the hw_params instance
1315 * @var: parameter to retrieve
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001316 * @dir: pointer to the direction (-1,0,1) or %NULL
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 *
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001318 * Inside configuration space defined by @params remove from @var all
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319 * values > minimum. Reduce configuration space accordingly.
1320 * Return the minimum.
1321 */
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001322int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm,
1323 struct snd_pcm_hw_params *params,
1324 snd_pcm_hw_param_t var, int *dir)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001325{
1326 int changed = _snd_pcm_hw_param_first(params, var);
1327 if (changed < 0)
1328 return changed;
1329 if (params->rmask) {
1330 int err = snd_pcm_hw_refine(pcm, params);
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001331 if (snd_BUG_ON(err < 0))
1332 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333 }
1334 return snd_pcm_hw_param_value(params, var, dir);
1335}
1336
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001337EXPORT_SYMBOL(snd_pcm_hw_param_first);
1338
Takashi Iwai877211f2005-11-17 13:59:38 +01001339static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params,
Adrian Bunk123992f2005-05-18 18:02:04 +02001340 snd_pcm_hw_param_t var)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341{
1342 int changed;
1343 if (hw_is_mask(var))
1344 changed = snd_mask_refine_last(hw_param_mask(params, var));
1345 else if (hw_is_interval(var))
1346 changed = snd_interval_refine_last(hw_param_interval(params, var));
Takashi Iwai2f4ca8e2006-04-28 15:13:40 +02001347 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349 if (changed) {
1350 params->cmask |= 1 << var;
1351 params->rmask |= 1 << var;
1352 }
1353 return changed;
1354}
1355
1356
1357/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001358 * snd_pcm_hw_param_last - refine config space and return maximum value
Takashi Iwaidf8db932005-09-07 13:38:19 +02001359 * @pcm: PCM instance
1360 * @params: the hw_params instance
1361 * @var: parameter to retrieve
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001362 * @dir: pointer to the direction (-1,0,1) or %NULL
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 *
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001364 * Inside configuration space defined by @params remove from @var all
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365 * values < maximum. Reduce configuration space accordingly.
1366 * Return the maximum.
1367 */
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001368int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm,
1369 struct snd_pcm_hw_params *params,
1370 snd_pcm_hw_param_t var, int *dir)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371{
1372 int changed = _snd_pcm_hw_param_last(params, var);
1373 if (changed < 0)
1374 return changed;
1375 if (params->rmask) {
1376 int err = snd_pcm_hw_refine(pcm, params);
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001377 if (snd_BUG_ON(err < 0))
1378 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379 }
1380 return snd_pcm_hw_param_value(params, var, dir);
1381}
1382
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001383EXPORT_SYMBOL(snd_pcm_hw_param_last);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384
1385/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001386 * snd_pcm_hw_param_choose - choose a configuration defined by @params
Takashi Iwaidf8db932005-09-07 13:38:19 +02001387 * @pcm: PCM instance
1388 * @params: the hw_params instance
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389 *
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001390 * Choose one configuration from configuration space defined by @params.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001391 * The configuration chosen is that obtained fixing in this order:
1392 * first access, first format, first subformat, min channels,
1393 * min rate, min period time, max buffer size, min tick time
1394 */
Takashi Iwai2f4ca8e2006-04-28 15:13:40 +02001395int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm,
1396 struct snd_pcm_hw_params *params)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397{
Takashi Iwai2f4ca8e2006-04-28 15:13:40 +02001398 static int vars[] = {
1399 SNDRV_PCM_HW_PARAM_ACCESS,
1400 SNDRV_PCM_HW_PARAM_FORMAT,
1401 SNDRV_PCM_HW_PARAM_SUBFORMAT,
1402 SNDRV_PCM_HW_PARAM_CHANNELS,
1403 SNDRV_PCM_HW_PARAM_RATE,
1404 SNDRV_PCM_HW_PARAM_PERIOD_TIME,
1405 SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
1406 SNDRV_PCM_HW_PARAM_TICK_TIME,
1407 -1
1408 };
1409 int err, *v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410
Takashi Iwai2f4ca8e2006-04-28 15:13:40 +02001411 for (v = vars; *v != -1; v++) {
1412 if (*v != SNDRV_PCM_HW_PARAM_BUFFER_SIZE)
1413 err = snd_pcm_hw_param_first(pcm, params, *v, NULL);
1414 else
1415 err = snd_pcm_hw_param_last(pcm, params, *v, NULL);
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001416 if (snd_BUG_ON(err < 0))
1417 return err;
Takashi Iwai2f4ca8e2006-04-28 15:13:40 +02001418 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419 return 0;
1420}
1421
Takashi Iwai877211f2005-11-17 13:59:38 +01001422static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423 void *arg)
1424{
Takashi Iwai877211f2005-11-17 13:59:38 +01001425 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426 unsigned long flags;
1427 snd_pcm_stream_lock_irqsave(substream, flags);
1428 if (snd_pcm_running(substream) &&
1429 snd_pcm_update_hw_ptr(substream) >= 0)
1430 runtime->status->hw_ptr %= runtime->buffer_size;
1431 else
1432 runtime->status->hw_ptr = 0;
1433 snd_pcm_stream_unlock_irqrestore(substream, flags);
1434 return 0;
1435}
1436
Takashi Iwai877211f2005-11-17 13:59:38 +01001437static int snd_pcm_lib_ioctl_channel_info(struct snd_pcm_substream *substream,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438 void *arg)
1439{
Takashi Iwai877211f2005-11-17 13:59:38 +01001440 struct snd_pcm_channel_info *info = arg;
1441 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442 int width;
1443 if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) {
1444 info->offset = -1;
1445 return 0;
1446 }
1447 width = snd_pcm_format_physical_width(runtime->format);
1448 if (width < 0)
1449 return width;
1450 info->offset = 0;
1451 switch (runtime->access) {
1452 case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED:
1453 case SNDRV_PCM_ACCESS_RW_INTERLEAVED:
1454 info->first = info->channel * width;
1455 info->step = runtime->channels * width;
1456 break;
1457 case SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED:
1458 case SNDRV_PCM_ACCESS_RW_NONINTERLEAVED:
1459 {
1460 size_t size = runtime->dma_bytes / runtime->channels;
1461 info->first = info->channel * size * 8;
1462 info->step = width;
1463 break;
1464 }
1465 default:
1466 snd_BUG();
1467 break;
1468 }
1469 return 0;
1470}
1471
1472/**
1473 * snd_pcm_lib_ioctl - a generic PCM ioctl callback
1474 * @substream: the pcm substream instance
1475 * @cmd: ioctl command
1476 * @arg: ioctl argument
1477 *
1478 * Processes the generic ioctl commands for PCM.
1479 * Can be passed as the ioctl callback for PCM ops.
1480 *
1481 * Returns zero if successful, or a negative error code on failure.
1482 */
Takashi Iwai877211f2005-11-17 13:59:38 +01001483int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001484 unsigned int cmd, void *arg)
1485{
1486 switch (cmd) {
1487 case SNDRV_PCM_IOCTL1_INFO:
1488 return 0;
1489 case SNDRV_PCM_IOCTL1_RESET:
1490 return snd_pcm_lib_ioctl_reset(substream, arg);
1491 case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
1492 return snd_pcm_lib_ioctl_channel_info(substream, arg);
1493 }
1494 return -ENXIO;
1495}
1496
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001497EXPORT_SYMBOL(snd_pcm_lib_ioctl);
1498
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499/**
1500 * snd_pcm_period_elapsed - update the pcm status for the next period
1501 * @substream: the pcm substream instance
1502 *
1503 * This function is called from the interrupt handler when the
1504 * PCM has processed the period size. It will update the current
Takashi Iwai31e89602008-01-08 18:09:57 +01001505 * pointer, wake up sleepers, etc.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506 *
1507 * Even if more than one periods have elapsed since the last call, you
1508 * have to call this only once.
1509 */
Takashi Iwai877211f2005-11-17 13:59:38 +01001510void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511{
Takashi Iwai877211f2005-11-17 13:59:38 +01001512 struct snd_pcm_runtime *runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513 unsigned long flags;
1514
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001515 if (PCM_RUNTIME_CHECK(substream))
1516 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517 runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518
1519 if (runtime->transfer_ack_begin)
1520 runtime->transfer_ack_begin(substream);
1521
1522 snd_pcm_stream_lock_irqsave(substream, flags);
1523 if (!snd_pcm_running(substream) ||
1524 snd_pcm_update_hw_ptr_interrupt(substream) < 0)
1525 goto _end;
1526
1527 if (substream->timer_running)
1528 snd_timer_interrupt(substream->timer, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529 _end:
1530 snd_pcm_stream_unlock_irqrestore(substream, flags);
1531 if (runtime->transfer_ack_end)
1532 runtime->transfer_ack_end(substream);
1533 kill_fasync(&runtime->fasync, SIGIO, POLL_IN);
1534}
1535
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001536EXPORT_SYMBOL(snd_pcm_period_elapsed);
1537
Takashi Iwai13075512008-01-08 18:08:14 +01001538/*
1539 * Wait until avail_min data becomes available
1540 * Returns a negative error code if any error occurs during operation.
1541 * The available space is stored on availp. When err = 0 and avail = 0
1542 * on the capture stream, it indicates the stream is in DRAINING state.
1543 */
1544static int wait_for_avail_min(struct snd_pcm_substream *substream,
1545 snd_pcm_uframes_t *availp)
1546{
1547 struct snd_pcm_runtime *runtime = substream->runtime;
1548 int is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
1549 wait_queue_t wait;
1550 int err = 0;
1551 snd_pcm_uframes_t avail = 0;
1552 long tout;
1553
1554 init_waitqueue_entry(&wait, current);
1555 add_wait_queue(&runtime->sleep, &wait);
1556 for (;;) {
1557 if (signal_pending(current)) {
1558 err = -ERESTARTSYS;
1559 break;
1560 }
1561 set_current_state(TASK_INTERRUPTIBLE);
1562 snd_pcm_stream_unlock_irq(substream);
1563 tout = schedule_timeout(msecs_to_jiffies(10000));
1564 snd_pcm_stream_lock_irq(substream);
1565 switch (runtime->status->state) {
1566 case SNDRV_PCM_STATE_SUSPENDED:
1567 err = -ESTRPIPE;
1568 goto _endloop;
1569 case SNDRV_PCM_STATE_XRUN:
1570 err = -EPIPE;
1571 goto _endloop;
1572 case SNDRV_PCM_STATE_DRAINING:
1573 if (is_playback)
1574 err = -EPIPE;
1575 else
1576 avail = 0; /* indicate draining */
1577 goto _endloop;
1578 case SNDRV_PCM_STATE_OPEN:
1579 case SNDRV_PCM_STATE_SETUP:
1580 case SNDRV_PCM_STATE_DISCONNECTED:
1581 err = -EBADFD;
1582 goto _endloop;
1583 }
1584 if (!tout) {
1585 snd_printd("%s write error (DMA or IRQ trouble?)\n",
1586 is_playback ? "playback" : "capture");
1587 err = -EIO;
1588 break;
1589 }
1590 if (is_playback)
1591 avail = snd_pcm_playback_avail(runtime);
1592 else
1593 avail = snd_pcm_capture_avail(runtime);
1594 if (avail >= runtime->control->avail_min)
1595 break;
1596 }
1597 _endloop:
1598 remove_wait_queue(&runtime->sleep, &wait);
1599 *availp = avail;
1600 return err;
1601}
1602
Takashi Iwai877211f2005-11-17 13:59:38 +01001603static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604 unsigned int hwoff,
1605 unsigned long data, unsigned int off,
1606 snd_pcm_uframes_t frames)
1607{
Takashi Iwai877211f2005-11-17 13:59:38 +01001608 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001609 int err;
1610 char __user *buf = (char __user *) data + frames_to_bytes(runtime, off);
1611 if (substream->ops->copy) {
1612 if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0)
1613 return err;
1614 } else {
1615 char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001616 if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames)))
1617 return -EFAULT;
1618 }
1619 return 0;
1620}
1621
Takashi Iwai877211f2005-11-17 13:59:38 +01001622typedef int (*transfer_f)(struct snd_pcm_substream *substream, unsigned int hwoff,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001623 unsigned long data, unsigned int off,
1624 snd_pcm_uframes_t size);
1625
Takashi Iwai877211f2005-11-17 13:59:38 +01001626static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001627 unsigned long data,
1628 snd_pcm_uframes_t size,
1629 int nonblock,
1630 transfer_f transfer)
1631{
Takashi Iwai877211f2005-11-17 13:59:38 +01001632 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001633 snd_pcm_uframes_t xfer = 0;
1634 snd_pcm_uframes_t offset = 0;
1635 int err = 0;
1636
1637 if (size == 0)
1638 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639
1640 snd_pcm_stream_lock_irq(substream);
1641 switch (runtime->status->state) {
1642 case SNDRV_PCM_STATE_PREPARED:
1643 case SNDRV_PCM_STATE_RUNNING:
1644 case SNDRV_PCM_STATE_PAUSED:
1645 break;
1646 case SNDRV_PCM_STATE_XRUN:
1647 err = -EPIPE;
1648 goto _end_unlock;
1649 case SNDRV_PCM_STATE_SUSPENDED:
1650 err = -ESTRPIPE;
1651 goto _end_unlock;
1652 default:
1653 err = -EBADFD;
1654 goto _end_unlock;
1655 }
1656
1657 while (size > 0) {
1658 snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
1659 snd_pcm_uframes_t avail;
1660 snd_pcm_uframes_t cont;
Takashi Iwai31e89602008-01-08 18:09:57 +01001661 if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662 snd_pcm_update_hw_ptr(substream);
1663 avail = snd_pcm_playback_avail(runtime);
Takashi Iwai13075512008-01-08 18:08:14 +01001664 if (!avail) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665 if (nonblock) {
1666 err = -EAGAIN;
1667 goto _end_unlock;
1668 }
Takashi Iwai13075512008-01-08 18:08:14 +01001669 err = wait_for_avail_min(substream, &avail);
1670 if (err < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001671 goto _end_unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673 frames = size > avail ? avail : size;
1674 cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
1675 if (frames > cont)
1676 frames = cont;
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001677 if (snd_BUG_ON(!frames)) {
1678 snd_pcm_stream_unlock_irq(substream);
1679 return -EINVAL;
1680 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001681 appl_ptr = runtime->control->appl_ptr;
1682 appl_ofs = appl_ptr % runtime->buffer_size;
1683 snd_pcm_stream_unlock_irq(substream);
1684 if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0)
1685 goto _end;
1686 snd_pcm_stream_lock_irq(substream);
1687 switch (runtime->status->state) {
1688 case SNDRV_PCM_STATE_XRUN:
1689 err = -EPIPE;
1690 goto _end_unlock;
1691 case SNDRV_PCM_STATE_SUSPENDED:
1692 err = -ESTRPIPE;
1693 goto _end_unlock;
1694 default:
1695 break;
1696 }
1697 appl_ptr += frames;
1698 if (appl_ptr >= runtime->boundary)
1699 appl_ptr -= runtime->boundary;
1700 runtime->control->appl_ptr = appl_ptr;
1701 if (substream->ops->ack)
1702 substream->ops->ack(substream);
1703
1704 offset += frames;
1705 size -= frames;
1706 xfer += frames;
1707 if (runtime->status->state == SNDRV_PCM_STATE_PREPARED &&
1708 snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) {
1709 err = snd_pcm_start(substream);
1710 if (err < 0)
1711 goto _end_unlock;
1712 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713 }
1714 _end_unlock:
1715 snd_pcm_stream_unlock_irq(substream);
1716 _end:
1717 return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
1718}
1719
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001720/* sanity-check for read/write methods */
1721static int pcm_sanity_check(struct snd_pcm_substream *substream)
1722{
1723 struct snd_pcm_runtime *runtime;
1724 if (PCM_RUNTIME_CHECK(substream))
1725 return -ENXIO;
1726 runtime = substream->runtime;
1727 if (snd_BUG_ON(!substream->ops->copy && !runtime->dma_area))
1728 return -EINVAL;
1729 if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
1730 return -EBADFD;
1731 return 0;
1732}
1733
Takashi Iwai877211f2005-11-17 13:59:38 +01001734snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001735{
Takashi Iwai877211f2005-11-17 13:59:38 +01001736 struct snd_pcm_runtime *runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001737 int nonblock;
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001738 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001740 err = pcm_sanity_check(substream);
1741 if (err < 0)
1742 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743 runtime = substream->runtime;
Takashi Iwai0df63e42006-04-28 15:13:41 +02001744 nonblock = !!(substream->f_flags & O_NONBLOCK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001745
1746 if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&
1747 runtime->channels > 1)
1748 return -EINVAL;
1749 return snd_pcm_lib_write1(substream, (unsigned long)buf, size, nonblock,
1750 snd_pcm_lib_write_transfer);
1751}
1752
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001753EXPORT_SYMBOL(snd_pcm_lib_write);
1754
Takashi Iwai877211f2005-11-17 13:59:38 +01001755static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001756 unsigned int hwoff,
1757 unsigned long data, unsigned int off,
1758 snd_pcm_uframes_t frames)
1759{
Takashi Iwai877211f2005-11-17 13:59:38 +01001760 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001761 int err;
1762 void __user **bufs = (void __user **)data;
1763 int channels = runtime->channels;
1764 int c;
1765 if (substream->ops->copy) {
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001766 if (snd_BUG_ON(!substream->ops->silence))
1767 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001768 for (c = 0; c < channels; ++c, ++bufs) {
1769 if (*bufs == NULL) {
1770 if ((err = substream->ops->silence(substream, c, hwoff, frames)) < 0)
1771 return err;
1772 } else {
1773 char __user *buf = *bufs + samples_to_bytes(runtime, off);
1774 if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0)
1775 return err;
1776 }
1777 }
1778 } else {
1779 /* default transfer behaviour */
1780 size_t dma_csize = runtime->dma_bytes / channels;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001781 for (c = 0; c < channels; ++c, ++bufs) {
1782 char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff);
1783 if (*bufs == NULL) {
1784 snd_pcm_format_set_silence(runtime->format, hwbuf, frames);
1785 } else {
1786 char __user *buf = *bufs + samples_to_bytes(runtime, off);
1787 if (copy_from_user(hwbuf, buf, samples_to_bytes(runtime, frames)))
1788 return -EFAULT;
1789 }
1790 }
1791 }
1792 return 0;
1793}
1794
Takashi Iwai877211f2005-11-17 13:59:38 +01001795snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001796 void __user **bufs,
1797 snd_pcm_uframes_t frames)
1798{
Takashi Iwai877211f2005-11-17 13:59:38 +01001799 struct snd_pcm_runtime *runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800 int nonblock;
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001801 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001802
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001803 err = pcm_sanity_check(substream);
1804 if (err < 0)
1805 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806 runtime = substream->runtime;
Takashi Iwai0df63e42006-04-28 15:13:41 +02001807 nonblock = !!(substream->f_flags & O_NONBLOCK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001808
1809 if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
1810 return -EINVAL;
1811 return snd_pcm_lib_write1(substream, (unsigned long)bufs, frames,
1812 nonblock, snd_pcm_lib_writev_transfer);
1813}
1814
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001815EXPORT_SYMBOL(snd_pcm_lib_writev);
1816
Takashi Iwai877211f2005-11-17 13:59:38 +01001817static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001818 unsigned int hwoff,
1819 unsigned long data, unsigned int off,
1820 snd_pcm_uframes_t frames)
1821{
Takashi Iwai877211f2005-11-17 13:59:38 +01001822 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001823 int err;
1824 char __user *buf = (char __user *) data + frames_to_bytes(runtime, off);
1825 if (substream->ops->copy) {
1826 if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0)
1827 return err;
1828 } else {
1829 char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001830 if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames)))
1831 return -EFAULT;
1832 }
1833 return 0;
1834}
1835
Takashi Iwai877211f2005-11-17 13:59:38 +01001836static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001837 unsigned long data,
1838 snd_pcm_uframes_t size,
1839 int nonblock,
1840 transfer_f transfer)
1841{
Takashi Iwai877211f2005-11-17 13:59:38 +01001842 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001843 snd_pcm_uframes_t xfer = 0;
1844 snd_pcm_uframes_t offset = 0;
1845 int err = 0;
1846
1847 if (size == 0)
1848 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001849
1850 snd_pcm_stream_lock_irq(substream);
1851 switch (runtime->status->state) {
1852 case SNDRV_PCM_STATE_PREPARED:
1853 if (size >= runtime->start_threshold) {
1854 err = snd_pcm_start(substream);
1855 if (err < 0)
1856 goto _end_unlock;
1857 }
1858 break;
1859 case SNDRV_PCM_STATE_DRAINING:
1860 case SNDRV_PCM_STATE_RUNNING:
1861 case SNDRV_PCM_STATE_PAUSED:
1862 break;
1863 case SNDRV_PCM_STATE_XRUN:
1864 err = -EPIPE;
1865 goto _end_unlock;
1866 case SNDRV_PCM_STATE_SUSPENDED:
1867 err = -ESTRPIPE;
1868 goto _end_unlock;
1869 default:
1870 err = -EBADFD;
1871 goto _end_unlock;
1872 }
1873
1874 while (size > 0) {
1875 snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
1876 snd_pcm_uframes_t avail;
1877 snd_pcm_uframes_t cont;
Takashi Iwai31e89602008-01-08 18:09:57 +01001878 if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001879 snd_pcm_update_hw_ptr(substream);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001880 avail = snd_pcm_capture_avail(runtime);
Takashi Iwai13075512008-01-08 18:08:14 +01001881 if (!avail) {
1882 if (runtime->status->state ==
1883 SNDRV_PCM_STATE_DRAINING) {
1884 snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001885 goto _end_unlock;
1886 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001887 if (nonblock) {
1888 err = -EAGAIN;
1889 goto _end_unlock;
1890 }
Takashi Iwai13075512008-01-08 18:08:14 +01001891 err = wait_for_avail_min(substream, &avail);
1892 if (err < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001893 goto _end_unlock;
Takashi Iwai13075512008-01-08 18:08:14 +01001894 if (!avail)
1895 continue; /* draining */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001896 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001897 frames = size > avail ? avail : size;
1898 cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
1899 if (frames > cont)
1900 frames = cont;
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001901 if (snd_BUG_ON(!frames)) {
1902 snd_pcm_stream_unlock_irq(substream);
1903 return -EINVAL;
1904 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001905 appl_ptr = runtime->control->appl_ptr;
1906 appl_ofs = appl_ptr % runtime->buffer_size;
1907 snd_pcm_stream_unlock_irq(substream);
1908 if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0)
1909 goto _end;
1910 snd_pcm_stream_lock_irq(substream);
1911 switch (runtime->status->state) {
1912 case SNDRV_PCM_STATE_XRUN:
1913 err = -EPIPE;
1914 goto _end_unlock;
1915 case SNDRV_PCM_STATE_SUSPENDED:
1916 err = -ESTRPIPE;
1917 goto _end_unlock;
1918 default:
1919 break;
1920 }
1921 appl_ptr += frames;
1922 if (appl_ptr >= runtime->boundary)
1923 appl_ptr -= runtime->boundary;
1924 runtime->control->appl_ptr = appl_ptr;
1925 if (substream->ops->ack)
1926 substream->ops->ack(substream);
1927
1928 offset += frames;
1929 size -= frames;
1930 xfer += frames;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001931 }
1932 _end_unlock:
1933 snd_pcm_stream_unlock_irq(substream);
1934 _end:
1935 return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
1936}
1937
Takashi Iwai877211f2005-11-17 13:59:38 +01001938snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __user *buf, snd_pcm_uframes_t size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939{
Takashi Iwai877211f2005-11-17 13:59:38 +01001940 struct snd_pcm_runtime *runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941 int nonblock;
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001942 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001944 err = pcm_sanity_check(substream);
1945 if (err < 0)
1946 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001947 runtime = substream->runtime;
Takashi Iwai0df63e42006-04-28 15:13:41 +02001948 nonblock = !!(substream->f_flags & O_NONBLOCK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001949 if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED)
1950 return -EINVAL;
1951 return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer);
1952}
1953
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001954EXPORT_SYMBOL(snd_pcm_lib_read);
1955
Takashi Iwai877211f2005-11-17 13:59:38 +01001956static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001957 unsigned int hwoff,
1958 unsigned long data, unsigned int off,
1959 snd_pcm_uframes_t frames)
1960{
Takashi Iwai877211f2005-11-17 13:59:38 +01001961 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001962 int err;
1963 void __user **bufs = (void __user **)data;
1964 int channels = runtime->channels;
1965 int c;
1966 if (substream->ops->copy) {
1967 for (c = 0; c < channels; ++c, ++bufs) {
1968 char __user *buf;
1969 if (*bufs == NULL)
1970 continue;
1971 buf = *bufs + samples_to_bytes(runtime, off);
1972 if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0)
1973 return err;
1974 }
1975 } else {
1976 snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977 for (c = 0; c < channels; ++c, ++bufs) {
1978 char *hwbuf;
1979 char __user *buf;
1980 if (*bufs == NULL)
1981 continue;
1982
1983 hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff);
1984 buf = *bufs + samples_to_bytes(runtime, off);
1985 if (copy_to_user(buf, hwbuf, samples_to_bytes(runtime, frames)))
1986 return -EFAULT;
1987 }
1988 }
1989 return 0;
1990}
1991
Takashi Iwai877211f2005-11-17 13:59:38 +01001992snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001993 void __user **bufs,
1994 snd_pcm_uframes_t frames)
1995{
Takashi Iwai877211f2005-11-17 13:59:38 +01001996 struct snd_pcm_runtime *runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001997 int nonblock;
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001998 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999
Takashi Iwai7eaa9432008-08-08 17:09:09 +02002000 err = pcm_sanity_check(substream);
2001 if (err < 0)
2002 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002003 runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002004 if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
2005 return -EBADFD;
2006
Takashi Iwai0df63e42006-04-28 15:13:41 +02002007 nonblock = !!(substream->f_flags & O_NONBLOCK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008 if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
2009 return -EINVAL;
2010 return snd_pcm_lib_read1(substream, (unsigned long)bufs, frames, nonblock, snd_pcm_lib_readv_transfer);
2011}
2012
Linus Torvalds1da177e2005-04-16 15:20:36 -07002013EXPORT_SYMBOL(snd_pcm_lib_readv);