⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pcm_native.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
	snd_pcm_uframes_t stop_threshold;};static int snd_pcm_drop(struct snd_pcm_substream *substream);/* * Drain the stream(s). * When the substream is linked, sync until the draining of all playback streams * is finished. * 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){	struct snd_card *card;	struct snd_pcm_runtime *runtime;	struct snd_pcm_substream *s;	int result = 0;	int i, num_drecs;	struct drain_rec *drec, drec_tmp, *d;	snd_assert(substream != NULL, return -ENXIO);	card = substream->pcm->card;	runtime = substream->runtime;	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)		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) {			snd_power_unlock(card);			return result;		}	}	/* 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);	snd_pcm_stream_lock_irq(substream);	/* resume pause */	if (substream->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;	}	for (;;) {		long tout;		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)				break;		}		if (i == num_drecs)			break; /* yes, all drained */		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) {			if (substream->runtime->status->state == SNDRV_PCM_STATE_SUSPENDED)				result = -ESTRPIPE;			else {				snd_printd("playback drain error (DMA or IRQ trouble?)\n");				snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);				result = -EIO;			}			break;		}	}	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);	snd_power_unlock(card);	return result;}/* * drop ioctl * * Immediately put all linked substreams into SETUP state. */static int snd_pcm_drop(struct snd_pcm_substream *substream){	struct snd_pcm_runtime *runtime;	struct snd_card *card;	int result = 0;		snd_assert(substream != NULL, return -ENXIO);	runtime = substream->runtime;	card = substream->pcm->card;	if (runtime->status->state == SNDRV_PCM_STATE_OPEN ||	    runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)		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)		snd_pcm_pause(substream, 0);	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;}/* WARNING: Don't forget to fput back the file */static struct file *snd_pcm_file_fd(int fd){	struct file *file;	struct inode *inode;	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;	}	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;}/* * PCM link handling */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;	file = snd_pcm_file_fd(fd);	if (!file)		return -EBADFD;	pcm_file = file->private_data;	substream1 = pcm_file->substream;	down_write(&snd_pcm_link_rwsem);	write_lock_irq(&snd_pcm_link_rwlock);	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN ||	    substream->runtime->status->state != substream1->runtime->status->state) {		res = -EBADFD;		goto _end;	}	if (snd_pcm_stream_linked(substream1)) {		res = -EALREADY;		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;		}		spin_lock_init(&substream->group->lock);		INIT_LIST_HEAD(&substream->group->substreams);		list_add_tail(&substream->link_list, &substream->group->substreams);		substream->group->count = 1;	}	list_add_tail(&substream1->link_list, &substream->group->substreams);	substream->group->count++;	substream1->group = substream->group; _end:	write_unlock_irq(&snd_pcm_link_rwlock);	up_write(&snd_pcm_link_rwsem);	fput(file);	return res;}static void relink_to_local(struct snd_pcm_substream *substream){	substream->group = &substream->self_group;	INIT_LIST_HEAD(&substream->self_group.substreams);	list_add_tail(&substream->link_list, &substream->self_group.substreams);}static int snd_pcm_unlink(struct snd_pcm_substream *substream){	struct snd_pcm_substream *s;	int res = 0;	down_write(&snd_pcm_link_rwsem);	write_lock_irq(&snd_pcm_link_rwlock);	if (!snd_pcm_stream_linked(substream)) {		res = -EALREADY;		goto _end;	}	list_del(&substream->link_list);	substream->group->count--;	if (substream->group->count == 1) {	/* detach the last stream, too */		snd_pcm_group_for_each_entry(s, substream) {			relink_to_local(s);			break;		}		kfree(substream->group);	}	relink_to_local(substream);       _end:	write_unlock_irq(&snd_pcm_link_rwlock);	up_write(&snd_pcm_link_rwsem);	return res;}/* * hw configurator */static int snd_pcm_hw_rule_mul(struct snd_pcm_hw_params *params,			       struct snd_pcm_hw_rule *rule){	struct snd_interval t;	snd_interval_mul(hw_param_interval_c(params, rule->deps[0]),		     hw_param_interval_c(params, rule->deps[1]), &t);	return snd_interval_refine(hw_param_interval(params, rule->var), &t);}static int snd_pcm_hw_rule_div(struct snd_pcm_hw_params *params,			       struct snd_pcm_hw_rule *rule){	struct snd_interval t;	snd_interval_div(hw_param_interval_c(params, rule->deps[0]),		     hw_param_interval_c(params, rule->deps[1]), &t);	return snd_interval_refine(hw_param_interval(params, rule->var), &t);}static int snd_pcm_hw_rule_muldivk(struct snd_pcm_hw_params *params,				   struct snd_pcm_hw_rule *rule){	struct snd_interval t;	snd_interval_muldivk(hw_param_interval_c(params, rule->deps[0]),			 hw_param_interval_c(params, rule->deps[1]),			 (unsigned long) rule->private, &t);	return snd_interval_refine(hw_param_interval(params, rule->var), &t);}static int snd_pcm_hw_rule_mulkdiv(struct snd_pcm_hw_params *params,				   struct snd_pcm_hw_rule *rule){	struct snd_interval t;	snd_interval_mulkdiv(hw_param_interval_c(params, rule->deps[0]),			 (unsigned long) rule->private,			 hw_param_interval_c(params, rule->deps[1]), &t);	return snd_interval_refine(hw_param_interval(params, rule->var), &t);}static int snd_pcm_hw_rule_format(struct snd_pcm_hw_params *params,				  struct snd_pcm_hw_rule *rule){	unsigned int k;	struct snd_interval *i = hw_param_interval(params, rule->deps[0]);	struct snd_mask m;	struct snd_mask *mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);	snd_mask_any(&m);	for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) {		int bits;		if (! snd_mask_test(mask, k))			continue;		bits = snd_pcm_format_physical_width(k);		if (bits <= 0)			continue; /* ignore invalid formats */		if ((unsigned)bits < i->min || (unsigned)bits > i->max)			snd_mask_reset(&m, k);	}	return snd_mask_refine(mask, &m);}static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params,				       struct snd_pcm_hw_rule *rule){	struct snd_interval t;	unsigned int k;	t.min = UINT_MAX;	t.max = 0;	t.openmin = 0;	t.openmax = 0;	for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) {		int bits;		if (! snd_mask_test(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), k))			continue;		bits = snd_pcm_format_physical_width(k);		if (bits <= 0)			continue; /* ignore invalid formats */		if (t.min > (unsigned)bits)			t.min = bits;		if (t.max < (unsigned)bits)			t.max = bits;	}	t.integer = 1;	return snd_interval_refine(hw_param_interval(params, rule->var), &t);}#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12#error "Change this table"#endifstatic 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),				 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,					    struct snd_pcm_hw_rule *rule){	struct snd_interval t;	struct snd_pcm_substream *substream = rule->private;	t.min = 0;	t.max = substream->buffer_bytes_max;	t.openmin = 0;	t.openmax = 0;	t.integer = 1;	return snd_interval_refine(hw_param_interval(params, rule->var), &t);}		int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream){	struct snd_pcm_runtime *runtime = substream->runtime;	struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;	int k, err;	for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) {		snd_mask_any(constrs_mask(constrs, k));	}	for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) {		snd_interval_any(constrs_interval(constrs, k));	}	snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_CHANNELS));	snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BUFFER_SIZE));	snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BUFFER_BYTES));	snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_SAMPLE_BITS));	snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_FRAME_BITS));	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,				   snd_pcm_hw_rule_format, NULL,				   SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);	if (err < 0)		return err;	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, 				  snd_pcm_hw_rule_sample_bits, NULL,				  SNDRV_PCM_HW_PARAM_FORMAT, 				  SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);	if (err < 0)		return err;	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, 				  snd_pcm_hw_rule_div, NULL,				  SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1);	if (err < 0)		return err;	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS, 				  snd_pcm_hw_rule_mul, NULL,				  SNDRV_PCM_HW_PARAM_SAMPLE_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1);	if (err < 0)		return err;	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS, 				  snd_pcm_hw_rule_mulkdiv, (void*) 8,				  SNDRV_PCM_HW_PARAM_PERIOD_BYTES, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);	if (err < 0)		return err;	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS, 				  snd_pcm_hw_rule_mulkdiv, (void*) 8,				  SNDRV_PCM_HW_PARAM_BUFFER_BYTES, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1);	if (err < 0)		return err;	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 				  snd_pcm_hw_rule_div, NULL,				  SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);	if (err < 0)		return err;	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 				  snd_pcm_hw_rule_mulkdiv, (void*) 1000000,				  SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_PERIOD_TIME, -1);	if (err < 0)		return err;	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 				  snd_pcm_hw_rule_mulkdiv, (void*) 1000000,				  SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_BUFFER_TIME, -1);	if (err < 0)		return err;	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS, 				  snd_pcm_hw_rule_div, NULL,				  SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);	if (err < 0)		return err;	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 				  snd_pcm_hw_rule_div, NULL,				  SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_PERIODS, -1);	if (err < 0)		return err;

⌨️ 快捷键说明

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