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)
接下来分析一下这个函数,函数会执行如下的操作:
- 分配snd_card和private_data的空间
- 根据传入的参数赋值xid, idx, module, parent
- 初始化结构体,变量
- 创建一个control设备
- 生成声卡的 proc 文件
- 获取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
用来注册一个声卡,继续分析一下这个函数:
- 创建声卡的设备节点
- 注册说有的逻辑设备
- 添加当前的声卡到声卡的数组
- 注册声卡的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);