Blame view

sound/core/pcm_lib.c 71.2 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
  /*
   *  Digital Audio (PCM) abstract layer
c1017a4cd   Jaroslav Kysela   [ALSA] Changed Ja...
3
   *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
   *                   Abramo Bagnara <abramo@alsa-project.org>
   *
   *
   *   This program is free software; you can redistribute it and/or modify
   *   it under the terms of the GNU General Public License as published by
   *   the Free Software Foundation; either version 2 of the License, or
   *   (at your option) any later version.
   *
   *   This program is distributed in the hope that it will be useful,
   *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *   GNU General Public License for more details.
   *
   *   You should have received a copy of the GNU General Public License
   *   along with this program; if not, write to the Free Software
   *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
   *
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
23
  #include <linux/slab.h>
  #include <linux/time.h>
3f7440a6b   Takashi Iwai   ALSA: Clean up 64...
24
  #include <linux/math64.h>
d81a6d717   Paul Gortmaker   sound: Add export...
25
  #include <linux/export.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26
27
  #include <sound/core.h>
  #include <sound/control.h>
2d3391ec0   Takashi Iwai   ALSA: PCM: channe...
28
  #include <sound/tlv.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
30
31
32
  #include <sound/info.h>
  #include <sound/pcm.h>
  #include <sound/pcm_params.h>
  #include <sound/timer.h>
f5914908a   Takashi Iwai   ALSA: pcm: Replac...
33
34
35
36
37
38
39
40
  #ifdef CONFIG_SND_PCM_XRUN_DEBUG
  #define CREATE_TRACE_POINTS
  #include "pcm_trace.h"
  #else
  #define trace_hwptr(substream, pos, in_interrupt)
  #define trace_xrun(substream)
  #define trace_hw_ptr_error(substream, reason)
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41
42
43
44
45
46
47
48
49
  /*
   * fill ring buffer with silence
   * runtime->silence_start: starting pointer to silence area
   * runtime->silence_filled: size filled with silence
   * runtime->silence_threshold: threshold from application
   * runtime->silence_size: maximal size from application
   *
   * when runtime->silence_size >= runtime->boundary - fill processed area with silence immediately
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
50
  void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_uframes_t new_hw_ptr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
52
  	struct snd_pcm_runtime *runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
53
54
55
56
57
58
59
60
61
62
63
64
65
66
  	snd_pcm_uframes_t frames, ofs, transfer;
  
  	if (runtime->silence_size < runtime->boundary) {
  		snd_pcm_sframes_t noise_dist, n;
  		if (runtime->silence_start != runtime->control->appl_ptr) {
  			n = runtime->control->appl_ptr - runtime->silence_start;
  			if (n < 0)
  				n += runtime->boundary;
  			if ((snd_pcm_uframes_t)n < runtime->silence_filled)
  				runtime->silence_filled -= n;
  			else
  				runtime->silence_filled = 0;
  			runtime->silence_start = runtime->control->appl_ptr;
  		}
235475cb7   Takashi Iwai   [ALSA] pcm - Fix ...
67
  		if (runtime->silence_filled >= runtime->buffer_size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
  			return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
70
71
72
73
74
75
76
77
  		noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled;
  		if (noise_dist >= (snd_pcm_sframes_t) runtime->silence_threshold)
  			return;
  		frames = runtime->silence_threshold - noise_dist;
  		if (frames > runtime->silence_size)
  			frames = runtime->silence_size;
  	} else {
  		if (new_hw_ptr == ULONG_MAX) {	/* initialization */
  			snd_pcm_sframes_t avail = snd_pcm_playback_hw_avail(runtime);
9e216e8a4   Jaroslav Kysela   ALSA: pcm core - ...
78
79
  			if (avail > runtime->buffer_size)
  				avail = runtime->buffer_size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
81
82
83
84
85
86
87
88
89
90
91
  			runtime->silence_filled = avail > 0 ? avail : 0;
  			runtime->silence_start = (runtime->status->hw_ptr +
  						  runtime->silence_filled) %
  						 runtime->boundary;
  		} else {
  			ofs = runtime->status->hw_ptr;
  			frames = new_hw_ptr - ofs;
  			if ((snd_pcm_sframes_t)frames < 0)
  				frames += runtime->boundary;
  			runtime->silence_filled -= frames;
  			if ((snd_pcm_sframes_t)runtime->silence_filled < 0) {
  				runtime->silence_filled = 0;
9a826ddba   Clemens Ladisch   [ALSA] pcm core: ...
92
  				runtime->silence_start = new_hw_ptr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93
  			} else {
9a826ddba   Clemens Ladisch   [ALSA] pcm core: ...
94
  				runtime->silence_start = ofs;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
97
98
  		}
  		frames = runtime->buffer_size - runtime->silence_filled;
  	}
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
99
100
  	if (snd_BUG_ON(frames > runtime->buffer_size))
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101
102
  	if (frames == 0)
  		return;
9a826ddba   Clemens Ladisch   [ALSA] pcm core: ...
103
  	ofs = runtime->silence_start % runtime->buffer_size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
104
105
106
107
108
109
110
  	while (frames > 0) {
  		transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames;
  		if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ||
  		    runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) {
  			if (substream->ops->silence) {
  				int err;
  				err = substream->ops->silence(substream, -1, ofs, transfer);
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
111
  				snd_BUG_ON(err < 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
112
113
114
115
116
117
118
119
120
121
122
  			} else {
  				char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs);
  				snd_pcm_format_set_silence(runtime->format, hwbuf, transfer * runtime->channels);
  			}
  		} else {
  			unsigned int c;
  			unsigned int channels = runtime->channels;
  			if (substream->ops->silence) {
  				for (c = 0; c < channels; ++c) {
  					int err;
  					err = substream->ops->silence(substream, c, ofs, transfer);
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
123
  					snd_BUG_ON(err < 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
125
126
127
128
129
130
131
132
133
134
135
136
137
  				}
  			} else {
  				size_t dma_csize = runtime->dma_bytes / channels;
  				for (c = 0; c < channels; ++c) {
  					char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, ofs);
  					snd_pcm_format_set_silence(runtime->format, hwbuf, transfer);
  				}
  			}
  		}
  		runtime->silence_filled += transfer;
  		frames -= transfer;
  		ofs = 0;
  	}
  }
acb03d440   Eliot Blennerhassett   ALSA: Make snd_pc...
138
139
  #ifdef CONFIG_SND_DEBUG
  void snd_pcm_debug_name(struct snd_pcm_substream *substream,
c00701101   Takashi Iwai   ALSA: pcm - A hel...
140
141
142
143
144
145
146
147
  			   char *name, size_t len)
  {
  	snprintf(name, len, "pcmC%dD%d%c:%d",
  		 substream->pcm->card->number,
  		 substream->pcm->device,
  		 substream->stream ? 'c' : 'p',
  		 substream->number);
  }
acb03d440   Eliot Blennerhassett   ALSA: Make snd_pc...
148
149
  EXPORT_SYMBOL(snd_pcm_debug_name);
  #endif
c00701101   Takashi Iwai   ALSA: pcm - A hel...
150

741b20cfb   Jaroslav Kysela   ALSA: pcm_lib.c -...
151
152
153
  #define XRUN_DEBUG_BASIC	(1<<0)
  #define XRUN_DEBUG_STACK	(1<<1)	/* dump also stack */
  #define XRUN_DEBUG_JIFFIESCHECK	(1<<2)	/* do jiffies check */
741b20cfb   Jaroslav Kysela   ALSA: pcm_lib.c -...
154

ed3da3d9a   Takashi Iwai   ALSA: Rewrite hw_...
155
  #ifdef CONFIG_SND_PCM_XRUN_DEBUG
4d96eb255   Jaroslav Kysela   ALSA: pcm_lib - a...
156

741b20cfb   Jaroslav Kysela   ALSA: pcm_lib.c -...
157
158
  #define xrun_debug(substream, mask) \
  			((substream)->pstr->xrun_debug & (mask))
0f17014b3   Jarkko Nikula   ALSA: pcm_lib - f...
159
160
161
  #else
  #define xrun_debug(substream, mask)	0
  #endif
ed3da3d9a   Takashi Iwai   ALSA: Rewrite hw_...
162

741b20cfb   Jaroslav Kysela   ALSA: pcm_lib.c -...
163
164
165
  #define dump_stack_on_xrun(substream) do {			\
  		if (xrun_debug(substream, XRUN_DEBUG_STACK))	\
  			dump_stack();				\
ed3da3d9a   Takashi Iwai   ALSA: Rewrite hw_...
166
  	} while (0)
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
167
  static void xrun(struct snd_pcm_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168
  {
13f040f9e   Jaroslav Kysela   ALSA: PCM midleve...
169
  	struct snd_pcm_runtime *runtime = substream->runtime;
f5914908a   Takashi Iwai   ALSA: pcm: Replac...
170
  	trace_xrun(substream);
13f040f9e   Jaroslav Kysela   ALSA: PCM midleve...
171
172
  	if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
  		snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173
  	snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
741b20cfb   Jaroslav Kysela   ALSA: pcm_lib.c -...
174
  	if (xrun_debug(substream, XRUN_DEBUG_BASIC)) {
c00701101   Takashi Iwai   ALSA: pcm - A hel...
175
  		char name[16];
acb03d440   Eliot Blennerhassett   ALSA: Make snd_pc...
176
  		snd_pcm_debug_name(substream, name, sizeof(name));
09e56df8b   Takashi Iwai   ALSA: pcm: Use st...
177
178
  		pcm_warn(substream->pcm, "XRUN: %s
  ", name);
ed3da3d9a   Takashi Iwai   ALSA: Rewrite hw_...
179
  		dump_stack_on_xrun(substream);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
  }
0f17014b3   Jarkko Nikula   ALSA: pcm_lib - f...
182
  #ifdef CONFIG_SND_PCM_XRUN_DEBUG
f5914908a   Takashi Iwai   ALSA: pcm: Replac...
183
  #define hw_ptr_error(substream, in_interrupt, reason, fmt, args...)	\
4d96eb255   Jaroslav Kysela   ALSA: pcm_lib - a...
184
  	do {								\
f5914908a   Takashi Iwai   ALSA: pcm: Replac...
185
  		trace_hw_ptr_error(substream, reason);	\
4d96eb255   Jaroslav Kysela   ALSA: pcm_lib - a...
186
  		if (xrun_debug(substream, XRUN_DEBUG_BASIC)) {		\
f5914908a   Takashi Iwai   ALSA: pcm: Replac...
187
188
  			pr_err_ratelimited("ALSA: PCM: [%c] " reason ": " fmt, \
  					   (in_interrupt) ? 'Q' : 'P', ##args);	\
4d96eb255   Jaroslav Kysela   ALSA: pcm_lib - a...
189
190
191
  			dump_stack_on_xrun(substream);			\
  		}							\
  	} while (0)
4d96eb255   Jaroslav Kysela   ALSA: pcm_lib - a...
192
  #else /* ! CONFIG_SND_PCM_XRUN_DEBUG */
4d96eb255   Jaroslav Kysela   ALSA: pcm_lib - a...
193
  #define hw_ptr_error(substream, fmt, args...) do { } while (0)
4d96eb255   Jaroslav Kysela   ALSA: pcm_lib - a...
194
195
  
  #endif
1250932e4   Jaroslav Kysela   ALSA: pcm_lib - o...
196
197
  int snd_pcm_update_state(struct snd_pcm_substream *substream,
  			 struct snd_pcm_runtime *runtime)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198
199
200
201
202
203
204
205
206
  {
  	snd_pcm_uframes_t avail;
  
  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  		avail = snd_pcm_playback_avail(runtime);
  	else
  		avail = snd_pcm_capture_avail(runtime);
  	if (avail > runtime->avail_max)
  		runtime->avail_max = avail;
4cdc115fd   Takashi Iwai   ALSA: pcm - Fix d...
207
208
  	if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
  		if (avail >= runtime->buffer_size) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
  			snd_pcm_drain_done(substream);
4cdc115fd   Takashi Iwai   ALSA: pcm - Fix d...
210
211
212
213
  			return -EPIPE;
  		}
  	} else {
  		if (avail >= runtime->stop_threshold) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
  			xrun(substream);
4cdc115fd   Takashi Iwai   ALSA: pcm - Fix d...
215
216
  			return -EPIPE;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217
  	}
5daeba34d   David Dillow   ALSA: pcm_lib: av...
218
219
220
221
222
  	if (runtime->twake) {
  		if (avail >= runtime->twake)
  			wake_up(&runtime->tsleep);
  	} else if (avail >= runtime->control->avail_min)
  		wake_up(&runtime->sleep);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
223
224
  	return 0;
  }
3179f6200   Pierre-Louis Bossart   ALSA: core: add ....
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
  static void update_audio_tstamp(struct snd_pcm_substream *substream,
  				struct timespec *curr_tstamp,
  				struct timespec *audio_tstamp)
  {
  	struct snd_pcm_runtime *runtime = substream->runtime;
  	u64 audio_frames, audio_nsecs;
  	struct timespec driver_tstamp;
  
  	if (runtime->tstamp_mode != SNDRV_PCM_TSTAMP_ENABLE)
  		return;
  
  	if (!(substream->ops->get_time_info) ||
  		(runtime->audio_tstamp_report.actual_type ==
  			SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) {
  
  		/*
  		 * provide audio timestamp derived from pointer position
  		 * add delay only if requested
  		 */
  
  		audio_frames = runtime->hw_ptr_wrap + runtime->status->hw_ptr;
  
  		if (runtime->audio_tstamp_config.report_delay) {
  			if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  				audio_frames -=  runtime->delay;
  			else
  				audio_frames +=  runtime->delay;
  		}
  		audio_nsecs = div_u64(audio_frames * 1000000000LL,
  				runtime->rate);
  		*audio_tstamp = ns_to_timespec(audio_nsecs);
  	}
  	runtime->status->audio_tstamp = *audio_tstamp;
  	runtime->status->tstamp = *curr_tstamp;
  
  	/*
  	 * re-take a driver timestamp to let apps detect if the reference tstamp
  	 * read by low-level hardware was provided with a delay
  	 */
  	snd_pcm_gettime(substream->runtime, (struct timespec *)&driver_tstamp);
  	runtime->driver_tstamp = driver_tstamp;
  }
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
267
268
  static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
  				  unsigned int in_interrupt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
270
  	struct snd_pcm_runtime *runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271
  	snd_pcm_uframes_t pos;
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
272
  	snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base;
bbf6ad139   Jaroslav Kysela   [ALSA] pcm-midlev...
273
274
  	snd_pcm_sframes_t hdelta, delta;
  	unsigned long jdelta;
3509a03f4   Pierre-Louis Bossart   ALSA: core: group...
275
276
  	unsigned long curr_jiffies;
  	struct timespec curr_tstamp;
4eeaaeaea   Pierre-Louis Bossart   ALSA: core: add h...
277
  	struct timespec audio_tstamp;
0e8014d77   Pierre-Louis Bossart   ALSA: core: keep ...
278
  	int crossed_boundary = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
279

bbf6ad139   Jaroslav Kysela   [ALSA] pcm-midlev...
280
  	old_hw_ptr = runtime->status->hw_ptr;
3509a03f4   Pierre-Louis Bossart   ALSA: core: group...
281
282
283
284
285
286
287
  
  	/*
  	 * group pointer, time and jiffies reads to allow for more
  	 * accurate correlations/corrections.
  	 * The values are stored at the end of this routine after
  	 * corrections for hw_ptr position
  	 */
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
288
  	pos = substream->ops->pointer(substream);
3509a03f4   Pierre-Louis Bossart   ALSA: core: group...
289
  	curr_jiffies = jiffies;
4eeaaeaea   Pierre-Louis Bossart   ALSA: core: add h...
290
  	if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
3179f6200   Pierre-Louis Bossart   ALSA: core: add ....
291
292
293
294
295
296
297
298
299
300
301
302
  		if ((substream->ops->get_time_info) &&
  			(runtime->audio_tstamp_config.type_requested != SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) {
  			substream->ops->get_time_info(substream, &curr_tstamp,
  						&audio_tstamp,
  						&runtime->audio_tstamp_config,
  						&runtime->audio_tstamp_report);
  
  			/* re-test in case tstamp type is not supported in hardware and was demoted to DEFAULT */
  			if (runtime->audio_tstamp_report.actual_type == SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)
  				snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp);
  		} else
  			snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp);
4eeaaeaea   Pierre-Louis Bossart   ALSA: core: add h...
303
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
305
306
307
  	if (pos == SNDRV_PCM_POS_XRUN) {
  		xrun(substream);
  		return -EPIPE;
  	}
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
308
  	if (pos >= runtime->buffer_size) {
09e56df8b   Takashi Iwai   ALSA: pcm: Use st...
309
  		if (printk_ratelimit()) {
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
310
  			char name[16];
acb03d440   Eliot Blennerhassett   ALSA: Make snd_pc...
311
  			snd_pcm_debug_name(substream, name, sizeof(name));
09e56df8b   Takashi Iwai   ALSA: pcm: Use st...
312
  			pcm_err(substream->pcm,
d507941be   Takashi Iwai   ALSA: pcm: Correc...
313
314
  				"BUG: %s, pos = %ld, buffer size = %ld, period size = %ld
  ",
09e56df8b   Takashi Iwai   ALSA: pcm: Use st...
315
316
  				name, pos, runtime->buffer_size,
  				runtime->period_size);
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
317
318
  		}
  		pos = 0;
cedb8118e   Takashi Iwai   ALSA: pcm - Add l...
319
  	}
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
320
  	pos -= pos % runtime->min_align;
f5914908a   Takashi Iwai   ALSA: pcm: Replac...
321
  	trace_hwptr(substream, pos, in_interrupt);
ed3da3d9a   Takashi Iwai   ALSA: Rewrite hw_...
322
323
  	hw_base = runtime->hw_ptr_base;
  	new_hw_ptr = hw_base + pos;
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
324
325
326
  	if (in_interrupt) {
  		/* we know that one period was processed */
  		/* delta = "expected next hw_ptr" for in_interrupt != 0 */
e76369257   Jaroslav Kysela   ALSA: pcm_lib - r...
327
  		delta = runtime->hw_ptr_interrupt + runtime->period_size;
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
328
  		if (delta > new_hw_ptr) {
bd76af0f8   Jaroslav Kysela   ALSA: pcm midleve...
329
  			/* check for double acknowledged interrupts */
3509a03f4   Pierre-Louis Bossart   ALSA: core: group...
330
  			hdelta = curr_jiffies - runtime->hw_ptr_jiffies;
13a988396   Koro Chen   ALSA: pcm: Modify...
331
  			if (hdelta > runtime->hw_ptr_buffer_jiffies/2 + 1) {
bd76af0f8   Jaroslav Kysela   ALSA: pcm midleve...
332
  				hw_base += runtime->buffer_size;
0e8014d77   Pierre-Louis Bossart   ALSA: core: keep ...
333
  				if (hw_base >= runtime->boundary) {
bd76af0f8   Jaroslav Kysela   ALSA: pcm midleve...
334
  					hw_base = 0;
0e8014d77   Pierre-Louis Bossart   ALSA: core: keep ...
335
336
  					crossed_boundary++;
  				}
bd76af0f8   Jaroslav Kysela   ALSA: pcm midleve...
337
338
339
  				new_hw_ptr = hw_base + pos;
  				goto __delta;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
341
  	}
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
342
343
344
345
  	/* new_hw_ptr might be lower than old_hw_ptr in case when */
  	/* pointer crosses the end of the ring buffer */
  	if (new_hw_ptr < old_hw_ptr) {
  		hw_base += runtime->buffer_size;
0e8014d77   Pierre-Louis Bossart   ALSA: core: keep ...
346
  		if (hw_base >= runtime->boundary) {
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
347
  			hw_base = 0;
0e8014d77   Pierre-Louis Bossart   ALSA: core: keep ...
348
349
  			crossed_boundary++;
  		}
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
350
351
352
  		new_hw_ptr = hw_base + pos;
  	}
        __delta:
b406e6103   Clemens Ladisch   ALSA: pcm: fix de...
353
354
355
  	delta = new_hw_ptr - old_hw_ptr;
  	if (delta < 0)
  		delta += runtime->boundary;
ab69a4904   Clemens Ladisch   ALSA: pcm: suppor...
356

59ff878ff   Clemens Ladisch   ALSA: pcm: detect...
357
  	if (runtime->no_period_wakeup) {
12ff414e2   Kelly Anderson   ALSA: pcm: fix in...
358
  		snd_pcm_sframes_t xrun_threshold;
59ff878ff   Clemens Ladisch   ALSA: pcm: detect...
359
360
361
362
  		/*
  		 * Without regular period interrupts, we have to check
  		 * the elapsed time to detect xruns.
  		 */
3509a03f4   Pierre-Louis Bossart   ALSA: core: group...
363
  		jdelta = curr_jiffies - runtime->hw_ptr_jiffies;
47228e48a   Clemens Ladisch   ALSA: pcm: optimi...
364
365
  		if (jdelta < runtime->hw_ptr_buffer_jiffies / 2)
  			goto no_delta_check;
59ff878ff   Clemens Ladisch   ALSA: pcm: detect...
366
  		hdelta = jdelta - delta * HZ / runtime->rate;
12ff414e2   Kelly Anderson   ALSA: pcm: fix in...
367
368
  		xrun_threshold = runtime->hw_ptr_buffer_jiffies / 2 + 1;
  		while (hdelta > xrun_threshold) {
59ff878ff   Clemens Ladisch   ALSA: pcm: detect...
369
370
  			delta += runtime->buffer_size;
  			hw_base += runtime->buffer_size;
0e8014d77   Pierre-Louis Bossart   ALSA: core: keep ...
371
  			if (hw_base >= runtime->boundary) {
59ff878ff   Clemens Ladisch   ALSA: pcm: detect...
372
  				hw_base = 0;
0e8014d77   Pierre-Louis Bossart   ALSA: core: keep ...
373
374
  				crossed_boundary++;
  			}
59ff878ff   Clemens Ladisch   ALSA: pcm: detect...
375
376
377
  			new_hw_ptr = hw_base + pos;
  			hdelta -= runtime->hw_ptr_buffer_jiffies;
  		}
ab69a4904   Clemens Ladisch   ALSA: pcm: suppor...
378
  		goto no_delta_check;
59ff878ff   Clemens Ladisch   ALSA: pcm: detect...
379
  	}
ab69a4904   Clemens Ladisch   ALSA: pcm: suppor...
380

f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
381
  	/* something must be really wrong */
7b3a177b0   Jaroslav Kysela   ALSA: pcm_lib: fi...
382
  	if (delta >= runtime->buffer_size + runtime->period_size) {
f5914908a   Takashi Iwai   ALSA: pcm: Replac...
383
384
385
386
387
  		hw_ptr_error(substream, in_interrupt, "Unexpected hw_ptr",
  			     "(stream=%i, pos=%ld, new_hw_ptr=%ld, old_hw_ptr=%ld)
  ",
  			     substream->stream, (long)pos,
  			     (long)new_hw_ptr, (long)old_hw_ptr);
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
388
389
  		return 0;
  	}
c87d97320   Takashi Iwai   ALSA: Enable PCM ...
390
391
  
  	/* Do jiffies check only in xrun_debug mode */
741b20cfb   Jaroslav Kysela   ALSA: pcm_lib.c -...
392
  	if (!xrun_debug(substream, XRUN_DEBUG_JIFFIESCHECK))
c87d97320   Takashi Iwai   ALSA: Enable PCM ...
393
  		goto no_jiffies_check;
3e5b50165   Takashi Iwai   ALSA: pcm core - ...
394
395
396
397
398
399
  	/* Skip the jiffies check for hardwares with BATCH flag.
  	 * Such hardware usually just increases the position at each IRQ,
  	 * thus it can't give any strange position.
  	 */
  	if (runtime->hw.info & SNDRV_PCM_INFO_BATCH)
  		goto no_jiffies_check;
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
400
  	hdelta = delta;
a4444da31   Jaroslav Kysela   ALSA: PCM midleve...
401
402
403
  	if (hdelta < runtime->delay)
  		goto no_jiffies_check;
  	hdelta -= runtime->delay;
3509a03f4   Pierre-Louis Bossart   ALSA: core: group...
404
  	jdelta = curr_jiffies - runtime->hw_ptr_jiffies;
bbf6ad139   Jaroslav Kysela   [ALSA] pcm-midlev...
405
406
407
408
  	if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) {
  		delta = jdelta /
  			(((runtime->period_size * HZ) / runtime->rate)
  								+ HZ/100);
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
409
410
  		/* move new_hw_ptr according jiffies not pos variable */
  		new_hw_ptr = old_hw_ptr;
ed69c6a8e   Jaroslav Kysela   ALSA: pcm_lib - f...
411
  		hw_base = delta;
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
412
413
414
415
  		/* use loop to avoid checks for delta overflows */
  		/* the delta value is small or zero in most cases */
  		while (delta > 0) {
  			new_hw_ptr += runtime->period_size;
0e8014d77   Pierre-Louis Bossart   ALSA: core: keep ...
416
  			if (new_hw_ptr >= runtime->boundary) {
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
417
  				new_hw_ptr -= runtime->boundary;
0e8014d77   Pierre-Louis Bossart   ALSA: core: keep ...
418
419
  				crossed_boundary--;
  			}
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
420
421
422
  			delta--;
  		}
  		/* align hw_base to buffer_size */
f5914908a   Takashi Iwai   ALSA: pcm: Replac...
423
424
425
  		hw_ptr_error(substream, in_interrupt, "hw_ptr skipping",
  			     "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)
  ",
bbf6ad139   Jaroslav Kysela   [ALSA] pcm-midlev...
426
427
  			     (long)pos, (long)hdelta,
  			     (long)runtime->period_size, jdelta,
ed69c6a8e   Jaroslav Kysela   ALSA: pcm_lib - f...
428
  			     ((hdelta * HZ) / runtime->rate), hw_base,
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
429
430
  			     (unsigned long)old_hw_ptr,
  			     (unsigned long)new_hw_ptr);
ed69c6a8e   Jaroslav Kysela   ALSA: pcm_lib - f...
431
432
433
  		/* reset values to proper state */
  		delta = 0;
  		hw_base = new_hw_ptr - (new_hw_ptr % runtime->buffer_size);
bbf6ad139   Jaroslav Kysela   [ALSA] pcm-midlev...
434
  	}
3e5b50165   Takashi Iwai   ALSA: pcm core - ...
435
   no_jiffies_check:
bbf6ad139   Jaroslav Kysela   [ALSA] pcm-midlev...
436
  	if (delta > runtime->period_size + runtime->period_size / 2) {
f5914908a   Takashi Iwai   ALSA: pcm: Replac...
437
438
439
440
  		hw_ptr_error(substream, in_interrupt,
  			     "Lost interrupts?",
  			     "(stream=%i, delta=%ld, new_hw_ptr=%ld, old_hw_ptr=%ld)
  ",
ed3da3d9a   Takashi Iwai   ALSA: Rewrite hw_...
441
  			     substream->stream, (long)delta,
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
442
443
  			     (long)new_hw_ptr,
  			     (long)old_hw_ptr);
ed3da3d9a   Takashi Iwai   ALSA: Rewrite hw_...
444
  	}
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
445

ab69a4904   Clemens Ladisch   ALSA: pcm: suppor...
446
   no_delta_check:
3179f6200   Pierre-Louis Bossart   ALSA: core: add ....
447
448
  	if (runtime->status->hw_ptr == new_hw_ptr) {
  		update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp);
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
449
  		return 0;
3179f6200   Pierre-Louis Bossart   ALSA: core: add ....
450
  	}
ab1863fc9   Takashi Iwai   ALSA: pcm - Fix u...
451

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
452
453
454
  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
  	    runtime->silence_size > 0)
  		snd_pcm_playback_silence(substream, new_hw_ptr);
e76369257   Jaroslav Kysela   ALSA: pcm_lib - r...
455
  	if (in_interrupt) {
ead4046b2   Clemens Ladisch   ALSA: pcm: fix th...
456
457
458
459
460
461
462
  		delta = new_hw_ptr - runtime->hw_ptr_interrupt;
  		if (delta < 0)
  			delta += runtime->boundary;
  		delta -= (snd_pcm_uframes_t)delta % runtime->period_size;
  		runtime->hw_ptr_interrupt += delta;
  		if (runtime->hw_ptr_interrupt >= runtime->boundary)
  			runtime->hw_ptr_interrupt -= runtime->boundary;
e76369257   Jaroslav Kysela   ALSA: pcm_lib - r...
463
  	}
ed3da3d9a   Takashi Iwai   ALSA: Rewrite hw_...
464
  	runtime->hw_ptr_base = hw_base;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
465
  	runtime->status->hw_ptr = new_hw_ptr;
3509a03f4   Pierre-Louis Bossart   ALSA: core: group...
466
  	runtime->hw_ptr_jiffies = curr_jiffies;
0e8014d77   Pierre-Louis Bossart   ALSA: core: keep ...
467
468
469
470
  	if (crossed_boundary) {
  		snd_BUG_ON(crossed_boundary != 1);
  		runtime->hw_ptr_wrap += runtime->boundary;
  	}
4eeaaeaea   Pierre-Louis Bossart   ALSA: core: add h...
471

3179f6200   Pierre-Louis Bossart   ALSA: core: add ....
472
  	update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp);
4eeaaeaea   Pierre-Louis Bossart   ALSA: core: add h...
473

1250932e4   Jaroslav Kysela   ALSA: pcm_lib - o...
474
  	return snd_pcm_update_state(substream, runtime);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
475
476
477
  }
  
  /* CAUTION: call it with irq disabled */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
478
  int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
479
  {
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
480
  	return snd_pcm_update_hw_ptr0(substream, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
481
482
483
484
485
486
487
488
489
490
  }
  
  /**
   * snd_pcm_set_ops - set the PCM operators
   * @pcm: the pcm instance
   * @direction: stream direction, SNDRV_PCM_STREAM_XXX
   * @ops: the operator table
   *
   * Sets the given PCM operators to the pcm instance.
   */
e6c2e7eb2   Lars-Peter Clausen   ALSA: Constify th...
491
492
  void snd_pcm_set_ops(struct snd_pcm *pcm, int direction,
  		     const struct snd_pcm_ops *ops)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
493
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
494
495
  	struct snd_pcm_str *stream = &pcm->streams[direction];
  	struct snd_pcm_substream *substream;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
496
497
498
499
  	
  	for (substream = stream->substream; substream != NULL; substream = substream->next)
  		substream->ops = ops;
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
500
  EXPORT_SYMBOL(snd_pcm_set_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501
502
503
504
505
506
507
  
  /**
   * snd_pcm_sync - set the PCM sync id
   * @substream: the pcm substream
   *
   * Sets the PCM sync identifier for the card.
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
508
  void snd_pcm_set_sync(struct snd_pcm_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
509
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
510
  	struct snd_pcm_runtime *runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
511
512
513
514
515
516
  	
  	runtime->sync.id32[0] = substream->pcm->card->number;
  	runtime->sync.id32[1] = -1;
  	runtime->sync.id32[2] = -1;
  	runtime->sync.id32[3] = -1;
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
517
  EXPORT_SYMBOL(snd_pcm_set_sync);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
518
519
520
  /*
   *  Standard ioctl routine
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
  static inline unsigned int div32(unsigned int a, unsigned int b, 
  				 unsigned int *r)
  {
  	if (b == 0) {
  		*r = 0;
  		return UINT_MAX;
  	}
  	*r = a % b;
  	return a / b;
  }
  
  static inline unsigned int div_down(unsigned int a, unsigned int b)
  {
  	if (b == 0)
  		return UINT_MAX;
  	return a / b;
  }
  
  static inline unsigned int div_up(unsigned int a, unsigned int b)
  {
  	unsigned int r;
  	unsigned int q;
  	if (b == 0)
  		return UINT_MAX;
  	q = div32(a, b, &r);
  	if (r)
  		++q;
  	return q;
  }
  
  static inline unsigned int mul(unsigned int a, unsigned int b)
  {
  	if (a == 0)
  		return 0;
  	if (div_down(UINT_MAX, a) < b)
  		return UINT_MAX;
  	return a * b;
  }
  
  static inline unsigned int muldiv32(unsigned int a, unsigned int b,
  				    unsigned int c, unsigned int *r)
  {
  	u_int64_t n = (u_int64_t) a * b;
  	if (c == 0) {
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
565
  		snd_BUG_ON(!n);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
566
567
568
  		*r = 0;
  		return UINT_MAX;
  	}
3f7440a6b   Takashi Iwai   ALSA: Clean up 64...
569
  	n = div_u64_rem(n, c, r);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
570
571
572
573
574
575
  	if (n >= UINT_MAX) {
  		*r = 0;
  		return UINT_MAX;
  	}
  	return n;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
576
577
578
579
580
581
582
583
584
  /**
   * snd_interval_refine - refine the interval value of configurator
   * @i: the interval value to refine
   * @v: the interval value to refer to
   *
   * Refines the interval value with the reference value.
   * The interval is changed to the range satisfying both intervals.
   * The interval status (min, max, integer, etc.) are evaluated.
   *
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
585
586
   * Return: Positive if the value is changed, zero if it's not changed, or a
   * negative error code.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
587
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
588
  int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
589
590
  {
  	int changed = 0;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
591
592
  	if (snd_BUG_ON(snd_interval_empty(i)))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
  	if (i->min < v->min) {
  		i->min = v->min;
  		i->openmin = v->openmin;
  		changed = 1;
  	} else if (i->min == v->min && !i->openmin && v->openmin) {
  		i->openmin = 1;
  		changed = 1;
  	}
  	if (i->max > v->max) {
  		i->max = v->max;
  		i->openmax = v->openmax;
  		changed = 1;
  	} else if (i->max == v->max && !i->openmax && v->openmax) {
  		i->openmax = 1;
  		changed = 1;
  	}
  	if (!i->integer && v->integer) {
  		i->integer = 1;
  		changed = 1;
  	}
  	if (i->integer) {
  		if (i->openmin) {
  			i->min++;
  			i->openmin = 0;
  		}
  		if (i->openmax) {
  			i->max--;
  			i->openmax = 0;
  		}
  	} else if (!i->openmin && !i->openmax && i->min == i->max)
  		i->integer = 1;
  	if (snd_interval_checkempty(i)) {
  		snd_interval_none(i);
  		return -EINVAL;
  	}
  	return changed;
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
630
  EXPORT_SYMBOL(snd_interval_refine);
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
631
  static int snd_interval_refine_first(struct snd_interval *i)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
632
  {
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
633
634
  	if (snd_BUG_ON(snd_interval_empty(i)))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
635
636
637
638
639
640
641
642
  	if (snd_interval_single(i))
  		return 0;
  	i->max = i->min;
  	i->openmax = i->openmin;
  	if (i->openmax)
  		i->max++;
  	return 1;
  }
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
643
  static int snd_interval_refine_last(struct snd_interval *i)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
644
  {
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
645
646
  	if (snd_BUG_ON(snd_interval_empty(i)))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
647
648
649
650
651
652
653
654
  	if (snd_interval_single(i))
  		return 0;
  	i->min = i->max;
  	i->openmin = i->openmax;
  	if (i->openmin)
  		i->min--;
  	return 1;
  }
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
655
  void snd_interval_mul(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
  {
  	if (a->empty || b->empty) {
  		snd_interval_none(c);
  		return;
  	}
  	c->empty = 0;
  	c->min = mul(a->min, b->min);
  	c->openmin = (a->openmin || b->openmin);
  	c->max = mul(a->max,  b->max);
  	c->openmax = (a->openmax || b->openmax);
  	c->integer = (a->integer && b->integer);
  }
  
  /**
   * snd_interval_div - refine the interval value with division
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
671
672
673
   * @a: dividend
   * @b: divisor
   * @c: quotient
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
674
675
676
677
678
   *
   * c = a / b
   *
   * Returns non-zero if the value is changed, zero if not changed.
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
679
  void snd_interval_div(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
  {
  	unsigned int r;
  	if (a->empty || b->empty) {
  		snd_interval_none(c);
  		return;
  	}
  	c->empty = 0;
  	c->min = div32(a->min, b->max, &r);
  	c->openmin = (r || a->openmin || b->openmax);
  	if (b->min > 0) {
  		c->max = div32(a->max, b->min, &r);
  		if (r) {
  			c->max++;
  			c->openmax = 1;
  		} else
  			c->openmax = (a->openmax || b->openmin);
  	} else {
  		c->max = UINT_MAX;
  		c->openmax = 0;
  	}
  	c->integer = 0;
  }
  
  /**
   * snd_interval_muldivk - refine the interval value
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
705
706
707
708
709
   * @a: dividend 1
   * @b: dividend 2
   * @k: divisor (as integer)
   * @c: result
    *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
710
711
712
713
   * c = a * b / k
   *
   * Returns non-zero if the value is changed, zero if not changed.
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
714
715
  void snd_interval_muldivk(const struct snd_interval *a, const struct snd_interval *b,
  		      unsigned int k, struct snd_interval *c)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
  {
  	unsigned int r;
  	if (a->empty || b->empty) {
  		snd_interval_none(c);
  		return;
  	}
  	c->empty = 0;
  	c->min = muldiv32(a->min, b->min, k, &r);
  	c->openmin = (r || a->openmin || b->openmin);
  	c->max = muldiv32(a->max, b->max, k, &r);
  	if (r) {
  		c->max++;
  		c->openmax = 1;
  	} else
  		c->openmax = (a->openmax || b->openmax);
  	c->integer = 0;
  }
  
  /**
   * snd_interval_mulkdiv - refine the interval value
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
736
737
738
739
   * @a: dividend 1
   * @k: dividend 2 (as integer)
   * @b: divisor
   * @c: result
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
740
741
742
743
744
   *
   * c = a * k / b
   *
   * Returns non-zero if the value is changed, zero if not changed.
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
745
746
  void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k,
  		      const struct snd_interval *b, struct snd_interval *c)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
  {
  	unsigned int r;
  	if (a->empty || b->empty) {
  		snd_interval_none(c);
  		return;
  	}
  	c->empty = 0;
  	c->min = muldiv32(a->min, k, b->max, &r);
  	c->openmin = (r || a->openmin || b->openmax);
  	if (b->min > 0) {
  		c->max = muldiv32(a->max, k, b->min, &r);
  		if (r) {
  			c->max++;
  			c->openmax = 1;
  		} else
  			c->openmax = (a->openmax || b->openmin);
  	} else {
  		c->max = UINT_MAX;
  		c->openmax = 0;
  	}
  	c->integer = 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
769
770
771
772
773
  /* ---- */
  
  
  /**
   * snd_interval_ratnum - refine the interval value
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
774
775
776
777
778
   * @i: interval to refine
   * @rats_count: number of ratnum_t 
   * @rats: ratnum_t array
   * @nump: pointer to store the resultant numerator
   * @denp: pointer to store the resultant denominator
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
779
   *
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
780
781
   * Return: Positive if the value is changed, zero if it's not changed, or a
   * negative error code.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
782
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
783
784
785
  int snd_interval_ratnum(struct snd_interval *i,
  			unsigned int rats_count, struct snd_ratnum *rats,
  			unsigned int *nump, unsigned int *denp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
786
  {
8374e24c2   Krzysztof Helt   ALSA: refine rate...
787
788
  	unsigned int best_num, best_den;
  	int best_diff;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
789
  	unsigned int k;
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
790
  	struct snd_interval t;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
791
  	int err;
8374e24c2   Krzysztof Helt   ALSA: refine rate...
792
793
  	unsigned int result_num, result_den;
  	int result_diff;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
794
795
796
797
798
799
800
801
802
  
  	best_num = best_den = best_diff = 0;
  	for (k = 0; k < rats_count; ++k) {
  		unsigned int num = rats[k].num;
  		unsigned int den;
  		unsigned int q = i->min;
  		int diff;
  		if (q == 0)
  			q = 1;
40962d7c7   Krzysztof Helt   ALSA: fix incorre...
803
  		den = div_up(num, q);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
804
805
806
807
808
809
810
811
812
813
814
  		if (den < rats[k].den_min)
  			continue;
  		if (den > rats[k].den_max)
  			den = rats[k].den_max;
  		else {
  			unsigned int r;
  			r = (den - rats[k].den_min) % rats[k].den_step;
  			if (r != 0)
  				den -= r;
  		}
  		diff = num - q * den;
8374e24c2   Krzysztof Helt   ALSA: refine rate...
815
816
  		if (diff < 0)
  			diff = -diff;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
817
818
819
820
821
822
823
824
825
826
827
828
829
830
  		if (best_num == 0 ||
  		    diff * best_den < best_diff * den) {
  			best_diff = diff;
  			best_den = den;
  			best_num = num;
  		}
  	}
  	if (best_den == 0) {
  		i->empty = 1;
  		return -EINVAL;
  	}
  	t.min = div_down(best_num, best_den);
  	t.openmin = !!(best_num % best_den);
  	
8374e24c2   Krzysztof Helt   ALSA: refine rate...
831
832
833
  	result_num = best_num;
  	result_diff = best_diff;
  	result_den = best_den;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
834
835
836
837
838
839
840
841
842
843
  	best_num = best_den = best_diff = 0;
  	for (k = 0; k < rats_count; ++k) {
  		unsigned int num = rats[k].num;
  		unsigned int den;
  		unsigned int q = i->max;
  		int diff;
  		if (q == 0) {
  			i->empty = 1;
  			return -EINVAL;
  		}
40962d7c7   Krzysztof Helt   ALSA: fix incorre...
844
  		den = div_down(num, q);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
845
846
847
848
849
850
851
852
853
854
855
  		if (den > rats[k].den_max)
  			continue;
  		if (den < rats[k].den_min)
  			den = rats[k].den_min;
  		else {
  			unsigned int r;
  			r = (den - rats[k].den_min) % rats[k].den_step;
  			if (r != 0)
  				den += rats[k].den_step - r;
  		}
  		diff = q * den - num;
8374e24c2   Krzysztof Helt   ALSA: refine rate...
856
857
  		if (diff < 0)
  			diff = -diff;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
  		if (best_num == 0 ||
  		    diff * best_den < best_diff * den) {
  			best_diff = diff;
  			best_den = den;
  			best_num = num;
  		}
  	}
  	if (best_den == 0) {
  		i->empty = 1;
  		return -EINVAL;
  	}
  	t.max = div_up(best_num, best_den);
  	t.openmax = !!(best_num % best_den);
  	t.integer = 0;
  	err = snd_interval_refine(i, &t);
  	if (err < 0)
  		return err;
  
  	if (snd_interval_single(i)) {
8374e24c2   Krzysztof Helt   ALSA: refine rate...
877
878
879
880
  		if (best_diff * result_den < result_diff * best_den) {
  			result_num = best_num;
  			result_den = best_den;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
881
  		if (nump)
8374e24c2   Krzysztof Helt   ALSA: refine rate...
882
  			*nump = result_num;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
883
  		if (denp)
8374e24c2   Krzysztof Helt   ALSA: refine rate...
884
  			*denp = result_den;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
885
886
887
  	}
  	return err;
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
888
  EXPORT_SYMBOL(snd_interval_ratnum);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
889
890
  /**
   * snd_interval_ratden - refine the interval value
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
891
   * @i: interval to refine
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
892
893
   * @rats_count: number of struct ratden
   * @rats: struct ratden array
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
894
895
   * @nump: pointer to store the resultant numerator
   * @denp: pointer to store the resultant denominator
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
896
   *
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
897
898
   * Return: Positive if the value is changed, zero if it's not changed, or a
   * negative error code.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
899
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
900
901
  static int snd_interval_ratden(struct snd_interval *i,
  			       unsigned int rats_count, struct snd_ratden *rats,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
902
903
904
905
  			       unsigned int *nump, unsigned int *denp)
  {
  	unsigned int best_num, best_diff, best_den;
  	unsigned int k;
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
906
  	struct snd_interval t;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
  	int err;
  
  	best_num = best_den = best_diff = 0;
  	for (k = 0; k < rats_count; ++k) {
  		unsigned int num;
  		unsigned int den = rats[k].den;
  		unsigned int q = i->min;
  		int diff;
  		num = mul(q, den);
  		if (num > rats[k].num_max)
  			continue;
  		if (num < rats[k].num_min)
  			num = rats[k].num_max;
  		else {
  			unsigned int r;
  			r = (num - rats[k].num_min) % rats[k].num_step;
  			if (r != 0)
  				num += rats[k].num_step - r;
  		}
  		diff = num - q * den;
  		if (best_num == 0 ||
  		    diff * best_den < best_diff * den) {
  			best_diff = diff;
  			best_den = den;
  			best_num = num;
  		}
  	}
  	if (best_den == 0) {
  		i->empty = 1;
  		return -EINVAL;
  	}
  	t.min = div_down(best_num, best_den);
  	t.openmin = !!(best_num % best_den);
  	
  	best_num = best_den = best_diff = 0;
  	for (k = 0; k < rats_count; ++k) {
  		unsigned int num;
  		unsigned int den = rats[k].den;
  		unsigned int q = i->max;
  		int diff;
  		num = mul(q, den);
  		if (num < rats[k].num_min)
  			continue;
  		if (num > rats[k].num_max)
  			num = rats[k].num_max;
  		else {
  			unsigned int r;
  			r = (num - rats[k].num_min) % rats[k].num_step;
  			if (r != 0)
  				num -= r;
  		}
  		diff = q * den - num;
  		if (best_num == 0 ||
  		    diff * best_den < best_diff * den) {
  			best_diff = diff;
  			best_den = den;
  			best_num = num;
  		}
  	}
  	if (best_den == 0) {
  		i->empty = 1;
  		return -EINVAL;
  	}
  	t.max = div_up(best_num, best_den);
  	t.openmax = !!(best_num % best_den);
  	t.integer = 0;
  	err = snd_interval_refine(i, &t);
  	if (err < 0)
  		return err;
  
  	if (snd_interval_single(i)) {
  		if (nump)
  			*nump = best_num;
  		if (denp)
  			*denp = best_den;
  	}
  	return err;
  }
  
  /**
   * snd_interval_list - refine the interval value from the list
   * @i: the interval value to refine
   * @count: the number of elements in the list
   * @list: the value list
   * @mask: the bit-mask to evaluate
   *
   * Refines the interval value from the list.
   * When mask is non-zero, only the elements corresponding to bit 1 are
   * evaluated.
   *
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
997
998
   * Return: Positive if the value is changed, zero if it's not changed, or a
   * negative error code.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
999
   */
4af87a939   Mark Brown   ALSA: pcm: Consti...
1000
1001
  int snd_interval_list(struct snd_interval *i, unsigned int count,
  		      const unsigned int *list, unsigned int mask)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1002
1003
  {
          unsigned int k;
b1ddaf681   Clemens Ladisch   sound: pcm_lib: f...
1004
  	struct snd_interval list_range;
0981a260a   Takashi Iwai   [ALSA] Fix possib...
1005
1006
1007
1008
1009
  
  	if (!count) {
  		i->empty = 1;
  		return -EINVAL;
  	}
b1ddaf681   Clemens Ladisch   sound: pcm_lib: f...
1010
1011
1012
  	snd_interval_any(&list_range);
  	list_range.min = UINT_MAX;
  	list_range.max = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1013
1014
1015
          for (k = 0; k < count; k++) {
  		if (mask && !(mask & (1 << k)))
  			continue;
b1ddaf681   Clemens Ladisch   sound: pcm_lib: f...
1016
  		if (!snd_interval_test(i, list[k]))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1017
  			continue;
b1ddaf681   Clemens Ladisch   sound: pcm_lib: f...
1018
1019
  		list_range.min = min(list_range.min, list[k]);
  		list_range.max = max(list_range.max, list[k]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1020
          }
b1ddaf681   Clemens Ladisch   sound: pcm_lib: f...
1021
  	return snd_interval_refine(i, &list_range);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1022
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1023
  EXPORT_SYMBOL(snd_interval_list);
f66f898e9   Peter Rosin   ALSA: pcm: Add sn...
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
  /**
   * snd_interval_ranges - refine the interval value from the list of ranges
   * @i: the interval value to refine
   * @count: the number of elements in the list of ranges
   * @ranges: the ranges list
   * @mask: the bit-mask to evaluate
   *
   * Refines the interval value from the list of ranges.
   * When mask is non-zero, only the elements corresponding to bit 1 are
   * evaluated.
   *
   * Return: Positive if the value is changed, zero if it's not changed, or a
   * negative error code.
   */
  int snd_interval_ranges(struct snd_interval *i, unsigned int count,
  			const struct snd_interval *ranges, unsigned int mask)
  {
  	unsigned int k;
  	struct snd_interval range_union;
  	struct snd_interval range;
  
  	if (!count) {
  		snd_interval_none(i);
  		return -EINVAL;
  	}
  	snd_interval_any(&range_union);
  	range_union.min = UINT_MAX;
  	range_union.max = 0;
  	for (k = 0; k < count; k++) {
  		if (mask && !(mask & (1 << k)))
  			continue;
  		snd_interval_copy(&range, &ranges[k]);
  		if (snd_interval_refine(&range, i) < 0)
  			continue;
  		if (snd_interval_empty(&range))
  			continue;
  
  		if (range.min < range_union.min) {
  			range_union.min = range.min;
  			range_union.openmin = 1;
  		}
  		if (range.min == range_union.min && !range.openmin)
  			range_union.openmin = 0;
  		if (range.max > range_union.max) {
  			range_union.max = range.max;
  			range_union.openmax = 1;
  		}
  		if (range.max == range_union.max && !range.openmax)
  			range_union.openmax = 0;
  	}
  	return snd_interval_refine(i, &range_union);
  }
  EXPORT_SYMBOL(snd_interval_ranges);
0f519b622   Clemens Ladisch   ALSA: pcm: snd_in...
1077
  static int snd_interval_step(struct snd_interval *i, unsigned int step)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1078
1079
1080
  {
  	unsigned int n;
  	int changed = 0;
0f519b622   Clemens Ladisch   ALSA: pcm: snd_in...
1081
  	n = i->min % step;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1082
1083
  	if (n != 0 || i->openmin) {
  		i->min += step - n;
df1e47196   Clemens Ladisch   ALSA: pcm: snd_in...
1084
  		i->openmin = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1085
1086
  		changed = 1;
  	}
0f519b622   Clemens Ladisch   ALSA: pcm: snd_in...
1087
  	n = i->max % step;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1088
1089
  	if (n != 0 || i->openmax) {
  		i->max -= n;
df1e47196   Clemens Ladisch   ALSA: pcm: snd_in...
1090
  		i->openmax = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
  		changed = 1;
  	}
  	if (snd_interval_checkempty(i)) {
  		i->empty = 1;
  		return -EINVAL;
  	}
  	return changed;
  }
  
  /* Info constraints helpers */
  
  /**
   * snd_pcm_hw_rule_add - add the hw-constraint rule
   * @runtime: the pcm runtime instance
   * @cond: condition bits
   * @var: the variable to evaluate
   * @func: the evaluation function
   * @private: the private data pointer passed to function
   * @dep: the dependent variables
   *
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
1111
   * Return: Zero if successful, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1112
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1113
  int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1114
1115
1116
1117
  			int var,
  			snd_pcm_hw_rule_func_t func, void *private,
  			int dep, ...)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1118
1119
  	struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;
  	struct snd_pcm_hw_rule *c;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1120
1121
1122
1123
  	unsigned int k;
  	va_list args;
  	va_start(args, dep);
  	if (constrs->rules_num >= constrs->rules_all) {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1124
  		struct snd_pcm_hw_rule *new;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1125
1126
  		unsigned int new_rules = constrs->rules_all + 16;
  		new = kcalloc(new_rules, sizeof(*c), GFP_KERNEL);
87a1c8aaa   Jesper Juhl   ALSA: pcm: rememb...
1127
1128
  		if (!new) {
  			va_end(args);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1129
  			return -ENOMEM;
87a1c8aaa   Jesper Juhl   ALSA: pcm: rememb...
1130
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
  		if (constrs->rules) {
  			memcpy(new, constrs->rules,
  			       constrs->rules_num * sizeof(*c));
  			kfree(constrs->rules);
  		}
  		constrs->rules = new;
  		constrs->rules_all = new_rules;
  	}
  	c = &constrs->rules[constrs->rules_num];
  	c->cond = cond;
  	c->func = func;
  	c->var = var;
  	c->private = private;
  	k = 0;
  	while (1) {
87a1c8aaa   Jesper Juhl   ALSA: pcm: rememb...
1146
1147
  		if (snd_BUG_ON(k >= ARRAY_SIZE(c->deps))) {
  			va_end(args);
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
1148
  			return -EINVAL;
87a1c8aaa   Jesper Juhl   ALSA: pcm: rememb...
1149
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1150
1151
1152
1153
1154
1155
1156
1157
  		c->deps[k++] = dep;
  		if (dep < 0)
  			break;
  		dep = va_arg(args, int);
  	}
  	constrs->rules_num++;
  	va_end(args);
  	return 0;
87a1c8aaa   Jesper Juhl   ALSA: pcm: rememb...
1158
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1159

e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1160
  EXPORT_SYMBOL(snd_pcm_hw_rule_add);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1161
  /**
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1162
   * snd_pcm_hw_constraint_mask - apply the given bitmap mask constraint
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1163
1164
1165
1166
   * @runtime: PCM runtime instance
   * @var: hw_params variable to apply the mask
   * @mask: the bitmap mask
   *
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1167
   * Apply the constraint of the given bitmap mask to a 32-bit mask parameter.
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
1168
1169
   *
   * Return: Zero if successful, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1170
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1171
  int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1172
1173
  			       u_int32_t mask)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1174
1175
  	struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;
  	struct snd_mask *maskp = constrs_mask(constrs, var);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1176
1177
1178
1179
1180
1181
1182
1183
  	*maskp->bits &= mask;
  	memset(maskp->bits + 1, 0, (SNDRV_MASK_MAX-32) / 8); /* clear rest */
  	if (*maskp->bits == 0)
  		return -EINVAL;
  	return 0;
  }
  
  /**
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1184
   * snd_pcm_hw_constraint_mask64 - apply the given bitmap mask constraint
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1185
1186
1187
1188
   * @runtime: PCM runtime instance
   * @var: hw_params variable to apply the mask
   * @mask: the 64bit bitmap mask
   *
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1189
   * Apply the constraint of the given bitmap mask to a 64-bit mask parameter.
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
1190
1191
   *
   * Return: Zero if successful, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1192
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1193
  int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1194
1195
  				 u_int64_t mask)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1196
1197
  	struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;
  	struct snd_mask *maskp = constrs_mask(constrs, var);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1198
1199
1200
1201
1202
1203
1204
  	maskp->bits[0] &= (u_int32_t)mask;
  	maskp->bits[1] &= (u_int32_t)(mask >> 32);
  	memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */
  	if (! maskp->bits[0] && ! maskp->bits[1])
  		return -EINVAL;
  	return 0;
  }
63a5d4c6a   Mark Brown   ALSA: Export snd_...
1205
  EXPORT_SYMBOL(snd_pcm_hw_constraint_mask64);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1206
1207
  
  /**
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1208
   * snd_pcm_hw_constraint_integer - apply an integer constraint to an interval
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1209
1210
1211
1212
   * @runtime: PCM runtime instance
   * @var: hw_params variable to apply the integer constraint
   *
   * Apply the constraint of integer to an interval parameter.
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
1213
1214
1215
   *
   * Return: Positive if the value is changed, zero if it's not changed, or a
   * negative error code.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1216
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1217
  int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1218
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1219
  	struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1220
1221
  	return snd_interval_setinteger(constrs_interval(constrs, var));
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1222
  EXPORT_SYMBOL(snd_pcm_hw_constraint_integer);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1223
  /**
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1224
   * snd_pcm_hw_constraint_minmax - apply a min/max range constraint to an interval
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1225
1226
1227
1228
1229
1230
   * @runtime: PCM runtime instance
   * @var: hw_params variable to apply the range
   * @min: the minimal value
   * @max: the maximal value
   * 
   * Apply the min/max range constraint to an interval parameter.
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
1231
1232
1233
   *
   * Return: Positive if the value is changed, zero if it's not changed, or a
   * negative error code.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1234
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1235
  int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1236
1237
  				 unsigned int min, unsigned int max)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1238
1239
  	struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;
  	struct snd_interval t;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1240
1241
1242
1243
1244
1245
  	t.min = min;
  	t.max = max;
  	t.openmin = t.openmax = 0;
  	t.integer = 0;
  	return snd_interval_refine(constrs_interval(constrs, var), &t);
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1246
  EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax);
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1247
1248
  static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params,
  				struct snd_pcm_hw_rule *rule)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1249
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1250
  	struct snd_pcm_hw_constraint_list *list = rule->private;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1251
1252
1253
1254
1255
  	return snd_interval_list(hw_param_interval(params, rule->var), list->count, list->list, list->mask);
  }		
  
  
  /**
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1256
   * snd_pcm_hw_constraint_list - apply a list of constraints to a parameter
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1257
1258
1259
1260
1261
1262
   * @runtime: PCM runtime instance
   * @cond: condition bits
   * @var: hw_params variable to apply the list constraint
   * @l: list
   * 
   * Apply the list of constraints to an interval parameter.
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
1263
1264
   *
   * Return: Zero if successful, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1265
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1266
  int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1267
1268
  			       unsigned int cond,
  			       snd_pcm_hw_param_t var,
1464189f8   Mark Brown   ALSA: pcm: Make c...
1269
  			       const struct snd_pcm_hw_constraint_list *l)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1270
1271
  {
  	return snd_pcm_hw_rule_add(runtime, cond, var,
1464189f8   Mark Brown   ALSA: pcm: Make c...
1272
  				   snd_pcm_hw_rule_list, (void *)l,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1273
1274
  				   var, -1);
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1275
  EXPORT_SYMBOL(snd_pcm_hw_constraint_list);
f66f898e9   Peter Rosin   ALSA: pcm: Add sn...
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
  static int snd_pcm_hw_rule_ranges(struct snd_pcm_hw_params *params,
  				  struct snd_pcm_hw_rule *rule)
  {
  	struct snd_pcm_hw_constraint_ranges *r = rule->private;
  	return snd_interval_ranges(hw_param_interval(params, rule->var),
  				   r->count, r->ranges, r->mask);
  }
  
  
  /**
   * snd_pcm_hw_constraint_ranges - apply list of range constraints to a parameter
   * @runtime: PCM runtime instance
   * @cond: condition bits
   * @var: hw_params variable to apply the list of range constraints
   * @r: ranges
   *
   * Apply the list of range constraints to an interval parameter.
   *
   * Return: Zero if successful, or a negative error code on failure.
   */
  int snd_pcm_hw_constraint_ranges(struct snd_pcm_runtime *runtime,
  				 unsigned int cond,
  				 snd_pcm_hw_param_t var,
  				 const struct snd_pcm_hw_constraint_ranges *r)
  {
  	return snd_pcm_hw_rule_add(runtime, cond, var,
  				   snd_pcm_hw_rule_ranges, (void *)r,
  				   var, -1);
  }
  EXPORT_SYMBOL(snd_pcm_hw_constraint_ranges);
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1306
1307
  static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params,
  				   struct snd_pcm_hw_rule *rule)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1308
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1309
  	struct snd_pcm_hw_constraint_ratnums *r = rule->private;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
  	unsigned int num = 0, den = 0;
  	int err;
  	err = snd_interval_ratnum(hw_param_interval(params, rule->var),
  				  r->nrats, r->rats, &num, &den);
  	if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) {
  		params->rate_num = num;
  		params->rate_den = den;
  	}
  	return err;
  }
  
  /**
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1322
   * snd_pcm_hw_constraint_ratnums - apply ratnums constraint to a parameter
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1323
1324
1325
   * @runtime: PCM runtime instance
   * @cond: condition bits
   * @var: hw_params variable to apply the ratnums constraint
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1326
   * @r: struct snd_ratnums constriants
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
1327
1328
   *
   * Return: Zero if successful, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1329
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1330
  int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, 
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1331
1332
  				  unsigned int cond,
  				  snd_pcm_hw_param_t var,
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1333
  				  struct snd_pcm_hw_constraint_ratnums *r)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1334
1335
1336
1337
1338
  {
  	return snd_pcm_hw_rule_add(runtime, cond, var,
  				   snd_pcm_hw_rule_ratnums, r,
  				   var, -1);
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1339
  EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums);
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1340
1341
  static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params,
  				   struct snd_pcm_hw_rule *rule)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1342
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1343
  	struct snd_pcm_hw_constraint_ratdens *r = rule->private;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
  	unsigned int num = 0, den = 0;
  	int err = snd_interval_ratden(hw_param_interval(params, rule->var),
  				  r->nrats, r->rats, &num, &den);
  	if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) {
  		params->rate_num = num;
  		params->rate_den = den;
  	}
  	return err;
  }
  
  /**
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1355
   * snd_pcm_hw_constraint_ratdens - apply ratdens constraint to a parameter
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1356
1357
1358
   * @runtime: PCM runtime instance
   * @cond: condition bits
   * @var: hw_params variable to apply the ratdens constraint
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1359
   * @r: struct snd_ratdens constriants
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
1360
1361
   *
   * Return: Zero if successful, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1362
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1363
  int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime, 
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1364
1365
  				  unsigned int cond,
  				  snd_pcm_hw_param_t var,
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1366
  				  struct snd_pcm_hw_constraint_ratdens *r)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1367
1368
1369
1370
1371
  {
  	return snd_pcm_hw_rule_add(runtime, cond, var,
  				   snd_pcm_hw_rule_ratdens, r,
  				   var, -1);
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1372
  EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens);
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1373
1374
  static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params,
  				  struct snd_pcm_hw_rule *rule)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1375
1376
1377
1378
  {
  	unsigned int l = (unsigned long) rule->private;
  	int width = l & 0xffff;
  	unsigned int msbits = l >> 16;
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1379
  	struct snd_interval *i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
8ef9df55a   Lars-Peter Clausen   ALSA: Add support...
1380
1381
1382
1383
1384
1385
  
  	if (!snd_interval_single(i))
  		return 0;
  
  	if ((snd_interval_value(i) == width) ||
  	    (width == 0 && snd_interval_value(i) > msbits))
19f52fae5   Lars-Peter Clausen   ALSA: Fix handlin...
1386
  		params->msbits = min_not_zero(params->msbits, msbits);
8ef9df55a   Lars-Peter Clausen   ALSA: Add support...
1387

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1388
1389
1390
1391
  	return 0;
  }
  
  /**
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1392
   * snd_pcm_hw_constraint_msbits - add a hw constraint msbits rule
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1393
1394
1395
1396
   * @runtime: PCM runtime instance
   * @cond: condition bits
   * @width: sample bits width
   * @msbits: msbits width
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
1397
   *
8ef9df55a   Lars-Peter Clausen   ALSA: Add support...
1398
1399
1400
1401
1402
   * This constraint will set the number of most significant bits (msbits) if a
   * sample format with the specified width has been select. If width is set to 0
   * the msbits will be set for any sample format with a width larger than the
   * specified msbits.
   *
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
1403
   * Return: Zero if successful, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1404
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1405
  int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime, 
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
  				 unsigned int cond,
  				 unsigned int width,
  				 unsigned int msbits)
  {
  	unsigned long l = (msbits << 16) | width;
  	return snd_pcm_hw_rule_add(runtime, cond, -1,
  				    snd_pcm_hw_rule_msbits,
  				    (void*) l,
  				    SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1416
  EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits);
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1417
1418
  static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params,
  				struct snd_pcm_hw_rule *rule)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1419
1420
  {
  	unsigned long step = (unsigned long) rule->private;
0f519b622   Clemens Ladisch   ALSA: pcm: snd_in...
1421
  	return snd_interval_step(hw_param_interval(params, rule->var), step);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1422
1423
1424
  }
  
  /**
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1425
   * snd_pcm_hw_constraint_step - add a hw constraint step rule
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1426
1427
1428
1429
   * @runtime: PCM runtime instance
   * @cond: condition bits
   * @var: hw_params variable to apply the step constraint
   * @step: step size
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
1430
1431
   *
   * Return: Zero if successful, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1432
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1433
  int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1434
1435
1436
1437
1438
1439
1440
1441
  			       unsigned int cond,
  			       snd_pcm_hw_param_t var,
  			       unsigned long step)
  {
  	return snd_pcm_hw_rule_add(runtime, cond, var, 
  				   snd_pcm_hw_rule_step, (void *) step,
  				   var, -1);
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1442
  EXPORT_SYMBOL(snd_pcm_hw_constraint_step);
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1443
  static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1444
  {
67c393172   Marcin Åšlusarz   [ALSA] pcm_lib: f...
1445
  	static unsigned int pow2_sizes[] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
  		1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7,
  		1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15,
  		1<<16, 1<<17, 1<<18, 1<<19, 1<<20, 1<<21, 1<<22, 1<<23,
  		1<<24, 1<<25, 1<<26, 1<<27, 1<<28, 1<<29, 1<<30
  	};
  	return snd_interval_list(hw_param_interval(params, rule->var),
  				 ARRAY_SIZE(pow2_sizes), pow2_sizes, 0);
  }		
  
  /**
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1456
   * snd_pcm_hw_constraint_pow2 - add a hw constraint power-of-2 rule
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1457
1458
1459
   * @runtime: PCM runtime instance
   * @cond: condition bits
   * @var: hw_params variable to apply the power-of-2 constraint
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
1460
1461
   *
   * Return: Zero if successful, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1462
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1463
  int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1464
1465
1466
1467
1468
1469
1470
  			       unsigned int cond,
  			       snd_pcm_hw_param_t var)
  {
  	return snd_pcm_hw_rule_add(runtime, cond, var, 
  				   snd_pcm_hw_rule_pow2, NULL,
  				   var, -1);
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1471
  EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2);
d5b702a64   Clemens Ladisch   ALSA: pcm: add sn...
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
  static int snd_pcm_hw_rule_noresample_func(struct snd_pcm_hw_params *params,
  					   struct snd_pcm_hw_rule *rule)
  {
  	unsigned int base_rate = (unsigned int)(uintptr_t)rule->private;
  	struct snd_interval *rate;
  
  	rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
  	return snd_interval_list(rate, 1, &base_rate, 0);
  }
  
  /**
   * snd_pcm_hw_rule_noresample - add a rule to allow disabling hw resampling
   * @runtime: PCM runtime instance
   * @base_rate: the rate at which the hardware does not resample
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
1486
1487
   *
   * Return: Zero if successful, or a negative error code on failure.
d5b702a64   Clemens Ladisch   ALSA: pcm: add sn...
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
   */
  int snd_pcm_hw_rule_noresample(struct snd_pcm_runtime *runtime,
  			       unsigned int base_rate)
  {
  	return snd_pcm_hw_rule_add(runtime, SNDRV_PCM_HW_PARAMS_NORESAMPLE,
  				   SNDRV_PCM_HW_PARAM_RATE,
  				   snd_pcm_hw_rule_noresample_func,
  				   (void *)(uintptr_t)base_rate,
  				   SNDRV_PCM_HW_PARAM_RATE, -1);
  }
  EXPORT_SYMBOL(snd_pcm_hw_rule_noresample);
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1499
  static void _snd_pcm_hw_param_any(struct snd_pcm_hw_params *params,
123992f72   Adrian Bunk   [ALSA] sound/core...
1500
  				  snd_pcm_hw_param_t var)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
  {
  	if (hw_is_mask(var)) {
  		snd_mask_any(hw_param_mask(params, var));
  		params->cmask |= 1 << var;
  		params->rmask |= 1 << var;
  		return;
  	}
  	if (hw_is_interval(var)) {
  		snd_interval_any(hw_param_interval(params, var));
  		params->cmask |= 1 << var;
  		params->rmask |= 1 << var;
  		return;
  	}
  	snd_BUG();
  }
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1516
  void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1517
1518
1519
1520
1521
1522
1523
1524
1525
  {
  	unsigned int k;
  	memset(params, 0, sizeof(*params));
  	for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++)
  		_snd_pcm_hw_param_any(params, k);
  	for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++)
  		_snd_pcm_hw_param_any(params, k);
  	params->info = ~0U;
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1526
  EXPORT_SYMBOL(_snd_pcm_hw_params_any);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1527
1528
  
  /**
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1529
   * snd_pcm_hw_param_value - return @params field @var value
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1530
1531
   * @params: the hw_params instance
   * @var: parameter to retrieve
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1532
   * @dir: pointer to the direction (-1,0,1) or %NULL
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1533
   *
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
1534
1535
   * Return: The value for field @var if it's fixed in configuration space
   * defined by @params. -%EINVAL otherwise.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1536
   */
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1537
1538
  int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params,
  			   snd_pcm_hw_param_t var, int *dir)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1539
1540
  {
  	if (hw_is_mask(var)) {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1541
  		const struct snd_mask *mask = hw_param_mask_c(params, var);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1542
1543
1544
1545
1546
1547
1548
  		if (!snd_mask_single(mask))
  			return -EINVAL;
  		if (dir)
  			*dir = 0;
  		return snd_mask_value(mask);
  	}
  	if (hw_is_interval(var)) {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1549
  		const struct snd_interval *i = hw_param_interval_c(params, var);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1550
1551
1552
1553
1554
1555
  		if (!snd_interval_single(i))
  			return -EINVAL;
  		if (dir)
  			*dir = i->openmin;
  		return snd_interval_value(i);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1556
1557
  	return -EINVAL;
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1558
  EXPORT_SYMBOL(snd_pcm_hw_param_value);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1559

877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1560
  void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
  				snd_pcm_hw_param_t var)
  {
  	if (hw_is_mask(var)) {
  		snd_mask_none(hw_param_mask(params, var));
  		params->cmask |= 1 << var;
  		params->rmask |= 1 << var;
  	} else if (hw_is_interval(var)) {
  		snd_interval_none(hw_param_interval(params, var));
  		params->cmask |= 1 << var;
  		params->rmask |= 1 << var;
  	} else {
  		snd_BUG();
  	}
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1575
  EXPORT_SYMBOL(_snd_pcm_hw_param_setempty);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1576

877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1577
  static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params,
123992f72   Adrian Bunk   [ALSA] sound/core...
1578
  				   snd_pcm_hw_param_t var)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1579
1580
1581
1582
1583
1584
  {
  	int changed;
  	if (hw_is_mask(var))
  		changed = snd_mask_refine_first(hw_param_mask(params, var));
  	else if (hw_is_interval(var))
  		changed = snd_interval_refine_first(hw_param_interval(params, var));
2f4ca8e5c   Takashi Iwai   [ALSA] Clean up u...
1585
  	else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1586
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1587
1588
1589
1590
1591
1592
1593
1594
1595
  	if (changed) {
  		params->cmask |= 1 << var;
  		params->rmask |= 1 << var;
  	}
  	return changed;
  }
  
  
  /**
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1596
   * snd_pcm_hw_param_first - refine config space and return minimum value
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1597
1598
1599
   * @pcm: PCM instance
   * @params: the hw_params instance
   * @var: parameter to retrieve
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1600
   * @dir: pointer to the direction (-1,0,1) or %NULL
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1601
   *
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1602
   * Inside configuration space defined by @params remove from @var all
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1603
   * values > minimum. Reduce configuration space accordingly.
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
1604
1605
   *
   * Return: The minimum, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1606
   */
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1607
1608
1609
  int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm, 
  			   struct snd_pcm_hw_params *params, 
  			   snd_pcm_hw_param_t var, int *dir)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1610
1611
1612
1613
1614
1615
  {
  	int changed = _snd_pcm_hw_param_first(params, var);
  	if (changed < 0)
  		return changed;
  	if (params->rmask) {
  		int err = snd_pcm_hw_refine(pcm, params);
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
1616
1617
  		if (snd_BUG_ON(err < 0))
  			return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1618
1619
1620
  	}
  	return snd_pcm_hw_param_value(params, var, dir);
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1621
  EXPORT_SYMBOL(snd_pcm_hw_param_first);
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1622
  static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params,
123992f72   Adrian Bunk   [ALSA] sound/core...
1623
  				  snd_pcm_hw_param_t var)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1624
1625
1626
1627
1628
1629
  {
  	int changed;
  	if (hw_is_mask(var))
  		changed = snd_mask_refine_last(hw_param_mask(params, var));
  	else if (hw_is_interval(var))
  		changed = snd_interval_refine_last(hw_param_interval(params, var));
2f4ca8e5c   Takashi Iwai   [ALSA] Clean up u...
1630
  	else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1631
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1632
1633
1634
1635
1636
1637
1638
1639
1640
  	if (changed) {
  		params->cmask |= 1 << var;
  		params->rmask |= 1 << var;
  	}
  	return changed;
  }
  
  
  /**
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1641
   * snd_pcm_hw_param_last - refine config space and return maximum value
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1642
1643
1644
   * @pcm: PCM instance
   * @params: the hw_params instance
   * @var: parameter to retrieve
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1645
   * @dir: pointer to the direction (-1,0,1) or %NULL
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1646
   *
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1647
   * Inside configuration space defined by @params remove from @var all
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1648
   * values < maximum. Reduce configuration space accordingly.
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
1649
1650
   *
   * Return: The maximum, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1651
   */
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1652
1653
1654
  int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm, 
  			  struct snd_pcm_hw_params *params,
  			  snd_pcm_hw_param_t var, int *dir)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1655
1656
1657
1658
1659
1660
  {
  	int changed = _snd_pcm_hw_param_last(params, var);
  	if (changed < 0)
  		return changed;
  	if (params->rmask) {
  		int err = snd_pcm_hw_refine(pcm, params);
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
1661
1662
  		if (snd_BUG_ON(err < 0))
  			return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1663
1664
1665
  	}
  	return snd_pcm_hw_param_value(params, var, dir);
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1666
  EXPORT_SYMBOL(snd_pcm_hw_param_last);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1667
1668
  
  /**
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1669
   * snd_pcm_hw_param_choose - choose a configuration defined by @params
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1670
1671
   * @pcm: PCM instance
   * @params: the hw_params instance
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1672
   *
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1673
   * Choose one configuration from configuration space defined by @params.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1674
1675
1676
   * The configuration chosen is that obtained fixing in this order:
   * first access, first format, first subformat, min channels,
   * min rate, min period time, max buffer size, min tick time
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
1677
1678
   *
   * Return: Zero if successful, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1679
   */
2f4ca8e5c   Takashi Iwai   [ALSA] Clean up u...
1680
1681
  int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm,
  			     struct snd_pcm_hw_params *params)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1682
  {
2f4ca8e5c   Takashi Iwai   [ALSA] Clean up u...
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
  	static int vars[] = {
  		SNDRV_PCM_HW_PARAM_ACCESS,
  		SNDRV_PCM_HW_PARAM_FORMAT,
  		SNDRV_PCM_HW_PARAM_SUBFORMAT,
  		SNDRV_PCM_HW_PARAM_CHANNELS,
  		SNDRV_PCM_HW_PARAM_RATE,
  		SNDRV_PCM_HW_PARAM_PERIOD_TIME,
  		SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
  		SNDRV_PCM_HW_PARAM_TICK_TIME,
  		-1
  	};
  	int err, *v;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1695

2f4ca8e5c   Takashi Iwai   [ALSA] Clean up u...
1696
1697
1698
1699
1700
  	for (v = vars; *v != -1; v++) {
  		if (*v != SNDRV_PCM_HW_PARAM_BUFFER_SIZE)
  			err = snd_pcm_hw_param_first(pcm, params, *v, NULL);
  		else
  			err = snd_pcm_hw_param_last(pcm, params, *v, NULL);
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
1701
1702
  		if (snd_BUG_ON(err < 0))
  			return err;
2f4ca8e5c   Takashi Iwai   [ALSA] Clean up u...
1703
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1704
1705
  	return 0;
  }
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1706
  static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1707
1708
  				   void *arg)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1709
  	struct snd_pcm_runtime *runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1710
1711
1712
1713
1714
  	unsigned long flags;
  	snd_pcm_stream_lock_irqsave(substream, flags);
  	if (snd_pcm_running(substream) &&
  	    snd_pcm_update_hw_ptr(substream) >= 0)
  		runtime->status->hw_ptr %= runtime->buffer_size;
0e8014d77   Pierre-Louis Bossart   ALSA: core: keep ...
1715
  	else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1716
  		runtime->status->hw_ptr = 0;
0e8014d77   Pierre-Louis Bossart   ALSA: core: keep ...
1717
1718
  		runtime->hw_ptr_wrap = 0;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1719
1720
1721
  	snd_pcm_stream_unlock_irqrestore(substream, flags);
  	return 0;
  }
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1722
  static int snd_pcm_lib_ioctl_channel_info(struct snd_pcm_substream *substream,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1723
1724
  					  void *arg)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1725
1726
  	struct snd_pcm_channel_info *info = arg;
  	struct snd_pcm_runtime *runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
  	int width;
  	if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) {
  		info->offset = -1;
  		return 0;
  	}
  	width = snd_pcm_format_physical_width(runtime->format);
  	if (width < 0)
  		return width;
  	info->offset = 0;
  	switch (runtime->access) {
  	case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED:
  	case SNDRV_PCM_ACCESS_RW_INTERLEAVED:
  		info->first = info->channel * width;
  		info->step = runtime->channels * width;
  		break;
  	case SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED:
  	case SNDRV_PCM_ACCESS_RW_NONINTERLEAVED:
  	{
  		size_t size = runtime->dma_bytes / runtime->channels;
  		info->first = info->channel * size * 8;
  		info->step = width;
  		break;
  	}
  	default:
  		snd_BUG();
  		break;
  	}
  	return 0;
  }
8bea869c5   Jaroslav Kysela   ALSA: PCM midleve...
1756
1757
1758
1759
1760
  static int snd_pcm_lib_ioctl_fifo_size(struct snd_pcm_substream *substream,
  				       void *arg)
  {
  	struct snd_pcm_hw_params *params = arg;
  	snd_pcm_format_t format;
a9960e6a2   Clemens Ladisch   ALSA: pcm: fix fi...
1761
1762
  	int channels;
  	ssize_t frame_size;
8bea869c5   Jaroslav Kysela   ALSA: PCM midleve...
1763
1764
1765
1766
1767
  
  	params->fifo_size = substream->runtime->hw.fifo_size;
  	if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_FIFO_IN_FRAMES)) {
  		format = params_format(params);
  		channels = params_channels(params);
a9960e6a2   Clemens Ladisch   ALSA: pcm: fix fi...
1768
1769
1770
  		frame_size = snd_pcm_format_size(format, channels);
  		if (frame_size > 0)
  			params->fifo_size /= (unsigned)frame_size;
8bea869c5   Jaroslav Kysela   ALSA: PCM midleve...
1771
1772
1773
  	}
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1774
1775
1776
1777
1778
1779
1780
1781
1782
  /**
   * snd_pcm_lib_ioctl - a generic PCM ioctl callback
   * @substream: the pcm substream instance
   * @cmd: ioctl command
   * @arg: ioctl argument
   *
   * Processes the generic ioctl commands for PCM.
   * Can be passed as the ioctl callback for PCM ops.
   *
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
1783
   * Return: Zero if successful, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1784
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1785
  int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1786
1787
1788
1789
1790
1791
1792
1793
1794
  		      unsigned int cmd, void *arg)
  {
  	switch (cmd) {
  	case SNDRV_PCM_IOCTL1_INFO:
  		return 0;
  	case SNDRV_PCM_IOCTL1_RESET:
  		return snd_pcm_lib_ioctl_reset(substream, arg);
  	case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
  		return snd_pcm_lib_ioctl_channel_info(substream, arg);
8bea869c5   Jaroslav Kysela   ALSA: PCM midleve...
1795
1796
  	case SNDRV_PCM_IOCTL1_FIFO_SIZE:
  		return snd_pcm_lib_ioctl_fifo_size(substream, arg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1797
1798
1799
  	}
  	return -ENXIO;
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1800
  EXPORT_SYMBOL(snd_pcm_lib_ioctl);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1801
1802
1803
1804
1805
1806
  /**
   * snd_pcm_period_elapsed - update the pcm status for the next period
   * @substream: the pcm substream instance
   *
   * This function is called from the interrupt handler when the
   * PCM has processed the period size.  It will update the current
31e8960b3   Takashi Iwai   [ALSA] Remove PCM...
1807
   * pointer, wake up sleepers, etc.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1808
1809
1810
1811
   *
   * Even if more than one periods have elapsed since the last call, you
   * have to call this only once.
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1812
  void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1813
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1814
  	struct snd_pcm_runtime *runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1815
  	unsigned long flags;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
1816
1817
  	if (PCM_RUNTIME_CHECK(substream))
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1818
  	runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1819
1820
1821
1822
1823
1824
  
  	if (runtime->transfer_ack_begin)
  		runtime->transfer_ack_begin(substream);
  
  	snd_pcm_stream_lock_irqsave(substream, flags);
  	if (!snd_pcm_running(substream) ||
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
1825
  	    snd_pcm_update_hw_ptr0(substream, 1) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1826
1827
1828
1829
  		goto _end;
  
  	if (substream->timer_running)
  		snd_timer_interrupt(substream->timer, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1830
1831
1832
1833
1834
1835
   _end:
  	snd_pcm_stream_unlock_irqrestore(substream, flags);
  	if (runtime->transfer_ack_end)
  		runtime->transfer_ack_end(substream);
  	kill_fasync(&runtime->fasync, SIGIO, POLL_IN);
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1836
  EXPORT_SYMBOL(snd_pcm_period_elapsed);
130755108   Takashi Iwai   [ALSA] PCM - clea...
1837
1838
1839
1840
1841
1842
  /*
   * Wait until avail_min data becomes available
   * Returns a negative error code if any error occurs during operation.
   * The available space is stored on availp.  When err = 0 and avail = 0
   * on the capture stream, it indicates the stream is in DRAINING state.
   */
5daeba34d   David Dillow   ALSA: pcm_lib: av...
1843
  static int wait_for_avail(struct snd_pcm_substream *substream,
130755108   Takashi Iwai   [ALSA] PCM - clea...
1844
1845
1846
1847
1848
1849
1850
  			      snd_pcm_uframes_t *availp)
  {
  	struct snd_pcm_runtime *runtime = substream->runtime;
  	int is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
  	wait_queue_t wait;
  	int err = 0;
  	snd_pcm_uframes_t avail = 0;
f2b3614ce   Takashi Iwai   ALSA: PCM - Don't...
1851
  	long wait_time, tout;
763437a9e   Arjan van de Ven   ALSA: pcm - fix r...
1852
1853
1854
  	init_waitqueue_entry(&wait, current);
  	set_current_state(TASK_INTERRUPTIBLE);
  	add_wait_queue(&runtime->tsleep, &wait);
f2b3614ce   Takashi Iwai   ALSA: PCM - Don't...
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
  	if (runtime->no_period_wakeup)
  		wait_time = MAX_SCHEDULE_TIMEOUT;
  	else {
  		wait_time = 10;
  		if (runtime->rate) {
  			long t = runtime->period_size * 2 / runtime->rate;
  			wait_time = max(t, wait_time);
  		}
  		wait_time = msecs_to_jiffies(wait_time * 1000);
  	}
763437a9e   Arjan van de Ven   ALSA: pcm - fix r...
1865

130755108   Takashi Iwai   [ALSA] PCM - clea...
1866
1867
1868
1869
1870
  	for (;;) {
  		if (signal_pending(current)) {
  			err = -ERESTARTSYS;
  			break;
  		}
763437a9e   Arjan van de Ven   ALSA: pcm - fix r...
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
  
  		/*
  		 * We need to check if space became available already
  		 * (and thus the wakeup happened already) first to close
  		 * the race of space already having become available.
  		 * This check must happen after been added to the waitqueue
  		 * and having current state be INTERRUPTIBLE.
  		 */
  		if (is_playback)
  			avail = snd_pcm_playback_avail(runtime);
  		else
  			avail = snd_pcm_capture_avail(runtime);
  		if (avail >= runtime->twake)
  			break;
130755108   Takashi Iwai   [ALSA] PCM - clea...
1885
  		snd_pcm_stream_unlock_irq(substream);
763437a9e   Arjan van de Ven   ALSA: pcm - fix r...
1886
1887
  
  		tout = schedule_timeout(wait_time);
130755108   Takashi Iwai   [ALSA] PCM - clea...
1888
  		snd_pcm_stream_lock_irq(substream);
763437a9e   Arjan van de Ven   ALSA: pcm - fix r...
1889
  		set_current_state(TASK_INTERRUPTIBLE);
130755108   Takashi Iwai   [ALSA] PCM - clea...
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
  		switch (runtime->status->state) {
  		case SNDRV_PCM_STATE_SUSPENDED:
  			err = -ESTRPIPE;
  			goto _endloop;
  		case SNDRV_PCM_STATE_XRUN:
  			err = -EPIPE;
  			goto _endloop;
  		case SNDRV_PCM_STATE_DRAINING:
  			if (is_playback)
  				err = -EPIPE;
  			else 
  				avail = 0; /* indicate draining */
  			goto _endloop;
  		case SNDRV_PCM_STATE_OPEN:
  		case SNDRV_PCM_STATE_SETUP:
  		case SNDRV_PCM_STATE_DISCONNECTED:
  			err = -EBADFD;
  			goto _endloop;
ed697e1aa   JongHo Kim   ALSA: Add SNDRV_P...
1908
1909
  		case SNDRV_PCM_STATE_PAUSED:
  			continue;
130755108   Takashi Iwai   [ALSA] PCM - clea...
1910
1911
  		}
  		if (!tout) {
09e56df8b   Takashi Iwai   ALSA: pcm: Use st...
1912
1913
1914
1915
  			pcm_dbg(substream->pcm,
  				"%s write error (DMA or IRQ trouble?)
  ",
  				is_playback ? "playback" : "capture");
130755108   Takashi Iwai   [ALSA] PCM - clea...
1916
1917
1918
  			err = -EIO;
  			break;
  		}
130755108   Takashi Iwai   [ALSA] PCM - clea...
1919
1920
  	}
   _endloop:
763437a9e   Arjan van de Ven   ALSA: pcm - fix r...
1921
  	set_current_state(TASK_RUNNING);
c91a988dc   Jaroslav Kysela   ALSA: pcm_core: F...
1922
  	remove_wait_queue(&runtime->tsleep, &wait);
130755108   Takashi Iwai   [ALSA] PCM - clea...
1923
1924
1925
1926
  	*availp = avail;
  	return err;
  }
  	
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1927
  static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1928
1929
1930
1931
  				      unsigned int hwoff,
  				      unsigned long data, unsigned int off,
  				      snd_pcm_uframes_t frames)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1932
  	struct snd_pcm_runtime *runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1933
1934
1935
1936
1937
1938
1939
  	int err;
  	char __user *buf = (char __user *) data + frames_to_bytes(runtime, off);
  	if (substream->ops->copy) {
  		if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0)
  			return err;
  	} else {
  		char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1940
1941
1942
1943
1944
1945
  		if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames)))
  			return -EFAULT;
  	}
  	return 0;
  }
   
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1946
  typedef int (*transfer_f)(struct snd_pcm_substream *substream, unsigned int hwoff,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1947
1948
  			  unsigned long data, unsigned int off,
  			  snd_pcm_uframes_t size);
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1949
  static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, 
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1950
1951
1952
1953
1954
  					    unsigned long data,
  					    snd_pcm_uframes_t size,
  					    int nonblock,
  					    transfer_f transfer)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1955
  	struct snd_pcm_runtime *runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1956
1957
  	snd_pcm_uframes_t xfer = 0;
  	snd_pcm_uframes_t offset = 0;
0910c216f   Takashi Iwai   ALSA: pcm - Optim...
1958
  	snd_pcm_uframes_t avail;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1959
1960
1961
1962
  	int err = 0;
  
  	if (size == 0)
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
  
  	snd_pcm_stream_lock_irq(substream);
  	switch (runtime->status->state) {
  	case SNDRV_PCM_STATE_PREPARED:
  	case SNDRV_PCM_STATE_RUNNING:
  	case SNDRV_PCM_STATE_PAUSED:
  		break;
  	case SNDRV_PCM_STATE_XRUN:
  		err = -EPIPE;
  		goto _end_unlock;
  	case SNDRV_PCM_STATE_SUSPENDED:
  		err = -ESTRPIPE;
  		goto _end_unlock;
  	default:
  		err = -EBADFD;
  		goto _end_unlock;
  	}
5daeba34d   David Dillow   ALSA: pcm_lib: av...
1980
  	runtime->twake = runtime->control->avail_min ? : 1;
0910c216f   Takashi Iwai   ALSA: pcm - Optim...
1981
1982
1983
  	if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
  		snd_pcm_update_hw_ptr(substream);
  	avail = snd_pcm_playback_avail(runtime);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1984
1985
  	while (size > 0) {
  		snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1986
  		snd_pcm_uframes_t cont;
130755108   Takashi Iwai   [ALSA] PCM - clea...
1987
  		if (!avail) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1988
1989
1990
1991
  			if (nonblock) {
  				err = -EAGAIN;
  				goto _end_unlock;
  			}
5daeba34d   David Dillow   ALSA: pcm_lib: av...
1992
1993
1994
  			runtime->twake = min_t(snd_pcm_uframes_t, size,
  					runtime->control->avail_min ? : 1);
  			err = wait_for_avail(substream, &avail);
130755108   Takashi Iwai   [ALSA] PCM - clea...
1995
  			if (err < 0)
443feb882   Karsten Wiese   [ALSA] ALSA's str...
1996
  				goto _end_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1997
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1998
1999
2000
2001
  		frames = size > avail ? avail : size;
  		cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
  		if (frames > cont)
  			frames = cont;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2002
  		if (snd_BUG_ON(!frames)) {
c91a988dc   Jaroslav Kysela   ALSA: pcm_core: F...
2003
  			runtime->twake = 0;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2004
2005
2006
  			snd_pcm_stream_unlock_irq(substream);
  			return -EINVAL;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2007
2008
2009
  		appl_ptr = runtime->control->appl_ptr;
  		appl_ofs = appl_ptr % runtime->buffer_size;
  		snd_pcm_stream_unlock_irq(substream);
1250932e4   Jaroslav Kysela   ALSA: pcm_lib - o...
2010
  		err = transfer(substream, appl_ofs, data, offset, frames);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2011
  		snd_pcm_stream_lock_irq(substream);
1250932e4   Jaroslav Kysela   ALSA: pcm_lib - o...
2012
2013
  		if (err < 0)
  			goto _end_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
  		switch (runtime->status->state) {
  		case SNDRV_PCM_STATE_XRUN:
  			err = -EPIPE;
  			goto _end_unlock;
  		case SNDRV_PCM_STATE_SUSPENDED:
  			err = -ESTRPIPE;
  			goto _end_unlock;
  		default:
  			break;
  		}
  		appl_ptr += frames;
  		if (appl_ptr >= runtime->boundary)
  			appl_ptr -= runtime->boundary;
  		runtime->control->appl_ptr = appl_ptr;
  		if (substream->ops->ack)
  			substream->ops->ack(substream);
  
  		offset += frames;
  		size -= frames;
  		xfer += frames;
0910c216f   Takashi Iwai   ALSA: pcm - Optim...
2034
  		avail -= frames;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2035
2036
2037
2038
2039
2040
  		if (runtime->status->state == SNDRV_PCM_STATE_PREPARED &&
  		    snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) {
  			err = snd_pcm_start(substream);
  			if (err < 0)
  				goto _end_unlock;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2041
2042
  	}
   _end_unlock:
c91a988dc   Jaroslav Kysela   ALSA: pcm_core: F...
2043
  	runtime->twake = 0;
1250932e4   Jaroslav Kysela   ALSA: pcm_lib - o...
2044
2045
  	if (xfer > 0 && err >= 0)
  		snd_pcm_update_state(substream, runtime);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2046
  	snd_pcm_stream_unlock_irq(substream);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2047
2048
  	return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
  }
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2049
2050
  /* sanity-check for read/write methods */
  static int pcm_sanity_check(struct snd_pcm_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2051
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2052
  	struct snd_pcm_runtime *runtime;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2053
2054
  	if (PCM_RUNTIME_CHECK(substream))
  		return -ENXIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2055
  	runtime = substream->runtime;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2056
2057
  	if (snd_BUG_ON(!substream->ops->copy && !runtime->dma_area))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2058
2059
  	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
  		return -EBADFD;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2060
2061
2062
2063
2064
2065
2066
2067
  	return 0;
  }
  
  snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size)
  {
  	struct snd_pcm_runtime *runtime;
  	int nonblock;
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2068

7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2069
2070
2071
2072
  	err = pcm_sanity_check(substream);
  	if (err < 0)
  		return err;
  	runtime = substream->runtime;
0df63e44c   Takashi Iwai   [ALSA] Add O_APPE...
2073
  	nonblock = !!(substream->f_flags & O_NONBLOCK);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2074
2075
2076
2077
2078
2079
2080
  
  	if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&
  	    runtime->channels > 1)
  		return -EINVAL;
  	return snd_pcm_lib_write1(substream, (unsigned long)buf, size, nonblock,
  				  snd_pcm_lib_write_transfer);
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
2081
  EXPORT_SYMBOL(snd_pcm_lib_write);
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2082
  static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2083
2084
2085
2086
  				       unsigned int hwoff,
  				       unsigned long data, unsigned int off,
  				       snd_pcm_uframes_t frames)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2087
  	struct snd_pcm_runtime *runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2088
2089
2090
2091
2092
  	int err;
  	void __user **bufs = (void __user **)data;
  	int channels = runtime->channels;
  	int c;
  	if (substream->ops->copy) {
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2093
2094
  		if (snd_BUG_ON(!substream->ops->silence))
  			return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
  		for (c = 0; c < channels; ++c, ++bufs) {
  			if (*bufs == NULL) {
  				if ((err = substream->ops->silence(substream, c, hwoff, frames)) < 0)
  					return err;
  			} else {
  				char __user *buf = *bufs + samples_to_bytes(runtime, off);
  				if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0)
  					return err;
  			}
  		}
  	} else {
  		/* default transfer behaviour */
  		size_t dma_csize = runtime->dma_bytes / channels;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
  		for (c = 0; c < channels; ++c, ++bufs) {
  			char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff);
  			if (*bufs == NULL) {
  				snd_pcm_format_set_silence(runtime->format, hwbuf, frames);
  			} else {
  				char __user *buf = *bufs + samples_to_bytes(runtime, off);
  				if (copy_from_user(hwbuf, buf, samples_to_bytes(runtime, frames)))
  					return -EFAULT;
  			}
  		}
  	}
  	return 0;
  }
   
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2122
  snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2123
2124
2125
  				     void __user **bufs,
  				     snd_pcm_uframes_t frames)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2126
  	struct snd_pcm_runtime *runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2127
  	int nonblock;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2128
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2129

7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2130
2131
2132
  	err = pcm_sanity_check(substream);
  	if (err < 0)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2133
  	runtime = substream->runtime;
0df63e44c   Takashi Iwai   [ALSA] Add O_APPE...
2134
  	nonblock = !!(substream->f_flags & O_NONBLOCK);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2135
2136
2137
2138
2139
2140
  
  	if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
  		return -EINVAL;
  	return snd_pcm_lib_write1(substream, (unsigned long)bufs, frames,
  				  nonblock, snd_pcm_lib_writev_transfer);
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
2141
  EXPORT_SYMBOL(snd_pcm_lib_writev);
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2142
  static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream, 
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2143
2144
2145
2146
  				     unsigned int hwoff,
  				     unsigned long data, unsigned int off,
  				     snd_pcm_uframes_t frames)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2147
  	struct snd_pcm_runtime *runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2148
2149
2150
2151
2152
2153
2154
  	int err;
  	char __user *buf = (char __user *) data + frames_to_bytes(runtime, off);
  	if (substream->ops->copy) {
  		if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0)
  			return err;
  	} else {
  		char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2155
2156
2157
2158
2159
  		if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames)))
  			return -EFAULT;
  	}
  	return 0;
  }
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2160
  static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2161
2162
2163
2164
2165
  					   unsigned long data,
  					   snd_pcm_uframes_t size,
  					   int nonblock,
  					   transfer_f transfer)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2166
  	struct snd_pcm_runtime *runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2167
2168
  	snd_pcm_uframes_t xfer = 0;
  	snd_pcm_uframes_t offset = 0;
0910c216f   Takashi Iwai   ALSA: pcm - Optim...
2169
  	snd_pcm_uframes_t avail;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2170
2171
2172
2173
  	int err = 0;
  
  	if (size == 0)
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
  
  	snd_pcm_stream_lock_irq(substream);
  	switch (runtime->status->state) {
  	case SNDRV_PCM_STATE_PREPARED:
  		if (size >= runtime->start_threshold) {
  			err = snd_pcm_start(substream);
  			if (err < 0)
  				goto _end_unlock;
  		}
  		break;
  	case SNDRV_PCM_STATE_DRAINING:
  	case SNDRV_PCM_STATE_RUNNING:
  	case SNDRV_PCM_STATE_PAUSED:
  		break;
  	case SNDRV_PCM_STATE_XRUN:
  		err = -EPIPE;
  		goto _end_unlock;
  	case SNDRV_PCM_STATE_SUSPENDED:
  		err = -ESTRPIPE;
  		goto _end_unlock;
  	default:
  		err = -EBADFD;
  		goto _end_unlock;
  	}
5daeba34d   David Dillow   ALSA: pcm_lib: av...
2198
  	runtime->twake = runtime->control->avail_min ? : 1;
0910c216f   Takashi Iwai   ALSA: pcm - Optim...
2199
2200
2201
  	if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
  		snd_pcm_update_hw_ptr(substream);
  	avail = snd_pcm_capture_avail(runtime);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2202
2203
  	while (size > 0) {
  		snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2204
  		snd_pcm_uframes_t cont;
130755108   Takashi Iwai   [ALSA] PCM - clea...
2205
2206
2207
2208
  		if (!avail) {
  			if (runtime->status->state ==
  			    SNDRV_PCM_STATE_DRAINING) {
  				snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2209
2210
  				goto _end_unlock;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2211
2212
2213
2214
  			if (nonblock) {
  				err = -EAGAIN;
  				goto _end_unlock;
  			}
5daeba34d   David Dillow   ALSA: pcm_lib: av...
2215
2216
2217
  			runtime->twake = min_t(snd_pcm_uframes_t, size,
  					runtime->control->avail_min ? : 1);
  			err = wait_for_avail(substream, &avail);
130755108   Takashi Iwai   [ALSA] PCM - clea...
2218
  			if (err < 0)
443feb882   Karsten Wiese   [ALSA] ALSA's str...
2219
  				goto _end_unlock;
130755108   Takashi Iwai   [ALSA] PCM - clea...
2220
2221
  			if (!avail)
  				continue; /* draining */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2222
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2223
2224
2225
2226
  		frames = size > avail ? avail : size;
  		cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
  		if (frames > cont)
  			frames = cont;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2227
  		if (snd_BUG_ON(!frames)) {
c91a988dc   Jaroslav Kysela   ALSA: pcm_core: F...
2228
  			runtime->twake = 0;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2229
2230
2231
  			snd_pcm_stream_unlock_irq(substream);
  			return -EINVAL;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2232
2233
2234
  		appl_ptr = runtime->control->appl_ptr;
  		appl_ofs = appl_ptr % runtime->buffer_size;
  		snd_pcm_stream_unlock_irq(substream);
1250932e4   Jaroslav Kysela   ALSA: pcm_lib - o...
2235
  		err = transfer(substream, appl_ofs, data, offset, frames);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2236
  		snd_pcm_stream_lock_irq(substream);
1250932e4   Jaroslav Kysela   ALSA: pcm_lib - o...
2237
2238
  		if (err < 0)
  			goto _end_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
  		switch (runtime->status->state) {
  		case SNDRV_PCM_STATE_XRUN:
  			err = -EPIPE;
  			goto _end_unlock;
  		case SNDRV_PCM_STATE_SUSPENDED:
  			err = -ESTRPIPE;
  			goto _end_unlock;
  		default:
  			break;
  		}
  		appl_ptr += frames;
  		if (appl_ptr >= runtime->boundary)
  			appl_ptr -= runtime->boundary;
  		runtime->control->appl_ptr = appl_ptr;
  		if (substream->ops->ack)
  			substream->ops->ack(substream);
  
  		offset += frames;
  		size -= frames;
  		xfer += frames;
0910c216f   Takashi Iwai   ALSA: pcm - Optim...
2259
  		avail -= frames;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2260
2261
  	}
   _end_unlock:
c91a988dc   Jaroslav Kysela   ALSA: pcm_core: F...
2262
  	runtime->twake = 0;
1250932e4   Jaroslav Kysela   ALSA: pcm_lib - o...
2263
2264
  	if (xfer > 0 && err >= 0)
  		snd_pcm_update_state(substream, runtime);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2265
  	snd_pcm_stream_unlock_irq(substream);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2266
2267
  	return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
  }
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2268
  snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __user *buf, snd_pcm_uframes_t size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2269
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2270
  	struct snd_pcm_runtime *runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2271
  	int nonblock;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2272
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2273
  	
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2274
2275
2276
  	err = pcm_sanity_check(substream);
  	if (err < 0)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2277
  	runtime = substream->runtime;
0df63e44c   Takashi Iwai   [ALSA] Add O_APPE...
2278
  	nonblock = !!(substream->f_flags & O_NONBLOCK);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2279
2280
2281
2282
  	if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED)
  		return -EINVAL;
  	return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer);
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
2283
  EXPORT_SYMBOL(snd_pcm_lib_read);
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2284
  static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2285
2286
2287
2288
  				      unsigned int hwoff,
  				      unsigned long data, unsigned int off,
  				      snd_pcm_uframes_t frames)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2289
  	struct snd_pcm_runtime *runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
  	int err;
  	void __user **bufs = (void __user **)data;
  	int channels = runtime->channels;
  	int c;
  	if (substream->ops->copy) {
  		for (c = 0; c < channels; ++c, ++bufs) {
  			char __user *buf;
  			if (*bufs == NULL)
  				continue;
  			buf = *bufs + samples_to_bytes(runtime, off);
  			if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0)
  				return err;
  		}
  	} else {
  		snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
  		for (c = 0; c < channels; ++c, ++bufs) {
  			char *hwbuf;
  			char __user *buf;
  			if (*bufs == NULL)
  				continue;
  
  			hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff);
  			buf = *bufs + samples_to_bytes(runtime, off);
  			if (copy_to_user(buf, hwbuf, samples_to_bytes(runtime, frames)))
  				return -EFAULT;
  		}
  	}
  	return 0;
  }
   
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2320
  snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2321
2322
2323
  				    void __user **bufs,
  				    snd_pcm_uframes_t frames)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2324
  	struct snd_pcm_runtime *runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2325
  	int nonblock;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2326
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2327

7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2328
2329
2330
  	err = pcm_sanity_check(substream);
  	if (err < 0)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2331
  	runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2332
2333
  	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
  		return -EBADFD;
0df63e44c   Takashi Iwai   [ALSA] Add O_APPE...
2334
  	nonblock = !!(substream->f_flags & O_NONBLOCK);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2335
2336
2337
2338
  	if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
  		return -EINVAL;
  	return snd_pcm_lib_read1(substream, (unsigned long)bufs, frames, nonblock, snd_pcm_lib_readv_transfer);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2339
  EXPORT_SYMBOL(snd_pcm_lib_readv);
2d3391ec0   Takashi Iwai   ALSA: PCM: channe...
2340
2341
2342
2343
2344
2345
2346
2347
  
  /*
   * standard channel mapping helpers
   */
  
  /* default channel maps for multi-channel playbacks, up to 8 channels */
  const struct snd_pcm_chmap_elem snd_pcm_std_chmaps[] = {
  	{ .channels = 1,
5efbc2610   Takashi Iwai   ALSA: Fix leftove...
2348
  	  .map = { SNDRV_CHMAP_MONO } },
2d3391ec0   Takashi Iwai   ALSA: PCM: channe...
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
  	{ .channels = 2,
  	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
  	{ .channels = 4,
  	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
  		   SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
  	{ .channels = 6,
  	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
  		   SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
  		   SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } },
  	{ .channels = 8,
  	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
  		   SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
  		   SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE,
  		   SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } },
  	{ }
  };
  EXPORT_SYMBOL_GPL(snd_pcm_std_chmaps);
  
  /* alternative channel maps with CLFE <-> surround swapped for 6/8 channels */
  const struct snd_pcm_chmap_elem snd_pcm_alt_chmaps[] = {
  	{ .channels = 1,
5efbc2610   Takashi Iwai   ALSA: Fix leftove...
2370
  	  .map = { SNDRV_CHMAP_MONO } },
2d3391ec0   Takashi Iwai   ALSA: PCM: channe...
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
  	{ .channels = 2,
  	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
  	{ .channels = 4,
  	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
  		   SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
  	{ .channels = 6,
  	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
  		   SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE,
  		   SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
  	{ .channels = 8,
  	  .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
  		   SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE,
  		   SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
  		   SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } },
  	{ }
  };
  EXPORT_SYMBOL_GPL(snd_pcm_alt_chmaps);
  
  static bool valid_chmap_channels(const struct snd_pcm_chmap *info, int ch)
  {
  	if (ch > info->max_channels)
  		return false;
  	return !info->channel_mask || (info->channel_mask & (1U << ch));
  }
  
  static int pcm_chmap_ctl_info(struct snd_kcontrol *kcontrol,
  			      struct snd_ctl_elem_info *uinfo)
  {
  	struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
  
  	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
  	uinfo->count = 0;
  	uinfo->count = info->max_channels;
  	uinfo->value.integer.min = 0;
  	uinfo->value.integer.max = SNDRV_CHMAP_LAST;
  	return 0;
  }
  
  /* get callback for channel map ctl element
   * stores the channel position firstly matching with the current channels
   */
  static int pcm_chmap_ctl_get(struct snd_kcontrol *kcontrol,
  			     struct snd_ctl_elem_value *ucontrol)
  {
  	struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
  	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
  	struct snd_pcm_substream *substream;
  	const struct snd_pcm_chmap_elem *map;
  
  	if (snd_BUG_ON(!info->chmap))
  		return -EINVAL;
  	substream = snd_pcm_chmap_substream(info, idx);
  	if (!substream)
  		return -ENODEV;
  	memset(ucontrol->value.integer.value, 0,
  	       sizeof(ucontrol->value.integer.value));
  	if (!substream->runtime)
  		return 0; /* no channels set */
  	for (map = info->chmap; map->channels; map++) {
  		int i;
  		if (map->channels == substream->runtime->channels &&
  		    valid_chmap_channels(info, map->channels)) {
  			for (i = 0; i < map->channels; i++)
  				ucontrol->value.integer.value[i] = map->map[i];
  			return 0;
  		}
  	}
  	return -EINVAL;
  }
  
  /* tlv callback for channel map ctl element
   * expands the pre-defined channel maps in a form of TLV
   */
  static int pcm_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
  			     unsigned int size, unsigned int __user *tlv)
  {
  	struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
  	const struct snd_pcm_chmap_elem *map;
  	unsigned int __user *dst;
  	int c, count = 0;
  
  	if (snd_BUG_ON(!info->chmap))
  		return -EINVAL;
  	if (size < 8)
  		return -ENOMEM;
  	if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))
  		return -EFAULT;
  	size -= 8;
  	dst = tlv + 2;
  	for (map = info->chmap; map->channels; map++) {
  		int chs_bytes = map->channels * 4;
  		if (!valid_chmap_channels(info, map->channels))
  			continue;
  		if (size < 8)
  			return -ENOMEM;
  		if (put_user(SNDRV_CTL_TLVT_CHMAP_FIXED, dst) ||
  		    put_user(chs_bytes, dst + 1))
  			return -EFAULT;
  		dst += 2;
  		size -= 8;
  		count += 8;
  		if (size < chs_bytes)
  			return -ENOMEM;
  		size -= chs_bytes;
  		count += chs_bytes;
  		for (c = 0; c < map->channels; c++) {
  			if (put_user(map->map[c], dst))
  				return -EFAULT;
  			dst++;
  		}
  	}
  	if (put_user(count, tlv + 1))
  		return -EFAULT;
  	return 0;
  }
  
  static void pcm_chmap_ctl_private_free(struct snd_kcontrol *kcontrol)
  {
  	struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
  	info->pcm->streams[info->stream].chmap_kctl = NULL;
  	kfree(info);
  }
  
  /**
   * snd_pcm_add_chmap_ctls - create channel-mapping control elements
   * @pcm: the assigned PCM instance
   * @stream: stream direction
   * @chmap: channel map elements (for query)
   * @max_channels: the max number of channels for the stream
   * @private_value: the value passed to each kcontrol's private_value field
   * @info_ret: store struct snd_pcm_chmap instance if non-NULL
   *
   * Create channel-mapping control elements assigned to the given PCM stream(s).
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
2504
   * Return: Zero if successful, or a negative error value.
2d3391ec0   Takashi Iwai   ALSA: PCM: channe...
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
   */
  int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream,
  			   const struct snd_pcm_chmap_elem *chmap,
  			   int max_channels,
  			   unsigned long private_value,
  			   struct snd_pcm_chmap **info_ret)
  {
  	struct snd_pcm_chmap *info;
  	struct snd_kcontrol_new knew = {
  		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
  		.access = SNDRV_CTL_ELEM_ACCESS_READ |
2d3391ec0   Takashi Iwai   ALSA: PCM: channe...
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
  			SNDRV_CTL_ELEM_ACCESS_TLV_READ |
  			SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
  		.info = pcm_chmap_ctl_info,
  		.get = pcm_chmap_ctl_get,
  		.tlv.c = pcm_chmap_ctl_tlv,
  	};
  	int err;
  
  	info = kzalloc(sizeof(*info), GFP_KERNEL);
  	if (!info)
  		return -ENOMEM;
  	info->pcm = pcm;
  	info->stream = stream;
  	info->chmap = chmap;
  	info->max_channels = max_channels;
  	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
  		knew.name = "Playback Channel Map";
  	else
  		knew.name = "Capture Channel Map";
  	knew.device = pcm->device;
  	knew.count = pcm->streams[stream].substream_count;
  	knew.private_value = private_value;
  	info->kctl = snd_ctl_new1(&knew, info);
  	if (!info->kctl) {
  		kfree(info);
  		return -ENOMEM;
  	}
  	info->kctl->private_free = pcm_chmap_ctl_private_free;
  	err = snd_ctl_add(pcm->card, info->kctl);
  	if (err < 0)
  		return err;
  	pcm->streams[stream].chmap_kctl = info->kctl;
  	if (info_ret)
  		*info_ret = info;
  	return 0;
  }
  EXPORT_SYMBOL_GPL(snd_pcm_add_chmap_ctls);