11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Digital Audio (PCM) abstract layer 3c1017a4cSJaroslav Kysela * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 41da177e4SLinus Torvalds * Abramo Bagnara <abramo@alsa-project.org> 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 81da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 91da177e4SLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 101da177e4SLinus Torvalds * (at your option) any later version. 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 131da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 141da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 151da177e4SLinus Torvalds * GNU General Public License for more details. 161da177e4SLinus Torvalds * 171da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 181da177e4SLinus Torvalds * along with this program; if not, write to the Free Software 191da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 201da177e4SLinus Torvalds * 211da177e4SLinus Torvalds */ 221da177e4SLinus Torvalds 231da177e4SLinus Torvalds #include <linux/slab.h> 241da177e4SLinus Torvalds #include <linux/time.h> 251da177e4SLinus Torvalds #include <sound/core.h> 261da177e4SLinus Torvalds #include <sound/control.h> 271da177e4SLinus Torvalds #include <sound/info.h> 281da177e4SLinus Torvalds #include <sound/pcm.h> 291da177e4SLinus Torvalds #include <sound/pcm_params.h> 301da177e4SLinus Torvalds #include <sound/timer.h> 311da177e4SLinus Torvalds 321da177e4SLinus Torvalds /* 331da177e4SLinus Torvalds * fill ring buffer with silence 341da177e4SLinus Torvalds * runtime->silence_start: starting pointer to silence area 351da177e4SLinus Torvalds * runtime->silence_filled: size filled with silence 361da177e4SLinus Torvalds * runtime->silence_threshold: threshold from application 371da177e4SLinus Torvalds * runtime->silence_size: maximal size from application 381da177e4SLinus Torvalds * 391da177e4SLinus Torvalds * when runtime->silence_size >= runtime->boundary - fill processed area with silence immediately 401da177e4SLinus Torvalds */ 41877211f5STakashi Iwai void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_uframes_t new_hw_ptr) 421da177e4SLinus Torvalds { 43877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 441da177e4SLinus Torvalds snd_pcm_uframes_t frames, ofs, transfer; 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds if (runtime->silence_size < runtime->boundary) { 471da177e4SLinus Torvalds snd_pcm_sframes_t noise_dist, n; 481da177e4SLinus Torvalds if (runtime->silence_start != runtime->control->appl_ptr) { 491da177e4SLinus Torvalds n = runtime->control->appl_ptr - runtime->silence_start; 501da177e4SLinus Torvalds if (n < 0) 511da177e4SLinus Torvalds n += runtime->boundary; 521da177e4SLinus Torvalds if ((snd_pcm_uframes_t)n < runtime->silence_filled) 531da177e4SLinus Torvalds runtime->silence_filled -= n; 541da177e4SLinus Torvalds else 551da177e4SLinus Torvalds runtime->silence_filled = 0; 561da177e4SLinus Torvalds runtime->silence_start = runtime->control->appl_ptr; 571da177e4SLinus Torvalds } 58235475cbSTakashi Iwai if (runtime->silence_filled >= runtime->buffer_size) 591da177e4SLinus Torvalds return; 601da177e4SLinus Torvalds noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled; 611da177e4SLinus Torvalds if (noise_dist >= (snd_pcm_sframes_t) runtime->silence_threshold) 621da177e4SLinus Torvalds return; 631da177e4SLinus Torvalds frames = runtime->silence_threshold - noise_dist; 641da177e4SLinus Torvalds if (frames > runtime->silence_size) 651da177e4SLinus Torvalds frames = runtime->silence_size; 661da177e4SLinus Torvalds } else { 671da177e4SLinus Torvalds if (new_hw_ptr == ULONG_MAX) { /* initialization */ 681da177e4SLinus Torvalds snd_pcm_sframes_t avail = snd_pcm_playback_hw_avail(runtime); 691da177e4SLinus Torvalds runtime->silence_filled = avail > 0 ? avail : 0; 701da177e4SLinus Torvalds runtime->silence_start = (runtime->status->hw_ptr + 711da177e4SLinus Torvalds runtime->silence_filled) % 721da177e4SLinus Torvalds runtime->boundary; 731da177e4SLinus Torvalds } else { 741da177e4SLinus Torvalds ofs = runtime->status->hw_ptr; 751da177e4SLinus Torvalds frames = new_hw_ptr - ofs; 761da177e4SLinus Torvalds if ((snd_pcm_sframes_t)frames < 0) 771da177e4SLinus Torvalds frames += runtime->boundary; 781da177e4SLinus Torvalds runtime->silence_filled -= frames; 791da177e4SLinus Torvalds if ((snd_pcm_sframes_t)runtime->silence_filled < 0) { 801da177e4SLinus Torvalds runtime->silence_filled = 0; 819a826ddbSClemens Ladisch runtime->silence_start = new_hw_ptr; 821da177e4SLinus Torvalds } else { 839a826ddbSClemens Ladisch runtime->silence_start = ofs; 841da177e4SLinus Torvalds } 851da177e4SLinus Torvalds } 861da177e4SLinus Torvalds frames = runtime->buffer_size - runtime->silence_filled; 871da177e4SLinus Torvalds } 881da177e4SLinus Torvalds snd_assert(frames <= runtime->buffer_size, return); 891da177e4SLinus Torvalds if (frames == 0) 901da177e4SLinus Torvalds return; 919a826ddbSClemens Ladisch ofs = runtime->silence_start % runtime->buffer_size; 921da177e4SLinus Torvalds while (frames > 0) { 931da177e4SLinus Torvalds transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames; 941da177e4SLinus Torvalds if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || 951da177e4SLinus Torvalds runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { 961da177e4SLinus Torvalds if (substream->ops->silence) { 971da177e4SLinus Torvalds int err; 981da177e4SLinus Torvalds err = substream->ops->silence(substream, -1, ofs, transfer); 991da177e4SLinus Torvalds snd_assert(err >= 0, ); 1001da177e4SLinus Torvalds } else { 1011da177e4SLinus Torvalds char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs); 1021da177e4SLinus Torvalds snd_pcm_format_set_silence(runtime->format, hwbuf, transfer * runtime->channels); 1031da177e4SLinus Torvalds } 1041da177e4SLinus Torvalds } else { 1051da177e4SLinus Torvalds unsigned int c; 1061da177e4SLinus Torvalds unsigned int channels = runtime->channels; 1071da177e4SLinus Torvalds if (substream->ops->silence) { 1081da177e4SLinus Torvalds for (c = 0; c < channels; ++c) { 1091da177e4SLinus Torvalds int err; 1101da177e4SLinus Torvalds err = substream->ops->silence(substream, c, ofs, transfer); 1111da177e4SLinus Torvalds snd_assert(err >= 0, ); 1121da177e4SLinus Torvalds } 1131da177e4SLinus Torvalds } else { 1141da177e4SLinus Torvalds size_t dma_csize = runtime->dma_bytes / channels; 1151da177e4SLinus Torvalds for (c = 0; c < channels; ++c) { 1161da177e4SLinus Torvalds char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, ofs); 1171da177e4SLinus Torvalds snd_pcm_format_set_silence(runtime->format, hwbuf, transfer); 1181da177e4SLinus Torvalds } 1191da177e4SLinus Torvalds } 1201da177e4SLinus Torvalds } 1211da177e4SLinus Torvalds runtime->silence_filled += transfer; 1221da177e4SLinus Torvalds frames -= transfer; 1231da177e4SLinus Torvalds ofs = 0; 1241da177e4SLinus Torvalds } 1251da177e4SLinus Torvalds } 1261da177e4SLinus Torvalds 127877211f5STakashi Iwai static void xrun(struct snd_pcm_substream *substream) 1281da177e4SLinus Torvalds { 1291da177e4SLinus Torvalds snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); 13061fb63c0SJaroslav Kysela #ifdef CONFIG_SND_PCM_XRUN_DEBUG 1311da177e4SLinus Torvalds if (substream->pstr->xrun_debug) { 1321da177e4SLinus Torvalds snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n", 1331da177e4SLinus Torvalds substream->pcm->card->number, 1341da177e4SLinus Torvalds substream->pcm->device, 1351da177e4SLinus Torvalds substream->stream ? 'c' : 'p'); 1361da177e4SLinus Torvalds if (substream->pstr->xrun_debug > 1) 1371da177e4SLinus Torvalds dump_stack(); 1381da177e4SLinus Torvalds } 1391da177e4SLinus Torvalds #endif 1401da177e4SLinus Torvalds } 1411da177e4SLinus Torvalds 142877211f5STakashi Iwai static inline snd_pcm_uframes_t snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream, 143877211f5STakashi Iwai struct snd_pcm_runtime *runtime) 1441da177e4SLinus Torvalds { 1451da177e4SLinus Torvalds snd_pcm_uframes_t pos; 1461da177e4SLinus Torvalds 1478c121586SJaroslav Kysela if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) 1488c121586SJaroslav Kysela snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); 1491da177e4SLinus Torvalds pos = substream->ops->pointer(substream); 1501da177e4SLinus Torvalds if (pos == SNDRV_PCM_POS_XRUN) 1511da177e4SLinus Torvalds return pos; /* XRUN */ 1521da177e4SLinus Torvalds #ifdef CONFIG_SND_DEBUG 1531da177e4SLinus Torvalds if (pos >= runtime->buffer_size) { 1541da177e4SLinus Torvalds snd_printk(KERN_ERR "BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size); 1557c22f1aaSTakashi Iwai } 1561da177e4SLinus Torvalds #endif 1571da177e4SLinus Torvalds pos -= pos % runtime->min_align; 1581da177e4SLinus Torvalds return pos; 1591da177e4SLinus Torvalds } 1601da177e4SLinus Torvalds 161877211f5STakashi Iwai static inline int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream, 162877211f5STakashi Iwai struct snd_pcm_runtime *runtime) 1631da177e4SLinus Torvalds { 1641da177e4SLinus Torvalds snd_pcm_uframes_t avail; 1651da177e4SLinus Torvalds 1661da177e4SLinus Torvalds if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 1671da177e4SLinus Torvalds avail = snd_pcm_playback_avail(runtime); 1681da177e4SLinus Torvalds else 1691da177e4SLinus Torvalds avail = snd_pcm_capture_avail(runtime); 1701da177e4SLinus Torvalds if (avail > runtime->avail_max) 1711da177e4SLinus Torvalds runtime->avail_max = avail; 1721da177e4SLinus Torvalds if (avail >= runtime->stop_threshold) { 1731da177e4SLinus Torvalds if (substream->runtime->status->state == SNDRV_PCM_STATE_DRAINING) 1741da177e4SLinus Torvalds snd_pcm_drain_done(substream); 1751da177e4SLinus Torvalds else 1761da177e4SLinus Torvalds xrun(substream); 1771da177e4SLinus Torvalds return -EPIPE; 1781da177e4SLinus Torvalds } 1791da177e4SLinus Torvalds if (avail >= runtime->control->avail_min) 1801da177e4SLinus Torvalds wake_up(&runtime->sleep); 1811da177e4SLinus Torvalds return 0; 1821da177e4SLinus Torvalds } 1831da177e4SLinus Torvalds 184877211f5STakashi Iwai static inline int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) 1851da177e4SLinus Torvalds { 186877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 1871da177e4SLinus Torvalds snd_pcm_uframes_t pos; 1881da177e4SLinus Torvalds snd_pcm_uframes_t new_hw_ptr, hw_ptr_interrupt; 1891da177e4SLinus Torvalds snd_pcm_sframes_t delta; 1901da177e4SLinus Torvalds 1911da177e4SLinus Torvalds pos = snd_pcm_update_hw_ptr_pos(substream, runtime); 1921da177e4SLinus Torvalds if (pos == SNDRV_PCM_POS_XRUN) { 1931da177e4SLinus Torvalds xrun(substream); 1941da177e4SLinus Torvalds return -EPIPE; 1951da177e4SLinus Torvalds } 1961da177e4SLinus Torvalds if (runtime->period_size == runtime->buffer_size) 1971da177e4SLinus Torvalds goto __next_buf; 1981da177e4SLinus Torvalds new_hw_ptr = runtime->hw_ptr_base + pos; 1991da177e4SLinus Torvalds hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size; 2001da177e4SLinus Torvalds 2011da177e4SLinus Torvalds delta = hw_ptr_interrupt - new_hw_ptr; 2021da177e4SLinus Torvalds if (delta > 0) { 2031da177e4SLinus Torvalds if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) { 20461fb63c0SJaroslav Kysela #ifdef CONFIG_SND_PCM_XRUN_DEBUG 2051da177e4SLinus Torvalds if (runtime->periods > 1 && substream->pstr->xrun_debug) { 2061da177e4SLinus Torvalds snd_printd(KERN_ERR "Unexpected hw_pointer value [1] (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2); 2071da177e4SLinus Torvalds if (substream->pstr->xrun_debug > 1) 2081da177e4SLinus Torvalds dump_stack(); 2091da177e4SLinus Torvalds } 2101da177e4SLinus Torvalds #endif 2111da177e4SLinus Torvalds return 0; 2121da177e4SLinus Torvalds } 2131da177e4SLinus Torvalds __next_buf: 2141da177e4SLinus Torvalds runtime->hw_ptr_base += runtime->buffer_size; 2151da177e4SLinus Torvalds if (runtime->hw_ptr_base == runtime->boundary) 2161da177e4SLinus Torvalds runtime->hw_ptr_base = 0; 2171da177e4SLinus Torvalds new_hw_ptr = runtime->hw_ptr_base + pos; 2181da177e4SLinus Torvalds } 2191da177e4SLinus Torvalds 2201da177e4SLinus Torvalds if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && 2211da177e4SLinus Torvalds runtime->silence_size > 0) 2221da177e4SLinus Torvalds snd_pcm_playback_silence(substream, new_hw_ptr); 2231da177e4SLinus Torvalds 2241da177e4SLinus Torvalds runtime->status->hw_ptr = new_hw_ptr; 2251da177e4SLinus Torvalds runtime->hw_ptr_interrupt = new_hw_ptr - new_hw_ptr % runtime->period_size; 2261da177e4SLinus Torvalds 2271da177e4SLinus Torvalds return snd_pcm_update_hw_ptr_post(substream, runtime); 2281da177e4SLinus Torvalds } 2291da177e4SLinus Torvalds 2301da177e4SLinus Torvalds /* CAUTION: call it with irq disabled */ 231877211f5STakashi Iwai int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) 2321da177e4SLinus Torvalds { 233877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 2341da177e4SLinus Torvalds snd_pcm_uframes_t pos; 2351da177e4SLinus Torvalds snd_pcm_uframes_t old_hw_ptr, new_hw_ptr; 2361da177e4SLinus Torvalds snd_pcm_sframes_t delta; 2371da177e4SLinus Torvalds 2381da177e4SLinus Torvalds old_hw_ptr = runtime->status->hw_ptr; 2391da177e4SLinus Torvalds pos = snd_pcm_update_hw_ptr_pos(substream, runtime); 2401da177e4SLinus Torvalds if (pos == SNDRV_PCM_POS_XRUN) { 2411da177e4SLinus Torvalds xrun(substream); 2421da177e4SLinus Torvalds return -EPIPE; 2431da177e4SLinus Torvalds } 2441da177e4SLinus Torvalds new_hw_ptr = runtime->hw_ptr_base + pos; 2451da177e4SLinus Torvalds 2461da177e4SLinus Torvalds delta = old_hw_ptr - new_hw_ptr; 2471da177e4SLinus Torvalds if (delta > 0) { 2481da177e4SLinus Torvalds if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) { 24961fb63c0SJaroslav Kysela #ifdef CONFIG_SND_PCM_XRUN_DEBUG 2501da177e4SLinus Torvalds if (runtime->periods > 2 && substream->pstr->xrun_debug) { 2511da177e4SLinus Torvalds snd_printd(KERN_ERR "Unexpected hw_pointer value [2] (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2); 2521da177e4SLinus Torvalds if (substream->pstr->xrun_debug > 1) 2531da177e4SLinus Torvalds dump_stack(); 2541da177e4SLinus Torvalds } 2551da177e4SLinus Torvalds #endif 2561da177e4SLinus Torvalds return 0; 2571da177e4SLinus Torvalds } 2581da177e4SLinus Torvalds runtime->hw_ptr_base += runtime->buffer_size; 2591da177e4SLinus Torvalds if (runtime->hw_ptr_base == runtime->boundary) 2601da177e4SLinus Torvalds runtime->hw_ptr_base = 0; 2611da177e4SLinus Torvalds new_hw_ptr = runtime->hw_ptr_base + pos; 2621da177e4SLinus Torvalds } 2631da177e4SLinus Torvalds if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && 2641da177e4SLinus Torvalds runtime->silence_size > 0) 2651da177e4SLinus Torvalds snd_pcm_playback_silence(substream, new_hw_ptr); 2661da177e4SLinus Torvalds 2671da177e4SLinus Torvalds runtime->status->hw_ptr = new_hw_ptr; 2681da177e4SLinus Torvalds 2691da177e4SLinus Torvalds return snd_pcm_update_hw_ptr_post(substream, runtime); 2701da177e4SLinus Torvalds } 2711da177e4SLinus Torvalds 2721da177e4SLinus Torvalds /** 2731da177e4SLinus Torvalds * snd_pcm_set_ops - set the PCM operators 2741da177e4SLinus Torvalds * @pcm: the pcm instance 2751da177e4SLinus Torvalds * @direction: stream direction, SNDRV_PCM_STREAM_XXX 2761da177e4SLinus Torvalds * @ops: the operator table 2771da177e4SLinus Torvalds * 2781da177e4SLinus Torvalds * Sets the given PCM operators to the pcm instance. 2791da177e4SLinus Torvalds */ 280877211f5STakashi Iwai void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, struct snd_pcm_ops *ops) 2811da177e4SLinus Torvalds { 282877211f5STakashi Iwai struct snd_pcm_str *stream = &pcm->streams[direction]; 283877211f5STakashi Iwai struct snd_pcm_substream *substream; 2841da177e4SLinus Torvalds 2851da177e4SLinus Torvalds for (substream = stream->substream; substream != NULL; substream = substream->next) 2861da177e4SLinus Torvalds substream->ops = ops; 2871da177e4SLinus Torvalds } 2881da177e4SLinus Torvalds 289e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_set_ops); 2901da177e4SLinus Torvalds 2911da177e4SLinus Torvalds /** 2921da177e4SLinus Torvalds * snd_pcm_sync - set the PCM sync id 2931da177e4SLinus Torvalds * @substream: the pcm substream 2941da177e4SLinus Torvalds * 2951da177e4SLinus Torvalds * Sets the PCM sync identifier for the card. 2961da177e4SLinus Torvalds */ 297877211f5STakashi Iwai void snd_pcm_set_sync(struct snd_pcm_substream *substream) 2981da177e4SLinus Torvalds { 299877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 3001da177e4SLinus Torvalds 3011da177e4SLinus Torvalds runtime->sync.id32[0] = substream->pcm->card->number; 3021da177e4SLinus Torvalds runtime->sync.id32[1] = -1; 3031da177e4SLinus Torvalds runtime->sync.id32[2] = -1; 3041da177e4SLinus Torvalds runtime->sync.id32[3] = -1; 3051da177e4SLinus Torvalds } 3061da177e4SLinus Torvalds 307e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_set_sync); 308e88e8ae6STakashi Iwai 3091da177e4SLinus Torvalds /* 3101da177e4SLinus Torvalds * Standard ioctl routine 3111da177e4SLinus Torvalds */ 3121da177e4SLinus Torvalds 3131da177e4SLinus Torvalds static inline unsigned int div32(unsigned int a, unsigned int b, 3141da177e4SLinus Torvalds unsigned int *r) 3151da177e4SLinus Torvalds { 3161da177e4SLinus Torvalds if (b == 0) { 3171da177e4SLinus Torvalds *r = 0; 3181da177e4SLinus Torvalds return UINT_MAX; 3191da177e4SLinus Torvalds } 3201da177e4SLinus Torvalds *r = a % b; 3211da177e4SLinus Torvalds return a / b; 3221da177e4SLinus Torvalds } 3231da177e4SLinus Torvalds 3241da177e4SLinus Torvalds static inline unsigned int div_down(unsigned int a, unsigned int b) 3251da177e4SLinus Torvalds { 3261da177e4SLinus Torvalds if (b == 0) 3271da177e4SLinus Torvalds return UINT_MAX; 3281da177e4SLinus Torvalds return a / b; 3291da177e4SLinus Torvalds } 3301da177e4SLinus Torvalds 3311da177e4SLinus Torvalds static inline unsigned int div_up(unsigned int a, unsigned int b) 3321da177e4SLinus Torvalds { 3331da177e4SLinus Torvalds unsigned int r; 3341da177e4SLinus Torvalds unsigned int q; 3351da177e4SLinus Torvalds if (b == 0) 3361da177e4SLinus Torvalds return UINT_MAX; 3371da177e4SLinus Torvalds q = div32(a, b, &r); 3381da177e4SLinus Torvalds if (r) 3391da177e4SLinus Torvalds ++q; 3401da177e4SLinus Torvalds return q; 3411da177e4SLinus Torvalds } 3421da177e4SLinus Torvalds 3431da177e4SLinus Torvalds static inline unsigned int mul(unsigned int a, unsigned int b) 3441da177e4SLinus Torvalds { 3451da177e4SLinus Torvalds if (a == 0) 3461da177e4SLinus Torvalds return 0; 3471da177e4SLinus Torvalds if (div_down(UINT_MAX, a) < b) 3481da177e4SLinus Torvalds return UINT_MAX; 3491da177e4SLinus Torvalds return a * b; 3501da177e4SLinus Torvalds } 3511da177e4SLinus Torvalds 3521da177e4SLinus Torvalds static inline unsigned int muldiv32(unsigned int a, unsigned int b, 3531da177e4SLinus Torvalds unsigned int c, unsigned int *r) 3541da177e4SLinus Torvalds { 3551da177e4SLinus Torvalds u_int64_t n = (u_int64_t) a * b; 3561da177e4SLinus Torvalds if (c == 0) { 3571da177e4SLinus Torvalds snd_assert(n > 0, ); 3581da177e4SLinus Torvalds *r = 0; 3591da177e4SLinus Torvalds return UINT_MAX; 3601da177e4SLinus Torvalds } 3611da177e4SLinus Torvalds div64_32(&n, c, r); 3621da177e4SLinus Torvalds if (n >= UINT_MAX) { 3631da177e4SLinus Torvalds *r = 0; 3641da177e4SLinus Torvalds return UINT_MAX; 3651da177e4SLinus Torvalds } 3661da177e4SLinus Torvalds return n; 3671da177e4SLinus Torvalds } 3681da177e4SLinus Torvalds 3691da177e4SLinus Torvalds /** 3701da177e4SLinus Torvalds * snd_interval_refine - refine the interval value of configurator 3711da177e4SLinus Torvalds * @i: the interval value to refine 3721da177e4SLinus Torvalds * @v: the interval value to refer to 3731da177e4SLinus Torvalds * 3741da177e4SLinus Torvalds * Refines the interval value with the reference value. 3751da177e4SLinus Torvalds * The interval is changed to the range satisfying both intervals. 3761da177e4SLinus Torvalds * The interval status (min, max, integer, etc.) are evaluated. 3771da177e4SLinus Torvalds * 3781da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 3791da177e4SLinus Torvalds */ 380877211f5STakashi Iwai int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v) 3811da177e4SLinus Torvalds { 3821da177e4SLinus Torvalds int changed = 0; 383e88e8ae6STakashi Iwai snd_assert(!snd_interval_empty(i), return -EINVAL); 3841da177e4SLinus Torvalds if (i->min < v->min) { 3851da177e4SLinus Torvalds i->min = v->min; 3861da177e4SLinus Torvalds i->openmin = v->openmin; 3871da177e4SLinus Torvalds changed = 1; 3881da177e4SLinus Torvalds } else if (i->min == v->min && !i->openmin && v->openmin) { 3891da177e4SLinus Torvalds i->openmin = 1; 3901da177e4SLinus Torvalds changed = 1; 3911da177e4SLinus Torvalds } 3921da177e4SLinus Torvalds if (i->max > v->max) { 3931da177e4SLinus Torvalds i->max = v->max; 3941da177e4SLinus Torvalds i->openmax = v->openmax; 3951da177e4SLinus Torvalds changed = 1; 3961da177e4SLinus Torvalds } else if (i->max == v->max && !i->openmax && v->openmax) { 3971da177e4SLinus Torvalds i->openmax = 1; 3981da177e4SLinus Torvalds changed = 1; 3991da177e4SLinus Torvalds } 4001da177e4SLinus Torvalds if (!i->integer && v->integer) { 4011da177e4SLinus Torvalds i->integer = 1; 4021da177e4SLinus Torvalds changed = 1; 4031da177e4SLinus Torvalds } 4041da177e4SLinus Torvalds if (i->integer) { 4051da177e4SLinus Torvalds if (i->openmin) { 4061da177e4SLinus Torvalds i->min++; 4071da177e4SLinus Torvalds i->openmin = 0; 4081da177e4SLinus Torvalds } 4091da177e4SLinus Torvalds if (i->openmax) { 4101da177e4SLinus Torvalds i->max--; 4111da177e4SLinus Torvalds i->openmax = 0; 4121da177e4SLinus Torvalds } 4131da177e4SLinus Torvalds } else if (!i->openmin && !i->openmax && i->min == i->max) 4141da177e4SLinus Torvalds i->integer = 1; 4151da177e4SLinus Torvalds if (snd_interval_checkempty(i)) { 4161da177e4SLinus Torvalds snd_interval_none(i); 4171da177e4SLinus Torvalds return -EINVAL; 4181da177e4SLinus Torvalds } 4191da177e4SLinus Torvalds return changed; 4201da177e4SLinus Torvalds } 4211da177e4SLinus Torvalds 422e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_interval_refine); 423e88e8ae6STakashi Iwai 424877211f5STakashi Iwai static int snd_interval_refine_first(struct snd_interval *i) 4251da177e4SLinus Torvalds { 426e88e8ae6STakashi Iwai snd_assert(!snd_interval_empty(i), return -EINVAL); 4271da177e4SLinus Torvalds if (snd_interval_single(i)) 4281da177e4SLinus Torvalds return 0; 4291da177e4SLinus Torvalds i->max = i->min; 4301da177e4SLinus Torvalds i->openmax = i->openmin; 4311da177e4SLinus Torvalds if (i->openmax) 4321da177e4SLinus Torvalds i->max++; 4331da177e4SLinus Torvalds return 1; 4341da177e4SLinus Torvalds } 4351da177e4SLinus Torvalds 436877211f5STakashi Iwai static int snd_interval_refine_last(struct snd_interval *i) 4371da177e4SLinus Torvalds { 438e88e8ae6STakashi Iwai snd_assert(!snd_interval_empty(i), return -EINVAL); 4391da177e4SLinus Torvalds if (snd_interval_single(i)) 4401da177e4SLinus Torvalds return 0; 4411da177e4SLinus Torvalds i->min = i->max; 4421da177e4SLinus Torvalds i->openmin = i->openmax; 4431da177e4SLinus Torvalds if (i->openmin) 4441da177e4SLinus Torvalds i->min--; 4451da177e4SLinus Torvalds return 1; 4461da177e4SLinus Torvalds } 4471da177e4SLinus Torvalds 448877211f5STakashi Iwai void snd_interval_mul(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c) 4491da177e4SLinus Torvalds { 4501da177e4SLinus Torvalds if (a->empty || b->empty) { 4511da177e4SLinus Torvalds snd_interval_none(c); 4521da177e4SLinus Torvalds return; 4531da177e4SLinus Torvalds } 4541da177e4SLinus Torvalds c->empty = 0; 4551da177e4SLinus Torvalds c->min = mul(a->min, b->min); 4561da177e4SLinus Torvalds c->openmin = (a->openmin || b->openmin); 4571da177e4SLinus Torvalds c->max = mul(a->max, b->max); 4581da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmax); 4591da177e4SLinus Torvalds c->integer = (a->integer && b->integer); 4601da177e4SLinus Torvalds } 4611da177e4SLinus Torvalds 4621da177e4SLinus Torvalds /** 4631da177e4SLinus Torvalds * snd_interval_div - refine the interval value with division 464df8db936STakashi Iwai * @a: dividend 465df8db936STakashi Iwai * @b: divisor 466df8db936STakashi Iwai * @c: quotient 4671da177e4SLinus Torvalds * 4681da177e4SLinus Torvalds * c = a / b 4691da177e4SLinus Torvalds * 4701da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 4711da177e4SLinus Torvalds */ 472877211f5STakashi Iwai void snd_interval_div(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c) 4731da177e4SLinus Torvalds { 4741da177e4SLinus Torvalds unsigned int r; 4751da177e4SLinus Torvalds if (a->empty || b->empty) { 4761da177e4SLinus Torvalds snd_interval_none(c); 4771da177e4SLinus Torvalds return; 4781da177e4SLinus Torvalds } 4791da177e4SLinus Torvalds c->empty = 0; 4801da177e4SLinus Torvalds c->min = div32(a->min, b->max, &r); 4811da177e4SLinus Torvalds c->openmin = (r || a->openmin || b->openmax); 4821da177e4SLinus Torvalds if (b->min > 0) { 4831da177e4SLinus Torvalds c->max = div32(a->max, b->min, &r); 4841da177e4SLinus Torvalds if (r) { 4851da177e4SLinus Torvalds c->max++; 4861da177e4SLinus Torvalds c->openmax = 1; 4871da177e4SLinus Torvalds } else 4881da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmin); 4891da177e4SLinus Torvalds } else { 4901da177e4SLinus Torvalds c->max = UINT_MAX; 4911da177e4SLinus Torvalds c->openmax = 0; 4921da177e4SLinus Torvalds } 4931da177e4SLinus Torvalds c->integer = 0; 4941da177e4SLinus Torvalds } 4951da177e4SLinus Torvalds 4961da177e4SLinus Torvalds /** 4971da177e4SLinus Torvalds * snd_interval_muldivk - refine the interval value 498df8db936STakashi Iwai * @a: dividend 1 499df8db936STakashi Iwai * @b: dividend 2 500df8db936STakashi Iwai * @k: divisor (as integer) 501df8db936STakashi Iwai * @c: result 5021da177e4SLinus Torvalds * 5031da177e4SLinus Torvalds * c = a * b / k 5041da177e4SLinus Torvalds * 5051da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 5061da177e4SLinus Torvalds */ 507877211f5STakashi Iwai void snd_interval_muldivk(const struct snd_interval *a, const struct snd_interval *b, 508877211f5STakashi Iwai unsigned int k, struct snd_interval *c) 5091da177e4SLinus Torvalds { 5101da177e4SLinus Torvalds unsigned int r; 5111da177e4SLinus Torvalds if (a->empty || b->empty) { 5121da177e4SLinus Torvalds snd_interval_none(c); 5131da177e4SLinus Torvalds return; 5141da177e4SLinus Torvalds } 5151da177e4SLinus Torvalds c->empty = 0; 5161da177e4SLinus Torvalds c->min = muldiv32(a->min, b->min, k, &r); 5171da177e4SLinus Torvalds c->openmin = (r || a->openmin || b->openmin); 5181da177e4SLinus Torvalds c->max = muldiv32(a->max, b->max, k, &r); 5191da177e4SLinus Torvalds if (r) { 5201da177e4SLinus Torvalds c->max++; 5211da177e4SLinus Torvalds c->openmax = 1; 5221da177e4SLinus Torvalds } else 5231da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmax); 5241da177e4SLinus Torvalds c->integer = 0; 5251da177e4SLinus Torvalds } 5261da177e4SLinus Torvalds 5271da177e4SLinus Torvalds /** 5281da177e4SLinus Torvalds * snd_interval_mulkdiv - refine the interval value 529df8db936STakashi Iwai * @a: dividend 1 530df8db936STakashi Iwai * @k: dividend 2 (as integer) 531df8db936STakashi Iwai * @b: divisor 532df8db936STakashi Iwai * @c: result 5331da177e4SLinus Torvalds * 5341da177e4SLinus Torvalds * c = a * k / b 5351da177e4SLinus Torvalds * 5361da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 5371da177e4SLinus Torvalds */ 538877211f5STakashi Iwai void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k, 539877211f5STakashi Iwai const struct snd_interval *b, struct snd_interval *c) 5401da177e4SLinus Torvalds { 5411da177e4SLinus Torvalds unsigned int r; 5421da177e4SLinus Torvalds if (a->empty || b->empty) { 5431da177e4SLinus Torvalds snd_interval_none(c); 5441da177e4SLinus Torvalds return; 5451da177e4SLinus Torvalds } 5461da177e4SLinus Torvalds c->empty = 0; 5471da177e4SLinus Torvalds c->min = muldiv32(a->min, k, b->max, &r); 5481da177e4SLinus Torvalds c->openmin = (r || a->openmin || b->openmax); 5491da177e4SLinus Torvalds if (b->min > 0) { 5501da177e4SLinus Torvalds c->max = muldiv32(a->max, k, b->min, &r); 5511da177e4SLinus Torvalds if (r) { 5521da177e4SLinus Torvalds c->max++; 5531da177e4SLinus Torvalds c->openmax = 1; 5541da177e4SLinus Torvalds } else 5551da177e4SLinus Torvalds c->openmax = (a->openmax || b->openmin); 5561da177e4SLinus Torvalds } else { 5571da177e4SLinus Torvalds c->max = UINT_MAX; 5581da177e4SLinus Torvalds c->openmax = 0; 5591da177e4SLinus Torvalds } 5601da177e4SLinus Torvalds c->integer = 0; 5611da177e4SLinus Torvalds } 5621da177e4SLinus Torvalds 5631da177e4SLinus Torvalds /* ---- */ 5641da177e4SLinus Torvalds 5651da177e4SLinus Torvalds 5661da177e4SLinus Torvalds /** 5671da177e4SLinus Torvalds * snd_interval_ratnum - refine the interval value 568df8db936STakashi Iwai * @i: interval to refine 569df8db936STakashi Iwai * @rats_count: number of ratnum_t 570df8db936STakashi Iwai * @rats: ratnum_t array 571df8db936STakashi Iwai * @nump: pointer to store the resultant numerator 572df8db936STakashi Iwai * @denp: pointer to store the resultant denominator 5731da177e4SLinus Torvalds * 5741da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 5751da177e4SLinus Torvalds */ 576877211f5STakashi Iwai int snd_interval_ratnum(struct snd_interval *i, 577877211f5STakashi Iwai unsigned int rats_count, struct snd_ratnum *rats, 5781da177e4SLinus Torvalds unsigned int *nump, unsigned int *denp) 5791da177e4SLinus Torvalds { 5801da177e4SLinus Torvalds unsigned int best_num, best_diff, best_den; 5811da177e4SLinus Torvalds unsigned int k; 582877211f5STakashi Iwai struct snd_interval t; 5831da177e4SLinus Torvalds int err; 5841da177e4SLinus Torvalds 5851da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 5861da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 5871da177e4SLinus Torvalds unsigned int num = rats[k].num; 5881da177e4SLinus Torvalds unsigned int den; 5891da177e4SLinus Torvalds unsigned int q = i->min; 5901da177e4SLinus Torvalds int diff; 5911da177e4SLinus Torvalds if (q == 0) 5921da177e4SLinus Torvalds q = 1; 5931da177e4SLinus Torvalds den = div_down(num, q); 5941da177e4SLinus Torvalds if (den < rats[k].den_min) 5951da177e4SLinus Torvalds continue; 5961da177e4SLinus Torvalds if (den > rats[k].den_max) 5971da177e4SLinus Torvalds den = rats[k].den_max; 5981da177e4SLinus Torvalds else { 5991da177e4SLinus Torvalds unsigned int r; 6001da177e4SLinus Torvalds r = (den - rats[k].den_min) % rats[k].den_step; 6011da177e4SLinus Torvalds if (r != 0) 6021da177e4SLinus Torvalds den -= r; 6031da177e4SLinus Torvalds } 6041da177e4SLinus Torvalds diff = num - q * den; 6051da177e4SLinus Torvalds if (best_num == 0 || 6061da177e4SLinus Torvalds diff * best_den < best_diff * den) { 6071da177e4SLinus Torvalds best_diff = diff; 6081da177e4SLinus Torvalds best_den = den; 6091da177e4SLinus Torvalds best_num = num; 6101da177e4SLinus Torvalds } 6111da177e4SLinus Torvalds } 6121da177e4SLinus Torvalds if (best_den == 0) { 6131da177e4SLinus Torvalds i->empty = 1; 6141da177e4SLinus Torvalds return -EINVAL; 6151da177e4SLinus Torvalds } 6161da177e4SLinus Torvalds t.min = div_down(best_num, best_den); 6171da177e4SLinus Torvalds t.openmin = !!(best_num % best_den); 6181da177e4SLinus Torvalds 6191da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 6201da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 6211da177e4SLinus Torvalds unsigned int num = rats[k].num; 6221da177e4SLinus Torvalds unsigned int den; 6231da177e4SLinus Torvalds unsigned int q = i->max; 6241da177e4SLinus Torvalds int diff; 6251da177e4SLinus Torvalds if (q == 0) { 6261da177e4SLinus Torvalds i->empty = 1; 6271da177e4SLinus Torvalds return -EINVAL; 6281da177e4SLinus Torvalds } 6291da177e4SLinus Torvalds den = div_up(num, q); 6301da177e4SLinus Torvalds if (den > rats[k].den_max) 6311da177e4SLinus Torvalds continue; 6321da177e4SLinus Torvalds if (den < rats[k].den_min) 6331da177e4SLinus Torvalds den = rats[k].den_min; 6341da177e4SLinus Torvalds else { 6351da177e4SLinus Torvalds unsigned int r; 6361da177e4SLinus Torvalds r = (den - rats[k].den_min) % rats[k].den_step; 6371da177e4SLinus Torvalds if (r != 0) 6381da177e4SLinus Torvalds den += rats[k].den_step - r; 6391da177e4SLinus Torvalds } 6401da177e4SLinus Torvalds diff = q * den - num; 6411da177e4SLinus Torvalds if (best_num == 0 || 6421da177e4SLinus Torvalds diff * best_den < best_diff * den) { 6431da177e4SLinus Torvalds best_diff = diff; 6441da177e4SLinus Torvalds best_den = den; 6451da177e4SLinus Torvalds best_num = num; 6461da177e4SLinus Torvalds } 6471da177e4SLinus Torvalds } 6481da177e4SLinus Torvalds if (best_den == 0) { 6491da177e4SLinus Torvalds i->empty = 1; 6501da177e4SLinus Torvalds return -EINVAL; 6511da177e4SLinus Torvalds } 6521da177e4SLinus Torvalds t.max = div_up(best_num, best_den); 6531da177e4SLinus Torvalds t.openmax = !!(best_num % best_den); 6541da177e4SLinus Torvalds t.integer = 0; 6551da177e4SLinus Torvalds err = snd_interval_refine(i, &t); 6561da177e4SLinus Torvalds if (err < 0) 6571da177e4SLinus Torvalds return err; 6581da177e4SLinus Torvalds 6591da177e4SLinus Torvalds if (snd_interval_single(i)) { 6601da177e4SLinus Torvalds if (nump) 6611da177e4SLinus Torvalds *nump = best_num; 6621da177e4SLinus Torvalds if (denp) 6631da177e4SLinus Torvalds *denp = best_den; 6641da177e4SLinus Torvalds } 6651da177e4SLinus Torvalds return err; 6661da177e4SLinus Torvalds } 6671da177e4SLinus Torvalds 668e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_interval_ratnum); 669e88e8ae6STakashi Iwai 6701da177e4SLinus Torvalds /** 6711da177e4SLinus Torvalds * snd_interval_ratden - refine the interval value 672df8db936STakashi Iwai * @i: interval to refine 673877211f5STakashi Iwai * @rats_count: number of struct ratden 674877211f5STakashi Iwai * @rats: struct ratden array 675df8db936STakashi Iwai * @nump: pointer to store the resultant numerator 676df8db936STakashi Iwai * @denp: pointer to store the resultant denominator 6771da177e4SLinus Torvalds * 6781da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 6791da177e4SLinus Torvalds */ 680877211f5STakashi Iwai static int snd_interval_ratden(struct snd_interval *i, 681877211f5STakashi Iwai unsigned int rats_count, struct snd_ratden *rats, 6821da177e4SLinus Torvalds unsigned int *nump, unsigned int *denp) 6831da177e4SLinus Torvalds { 6841da177e4SLinus Torvalds unsigned int best_num, best_diff, best_den; 6851da177e4SLinus Torvalds unsigned int k; 686877211f5STakashi Iwai struct snd_interval t; 6871da177e4SLinus Torvalds int err; 6881da177e4SLinus Torvalds 6891da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 6901da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 6911da177e4SLinus Torvalds unsigned int num; 6921da177e4SLinus Torvalds unsigned int den = rats[k].den; 6931da177e4SLinus Torvalds unsigned int q = i->min; 6941da177e4SLinus Torvalds int diff; 6951da177e4SLinus Torvalds num = mul(q, den); 6961da177e4SLinus Torvalds if (num > rats[k].num_max) 6971da177e4SLinus Torvalds continue; 6981da177e4SLinus Torvalds if (num < rats[k].num_min) 6991da177e4SLinus Torvalds num = rats[k].num_max; 7001da177e4SLinus Torvalds else { 7011da177e4SLinus Torvalds unsigned int r; 7021da177e4SLinus Torvalds r = (num - rats[k].num_min) % rats[k].num_step; 7031da177e4SLinus Torvalds if (r != 0) 7041da177e4SLinus Torvalds num += rats[k].num_step - r; 7051da177e4SLinus Torvalds } 7061da177e4SLinus Torvalds diff = num - q * den; 7071da177e4SLinus Torvalds if (best_num == 0 || 7081da177e4SLinus Torvalds diff * best_den < best_diff * den) { 7091da177e4SLinus Torvalds best_diff = diff; 7101da177e4SLinus Torvalds best_den = den; 7111da177e4SLinus Torvalds best_num = num; 7121da177e4SLinus Torvalds } 7131da177e4SLinus Torvalds } 7141da177e4SLinus Torvalds if (best_den == 0) { 7151da177e4SLinus Torvalds i->empty = 1; 7161da177e4SLinus Torvalds return -EINVAL; 7171da177e4SLinus Torvalds } 7181da177e4SLinus Torvalds t.min = div_down(best_num, best_den); 7191da177e4SLinus Torvalds t.openmin = !!(best_num % best_den); 7201da177e4SLinus Torvalds 7211da177e4SLinus Torvalds best_num = best_den = best_diff = 0; 7221da177e4SLinus Torvalds for (k = 0; k < rats_count; ++k) { 7231da177e4SLinus Torvalds unsigned int num; 7241da177e4SLinus Torvalds unsigned int den = rats[k].den; 7251da177e4SLinus Torvalds unsigned int q = i->max; 7261da177e4SLinus Torvalds int diff; 7271da177e4SLinus Torvalds num = mul(q, den); 7281da177e4SLinus Torvalds if (num < rats[k].num_min) 7291da177e4SLinus Torvalds continue; 7301da177e4SLinus Torvalds if (num > rats[k].num_max) 7311da177e4SLinus Torvalds num = rats[k].num_max; 7321da177e4SLinus Torvalds else { 7331da177e4SLinus Torvalds unsigned int r; 7341da177e4SLinus Torvalds r = (num - rats[k].num_min) % rats[k].num_step; 7351da177e4SLinus Torvalds if (r != 0) 7361da177e4SLinus Torvalds num -= r; 7371da177e4SLinus Torvalds } 7381da177e4SLinus Torvalds diff = q * den - num; 7391da177e4SLinus Torvalds if (best_num == 0 || 7401da177e4SLinus Torvalds diff * best_den < best_diff * den) { 7411da177e4SLinus Torvalds best_diff = diff; 7421da177e4SLinus Torvalds best_den = den; 7431da177e4SLinus Torvalds best_num = num; 7441da177e4SLinus Torvalds } 7451da177e4SLinus Torvalds } 7461da177e4SLinus Torvalds if (best_den == 0) { 7471da177e4SLinus Torvalds i->empty = 1; 7481da177e4SLinus Torvalds return -EINVAL; 7491da177e4SLinus Torvalds } 7501da177e4SLinus Torvalds t.max = div_up(best_num, best_den); 7511da177e4SLinus Torvalds t.openmax = !!(best_num % best_den); 7521da177e4SLinus Torvalds t.integer = 0; 7531da177e4SLinus Torvalds err = snd_interval_refine(i, &t); 7541da177e4SLinus Torvalds if (err < 0) 7551da177e4SLinus Torvalds return err; 7561da177e4SLinus Torvalds 7571da177e4SLinus Torvalds if (snd_interval_single(i)) { 7581da177e4SLinus Torvalds if (nump) 7591da177e4SLinus Torvalds *nump = best_num; 7601da177e4SLinus Torvalds if (denp) 7611da177e4SLinus Torvalds *denp = best_den; 7621da177e4SLinus Torvalds } 7631da177e4SLinus Torvalds return err; 7641da177e4SLinus Torvalds } 7651da177e4SLinus Torvalds 7661da177e4SLinus Torvalds /** 7671da177e4SLinus Torvalds * snd_interval_list - refine the interval value from the list 7681da177e4SLinus Torvalds * @i: the interval value to refine 7691da177e4SLinus Torvalds * @count: the number of elements in the list 7701da177e4SLinus Torvalds * @list: the value list 7711da177e4SLinus Torvalds * @mask: the bit-mask to evaluate 7721da177e4SLinus Torvalds * 7731da177e4SLinus Torvalds * Refines the interval value from the list. 7741da177e4SLinus Torvalds * When mask is non-zero, only the elements corresponding to bit 1 are 7751da177e4SLinus Torvalds * evaluated. 7761da177e4SLinus Torvalds * 7771da177e4SLinus Torvalds * Returns non-zero if the value is changed, zero if not changed. 7781da177e4SLinus Torvalds */ 779877211f5STakashi Iwai int snd_interval_list(struct snd_interval *i, unsigned int count, unsigned int *list, unsigned int mask) 7801da177e4SLinus Torvalds { 7811da177e4SLinus Torvalds unsigned int k; 7821da177e4SLinus Torvalds int changed = 0; 7830981a260STakashi Iwai 7840981a260STakashi Iwai if (!count) { 7850981a260STakashi Iwai i->empty = 1; 7860981a260STakashi Iwai return -EINVAL; 7870981a260STakashi Iwai } 7881da177e4SLinus Torvalds for (k = 0; k < count; k++) { 7891da177e4SLinus Torvalds if (mask && !(mask & (1 << k))) 7901da177e4SLinus Torvalds continue; 7911da177e4SLinus Torvalds if (i->min == list[k] && !i->openmin) 7921da177e4SLinus Torvalds goto _l1; 7931da177e4SLinus Torvalds if (i->min < list[k]) { 7941da177e4SLinus Torvalds i->min = list[k]; 7951da177e4SLinus Torvalds i->openmin = 0; 7961da177e4SLinus Torvalds changed = 1; 7971da177e4SLinus Torvalds goto _l1; 7981da177e4SLinus Torvalds } 7991da177e4SLinus Torvalds } 8001da177e4SLinus Torvalds i->empty = 1; 8011da177e4SLinus Torvalds return -EINVAL; 8021da177e4SLinus Torvalds _l1: 8031da177e4SLinus Torvalds for (k = count; k-- > 0;) { 8041da177e4SLinus Torvalds if (mask && !(mask & (1 << k))) 8051da177e4SLinus Torvalds continue; 8061da177e4SLinus Torvalds if (i->max == list[k] && !i->openmax) 8071da177e4SLinus Torvalds goto _l2; 8081da177e4SLinus Torvalds if (i->max > list[k]) { 8091da177e4SLinus Torvalds i->max = list[k]; 8101da177e4SLinus Torvalds i->openmax = 0; 8111da177e4SLinus Torvalds changed = 1; 8121da177e4SLinus Torvalds goto _l2; 8131da177e4SLinus Torvalds } 8141da177e4SLinus Torvalds } 8151da177e4SLinus Torvalds i->empty = 1; 8161da177e4SLinus Torvalds return -EINVAL; 8171da177e4SLinus Torvalds _l2: 8181da177e4SLinus Torvalds if (snd_interval_checkempty(i)) { 8191da177e4SLinus Torvalds i->empty = 1; 8201da177e4SLinus Torvalds return -EINVAL; 8211da177e4SLinus Torvalds } 8221da177e4SLinus Torvalds return changed; 8231da177e4SLinus Torvalds } 8241da177e4SLinus Torvalds 825e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_interval_list); 826e88e8ae6STakashi Iwai 827877211f5STakashi Iwai static int snd_interval_step(struct snd_interval *i, unsigned int min, unsigned int step) 8281da177e4SLinus Torvalds { 8291da177e4SLinus Torvalds unsigned int n; 8301da177e4SLinus Torvalds int changed = 0; 8311da177e4SLinus Torvalds n = (i->min - min) % step; 8321da177e4SLinus Torvalds if (n != 0 || i->openmin) { 8331da177e4SLinus Torvalds i->min += step - n; 8341da177e4SLinus Torvalds changed = 1; 8351da177e4SLinus Torvalds } 8361da177e4SLinus Torvalds n = (i->max - min) % step; 8371da177e4SLinus Torvalds if (n != 0 || i->openmax) { 8381da177e4SLinus Torvalds i->max -= n; 8391da177e4SLinus Torvalds changed = 1; 8401da177e4SLinus Torvalds } 8411da177e4SLinus Torvalds if (snd_interval_checkempty(i)) { 8421da177e4SLinus Torvalds i->empty = 1; 8431da177e4SLinus Torvalds return -EINVAL; 8441da177e4SLinus Torvalds } 8451da177e4SLinus Torvalds return changed; 8461da177e4SLinus Torvalds } 8471da177e4SLinus Torvalds 8481da177e4SLinus Torvalds /* Info constraints helpers */ 8491da177e4SLinus Torvalds 8501da177e4SLinus Torvalds /** 8511da177e4SLinus Torvalds * snd_pcm_hw_rule_add - add the hw-constraint rule 8521da177e4SLinus Torvalds * @runtime: the pcm runtime instance 8531da177e4SLinus Torvalds * @cond: condition bits 8541da177e4SLinus Torvalds * @var: the variable to evaluate 8551da177e4SLinus Torvalds * @func: the evaluation function 8561da177e4SLinus Torvalds * @private: the private data pointer passed to function 8571da177e4SLinus Torvalds * @dep: the dependent variables 8581da177e4SLinus Torvalds * 8591da177e4SLinus Torvalds * Returns zero if successful, or a negative error code on failure. 8601da177e4SLinus Torvalds */ 861877211f5STakashi Iwai int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond, 8621da177e4SLinus Torvalds int var, 8631da177e4SLinus Torvalds snd_pcm_hw_rule_func_t func, void *private, 8641da177e4SLinus Torvalds int dep, ...) 8651da177e4SLinus Torvalds { 866877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 867877211f5STakashi Iwai struct snd_pcm_hw_rule *c; 8681da177e4SLinus Torvalds unsigned int k; 8691da177e4SLinus Torvalds va_list args; 8701da177e4SLinus Torvalds va_start(args, dep); 8711da177e4SLinus Torvalds if (constrs->rules_num >= constrs->rules_all) { 872877211f5STakashi Iwai struct snd_pcm_hw_rule *new; 8731da177e4SLinus Torvalds unsigned int new_rules = constrs->rules_all + 16; 8741da177e4SLinus Torvalds new = kcalloc(new_rules, sizeof(*c), GFP_KERNEL); 8751da177e4SLinus Torvalds if (!new) 8761da177e4SLinus Torvalds return -ENOMEM; 8771da177e4SLinus Torvalds if (constrs->rules) { 8781da177e4SLinus Torvalds memcpy(new, constrs->rules, 8791da177e4SLinus Torvalds constrs->rules_num * sizeof(*c)); 8801da177e4SLinus Torvalds kfree(constrs->rules); 8811da177e4SLinus Torvalds } 8821da177e4SLinus Torvalds constrs->rules = new; 8831da177e4SLinus Torvalds constrs->rules_all = new_rules; 8841da177e4SLinus Torvalds } 8851da177e4SLinus Torvalds c = &constrs->rules[constrs->rules_num]; 8861da177e4SLinus Torvalds c->cond = cond; 8871da177e4SLinus Torvalds c->func = func; 8881da177e4SLinus Torvalds c->var = var; 8891da177e4SLinus Torvalds c->private = private; 8901da177e4SLinus Torvalds k = 0; 8911da177e4SLinus Torvalds while (1) { 8921da177e4SLinus Torvalds snd_assert(k < ARRAY_SIZE(c->deps), return -EINVAL); 8931da177e4SLinus Torvalds c->deps[k++] = dep; 8941da177e4SLinus Torvalds if (dep < 0) 8951da177e4SLinus Torvalds break; 8961da177e4SLinus Torvalds dep = va_arg(args, int); 8971da177e4SLinus Torvalds } 8981da177e4SLinus Torvalds constrs->rules_num++; 8991da177e4SLinus Torvalds va_end(args); 9001da177e4SLinus Torvalds return 0; 9011da177e4SLinus Torvalds } 9021da177e4SLinus Torvalds 903e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_rule_add); 904e88e8ae6STakashi Iwai 9051da177e4SLinus Torvalds /** 9061da177e4SLinus Torvalds * snd_pcm_hw_constraint_mask 907df8db936STakashi Iwai * @runtime: PCM runtime instance 908df8db936STakashi Iwai * @var: hw_params variable to apply the mask 909df8db936STakashi Iwai * @mask: the bitmap mask 910df8db936STakashi Iwai * 911df8db936STakashi Iwai * Apply the constraint of the given bitmap mask to a mask parameter. 9121da177e4SLinus Torvalds */ 913877211f5STakashi Iwai int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, 9141da177e4SLinus Torvalds u_int32_t mask) 9151da177e4SLinus Torvalds { 916877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 917877211f5STakashi Iwai struct snd_mask *maskp = constrs_mask(constrs, var); 9181da177e4SLinus Torvalds *maskp->bits &= mask; 9191da177e4SLinus Torvalds memset(maskp->bits + 1, 0, (SNDRV_MASK_MAX-32) / 8); /* clear rest */ 9201da177e4SLinus Torvalds if (*maskp->bits == 0) 9211da177e4SLinus Torvalds return -EINVAL; 9221da177e4SLinus Torvalds return 0; 9231da177e4SLinus Torvalds } 9241da177e4SLinus Torvalds 9251da177e4SLinus Torvalds /** 9261da177e4SLinus Torvalds * snd_pcm_hw_constraint_mask64 927df8db936STakashi Iwai * @runtime: PCM runtime instance 928df8db936STakashi Iwai * @var: hw_params variable to apply the mask 929df8db936STakashi Iwai * @mask: the 64bit bitmap mask 930df8db936STakashi Iwai * 931df8db936STakashi Iwai * Apply the constraint of the given bitmap mask to a mask parameter. 9321da177e4SLinus Torvalds */ 933877211f5STakashi Iwai int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, 9341da177e4SLinus Torvalds u_int64_t mask) 9351da177e4SLinus Torvalds { 936877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 937877211f5STakashi Iwai struct snd_mask *maskp = constrs_mask(constrs, var); 9381da177e4SLinus Torvalds maskp->bits[0] &= (u_int32_t)mask; 9391da177e4SLinus Torvalds maskp->bits[1] &= (u_int32_t)(mask >> 32); 9401da177e4SLinus Torvalds memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */ 9411da177e4SLinus Torvalds if (! maskp->bits[0] && ! maskp->bits[1]) 9421da177e4SLinus Torvalds return -EINVAL; 9431da177e4SLinus Torvalds return 0; 9441da177e4SLinus Torvalds } 9451da177e4SLinus Torvalds 9461da177e4SLinus Torvalds /** 9471da177e4SLinus Torvalds * snd_pcm_hw_constraint_integer 948df8db936STakashi Iwai * @runtime: PCM runtime instance 949df8db936STakashi Iwai * @var: hw_params variable to apply the integer constraint 950df8db936STakashi Iwai * 951df8db936STakashi Iwai * Apply the constraint of integer to an interval parameter. 9521da177e4SLinus Torvalds */ 953877211f5STakashi Iwai int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var) 9541da177e4SLinus Torvalds { 955877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 9561da177e4SLinus Torvalds return snd_interval_setinteger(constrs_interval(constrs, var)); 9571da177e4SLinus Torvalds } 9581da177e4SLinus Torvalds 959e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_integer); 960e88e8ae6STakashi Iwai 9611da177e4SLinus Torvalds /** 9621da177e4SLinus Torvalds * snd_pcm_hw_constraint_minmax 963df8db936STakashi Iwai * @runtime: PCM runtime instance 964df8db936STakashi Iwai * @var: hw_params variable to apply the range 965df8db936STakashi Iwai * @min: the minimal value 966df8db936STakashi Iwai * @max: the maximal value 967df8db936STakashi Iwai * 968df8db936STakashi Iwai * Apply the min/max range constraint to an interval parameter. 9691da177e4SLinus Torvalds */ 970877211f5STakashi Iwai int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, 9711da177e4SLinus Torvalds unsigned int min, unsigned int max) 9721da177e4SLinus Torvalds { 973877211f5STakashi Iwai struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; 974877211f5STakashi Iwai struct snd_interval t; 9751da177e4SLinus Torvalds t.min = min; 9761da177e4SLinus Torvalds t.max = max; 9771da177e4SLinus Torvalds t.openmin = t.openmax = 0; 9781da177e4SLinus Torvalds t.integer = 0; 9791da177e4SLinus Torvalds return snd_interval_refine(constrs_interval(constrs, var), &t); 9801da177e4SLinus Torvalds } 9811da177e4SLinus Torvalds 982e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax); 983e88e8ae6STakashi Iwai 984877211f5STakashi Iwai static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params, 985877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 9861da177e4SLinus Torvalds { 987877211f5STakashi Iwai struct snd_pcm_hw_constraint_list *list = rule->private; 9881da177e4SLinus Torvalds return snd_interval_list(hw_param_interval(params, rule->var), list->count, list->list, list->mask); 9891da177e4SLinus Torvalds } 9901da177e4SLinus Torvalds 9911da177e4SLinus Torvalds 9921da177e4SLinus Torvalds /** 9931da177e4SLinus Torvalds * snd_pcm_hw_constraint_list 994df8db936STakashi Iwai * @runtime: PCM runtime instance 995df8db936STakashi Iwai * @cond: condition bits 996df8db936STakashi Iwai * @var: hw_params variable to apply the list constraint 997df8db936STakashi Iwai * @l: list 998df8db936STakashi Iwai * 999df8db936STakashi Iwai * Apply the list of constraints to an interval parameter. 10001da177e4SLinus Torvalds */ 1001877211f5STakashi Iwai int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, 10021da177e4SLinus Torvalds unsigned int cond, 10031da177e4SLinus Torvalds snd_pcm_hw_param_t var, 1004877211f5STakashi Iwai struct snd_pcm_hw_constraint_list *l) 10051da177e4SLinus Torvalds { 10061da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 10071da177e4SLinus Torvalds snd_pcm_hw_rule_list, l, 10081da177e4SLinus Torvalds var, -1); 10091da177e4SLinus Torvalds } 10101da177e4SLinus Torvalds 1011e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_list); 1012e88e8ae6STakashi Iwai 1013877211f5STakashi Iwai static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params, 1014877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 10151da177e4SLinus Torvalds { 1016877211f5STakashi Iwai struct snd_pcm_hw_constraint_ratnums *r = rule->private; 10171da177e4SLinus Torvalds unsigned int num = 0, den = 0; 10181da177e4SLinus Torvalds int err; 10191da177e4SLinus Torvalds err = snd_interval_ratnum(hw_param_interval(params, rule->var), 10201da177e4SLinus Torvalds r->nrats, r->rats, &num, &den); 10211da177e4SLinus Torvalds if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { 10221da177e4SLinus Torvalds params->rate_num = num; 10231da177e4SLinus Torvalds params->rate_den = den; 10241da177e4SLinus Torvalds } 10251da177e4SLinus Torvalds return err; 10261da177e4SLinus Torvalds } 10271da177e4SLinus Torvalds 10281da177e4SLinus Torvalds /** 10291da177e4SLinus Torvalds * snd_pcm_hw_constraint_ratnums 1030df8db936STakashi Iwai * @runtime: PCM runtime instance 1031df8db936STakashi Iwai * @cond: condition bits 1032df8db936STakashi Iwai * @var: hw_params variable to apply the ratnums constraint 1033877211f5STakashi Iwai * @r: struct snd_ratnums constriants 10341da177e4SLinus Torvalds */ 1035877211f5STakashi Iwai int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, 10361da177e4SLinus Torvalds unsigned int cond, 10371da177e4SLinus Torvalds snd_pcm_hw_param_t var, 1038877211f5STakashi Iwai struct snd_pcm_hw_constraint_ratnums *r) 10391da177e4SLinus Torvalds { 10401da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 10411da177e4SLinus Torvalds snd_pcm_hw_rule_ratnums, r, 10421da177e4SLinus Torvalds var, -1); 10431da177e4SLinus Torvalds } 10441da177e4SLinus Torvalds 1045e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums); 1046e88e8ae6STakashi Iwai 1047877211f5STakashi Iwai static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params, 1048877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 10491da177e4SLinus Torvalds { 1050877211f5STakashi Iwai struct snd_pcm_hw_constraint_ratdens *r = rule->private; 10511da177e4SLinus Torvalds unsigned int num = 0, den = 0; 10521da177e4SLinus Torvalds int err = snd_interval_ratden(hw_param_interval(params, rule->var), 10531da177e4SLinus Torvalds r->nrats, r->rats, &num, &den); 10541da177e4SLinus Torvalds if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { 10551da177e4SLinus Torvalds params->rate_num = num; 10561da177e4SLinus Torvalds params->rate_den = den; 10571da177e4SLinus Torvalds } 10581da177e4SLinus Torvalds return err; 10591da177e4SLinus Torvalds } 10601da177e4SLinus Torvalds 10611da177e4SLinus Torvalds /** 10621da177e4SLinus Torvalds * snd_pcm_hw_constraint_ratdens 1063df8db936STakashi Iwai * @runtime: PCM runtime instance 1064df8db936STakashi Iwai * @cond: condition bits 1065df8db936STakashi Iwai * @var: hw_params variable to apply the ratdens constraint 1066877211f5STakashi Iwai * @r: struct snd_ratdens constriants 10671da177e4SLinus Torvalds */ 1068877211f5STakashi Iwai int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime, 10691da177e4SLinus Torvalds unsigned int cond, 10701da177e4SLinus Torvalds snd_pcm_hw_param_t var, 1071877211f5STakashi Iwai struct snd_pcm_hw_constraint_ratdens *r) 10721da177e4SLinus Torvalds { 10731da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 10741da177e4SLinus Torvalds snd_pcm_hw_rule_ratdens, r, 10751da177e4SLinus Torvalds var, -1); 10761da177e4SLinus Torvalds } 10771da177e4SLinus Torvalds 1078e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens); 1079e88e8ae6STakashi Iwai 1080877211f5STakashi Iwai static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params, 1081877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 10821da177e4SLinus Torvalds { 10831da177e4SLinus Torvalds unsigned int l = (unsigned long) rule->private; 10841da177e4SLinus Torvalds int width = l & 0xffff; 10851da177e4SLinus Torvalds unsigned int msbits = l >> 16; 1086877211f5STakashi Iwai struct snd_interval *i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); 10871da177e4SLinus Torvalds if (snd_interval_single(i) && snd_interval_value(i) == width) 10881da177e4SLinus Torvalds params->msbits = msbits; 10891da177e4SLinus Torvalds return 0; 10901da177e4SLinus Torvalds } 10911da177e4SLinus Torvalds 10921da177e4SLinus Torvalds /** 10931da177e4SLinus Torvalds * snd_pcm_hw_constraint_msbits 1094df8db936STakashi Iwai * @runtime: PCM runtime instance 1095df8db936STakashi Iwai * @cond: condition bits 1096df8db936STakashi Iwai * @width: sample bits width 1097df8db936STakashi Iwai * @msbits: msbits width 10981da177e4SLinus Torvalds */ 1099877211f5STakashi Iwai int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime, 11001da177e4SLinus Torvalds unsigned int cond, 11011da177e4SLinus Torvalds unsigned int width, 11021da177e4SLinus Torvalds unsigned int msbits) 11031da177e4SLinus Torvalds { 11041da177e4SLinus Torvalds unsigned long l = (msbits << 16) | width; 11051da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, -1, 11061da177e4SLinus Torvalds snd_pcm_hw_rule_msbits, 11071da177e4SLinus Torvalds (void*) l, 11081da177e4SLinus Torvalds SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); 11091da177e4SLinus Torvalds } 11101da177e4SLinus Torvalds 1111e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits); 1112e88e8ae6STakashi Iwai 1113877211f5STakashi Iwai static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params, 1114877211f5STakashi Iwai struct snd_pcm_hw_rule *rule) 11151da177e4SLinus Torvalds { 11161da177e4SLinus Torvalds unsigned long step = (unsigned long) rule->private; 11171da177e4SLinus Torvalds return snd_interval_step(hw_param_interval(params, rule->var), 0, step); 11181da177e4SLinus Torvalds } 11191da177e4SLinus Torvalds 11201da177e4SLinus Torvalds /** 11211da177e4SLinus Torvalds * snd_pcm_hw_constraint_step 1122df8db936STakashi Iwai * @runtime: PCM runtime instance 1123df8db936STakashi Iwai * @cond: condition bits 1124df8db936STakashi Iwai * @var: hw_params variable to apply the step constraint 1125df8db936STakashi Iwai * @step: step size 11261da177e4SLinus Torvalds */ 1127877211f5STakashi Iwai int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime, 11281da177e4SLinus Torvalds unsigned int cond, 11291da177e4SLinus Torvalds snd_pcm_hw_param_t var, 11301da177e4SLinus Torvalds unsigned long step) 11311da177e4SLinus Torvalds { 11321da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 11331da177e4SLinus Torvalds snd_pcm_hw_rule_step, (void *) step, 11341da177e4SLinus Torvalds var, -1); 11351da177e4SLinus Torvalds } 11361da177e4SLinus Torvalds 1137e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_step); 1138e88e8ae6STakashi Iwai 1139877211f5STakashi Iwai static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) 11401da177e4SLinus Torvalds { 114167c39317SMarcin Ślusarz static unsigned int pow2_sizes[] = { 11421da177e4SLinus Torvalds 1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7, 11431da177e4SLinus Torvalds 1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15, 11441da177e4SLinus Torvalds 1<<16, 1<<17, 1<<18, 1<<19, 1<<20, 1<<21, 1<<22, 1<<23, 11451da177e4SLinus Torvalds 1<<24, 1<<25, 1<<26, 1<<27, 1<<28, 1<<29, 1<<30 11461da177e4SLinus Torvalds }; 11471da177e4SLinus Torvalds return snd_interval_list(hw_param_interval(params, rule->var), 11481da177e4SLinus Torvalds ARRAY_SIZE(pow2_sizes), pow2_sizes, 0); 11491da177e4SLinus Torvalds } 11501da177e4SLinus Torvalds 11511da177e4SLinus Torvalds /** 11521da177e4SLinus Torvalds * snd_pcm_hw_constraint_pow2 1153df8db936STakashi Iwai * @runtime: PCM runtime instance 1154df8db936STakashi Iwai * @cond: condition bits 1155df8db936STakashi Iwai * @var: hw_params variable to apply the power-of-2 constraint 11561da177e4SLinus Torvalds */ 1157877211f5STakashi Iwai int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime, 11581da177e4SLinus Torvalds unsigned int cond, 11591da177e4SLinus Torvalds snd_pcm_hw_param_t var) 11601da177e4SLinus Torvalds { 11611da177e4SLinus Torvalds return snd_pcm_hw_rule_add(runtime, cond, var, 11621da177e4SLinus Torvalds snd_pcm_hw_rule_pow2, NULL, 11631da177e4SLinus Torvalds var, -1); 11641da177e4SLinus Torvalds } 11651da177e4SLinus Torvalds 1166e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2); 1167e88e8ae6STakashi Iwai 1168877211f5STakashi Iwai static void _snd_pcm_hw_param_any(struct snd_pcm_hw_params *params, 1169123992f7SAdrian Bunk snd_pcm_hw_param_t var) 11701da177e4SLinus Torvalds { 11711da177e4SLinus Torvalds if (hw_is_mask(var)) { 11721da177e4SLinus Torvalds snd_mask_any(hw_param_mask(params, var)); 11731da177e4SLinus Torvalds params->cmask |= 1 << var; 11741da177e4SLinus Torvalds params->rmask |= 1 << var; 11751da177e4SLinus Torvalds return; 11761da177e4SLinus Torvalds } 11771da177e4SLinus Torvalds if (hw_is_interval(var)) { 11781da177e4SLinus Torvalds snd_interval_any(hw_param_interval(params, var)); 11791da177e4SLinus Torvalds params->cmask |= 1 << var; 11801da177e4SLinus Torvalds params->rmask |= 1 << var; 11811da177e4SLinus Torvalds return; 11821da177e4SLinus Torvalds } 11831da177e4SLinus Torvalds snd_BUG(); 11841da177e4SLinus Torvalds } 11851da177e4SLinus Torvalds 1186877211f5STakashi Iwai void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params) 11871da177e4SLinus Torvalds { 11881da177e4SLinus Torvalds unsigned int k; 11891da177e4SLinus Torvalds memset(params, 0, sizeof(*params)); 11901da177e4SLinus Torvalds for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) 11911da177e4SLinus Torvalds _snd_pcm_hw_param_any(params, k); 11921da177e4SLinus Torvalds for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) 11931da177e4SLinus Torvalds _snd_pcm_hw_param_any(params, k); 11941da177e4SLinus Torvalds params->info = ~0U; 11951da177e4SLinus Torvalds } 11961da177e4SLinus Torvalds 1197e88e8ae6STakashi Iwai EXPORT_SYMBOL(_snd_pcm_hw_params_any); 11981da177e4SLinus Torvalds 11991da177e4SLinus Torvalds /** 12001da177e4SLinus Torvalds * snd_pcm_hw_param_value 1201df8db936STakashi Iwai * @params: the hw_params instance 1202df8db936STakashi Iwai * @var: parameter to retrieve 1203df8db936STakashi Iwai * @dir: pointer to the direction (-1,0,1) or NULL 12041da177e4SLinus Torvalds * 12051da177e4SLinus Torvalds * Return the value for field PAR if it's fixed in configuration space 12061da177e4SLinus Torvalds * defined by PARAMS. Return -EINVAL otherwise 12071da177e4SLinus Torvalds */ 1208e88e8ae6STakashi Iwai int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params, 12091da177e4SLinus Torvalds snd_pcm_hw_param_t var, int *dir) 12101da177e4SLinus Torvalds { 12111da177e4SLinus Torvalds if (hw_is_mask(var)) { 1212877211f5STakashi Iwai const struct snd_mask *mask = hw_param_mask_c(params, var); 12131da177e4SLinus Torvalds if (!snd_mask_single(mask)) 12141da177e4SLinus Torvalds return -EINVAL; 12151da177e4SLinus Torvalds if (dir) 12161da177e4SLinus Torvalds *dir = 0; 12171da177e4SLinus Torvalds return snd_mask_value(mask); 12181da177e4SLinus Torvalds } 12191da177e4SLinus Torvalds if (hw_is_interval(var)) { 1220877211f5STakashi Iwai const struct snd_interval *i = hw_param_interval_c(params, var); 12211da177e4SLinus Torvalds if (!snd_interval_single(i)) 12221da177e4SLinus Torvalds return -EINVAL; 12231da177e4SLinus Torvalds if (dir) 12241da177e4SLinus Torvalds *dir = i->openmin; 12251da177e4SLinus Torvalds return snd_interval_value(i); 12261da177e4SLinus Torvalds } 12271da177e4SLinus Torvalds return -EINVAL; 12281da177e4SLinus Torvalds } 12291da177e4SLinus Torvalds 1230e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_param_value); 12311da177e4SLinus Torvalds 1232877211f5STakashi Iwai void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params, 12331da177e4SLinus Torvalds snd_pcm_hw_param_t var) 12341da177e4SLinus Torvalds { 12351da177e4SLinus Torvalds if (hw_is_mask(var)) { 12361da177e4SLinus Torvalds snd_mask_none(hw_param_mask(params, var)); 12371da177e4SLinus Torvalds params->cmask |= 1 << var; 12381da177e4SLinus Torvalds params->rmask |= 1 << var; 12391da177e4SLinus Torvalds } else if (hw_is_interval(var)) { 12401da177e4SLinus Torvalds snd_interval_none(hw_param_interval(params, var)); 12411da177e4SLinus Torvalds params->cmask |= 1 << var; 12421da177e4SLinus Torvalds params->rmask |= 1 << var; 12431da177e4SLinus Torvalds } else { 12441da177e4SLinus Torvalds snd_BUG(); 12451da177e4SLinus Torvalds } 12461da177e4SLinus Torvalds } 12471da177e4SLinus Torvalds 1248e88e8ae6STakashi Iwai EXPORT_SYMBOL(_snd_pcm_hw_param_setempty); 12491da177e4SLinus Torvalds 1250877211f5STakashi Iwai static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params, 12511da177e4SLinus Torvalds snd_pcm_hw_param_t var) 12521da177e4SLinus Torvalds { 12531da177e4SLinus Torvalds int changed; 12541da177e4SLinus Torvalds if (hw_is_mask(var)) 12551da177e4SLinus Torvalds changed = snd_mask_refine_first(hw_param_mask(params, var)); 12561da177e4SLinus Torvalds else if (hw_is_interval(var)) 12571da177e4SLinus Torvalds changed = snd_interval_refine_first(hw_param_interval(params, var)); 12582f4ca8e5STakashi Iwai else 12591da177e4SLinus Torvalds return -EINVAL; 12601da177e4SLinus Torvalds if (changed) { 12611da177e4SLinus Torvalds params->cmask |= 1 << var; 12621da177e4SLinus Torvalds params->rmask |= 1 << var; 12631da177e4SLinus Torvalds } 12641da177e4SLinus Torvalds return changed; 12651da177e4SLinus Torvalds } 12661da177e4SLinus Torvalds 12671da177e4SLinus Torvalds 12681da177e4SLinus Torvalds /** 12691da177e4SLinus Torvalds * snd_pcm_hw_param_first 1270df8db936STakashi Iwai * @pcm: PCM instance 1271df8db936STakashi Iwai * @params: the hw_params instance 1272df8db936STakashi Iwai * @var: parameter to retrieve 1273df8db936STakashi Iwai * @dir: pointer to the direction (-1,0,1) or NULL 12741da177e4SLinus Torvalds * 12751da177e4SLinus Torvalds * Inside configuration space defined by PARAMS remove from PAR all 12761da177e4SLinus Torvalds * values > minimum. Reduce configuration space accordingly. 12771da177e4SLinus Torvalds * Return the minimum. 12781da177e4SLinus Torvalds */ 1279e88e8ae6STakashi Iwai int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm, 1280877211f5STakashi Iwai struct snd_pcm_hw_params *params, 12811da177e4SLinus Torvalds snd_pcm_hw_param_t var, int *dir) 12821da177e4SLinus Torvalds { 12831da177e4SLinus Torvalds int changed = _snd_pcm_hw_param_first(params, var); 12841da177e4SLinus Torvalds if (changed < 0) 12851da177e4SLinus Torvalds return changed; 12861da177e4SLinus Torvalds if (params->rmask) { 12871da177e4SLinus Torvalds int err = snd_pcm_hw_refine(pcm, params); 12882f4ca8e5STakashi Iwai snd_assert(err >= 0, return err); 12891da177e4SLinus Torvalds } 12901da177e4SLinus Torvalds return snd_pcm_hw_param_value(params, var, dir); 12911da177e4SLinus Torvalds } 12921da177e4SLinus Torvalds 1293e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_param_first); 1294e88e8ae6STakashi Iwai 1295877211f5STakashi Iwai static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params, 12961da177e4SLinus Torvalds snd_pcm_hw_param_t var) 12971da177e4SLinus Torvalds { 12981da177e4SLinus Torvalds int changed; 12991da177e4SLinus Torvalds if (hw_is_mask(var)) 13001da177e4SLinus Torvalds changed = snd_mask_refine_last(hw_param_mask(params, var)); 13011da177e4SLinus Torvalds else if (hw_is_interval(var)) 13021da177e4SLinus Torvalds changed = snd_interval_refine_last(hw_param_interval(params, var)); 13032f4ca8e5STakashi Iwai else 13041da177e4SLinus Torvalds return -EINVAL; 13051da177e4SLinus Torvalds if (changed) { 13061da177e4SLinus Torvalds params->cmask |= 1 << var; 13071da177e4SLinus Torvalds params->rmask |= 1 << var; 13081da177e4SLinus Torvalds } 13091da177e4SLinus Torvalds return changed; 13101da177e4SLinus Torvalds } 13111da177e4SLinus Torvalds 13121da177e4SLinus Torvalds 13131da177e4SLinus Torvalds /** 13141da177e4SLinus Torvalds * snd_pcm_hw_param_last 1315df8db936STakashi Iwai * @pcm: PCM instance 1316df8db936STakashi Iwai * @params: the hw_params instance 1317df8db936STakashi Iwai * @var: parameter to retrieve 1318df8db936STakashi Iwai * @dir: pointer to the direction (-1,0,1) or NULL 13191da177e4SLinus Torvalds * 13201da177e4SLinus Torvalds * Inside configuration space defined by PARAMS remove from PAR all 13211da177e4SLinus Torvalds * values < maximum. Reduce configuration space accordingly. 13221da177e4SLinus Torvalds * Return the maximum. 13231da177e4SLinus Torvalds */ 1324e88e8ae6STakashi Iwai int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm, 1325877211f5STakashi Iwai struct snd_pcm_hw_params *params, 13261da177e4SLinus Torvalds snd_pcm_hw_param_t var, int *dir) 13271da177e4SLinus Torvalds { 13281da177e4SLinus Torvalds int changed = _snd_pcm_hw_param_last(params, var); 13291da177e4SLinus Torvalds if (changed < 0) 13301da177e4SLinus Torvalds return changed; 13311da177e4SLinus Torvalds if (params->rmask) { 13321da177e4SLinus Torvalds int err = snd_pcm_hw_refine(pcm, params); 13332f4ca8e5STakashi Iwai snd_assert(err >= 0, return err); 13341da177e4SLinus Torvalds } 13351da177e4SLinus Torvalds return snd_pcm_hw_param_value(params, var, dir); 13361da177e4SLinus Torvalds } 13371da177e4SLinus Torvalds 1338e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_hw_param_last); 13391da177e4SLinus Torvalds 13401da177e4SLinus Torvalds /** 13411da177e4SLinus Torvalds * snd_pcm_hw_param_choose 1342df8db936STakashi Iwai * @pcm: PCM instance 1343df8db936STakashi Iwai * @params: the hw_params instance 13441da177e4SLinus Torvalds * 13451da177e4SLinus Torvalds * Choose one configuration from configuration space defined by PARAMS 13461da177e4SLinus Torvalds * The configuration chosen is that obtained fixing in this order: 13471da177e4SLinus Torvalds * first access, first format, first subformat, min channels, 13481da177e4SLinus Torvalds * min rate, min period time, max buffer size, min tick time 13491da177e4SLinus Torvalds */ 13502f4ca8e5STakashi Iwai int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm, 13512f4ca8e5STakashi Iwai struct snd_pcm_hw_params *params) 13521da177e4SLinus Torvalds { 13532f4ca8e5STakashi Iwai static int vars[] = { 13542f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_ACCESS, 13552f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_FORMAT, 13562f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_SUBFORMAT, 13572f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_CHANNELS, 13582f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_RATE, 13592f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_PERIOD_TIME, 13602f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 13612f4ca8e5STakashi Iwai SNDRV_PCM_HW_PARAM_TICK_TIME, 13622f4ca8e5STakashi Iwai -1 13632f4ca8e5STakashi Iwai }; 13642f4ca8e5STakashi Iwai int err, *v; 13651da177e4SLinus Torvalds 13662f4ca8e5STakashi Iwai for (v = vars; *v != -1; v++) { 13672f4ca8e5STakashi Iwai if (*v != SNDRV_PCM_HW_PARAM_BUFFER_SIZE) 13682f4ca8e5STakashi Iwai err = snd_pcm_hw_param_first(pcm, params, *v, NULL); 13692f4ca8e5STakashi Iwai else 13702f4ca8e5STakashi Iwai err = snd_pcm_hw_param_last(pcm, params, *v, NULL); 13712f4ca8e5STakashi Iwai snd_assert(err >= 0, return err); 13722f4ca8e5STakashi Iwai } 13731da177e4SLinus Torvalds return 0; 13741da177e4SLinus Torvalds } 13751da177e4SLinus Torvalds 1376877211f5STakashi Iwai static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream, 13771da177e4SLinus Torvalds void *arg) 13781da177e4SLinus Torvalds { 1379877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 13801da177e4SLinus Torvalds unsigned long flags; 13811da177e4SLinus Torvalds snd_pcm_stream_lock_irqsave(substream, flags); 13821da177e4SLinus Torvalds if (snd_pcm_running(substream) && 13831da177e4SLinus Torvalds snd_pcm_update_hw_ptr(substream) >= 0) 13841da177e4SLinus Torvalds runtime->status->hw_ptr %= runtime->buffer_size; 13851da177e4SLinus Torvalds else 13861da177e4SLinus Torvalds runtime->status->hw_ptr = 0; 13871da177e4SLinus Torvalds snd_pcm_stream_unlock_irqrestore(substream, flags); 13881da177e4SLinus Torvalds return 0; 13891da177e4SLinus Torvalds } 13901da177e4SLinus Torvalds 1391877211f5STakashi Iwai static int snd_pcm_lib_ioctl_channel_info(struct snd_pcm_substream *substream, 13921da177e4SLinus Torvalds void *arg) 13931da177e4SLinus Torvalds { 1394877211f5STakashi Iwai struct snd_pcm_channel_info *info = arg; 1395877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 13961da177e4SLinus Torvalds int width; 13971da177e4SLinus Torvalds if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) { 13981da177e4SLinus Torvalds info->offset = -1; 13991da177e4SLinus Torvalds return 0; 14001da177e4SLinus Torvalds } 14011da177e4SLinus Torvalds width = snd_pcm_format_physical_width(runtime->format); 14021da177e4SLinus Torvalds if (width < 0) 14031da177e4SLinus Torvalds return width; 14041da177e4SLinus Torvalds info->offset = 0; 14051da177e4SLinus Torvalds switch (runtime->access) { 14061da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED: 14071da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_RW_INTERLEAVED: 14081da177e4SLinus Torvalds info->first = info->channel * width; 14091da177e4SLinus Torvalds info->step = runtime->channels * width; 14101da177e4SLinus Torvalds break; 14111da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED: 14121da177e4SLinus Torvalds case SNDRV_PCM_ACCESS_RW_NONINTERLEAVED: 14131da177e4SLinus Torvalds { 14141da177e4SLinus Torvalds size_t size = runtime->dma_bytes / runtime->channels; 14151da177e4SLinus Torvalds info->first = info->channel * size * 8; 14161da177e4SLinus Torvalds info->step = width; 14171da177e4SLinus Torvalds break; 14181da177e4SLinus Torvalds } 14191da177e4SLinus Torvalds default: 14201da177e4SLinus Torvalds snd_BUG(); 14211da177e4SLinus Torvalds break; 14221da177e4SLinus Torvalds } 14231da177e4SLinus Torvalds return 0; 14241da177e4SLinus Torvalds } 14251da177e4SLinus Torvalds 14261da177e4SLinus Torvalds /** 14271da177e4SLinus Torvalds * snd_pcm_lib_ioctl - a generic PCM ioctl callback 14281da177e4SLinus Torvalds * @substream: the pcm substream instance 14291da177e4SLinus Torvalds * @cmd: ioctl command 14301da177e4SLinus Torvalds * @arg: ioctl argument 14311da177e4SLinus Torvalds * 14321da177e4SLinus Torvalds * Processes the generic ioctl commands for PCM. 14331da177e4SLinus Torvalds * Can be passed as the ioctl callback for PCM ops. 14341da177e4SLinus Torvalds * 14351da177e4SLinus Torvalds * Returns zero if successful, or a negative error code on failure. 14361da177e4SLinus Torvalds */ 1437877211f5STakashi Iwai int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, 14381da177e4SLinus Torvalds unsigned int cmd, void *arg) 14391da177e4SLinus Torvalds { 14401da177e4SLinus Torvalds switch (cmd) { 14411da177e4SLinus Torvalds case SNDRV_PCM_IOCTL1_INFO: 14421da177e4SLinus Torvalds return 0; 14431da177e4SLinus Torvalds case SNDRV_PCM_IOCTL1_RESET: 14441da177e4SLinus Torvalds return snd_pcm_lib_ioctl_reset(substream, arg); 14451da177e4SLinus Torvalds case SNDRV_PCM_IOCTL1_CHANNEL_INFO: 14461da177e4SLinus Torvalds return snd_pcm_lib_ioctl_channel_info(substream, arg); 14471da177e4SLinus Torvalds } 14481da177e4SLinus Torvalds return -ENXIO; 14491da177e4SLinus Torvalds } 14501da177e4SLinus Torvalds 1451e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_lib_ioctl); 1452e88e8ae6STakashi Iwai 14531da177e4SLinus Torvalds /** 14541da177e4SLinus Torvalds * snd_pcm_period_elapsed - update the pcm status for the next period 14551da177e4SLinus Torvalds * @substream: the pcm substream instance 14561da177e4SLinus Torvalds * 14571da177e4SLinus Torvalds * This function is called from the interrupt handler when the 14581da177e4SLinus Torvalds * PCM has processed the period size. It will update the current 145931e8960bSTakashi Iwai * pointer, wake up sleepers, etc. 14601da177e4SLinus Torvalds * 14611da177e4SLinus Torvalds * Even if more than one periods have elapsed since the last call, you 14621da177e4SLinus Torvalds * have to call this only once. 14631da177e4SLinus Torvalds */ 1464877211f5STakashi Iwai void snd_pcm_period_elapsed(struct snd_pcm_substream *substream) 14651da177e4SLinus Torvalds { 1466877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 14671da177e4SLinus Torvalds unsigned long flags; 14681da177e4SLinus Torvalds 14691da177e4SLinus Torvalds snd_assert(substream != NULL, return); 14701da177e4SLinus Torvalds runtime = substream->runtime; 14711da177e4SLinus Torvalds snd_assert(runtime != NULL, return); 14721da177e4SLinus Torvalds 14731da177e4SLinus Torvalds if (runtime->transfer_ack_begin) 14741da177e4SLinus Torvalds runtime->transfer_ack_begin(substream); 14751da177e4SLinus Torvalds 14761da177e4SLinus Torvalds snd_pcm_stream_lock_irqsave(substream, flags); 14771da177e4SLinus Torvalds if (!snd_pcm_running(substream) || 14781da177e4SLinus Torvalds snd_pcm_update_hw_ptr_interrupt(substream) < 0) 14791da177e4SLinus Torvalds goto _end; 14801da177e4SLinus Torvalds 14811da177e4SLinus Torvalds if (substream->timer_running) 14821da177e4SLinus Torvalds snd_timer_interrupt(substream->timer, 1); 14831da177e4SLinus Torvalds _end: 14841da177e4SLinus Torvalds snd_pcm_stream_unlock_irqrestore(substream, flags); 14851da177e4SLinus Torvalds if (runtime->transfer_ack_end) 14861da177e4SLinus Torvalds runtime->transfer_ack_end(substream); 14871da177e4SLinus Torvalds kill_fasync(&runtime->fasync, SIGIO, POLL_IN); 14881da177e4SLinus Torvalds } 14891da177e4SLinus Torvalds 1490e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_period_elapsed); 1491e88e8ae6STakashi Iwai 149213075510STakashi Iwai /* 149313075510STakashi Iwai * Wait until avail_min data becomes available 149413075510STakashi Iwai * Returns a negative error code if any error occurs during operation. 149513075510STakashi Iwai * The available space is stored on availp. When err = 0 and avail = 0 149613075510STakashi Iwai * on the capture stream, it indicates the stream is in DRAINING state. 149713075510STakashi Iwai */ 149813075510STakashi Iwai static int wait_for_avail_min(struct snd_pcm_substream *substream, 149913075510STakashi Iwai snd_pcm_uframes_t *availp) 150013075510STakashi Iwai { 150113075510STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 150213075510STakashi Iwai int is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; 150313075510STakashi Iwai wait_queue_t wait; 150413075510STakashi Iwai int err = 0; 150513075510STakashi Iwai snd_pcm_uframes_t avail = 0; 150613075510STakashi Iwai long tout; 150713075510STakashi Iwai 150813075510STakashi Iwai init_waitqueue_entry(&wait, current); 150913075510STakashi Iwai add_wait_queue(&runtime->sleep, &wait); 151013075510STakashi Iwai for (;;) { 151113075510STakashi Iwai if (signal_pending(current)) { 151213075510STakashi Iwai err = -ERESTARTSYS; 151313075510STakashi Iwai break; 151413075510STakashi Iwai } 151513075510STakashi Iwai set_current_state(TASK_INTERRUPTIBLE); 151613075510STakashi Iwai snd_pcm_stream_unlock_irq(substream); 151713075510STakashi Iwai tout = schedule_timeout(msecs_to_jiffies(10000)); 151813075510STakashi Iwai snd_pcm_stream_lock_irq(substream); 151913075510STakashi Iwai switch (runtime->status->state) { 152013075510STakashi Iwai case SNDRV_PCM_STATE_SUSPENDED: 152113075510STakashi Iwai err = -ESTRPIPE; 152213075510STakashi Iwai goto _endloop; 152313075510STakashi Iwai case SNDRV_PCM_STATE_XRUN: 152413075510STakashi Iwai err = -EPIPE; 152513075510STakashi Iwai goto _endloop; 152613075510STakashi Iwai case SNDRV_PCM_STATE_DRAINING: 152713075510STakashi Iwai if (is_playback) 152813075510STakashi Iwai err = -EPIPE; 152913075510STakashi Iwai else 153013075510STakashi Iwai avail = 0; /* indicate draining */ 153113075510STakashi Iwai goto _endloop; 153213075510STakashi Iwai case SNDRV_PCM_STATE_OPEN: 153313075510STakashi Iwai case SNDRV_PCM_STATE_SETUP: 153413075510STakashi Iwai case SNDRV_PCM_STATE_DISCONNECTED: 153513075510STakashi Iwai err = -EBADFD; 153613075510STakashi Iwai goto _endloop; 153713075510STakashi Iwai } 153813075510STakashi Iwai if (!tout) { 153913075510STakashi Iwai snd_printd("%s write error (DMA or IRQ trouble?)\n", 154013075510STakashi Iwai is_playback ? "playback" : "capture"); 154113075510STakashi Iwai err = -EIO; 154213075510STakashi Iwai break; 154313075510STakashi Iwai } 154413075510STakashi Iwai if (is_playback) 154513075510STakashi Iwai avail = snd_pcm_playback_avail(runtime); 154613075510STakashi Iwai else 154713075510STakashi Iwai avail = snd_pcm_capture_avail(runtime); 154813075510STakashi Iwai if (avail >= runtime->control->avail_min) 154913075510STakashi Iwai break; 155013075510STakashi Iwai } 155113075510STakashi Iwai _endloop: 155213075510STakashi Iwai remove_wait_queue(&runtime->sleep, &wait); 155313075510STakashi Iwai *availp = avail; 155413075510STakashi Iwai return err; 155513075510STakashi Iwai } 155613075510STakashi Iwai 1557877211f5STakashi Iwai static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream, 15581da177e4SLinus Torvalds unsigned int hwoff, 15591da177e4SLinus Torvalds unsigned long data, unsigned int off, 15601da177e4SLinus Torvalds snd_pcm_uframes_t frames) 15611da177e4SLinus Torvalds { 1562877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 15631da177e4SLinus Torvalds int err; 15641da177e4SLinus Torvalds char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); 15651da177e4SLinus Torvalds if (substream->ops->copy) { 15661da177e4SLinus Torvalds if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) 15671da177e4SLinus Torvalds return err; 15681da177e4SLinus Torvalds } else { 15691da177e4SLinus Torvalds char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); 15701da177e4SLinus Torvalds snd_assert(runtime->dma_area, return -EFAULT); 15711da177e4SLinus Torvalds if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames))) 15721da177e4SLinus Torvalds return -EFAULT; 15731da177e4SLinus Torvalds } 15741da177e4SLinus Torvalds return 0; 15751da177e4SLinus Torvalds } 15761da177e4SLinus Torvalds 1577877211f5STakashi Iwai typedef int (*transfer_f)(struct snd_pcm_substream *substream, unsigned int hwoff, 15781da177e4SLinus Torvalds unsigned long data, unsigned int off, 15791da177e4SLinus Torvalds snd_pcm_uframes_t size); 15801da177e4SLinus Torvalds 1581877211f5STakashi Iwai static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, 15821da177e4SLinus Torvalds unsigned long data, 15831da177e4SLinus Torvalds snd_pcm_uframes_t size, 15841da177e4SLinus Torvalds int nonblock, 15851da177e4SLinus Torvalds transfer_f transfer) 15861da177e4SLinus Torvalds { 1587877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 15881da177e4SLinus Torvalds snd_pcm_uframes_t xfer = 0; 15891da177e4SLinus Torvalds snd_pcm_uframes_t offset = 0; 15901da177e4SLinus Torvalds int err = 0; 15911da177e4SLinus Torvalds 15921da177e4SLinus Torvalds if (size == 0) 15931da177e4SLinus Torvalds return 0; 15941da177e4SLinus Torvalds 15951da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 15961da177e4SLinus Torvalds switch (runtime->status->state) { 15971da177e4SLinus Torvalds case SNDRV_PCM_STATE_PREPARED: 15981da177e4SLinus Torvalds case SNDRV_PCM_STATE_RUNNING: 15991da177e4SLinus Torvalds case SNDRV_PCM_STATE_PAUSED: 16001da177e4SLinus Torvalds break; 16011da177e4SLinus Torvalds case SNDRV_PCM_STATE_XRUN: 16021da177e4SLinus Torvalds err = -EPIPE; 16031da177e4SLinus Torvalds goto _end_unlock; 16041da177e4SLinus Torvalds case SNDRV_PCM_STATE_SUSPENDED: 16051da177e4SLinus Torvalds err = -ESTRPIPE; 16061da177e4SLinus Torvalds goto _end_unlock; 16071da177e4SLinus Torvalds default: 16081da177e4SLinus Torvalds err = -EBADFD; 16091da177e4SLinus Torvalds goto _end_unlock; 16101da177e4SLinus Torvalds } 16111da177e4SLinus Torvalds 16121da177e4SLinus Torvalds while (size > 0) { 16131da177e4SLinus Torvalds snd_pcm_uframes_t frames, appl_ptr, appl_ofs; 16141da177e4SLinus Torvalds snd_pcm_uframes_t avail; 16151da177e4SLinus Torvalds snd_pcm_uframes_t cont; 161631e8960bSTakashi Iwai if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) 16171da177e4SLinus Torvalds snd_pcm_update_hw_ptr(substream); 16181da177e4SLinus Torvalds avail = snd_pcm_playback_avail(runtime); 161913075510STakashi Iwai if (!avail) { 16201da177e4SLinus Torvalds if (nonblock) { 16211da177e4SLinus Torvalds err = -EAGAIN; 16221da177e4SLinus Torvalds goto _end_unlock; 16231da177e4SLinus Torvalds } 162413075510STakashi Iwai err = wait_for_avail_min(substream, &avail); 162513075510STakashi Iwai if (err < 0) 16261da177e4SLinus Torvalds goto _end_unlock; 16271da177e4SLinus Torvalds } 16281da177e4SLinus Torvalds frames = size > avail ? avail : size; 16291da177e4SLinus Torvalds cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; 16301da177e4SLinus Torvalds if (frames > cont) 16311da177e4SLinus Torvalds frames = cont; 16321da177e4SLinus Torvalds snd_assert(frames != 0, snd_pcm_stream_unlock_irq(substream); return -EINVAL); 16331da177e4SLinus Torvalds appl_ptr = runtime->control->appl_ptr; 16341da177e4SLinus Torvalds appl_ofs = appl_ptr % runtime->buffer_size; 16351da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 16361da177e4SLinus Torvalds if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0) 16371da177e4SLinus Torvalds goto _end; 16381da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 16391da177e4SLinus Torvalds switch (runtime->status->state) { 16401da177e4SLinus Torvalds case SNDRV_PCM_STATE_XRUN: 16411da177e4SLinus Torvalds err = -EPIPE; 16421da177e4SLinus Torvalds goto _end_unlock; 16431da177e4SLinus Torvalds case SNDRV_PCM_STATE_SUSPENDED: 16441da177e4SLinus Torvalds err = -ESTRPIPE; 16451da177e4SLinus Torvalds goto _end_unlock; 16461da177e4SLinus Torvalds default: 16471da177e4SLinus Torvalds break; 16481da177e4SLinus Torvalds } 16491da177e4SLinus Torvalds appl_ptr += frames; 16501da177e4SLinus Torvalds if (appl_ptr >= runtime->boundary) 16511da177e4SLinus Torvalds appl_ptr -= runtime->boundary; 16521da177e4SLinus Torvalds runtime->control->appl_ptr = appl_ptr; 16531da177e4SLinus Torvalds if (substream->ops->ack) 16541da177e4SLinus Torvalds substream->ops->ack(substream); 16551da177e4SLinus Torvalds 16561da177e4SLinus Torvalds offset += frames; 16571da177e4SLinus Torvalds size -= frames; 16581da177e4SLinus Torvalds xfer += frames; 16591da177e4SLinus Torvalds if (runtime->status->state == SNDRV_PCM_STATE_PREPARED && 16601da177e4SLinus Torvalds snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) { 16611da177e4SLinus Torvalds err = snd_pcm_start(substream); 16621da177e4SLinus Torvalds if (err < 0) 16631da177e4SLinus Torvalds goto _end_unlock; 16641da177e4SLinus Torvalds } 16651da177e4SLinus Torvalds } 16661da177e4SLinus Torvalds _end_unlock: 16671da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 16681da177e4SLinus Torvalds _end: 16691da177e4SLinus Torvalds return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; 16701da177e4SLinus Torvalds } 16711da177e4SLinus Torvalds 1672877211f5STakashi Iwai snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size) 16731da177e4SLinus Torvalds { 1674877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 16751da177e4SLinus Torvalds int nonblock; 16761da177e4SLinus Torvalds 16771da177e4SLinus Torvalds snd_assert(substream != NULL, return -ENXIO); 16781da177e4SLinus Torvalds runtime = substream->runtime; 16791da177e4SLinus Torvalds snd_assert(runtime != NULL, return -ENXIO); 16801da177e4SLinus Torvalds snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); 16811da177e4SLinus Torvalds if (runtime->status->state == SNDRV_PCM_STATE_OPEN) 16821da177e4SLinus Torvalds return -EBADFD; 16831da177e4SLinus Torvalds 16840df63e44STakashi Iwai nonblock = !!(substream->f_flags & O_NONBLOCK); 16851da177e4SLinus Torvalds 16861da177e4SLinus Torvalds if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && 16871da177e4SLinus Torvalds runtime->channels > 1) 16881da177e4SLinus Torvalds return -EINVAL; 16891da177e4SLinus Torvalds return snd_pcm_lib_write1(substream, (unsigned long)buf, size, nonblock, 16901da177e4SLinus Torvalds snd_pcm_lib_write_transfer); 16911da177e4SLinus Torvalds } 16921da177e4SLinus Torvalds 1693e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_lib_write); 1694e88e8ae6STakashi Iwai 1695877211f5STakashi Iwai static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream, 16961da177e4SLinus Torvalds unsigned int hwoff, 16971da177e4SLinus Torvalds unsigned long data, unsigned int off, 16981da177e4SLinus Torvalds snd_pcm_uframes_t frames) 16991da177e4SLinus Torvalds { 1700877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 17011da177e4SLinus Torvalds int err; 17021da177e4SLinus Torvalds void __user **bufs = (void __user **)data; 17031da177e4SLinus Torvalds int channels = runtime->channels; 17041da177e4SLinus Torvalds int c; 17051da177e4SLinus Torvalds if (substream->ops->copy) { 17061da177e4SLinus Torvalds snd_assert(substream->ops->silence != NULL, return -EINVAL); 17071da177e4SLinus Torvalds for (c = 0; c < channels; ++c, ++bufs) { 17081da177e4SLinus Torvalds if (*bufs == NULL) { 17091da177e4SLinus Torvalds if ((err = substream->ops->silence(substream, c, hwoff, frames)) < 0) 17101da177e4SLinus Torvalds return err; 17111da177e4SLinus Torvalds } else { 17121da177e4SLinus Torvalds char __user *buf = *bufs + samples_to_bytes(runtime, off); 17131da177e4SLinus Torvalds if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) 17141da177e4SLinus Torvalds return err; 17151da177e4SLinus Torvalds } 17161da177e4SLinus Torvalds } 17171da177e4SLinus Torvalds } else { 17181da177e4SLinus Torvalds /* default transfer behaviour */ 17191da177e4SLinus Torvalds size_t dma_csize = runtime->dma_bytes / channels; 17201da177e4SLinus Torvalds snd_assert(runtime->dma_area, return -EFAULT); 17211da177e4SLinus Torvalds for (c = 0; c < channels; ++c, ++bufs) { 17221da177e4SLinus Torvalds char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff); 17231da177e4SLinus Torvalds if (*bufs == NULL) { 17241da177e4SLinus Torvalds snd_pcm_format_set_silence(runtime->format, hwbuf, frames); 17251da177e4SLinus Torvalds } else { 17261da177e4SLinus Torvalds char __user *buf = *bufs + samples_to_bytes(runtime, off); 17271da177e4SLinus Torvalds if (copy_from_user(hwbuf, buf, samples_to_bytes(runtime, frames))) 17281da177e4SLinus Torvalds return -EFAULT; 17291da177e4SLinus Torvalds } 17301da177e4SLinus Torvalds } 17311da177e4SLinus Torvalds } 17321da177e4SLinus Torvalds return 0; 17331da177e4SLinus Torvalds } 17341da177e4SLinus Torvalds 1735877211f5STakashi Iwai snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream, 17361da177e4SLinus Torvalds void __user **bufs, 17371da177e4SLinus Torvalds snd_pcm_uframes_t frames) 17381da177e4SLinus Torvalds { 1739877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 17401da177e4SLinus Torvalds int nonblock; 17411da177e4SLinus Torvalds 17421da177e4SLinus Torvalds snd_assert(substream != NULL, return -ENXIO); 17431da177e4SLinus Torvalds runtime = substream->runtime; 17441da177e4SLinus Torvalds snd_assert(runtime != NULL, return -ENXIO); 17451da177e4SLinus Torvalds snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); 17461da177e4SLinus Torvalds if (runtime->status->state == SNDRV_PCM_STATE_OPEN) 17471da177e4SLinus Torvalds return -EBADFD; 17481da177e4SLinus Torvalds 17490df63e44STakashi Iwai nonblock = !!(substream->f_flags & O_NONBLOCK); 17501da177e4SLinus Torvalds 17511da177e4SLinus Torvalds if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) 17521da177e4SLinus Torvalds return -EINVAL; 17531da177e4SLinus Torvalds return snd_pcm_lib_write1(substream, (unsigned long)bufs, frames, 17541da177e4SLinus Torvalds nonblock, snd_pcm_lib_writev_transfer); 17551da177e4SLinus Torvalds } 17561da177e4SLinus Torvalds 1757e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_lib_writev); 1758e88e8ae6STakashi Iwai 1759877211f5STakashi Iwai static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream, 17601da177e4SLinus Torvalds unsigned int hwoff, 17611da177e4SLinus Torvalds unsigned long data, unsigned int off, 17621da177e4SLinus Torvalds snd_pcm_uframes_t frames) 17631da177e4SLinus Torvalds { 1764877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 17651da177e4SLinus Torvalds int err; 17661da177e4SLinus Torvalds char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); 17671da177e4SLinus Torvalds if (substream->ops->copy) { 17681da177e4SLinus Torvalds if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) 17691da177e4SLinus Torvalds return err; 17701da177e4SLinus Torvalds } else { 17711da177e4SLinus Torvalds char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); 17721da177e4SLinus Torvalds snd_assert(runtime->dma_area, return -EFAULT); 17731da177e4SLinus Torvalds if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames))) 17741da177e4SLinus Torvalds return -EFAULT; 17751da177e4SLinus Torvalds } 17761da177e4SLinus Torvalds return 0; 17771da177e4SLinus Torvalds } 17781da177e4SLinus Torvalds 1779877211f5STakashi Iwai static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, 17801da177e4SLinus Torvalds unsigned long data, 17811da177e4SLinus Torvalds snd_pcm_uframes_t size, 17821da177e4SLinus Torvalds int nonblock, 17831da177e4SLinus Torvalds transfer_f transfer) 17841da177e4SLinus Torvalds { 1785877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 17861da177e4SLinus Torvalds snd_pcm_uframes_t xfer = 0; 17871da177e4SLinus Torvalds snd_pcm_uframes_t offset = 0; 17881da177e4SLinus Torvalds int err = 0; 17891da177e4SLinus Torvalds 17901da177e4SLinus Torvalds if (size == 0) 17911da177e4SLinus Torvalds return 0; 17921da177e4SLinus Torvalds 17931da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 17941da177e4SLinus Torvalds switch (runtime->status->state) { 17951da177e4SLinus Torvalds case SNDRV_PCM_STATE_PREPARED: 17961da177e4SLinus Torvalds if (size >= runtime->start_threshold) { 17971da177e4SLinus Torvalds err = snd_pcm_start(substream); 17981da177e4SLinus Torvalds if (err < 0) 17991da177e4SLinus Torvalds goto _end_unlock; 18001da177e4SLinus Torvalds } 18011da177e4SLinus Torvalds break; 18021da177e4SLinus Torvalds case SNDRV_PCM_STATE_DRAINING: 18031da177e4SLinus Torvalds case SNDRV_PCM_STATE_RUNNING: 18041da177e4SLinus Torvalds case SNDRV_PCM_STATE_PAUSED: 18051da177e4SLinus Torvalds break; 18061da177e4SLinus Torvalds case SNDRV_PCM_STATE_XRUN: 18071da177e4SLinus Torvalds err = -EPIPE; 18081da177e4SLinus Torvalds goto _end_unlock; 18091da177e4SLinus Torvalds case SNDRV_PCM_STATE_SUSPENDED: 18101da177e4SLinus Torvalds err = -ESTRPIPE; 18111da177e4SLinus Torvalds goto _end_unlock; 18121da177e4SLinus Torvalds default: 18131da177e4SLinus Torvalds err = -EBADFD; 18141da177e4SLinus Torvalds goto _end_unlock; 18151da177e4SLinus Torvalds } 18161da177e4SLinus Torvalds 18171da177e4SLinus Torvalds while (size > 0) { 18181da177e4SLinus Torvalds snd_pcm_uframes_t frames, appl_ptr, appl_ofs; 18191da177e4SLinus Torvalds snd_pcm_uframes_t avail; 18201da177e4SLinus Torvalds snd_pcm_uframes_t cont; 182131e8960bSTakashi Iwai if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) 18221da177e4SLinus Torvalds snd_pcm_update_hw_ptr(substream); 18231da177e4SLinus Torvalds avail = snd_pcm_capture_avail(runtime); 1824d948035aSTakashi Iwai if (!avail) { 182513075510STakashi Iwai if (runtime->status->state == 182613075510STakashi Iwai SNDRV_PCM_STATE_DRAINING) { 182713075510STakashi Iwai snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); 18281da177e4SLinus Torvalds goto _end_unlock; 18291da177e4SLinus Torvalds } 18301da177e4SLinus Torvalds if (nonblock) { 18311da177e4SLinus Torvalds err = -EAGAIN; 18321da177e4SLinus Torvalds goto _end_unlock; 18331da177e4SLinus Torvalds } 183413075510STakashi Iwai err = wait_for_avail_min(substream, &avail); 183513075510STakashi Iwai if (err < 0) 18361da177e4SLinus Torvalds goto _end_unlock; 183713075510STakashi Iwai if (!avail) 183813075510STakashi Iwai continue; /* draining */ 18391da177e4SLinus Torvalds } 18401da177e4SLinus Torvalds frames = size > avail ? avail : size; 18411da177e4SLinus Torvalds cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; 18421da177e4SLinus Torvalds if (frames > cont) 18431da177e4SLinus Torvalds frames = cont; 18441da177e4SLinus Torvalds snd_assert(frames != 0, snd_pcm_stream_unlock_irq(substream); return -EINVAL); 18451da177e4SLinus Torvalds appl_ptr = runtime->control->appl_ptr; 18461da177e4SLinus Torvalds appl_ofs = appl_ptr % runtime->buffer_size; 18471da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 18481da177e4SLinus Torvalds if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0) 18491da177e4SLinus Torvalds goto _end; 18501da177e4SLinus Torvalds snd_pcm_stream_lock_irq(substream); 18511da177e4SLinus Torvalds switch (runtime->status->state) { 18521da177e4SLinus Torvalds case SNDRV_PCM_STATE_XRUN: 18531da177e4SLinus Torvalds err = -EPIPE; 18541da177e4SLinus Torvalds goto _end_unlock; 18551da177e4SLinus Torvalds case SNDRV_PCM_STATE_SUSPENDED: 18561da177e4SLinus Torvalds err = -ESTRPIPE; 18571da177e4SLinus Torvalds goto _end_unlock; 18581da177e4SLinus Torvalds default: 18591da177e4SLinus Torvalds break; 18601da177e4SLinus Torvalds } 18611da177e4SLinus Torvalds appl_ptr += frames; 18621da177e4SLinus Torvalds if (appl_ptr >= runtime->boundary) 18631da177e4SLinus Torvalds appl_ptr -= runtime->boundary; 18641da177e4SLinus Torvalds runtime->control->appl_ptr = appl_ptr; 18651da177e4SLinus Torvalds if (substream->ops->ack) 18661da177e4SLinus Torvalds substream->ops->ack(substream); 18671da177e4SLinus Torvalds 18681da177e4SLinus Torvalds offset += frames; 18691da177e4SLinus Torvalds size -= frames; 18701da177e4SLinus Torvalds xfer += frames; 18711da177e4SLinus Torvalds } 18721da177e4SLinus Torvalds _end_unlock: 18731da177e4SLinus Torvalds snd_pcm_stream_unlock_irq(substream); 18741da177e4SLinus Torvalds _end: 18751da177e4SLinus Torvalds return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; 18761da177e4SLinus Torvalds } 18771da177e4SLinus Torvalds 1878877211f5STakashi Iwai snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __user *buf, snd_pcm_uframes_t size) 18791da177e4SLinus Torvalds { 1880877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 18811da177e4SLinus Torvalds int nonblock; 18821da177e4SLinus Torvalds 18831da177e4SLinus Torvalds snd_assert(substream != NULL, return -ENXIO); 18841da177e4SLinus Torvalds runtime = substream->runtime; 18851da177e4SLinus Torvalds snd_assert(runtime != NULL, return -ENXIO); 18861da177e4SLinus Torvalds snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); 18871da177e4SLinus Torvalds if (runtime->status->state == SNDRV_PCM_STATE_OPEN) 18881da177e4SLinus Torvalds return -EBADFD; 18891da177e4SLinus Torvalds 18900df63e44STakashi Iwai nonblock = !!(substream->f_flags & O_NONBLOCK); 18911da177e4SLinus Torvalds if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED) 18921da177e4SLinus Torvalds return -EINVAL; 18931da177e4SLinus Torvalds return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer); 18941da177e4SLinus Torvalds } 18951da177e4SLinus Torvalds 1896e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_lib_read); 1897e88e8ae6STakashi Iwai 1898877211f5STakashi Iwai static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream, 18991da177e4SLinus Torvalds unsigned int hwoff, 19001da177e4SLinus Torvalds unsigned long data, unsigned int off, 19011da177e4SLinus Torvalds snd_pcm_uframes_t frames) 19021da177e4SLinus Torvalds { 1903877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 19041da177e4SLinus Torvalds int err; 19051da177e4SLinus Torvalds void __user **bufs = (void __user **)data; 19061da177e4SLinus Torvalds int channels = runtime->channels; 19071da177e4SLinus Torvalds int c; 19081da177e4SLinus Torvalds if (substream->ops->copy) { 19091da177e4SLinus Torvalds for (c = 0; c < channels; ++c, ++bufs) { 19101da177e4SLinus Torvalds char __user *buf; 19111da177e4SLinus Torvalds if (*bufs == NULL) 19121da177e4SLinus Torvalds continue; 19131da177e4SLinus Torvalds buf = *bufs + samples_to_bytes(runtime, off); 19141da177e4SLinus Torvalds if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) 19151da177e4SLinus Torvalds return err; 19161da177e4SLinus Torvalds } 19171da177e4SLinus Torvalds } else { 19181da177e4SLinus Torvalds snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels; 19191da177e4SLinus Torvalds snd_assert(runtime->dma_area, return -EFAULT); 19201da177e4SLinus Torvalds for (c = 0; c < channels; ++c, ++bufs) { 19211da177e4SLinus Torvalds char *hwbuf; 19221da177e4SLinus Torvalds char __user *buf; 19231da177e4SLinus Torvalds if (*bufs == NULL) 19241da177e4SLinus Torvalds continue; 19251da177e4SLinus Torvalds 19261da177e4SLinus Torvalds hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff); 19271da177e4SLinus Torvalds buf = *bufs + samples_to_bytes(runtime, off); 19281da177e4SLinus Torvalds if (copy_to_user(buf, hwbuf, samples_to_bytes(runtime, frames))) 19291da177e4SLinus Torvalds return -EFAULT; 19301da177e4SLinus Torvalds } 19311da177e4SLinus Torvalds } 19321da177e4SLinus Torvalds return 0; 19331da177e4SLinus Torvalds } 19341da177e4SLinus Torvalds 1935877211f5STakashi Iwai snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream, 19361da177e4SLinus Torvalds void __user **bufs, 19371da177e4SLinus Torvalds snd_pcm_uframes_t frames) 19381da177e4SLinus Torvalds { 1939877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 19401da177e4SLinus Torvalds int nonblock; 19411da177e4SLinus Torvalds 19421da177e4SLinus Torvalds snd_assert(substream != NULL, return -ENXIO); 19431da177e4SLinus Torvalds runtime = substream->runtime; 19441da177e4SLinus Torvalds snd_assert(runtime != NULL, return -ENXIO); 19451da177e4SLinus Torvalds snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); 19461da177e4SLinus Torvalds if (runtime->status->state == SNDRV_PCM_STATE_OPEN) 19471da177e4SLinus Torvalds return -EBADFD; 19481da177e4SLinus Torvalds 19490df63e44STakashi Iwai nonblock = !!(substream->f_flags & O_NONBLOCK); 19501da177e4SLinus Torvalds if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) 19511da177e4SLinus Torvalds return -EINVAL; 19521da177e4SLinus Torvalds return snd_pcm_lib_read1(substream, (unsigned long)bufs, frames, nonblock, snd_pcm_lib_readv_transfer); 19531da177e4SLinus Torvalds } 19541da177e4SLinus Torvalds 19551da177e4SLinus Torvalds EXPORT_SYMBOL(snd_pcm_lib_readv); 1956