第3篇 ALSA Core层(生成字符设备)

参考:韦东山Linux驱动

生成字符设备接口:

(1)在文件 linux-2.6.31.14\sound\core\sound.c
	static int __init alsa_sound_init(void)
			|
			|register_chrdev(major, "alsa", &snd_fops)		

				static const struct file_operations snd_fops =		//重要
				{
					.owner =	THIS_MODULE,
					.open =		snd_open				//
				};
				
	snd_open(struct inode *inode, struct file *file)
		|
		|__snd_open(inode, file);
			|
			|struct snd_minor *mptr = NULL;
			|const struct file_operations *old_fops;
			|mptr = snd_minors[minor];						//从 snd_minors 中取出新的 file_operations
			|old_fops = file->f_op;
			|file->f_op = fops_get(mptr->f_ops);			//赋予新的 f_op
			|err = file->f_op->open(inode, file);			//



	//重要函数, 核心函数  
	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)
		|
		|struct snd_minor *preg;
		|preg = kmalloc(sizeof *preg, GFP_KERNEL);
		|preg->f_ops = f_ops;
		|snd_minors[minor] = preg;							//
		|preg->dev = device_create(sound_class, device, MKDEV(major, minor),		//
					  private_data, "%s", name);
	
	
		init_soundcore(void)								//linux-2.6.31.14\sound\sound_core.c
			|
			|sound_class = class_create(THIS_MODULE, "sound");
			
			
(2)控制接口 方面的调用
	snd_card_create(int idx, const char *xid,  struct module *module, int extra_size,  struct snd_card **card_ret)		//linux-2.6.31.14\sound\core\init.c
		|
		|snd_ctl_create(card);														//linux-2.6.31.14\sound\core\control.c
			|
			|static struct snd_device_ops ops = {
			|	.dev_free = snd_ctl_dev_free,
			|	.dev_register =	snd_ctl_dev_register,							//
			|	.dev_disconnect = snd_ctl_dev_disconnect,
			|};
			|return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);		//创建 CONTROL 控制类逻辑设备
		
		
	snd_ctl_dev_register(struct snd_device *device)								//linux-2.6.31.14\sound\core\control.c
		|
		|snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,				//linux-2.6.31.14\include\sound\core.h
				   &snd_ctl_f_ops, card, name))
			|
			|return snd_register_device_for_dev(type, card, dev, f_ops,		//核心函数
								   private_data, name,


		static const struct file_operations snd_ctl_f_ops =				//即对应上面提到的  从 snd_minors 中取出新的 file_operations
		{																//重要 , 应该会调用后面的 platform 和 codec 的函数  
			.owner =	THIS_MODULE,
			.read =		snd_ctl_read,
			.open =		snd_ctl_open,
			.release =	snd_ctl_release,
			.poll =		snd_ctl_poll,
			.unlocked_ioctl =	snd_ctl_ioctl,							//重要
			.compat_ioctl =	snd_ctl_ioctl_compat,
			.fasync =	snd_ctl_fasync,
		};

(3)PCM 方面的调用: (Pulse-code modulation 的缩写, 脉冲编码调制)
	snd_pcm_new(struct snd_card *card, const char *id, int device,			//linux-2.6.31.14\sound\core\pcm.c
			int playback_count, int capture_count,
				struct snd_pcm ** rpcm)	
		|
		|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_device_new(card, SNDRV_DEV_PCM, pcm, &ops))					//创建声卡 PCM 类逻辑设备
		

	snd_pcm_dev_register(struct snd_device *device)						//linux-2.6.31.14\sound\core\pcm.c
		|
		|for (cidx = 0; cidx < 2; cidx++) {								//
		|	switch (cidx) {
		|		case SNDRV_PCM_STREAM_PLAYBACK:
		|			sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device);
		|			devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
		|			break;
		|		case SNDRV_PCM_STREAM_CAPTURE:
		|			sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device);
		|			devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
		|			break;
		|		}
		|	snd_register_device_for_dev(devtype, pcm->card,				//核心函数		注册两个设备,一个播放,一个录音
		|			  pcm->device,
		|			  &snd_pcm_f_ops[cidx],
		|			  pcm, str, dev);


	const struct file_operations snd_pcm_f_ops[2] = {					//重要 , 应该会调用后面的 platform 和 codec 的函数
		{
			.owner =		THIS_MODULE,
			.write =		snd_pcm_write,
			.aio_write =		snd_pcm_aio_write,
			.open =			snd_pcm_playback_open,
			.release =		snd_pcm_release,
			.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 =	dummy_get_unmapped_area,
		},
		{
			.owner =		THIS_MODULE,
			.read =			snd_pcm_read,
			.aio_read =		snd_pcm_aio_read,
			.open =			snd_pcm_capture_open,
			.release =		snd_pcm_release,
			.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 =	dummy_get_unmapped_area,
		}
	};


(4)snd_card_register(struct snd_card *card)		//linux-2.6.31.14\sound\core\init.c
	|
	|snd_device_register_all(card))
		|
		|list_for_each_entry(dev, &card->devices, list) {
			dev->ops->dev_register(dev))					//即调用上面的 	snd_ctl_dev_register  和  snd_pcm_dev_register



怎么写 ALSA 声卡驱动:

(1)创建 CONTROL 控制类逻辑设备		//只有在调用 snd_card_register 后才会真正创建设备
	snd_card_create
		|
		|snd_ctl_create
			|
			|snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops)	//ops.dev_register (对应有1个 file_operations )
					
(2)初始化, 创建 PCM 逻辑设备		//只有在调用 snd_card_register 后才会真正创建设备
	snd_pcm_new 
		|
		|snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops))			//ops.dev_register (对应有2个 file_operations, 一个播放,一个录音 )
		
(3)snd_card_register				//真正创建设备节点
		|
		|dev->ops->dev_register							//调用 CONTROL 和 PCM 的 dev_register 函数
					|
					|snd_register_device_for_dev		//重要函数, 核心函数 
						|
						|preg->f_ops = f_ops;
						|preg->dev = device_create(sound_class, device, MKDEV(major, minor), private_data, "%s", name);

posted @ 2023-09-04 18:42  charlie12345  阅读(97)  评论(0)    收藏  举报