diff options
Diffstat (limited to 'drivers/acpi/scan.c')
| -rw-r--r-- | drivers/acpi/scan.c | 104 |
1 files changed, 43 insertions, 61 deletions
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 6d85289f1c12..f3132aa47a69 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c | |||
| @@ -6,6 +6,8 @@ | |||
| 6 | #include <linux/init.h> | 6 | #include <linux/init.h> |
| 7 | #include <linux/kernel.h> | 7 | #include <linux/kernel.h> |
| 8 | #include <linux/acpi.h> | 8 | #include <linux/acpi.h> |
| 9 | #include <linux/signal.h> | ||
| 10 | #include <linux/kthread.h> | ||
| 9 | 11 | ||
| 10 | #include <acpi/acpi_drivers.h> | 12 | #include <acpi/acpi_drivers.h> |
| 11 | #include <acpi/acinterp.h> /* for acpi_ex_eisa_id_to_string() */ | 13 | #include <acpi/acinterp.h> /* for acpi_ex_eisa_id_to_string() */ |
| @@ -92,17 +94,37 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha | |||
| 92 | } | 94 | } |
| 93 | static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); | 95 | static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); |
| 94 | 96 | ||
| 95 | static int acpi_eject_operation(acpi_handle handle, int lockable) | 97 | static int acpi_bus_hot_remove_device(void *context) |
| 96 | { | 98 | { |
| 99 | struct acpi_device *device; | ||
| 100 | acpi_handle handle = context; | ||
| 97 | struct acpi_object_list arg_list; | 101 | struct acpi_object_list arg_list; |
| 98 | union acpi_object arg; | 102 | union acpi_object arg; |
| 99 | acpi_status status = AE_OK; | 103 | acpi_status status = AE_OK; |
| 100 | 104 | ||
| 101 | /* | 105 | if (acpi_bus_get_device(handle, &device)) |
| 102 | * TBD: evaluate _PS3? | 106 | return 0; |
| 103 | */ | 107 | |
| 108 | if (!device) | ||
| 109 | return 0; | ||
| 110 | |||
| 111 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
| 112 | "Hot-removing device %s...\n", device->dev.bus_id)); | ||
| 113 | |||
| 104 | 114 | ||
| 105 | if (lockable) { | 115 | if (acpi_bus_trim(device, 1)) { |
| 116 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
| 117 | "Removing device failed\n")); | ||
| 118 | return -1; | ||
| 119 | } | ||
| 120 | |||
| 121 | /* power off device */ | ||
| 122 | status = acpi_evaluate_object(handle, "_PS3", NULL, NULL); | ||
| 123 | if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) | ||
| 124 | ACPI_DEBUG_PRINT((ACPI_DB_WARN, | ||
| 125 | "Power-off device failed\n")); | ||
| 126 | |||
| 127 | if (device->flags.lockable) { | ||
| 106 | arg_list.count = 1; | 128 | arg_list.count = 1; |
| 107 | arg_list.pointer = &arg; | 129 | arg_list.pointer = &arg; |
| 108 | arg.type = ACPI_TYPE_INTEGER; | 130 | arg.type = ACPI_TYPE_INTEGER; |
| @@ -118,26 +140,22 @@ static int acpi_eject_operation(acpi_handle handle, int lockable) | |||
| 118 | /* | 140 | /* |
| 119 | * TBD: _EJD support. | 141 | * TBD: _EJD support. |
| 120 | */ | 142 | */ |
| 121 | |||
| 122 | status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL); | 143 | status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL); |
| 123 | if (ACPI_FAILURE(status)) { | 144 | if (ACPI_FAILURE(status)) |
| 124 | return (-ENODEV); | 145 | return -ENODEV; |
| 125 | } | ||
| 126 | 146 | ||
| 127 | return (0); | 147 | return 0; |
| 128 | } | 148 | } |
| 129 | 149 | ||
| 130 | static ssize_t | 150 | static ssize_t |
| 131 | acpi_eject_store(struct device *d, struct device_attribute *attr, | 151 | acpi_eject_store(struct device *d, struct device_attribute *attr, |
| 132 | const char *buf, size_t count) | 152 | const char *buf, size_t count) |
| 133 | { | 153 | { |
| 134 | int result; | ||
| 135 | int ret = count; | 154 | int ret = count; |
| 136 | int islockable; | ||
| 137 | acpi_status status; | 155 | acpi_status status; |
| 138 | acpi_handle handle; | ||
| 139 | acpi_object_type type = 0; | 156 | acpi_object_type type = 0; |
| 140 | struct acpi_device *acpi_device = to_acpi_device(d); | 157 | struct acpi_device *acpi_device = to_acpi_device(d); |
| 158 | struct task_struct *task; | ||
| 141 | 159 | ||
| 142 | if ((!count) || (buf[0] != '1')) { | 160 | if ((!count) || (buf[0] != '1')) { |
| 143 | return -EINVAL; | 161 | return -EINVAL; |
| @@ -154,18 +172,12 @@ acpi_eject_store(struct device *d, struct device_attribute *attr, | |||
| 154 | goto err; | 172 | goto err; |
| 155 | } | 173 | } |
| 156 | 174 | ||
| 157 | islockable = acpi_device->flags.lockable; | 175 | /* remove the device in another thread to fix the deadlock issue */ |
| 158 | handle = acpi_device->handle; | 176 | task = kthread_run(acpi_bus_hot_remove_device, |
| 159 | 177 | acpi_device->handle, "acpi_hot_remove_device"); | |
| 160 | result = acpi_bus_trim(acpi_device, 1); | 178 | if (IS_ERR(task)) |
| 161 | 179 | ret = PTR_ERR(task); | |
| 162 | if (!result) | 180 | err: |
| 163 | result = acpi_eject_operation(handle, islockable); | ||
| 164 | |||
| 165 | if (result) { | ||
| 166 | ret = -EBUSY; | ||
| 167 | } | ||
| 168 | err: | ||
| 169 | return ret; | 181 | return ret; |
| 170 | } | 182 | } |
| 171 | 183 | ||
| @@ -691,9 +703,7 @@ static int acpi_bus_get_wakeup_device_flags(struct acpi_device *device) | |||
| 691 | acpi_status status = 0; | 703 | acpi_status status = 0; |
| 692 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | 704 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; |
| 693 | union acpi_object *package = NULL; | 705 | union acpi_object *package = NULL; |
| 694 | union acpi_object in_arg[3]; | 706 | int psw_error; |
| 695 | struct acpi_object_list arg_list = { 3, in_arg }; | ||
| 696 | acpi_status psw_status = AE_OK; | ||
| 697 | 707 | ||
| 698 | struct acpi_device_id button_device_ids[] = { | 708 | struct acpi_device_id button_device_ids[] = { |
| 699 | {"PNP0C0D", 0}, | 709 | {"PNP0C0D", 0}, |
| @@ -725,39 +735,11 @@ static int acpi_bus_get_wakeup_device_flags(struct acpi_device *device) | |||
| 725 | * So it is necessary to call _DSW object first. Only when it is not | 735 | * So it is necessary to call _DSW object first. Only when it is not |
| 726 | * present will the _PSW object used. | 736 | * present will the _PSW object used. |
| 727 | */ | 737 | */ |
| 728 | /* | 738 | psw_error = acpi_device_sleep_wake(device, 0, 0, 0); |
| 729 | * Three agruments are needed for the _DSW object. | 739 | if (psw_error) |
| 730 | * Argument 0: enable/disable the wake capabilities | 740 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
| 731 | * When _DSW object is called to disable the wake capabilities, maybe | 741 | "error in _DSW or _PSW evaluation\n")); |
| 732 | * the first argument is filled. The value of the other two agruments | 742 | |
| 733 | * is meaningless. | ||
| 734 | */ | ||
| 735 | in_arg[0].type = ACPI_TYPE_INTEGER; | ||
| 736 | in_arg[0].integer.value = 0; | ||
| 737 | in_arg[1].type = ACPI_TYPE_INTEGER; | ||
| 738 | in_arg[1].integer.value = 0; | ||
| 739 | in_arg[2].type = ACPI_TYPE_INTEGER; | ||
| 740 | in_arg[2].integer.value = 0; | ||
| 741 | psw_status = acpi_evaluate_object(device->handle, "_DSW", | ||
| 742 | &arg_list, NULL); | ||
| 743 | if (ACPI_FAILURE(psw_status) && (psw_status != AE_NOT_FOUND)) | ||
| 744 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "error in evaluate _DSW\n")); | ||
| 745 | /* | ||
| 746 | * When the _DSW object is not present, OSPM will call _PSW object. | ||
| 747 | */ | ||
| 748 | if (psw_status == AE_NOT_FOUND) { | ||
| 749 | /* | ||
| 750 | * Only one agruments is required for the _PSW object. | ||
| 751 | * agrument 0: enable/disable the wake capabilities | ||
| 752 | */ | ||
| 753 | arg_list.count = 1; | ||
| 754 | in_arg[0].integer.value = 0; | ||
| 755 | psw_status = acpi_evaluate_object(device->handle, "_PSW", | ||
| 756 | &arg_list, NULL); | ||
| 757 | if (ACPI_FAILURE(psw_status) && (psw_status != AE_NOT_FOUND)) | ||
| 758 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "error in " | ||
| 759 | "evaluate _PSW\n")); | ||
| 760 | } | ||
| 761 | /* Power button, Lid switch always enable wakeup */ | 743 | /* Power button, Lid switch always enable wakeup */ |
| 762 | if (!acpi_match_device_ids(device, button_device_ids)) | 744 | if (!acpi_match_device_ids(device, button_device_ids)) |
| 763 | device->wakeup.flags.run_wake = 1; | 745 | device->wakeup.flags.run_wake = 1; |
