pcm_native.c

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

C
2,359
字号
		return 0;	}	result = substream->ops->hw_free(substream);	runtime->status->state = SNDRV_PCM_STATE_OPEN;	return result;}static int snd_pcm_sw_params(snd_pcm_substream_t * substream, snd_pcm_sw_params_t *params){	snd_pcm_runtime_t *runtime;	snd_assert(substream != NULL, 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);		return -EBADFD;	}	snd_pcm_stream_unlock_irq(substream);	if (params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST)		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;	} else {		if (params->silence_size > params->silence_threshold)			return -EINVAL;		if (params->silence_threshold > runtime->buffer_size)			return -EINVAL;	}	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);	}	snd_pcm_stream_unlock_irq(substream);	return 0;}static int snd_pcm_sw_params_user(snd_pcm_substream_t * substream, snd_pcm_sw_params_t __user * _params){	snd_pcm_sw_params_t params;	int err;	if (copy_from_user(&params, _params, sizeof(params)))		return -EFAULT;	err = snd_pcm_sw_params(substream, &params);	if (copy_to_user(_params, &params, sizeof(params)))		return -EFAULT;	return err;}int snd_pcm_status(snd_pcm_substream_t *substream,		   snd_pcm_status_t *status){	snd_pcm_runtime_t *runtime = substream->runtime;	snd_pcm_stream_lock_irq(substream);	status->state = runtime->status->state;	status->suspended_state = runtime->status->suspended_state;	if (status->state == SNDRV_PCM_STATE_OPEN)		goto _end;	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)			status->tstamp = runtime->status->tstamp;		else			snd_timestamp_now(&status->tstamp, runtime->tstamp_timespec);	} else		snd_timestamp_now(&status->tstamp, runtime->tstamp_timespec);	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)			status->delay = runtime->buffer_size - status->avail;		else			status->delay = 0;	} else {		status->avail = snd_pcm_capture_avail(runtime);		if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)			status->delay = status->avail;		else			status->delay = 0;	}	status->avail_max = runtime->avail_max;	status->overrange = runtime->overrange;	runtime->avail_max = 0;	runtime->overrange = 0; _end: 	snd_pcm_stream_unlock_irq(substream);	return 0;}static int snd_pcm_status_user(snd_pcm_substream_t * substream, snd_pcm_status_t __user * _status){	snd_pcm_status_t status;	snd_pcm_runtime_t *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)		return res;	if (copy_to_user(_status, &status, sizeof(status)))		return -EFAULT;	return 0;}static int snd_pcm_channel_info(snd_pcm_substream_t * substream, snd_pcm_channel_info_t __user * _info){	snd_pcm_channel_info_t info;	snd_pcm_runtime_t *runtime;	int res;	unsigned int channel;		snd_assert(substream != NULL, return -ENXIO);	if (copy_from_user(&info, _info, sizeof(info)))		return -EFAULT;	channel = info.channel;	runtime = substream->runtime;	snd_pcm_stream_lock_irq(substream);	if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {		snd_pcm_stream_unlock_irq(substream);		return -EBADFD;	}	snd_pcm_stream_unlock_irq(substream);	if (channel >= runtime->channels)		return -EINVAL;	memset(&info, 0, sizeof(info));	info.channel = channel;	res = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_CHANNEL_INFO, &info);	if (res < 0)		return res;	if (copy_to_user(_info, &info, sizeof(info)))		return -EFAULT;	return 0;}static void snd_pcm_trigger_tstamp(snd_pcm_substream_t *substream){	snd_pcm_runtime_t *runtime = substream->runtime;	if (runtime->trigger_master == NULL)		return;	if (runtime->trigger_master == substream) {		snd_timestamp_now(&runtime->trigger_tstamp, runtime->tstamp_timespec);	} else {		snd_pcm_trigger_tstamp(runtime->trigger_master);		runtime->trigger_tstamp = runtime->trigger_master->runtime->trigger_tstamp;	}	runtime->trigger_master = NULL;}struct action_ops {	int (*pre_action)(snd_pcm_substream_t *substream, int state);	int (*do_action)(snd_pcm_substream_t *substream, int state);	void (*post_action)(snd_pcm_substream_t *substream, int state);};/* *  this functions is core for handling of linked stream *  Note: the stream state might be changed also on failure *  Note2: call with calling stream lock + link lock */static int snd_pcm_action_group(struct action_ops *ops,				snd_pcm_substream_t *substream,				int state, int do_lock){	struct list_head *pos;	snd_pcm_substream_t *s = NULL;	int err, res = 0;	snd_pcm_group_for_each(pos, substream) {		s = snd_pcm_group_substream_entry(pos);		if (do_lock && s != substream)			spin_lock(&s->self_group.lock);		res = ops->pre_action(s, state);		if (res < 0)			break;	}	if (res >= 0) {		snd_pcm_group_for_each(pos, substream) {			s = snd_pcm_group_substream_entry(pos);			err = ops->do_action(s, state);			if (err < 0) {				if (res == 0)					res = err;			} else {				ops->post_action(s, state);			}			if (do_lock && s != substream)				spin_unlock(&s->self_group.lock);		}	} else if (do_lock) {		snd_pcm_substream_t *s1;		/* unlock all streams */		snd_pcm_group_for_each(pos, substream) {			s1 = snd_pcm_group_substream_entry(pos);			if (s1 != substream)				spin_unlock(&s1->self_group.lock);			if (s1 == s)	/* end */				break;		}	}	return res;}/* *  Note: call with stream lock */static int snd_pcm_action_single(struct action_ops *ops,				 snd_pcm_substream_t *substream,				 int state){	int res;		res = ops->pre_action(substream, state);	if (res < 0)		return res;	res = ops->do_action(substream, state);	if (res == 0) {		ops->post_action(substream, state);	}	return res;}/* *  Note: call with stream lock */static int snd_pcm_action(struct action_ops *ops,			  snd_pcm_substream_t *substream,			  int state){	int res;	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);		}		res = snd_pcm_action_group(ops, substream, state, 1);		spin_unlock(&substream->group->lock);	} else {		res = snd_pcm_action_single(ops, substream, state);	}	return res;}/* *  Note: don't use any locks before */static int snd_pcm_action_lock_irq(struct action_ops *ops,				   snd_pcm_substream_t *substream,				   int state){	int res;	read_lock_irq(&snd_pcm_link_rwlock);	if (snd_pcm_stream_linked(substream)) {		spin_lock(&substream->group->lock);		spin_lock(&substream->self_group.lock);		res = snd_pcm_action_group(ops, substream, state, 1);		spin_unlock(&substream->self_group.lock);		spin_unlock(&substream->group->lock);	} else {		spin_lock(&substream->self_group.lock);		res = snd_pcm_action_single(ops, substream, state);		spin_unlock(&substream->self_group.lock);	}	read_unlock_irq(&snd_pcm_link_rwlock);	return res;}/* */static int snd_pcm_action_nonatomic(struct action_ops *ops,				    snd_pcm_substream_t *substream,				    int state){	int res;	down_read(&snd_pcm_link_rwsem);	if (snd_pcm_stream_linked(substream))		res = snd_pcm_action_group(ops, substream, state, 0);	else		res = snd_pcm_action_single(ops, substream, state);	up_read(&snd_pcm_link_rwsem);	return res;}static int snd_pcm_pre_start(snd_pcm_substream_t *substream, int state){	snd_pcm_runtime_t *runtime = substream->runtime;	if (runtime->status->state != SNDRV_PCM_STATE_PREPARED)		return -EBADFD;	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&	    !snd_pcm_playback_data(substream))		return -EPIPE;	runtime->trigger_master = substream;	return 0;}static int snd_pcm_do_start(snd_pcm_substream_t *substream, int state){	if (substream->runtime->trigger_master != substream)		return 0;        return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START);}static void snd_pcm_post_start(snd_pcm_substream_t *substream, int state){	snd_pcm_runtime_t *runtime = substream->runtime;	snd_pcm_trigger_tstamp(substream);	runtime->status->state = SNDRV_PCM_STATE_RUNNING;	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);}static struct action_ops snd_pcm_action_start = {	.pre_action = snd_pcm_pre_start,	.do_action = snd_pcm_do_start,	.post_action = snd_pcm_post_start};/** * snd_pcm_start */int snd_pcm_start(snd_pcm_substream_t *substream){	return snd_pcm_action(&snd_pcm_action_start, substream, 0);}static int snd_pcm_pre_stop(snd_pcm_substream_t *substream, int state){	snd_pcm_runtime_t *runtime = substream->runtime;	if (substream->runtime->status->state != SNDRV_PCM_STATE_RUNNING &&	    substream->runtime->status->state != SNDRV_PCM_STATE_DRAINING)		return -EBADFD;	runtime->trigger_master = substream;	return 0;}static int snd_pcm_do_stop(snd_pcm_substream_t *substream, int state){	if (substream->runtime->trigger_master != substream)		return 0;	return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP);}static void snd_pcm_post_stop(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_MSTOP, &runtime->trigger_tstamp);	runtime->status->state = state;	snd_pcm_tick_set(substream, 0);	wake_up(&runtime->sleep);}static struct action_ops snd_pcm_action_stop = {	.pre_action = snd_pcm_pre_stop,	.do_action = snd_pcm_do_stop,	.post_action = snd_pcm_post_stop};/** * snd_pcm_stop */int snd_pcm_stop(snd_pcm_substream_t *substream, int state){	return snd_pcm_action(&snd_pcm_action_stop, substream, state);}static int snd_pcm_pre_pause(snd_pcm_substream_t *substream, int push){	snd_pcm_runtime_t *runtime = substream->runtime;	if (!(runtime->info & SNDRV_PCM_INFO_PAUSE))		return -ENOSYS;	if (push) {		if (runtime->status->state != SNDRV_PCM_STATE_RUNNING)			return -EBADFD;	} else if (runtime->status->state != SNDRV_PCM_STATE_PAUSED)		return -EBADFD;	runtime->trigger_master = substream;	return 0;}static int snd_pcm_do_pause(snd_pcm_substream_t *substream, int push){	if (substream->runtime->trigger_master != substream)		return 0;	return substream->ops->trigger(substream,				       push ? SNDRV_PCM_TRIGGER_PAUSE_PUSH :					      SNDRV_PCM_TRIGGER_PAUSE_RELEASE);}static void snd_pcm_post_pause(snd_pcm_substream_t *substream, int push){	snd_pcm_runtime_t *runtime = substream->runtime;	snd_pcm_trigger_tstamp(substream);	if (push) {		runtime->status->state = SNDRV_PCM_STATE_PAUSED;		if (substream->timer)			snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MPAUSE, &runtime->trigger_tstamp);		snd_pcm_tick_set(substream, 0);		wake_up(&runtime->sleep);	} 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, &runtime->trigger_tstamp);	}}static struct action_ops snd_pcm_action_pause = {	.pre_action = snd_pcm_pre_pause,	.do_action = snd_pcm_do_pause,	.post_action = snd_pcm_post_pause};static int snd_pcm_pause(snd_pcm_substream_t *substream, int push){	return snd_pcm_action(&snd_pcm_action_pause, substream, push);}#ifdef CONFIG_PM/* suspend */static int snd_pcm_pre_suspend(snd_pcm_substream_t *substream, int state){	snd_pcm_runtime_t *runtime = substream->runtime;	if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED)		return -EBUSY;	runtime->status->suspended_state = runtime->status->state;	runtime->trigger_master = substream;	return 0;}

⌨️ 快捷键说明

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