acpi_init
[ 0.869605] [<c074461f>] dump_stack+0x49/0x73
[ 0.869813] [<c022fb0c>] warn_slowpath_common+0x62/0x79
[ 0.870005] [<c0485b68>] ? platform_device_alloc+0x72/0x7c
[ 0.870262] [<c022fba9>] warn_slowpath_null+0xf/0x13
[ 0.870494] [<c0485b68>] platform_device_alloc+0x72/0x7c
[ 0.870742] [<c0485e64>] platform_device_register_full+0x13/0xd4
[ 0.871046] [<c0425131>] acpi_create_platform_device+0x13f/0x176
[ 0.871326] [<c04219f6>] acpi_bus_attach+0x144/0x151
[ 0.871558] [<c04219a7>] acpi_bus_attach+0xf5/0x151
[ 0.871804] [<c0484a0e>] ? device_attach+0x6b/0x75
[ 0.872035] [<c04219a7>] acpi_bus_attach+0xf5/0x151
[ 0.872274] [<c0421a85>] acpi_bus_scan+0x49/0x54
[ 0.872492] [<c0a41a1e>] ? acpi_sleep_proc_init+0x23/0x23
[ 0.872744] [<c0a41dd0>] acpi_scan_init+0x5e/0x175
[ 0.872969] [<c0a41a1e>] ? acpi_sleep_proc_init+0x23/0x23
[ 0.873221] [<c0a41c2a>] acpi_init+0x20c/0x229
[ 0.873430] [<c0a19b1f>] do_one_initcall+0xd0/0x142
[ 0.873685] [<c0a19400>] ? do_early_param+0x5e/0x73
[ 0.873933] [<c0242891>] ? parse_args+0x1b5/0x291
[ 0.874172] [<c0a19c79>] kernel_init_freeable+0xe8/0x165
[ 0.874441] [<c07416f8>] kernel_init+0x8/0xb8
[ 0.874664] [<c074ac01>] ret_from_kernel_thread+0x21/0x30
[ 0.874939] [<c07416f0>] ? rest_init+0x70/0x70
[ 0.875170] ---[ end trace 067ac724fed6b503 ]---
[ 0.875456] alloc device : GFSH0003:00
下面的流程都会根据这个调用栈分析
static int __init acpi_init(void)
{
int result;
if (acpi_disabled) {
printk(KERN_INFO PREFIX "Interpreter disabled.\n");
return -ENODEV;
}
acpi_kobj = kobject_create_and_add("acpi", firmware_kobj);
if (!acpi_kobj) {
printk(KERN_WARNING "%s: kset create error\n", __func__);
acpi_kobj = NULL;
}
init_acpi_device_notify();
result = acpi_bus_init();
if (result) {
disable_acpi();
return result;
}
pci_mmcfg_late_init();
acpi_scan_init();
acpi_ec_init();
acpi_debugfs_init();
acpi_sleep_proc_init();
acpi_wakeup_device_init();
return 0;
}
subsys_initcall(acpi_init);
subsys_initcall宏将acpi_init注册到init.setup节,所以在内核启动过程中会执行该函数
主要执行了以下动作
1 创建acpi 的kobj,对应/sys/firmware/acpi目录
2调用 init_acpi_device_notify 函数,初始化两个函数指针如下
platform_notify = acpi_platform_notify;
platform_notify_remove = acpi_platform_notify_remove;
3 初始化acpi总线
4 初始化mmconfig先关的逻辑,这是和pci相关的部分 我们不分析
5 acpi_scan_init 扫描acpi总线
6 初始化acpi ec功能(笔记本)
7 初始化acpi debugfs
8 初始化acpi 睡眠进程
9 acpi_wakeup_device_init
我们这里重点关心acpi_scan_init看下如何创建acpi设备
int __init acpi_scan_init(void)
{
int result;
result = bus_register(&acpi_bus_type);
if (result) {
/* We don't want to quit even if we failed to add suspend/resume */
printk(KERN_ERR PREFIX "Could not register bus type\n");
}
acpi_pci_root_init();
acpi_pci_link_init();
acpi_processor_init();
acpi_lpss_init();
acpi_cmos_rtc_init();
acpi_container_init();
acpi_memory_hotplug_init();
acpi_pnp_init();
acpi_int340x_thermal_init();
mutex_lock(&acpi_scan_lock);
/*
* Enumerate devices in the ACPI namespace.
*/
result = acpi_bus_scan(ACPI_ROOT_OBJECT);
if (result)
goto out;
result = acpi_bus_get_device(ACPI_ROOT_OBJECT, &acpi_root);
if (result)
goto out;
/* Fixed feature devices do not exist on HW-reduced platform */
if (!acpi_gbl_reduced_hardware) {
result = acpi_bus_scan_fixed();
if (result) {
acpi_detach_data(acpi_root->handle,
acpi_scan_drop_device);
acpi_device_del(acpi_root);
put_device(&acpi_root->dev);
goto out;
}
}
acpi_update_all_gpes();
out:
mutex_unlock(&acpi_scan_lock);
return result;
}
1 注册acpi总线
2 初始化acpi根节点, 注册一系列处理函数和 kobject 如下
- pci_root_handler到链表acpi_scan_handlers_list, 并创建/sys/firmware/acpi/hotplug/pci_root 的kobj
- acpi_pci_link_init又向acpi_scan_handlers_list追加了一个pci_link_handler 处理句柄
- processor_handler追加到acpi_scan_handlers_list, 对应/sys/firmware/acpi/hotplug/process
- lpss_handler追加到acpi_scan_handlers_list
- cmos_rtc_handler 追加到acpi_scan_handlers_list
- container_handler追加到acpi_scan_handlers_list,对/sys/firmware/acpi/hotplug/container
- memory_device_handler追加到acpi_scan_handlers_list
- acpi_pnp_handler追加到acpi_scan_handlers_list
- acpi_int340x_thermal_init 追加到acpi_scan_handlers_list 用于温度相关的处理
3 扫描总线 (acpi_bus_scan), 参数为ACPI_ROOT_OBJECT
4 笔记本es相关(acpi_update_all_gpes)
acpi总线扫
在分析总线扫描函数之前我们先看下参数ACPI_ROOT_OBJECT
参数acpi_handle handle为要扫描的命名空间根节点,这里我们以ACPI_ROOT_OBJECT为例,先来看下ACPI_ROOT_OBJECT的定义
#define ACPI_MAX_PTR ACPI_UINT64_MAX
#define ACPI_CAST_PTR(t, p) ((t *) (acpi_uintptr_t) (p))
#define ACPI_CAST_INDIRECT_PTR(t, p) ((t **) (acpi_uintptr_t) (p))
#define ACPI_ADD_PTR(t, a, b) ACPI_CAST_PTR (t, (ACPI_CAST_PTR (u8, (a)) + (acpi_size)(b)))
#define ACPI_ROOT_OBJECT ACPI_ADD_PTR (acpi_handle, NULL, ACPI_MAX_PTR)
展开来看就是
((acpi_handle *) (acpi_uintptr_t) (((u8*) (acpi_uintptr_t) (NULL)) + (acpi_size)(ACPI_MAX_PTR))))
看起来还比较负责,其实就是把ACPI_MAX_PTR地址转为acpi_handle *类型
由此可见ACPI_ROOT_OBJECT的地址指向long类型的最大值,一般这种都是起代表意义,用max long地址表示根命名空间
/**
* acpi_bus_scan - Add ACPI device node objects in a given namespace scope.
* @handle: Root of the namespace scope to scan.
*
* Scan a given ACPI tree (probably recently hot-plugged) and create and add
* found devices.
*
* If no devices were found, -ENODEV is returned, but it does not mean that
* there has been a real error. There just have been no suitable ACPI objects
* in the table trunk from which the kernel could create a device and add an
* appropriate driver.
*
* Must be called under acpi_scan_lock.
*/
int acpi_bus_scan(acpi_handle handle)
{
void *device = NULL;
if (ACPI_SUCCESS(acpi_bus_check_add(handle, 0, NULL, &device)))
acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
acpi_bus_check_add, NULL, NULL, &device);
if (device) {
acpi_bus_attach(device);
return 0;
}
return -ENODEV;
}
函数比较好了解
1 使用acpi_bus_check_add 检查并添加参数handle指向的命名空间
2 调用 acpi_walk_namespace 遍历handler命名空间,执行acpi_bus_check_add函数进行检查添加
3 acpi_bus_attach 扫描到的设备(一串设备,并非1个)
先来分析acpi_bus_check_add 检查和添加命名空间
drivers/acpi/scan.c
static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
void *not_used, void **return_value)
{
struct acpi_device *device = NULL;
int type;
unsigned long long sta;
int result;
acpi_bus_get_device(handle, &device);
if (device)
goto out;
result = acpi_bus_type_and_status(handle, &type, &sta);
if (result)
return AE_OK;
if (type == ACPI_BUS_TYPE_POWER) {
acpi_add_power_resource(handle);
return AE_OK;
}
acpi_add_single_object(&device, handle, type, sta);
if (!device)
return AE_CTRL_DEPTH;
acpi_scan_init_hotplug(device);
out:
if (!*return_value)
*return_value = device;
return AE_OK;
}
1 调用acpi_bus_get_device从acpi_handle中获取到acpi_device设备(acpi_bus_get_device)
2 获取设备类型和状态 acpi_bus_type_and_status (acpi_bus_type_and_status)
3 如果是ACPI_BUS_TYPE_POWER类型设备 则调用acpi_add_power_resource添加
4 对于不是ACPI_BUS_TYPE_POWER类型的设备 acpi_add_single_object()添加设备
5 acpi_scan_init_hotplug 初始化热插拔接口
下面我们就按照这五个步骤对acpi设备总线进行分析
如何从命名空间节点获取acpi_device ? acpi_bus_get_device函数
int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device)
{
return acpi_get_device_data(handle, device, NULL);
}
static int acpi_get_device_data(acpi_handle handle, struct acpi_device **device,
void (*callback)(void *))
{
acpi_status status;
if (!device)
return -EINVAL;
status = acpi_get_data_full(handle, acpi_scan_drop_device,
(void **)device, callback);
if (ACPI_FAILURE(status) || !*device) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n",
handle));
return -ENODEV;
}
return 0;
}
/*******************************************************************************
*
* FUNCTION: acpi_get_data_full
*
* PARAMETERS: obj_handle - Namespace node
* handler - Handler used in call to attach_data
* data - Where the data is returned
* callback - function to execute before returning
*
* RETURN: Status
*
* DESCRIPTION: Retrieve data that was previously attached to a namespace node
* and execute a callback before returning.
*
******************************************************************************/
acpi_status
acpi_get_data_full(acpi_handle obj_handle, acpi_object_handler handler,
void **data, void (*callback)(void *))
{
struct acpi_namespace_node *node;
acpi_status status;
/* Parameter validation */
if (!obj_handle || !handler || !data) {
return (AE_BAD_PARAMETER);
}
status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
if (ACPI_FAILURE(status)) {
return (status);
}
/* Convert and validate the handle */
node = acpi_ns_validate_handle(obj_handle);
if (!node) {
status = AE_BAD_PARAMETER;
goto unlock_and_exit;
}
status = acpi_ns_get_attached_data(node, handler, data);
if (ACPI_SUCCESS(status) && callback) {
callback(*data);
}
unlock_and_exit:
(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
return (status);
}
函数其实是调用acpi_get_data_full 来获取acpi_device的,接收的参数分别是命名空间节点,和一个处理函数acpi_object_handler handler,以及传处参数 data和回调函数callback。 执行流程如下
1 转换acpi_handle为acpi_namespace_node结构
2 从acpi_namespace_node拿到attach data
3 成功调用callback回调
对于1 获取命名空间对象函数如下(我们前面分析了acpi_handler实际是void *)
/*******************************************************************************
*
* FUNCTION: acpi_ns_validate_handle
*
* PARAMETERS: handle - Handle to be validated and typecast to a
* namespace node.
*
* RETURN: A pointer to a namespace node
*
* DESCRIPTION: Convert a namespace handle to a namespace node. Handles special
* cases for the root node.
*
* NOTE: Real integer handles would allow for more verification
* and keep all pointers within this subsystem - however this introduces
* more overhead and has not been necessary to this point. Drivers
* holding handles are typically notified before a node becomes invalid
* due to a table unload.
*
******************************************************************************/
struct acpi_namespace_node *acpi_ns_validate_handle(acpi_handle handle)
{
ACPI_FUNCTION_ENTRY();
/* Parameter validation */
if ((!handle) || (handle == ACPI_ROOT_OBJECT)) {
return (acpi_gbl_root_node);
}
/* We can at least attempt to verify the handle */
if (ACPI_GET_DESCRIPTOR_TYPE(handle) != ACPI_DESC_TYPE_NAMED) {
return (NULL);
}
return (ACPI_CAST_PTR(struct acpi_namespace_node, handle));
}
这里处理的一种特殊情况为ACPI_ROOT_OBJECT 直接返回acpi_gbl_root_node节点,所以之前说ACPI_ROOT_OBJECT起标示作用是正确的
另外还进行了简单的验证
#define ACPI_GET_DESCRIPTOR_TYPE(d) (((union acpi_descriptor *)(void *)(d))->common.descriptor_type)
验证类型为ACPI_DESC_TYPE_NAMED, 最后只是强制转换acpi_handle为struct acpi_namespace_node *类型。
2 从acpi_namespace_node拿到attach data 是如何实现的呢
/*******************************************************************************
*
* FUNCTION: acpi_ns_get_attached_data
*
* PARAMETERS: node - Namespace node
* handler - Handler associated with the data
* data - Where the data is returned
*
* RETURN: Status
*
* DESCRIPTION: Low level interface to obtain data previously associated with
* a namespace node.
*
******************************************************************************/
acpi_status
acpi_ns_get_attached_data(struct acpi_namespace_node * node,
acpi_object_handler handler, void **data)
{
union acpi_operand_object *obj_desc;
obj_desc = node->object;
while (obj_desc) {
if ((obj_desc->common.type == ACPI_TYPE_LOCAL_DATA) &&
(obj_desc->data.handler == handler)) {
*data = obj_desc->data.pointer;
return (AE_OK);
}
obj_desc = obj_desc->common.next_object;
}
return (AE_NOT_FOUND);
}
是遍历 namespace得到的。
这里我们不得不来看下struct acpi_namespace_node 数据结构了
/*
* The Namespace Node describes a named object that appears in the AML.
* descriptor_type is used to differentiate between internal descriptors.
*
* The node is optimized for both 32-bit and 64-bit platforms:
* 20 bytes for the 32-bit case, 32 bytes for the 64-bit case.
*
* Note: The descriptor_type and Type fields must appear in the identical
* position in both the struct acpi_namespace_node and union acpi_operand_object
* structures.
*/
struct acpi_namespace_node {
union acpi_operand_object *object; /* Interpreter object */
u8 descriptor_type; /* Differentiate object descriptor types */
u8 type; /* ACPI Type associated with this name */
u8 flags; /* Miscellaneous flags */
acpi_owner_id owner_id; /* Node creator */
union acpi_name_union name; /* ACPI Name, always 4 chars per ACPI spec */
struct acpi_namespace_node *parent; /* Parent node */
struct acpi_namespace_node *child; /* First child */
struct acpi_namespace_node *peer; /* First peer */
/*
* The following fields are used by the ASL compiler and disassembler only
*/
#ifdef ACPI_LARGE_NAMESPACE_NODE
union acpi_parse_object *op;
u32 value;
u32 length;
#endif
};
原来acpi_namespace_node是从aml解析出来的节点。第一项为acpi_operand_object是一个union结构,里面是不同类型的 acpi操作对象,但是每个操作对象第一项都是ACPI_OBJECT_COMMON_HEADER,也就是
#define ACPI_OBJECT_COMMON_HEADER \
union acpi_operand_object *next_object; /* Objects linked to parent NS node */\
u8 descriptor_type; /* To differentiate various internal objs */\
u8 type; /* acpi_object_type */\
u16 reference_count; /* For object deletion management */\
u8 flags;
/*
* Note: There are 3 bytes available here before the
* next natural alignment boundary (for both 32/64 cases)
*/
链表,类型 和引用计数以及标记信息。
所以acpi_ns_get_attached_data实际上是遍历next_object链表,找到ACPI_TYPE_LOCAL_DATA类型的节点,从而获取到attach的device信息。
这些信息如何绑定的?????
参考acpi_initialize_subsystem