blob: 2288fa07bf5975455ad869ae5244944a784f1e83 [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
Jaroslav Kyselac62a01a2009-05-28 11:21:52 +0200129#define xrun_debug(substream, mask) ((substream)->pstr->xrun_debug & (mask))
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100130#else
Jaroslav Kyselac62a01a2009-05-28 11:21:52 +0200131#define xrun_debug(substream, mask) 0
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100132#endif
133
Jaroslav Kyselac62a01a2009-05-28 11:21:52 +0200134#define dump_stack_on_xrun(substream) do { \
135 if (xrun_debug(substream, 2)) \
136 dump_stack(); \
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100137 } while (0)
138
Takashi Iwaic0070112009-06-08 15:58:48 +0200139static void pcm_debug_name(struct snd_pcm_substream *substream,
140 char *name, size_t len)
141{
142 snprintf(name, len, "pcmC%dD%d%c:%d",
143 substream->pcm->card->number,
144 substream->pcm->device,
145 substream->stream ? 'c' : 'p',
146 substream->number);
147}
148
Takashi Iwai877211f2005-11-17 13:59:38 +0100149static void xrun(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150{
Jaroslav Kysela13f040f2009-05-28 11:31:20 +0200151 struct snd_pcm_runtime *runtime = substream->runtime;
152
153 if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
154 snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
Jaroslav Kyselac62a01a2009-05-28 11:21:52 +0200156 if (xrun_debug(substream, 1)) {
Takashi Iwaic0070112009-06-08 15:58:48 +0200157 char name[16];
158 pcm_debug_name(substream, name, sizeof(name));
159 snd_printd(KERN_DEBUG "XRUN: %s\n", name);
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100160 dump_stack_on_xrun(substream);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162}
163
Takashi Iwai98204642009-03-19 09:59:21 +0100164static snd_pcm_uframes_t
165snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream,
166 struct snd_pcm_runtime *runtime)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167{
168 snd_pcm_uframes_t pos;
169
170 pos = substream->ops->pointer(substream);
171 if (pos == SNDRV_PCM_POS_XRUN)
172 return pos; /* XRUN */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 if (pos >= runtime->buffer_size) {
Takashi Iwai5f513e12009-03-19 10:01:47 +0100174 if (printk_ratelimit()) {
Takashi Iwaic0070112009-06-08 15:58:48 +0200175 char name[16];
176 pcm_debug_name(substream, name, sizeof(name));
177 snd_printd(KERN_ERR "BUG: %s, pos = 0x%lx, "
Takashi Iwai5f513e12009-03-19 10:01:47 +0100178 "buffer size = 0x%lx, period size = 0x%lx\n",
Takashi Iwaic0070112009-06-08 15:58:48 +0200179 name, pos, runtime->buffer_size,
Takashi Iwai5f513e12009-03-19 10:01:47 +0100180 runtime->period_size);
181 }
182 pos = 0;
Takashi Iwai7c22f1a2005-10-10 11:46:31 +0200183 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 pos -= pos % runtime->min_align;
185 return pos;
186}
187
Takashi Iwai98204642009-03-19 09:59:21 +0100188static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream,
189 struct snd_pcm_runtime *runtime)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190{
191 snd_pcm_uframes_t avail;
192
193 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
194 avail = snd_pcm_playback_avail(runtime);
195 else
196 avail = snd_pcm_capture_avail(runtime);
197 if (avail > runtime->avail_max)
198 runtime->avail_max = avail;
199 if (avail >= runtime->stop_threshold) {
200 if (substream->runtime->status->state == SNDRV_PCM_STATE_DRAINING)
201 snd_pcm_drain_done(substream);
202 else
203 xrun(substream);
204 return -EPIPE;
205 }
206 if (avail >= runtime->control->avail_min)
207 wake_up(&runtime->sleep);
208 return 0;
209}
210
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100211#define hw_ptr_error(substream, fmt, args...) \
212 do { \
Jaroslav Kyselac62a01a2009-05-28 11:21:52 +0200213 if (xrun_debug(substream, 1)) { \
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100214 if (printk_ratelimit()) { \
Takashi Iwaicad377a2009-03-19 09:55:15 +0100215 snd_printd("PCM: " fmt, ##args); \
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100216 } \
217 dump_stack_on_xrun(substream); \
218 } \
219 } while (0)
220
Takashi Iwai98204642009-03-19 09:59:21 +0100221static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222{
Takashi Iwai877211f2005-11-17 13:59:38 +0100223 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 snd_pcm_uframes_t pos;
Jaroslav Kyselabbf6ad12009-04-10 12:28:58 +0200225 snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_ptr_interrupt, hw_base;
226 snd_pcm_sframes_t hdelta, delta;
227 unsigned long jdelta;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228
Jaroslav Kyselabbf6ad12009-04-10 12:28:58 +0200229 old_hw_ptr = runtime->status->hw_ptr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 pos = snd_pcm_update_hw_ptr_pos(substream, runtime);
231 if (pos == SNDRV_PCM_POS_XRUN) {
232 xrun(substream);
233 return -EPIPE;
234 }
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100235 hw_base = runtime->hw_ptr_base;
236 new_hw_ptr = hw_base + pos;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size;
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100238 delta = new_hw_ptr - hw_ptr_interrupt;
Takashi Iwaided652f2009-03-19 10:08:49 +0100239 if (hw_ptr_interrupt >= runtime->boundary) {
Takashi Iwai8b22d942009-03-20 16:26:15 +0100240 hw_ptr_interrupt -= runtime->boundary;
241 if (hw_base < runtime->boundary / 2)
242 /* hw_base was already lapped; recalc delta */
Takashi Iwaided652f2009-03-19 10:08:49 +0100243 delta = new_hw_ptr - hw_ptr_interrupt;
244 }
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100245 if (delta < 0) {
246 delta += runtime->buffer_size;
247 if (delta < 0) {
248 hw_ptr_error(substream,
249 "Unexpected hw_pointer value "
250 "(stream=%i, pos=%ld, intr_ptr=%ld)\n",
251 substream->stream, (long)pos,
252 (long)hw_ptr_interrupt);
253 /* rebase to interrupt position */
254 hw_base = new_hw_ptr = hw_ptr_interrupt;
Takashi Iwaided652f2009-03-19 10:08:49 +0100255 /* align hw_base to buffer_size */
256 hw_base -= hw_base % runtime->buffer_size;
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100257 delta = 0;
258 } else {
259 hw_base += runtime->buffer_size;
Takashi Iwai8b22d942009-03-20 16:26:15 +0100260 if (hw_base >= runtime->boundary)
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100261 hw_base = 0;
262 new_hw_ptr = hw_base + pos;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 }
Takashi Iwaic87d9732009-05-27 10:53:33 +0200265
266 /* Do jiffies check only in xrun_debug mode */
Jaroslav Kyselac62a01a2009-05-28 11:21:52 +0200267 if (!xrun_debug(substream, 4))
Takashi Iwaic87d9732009-05-27 10:53:33 +0200268 goto no_jiffies_check;
269
Takashi Iwai3e5b5012009-04-28 12:07:08 +0200270 /* Skip the jiffies check for hardwares with BATCH flag.
271 * Such hardware usually just increases the position at each IRQ,
272 * thus it can't give any strange position.
273 */
274 if (runtime->hw.info & SNDRV_PCM_INFO_BATCH)
275 goto no_jiffies_check;
Jaroslav Kyselabbf6ad12009-04-10 12:28:58 +0200276 hdelta = new_hw_ptr - old_hw_ptr;
Jaroslav Kyselaa4444da2009-05-28 12:31:56 +0200277 if (hdelta < runtime->delay)
278 goto no_jiffies_check;
279 hdelta -= runtime->delay;
Jaroslav Kyselabbf6ad12009-04-10 12:28:58 +0200280 jdelta = jiffies - runtime->hw_ptr_jiffies;
281 if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) {
282 delta = jdelta /
283 (((runtime->period_size * HZ) / runtime->rate)
284 + HZ/100);
285 hw_ptr_error(substream,
286 "hw_ptr skipping! [Q] "
287 "(pos=%ld, delta=%ld, period=%ld, "
288 "jdelta=%lu/%lu/%lu)\n",
289 (long)pos, (long)hdelta,
290 (long)runtime->period_size, jdelta,
291 ((hdelta * HZ) / runtime->rate), delta);
292 hw_ptr_interrupt = runtime->hw_ptr_interrupt +
293 runtime->period_size * delta;
294 if (hw_ptr_interrupt >= runtime->boundary)
295 hw_ptr_interrupt -= runtime->boundary;
296 /* rebase to interrupt position */
297 hw_base = new_hw_ptr = hw_ptr_interrupt;
298 /* align hw_base to buffer_size */
299 hw_base -= hw_base % runtime->buffer_size;
300 delta = 0;
301 }
Takashi Iwai3e5b5012009-04-28 12:07:08 +0200302 no_jiffies_check:
Jaroslav Kyselabbf6ad12009-04-10 12:28:58 +0200303 if (delta > runtime->period_size + runtime->period_size / 2) {
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100304 hw_ptr_error(substream,
305 "Lost interrupts? "
306 "(stream=%i, delta=%ld, intr_ptr=%ld)\n",
307 substream->stream, (long)delta,
308 (long)hw_ptr_interrupt);
309 /* rebase hw_ptr_interrupt */
310 hw_ptr_interrupt =
311 new_hw_ptr - new_hw_ptr % runtime->period_size;
312 }
Takashi Iwaiab1863f2009-06-07 12:09:17 +0200313 runtime->hw_ptr_interrupt = hw_ptr_interrupt;
314
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
316 runtime->silence_size > 0)
317 snd_pcm_playback_silence(substream, new_hw_ptr);
318
Jaroslav Kysela13f040f2009-05-28 11:31:20 +0200319 if (runtime->status->hw_ptr == new_hw_ptr)
320 return 0;
321
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100322 runtime->hw_ptr_base = hw_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 runtime->status->hw_ptr = new_hw_ptr;
Jaroslav Kyselabbf6ad12009-04-10 12:28:58 +0200324 runtime->hw_ptr_jiffies = jiffies;
Jaroslav Kysela13f040f2009-05-28 11:31:20 +0200325 if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
326 snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327
328 return snd_pcm_update_hw_ptr_post(substream, runtime);
329}
330
331/* CAUTION: call it with irq disabled */
Takashi Iwai877211f2005-11-17 13:59:38 +0100332int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333{
Takashi Iwai877211f2005-11-17 13:59:38 +0100334 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 snd_pcm_uframes_t pos;
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100336 snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 snd_pcm_sframes_t delta;
Jaroslav Kyselabbf6ad12009-04-10 12:28:58 +0200338 unsigned long jdelta;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339
340 old_hw_ptr = runtime->status->hw_ptr;
341 pos = snd_pcm_update_hw_ptr_pos(substream, runtime);
342 if (pos == SNDRV_PCM_POS_XRUN) {
343 xrun(substream);
344 return -EPIPE;
345 }
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100346 hw_base = runtime->hw_ptr_base;
347 new_hw_ptr = hw_base + pos;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100349 delta = new_hw_ptr - old_hw_ptr;
Jaroslav Kyselabbf6ad12009-04-10 12:28:58 +0200350 jdelta = jiffies - runtime->hw_ptr_jiffies;
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100351 if (delta < 0) {
352 delta += runtime->buffer_size;
353 if (delta < 0) {
354 hw_ptr_error(substream,
355 "Unexpected hw_pointer value [2] "
Jaroslav Kyselabbf6ad12009-04-10 12:28:58 +0200356 "(stream=%i, pos=%ld, old_ptr=%ld, jdelta=%li)\n",
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100357 substream->stream, (long)pos,
Jaroslav Kyselabbf6ad12009-04-10 12:28:58 +0200358 (long)old_hw_ptr, jdelta);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 return 0;
360 }
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100361 hw_base += runtime->buffer_size;
Takashi Iwai8b22d942009-03-20 16:26:15 +0100362 if (hw_base >= runtime->boundary)
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100363 hw_base = 0;
364 new_hw_ptr = hw_base + pos;
365 }
Takashi Iwaic87d9732009-05-27 10:53:33 +0200366 /* Do jiffies check only in xrun_debug mode */
Jaroslav Kyselaa4444da2009-05-28 12:31:56 +0200367 if (!xrun_debug(substream, 4))
368 goto no_jiffies_check;
369 if (delta < runtime->delay)
370 goto no_jiffies_check;
371 delta -= runtime->delay;
372 if (((delta * HZ) / runtime->rate) > jdelta + HZ/100) {
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100373 hw_ptr_error(substream,
374 "hw_ptr skipping! "
Jaroslav Kyselabbf6ad12009-04-10 12:28:58 +0200375 "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n",
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100376 (long)pos, (long)delta,
Jaroslav Kyselabbf6ad12009-04-10 12:28:58 +0200377 (long)runtime->period_size, jdelta,
378 ((delta * HZ) / runtime->rate));
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100379 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 }
Jaroslav Kyselaa4444da2009-05-28 12:31:56 +0200381 no_jiffies_check:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
383 runtime->silence_size > 0)
384 snd_pcm_playback_silence(substream, new_hw_ptr);
385
Jaroslav Kyselad86bf922009-06-06 18:32:06 +0200386 if (runtime->status->hw_ptr == new_hw_ptr)
Jaroslav Kysela13f040f2009-05-28 11:31:20 +0200387 return 0;
388
Takashi Iwaied3da3d2009-03-03 17:00:15 +0100389 runtime->hw_ptr_base = hw_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 runtime->status->hw_ptr = new_hw_ptr;
Jaroslav Kyselabbf6ad12009-04-10 12:28:58 +0200391 runtime->hw_ptr_jiffies = jiffies;
Jaroslav Kysela13f040f2009-05-28 11:31:20 +0200392 if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
393 snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394
395 return snd_pcm_update_hw_ptr_post(substream, runtime);
396}
397
398/**
399 * snd_pcm_set_ops - set the PCM operators
400 * @pcm: the pcm instance
401 * @direction: stream direction, SNDRV_PCM_STREAM_XXX
402 * @ops: the operator table
403 *
404 * Sets the given PCM operators to the pcm instance.
405 */
Takashi Iwai877211f2005-11-17 13:59:38 +0100406void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, struct snd_pcm_ops *ops)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407{
Takashi Iwai877211f2005-11-17 13:59:38 +0100408 struct snd_pcm_str *stream = &pcm->streams[direction];
409 struct snd_pcm_substream *substream;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410
411 for (substream = stream->substream; substream != NULL; substream = substream->next)
412 substream->ops = ops;
413}
414
Takashi Iwaie88e8ae62006-04-28 15:13:40 +0200415EXPORT_SYMBOL(snd_pcm_set_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416
417/**
418 * snd_pcm_sync - set the PCM sync id
419 * @substream: the pcm substream
420 *
421 * Sets the PCM sync identifier for the card.
422 */
Takashi Iwai877211f2005-11-17 13:59:38 +0100423void snd_pcm_set_sync(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424{
Takashi Iwai877211f2005-11-17 13:59:38 +0100425 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426
427 runtime->sync.id32[0] = substream->pcm->card->number;
428 runtime->sync.id32[1] = -1;
429 runtime->sync.id32[2] = -1;
430 runtime->sync.id32[3] = -1;
431}
432
Takashi Iwaie88e8ae62006-04-28 15:13:40 +0200433EXPORT_SYMBOL(snd_pcm_set_sync);
434
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435/*
436 * Standard ioctl routine
437 */
438
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439static inline unsigned int div32(unsigned int a, unsigned int b,
440 unsigned int *r)
441{
442 if (b == 0) {
443 *r = 0;
444 return UINT_MAX;
445 }
446 *r = a % b;
447 return a / b;
448}
449
450static inline unsigned int div_down(unsigned int a, unsigned int b)
451{
452 if (b == 0)
453 return UINT_MAX;
454 return a / b;
455}
456
457static inline unsigned int div_up(unsigned int a, unsigned int b)
458{
459 unsigned int r;
460 unsigned int q;
461 if (b == 0)
462 return UINT_MAX;
463 q = div32(a, b, &r);
464 if (r)
465 ++q;
466 return q;
467}
468
469static inline unsigned int mul(unsigned int a, unsigned int b)
470{
471 if (a == 0)
472 return 0;
473 if (div_down(UINT_MAX, a) < b)
474 return UINT_MAX;
475 return a * b;
476}
477
478static inline unsigned int muldiv32(unsigned int a, unsigned int b,
479 unsigned int c, unsigned int *r)
480{
481 u_int64_t n = (u_int64_t) a * b;
482 if (c == 0) {
Takashi Iwai7eaa9432008-08-08 17:09:09 +0200483 snd_BUG_ON(!n);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 *r = 0;
485 return UINT_MAX;
486 }
487 div64_32(&n, c, r);
488 if (n >= UINT_MAX) {
489 *r = 0;
490 return UINT_MAX;
491 }
492 return n;
493}
494
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495/**
496 * snd_interval_refine - refine the interval value of configurator
497 * @i: the interval value to refine
498 * @v: the interval value to refer to
499 *
500 * Refines the interval value with the reference value.
501 * The interval is changed to the range satisfying both intervals.
502 * The interval status (min, max, integer, etc.) are evaluated.
503 *
504 * Returns non-zero if the value is changed, zero if not changed.
505 */
Takashi Iwai877211f2005-11-17 13:59:38 +0100506int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507{
508 int changed = 0;
Takashi Iwai7eaa9432008-08-08 17:09:09 +0200509 if (snd_BUG_ON(snd_interval_empty(i)))
510 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 if (i->min < v->min) {
512 i->min = v->min;
513 i->openmin = v->openmin;
514 changed = 1;
515 } else if (i->min == v->min && !i->openmin && v->openmin) {
516 i->openmin = 1;
517 changed = 1;
518 }
519 if (i->max > v->max) {
520 i->max = v->max;
521 i->openmax = v->openmax;
522 changed = 1;
523 } else if (i->max == v->max && !i->openmax && v->openmax) {
524 i->openmax = 1;
525 changed = 1;
526 }
527 if (!i->integer && v->integer) {
528 i->integer = 1;
529 changed = 1;
530 }
531 if (i->integer) {
532 if (i->openmin) {
533 i->min++;
534 i->openmin = 0;
535 }
536 if (i->openmax) {
537 i->max--;
538 i->openmax = 0;
539 }
540 } else if (!i->openmin && !i->openmax && i->min == i->max)
541 i->integer = 1;
542 if (snd_interval_checkempty(i)) {
543 snd_interval_none(i);
544 return -EINVAL;
545 }
546 return changed;
547}
548
Takashi Iwaie88e8ae62006-04-28 15:13:40 +0200549EXPORT_SYMBOL(snd_interval_refine);
550
Takashi Iwai877211f2005-11-17 13:59:38 +0100551static int snd_interval_refine_first(struct snd_interval *i)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552{
Takashi Iwai7eaa9432008-08-08 17:09:09 +0200553 if (snd_BUG_ON(snd_interval_empty(i)))
554 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 if (snd_interval_single(i))
556 return 0;
557 i->max = i->min;
558 i->openmax = i->openmin;
559 if (i->openmax)
560 i->max++;
561 return 1;
562}
563
Takashi Iwai877211f2005-11-17 13:59:38 +0100564static int snd_interval_refine_last(struct snd_interval *i)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565{
Takashi Iwai7eaa9432008-08-08 17:09:09 +0200566 if (snd_BUG_ON(snd_interval_empty(i)))
567 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 if (snd_interval_single(i))
569 return 0;
570 i->min = i->max;
571 i->openmin = i->openmax;
572 if (i->openmin)
573 i->min--;
574 return 1;
575}
576
Takashi Iwai877211f2005-11-17 13:59:38 +0100577void snd_interval_mul(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578{
579 if (a->empty || b->empty) {
580 snd_interval_none(c);
581 return;
582 }
583 c->empty = 0;
584 c->min = mul(a->min, b->min);
585 c->openmin = (a->openmin || b->openmin);
586 c->max = mul(a->max, b->max);
587 c->openmax = (a->openmax || b->openmax);
588 c->integer = (a->integer && b->integer);
589}
590
591/**
592 * snd_interval_div - refine the interval value with division
Takashi Iwaidf8db932005-09-07 13:38:19 +0200593 * @a: dividend
594 * @b: divisor
595 * @c: quotient
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 *
597 * c = a / b
598 *
599 * Returns non-zero if the value is changed, zero if not changed.
600 */
Takashi Iwai877211f2005-11-17 13:59:38 +0100601void snd_interval_div(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602{
603 unsigned int r;
604 if (a->empty || b->empty) {
605 snd_interval_none(c);
606 return;
607 }
608 c->empty = 0;
609 c->min = div32(a->min, b->max, &r);
610 c->openmin = (r || a->openmin || b->openmax);
611 if (b->min > 0) {
612 c->max = div32(a->max, b->min, &r);
613 if (r) {
614 c->max++;
615 c->openmax = 1;
616 } else
617 c->openmax = (a->openmax || b->openmin);
618 } else {
619 c->max = UINT_MAX;
620 c->openmax = 0;
621 }
622 c->integer = 0;
623}
624
625/**
626 * snd_interval_muldivk - refine the interval value
Takashi Iwaidf8db932005-09-07 13:38:19 +0200627 * @a: dividend 1
628 * @b: dividend 2
629 * @k: divisor (as integer)
630 * @c: result
631 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 * c = a * b / k
633 *
634 * Returns non-zero if the value is changed, zero if not changed.
635 */
Takashi Iwai877211f2005-11-17 13:59:38 +0100636void snd_interval_muldivk(const struct snd_interval *a, const struct snd_interval *b,
637 unsigned int k, struct snd_interval *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638{
639 unsigned int r;
640 if (a->empty || b->empty) {
641 snd_interval_none(c);
642 return;
643 }
644 c->empty = 0;
645 c->min = muldiv32(a->min, b->min, k, &r);
646 c->openmin = (r || a->openmin || b->openmin);
647 c->max = muldiv32(a->max, b->max, k, &r);
648 if (r) {
649 c->max++;
650 c->openmax = 1;
651 } else
652 c->openmax = (a->openmax || b->openmax);
653 c->integer = 0;
654}
655
656/**
657 * snd_interval_mulkdiv - refine the interval value
Takashi Iwaidf8db932005-09-07 13:38:19 +0200658 * @a: dividend 1
659 * @k: dividend 2 (as integer)
660 * @b: divisor
661 * @c: result
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 *
663 * c = a * k / b
664 *
665 * Returns non-zero if the value is changed, zero if not changed.
666 */
Takashi Iwai877211f2005-11-17 13:59:38 +0100667void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k,
668 const struct snd_interval *b, struct snd_interval *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669{
670 unsigned int r;
671 if (a->empty || b->empty) {
672 snd_interval_none(c);
673 return;
674 }
675 c->empty = 0;
676 c->min = muldiv32(a->min, k, b->max, &r);
677 c->openmin = (r || a->openmin || b->openmax);
678 if (b->min > 0) {
679 c->max = muldiv32(a->max, k, b->min, &r);
680 if (r) {
681 c->max++;
682 c->openmax = 1;
683 } else
684 c->openmax = (a->openmax || b->openmin);
685 } else {
686 c->max = UINT_MAX;
687 c->openmax = 0;
688 }
689 c->integer = 0;
690}
691
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692/* ---- */
693
694
695/**
696 * snd_interval_ratnum - refine the interval value
Takashi Iwaidf8db932005-09-07 13:38:19 +0200697 * @i: interval to refine
698 * @rats_count: number of ratnum_t
699 * @rats: ratnum_t array
700 * @nump: pointer to store the resultant numerator
701 * @denp: pointer to store the resultant denominator
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 *
703 * Returns non-zero if the value is changed, zero if not changed.
704 */
Takashi Iwai877211f2005-11-17 13:59:38 +0100705int snd_interval_ratnum(struct snd_interval *i,
706 unsigned int rats_count, struct snd_ratnum *rats,
707 unsigned int *nump, unsigned int *denp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708{
709 unsigned int best_num, best_diff, best_den;
710 unsigned int k;
Takashi Iwai877211f2005-11-17 13:59:38 +0100711 struct snd_interval t;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 int err;
713
714 best_num = best_den = best_diff = 0;
715 for (k = 0; k < rats_count; ++k) {
716 unsigned int num = rats[k].num;
717 unsigned int den;
718 unsigned int q = i->min;
719 int diff;
720 if (q == 0)
721 q = 1;
722 den = div_down(num, q);
723 if (den < rats[k].den_min)
724 continue;
725 if (den > rats[k].den_max)
726 den = rats[k].den_max;
727 else {
728 unsigned int r;
729 r = (den - rats[k].den_min) % rats[k].den_step;
730 if (r != 0)
731 den -= r;
732 }
733 diff = num - q * den;
734 if (best_num == 0 ||
735 diff * best_den < best_diff * den) {
736 best_diff = diff;
737 best_den = den;
738 best_num = num;
739 }
740 }
741 if (best_den == 0) {
742 i->empty = 1;
743 return -EINVAL;
744 }
745 t.min = div_down(best_num, best_den);
746 t.openmin = !!(best_num % best_den);
747
748 best_num = best_den = best_diff = 0;
749 for (k = 0; k < rats_count; ++k) {
750 unsigned int num = rats[k].num;
751 unsigned int den;
752 unsigned int q = i->max;
753 int diff;
754 if (q == 0) {
755 i->empty = 1;
756 return -EINVAL;
757 }
758 den = div_up(num, q);
759 if (den > rats[k].den_max)
760 continue;
761 if (den < rats[k].den_min)
762 den = rats[k].den_min;
763 else {
764 unsigned int r;
765 r = (den - rats[k].den_min) % rats[k].den_step;
766 if (r != 0)
767 den += rats[k].den_step - r;
768 }
769 diff = q * den - num;
770 if (best_num == 0 ||
771 diff * best_den < best_diff * den) {
772 best_diff = diff;
773 best_den = den;
774 best_num = num;
775 }
776 }
777 if (best_den == 0) {
778 i->empty = 1;
779 return -EINVAL;
780 }
781 t.max = div_up(best_num, best_den);
782 t.openmax = !!(best_num % best_den);
783 t.integer = 0;
784 err = snd_interval_refine(i, &t);
785 if (err < 0)
786 return err;
787
788 if (snd_interval_single(i)) {
789 if (nump)
790 *nump = best_num;
791 if (denp)
792 *denp = best_den;
793 }
794 return err;
795}
796
Takashi Iwaie88e8ae62006-04-28 15:13:40 +0200797EXPORT_SYMBOL(snd_interval_ratnum);
798
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799/**
800 * snd_interval_ratden - refine the interval value
Takashi Iwaidf8db932005-09-07 13:38:19 +0200801 * @i: interval to refine
Takashi Iwai877211f2005-11-17 13:59:38 +0100802 * @rats_count: number of struct ratden
803 * @rats: struct ratden array
Takashi Iwaidf8db932005-09-07 13:38:19 +0200804 * @nump: pointer to store the resultant numerator
805 * @denp: pointer to store the resultant denominator
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 *
807 * Returns non-zero if the value is changed, zero if not changed.
808 */
Takashi Iwai877211f2005-11-17 13:59:38 +0100809static int snd_interval_ratden(struct snd_interval *i,
810 unsigned int rats_count, struct snd_ratden *rats,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811 unsigned int *nump, unsigned int *denp)
812{
813 unsigned int best_num, best_diff, best_den;
814 unsigned int k;
Takashi Iwai877211f2005-11-17 13:59:38 +0100815 struct snd_interval t;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 int err;
817
818 best_num = best_den = best_diff = 0;
819 for (k = 0; k < rats_count; ++k) {
820 unsigned int num;
821 unsigned int den = rats[k].den;
822 unsigned int q = i->min;
823 int diff;
824 num = mul(q, den);
825 if (num > rats[k].num_max)
826 continue;
827 if (num < rats[k].num_min)
828 num = rats[k].num_max;
829 else {
830 unsigned int r;
831 r = (num - rats[k].num_min) % rats[k].num_step;
832 if (r != 0)
833 num += rats[k].num_step - r;
834 }
835 diff = num - q * den;
836 if (best_num == 0 ||
837 diff * best_den < best_diff * den) {
838 best_diff = diff;
839 best_den = den;
840 best_num = num;
841 }
842 }
843 if (best_den == 0) {
844 i->empty = 1;
845 return -EINVAL;
846 }
847 t.min = div_down(best_num, best_den);
848 t.openmin = !!(best_num % best_den);
849
850 best_num = best_den = best_diff = 0;
851 for (k = 0; k < rats_count; ++k) {
852 unsigned int num;
853 unsigned int den = rats[k].den;
854 unsigned int q = i->max;
855 int diff;
856 num = mul(q, den);
857 if (num < rats[k].num_min)
858 continue;
859 if (num > rats[k].num_max)
860 num = rats[k].num_max;
861 else {
862 unsigned int r;
863 r = (num - rats[k].num_min) % rats[k].num_step;
864 if (r != 0)
865 num -= r;
866 }
867 diff = q * den - num;
868 if (best_num == 0 ||
869 diff * best_den < best_diff * den) {
870 best_diff = diff;
871 best_den = den;
872 best_num = num;
873 }
874 }
875 if (best_den == 0) {
876 i->empty = 1;
877 return -EINVAL;
878 }
879 t.max = div_up(best_num, best_den);
880 t.openmax = !!(best_num % best_den);
881 t.integer = 0;
882 err = snd_interval_refine(i, &t);
883 if (err < 0)
884 return err;
885
886 if (snd_interval_single(i)) {
887 if (nump)
888 *nump = best_num;
889 if (denp)
890 *denp = best_den;
891 }
892 return err;
893}
894
895/**
896 * snd_interval_list - refine the interval value from the list
897 * @i: the interval value to refine
898 * @count: the number of elements in the list
899 * @list: the value list
900 * @mask: the bit-mask to evaluate
901 *
902 * Refines the interval value from the list.
903 * When mask is non-zero, only the elements corresponding to bit 1 are
904 * evaluated.
905 *
906 * Returns non-zero if the value is changed, zero if not changed.
907 */
Takashi Iwai877211f2005-11-17 13:59:38 +0100908int snd_interval_list(struct snd_interval *i, unsigned int count, unsigned int *list, unsigned int mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909{
910 unsigned int k;
911 int changed = 0;
Takashi Iwai0981a262007-02-01 14:53:49 +0100912
913 if (!count) {
914 i->empty = 1;
915 return -EINVAL;
916 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917 for (k = 0; k < count; k++) {
918 if (mask && !(mask & (1 << k)))
919 continue;
920 if (i->min == list[k] && !i->openmin)
921 goto _l1;
922 if (i->min < list[k]) {
923 i->min = list[k];
924 i->openmin = 0;
925 changed = 1;
926 goto _l1;
927 }
928 }
929 i->empty = 1;
930 return -EINVAL;
931 _l1:
932 for (k = count; k-- > 0;) {
933 if (mask && !(mask & (1 << k)))
934 continue;
935 if (i->max == list[k] && !i->openmax)
936 goto _l2;
937 if (i->max > list[k]) {
938 i->max = list[k];
939 i->openmax = 0;
940 changed = 1;
941 goto _l2;
942 }
943 }
944 i->empty = 1;
945 return -EINVAL;
946 _l2:
947 if (snd_interval_checkempty(i)) {
948 i->empty = 1;
949 return -EINVAL;
950 }
951 return changed;
952}
953
Takashi Iwaie88e8ae62006-04-28 15:13:40 +0200954EXPORT_SYMBOL(snd_interval_list);
955
Takashi Iwai877211f2005-11-17 13:59:38 +0100956static int snd_interval_step(struct snd_interval *i, unsigned int min, unsigned int step)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957{
958 unsigned int n;
959 int changed = 0;
960 n = (i->min - min) % step;
961 if (n != 0 || i->openmin) {
962 i->min += step - n;
963 changed = 1;
964 }
965 n = (i->max - min) % step;
966 if (n != 0 || i->openmax) {
967 i->max -= n;
968 changed = 1;
969 }
970 if (snd_interval_checkempty(i)) {
971 i->empty = 1;
972 return -EINVAL;
973 }
974 return changed;
975}
976
977/* Info constraints helpers */
978
979/**
980 * snd_pcm_hw_rule_add - add the hw-constraint rule
981 * @runtime: the pcm runtime instance
982 * @cond: condition bits
983 * @var: the variable to evaluate
984 * @func: the evaluation function
985 * @private: the private data pointer passed to function
986 * @dep: the dependent variables
987 *
988 * Returns zero if successful, or a negative error code on failure.
989 */
Takashi Iwai877211f2005-11-17 13:59:38 +0100990int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 int var,
992 snd_pcm_hw_rule_func_t func, void *private,
993 int dep, ...)
994{
Takashi Iwai877211f2005-11-17 13:59:38 +0100995 struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;
996 struct snd_pcm_hw_rule *c;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 unsigned int k;
998 va_list args;
999 va_start(args, dep);
1000 if (constrs->rules_num >= constrs->rules_all) {
Takashi Iwai877211f2005-11-17 13:59:38 +01001001 struct snd_pcm_hw_rule *new;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002 unsigned int new_rules = constrs->rules_all + 16;
1003 new = kcalloc(new_rules, sizeof(*c), GFP_KERNEL);
1004 if (!new)
1005 return -ENOMEM;
1006 if (constrs->rules) {
1007 memcpy(new, constrs->rules,
1008 constrs->rules_num * sizeof(*c));
1009 kfree(constrs->rules);
1010 }
1011 constrs->rules = new;
1012 constrs->rules_all = new_rules;
1013 }
1014 c = &constrs->rules[constrs->rules_num];
1015 c->cond = cond;
1016 c->func = func;
1017 c->var = var;
1018 c->private = private;
1019 k = 0;
1020 while (1) {
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001021 if (snd_BUG_ON(k >= ARRAY_SIZE(c->deps)))
1022 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023 c->deps[k++] = dep;
1024 if (dep < 0)
1025 break;
1026 dep = va_arg(args, int);
1027 }
1028 constrs->rules_num++;
1029 va_end(args);
1030 return 0;
1031}
1032
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001033EXPORT_SYMBOL(snd_pcm_hw_rule_add);
1034
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001036 * snd_pcm_hw_constraint_mask - apply the given bitmap mask constraint
Takashi Iwaidf8db932005-09-07 13:38:19 +02001037 * @runtime: PCM runtime instance
1038 * @var: hw_params variable to apply the mask
1039 * @mask: the bitmap mask
1040 *
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001041 * Apply the constraint of the given bitmap mask to a 32-bit mask parameter.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 */
Takashi Iwai877211f2005-11-17 13:59:38 +01001043int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 u_int32_t mask)
1045{
Takashi Iwai877211f2005-11-17 13:59:38 +01001046 struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;
1047 struct snd_mask *maskp = constrs_mask(constrs, var);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048 *maskp->bits &= mask;
1049 memset(maskp->bits + 1, 0, (SNDRV_MASK_MAX-32) / 8); /* clear rest */
1050 if (*maskp->bits == 0)
1051 return -EINVAL;
1052 return 0;
1053}
1054
1055/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001056 * snd_pcm_hw_constraint_mask64 - apply the given bitmap mask constraint
Takashi Iwaidf8db932005-09-07 13:38:19 +02001057 * @runtime: PCM runtime instance
1058 * @var: hw_params variable to apply the mask
1059 * @mask: the 64bit bitmap mask
1060 *
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001061 * Apply the constraint of the given bitmap mask to a 64-bit mask parameter.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 */
Takashi Iwai877211f2005-11-17 13:59:38 +01001063int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 u_int64_t mask)
1065{
Takashi Iwai877211f2005-11-17 13:59:38 +01001066 struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;
1067 struct snd_mask *maskp = constrs_mask(constrs, var);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068 maskp->bits[0] &= (u_int32_t)mask;
1069 maskp->bits[1] &= (u_int32_t)(mask >> 32);
1070 memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */
1071 if (! maskp->bits[0] && ! maskp->bits[1])
1072 return -EINVAL;
1073 return 0;
1074}
1075
1076/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001077 * snd_pcm_hw_constraint_integer - apply an integer constraint to an interval
Takashi Iwaidf8db932005-09-07 13:38:19 +02001078 * @runtime: PCM runtime instance
1079 * @var: hw_params variable to apply the integer constraint
1080 *
1081 * Apply the constraint of integer to an interval parameter.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 */
Takashi Iwai877211f2005-11-17 13:59:38 +01001083int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084{
Takashi Iwai877211f2005-11-17 13:59:38 +01001085 struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086 return snd_interval_setinteger(constrs_interval(constrs, var));
1087}
1088
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001089EXPORT_SYMBOL(snd_pcm_hw_constraint_integer);
1090
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001092 * snd_pcm_hw_constraint_minmax - apply a min/max range constraint to an interval
Takashi Iwaidf8db932005-09-07 13:38:19 +02001093 * @runtime: PCM runtime instance
1094 * @var: hw_params variable to apply the range
1095 * @min: the minimal value
1096 * @max: the maximal value
1097 *
1098 * Apply the min/max range constraint to an interval parameter.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099 */
Takashi Iwai877211f2005-11-17 13:59:38 +01001100int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 unsigned int min, unsigned int max)
1102{
Takashi Iwai877211f2005-11-17 13:59:38 +01001103 struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;
1104 struct snd_interval t;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 t.min = min;
1106 t.max = max;
1107 t.openmin = t.openmax = 0;
1108 t.integer = 0;
1109 return snd_interval_refine(constrs_interval(constrs, var), &t);
1110}
1111
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001112EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax);
1113
Takashi Iwai877211f2005-11-17 13:59:38 +01001114static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params,
1115 struct snd_pcm_hw_rule *rule)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116{
Takashi Iwai877211f2005-11-17 13:59:38 +01001117 struct snd_pcm_hw_constraint_list *list = rule->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118 return snd_interval_list(hw_param_interval(params, rule->var), list->count, list->list, list->mask);
1119}
1120
1121
1122/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001123 * snd_pcm_hw_constraint_list - apply a list of constraints to a parameter
Takashi Iwaidf8db932005-09-07 13:38:19 +02001124 * @runtime: PCM runtime instance
1125 * @cond: condition bits
1126 * @var: hw_params variable to apply the list constraint
1127 * @l: list
1128 *
1129 * Apply the list of constraints to an interval parameter.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130 */
Takashi Iwai877211f2005-11-17 13:59:38 +01001131int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 unsigned int cond,
1133 snd_pcm_hw_param_t var,
Takashi Iwai877211f2005-11-17 13:59:38 +01001134 struct snd_pcm_hw_constraint_list *l)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135{
1136 return snd_pcm_hw_rule_add(runtime, cond, var,
1137 snd_pcm_hw_rule_list, l,
1138 var, -1);
1139}
1140
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001141EXPORT_SYMBOL(snd_pcm_hw_constraint_list);
1142
Takashi Iwai877211f2005-11-17 13:59:38 +01001143static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params,
1144 struct snd_pcm_hw_rule *rule)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145{
Takashi Iwai877211f2005-11-17 13:59:38 +01001146 struct snd_pcm_hw_constraint_ratnums *r = rule->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 unsigned int num = 0, den = 0;
1148 int err;
1149 err = snd_interval_ratnum(hw_param_interval(params, rule->var),
1150 r->nrats, r->rats, &num, &den);
1151 if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) {
1152 params->rate_num = num;
1153 params->rate_den = den;
1154 }
1155 return err;
1156}
1157
1158/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001159 * snd_pcm_hw_constraint_ratnums - apply ratnums constraint to a parameter
Takashi Iwaidf8db932005-09-07 13:38:19 +02001160 * @runtime: PCM runtime instance
1161 * @cond: condition bits
1162 * @var: hw_params variable to apply the ratnums constraint
Takashi Iwai877211f2005-11-17 13:59:38 +01001163 * @r: struct snd_ratnums constriants
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 */
Takashi Iwai877211f2005-11-17 13:59:38 +01001165int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 unsigned int cond,
1167 snd_pcm_hw_param_t var,
Takashi Iwai877211f2005-11-17 13:59:38 +01001168 struct snd_pcm_hw_constraint_ratnums *r)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169{
1170 return snd_pcm_hw_rule_add(runtime, cond, var,
1171 snd_pcm_hw_rule_ratnums, r,
1172 var, -1);
1173}
1174
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001175EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums);
1176
Takashi Iwai877211f2005-11-17 13:59:38 +01001177static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params,
1178 struct snd_pcm_hw_rule *rule)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179{
Takashi Iwai877211f2005-11-17 13:59:38 +01001180 struct snd_pcm_hw_constraint_ratdens *r = rule->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 unsigned int num = 0, den = 0;
1182 int err = snd_interval_ratden(hw_param_interval(params, rule->var),
1183 r->nrats, r->rats, &num, &den);
1184 if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) {
1185 params->rate_num = num;
1186 params->rate_den = den;
1187 }
1188 return err;
1189}
1190
1191/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001192 * snd_pcm_hw_constraint_ratdens - apply ratdens constraint to a parameter
Takashi Iwaidf8db932005-09-07 13:38:19 +02001193 * @runtime: PCM runtime instance
1194 * @cond: condition bits
1195 * @var: hw_params variable to apply the ratdens constraint
Takashi Iwai877211f2005-11-17 13:59:38 +01001196 * @r: struct snd_ratdens constriants
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197 */
Takashi Iwai877211f2005-11-17 13:59:38 +01001198int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 unsigned int cond,
1200 snd_pcm_hw_param_t var,
Takashi Iwai877211f2005-11-17 13:59:38 +01001201 struct snd_pcm_hw_constraint_ratdens *r)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202{
1203 return snd_pcm_hw_rule_add(runtime, cond, var,
1204 snd_pcm_hw_rule_ratdens, r,
1205 var, -1);
1206}
1207
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001208EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens);
1209
Takashi Iwai877211f2005-11-17 13:59:38 +01001210static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params,
1211 struct snd_pcm_hw_rule *rule)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212{
1213 unsigned int l = (unsigned long) rule->private;
1214 int width = l & 0xffff;
1215 unsigned int msbits = l >> 16;
Takashi Iwai877211f2005-11-17 13:59:38 +01001216 struct snd_interval *i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217 if (snd_interval_single(i) && snd_interval_value(i) == width)
1218 params->msbits = msbits;
1219 return 0;
1220}
1221
1222/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001223 * snd_pcm_hw_constraint_msbits - add a hw constraint msbits rule
Takashi Iwaidf8db932005-09-07 13:38:19 +02001224 * @runtime: PCM runtime instance
1225 * @cond: condition bits
1226 * @width: sample bits width
1227 * @msbits: msbits width
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228 */
Takashi Iwai877211f2005-11-17 13:59:38 +01001229int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230 unsigned int cond,
1231 unsigned int width,
1232 unsigned int msbits)
1233{
1234 unsigned long l = (msbits << 16) | width;
1235 return snd_pcm_hw_rule_add(runtime, cond, -1,
1236 snd_pcm_hw_rule_msbits,
1237 (void*) l,
1238 SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
1239}
1240
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001241EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits);
1242
Takashi Iwai877211f2005-11-17 13:59:38 +01001243static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params,
1244 struct snd_pcm_hw_rule *rule)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001245{
1246 unsigned long step = (unsigned long) rule->private;
1247 return snd_interval_step(hw_param_interval(params, rule->var), 0, step);
1248}
1249
1250/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001251 * snd_pcm_hw_constraint_step - add a hw constraint step rule
Takashi Iwaidf8db932005-09-07 13:38:19 +02001252 * @runtime: PCM runtime instance
1253 * @cond: condition bits
1254 * @var: hw_params variable to apply the step constraint
1255 * @step: step size
Linus Torvalds1da177e2005-04-16 15:20:36 -07001256 */
Takashi Iwai877211f2005-11-17 13:59:38 +01001257int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001258 unsigned int cond,
1259 snd_pcm_hw_param_t var,
1260 unsigned long step)
1261{
1262 return snd_pcm_hw_rule_add(runtime, cond, var,
1263 snd_pcm_hw_rule_step, (void *) step,
1264 var, -1);
1265}
1266
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001267EXPORT_SYMBOL(snd_pcm_hw_constraint_step);
1268
Takashi Iwai877211f2005-11-17 13:59:38 +01001269static 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 -07001270{
Marcin Åšlusarz67c39312007-12-14 12:53:21 +01001271 static unsigned int pow2_sizes[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272 1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7,
1273 1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15,
1274 1<<16, 1<<17, 1<<18, 1<<19, 1<<20, 1<<21, 1<<22, 1<<23,
1275 1<<24, 1<<25, 1<<26, 1<<27, 1<<28, 1<<29, 1<<30
1276 };
1277 return snd_interval_list(hw_param_interval(params, rule->var),
1278 ARRAY_SIZE(pow2_sizes), pow2_sizes, 0);
1279}
1280
1281/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001282 * snd_pcm_hw_constraint_pow2 - add a hw constraint power-of-2 rule
Takashi Iwaidf8db932005-09-07 13:38:19 +02001283 * @runtime: PCM runtime instance
1284 * @cond: condition bits
1285 * @var: hw_params variable to apply the power-of-2 constraint
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 */
Takashi Iwai877211f2005-11-17 13:59:38 +01001287int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288 unsigned int cond,
1289 snd_pcm_hw_param_t var)
1290{
1291 return snd_pcm_hw_rule_add(runtime, cond, var,
1292 snd_pcm_hw_rule_pow2, NULL,
1293 var, -1);
1294}
1295
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001296EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2);
1297
Takashi Iwai877211f2005-11-17 13:59:38 +01001298static void _snd_pcm_hw_param_any(struct snd_pcm_hw_params *params,
Adrian Bunk123992f2005-05-18 18:02:04 +02001299 snd_pcm_hw_param_t var)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300{
1301 if (hw_is_mask(var)) {
1302 snd_mask_any(hw_param_mask(params, var));
1303 params->cmask |= 1 << var;
1304 params->rmask |= 1 << var;
1305 return;
1306 }
1307 if (hw_is_interval(var)) {
1308 snd_interval_any(hw_param_interval(params, var));
1309 params->cmask |= 1 << var;
1310 params->rmask |= 1 << var;
1311 return;
1312 }
1313 snd_BUG();
1314}
1315
Takashi Iwai877211f2005-11-17 13:59:38 +01001316void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317{
1318 unsigned int k;
1319 memset(params, 0, sizeof(*params));
1320 for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++)
1321 _snd_pcm_hw_param_any(params, k);
1322 for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++)
1323 _snd_pcm_hw_param_any(params, k);
1324 params->info = ~0U;
1325}
1326
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001327EXPORT_SYMBOL(_snd_pcm_hw_params_any);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328
1329/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001330 * snd_pcm_hw_param_value - return @params field @var value
Takashi Iwaidf8db932005-09-07 13:38:19 +02001331 * @params: the hw_params instance
1332 * @var: parameter to retrieve
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001333 * @dir: pointer to the direction (-1,0,1) or %NULL
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334 *
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001335 * Return the value for field @var if it's fixed in configuration space
1336 * defined by @params. Return -%EINVAL otherwise.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337 */
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001338int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params,
1339 snd_pcm_hw_param_t var, int *dir)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340{
1341 if (hw_is_mask(var)) {
Takashi Iwai877211f2005-11-17 13:59:38 +01001342 const struct snd_mask *mask = hw_param_mask_c(params, var);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343 if (!snd_mask_single(mask))
1344 return -EINVAL;
1345 if (dir)
1346 *dir = 0;
1347 return snd_mask_value(mask);
1348 }
1349 if (hw_is_interval(var)) {
Takashi Iwai877211f2005-11-17 13:59:38 +01001350 const struct snd_interval *i = hw_param_interval_c(params, var);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351 if (!snd_interval_single(i))
1352 return -EINVAL;
1353 if (dir)
1354 *dir = i->openmin;
1355 return snd_interval_value(i);
1356 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357 return -EINVAL;
1358}
1359
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001360EXPORT_SYMBOL(snd_pcm_hw_param_value);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361
Takashi Iwai877211f2005-11-17 13:59:38 +01001362void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 snd_pcm_hw_param_t var)
1364{
1365 if (hw_is_mask(var)) {
1366 snd_mask_none(hw_param_mask(params, var));
1367 params->cmask |= 1 << var;
1368 params->rmask |= 1 << var;
1369 } else if (hw_is_interval(var)) {
1370 snd_interval_none(hw_param_interval(params, var));
1371 params->cmask |= 1 << var;
1372 params->rmask |= 1 << var;
1373 } else {
1374 snd_BUG();
1375 }
1376}
1377
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001378EXPORT_SYMBOL(_snd_pcm_hw_param_setempty);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379
Takashi Iwai877211f2005-11-17 13:59:38 +01001380static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params,
Adrian Bunk123992f2005-05-18 18:02:04 +02001381 snd_pcm_hw_param_t var)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382{
1383 int changed;
1384 if (hw_is_mask(var))
1385 changed = snd_mask_refine_first(hw_param_mask(params, var));
1386 else if (hw_is_interval(var))
1387 changed = snd_interval_refine_first(hw_param_interval(params, var));
Takashi Iwai2f4ca8e2006-04-28 15:13:40 +02001388 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390 if (changed) {
1391 params->cmask |= 1 << var;
1392 params->rmask |= 1 << var;
1393 }
1394 return changed;
1395}
1396
1397
1398/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001399 * snd_pcm_hw_param_first - refine config space and return minimum value
Takashi Iwaidf8db932005-09-07 13:38:19 +02001400 * @pcm: PCM instance
1401 * @params: the hw_params instance
1402 * @var: parameter to retrieve
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001403 * @dir: pointer to the direction (-1,0,1) or %NULL
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404 *
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001405 * Inside configuration space defined by @params remove from @var all
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406 * values > minimum. Reduce configuration space accordingly.
1407 * Return the minimum.
1408 */
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001409int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm,
1410 struct snd_pcm_hw_params *params,
1411 snd_pcm_hw_param_t var, int *dir)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412{
1413 int changed = _snd_pcm_hw_param_first(params, var);
1414 if (changed < 0)
1415 return changed;
1416 if (params->rmask) {
1417 int err = snd_pcm_hw_refine(pcm, params);
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001418 if (snd_BUG_ON(err < 0))
1419 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420 }
1421 return snd_pcm_hw_param_value(params, var, dir);
1422}
1423
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001424EXPORT_SYMBOL(snd_pcm_hw_param_first);
1425
Takashi Iwai877211f2005-11-17 13:59:38 +01001426static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params,
Adrian Bunk123992f2005-05-18 18:02:04 +02001427 snd_pcm_hw_param_t var)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001428{
1429 int changed;
1430 if (hw_is_mask(var))
1431 changed = snd_mask_refine_last(hw_param_mask(params, var));
1432 else if (hw_is_interval(var))
1433 changed = snd_interval_refine_last(hw_param_interval(params, var));
Takashi Iwai2f4ca8e2006-04-28 15:13:40 +02001434 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436 if (changed) {
1437 params->cmask |= 1 << var;
1438 params->rmask |= 1 << var;
1439 }
1440 return changed;
1441}
1442
1443
1444/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001445 * snd_pcm_hw_param_last - refine config space and return maximum value
Takashi Iwaidf8db932005-09-07 13:38:19 +02001446 * @pcm: PCM instance
1447 * @params: the hw_params instance
1448 * @var: parameter to retrieve
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001449 * @dir: pointer to the direction (-1,0,1) or %NULL
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450 *
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001451 * Inside configuration space defined by @params remove from @var all
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 * values < maximum. Reduce configuration space accordingly.
1453 * Return the maximum.
1454 */
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001455int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm,
1456 struct snd_pcm_hw_params *params,
1457 snd_pcm_hw_param_t var, int *dir)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001458{
1459 int changed = _snd_pcm_hw_param_last(params, var);
1460 if (changed < 0)
1461 return changed;
1462 if (params->rmask) {
1463 int err = snd_pcm_hw_refine(pcm, params);
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001464 if (snd_BUG_ON(err < 0))
1465 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466 }
1467 return snd_pcm_hw_param_value(params, var, dir);
1468}
1469
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001470EXPORT_SYMBOL(snd_pcm_hw_param_last);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471
1472/**
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001473 * snd_pcm_hw_param_choose - choose a configuration defined by @params
Takashi Iwaidf8db932005-09-07 13:38:19 +02001474 * @pcm: PCM instance
1475 * @params: the hw_params instance
Linus Torvalds1da177e2005-04-16 15:20:36 -07001476 *
Randy Dunlap1c85cc62008-10-15 14:38:40 -07001477 * Choose one configuration from configuration space defined by @params.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478 * The configuration chosen is that obtained fixing in this order:
1479 * first access, first format, first subformat, min channels,
1480 * min rate, min period time, max buffer size, min tick time
1481 */
Takashi Iwai2f4ca8e2006-04-28 15:13:40 +02001482int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm,
1483 struct snd_pcm_hw_params *params)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001484{
Takashi Iwai2f4ca8e2006-04-28 15:13:40 +02001485 static int vars[] = {
1486 SNDRV_PCM_HW_PARAM_ACCESS,
1487 SNDRV_PCM_HW_PARAM_FORMAT,
1488 SNDRV_PCM_HW_PARAM_SUBFORMAT,
1489 SNDRV_PCM_HW_PARAM_CHANNELS,
1490 SNDRV_PCM_HW_PARAM_RATE,
1491 SNDRV_PCM_HW_PARAM_PERIOD_TIME,
1492 SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
1493 SNDRV_PCM_HW_PARAM_TICK_TIME,
1494 -1
1495 };
1496 int err, *v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001497
Takashi Iwai2f4ca8e2006-04-28 15:13:40 +02001498 for (v = vars; *v != -1; v++) {
1499 if (*v != SNDRV_PCM_HW_PARAM_BUFFER_SIZE)
1500 err = snd_pcm_hw_param_first(pcm, params, *v, NULL);
1501 else
1502 err = snd_pcm_hw_param_last(pcm, params, *v, NULL);
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001503 if (snd_BUG_ON(err < 0))
1504 return err;
Takashi Iwai2f4ca8e2006-04-28 15:13:40 +02001505 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506 return 0;
1507}
1508
Takashi Iwai877211f2005-11-17 13:59:38 +01001509static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001510 void *arg)
1511{
Takashi Iwai877211f2005-11-17 13:59:38 +01001512 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513 unsigned long flags;
1514 snd_pcm_stream_lock_irqsave(substream, flags);
1515 if (snd_pcm_running(substream) &&
1516 snd_pcm_update_hw_ptr(substream) >= 0)
1517 runtime->status->hw_ptr %= runtime->buffer_size;
1518 else
1519 runtime->status->hw_ptr = 0;
1520 snd_pcm_stream_unlock_irqrestore(substream, flags);
1521 return 0;
1522}
1523
Takashi Iwai877211f2005-11-17 13:59:38 +01001524static int snd_pcm_lib_ioctl_channel_info(struct snd_pcm_substream *substream,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525 void *arg)
1526{
Takashi Iwai877211f2005-11-17 13:59:38 +01001527 struct snd_pcm_channel_info *info = arg;
1528 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529 int width;
1530 if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) {
1531 info->offset = -1;
1532 return 0;
1533 }
1534 width = snd_pcm_format_physical_width(runtime->format);
1535 if (width < 0)
1536 return width;
1537 info->offset = 0;
1538 switch (runtime->access) {
1539 case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED:
1540 case SNDRV_PCM_ACCESS_RW_INTERLEAVED:
1541 info->first = info->channel * width;
1542 info->step = runtime->channels * width;
1543 break;
1544 case SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED:
1545 case SNDRV_PCM_ACCESS_RW_NONINTERLEAVED:
1546 {
1547 size_t size = runtime->dma_bytes / runtime->channels;
1548 info->first = info->channel * size * 8;
1549 info->step = width;
1550 break;
1551 }
1552 default:
1553 snd_BUG();
1554 break;
1555 }
1556 return 0;
1557}
1558
Jaroslav Kysela8bea8692009-04-27 09:44:40 +02001559static int snd_pcm_lib_ioctl_fifo_size(struct snd_pcm_substream *substream,
1560 void *arg)
1561{
1562 struct snd_pcm_hw_params *params = arg;
1563 snd_pcm_format_t format;
1564 int channels, width;
1565
1566 params->fifo_size = substream->runtime->hw.fifo_size;
1567 if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_FIFO_IN_FRAMES)) {
1568 format = params_format(params);
1569 channels = params_channels(params);
1570 width = snd_pcm_format_physical_width(format);
1571 params->fifo_size /= width * channels;
1572 }
1573 return 0;
1574}
1575
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576/**
1577 * snd_pcm_lib_ioctl - a generic PCM ioctl callback
1578 * @substream: the pcm substream instance
1579 * @cmd: ioctl command
1580 * @arg: ioctl argument
1581 *
1582 * Processes the generic ioctl commands for PCM.
1583 * Can be passed as the ioctl callback for PCM ops.
1584 *
1585 * Returns zero if successful, or a negative error code on failure.
1586 */
Takashi Iwai877211f2005-11-17 13:59:38 +01001587int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588 unsigned int cmd, void *arg)
1589{
1590 switch (cmd) {
1591 case SNDRV_PCM_IOCTL1_INFO:
1592 return 0;
1593 case SNDRV_PCM_IOCTL1_RESET:
1594 return snd_pcm_lib_ioctl_reset(substream, arg);
1595 case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
1596 return snd_pcm_lib_ioctl_channel_info(substream, arg);
Jaroslav Kysela8bea8692009-04-27 09:44:40 +02001597 case SNDRV_PCM_IOCTL1_FIFO_SIZE:
1598 return snd_pcm_lib_ioctl_fifo_size(substream, arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599 }
1600 return -ENXIO;
1601}
1602
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001603EXPORT_SYMBOL(snd_pcm_lib_ioctl);
1604
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605/**
1606 * snd_pcm_period_elapsed - update the pcm status for the next period
1607 * @substream: the pcm substream instance
1608 *
1609 * This function is called from the interrupt handler when the
1610 * PCM has processed the period size. It will update the current
Takashi Iwai31e89602008-01-08 18:09:57 +01001611 * pointer, wake up sleepers, etc.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001612 *
1613 * Even if more than one periods have elapsed since the last call, you
1614 * have to call this only once.
1615 */
Takashi Iwai877211f2005-11-17 13:59:38 +01001616void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001617{
Takashi Iwai877211f2005-11-17 13:59:38 +01001618 struct snd_pcm_runtime *runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001619 unsigned long flags;
1620
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001621 if (PCM_RUNTIME_CHECK(substream))
1622 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001623 runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624
1625 if (runtime->transfer_ack_begin)
1626 runtime->transfer_ack_begin(substream);
1627
1628 snd_pcm_stream_lock_irqsave(substream, flags);
1629 if (!snd_pcm_running(substream) ||
1630 snd_pcm_update_hw_ptr_interrupt(substream) < 0)
1631 goto _end;
1632
1633 if (substream->timer_running)
1634 snd_timer_interrupt(substream->timer, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001635 _end:
1636 snd_pcm_stream_unlock_irqrestore(substream, flags);
1637 if (runtime->transfer_ack_end)
1638 runtime->transfer_ack_end(substream);
1639 kill_fasync(&runtime->fasync, SIGIO, POLL_IN);
1640}
1641
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001642EXPORT_SYMBOL(snd_pcm_period_elapsed);
1643
Takashi Iwai13075512008-01-08 18:08:14 +01001644/*
1645 * Wait until avail_min data becomes available
1646 * Returns a negative error code if any error occurs during operation.
1647 * The available space is stored on availp. When err = 0 and avail = 0
1648 * on the capture stream, it indicates the stream is in DRAINING state.
1649 */
1650static int wait_for_avail_min(struct snd_pcm_substream *substream,
1651 snd_pcm_uframes_t *availp)
1652{
1653 struct snd_pcm_runtime *runtime = substream->runtime;
1654 int is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
1655 wait_queue_t wait;
1656 int err = 0;
1657 snd_pcm_uframes_t avail = 0;
1658 long tout;
1659
1660 init_waitqueue_entry(&wait, current);
1661 add_wait_queue(&runtime->sleep, &wait);
1662 for (;;) {
1663 if (signal_pending(current)) {
1664 err = -ERESTARTSYS;
1665 break;
1666 }
1667 set_current_state(TASK_INTERRUPTIBLE);
1668 snd_pcm_stream_unlock_irq(substream);
1669 tout = schedule_timeout(msecs_to_jiffies(10000));
1670 snd_pcm_stream_lock_irq(substream);
1671 switch (runtime->status->state) {
1672 case SNDRV_PCM_STATE_SUSPENDED:
1673 err = -ESTRPIPE;
1674 goto _endloop;
1675 case SNDRV_PCM_STATE_XRUN:
1676 err = -EPIPE;
1677 goto _endloop;
1678 case SNDRV_PCM_STATE_DRAINING:
1679 if (is_playback)
1680 err = -EPIPE;
1681 else
1682 avail = 0; /* indicate draining */
1683 goto _endloop;
1684 case SNDRV_PCM_STATE_OPEN:
1685 case SNDRV_PCM_STATE_SETUP:
1686 case SNDRV_PCM_STATE_DISCONNECTED:
1687 err = -EBADFD;
1688 goto _endloop;
1689 }
1690 if (!tout) {
1691 snd_printd("%s write error (DMA or IRQ trouble?)\n",
1692 is_playback ? "playback" : "capture");
1693 err = -EIO;
1694 break;
1695 }
1696 if (is_playback)
1697 avail = snd_pcm_playback_avail(runtime);
1698 else
1699 avail = snd_pcm_capture_avail(runtime);
1700 if (avail >= runtime->control->avail_min)
1701 break;
1702 }
1703 _endloop:
1704 remove_wait_queue(&runtime->sleep, &wait);
1705 *availp = avail;
1706 return err;
1707}
1708
Takashi Iwai877211f2005-11-17 13:59:38 +01001709static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001710 unsigned int hwoff,
1711 unsigned long data, unsigned int off,
1712 snd_pcm_uframes_t frames)
1713{
Takashi Iwai877211f2005-11-17 13:59:38 +01001714 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001715 int err;
1716 char __user *buf = (char __user *) data + frames_to_bytes(runtime, off);
1717 if (substream->ops->copy) {
1718 if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0)
1719 return err;
1720 } else {
1721 char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722 if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames)))
1723 return -EFAULT;
1724 }
1725 return 0;
1726}
1727
Takashi Iwai877211f2005-11-17 13:59:38 +01001728typedef int (*transfer_f)(struct snd_pcm_substream *substream, unsigned int hwoff,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001729 unsigned long data, unsigned int off,
1730 snd_pcm_uframes_t size);
1731
Takashi Iwai877211f2005-11-17 13:59:38 +01001732static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733 unsigned long data,
1734 snd_pcm_uframes_t size,
1735 int nonblock,
1736 transfer_f transfer)
1737{
Takashi Iwai877211f2005-11-17 13:59:38 +01001738 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739 snd_pcm_uframes_t xfer = 0;
1740 snd_pcm_uframes_t offset = 0;
1741 int err = 0;
1742
1743 if (size == 0)
1744 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001745
1746 snd_pcm_stream_lock_irq(substream);
1747 switch (runtime->status->state) {
1748 case SNDRV_PCM_STATE_PREPARED:
1749 case SNDRV_PCM_STATE_RUNNING:
1750 case SNDRV_PCM_STATE_PAUSED:
1751 break;
1752 case SNDRV_PCM_STATE_XRUN:
1753 err = -EPIPE;
1754 goto _end_unlock;
1755 case SNDRV_PCM_STATE_SUSPENDED:
1756 err = -ESTRPIPE;
1757 goto _end_unlock;
1758 default:
1759 err = -EBADFD;
1760 goto _end_unlock;
1761 }
1762
1763 while (size > 0) {
1764 snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
1765 snd_pcm_uframes_t avail;
1766 snd_pcm_uframes_t cont;
Takashi Iwai31e89602008-01-08 18:09:57 +01001767 if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001768 snd_pcm_update_hw_ptr(substream);
1769 avail = snd_pcm_playback_avail(runtime);
Takashi Iwai13075512008-01-08 18:08:14 +01001770 if (!avail) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001771 if (nonblock) {
1772 err = -EAGAIN;
1773 goto _end_unlock;
1774 }
Takashi Iwai13075512008-01-08 18:08:14 +01001775 err = wait_for_avail_min(substream, &avail);
1776 if (err < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001777 goto _end_unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001778 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001779 frames = size > avail ? avail : size;
1780 cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
1781 if (frames > cont)
1782 frames = cont;
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001783 if (snd_BUG_ON(!frames)) {
1784 snd_pcm_stream_unlock_irq(substream);
1785 return -EINVAL;
1786 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001787 appl_ptr = runtime->control->appl_ptr;
1788 appl_ofs = appl_ptr % runtime->buffer_size;
1789 snd_pcm_stream_unlock_irq(substream);
1790 if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0)
1791 goto _end;
1792 snd_pcm_stream_lock_irq(substream);
1793 switch (runtime->status->state) {
1794 case SNDRV_PCM_STATE_XRUN:
1795 err = -EPIPE;
1796 goto _end_unlock;
1797 case SNDRV_PCM_STATE_SUSPENDED:
1798 err = -ESTRPIPE;
1799 goto _end_unlock;
1800 default:
1801 break;
1802 }
1803 appl_ptr += frames;
1804 if (appl_ptr >= runtime->boundary)
1805 appl_ptr -= runtime->boundary;
1806 runtime->control->appl_ptr = appl_ptr;
1807 if (substream->ops->ack)
1808 substream->ops->ack(substream);
1809
1810 offset += frames;
1811 size -= frames;
1812 xfer += frames;
1813 if (runtime->status->state == SNDRV_PCM_STATE_PREPARED &&
1814 snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) {
1815 err = snd_pcm_start(substream);
1816 if (err < 0)
1817 goto _end_unlock;
1818 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001819 }
1820 _end_unlock:
1821 snd_pcm_stream_unlock_irq(substream);
1822 _end:
1823 return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
1824}
1825
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001826/* sanity-check for read/write methods */
1827static int pcm_sanity_check(struct snd_pcm_substream *substream)
1828{
1829 struct snd_pcm_runtime *runtime;
1830 if (PCM_RUNTIME_CHECK(substream))
1831 return -ENXIO;
1832 runtime = substream->runtime;
1833 if (snd_BUG_ON(!substream->ops->copy && !runtime->dma_area))
1834 return -EINVAL;
1835 if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
1836 return -EBADFD;
1837 return 0;
1838}
1839
Takashi Iwai877211f2005-11-17 13:59:38 +01001840snd_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 -07001841{
Takashi Iwai877211f2005-11-17 13:59:38 +01001842 struct snd_pcm_runtime *runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001843 int nonblock;
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001844 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001845
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001846 err = pcm_sanity_check(substream);
1847 if (err < 0)
1848 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001849 runtime = substream->runtime;
Takashi Iwai0df63e42006-04-28 15:13:41 +02001850 nonblock = !!(substream->f_flags & O_NONBLOCK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001851
1852 if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&
1853 runtime->channels > 1)
1854 return -EINVAL;
1855 return snd_pcm_lib_write1(substream, (unsigned long)buf, size, nonblock,
1856 snd_pcm_lib_write_transfer);
1857}
1858
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001859EXPORT_SYMBOL(snd_pcm_lib_write);
1860
Takashi Iwai877211f2005-11-17 13:59:38 +01001861static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001862 unsigned int hwoff,
1863 unsigned long data, unsigned int off,
1864 snd_pcm_uframes_t frames)
1865{
Takashi Iwai877211f2005-11-17 13:59:38 +01001866 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001867 int err;
1868 void __user **bufs = (void __user **)data;
1869 int channels = runtime->channels;
1870 int c;
1871 if (substream->ops->copy) {
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001872 if (snd_BUG_ON(!substream->ops->silence))
1873 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001874 for (c = 0; c < channels; ++c, ++bufs) {
1875 if (*bufs == NULL) {
1876 if ((err = substream->ops->silence(substream, c, hwoff, frames)) < 0)
1877 return err;
1878 } else {
1879 char __user *buf = *bufs + samples_to_bytes(runtime, off);
1880 if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0)
1881 return err;
1882 }
1883 }
1884 } else {
1885 /* default transfer behaviour */
1886 size_t dma_csize = runtime->dma_bytes / channels;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001887 for (c = 0; c < channels; ++c, ++bufs) {
1888 char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff);
1889 if (*bufs == NULL) {
1890 snd_pcm_format_set_silence(runtime->format, hwbuf, frames);
1891 } else {
1892 char __user *buf = *bufs + samples_to_bytes(runtime, off);
1893 if (copy_from_user(hwbuf, buf, samples_to_bytes(runtime, frames)))
1894 return -EFAULT;
1895 }
1896 }
1897 }
1898 return 0;
1899}
1900
Takashi Iwai877211f2005-11-17 13:59:38 +01001901snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001902 void __user **bufs,
1903 snd_pcm_uframes_t frames)
1904{
Takashi Iwai877211f2005-11-17 13:59:38 +01001905 struct snd_pcm_runtime *runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001906 int nonblock;
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001907 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001909 err = pcm_sanity_check(substream);
1910 if (err < 0)
1911 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001912 runtime = substream->runtime;
Takashi Iwai0df63e42006-04-28 15:13:41 +02001913 nonblock = !!(substream->f_flags & O_NONBLOCK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001914
1915 if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
1916 return -EINVAL;
1917 return snd_pcm_lib_write1(substream, (unsigned long)bufs, frames,
1918 nonblock, snd_pcm_lib_writev_transfer);
1919}
1920
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001921EXPORT_SYMBOL(snd_pcm_lib_writev);
1922
Takashi Iwai877211f2005-11-17 13:59:38 +01001923static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001924 unsigned int hwoff,
1925 unsigned long data, unsigned int off,
1926 snd_pcm_uframes_t frames)
1927{
Takashi Iwai877211f2005-11-17 13:59:38 +01001928 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929 int err;
1930 char __user *buf = (char __user *) data + frames_to_bytes(runtime, off);
1931 if (substream->ops->copy) {
1932 if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0)
1933 return err;
1934 } else {
1935 char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001936 if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames)))
1937 return -EFAULT;
1938 }
1939 return 0;
1940}
1941
Takashi Iwai877211f2005-11-17 13:59:38 +01001942static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943 unsigned long data,
1944 snd_pcm_uframes_t size,
1945 int nonblock,
1946 transfer_f transfer)
1947{
Takashi Iwai877211f2005-11-17 13:59:38 +01001948 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001949 snd_pcm_uframes_t xfer = 0;
1950 snd_pcm_uframes_t offset = 0;
1951 int err = 0;
1952
1953 if (size == 0)
1954 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955
1956 snd_pcm_stream_lock_irq(substream);
1957 switch (runtime->status->state) {
1958 case SNDRV_PCM_STATE_PREPARED:
1959 if (size >= runtime->start_threshold) {
1960 err = snd_pcm_start(substream);
1961 if (err < 0)
1962 goto _end_unlock;
1963 }
1964 break;
1965 case SNDRV_PCM_STATE_DRAINING:
1966 case SNDRV_PCM_STATE_RUNNING:
1967 case SNDRV_PCM_STATE_PAUSED:
1968 break;
1969 case SNDRV_PCM_STATE_XRUN:
1970 err = -EPIPE;
1971 goto _end_unlock;
1972 case SNDRV_PCM_STATE_SUSPENDED:
1973 err = -ESTRPIPE;
1974 goto _end_unlock;
1975 default:
1976 err = -EBADFD;
1977 goto _end_unlock;
1978 }
1979
1980 while (size > 0) {
1981 snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
1982 snd_pcm_uframes_t avail;
1983 snd_pcm_uframes_t cont;
Takashi Iwai31e89602008-01-08 18:09:57 +01001984 if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001985 snd_pcm_update_hw_ptr(substream);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001986 avail = snd_pcm_capture_avail(runtime);
Takashi Iwai13075512008-01-08 18:08:14 +01001987 if (!avail) {
1988 if (runtime->status->state ==
1989 SNDRV_PCM_STATE_DRAINING) {
1990 snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001991 goto _end_unlock;
1992 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001993 if (nonblock) {
1994 err = -EAGAIN;
1995 goto _end_unlock;
1996 }
Takashi Iwai13075512008-01-08 18:08:14 +01001997 err = wait_for_avail_min(substream, &avail);
1998 if (err < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999 goto _end_unlock;
Takashi Iwai13075512008-01-08 18:08:14 +01002000 if (!avail)
2001 continue; /* draining */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002002 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002003 frames = size > avail ? avail : size;
2004 cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
2005 if (frames > cont)
2006 frames = cont;
Takashi Iwai7eaa9432008-08-08 17:09:09 +02002007 if (snd_BUG_ON(!frames)) {
2008 snd_pcm_stream_unlock_irq(substream);
2009 return -EINVAL;
2010 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002011 appl_ptr = runtime->control->appl_ptr;
2012 appl_ofs = appl_ptr % runtime->buffer_size;
2013 snd_pcm_stream_unlock_irq(substream);
2014 if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0)
2015 goto _end;
2016 snd_pcm_stream_lock_irq(substream);
2017 switch (runtime->status->state) {
2018 case SNDRV_PCM_STATE_XRUN:
2019 err = -EPIPE;
2020 goto _end_unlock;
2021 case SNDRV_PCM_STATE_SUSPENDED:
2022 err = -ESTRPIPE;
2023 goto _end_unlock;
2024 default:
2025 break;
2026 }
2027 appl_ptr += frames;
2028 if (appl_ptr >= runtime->boundary)
2029 appl_ptr -= runtime->boundary;
2030 runtime->control->appl_ptr = appl_ptr;
2031 if (substream->ops->ack)
2032 substream->ops->ack(substream);
2033
2034 offset += frames;
2035 size -= frames;
2036 xfer += frames;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002037 }
2038 _end_unlock:
2039 snd_pcm_stream_unlock_irq(substream);
2040 _end:
2041 return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
2042}
2043
Takashi Iwai877211f2005-11-17 13:59:38 +01002044snd_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 -07002045{
Takashi Iwai877211f2005-11-17 13:59:38 +01002046 struct snd_pcm_runtime *runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002047 int nonblock;
Takashi Iwai7eaa9432008-08-08 17:09:09 +02002048 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002049
Takashi Iwai7eaa9432008-08-08 17:09:09 +02002050 err = pcm_sanity_check(substream);
2051 if (err < 0)
2052 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053 runtime = substream->runtime;
Takashi Iwai0df63e42006-04-28 15:13:41 +02002054 nonblock = !!(substream->f_flags & O_NONBLOCK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002055 if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED)
2056 return -EINVAL;
2057 return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer);
2058}
2059
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02002060EXPORT_SYMBOL(snd_pcm_lib_read);
2061
Takashi Iwai877211f2005-11-17 13:59:38 +01002062static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002063 unsigned int hwoff,
2064 unsigned long data, unsigned int off,
2065 snd_pcm_uframes_t frames)
2066{
Takashi Iwai877211f2005-11-17 13:59:38 +01002067 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002068 int err;
2069 void __user **bufs = (void __user **)data;
2070 int channels = runtime->channels;
2071 int c;
2072 if (substream->ops->copy) {
2073 for (c = 0; c < channels; ++c, ++bufs) {
2074 char __user *buf;
2075 if (*bufs == NULL)
2076 continue;
2077 buf = *bufs + samples_to_bytes(runtime, off);
2078 if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0)
2079 return err;
2080 }
2081 } else {
2082 snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083 for (c = 0; c < channels; ++c, ++bufs) {
2084 char *hwbuf;
2085 char __user *buf;
2086 if (*bufs == NULL)
2087 continue;
2088
2089 hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff);
2090 buf = *bufs + samples_to_bytes(runtime, off);
2091 if (copy_to_user(buf, hwbuf, samples_to_bytes(runtime, frames)))
2092 return -EFAULT;
2093 }
2094 }
2095 return 0;
2096}
2097
Takashi Iwai877211f2005-11-17 13:59:38 +01002098snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002099 void __user **bufs,
2100 snd_pcm_uframes_t frames)
2101{
Takashi Iwai877211f2005-11-17 13:59:38 +01002102 struct snd_pcm_runtime *runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002103 int nonblock;
Takashi Iwai7eaa9432008-08-08 17:09:09 +02002104 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002105
Takashi Iwai7eaa9432008-08-08 17:09:09 +02002106 err = pcm_sanity_check(substream);
2107 if (err < 0)
2108 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002109 runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002110 if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
2111 return -EBADFD;
2112
Takashi Iwai0df63e42006-04-28 15:13:41 +02002113 nonblock = !!(substream->f_flags & O_NONBLOCK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002114 if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
2115 return -EINVAL;
2116 return snd_pcm_lib_read1(substream, (unsigned long)bufs, frames, nonblock, snd_pcm_lib_readv_transfer);
2117}
2118
Linus Torvalds1da177e2005-04-16 15:20:36 -07002119EXPORT_SYMBOL(snd_pcm_lib_readv);