summaryrefslogtreecommitdiff
path: root/drivers/acpi/glue.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi/glue.c')
-rw-r--r--drivers/acpi/glue.c101
1 files changed, 62 insertions, 39 deletions
diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c
index 8d769114a048..552d82dfa476 100644
--- a/drivers/acpi/glue.c
+++ b/drivers/acpi/glue.c
@@ -105,51 +105,74 @@ static int find_child_checks(struct acpi_device *adev, bool check_children)
return FIND_CHILD_MAX_SCORE;
}
-struct acpi_device *acpi_find_child_device(struct acpi_device *parent,
- u64 address, bool check_children)
+struct find_child_walk_data {
+ struct acpi_device *adev;
+ u64 address;
+ int score;
+ bool check_children;
+};
+
+static int check_one_child(struct acpi_device *adev, void *data)
{
- struct acpi_device *adev, *ret = NULL;
- int ret_score = 0;
+ struct find_child_walk_data *wd = data;
+ int score;
- if (!parent)
- return NULL;
+ if (!adev->pnp.type.bus_address || acpi_device_adr(adev) != wd->address)
+ return 0;
- list_for_each_entry(adev, &parent->children, node) {
- acpi_bus_address addr = acpi_device_adr(adev);
- int score;
+ if (!wd->adev) {
+ /* This is the first matching object. Save it and continue. */
+ wd->adev = adev;
+ return 0;
+ }
- if (!adev->pnp.type.bus_address || addr != address)
- continue;
+ /*
+ * There is more than one matching device object with the same _ADR
+ * value. That really is unexpected, so we are kind of beyond the scope
+ * of the spec here. We have to choose which one to return, though.
+ *
+ * First, get the score for the previously found object and terminate
+ * the walk if it is maximum.
+ */
+ if (!wd->score) {
+ score = find_child_checks(wd->adev, wd->check_children);
+ if (score == FIND_CHILD_MAX_SCORE)
+ return 1;
+
+ wd->score = score;
+ }
+ /*
+ * Second, if the object that has just been found has a better score,
+ * replace the previously found one with it and terminate the walk if
+ * the new score is maximum.
+ */
+ score = find_child_checks(adev, wd->check_children);
+ if (score > wd->score) {
+ wd->adev = adev;
+ if (score == FIND_CHILD_MAX_SCORE)
+ return 1;
- if (!ret) {
- /* This is the first matching object. Save it. */
- ret = adev;
- continue;
- }
- /*
- * There is more than one matching device object with the same
- * _ADR value. That really is unexpected, so we are kind of
- * beyond the scope of the spec here. We have to choose which
- * one to return, though.
- *
- * First, check if the previously found object is good enough
- * and return it if so. Second, do the same for the object that
- * we've just found.
- */
- if (!ret_score) {
- ret_score = find_child_checks(ret, check_children);
- if (ret_score == FIND_CHILD_MAX_SCORE)
- return ret;
- }
- score = find_child_checks(adev, check_children);
- if (score == FIND_CHILD_MAX_SCORE) {
- return adev;
- } else if (score > ret_score) {
- ret = adev;
- ret_score = score;
- }
+ wd->score = score;
}
- return ret;
+
+ /* Continue, because there may be better matches. */
+ return 0;
+}
+
+struct acpi_device *acpi_find_child_device(struct acpi_device *parent,
+ u64 address, bool check_children)
+{
+ struct find_child_walk_data wd = {
+ .address = address,
+ .check_children = check_children,
+ .adev = NULL,
+ .score = 0,
+ };
+
+ if (parent)
+ acpi_dev_for_each_child(parent, check_one_child, &wd);
+
+ return wd.adev;
}
EXPORT_SYMBOL_GPL(acpi_find_child_device);