ALSA驱动源码之devm_snd_soc_register_component源码分析
一、devm_snd_soc_register_component
/** * devm_snd_soc_register_component - resource managed component registration * @dev: Device used to manage component * @cmpnt_drv: Component driver * @dai_drv: DAI driver * @num_dai: Number of DAIs to register * * Register a component with automatic unregistration when the device is * unregistered. */ int devm_snd_soc_register_component(struct device *dev, const struct snd_soc_component_driver *cmpnt_drv, struct snd_soc_dai_driver *dai_drv, int num_dai) { const struct snd_soc_component_driver **ptr; int ret; ptr = devres_alloc(devm_component_release, sizeof(*ptr), GFP_KERNEL); if (!ptr) return -ENOMEM; ret = snd_soc_register_component(dev, cmpnt_drv, dai_drv, num_dai); if (ret == 0) { *ptr = cmpnt_drv; devres_add(dev, ptr); } else { devres_free(ptr); } return ret; } EXPORT_SYMBOL_GPL(devm_snd_soc_register_component);
通过devm_snd_soc_register_component源码可知,此函数主要通过devres_alloc函数完成了devm_component_release的注册,并申请了资源,然后通过snd_soc_register_component完成了snd_soc_component_driver和snd_soc_dai_driver完成了dai的注册。
1.1、devres_alloc宏
我么来看一下devres_alloc这个宏,它是通过调研__devres_alloc_node来完成资源的申请,具体实现如下:
#define devres_alloc(release, size, gfp) \ __devres_alloc_node(release, size, gfp, NUMA_NO_NODE, #release)
1.2、snd_soc_register_component
此处来看一下snd_soc_register_component函数,此函数中主要通过devm_kzalloc函数来完成了资源的申请,然后通过snd_soc_component_initialize函数来完成component组件的初始化,最后通过snd_soc_add_component函数将component添加到component->list当中,具体代码实现如下:
int snd_soc_register_component(struct device *dev, const struct snd_soc_component_driver *component_driver, struct snd_soc_dai_driver *dai_drv, int num_dai) { struct snd_soc_component *component; int ret; component = devm_kzalloc(dev, sizeof(*component), GFP_KERNEL); if (!component) return -ENOMEM; ret = snd_soc_component_initialize(component, component_driver, dev); if (ret < 0) return ret; return snd_soc_add_component(component, dai_drv, num_dai); } EXPORT_SYMBOL_GPL(snd_soc_register_component);
1.2.1、snd_soc_component_initialize
再来看一下snd_soc_component_initialize函数,此函数通过INIT_LIST_HEAD这宏完成了四个list的初始化,有component->dai_list、component->dobj_list、component->card_list和component->list,然后通过fmt_single_name函数来完成component->id的component组件的匹配工作,具体源码实现如下:
int snd_soc_component_initialize(struct snd_soc_component *component, const struct snd_soc_component_driver *driver, struct device *dev) { INIT_LIST_HEAD(&component->dai_list); INIT_LIST_HEAD(&component->dobj_list); INIT_LIST_HEAD(&component->card_list); INIT_LIST_HEAD(&component->list); mutex_init(&component->io_mutex); component->name = fmt_single_name(dev, &component->id); if (!component->name) { dev_err(dev, "ASoC: Failed to allocate name\n"); return -ENOMEM; } component->dev = dev; component->driver = driver; #ifdef CONFIG_DEBUG_FS if (!component->debugfs_prefix) component->debugfs_prefix = driver->debugfs_prefix; #endif return 0; } EXPORT_SYMBOL_GPL(snd_soc_component_initialize);
1.2.2、snd_soc_add_component
我们再来看一下snd_soc_add_component函数,此函数主要是通过snd_soc_register_dais函数来完成snd_soc_dai_driver的注册,并将component来添加到component->list里面去,具体源码实现如下:
int snd_soc_add_component(struct snd_soc_component *component, struct snd_soc_dai_driver *dai_drv, int num_dai) { int ret; int i; mutex_lock(&client_mutex); if (component->driver->endianness) { for (i = 0; i < num_dai; i++) { convert_endianness_formats(&dai_drv[i].playback); convert_endianness_formats(&dai_drv[i].capture); } } ret = snd_soc_register_dais(component, dai_drv, num_dai); if (ret < 0) { dev_err(component->dev, "ASoC: Failed to register DAIs: %d\n", ret); goto err_cleanup; } if (!component->driver->write && !component->driver->read) { if (!component->regmap) component->regmap = dev_get_regmap(component->dev, NULL); if (component->regmap) snd_soc_component_setup_regmap(component); } /* see for_each_component */ list_add(&component->list, &component_list); err_cleanup: if (ret < 0) snd_soc_del_component_unlocked(component); mutex_unlock(&client_mutex); if (ret == 0) snd_soc_try_rebind_card(); return ret; } EXPORT_SYMBOL_GPL(snd_soc_add_component);
此处一块来看一下snd_soc_add_component函数中的异常处理函数,snd_soc_del_component_unlocked函数中通过snd_soc_unregister_dais来完成snd_soc_dai_driver的反注册过程,然后通过snd_soc_unbind_card函数将card从list中删除,最后通过list_del将component组件的component->list销毁掉,具体函数实现如下:
static void snd_soc_del_component_unlocked(struct snd_soc_component *component) { struct snd_soc_card *card = component->card; snd_soc_unregister_dais(component); if (card) snd_soc_unbind_card(card, false); list_del(&component->list); }
一块再看一下snd_soc_unregister_dais函数,此函数中通过for_each_component_dais_safe(component, dai, _dai)遍历dai->list,如果发现匹配的snd_soc_dai_driver则调用snd_soc_unregister_dai函数完成snd_soc_dai_driver的反初始化操作,具体的snd_soc_unregister_dais和snd_soc_unregister_dai源码实现如下:
/** * snd_soc_unregister_dais - Unregister DAIs from the ASoC core * * @component: The component for which the DAIs should be unregistered */ static void snd_soc_unregister_dais(struct snd_soc_component *component) { struct snd_soc_dai *dai, *_dai; for_each_component_dais_safe(component, dai, _dai) snd_soc_unregister_dai(dai); }
snd_soc_unregister_dai源码是直接调用list_del函数将dai->list删除。
void snd_soc_unregister_dai(struct snd_soc_dai *dai) { dev_dbg(dai->dev, "ASoC: Unregistered DAI '%s'\n", dai->name); list_del(&dai->list); } EXPORT_SYMBOL_GPL(snd_soc_unregister_dai);