acpi设备管理分析

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

发布了113 篇原创文章 · 获赞 22 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/woai110120130/article/details/92685649