diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2014-03-17 08:47:14 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2014-03-17 08:47:14 -0400 |
commit | 6621c5a69a1dac50852ef09d5cf33154a174805a (patch) | |
tree | 88f4ff7d73006abcef64f9793daadda9d5034a32 /drivers | |
parent | d983f93328041f85e884a586810a67528ae59945 (diff) | |
parent | d901188f002104905a7845298e1a7dc94189007a (diff) |
Merge branch 'acpi-hotplug'
* acpi-hotplug:
ACPI / hotplug: Rework deferred execution of acpi_device_hotplug()
ACPI / dock: Update copyright notice
ACPI / dock: Drop remove_dock_dependent_devices()
ACPI / dock: Drop struct acpi_dock_ops and all code related to it
ACPI / ATA: Add hotplug contexts to ACPI companions of SATA devices
ACPI / dock: Add .uevent() callback to struct acpi_hotplug_context
ACPI / dock: Use callback pointers from devices' ACPI hotplug contexts
ACPI / dock: Use ACPI device object pointers instead of ACPI handles
ACPI / hotplug: Add .fixup() callback to struct acpi_hotplug_context
ACPI / hotplug / PCI: Do not clear event callback pointer for docks
ACPI / dock: Associate dock platform devices with ACPI device objects
ACPI / dock: Pass ACPI device pointer to acpi_device_is_battery()
ACPI / dock: Dispatch dock notifications from the global notify handler
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/acpi/bus.c | 2 | ||||
-rw-r--r-- | drivers/acpi/container.c | 3 | ||||
-rw-r--r-- | drivers/acpi/dock.c | 452 | ||||
-rw-r--r-- | drivers/acpi/internal.h | 14 | ||||
-rw-r--r-- | drivers/acpi/osl.c | 14 | ||||
-rw-r--r-- | drivers/acpi/scan.c | 59 | ||||
-rw-r--r-- | drivers/ata/libata-acpi.c | 72 | ||||
-rw-r--r-- | drivers/pci/hotplug/acpiphp_glue.c | 134 |
8 files changed, 277 insertions, 473 deletions
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index e61e7b8a2eaf..afe6f9a919c1 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c | |||
@@ -400,7 +400,7 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data) | |||
400 | case ACPI_NOTIFY_BUS_CHECK: | 400 | case ACPI_NOTIFY_BUS_CHECK: |
401 | case ACPI_NOTIFY_DEVICE_CHECK: | 401 | case ACPI_NOTIFY_DEVICE_CHECK: |
402 | case ACPI_NOTIFY_EJECT_REQUEST: | 402 | case ACPI_NOTIFY_EJECT_REQUEST: |
403 | status = acpi_hotplug_execute(acpi_device_hotplug, adev, type); | 403 | status = acpi_hotplug_schedule(adev, type); |
404 | if (ACPI_SUCCESS(status)) | 404 | if (ACPI_SUCCESS(status)) |
405 | return; | 405 | return; |
406 | default: | 406 | default: |
diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c index 368f9ddb8480..a55cccbc7356 100644 --- a/drivers/acpi/container.c +++ b/drivers/acpi/container.c | |||
@@ -68,6 +68,9 @@ static int container_device_attach(struct acpi_device *adev, | |||
68 | struct device *dev; | 68 | struct device *dev; |
69 | int ret; | 69 | int ret; |
70 | 70 | ||
71 | if (adev->flags.is_dock_station) | ||
72 | return 0; | ||
73 | |||
71 | cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); | 74 | cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); |
72 | if (!cdev) | 75 | if (!cdev) |
73 | return -ENOMEM; | 76 | return -ENOMEM; |
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 5bfd769fc91f..a7bd3002dbbc 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c | |||
@@ -1,7 +1,9 @@ | |||
1 | /* | 1 | /* |
2 | * dock.c - ACPI dock station driver | 2 | * dock.c - ACPI dock station driver |
3 | * | 3 | * |
4 | * Copyright (C) 2006 Kristen Carlson Accardi <kristen.c.accardi@intel.com> | 4 | * Copyright (C) 2006, 2014, Intel Corp. |
5 | * Author: Kristen Carlson Accardi <kristen.c.accardi@intel.com> | ||
6 | * Rafael J. Wysocki <rafael.j.wysocki@intel.com> | ||
5 | * | 7 | * |
6 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 8 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
7 | * | 9 | * |
@@ -68,15 +70,10 @@ struct dock_station { | |||
68 | }; | 70 | }; |
69 | static LIST_HEAD(dock_stations); | 71 | static LIST_HEAD(dock_stations); |
70 | static int dock_station_count; | 72 | static int dock_station_count; |
71 | static DEFINE_MUTEX(hotplug_lock); | ||
72 | 73 | ||
73 | struct dock_dependent_device { | 74 | struct dock_dependent_device { |
74 | struct list_head list; | 75 | struct list_head list; |
75 | acpi_handle handle; | 76 | struct acpi_device *adev; |
76 | const struct acpi_dock_ops *hp_ops; | ||
77 | void *hp_context; | ||
78 | unsigned int hp_refcount; | ||
79 | void (*hp_release)(void *); | ||
80 | }; | 77 | }; |
81 | 78 | ||
82 | #define DOCK_DOCKING 0x00000001 | 79 | #define DOCK_DOCKING 0x00000001 |
@@ -98,13 +95,13 @@ enum dock_callback_type { | |||
98 | *****************************************************************************/ | 95 | *****************************************************************************/ |
99 | /** | 96 | /** |
100 | * add_dock_dependent_device - associate a device with the dock station | 97 | * add_dock_dependent_device - associate a device with the dock station |
101 | * @ds: The dock station | 98 | * @ds: Dock station. |
102 | * @handle: handle of the dependent device | 99 | * @adev: Dependent ACPI device object. |
103 | * | 100 | * |
104 | * Add the dependent device to the dock's dependent device list. | 101 | * Add the dependent device to the dock's dependent device list. |
105 | */ | 102 | */ |
106 | static int __init | 103 | static int add_dock_dependent_device(struct dock_station *ds, |
107 | add_dock_dependent_device(struct dock_station *ds, acpi_handle handle) | 104 | struct acpi_device *adev) |
108 | { | 105 | { |
109 | struct dock_dependent_device *dd; | 106 | struct dock_dependent_device *dd; |
110 | 107 | ||
@@ -112,180 +109,120 @@ add_dock_dependent_device(struct dock_station *ds, acpi_handle handle) | |||
112 | if (!dd) | 109 | if (!dd) |
113 | return -ENOMEM; | 110 | return -ENOMEM; |
114 | 111 | ||
115 | dd->handle = handle; | 112 | dd->adev = adev; |
116 | INIT_LIST_HEAD(&dd->list); | 113 | INIT_LIST_HEAD(&dd->list); |
117 | list_add_tail(&dd->list, &ds->dependent_devices); | 114 | list_add_tail(&dd->list, &ds->dependent_devices); |
118 | 115 | ||
119 | return 0; | 116 | return 0; |
120 | } | 117 | } |
121 | 118 | ||
122 | static void remove_dock_dependent_devices(struct dock_station *ds) | 119 | static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event, |
120 | enum dock_callback_type cb_type) | ||
123 | { | 121 | { |
124 | struct dock_dependent_device *dd, *aux; | 122 | struct acpi_device *adev = dd->adev; |
125 | 123 | ||
126 | list_for_each_entry_safe(dd, aux, &ds->dependent_devices, list) { | 124 | acpi_lock_hp_context(); |
127 | list_del(&dd->list); | ||
128 | kfree(dd); | ||
129 | } | ||
130 | } | ||
131 | 125 | ||
132 | /** | 126 | if (!adev->hp) |
133 | * dock_init_hotplug - Initialize a hotplug device on a docking station. | 127 | goto out; |
134 | * @dd: Dock-dependent device. | ||
135 | * @ops: Dock operations to attach to the dependent device. | ||
136 | * @context: Data to pass to the @ops callbacks and @release. | ||
137 | * @init: Optional initialization routine to run after setting up context. | ||
138 | * @release: Optional release routine to run on removal. | ||
139 | */ | ||
140 | static int dock_init_hotplug(struct dock_dependent_device *dd, | ||
141 | const struct acpi_dock_ops *ops, void *context, | ||
142 | void (*init)(void *), void (*release)(void *)) | ||
143 | { | ||
144 | int ret = 0; | ||
145 | 128 | ||
146 | mutex_lock(&hotplug_lock); | 129 | if (cb_type == DOCK_CALL_FIXUP) { |
147 | if (WARN_ON(dd->hp_context)) { | 130 | void (*fixup)(struct acpi_device *); |
148 | ret = -EEXIST; | ||
149 | } else { | ||
150 | dd->hp_refcount = 1; | ||
151 | dd->hp_ops = ops; | ||
152 | dd->hp_context = context; | ||
153 | dd->hp_release = release; | ||
154 | if (init) | ||
155 | init(context); | ||
156 | } | ||
157 | mutex_unlock(&hotplug_lock); | ||
158 | return ret; | ||
159 | } | ||
160 | 131 | ||
161 | /** | 132 | fixup = adev->hp->fixup; |
162 | * dock_release_hotplug - Decrement hotplug reference counter of dock device. | 133 | if (fixup) { |
163 | * @dd: Dock-dependent device. | 134 | acpi_unlock_hp_context(); |
164 | * | 135 | fixup(adev); |
165 | * Decrement the reference counter of @dd and if 0, detach its hotplug | 136 | return; |
166 | * operations from it, reset its context pointer and run the optional release | 137 | } |
167 | * routine if present. | 138 | } else if (cb_type == DOCK_CALL_UEVENT) { |
168 | */ | 139 | void (*uevent)(struct acpi_device *, u32); |
169 | static void dock_release_hotplug(struct dock_dependent_device *dd) | 140 | |
170 | { | 141 | uevent = adev->hp->uevent; |
171 | mutex_lock(&hotplug_lock); | 142 | if (uevent) { |
172 | if (dd->hp_context && !--dd->hp_refcount) { | 143 | acpi_unlock_hp_context(); |
173 | void (*release)(void *) = dd->hp_release; | 144 | uevent(adev, event); |
174 | void *context = dd->hp_context; | 145 | return; |
175 | 146 | } | |
176 | dd->hp_ops = NULL; | 147 | } else { |
177 | dd->hp_context = NULL; | 148 | int (*notify)(struct acpi_device *, u32); |
178 | dd->hp_release = NULL; | ||
179 | if (release) | ||
180 | release(context); | ||
181 | } | ||
182 | mutex_unlock(&hotplug_lock); | ||
183 | } | ||
184 | 149 | ||
185 | static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event, | 150 | notify = adev->hp->notify; |
186 | enum dock_callback_type cb_type) | 151 | if (notify) { |
187 | { | 152 | acpi_unlock_hp_context(); |
188 | acpi_notify_handler cb = NULL; | 153 | notify(adev, event); |
189 | bool run = false; | 154 | return; |
190 | |||
191 | mutex_lock(&hotplug_lock); | ||
192 | |||
193 | if (dd->hp_context) { | ||
194 | run = true; | ||
195 | dd->hp_refcount++; | ||
196 | if (dd->hp_ops) { | ||
197 | switch (cb_type) { | ||
198 | case DOCK_CALL_FIXUP: | ||
199 | cb = dd->hp_ops->fixup; | ||
200 | break; | ||
201 | case DOCK_CALL_UEVENT: | ||
202 | cb = dd->hp_ops->uevent; | ||
203 | break; | ||
204 | default: | ||
205 | cb = dd->hp_ops->handler; | ||
206 | } | ||
207 | } | 155 | } |
208 | } | 156 | } |
209 | 157 | ||
210 | mutex_unlock(&hotplug_lock); | 158 | out: |
159 | acpi_unlock_hp_context(); | ||
160 | } | ||
211 | 161 | ||
212 | if (!run) | 162 | static struct dock_station *find_dock_station(acpi_handle handle) |
213 | return; | 163 | { |
164 | struct dock_station *ds; | ||
214 | 165 | ||
215 | if (cb) | 166 | list_for_each_entry(ds, &dock_stations, sibling) |
216 | cb(dd->handle, event, dd->hp_context); | 167 | if (ds->handle == handle) |
168 | return ds; | ||
217 | 169 | ||
218 | dock_release_hotplug(dd); | 170 | return NULL; |
219 | } | 171 | } |
220 | 172 | ||
221 | /** | 173 | /** |
222 | * find_dock_dependent_device - get a device dependent on this dock | 174 | * find_dock_dependent_device - get a device dependent on this dock |
223 | * @ds: the dock station | 175 | * @ds: the dock station |
224 | * @handle: the acpi_handle of the device we want | 176 | * @adev: ACPI device object to find. |
225 | * | 177 | * |
226 | * iterate over the dependent device list for this dock. If the | 178 | * iterate over the dependent device list for this dock. If the |
227 | * dependent device matches the handle, return. | 179 | * dependent device matches the handle, return. |
228 | */ | 180 | */ |
229 | static struct dock_dependent_device * | 181 | static struct dock_dependent_device * |
230 | find_dock_dependent_device(struct dock_station *ds, acpi_handle handle) | 182 | find_dock_dependent_device(struct dock_station *ds, struct acpi_device *adev) |
231 | { | 183 | { |
232 | struct dock_dependent_device *dd; | 184 | struct dock_dependent_device *dd; |
233 | 185 | ||
234 | list_for_each_entry(dd, &ds->dependent_devices, list) | 186 | list_for_each_entry(dd, &ds->dependent_devices, list) |
235 | if (handle == dd->handle) | 187 | if (adev == dd->adev) |
236 | return dd; | 188 | return dd; |
237 | 189 | ||
238 | return NULL; | 190 | return NULL; |
239 | } | 191 | } |
240 | 192 | ||
241 | /***************************************************************************** | 193 | void register_dock_dependent_device(struct acpi_device *adev, |
242 | * Dock functions * | 194 | acpi_handle dshandle) |
243 | *****************************************************************************/ | ||
244 | static int __init is_battery(acpi_handle handle) | ||
245 | { | 195 | { |
246 | struct acpi_device_info *info; | 196 | struct dock_station *ds = find_dock_station(dshandle); |
247 | int ret = 1; | ||
248 | 197 | ||
249 | if (!ACPI_SUCCESS(acpi_get_object_info(handle, &info))) | 198 | if (ds && !find_dock_dependent_device(ds, adev)) |
250 | return 0; | 199 | add_dock_dependent_device(ds, adev); |
251 | if (!(info->valid & ACPI_VALID_HID)) | ||
252 | ret = 0; | ||
253 | else | ||
254 | ret = !strcmp("PNP0C0A", info->hardware_id.string); | ||
255 | |||
256 | kfree(info); | ||
257 | return ret; | ||
258 | } | 200 | } |
259 | 201 | ||
260 | /* Check whether ACPI object is an ejectable battery or disk bay */ | 202 | /***************************************************************************** |
261 | static bool __init is_ejectable_bay(acpi_handle handle) | 203 | * Dock functions * |
262 | { | 204 | *****************************************************************************/ |
263 | if (acpi_has_method(handle, "_EJ0") && is_battery(handle)) | ||
264 | return true; | ||
265 | |||
266 | return acpi_bay_match(handle); | ||
267 | } | ||
268 | 205 | ||
269 | /** | 206 | /** |
270 | * is_dock_device - see if a device is on a dock station | 207 | * is_dock_device - see if a device is on a dock station |
271 | * @handle: acpi handle of the device | 208 | * @adev: ACPI device object to check. |
272 | * | 209 | * |
273 | * If this device is either the dock station itself, | 210 | * If this device is either the dock station itself, |
274 | * or is a device dependent on the dock station, then it | 211 | * or is a device dependent on the dock station, then it |
275 | * is a dock device | 212 | * is a dock device |
276 | */ | 213 | */ |
277 | int is_dock_device(acpi_handle handle) | 214 | int is_dock_device(struct acpi_device *adev) |
278 | { | 215 | { |
279 | struct dock_station *dock_station; | 216 | struct dock_station *dock_station; |
280 | 217 | ||
281 | if (!dock_station_count) | 218 | if (!dock_station_count) |
282 | return 0; | 219 | return 0; |
283 | 220 | ||
284 | if (acpi_dock_match(handle)) | 221 | if (acpi_dock_match(adev->handle)) |
285 | return 1; | 222 | return 1; |
286 | 223 | ||
287 | list_for_each_entry(dock_station, &dock_stations, sibling) | 224 | list_for_each_entry(dock_station, &dock_stations, sibling) |
288 | if (find_dock_dependent_device(dock_station, handle)) | 225 | if (find_dock_dependent_device(dock_station, adev)) |
289 | return 1; | 226 | return 1; |
290 | 227 | ||
291 | return 0; | 228 | return 0; |
@@ -313,43 +250,6 @@ static int dock_present(struct dock_station *ds) | |||
313 | } | 250 | } |
314 | 251 | ||
315 | /** | 252 | /** |
316 | * dock_create_acpi_device - add new devices to acpi | ||
317 | * @handle - handle of the device to add | ||
318 | * | ||
319 | * This function will create a new acpi_device for the given | ||
320 | * handle if one does not exist already. This should cause | ||
321 | * acpi to scan for drivers for the given devices, and call | ||
322 | * matching driver's add routine. | ||
323 | */ | ||
324 | static void dock_create_acpi_device(acpi_handle handle) | ||
325 | { | ||
326 | struct acpi_device *device = NULL; | ||
327 | int ret; | ||
328 | |||
329 | acpi_bus_get_device(handle, &device); | ||
330 | if (!acpi_device_enumerated(device)) { | ||
331 | ret = acpi_bus_scan(handle); | ||
332 | if (ret) | ||
333 | pr_debug("error adding bus, %x\n", -ret); | ||
334 | } | ||
335 | } | ||
336 | |||
337 | /** | ||
338 | * dock_remove_acpi_device - remove the acpi_device struct from acpi | ||
339 | * @handle - the handle of the device to remove | ||
340 | * | ||
341 | * Tell acpi to remove the acpi_device. This should cause any loaded | ||
342 | * driver to have it's remove routine called. | ||
343 | */ | ||
344 | static void dock_remove_acpi_device(acpi_handle handle) | ||
345 | { | ||
346 | struct acpi_device *device; | ||
347 | |||
348 | if (!acpi_bus_get_device(handle, &device)) | ||
349 | acpi_bus_trim(device); | ||
350 | } | ||
351 | |||
352 | /** | ||
353 | * hot_remove_dock_devices - Remove dock station devices. | 253 | * hot_remove_dock_devices - Remove dock station devices. |
354 | * @ds: Dock station. | 254 | * @ds: Dock station. |
355 | */ | 255 | */ |
@@ -366,7 +266,7 @@ static void hot_remove_dock_devices(struct dock_station *ds) | |||
366 | dock_hotplug_event(dd, ACPI_NOTIFY_EJECT_REQUEST, false); | 266 | dock_hotplug_event(dd, ACPI_NOTIFY_EJECT_REQUEST, false); |
367 | 267 | ||
368 | list_for_each_entry_reverse(dd, &ds->dependent_devices, list) | 268 | list_for_each_entry_reverse(dd, &ds->dependent_devices, list) |
369 | dock_remove_acpi_device(dd->handle); | 269 | acpi_bus_trim(dd->adev); |
370 | } | 270 | } |
371 | 271 | ||
372 | /** | 272 | /** |
@@ -392,12 +292,20 @@ static void hotplug_dock_devices(struct dock_station *ds, u32 event) | |||
392 | dock_hotplug_event(dd, event, DOCK_CALL_HANDLER); | 292 | dock_hotplug_event(dd, event, DOCK_CALL_HANDLER); |
393 | 293 | ||
394 | /* | 294 | /* |
395 | * Now make sure that an acpi_device is created for each dependent | 295 | * Check if all devices have been enumerated already. If not, run |
396 | * device. That will cause scan handlers to be attached to device | 296 | * acpi_bus_scan() for them and that will cause scan handlers to be |
397 | * objects or acpi_drivers to be stopped/started if they are present. | 297 | * attached to device objects or acpi_drivers to be stopped/started if |
298 | * they are present. | ||
398 | */ | 299 | */ |
399 | list_for_each_entry(dd, &ds->dependent_devices, list) | 300 | list_for_each_entry(dd, &ds->dependent_devices, list) { |
400 | dock_create_acpi_device(dd->handle); | 301 | struct acpi_device *adev = dd->adev; |
302 | |||
303 | if (!acpi_device_enumerated(adev)) { | ||
304 | int ret = acpi_bus_scan(adev->handle); | ||
305 | if (ret) | ||
306 | dev_dbg(&adev->dev, "scan error %d\n", -ret); | ||
307 | } | ||
308 | } | ||
401 | } | 309 | } |
402 | 310 | ||
403 | static void dock_event(struct dock_station *ds, u32 event, int num) | 311 | static void dock_event(struct dock_station *ds, u32 event, int num) |
@@ -501,71 +409,6 @@ static int dock_in_progress(struct dock_station *ds) | |||
501 | } | 409 | } |
502 | 410 | ||
503 | /** | 411 | /** |
504 | * register_hotplug_dock_device - register a hotplug function | ||
505 | * @handle: the handle of the device | ||
506 | * @ops: handlers to call after docking | ||
507 | * @context: device specific data | ||
508 | * @init: Optional initialization routine to run after registration | ||
509 | * @release: Optional release routine to run on unregistration | ||
510 | * | ||
511 | * If a driver would like to perform a hotplug operation after a dock | ||
512 | * event, they can register an acpi_notifiy_handler to be called by | ||
513 | * the dock driver after _DCK is executed. | ||
514 | */ | ||
515 | int register_hotplug_dock_device(acpi_handle handle, | ||
516 | const struct acpi_dock_ops *ops, void *context, | ||
517 | void (*init)(void *), void (*release)(void *)) | ||
518 | { | ||
519 | struct dock_dependent_device *dd; | ||
520 | struct dock_station *dock_station; | ||
521 | int ret = -EINVAL; | ||
522 | |||
523 | if (WARN_ON(!context)) | ||
524 | return -EINVAL; | ||
525 | |||
526 | if (!dock_station_count) | ||
527 | return -ENODEV; | ||
528 | |||
529 | /* | ||
530 | * make sure this handle is for a device dependent on the dock, | ||
531 | * this would include the dock station itself | ||
532 | */ | ||
533 | list_for_each_entry(dock_station, &dock_stations, sibling) { | ||
534 | /* | ||
535 | * An ATA bay can be in a dock and itself can be ejected | ||
536 | * separately, so there are two 'dock stations' which need the | ||
537 | * ops | ||
538 | */ | ||
539 | dd = find_dock_dependent_device(dock_station, handle); | ||
540 | if (dd && !dock_init_hotplug(dd, ops, context, init, release)) | ||
541 | ret = 0; | ||
542 | } | ||
543 | |||
544 | return ret; | ||
545 | } | ||
546 | EXPORT_SYMBOL_GPL(register_hotplug_dock_device); | ||
547 | |||
548 | /** | ||
549 | * unregister_hotplug_dock_device - remove yourself from the hotplug list | ||
550 | * @handle: the acpi handle of the device | ||
551 | */ | ||
552 | void unregister_hotplug_dock_device(acpi_handle handle) | ||
553 | { | ||
554 | struct dock_dependent_device *dd; | ||
555 | struct dock_station *dock_station; | ||
556 | |||
557 | if (!dock_station_count) | ||
558 | return; | ||
559 | |||
560 | list_for_each_entry(dock_station, &dock_stations, sibling) { | ||
561 | dd = find_dock_dependent_device(dock_station, handle); | ||
562 | if (dd) | ||
563 | dock_release_hotplug(dd); | ||
564 | } | ||
565 | } | ||
566 | EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device); | ||
567 | |||
568 | /** | ||
569 | * handle_eject_request - handle an undock request checking for error conditions | 412 | * handle_eject_request - handle an undock request checking for error conditions |
570 | * | 413 | * |
571 | * Check to make sure the dock device is still present, then undock and | 414 | * Check to make sure the dock device is still present, then undock and |
@@ -598,20 +441,23 @@ static int handle_eject_request(struct dock_station *ds, u32 event) | |||
598 | } | 441 | } |
599 | 442 | ||
600 | /** | 443 | /** |
601 | * dock_notify - act upon an acpi dock notification | 444 | * dock_notify - Handle ACPI dock notification. |
602 | * @ds: dock station | 445 | * @adev: Dock station's ACPI device object. |
603 | * @event: the acpi event | 446 | * @event: Event code. |
604 | * | 447 | * |
605 | * If we are notified to dock, then check to see if the dock is | 448 | * If we are notified to dock, then check to see if the dock is |
606 | * present and then dock. Notify all drivers of the dock event, | 449 | * present and then dock. Notify all drivers of the dock event, |
607 | * and then hotplug and devices that may need hotplugging. | 450 | * and then hotplug and devices that may need hotplugging. |
608 | */ | 451 | */ |
609 | static void dock_notify(struct dock_station *ds, u32 event) | 452 | int dock_notify(struct acpi_device *adev, u32 event) |
610 | { | 453 | { |
611 | acpi_handle handle = ds->handle; | 454 | acpi_handle handle = adev->handle; |
612 | struct acpi_device *adev = NULL; | 455 | struct dock_station *ds = find_dock_station(handle); |
613 | int surprise_removal = 0; | 456 | int surprise_removal = 0; |
614 | 457 | ||
458 | if (!ds) | ||
459 | return -ENODEV; | ||
460 | |||
615 | /* | 461 | /* |
616 | * According to acpi spec 3.0a, if a DEVICE_CHECK notification | 462 | * According to acpi spec 3.0a, if a DEVICE_CHECK notification |
617 | * is sent and _DCK is present, it is assumed to mean an undock | 463 | * is sent and _DCK is present, it is assumed to mean an undock |
@@ -632,7 +478,6 @@ static void dock_notify(struct dock_station *ds, u32 event) | |||
632 | switch (event) { | 478 | switch (event) { |
633 | case ACPI_NOTIFY_BUS_CHECK: | 479 | case ACPI_NOTIFY_BUS_CHECK: |
634 | case ACPI_NOTIFY_DEVICE_CHECK: | 480 | case ACPI_NOTIFY_DEVICE_CHECK: |
635 | acpi_bus_get_device(handle, &adev); | ||
636 | if (!dock_in_progress(ds) && !acpi_device_enumerated(adev)) { | 481 | if (!dock_in_progress(ds) && !acpi_device_enumerated(adev)) { |
637 | begin_dock(ds); | 482 | begin_dock(ds); |
638 | dock(ds); | 483 | dock(ds); |
@@ -662,49 +507,8 @@ static void dock_notify(struct dock_station *ds, u32 event) | |||
662 | else | 507 | else |
663 | dock_event(ds, event, UNDOCK_EVENT); | 508 | dock_event(ds, event, UNDOCK_EVENT); |
664 | break; | 509 | break; |
665 | default: | ||
666 | acpi_handle_err(handle, "Unknown dock event %d\n", event); | ||
667 | } | 510 | } |
668 | } | 511 | return 0; |
669 | |||
670 | static void acpi_dock_deferred_cb(void *data, u32 event) | ||
671 | { | ||
672 | acpi_scan_lock_acquire(); | ||
673 | dock_notify(data, event); | ||
674 | acpi_scan_lock_release(); | ||
675 | } | ||
676 | |||
677 | static void dock_notify_handler(acpi_handle handle, u32 event, void *data) | ||
678 | { | ||
679 | if (event != ACPI_NOTIFY_BUS_CHECK && event != ACPI_NOTIFY_DEVICE_CHECK | ||
680 | && event != ACPI_NOTIFY_EJECT_REQUEST) | ||
681 | return; | ||
682 | |||
683 | acpi_hotplug_execute(acpi_dock_deferred_cb, data, event); | ||
684 | } | ||
685 | |||
686 | /** | ||
687 | * find_dock_devices - find devices on the dock station | ||
688 | * @handle: the handle of the device we are examining | ||
689 | * @lvl: unused | ||
690 | * @context: the dock station private data | ||
691 | * @rv: unused | ||
692 | * | ||
693 | * This function is called by acpi_walk_namespace. It will | ||
694 | * check to see if an object has an _EJD method. If it does, then it | ||
695 | * will see if it is dependent on the dock station. | ||
696 | */ | ||
697 | static acpi_status __init find_dock_devices(acpi_handle handle, u32 lvl, | ||
698 | void *context, void **rv) | ||
699 | { | ||
700 | struct dock_station *ds = context; | ||
701 | acpi_handle ejd = NULL; | ||
702 | |||
703 | acpi_bus_get_ejd(handle, &ejd); | ||
704 | if (ejd == ds->handle) | ||
705 | add_dock_dependent_device(ds, handle); | ||
706 | |||
707 | return AE_OK; | ||
708 | } | 512 | } |
709 | 513 | ||
710 | /* | 514 | /* |
@@ -803,23 +607,28 @@ static struct attribute_group dock_attribute_group = { | |||
803 | }; | 607 | }; |
804 | 608 | ||
805 | /** | 609 | /** |
806 | * dock_add - add a new dock station | 610 | * acpi_dock_add - Add a new dock station |
807 | * @handle: the dock station handle | 611 | * @adev: Dock station ACPI device object. |
808 | * | 612 | * |
809 | * allocated and initialize a new dock station device. Find all devices | 613 | * allocated and initialize a new dock station device. |
810 | * that are on the dock station, and register for dock event notifications. | ||
811 | */ | 614 | */ |
812 | static int __init dock_add(acpi_handle handle) | 615 | void acpi_dock_add(struct acpi_device *adev) |
813 | { | 616 | { |
814 | struct dock_station *dock_station, ds = { NULL, }; | 617 | struct dock_station *dock_station, ds = { NULL, }; |
618 | struct platform_device_info pdevinfo; | ||
619 | acpi_handle handle = adev->handle; | ||
815 | struct platform_device *dd; | 620 | struct platform_device *dd; |
816 | acpi_status status; | ||
817 | int ret; | 621 | int ret; |
818 | 622 | ||
819 | dd = platform_device_register_data(NULL, "dock", dock_station_count, | 623 | memset(&pdevinfo, 0, sizeof(pdevinfo)); |
820 | &ds, sizeof(ds)); | 624 | pdevinfo.name = "dock"; |
625 | pdevinfo.id = dock_station_count; | ||
626 | pdevinfo.acpi_node.companion = adev; | ||
627 | pdevinfo.data = &ds; | ||
628 | pdevinfo.size_data = sizeof(ds); | ||
629 | dd = platform_device_register_full(&pdevinfo); | ||
821 | if (IS_ERR(dd)) | 630 | if (IS_ERR(dd)) |
822 | return PTR_ERR(dd); | 631 | return; |
823 | 632 | ||
824 | dock_station = dd->dev.platform_data; | 633 | dock_station = dd->dev.platform_data; |
825 | 634 | ||
@@ -837,72 +646,29 @@ static int __init dock_add(acpi_handle handle) | |||
837 | dock_station->flags |= DOCK_IS_DOCK; | 646 | dock_station->flags |= DOCK_IS_DOCK; |
838 | if (acpi_ata_match(handle)) | 647 | if (acpi_ata_match(handle)) |
839 | dock_station->flags |= DOCK_IS_ATA; | 648 | dock_station->flags |= DOCK_IS_ATA; |
840 | if (is_battery(handle)) | 649 | if (acpi_device_is_battery(adev)) |
841 | dock_station->flags |= DOCK_IS_BAT; | 650 | dock_station->flags |= DOCK_IS_BAT; |
842 | 651 | ||
843 | ret = sysfs_create_group(&dd->dev.kobj, &dock_attribute_group); | 652 | ret = sysfs_create_group(&dd->dev.kobj, &dock_attribute_group); |
844 | if (ret) | 653 | if (ret) |
845 | goto err_unregister; | 654 | goto err_unregister; |
846 | 655 | ||
847 | /* Find dependent devices */ | ||
848 | acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, | ||
849 | ACPI_UINT32_MAX, find_dock_devices, NULL, | ||
850 | dock_station, NULL); | ||
851 | |||
852 | /* add the dock station as a device dependent on itself */ | 656 | /* add the dock station as a device dependent on itself */ |
853 | ret = add_dock_dependent_device(dock_station, handle); | 657 | ret = add_dock_dependent_device(dock_station, adev); |
854 | if (ret) | 658 | if (ret) |
855 | goto err_rmgroup; | 659 | goto err_rmgroup; |
856 | 660 | ||
857 | status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, | ||
858 | dock_notify_handler, dock_station); | ||
859 | if (ACPI_FAILURE(status)) { | ||
860 | ret = -ENODEV; | ||
861 | goto err_rmgroup; | ||
862 | } | ||
863 | |||
864 | dock_station_count++; | 661 | dock_station_count++; |
865 | list_add(&dock_station->sibling, &dock_stations); | 662 | list_add(&dock_station->sibling, &dock_stations); |
866 | return 0; | 663 | adev->flags.is_dock_station = true; |
664 | dev_info(&adev->dev, "ACPI dock station (docks/bays count: %d)\n", | ||
665 | dock_station_count); | ||
666 | return; | ||
867 | 667 | ||
868 | err_rmgroup: | 668 | err_rmgroup: |
869 | remove_dock_dependent_devices(dock_station); | ||
870 | sysfs_remove_group(&dd->dev.kobj, &dock_attribute_group); | 669 | sysfs_remove_group(&dd->dev.kobj, &dock_attribute_group); |
670 | |||
871 | err_unregister: | 671 | err_unregister: |
872 | platform_device_unregister(dd); | 672 | platform_device_unregister(dd); |
873 | acpi_handle_err(handle, "%s encountered error %d\n", __func__, ret); | 673 | acpi_handle_err(handle, "%s encountered error %d\n", __func__, ret); |
874 | return ret; | ||
875 | } | ||
876 | |||
877 | /** | ||
878 | * find_dock_and_bay - look for dock stations and bays | ||
879 | * @handle: acpi handle of a device | ||
880 | * @lvl: unused | ||
881 | * @context: unused | ||
882 | * @rv: unused | ||
883 | * | ||
884 | * This is called by acpi_walk_namespace to look for dock stations and bays. | ||
885 | */ | ||
886 | static acpi_status __init | ||
887 | find_dock_and_bay(acpi_handle handle, u32 lvl, void *context, void **rv) | ||
888 | { | ||
889 | if (acpi_dock_match(handle) || is_ejectable_bay(handle)) | ||
890 | dock_add(handle); | ||
891 | |||
892 | return AE_OK; | ||
893 | } | ||
894 | |||
895 | void __init acpi_dock_init(void) | ||
896 | { | ||
897 | /* look for dock stations and bays */ | ||
898 | acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, | ||
899 | ACPI_UINT32_MAX, find_dock_and_bay, NULL, NULL, NULL); | ||
900 | |||
901 | if (!dock_station_count) { | ||
902 | pr_info(PREFIX "No dock devices found.\n"); | ||
903 | return; | ||
904 | } | ||
905 | |||
906 | pr_info(PREFIX "%s: %d docks/bays found\n", | ||
907 | ACPI_DOCK_DRIVER_DESCRIPTION, dock_station_count); | ||
908 | } | 674 | } |
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 143d5df5ec32..957391306cbf 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h | |||
@@ -37,9 +37,15 @@ void acpi_container_init(void); | |||
37 | static inline void acpi_container_init(void) {} | 37 | static inline void acpi_container_init(void) {} |
38 | #endif | 38 | #endif |
39 | #ifdef CONFIG_ACPI_DOCK | 39 | #ifdef CONFIG_ACPI_DOCK |
40 | void acpi_dock_init(void); | 40 | void register_dock_dependent_device(struct acpi_device *adev, |
41 | acpi_handle dshandle); | ||
42 | int dock_notify(struct acpi_device *adev, u32 event); | ||
43 | void acpi_dock_add(struct acpi_device *adev); | ||
41 | #else | 44 | #else |
42 | static inline void acpi_dock_init(void) {} | 45 | static inline void register_dock_dependent_device(struct acpi_device *adev, |
46 | acpi_handle dshandle) {} | ||
47 | static inline int dock_notify(struct acpi_device *adev, u32 event) { return -ENODEV; } | ||
48 | static inline void acpi_dock_add(struct acpi_device *adev) {} | ||
43 | #endif | 49 | #endif |
44 | #ifdef CONFIG_ACPI_HOTPLUG_MEMORY | 50 | #ifdef CONFIG_ACPI_HOTPLUG_MEMORY |
45 | void acpi_memory_hotplug_init(void); | 51 | void acpi_memory_hotplug_init(void); |
@@ -72,8 +78,9 @@ void acpi_lpss_init(void); | |||
72 | static inline void acpi_lpss_init(void) {} | 78 | static inline void acpi_lpss_init(void) {} |
73 | #endif | 79 | #endif |
74 | 80 | ||
81 | acpi_status acpi_hotplug_schedule(struct acpi_device *adev, u32 src); | ||
75 | bool acpi_queue_hotplug_work(struct work_struct *work); | 82 | bool acpi_queue_hotplug_work(struct work_struct *work); |
76 | void acpi_device_hotplug(void *data, u32 src); | 83 | void acpi_device_hotplug(struct acpi_device *adev, u32 src); |
77 | bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent); | 84 | bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent); |
78 | 85 | ||
79 | /* -------------------------------------------------------------------------- | 86 | /* -------------------------------------------------------------------------- |
@@ -91,6 +98,7 @@ void acpi_free_pnp_ids(struct acpi_device_pnp *pnp); | |||
91 | int acpi_bind_one(struct device *dev, struct acpi_device *adev); | 98 | int acpi_bind_one(struct device *dev, struct acpi_device *adev); |
92 | int acpi_unbind_one(struct device *dev); | 99 | int acpi_unbind_one(struct device *dev); |
93 | bool acpi_device_is_present(struct acpi_device *adev); | 100 | bool acpi_device_is_present(struct acpi_device *adev); |
101 | bool acpi_device_is_battery(struct acpi_device *adev); | ||
94 | 102 | ||
95 | /* -------------------------------------------------------------------------- | 103 | /* -------------------------------------------------------------------------- |
96 | Power Resource | 104 | Power Resource |
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index fc1aa7909690..afb4be566940 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c | |||
@@ -1168,8 +1168,7 @@ void acpi_os_wait_events_complete(void) | |||
1168 | 1168 | ||
1169 | struct acpi_hp_work { | 1169 | struct acpi_hp_work { |
1170 | struct work_struct work; | 1170 | struct work_struct work; |
1171 | acpi_hp_callback func; | 1171 | struct acpi_device *adev; |
1172 | void *data; | ||
1173 | u32 src; | 1172 | u32 src; |
1174 | }; | 1173 | }; |
1175 | 1174 | ||
@@ -1178,25 +1177,24 @@ static void acpi_hotplug_work_fn(struct work_struct *work) | |||
1178 | struct acpi_hp_work *hpw = container_of(work, struct acpi_hp_work, work); | 1177 | struct acpi_hp_work *hpw = container_of(work, struct acpi_hp_work, work); |
1179 | 1178 | ||
1180 | acpi_os_wait_events_complete(); | 1179 | acpi_os_wait_events_complete(); |
1181 | hpw->func(hpw->data, hpw->src); | 1180 | acpi_device_hotplug(hpw->adev, hpw->src); |
1182 | kfree(hpw); | 1181 | kfree(hpw); |
1183 | } | 1182 | } |
1184 | 1183 | ||
1185 | acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src) | 1184 | acpi_status acpi_hotplug_schedule(struct acpi_device *adev, u32 src) |
1186 | { | 1185 | { |
1187 | struct acpi_hp_work *hpw; | 1186 | struct acpi_hp_work *hpw; |
1188 | 1187 | ||
1189 | ACPI_DEBUG_PRINT((ACPI_DB_EXEC, | 1188 | ACPI_DEBUG_PRINT((ACPI_DB_EXEC, |
1190 | "Scheduling function [%p(%p, %u)] for deferred execution.\n", | 1189 | "Scheduling hotplug event (%p, %u) for deferred execution.\n", |
1191 | func, data, src)); | 1190 | adev, src)); |
1192 | 1191 | ||
1193 | hpw = kmalloc(sizeof(*hpw), GFP_KERNEL); | 1192 | hpw = kmalloc(sizeof(*hpw), GFP_KERNEL); |
1194 | if (!hpw) | 1193 | if (!hpw) |
1195 | return AE_NO_MEMORY; | 1194 | return AE_NO_MEMORY; |
1196 | 1195 | ||
1197 | INIT_WORK(&hpw->work, acpi_hotplug_work_fn); | 1196 | INIT_WORK(&hpw->work, acpi_hotplug_work_fn); |
1198 | hpw->func = func; | 1197 | hpw->adev = adev; |
1199 | hpw->data = data; | ||
1200 | hpw->src = src; | 1198 | hpw->src = src; |
1201 | /* | 1199 | /* |
1202 | * We can't run hotplug code in kacpid_wq/kacpid_notify_wq etc., because | 1200 | * We can't run hotplug code in kacpid_wq/kacpid_notify_wq etc., because |
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 8bb48bfab1df..eb7a1ff224e7 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c | |||
@@ -71,6 +71,17 @@ void acpi_unlock_hp_context(void) | |||
71 | mutex_unlock(&acpi_hp_context_lock); | 71 | mutex_unlock(&acpi_hp_context_lock); |
72 | } | 72 | } |
73 | 73 | ||
74 | void acpi_initialize_hp_context(struct acpi_device *adev, | ||
75 | struct acpi_hotplug_context *hp, | ||
76 | int (*notify)(struct acpi_device *, u32), | ||
77 | void (*uevent)(struct acpi_device *, u32)) | ||
78 | { | ||
79 | acpi_lock_hp_context(); | ||
80 | acpi_set_hp_context(adev, hp, notify, uevent, NULL); | ||
81 | acpi_unlock_hp_context(); | ||
82 | } | ||
83 | EXPORT_SYMBOL_GPL(acpi_initialize_hp_context); | ||
84 | |||
74 | int acpi_scan_add_handler(struct acpi_scan_handler *handler) | 85 | int acpi_scan_add_handler(struct acpi_scan_handler *handler) |
75 | { | 86 | { |
76 | if (!handler || !handler->attach) | 87 | if (!handler || !handler->attach) |
@@ -470,10 +481,9 @@ static int acpi_generic_hotplug_event(struct acpi_device *adev, u32 type) | |||
470 | return -EINVAL; | 481 | return -EINVAL; |
471 | } | 482 | } |
472 | 483 | ||
473 | void acpi_device_hotplug(void *data, u32 src) | 484 | void acpi_device_hotplug(struct acpi_device *adev, u32 src) |
474 | { | 485 | { |
475 | u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; | 486 | u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; |
476 | struct acpi_device *adev = data; | ||
477 | int error = -ENODEV; | 487 | int error = -ENODEV; |
478 | 488 | ||
479 | lock_device_hotplug(); | 489 | lock_device_hotplug(); |
@@ -487,24 +497,26 @@ void acpi_device_hotplug(void *data, u32 src) | |||
487 | if (adev->handle == INVALID_ACPI_HANDLE) | 497 | if (adev->handle == INVALID_ACPI_HANDLE) |
488 | goto err_out; | 498 | goto err_out; |
489 | 499 | ||
490 | if (adev->flags.hotplug_notify) { | 500 | if (adev->flags.is_dock_station) { |
501 | error = dock_notify(adev, src); | ||
502 | } else if (adev->flags.hotplug_notify) { | ||
491 | error = acpi_generic_hotplug_event(adev, src); | 503 | error = acpi_generic_hotplug_event(adev, src); |
492 | if (error == -EPERM) { | 504 | if (error == -EPERM) { |
493 | ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED; | 505 | ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED; |
494 | goto err_out; | 506 | goto err_out; |
495 | } | 507 | } |
496 | } else { | 508 | } else { |
497 | int (*event)(struct acpi_device *, u32); | 509 | int (*notify)(struct acpi_device *, u32); |
498 | 510 | ||
499 | acpi_lock_hp_context(); | 511 | acpi_lock_hp_context(); |
500 | event = adev->hp ? adev->hp->event : NULL; | 512 | notify = adev->hp ? adev->hp->notify : NULL; |
501 | acpi_unlock_hp_context(); | 513 | acpi_unlock_hp_context(); |
502 | /* | 514 | /* |
503 | * There may be additional notify handlers for device objects | 515 | * There may be additional notify handlers for device objects |
504 | * without the .event() callback, so ignore them here. | 516 | * without the .event() callback, so ignore them here. |
505 | */ | 517 | */ |
506 | if (event) | 518 | if (notify) |
507 | error = event(adev, src); | 519 | error = notify(adev, src); |
508 | else | 520 | else |
509 | goto out; | 521 | goto out; |
510 | } | 522 | } |
@@ -566,8 +578,7 @@ acpi_eject_store(struct device *d, struct device_attribute *attr, | |||
566 | return -ENODEV; | 578 | return -ENODEV; |
567 | 579 | ||
568 | get_device(&acpi_device->dev); | 580 | get_device(&acpi_device->dev); |
569 | status = acpi_hotplug_execute(acpi_device_hotplug, acpi_device, | 581 | status = acpi_hotplug_schedule(acpi_device, ACPI_OST_EC_OSPM_EJECT); |
570 | ACPI_OST_EC_OSPM_EJECT); | ||
571 | if (ACPI_SUCCESS(status)) | 582 | if (ACPI_SUCCESS(status)) |
572 | return count; | 583 | return count; |
573 | 584 | ||
@@ -1660,6 +1671,27 @@ bool acpi_bay_match(acpi_handle handle) | |||
1660 | return acpi_ata_match(phandle); | 1671 | return acpi_ata_match(phandle); |
1661 | } | 1672 | } |
1662 | 1673 | ||
1674 | bool acpi_device_is_battery(struct acpi_device *adev) | ||
1675 | { | ||
1676 | struct acpi_hardware_id *hwid; | ||
1677 | |||
1678 | list_for_each_entry(hwid, &adev->pnp.ids, list) | ||
1679 | if (!strcmp("PNP0C0A", hwid->id)) | ||
1680 | return true; | ||
1681 | |||
1682 | return false; | ||
1683 | } | ||
1684 | |||
1685 | static bool is_ejectable_bay(struct acpi_device *adev) | ||
1686 | { | ||
1687 | acpi_handle handle = adev->handle; | ||
1688 | |||
1689 | if (acpi_has_method(handle, "_EJ0") && acpi_device_is_battery(adev)) | ||
1690 | return true; | ||
1691 | |||
1692 | return acpi_bay_match(handle); | ||
1693 | } | ||
1694 | |||
1663 | /* | 1695 | /* |
1664 | * acpi_dock_match - see if an acpi object has a _DCK method | 1696 | * acpi_dock_match - see if an acpi object has a _DCK method |
1665 | */ | 1697 | */ |
@@ -1964,6 +1996,10 @@ static void acpi_scan_init_hotplug(struct acpi_device *adev) | |||
1964 | { | 1996 | { |
1965 | struct acpi_hardware_id *hwid; | 1997 | struct acpi_hardware_id *hwid; |
1966 | 1998 | ||
1999 | if (acpi_dock_match(adev->handle) || is_ejectable_bay(adev)) { | ||
2000 | acpi_dock_add(adev); | ||
2001 | return; | ||
2002 | } | ||
1967 | list_for_each_entry(hwid, &adev->pnp.ids, list) { | 2003 | list_for_each_entry(hwid, &adev->pnp.ids, list) { |
1968 | struct acpi_scan_handler *handler; | 2004 | struct acpi_scan_handler *handler; |
1969 | 2005 | ||
@@ -2035,8 +2071,12 @@ static int acpi_scan_attach_handler(struct acpi_device *device) | |||
2035 | static void acpi_bus_attach(struct acpi_device *device) | 2071 | static void acpi_bus_attach(struct acpi_device *device) |
2036 | { | 2072 | { |
2037 | struct acpi_device *child; | 2073 | struct acpi_device *child; |
2074 | acpi_handle ejd; | ||
2038 | int ret; | 2075 | int ret; |
2039 | 2076 | ||
2077 | if (ACPI_SUCCESS(acpi_bus_get_ejd(device->handle, &ejd))) | ||
2078 | register_dock_dependent_device(device, ejd); | ||
2079 | |||
2040 | acpi_bus_get_status(device); | 2080 | acpi_bus_get_status(device); |
2041 | /* Skip devices that are not present. */ | 2081 | /* Skip devices that are not present. */ |
2042 | if (!acpi_device_is_present(device)) { | 2082 | if (!acpi_device_is_present(device)) { |
@@ -2189,7 +2229,6 @@ int __init acpi_scan_init(void) | |||
2189 | acpi_cmos_rtc_init(); | 2229 | acpi_cmos_rtc_init(); |
2190 | acpi_container_init(); | 2230 | acpi_container_init(); |
2191 | acpi_memory_hotplug_init(); | 2231 | acpi_memory_hotplug_init(); |
2192 | acpi_dock_init(); | ||
2193 | 2232 | ||
2194 | mutex_lock(&acpi_scan_lock); | 2233 | mutex_lock(&acpi_scan_lock); |
2195 | /* | 2234 | /* |
diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index 9e69a5308693..acb95dffdb6a 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c | |||
@@ -38,6 +38,16 @@ static void ata_acpi_clear_gtf(struct ata_device *dev) | |||
38 | dev->gtf_cache = NULL; | 38 | dev->gtf_cache = NULL; |
39 | } | 39 | } |
40 | 40 | ||
41 | struct ata_acpi_hotplug_context { | ||
42 | struct acpi_hotplug_context hp; | ||
43 | union { | ||
44 | struct ata_port *ap; | ||
45 | struct ata_device *dev; | ||
46 | } data; | ||
47 | }; | ||
48 | |||
49 | #define ata_hotplug_data(context) (container_of((context), struct ata_acpi_hotplug_context, hp)->data) | ||
50 | |||
41 | /** | 51 | /** |
42 | * ata_dev_acpi_handle - provide the acpi_handle for an ata_device | 52 | * ata_dev_acpi_handle - provide the acpi_handle for an ata_device |
43 | * @dev: the acpi_handle returned will correspond to this device | 53 | * @dev: the acpi_handle returned will correspond to this device |
@@ -121,18 +131,17 @@ static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device *dev, | |||
121 | ata_port_wait_eh(ap); | 131 | ata_port_wait_eh(ap); |
122 | } | 132 | } |
123 | 133 | ||
124 | static void ata_acpi_dev_notify_dock(acpi_handle handle, u32 event, void *data) | 134 | static int ata_acpi_dev_notify_dock(struct acpi_device *adev, u32 event) |
125 | { | 135 | { |
126 | struct ata_device *dev = data; | 136 | struct ata_device *dev = ata_hotplug_data(adev->hp).dev; |
127 | |||
128 | ata_acpi_handle_hotplug(dev->link->ap, dev, event); | 137 | ata_acpi_handle_hotplug(dev->link->ap, dev, event); |
138 | return 0; | ||
129 | } | 139 | } |
130 | 140 | ||
131 | static void ata_acpi_ap_notify_dock(acpi_handle handle, u32 event, void *data) | 141 | static int ata_acpi_ap_notify_dock(struct acpi_device *adev, u32 event) |
132 | { | 142 | { |
133 | struct ata_port *ap = data; | 143 | ata_acpi_handle_hotplug(ata_hotplug_data(adev->hp).ap, NULL, event); |
134 | 144 | return 0; | |
135 | ata_acpi_handle_hotplug(ap, NULL, event); | ||
136 | } | 145 | } |
137 | 146 | ||
138 | static void ata_acpi_uevent(struct ata_port *ap, struct ata_device *dev, | 147 | static void ata_acpi_uevent(struct ata_port *ap, struct ata_device *dev, |
@@ -154,31 +163,23 @@ static void ata_acpi_uevent(struct ata_port *ap, struct ata_device *dev, | |||
154 | } | 163 | } |
155 | } | 164 | } |
156 | 165 | ||
157 | static void ata_acpi_ap_uevent(acpi_handle handle, u32 event, void *data) | 166 | static void ata_acpi_ap_uevent(struct acpi_device *adev, u32 event) |
158 | { | 167 | { |
159 | ata_acpi_uevent(data, NULL, event); | 168 | ata_acpi_uevent(ata_hotplug_data(adev->hp).ap, NULL, event); |
160 | } | 169 | } |
161 | 170 | ||
162 | static void ata_acpi_dev_uevent(acpi_handle handle, u32 event, void *data) | 171 | static void ata_acpi_dev_uevent(struct acpi_device *adev, u32 event) |
163 | { | 172 | { |
164 | struct ata_device *dev = data; | 173 | struct ata_device *dev = ata_hotplug_data(adev->hp).dev; |
165 | ata_acpi_uevent(dev->link->ap, dev, event); | 174 | ata_acpi_uevent(dev->link->ap, dev, event); |
166 | } | 175 | } |
167 | 176 | ||
168 | static const struct acpi_dock_ops ata_acpi_dev_dock_ops = { | ||
169 | .handler = ata_acpi_dev_notify_dock, | ||
170 | .uevent = ata_acpi_dev_uevent, | ||
171 | }; | ||
172 | |||
173 | static const struct acpi_dock_ops ata_acpi_ap_dock_ops = { | ||
174 | .handler = ata_acpi_ap_notify_dock, | ||
175 | .uevent = ata_acpi_ap_uevent, | ||
176 | }; | ||
177 | |||
178 | /* bind acpi handle to pata port */ | 177 | /* bind acpi handle to pata port */ |
179 | void ata_acpi_bind_port(struct ata_port *ap) | 178 | void ata_acpi_bind_port(struct ata_port *ap) |
180 | { | 179 | { |
181 | struct acpi_device *host_companion = ACPI_COMPANION(ap->host->dev); | 180 | struct acpi_device *host_companion = ACPI_COMPANION(ap->host->dev); |
181 | struct acpi_device *adev; | ||
182 | struct ata_acpi_hotplug_context *context; | ||
182 | 183 | ||
183 | if (libata_noacpi || ap->flags & ATA_FLAG_ACPI_SATA || !host_companion) | 184 | if (libata_noacpi || ap->flags & ATA_FLAG_ACPI_SATA || !host_companion) |
184 | return; | 185 | return; |
@@ -188,9 +189,17 @@ void ata_acpi_bind_port(struct ata_port *ap) | |||
188 | if (ata_acpi_gtm(ap, &ap->__acpi_init_gtm) == 0) | 189 | if (ata_acpi_gtm(ap, &ap->__acpi_init_gtm) == 0) |
189 | ap->pflags |= ATA_PFLAG_INIT_GTM_VALID; | 190 | ap->pflags |= ATA_PFLAG_INIT_GTM_VALID; |
190 | 191 | ||
191 | /* we might be on a docking station */ | 192 | adev = ACPI_COMPANION(&ap->tdev); |
192 | register_hotplug_dock_device(ACPI_HANDLE(&ap->tdev), | 193 | if (!adev || adev->hp) |
193 | &ata_acpi_ap_dock_ops, ap, NULL, NULL); | 194 | return; |
195 | |||
196 | context = kzalloc(sizeof(*context), GFP_KERNEL); | ||
197 | if (!context) | ||
198 | return; | ||
199 | |||
200 | context->data.ap = ap; | ||
201 | acpi_initialize_hp_context(adev, &context->hp, ata_acpi_ap_notify_dock, | ||
202 | ata_acpi_ap_uevent); | ||
194 | } | 203 | } |
195 | 204 | ||
196 | void ata_acpi_bind_dev(struct ata_device *dev) | 205 | void ata_acpi_bind_dev(struct ata_device *dev) |
@@ -198,7 +207,8 @@ void ata_acpi_bind_dev(struct ata_device *dev) | |||
198 | struct ata_port *ap = dev->link->ap; | 207 | struct ata_port *ap = dev->link->ap; |
199 | struct acpi_device *port_companion = ACPI_COMPANION(&ap->tdev); | 208 | struct acpi_device *port_companion = ACPI_COMPANION(&ap->tdev); |
200 | struct acpi_device *host_companion = ACPI_COMPANION(ap->host->dev); | 209 | struct acpi_device *host_companion = ACPI_COMPANION(ap->host->dev); |
201 | struct acpi_device *parent; | 210 | struct acpi_device *parent, *adev; |
211 | struct ata_acpi_hotplug_context *context; | ||
202 | u64 adr; | 212 | u64 adr; |
203 | 213 | ||
204 | /* | 214 | /* |
@@ -221,9 +231,17 @@ void ata_acpi_bind_dev(struct ata_device *dev) | |||
221 | } | 231 | } |
222 | 232 | ||
223 | acpi_preset_companion(&dev->tdev, parent, adr); | 233 | acpi_preset_companion(&dev->tdev, parent, adr); |
234 | adev = ACPI_COMPANION(&dev->tdev); | ||
235 | if (!adev || adev->hp) | ||
236 | return; | ||
237 | |||
238 | context = kzalloc(sizeof(*context), GFP_KERNEL); | ||
239 | if (!context) | ||
240 | return; | ||
224 | 241 | ||
225 | register_hotplug_dock_device(ata_dev_acpi_handle(dev), | 242 | context->data.dev = dev; |
226 | &ata_acpi_dev_dock_ops, dev, NULL, NULL); | 243 | acpi_initialize_hp_context(adev, &context->hp, ata_acpi_dev_notify_dock, |
244 | ata_acpi_dev_uevent); | ||
227 | } | 245 | } |
228 | 246 | ||
229 | /** | 247 | /** |
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 2d51bf7e9fe0..828acf422c17 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c | |||
@@ -59,7 +59,8 @@ | |||
59 | static LIST_HEAD(bridge_list); | 59 | static LIST_HEAD(bridge_list); |
60 | static DEFINE_MUTEX(bridge_mutex); | 60 | static DEFINE_MUTEX(bridge_mutex); |
61 | 61 | ||
62 | static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type); | 62 | static int acpiphp_hotplug_notify(struct acpi_device *adev, u32 type); |
63 | static void acpiphp_post_dock_fixup(struct acpi_device *adev); | ||
63 | static void acpiphp_sanitize_bus(struct pci_bus *bus); | 64 | static void acpiphp_sanitize_bus(struct pci_bus *bus); |
64 | static void acpiphp_set_hpp_values(struct pci_bus *bus); | 65 | static void acpiphp_set_hpp_values(struct pci_bus *bus); |
65 | static void hotplug_event(u32 type, struct acpiphp_context *context); | 66 | static void hotplug_event(u32 type, struct acpiphp_context *context); |
@@ -80,7 +81,8 @@ static struct acpiphp_context *acpiphp_init_context(struct acpi_device *adev) | |||
80 | return NULL; | 81 | return NULL; |
81 | 82 | ||
82 | context->refcount = 1; | 83 | context->refcount = 1; |
83 | acpi_set_hp_context(adev, &context->hp, acpiphp_hotplug_event); | 84 | acpi_set_hp_context(adev, &context->hp, acpiphp_hotplug_notify, NULL, |
85 | acpiphp_post_dock_fixup); | ||
84 | return context; | 86 | return context; |
85 | } | 87 | } |
86 | 88 | ||
@@ -130,6 +132,27 @@ static inline void put_bridge(struct acpiphp_bridge *bridge) | |||
130 | kref_put(&bridge->ref, free_bridge); | 132 | kref_put(&bridge->ref, free_bridge); |
131 | } | 133 | } |
132 | 134 | ||
135 | static struct acpiphp_context *acpiphp_grab_context(struct acpi_device *adev) | ||
136 | { | ||
137 | struct acpiphp_context *context; | ||
138 | |||
139 | acpi_lock_hp_context(); | ||
140 | context = acpiphp_get_context(adev); | ||
141 | if (!context || context->func.parent->is_going_away) { | ||
142 | acpi_unlock_hp_context(); | ||
143 | return NULL; | ||
144 | } | ||
145 | get_bridge(context->func.parent); | ||
146 | acpiphp_put_context(context); | ||
147 | acpi_unlock_hp_context(); | ||
148 | return context; | ||
149 | } | ||
150 | |||
151 | static void acpiphp_let_context_go(struct acpiphp_context *context) | ||
152 | { | ||
153 | put_bridge(context->func.parent); | ||
154 | } | ||
155 | |||
133 | static void free_bridge(struct kref *kref) | 156 | static void free_bridge(struct kref *kref) |
134 | { | 157 | { |
135 | struct acpiphp_context *context; | 158 | struct acpiphp_context *context; |
@@ -164,28 +187,29 @@ static void free_bridge(struct kref *kref) | |||
164 | acpi_unlock_hp_context(); | 187 | acpi_unlock_hp_context(); |
165 | } | 188 | } |
166 | 189 | ||
167 | /* | 190 | /** |
168 | * the _DCK method can do funny things... and sometimes not | 191 | * acpiphp_post_dock_fixup - Post-dock fixups for PCI devices. |
169 | * hah-hah funny. | 192 | * @adev: ACPI device object corresponding to a PCI device. |
170 | * | 193 | * |
171 | * TBD - figure out a way to only call fixups for | 194 | * TBD - figure out a way to only call fixups for systems that require them. |
172 | * systems that require them. | ||
173 | */ | 195 | */ |
174 | static void post_dock_fixups(acpi_handle not_used, u32 event, void *data) | 196 | static void acpiphp_post_dock_fixup(struct acpi_device *adev) |
175 | { | 197 | { |
176 | struct acpiphp_context *context = data; | 198 | struct acpiphp_context *context = acpiphp_grab_context(adev); |
177 | struct pci_bus *bus = context->func.slot->bus; | 199 | struct pci_bus *bus; |
178 | u32 buses; | 200 | u32 buses; |
179 | 201 | ||
180 | if (!bus->self) | 202 | if (!context) |
181 | return; | 203 | return; |
182 | 204 | ||
205 | bus = context->func.slot->bus; | ||
206 | if (!bus->self) | ||
207 | goto out; | ||
208 | |||
183 | /* fixup bad _DCK function that rewrites | 209 | /* fixup bad _DCK function that rewrites |
184 | * secondary bridge on slot | 210 | * secondary bridge on slot |
185 | */ | 211 | */ |
186 | pci_read_config_dword(bus->self, | 212 | pci_read_config_dword(bus->self, PCI_PRIMARY_BUS, &buses); |
187 | PCI_PRIMARY_BUS, | ||
188 | &buses); | ||
189 | 213 | ||
190 | if (((buses >> 8) & 0xff) != bus->busn_res.start) { | 214 | if (((buses >> 8) & 0xff) != bus->busn_res.start) { |
191 | buses = (buses & 0xff000000) | 215 | buses = (buses & 0xff000000) |
@@ -194,24 +218,11 @@ static void post_dock_fixups(acpi_handle not_used, u32 event, void *data) | |||
194 | | ((unsigned int)(bus->busn_res.end) << 16); | 218 | | ((unsigned int)(bus->busn_res.end) << 16); |
195 | pci_write_config_dword(bus->self, PCI_PRIMARY_BUS, buses); | 219 | pci_write_config_dword(bus->self, PCI_PRIMARY_BUS, buses); |
196 | } | 220 | } |
197 | } | ||
198 | |||
199 | static void dock_event(acpi_handle handle, u32 type, void *data) | ||
200 | { | ||
201 | struct acpi_device *adev; | ||
202 | 221 | ||
203 | adev = acpi_bus_get_acpi_device(handle); | 222 | out: |
204 | if (adev) { | 223 | acpiphp_let_context_go(context); |
205 | acpiphp_hotplug_event(adev, type); | ||
206 | acpi_bus_put_acpi_device(adev); | ||
207 | } | ||
208 | } | 224 | } |
209 | 225 | ||
210 | static const struct acpi_dock_ops acpiphp_dock_ops = { | ||
211 | .fixup = post_dock_fixups, | ||
212 | .handler = dock_event, | ||
213 | }; | ||
214 | |||
215 | /* Check whether the PCI device is managed by native PCIe hotplug driver */ | 226 | /* Check whether the PCI device is managed by native PCIe hotplug driver */ |
216 | static bool device_is_managed_by_native_pciehp(struct pci_dev *pdev) | 227 | static bool device_is_managed_by_native_pciehp(struct pci_dev *pdev) |
217 | { | 228 | { |
@@ -241,20 +252,6 @@ static bool device_is_managed_by_native_pciehp(struct pci_dev *pdev) | |||
241 | return true; | 252 | return true; |
242 | } | 253 | } |
243 | 254 | ||
244 | static void acpiphp_dock_init(void *data) | ||
245 | { | ||
246 | struct acpiphp_context *context = data; | ||
247 | |||
248 | get_bridge(context->func.parent); | ||
249 | } | ||
250 | |||
251 | static void acpiphp_dock_release(void *data) | ||
252 | { | ||
253 | struct acpiphp_context *context = data; | ||
254 | |||
255 | put_bridge(context->func.parent); | ||
256 | } | ||
257 | |||
258 | /** | 255 | /** |
259 | * acpiphp_add_context - Add ACPIPHP context to an ACPI device object. | 256 | * acpiphp_add_context - Add ACPIPHP context to an ACPI device object. |
260 | * @handle: ACPI handle of the object to add a context to. | 257 | * @handle: ACPI handle of the object to add a context to. |
@@ -300,22 +297,18 @@ static acpi_status acpiphp_add_context(acpi_handle handle, u32 lvl, void *data, | |||
300 | newfunc = &context->func; | 297 | newfunc = &context->func; |
301 | newfunc->function = function; | 298 | newfunc->function = function; |
302 | newfunc->parent = bridge; | 299 | newfunc->parent = bridge; |
300 | acpi_unlock_hp_context(); | ||
303 | 301 | ||
304 | if (acpi_has_method(handle, "_EJ0")) | 302 | /* |
303 | * If this is a dock device, its _EJ0 should be executed by the dock | ||
304 | * notify handler after calling _DCK. | ||
305 | */ | ||
306 | if (!is_dock_device(adev) && acpi_has_method(handle, "_EJ0")) | ||
305 | newfunc->flags = FUNC_HAS_EJ0; | 307 | newfunc->flags = FUNC_HAS_EJ0; |
306 | 308 | ||
307 | if (acpi_has_method(handle, "_STA")) | 309 | if (acpi_has_method(handle, "_STA")) |
308 | newfunc->flags |= FUNC_HAS_STA; | 310 | newfunc->flags |= FUNC_HAS_STA; |
309 | 311 | ||
310 | /* | ||
311 | * Dock stations' notify handler should be used for dock devices instead | ||
312 | * of the common one, so clear hp.event in their contexts. | ||
313 | */ | ||
314 | if (acpi_has_method(handle, "_DCK")) | ||
315 | context->hp.event = NULL; | ||
316 | |||
317 | acpi_unlock_hp_context(); | ||
318 | |||
319 | /* search for objects that share the same slot */ | 312 | /* search for objects that share the same slot */ |
320 | list_for_each_entry(slot, &bridge->slots, node) | 313 | list_for_each_entry(slot, &bridge->slots, node) |
321 | if (slot->device == device) | 314 | if (slot->device == device) |
@@ -341,7 +334,7 @@ static acpi_status acpiphp_add_context(acpi_handle handle, u32 lvl, void *data, | |||
341 | * by the native PCIe hotplug (PCIeHP), becuase that code is supposed to | 334 | * by the native PCIe hotplug (PCIeHP), becuase that code is supposed to |
342 | * expose slots to user space in those cases. | 335 | * expose slots to user space in those cases. |
343 | */ | 336 | */ |
344 | if ((acpi_pci_check_ejectable(pbus, handle) || is_dock_device(handle)) | 337 | if ((acpi_pci_check_ejectable(pbus, handle) || is_dock_device(adev)) |
345 | && !(pdev && device_is_managed_by_native_pciehp(pdev))) { | 338 | && !(pdev && device_is_managed_by_native_pciehp(pdev))) { |
346 | unsigned long long sun; | 339 | unsigned long long sun; |
347 | int retval; | 340 | int retval; |
@@ -376,18 +369,6 @@ static acpi_status acpiphp_add_context(acpi_handle handle, u32 lvl, void *data, | |||
376 | &val, 60*1000)) | 369 | &val, 60*1000)) |
377 | slot->flags |= SLOT_ENABLED; | 370 | slot->flags |= SLOT_ENABLED; |
378 | 371 | ||
379 | if (is_dock_device(handle)) { | ||
380 | /* we don't want to call this device's _EJ0 | ||
381 | * because we want the dock notify handler | ||
382 | * to call it after it calls _DCK | ||
383 | */ | ||
384 | newfunc->flags &= ~FUNC_HAS_EJ0; | ||
385 | if (register_hotplug_dock_device(handle, | ||
386 | &acpiphp_dock_ops, context, | ||
387 | acpiphp_dock_init, acpiphp_dock_release)) | ||
388 | pr_debug("failed to register dock device\n"); | ||
389 | } | ||
390 | |||
391 | return AE_OK; | 372 | return AE_OK; |
392 | } | 373 | } |
393 | 374 | ||
@@ -418,11 +399,9 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) | |||
418 | list_for_each_entry(func, &slot->funcs, sibling) { | 399 | list_for_each_entry(func, &slot->funcs, sibling) { |
419 | struct acpi_device *adev = func_to_acpi_device(func); | 400 | struct acpi_device *adev = func_to_acpi_device(func); |
420 | 401 | ||
421 | if (is_dock_device(adev->handle)) | ||
422 | unregister_hotplug_dock_device(adev->handle); | ||
423 | |||
424 | acpi_lock_hp_context(); | 402 | acpi_lock_hp_context(); |
425 | adev->hp->event = NULL; | 403 | adev->hp->notify = NULL; |
404 | adev->hp->fixup = NULL; | ||
426 | acpi_unlock_hp_context(); | 405 | acpi_unlock_hp_context(); |
427 | } | 406 | } |
428 | slot->flags |= SLOT_IS_GOING_AWAY; | 407 | slot->flags |= SLOT_IS_GOING_AWAY; |
@@ -851,23 +830,16 @@ static void hotplug_event(u32 type, struct acpiphp_context *context) | |||
851 | put_bridge(bridge); | 830 | put_bridge(bridge); |
852 | } | 831 | } |
853 | 832 | ||
854 | static int acpiphp_hotplug_event(struct acpi_device *adev, u32 type) | 833 | static int acpiphp_hotplug_notify(struct acpi_device *adev, u32 type) |
855 | { | 834 | { |
856 | struct acpiphp_context *context; | 835 | struct acpiphp_context *context; |
857 | 836 | ||
858 | acpi_lock_hp_context(); | 837 | context = acpiphp_grab_context(adev); |
859 | context = acpiphp_get_context(adev); | 838 | if (!context) |
860 | if (!context || context->func.parent->is_going_away) { | ||
861 | acpi_unlock_hp_context(); | ||
862 | return -ENODATA; | 839 | return -ENODATA; |
863 | } | ||
864 | get_bridge(context->func.parent); | ||
865 | acpiphp_put_context(context); | ||
866 | acpi_unlock_hp_context(); | ||
867 | 840 | ||
868 | hotplug_event(type, context); | 841 | hotplug_event(type, context); |
869 | 842 | acpiphp_let_context_go(context); | |
870 | put_bridge(context->func.parent); | ||
871 | return 0; | 843 | return 0; |
872 | } | 844 | } |
873 | 845 | ||