【补充】
关于 ASoC Codec驱动代码框架更详细的介绍可以阅读《ASoC Codec 驱动代码框架图》。(2016年9月7日 添加)
【前言】
Linux下的音频驱动多采用 ASoC 架构。在这个架构里,驱动分 Platform、Codec、Machine 这 3 部分,相关介绍可以参见前文《Linux AsoC音频驱动架构 及 Machine驱动代码分析》。本文分析的是 Codec 部分,即音频编解码芯片的驱动代码。这里用作例子的芯片型号为 ALC5677。
如前文《Linux I2C总线框架 学习笔记》所述,编写 I2C 驱动时会添加 2 个模块——Bus Driver 和 Device Driver。分别添加 client 结构体相关信息和实现 driver 函数如 (*probe)、(*remove) 等。
【client 设备注册流程】
先来看看 client 部分的内容。在源文件 rt5677.c 末尾可以看到如下一行代码:
device_initcall(rt5677_i2c_dev_init);
这里的代码入口不是我们常见的 module_init(fn) 方式,他们区别在哪里呢?使用 SourceInsight 可以很方便地跳转到 device_initcall(fn) 定义处,可以看到下面这段预处理代码:
#ifndef MODULE /* 如果没有定义 MODULE */
#ifndef __ASSEMBLY__
…
#define fs_initcall(fn) __define_initcall(fn,5)
#define fs_initcall_sync(fn) __define_initcall(fn, 5s)
#define rootfs_initcall(fn) __define_initcall(fn, rootfs)
#define device_initcall(fn) __define_initcall(fn,6)
#define device_initcall_sync(fn) __define_initcall(fn, 6s) /* 注意 */
#define late_initcall(fn) __define_initcall(fn, 7)
#define late_initcall_sync(fn) __define_initcall(fn, 7s)
…
#endif /* __ASSEMBLY__ */
#define module_init(x) __initcall(x);
#define module_exit(x) __exitcall(x);
#else /* MODULE*/ /* 如果定义了 MODULE */
…
#define subsys_initcall(fn) module_init(fn)
#define fs_initcall(fn) module_init(fn)
#define rootfs_initcall(fn) module_init(fn)
#define device_initcall(fn) module_init(fn) /* 注意 */
#define late_initcall(fn) module_init(fn)
#define console_initcall(fn) module_init(fn)
#define security_initcall(fn) module_init(fn)
#define module_init(initfn) \
staticinline initcall_t __inittest(void) \
{return initfn; } \
intinit_module(void) __attribute__((alias(#initfn)));
#define module_exit(exitfn) \
staticinline exitcall_t __exittest(void) \
{return exitfn; } \
voidcleanup_module(void) __attribute__((alias(#exitfn)));
…
#endif
可以看到,在定义了 MODULE 的情况下,device_inticall(fn) 就是 module_init(fn),而在另一种情况下是 __define_initcall(fn, 6)。对于后者,其中的参数值 6 表示 id,取值范围从 0~7s 共 17 种。这个 id 值越大,模块被内核加载的时间越晚。有的时候,一个模块 a 的正常工作需要依赖于模块 b,这种情况下就可以使用 __define_initcall(fn, id) 来规定模块的加载顺序。
继续分析,进入 rt5677_i2c_dev_init() 函数查看 rt5677 驱动模块初始化流程。代码如下:
static int __init rt5677_i2c_dev_init(void)
{
int i2c_busnum = 0;
struct i2c_board_info i2c_info;
struct i2c_adapter *adapter;
...
memset(&i2c_info,0, sizeof(i2c_info));
strncpy(i2c_info.type,"rt5677", strlen("rt5677"));
i2c_info.addr = 0x2c; /* 101100X. 读周期 X 为 1, 写周期 X 为 0 */
...
adapter = i2c_get_adapter(i2c_busnum);
if(adapter) {
if(i2c_new_device(adapter, &i2c_info)){
printk("addnew i2c device %s , addr 0x%x\n", "rt5677", i2c_info.addr);
return0;
}else{
printk("addnew i2c device %s , addr 0x%x fail !!!\n", "rt5677",i2c_info.addr);
}
}else{
printk("[%s]getadapter %d fail\n",__func__, i2c_busnum);
return-EINVAL;
}
}
代码很短,流程很清楚。首先手动将 alc5677 的 i2c_busnum 划分为 0 ,然后给板级信息结构体 i2c_info 初始化 I2C 地址,值为 0x2c。该地址是芯片的 datasheet 里给定的,其中 I2C_ADDR 位对应的引脚电平为低电平,所以值为0,如下图:
之后,获取用于管理挂接在编号为 0 的 i2c_busnum 下的 i2c 设备的适配器(没看懂?可以参考一下《LinuxI2C总线框架 学习笔记》这篇文章)。如果这里获取适配器 adapter 失败,则直接退出,返回值为 -EINVAL; 如果获取成功,则调用 i2c_new_device(adapter, &i2c_info) 函数为该 adapter 添加一个内容与编解码芯片 ALC5677 一致的 client。相应代码如下:
struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_infoconst *info)
{
struct i2c_client *client;
int status;
client = kzalloc(sizeof *client,GFP_KERNEL);
if (!client)
return NULL;
client->adapter = adap; // 将 adapter 和 client 绑定
client->dev.platform_data = info->platform_data;
if (info->archdata)
client->dev.archdata = *info->archdata;
client->flags = info->flags; // 将板级信息结构体中的配置对应写入 client
client->addr = info->addr;
client->irq = info->irq;
client->irq_flags = info->irq_flags;
...
strlcpy(client->name,info->type, sizeof(client->name));
/* Check for address validity */
status = i2c_check_client_addr_validity(client);
if (status) {
dev_err(&adap->dev,"Invalid %d-bit I2C address 0x%02hx\n",
client->flags& I2C_CLIENT_TEN ? 10 : 7, client->addr);
goto out_err_silent;
}
/* Check for address business */
status = i2c_check_addr_busy(adap,client->addr);
if (status)
goto out_err;
client->dev.parent = &client->adapter->dev;
client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type;
client->dev.of_node = info->of_node;
...
status = device_register(&client->dev); /* 注册 client 设备 */
if (status)
goto out_err;
dev_dbg(&adap->dev, "client[%s] registered with bus id %s\n",
client->name, dev_name(&client->dev));
return client;
out_err:
dev_err(&adap->dev, "Failedto register i2c client %s at 0x%02x "
"(%d)\n",client->name, client->addr, status);
out_err_silent:
kfree(client);
return NULL;
}
可以看到在这个函数里,1 个新的 client 被创建并被关联到 adapter 上,相应的 i2c 地址、中断号等信息都被赋为 info 结构体里的值。在所需 client 结构体的成员都被初始化之后,调用 device_register(&client->dev) 函数对 client 设备进行注册。至此,设备注册完成,即 client 部分代码结束。
【driver 驱动注册流程】
再来看看 driver 函数相关的内容。在源文件 rt5677.c 中,我们还能看到下面这样一句代码:
module_i2c_driver(rt5677_i2c_driver);
这是一个宏定义,对其进行追踪可以看到如下声明:
(〇)driver 结构体定义:
struct i2c_driver rt5677_i2c_driver = {
.driver= {
.name = "rt5677",
.owner = THIS_MODULE,
#ifdef CONFIG_PM
.pm = &rt5677_pm_ops,
#endif
#ifdef RTACPI_I2C
.acpi_match_table = ACPI_PTR(rt5677_acpi_match),
#endif
},
.probe = rt5677_i2c_probe, /* 关联 probe 函数 */
.remove = rt5677_i2c_remove, /* 关联 remove 函数 */
.shutdown = rt5677_i2c_shutdown, /* 关联 shutdown 函数 */
.id_table = rt5677_i2c_id, /* 所支持设备的 ID 表 */
};
(一)module_i2c_driver() 宏:
/**
*module_i2c_driver() - Helper macro for registering a I2C driver
*@__i2c_driver: i2c_driver struct
*
*Helper macro for I2C drivers which do not do anything special in module
*init/exit. This eliminates a lot of boilerplate. Each module may only
*use this macro once, and calling it replaces module_init() and module_exit()
*/
#define module_i2c_driver(__i2c_driver)\
module_driver(__i2c_driver,i2c_add_driver, \
i2c_del_driver)
(二)module_driver() 宏:
#define module_driver(__driver, __register, __unregister, ...) \
static int __init__driver##_init(void) \
{ \
return__register(&(__driver) ,##__VA_ARGS__); \
} \
module_init(__driver##_init); \ /* 模块入口声明 */
static void __exit __driver##_exit(void) \
{ \
__unregister(&(__driver), ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);
(三)i2c_add_driver() 函数:
/* use a define to avoid include chaining to get THIS_MODULE */
#define i2c_add_driver(driver)\
i2c_register_driver(THIS_MODULE,driver)
/*
* Ani2c_driver is used with one or more i2c_client (device) nodes to access
*i2c slave chips, on a bus instance associated with some i2c_adapter.
*/
int i2c_register_driver(structmodule *owner, struct i2c_driver *driver)
{
int res;
/* Can't register until after driver model init */
if(unlikely(WARN_ON(!i2c_bus_type.p)))
return-EAGAIN;
/*add the driver to the list of i2c drivers in the driver core */
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type;
/*When registration returns, the driver core
* will have called probe() for allmatching-but-unbound devices.
*/
res = driver_register(&driver->driver); /* 驱动注册。注册成功返回 0 */
if(res)
return res;
...
INIT_LIST_HEAD(&driver->clients);
/* Walk the adapters that are already present */
i2c_for_each_dev(driver,__process_new_driver);
return 0;
}
EXPORT_SYMBOL(i2c_register_driver);
通过层层传递,我们找到了 driver 函数注册相关代码的起始点—— i2c_register_driver() 函数。在这个函数里,我们传入了 driver 结构体并调用函数 driver_register() 对驱动进行了注册。至此,表层的驱动注册完成,即 driver 函数相关代码结束。
实际上,源文件 rt5677.c 中还有类似 rt5677_probe()、rt5677_remove() 这类更底层一些的驱动函数,我们在这篇文章里暂时先不讨论,随着代码阅读和学习的深入,以后我再找时间单独写一篇。
【扩展阅读】
[1] 《postcore_initcall(), arch_initcall(), subsys_initcall(),device_initcall() 调用顺序》
[2] 《linux内核__define_initcall分析》