Linux ALSA驱动之二:声卡的创建流程

news/2025/2/5 5:56:49/

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 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 */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 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? */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_DEBUGstruct dentry *debugfs_root;    /* debugfs root for card */
#endif#ifdef CONFIG_PMunsigned 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;
}

3、snd_card_new函数详解

        用于创建并初始化一个声卡的结构体

/***  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**  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)
{struct snd_card *card;int err;if (snd_BUG_ON(!card_ret))return -EINVAL;*card_ret = NULL;if (extra_size < 0)extra_size = 0;/* 1. 分配snd_card和private_data的空间在snd_card后面的空间分配,card->private_data指向该空间*/card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);if (!card)return -ENOMEM;err = snd_card_init(card, parent, idx, xid, module, extra_size);if (err < 0) {kfree(card);return err;}*card_ret = card;return 0;
}static int snd_card_init(struct snd_card *card, struct device *parent,int idx, const char *xid, struct module *module,size_t extra_size)
{int err;
#ifdef CONFIG_SND_DEBUGchar name[8];
#endif/* 1、根据传入的参数赋值xid, idx, module, parent *//* (1). 为 card->private_datad 赋值 */if (extra_size > 0)card->private_data = (char *)card + sizeof(struct snd_card);/* (2). 为 card->id 赋值 */if (xid)strscpy(card->id, xid, sizeof(card->id));err = 0;mutex_lock(&snd_card_mutex);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);if (idx < 0)err = -ENODEV;else if (idx < snd_ecards_limit) {if (test_bit(idx, snd_cards_lock))err = -EBUSY;	/* invalid */} else if (idx >= SNDRV_CARDS)err = -ENODEV;if (err < 0) {mutex_unlock(&snd_card_mutex);dev_err(parent, "cannot find the slot for index %d (range 0-%i), error: %d\n",idx, snd_ecards_limit - 1, err);return err;}set_bit(idx, snd_cards_lock);		/* lock it */if (idx >= snd_ecards_limit)snd_ecards_limit = idx + 1; /* increase the limit */mutex_unlock(&snd_card_mutex);/* (3). 赋值parent */card->dev = parent;/* (4). 分配snd_card的序号 */card->number = idx;
#ifdef MODULEWARN_ON(!module);/* (5). 赋值module */card->module = module;
#endif/* 2、初始化结构体和变量 */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);mutex_init(&card->memory_mutex);
#ifdef CONFIG_PMinit_waitqueue_head(&card->power_sleep);init_waitqueue_head(&card->power_ref_sleep);atomic_set(&card->power_ref, 0);
#endifinit_waitqueue_head(&card->remove_sleep);card->sync_irq = -1;/* 设置设备文件节点的名字 */device_initialize(&card->card_dev);card->card_dev.parent = parent;card->card_dev.class = sound_class;card->card_dev.release = release_card_device;card->card_dev.groups = card->dev_groups;card->dev_groups[0] = &card_dev_attr_group;err = kobject_set_name(&card->card_dev.kobj, "card%d", idx);if (err < 0)goto __error;snprintf(card->irq_descr, sizeof(card->irq_descr), "%s:%s",dev_driver_string(card->dev), dev_name(&card->card_dev));/* the control interface cannot be accessed from the user space until *//* snd_cards_bitmask and snd_cards are set with snd_card_register *//* 创建一个control设备 */err = snd_ctl_create(card);if (err < 0) {dev_err(parent, "unable to register control minors\n");goto __error;}/* 生成声卡的proc文件 */err = snd_info_card_create(card);if (err < 0) {dev_err(parent, "unable to create card info\n");goto __error_ctl;}#ifdef CONFIG_SND_DEBUGsprintf(name, "card%d", idx);card->debugfs_root = debugfs_create_dir(name, sound_debugfs_root);
#endifreturn 0;__error_ctl:snd_device_free_all(card);__error:put_device(&card->card_dev);return err;
}

        这函数会执行如下操作:

                1、分配snd_card和private_data的空间。

                2、初始化结构体、变量、创建control设备、生成声卡的proc文件等全部都在snd_card_init函数中完成。

                3、获取private_data的地址等。

                4、sound_class会在init_soundcore中做初始化操作。

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;
}

                5、创建card的control设备。根据注释control接口在snd_card_register之后,用户空间才可以访问。

                6、调用snd_info_card_create函数在proc下创建card0目录,最終会根据entry的mode,创建目录。

/** create a card proc file* called from init.c*/
int snd_info_card_create(struct snd_card *card)
{char str[8];struct snd_info_entry *entry;if (snd_BUG_ON(!card))return -ENXIO;sprintf(str, "card%i", card->number);entry = create_subdir(card->module, str);if (!entry)return -ENOMEM;card->proc_root = entry;return snd_card_ro_proc_new(card, "id", card, snd_card_id_read);
}

4、snd_card_register函数详解

        用来注册声卡,主要完成了如下操作:

                1、创建声卡的设备节点。

                2、注册所有的逻辑设备。

                3、添加当前的声卡到声卡数组。

                4、注册声卡的proc文件

/***  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)
{int err;/* 合法性判断,如果此处card不存在,panic。 */if (snd_BUG_ON(!card))return -EINVAL;/* 1、根据card的registered判断是否已经注册,如果注册继续。否则调用device_add添加设备,设置registered标志。创建声卡的sysfs设备节点。其中card->card_dev在创建声卡结构体的时候被赋值。card->card_dev.class = sound_class;sound_class在sound模块被加载的时候创建设备节点:/dev/snd/cartd%i*/if (!card->registered) {err = device_add(&card->card_dev);if (err < 0)return err;card->registered = true;} else {if (card->managed)devm_remove_action(card->dev, trigger_card_free, card);}if (card->managed) {err = devm_add_action(card->dev, trigger_card_free, card);if (err < 0)return err;}/* 2、调用snd_device_register_all注册所有card的设备,包括pcm, control等 */err = snd_device_register_all(card);if (err < 0)return err;mutex_lock(&snd_card_mutex);/* 3、添加当前的声卡到声卡数组 */if (snd_cards[card->number]) {/* already registered */mutex_unlock(&snd_card_mutex);return snd_info_card_register(card); /* register pending info */}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;mutex_unlock(&snd_card_mutex);/* 4、注册声卡的proc文件 */err = snd_info_card_register(card);if (err < 0)return err;#if IS_ENABLED(CONFIG_SND_MIXER_OSS)if (snd_mixer_oss_notify_callback)snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
#endifreturn 0;
}/** register all the devices on the card.* called from init.c*/
int snd_device_register_all(struct snd_card *card)
{struct snd_device *dev;int err;if (snd_BUG_ON(!card))return -ENXIO;/* 遍历注册所有的snd_device,调用__snd_device_register函数完成注册 */list_for_each_entry(dev, &card->devices, list) {err = __snd_device_register(dev);if (err < 0)return err;}return 0;
}
static int __snd_device_register(struct snd_device *dev)
{if (dev->state == SNDRV_DEV_BUILD) {if (dev->ops->dev_register) {int err = dev->ops->dev_register(dev);if (err < 0)return err;}dev->state = SNDRV_DEV_REGISTERED;}return 0;
}

        此函数最终会调用各个devices的snd_device_ops中的dev_register函数。

        声卡注册完成之后,声卡的软件逻辑结果如下:


http://www.ppmy.cn/news/193498.html

相关文章

ALSA声卡驱动(二)声卡创建

文章目录 ALSA声卡驱动&#xff08;一&#xff09;初识alsa 目录 文章目录1、声卡实例1.1、struct snd_card1.2、struct snd_devide 2、声卡的创建流程2.1、创建一个声卡实例2.2、创建声卡芯片的专用数据2.3、设置Driver和ID和名字2.4、创建声卡的功能部件&#xff08;逻辑设备…

linux系统声卡安装教程,Linux操作系统声卡驱动的安装与配置

一般的声卡驱动是支持windows的&#xff0c;linux很少&#xff0c;所以安装声卡驱动很麻烦。 Linux下安装声卡驱动&#xff0c;用的是alsa&#xff0c;它就好像是万能的一样&#xff0c;可以支持很多类型的声卡&#xff0c;如&#xff1a; AC97 CodecALC100,100PALC200,200PALC…

petalinux2022.2在ubantu20.04下的安装

1.Petalinux的下载路径 Downloads 这个是下载petalinux的官网路径。默认是2022.2版本&#xff0c;后期更新的均是以petalinux2022.2版本做的更新。 2.安装流程 在官网下载完成之后&#xff0c;会得到一个名为petalinux-v2022.2-10141622-installer.run的文件&#xff0c;这个文…

解决win10声卡驱动不兼容问题和成功安装战神k650-i5-d2上的Sound Blaster Cinema2在win10系统上

安装win10后&#xff0c;偶尔会出现蓝屏&#xff0c;经过我的发现&#xff0c;每次听歌用扬声器并且长时间。都会发生蓝屏 1 . 所以怀疑声卡驱动VIA HD Audio&#xff08;Win 8.1&#xff09;与系统不兼容。 2 .干脆重新安装声卡驱动。在网上找到与win10兼容的VIAHDAud_v11_11…

linux alsa 不创建声卡能否创建pcm设备,Linux ALSA声卡驱动之一:声卡的创建

1. struct snd_card 1.1. snd_card是什么 snd_card可以说是整个ALSA音频驱动最顶层的一个结构&#xff0c;整个声卡的软件逻辑结构开始于该结构&#xff0c;几乎所有与声音相关的逻辑设备都是在snd_card的管理之下&#xff0c;声卡驱动的第一个动作通常就是创建一个snd_card结构…

Fedora Core下声卡驱动全功略(转)

Fedora Core下声卡驱动全功略(转) 1.了解你的声卡 声卡如同计算机的喉舌和耳朵&#xff0c;负责计算机音频的输入、信号转换、输出的工作&#xff0c;有了声卡&#xff0c;我们才能通过电脑欣赏美妙的音乐&#xff0c;才能对着话筒怒吼。99年后&#xff0c;随着计算机的进一步…

计算机设备管理器驱动,驱动技巧:解决设备管理器中声卡驱动安装不正确的问题...

分类&#xff1a;声卡驱动 问题&#xff1a;设备管理器中声卡驱动安装不正确 描述&#xff1a;电脑没有声音&#xff0c;有部分朋友是因为声卡驱动没有正确安装&#xff0c;除了我们常见到的设备管理器出现黄色感叹号之外&#xff0c;另一种情况就是让一般人很难发现的问题&…

ALSA声卡驱动二之声卡的创建

1. struct snd_card 1.1. snd_card是什么 snd_card可以说是整个ALSA音频驱动最顶层的一个结构&#xff0c;整个声卡的软件逻辑结构开始于该结构&#xff0c;几乎所有与声音相关的逻辑设备都是在snd_card的管理之下&#xff0c;声卡驱动的第一个动作通常就是创建一个snd_card结构…