Blame view

sound/core/pcm_lib.c 70.1 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
33
34
35
36
37
38
39
40
41
42
  #include <sound/info.h>
  #include <sound/pcm.h>
  #include <sound/pcm_params.h>
  #include <sound/timer.h>
  
  /*
   * 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...
43
  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
44
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
45
  	struct snd_pcm_runtime *runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46
47
48
49
50
51
52
53
54
55
56
57
58
59
  	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 ...
60
  		if (runtime->silence_filled >= runtime->buffer_size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
61
  			return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
63
64
65
66
67
68
69
70
  		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 - ...
71
72
  			if (avail > runtime->buffer_size)
  				avail = runtime->buffer_size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
74
75
76
77
78
79
80
81
82
83
84
  			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: ...
85
  				runtime->silence_start = new_hw_ptr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
  			} else {
9a826ddba   Clemens Ladisch   [ALSA] pcm core: ...
87
  				runtime->silence_start = ofs;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
90
91
  		}
  		frames = runtime->buffer_size - runtime->silence_filled;
  	}
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
92
93
  	if (snd_BUG_ON(frames > runtime->buffer_size))
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
95
  	if (frames == 0)
  		return;
9a826ddba   Clemens Ladisch   [ALSA] pcm core: ...
96
  	ofs = runtime->silence_start % runtime->buffer_size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97
98
99
100
101
102
103
  	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...
104
  				snd_BUG_ON(err < 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
105
106
107
108
109
110
111
112
113
114
115
  			} 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...
116
  					snd_BUG_ON(err < 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117
118
119
120
121
122
123
124
125
126
127
128
129
130
  				}
  			} 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...
131
132
  #ifdef CONFIG_SND_DEBUG
  void snd_pcm_debug_name(struct snd_pcm_substream *substream,
c00701101   Takashi Iwai   ALSA: pcm - A hel...
133
134
135
136
137
138
139
140
  			   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...
141
142
  EXPORT_SYMBOL(snd_pcm_debug_name);
  #endif
c00701101   Takashi Iwai   ALSA: pcm - A hel...
143

741b20cfb   Jaroslav Kysela   ALSA: pcm_lib.c -...
144
145
146
147
148
  #define XRUN_DEBUG_BASIC	(1<<0)
  #define XRUN_DEBUG_STACK	(1<<1)	/* dump also stack */
  #define XRUN_DEBUG_JIFFIESCHECK	(1<<2)	/* do jiffies check */
  #define XRUN_DEBUG_PERIODUPDATE	(1<<3)	/* full period update info */
  #define XRUN_DEBUG_HWPTRUPDATE	(1<<4)	/* full hwptr update info */
4d96eb255   Jaroslav Kysela   ALSA: pcm_lib - a...
149
150
  #define XRUN_DEBUG_LOG		(1<<5)	/* show last 10 positions on err */
  #define XRUN_DEBUG_LOGONCE	(1<<6)	/* do above only once */
741b20cfb   Jaroslav Kysela   ALSA: pcm_lib.c -...
151

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

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

741b20cfb   Jaroslav Kysela   ALSA: pcm_lib.c -...
160
161
162
  #define dump_stack_on_xrun(substream) do {			\
  		if (xrun_debug(substream, XRUN_DEBUG_STACK))	\
  			dump_stack();				\
ed3da3d9a   Takashi Iwai   ALSA: Rewrite hw_...
163
  	} while (0)
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
164
  static void xrun(struct snd_pcm_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
  {
13f040f9e   Jaroslav Kysela   ALSA: PCM midleve...
166
167
168
169
  	struct snd_pcm_runtime *runtime = substream->runtime;
  
  	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
170
  	snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
741b20cfb   Jaroslav Kysela   ALSA: pcm_lib.c -...
171
  	if (xrun_debug(substream, XRUN_DEBUG_BASIC)) {
c00701101   Takashi Iwai   ALSA: pcm - A hel...
172
  		char name[16];
acb03d440   Eliot Blennerhassett   ALSA: Make snd_pc...
173
  		snd_pcm_debug_name(substream, name, sizeof(name));
09e56df8b   Takashi Iwai   ALSA: pcm: Use st...
174
175
  		pcm_warn(substream->pcm, "XRUN: %s
  ", name);
ed3da3d9a   Takashi Iwai   ALSA: Rewrite hw_...
176
  		dump_stack_on_xrun(substream);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178
  }
0f17014b3   Jarkko Nikula   ALSA: pcm_lib - f...
179
  #ifdef CONFIG_SND_PCM_XRUN_DEBUG
4d96eb255   Jaroslav Kysela   ALSA: pcm_lib - a...
180
181
182
  #define hw_ptr_error(substream, fmt, args...)				\
  	do {								\
  		if (xrun_debug(substream, XRUN_DEBUG_BASIC)) {		\
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
183
  			xrun_log_show(substream);			\
09e56df8b   Takashi Iwai   ALSA: pcm: Use st...
184
  			pr_err_ratelimited("ALSA: PCM: " fmt, ##args);	\
4d96eb255   Jaroslav Kysela   ALSA: pcm_lib - a...
185
186
187
188
189
190
191
  			dump_stack_on_xrun(substream);			\
  		}							\
  	} while (0)
  
  #define XRUN_LOG_CNT	10
  
  struct hwptr_log_entry {
ec08b1448   Ben Gardiner   ALSA: sound, core...
192
  	unsigned int in_interrupt;
4d96eb255   Jaroslav Kysela   ALSA: pcm_lib - a...
193
  	unsigned long jiffies;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
  	snd_pcm_uframes_t pos;
4d96eb255   Jaroslav Kysela   ALSA: pcm_lib - a...
195
196
197
198
  	snd_pcm_uframes_t period_size;
  	snd_pcm_uframes_t buffer_size;
  	snd_pcm_uframes_t old_hw_ptr;
  	snd_pcm_uframes_t hw_ptr_base;
4d96eb255   Jaroslav Kysela   ALSA: pcm_lib - a...
199
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200

4d96eb255   Jaroslav Kysela   ALSA: pcm_lib - a...
201
202
203
204
205
206
207
  struct snd_pcm_hwptr_log {
  	unsigned int idx;
  	unsigned int hit: 1;
  	struct hwptr_log_entry entries[XRUN_LOG_CNT];
  };
  
  static void xrun_log(struct snd_pcm_substream *substream,
ec08b1448   Ben Gardiner   ALSA: sound, core...
208
  		     snd_pcm_uframes_t pos, int in_interrupt)
4d96eb255   Jaroslav Kysela   ALSA: pcm_lib - a...
209
210
211
212
213
214
215
216
217
218
219
220
221
  {
  	struct snd_pcm_runtime *runtime = substream->runtime;
  	struct snd_pcm_hwptr_log *log = runtime->hwptr_log;
  	struct hwptr_log_entry *entry;
  
  	if (log == NULL) {
  		log = kzalloc(sizeof(*log), GFP_ATOMIC);
  		if (log == NULL)
  			return;
  		runtime->hwptr_log = log;
  	} else {
  		if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit)
  			return;
7c22f1aaa   Takashi Iwai   [ALSA] Remove snd...
222
  	}
4d96eb255   Jaroslav Kysela   ALSA: pcm_lib - a...
223
  	entry = &log->entries[log->idx];
ec08b1448   Ben Gardiner   ALSA: sound, core...
224
  	entry->in_interrupt = in_interrupt;
4d96eb255   Jaroslav Kysela   ALSA: pcm_lib - a...
225
226
227
  	entry->jiffies = jiffies;
  	entry->pos = pos;
  	entry->period_size = runtime->period_size;
c80c1d542   Joe Perches   ALSA: sound/core/...
228
  	entry->buffer_size = runtime->buffer_size;
4d96eb255   Jaroslav Kysela   ALSA: pcm_lib - a...
229
230
  	entry->old_hw_ptr = runtime->status->hw_ptr;
  	entry->hw_ptr_base = runtime->hw_ptr_base;
4d96eb255   Jaroslav Kysela   ALSA: pcm_lib - a...
231
  	log->idx = (log->idx + 1) % XRUN_LOG_CNT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
  }
4d96eb255   Jaroslav Kysela   ALSA: pcm_lib - a...
233
234
235
236
237
238
239
240
241
242
243
244
  static void xrun_log_show(struct snd_pcm_substream *substream)
  {
  	struct snd_pcm_hwptr_log *log = substream->runtime->hwptr_log;
  	struct hwptr_log_entry *entry;
  	char name[16];
  	unsigned int idx;
  	int cnt;
  
  	if (log == NULL)
  		return;
  	if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit)
  		return;
acb03d440   Eliot Blennerhassett   ALSA: Make snd_pc...
245
  	snd_pcm_debug_name(substream, name, sizeof(name));
4d96eb255   Jaroslav Kysela   ALSA: pcm_lib - a...
246
247
248
249
  	for (cnt = 0, idx = log->idx; cnt < XRUN_LOG_CNT; cnt++) {
  		entry = &log->entries[idx];
  		if (entry->period_size == 0)
  			break;
09e56df8b   Takashi Iwai   ALSA: pcm: Use st...
250
  		pr_info("hwptr log: %s: %sj=%lu, pos=%ld/%ld/%ld, "
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
251
252
  			   "hwptr=%ld/%ld
  ",
ec08b1448   Ben Gardiner   ALSA: sound, core...
253
254
255
  			   name, entry->in_interrupt ? "[Q] " : "",
  			   entry->jiffies,
  			   (unsigned long)entry->pos,
4d96eb255   Jaroslav Kysela   ALSA: pcm_lib - a...
256
257
258
  			   (unsigned long)entry->period_size,
  			   (unsigned long)entry->buffer_size,
  			   (unsigned long)entry->old_hw_ptr,
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
259
  			   (unsigned long)entry->hw_ptr_base);
4d96eb255   Jaroslav Kysela   ALSA: pcm_lib - a...
260
261
262
263
264
265
266
  		idx++;
  		idx %= XRUN_LOG_CNT;
  	}
  	log->hit = 1;
  }
  
  #else /* ! CONFIG_SND_PCM_XRUN_DEBUG */
4d96eb255   Jaroslav Kysela   ALSA: pcm_lib - a...
267
  #define hw_ptr_error(substream, fmt, args...) do { } while (0)
217658f46   Ben Gardiner   ALSA: sound, core...
268
  #define xrun_log(substream, pos, in_interrupt)	do { } while (0)
4d96eb255   Jaroslav Kysela   ALSA: pcm_lib - a...
269
270
271
  #define xrun_log_show(substream)	do { } while (0)
  
  #endif
1250932e4   Jaroslav Kysela   ALSA: pcm_lib - o...
272
273
  int snd_pcm_update_state(struct snd_pcm_substream *substream,
  			 struct snd_pcm_runtime *runtime)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
274
275
276
277
278
279
280
281
282
  {
  	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...
283
284
  	if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
  		if (avail >= runtime->buffer_size) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
285
  			snd_pcm_drain_done(substream);
4cdc115fd   Takashi Iwai   ALSA: pcm - Fix d...
286
287
288
289
  			return -EPIPE;
  		}
  	} else {
  		if (avail >= runtime->stop_threshold) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290
  			xrun(substream);
4cdc115fd   Takashi Iwai   ALSA: pcm - Fix d...
291
292
  			return -EPIPE;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293
  	}
5daeba34d   David Dillow   ALSA: pcm_lib: av...
294
295
296
297
298
  	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
299
300
  	return 0;
  }
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
301
302
  static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
  				  unsigned int in_interrupt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
303
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
304
  	struct snd_pcm_runtime *runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
305
  	snd_pcm_uframes_t pos;
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
306
  	snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base;
bbf6ad139   Jaroslav Kysela   [ALSA] pcm-midlev...
307
308
  	snd_pcm_sframes_t hdelta, delta;
  	unsigned long jdelta;
3509a03f4   Pierre-Louis Bossart   ALSA: core: group...
309
310
  	unsigned long curr_jiffies;
  	struct timespec curr_tstamp;
4eeaaeaea   Pierre-Louis Bossart   ALSA: core: add h...
311
  	struct timespec audio_tstamp;
0e8014d77   Pierre-Louis Bossart   ALSA: core: keep ...
312
  	int crossed_boundary = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313

bbf6ad139   Jaroslav Kysela   [ALSA] pcm-midlev...
314
  	old_hw_ptr = runtime->status->hw_ptr;
3509a03f4   Pierre-Louis Bossart   ALSA: core: group...
315
316
317
318
319
320
321
  
  	/*
  	 * 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...
322
  	pos = substream->ops->pointer(substream);
3509a03f4   Pierre-Louis Bossart   ALSA: core: group...
323
  	curr_jiffies = jiffies;
4eeaaeaea   Pierre-Louis Bossart   ALSA: core: add h...
324
  	if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
3509a03f4   Pierre-Louis Bossart   ALSA: core: group...
325
  		snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp);
4eeaaeaea   Pierre-Louis Bossart   ALSA: core: add h...
326
327
328
329
  		if ((runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK) &&
  			(substream->ops->wall_clock))
  			substream->ops->wall_clock(substream, &audio_tstamp);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
331
332
333
  	if (pos == SNDRV_PCM_POS_XRUN) {
  		xrun(substream);
  		return -EPIPE;
  	}
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
334
  	if (pos >= runtime->buffer_size) {
09e56df8b   Takashi Iwai   ALSA: pcm: Use st...
335
  		if (printk_ratelimit()) {
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
336
  			char name[16];
acb03d440   Eliot Blennerhassett   ALSA: Make snd_pc...
337
  			snd_pcm_debug_name(substream, name, sizeof(name));
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
338
  			xrun_log_show(substream);
09e56df8b   Takashi Iwai   ALSA: pcm: Use st...
339
340
341
342
343
  			pcm_err(substream->pcm,
  				"BUG: %s, pos = %ld, buffer size = %ld, period size = %ld
  ",
  				name, pos, runtime->buffer_size,
  				runtime->period_size);
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
344
345
  		}
  		pos = 0;
cedb8118e   Takashi Iwai   ALSA: pcm - Add l...
346
  	}
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
347
348
  	pos -= pos % runtime->min_align;
  	if (xrun_debug(substream, XRUN_DEBUG_LOG))
ec08b1448   Ben Gardiner   ALSA: sound, core...
349
  		xrun_log(substream, pos, in_interrupt);
ed3da3d9a   Takashi Iwai   ALSA: Rewrite hw_...
350
351
  	hw_base = runtime->hw_ptr_base;
  	new_hw_ptr = hw_base + pos;
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
352
353
354
  	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...
355
  		delta = runtime->hw_ptr_interrupt + runtime->period_size;
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
356
  		if (delta > new_hw_ptr) {
bd76af0f8   Jaroslav Kysela   ALSA: pcm midleve...
357
  			/* check for double acknowledged interrupts */
3509a03f4   Pierre-Louis Bossart   ALSA: core: group...
358
  			hdelta = curr_jiffies - runtime->hw_ptr_jiffies;
bd76af0f8   Jaroslav Kysela   ALSA: pcm midleve...
359
360
  			if (hdelta > runtime->hw_ptr_buffer_jiffies/2) {
  				hw_base += runtime->buffer_size;
0e8014d77   Pierre-Louis Bossart   ALSA: core: keep ...
361
  				if (hw_base >= runtime->boundary) {
bd76af0f8   Jaroslav Kysela   ALSA: pcm midleve...
362
  					hw_base = 0;
0e8014d77   Pierre-Louis Bossart   ALSA: core: keep ...
363
364
  					crossed_boundary++;
  				}
bd76af0f8   Jaroslav Kysela   ALSA: pcm midleve...
365
366
367
  				new_hw_ptr = hw_base + pos;
  				goto __delta;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
369
  	}
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
370
371
372
373
  	/* 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 ...
374
  		if (hw_base >= runtime->boundary) {
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
375
  			hw_base = 0;
0e8014d77   Pierre-Louis Bossart   ALSA: core: keep ...
376
377
  			crossed_boundary++;
  		}
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
378
379
380
  		new_hw_ptr = hw_base + pos;
  	}
        __delta:
b406e6103   Clemens Ladisch   ALSA: pcm: fix de...
381
382
383
  	delta = new_hw_ptr - old_hw_ptr;
  	if (delta < 0)
  		delta += runtime->boundary;
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
384
385
386
  	if (xrun_debug(substream, in_interrupt ?
  			XRUN_DEBUG_PERIODUPDATE : XRUN_DEBUG_HWPTRUPDATE)) {
  		char name[16];
acb03d440   Eliot Blennerhassett   ALSA: Make snd_pc...
387
  		snd_pcm_debug_name(substream, name, sizeof(name));
09e56df8b   Takashi Iwai   ALSA: pcm: Use st...
388
389
390
  		pcm_dbg(substream->pcm,
  			"%s_update: %s: pos=%u/%u/%u, hwptr=%ld/%ld/%ld/%ld
  ",
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
391
392
393
394
395
396
397
398
399
400
  			   in_interrupt ? "period" : "hwptr",
  			   name,
  			   (unsigned int)pos,
  			   (unsigned int)runtime->period_size,
  			   (unsigned int)runtime->buffer_size,
  			   (unsigned long)delta,
  			   (unsigned long)old_hw_ptr,
  			   (unsigned long)new_hw_ptr,
  			   (unsigned long)runtime->hw_ptr_base);
  	}
ab69a4904   Clemens Ladisch   ALSA: pcm: suppor...
401

59ff878ff   Clemens Ladisch   ALSA: pcm: detect...
402
  	if (runtime->no_period_wakeup) {
12ff414e2   Kelly Anderson   ALSA: pcm: fix in...
403
  		snd_pcm_sframes_t xrun_threshold;
59ff878ff   Clemens Ladisch   ALSA: pcm: detect...
404
405
406
407
  		/*
  		 * Without regular period interrupts, we have to check
  		 * the elapsed time to detect xruns.
  		 */
3509a03f4   Pierre-Louis Bossart   ALSA: core: group...
408
  		jdelta = curr_jiffies - runtime->hw_ptr_jiffies;
47228e48a   Clemens Ladisch   ALSA: pcm: optimi...
409
410
  		if (jdelta < runtime->hw_ptr_buffer_jiffies / 2)
  			goto no_delta_check;
59ff878ff   Clemens Ladisch   ALSA: pcm: detect...
411
  		hdelta = jdelta - delta * HZ / runtime->rate;
12ff414e2   Kelly Anderson   ALSA: pcm: fix in...
412
413
  		xrun_threshold = runtime->hw_ptr_buffer_jiffies / 2 + 1;
  		while (hdelta > xrun_threshold) {
59ff878ff   Clemens Ladisch   ALSA: pcm: detect...
414
415
  			delta += runtime->buffer_size;
  			hw_base += runtime->buffer_size;
0e8014d77   Pierre-Louis Bossart   ALSA: core: keep ...
416
  			if (hw_base >= runtime->boundary) {
59ff878ff   Clemens Ladisch   ALSA: pcm: detect...
417
  				hw_base = 0;
0e8014d77   Pierre-Louis Bossart   ALSA: core: keep ...
418
419
  				crossed_boundary++;
  			}
59ff878ff   Clemens Ladisch   ALSA: pcm: detect...
420
421
422
  			new_hw_ptr = hw_base + pos;
  			hdelta -= runtime->hw_ptr_buffer_jiffies;
  		}
ab69a4904   Clemens Ladisch   ALSA: pcm: suppor...
423
  		goto no_delta_check;
59ff878ff   Clemens Ladisch   ALSA: pcm: detect...
424
  	}
ab69a4904   Clemens Ladisch   ALSA: pcm: suppor...
425

f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
426
  	/* something must be really wrong */
7b3a177b0   Jaroslav Kysela   ALSA: pcm_lib: fi...
427
  	if (delta >= runtime->buffer_size + runtime->period_size) {
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
428
429
430
431
432
433
434
435
436
437
  		hw_ptr_error(substream,
  			       "Unexpected hw_pointer value %s"
  			       "(stream=%i, pos=%ld, new_hw_ptr=%ld, "
  			       "old_hw_ptr=%ld)
  ",
  				     in_interrupt ? "[Q] " : "[P]",
  				     substream->stream, (long)pos,
  				     (long)new_hw_ptr, (long)old_hw_ptr);
  		return 0;
  	}
c87d97320   Takashi Iwai   ALSA: Enable PCM ...
438
439
  
  	/* Do jiffies check only in xrun_debug mode */
741b20cfb   Jaroslav Kysela   ALSA: pcm_lib.c -...
440
  	if (!xrun_debug(substream, XRUN_DEBUG_JIFFIESCHECK))
c87d97320   Takashi Iwai   ALSA: Enable PCM ...
441
  		goto no_jiffies_check;
3e5b50165   Takashi Iwai   ALSA: pcm core - ...
442
443
444
445
446
447
  	/* 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...
448
  	hdelta = delta;
a4444da31   Jaroslav Kysela   ALSA: PCM midleve...
449
450
451
  	if (hdelta < runtime->delay)
  		goto no_jiffies_check;
  	hdelta -= runtime->delay;
3509a03f4   Pierre-Louis Bossart   ALSA: core: group...
452
  	jdelta = curr_jiffies - runtime->hw_ptr_jiffies;
bbf6ad139   Jaroslav Kysela   [ALSA] pcm-midlev...
453
454
455
456
  	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...
457
458
  		/* move new_hw_ptr according jiffies not pos variable */
  		new_hw_ptr = old_hw_ptr;
ed69c6a8e   Jaroslav Kysela   ALSA: pcm_lib - f...
459
  		hw_base = delta;
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
460
461
462
463
  		/* 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 ...
464
  			if (new_hw_ptr >= runtime->boundary) {
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
465
  				new_hw_ptr -= runtime->boundary;
0e8014d77   Pierre-Louis Bossart   ALSA: core: keep ...
466
467
  				crossed_boundary--;
  			}
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
468
469
470
  			delta--;
  		}
  		/* align hw_base to buffer_size */
bbf6ad139   Jaroslav Kysela   [ALSA] pcm-midlev...
471
  		hw_ptr_error(substream,
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
472
  			     "hw_ptr skipping! %s"
bbf6ad139   Jaroslav Kysela   [ALSA] pcm-midlev...
473
  			     "(pos=%ld, delta=%ld, period=%ld, "
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
474
475
476
  			     "jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)
  ",
  			     in_interrupt ? "[Q] " : "",
bbf6ad139   Jaroslav Kysela   [ALSA] pcm-midlev...
477
478
  			     (long)pos, (long)hdelta,
  			     (long)runtime->period_size, jdelta,
ed69c6a8e   Jaroslav Kysela   ALSA: pcm_lib - f...
479
  			     ((hdelta * HZ) / runtime->rate), hw_base,
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
480
481
  			     (unsigned long)old_hw_ptr,
  			     (unsigned long)new_hw_ptr);
ed69c6a8e   Jaroslav Kysela   ALSA: pcm_lib - f...
482
483
484
  		/* reset values to proper state */
  		delta = 0;
  		hw_base = new_hw_ptr - (new_hw_ptr % runtime->buffer_size);
bbf6ad139   Jaroslav Kysela   [ALSA] pcm-midlev...
485
  	}
3e5b50165   Takashi Iwai   ALSA: pcm core - ...
486
   no_jiffies_check:
bbf6ad139   Jaroslav Kysela   [ALSA] pcm-midlev...
487
  	if (delta > runtime->period_size + runtime->period_size / 2) {
ed3da3d9a   Takashi Iwai   ALSA: Rewrite hw_...
488
  		hw_ptr_error(substream,
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
489
490
491
492
493
  			     "Lost interrupts? %s"
  			     "(stream=%i, delta=%ld, new_hw_ptr=%ld, "
  			     "old_hw_ptr=%ld)
  ",
  			     in_interrupt ? "[Q] " : "",
ed3da3d9a   Takashi Iwai   ALSA: Rewrite hw_...
494
  			     substream->stream, (long)delta,
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
495
496
  			     (long)new_hw_ptr,
  			     (long)old_hw_ptr);
ed3da3d9a   Takashi Iwai   ALSA: Rewrite hw_...
497
  	}
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
498

ab69a4904   Clemens Ladisch   ALSA: pcm: suppor...
499
   no_delta_check:
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
500
501
  	if (runtime->status->hw_ptr == new_hw_ptr)
  		return 0;
ab1863fc9   Takashi Iwai   ALSA: pcm - Fix u...
502

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
503
504
505
  	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...
506
  	if (in_interrupt) {
ead4046b2   Clemens Ladisch   ALSA: pcm: fix th...
507
508
509
510
511
512
513
  		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...
514
  	}
ed3da3d9a   Takashi Iwai   ALSA: Rewrite hw_...
515
  	runtime->hw_ptr_base = hw_base;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
516
  	runtime->status->hw_ptr = new_hw_ptr;
3509a03f4   Pierre-Louis Bossart   ALSA: core: group...
517
  	runtime->hw_ptr_jiffies = curr_jiffies;
0e8014d77   Pierre-Louis Bossart   ALSA: core: keep ...
518
519
520
521
  	if (crossed_boundary) {
  		snd_BUG_ON(crossed_boundary != 1);
  		runtime->hw_ptr_wrap += runtime->boundary;
  	}
4eeaaeaea   Pierre-Louis Bossart   ALSA: core: add h...
522
  	if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
3509a03f4   Pierre-Louis Bossart   ALSA: core: group...
523
  		runtime->status->tstamp = curr_tstamp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
524

4eeaaeaea   Pierre-Louis Bossart   ALSA: core: add h...
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
  		if (!(runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK)) {
  			/*
  			 * no wall clock available, provide audio timestamp
  			 * derived from pointer position+delay
  			 */
  			u64 audio_frames, audio_nsecs;
  
  			if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  				audio_frames = runtime->hw_ptr_wrap
  					+ runtime->status->hw_ptr
  					- runtime->delay;
  			else
  				audio_frames = runtime->hw_ptr_wrap
  					+ runtime->status->hw_ptr
  					+ runtime->delay;
  			audio_nsecs = div_u64(audio_frames * 1000000000LL,
  					runtime->rate);
  			audio_tstamp = ns_to_timespec(audio_nsecs);
  		}
  		runtime->status->audio_tstamp = audio_tstamp;
  	}
1250932e4   Jaroslav Kysela   ALSA: pcm_lib - o...
546
  	return snd_pcm_update_state(substream, runtime);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
547
548
549
  }
  
  /* CAUTION: call it with irq disabled */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
550
  int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
551
  {
f240406ba   Jaroslav Kysela   ALSA: pcm_lib - c...
552
  	return snd_pcm_update_hw_ptr0(substream, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553
554
555
556
557
558
559
560
561
562
  }
  
  /**
   * 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...
563
564
  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
565
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
566
567
  	struct snd_pcm_str *stream = &pcm->streams[direction];
  	struct snd_pcm_substream *substream;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
568
569
570
571
  	
  	for (substream = stream->substream; substream != NULL; substream = substream->next)
  		substream->ops = ops;
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
572
  EXPORT_SYMBOL(snd_pcm_set_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
573
574
575
576
577
578
579
  
  /**
   * 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...
580
  void snd_pcm_set_sync(struct snd_pcm_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
581
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
582
  	struct snd_pcm_runtime *runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
583
584
585
586
587
588
  	
  	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...
589
  EXPORT_SYMBOL(snd_pcm_set_sync);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
590
591
592
  /*
   *  Standard ioctl routine
   */
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
630
631
632
633
634
635
636
  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...
637
  		snd_BUG_ON(!n);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
638
639
640
  		*r = 0;
  		return UINT_MAX;
  	}
3f7440a6b   Takashi Iwai   ALSA: Clean up 64...
641
  	n = div_u64_rem(n, c, r);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
642
643
644
645
646
647
  	if (n >= UINT_MAX) {
  		*r = 0;
  		return UINT_MAX;
  	}
  	return n;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
648
649
650
651
652
653
654
655
656
  /**
   * 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 ...
657
658
   * 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
659
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
660
  int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
661
662
  {
  	int changed = 0;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
663
664
  	if (snd_BUG_ON(snd_interval_empty(i)))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
  	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...
702
  EXPORT_SYMBOL(snd_interval_refine);
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
703
  static int snd_interval_refine_first(struct snd_interval *i)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
704
  {
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
705
706
  	if (snd_BUG_ON(snd_interval_empty(i)))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
707
708
709
710
711
712
713
714
  	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...
715
  static int snd_interval_refine_last(struct snd_interval *i)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
716
  {
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
717
718
  	if (snd_BUG_ON(snd_interval_empty(i)))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
719
720
721
722
723
724
725
726
  	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...
727
  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
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
  {
  	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...
743
744
745
   * @a: dividend
   * @b: divisor
   * @c: quotient
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
746
747
748
749
750
   *
   * c = a / b
   *
   * Returns non-zero if the value is changed, zero if not changed.
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
751
  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
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
  {
  	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...
777
778
779
780
781
   * @a: dividend 1
   * @b: dividend 2
   * @k: divisor (as integer)
   * @c: result
    *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
782
783
784
785
   * c = a * b / k
   *
   * Returns non-zero if the value is changed, zero if not changed.
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
786
787
  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
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
  {
  	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...
808
809
810
811
   * @a: dividend 1
   * @k: dividend 2 (as integer)
   * @b: divisor
   * @c: result
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
812
813
814
815
816
   *
   * c = a * k / b
   *
   * Returns non-zero if the value is changed, zero if not changed.
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
817
818
  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
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
  {
  	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
841
842
843
844
845
  /* ---- */
  
  
  /**
   * snd_interval_ratnum - refine the interval value
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
846
847
848
849
850
   * @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
851
   *
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
852
853
   * 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
854
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
855
856
857
  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
858
  {
8374e24c2   Krzysztof Helt   ALSA: refine rate...
859
860
  	unsigned int best_num, best_den;
  	int best_diff;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
861
  	unsigned int k;
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
862
  	struct snd_interval t;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
863
  	int err;
8374e24c2   Krzysztof Helt   ALSA: refine rate...
864
865
  	unsigned int result_num, result_den;
  	int result_diff;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
866
867
868
869
870
871
872
873
874
  
  	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...
875
  		den = div_up(num, q);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
876
877
878
879
880
881
882
883
884
885
886
  		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...
887
888
  		if (diff < 0)
  			diff = -diff;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
889
890
891
892
893
894
895
896
897
898
899
900
901
902
  		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...
903
904
905
  	result_num = best_num;
  	result_diff = best_diff;
  	result_den = best_den;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
906
907
908
909
910
911
912
913
914
915
  	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...
916
  		den = div_down(num, q);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
917
918
919
920
921
922
923
924
925
926
927
  		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...
928
929
  		if (diff < 0)
  			diff = -diff;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
  		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...
949
950
951
952
  		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
953
  		if (nump)
8374e24c2   Krzysztof Helt   ALSA: refine rate...
954
  			*nump = result_num;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
955
  		if (denp)
8374e24c2   Krzysztof Helt   ALSA: refine rate...
956
  			*denp = result_den;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
957
958
959
  	}
  	return err;
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
960
  EXPORT_SYMBOL(snd_interval_ratnum);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
961
962
  /**
   * snd_interval_ratden - refine the interval value
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
963
   * @i: interval to refine
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
964
965
   * @rats_count: number of struct ratden
   * @rats: struct ratden array
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
966
967
   * @nump: pointer to store the resultant numerator
   * @denp: pointer to store the resultant denominator
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
968
   *
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
969
970
   * 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
971
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
972
973
  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
974
975
976
977
  			       unsigned int *nump, unsigned int *denp)
  {
  	unsigned int best_num, best_diff, best_den;
  	unsigned int k;
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
978
  	struct snd_interval t;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
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
  	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 ...
1069
1070
   * 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
1071
   */
4af87a939   Mark Brown   ALSA: pcm: Consti...
1072
1073
  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
1074
1075
  {
          unsigned int k;
b1ddaf681   Clemens Ladisch   sound: pcm_lib: f...
1076
  	struct snd_interval list_range;
0981a260a   Takashi Iwai   [ALSA] Fix possib...
1077
1078
1079
1080
1081
  
  	if (!count) {
  		i->empty = 1;
  		return -EINVAL;
  	}
b1ddaf681   Clemens Ladisch   sound: pcm_lib: f...
1082
1083
1084
  	snd_interval_any(&list_range);
  	list_range.min = UINT_MAX;
  	list_range.max = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1085
1086
1087
          for (k = 0; k < count; k++) {
  		if (mask && !(mask & (1 << k)))
  			continue;
b1ddaf681   Clemens Ladisch   sound: pcm_lib: f...
1088
  		if (!snd_interval_test(i, list[k]))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1089
  			continue;
b1ddaf681   Clemens Ladisch   sound: pcm_lib: f...
1090
1091
  		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
1092
          }
b1ddaf681   Clemens Ladisch   sound: pcm_lib: f...
1093
  	return snd_interval_refine(i, &list_range);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1094
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1095
  EXPORT_SYMBOL(snd_interval_list);
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1096
  static int snd_interval_step(struct snd_interval *i, unsigned int min, unsigned int step)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
  {
  	unsigned int n;
  	int changed = 0;
  	n = (i->min - min) % step;
  	if (n != 0 || i->openmin) {
  		i->min += step - n;
  		changed = 1;
  	}
  	n = (i->max - min) % step;
  	if (n != 0 || i->openmax) {
  		i->max -= n;
  		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 ...
1128
   * Return: Zero if successful, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1129
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1130
  int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1131
1132
1133
1134
  			int var,
  			snd_pcm_hw_rule_func_t func, void *private,
  			int dep, ...)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1135
1136
  	struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;
  	struct snd_pcm_hw_rule *c;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1137
1138
1139
1140
  	unsigned int k;
  	va_list args;
  	va_start(args, dep);
  	if (constrs->rules_num >= constrs->rules_all) {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1141
  		struct snd_pcm_hw_rule *new;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1142
1143
  		unsigned int new_rules = constrs->rules_all + 16;
  		new = kcalloc(new_rules, sizeof(*c), GFP_KERNEL);
87a1c8aaa   Jesper Juhl   ALSA: pcm: rememb...
1144
1145
  		if (!new) {
  			va_end(args);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1146
  			return -ENOMEM;
87a1c8aaa   Jesper Juhl   ALSA: pcm: rememb...
1147
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
  		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...
1163
1164
  		if (snd_BUG_ON(k >= ARRAY_SIZE(c->deps))) {
  			va_end(args);
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
1165
  			return -EINVAL;
87a1c8aaa   Jesper Juhl   ALSA: pcm: rememb...
1166
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1167
1168
1169
1170
1171
1172
1173
1174
  		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...
1175
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1176

e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1177
  EXPORT_SYMBOL(snd_pcm_hw_rule_add);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1178
  /**
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1179
   * snd_pcm_hw_constraint_mask - apply the given bitmap mask constraint
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1180
1181
1182
1183
   * @runtime: PCM runtime instance
   * @var: hw_params variable to apply the mask
   * @mask: the bitmap mask
   *
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1184
   * Apply the constraint of the given bitmap mask to a 32-bit mask parameter.
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
1185
1186
   *
   * Return: Zero if successful, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1187
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1188
  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
1189
1190
  			       u_int32_t mask)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1191
1192
  	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
1193
1194
1195
1196
1197
1198
1199
1200
  	*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...
1201
   * snd_pcm_hw_constraint_mask64 - apply the given bitmap mask constraint
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1202
1203
1204
1205
   * @runtime: PCM runtime instance
   * @var: hw_params variable to apply the mask
   * @mask: the 64bit bitmap mask
   *
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1206
   * Apply the constraint of the given bitmap mask to a 64-bit mask parameter.
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
1207
1208
   *
   * Return: Zero if successful, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1209
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1210
  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
1211
1212
  				 u_int64_t mask)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1213
1214
  	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
1215
1216
1217
1218
1219
1220
1221
  	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_...
1222
  EXPORT_SYMBOL(snd_pcm_hw_constraint_mask64);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1223
1224
  
  /**
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1225
   * snd_pcm_hw_constraint_integer - apply an integer constraint to an interval
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1226
1227
1228
1229
   * @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 ...
1230
1231
1232
   *
   * 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
1233
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1234
  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
1235
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1236
  	struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1237
1238
  	return snd_interval_setinteger(constrs_interval(constrs, var));
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1239
  EXPORT_SYMBOL(snd_pcm_hw_constraint_integer);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1240
  /**
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1241
   * snd_pcm_hw_constraint_minmax - apply a min/max range constraint to an interval
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1242
1243
1244
1245
1246
1247
   * @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 ...
1248
1249
1250
   *
   * 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
1251
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1252
  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
1253
1254
  				 unsigned int min, unsigned int max)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1255
1256
  	struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;
  	struct snd_interval t;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1257
1258
1259
1260
1261
1262
  	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...
1263
  EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax);
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1264
1265
  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
1266
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1267
  	struct snd_pcm_hw_constraint_list *list = rule->private;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1268
1269
1270
1271
1272
  	return snd_interval_list(hw_param_interval(params, rule->var), list->count, list->list, list->mask);
  }		
  
  
  /**
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1273
   * snd_pcm_hw_constraint_list - apply a list of constraints to a parameter
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1274
1275
1276
1277
1278
1279
   * @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 ...
1280
1281
   *
   * Return: Zero if successful, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1282
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1283
  int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1284
1285
  			       unsigned int cond,
  			       snd_pcm_hw_param_t var,
1464189f8   Mark Brown   ALSA: pcm: Make c...
1286
  			       const struct snd_pcm_hw_constraint_list *l)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1287
1288
  {
  	return snd_pcm_hw_rule_add(runtime, cond, var,
1464189f8   Mark Brown   ALSA: pcm: Make c...
1289
  				   snd_pcm_hw_rule_list, (void *)l,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1290
1291
  				   var, -1);
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1292
  EXPORT_SYMBOL(snd_pcm_hw_constraint_list);
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1293
1294
  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
1295
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1296
  	struct snd_pcm_hw_constraint_ratnums *r = rule->private;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
  	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...
1309
   * snd_pcm_hw_constraint_ratnums - apply ratnums constraint to a parameter
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1310
1311
1312
   * @runtime: PCM runtime instance
   * @cond: condition bits
   * @var: hw_params variable to apply the ratnums constraint
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1313
   * @r: struct snd_ratnums constriants
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
1314
1315
   *
   * Return: Zero if successful, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1316
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1317
  int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, 
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1318
1319
  				  unsigned int cond,
  				  snd_pcm_hw_param_t var,
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1320
  				  struct snd_pcm_hw_constraint_ratnums *r)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1321
1322
1323
1324
1325
  {
  	return snd_pcm_hw_rule_add(runtime, cond, var,
  				   snd_pcm_hw_rule_ratnums, r,
  				   var, -1);
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1326
  EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums);
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1327
1328
  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
1329
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1330
  	struct snd_pcm_hw_constraint_ratdens *r = rule->private;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
  	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...
1342
   * snd_pcm_hw_constraint_ratdens - apply ratdens constraint to a parameter
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1343
1344
1345
   * @runtime: PCM runtime instance
   * @cond: condition bits
   * @var: hw_params variable to apply the ratdens constraint
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1346
   * @r: struct snd_ratdens constriants
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
1347
1348
   *
   * Return: Zero if successful, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1349
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1350
  int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime, 
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1351
1352
  				  unsigned int cond,
  				  snd_pcm_hw_param_t var,
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1353
  				  struct snd_pcm_hw_constraint_ratdens *r)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1354
1355
1356
1357
1358
  {
  	return snd_pcm_hw_rule_add(runtime, cond, var,
  				   snd_pcm_hw_rule_ratdens, r,
  				   var, -1);
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1359
  EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens);
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1360
1361
  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
1362
1363
1364
1365
  {
  	unsigned int l = (unsigned long) rule->private;
  	int width = l & 0xffff;
  	unsigned int msbits = l >> 16;
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1366
  	struct snd_interval *i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1367
1368
1369
1370
1371
1372
  	if (snd_interval_single(i) && snd_interval_value(i) == width)
  		params->msbits = msbits;
  	return 0;
  }
  
  /**
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1373
   * snd_pcm_hw_constraint_msbits - add a hw constraint msbits rule
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1374
1375
1376
1377
   * @runtime: PCM runtime instance
   * @cond: condition bits
   * @width: sample bits width
   * @msbits: msbits width
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
1378
1379
   *
   * Return: Zero if successful, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1380
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1381
  int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime, 
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
  				 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...
1392
  EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits);
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1393
1394
  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
1395
1396
1397
1398
1399
1400
  {
  	unsigned long step = (unsigned long) rule->private;
  	return snd_interval_step(hw_param_interval(params, rule->var), 0, step);
  }
  
  /**
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1401
   * snd_pcm_hw_constraint_step - add a hw constraint step rule
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1402
1403
1404
1405
   * @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 ...
1406
1407
   *
   * Return: Zero if successful, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1408
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1409
  int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1410
1411
1412
1413
1414
1415
1416
1417
  			       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...
1418
  EXPORT_SYMBOL(snd_pcm_hw_constraint_step);
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1419
  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
1420
  {
67c393172   Marcin Åšlusarz   [ALSA] pcm_lib: f...
1421
  	static unsigned int pow2_sizes[] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
  		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...
1432
   * snd_pcm_hw_constraint_pow2 - add a hw constraint power-of-2 rule
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1433
1434
1435
   * @runtime: PCM runtime instance
   * @cond: condition bits
   * @var: hw_params variable to apply the power-of-2 constraint
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
1436
1437
   *
   * Return: Zero if successful, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1438
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1439
  int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1440
1441
1442
1443
1444
1445
1446
  			       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...
1447
  EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2);
d5b702a64   Clemens Ladisch   ALSA: pcm: add sn...
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
  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 ...
1462
1463
   *
   * Return: Zero if successful, or a negative error code on failure.
d5b702a64   Clemens Ladisch   ALSA: pcm: add sn...
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
   */
  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...
1475
  static void _snd_pcm_hw_param_any(struct snd_pcm_hw_params *params,
123992f72   Adrian Bunk   [ALSA] sound/core...
1476
  				  snd_pcm_hw_param_t var)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
  {
  	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...
1492
  void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1493
1494
1495
1496
1497
1498
1499
1500
1501
  {
  	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...
1502
  EXPORT_SYMBOL(_snd_pcm_hw_params_any);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1503
1504
  
  /**
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1505
   * snd_pcm_hw_param_value - return @params field @var value
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1506
1507
   * @params: the hw_params instance
   * @var: parameter to retrieve
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1508
   * @dir: pointer to the direction (-1,0,1) or %NULL
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1509
   *
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
1510
1511
   * 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
1512
   */
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1513
1514
  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
1515
1516
  {
  	if (hw_is_mask(var)) {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1517
  		const struct snd_mask *mask = hw_param_mask_c(params, var);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1518
1519
1520
1521
1522
1523
1524
  		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...
1525
  		const struct snd_interval *i = hw_param_interval_c(params, var);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1526
1527
1528
1529
1530
1531
  		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
1532
1533
  	return -EINVAL;
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1534
  EXPORT_SYMBOL(snd_pcm_hw_param_value);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1535

877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1536
  void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
  				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...
1551
  EXPORT_SYMBOL(_snd_pcm_hw_param_setempty);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1552

877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1553
  static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params,
123992f72   Adrian Bunk   [ALSA] sound/core...
1554
  				   snd_pcm_hw_param_t var)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1555
1556
1557
1558
1559
1560
  {
  	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...
1561
  	else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1562
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1563
1564
1565
1566
1567
1568
1569
1570
1571
  	if (changed) {
  		params->cmask |= 1 << var;
  		params->rmask |= 1 << var;
  	}
  	return changed;
  }
  
  
  /**
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1572
   * snd_pcm_hw_param_first - refine config space and return minimum value
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1573
1574
1575
   * @pcm: PCM instance
   * @params: the hw_params instance
   * @var: parameter to retrieve
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1576
   * @dir: pointer to the direction (-1,0,1) or %NULL
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1577
   *
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1578
   * Inside configuration space defined by @params remove from @var all
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1579
   * values > minimum. Reduce configuration space accordingly.
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
1580
1581
   *
   * Return: The minimum, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1582
   */
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1583
1584
1585
  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
1586
1587
1588
1589
1590
1591
  {
  	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...
1592
1593
  		if (snd_BUG_ON(err < 0))
  			return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1594
1595
1596
  	}
  	return snd_pcm_hw_param_value(params, var, dir);
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1597
  EXPORT_SYMBOL(snd_pcm_hw_param_first);
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1598
  static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params,
123992f72   Adrian Bunk   [ALSA] sound/core...
1599
  				  snd_pcm_hw_param_t var)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1600
1601
1602
1603
1604
1605
  {
  	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...
1606
  	else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1607
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1608
1609
1610
1611
1612
1613
1614
1615
1616
  	if (changed) {
  		params->cmask |= 1 << var;
  		params->rmask |= 1 << var;
  	}
  	return changed;
  }
  
  
  /**
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1617
   * snd_pcm_hw_param_last - refine config space and return maximum value
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1618
1619
1620
   * @pcm: PCM instance
   * @params: the hw_params instance
   * @var: parameter to retrieve
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1621
   * @dir: pointer to the direction (-1,0,1) or %NULL
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1622
   *
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1623
   * Inside configuration space defined by @params remove from @var all
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1624
   * values < maximum. Reduce configuration space accordingly.
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
1625
1626
   *
   * Return: The maximum, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1627
   */
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1628
1629
1630
  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
1631
1632
1633
1634
1635
1636
  {
  	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...
1637
1638
  		if (snd_BUG_ON(err < 0))
  			return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1639
1640
1641
  	}
  	return snd_pcm_hw_param_value(params, var, dir);
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1642
  EXPORT_SYMBOL(snd_pcm_hw_param_last);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1643
1644
  
  /**
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1645
   * snd_pcm_hw_param_choose - choose a configuration defined by @params
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
1646
1647
   * @pcm: PCM instance
   * @params: the hw_params instance
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1648
   *
1c85cc644   Randy Dunlap   ALSA: kernel docs...
1649
   * Choose one configuration from configuration space defined by @params.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1650
1651
1652
   * 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 ...
1653
1654
   *
   * Return: Zero if successful, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1655
   */
2f4ca8e5c   Takashi Iwai   [ALSA] Clean up u...
1656
1657
  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
1658
  {
2f4ca8e5c   Takashi Iwai   [ALSA] Clean up u...
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
  	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
1671

2f4ca8e5c   Takashi Iwai   [ALSA] Clean up u...
1672
1673
1674
1675
1676
  	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...
1677
1678
  		if (snd_BUG_ON(err < 0))
  			return err;
2f4ca8e5c   Takashi Iwai   [ALSA] Clean up u...
1679
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1680
1681
  	return 0;
  }
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1682
  static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1683
1684
  				   void *arg)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1685
  	struct snd_pcm_runtime *runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1686
1687
1688
1689
1690
  	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 ...
1691
  	else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1692
  		runtime->status->hw_ptr = 0;
0e8014d77   Pierre-Louis Bossart   ALSA: core: keep ...
1693
1694
  		runtime->hw_ptr_wrap = 0;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1695
1696
1697
  	snd_pcm_stream_unlock_irqrestore(substream, flags);
  	return 0;
  }
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1698
  static int snd_pcm_lib_ioctl_channel_info(struct snd_pcm_substream *substream,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1699
1700
  					  void *arg)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1701
1702
  	struct snd_pcm_channel_info *info = arg;
  	struct snd_pcm_runtime *runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
  	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...
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
  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;
  	int channels, width;
  
  	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);
  		width = snd_pcm_format_physical_width(format);
  		params->fifo_size /= width * channels;
  	}
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1748
1749
1750
1751
1752
1753
1754
1755
1756
  /**
   * 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 ...
1757
   * Return: Zero if successful, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1758
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1759
  int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1760
1761
1762
1763
1764
1765
1766
1767
1768
  		      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...
1769
1770
  	case SNDRV_PCM_IOCTL1_FIFO_SIZE:
  		return snd_pcm_lib_ioctl_fifo_size(substream, arg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1771
1772
1773
  	}
  	return -ENXIO;
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
1774
  EXPORT_SYMBOL(snd_pcm_lib_ioctl);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1775
1776
1777
1778
1779
1780
  /**
   * 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...
1781
   * pointer, wake up sleepers, etc.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1782
1783
1784
1785
   *
   * 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...
1786
  void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1787
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1788
  	struct snd_pcm_runtime *runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1789
  	unsigned long flags;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
1790
1791
  	if (PCM_RUNTIME_CHECK(substream))
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1792
  	runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1793
1794
1795
1796
1797
1798
  
  	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...
1799
  	    snd_pcm_update_hw_ptr0(substream, 1) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1800
1801
1802
1803
  		goto _end;
  
  	if (substream->timer_running)
  		snd_timer_interrupt(substream->timer, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1804
1805
1806
1807
1808
1809
   _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...
1810
  EXPORT_SYMBOL(snd_pcm_period_elapsed);
130755108   Takashi Iwai   [ALSA] PCM - clea...
1811
1812
1813
1814
1815
1816
  /*
   * 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...
1817
  static int wait_for_avail(struct snd_pcm_substream *substream,
130755108   Takashi Iwai   [ALSA] PCM - clea...
1818
1819
1820
1821
1822
1823
1824
  			      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...
1825
  	long wait_time, tout;
763437a9e   Arjan van de Ven   ALSA: pcm - fix r...
1826
1827
1828
  	init_waitqueue_entry(&wait, current);
  	set_current_state(TASK_INTERRUPTIBLE);
  	add_wait_queue(&runtime->tsleep, &wait);
f2b3614ce   Takashi Iwai   ALSA: PCM - Don't...
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
  	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...
1839

130755108   Takashi Iwai   [ALSA] PCM - clea...
1840
1841
1842
1843
1844
  	for (;;) {
  		if (signal_pending(current)) {
  			err = -ERESTARTSYS;
  			break;
  		}
763437a9e   Arjan van de Ven   ALSA: pcm - fix r...
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
  
  		/*
  		 * 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...
1859
  		snd_pcm_stream_unlock_irq(substream);
763437a9e   Arjan van de Ven   ALSA: pcm - fix r...
1860
1861
  
  		tout = schedule_timeout(wait_time);
130755108   Takashi Iwai   [ALSA] PCM - clea...
1862
  		snd_pcm_stream_lock_irq(substream);
763437a9e   Arjan van de Ven   ALSA: pcm - fix r...
1863
  		set_current_state(TASK_INTERRUPTIBLE);
130755108   Takashi Iwai   [ALSA] PCM - clea...
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
  		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...
1882
1883
  		case SNDRV_PCM_STATE_PAUSED:
  			continue;
130755108   Takashi Iwai   [ALSA] PCM - clea...
1884
1885
  		}
  		if (!tout) {
09e56df8b   Takashi Iwai   ALSA: pcm: Use st...
1886
1887
1888
1889
  			pcm_dbg(substream->pcm,
  				"%s write error (DMA or IRQ trouble?)
  ",
  				is_playback ? "playback" : "capture");
130755108   Takashi Iwai   [ALSA] PCM - clea...
1890
1891
1892
  			err = -EIO;
  			break;
  		}
130755108   Takashi Iwai   [ALSA] PCM - clea...
1893
1894
  	}
   _endloop:
763437a9e   Arjan van de Ven   ALSA: pcm - fix r...
1895
  	set_current_state(TASK_RUNNING);
c91a988dc   Jaroslav Kysela   ALSA: pcm_core: F...
1896
  	remove_wait_queue(&runtime->tsleep, &wait);
130755108   Takashi Iwai   [ALSA] PCM - clea...
1897
1898
1899
1900
  	*availp = avail;
  	return err;
  }
  	
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1901
  static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1902
1903
1904
1905
  				      unsigned int hwoff,
  				      unsigned long data, unsigned int off,
  				      snd_pcm_uframes_t frames)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1906
  	struct snd_pcm_runtime *runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1907
1908
1909
1910
1911
1912
1913
  	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
1914
1915
1916
1917
1918
1919
  		if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames)))
  			return -EFAULT;
  	}
  	return 0;
  }
   
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1920
  typedef int (*transfer_f)(struct snd_pcm_substream *substream, unsigned int hwoff,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1921
1922
  			  unsigned long data, unsigned int off,
  			  snd_pcm_uframes_t size);
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1923
  static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, 
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1924
1925
1926
1927
1928
  					    unsigned long data,
  					    snd_pcm_uframes_t size,
  					    int nonblock,
  					    transfer_f transfer)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
1929
  	struct snd_pcm_runtime *runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1930
1931
  	snd_pcm_uframes_t xfer = 0;
  	snd_pcm_uframes_t offset = 0;
0910c216f   Takashi Iwai   ALSA: pcm - Optim...
1932
  	snd_pcm_uframes_t avail;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1933
1934
1935
1936
  	int err = 0;
  
  	if (size == 0)
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
  
  	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...
1954
  	runtime->twake = runtime->control->avail_min ? : 1;
0910c216f   Takashi Iwai   ALSA: pcm - Optim...
1955
1956
1957
  	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
1958
1959
  	while (size > 0) {
  		snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1960
  		snd_pcm_uframes_t cont;
130755108   Takashi Iwai   [ALSA] PCM - clea...
1961
  		if (!avail) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1962
1963
1964
1965
  			if (nonblock) {
  				err = -EAGAIN;
  				goto _end_unlock;
  			}
5daeba34d   David Dillow   ALSA: pcm_lib: av...
1966
1967
1968
  			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...
1969
  			if (err < 0)
443feb882   Karsten Wiese   [ALSA] ALSA's str...
1970
  				goto _end_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1971
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1972
1973
1974
1975
  		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...
1976
  		if (snd_BUG_ON(!frames)) {
c91a988dc   Jaroslav Kysela   ALSA: pcm_core: F...
1977
  			runtime->twake = 0;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
1978
1979
1980
  			snd_pcm_stream_unlock_irq(substream);
  			return -EINVAL;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1981
1982
1983
  		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...
1984
  		err = transfer(substream, appl_ofs, data, offset, frames);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1985
  		snd_pcm_stream_lock_irq(substream);
1250932e4   Jaroslav Kysela   ALSA: pcm_lib - o...
1986
1987
  		if (err < 0)
  			goto _end_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
  		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...
2008
  		avail -= frames;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2009
2010
2011
2012
2013
2014
  		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
2015
2016
  	}
   _end_unlock:
c91a988dc   Jaroslav Kysela   ALSA: pcm_core: F...
2017
  	runtime->twake = 0;
1250932e4   Jaroslav Kysela   ALSA: pcm_lib - o...
2018
2019
  	if (xfer > 0 && err >= 0)
  		snd_pcm_update_state(substream, runtime);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2020
  	snd_pcm_stream_unlock_irq(substream);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2021
2022
  	return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
  }
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2023
2024
  /* sanity-check for read/write methods */
  static int pcm_sanity_check(struct snd_pcm_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2025
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2026
  	struct snd_pcm_runtime *runtime;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2027
2028
  	if (PCM_RUNTIME_CHECK(substream))
  		return -ENXIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2029
  	runtime = substream->runtime;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2030
2031
  	if (snd_BUG_ON(!substream->ops->copy && !runtime->dma_area))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2032
2033
  	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
  		return -EBADFD;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2034
2035
2036
2037
2038
2039
2040
2041
  	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
2042

7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2043
2044
2045
2046
  	err = pcm_sanity_check(substream);
  	if (err < 0)
  		return err;
  	runtime = substream->runtime;
0df63e44c   Takashi Iwai   [ALSA] Add O_APPE...
2047
  	nonblock = !!(substream->f_flags & O_NONBLOCK);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2048
2049
2050
2051
2052
2053
2054
  
  	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...
2055
  EXPORT_SYMBOL(snd_pcm_lib_write);
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2056
  static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2057
2058
2059
2060
  				       unsigned int hwoff,
  				       unsigned long data, unsigned int off,
  				       snd_pcm_uframes_t frames)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2061
  	struct snd_pcm_runtime *runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2062
2063
2064
2065
2066
  	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...
2067
2068
  		if (snd_BUG_ON(!substream->ops->silence))
  			return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
  		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
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
  		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...
2096
  snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2097
2098
2099
  				     void __user **bufs,
  				     snd_pcm_uframes_t frames)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2100
  	struct snd_pcm_runtime *runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2101
  	int nonblock;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2102
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2103

7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2104
2105
2106
  	err = pcm_sanity_check(substream);
  	if (err < 0)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2107
  	runtime = substream->runtime;
0df63e44c   Takashi Iwai   [ALSA] Add O_APPE...
2108
  	nonblock = !!(substream->f_flags & O_NONBLOCK);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2109
2110
2111
2112
2113
2114
  
  	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...
2115
  EXPORT_SYMBOL(snd_pcm_lib_writev);
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2116
  static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream, 
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2117
2118
2119
2120
  				     unsigned int hwoff,
  				     unsigned long data, unsigned int off,
  				     snd_pcm_uframes_t frames)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2121
  	struct snd_pcm_runtime *runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2122
2123
2124
2125
2126
2127
2128
  	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
2129
2130
2131
2132
2133
  		if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames)))
  			return -EFAULT;
  	}
  	return 0;
  }
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2134
  static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2135
2136
2137
2138
2139
  					   unsigned long data,
  					   snd_pcm_uframes_t size,
  					   int nonblock,
  					   transfer_f transfer)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2140
  	struct snd_pcm_runtime *runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2141
2142
  	snd_pcm_uframes_t xfer = 0;
  	snd_pcm_uframes_t offset = 0;
0910c216f   Takashi Iwai   ALSA: pcm - Optim...
2143
  	snd_pcm_uframes_t avail;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2144
2145
2146
2147
  	int err = 0;
  
  	if (size == 0)
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
  
  	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...
2172
  	runtime->twake = runtime->control->avail_min ? : 1;
0910c216f   Takashi Iwai   ALSA: pcm - Optim...
2173
2174
2175
  	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
2176
2177
  	while (size > 0) {
  		snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2178
  		snd_pcm_uframes_t cont;
130755108   Takashi Iwai   [ALSA] PCM - clea...
2179
2180
2181
2182
  		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
2183
2184
  				goto _end_unlock;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2185
2186
2187
2188
  			if (nonblock) {
  				err = -EAGAIN;
  				goto _end_unlock;
  			}
5daeba34d   David Dillow   ALSA: pcm_lib: av...
2189
2190
2191
  			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...
2192
  			if (err < 0)
443feb882   Karsten Wiese   [ALSA] ALSA's str...
2193
  				goto _end_unlock;
130755108   Takashi Iwai   [ALSA] PCM - clea...
2194
2195
  			if (!avail)
  				continue; /* draining */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2196
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2197
2198
2199
2200
  		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...
2201
  		if (snd_BUG_ON(!frames)) {
c91a988dc   Jaroslav Kysela   ALSA: pcm_core: F...
2202
  			runtime->twake = 0;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2203
2204
2205
  			snd_pcm_stream_unlock_irq(substream);
  			return -EINVAL;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2206
2207
2208
  		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...
2209
  		err = transfer(substream, appl_ofs, data, offset, frames);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2210
  		snd_pcm_stream_lock_irq(substream);
1250932e4   Jaroslav Kysela   ALSA: pcm_lib - o...
2211
2212
  		if (err < 0)
  			goto _end_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
  		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...
2233
  		avail -= frames;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2234
2235
  	}
   _end_unlock:
c91a988dc   Jaroslav Kysela   ALSA: pcm_core: F...
2236
  	runtime->twake = 0;
1250932e4   Jaroslav Kysela   ALSA: pcm_lib - o...
2237
2238
  	if (xfer > 0 && err >= 0)
  		snd_pcm_update_state(substream, runtime);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2239
  	snd_pcm_stream_unlock_irq(substream);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2240
2241
  	return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
  }
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2242
  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
2243
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2244
  	struct snd_pcm_runtime *runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2245
  	int nonblock;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2246
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2247
  	
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2248
2249
2250
  	err = pcm_sanity_check(substream);
  	if (err < 0)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2251
  	runtime = substream->runtime;
0df63e44c   Takashi Iwai   [ALSA] Add O_APPE...
2252
  	nonblock = !!(substream->f_flags & O_NONBLOCK);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2253
2254
2255
2256
  	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...
2257
  EXPORT_SYMBOL(snd_pcm_lib_read);
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2258
  static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2259
2260
2261
2262
  				      unsigned int hwoff,
  				      unsigned long data, unsigned int off,
  				      snd_pcm_uframes_t frames)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2263
  	struct snd_pcm_runtime *runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
  	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
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
  		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...
2294
  snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2295
2296
2297
  				    void __user **bufs,
  				    snd_pcm_uframes_t frames)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
2298
  	struct snd_pcm_runtime *runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2299
  	int nonblock;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2300
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2301

7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
2302
2303
2304
  	err = pcm_sanity_check(substream);
  	if (err < 0)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2305
  	runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2306
2307
  	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
  		return -EBADFD;
0df63e44c   Takashi Iwai   [ALSA] Add O_APPE...
2308
  	nonblock = !!(substream->f_flags & O_NONBLOCK);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2309
2310
2311
2312
  	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
2313
  EXPORT_SYMBOL(snd_pcm_lib_readv);
2d3391ec0   Takashi Iwai   ALSA: PCM: channe...
2314
2315
2316
2317
2318
2319
2320
2321
  
  /*
   * 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...
2322
  	  .map = { SNDRV_CHMAP_MONO } },
2d3391ec0   Takashi Iwai   ALSA: PCM: channe...
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
  	{ .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...
2344
  	  .map = { SNDRV_CHMAP_MONO } },
2d3391ec0   Takashi Iwai   ALSA: PCM: channe...
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
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
  	{ .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 ...
2478
   * Return: Zero if successful, or a negative error value.
2d3391ec0   Takashi Iwai   ALSA: PCM: channe...
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
   */
  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...
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
  			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);