diff options
Diffstat (limited to 'drivers/acpi/scan.c')
-rw-r--r-- | drivers/acpi/scan.c | 137 |
1 files changed, 89 insertions, 48 deletions
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index d64a5826ef35..984eaff235df 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -450,43 +450,61 @@ static int acpi_scan_bus_check(struct acpi_device *adev) return 0; } +static int acpi_generic_hotplug_event(struct acpi_device *adev, u32 type) +{ + switch (type) { + case ACPI_NOTIFY_BUS_CHECK: + return acpi_scan_bus_check(adev); + case ACPI_NOTIFY_DEVICE_CHECK: + return acpi_scan_device_check(adev); + case ACPI_NOTIFY_EJECT_REQUEST: + case ACPI_OST_EC_OSPM_EJECT: + return acpi_scan_hot_remove(adev); + } + return -EINVAL; +} + static void acpi_device_hotplug(void *data, u32 src) { u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; struct acpi_device *adev = data; - int error; + int error = -ENODEV; lock_device_hotplug(); mutex_lock(&acpi_scan_lock); /* * The device object's ACPI handle cannot become invalid as long as we - * are holding acpi_scan_lock, but it may have become invalid before + * are holding acpi_scan_lock, but it might have become invalid before * that lock was acquired. */ if (adev->handle == INVALID_ACPI_HANDLE) - goto out; + goto err_out; - switch (src) { - case ACPI_NOTIFY_BUS_CHECK: - error = acpi_scan_bus_check(adev); - break; - case ACPI_NOTIFY_DEVICE_CHECK: - error = acpi_scan_device_check(adev); - break; - case ACPI_NOTIFY_EJECT_REQUEST: - case ACPI_OST_EC_OSPM_EJECT: - error = acpi_scan_hot_remove(adev); - break; - default: - error = -EINVAL; - break; + if (adev->flags.hotplug_notify) { + error = acpi_generic_hotplug_event(adev, src); + } else { + int (*event)(struct acpi_device *, u32); + + acpi_lock_hp_context(); + event = adev->hp ? adev->hp->event : NULL; + acpi_unlock_hp_context(); + /* + * There may be additional notify handlers for device objects + * without the .event() callback, so ignore them here. + */ + if (event) + error = event(adev, src); + else + goto out; } if (!error) ost_code = ACPI_OST_SC_SUCCESS; - out: + err_out: acpi_evaluate_hotplug_ost(adev->handle, src, ost_code, NULL); + + out: acpi_bus_put_acpi_device(adev); mutex_unlock(&acpi_scan_lock); unlock_device_hotplug(); @@ -494,8 +512,8 @@ static void acpi_device_hotplug(void *data, u32 src) static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data) { - u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; struct acpi_scan_handler *handler = data; + u32 ost_code = ACPI_OST_SC_SUCCESS; struct acpi_device *adev; acpi_status status; @@ -503,26 +521,49 @@ static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data) case ACPI_NOTIFY_BUS_CHECK: acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n"); break; + case ACPI_NOTIFY_DEVICE_CHECK: acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n"); break; + case ACPI_NOTIFY_EJECT_REQUEST: acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n"); - if (!handler->hotplug.enabled) { + if (handler && !handler->hotplug.enabled) { acpi_handle_err(handle, "Eject disabled\n"); ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED; - goto err_out; + goto out; } acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST, ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); break; - default: - /* non-hotplug event; possibly handled by other handler */ + + case ACPI_NOTIFY_DEVICE_WAKE: return; + + case ACPI_NOTIFY_FREQUENCY_MISMATCH: + acpi_handle_err(handle, "Device cannot be configured due " + "to a frequency mismatch\n"); + goto out; + + case ACPI_NOTIFY_BUS_MODE_MISMATCH: + acpi_handle_err(handle, "Device cannot be configured due " + "to a bus mode mismatch\n"); + goto out; + + case ACPI_NOTIFY_POWER_FAULT: + acpi_handle_err(handle, "Device has suffered a power fault\n"); + goto out; + + default: + acpi_handle_warn(handle, "Unsupported event type 0x%x\n", type); + ost_code = ACPI_OST_SC_UNRECOGNIZED_NOTIFY; + goto out; } + + ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; adev = acpi_bus_get_acpi_device(handle); if (!adev) - goto err_out; + goto out; status = acpi_hotplug_execute(acpi_device_hotplug, adev, type); if (ACPI_SUCCESS(status)) @@ -530,10 +571,22 @@ static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data) acpi_bus_put_acpi_device(adev); - err_out: + out: acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL); } +void acpi_install_hotplug_notify_handler(acpi_handle handle, void *data) +{ + acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + acpi_hotplug_notify_cb, data); +} + +void acpi_remove_hotplug_notify_handler(acpi_handle handle) +{ + acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + acpi_hotplug_notify_cb); +} + static ssize_t real_power_state_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1976,33 +2029,21 @@ void acpi_scan_hotplug_enabled(struct acpi_hotplug_profile *hotplug, bool val) mutex_unlock(&acpi_scan_lock); } -static void acpi_scan_init_hotplug(acpi_handle handle, int type) +static void acpi_scan_init_hotplug(struct acpi_device *adev) { - struct acpi_device_pnp pnp = {}; struct acpi_hardware_id *hwid; - struct acpi_scan_handler *handler; - INIT_LIST_HEAD(&pnp.ids); - acpi_set_pnp_ids(handle, &pnp, type); - - if (!pnp.type.hardware_id) - goto out; + list_for_each_entry(hwid, &adev->pnp.ids, list) { + struct acpi_scan_handler *handler; - /* - * This relies on the fact that acpi_install_notify_handler() will not - * install the same notify handler routine twice for the same handle. - */ - list_for_each_entry(hwid, &pnp.ids, list) { handler = acpi_scan_match_handler(hwid->id, NULL); - if (handler) { - acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, - acpi_hotplug_notify_cb, handler); - break; - } - } + if (!handler) + continue; -out: - acpi_free_pnp_ids(&pnp); + acpi_install_hotplug_notify_handler(adev->handle, handler); + adev->flags.hotplug_notify = true; + break; + } } static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, @@ -2026,12 +2067,12 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, return AE_OK; } - acpi_scan_init_hotplug(handle, type); - 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; |