X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=sound%2Fcore%2Fpcm_native.c;h=303ac04ff6e427066bf95fe0932361efd7a94ea3;hb=4434ade8c9334a3ab975d8993de456f06841899e;hp=59b29cd482aed51bb1fd0c5b8553dd3470ccb41a;hpb=1a7fa543cb5d8185d293a03842b33136d8dcc04c;p=safe%2Fjmp%2Flinux-2.6 diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 59b29cd..303ac04 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -1,6 +1,6 @@ /* * Digital Audio (PCM) abstract layer - * Copyright (c) by Jaroslav Kysela + * Copyright (c) by Jaroslav Kysela * * * This program is free software; you can redistribute it and/or modify @@ -19,13 +19,14 @@ * */ -#include #include #include #include +#include #include -#include +#include #include +#include #include #include #include @@ -34,6 +35,9 @@ #include #include #include +#if defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT) +#include +#endif /* * Compatibility @@ -95,7 +99,6 @@ int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info) struct snd_pcm *pcm = substream->pcm; struct snd_pcm_str *pstr = substream->pstr; - snd_assert(substream != NULL, return -ENXIO); memset(info, 0, sizeof(*info)); info->card = pcm->card->number; info->device = pcm->device; @@ -187,7 +190,7 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, if (!(params->rmask & (1 << k))) continue; #ifdef RULES_DEBUG - printk("%s = ", snd_pcm_hw_param_names[k]); + 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]); #endif changed = snd_mask_refine(m, constrs_mask(constrs, k)); @@ -207,7 +210,7 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, if (!(params->rmask & (1 << k))) continue; #ifdef RULES_DEBUG - printk("%s = ", snd_pcm_hw_param_names[k]); + printk(KERN_DEBUG "%s = ", snd_pcm_hw_param_names[k]); if (i->empty) printk("empty"); else @@ -252,7 +255,7 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, if (!doit) continue; #ifdef RULES_DEBUG - printk("Rule %d [%p]: ", k, r->func); + printk(KERN_DEBUG "Rule %d [%p]: ", k, r->func); if (r->var >= 0) { printk("%s = ", snd_pcm_hw_param_names[r->var]); if (hw_is_mask(r->var)) { @@ -313,9 +316,18 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, hw = &substream->runtime->hw; if (!params->info) - params->info = hw->info; - if (!params->fifo_size) - params->fifo_size = hw->fifo_size; + params->info = hw->info & ~SNDRV_PCM_INFO_FIFO_IN_FRAMES; + if (!params->fifo_size) { + 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) + return changed; + } + } params->rmask = 0; return 0; } @@ -328,21 +340,16 @@ static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params; int err; - params = kmalloc(sizeof(*params), GFP_KERNEL); - if (!params) { - err = -ENOMEM; - goto out; - } - if (copy_from_user(params, _params, sizeof(*params))) { - err = -EFAULT; - goto out; - } + params = memdup_user(_params, sizeof(*params)); + if (IS_ERR(params)) + return PTR_ERR(params); + err = snd_pcm_hw_refine(substream, params); if (copy_to_user(_params, params, sizeof(*params))) { if (!err) err = -EFAULT; } -out: + kfree(params); return err; } @@ -370,9 +377,9 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, unsigned int bits; snd_pcm_uframes_t frames; - snd_assert(substream != NULL, return -ENXIO); + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; runtime = substream->runtime; - snd_assert(runtime != NULL, return -ENXIO); snd_pcm_stream_lock_irq(substream); switch (runtime->status->state) { case SNDRV_PCM_STATE_OPEN: @@ -413,7 +420,6 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, runtime->period_size = params_period_size(params); runtime->periods = params_periods(params); runtime->buffer_size = params_buffer_size(params); - runtime->tick_time = params_tick_time(params); runtime->info = params->info; runtime->rate_num = params->rate_num; runtime->rate_den = params->rate_den; @@ -433,9 +439,7 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, /* Default sw params */ runtime->tstamp_mode = SNDRV_PCM_TSTAMP_NONE; runtime->period_step = 1; - runtime->sleep_min = 0; runtime->control->avail_min = runtime->period_size; - runtime->xfer_align = runtime->period_size; runtime->start_threshold = 1; runtime->stop_threshold = runtime->buffer_size; runtime->silence_threshold = 0; @@ -447,9 +451,13 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, snd_pcm_timer_resolution_change(substream); runtime->status->state = SNDRV_PCM_STATE_SETUP; - remove_acceptable_latency(substream->latency_id); + if (substream->latency_pm_qos_req) { + pm_qos_remove_request(substream->latency_pm_qos_req); + substream->latency_pm_qos_req = NULL; + } if ((usecs = period_to_usecs(runtime)) >= 0) - set_acceptable_latency(substream->latency_id, usecs); + substream->latency_pm_qos_req = pm_qos_add_request( + PM_QOS_CPU_DMA_LATENCY, usecs); return 0; _error: /* hardware might be unuseable from this time, @@ -467,21 +475,16 @@ static int snd_pcm_hw_params_user(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params; int err; - params = kmalloc(sizeof(*params), GFP_KERNEL); - if (!params) { - err = -ENOMEM; - goto out; - } - if (copy_from_user(params, _params, sizeof(*params))) { - err = -EFAULT; - goto out; - } + params = memdup_user(_params, sizeof(*params)); + if (IS_ERR(params)) + return PTR_ERR(params); + err = snd_pcm_hw_params(substream, params); if (copy_to_user(_params, params, sizeof(*params))) { if (!err) err = -EFAULT; } -out: + kfree(params); return err; } @@ -491,9 +494,9 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime; int result = 0; - snd_assert(substream != NULL, return -ENXIO); + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; runtime = substream->runtime; - snd_assert(runtime != NULL, return -ENXIO); snd_pcm_stream_lock_irq(substream); switch (runtime->status->state) { case SNDRV_PCM_STATE_SETUP: @@ -509,7 +512,8 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream) if (substream->ops->hw_free) result = substream->ops->hw_free(substream); runtime->status->state = SNDRV_PCM_STATE_OPEN; - remove_acceptable_latency(substream->latency_id); + pm_qos_remove_request(substream->latency_pm_qos_req); + substream->latency_pm_qos_req = NULL; return result; } @@ -517,10 +521,11 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream, struct snd_pcm_sw_params *params) { struct snd_pcm_runtime *runtime; + int err; - snd_assert(substream != NULL, return -ENXIO); + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; runtime = substream->runtime; - snd_assert(runtime != NULL, return -ENXIO); snd_pcm_stream_lock_irq(substream); if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { snd_pcm_stream_unlock_irq(substream); @@ -532,9 +537,6 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream, return -EINVAL; if (params->avail_min == 0) return -EINVAL; - if (params->xfer_align == 0 || - params->xfer_align % runtime->min_align != 0) - return -EINVAL; if (params->silence_size >= runtime->boundary) { if (params->silence_threshold != 0) return -EINVAL; @@ -544,29 +546,24 @@ 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->sleep_min = params->sleep_min; runtime->period_step = params->period_step; runtime->control->avail_min = params->avail_min; runtime->start_threshold = params->start_threshold; runtime->stop_threshold = params->stop_threshold; runtime->silence_threshold = params->silence_threshold; runtime->silence_size = params->silence_size; - runtime->xfer_align = params->xfer_align; params->boundary = runtime->boundary; if (snd_pcm_running(substream)) { - if (runtime->sleep_min) - snd_pcm_tick_prepare(substream); - else - snd_pcm_tick_set(substream, 0); 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, @@ -595,25 +592,27 @@ int snd_pcm_status(struct snd_pcm_substream *substream, status->trigger_tstamp = runtime->trigger_tstamp; if (snd_pcm_running(substream)) { snd_pcm_update_hw_ptr(substream); - if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP) + if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { status->tstamp = runtime->status->tstamp; - else - getnstimeofday(&status->tstamp); - } else - getnstimeofday(&status->tstamp); + goto _tstamp_end; + } + } + snd_pcm_gettime(runtime, &status->tstamp); + _tstamp_end: status->appl_ptr = runtime->control->appl_ptr; status->hw_ptr = runtime->status->hw_ptr; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { status->avail = snd_pcm_playback_avail(runtime); if (runtime->status->state == SNDRV_PCM_STATE_RUNNING || - runtime->status->state == SNDRV_PCM_STATE_DRAINING) + runtime->status->state == SNDRV_PCM_STATE_DRAINING) { status->delay = runtime->buffer_size - status->avail; - else + status->delay += runtime->delay; + } else status->delay = 0; } else { status->avail = snd_pcm_capture_avail(runtime); if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) - status->delay = status->avail; + status->delay = status->avail + runtime->delay; else status->delay = 0; } @@ -630,11 +629,8 @@ static int snd_pcm_status_user(struct snd_pcm_substream *substream, struct snd_pcm_status __user * _status) { struct snd_pcm_status status; - struct snd_pcm_runtime *runtime; int res; - snd_assert(substream != NULL, return -ENXIO); - runtime = substream->runtime; memset(&status, 0, sizeof(status)); res = snd_pcm_status(substream, &status); if (res < 0) @@ -650,7 +646,6 @@ static int snd_pcm_channel_info(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime; unsigned int channel; - snd_assert(substream != NULL, return -ENXIO); channel = info->channel; runtime = substream->runtime; snd_pcm_stream_lock_irq(substream); @@ -688,7 +683,7 @@ static void snd_pcm_trigger_tstamp(struct snd_pcm_substream *substream) if (runtime->trigger_master == NULL) return; if (runtime->trigger_master == substream) { - getnstimeofday(&runtime->trigger_tstamp); + snd_pcm_gettime(runtime, &runtime->trigger_tstamp); } else { snd_pcm_trigger_tstamp(runtime->trigger_master); runtime->trigger_tstamp = runtime->trigger_master->runtime->trigger_tstamp; @@ -871,12 +866,11 @@ 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->status->state = state; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) snd_pcm_playback_silence(substream, ULONG_MAX); - if (runtime->sleep_min) - snd_pcm_tick_prepare(substream); if (substream->timer) snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTART, &runtime->trigger_tstamp); @@ -890,10 +884,8 @@ static struct action_ops snd_pcm_action_start = { }; /** - * snd_pcm_start + * snd_pcm_start - start all linked streams * @substream: the PCM substream instance - * - * Start all linked streams. */ int snd_pcm_start(struct snd_pcm_substream *substream) { @@ -930,9 +922,9 @@ static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state) snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTOP, &runtime->trigger_tstamp); runtime->status->state = state; - snd_pcm_tick_set(substream, 0); } wake_up(&runtime->sleep); + wake_up(&runtime->tsleep); } static struct action_ops snd_pcm_action_stop = { @@ -942,12 +934,11 @@ static struct action_ops snd_pcm_action_stop = { }; /** - * snd_pcm_stop + * snd_pcm_stop - try to stop all running streams in the substream group * @substream: the PCM substream instance * @state: PCM state after stopping the stream * - * Try to stop all running streams in the substream group. - * The state of each stream is changed to the given value after that unconditionally. + * The state of each stream is then changed to the given state unconditionally. */ int snd_pcm_stop(struct snd_pcm_substream *substream, int state) { @@ -957,11 +948,10 @@ int snd_pcm_stop(struct snd_pcm_substream *substream, int state) EXPORT_SYMBOL(snd_pcm_stop); /** - * snd_pcm_drain_done + * snd_pcm_drain_done - stop the DMA only when the given stream is playback * @substream: the PCM substream * - * Stop the DMA only when the given stream is playback. - * The state is changed to SETUP. + * After stopping, the state is changed to SETUP. * Unlike snd_pcm_stop(), this affects only the given stream. */ int snd_pcm_drain_done(struct snd_pcm_substream *substream) @@ -991,6 +981,11 @@ static int snd_pcm_do_pause(struct snd_pcm_substream *substream, int push) { if (substream->runtime->trigger_master != substream) return 0; + /* The jiffies check in snd_pcm_update_hw_ptr*() is done by + * a delta betwen the current jiffies, this gives a large enough + * delta, effectively to skip the check once. + */ + substream->runtime->hw_ptr_jiffies = jiffies - HZ * 1000; return substream->ops->trigger(substream, push ? SNDRV_PCM_TRIGGER_PAUSE_PUSH : SNDRV_PCM_TRIGGER_PAUSE_RELEASE); @@ -1014,12 +1009,10 @@ static void snd_pcm_post_pause(struct snd_pcm_substream *substream, int push) snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MPAUSE, &runtime->trigger_tstamp); - snd_pcm_tick_set(substream, 0); wake_up(&runtime->sleep); + wake_up(&runtime->tsleep); } else { runtime->status->state = SNDRV_PCM_STATE_RUNNING; - if (runtime->sleep_min) - snd_pcm_tick_prepare(substream); if (substream->timer) snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MCONTINUE, @@ -1074,8 +1067,8 @@ static void snd_pcm_post_suspend(struct snd_pcm_substream *substream, int state) &runtime->trigger_tstamp); runtime->status->suspended_state = runtime->status->state; runtime->status->state = SNDRV_PCM_STATE_SUSPENDED; - snd_pcm_tick_set(substream, 0); wake_up(&runtime->sleep); + wake_up(&runtime->tsleep); } static struct action_ops snd_pcm_action_suspend = { @@ -1085,10 +1078,9 @@ static struct action_ops snd_pcm_action_suspend = { }; /** - * snd_pcm_suspend + * snd_pcm_suspend - trigger SUSPEND to all linked streams * @substream: the PCM substream * - * Trigger SUSPEND to all linked streams. * After this call, all streams are changed to SUSPENDED state. */ int snd_pcm_suspend(struct snd_pcm_substream *substream) @@ -1108,10 +1100,9 @@ int snd_pcm_suspend(struct snd_pcm_substream *substream) EXPORT_SYMBOL(snd_pcm_suspend); /** - * snd_pcm_suspend_all + * snd_pcm_suspend_all - trigger SUSPEND to all substreams in the given pcm * @pcm: the PCM instance * - * Trigger SUSPEND to all substreams in the given pcm. * After this call, all streams are changed to SUSPENDED state. */ int snd_pcm_suspend_all(struct snd_pcm *pcm) @@ -1177,8 +1168,6 @@ static void snd_pcm_post_resume(struct snd_pcm_substream *substream, int state) snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MRESUME, &runtime->trigger_tstamp); runtime->status->state = runtime->status->suspended_state; - if (runtime->sleep_min) - snd_pcm_tick_prepare(substream); } static struct action_ops snd_pcm_action_resume = { @@ -1267,7 +1256,6 @@ static int snd_pcm_do_reset(struct snd_pcm_substream *substream, int state) int err = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_RESET, NULL); if (err < 0) return err; - // snd_assert(runtime->status->hw_ptr < runtime->buffer_size, ); runtime->hw_ptr_base = 0; runtime->hw_ptr_interrupt = runtime->status->hw_ptr - runtime->status->hw_ptr % runtime->period_size; @@ -1336,11 +1324,9 @@ static struct action_ops snd_pcm_action_prepare = { }; /** - * snd_pcm_prepare + * snd_pcm_prepare - prepare the PCM substream to be triggerable * @substream: the PCM substream instance * @file: file to refer f_flags - * - * Prepare the PCM substream to be triggerable. */ static int snd_pcm_prepare(struct snd_pcm_substream *substream, struct file *file) @@ -1368,8 +1354,6 @@ static int snd_pcm_prepare(struct snd_pcm_substream *substream, static int snd_pcm_pre_drain_init(struct snd_pcm_substream *substream, int state) { - if (substream->f_flags & O_NONBLOCK) - return -EAGAIN; substream->runtime->trigger_master = substream; return 0; } @@ -1395,10 +1379,10 @@ static int snd_pcm_do_drain_init(struct snd_pcm_substream *substream, int state) } else { /* stop running stream */ if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) { - int state = snd_pcm_capture_avail(runtime) > 0 ? + int new_state = snd_pcm_capture_avail(runtime) > 0 ? SNDRV_PCM_STATE_DRAINING : SNDRV_PCM_STATE_SETUP; - snd_pcm_do_stop(substream, state); - snd_pcm_post_stop(substream, state); + snd_pcm_do_stop(substream, new_state); + snd_pcm_post_stop(substream, new_state); } } return 0; @@ -1414,12 +1398,6 @@ static struct action_ops snd_pcm_action_drain_init = { .post_action = snd_pcm_post_drain_init }; -struct drain_rec { - struct snd_pcm_substream *substream; - wait_queue_t wait; - snd_pcm_uframes_t stop_threshold; -}; - static int snd_pcm_drop(struct snd_pcm_substream *substream); /* @@ -1429,16 +1407,16 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream); * After this call, all streams are supposed to be either SETUP or DRAINING * (capture only) state. */ -static int snd_pcm_drain(struct snd_pcm_substream *substream) +static int snd_pcm_drain(struct snd_pcm_substream *substream, + struct file *file) { struct snd_card *card; struct snd_pcm_runtime *runtime; struct snd_pcm_substream *s; + wait_queue_t wait; int result = 0; - int i, num_drecs; - struct drain_rec *drec, drec_tmp, *d; + int nonblock = 0; - snd_assert(substream != NULL, return -ENXIO); card = substream->pcm->card; runtime = substream->runtime; @@ -1454,70 +1432,59 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream) } } - /* allocate temporary record for drain sync */ - down_read(&snd_pcm_link_rwsem); - if (snd_pcm_stream_linked(substream)) { - drec = kmalloc(substream->group->count * sizeof(*drec), GFP_KERNEL); - if (! drec) { - up_read(&snd_pcm_link_rwsem); - snd_power_unlock(card); - return -ENOMEM; - } - } else - drec = &drec_tmp; - - /* count only playback streams */ - num_drecs = 0; - snd_pcm_group_for_each_entry(s, substream) { - runtime = s->runtime; - if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { - d = &drec[num_drecs++]; - d->substream = s; - init_waitqueue_entry(&d->wait, current); - add_wait_queue(&runtime->sleep, &d->wait); - /* stop_threshold fixup to avoid endless loop when - * stop_threshold > buffer_size - */ - d->stop_threshold = runtime->stop_threshold; - if (runtime->stop_threshold > runtime->buffer_size) - runtime->stop_threshold = runtime->buffer_size; - } - } - up_read(&snd_pcm_link_rwsem); + if (file) { + if (file->f_flags & O_NONBLOCK) + nonblock = 1; + } else if (substream->f_flags & O_NONBLOCK) + nonblock = 1; + down_read(&snd_pcm_link_rwsem); snd_pcm_stream_lock_irq(substream); /* resume pause */ - if (substream->runtime->status->state == SNDRV_PCM_STATE_PAUSED) + if (runtime->status->state == SNDRV_PCM_STATE_PAUSED) snd_pcm_pause(substream, 0); /* pre-start/stop - all running streams are changed to DRAINING state */ result = snd_pcm_action(&snd_pcm_action_drain_init, substream, 0); - if (result < 0) { - snd_pcm_stream_unlock_irq(substream); - goto _error; + if (result < 0) + goto unlock; + /* in non-blocking, we don't wait in ioctl but let caller poll */ + if (nonblock) { + result = -EAGAIN; + goto unlock; } for (;;) { long tout; + struct snd_pcm_runtime *to_check; if (signal_pending(current)) { result = -ERESTARTSYS; break; } - /* all finished? */ - for (i = 0; i < num_drecs; i++) { - runtime = drec[i].substream->runtime; - if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) + /* find a substream to drain */ + to_check = NULL; + snd_pcm_group_for_each_entry(s, substream) { + if (s->stream != SNDRV_PCM_STREAM_PLAYBACK) + continue; + runtime = s->runtime; + if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { + to_check = runtime; break; + } } - if (i == num_drecs) - break; /* yes, all drained */ - + if (!to_check) + 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); snd_power_lock(card); + down_read(&snd_pcm_link_rwsem); snd_pcm_stream_lock_irq(substream); + remove_wait_queue(&to_check->sleep, &wait); if (tout == 0) { if (substream->runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) result = -ESTRPIPE; @@ -1530,18 +1497,9 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream) } } + unlock: snd_pcm_stream_unlock_irq(substream); - - _error: - for (i = 0; i < num_drecs; i++) { - d = &drec[i]; - runtime = d->substream->runtime; - remove_wait_queue(&runtime->sleep, &d->wait); - runtime->stop_threshold = d->stop_threshold; - } - - if (drec != &drec_tmp) - kfree(drec); + up_read(&snd_pcm_link_rwsem); snd_power_unlock(card); return result; @@ -1558,21 +1516,16 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream) struct snd_card *card; int result = 0; - snd_assert(substream != NULL, return -ENXIO); + 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) + runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED || + runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) return -EBADFD; - snd_power_lock(card); - if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { - result = snd_power_wait(card, SNDRV_CTL_POWER_D0); - if (result < 0) - goto _unlock; - } - snd_pcm_stream_lock_irq(substream); /* resume pause */ if (runtime->status->state == SNDRV_PCM_STATE_PAUSED) @@ -1581,8 +1534,7 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream) snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); /* runtime->control->appl_ptr = runtime->status->hw_ptr; */ snd_pcm_stream_unlock_irq(substream); - _unlock: - snd_power_unlock(card); + return result; } @@ -1787,12 +1739,18 @@ static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params, static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000 }; +const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, +}; + static int snd_pcm_hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_pcm_hardware *hw = rule->private; return snd_interval_list(hw_param_interval(params, rule->var), - ARRAY_SIZE(rates), rates, hw->rates); + snd_pcm_known_rates.count, + snd_pcm_known_rates.list, hw->rates); } static int snd_pcm_hw_rule_buffer_bytes_max(struct snd_pcm_hw_params *params, @@ -1952,33 +1910,41 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream) mask |= 1 << SNDRV_PCM_ACCESS_MMAP_COMPLEX; } err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_ACCESS, mask); - snd_assert(err >= 0, return -EINVAL); + if (err < 0) + return err; err = snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT, hw->formats); - snd_assert(err >= 0, return -EINVAL); + if (err < 0) + return err; err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT, 1 << SNDRV_PCM_SUBFORMAT_STD); - snd_assert(err >= 0, return -EINVAL); + if (err < 0) + return err; err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, hw->channels_min, hw->channels_max); - snd_assert(err >= 0, return -EINVAL); + if (err < 0) + return err; err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE, hw->rate_min, hw->rate_max); - snd_assert(err >= 0, return -EINVAL); + if (err < 0) + return err; err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, hw->period_bytes_min, hw->period_bytes_max); - snd_assert(err >= 0, return -EINVAL); + if (err < 0) + return err; err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIODS, hw->periods_min, hw->periods_max); - snd_assert(err >= 0, return -EINVAL); + if (err < 0) + return err; err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, hw->period_bytes_min, hw->buffer_bytes_max); - snd_assert(err >= 0, return -EINVAL); + if (err < 0) + return err; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, snd_pcm_hw_rule_buffer_bytes_max, substream, @@ -1989,7 +1955,8 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream) /* FIXME: remove */ if (runtime->dma_bytes) { err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, runtime->dma_bytes); - snd_assert(err >= 0, return -EINVAL); + if (err < 0) + return -EINVAL; } if (!(hw->rates & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))) { @@ -2001,8 +1968,6 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream) } /* FIXME: this belong to lowlevel */ - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_TICK_TIME, - 1000000 / HZ, 1000000 / HZ); snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); return 0; @@ -2087,8 +2052,8 @@ static int snd_pcm_open_file(struct file *file, struct snd_pcm_str *str; int err; - snd_assert(rpcm_file != NULL, return -EINVAL); - *rpcm_file = NULL; + if (rpcm_file) + *rpcm_file = NULL; err = snd_pcm_open_substream(pcm, stream, file, &substream); if (err < 0) @@ -2106,14 +2071,17 @@ static int snd_pcm_open_file(struct file *file, substream->pcm_release = pcm_release_private; } file->private_data = pcm_file; - *rpcm_file = 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); @@ -2122,7 +2090,9 @@ static int snd_pcm_playback_open(struct inode *inode, struct file *file) 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); @@ -2190,9 +2160,9 @@ static int snd_pcm_release(struct inode *inode, struct file *file) pcm_file = file->private_data; substream = pcm_file->substream; - snd_assert(substream != NULL, return -ENXIO); + if (snd_BUG_ON(!substream)) + return -ENXIO; pcm = substream->pcm; - fasync_helper(-1, file, 0, &substream->runtime->fasync); mutex_lock(&pcm->open_mutex); snd_pcm_release_substream(substream); kfree(pcm_file); @@ -2226,6 +2196,9 @@ static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *subst case SNDRV_PCM_STATE_XRUN: ret = -EPIPE; goto __end; + case SNDRV_PCM_STATE_SUSPENDED: + ret = -ESTRPIPE; + goto __end; default: ret = -EBADFD; goto __end; @@ -2238,15 +2211,10 @@ static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *subst } if (frames > (snd_pcm_uframes_t)hw_avail) frames = hw_avail; - else - frames -= frames % runtime->xfer_align; appl_ptr = runtime->control->appl_ptr - frames; if (appl_ptr < 0) appl_ptr += runtime->boundary; runtime->control->appl_ptr = appl_ptr; - if (runtime->status->state == SNDRV_PCM_STATE_RUNNING && - runtime->sleep_min) - snd_pcm_tick_prepare(substream); ret = frames; __end: snd_pcm_stream_unlock_irq(substream); @@ -2276,6 +2244,9 @@ static snd_pcm_sframes_t snd_pcm_capture_rewind(struct snd_pcm_substream *substr case SNDRV_PCM_STATE_XRUN: ret = -EPIPE; goto __end; + case SNDRV_PCM_STATE_SUSPENDED: + ret = -ESTRPIPE; + goto __end; default: ret = -EBADFD; goto __end; @@ -2288,15 +2259,10 @@ static snd_pcm_sframes_t snd_pcm_capture_rewind(struct snd_pcm_substream *substr } if (frames > (snd_pcm_uframes_t)hw_avail) frames = hw_avail; - else - frames -= frames % runtime->xfer_align; appl_ptr = runtime->control->appl_ptr - frames; if (appl_ptr < 0) appl_ptr += runtime->boundary; runtime->control->appl_ptr = appl_ptr; - if (runtime->status->state == SNDRV_PCM_STATE_RUNNING && - runtime->sleep_min) - snd_pcm_tick_prepare(substream); ret = frames; __end: snd_pcm_stream_unlock_irq(substream); @@ -2327,6 +2293,9 @@ static snd_pcm_sframes_t snd_pcm_playback_forward(struct snd_pcm_substream *subs case SNDRV_PCM_STATE_XRUN: ret = -EPIPE; goto __end; + case SNDRV_PCM_STATE_SUSPENDED: + ret = -ESTRPIPE; + goto __end; default: ret = -EBADFD; goto __end; @@ -2339,15 +2308,10 @@ static snd_pcm_sframes_t snd_pcm_playback_forward(struct snd_pcm_substream *subs } if (frames > (snd_pcm_uframes_t)avail) frames = avail; - else - frames -= frames % runtime->xfer_align; appl_ptr = runtime->control->appl_ptr + frames; if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary) appl_ptr -= runtime->boundary; runtime->control->appl_ptr = appl_ptr; - if (runtime->status->state == SNDRV_PCM_STATE_RUNNING && - runtime->sleep_min) - snd_pcm_tick_prepare(substream); ret = frames; __end: snd_pcm_stream_unlock_irq(substream); @@ -2378,6 +2342,9 @@ static snd_pcm_sframes_t snd_pcm_capture_forward(struct snd_pcm_substream *subst case SNDRV_PCM_STATE_XRUN: ret = -EPIPE; goto __end; + case SNDRV_PCM_STATE_SUSPENDED: + ret = -ESTRPIPE; + goto __end; default: ret = -EBADFD; goto __end; @@ -2390,15 +2357,10 @@ static snd_pcm_sframes_t snd_pcm_capture_forward(struct snd_pcm_substream *subst } if (frames > (snd_pcm_uframes_t)avail) frames = avail; - else - frames -= frames % runtime->xfer_align; appl_ptr = runtime->control->appl_ptr + frames; if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary) appl_ptr -= runtime->boundary; runtime->control->appl_ptr = appl_ptr; - if (runtime->status->state == SNDRV_PCM_STATE_RUNNING && - runtime->sleep_min) - snd_pcm_tick_prepare(substream); ret = frames; __end: snd_pcm_stream_unlock_irq(substream); @@ -2458,6 +2420,7 @@ static int snd_pcm_delay(struct snd_pcm_substream *substream, n = snd_pcm_playback_hw_avail(runtime); else n = snd_pcm_capture_avail(runtime); + n += runtime->delay; break; case SNDRV_PCM_STATE_XRUN: err = -EPIPE; @@ -2513,20 +2476,35 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream, return -EFAULT; return 0; } + +static int snd_pcm_tstamp(struct snd_pcm_substream *substream, int __user *_arg) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int arg; + + if (get_user(arg, _arg)) + return -EFAULT; + if (arg < 0 || arg > SNDRV_PCM_TSTAMP_TYPE_LAST) + return -EINVAL; + runtime->tstamp_type = SNDRV_PCM_TSTAMP_TYPE_GETTIMEOFDAY; + if (arg == SNDRV_PCM_TSTAMP_TYPE_MONOTONIC) + runtime->tstamp_type = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC; + return 0; +} static int snd_pcm_common_ioctl1(struct file *file, struct snd_pcm_substream *substream, unsigned int cmd, void __user *arg) { - snd_assert(substream != NULL, return -ENXIO); - switch (cmd) { case SNDRV_PCM_IOCTL_PVERSION: return put_user(SNDRV_PCM_VERSION, (int __user *)arg) ? -EFAULT : 0; case SNDRV_PCM_IOCTL_INFO: return snd_pcm_info_user(substream, arg); - case SNDRV_PCM_IOCTL_TSTAMP: /* just for compatibility */ + case SNDRV_PCM_IOCTL_TSTAMP: /* just for compatibility */ return 0; + case SNDRV_PCM_IOCTL_TTSTAMP: + return snd_pcm_tstamp(substream, arg); case SNDRV_PCM_IOCTL_HW_REFINE: return snd_pcm_hw_refine_user(substream, arg); case SNDRV_PCM_IOCTL_HW_PARAMS: @@ -2566,7 +2544,7 @@ static int snd_pcm_common_ioctl1(struct file *file, return snd_pcm_hw_params_old_user(substream, arg); #endif case SNDRV_PCM_IOCTL_DRAIN: - return snd_pcm_drain(substream); + return snd_pcm_drain(substream, file); case SNDRV_PCM_IOCTL_DROP: return snd_pcm_drop(substream); case SNDRV_PCM_IOCTL_PAUSE: @@ -2586,8 +2564,10 @@ static int snd_pcm_playback_ioctl1(struct file *file, struct snd_pcm_substream *substream, unsigned int cmd, void __user *arg) { - snd_assert(substream != NULL, return -ENXIO); - snd_assert(substream->stream == SNDRV_PCM_STREAM_PLAYBACK, return -EINVAL); + if (snd_BUG_ON(!substream)) + return -ENXIO; + if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_PLAYBACK)) + return -EINVAL; switch (cmd) { case SNDRV_PCM_IOCTL_WRITEI_FRAMES: { @@ -2620,13 +2600,11 @@ static int snd_pcm_playback_ioctl1(struct file *file, return -EFAULT; if (copy_from_user(&xfern, _xfern, sizeof(xfern))) return -EFAULT; - bufs = kmalloc(sizeof(void *) * runtime->channels, GFP_KERNEL); - if (bufs == NULL) - return -ENOMEM; - if (copy_from_user(bufs, xfern.bufs, sizeof(void *) * runtime->channels)) { - kfree(bufs); - return -EFAULT; - } + + bufs = memdup_user(xfern.bufs, + sizeof(void *) * runtime->channels); + if (IS_ERR(bufs)) + return PTR_ERR(bufs); result = snd_pcm_lib_writev(substream, bufs, xfern.frames); kfree(bufs); __put_user(result, &_xfern->result); @@ -2666,8 +2644,10 @@ static int snd_pcm_capture_ioctl1(struct file *file, struct snd_pcm_substream *substream, unsigned int cmd, void __user *arg) { - snd_assert(substream != NULL, return -ENXIO); - snd_assert(substream->stream == SNDRV_PCM_STREAM_CAPTURE, return -EINVAL); + if (snd_BUG_ON(!substream)) + return -ENXIO; + if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_CAPTURE)) + return -EINVAL; switch (cmd) { case SNDRV_PCM_IOCTL_READI_FRAMES: { @@ -2700,13 +2680,11 @@ static int snd_pcm_capture_ioctl1(struct file *file, return -EFAULT; if (copy_from_user(&xfern, _xfern, sizeof(xfern))) return -EFAULT; - bufs = kmalloc(sizeof(void *) * runtime->channels, GFP_KERNEL); - if (bufs == NULL) - return -ENOMEM; - if (copy_from_user(bufs, xfern.bufs, sizeof(void *) * runtime->channels)) { - kfree(bufs); - return -EFAULT; - } + + bufs = memdup_user(xfern.bufs, + sizeof(void *) * runtime->channels); + if (IS_ERR(bufs)) + return PTR_ERR(bufs); result = snd_pcm_lib_readv(substream, bufs, xfern.frames); kfree(bufs); __put_user(result, &_xfern->result); @@ -2806,7 +2784,8 @@ static ssize_t snd_pcm_read(struct file *file, char __user *buf, size_t count, pcm_file = file->private_data; substream = pcm_file->substream; - snd_assert(substream != NULL, return -ENXIO); + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; runtime = substream->runtime; if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; @@ -2829,21 +2808,17 @@ static ssize_t snd_pcm_write(struct file *file, const char __user *buf, pcm_file = file->private_data; substream = pcm_file->substream; - snd_assert(substream != NULL, result = -ENXIO; goto end); + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; runtime = substream->runtime; - if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { - result = -EBADFD; - goto end; - } - if (!frame_aligned(runtime, count)) { - result = -EINVAL; - goto end; - } + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; + if (!frame_aligned(runtime, count)) + return -EINVAL; count = bytes_to_frames(runtime, count); result = snd_pcm_lib_write(substream, buf, count); if (result > 0) result = frames_to_bytes(runtime, result); - end: return result; } @@ -2861,7 +2836,8 @@ static ssize_t snd_pcm_aio_read(struct kiocb *iocb, const struct iovec *iov, pcm_file = iocb->ki_filp->private_data; substream = pcm_file->substream; - snd_assert(substream != NULL, return -ENXIO); + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; runtime = substream->runtime; if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; @@ -2895,17 +2871,14 @@ static ssize_t snd_pcm_aio_write(struct kiocb *iocb, const struct iovec *iov, pcm_file = iocb->ki_filp->private_data; substream = pcm_file->substream; - snd_assert(substream != NULL, result = -ENXIO; goto end); + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; runtime = substream->runtime; - if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { - result = -EBADFD; - goto end; - } + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; if (nr_segs > 128 || nr_segs != runtime->channels || - !frame_aligned(runtime, iov->iov_len)) { - result = -EINVAL; - goto end; - } + !frame_aligned(runtime, iov->iov_len)) + return -EINVAL; frames = bytes_to_samples(runtime, iov->iov_len); bufs = kmalloc(sizeof(void *) * nr_segs, GFP_KERNEL); if (bufs == NULL) @@ -2916,7 +2889,6 @@ static ssize_t snd_pcm_aio_write(struct kiocb *iocb, const struct iovec *iov, if (result > 0) result = frames_to_bytes(runtime, result); kfree(bufs); - end: return result; } @@ -2931,7 +2903,8 @@ static unsigned int snd_pcm_playback_poll(struct file *file, poll_table * wait) pcm_file = file->private_data; substream = pcm_file->substream; - snd_assert(substream != NULL, return -ENXIO); + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; runtime = substream->runtime; poll_wait(file, &runtime->sleep, wait); @@ -2969,7 +2942,8 @@ static unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait) pcm_file = file->private_data; substream = pcm_file->substream; - snd_assert(substream != NULL, return -ENXIO); + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; runtime = substream->runtime; poll_wait(file, &runtime->sleep, wait); @@ -3012,26 +2986,23 @@ static unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait) /* * mmap status record */ -static struct page * snd_pcm_mmap_status_nopage(struct vm_area_struct *area, - unsigned long address, int *type) +static int snd_pcm_mmap_status_fault(struct vm_area_struct *area, + struct vm_fault *vmf) { struct snd_pcm_substream *substream = area->vm_private_data; struct snd_pcm_runtime *runtime; - struct page * page; if (substream == NULL) - return NOPAGE_SIGBUS; + return VM_FAULT_SIGBUS; runtime = substream->runtime; - page = virt_to_page(runtime->status); - get_page(page); - if (type) - *type = VM_FAULT_MINOR; - return page; + vmf->page = virt_to_page(runtime->status); + get_page(vmf->page); + return 0; } -static struct vm_operations_struct snd_pcm_vm_ops_status = +static const struct vm_operations_struct snd_pcm_vm_ops_status = { - .nopage = snd_pcm_mmap_status_nopage, + .fault = snd_pcm_mmap_status_fault, }; static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file *file, @@ -3042,7 +3013,6 @@ static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file if (!(area->vm_flags & VM_READ)) return -EINVAL; runtime = substream->runtime; - snd_assert(runtime != NULL, return -EAGAIN); size = area->vm_end - area->vm_start; if (size != PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))) return -EINVAL; @@ -3055,26 +3025,23 @@ static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file /* * mmap control record */ -static struct page * snd_pcm_mmap_control_nopage(struct vm_area_struct *area, - unsigned long address, int *type) +static int snd_pcm_mmap_control_fault(struct vm_area_struct *area, + struct vm_fault *vmf) { struct snd_pcm_substream *substream = area->vm_private_data; struct snd_pcm_runtime *runtime; - struct page * page; if (substream == NULL) - return NOPAGE_SIGBUS; + return VM_FAULT_SIGBUS; runtime = substream->runtime; - page = virt_to_page(runtime->control); - get_page(page); - if (type) - *type = VM_FAULT_MINOR; - return page; + vmf->page = virt_to_page(runtime->control); + get_page(vmf->page); + return 0; } -static struct vm_operations_struct snd_pcm_vm_ops_control = +static const struct vm_operations_struct snd_pcm_vm_ops_control = { - .nopage = snd_pcm_mmap_control_nopage, + .fault = snd_pcm_mmap_control_fault, }; static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file *file, @@ -3085,7 +3052,6 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file if (!(area->vm_flags & VM_READ)) return -EINVAL; runtime = substream->runtime; - snd_assert(runtime != NULL, return -EAGAIN); size = area->vm_end - area->vm_start; if (size != PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))) return -EINVAL; @@ -3110,59 +3076,97 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file } #endif /* coherent mmap */ +static inline struct page * +snd_pcm_default_page_ops(struct snd_pcm_substream *substream, unsigned long ofs) +{ + void *vaddr = substream->runtime->dma_area + ofs; +#if defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT) + if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) + return virt_to_page(CAC_ADDR(vaddr)); +#endif +#if defined(CONFIG_PPC32) && defined(CONFIG_NOT_COHERENT_CACHE) + if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) { + dma_addr_t addr = substream->runtime->dma_addr + ofs; + addr -= get_dma_offset(substream->dma_buffer.dev.dev); + /* assume dma_handle set via pfn_to_phys() in + * mm/dma-noncoherent.c + */ + return pfn_to_page(addr >> PAGE_SHIFT); + } +#endif + return virt_to_page(vaddr); +} + /* - * nopage callback for mmapping a RAM page + * fault callback for mmapping a RAM page */ -static struct page *snd_pcm_mmap_data_nopage(struct vm_area_struct *area, - unsigned long address, int *type) +static int snd_pcm_mmap_data_fault(struct vm_area_struct *area, + struct vm_fault *vmf) { struct snd_pcm_substream *substream = area->vm_private_data; struct snd_pcm_runtime *runtime; unsigned long offset; struct page * page; - void *vaddr; size_t dma_bytes; if (substream == NULL) - return NOPAGE_SIGBUS; + return VM_FAULT_SIGBUS; runtime = substream->runtime; - offset = area->vm_pgoff << PAGE_SHIFT; - offset += address - area->vm_start; - snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_SIGBUS); + offset = vmf->pgoff << PAGE_SHIFT; dma_bytes = PAGE_ALIGN(runtime->dma_bytes); if (offset > dma_bytes - PAGE_SIZE) - return NOPAGE_SIGBUS; - if (substream->ops->page) { + return VM_FAULT_SIGBUS; + if (substream->ops->page) page = substream->ops->page(substream, offset); - if (! page) - return NOPAGE_OOM; /* XXX: is this really due to OOM? */ - } else { - vaddr = runtime->dma_area + offset; - page = virt_to_page(vaddr); - } + else + page = snd_pcm_default_page_ops(substream, offset); + if (!page) + return VM_FAULT_SIGBUS; get_page(page); - if (type) - *type = VM_FAULT_MINOR; - return page; + vmf->page = page; + return 0; } -static struct vm_operations_struct snd_pcm_vm_ops_data = -{ +static const struct vm_operations_struct snd_pcm_vm_ops_data = { + .open = snd_pcm_mmap_data_open, + .close = snd_pcm_mmap_data_close, +}; + +static const struct vm_operations_struct snd_pcm_vm_ops_data_fault = { .open = snd_pcm_mmap_data_open, .close = snd_pcm_mmap_data_close, - .nopage = snd_pcm_mmap_data_nopage, + .fault = snd_pcm_mmap_data_fault, }; +#ifndef ARCH_HAS_DMA_MMAP_COHERENT +/* This should be defined / handled globally! */ +#ifdef CONFIG_ARM +#define ARCH_HAS_DMA_MMAP_COHERENT +#endif +#endif + /* * mmap the DMA buffer on RAM */ static int snd_pcm_default_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *area) { - area->vm_ops = &snd_pcm_vm_ops_data; - area->vm_private_data = substream; area->vm_flags |= VM_RESERVED; - atomic_inc(&substream->mmap_count); +#ifdef ARCH_HAS_DMA_MMAP_COHERENT + if (!substream->ops->page && + substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) + return dma_mmap_coherent(substream->dma_buffer.dev.dev, + area, + 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; } @@ -3170,23 +3174,13 @@ static int snd_pcm_default_mmap(struct snd_pcm_substream *substream, * mmap the DMA buffer on I/O memory area */ #if SNDRV_PCM_INFO_MMAP_IOMEM -static struct vm_operations_struct snd_pcm_vm_ops_data_mmio = -{ - .open = snd_pcm_mmap_data_open, - .close = snd_pcm_mmap_data_close, -}; - int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream, struct vm_area_struct *area) { long size; unsigned long offset; -#ifdef pgprot_noncached area->vm_page_prot = pgprot_noncached(area->vm_page_prot); -#endif - area->vm_ops = &snd_pcm_vm_ops_data_mmio; - area->vm_private_data = substream; area->vm_flags |= VM_IO; size = area->vm_end - area->vm_start; offset = area->vm_pgoff << PAGE_SHIFT; @@ -3194,13 +3188,21 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream, (substream->runtime->dma_addr + offset) >> PAGE_SHIFT, size, area->vm_page_prot)) return -EAGAIN; - atomic_inc(&substream->mmap_count); return 0; } EXPORT_SYMBOL(snd_pcm_lib_mmap_iomem); #endif /* SNDRV_PCM_INFO_MMAP */ +/* mmap callback with pgprot_noncached */ +int snd_pcm_lib_mmap_noncached(struct snd_pcm_substream *substream, + struct vm_area_struct *area) +{ + area->vm_page_prot = pgprot_noncached(area->vm_page_prot); + return snd_pcm_default_mmap(substream, area); +} +EXPORT_SYMBOL(snd_pcm_lib_mmap_noncached); + /* * mmap DMA buffer */ @@ -3211,6 +3213,7 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, long size; unsigned long offset; size_t dma_bytes; + int err; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (!(area->vm_flags & (VM_WRITE|VM_READ))) @@ -3220,7 +3223,6 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, return -EINVAL; } runtime = substream->runtime; - snd_assert(runtime != NULL, return -EAGAIN); if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) @@ -3236,10 +3238,15 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, if (offset > dma_bytes - size) return -EINVAL; + area->vm_ops = &snd_pcm_vm_ops_data; + area->vm_private_data = substream; if (substream->ops->mmap) - return substream->ops->mmap(substream, area); + err = substream->ops->mmap(substream, area); else - return snd_pcm_default_mmap(substream, area); + err = snd_pcm_default_mmap(substream, area); + if (!err) + atomic_inc(&substream->mmap_count); + return err; } EXPORT_SYMBOL(snd_pcm_mmap_data); @@ -3252,7 +3259,8 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area) pcm_file = file->private_data; substream = pcm_file->substream; - snd_assert(substream != NULL, return -ENXIO); + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; offset = area->vm_pgoff << PAGE_SHIFT; switch (offset) { @@ -3275,17 +3283,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; pcm_file = file->private_data; substream = pcm_file->substream; - snd_assert(substream != NULL, return -ENXIO); + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; runtime = substream->runtime; - - err = fasync_helper(fd, file, on, &runtime->fasync); - if (err < 0) - return err; - return 0; + return fasync_helper(fd, file, on, &runtime->fasync); } /* @@ -3351,18 +3355,12 @@ static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream, int err; params = kmalloc(sizeof(*params), GFP_KERNEL); - if (!params) { - err = -ENOMEM; - goto out; - } - oparams = kmalloc(sizeof(*oparams), GFP_KERNEL); - if (!oparams) { - err = -ENOMEM; - goto out; - } + if (!params) + return -ENOMEM; - if (copy_from_user(oparams, _oparams, sizeof(*oparams))) { - err = -EFAULT; + oparams = memdup_user(_oparams, sizeof(*oparams)); + if (IS_ERR(oparams)) { + err = PTR_ERR(oparams); goto out; } snd_pcm_hw_convert_from_old_params(params, oparams); @@ -3372,9 +3370,10 @@ static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream, if (!err) err = -EFAULT; } + + kfree(oparams); out: kfree(params); - kfree(oparams); return err; } @@ -3386,17 +3385,12 @@ static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream, int err; params = kmalloc(sizeof(*params), GFP_KERNEL); - if (!params) { - err = -ENOMEM; - goto out; - } - oparams = kmalloc(sizeof(*oparams), GFP_KERNEL); - if (!oparams) { - err = -ENOMEM; - goto out; - } - if (copy_from_user(oparams, _oparams, sizeof(*oparams))) { - err = -EFAULT; + if (!params) + return -ENOMEM; + + oparams = memdup_user(_oparams, sizeof(*oparams)); + if (IS_ERR(oparams)) { + err = PTR_ERR(oparams); goto out; } snd_pcm_hw_convert_from_old_params(params, oparams); @@ -3406,13 +3400,39 @@ static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream, if (!err) err = -EFAULT; } + + kfree(oparams); out: kfree(params); - kfree(oparams); return err; } #endif /* CONFIG_SND_SUPPORT_OLD_API */ +#ifndef CONFIG_MMU +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 snd_pcm_get_unmapped_area NULL +#endif + /* * Register section */ @@ -3424,11 +3444,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 = snd_pcm_get_unmapped_area, }, { .owner = THIS_MODULE, @@ -3436,10 +3458,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 = snd_pcm_get_unmapped_area, } };