diff options
Diffstat (limited to 'drivers/acpi/property.c')
-rw-r--r-- | drivers/acpi/property.c | 125 |
1 files changed, 89 insertions, 36 deletions
diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 03f5ec11ab31..3afddcd834ef 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -41,14 +41,13 @@ static bool acpi_enumerate_nondev_subnodes(acpi_handle scope, static bool acpi_extract_properties(const union acpi_object *desc, struct acpi_device_data *data); -static bool acpi_nondev_subnode_ok(acpi_handle scope, - const union acpi_object *link, - struct list_head *list) +static bool acpi_nondev_subnode_extract(const union acpi_object *desc, + acpi_handle handle, + const union acpi_object *link, + struct list_head *list) { - struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; struct acpi_data_node *dn; - acpi_handle handle; - acpi_status status; + bool result; dn = kzalloc(sizeof(*dn), GFP_KERNEL); if (!dn) @@ -58,43 +57,75 @@ static bool acpi_nondev_subnode_ok(acpi_handle scope, dn->fwnode.type = FWNODE_ACPI_DATA; INIT_LIST_HEAD(&dn->data.subnodes); - status = acpi_get_handle(scope, link->package.elements[1].string.pointer, - &handle); - if (ACPI_FAILURE(status)) - goto fail; + result = acpi_extract_properties(desc, &dn->data); - status = acpi_evaluate_object_typed(handle, NULL, NULL, &buf, - ACPI_TYPE_PACKAGE); - if (ACPI_FAILURE(status)) - goto fail; + if (handle) { + acpi_handle scope; + acpi_status status; - if (acpi_extract_properties(buf.pointer, &dn->data)) - dn->handle = handle; + /* + * The scope for the subnode object lookup is the one of the + * namespace node (device) containing the object that has + * returned the package. That is, it's the scope of that + * object's parent. + */ + status = acpi_get_parent(handle, &scope); + if (ACPI_SUCCESS(status) + && acpi_enumerate_nondev_subnodes(scope, desc, &dn->data)) + result = true; + } else if (acpi_enumerate_nondev_subnodes(NULL, desc, &dn->data)) { + result = true; + } - /* - * The scope for the subnode object lookup is the one of the namespace - * node (device) containing the object that has returned the package. - * That is, it's the scope of that object's parent. - */ - status = acpi_get_parent(handle, &scope); - if (ACPI_SUCCESS(status) - && acpi_enumerate_nondev_subnodes(scope, buf.pointer, &dn->data)) + if (result) { dn->handle = handle; - - if (dn->handle) { - dn->data.pointer = buf.pointer; + dn->data.pointer = desc; list_add_tail(&dn->sibling, list); return true; } + kfree(dn); acpi_handle_debug(handle, "Invalid properties/subnodes data, skipping\n"); + return false; +} + +static bool acpi_nondev_subnode_data_ok(acpi_handle handle, + const union acpi_object *link, + struct list_head *list) +{ + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; + acpi_status status; + + status = acpi_evaluate_object_typed(handle, NULL, NULL, &buf, + ACPI_TYPE_PACKAGE); + if (ACPI_FAILURE(status)) + return false; + + if (acpi_nondev_subnode_extract(buf.pointer, handle, link, list)) + return true; - fail: ACPI_FREE(buf.pointer); - kfree(dn); return false; } +static bool acpi_nondev_subnode_ok(acpi_handle scope, + const union acpi_object *link, + struct list_head *list) +{ + acpi_handle handle; + acpi_status status; + + if (!scope) + return false; + + status = acpi_get_handle(scope, link->package.elements[1].string.pointer, + &handle); + if (ACPI_FAILURE(status)) + return false; + + return acpi_nondev_subnode_data_ok(handle, link, list); +} + static int acpi_add_nondev_subnodes(acpi_handle scope, const union acpi_object *links, struct list_head *list) @@ -103,15 +134,37 @@ static int acpi_add_nondev_subnodes(acpi_handle scope, int i; for (i = 0; i < links->package.count; i++) { - const union acpi_object *link; + const union acpi_object *link, *desc; + acpi_handle handle; + bool result; link = &links->package.elements[i]; - /* Only two elements allowed, both must be strings. */ - if (link->package.count == 2 - && link->package.elements[0].type == ACPI_TYPE_STRING - && link->package.elements[1].type == ACPI_TYPE_STRING - && acpi_nondev_subnode_ok(scope, link, list)) - ret = true; + /* Only two elements allowed. */ + if (link->package.count != 2) + continue; + + /* The first one must be a string. */ + if (link->package.elements[0].type != ACPI_TYPE_STRING) + continue; + + /* The second one may be a string, a reference or a package. */ + switch (link->package.elements[1].type) { + case ACPI_TYPE_STRING: + result = acpi_nondev_subnode_ok(scope, link, list); + break; + case ACPI_TYPE_LOCAL_REFERENCE: + handle = link->package.elements[1].reference.handle; + result = acpi_nondev_subnode_data_ok(handle, link, list); + break; + case ACPI_TYPE_PACKAGE: + desc = &link->package.elements[1]; + result = acpi_nondev_subnode_extract(desc, NULL, link, list); + break; + default: + result = false; + break; + } + ret = ret || result; } return ret; |