ALSA学习笔记 (2) 声卡

1. snd_card 描述声卡的结构体

对于每个声卡,都需要有一个snd_card结构体来描述。
它记录着声卡的信息并管理声卡的所有设备。
其中几个比较重要的成员:
int number: 声卡的ID
void *private_data; 声卡的私有数据
struct list_head devices devices 声卡下所有逻辑设备的链表
struct list_head controls 声卡下所有的控制单元的链表

struct snd_card {
	int number;			/* number of soundcard (index to snd_cards) */

	char id[16];			/* id string of this card */
	char driver[16];		/* driver name */
	char shortname[32];		/* short name of this soundcard */
	char longname[80];		/* name of this soundcard */
	char irq_descr[32];		/* Interrupt description */
	char mixername[80];		/* mixer name */
	char components[128];		/* card components delimited with space */
	struct module *module;		/* top-level module */

	void *private_data;		/* private data for soundcard */
	void (*private_free) (struct snd_card *card); /* callback for freeing of private data */
	struct list_head devices;	/* devices */

	struct device ctl_dev;		/* control device */
	unsigned int last_numid;	/* last used numeric ID */
	struct rw_semaphore controls_rwsem;	/* controls list lock */
	rwlock_t ctl_files_rwlock;	/* ctl_files list lock */
	int controls_count;		/* count of all controls */
	int user_ctl_count;		/* count of all user controls */
	struct list_head controls;	/* all controls for this card */
	struct list_head ctl_files;	/* active control files */

	struct snd_info_entry *proc_root;	/* root for soundcard specific files */
	struct snd_info_entry *proc_id;	/* the card id */
	struct proc_dir_entry *proc_root_link;	/* number link to real id */

	struct list_head files_list;	/* all files associated to this card */
	struct snd_shutdown_f_ops *s_f_ops; /* file operations in the shutdown state */
	spinlock_t files_lock;		/* lock the files for this card */
	int shutdown;			/* this card is going down */
	struct completion *release_completion;
	struct device *dev;		/* device assigned to this card */
	struct device card_dev;		/* cardX object for sysfs */
	const struct attribute_group *dev_groups[4]; /* assigned sysfs attr */
	bool registered;		/* card_dev is registered? */
	int offline;			/* if this sound card is offline */
	unsigned long offline_change;
	wait_queue_head_t offline_poll_wait;
};

2 创建声卡的步骤

2.1 创建一个 card 实例

struct snd_card *card;
snd_card_new(&dev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
				THIS_MODULE, 0, &card);

2.2 创建声卡的芯片专用数据

该声卡的一些资源信息,例如中断资源、 io资源、 dma资源
两种方式分配
(1) 作为声卡的private_data
创建声卡的时候, 传入 extra-data-length

snd_card_new(index[dev], id[dev], THIS_MODULE, sizeof(struct mychip), &card);
struct mychip *chip = char->private_data;

(2) 作为声卡的一个子设备
声卡注销时,会调用snd_mychip_dev_free, 自动释放内存

chip = kzalloc(sizeof(*chip), GFP_KERNEL);

static int snd_mychip_dev_free(struct snd_device *dev
	return snd_mychip_free(device‐>device_data);

static struct snd_device_ops ops = {
	.dev_free = snd_mychip_dev_free, };
snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);

2.3 设定驱动 ID 和名字

strcpy(card->driver, “My Chip”);
strcpy(card->shortname, “My Own Chip 123);
sprintf(card->longname,%s at 0x%lx irq %i”,
	card->shortname, chip->ioport, chip->irq)

2.4 创建声卡的逻辑设备

创建声卡的功能部件(逻辑设备),例如PCM, Mixer, MIDI等

2.5 注册声卡

if ((err = snd_card_register(card)) < 0){
    snd_card_free(card);
    return err;
}

3 snd_card_new

用于创建并初始化一个声卡的结构体
看一下具体的函数定义

//*  @parent: the parent device object
//*  @idx: 声卡的ID
//*  @xid: card identification (ASCII string)
//*  @module: 模块
//*  @extra_size: 外部数据(私有数据)的大小,用于为私有数据分配空间
//*  @card_ret: 用于返回声卡结构体的地址
int snd_card_new(struct device *parent, int idx, const char *xid,
	struct module *module, int extra_size,
	struct snd_card **card_ret)

接下来分析一下这个函数,函数会执行如下的操作:

  1. 分配snd_card和private_data的空间
  2. 根据传入的参数赋值xid, idx, module, parent
  3. 初始化结构体,变量
  4. 创建一个control设备
  5. 生成声卡的 proc 文件
  6. 获取private_data的地址,snd_card空间之后
int snd_card_new(struct device *parent, int idx, const char *xid,
		    struct module *module, int extra_size,
		    struct snd_card **card_ret)

	// 1. 分配snd_card和private_data的空间
	//   在snd_card后面的空间分配,card->private_data指向该空间
	card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);
	if (extra_size > 0)
		card->private_data = (char *)card + sizeof(struct snd_card);

	// 2. 根据传入的参数赋值xid, idx, module, parent
	//  (1). 为 snd_card->id 赋值
	strlcpy(card->id, xid, sizeof(card->id));
	//  (2). 分配/赋值 snd_card 的序号
	card->number = idx;
	//  (3). 赋值module, parent
	card->dev = parent;
	card->module = module;
									   
	// 3. 初始化结构体,变量
	INIT_LIST_HEAD(&card->devices);
	init_rwsem(&card->controls_rwsem);
	rwlock_init(&card->ctl_files_rwlock);
	mutex_init(&card->user_ctl_lock);
	INIT_LIST_HEAD(&card->controls);
	INIT_LIST_HEAD(&card->ctl_files);
	spin_lock_init(&card->files_lock);
	INIT_LIST_HEAD(&card->files_list);
	init_waitqueue_head(&card->shutdown_sleep);
	atomic_set(&card->refcount, 0);
	init_waitqueue_head(&card->offline_poll_wait)

	//4. 创建一个control设备
	snd_ctl_create(card);
		//(1)设置回掉函数
		static struct snd_device_ops ops = {
			.dev_free = snd_ctl_dev_free,
			.dev_register =	snd_ctl_dev_register,
			.dev_disconnect = snd_ctl_dev_disconnect,
		};

		//  (2) 设置设备文件节点的名字
		snd_device_initialize(&card->ctl_dev, card);
			device_initialize(dev);
			dev->parent = &card->card_dev;
			dev->class = sound_class;
			dev->release = default_release;
		dev_set_name(&card->ctl_dev, "controlC%d", card->number);
			card->ctl_dev = "controlC%d"
			
		// (3)生成一个snd_device的结构体,添加到snd_card->devices的链表
		snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
			// 分配snd_device的空间
			struct snd_device *dev;
			dev = kzalloc(sizeof(*dev), GFP_KERNEL);
			// 为snd_device的变量赋值 其中type为SNDRV_DEV_CONTROL
			dev->card = card;
			dev->type = type;
			dev->state = SNDRV_DEV_BUILD;
			dev->device_data = device_data;
			// 设置传入的回调函数为逻辑设备的操作函数
			dev->ops = ops;
			// 添加逻辑设备到
			list_add(&dev->list, &card->devices);
	//5. 生成声卡的 proc 文件
	snd_info_card_create(card);
	
		sprintf(str, "card%i", card->number);
		snd_info_create_module_entry(card->module, str, NULL)
		snd_info_register(entry)
		card->proc_root = entry;

	//6. 获取private_data的地址,snd_card空间之后
	card->private_data = (char *)card + sizeof(struct snd_card);

4 snd_card_register

用来注册一个声卡,继续分析一下这个函数:

  1. 创建声卡的设备节点
  2. 注册说有的逻辑设备
  3. 添加当前的声卡到声卡的数组
  4. 注册声卡的proc文件
int snd_card_register(struct snd_card *card)
{
	// 1. 创建声卡的sysfs设备节点
	// 其中 card->card_dev 在创建声卡结构体的时候被赋值
	// card->card_dev.class = sound_class;
	// sound_class在“sound”模块被加载的时候创建
	// 设备节点:/dev/snd/card%i
	err = device_add(&card->card_dev);
	card->registered = true;

	// 2. 注册全部的 snd_device
	snd_device_register_all(card)

	//3. 添加当前的声卡到声卡的数组
	snd_cards[card->number] = card;

	// 4. 注册声卡的proc文件
	snd_cards[card->number] = card;
	init_info_for_card(card);

int snd_device_register_all(struct snd_card *card)
{
	// 遍历注册所有的snd_device, 调用所有的 dev_register 函数
	list_for_each_entry(dev, &card->devices, list) {
		err = __snd_device_register(dev);
			dev->ops->dev_register(dev)
}		

注册完成之后,我们会得到如下的结构:

在这里插入图片描述

5. 关于声卡的其它接口

5.1. 声卡管理

 int snd_card_disconnect (struct snd_card * card);
 void snd_card_set_id (struct snd_card * card, const char * nid);
 int snd_component_add (struct snd_card * card, const char * component);
 int snd_card_file_add (struct snd_card * card, struct file * file);
 int snd_card_file_remove (struct snd_card * card, struct file * file);
 int snd_power_wait (struct snd_card * card, unsigned int power_state);

5.2. 声卡逻辑设备管理

int  snd_device_new  (struct snd_card *  card, snd_device_type_t  type,
void * device_data, struct snd_device_ops * ops);
int snd_device_free (struct snd_card * card, void * device_data);
int snd_device_register (struct snd_card * card, void * device_data);

5.3. Linux模块和设备相关

void snd_request_card (int card);
void * snd_lookup_minor_data (unsigned int minor, int type);
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);
int snd_unregister_device (int type, struct snd_card * card, int dev);

5.4. 内存管理

 int copy_to_user_fromio (void __user * dst, const volatile void __iomem* src,
                     size_t count);
    int copy_from_user_toio (volatile void __iomem * dst, const void __user* src,
              size_t count);
    void * snd_malloc_pages (size_t size, gfp_t gfp_flags);
    void snd_free_pages (void * ptr, size_t size);
    int snd_dma_alloc_pages (int type, struct device * device, size_t size,
                struct snd_dma_buffer * dmab);
    int  snd_dma_alloc_pages_fallback  (int  type, struct device *  device,
                             size_t size, struct snd_dma_buffer * dmab);
    void snd_dma_free_pages (struct snd_dma_buffer * dmab);
    size_t snd_dma_get_reserved_buf (struct snd_dma_buffer * dmab, unsigned int id);
    int  snd_dma_reserve_buf  (struct snd_dma_buffer *  dmab, unsigned int  id);
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值