pcm_native.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,359 行 · 第 1/5 页

C
2,359
字号
static int snd_pcm_do_suspend(snd_pcm_substream_t *substream, int state){	snd_pcm_runtime_t *runtime = substream->runtime;	if (runtime->trigger_master != substream)		return 0;	if (runtime->status->suspended_state != SNDRV_PCM_STATE_RUNNING &&	    runtime->status->suspended_state != SNDRV_PCM_STATE_DRAINING)		return 0;	return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_SUSPEND);}static void snd_pcm_post_suspend(snd_pcm_substream_t *substream, int state){	snd_pcm_runtime_t *runtime = substream->runtime;	snd_pcm_trigger_tstamp(substream);	if (substream->timer)		snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MPAUSE, &runtime->trigger_tstamp);	runtime->status->state = SNDRV_PCM_STATE_SUSPENDED;	snd_pcm_tick_set(substream, 0);	wake_up(&runtime->sleep);}static struct action_ops snd_pcm_action_suspend = {	.pre_action = snd_pcm_pre_suspend,	.do_action = snd_pcm_do_suspend,	.post_action = snd_pcm_post_suspend};/** * snd_pcm_suspend */int snd_pcm_suspend(snd_pcm_substream_t *substream){	return snd_pcm_action(&snd_pcm_action_suspend, substream, 0);}/** * snd_pcm_suspend_all */int snd_pcm_suspend_all(snd_pcm_t *pcm){	snd_pcm_substream_t *substream;	int stream, err;	for (stream = 0; stream < 2; stream++) {		for (substream = pcm->streams[stream].substream; substream; substream = substream->next) {			/* FIXME: the open/close code should lock this as well */			if (substream->runtime == NULL)				continue;			snd_pcm_stream_lock(substream);			if (substream->runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {				snd_pcm_stream_unlock(substream);				continue;			}			if ((err = snd_pcm_suspend(substream)) < 0) {				snd_pcm_stream_unlock(substream);				return err;			}			snd_pcm_stream_unlock(substream);		}	}	return 0;}/* resume */static int snd_pcm_pre_resume(snd_pcm_substream_t *substream, int state){	snd_pcm_runtime_t *runtime = substream->runtime;	if (!(runtime->info & SNDRV_PCM_INFO_RESUME))		return -ENOSYS;	runtime->trigger_master = substream;	return 0;}static int snd_pcm_do_resume(snd_pcm_substream_t *substream, int state){	snd_pcm_runtime_t *runtime = substream->runtime;	if (runtime->trigger_master != substream)		return 0;	if (runtime->status->suspended_state != SNDRV_PCM_STATE_RUNNING &&	    runtime->status->suspended_state != SNDRV_PCM_STATE_DRAINING)		return 0;	return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_RESUME);}static void snd_pcm_post_resume(snd_pcm_substream_t *substream, int state){	snd_pcm_runtime_t *runtime = substream->runtime;	snd_pcm_trigger_tstamp(substream);	if (substream->timer)		snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MCONTINUE, &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 = {	.pre_action = snd_pcm_pre_resume,	.do_action = snd_pcm_do_resume,	.post_action = snd_pcm_post_resume};static int snd_pcm_resume(snd_pcm_substream_t *substream){	snd_card_t *card = substream->pcm->card;	int res;	snd_power_lock(card);	if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile)) >= 0)		res = snd_pcm_action_lock_irq(&snd_pcm_action_resume, substream, 0);	snd_power_unlock(card);	return res;}#elsestatic int snd_pcm_resume(snd_pcm_substream_t *substream){	return -ENOSYS;}#endif /* CONFIG_PM */static int snd_pcm_xrun(snd_pcm_substream_t *substream){	snd_card_t *card = substream->pcm->card;	snd_pcm_runtime_t *runtime = substream->runtime;	int result;	snd_power_lock(card);	snd_pcm_stream_lock_irq(substream);       _xrun_recovery:	switch (runtime->status->state) {	case SNDRV_PCM_STATE_XRUN:		result = 0;	/* already there */		break;	case SNDRV_PCM_STATE_RUNNING:		result = snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);		break;	case SNDRV_PCM_STATE_SUSPENDED:		snd_pcm_stream_unlock_irq(substream);		result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);		snd_pcm_stream_lock_irq(substream);		if (result >= 0)			goto _xrun_recovery;		break;	default:		result = -EBADFD;	}	snd_pcm_stream_unlock_irq(substream);	snd_power_unlock(card);	return result;}static int snd_pcm_pre_reset(snd_pcm_substream_t * substream, int state){	snd_pcm_runtime_t *runtime = substream->runtime;	switch (runtime->status->state) {	case SNDRV_PCM_STATE_RUNNING:	case SNDRV_PCM_STATE_PREPARED:	case SNDRV_PCM_STATE_PAUSED:	case SNDRV_PCM_STATE_SUSPENDED:		return 0;	default:		return -EBADFD;	}}static int snd_pcm_do_reset(snd_pcm_substream_t * substream, int state){	snd_pcm_runtime_t *runtime = substream->runtime;	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;	runtime->silence_start = runtime->status->hw_ptr;	runtime->silence_filled = 0;	return 0;}static void snd_pcm_post_reset(snd_pcm_substream_t * substream, int state){	snd_pcm_runtime_t *runtime = substream->runtime;	runtime->control->appl_ptr = runtime->status->hw_ptr;	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&	    runtime->silence_size > 0)		snd_pcm_playback_silence(substream, ULONG_MAX);}static struct action_ops snd_pcm_action_reset = {	.pre_action = snd_pcm_pre_reset,	.do_action = snd_pcm_do_reset,	.post_action = snd_pcm_post_reset};static int snd_pcm_reset(snd_pcm_substream_t *substream){	return snd_pcm_action_nonatomic(&snd_pcm_action_reset, substream, 0);}static int snd_pcm_pre_prepare(snd_pcm_substream_t * substream, int state){	snd_pcm_runtime_t *runtime = substream->runtime;	switch (runtime->status->state) {	case SNDRV_PCM_STATE_OPEN:		return -EBADFD;	case SNDRV_PCM_STATE_RUNNING:		return -EBUSY;	default:		return 0;	}}static int snd_pcm_do_prepare(snd_pcm_substream_t * substream, int state){	int err;	err = substream->ops->prepare(substream);	if (err < 0)		return err;	return snd_pcm_do_reset(substream, 0);}static void snd_pcm_post_prepare(snd_pcm_substream_t * substream, int state){	snd_pcm_runtime_t *runtime = substream->runtime;	runtime->control->appl_ptr = runtime->status->hw_ptr;	runtime->status->state = SNDRV_PCM_STATE_PREPARED;}static struct action_ops snd_pcm_action_prepare = {	.pre_action = snd_pcm_pre_prepare,	.do_action = snd_pcm_do_prepare,	.post_action = snd_pcm_post_prepare};/** * snd_pcm_prepare */int snd_pcm_prepare(snd_pcm_substream_t *substream){	int res;	snd_card_t *card = substream->pcm->card;	snd_power_lock(card);	if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile)) >= 0)		res = snd_pcm_action_nonatomic(&snd_pcm_action_prepare, substream, 0);	snd_power_unlock(card);	return res;}static void snd_pcm_change_state(snd_pcm_substream_t *substream, int state){	struct list_head *pos;	snd_pcm_substream_t *s;	if (snd_pcm_stream_linked(substream)) {		if (!spin_trylock(&substream->group->lock)) {			spin_unlock(&substream->self_group.lock);			spin_lock(&substream->group->lock);			spin_lock(&substream->self_group.lock);		}		snd_pcm_group_for_each(pos, substream) {			s = snd_pcm_group_substream_entry(pos);			if (s != substream)				spin_lock(&s->self_group.lock);			s->runtime->status->state = state;			if (s != substream)				spin_unlock(&s->self_group.lock);		}		spin_unlock(&substream->group->lock);	} else {		substream->runtime->status->state = state;	}}static int snd_pcm_playback_drop(snd_pcm_substream_t *substream);static int snd_pcm_playback_drain(snd_pcm_substream_t * substream){	snd_card_t *card;	snd_pcm_runtime_t *runtime;	int err, result = 0;	wait_queue_t wait;	enum { READY, EXPIRED, SUSPENDED, SIGNALED } state = READY;	snd_pcm_uframes_t stop_threshold;	snd_assert(substream != NULL, return -ENXIO);	snd_assert(substream->stream == SNDRV_PCM_STREAM_PLAYBACK, return -EINVAL);	runtime = substream->runtime;	card = substream->pcm->card;	snd_power_lock(card);	snd_pcm_stream_lock_irq(substream);	/* stop_threshold fixup to avoid endless loop when */	/* stop_threshold > buffer_size */	stop_threshold = runtime->stop_threshold;	if (runtime->stop_threshold > runtime->buffer_size)		runtime->stop_threshold = runtime->buffer_size;	switch (runtime->status->state) {	case SNDRV_PCM_STATE_PAUSED:		snd_pcm_pause(substream, 0);		/* Fall through */	case SNDRV_PCM_STATE_RUNNING:	case SNDRV_PCM_STATE_DRAINING:		break;	case SNDRV_PCM_STATE_SUSPENDED:		snd_pcm_stream_unlock_irq(substream);		result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);		snd_pcm_stream_lock_irq(substream);		if (result >= 0)			snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP);		goto _end;	case SNDRV_PCM_STATE_OPEN:		result = -EBADFD;		goto _end;	case SNDRV_PCM_STATE_PREPARED:		if (!snd_pcm_playback_empty(substream)) {			err = snd_pcm_start(substream);			if (err < 0) {				result = err;				goto _end;			}			break;		}		/* Fall through */	case SNDRV_PCM_STATE_XRUN:		snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP);		/* Fall through */	case SNDRV_PCM_STATE_SETUP:		goto _end;	default: 		break; 	}	if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) {		if (snd_pcm_playback_empty(substream)) {			snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);			goto _end;		}		snd_pcm_change_state(substream, SNDRV_PCM_STATE_DRAINING);	}	if (substream->ffile->f_flags & O_NONBLOCK) {		result = -EAGAIN;		goto _end;	}	init_waitqueue_entry(&wait, current);	add_wait_queue(&runtime->sleep, &wait);	while (1) {		long tout;		if (signal_pending(current)) {			state = SIGNALED;			break;		}		set_current_state(TASK_INTERRUPTIBLE);		snd_pcm_stream_unlock_irq(substream);		snd_power_unlock(card);		tout = schedule_timeout(10 * HZ);		snd_power_lock(card);		snd_pcm_stream_lock_irq(substream);		if (tout == 0) {			state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED;			break;		}		if (runtime->status->state != SNDRV_PCM_STATE_DRAINING) {			state = READY;			break;		}	}	remove_wait_queue(&runtime->sleep, &wait);	switch (state) {	case SIGNALED:		result = -ERESTARTSYS;		goto _end;	case SUSPENDED:		result = -ESTRPIPE;		goto _end;	case EXPIRED:		snd_printd("playback drain error (DMA or IRQ trouble?)\n");		result = -EIO;		goto _end;	default:		break;	}      _end:	runtime->stop_threshold = stop_threshold;	snd_pcm_stream_unlock_irq(substream);	snd_power_unlock(card);	if (state == EXPIRED)		snd_pcm_playback_drop(substream);	return result;}static int snd_pcm_playback_drop(snd_pcm_substream_t *substream){	snd_pcm_runtime_t *runtime = substream->runtime;	snd_card_t *card = substream->pcm->card;	int res = 0;		snd_power_lock(card);	snd_pcm_stream_lock_irq(substream);	switch (runtime->status->state) {	case SNDRV_PCM_STATE_OPEN:		res = -EBADFD;		break;	case SNDRV_PCM_STATE_SETUP:		break;	case SNDRV_PCM_STATE_PAUSED:		snd_pcm_pause(substream, 0);		/* Fall through */	case SNDRV_PCM_STATE_RUNNING:	case SNDRV_PCM_STATE_DRAINING:		if (snd_pcm_update_hw_ptr(substream) >= 0) {			snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);			break;		}		/* Fall through */	case SNDRV_PCM_STATE_PREPARED:	case SNDRV_PCM_STATE_XRUN:		snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP);		break;	case SNDRV_PCM_STATE_SUSPENDED:		snd_pcm_stream_unlock_irq(substream);		res = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);		snd_pcm_stream_lock_irq(substream);		if (res >= 0)			snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP);		break;	default:		break; 	}	runtime->control->appl_ptr = runtime->status->hw_ptr;	snd_pcm_stream_unlock_irq(substream);	snd_power_unlock(card);	return res;}static int snd_pcm_capture_drain(snd_pcm_substream_t * substream){	snd_pcm_runtime_t *runtime = substream->runtime;	snd_card_t *card = substream->pcm->card;	int res = 0;	snd_power_lock(card);	snd_pcm_stream_lock_irq(substream);	switch (runtime->status->state) {	case SNDRV_PCM_STATE_OPEN:		res = -EBADFD;		break;	case SNDRV_PCM_STATE_PREPARED:		snd_pcm_change_state(substream, SNDRV_PCM_STATE_SETUP);		break;	case SNDRV_PCM_STATE_SETUP:	case SNDRV_PCM_STATE_DRAINING:		break;	case SNDRV_PCM_STATE_PAUSED:		snd_pcm_pause(substream, 0);		/* Fall through */	case SNDRV_PCM_STATE_RUNNING:		if (snd_pcm_update_hw_ptr(substream) >= 0) {			snd_pcm_stop(substream, 				     snd_pcm_capture_avail(runtime) > 0 ?				     SNDRV_PCM_STATE_DRAINING : SNDRV_PCM_STATE_SETUP);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?