diff options
Diffstat (limited to 'sound/core/pcm_native.c')
| -rw-r--r-- | sound/core/pcm_native.c | 348 |
1 files changed, 213 insertions, 135 deletions
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 25b0641e6b8..b653ab001fb 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -20,12 +20,12 @@ */ #include <linux/mm.h> +#include <linux/module.h> #include <linux/file.h> #include <linux/slab.h> -#include <linux/smp_lock.h> #include <linux/time.h> -#include <linux/pm_qos_params.h> -#include <linux/uio.h> +#include <linux/pm_qos.h> +#include <linux/aio.h> #include <linux/dma-mapping.h> #include <sound/core.h> #include <sound/control.h> @@ -35,6 +35,9 @@ #include <sound/timer.h> #include <sound/minors.h> #include <asm/io.h> +#if defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT) +#include <dma-coherence.h> +#endif /* * Compatibility @@ -139,7 +142,7 @@ int snd_pcm_info_user(struct snd_pcm_substream *substream, #ifdef RULES_DEBUG #define HW_PARAM(v) [SNDRV_PCM_HW_PARAM_##v] = #v -char *snd_pcm_hw_param_names[] = { +static const char * const snd_pcm_hw_param_names[] = { HW_PARAM(ACCESS), HW_PARAM(FORMAT), HW_PARAM(SUBFORMAT), @@ -187,12 +190,12 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, if (!(params->rmask & (1 << k))) continue; #ifdef RULES_DEBUG - printk(KERN_DEBUG "%s = ", snd_pcm_hw_param_names[k]); - printk("%04x%04x%04x%04x -> ", m->bits[3], m->bits[2], m->bits[1], m->bits[0]); + pr_debug("%s = ", snd_pcm_hw_param_names[k]); + pr_cont("%04x%04x%04x%04x -> ", m->bits[3], m->bits[2], m->bits[1], m->bits[0]); #endif changed = snd_mask_refine(m, constrs_mask(constrs, k)); #ifdef RULES_DEBUG - printk("%04x%04x%04x%04x\n", m->bits[3], m->bits[2], m->bits[1], m->bits[0]); + pr_cont("%04x%04x%04x%04x\n", m->bits[3], m->bits[2], m->bits[1], m->bits[0]); #endif if (changed) params->cmask |= 1 << k; @@ -207,21 +210,21 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, if (!(params->rmask & (1 << k))) continue; #ifdef RULES_DEBUG - printk(KERN_DEBUG "%s = ", snd_pcm_hw_param_names[k]); + pr_debug("%s = ", snd_pcm_hw_param_names[k]); if (i->empty) - printk("empty"); + pr_cont("empty"); else - printk("%c%u %u%c", + pr_cont("%c%u %u%c", i->openmin ? '(' : '[', i->min, i->max, i->openmax ? ')' : ']'); - printk(" -> "); + pr_cont(" -> "); #endif changed = snd_interval_refine(i, constrs_interval(constrs, k)); #ifdef RULES_DEBUG if (i->empty) - printk("empty\n"); + pr_cont("empty\n"); else - printk("%c%u %u%c\n", + pr_cont("%c%u %u%c\n", i->openmin ? '(' : '[', i->min, i->max, i->openmax ? ')' : ']'); #endif @@ -252,18 +255,18 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, if (!doit) continue; #ifdef RULES_DEBUG - printk(KERN_DEBUG "Rule %d [%p]: ", k, r->func); + pr_debug("Rule %d [%p]: ", k, r->func); if (r->var >= 0) { - printk("%s = ", snd_pcm_hw_param_names[r->var]); + pr_cont("%s = ", snd_pcm_hw_param_names[r->var]); if (hw_is_mask(r->var)) { m = hw_param_mask(params, r->var); - printk("%x", *m->bits); + pr_cont("%x", *m->bits); } else { i = hw_param_interval(params, r->var); if (i->empty) - printk("empty"); + pr_cont("empty"); else - printk("%c%u %u%c", + pr_cont("%c%u %u%c", i->openmin ? '(' : '[', i->min, i->max, i->openmax ? ')' : ']'); } @@ -272,19 +275,19 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, changed = r->func(params, r); #ifdef RULES_DEBUG if (r->var >= 0) { - printk(" -> "); + pr_cont(" -> "); if (hw_is_mask(r->var)) - printk("%x", *m->bits); + pr_cont("%x", *m->bits); else { if (i->empty) - printk("empty"); + pr_cont("empty"); else - printk("%c%u %u%c", + pr_cont("%c%u %u%c", i->openmin ? '(' : '[', i->min, i->max, i->openmax ? ')' : ']'); } } - printk("\n"); + pr_cont("\n"); #endif rstamps[k] = stamp; if (changed && r->var >= 0) { @@ -315,10 +318,10 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, if (!params->info) params->info = hw->info & ~SNDRV_PCM_INFO_FIFO_IN_FRAMES; if (!params->fifo_size) { - if (snd_mask_min(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT]) == - snd_mask_max(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT]) && - snd_mask_min(¶ms->masks[SNDRV_PCM_HW_PARAM_CHANNELS]) == - snd_mask_max(¶ms->masks[SNDRV_PCM_HW_PARAM_CHANNELS])) { + m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + if (snd_mask_min(m) == snd_mask_max(m) && + snd_interval_min(i) == snd_interval_max(i)) { changed = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_FIFO_SIZE, params); if (changed < 0) @@ -366,6 +369,14 @@ static int period_to_usecs(struct snd_pcm_runtime *runtime) return usecs; } +static void snd_pcm_set_state(struct snd_pcm_substream *substream, int state) +{ + snd_pcm_stream_lock_irq(substream); + if (substream->runtime->status->state != SNDRV_PCM_STATE_DISCONNECTED) + substream->runtime->status->state = state; + snd_pcm_stream_unlock_irq(substream); +} + static int snd_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -388,7 +399,7 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, return -EBADFD; } snd_pcm_stream_unlock_irq(substream); -#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) +#if IS_ENABLED(CONFIG_SND_PCM_OSS) if (!substream->oss.oss) #endif if (atomic_read(&substream->mmap_count)) @@ -420,6 +431,9 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, runtime->info = params->info; runtime->rate_num = params->rate_num; runtime->rate_den = params->rate_den; + runtime->no_period_wakeup = + (params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) && + (params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP); bits = snd_pcm_format_physical_width(runtime->format); runtime->sample_bits = bits; @@ -446,19 +460,19 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, runtime->boundary *= 2; snd_pcm_timer_resolution_change(substream); - runtime->status->state = SNDRV_PCM_STATE_SETUP; + snd_pcm_set_state(substream, SNDRV_PCM_STATE_SETUP); - pm_qos_remove_requirement(PM_QOS_CPU_DMA_LATENCY, - substream->latency_id); + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); if ((usecs = period_to_usecs(runtime)) >= 0) - pm_qos_add_requirement(PM_QOS_CPU_DMA_LATENCY, - substream->latency_id, usecs); + pm_qos_add_request(&substream->latency_pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, usecs); return 0; _error: - /* hardware might be unuseable from this time, + /* hardware might be unusable from this time, so we force application to retry to set the correct hardware parameter settings */ - runtime->status->state = SNDRV_PCM_STATE_OPEN; + snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN); if (substream->ops->hw_free != NULL) substream->ops->hw_free(substream); return err; @@ -506,9 +520,8 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream) return -EBADFD; if (substream->ops->hw_free) result = substream->ops->hw_free(substream); - runtime->status->state = SNDRV_PCM_STATE_OPEN; - pm_qos_remove_requirement(PM_QOS_CPU_DMA_LATENCY, - substream->latency_id); + snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN); + pm_qos_remove_request(&substream->latency_pm_qos_req); return result; } @@ -516,6 +529,7 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream, struct snd_pcm_sw_params *params) { struct snd_pcm_runtime *runtime; + int err; if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; @@ -540,6 +554,7 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream, if (params->silence_threshold > runtime->buffer_size) return -EINVAL; } + err = 0; snd_pcm_stream_lock_irq(substream); runtime->tstamp_mode = params->tstamp_mode; runtime->period_step = params->period_step; @@ -553,10 +568,10 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) snd_pcm_playback_silence(substream, ULONG_MAX); - wake_up(&runtime->sleep); + err = snd_pcm_update_state(substream, runtime); } snd_pcm_stream_unlock_irq(substream); - return 0; + return err; } static int snd_pcm_sw_params_user(struct snd_pcm_substream *substream, @@ -587,6 +602,8 @@ int snd_pcm_status(struct snd_pcm_substream *substream, snd_pcm_update_hw_ptr(substream); if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { status->tstamp = runtime->status->tstamp; + status->audio_tstamp = + runtime->status->audio_tstamp; goto _tstamp_end; } } @@ -860,6 +877,8 @@ static void snd_pcm_post_start(struct snd_pcm_substream *substream, int state) struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_trigger_tstamp(substream); runtime->hw_ptr_jiffies = jiffies; + runtime->hw_ptr_buffer_jiffies = (runtime->buffer_size * HZ) / + runtime->rate; runtime->status->state = state; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) @@ -879,6 +898,8 @@ static struct action_ops snd_pcm_action_start = { /** * snd_pcm_start - start all linked streams * @substream: the PCM substream instance + * + * Return: Zero if successful, or a negative error code. */ int snd_pcm_start(struct snd_pcm_substream *substream) { @@ -917,6 +938,7 @@ static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state) runtime->status->state = state; } wake_up(&runtime->sleep); + wake_up(&runtime->tsleep); } static struct action_ops snd_pcm_action_stop = { @@ -931,8 +953,10 @@ static struct action_ops snd_pcm_action_stop = { * @state: PCM state after stopping the stream * * The state of each stream is then changed to the given state unconditionally. + * + * Return: Zero if successful, or a negative error code. */ -int snd_pcm_stop(struct snd_pcm_substream *substream, int state) +int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t state) { return snd_pcm_action(&snd_pcm_action_stop, substream, state); } @@ -945,6 +969,8 @@ EXPORT_SYMBOL(snd_pcm_stop); * * After stopping, the state is changed to SETUP. * Unlike snd_pcm_stop(), this affects only the given stream. + * + * Return: Zero if succesful, or a negative error code. */ int snd_pcm_drain_done(struct snd_pcm_substream *substream) { @@ -973,8 +999,12 @@ static int snd_pcm_do_pause(struct snd_pcm_substream *substream, int push) { if (substream->runtime->trigger_master != substream) return 0; + /* some drivers might use hw_ptr to recover from the pause - + update the hw_ptr now */ + if (push) + snd_pcm_update_hw_ptr(substream); /* The jiffies check in snd_pcm_update_hw_ptr*() is done by - * a delta betwen the current jiffies, this gives a large enough + * a delta between the current jiffies, this gives a large enough * delta, effectively to skip the check once. */ substream->runtime->hw_ptr_jiffies = jiffies - HZ * 1000; @@ -1002,6 +1032,7 @@ static void snd_pcm_post_pause(struct snd_pcm_substream *substream, int push) SNDRV_TIMER_EVENT_MPAUSE, &runtime->trigger_tstamp); wake_up(&runtime->sleep); + wake_up(&runtime->tsleep); } else { runtime->status->state = SNDRV_PCM_STATE_RUNNING; if (substream->timer) @@ -1059,6 +1090,7 @@ static void snd_pcm_post_suspend(struct snd_pcm_substream *substream, int state) runtime->status->suspended_state = runtime->status->state; runtime->status->state = SNDRV_PCM_STATE_SUSPENDED; wake_up(&runtime->sleep); + wake_up(&runtime->tsleep); } static struct action_ops snd_pcm_action_suspend = { @@ -1072,6 +1104,9 @@ static struct action_ops snd_pcm_action_suspend = { * @substream: the PCM substream * * After this call, all streams are changed to SUSPENDED state. + * + * Return: Zero if successful (or @substream is %NULL), or a negative error + * code. */ int snd_pcm_suspend(struct snd_pcm_substream *substream) { @@ -1094,6 +1129,8 @@ EXPORT_SYMBOL(snd_pcm_suspend); * @pcm: the PCM instance * * After this call, all streams are changed to SUSPENDED state. + * + * Return: Zero if successful (or @pcm is %NULL), or a negative error code. */ int snd_pcm_suspend_all(struct snd_pcm *pcm) { @@ -1304,7 +1341,7 @@ static void snd_pcm_post_prepare(struct snd_pcm_substream *substream, int state) { struct snd_pcm_runtime *runtime = substream->runtime; runtime->control->appl_ptr = runtime->status->hw_ptr; - runtime->status->state = SNDRV_PCM_STATE_PREPARED; + snd_pcm_set_state(substream, SNDRV_PCM_STATE_PREPARED); } static struct action_ops snd_pcm_action_prepare = { @@ -1317,6 +1354,8 @@ static struct action_ops snd_pcm_action_prepare = { * snd_pcm_prepare - prepare the PCM substream to be triggerable * @substream: the PCM substream instance * @file: file to refer f_flags + * + * Return: Zero if successful, or a negative error code. */ static int snd_pcm_prepare(struct snd_pcm_substream *substream, struct file *file) @@ -1344,7 +1383,14 @@ static int snd_pcm_prepare(struct snd_pcm_substream *substream, static int snd_pcm_pre_drain_init(struct snd_pcm_substream *substream, int state) { - substream->runtime->trigger_master = substream; + struct snd_pcm_runtime *runtime = substream->runtime; + switch (runtime->status->state) { + case SNDRV_PCM_STATE_OPEN: + case SNDRV_PCM_STATE_DISCONNECTED: + case SNDRV_PCM_STATE_SUSPENDED: + return -EBADFD; + } + runtime->trigger_master = substream; return 0; } @@ -1363,6 +1409,9 @@ static int snd_pcm_do_drain_init(struct snd_pcm_substream *substream, int state) case SNDRV_PCM_STATE_RUNNING: runtime->status->state = SNDRV_PCM_STATE_DRAINING; break; + case SNDRV_PCM_STATE_XRUN: + runtime->status->state = SNDRV_PCM_STATE_SETUP; + break; default: break; } @@ -1466,20 +1515,34 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream, break; /* all drained */ init_waitqueue_entry(&wait, current); add_wait_queue(&to_check->sleep, &wait); - set_current_state(TASK_INTERRUPTIBLE); snd_pcm_stream_unlock_irq(substream); up_read(&snd_pcm_link_rwsem); snd_power_unlock(card); - tout = schedule_timeout(10 * HZ); + if (runtime->no_period_wakeup) + tout = MAX_SCHEDULE_TIMEOUT; + else { + tout = 10; + if (runtime->rate) { + long t = runtime->period_size * 2 / runtime->rate; + tout = max(t, tout); + } + tout = msecs_to_jiffies(tout * 1000); + } + tout = schedule_timeout_interruptible(tout); snd_power_lock(card); down_read(&snd_pcm_link_rwsem); snd_pcm_stream_lock_irq(substream); remove_wait_queue(&to_check->sleep, &wait); + if (card->shutdown) { + result = -ENODEV; + break; + } if (tout == 0) { if (substream->runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) result = -ESTRPIPE; else { - snd_printd("playback drain error (DMA or IRQ trouble?)\n"); + dev_dbg(substream->pcm->card->dev, + "playback drain error (DMA or IRQ trouble?)\n"); snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); result = -EIO; } @@ -1503,13 +1566,11 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream, static int snd_pcm_drop(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime; - struct snd_card *card; int result = 0; if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; runtime = substream->runtime; - card = substream->pcm->card; if (runtime->status->state == SNDRV_PCM_STATE_OPEN || runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED || @@ -1529,29 +1590,16 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream) } -/* WARNING: Don't forget to fput back the file */ -static struct file *snd_pcm_file_fd(int fd) +static bool is_pcm_file(struct file *file) { - struct file *file; - struct inode *inode; + struct inode *inode = file_inode(file); unsigned int minor; - file = fget(fd); - if (!file) - return NULL; - inode = file->f_path.dentry->d_inode; - if (!S_ISCHR(inode->i_mode) || - imajor(inode) != snd_major) { - fput(file); - return NULL; - } + if (!S_ISCHR(inode->i_mode) || imajor(inode) != snd_major) + return false; minor = iminor(inode); - if (!snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_PLAYBACK) && - !snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_CAPTURE)) { - fput(file); - return NULL; - } - return file; + return snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_PLAYBACK) || + snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_CAPTURE); } /* @@ -1560,15 +1608,24 @@ static struct file *snd_pcm_file_fd(int fd) static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) { int res = 0; - struct file *file; struct snd_pcm_file *pcm_file; struct snd_pcm_substream *substream1; + struct snd_pcm_group *group; + struct fd f = fdget(fd); - file = snd_pcm_file_fd(fd); - if (!file) + if (!f.file) return -EBADFD; - pcm_file = file->private_data; + if (!is_pcm_file(f.file)) { + res = -EBADFD; + goto _badf; + } + pcm_file = f.file->private_data; substream1 = pcm_file->substream; + group = kmalloc(sizeof(*group), GFP_KERNEL); + if (!group) { + res = -ENOMEM; + goto _nolock; + } down_write(&snd_pcm_link_rwsem); write_lock_irq(&snd_pcm_link_rwlock); if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN || @@ -1581,11 +1638,8 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) goto _end; } if (!snd_pcm_stream_linked(substream)) { - substream->group = kmalloc(sizeof(struct snd_pcm_group), GFP_ATOMIC); - if (substream->group == NULL) { - res = -ENOMEM; - goto _end; - } + substream->group = group; + group = NULL; spin_lock_init(&substream->group->lock); INIT_LIST_HEAD(&substream->group->substreams); list_add_tail(&substream->link_list, &substream->group->substreams); @@ -1597,7 +1651,11 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) _end: write_unlock_irq(&snd_pcm_link_rwlock); up_write(&snd_pcm_link_rwsem); - fput(file); + _nolock: + snd_card_unref(substream1->pcm->card); + kfree(group); + _badf: + fdput(f); return res; } @@ -1946,7 +2004,7 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream) if (runtime->dma_bytes) { err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, runtime->dma_bytes); if (err < 0) - return -EINVAL; + return err; } if (!(hw->rates & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))) { @@ -1981,6 +2039,8 @@ void snd_pcm_release_substream(struct snd_pcm_substream *substream) substream->ops->close(substream); substream->hw_opened = 0; } + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); if (substream->pcm_release) { substream->pcm_release(substream); substream->pcm_release = NULL; @@ -2007,7 +2067,7 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream, err = snd_pcm_hw_constraints_init(substream); if (err < 0) { - snd_printd("snd_pcm_hw_constraints_init failed\n"); + pcm_dbg(pcm, "snd_pcm_hw_constraints_init failed\n"); goto error; } @@ -2018,7 +2078,7 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream, err = snd_pcm_hw_constraints_complete(substream); if (err < 0) { - snd_printd("snd_pcm_hw_constraints_complete failed\n"); + pcm_dbg(pcm, "snd_pcm_hw_constraints_complete failed\n"); goto error; } @@ -2034,17 +2094,12 @@ EXPORT_SYMBOL(snd_pcm_open_substream); static int snd_pcm_open_file(struct file *file, struct snd_pcm *pcm, - int stream, - struct snd_pcm_file **rpcm_file) + int stream) { struct snd_pcm_file *pcm_file; struct snd_pcm_substream *substream; - struct snd_pcm_str *str; int err; - if (rpcm_file) - *rpcm_file = NULL; - err = snd_pcm_open_substream(pcm, stream, file, &substream); if (err < 0) return err; @@ -2056,38 +2111,45 @@ static int snd_pcm_open_file(struct file *file, } pcm_file->substream = substream; if (substream->ref_count == 1) { - str = substream->pstr; substream->file = pcm_file; substream->pcm_release = pcm_release_private; } file->private_data = pcm_file; - if (rpcm_file) - *rpcm_file = pcm_file; + return 0; } static int snd_pcm_playback_open(struct inode *inode, struct file *file) { struct snd_pcm *pcm; - + int err = nonseekable_open(inode, file); + if (err < 0) + return err; pcm = snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_PCM_PLAYBACK); - return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK); + err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK); + if (pcm) + snd_card_unref(pcm->card); + return err; } static int snd_pcm_capture_open(struct inode *inode, struct file *file) { struct snd_pcm *pcm; - + int err = nonseekable_open(inode, file); + if (err < 0) + return err; pcm = snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_PCM_CAPTURE); - return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE); + err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE); + if (pcm) + snd_card_unref(pcm->card); + return err; } static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream) { int err; - struct snd_pcm_file *pcm_file; wait_queue_t wait; if (pcm == NULL) { @@ -2105,7 +2167,7 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream) add_wait_queue(&pcm->open_wait, &wait); mutex_lock(&pcm->open_mutex); while (1) { - err = snd_pcm_open_file(file, pcm, stream, &pcm_file); + err = snd_pcm_open_file(file, pcm, stream); if (err >= 0) break; if (err == -EAGAIN) { @@ -2119,6 +2181,10 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream) mutex_unlock(&pcm->open_mutex); schedule(); mutex_lock(&pcm->open_mutex); + if (pcm->card->shutdown) { + err = -ENODEV; + break; + } if (signal_pending(current)) { err = -ERESTARTSYS; break; @@ -2363,6 +2429,7 @@ static int snd_pcm_hwsync(struct snd_pcm_substream *substream) case SNDRV_PCM_STATE_DRAINING: if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) goto __badfd; + /* Fall through */ case SNDRV_PCM_STATE_RUNNING: if ((err = snd_pcm_update_hw_ptr(substream)) < 0) break; @@ -2395,6 +2462,7 @@ static int snd_pcm_delay(struct snd_pcm_substream *substream, case SNDRV_PCM_STATE_DRAINING: if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) goto __badfd; + /* Fall through */ case SNDRV_PCM_STATE_RUNNING: if ((err = snd_pcm_update_hw_ptr(substream)) < 0) break; @@ -2542,7 +2610,7 @@ static int snd_pcm_common_ioctl1(struct file *file, return res; } } - snd_printd("unknown ioctl = 0x%x\n", cmd); + pcm_dbg(substream->pcm, "unknown ioctl = 0x%x\n", cmd); return -ENOTTY; } @@ -2994,17 +3062,15 @@ static const struct vm_operations_struct snd_pcm_vm_ops_status = static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file *file, struct vm_area_struct *area) { - struct snd_pcm_runtime *runtime; long size; if (!(area->vm_flags & VM_READ)) return -EINVAL; - runtime = substream->runtime; size = area->vm_end - area->vm_start; if (size != PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))) return -EINVAL; area->vm_ops = &snd_pcm_vm_ops_status; area->vm_private_data = substream; - area->vm_flags |= VM_RESERVED; + area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; return 0; } @@ -3033,17 +3099,15 @@ static const struct vm_operations_struct snd_pcm_vm_ops_control = static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file *file, struct vm_area_struct *area) { - struct snd_pcm_runtime *runtime; long size; if (!(area->vm_flags & VM_READ)) return -EINVAL; - runtime = substream->runtime; size = area->vm_end - area->vm_start; if (size != PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))) return -EINVAL; area->vm_ops = &snd_pcm_vm_ops_control; area->vm_private_data = substream; - area->vm_flags |= VM_RESERVED; + area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; return 0; } #else /* ! coherent mmap */ @@ -3134,10 +3198,18 @@ static const struct vm_operations_struct snd_pcm_vm_ops_data_fault = { /* * mmap the DMA buffer on RAM */ -static int snd_pcm_default_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *area) -{ - area->vm_flags |= VM_RESERVED; +int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *area) +{ + area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; +#ifdef CONFIG_GENERIC_ALLOCATOR + if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_IRAM) { + area->vm_page_prot = pgprot_writecombine(area->vm_page_prot); + return remap_pfn_range(area, area->vm_start, + substream->dma_buffer.addr >> PAGE_SHIFT, + area->vm_end - area->vm_start, area->vm_page_prot); + } +#endif /* CONFIG_GENERIC_ALLOCATOR */ #ifdef ARCH_HAS_DMA_MMAP_COHERENT if (!substream->ops->page && substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) @@ -3146,11 +3218,16 @@ static int snd_pcm_default_mmap(struct snd_pcm_substream *substream, substream->runtime->dma_area, substream->runtime->dma_addr, area->vm_end - area->vm_start); +#elif defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT) + if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV && + !plat_device_is_coherent(substream->dma_buffer.dev.dev)) + area->vm_page_prot = pgprot_noncached(area->vm_page_prot); #endif /* ARCH_HAS_DMA_MMAP_COHERENT */ /* mmap with fault handler */ area->vm_ops = &snd_pcm_vm_ops_data_fault; return 0; } +EXPORT_SYMBOL_GPL(snd_pcm_lib_default_mmap); /* * mmap the DMA buffer on I/O memory area @@ -3159,20 +3236,10 @@ static int snd_pcm_default_mmap(struct snd_pcm_substream *substream, int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream, struct vm_area_struct *area) { - long size; - unsigned long offset; + struct snd_pcm_runtime *runtime = substream->runtime;; -#ifdef pgprot_noncached area->vm_page_prot = pgprot_noncached(area->vm_page_prot); -#endif - area->vm_flags |= VM_IO; - size = area->vm_end - area->vm_start; - offset = area->vm_pgoff << PAGE_SHIFT; - if (io_remap_pfn_range(area, area->vm_start, - (substream->runtime->dma_addr + offset) >> PAGE_SHIFT, - size, area->vm_page_prot)) - return -EAGAIN; - return 0; + return vm_iomap_memory(area, runtime->dma_addr, runtime->dma_bytes); } EXPORT_SYMBOL(snd_pcm_lib_mmap_iomem); @@ -3218,7 +3285,7 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, if (substream->ops->mmap) err = substream->ops->mmap(substream, area); else - err = snd_pcm_default_mmap(substream, area); + err = snd_pcm_lib_default_mmap(substream, area); if (!err) atomic_inc(&substream->mmap_count); return err; @@ -3258,18 +3325,13 @@ static int snd_pcm_fasync(int fd, struct file * file, int on) struct snd_pcm_file * pcm_file; struct snd_pcm_substream *substream; struct snd_pcm_runtime *runtime; - int err = -ENXIO; - lock_kernel(); pcm_file = file->private_data; substream = pcm_file->substream; if (PCM_RUNTIME_CHECK(substream)) - goto out; + return -ENXIO; runtime = substream->runtime; - err = fasync_helper(fd, file, on, &runtime->fasync); -out: - unlock_kernel(); - return err; + return fasync_helper(fd, file, on, &runtime->fasync); } /* @@ -3389,14 +3451,28 @@ out: #endif /* CONFIG_SND_SUPPORT_OLD_API */ #ifndef CONFIG_MMU -unsigned long dummy_get_unmapped_area(struct file *file, unsigned long addr, - unsigned long len, unsigned long pgoff, - unsigned long flags) -{ - return 0; +static unsigned long snd_pcm_get_unmapped_area(struct file *file, + unsigned long addr, + unsigned long len, + unsigned long pgoff, + unsigned long flags) +{ + struct snd_pcm_file *pcm_file = file->private_data; + struct snd_pcm_substream *substream = pcm_file->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned long offset = pgoff << PAGE_SHIFT; + + switch (offset) { + case SNDRV_PCM_MMAP_OFFSET_STATUS: + return (unsigned long)runtime->status; + case SNDRV_PCM_MMAP_OFFSET_CONTROL: + return (unsigned long)runtime->control; + default: + return (unsigned long)runtime->dma_area + offset; + } } #else -# define dummy_get_unmapped_area NULL +# define snd_pcm_get_unmapped_area NULL #endif /* @@ -3410,12 +3486,13 @@ const struct file_operations snd_pcm_f_ops[2] = { .aio_write = snd_pcm_aio_write, .open = snd_pcm_playback_open, .release = snd_pcm_release, + .llseek = no_llseek, .poll = snd_pcm_playback_poll, .unlocked_ioctl = snd_pcm_playback_ioctl, .compat_ioctl = snd_pcm_ioctl_compat, .mmap = snd_pcm_mmap, .fasync = snd_pcm_fasync, - .get_unmapped_area = dummy_get_unmapped_area, + .get_unmapped_area = snd_pcm_get_unmapped_area, }, { .owner = THIS_MODULE, @@ -3423,11 +3500,12 @@ const struct file_operations snd_pcm_f_ops[2] = { .aio_read = snd_pcm_aio_read, .open = snd_pcm_capture_open, .release = snd_pcm_release, + .llseek = no_llseek, .poll = snd_pcm_capture_poll, .unlocked_ioctl = snd_pcm_capture_ioctl, .compat_ioctl = snd_pcm_ioctl_compat, .mmap = snd_pcm_mmap, .fasync = snd_pcm_fasync, - .get_unmapped_area = dummy_get_unmapped_area, + .get_unmapped_area = snd_pcm_get_unmapped_area, } }; |
