Linux ALSA驱动之二:声卡的创建流程 上
1、struct snd_card
1.1、snd_card是啥
snd_card可以说是整个ALSA音频驱动最顶层的一个结构,整个声卡的软件逻辑结构开始于该结构,几乎所有与声音相关的逻辑设备都是在snd_card的管理之下,声卡驱动的第一个动作通常就是创建一个snd_card结构体。因此我们也从 struct snd_card的讲解开始。
1.2、snd_card定义
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 */ size_t user_ctl_alloc_size; // current memory allocation by 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 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? */ bool managed; /* managed via devres */ bool releasing; /* during card free process */ int sync_irq; /* assigned irq, used for PCM sync */ wait_queue_head_t remove_sleep; size_t total_pcm_alloc_bytes; /* total amount of allocated buffers */ struct mutex memory_mutex; /* protection for the above */ #ifdef CONFIG_SND_DEBUG struct dentry *debugfs_root; /* debugfs root for card */ #endif #ifdef CONFIG_PM unsigned int power_state; /* power state */ atomic_t power_ref; wait_queue_head_t power_sleep; wait_queue_head_t power_ref_sleep; #endif #if IS_ENABLED(CONFIG_SND_MIXER_OSS) struct snd_mixer_oss *mixer_oss; int mixer_oss_change_count; #endif };
对于每个声卡,都需要有一个snd_card结构体来描述。它记录着声卡的信息并管理声卡的所有设备。其中几个比较重要的成员:
int number 声卡的序号,通常为0。
struct list_head devices 记录该声卡下所有逻辑设备的链表。
struct list_head controls 记录该声卡下所有的控制单元的链表。
void *private_data 声卡的私有数据,可以在创建声卡时通过参数指定数据的大小。
bool registered 声卡是否在系统中注册了。
2、声卡创建流程
2.1、创建一个card实例
struct snd_card *card; snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, THIS_MODULE, sizeof(struct snd_dw_hdmi), &card);
2.2、创建声卡的芯片专用数据
设置该声卡的一些资源信息,例如:中断、IO、DMA等。有两种方式进行创建。
1)、作为声卡的private_data
在创建声卡的时候传入外部数据长度。
struct mychip { struct snd_card *card; .... }; err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, sizeof(struct mychip), &card); struct mychip *chip = card->private_data; chip->card = card;
2)、作为声卡的一个子设备
在snd_device_new中指定extra_size为0。
static int snd_mychip_dev_free(struct snd_device *device) { return snd_mychip_free(device->device_data); } struct snd_card *card; struct mychip *chip; err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 0, &card); ..... chip = kzalloc(sizeof(*chip), GFP_KERNEL); chip->card = card; static struct snd_device_ops ops = { .dev_free = snd_mychip_dev_free, }; .... snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
声卡注销时,会调用snd_mychip_dev_free, 自动释放内存。
snd_device_new不会给芯片专用数据device_data分配空间,因此在调用之前,必须为芯片专用分配空间,在ops的dev_free中定义析构函数对芯片专用数据进行析构。dev_free会在调用snd_card_free时自动调用。对于用户自定义的 device、type可以使用SNDRV_DEV_LOWLEVEL。
snd_mychip_dev_free() 是用来free前面kzmalloc的空间。
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) 或 strncpy(card->driver, shortname, sizeof(card->driver)); strncpy(card->shortname, shortname, sizeof(card->shortname)); strncpy(card->longname, longname, sizeof(card->longname));
2.4、创建声卡功能逻辑部件,如PCM,mixer, MIDI
每一种部件的创建最终会调用snd_device_new()来生成一个snd_device实例,并把该实例链接到snd_card的devices链表中。通常,alsa-driver的已经提供了一些常用的部件的创建函数,而不必直接调用snd_device_new(),比如: snd_pcm_new()。
2.5、注册声卡
/* register it */ err = snd_card_register(card); if (err < 0) { pk_error("failed to register pc-midi sound card: error %d\n", err); goto fail_register; }