alsa音频架构3-pcm

 

第十四部分 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;
};

alsa音频架构3-pcm_第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);


alsa音频架构3-pcm_第2张图片

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 (cidx = 0; cidx < 2; cidx++) {
		int devtype = -1;
		if (pcm->streams[cidx].substream == NULL)	//判断pcm子流是否为空
			continue;
		switch (cidx) {
		case SNDRV_PCM_STREAM_PLAYBACK:	//回放
			sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device);//pcmC+声卡号+D+设备号+p
			devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;	//设置设备类型
			break;
		case SNDRV_PCM_STREAM_CAPTURE:	//捕捉
			sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device);//pcmC+声卡号+D+设备号+c
			devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;	//设置设备类型
			break;
		}

		dev = pcm->dev;
		if (!dev)
			dev = snd_card_get_device_link(pcm->card);
		err = snd_register_device_for_dev(devtype, pcm->card,pcm->device,&snd_pcm_f_ops[cidx],pcm, str, dev);	
		//注册声卡设备 "/dev/snd/pcmC0D0p" "/dev/snd/pcmC0D0c"...这里特别注意私有数据是snd_pcm对象
		if (err < 0) {
			list_del(&pcm->list);
			mutex_unlock(®ister_mutex);
			return err;
		}
		snd_add_device_sysfs_file(devtype, pcm->card, pcm->device,&pcm_attrs);	//添加属性文件
		for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) //遍历PCM子流链表
			snd_pcm_timer_init(substream);	//初始化子流定时器
	}
	list_for_each_entry(notify, &snd_pcm_notify_list, list)
		notify->n_register(pcm);

	mutex_unlock(®ister_mutex);
	return 0;
}

6.1 添加snd_pcm对象到全局snd_pcm_devices链表

static int snd_pcm_add(struct snd_pcm *newpcm)
{
	struct snd_pcm *pcm;
	list_for_each_entry(pcm, &snd_pcm_devices, list) {	//遍历全局snd_pcm_devices链表
		if (pcm->card == newpcm->card && pcm->device == newpcm->device)	//匹配声卡和设备号
			return -EBUSY;
		if (pcm->card->number > newpcm->card->number ||(pcm->card == newpcm->card && pcm->device > newpcm->device)) {
			list_add(&newpcm->list, pcm->list.prev);	//链接新的pcm到旧的pcm(根据设备号来排序)
			return 0;
		}
	}
	list_add_tail(&newpcm->list, &snd_pcm_devices);	//添加pcm到全局snd_pcm_devices
	return 0;
}

7.pcm相关的字符设备

snd_register_device_for_dev函数前面讲过注册字符设备文件,捆绑对应的文件操作函数集,也就是snd_pcm_f_ops数组项

PCM声卡设备操作函数集

const struct file_operations snd_pcm_f_ops[2] = {
	{	//回放 对应"/dev/snd/pcmC0D0p"设备文件的操作接口
		.owner =		THIS_MODULE,
		.write =		snd_pcm_write,				//写方法
		.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,	//获取没映射的地址
	},
	{	//捕捉 对应"/dev/snd/pcmC0D0c"设备文件的操作接口
		.owner =		THIS_MODULE,
		.read =			snd_pcm_read,				//读方法
		.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,	//获取没映射的地址
	}
};

当我们在操作/dev/snd/pcmCxDxX时候会调用到上面的函数集的方法,

X为'c'时代表capture调用snd_pcm_f_ops[1]的方法,X为'p'时代表playback调用snd_pcm_f_ops[0]的方法
 

我在内核代码上添加了打印语句:

当我用arecord时候调用open方法接着会调用ioctl方法设置参数属性,接着会调用mmap方法映射内存,接着不断继续用ioctl方法录制声音

主要是SNDRV_PCM_IOCTL_WRITEI_FRAMES命令录制,和SNDRV_PCM_IOCTL_SYNC_PTR命令同步.

 

当我用arecord时候调用open方法接着会调用ioctl方法设置参数属性,接着会调用mmap方法映射内存,接着不断继续用ioctl方法播放声音

主要是SNDRV_PCM_IOCTL_READI_FRAMES命令回放,和SNDRV_PCM_IOCTL_SYNC_PTR命令同步.

 

在pcm字符设备的操作函数集中会经常用到下面的结构体

8.snd_pcm_file

struct snd_pcm_file {
	struct snd_pcm_substream *substream;
	int no_compat_mmap;
};

该结构体很简单基本就是封装了pcm子流和一个映射方式标志

9.pcm runtime

struct snd_pcm_runtime {
	/* -- Status -- */
	struct snd_pcm_substream *trigger_master;	//pcm子流
	struct timespec trigger_tstamp;	/* trigger timestamp */
	int overrange;
	snd_pcm_uframes_t avail_max;
	snd_pcm_uframes_t hw_ptr_base;	/* Position at buffer restart */
	snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time */
	unsigned long hw_ptr_jiffies;	/* Time when hw_ptr is updated */
	unsigned long hw_ptr_buffer_jiffies; /* buffer time in jiffies */
	snd_pcm_sframes_t delay;	/* extra delay; typically FIFO size */
	/* -- HW params -- */
	snd_pcm_access_t access;	/* access mode */
	snd_pcm_format_t format;	/* SNDRV_PCM_FORMAT_* */
	snd_pcm_subformat_t subformat;	/* subformat */
	unsigned int rate;		/* rate in Hz */
	unsigned int channels;		/* channels */
	snd_pcm_uframes_t period_size;	/* period size */
	unsigned int periods;		/* periods */
	snd_pcm_uframes_t buffer_size;	/* buffer size */
	snd_pcm_uframes_t min_align;	/* Min alignment for the format */
	size_t byte_align;
	unsigned int frame_bits;
	unsigned int sample_bits;
	unsigned int info;
	unsigned int rate_num;
	unsigned int rate_den;
	/* -- SW params -- */
	int tstamp_mode;		/* mmap timestamp is updated */
  	unsigned int period_step;
	snd_pcm_uframes_t start_threshold;
	snd_pcm_uframes_t stop_threshold;
	snd_pcm_uframes_t silence_threshold; /* Silence filling happens when noise is nearest than this */
	snd_pcm_uframes_t silence_size;	/* Silence filling size */
	snd_pcm_uframes_t boundary;	/* pointers wrap point */
	snd_pcm_uframes_t silence_start; /* starting pointer to silence area */
	snd_pcm_uframes_t silence_filled; /* size filled with silence */
	union snd_pcm_sync_id sync;	/* hardware synchronization ID */
	/* -- mmap -- */
	struct snd_pcm_mmap_status *status;
	struct snd_pcm_mmap_control *control;
	/* -- locking / scheduling -- */
	snd_pcm_uframes_t twake; 	/* do transfer (!poll) wakeup if non-zero */
	wait_queue_head_t sleep;	/* poll sleep */
	wait_queue_head_t tsleep;	/* transfer sleep */
	struct fasync_struct *fasync;
	/* -- private section -- */
	void *private_data;
	void (*private_free)(struct snd_pcm_runtime *runtime);
	/* -- hardware description -- */
	struct snd_pcm_hardware hw;	//pcm硬件信息
	struct snd_pcm_hw_constraints hw_constraints;	//pcm硬件约束
	/* -- interrupt callbacks -- */
	void (*transfer_ack_begin)(struct snd_pcm_substream *substream);
	void (*transfer_ack_end)(struct snd_pcm_substream *substream);
	/* -- timer -- */
	unsigned int timer_resolution;	/* timer resolution */
	int tstamp_type;		/* timestamp type */
	/* -- DMA -- */           
	unsigned char *dma_area;	/* DMA area */
	dma_addr_t dma_addr;		/* physical bus address (not accessible from main CPU) */
	size_t dma_bytes;		/* size of DMA area */
	struct snd_dma_buffer *dma_buffer_p;	/* allocated buffer */
};


10.pcm字符设备打开方法

回放

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);	//获取snd_pcm对象
	return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK);	//打开回放通道
}

捕捉

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);	//获取snd_pcm对象
	return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE);	//打开捕捉通道
}

10.1.次设备号获取snd_pcm对象

void *snd_lookup_minor_data(unsigned int minor, int type)
{
	struct snd_minor *mreg;
	void *private_data;

	if (minor >= ARRAY_SIZE(snd_minors))
		return NULL;
	mutex_lock(&sound_mutex);
	mreg = snd_minors[minor];	//根据次设备号,从全局snd_minor数组获取对应的snd_minor对象
	if (mreg && mreg->type == type)
		private_data = mreg->private_data;	//前面特别注意私有数据为snd_pcm对象
	else
		private_data = NULL;
	mutex_unlock(&sound_mutex);
	return private_data;	//返回snd_pcm对象
}
EXPORT_SYMBOL(snd_lookup_minor_data);

10.2.snd_pcm_open

static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream)
{
	int err;
	struct snd_pcm_file *pcm_file;
	wait_queue_t wait;

	if (pcm == NULL) {
		err = -ENODEV;
		goto __error1;
	}
	err = snd_card_file_add(pcm->card, file);
	if (err < 0)
		goto __error1;
	if (!try_module_get(pcm->card->module)) {
		err = -EFAULT;
		goto __error2;
	}
	init_waitqueue_entry(&wait, current);
	add_wait_queue(&pcm->open_wait, &wait);
	mutex_lock(&pcm->open_mutex);
	while (1) {
		err = snd_pcm_open_file(file, pcm, stream, &pcm_file);	//4.1 snd_pcm_open_file
		if (err >= 0)
			break;
		if (err == -EAGAIN) {
			if (file->f_flags & O_NONBLOCK) {
				err = -EBUSY;
				break;
			}
		} else
			break;
		set_current_state(TASK_INTERRUPTIBLE);	//设置为可中断
		mutex_unlock(&pcm->open_mutex);
		schedule();	//进入调度
		mutex_lock(&pcm->open_mutex);
		if (signal_pending(current)) {	//挂起当前进程
			err = -ERESTARTSYS;
			break;
		}
	}
	remove_wait_queue(&pcm->open_wait, &wait);
	mutex_unlock(&pcm->open_mutex);
	if (err < 0)
		goto __error;
	return err;

      __error:
	module_put(pcm->card->module);
      __error2:
      	snd_card_file_remove(pcm->card, file);
      __error1:
      	return err;
}

10.2.1 snd_pcm_open_file

static int snd_pcm_open_file(struct file *file,struct snd_pcm *pcm,int stream,struct snd_pcm_file **rpcm_file)
{
	struct snd_pcm_file *pcm_file;
	struct snd_pcm_substream *substream;
	struct snd_pcm_str *str;
	int err;

	if (rpcm_file)
		*rpcm_file = NULL;

	err = snd_pcm_open_substream(pcm, stream, file, &substream);	//4.1.1 snd_pcm_open_substream
	if (err < 0)
		return err;

	pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL);	//分配snd_pcm_file对象内存
	if (pcm_file == NULL) {
		snd_pcm_release_substream(substream);
		return -ENOMEM;
	}
	pcm_file->substream = substream;	//snd_pcm_file对象捆绑pcm子流
	if (substream->ref_count == 1) {
		str = substream->pstr;	//获取pcm流
		substream->file = pcm_file;		//pcm子流捆绑snd_pcm_file对象
		substream->pcm_release = pcm_release_private;
	}
	file->private_data = pcm_file;	//snd_pcm_file对象存放在文件对象的私有数据里
	if (rpcm_file)
		*rpcm_file = pcm_file;
	return 0;
}

10.2.1.1 snd_pcm_open_substream

int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,struct file *file,struct snd_pcm_substream **rsubstream)
{
	struct snd_pcm_substream *substream;
	int err;

	err = snd_pcm_attach_substream(pcm, stream, file, &substream);	//获取pcm子流
	if (err < 0)
		return err;
	if (substream->ref_count > 1) {
		*rsubstream = substream;
		return 0;
	}
	err = snd_pcm_hw_constraints_init(substream);	//初始化pcm硬件约束
	if (err < 0) {
		snd_printd("snd_pcm_hw_constraints_init failed\n");
		goto error;
	}
	if ((err = substream->ops->open(substream)) < 0)	//调用pcm子流的open方法
		goto error;
	substream->hw_opened = 1;
	err = snd_pcm_hw_constraints_complete(substream);	//设置pcm硬件约束
	if (err < 0) {
		snd_printd("snd_pcm_hw_constraints_complete failed\n");
		goto error;
	}
	*rsubstream = substream;
	return 0;
 error:
	snd_pcm_release_substream(substream);
	return err;
}
EXPORT_SYMBOL(snd_pcm_open_substream);


这里调用了pcm子流的open方法,在上一篇文章ASoc中有这么几句代码

snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops);

11.pcm子流操作集

static struct snd_pcm_ops soc_pcm_ops = {
	.open		= soc_pcm_open,
	.close		= soc_codec_close,
	.hw_params	= soc_pcm_hw_params,
	.hw_free	= soc_pcm_hw_free,
	.prepare	= soc_pcm_prepare,
	.trigger	= soc_pcm_trigger,
	.pointer	= soc_pcm_pointer,
};

除了open方法这里给调用,其他的方法在ioctl和close等字符设备方法中会使用到,souceinsight搜索ops->xxx,基本上在pcm_native.c文件中会调用

11.1 open方法soc_pcm_open

static int soc_pcm_open(struct snd_pcm_substream *substream)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;	//获取soc pcm
	struct snd_pcm_runtime *runtime = substream->runtime;	//获取pcmrt
	struct snd_soc_platform *platform = rtd->platform;	//获取soc平台
	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;	//获取dai设备(cpu_dai)
	struct snd_soc_dai *codec_dai = rtd->codec_dai;	//获取dai设备(codec_dai)
	struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver;	//获取dai(cpu_dai)声卡驱动
	struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver;	//获取dai(codec_dai)声卡驱动
	int ret = 0;

	mutex_lock(&pcm_mutex);
	/* startup the audio subsystem */
	if (cpu_dai->driver->ops->startup) {	
		ret = cpu_dai->driver->ops->startup(substream, cpu_dai);	//调用dai设备(cpu_dai)声卡驱动的startup方法
		if (ret < 0) {
			printk(KERN_ERR "asoc: can't open interface %s\n",cpu_dai->name);
			goto out;
		}
	}
	if (platform->driver->ops->open) {	
		ret = platform->driver->ops->open(substream);	//调用soc平台驱动的open方法
		if (ret < 0) {
			printk(KERN_ERR "asoc: can't open platform %s\n", platform->name);
			goto platform_err;
		}
	}
	if (codec_dai->driver->ops->startup) {
		ret = codec_dai->driver->ops->startup(substream, codec_dai); //调用dai设备(codec_dai)声卡驱动的startup方法
		if (ret < 0) {
			printk(KERN_ERR "asoc: can't open codec %s\n",codec_dai->name);
			goto codec_dai_err;
		}
	}
	if (rtd->dai_link->ops && rtd->dai_link->ops->startup) {
		ret = rtd->dai_link->ops->startup(substream);	//调用dai link的startup方法
		if (ret < 0) {
			printk(KERN_ERR "asoc: %s startup failed\n", rtd->dai_link->name);
			goto machine_err;
		}
	}
	/* Check that the codec and cpu DAI's are compatible */
	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {	//回放
		runtime->hw.rate_min =max(codec_dai_drv->playback.rate_min,cpu_dai_drv->playback.rate_min);	//最小采样率
		runtime->hw.rate_max =min(codec_dai_drv->playback.rate_max,cpu_dai_drv->playback.rate_max);	//最大采样率
		runtime->hw.channels_min =max(codec_dai_drv->playback.channels_min,cpu_dai_drv->playback.channels_min);	//最小通道数
		runtime->hw.channels_max =min(codec_dai_drv->playback.channels_max,cpu_dai_drv->playback.channels_max);	//最大通道数
		runtime->hw.formats =codec_dai_drv->playback.formats & cpu_dai_drv->playback.formats;	//采样格式
		runtime->hw.rates =codec_dai_drv->playback.rates & cpu_dai_drv->playback.rates;	//设置采样率
		if (codec_dai_drv->playback.rates& (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
			runtime->hw.rates |= cpu_dai_drv->playback.rates;	//设置采样率
		if (cpu_dai_drv->playback.rates& (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
			runtime->hw.rates |= codec_dai_drv->playback.rates;	//设置采样率
	} else {												//捕捉
		runtime->hw.rate_min =max(codec_dai_drv->capture.rate_min,cpu_dai_drv->capture.rate_min);	//最小采样率
		runtime->hw.rate_max =min(codec_dai_drv->capture.rate_max,cpu_dai_drv->capture.rate_max);	//最大采样率
		runtime->hw.channels_min =max(codec_dai_drv->capture.channels_min,cpu_dai_drv->capture.channels_min);	//最小通道数
		runtime->hw.channels_max =min(codec_dai_drv->capture.channels_max,cpu_dai_drv->capture.channels_max);	//最大通道数
		runtime->hw.formats =codec_dai_drv->capture.formats & cpu_dai_drv->capture.formats;	//采样格式
		runtime->hw.rates =codec_dai_drv->capture.rates & cpu_dai_drv->capture.rates;	//设置采样率
		if (codec_dai_drv->capture.rates& (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
			runtime->hw.rates |= cpu_dai_drv->capture.rates;	//设置采样率
		if (cpu_dai_drv->capture.rates& (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
			runtime->hw.rates |= codec_dai_drv->capture.rates;	//设置采样率
	}
	snd_pcm_limit_hw_rates(runtime);	//设置采样率范围
	if (!runtime->hw.rates) {
		printk(KERN_ERR "asoc: %s <-> %s No matching rates\n",codec_dai->name, cpu_dai->name);
		goto config_err;
	}
	if (!runtime->hw.formats) {
		printk(KERN_ERR "asoc: %s <-> %s No matching formats\n",codec_dai->name, cpu_dai->name);
		goto config_err;
	}
	if (!runtime->hw.channels_min || !runtime->hw.channels_max) {
		printk(KERN_ERR "asoc: %s <-> %s No matching channels\n",codec_dai->name, cpu_dai->name);
		goto config_err;
	}
	/* Symmetry only applies if we've already got an active stream. */
	if (cpu_dai->active || codec_dai->active) {
		ret = soc_pcm_apply_symmetry(substream);
		if (ret != 0)
			goto config_err;
	}
	pr_debug("asoc: %s <-> %s info:\n",codec_dai->name, cpu_dai->name);
	pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates);
	pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,runtime->hw.channels_max);
	pr_debug("asoc: min rate %d max rate %d\n", runtime->hw.rate_min,runtime->hw.rate_max);
	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
		cpu_dai->playback_active++;	//更新dai(cpu_dai)的playback_active计数
		codec_dai->playback_active++;	//更新dai(codec_dai)的playback_active计数
	} else {
		cpu_dai->capture_active++;	//更新dai(cpu_dai)的capture_active计数
		codec_dai->capture_active++;	//更新dai(codec_dai)的capture_active计数
	}
	cpu_dai->active++;	//更新dai(cpu_dai)的active计数
	codec_dai->active++;	//更新dai(codec_dai)的active计数
	rtd->codec->active++;	//更新codec设备的active计数
	mutex_unlock(&pcm_mutex);
	return 0;
config_err:
	if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
		rtd->dai_link->ops->shutdown(substream);
machine_err:
	if (codec_dai->driver->ops->shutdown)
		codec_dai->driver->ops->shutdown(substream, codec_dai);
codec_dai_err:
	if (platform->driver->ops->close)
		platform->driver->ops->close(substream);
platform_err:
	if (cpu_dai->driver->ops->shutdown)
		cpu_dai->driver->ops->shutdown(substream, cpu_dai);
out:
	mutex_unlock(&pcm_mutex);
	return ret;
}


12.pcm字符设备映射方法

。。。

13.pcm字符设备命令控制方法

13.1 捕捉控制命令

static long snd_pcm_capture_ioctl(struct file *file, unsigned int cmd,unsigned long arg)
{
	struct snd_pcm_file *pcm_file;

	pcm_file = file->private_data;	//获取snd_pcm_file对象
	if (((cmd >> 8) & 0xff) != 'A')
		return -ENOTTY;
	return snd_pcm_capture_ioctl1(file, pcm_file->substream, cmd,(void __user *)arg);
}

13.1.1 snd_pcm_capture_ioctl1

static int snd_pcm_capture_ioctl1(struct file *file,struct snd_pcm_substream *substream,unsigned int cmd, void __user *arg)
{
	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:
	{
		struct snd_xferi xferi;
		struct snd_xferi __user *_xferi = arg;
		struct snd_pcm_runtime *runtime = substream->runtime;
		snd_pcm_sframes_t result;
		if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
			return -EBADFD;
		if (put_user(0, &_xferi->result))
			return -EFAULT;
		if (copy_from_user(&xferi, _xferi, sizeof(xferi)))
			return -EFAULT;
		result = snd_pcm_lib_read(substream, xferi.buf, xferi.frames);
		__put_user(result, &_xferi->result);
		return result < 0 ? result : 0;
	}
	case SNDRV_PCM_IOCTL_READN_FRAMES:
	{
		struct snd_xfern xfern;
		struct snd_xfern __user *_xfern = arg;
		struct snd_pcm_runtime *runtime = substream->runtime;
		void *bufs;
		snd_pcm_sframes_t result;
		if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
			return -EBADFD;
		if (runtime->channels > 128)
			return -EINVAL;
		if (put_user(0, &_xfern->result))
			return -EFAULT;
		if (copy_from_user(&xfern, _xfern, sizeof(xfern)))
			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);
		return result < 0 ? result : 0;
	}
	case SNDRV_PCM_IOCTL_REWIND:
	{
		snd_pcm_uframes_t frames;
		snd_pcm_uframes_t __user *_frames = arg;
		snd_pcm_sframes_t result;
		if (get_user(frames, _frames))
			return -EFAULT;
		if (put_user(0, _frames))
			return -EFAULT;
		result = snd_pcm_capture_rewind(substream, frames);
		__put_user(result, _frames);
		return result < 0 ? result : 0;
	}
	case SNDRV_PCM_IOCTL_FORWARD:
	{
		snd_pcm_uframes_t frames;
		snd_pcm_uframes_t __user *_frames = arg;
		snd_pcm_sframes_t result;
		if (get_user(frames, _frames))
			return -EFAULT;
		if (put_user(0, _frames))
			return -EFAULT;
		result = snd_pcm_capture_forward(substream, frames);
		__put_user(result, _frames);
		return result < 0 ? result : 0;
	}
	}
	return snd_pcm_common_ioctl1(file, substream, cmd, arg);  // 通用命令
}

13.2 回放控制命令

static long snd_pcm_playback_ioctl(struct file *file, unsigned int cmd,unsigned long arg)
{
	struct snd_pcm_file *pcm_file;

	pcm_file = file->private_data;	//获取snd_pcm_file对?
	if (((cmd >> 8) & 0xff) != 'A')
		return -ENOTTY;
	return snd_pcm_playback_ioctl1(file, pcm_file->substream, cmd,(void __user *)arg);
}

13.2.1 snd_pcm_playback_ioctl1

static int snd_pcm_playback_ioctl1(struct file *file,struct snd_pcm_substream *substream,unsigned int cmd, void __user *arg)
{
	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:
	{
		struct snd_xferi xferi;
		struct snd_xferi __user *_xferi = arg;
		struct snd_pcm_runtime *runtime = substream->runtime;
		snd_pcm_sframes_t result;
		if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
			return -EBADFD;
		if (put_user(0, &_xferi->result))
			return -EFAULT;
		if (copy_from_user(&xferi, _xferi, sizeof(xferi)))
			return -EFAULT;
		result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames);
		__put_user(result, &_xferi->result);
		return result < 0 ? result : 0;
	}
	case SNDRV_PCM_IOCTL_WRITEN_FRAMES:
	{
		struct snd_xfern xfern;
		struct snd_xfern __user *_xfern = arg;
		struct snd_pcm_runtime *runtime = substream->runtime;
		void __user **bufs;
		snd_pcm_sframes_t result;
		if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
			return -EBADFD;
		if (runtime->channels > 128)
			return -EINVAL;
		if (put_user(0, &_xfern->result))
			return -EFAULT;
		if (copy_from_user(&xfern, _xfern, sizeof(xfern)))
			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);
		return result < 0 ? result : 0;
	}
	case SNDRV_PCM_IOCTL_REWIND:
	{
		snd_pcm_uframes_t frames;
		snd_pcm_uframes_t __user *_frames = arg;
		snd_pcm_sframes_t result;
		if (get_user(frames, _frames))
			return -EFAULT;
		if (put_user(0, _frames))
			return -EFAULT;
		result = snd_pcm_playback_rewind(substream, frames);
		__put_user(result, _frames);
		return result < 0 ? result : 0;
	}
	case SNDRV_PCM_IOCTL_FORWARD:
	{
		snd_pcm_uframes_t frames;
		snd_pcm_uframes_t __user *_frames = arg;
		snd_pcm_sframes_t result;
		if (get_user(frames, _frames))
			return -EFAULT;
		if (put_user(0, _frames))
			return -EFAULT;
		result = snd_pcm_playback_forward(substream, frames);
		__put_user(result, _frames);
		return result < 0 ? result : 0;
	}
	}
	return snd_pcm_common_ioctl1(file, substream, cmd, arg); //通用命令
}

13.3 通用控制命令

static int snd_pcm_common_ioctl1(struct file *file,struct snd_pcm_substream *substream,unsigned int cmd, void __user *arg)
{
	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 */
		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:
		return snd_pcm_hw_params_user(substream, arg);
	case SNDRV_PCM_IOCTL_HW_FREE:
		return snd_pcm_hw_free(substream);
	case SNDRV_PCM_IOCTL_SW_PARAMS:
		return snd_pcm_sw_params_user(substream, arg);
	case SNDRV_PCM_IOCTL_STATUS:
		return snd_pcm_status_user(substream, arg);
	case SNDRV_PCM_IOCTL_CHANNEL_INFO:
		return snd_pcm_channel_info_user(substream, arg);
	case SNDRV_PCM_IOCTL_PREPARE:
		return snd_pcm_prepare(substream, file);
	case SNDRV_PCM_IOCTL_RESET:
		return snd_pcm_reset(substream);
	case SNDRV_PCM_IOCTL_START:
		return snd_pcm_action_lock_irq(&snd_pcm_action_start, substream, SNDRV_PCM_STATE_RUNNING);
	case SNDRV_PCM_IOCTL_LINK:
		return snd_pcm_link(substream, (int)(unsigned long) arg);
	case SNDRV_PCM_IOCTL_UNLINK:
		return snd_pcm_unlink(substream);
	case SNDRV_PCM_IOCTL_RESUME:
		return snd_pcm_resume(substream);
	case SNDRV_PCM_IOCTL_XRUN:
		return snd_pcm_xrun(substream);
	case SNDRV_PCM_IOCTL_HWSYNC:
		return snd_pcm_hwsync(substream);
	case SNDRV_PCM_IOCTL_DELAY:
		return snd_pcm_delay(substream, arg);
	case SNDRV_PCM_IOCTL_SYNC_PTR:
		return snd_pcm_sync_ptr(substream, arg);
#ifdef CONFIG_SND_SUPPORT_OLD_API
	case SNDRV_PCM_IOCTL_HW_REFINE_OLD:
		return snd_pcm_hw_refine_old_user(substream, arg);
	case SNDRV_PCM_IOCTL_HW_PARAMS_OLD:
		return snd_pcm_hw_params_old_user(substream, arg);
#endif
	case SNDRV_PCM_IOCTL_DRAIN:
		return snd_pcm_drain(substream, file);
	case SNDRV_PCM_IOCTL_DROP:
		return snd_pcm_drop(substream);
	case SNDRV_PCM_IOCTL_PAUSE:
	{
		int res;
		snd_pcm_stream_lock_irq(substream);
		res = snd_pcm_pause(substream, (int)(unsigned long)arg);
		snd_pcm_stream_unlock_irq(substream);
		return res;
	}
	}
	snd_printd("unknown ioctl = 0x%x\n", cmd);
	return -ENOTTY;
}

13.4 命令总结

//capture专用命令
SNDRV_PCM_IOCTL_WRITEI_FRAMES					
SNDRV_PCM_IOCTL_WRITEN_FRAMES	
SNDRV_PCM_IOCTL_REWIND			
SNDRV_PCM_IOCTL_FORWARD			
//playback专用命令
SNDRV_PCM_IOCTL_READI_FRAMES					
SNDRV_PCM_IOCTL_READN_FRAMES	
SNDRV_PCM_IOCTL_REWIND			
SNDRV_PCM_IOCTL_FORWARD			
//通用命令
SNDRV_PCM_IOCTL_PVERSION			
SNDRV_PCM_IOCTL_INFO				
SNDRV_PCM_IOCTL_TSTAMP		
SNDRV_PCM_IOCTL_TTSTAMP			
SNDRV_PCM_IOCTL_HW_REFINE		
SNDRV_PCM_IOCTL_HW_PARAMS		
SNDRV_PCM_IOCTL_HW_FREE			
SNDRV_PCM_IOCTL_SW_PARAMS		
SNDRV_PCM_IOCTL_STATUS			
SNDRV_PCM_IOCTL_CHANNEL_INFO	
SNDRV_PCM_IOCTL_PREPARE			
SNDRV_PCM_IOCTL_RESET			
SNDRV_PCM_IOCTL_START			
SNDRV_PCM_IOCTL_LINK			
SNDRV_PCM_IOCTL_UNLINK			
SNDRV_PCM_IOCTL_RESUME			
SNDRV_PCM_IOCTL_XRUN			
SNDRV_PCM_IOCTL_HWSYNC			
SNDRV_PCM_IOCTL_DELAY			
SNDRV_PCM_IOCTL_SYNC_PTR		
SNDRV_PCM_IOCTL_HW_REFINE_OLD	
SNDRV_PCM_IOCTL_HW_PARAMS_OLD	
SNDRV_PCM_IOCTL_DRAIN			
SNDRV_PCM_IOCTL_DROP			
SNDRV_PCM_IOCTL_PAUSE









 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(linux设备驱动,linux设备驱动)