8.3.1alsa音频驱动框架

 

在上小节我们分析了Adndroid系统音频的框架,这么一个复杂的系统我们怎么去学习呢?我们从下往上学,先分析音频的驱动程序,看看linux系统中驱动程序是怎么编写的,他的结构是怎么样的,然后在琢磨Tinyalsa,是如何去播放,录制声音的。在该课时接下来的所有小节都会讲解linux音频驱动程序。该小节先讲解一下alsa音频驱动的框架:

在编写应用程序的时候,我们都是使用标准的open,read,write等访问驱动程序,最简单的方法就是驱动也提供与应用程序对应的open,read,write等。

一般编写驱动程序如下:
1.构造一个结构体,即file_opevations结构体
2.告诉内核,即通过register_char注册。

上面是简单的驱动程序,但是我们的alsa音频驱动也属于linux,所以他也遵循以上套路,linux的声卡驱动有两个版本:

分别为oss,alsa,但是oss是一个收费的版本,现在流行的是alsa(Advanced Linux Sound Architecture),下面是我们开发板声卡的相关设备节点:
执行ls -l /dev/snd/

3288:

从名字上看controlC0起控制作用,C0的意思代表第一个声卡,pcmC0D0c与pcmC0D0p是一对,其pcmC0D0c表示声卡0的录音(capture),pcmC0D0p表示声卡0的播放(playbaclc)。

从设备节点,我们可以猜测,一个声卡可以由多个device,一个device有播放,录音通道。

每个设备节点对应一个file_opevations结构体,但是他们的主设备号一样,则对应同一个file_opevations结构体。

我们看看代码是如何实现的,代开kennel/sound/core/sound.c:
 

static const struct file_operations snd_fops =
{
	.owner =	THIS_MODULE,
	.open =		snd_open,
	.llseek =	noop_llseek,
};

static int __init alsa_sound_init(void)
	register_chrdev(major, "alsa", &snd_fops)

可以看到snd_fops结构体比较特殊,只有一个snd_open函数,这里snd_open起到的是一个中转的作用,会根据次设备号找到更加具体的file_operations结构体。如controlC0,pcmC0D0c,pcmC0D0p等节点分别有自己file_operations结构体。
进入snd_open函数如下:
 

static int snd_open(struct inode *inode, struct file *file)
	unsigned int minor = iminor(inode);
	mptr = snd_minors[minor];
	/*根据次设备号,获得一个新的file_operations结构体*/
	new_fops = fops_get(mptr->f_ops);
	/*把老的file_operations结构体换成重新获得的file_operations结构体*/
	replace_fops(file, new_fops);

3288

static int snd_open(struct inode *inode, struct file *file)
    unsigned int minor = iminor(inode);
    mptr = snd_minors[minor];
    old_fops = file->f_op;
	/*根据次设备号,获得一个新的file_operations结构体*/
	file->f_op = fops_get(mptr->f_ops);

从这里我们可以总结出既然都遵循alsa规范,如下:

事先定义好file_opevations结构体,首先是:
顶层:定义一个file_opevations结构体,只实现open。
下层:controlC0,pcmC0D0c,pcmC0D0p等节点分别有自己对应的file_opevations结构体。

这些接口确定之后,上层应用程序就可以使用确定的接口,访问声卡,那么谁访去访问硬件呢?那么显然,还有更加底层的工作:

分配设置一个snd_card结构体,这个结构体能提供参数,设置参数,传输数据等等。我们继续查看源码,在kennel/sound/core/sound.c搜索snd_minors:

 

static inline int snd_register_device(int type, struct snd_card *card, int dev,
				      const struct file_operations *f_ops,
				      void *private_data,
				      const char *name)

int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
				const struct file_operations *f_ops,
				void *private_data,
				const char *name, struct device *device)

        snd_minors[minor] = preg;

 

可以知道snd_minors数组是在snd_register_device_for_dev函数中被设置,我们在源码中搜索  snd_register_device_for_dev,看他在哪里被调用,然后一路跟随,可以知道snd_register_device_for_dev被调用的过程如下,注意下面是从上到下被调用的过程:

 

/*sound.c*/
int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
				const struct file_operations *f_ops,
				void *private_data,
				const char *name, struct device *device)
/*core.h*/

static inline int snd_register_device(int type, struct snd_card *card, int dev,
				      const struct file_operations *f_ops,
				      void *private_data,
				      const char *name)

/*control.c*/
static int snd_ctl_dev_register(struct snd_device *device)
    int snd_ctl_create(struct snd_card *card)
    /*init.c*/
    int snd_card_create(int idx, const char *xid,
		    struct module *module, int extra_size,
		    struct snd_card **card_ret)

最后我们在snd_card_new函数中可以看到一个struct snd_card *card;结构体,如果我们在搜索snd_card_create,可以看到一大堆。

那么我们的声卡驱动应该如何编写呢?一般如下:
1.定义一个struct snd_card *card;
2.设置snd_pcm_new
3.注册snd_card_register(card)

下面我们进行流程分析:

1.sound.c确定最顶层的结构体

register_chrdev(major, "alsa", &snd_fops)//major:116

2.init.c

snd_card_create

err = snd_ctl_create(card);//创建控制接口,如:/dev/snd/controlC0

snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);

//一旦创建就调用,结构体里面的

snd_ctl_dev_register

snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
                       &snd_ctl_f_ops, card, name)//节点真正的接口体snd_ctl_f_ops

.....................


我们注册声卡的时候,要调用snd_card_create:

 

int snd_card_create(int idx, const char *xid,
		    struct module *module, int extra_size,
		    struct snd_card **card_ret)

static struct snd_device_ops ops = {
		.dev_free = snd_ctl_dev_free,
		.dev_register =	snd_ctl_dev_register,
		.dev_disconnect = snd_ctl_dev_disconnect,
	};

     err = snd_ctl_create(card);

 

可以知道其会创建一个controlC接口,这是一个声卡最最基本的东西,如pcmC0D0c,pcmC0D0p是可以不存在的 ,但是controlC我们必须创建。
执行snd_device_new函数的时候,snd_device_ops结构体的snd_ctl_dev_register函数会被调用:
 

static const struct file_operations snd_ctl_f_ops =
{
	.owner =	THIS_MODULE,
	.read =		snd_ctl_read,
	.open =		snd_ctl_open,
	.release =	snd_ctl_release,
	.llseek =	no_llseek,
	.poll =		snd_ctl_poll,
	.unlocked_ioctl =	snd_ctl_ioctl,
	.compat_ioctl =	snd_ctl_ioctl_compat,
	.fasync =	snd_ctl_fasync,
};
static int snd_ctl_dev_register(struct snd_device *device)
	snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,&snd_ctl_f_ops, card, &card->ctl_dev);
		

其上的snd_ctl_f_ops结构体,就是对节点真正控制的描述符。我们知道对于声卡还有pcmC0D0c,pcmC0D0p等节点,他们是在哪里设置的呢?我们随便打开一个声卡驱动程序,如Echoaudio.c文件:

static int snd_echo_probe(struct pci_dev *pci,const struct pci_device_id *pci_id)
		err = snd_card_create(&pci->dev, index[dev], id[dev], THIS_MODULE,0, &card);
		snd_echo_new_pcm(chip)
			snd_pcm_new(chip->card, "PCM", 0, num_pipes_out(chip),num_analog_busses_in(chip), &pcm)
				static struct snd_device_ops ops = {
					.dev_free = snd_pcm_dev_free,
					.dev_register =	snd_pcm_dev_register,
					.dev_disconnect = snd_pcm_dev_disconnect,
				};
				snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count))
				snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) 
				snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops))

其是通过snd_pcm_new进行创建pcmC0D0c,pcmC0D0p节点,在调用snd_pcm_new之后,会导致dev_register = snd_pcm_dev_register函数被调用:

static int snd_pcm_dev_register(struct snd_device *device)
	case SNDRV_PCM_STREAM_PLAYBACK:
		devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
	case SNDRV_PCM_STREAM_CAPTURE:
		devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
	snd_register_device(devtype, pcm->card, pcm->device,&snd_pcm_f_ops[cidx], pcm,&pcm->streams[cidx].dev);		

可以知道,其根据SNDRV_PCM_STREAM_PLAYBACK创建pcmC0D0p,根据SNDRV_DEVICE_TYPE_PCM_CAPTURE则创建pcmC0D0c节点。

一个pcm,就是一个逻辑设备device,一个pcm中有两个通道stream,一个为playback,一个为capture。他们分别对应节点pcmC0D0c,pcmC0D0p。

在调用snd_register_device函数时,我们需要注册一个snd_pcm_f_ops结构体:
 

const struct file_operations snd_pcm_f_ops[2] = {
	{
		.owner =		THIS_MODULE,
		.write =		snd_pcm_write,
		.write_iter =		snd_pcm_writev,
		.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,
	},
	{
		.owner =		THIS_MODULE,
		.read =			snd_pcm_read,
		.read_iter =		snd_pcm_readv,
		.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,
	}
};

可以看到有两个定义,一个对应输出,一个对应输入。

整体总结





sound/core/sound.c
alsa_sound_init
	static int major = CONFIG_SND_MAJOR;//116
	static const struct file_operations snd_fops =
	{
		.owner =	THIS_MODULE,
		.open =		snd_open,
		.llseek =	noop_llseek,
	};
	register_chrdev(major, "alsa", &snd_fops)///proc/devices/116-->alsa

-----------------------------------------------------------------------
static int snd_open(struct inode *inode, struct file *file)
	/*把老的file_operations结构体换成重新获得的file_operations结构体*/
	old_fops = file->f_op;
	/*根据次设备号,获得一个新的file_operations结构体*/
	new_fops = fops_get(mptr->f_ops);
	
谁设置了:snd_minors数组
下面是从上往下是被调用过程
/*sound.c*/
int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
				const struct file_operations *f_ops,
				void *private_data,
				const char *name, struct device *device)
	snd_minors[minor] = preg;
<---
/*core.h*/
static inline int snd_register_device(int type, struct snd_card *card, int dev,
				      const struct file_operations *f_ops,
				      void *private_data,
				      const char *name)
	<---
	/*control.c*/
	static int snd_ctl_dev_register(struct snd_device *device)
		<---
			/*control.c*/
			int snd_ctl_create(struct snd_card *card)
			<---
				/*init.c*/
				int snd_card_create(int idx, const char *xid,
						struct module *module, int extra_size,
						struct snd_card **card_ret)
					函数中可以看到一个struct snd_card *card结构体,搜索会有一大堆


-----------------------------------------------------------------------------------
alsa音频驱动框架
声卡驱动编写
1.定义一个struct snd_card *card;
2.设置snd_pcm_new
3.注册snd_card_register(card)

流程分析:
1.sound.c确定最顶层的结构体
	register_chrdev(major, "alsa", &snd_fops)//major:116

//创建一个controlC接口
2.init.c
	snd_card_create
		err = snd_ctl_create(card);//创建控制接口,如:/dev/snd/controlC0
			snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
				//一旦创建就调用,结构体里面的
				snd_ctl_dev_register
					snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
                       		&snd_ctl_f_ops, card, name)//节点真正的接口体snd_ctl_f_ops

------------------------------------------------------------------------------------
//创建pcmC0D0c,pcmC0D0p节点
通过snd_pcm_new进行创建pcmC0D0c,pcmC0D0p节点
int snd_pcm_new(struct snd_card *card, const char *id, int device,
		int playback_count, int capture_count, struct snd_pcm **rpcm)
	static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
		int playback_count, int capture_count, bool internal,
		struct snd_pcm **rpcm)
		//调用snd_pcm_new之后,会导致dev_register = snd_pcm_dev_register函数被调用
		static int snd_pcm_dev_register(struct snd_device *device)
			switch (cidx) {
			//根据SNDRV_PCM_STREAM_PLAYBACK创建pcmC0D0p
			case SNDRV_PCM_STREAM_PLAYBACK:
				sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device);
				devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
				break;
			//根据SNDRV_DEVICE_TYPE_PCM_CAPTURE则创建pcmC0D0c节点。
			case SNDRV_PCM_STREAM_CAPTURE:
				sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device);
				devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
				break;
			}	一个pcm,就是一个逻辑设备device,一个pcm中有两个通道stream,一个为playback,一个为capture。他们分别对应节点pcmC0D0c,pcmC0D0p。
			
			//调用snd_register_device函数时,我们需要注册一个snd_pcm_f_ops结构体
			//有两个定义,一个对应输出,一个对应输入
			const struct file_operations snd_pcm_f_ops[2] = {....}
			err = snd_register_device_for_dev(devtype, pcm->card,
						  pcm->device,
						  &snd_pcm_f_ops[cidx],
						  pcm, str, dev);


总结:
1.定义一个struct snd_card *card;
2.
snd_card_create:创建一个controlC接口
snd_pcm_new:创建pcmC0D0c,pcmC0D0p节点
3.注册snd_card_register(card)



 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值