alsa音频架构3-pcm

本文深入探讨了Linux ALSA架构中的PCM组件,包括PCM结构体、PCM流、PCM子流的创建与管理,以及snd_pcm_dev_register的注册过程。在PCM设备的操作中,详细阐述了open、ioctl、mmap方法的调用流程,特别是在音频录制和回放过程中的帧写入和同步指针操作。此外,还介绍了snd_pcm_file结构体和pcm runtime的概念,以及pcm子流的open方法和命令控制机制。
摘要由CSDN通过智能技术生成

 

第十四部分 snd_pcm

1.pcm结构体

struct snd_pcm {
	struct snd_card *card;	//声卡
	struct list_head list;
	int device;	//设备号
	unsigned int info_flags;
	unsigned short dev_class;
	unsigned short dev_subclass;
	char id[64];	//id字串
	char name[80];	//名字
	struct snd_pcm_str streams[2];	//pcm流数组 0-回放 1-捕捉
	struct mutex open_mutex;
	wait_queue_head_t open_wait;	//PCM打开等待队列
	void *private_data;
	void (*private_free) (struct snd_pcm *pcm);	//(soc_new_pcm函数)platform->driver->pcm_free
	struct device *dev; //设备文件
};


2.pcm流

struct snd_pcm_str {
	int stream;				//流类型
	struct snd_pcm *pcm;	//捆绑的pcm结构体
	/* -- substreams -- */
	unsigned int substream_count;	//子流个数
	unsigned int substream_opened;	//子流打开标志
	struct snd_pcm_substream *substream;	//pcm子流
#ifdef CONFIG_SND_VERBOSE_PROCFS
	struct snd_info_entry *proc_root;
	struct snd_info_entry *proc_info_entry;
#endif
};


3.pcm子流

struct snd_pcm_substream {
	struct snd_pcm *pcm;	//捆绑pcm结构体
	struct snd_pcm_str *pstr;	//捆绑的pcm流
	void *private_data;		/* copied from pcm->private_data */
	int number;
	char name[32];			//子流名
	int stream;			//流类型
	struct pm_qos_request_list latency_pm_qos_req; /* pm_qos request */
	size_t buffer_bytes_max;	/* limit ring buffer size */
	struct snd_dma_buffer dma_buffer;
	unsigned int dma_buf_id;
	size_t dma_max;
	/* -- hardware operations -- */
	struct snd_pcm_ops *ops;	//pcm操作函数集
	/* -- runtime information -- */
	struct snd_pcm_runtime *runtime;	//pcm runtime
        /* -- timer section -- */
	struct snd_timer *timer;		//声卡定时器
	unsigned timer_running: 1;	/* time is running */
	/* -- next substream -- */
	struct snd_pcm_substream *next;	//下一个pcm子流
	/* -- linked substreams -- */
	struct list_head link_list;	/* linked list member */
	struct snd_pcm_group self_group;	/* fake group for non linked substream (with substream lock inside) */
	struct snd_pcm_group *group;		/* pointer to current group */
	/* -- assigned files -- */
	void *file;	//指向 snd_pcm_file结构体
	int ref_count;
	atomic_t mmap_count;
	unsigned int f_flags;
	void (*pcm_release)(struct snd_pcm_substream *);
	struct pid *pid;
#ifdef CONFIG_SND_VERBOSE_PROCFS
	struct snd_info_entry *proc_root;
	struct snd_info_entry *proc_info_entry;
	struct snd_info_entry *proc_hw_params_entry;
	struct snd_info_entry *proc_sw_params_entry;
	struct snd_info_entry *proc_status_entry;
	struct snd_info_entry *proc_prealloc_entry;
	struct snd_info_entry *proc_prealloc_max_entry;
#endif
	/* misc flags */
	unsigned int hw_opened: 1;
};


4.snd_pcm_new 创建PCM设备

int snd_pcm_new(struct snd_card *card, const char *id, int device,int playback_count, int capture_count,struct snd_pcm ** rpcm)
{
	struct snd_pcm *pcm;
	int err;
	static struct snd_device_ops ops = {
		.dev_free = snd_pcm_dev_free,				//释放
		.dev_register =	snd_pcm_dev_register,		//注册
		.dev_disconnect = snd_pcm_dev_disconnect,	//断开连接
	};
	if (snd_BUG_ON(!card))
		return -ENXIO;
	if (rpcm)
		*rpcm = NULL;
	pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);	//分配snd_pcm内存
	if (pcm == NULL) {
		snd_printk(KERN_ERR "Cannot allocate PCM\n");
		return -ENOMEM;
	}
	pcm->card = card;	//捆绑声卡
	pcm->device = device;	//设备号
	if (id)	//id识别字串
		strlcpy(pcm->id, id, sizeof(pcm->id));	//填充pcm结构体id字串
	if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) {	//回放 SNDRV_PCM_STREAM_PLAYBACK=0
		snd_pcm_free(pcm);
		return err;
	}
	if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) < 0) {	//捕捉 SNDRV_PCM_STREAM_CAPTURE=1
		snd_pcm_free(pcm);
		return err;
	}
	mutex_init(&pcm->open_mutex);
	init_waitqueue_head(&pcm->open_wait);	//初始化PCM打开等待队列
	if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) {	//创建声卡设备,声卡设备的device_data指向snd_pcm结构体对象
		snd_pcm_free(pcm);
		return err;
	}
	if (rpcm)
		*rpcm = pcm;
	return 0;
}
EXPORT_SYMBOL(snd_pcm_new);


5.创建pcm子流

int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
{
	int idx, err;
	struct snd_pcm_str *pstr = &pcm->streams[stream];	//获取pcm流
	struct snd_pcm_substream *substream, *prev;

#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
	mutex_init(&pstr->oss.setup_mutex);
#endif
	pstr->stream = stream;	//获取流类型 0:回放 1:捕捉
	pstr->pcm = pcm;	//捆绑pcm设备
	pstr->substream_count = substream_count;	//子流个数
	if (substream_count > 0) {
		err = snd_pcm_stream_proc_init(pstr);	//proc接口
		if (err < 0) {
			snd_printk(KERN_ERR "Error in snd_pcm_stream_proc_init\n");
			return err;
		}
	}
	prev = NULL;
	for (idx = 0, prev = NULL; idx < substream_count; idx++) {
		substream = kzalloc(sizeof(*substream), GFP_KERNEL);	//分配子流内存
		if (substream == NULL) {
			snd_printk(KERN_ERR "Cannot allocate PCM substream\n");
			return -ENOMEM;
		}
		substream->pcm = pcm;	//捆绑pcm设备
		substream->pstr = pstr;	//捆绑pcm子流
		substream->number = idx;	//索引号
		substream->stream = stream;	//子流类型 0:回放 1:捕捉
		sprintf(substream->name, "subdevice #%i", idx);	//子流名
		substream->buffer_bytes_max = UINT_MAX;
		if (prev == NULL)	//处理第一个pcm子流时走的分支
			pstr->substream = substream;	//pcm流捆绑子流
		else	//前一个子流的next指针指向当前子流,也就是串成一串(0->next--1->next-->2...)
			prev->next = substream;
		err = snd_pcm_substream_proc_init(substream);	//proc接口
		if (err < 0) {
			snd_printk(KERN_ERR "Error in snd_pcm_stream_proc_init\n");
			if (prev == NULL)
				pstr->substream = NULL;
			else
				prev->next = NULL;
			kfree(substream);
			return err;
		}
		substream->group = &substream->self_group;
		spin_lock_init(&substream->self_group.lock);
		INIT_LIST_HEAD(&substream->self_group.substreams);
		list_add_tail(&substream->link_list, &substream->self_group.substreams);
		atomic_set(&substream->mmap_count, 0);
		prev = substream;	//prev指向当前substream
	}
	return 0;
}				
EXPORT_SYMBOL(snd_pcm_new_stream);


6 注册方法snd_pcm_dev_register

static int snd_pcm_dev_register(struct snd_device *device)
{
	int cidx, err;
	struct snd_pcm_substream *substream;
	struct snd_pcm_notify *notify;
	char str[16];
	struct snd_pcm *pcm;
	struct device *dev;

	if (snd_BUG_ON(!device || !device->device_data))
		return -ENXIO;
	pcm = device->device_data;	//声卡设备的私有数据中获取snd_pcm对象
	mutex_lock(®ister_mutex);
	err = snd_pcm_add(pcm);	//添加snd_pcm对象到全局snd_pcm_devices链表
	if (err) {
		mutex_unlock(®ister_mutex);
		return err;
	}
	for (
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值