diff options
Diffstat (limited to 'drivers/acpi/scan.c')
-rw-r--r-- | drivers/acpi/scan.c | 60 |
1 files changed, 53 insertions, 7 deletions
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index fe9f2c926663..1453cd0672fb 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c | |||
@@ -53,6 +53,7 @@ static const struct acpi_device_id acpi_platform_device_ids[] = { | |||
53 | static LIST_HEAD(acpi_device_list); | 53 | static LIST_HEAD(acpi_device_list); |
54 | static LIST_HEAD(acpi_bus_id_list); | 54 | static LIST_HEAD(acpi_bus_id_list); |
55 | static DEFINE_MUTEX(acpi_scan_lock); | 55 | static DEFINE_MUTEX(acpi_scan_lock); |
56 | static LIST_HEAD(acpi_scan_handlers_list); | ||
56 | DEFINE_MUTEX(acpi_device_lock); | 57 | DEFINE_MUTEX(acpi_device_lock); |
57 | LIST_HEAD(acpi_wakeup_device_list); | 58 | LIST_HEAD(acpi_wakeup_device_list); |
58 | 59 | ||
@@ -62,6 +63,15 @@ struct acpi_device_bus_id{ | |||
62 | struct list_head node; | 63 | struct list_head node; |
63 | }; | 64 | }; |
64 | 65 | ||
66 | int acpi_scan_add_handler(struct acpi_scan_handler *handler) | ||
67 | { | ||
68 | if (!handler || !handler->attach) | ||
69 | return -EINVAL; | ||
70 | |||
71 | list_add_tail(&handler->list_node, &acpi_scan_handlers_list); | ||
72 | return 0; | ||
73 | } | ||
74 | |||
65 | /* | 75 | /* |
66 | * Creates hid/cid(s) string needed for modalias and uevent | 76 | * Creates hid/cid(s) string needed for modalias and uevent |
67 | * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get: | 77 | * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get: |
@@ -1570,20 +1580,42 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, | |||
1570 | return AE_OK; | 1580 | return AE_OK; |
1571 | } | 1581 | } |
1572 | 1582 | ||
1583 | static int acpi_scan_attach_handler(struct acpi_device *device) | ||
1584 | { | ||
1585 | struct acpi_scan_handler *handler; | ||
1586 | int ret = 0; | ||
1587 | |||
1588 | list_for_each_entry(handler, &acpi_scan_handlers_list, list_node) { | ||
1589 | const struct acpi_device_id *id; | ||
1590 | |||
1591 | id = __acpi_match_device(device, handler->ids); | ||
1592 | if (!id) | ||
1593 | continue; | ||
1594 | |||
1595 | ret = handler->attach(device, id); | ||
1596 | if (ret > 0) { | ||
1597 | device->handler = handler; | ||
1598 | break; | ||
1599 | } else if (ret < 0) { | ||
1600 | break; | ||
1601 | } | ||
1602 | } | ||
1603 | return ret; | ||
1604 | } | ||
1605 | |||
1573 | static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, | 1606 | static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, |
1574 | void *not_used, void **ret_not_used) | 1607 | void *not_used, void **ret_not_used) |
1575 | { | 1608 | { |
1576 | const struct acpi_device_id *id; | 1609 | const struct acpi_device_id *id; |
1577 | acpi_status status = AE_OK; | ||
1578 | struct acpi_device *device; | 1610 | struct acpi_device *device; |
1579 | unsigned long long sta_not_used; | 1611 | unsigned long long sta_not_used; |
1580 | int type_not_used; | 1612 | int ret; |
1581 | 1613 | ||
1582 | /* | 1614 | /* |
1583 | * Ignore errors ignored by acpi_bus_check_add() to avoid terminating | 1615 | * Ignore errors ignored by acpi_bus_check_add() to avoid terminating |
1584 | * namespace walks prematurely. | 1616 | * namespace walks prematurely. |
1585 | */ | 1617 | */ |
1586 | if (acpi_bus_type_and_status(handle, &type_not_used, &sta_not_used)) | 1618 | if (acpi_bus_type_and_status(handle, &ret, &sta_not_used)) |
1587 | return AE_OK; | 1619 | return AE_OK; |
1588 | 1620 | ||
1589 | if (acpi_bus_get_device(handle, &device)) | 1621 | if (acpi_bus_get_device(handle, &device)) |
@@ -1593,10 +1625,15 @@ static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, | |||
1593 | if (id) { | 1625 | if (id) { |
1594 | /* This is a known good platform device. */ | 1626 | /* This is a known good platform device. */ |
1595 | acpi_create_platform_device(device, id->driver_data); | 1627 | acpi_create_platform_device(device, id->driver_data); |
1596 | } else if (device_attach(&device->dev) < 0) { | 1628 | return AE_OK; |
1597 | status = AE_CTRL_DEPTH; | ||
1598 | } | 1629 | } |
1599 | return status; | 1630 | |
1631 | ret = acpi_scan_attach_handler(device); | ||
1632 | if (ret) | ||
1633 | return ret > 0 ? AE_OK : AE_CTRL_DEPTH; | ||
1634 | |||
1635 | ret = device_attach(&device->dev); | ||
1636 | return ret >= 0 ? AE_OK : AE_CTRL_DEPTH; | ||
1600 | } | 1637 | } |
1601 | 1638 | ||
1602 | /** | 1639 | /** |
@@ -1639,8 +1676,17 @@ static acpi_status acpi_bus_device_detach(acpi_handle handle, u32 lvl_not_used, | |||
1639 | struct acpi_device *device = NULL; | 1676 | struct acpi_device *device = NULL; |
1640 | 1677 | ||
1641 | if (!acpi_bus_get_device(handle, &device)) { | 1678 | if (!acpi_bus_get_device(handle, &device)) { |
1679 | struct acpi_scan_handler *dev_handler = device->handler; | ||
1680 | |||
1642 | device->removal_type = ACPI_BUS_REMOVAL_EJECT; | 1681 | device->removal_type = ACPI_BUS_REMOVAL_EJECT; |
1643 | device_release_driver(&device->dev); | 1682 | if (dev_handler) { |
1683 | if (dev_handler->detach) | ||
1684 | dev_handler->detach(device); | ||
1685 | |||
1686 | device->handler = NULL; | ||
1687 | } else { | ||
1688 | device_release_driver(&device->dev); | ||
1689 | } | ||
1644 | } | 1690 | } |
1645 | return AE_OK; | 1691 | return AE_OK; |
1646 | } | 1692 | } |