文章目录
ALSA声卡驱动(一)初识alsa
目录
- 文章目录
- 1、声卡实例
- 1.1、struct snd_card
- 1.2、struct snd_devide
- 2、声卡的创建流程
- 2.1、创建一个声卡实例
- 2.2、创建声卡芯片的专用数据
- 2.3、设置Driver和ID和名字
- 2.4、创建声卡的功能部件(逻辑设备)
- 2.5、注册声卡
- 3、snd_card_create()和snd_card_new()
- 4、snd_card_register()
- 总结
1、声卡实例
1.1、struct snd_card
snd_card可以说是整个ALSA音频驱动最顶层的一个结构,几乎所有与声卡相关的逻辑设备都是在snd_card的管理之下。声卡驱动的第一个动作通常就是创建一个snd_card结构体。
snd_card结构体定义在头文件include/sound/core.h中:
struct snd_card {int number; /* number of soundcard (index tosnd_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 withspace */struct module *module; /* top-level module */void *private_data; /* private data for soundcard */void (*private_free) (struct snd_card *card); /* callback for freeing ofprivate 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 shutdownstate */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? */
#ifdef CONFIG_PMunsigned int power_state; /* power state */wait_queue_head_t power_sleep;
#endif#if IS_ENABLED(CONFIG_SND_MIXER_OSS)struct snd_mixer_oss *mixer_oss;int mixer_oss_change_count;
#endif
};
struct list_head devices 记录该声卡下所有逻辑设备的链表
struct list_head controls 记录该声卡下所有的控制单元的链表
void *private_data 声卡的私有数据,可以在创建声卡时通过参数指定数据的大小
1.2、struct snd_devide
snd_card结构表示一个声卡,而snd_devide结构来抽象表示一个声卡的逻辑设备。
struct snd_device { struct list_head list; /* list of registered devices */struct snd_card *card; /* card which holds this device */enum snd_device_state state; /* state of the device */enum snd_device_type type; /* device type */void *device_data; /* device structure */struct snd_device_ops *ops; /* operations */
};
2、声卡的创建流程
2.1、创建一个声卡实例
声卡的创建使用snd_card_create() 函数(注意:现在很多地方都换成了snd_card_new()函数):
struct snd_card *card;
int err;
...
err = snd_card_create(index, id, THIS_MODULE, 0, &card);
index是一个整数值,表示该声卡的编号。
id是一个字符串,表示声卡的标识符。
第四个参数决定在创建声卡实例的时候需要同时额外分配的私有数据大小,私有数据的指针最终会赋值给snd_card的private_data数据成员。
card将返回所创建的snd_card实例指针。
2.2、创建声卡芯片的专用数据
声卡的专用数据主要用于存放声卡的资源信息,例如中断资源、io资源、dma资源等。
创建的方法有两种:
- 第一种:给snd_card_create的第四个参数递一个值,让函数自行创建:
//struct mychip 用于保存专用数据
err = snd_card_create(index, id, THIS_MODULE,sizeof(struct mychip), &card);
// 从private_data中取出
struct mychip *chip = card‐>private_data;
- 第二种:自行创建:
struct mychip {struct snd_card *card;...
};
struct snd_card *card;
struct mychip *chip;
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
...
err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
chip‐>card = card;/* 再把芯片的专有数据注册为声卡的一个低阶设备 */
static int snd_mychip_dev_free(struct snd_device *device)
{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、设置Driver和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);
snd_card的driver域保存着芯片的ID字符串,用户空间的alsa-lib会使用到该字符串,必须保证ID的唯一性。
shortname更多的是用于打印信息;
longname域则会出现在 /proc/asound/cards中。
2.4、创建声卡的功能部件(逻辑设备)
声卡的每一种部件的创建最终都会调用snd_device_new() 来生成一个snd_device实例,并把这个实例链接到snd_card的devices链表中。
通常alsa-driver会提供一些常用部件的创建函数,不必直接调用snd_device_new() ,比如:
PCM —> snd_pcm_new()
RAWMIDI —> snd_rawmidi_new()
CONTROL —> snd_ctl_create()
TIMER —> snd_timer_new()
INFO —> snd_card_proc_new()
JACK —> snd_jack_new()
2.5、注册声卡
err = snd_card_register(card);
if (err < 0) {snd_card_free(card);return err;
}
经过以上的创建步骤之后,声卡的逻辑结构如下图所示:
3、snd_card_create()和snd_card_new()
snd_card_create() 函数在新的内核版本中被替换成了snd_card_new() 函数,定义在sound/core/init.c中:
/*** snd_card_new - create and initialize a soundcard structure* @parent: the parent device object* @idx: card index (address) [0 ... (SNDRV_CARDS-1)]* @xid: card identification (ASCII string)* @module: top level module for locking* @extra_size: allocate this extra size after the main soundcard structure* @card_ret: the pointer to store the created card instance** Creates and initializes a soundcard structure.** The function allocates snd_card instance via kzalloc with the given* space for the driver to use freely. The allocated struct is stored* in the given card_ret pointer.** Return: Zero if successful or a negative error code.*/
int snd_card_new(struct device *parent, int idx, const char *xid, struct module *module, int extra_size,struct snd_card **card_ret)
根据extra_size参数的大小分配内存,该内存区通常作为芯片的私有数据使用:
if (extra_size < 0)extra_size = 0;card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);if (!card)return -ENOMEM;if (extra_size > 0)card->private_data = (char *)card + sizeof(struct snd_card);
拷贝声卡ID字符串:
if (xid)strlcpy(card->id, xid, sizeof(card->id));
如果传入的声卡编号为-1,自动分配一个新的索引号:
if (idx < 0) /* first check the matching module-name slot */idx = get_slot_from_bitmask(idx, module_slot_match, module);if (idx < 0) /* if not matched, assign an empty slot */idx = get_slot_from_bitmask(idx, check_empty_slot, module);
初始化snd_card结构中必要的字段:
card->dev = parent;card->number = idx;card->module = module;INIT_LIST_HEAD(&card->devices);init_rwsem(&card->controls_rwsem);rwlock_init(&card->ctl_files_rwlock);INIT_LIST_HEAD(&card->controls);INIT_LIST_HEAD(&card->ctl_files);spin_lock_init(&card->files_lock);INIT_LIST_HEAD(&card->files_list);
#ifdef CONFIG_PMinit_waitqueue_head(&card->power_sleep);
#endif
建立逻辑设备: Control
/* the control interface cannot be accessed from the user space until *//* snd_cards_bitmask and snd_cards are set with snd_card_register */err = snd_ctl_create(card);if (err < 0) {dev_err(parent, "unable to register control minors\n");goto __error;}
建立proc文件中的info节点:通常就是 /proc/asound/card0
err = snd_info_card_create(card);if (err < 0) {dev_err(parent, "unable to create card info\n");goto __error_ctl;}
返回创建的snd_card对象:
struct snd_card *card;
...
*card_ret = card;
4、snd_card_register()
snd_card_register() 函数也定义在 /sound/core/init.c中:
/*** snd_card_register - register the soundcard* @card: soundcard structure** This function registers all the devices assigned to the soundcard.* Until calling this, the ALSA control interface is blocked from the* external accesses. Thus, you should call this function at the end* of the initialization of the card.** Return: Zero otherwise a negative error code if the registration failed.*/
int snd_card_register(struct snd_card *card)
创建sysfs下的设备:
if (!card->registered) {err = device_add(&card->card_dev); if (err < 0)return err;card->registered = true;}
通过snd_device_register_all() 注册所有挂在该声卡下的逻辑设备, snd_device_register_all() 实际上是通过snd_card的devices链表,遍历所有的snd_device,并且调用snd_device的ops->dev_register() 来实现各自设备的注册的:
if ((err = snd_device_register_all(card)) < 0)return err;
最后就是建立一些相应的proc和sysfs下的文件或属性节点:
if (*card->id) {/* make a unique id name from the given string */char tmpid[sizeof(card->id)];memcpy(tmpid, card->id, sizeof(card->id));snd_card_set_id_no_lock(card, tmpid, tmpid);} else {/* create an id from either shortname or longname */const char *src;src = *card->shortname ? card->shortname : card->longname;snd_card_set_id_no_lock(card, src,retrieve_id_from_card_name(src));}snd_cards[card->number] = card; init_info_for_card(card); //前面都是在填充,这里建立节点
另外,设备对应的class在 /sound/sound_core.c中创建:
static char *sound_devnode(struct device *dev, umode_t *mode)
{if (MAJOR(dev->devt) == SOUND_MAJOR)return NULL;return kasprintf(GFP_KERNEL, "snd/%s", dev_name(dev));
}static int __init init_soundcore(void)
{int rc; rc = init_oss_soundcore();if (rc)return rc; sound_class = class_create(THIS_MODULE, "sound");if (IS_ERR(sound_class)) {cleanup_oss_soundcore();return PTR_ERR(sound_class);} sound_class->devnode = sound_devnode;return 0;
}
由此可见,声卡的class将会出现在文件系统的 /sys/class/sound/ 下面,并且, sound_devnode() 也决定了相应的设备节点也将会出现在 /dev/snd/ 下面
总结
声卡实例snd_card由snd_card_new() 函数创建,指定了声卡的编号、标识符和私有数据等。然后填充Driver、ID还有shortname和longname。紧接着创建声卡所需的逻辑设备,包括control、pcm等,最后将声卡注册到内核中,会在/proc和sysfs中生成相应的节点。