diff options
Diffstat (limited to 'drivers/acpi/dock.c')
-rw-r--r-- | drivers/acpi/dock.c | 388 |
1 files changed, 300 insertions, 88 deletions
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 7d2edf143f16..5b30b8d91d71 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c | |||
@@ -48,7 +48,6 @@ MODULE_PARM_DESC(immediate_undock, "1 (default) will cause the driver to " | |||
48 | " before undocking"); | 48 | " before undocking"); |
49 | 49 | ||
50 | static struct atomic_notifier_head dock_notifier_list; | 50 | static struct atomic_notifier_head dock_notifier_list; |
51 | static struct platform_device *dock_device; | ||
52 | static char dock_device_name[] = "dock"; | 51 | static char dock_device_name[] = "dock"; |
53 | 52 | ||
54 | static const struct acpi_device_id dock_device_ids[] = { | 53 | static const struct acpi_device_id dock_device_ids[] = { |
@@ -65,23 +64,29 @@ struct dock_station { | |||
65 | struct mutex hp_lock; | 64 | struct mutex hp_lock; |
66 | struct list_head dependent_devices; | 65 | struct list_head dependent_devices; |
67 | struct list_head hotplug_devices; | 66 | struct list_head hotplug_devices; |
67 | |||
68 | struct list_head sibiling; | ||
69 | struct platform_device *dock_device; | ||
68 | }; | 70 | }; |
71 | static LIST_HEAD(dock_stations); | ||
72 | static int dock_station_count; | ||
69 | 73 | ||
70 | struct dock_dependent_device { | 74 | struct dock_dependent_device { |
71 | struct list_head list; | 75 | struct list_head list; |
72 | struct list_head hotplug_list; | 76 | struct list_head hotplug_list; |
73 | acpi_handle handle; | 77 | acpi_handle handle; |
74 | acpi_notify_handler handler; | 78 | struct acpi_dock_ops *ops; |
75 | void *context; | 79 | void *context; |
76 | }; | 80 | }; |
77 | 81 | ||
78 | #define DOCK_DOCKING 0x00000001 | 82 | #define DOCK_DOCKING 0x00000001 |
79 | #define DOCK_UNDOCKING 0x00000002 | 83 | #define DOCK_UNDOCKING 0x00000002 |
84 | #define DOCK_IS_DOCK 0x00000010 | ||
85 | #define DOCK_IS_ATA 0x00000020 | ||
86 | #define DOCK_IS_BAT 0x00000040 | ||
80 | #define DOCK_EVENT 3 | 87 | #define DOCK_EVENT 3 |
81 | #define UNDOCK_EVENT 2 | 88 | #define UNDOCK_EVENT 2 |
82 | 89 | ||
83 | static struct dock_station *dock_station; | ||
84 | |||
85 | /***************************************************************************** | 90 | /***************************************************************************** |
86 | * Dock Dependent device functions * | 91 | * Dock Dependent device functions * |
87 | *****************************************************************************/ | 92 | *****************************************************************************/ |
@@ -199,6 +204,60 @@ static int is_dock(acpi_handle handle) | |||
199 | return 1; | 204 | return 1; |
200 | } | 205 | } |
201 | 206 | ||
207 | static int is_ejectable(acpi_handle handle) | ||
208 | { | ||
209 | acpi_status status; | ||
210 | acpi_handle tmp; | ||
211 | |||
212 | status = acpi_get_handle(handle, "_EJ0", &tmp); | ||
213 | if (ACPI_FAILURE(status)) | ||
214 | return 0; | ||
215 | return 1; | ||
216 | } | ||
217 | |||
218 | static int is_ata(acpi_handle handle) | ||
219 | { | ||
220 | acpi_handle tmp; | ||
221 | |||
222 | if ((ACPI_SUCCESS(acpi_get_handle(handle, "_GTF", &tmp))) || | ||
223 | (ACPI_SUCCESS(acpi_get_handle(handle, "_GTM", &tmp))) || | ||
224 | (ACPI_SUCCESS(acpi_get_handle(handle, "_STM", &tmp))) || | ||
225 | (ACPI_SUCCESS(acpi_get_handle(handle, "_SDD", &tmp)))) | ||
226 | return 1; | ||
227 | |||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | static int is_battery(acpi_handle handle) | ||
232 | { | ||
233 | struct acpi_device_info *info; | ||
234 | struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; | ||
235 | int ret = 1; | ||
236 | |||
237 | if (!ACPI_SUCCESS(acpi_get_object_info(handle, &buffer))) | ||
238 | return 0; | ||
239 | info = buffer.pointer; | ||
240 | if (!(info->valid & ACPI_VALID_HID)) | ||
241 | ret = 0; | ||
242 | else | ||
243 | ret = !strcmp("PNP0C0A", info->hardware_id.value); | ||
244 | |||
245 | kfree(buffer.pointer); | ||
246 | return ret; | ||
247 | } | ||
248 | |||
249 | static int is_ejectable_bay(acpi_handle handle) | ||
250 | { | ||
251 | acpi_handle phandle; | ||
252 | if (!is_ejectable(handle)) | ||
253 | return 0; | ||
254 | if (is_battery(handle) || is_ata(handle)) | ||
255 | return 1; | ||
256 | if (!acpi_get_parent(handle, &phandle) && is_ata(phandle)) | ||
257 | return 1; | ||
258 | return 0; | ||
259 | } | ||
260 | |||
202 | /** | 261 | /** |
203 | * is_dock_device - see if a device is on a dock station | 262 | * is_dock_device - see if a device is on a dock station |
204 | * @handle: acpi handle of the device | 263 | * @handle: acpi handle of the device |
@@ -209,11 +268,17 @@ static int is_dock(acpi_handle handle) | |||
209 | */ | 268 | */ |
210 | int is_dock_device(acpi_handle handle) | 269 | int is_dock_device(acpi_handle handle) |
211 | { | 270 | { |
212 | if (!dock_station) | 271 | struct dock_station *dock_station; |
272 | |||
273 | if (!dock_station_count) | ||
213 | return 0; | 274 | return 0; |
214 | 275 | ||
215 | if (is_dock(handle) || find_dock_dependent_device(dock_station, handle)) | 276 | if (is_dock(handle)) |
216 | return 1; | 277 | return 1; |
278 | list_for_each_entry(dock_station, &dock_stations, sibiling) { | ||
279 | if (find_dock_dependent_device(dock_station, handle)) | ||
280 | return 1; | ||
281 | } | ||
217 | 282 | ||
218 | return 0; | 283 | return 0; |
219 | } | 284 | } |
@@ -229,7 +294,7 @@ EXPORT_SYMBOL_GPL(is_dock_device); | |||
229 | */ | 294 | */ |
230 | static int dock_present(struct dock_station *ds) | 295 | static int dock_present(struct dock_station *ds) |
231 | { | 296 | { |
232 | unsigned long sta; | 297 | unsigned long long sta; |
233 | acpi_status status; | 298 | acpi_status status; |
234 | 299 | ||
235 | if (ds) { | 300 | if (ds) { |
@@ -320,8 +385,8 @@ static void hotplug_dock_devices(struct dock_station *ds, u32 event) | |||
320 | * First call driver specific hotplug functions | 385 | * First call driver specific hotplug functions |
321 | */ | 386 | */ |
322 | list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) { | 387 | list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) { |
323 | if (dd->handler) | 388 | if (dd->ops && dd->ops->handler) |
324 | dd->handler(dd->handle, event, dd->context); | 389 | dd->ops->handler(dd->handle, event, dd->context); |
325 | } | 390 | } |
326 | 391 | ||
327 | /* | 392 | /* |
@@ -341,9 +406,10 @@ static void hotplug_dock_devices(struct dock_station *ds, u32 event) | |||
341 | 406 | ||
342 | static void dock_event(struct dock_station *ds, u32 event, int num) | 407 | static void dock_event(struct dock_station *ds, u32 event, int num) |
343 | { | 408 | { |
344 | struct device *dev = &dock_device->dev; | 409 | struct device *dev = &ds->dock_device->dev; |
345 | char event_string[13]; | 410 | char event_string[13]; |
346 | char *envp[] = { event_string, NULL }; | 411 | char *envp[] = { event_string, NULL }; |
412 | struct dock_dependent_device *dd; | ||
347 | 413 | ||
348 | if (num == UNDOCK_EVENT) | 414 | if (num == UNDOCK_EVENT) |
349 | sprintf(event_string, "EVENT=undock"); | 415 | sprintf(event_string, "EVENT=undock"); |
@@ -354,7 +420,14 @@ static void dock_event(struct dock_station *ds, u32 event, int num) | |||
354 | * Indicate that the status of the dock station has | 420 | * Indicate that the status of the dock station has |
355 | * changed. | 421 | * changed. |
356 | */ | 422 | */ |
357 | kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); | 423 | if (num == DOCK_EVENT) |
424 | kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); | ||
425 | |||
426 | list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) | ||
427 | if (dd->ops && dd->ops->uevent) | ||
428 | dd->ops->uevent(dd->handle, event, dd->context); | ||
429 | if (num != DOCK_EVENT) | ||
430 | kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); | ||
358 | } | 431 | } |
359 | 432 | ||
360 | /** | 433 | /** |
@@ -414,9 +487,10 @@ static void handle_dock(struct dock_station *ds, int dock) | |||
414 | arg.type = ACPI_TYPE_INTEGER; | 487 | arg.type = ACPI_TYPE_INTEGER; |
415 | arg.integer.value = dock; | 488 | arg.integer.value = dock; |
416 | status = acpi_evaluate_object(ds->handle, "_DCK", &arg_list, &buffer); | 489 | status = acpi_evaluate_object(ds->handle, "_DCK", &arg_list, &buffer); |
417 | if (ACPI_FAILURE(status)) | 490 | if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) |
418 | printk(KERN_ERR PREFIX "%s - failed to execute _DCK\n", | 491 | ACPI_EXCEPTION((AE_INFO, status, "%s - failed to execute" |
419 | (char *)name_buffer.pointer); | 492 | " _DCK\n", (char *)name_buffer.pointer)); |
493 | |||
420 | kfree(buffer.pointer); | 494 | kfree(buffer.pointer); |
421 | kfree(name_buffer.pointer); | 495 | kfree(name_buffer.pointer); |
422 | } | 496 | } |
@@ -452,6 +526,25 @@ static inline void complete_undock(struct dock_station *ds) | |||
452 | ds->flags &= ~(DOCK_UNDOCKING); | 526 | ds->flags &= ~(DOCK_UNDOCKING); |
453 | } | 527 | } |
454 | 528 | ||
529 | static void dock_lock(struct dock_station *ds, int lock) | ||
530 | { | ||
531 | struct acpi_object_list arg_list; | ||
532 | union acpi_object arg; | ||
533 | acpi_status status; | ||
534 | |||
535 | arg_list.count = 1; | ||
536 | arg_list.pointer = &arg; | ||
537 | arg.type = ACPI_TYPE_INTEGER; | ||
538 | arg.integer.value = !!lock; | ||
539 | status = acpi_evaluate_object(ds->handle, "_LCK", &arg_list, NULL); | ||
540 | if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { | ||
541 | if (lock) | ||
542 | printk(KERN_WARNING PREFIX "Locking device failed\n"); | ||
543 | else | ||
544 | printk(KERN_WARNING PREFIX "Unlocking device failed\n"); | ||
545 | } | ||
546 | } | ||
547 | |||
455 | /** | 548 | /** |
456 | * dock_in_progress - see if we are in the middle of handling a dock event | 549 | * dock_in_progress - see if we are in the middle of handling a dock event |
457 | * @ds: the dock station | 550 | * @ds: the dock station |
@@ -479,7 +572,7 @@ static int dock_in_progress(struct dock_station *ds) | |||
479 | */ | 572 | */ |
480 | int register_dock_notifier(struct notifier_block *nb) | 573 | int register_dock_notifier(struct notifier_block *nb) |
481 | { | 574 | { |
482 | if (!dock_station) | 575 | if (!dock_station_count) |
483 | return -ENODEV; | 576 | return -ENODEV; |
484 | 577 | ||
485 | return atomic_notifier_chain_register(&dock_notifier_list, nb); | 578 | return atomic_notifier_chain_register(&dock_notifier_list, nb); |
@@ -493,7 +586,7 @@ EXPORT_SYMBOL_GPL(register_dock_notifier); | |||
493 | */ | 586 | */ |
494 | void unregister_dock_notifier(struct notifier_block *nb) | 587 | void unregister_dock_notifier(struct notifier_block *nb) |
495 | { | 588 | { |
496 | if (!dock_station) | 589 | if (!dock_station_count) |
497 | return; | 590 | return; |
498 | 591 | ||
499 | atomic_notifier_chain_unregister(&dock_notifier_list, nb); | 592 | atomic_notifier_chain_unregister(&dock_notifier_list, nb); |
@@ -504,7 +597,7 @@ EXPORT_SYMBOL_GPL(unregister_dock_notifier); | |||
504 | /** | 597 | /** |
505 | * register_hotplug_dock_device - register a hotplug function | 598 | * register_hotplug_dock_device - register a hotplug function |
506 | * @handle: the handle of the device | 599 | * @handle: the handle of the device |
507 | * @handler: the acpi_notifier_handler to call after docking | 600 | * @ops: handlers to call after docking |
508 | * @context: device specific data | 601 | * @context: device specific data |
509 | * | 602 | * |
510 | * If a driver would like to perform a hotplug operation after a dock | 603 | * If a driver would like to perform a hotplug operation after a dock |
@@ -512,27 +605,36 @@ EXPORT_SYMBOL_GPL(unregister_dock_notifier); | |||
512 | * the dock driver after _DCK is executed. | 605 | * the dock driver after _DCK is executed. |
513 | */ | 606 | */ |
514 | int | 607 | int |
515 | register_hotplug_dock_device(acpi_handle handle, acpi_notify_handler handler, | 608 | register_hotplug_dock_device(acpi_handle handle, struct acpi_dock_ops *ops, |
516 | void *context) | 609 | void *context) |
517 | { | 610 | { |
518 | struct dock_dependent_device *dd; | 611 | struct dock_dependent_device *dd; |
612 | struct dock_station *dock_station; | ||
613 | int ret = -EINVAL; | ||
519 | 614 | ||
520 | if (!dock_station) | 615 | if (!dock_station_count) |
521 | return -ENODEV; | 616 | return -ENODEV; |
522 | 617 | ||
523 | /* | 618 | /* |
524 | * make sure this handle is for a device dependent on the dock, | 619 | * make sure this handle is for a device dependent on the dock, |
525 | * this would include the dock station itself | 620 | * this would include the dock station itself |
526 | */ | 621 | */ |
527 | dd = find_dock_dependent_device(dock_station, handle); | 622 | list_for_each_entry(dock_station, &dock_stations, sibiling) { |
528 | if (dd) { | 623 | /* |
529 | dd->handler = handler; | 624 | * An ATA bay can be in a dock and itself can be ejected |
530 | dd->context = context; | 625 | * seperately, so there are two 'dock stations' which need the |
531 | dock_add_hotplug_device(dock_station, dd); | 626 | * ops |
532 | return 0; | 627 | */ |
628 | dd = find_dock_dependent_device(dock_station, handle); | ||
629 | if (dd) { | ||
630 | dd->ops = ops; | ||
631 | dd->context = context; | ||
632 | dock_add_hotplug_device(dock_station, dd); | ||
633 | ret = 0; | ||
634 | } | ||
533 | } | 635 | } |
534 | 636 | ||
535 | return -EINVAL; | 637 | return ret; |
536 | } | 638 | } |
537 | 639 | ||
538 | EXPORT_SYMBOL_GPL(register_hotplug_dock_device); | 640 | EXPORT_SYMBOL_GPL(register_hotplug_dock_device); |
@@ -544,13 +646,16 @@ EXPORT_SYMBOL_GPL(register_hotplug_dock_device); | |||
544 | void unregister_hotplug_dock_device(acpi_handle handle) | 646 | void unregister_hotplug_dock_device(acpi_handle handle) |
545 | { | 647 | { |
546 | struct dock_dependent_device *dd; | 648 | struct dock_dependent_device *dd; |
649 | struct dock_station *dock_station; | ||
547 | 650 | ||
548 | if (!dock_station) | 651 | if (!dock_station_count) |
549 | return; | 652 | return; |
550 | 653 | ||
551 | dd = find_dock_dependent_device(dock_station, handle); | 654 | list_for_each_entry(dock_station, &dock_stations, sibiling) { |
552 | if (dd) | 655 | dd = find_dock_dependent_device(dock_station, handle); |
553 | dock_del_hotplug_device(dock_station, dd); | 656 | if (dd) |
657 | dock_del_hotplug_device(dock_station, dd); | ||
658 | } | ||
554 | } | 659 | } |
555 | 660 | ||
556 | EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device); | 661 | EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device); |
@@ -575,13 +680,9 @@ static int handle_eject_request(struct dock_station *ds, u32 event) | |||
575 | */ | 680 | */ |
576 | dock_event(ds, event, UNDOCK_EVENT); | 681 | dock_event(ds, event, UNDOCK_EVENT); |
577 | 682 | ||
578 | if (!dock_present(ds)) { | ||
579 | complete_undock(ds); | ||
580 | return -ENODEV; | ||
581 | } | ||
582 | |||
583 | hotplug_dock_devices(ds, ACPI_NOTIFY_EJECT_REQUEST); | 683 | hotplug_dock_devices(ds, ACPI_NOTIFY_EJECT_REQUEST); |
584 | undock(ds); | 684 | undock(ds); |
685 | dock_lock(ds, 0); | ||
585 | eject_dock(ds); | 686 | eject_dock(ds); |
586 | if (dock_present(ds)) { | 687 | if (dock_present(ds)) { |
587 | printk(KERN_ERR PREFIX "Unable to undock!\n"); | 688 | printk(KERN_ERR PREFIX "Unable to undock!\n"); |
@@ -604,14 +705,36 @@ static int handle_eject_request(struct dock_station *ds, u32 event) | |||
604 | static void dock_notify(acpi_handle handle, u32 event, void *data) | 705 | static void dock_notify(acpi_handle handle, u32 event, void *data) |
605 | { | 706 | { |
606 | struct dock_station *ds = data; | 707 | struct dock_station *ds = data; |
708 | struct acpi_device *tmp; | ||
709 | int surprise_removal = 0; | ||
710 | |||
711 | /* | ||
712 | * According to acpi spec 3.0a, if a DEVICE_CHECK notification | ||
713 | * is sent and _DCK is present, it is assumed to mean an undock | ||
714 | * request. | ||
715 | */ | ||
716 | if ((ds->flags & DOCK_IS_DOCK) && event == ACPI_NOTIFY_DEVICE_CHECK) | ||
717 | event = ACPI_NOTIFY_EJECT_REQUEST; | ||
607 | 718 | ||
719 | /* | ||
720 | * dock station: BUS_CHECK - docked or surprise removal | ||
721 | * DEVICE_CHECK - undocked | ||
722 | * other device: BUS_CHECK/DEVICE_CHECK - added or surprise removal | ||
723 | * | ||
724 | * To simplify event handling, dock dependent device handler always | ||
725 | * get ACPI_NOTIFY_BUS_CHECK/ACPI_NOTIFY_DEVICE_CHECK for add and | ||
726 | * ACPI_NOTIFY_EJECT_REQUEST for removal | ||
727 | */ | ||
608 | switch (event) { | 728 | switch (event) { |
609 | case ACPI_NOTIFY_BUS_CHECK: | 729 | case ACPI_NOTIFY_BUS_CHECK: |
610 | if (!dock_in_progress(ds) && dock_present(ds)) { | 730 | case ACPI_NOTIFY_DEVICE_CHECK: |
731 | if (!dock_in_progress(ds) && acpi_bus_get_device(ds->handle, | ||
732 | &tmp)) { | ||
611 | begin_dock(ds); | 733 | begin_dock(ds); |
612 | dock(ds); | 734 | dock(ds); |
613 | if (!dock_present(ds)) { | 735 | if (!dock_present(ds)) { |
614 | printk(KERN_ERR PREFIX "Unable to dock!\n"); | 736 | printk(KERN_ERR PREFIX "Unable to dock!\n"); |
737 | complete_dock(ds); | ||
615 | break; | 738 | break; |
616 | } | 739 | } |
617 | atomic_notifier_call_chain(&dock_notifier_list, | 740 | atomic_notifier_call_chain(&dock_notifier_list, |
@@ -619,20 +742,19 @@ static void dock_notify(acpi_handle handle, u32 event, void *data) | |||
619 | hotplug_dock_devices(ds, event); | 742 | hotplug_dock_devices(ds, event); |
620 | complete_dock(ds); | 743 | complete_dock(ds); |
621 | dock_event(ds, event, DOCK_EVENT); | 744 | dock_event(ds, event, DOCK_EVENT); |
745 | dock_lock(ds, 1); | ||
746 | break; | ||
622 | } | 747 | } |
623 | break; | 748 | if (dock_present(ds) || dock_in_progress(ds)) |
624 | case ACPI_NOTIFY_DEVICE_CHECK: | 749 | break; |
625 | /* | 750 | /* This is a surprise removal */ |
626 | * According to acpi spec 3.0a, if a DEVICE_CHECK notification | 751 | surprise_removal = 1; |
627 | * is sent and _DCK is present, it is assumed to mean an | 752 | event = ACPI_NOTIFY_EJECT_REQUEST; |
628 | * undock request. This notify routine will only be called | 753 | /* Fall back */ |
629 | * for objects defining _DCK, so we will fall through to eject | ||
630 | * request here. However, we will pass an eject request through | ||
631 | * to the driver who wish to hotplug. | ||
632 | */ | ||
633 | case ACPI_NOTIFY_EJECT_REQUEST: | 754 | case ACPI_NOTIFY_EJECT_REQUEST: |
634 | begin_undock(ds); | 755 | begin_undock(ds); |
635 | if (immediate_undock) | 756 | if ((immediate_undock && !(ds->flags & DOCK_IS_ATA)) |
757 | || surprise_removal) | ||
636 | handle_eject_request(ds, event); | 758 | handle_eject_request(ds, event); |
637 | else | 759 | else |
638 | dock_event(ds, event, UNDOCK_EVENT); | 760 | dock_event(ds, event, UNDOCK_EVENT); |
@@ -642,6 +764,51 @@ static void dock_notify(acpi_handle handle, u32 event, void *data) | |||
642 | } | 764 | } |
643 | } | 765 | } |
644 | 766 | ||
767 | struct dock_data { | ||
768 | acpi_handle handle; | ||
769 | unsigned long event; | ||
770 | struct dock_station *ds; | ||
771 | }; | ||
772 | |||
773 | static void acpi_dock_deferred_cb(void *context) | ||
774 | { | ||
775 | struct dock_data *data = (struct dock_data *)context; | ||
776 | |||
777 | dock_notify(data->handle, data->event, data->ds); | ||
778 | kfree(data); | ||
779 | } | ||
780 | |||
781 | static int acpi_dock_notifier_call(struct notifier_block *this, | ||
782 | unsigned long event, void *data) | ||
783 | { | ||
784 | struct dock_station *dock_station; | ||
785 | acpi_handle handle = (acpi_handle)data; | ||
786 | |||
787 | if (event != ACPI_NOTIFY_BUS_CHECK && event != ACPI_NOTIFY_DEVICE_CHECK | ||
788 | && event != ACPI_NOTIFY_EJECT_REQUEST) | ||
789 | return 0; | ||
790 | list_for_each_entry(dock_station, &dock_stations, sibiling) { | ||
791 | if (dock_station->handle == handle) { | ||
792 | struct dock_data *dock_data; | ||
793 | |||
794 | dock_data = kmalloc(sizeof(*dock_data), GFP_KERNEL); | ||
795 | if (!dock_data) | ||
796 | return 0; | ||
797 | dock_data->handle = handle; | ||
798 | dock_data->event = event; | ||
799 | dock_data->ds = dock_station; | ||
800 | acpi_os_hotplug_execute(acpi_dock_deferred_cb, | ||
801 | dock_data); | ||
802 | return 0 ; | ||
803 | } | ||
804 | } | ||
805 | return 0; | ||
806 | } | ||
807 | |||
808 | static struct notifier_block dock_acpi_notifier = { | ||
809 | .notifier_call = acpi_dock_notifier_call, | ||
810 | }; | ||
811 | |||
645 | /** | 812 | /** |
646 | * find_dock_devices - find devices on the dock station | 813 | * find_dock_devices - find devices on the dock station |
647 | * @handle: the handle of the device we are examining | 814 | * @handle: the handle of the device we are examining |
@@ -688,6 +855,8 @@ fdd_out: | |||
688 | static ssize_t show_docked(struct device *dev, | 855 | static ssize_t show_docked(struct device *dev, |
689 | struct device_attribute *attr, char *buf) | 856 | struct device_attribute *attr, char *buf) |
690 | { | 857 | { |
858 | struct dock_station *dock_station = *((struct dock_station **) | ||
859 | dev->platform_data); | ||
691 | return snprintf(buf, PAGE_SIZE, "%d\n", dock_present(dock_station)); | 860 | return snprintf(buf, PAGE_SIZE, "%d\n", dock_present(dock_station)); |
692 | 861 | ||
693 | } | 862 | } |
@@ -699,6 +868,8 @@ static DEVICE_ATTR(docked, S_IRUGO, show_docked, NULL); | |||
699 | static ssize_t show_flags(struct device *dev, | 868 | static ssize_t show_flags(struct device *dev, |
700 | struct device_attribute *attr, char *buf) | 869 | struct device_attribute *attr, char *buf) |
701 | { | 870 | { |
871 | struct dock_station *dock_station = *((struct dock_station **) | ||
872 | dev->platform_data); | ||
702 | return snprintf(buf, PAGE_SIZE, "%d\n", dock_station->flags); | 873 | return snprintf(buf, PAGE_SIZE, "%d\n", dock_station->flags); |
703 | 874 | ||
704 | } | 875 | } |
@@ -711,6 +882,8 @@ static ssize_t write_undock(struct device *dev, struct device_attribute *attr, | |||
711 | const char *buf, size_t count) | 882 | const char *buf, size_t count) |
712 | { | 883 | { |
713 | int ret; | 884 | int ret; |
885 | struct dock_station *dock_station = *((struct dock_station **) | ||
886 | dev->platform_data); | ||
714 | 887 | ||
715 | if (!count) | 888 | if (!count) |
716 | return -EINVAL; | 889 | return -EINVAL; |
@@ -727,16 +900,38 @@ static DEVICE_ATTR(undock, S_IWUSR, NULL, write_undock); | |||
727 | static ssize_t show_dock_uid(struct device *dev, | 900 | static ssize_t show_dock_uid(struct device *dev, |
728 | struct device_attribute *attr, char *buf) | 901 | struct device_attribute *attr, char *buf) |
729 | { | 902 | { |
730 | unsigned long lbuf; | 903 | unsigned long long lbuf; |
904 | struct dock_station *dock_station = *((struct dock_station **) | ||
905 | dev->platform_data); | ||
731 | acpi_status status = acpi_evaluate_integer(dock_station->handle, | 906 | acpi_status status = acpi_evaluate_integer(dock_station->handle, |
732 | "_UID", NULL, &lbuf); | 907 | "_UID", NULL, &lbuf); |
733 | if (ACPI_FAILURE(status)) | 908 | if (ACPI_FAILURE(status)) |
734 | return 0; | 909 | return 0; |
735 | 910 | ||
736 | return snprintf(buf, PAGE_SIZE, "%lx\n", lbuf); | 911 | return snprintf(buf, PAGE_SIZE, "%llx\n", lbuf); |
737 | } | 912 | } |
738 | static DEVICE_ATTR(uid, S_IRUGO, show_dock_uid, NULL); | 913 | static DEVICE_ATTR(uid, S_IRUGO, show_dock_uid, NULL); |
739 | 914 | ||
915 | static ssize_t show_dock_type(struct device *dev, | ||
916 | struct device_attribute *attr, char *buf) | ||
917 | { | ||
918 | struct dock_station *dock_station = *((struct dock_station **) | ||
919 | dev->platform_data); | ||
920 | char *type; | ||
921 | |||
922 | if (dock_station->flags & DOCK_IS_DOCK) | ||
923 | type = "dock_station"; | ||
924 | else if (dock_station->flags & DOCK_IS_ATA) | ||
925 | type = "ata_bay"; | ||
926 | else if (dock_station->flags & DOCK_IS_BAT) | ||
927 | type = "battery_bay"; | ||
928 | else | ||
929 | type = "unknown"; | ||
930 | |||
931 | return snprintf(buf, PAGE_SIZE, "%s\n", type); | ||
932 | } | ||
933 | static DEVICE_ATTR(type, S_IRUGO, show_dock_type, NULL); | ||
934 | |||
740 | /** | 935 | /** |
741 | * dock_add - add a new dock station | 936 | * dock_add - add a new dock station |
742 | * @handle: the dock station handle | 937 | * @handle: the dock station handle |
@@ -747,8 +942,9 @@ static DEVICE_ATTR(uid, S_IRUGO, show_dock_uid, NULL); | |||
747 | static int dock_add(acpi_handle handle) | 942 | static int dock_add(acpi_handle handle) |
748 | { | 943 | { |
749 | int ret; | 944 | int ret; |
750 | acpi_status status; | ||
751 | struct dock_dependent_device *dd; | 945 | struct dock_dependent_device *dd; |
946 | struct dock_station *dock_station; | ||
947 | struct platform_device *dock_device; | ||
752 | 948 | ||
753 | /* allocate & initialize the dock_station private data */ | 949 | /* allocate & initialize the dock_station private data */ |
754 | dock_station = kzalloc(sizeof(*dock_station), GFP_KERNEL); | 950 | dock_station = kzalloc(sizeof(*dock_station), GFP_KERNEL); |
@@ -758,22 +954,34 @@ static int dock_add(acpi_handle handle) | |||
758 | dock_station->last_dock_time = jiffies - HZ; | 954 | dock_station->last_dock_time = jiffies - HZ; |
759 | INIT_LIST_HEAD(&dock_station->dependent_devices); | 955 | INIT_LIST_HEAD(&dock_station->dependent_devices); |
760 | INIT_LIST_HEAD(&dock_station->hotplug_devices); | 956 | INIT_LIST_HEAD(&dock_station->hotplug_devices); |
957 | INIT_LIST_HEAD(&dock_station->sibiling); | ||
761 | spin_lock_init(&dock_station->dd_lock); | 958 | spin_lock_init(&dock_station->dd_lock); |
762 | mutex_init(&dock_station->hp_lock); | 959 | mutex_init(&dock_station->hp_lock); |
763 | ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list); | 960 | ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list); |
764 | 961 | ||
765 | /* initialize platform device stuff */ | 962 | /* initialize platform device stuff */ |
766 | dock_device = | 963 | dock_station->dock_device = |
767 | platform_device_register_simple(dock_device_name, 0, NULL, 0); | 964 | platform_device_register_simple(dock_device_name, |
965 | dock_station_count, NULL, 0); | ||
966 | dock_device = dock_station->dock_device; | ||
768 | if (IS_ERR(dock_device)) { | 967 | if (IS_ERR(dock_device)) { |
769 | kfree(dock_station); | 968 | kfree(dock_station); |
770 | dock_station = NULL; | 969 | dock_station = NULL; |
771 | return PTR_ERR(dock_device); | 970 | return PTR_ERR(dock_device); |
772 | } | 971 | } |
972 | platform_device_add_data(dock_device, &dock_station, | ||
973 | sizeof(struct dock_station *)); | ||
773 | 974 | ||
774 | /* we want the dock device to send uevents */ | 975 | /* we want the dock device to send uevents */ |
775 | dock_device->dev.uevent_suppress = 0; | 976 | dock_device->dev.uevent_suppress = 0; |
776 | 977 | ||
978 | if (is_dock(handle)) | ||
979 | dock_station->flags |= DOCK_IS_DOCK; | ||
980 | if (is_ata(handle)) | ||
981 | dock_station->flags |= DOCK_IS_ATA; | ||
982 | if (is_battery(handle)) | ||
983 | dock_station->flags |= DOCK_IS_BAT; | ||
984 | |||
777 | ret = device_create_file(&dock_device->dev, &dev_attr_docked); | 985 | ret = device_create_file(&dock_device->dev, &dev_attr_docked); |
778 | if (ret) { | 986 | if (ret) { |
779 | printk("Error %d adding sysfs file\n", ret); | 987 | printk("Error %d adding sysfs file\n", ret); |
@@ -812,6 +1020,9 @@ static int dock_add(acpi_handle handle) | |||
812 | dock_station = NULL; | 1020 | dock_station = NULL; |
813 | return ret; | 1021 | return ret; |
814 | } | 1022 | } |
1023 | ret = device_create_file(&dock_device->dev, &dev_attr_type); | ||
1024 | if (ret) | ||
1025 | printk(KERN_ERR"Error %d adding sysfs file\n", ret); | ||
815 | 1026 | ||
816 | /* Find dependent devices */ | 1027 | /* Find dependent devices */ |
817 | acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, | 1028 | acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, |
@@ -828,24 +1039,12 @@ static int dock_add(acpi_handle handle) | |||
828 | } | 1039 | } |
829 | add_dock_dependent_device(dock_station, dd); | 1040 | add_dock_dependent_device(dock_station, dd); |
830 | 1041 | ||
831 | /* register for dock events */ | 1042 | dock_station_count++; |
832 | status = acpi_install_notify_handler(dock_station->handle, | 1043 | list_add(&dock_station->sibiling, &dock_stations); |
833 | ACPI_SYSTEM_NOTIFY, | ||
834 | dock_notify, dock_station); | ||
835 | |||
836 | if (ACPI_FAILURE(status)) { | ||
837 | printk(KERN_ERR PREFIX "Error installing notify handler\n"); | ||
838 | ret = -ENODEV; | ||
839 | goto dock_add_err; | ||
840 | } | ||
841 | |||
842 | printk(KERN_INFO PREFIX "%s\n", ACPI_DOCK_DRIVER_DESCRIPTION); | ||
843 | |||
844 | return 0; | 1044 | return 0; |
845 | 1045 | ||
846 | dock_add_err: | ||
847 | kfree(dd); | ||
848 | dock_add_err_unregister: | 1046 | dock_add_err_unregister: |
1047 | device_remove_file(&dock_device->dev, &dev_attr_type); | ||
849 | device_remove_file(&dock_device->dev, &dev_attr_docked); | 1048 | device_remove_file(&dock_device->dev, &dev_attr_docked); |
850 | device_remove_file(&dock_device->dev, &dev_attr_undock); | 1049 | device_remove_file(&dock_device->dev, &dev_attr_undock); |
851 | device_remove_file(&dock_device->dev, &dev_attr_uid); | 1050 | device_remove_file(&dock_device->dev, &dev_attr_uid); |
@@ -859,12 +1058,12 @@ dock_add_err_unregister: | |||
859 | /** | 1058 | /** |
860 | * dock_remove - free up resources related to the dock station | 1059 | * dock_remove - free up resources related to the dock station |
861 | */ | 1060 | */ |
862 | static int dock_remove(void) | 1061 | static int dock_remove(struct dock_station *dock_station) |
863 | { | 1062 | { |
864 | struct dock_dependent_device *dd, *tmp; | 1063 | struct dock_dependent_device *dd, *tmp; |
865 | acpi_status status; | 1064 | struct platform_device *dock_device = dock_station->dock_device; |
866 | 1065 | ||
867 | if (!dock_station) | 1066 | if (!dock_station_count) |
868 | return 0; | 1067 | return 0; |
869 | 1068 | ||
870 | /* remove dependent devices */ | 1069 | /* remove dependent devices */ |
@@ -872,14 +1071,8 @@ static int dock_remove(void) | |||
872 | list) | 1071 | list) |
873 | kfree(dd); | 1072 | kfree(dd); |
874 | 1073 | ||
875 | /* remove dock notify handler */ | ||
876 | status = acpi_remove_notify_handler(dock_station->handle, | ||
877 | ACPI_SYSTEM_NOTIFY, | ||
878 | dock_notify); | ||
879 | if (ACPI_FAILURE(status)) | ||
880 | printk(KERN_ERR "Error removing notify handler\n"); | ||
881 | |||
882 | /* cleanup sysfs */ | 1074 | /* cleanup sysfs */ |
1075 | device_remove_file(&dock_device->dev, &dev_attr_type); | ||
883 | device_remove_file(&dock_device->dev, &dev_attr_docked); | 1076 | device_remove_file(&dock_device->dev, &dev_attr_docked); |
884 | device_remove_file(&dock_device->dev, &dev_attr_undock); | 1077 | device_remove_file(&dock_device->dev, &dev_attr_undock); |
885 | device_remove_file(&dock_device->dev, &dev_attr_uid); | 1078 | device_remove_file(&dock_device->dev, &dev_attr_uid); |
@@ -904,41 +1097,60 @@ static int dock_remove(void) | |||
904 | static acpi_status | 1097 | static acpi_status |
905 | find_dock(acpi_handle handle, u32 lvl, void *context, void **rv) | 1098 | find_dock(acpi_handle handle, u32 lvl, void *context, void **rv) |
906 | { | 1099 | { |
907 | int *count = context; | ||
908 | acpi_status status = AE_OK; | 1100 | acpi_status status = AE_OK; |
909 | 1101 | ||
910 | if (is_dock(handle)) { | 1102 | if (is_dock(handle)) { |
911 | if (dock_add(handle) >= 0) { | 1103 | if (dock_add(handle) >= 0) { |
912 | (*count)++; | ||
913 | status = AE_CTRL_TERMINATE; | 1104 | status = AE_CTRL_TERMINATE; |
914 | } | 1105 | } |
915 | } | 1106 | } |
916 | return status; | 1107 | return status; |
917 | } | 1108 | } |
918 | 1109 | ||
919 | static int __init dock_init(void) | 1110 | static acpi_status |
1111 | find_bay(acpi_handle handle, u32 lvl, void *context, void **rv) | ||
920 | { | 1112 | { |
921 | int num = 0; | 1113 | /* If bay is a dock, it's already handled */ |
922 | 1114 | if (is_ejectable_bay(handle) && !is_dock(handle)) | |
923 | dock_station = NULL; | 1115 | dock_add(handle); |
1116 | return AE_OK; | ||
1117 | } | ||
924 | 1118 | ||
1119 | static int __init dock_init(void) | ||
1120 | { | ||
925 | if (acpi_disabled) | 1121 | if (acpi_disabled) |
926 | return 0; | 1122 | return 0; |
927 | 1123 | ||
928 | /* look for a dock station */ | 1124 | /* look for a dock station */ |
929 | acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, | 1125 | acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, |
930 | ACPI_UINT32_MAX, find_dock, &num, NULL); | 1126 | ACPI_UINT32_MAX, find_dock, NULL, NULL); |
931 | 1127 | ||
932 | if (!num) | 1128 | /* look for bay */ |
933 | printk(KERN_INFO "No dock devices found.\n"); | 1129 | acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, |
1130 | ACPI_UINT32_MAX, find_bay, NULL, NULL); | ||
1131 | if (!dock_station_count) { | ||
1132 | printk(KERN_INFO PREFIX "No dock devices found.\n"); | ||
1133 | return 0; | ||
1134 | } | ||
934 | 1135 | ||
1136 | register_acpi_bus_notifier(&dock_acpi_notifier); | ||
1137 | printk(KERN_INFO PREFIX "%s: %d docks/bays found\n", | ||
1138 | ACPI_DOCK_DRIVER_DESCRIPTION, dock_station_count); | ||
935 | return 0; | 1139 | return 0; |
936 | } | 1140 | } |
937 | 1141 | ||
938 | static void __exit dock_exit(void) | 1142 | static void __exit dock_exit(void) |
939 | { | 1143 | { |
940 | dock_remove(); | 1144 | struct dock_station *dock_station; |
1145 | |||
1146 | unregister_acpi_bus_notifier(&dock_acpi_notifier); | ||
1147 | list_for_each_entry(dock_station, &dock_stations, sibiling) | ||
1148 | dock_remove(dock_station); | ||
941 | } | 1149 | } |
942 | 1150 | ||
943 | postcore_initcall(dock_init); | 1151 | /* |
1152 | * Must be called before drivers of devices in dock, otherwise we can't know | ||
1153 | * which devices are in a dock | ||
1154 | */ | ||
1155 | subsys_initcall(dock_init); | ||
944 | module_exit(dock_exit); | 1156 | module_exit(dock_exit); |