diff options
| -rw-r--r-- | drivers/acpi/dock.c | 131 |
1 files changed, 110 insertions, 21 deletions
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index bf5b79ed3613..215f5b30a1f1 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c | |||
| @@ -27,6 +27,7 @@ | |||
| 27 | #include <linux/init.h> | 27 | #include <linux/init.h> |
| 28 | #include <linux/types.h> | 28 | #include <linux/types.h> |
| 29 | #include <linux/notifier.h> | 29 | #include <linux/notifier.h> |
| 30 | #include <linux/platform_device.h> | ||
| 30 | #include <linux/jiffies.h> | 31 | #include <linux/jiffies.h> |
| 31 | #include <acpi/acpi_bus.h> | 32 | #include <acpi/acpi_bus.h> |
| 32 | #include <acpi/acpi_drivers.h> | 33 | #include <acpi/acpi_drivers.h> |
| @@ -39,6 +40,8 @@ MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_NAME); | |||
| 39 | MODULE_LICENSE("GPL"); | 40 | MODULE_LICENSE("GPL"); |
| 40 | 41 | ||
| 41 | static struct atomic_notifier_head dock_notifier_list; | 42 | static struct atomic_notifier_head dock_notifier_list; |
| 43 | static struct platform_device dock_device; | ||
| 44 | static char dock_device_name[] = "dock"; | ||
| 42 | 45 | ||
| 43 | struct dock_station { | 46 | struct dock_station { |
| 44 | acpi_handle handle; | 47 | acpi_handle handle; |
| @@ -323,10 +326,12 @@ static void hotplug_dock_devices(struct dock_station *ds, u32 event) | |||
| 323 | 326 | ||
| 324 | static void dock_event(struct dock_station *ds, u32 event, int num) | 327 | static void dock_event(struct dock_station *ds, u32 event, int num) |
| 325 | { | 328 | { |
| 329 | struct device *dev = &dock_device.dev; | ||
| 326 | /* | 330 | /* |
| 327 | * we don't do events until someone tells me that | 331 | * Indicate that the status of the dock station has |
| 328 | * they would like to have them. | 332 | * changed. |
| 329 | */ | 333 | */ |
| 334 | kobject_uevent(&dev->kobj, KOBJ_CHANGE); | ||
| 330 | } | 335 | } |
| 331 | 336 | ||
| 332 | /** | 337 | /** |
| @@ -441,6 +446,9 @@ static int dock_in_progress(struct dock_station *ds) | |||
| 441 | */ | 446 | */ |
| 442 | int register_dock_notifier(struct notifier_block *nb) | 447 | int register_dock_notifier(struct notifier_block *nb) |
| 443 | { | 448 | { |
| 449 | if (!dock_station) | ||
| 450 | return -ENODEV; | ||
| 451 | |||
| 444 | return atomic_notifier_chain_register(&dock_notifier_list, nb); | 452 | return atomic_notifier_chain_register(&dock_notifier_list, nb); |
| 445 | } | 453 | } |
| 446 | 454 | ||
| @@ -452,6 +460,9 @@ EXPORT_SYMBOL_GPL(register_dock_notifier); | |||
| 452 | */ | 460 | */ |
| 453 | void unregister_dock_notifier(struct notifier_block *nb) | 461 | void unregister_dock_notifier(struct notifier_block *nb) |
| 454 | { | 462 | { |
| 463 | if (!dock_station) | ||
| 464 | return; | ||
| 465 | |||
| 455 | atomic_notifier_chain_unregister(&dock_notifier_list, nb); | 466 | atomic_notifier_chain_unregister(&dock_notifier_list, nb); |
| 456 | } | 467 | } |
| 457 | 468 | ||
| @@ -512,6 +523,37 @@ void unregister_hotplug_dock_device(acpi_handle handle) | |||
| 512 | EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device); | 523 | EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device); |
| 513 | 524 | ||
| 514 | /** | 525 | /** |
| 526 | * handle_eject_request - handle an undock request checking for error conditions | ||
| 527 | * | ||
| 528 | * Check to make sure the dock device is still present, then undock and | ||
| 529 | * hotremove all the devices that may need removing. | ||
| 530 | */ | ||
| 531 | static int handle_eject_request(struct dock_station *ds, u32 event) | ||
| 532 | { | ||
| 533 | if (!dock_present(ds)) | ||
| 534 | return -ENODEV; | ||
| 535 | |||
| 536 | if (dock_in_progress(ds)) | ||
| 537 | return -EBUSY; | ||
| 538 | |||
| 539 | /* | ||
| 540 | * here we need to generate the undock | ||
| 541 | * event prior to actually doing the undock | ||
| 542 | * so that the device struct still exists. | ||
| 543 | */ | ||
| 544 | dock_event(ds, event, UNDOCK_EVENT); | ||
| 545 | hotplug_dock_devices(ds, ACPI_NOTIFY_EJECT_REQUEST); | ||
| 546 | undock(ds); | ||
| 547 | eject_dock(ds); | ||
| 548 | if (dock_present(ds)) { | ||
| 549 | printk(KERN_ERR PREFIX "Unable to undock!\n"); | ||
| 550 | return -EBUSY; | ||
| 551 | } | ||
| 552 | |||
| 553 | return 0; | ||
| 554 | } | ||
| 555 | |||
| 556 | /** | ||
| 515 | * dock_notify - act upon an acpi dock notification | 557 | * dock_notify - act upon an acpi dock notification |
| 516 | * @handle: the dock station handle | 558 | * @handle: the dock station handle |
| 517 | * @event: the acpi event | 559 | * @event: the acpi event |
| @@ -519,9 +561,7 @@ EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device); | |||
| 519 | * | 561 | * |
| 520 | * If we are notified to dock, then check to see if the dock is | 562 | * If we are notified to dock, then check to see if the dock is |
| 521 | * present and then dock. Notify all drivers of the dock event, | 563 | * present and then dock. Notify all drivers of the dock event, |
| 522 | * and then hotplug and devices that may need hotplugging. For undock | 564 | * and then hotplug and devices that may need hotplugging. |
| 523 | * check to make sure the dock device is still present, then undock | ||
| 524 | * and hotremove all the devices that may need removing. | ||
| 525 | */ | 565 | */ |
| 526 | static void dock_notify(acpi_handle handle, u32 event, void *data) | 566 | static void dock_notify(acpi_handle handle, u32 event, void *data) |
| 527 | { | 567 | { |
| @@ -553,19 +593,7 @@ static void dock_notify(acpi_handle handle, u32 event, void *data) | |||
| 553 | * to the driver who wish to hotplug. | 593 | * to the driver who wish to hotplug. |
| 554 | */ | 594 | */ |
| 555 | case ACPI_NOTIFY_EJECT_REQUEST: | 595 | case ACPI_NOTIFY_EJECT_REQUEST: |
| 556 | if (!dock_in_progress(ds) && dock_present(ds)) { | 596 | handle_eject_request(ds, event); |
| 557 | /* | ||
| 558 | * here we need to generate the undock | ||
| 559 | * event prior to actually doing the undock | ||
| 560 | * so that the device struct still exists. | ||
| 561 | */ | ||
| 562 | dock_event(ds, event, UNDOCK_EVENT); | ||
| 563 | hotplug_dock_devices(ds, ACPI_NOTIFY_EJECT_REQUEST); | ||
| 564 | undock(ds); | ||
| 565 | eject_dock(ds); | ||
| 566 | if (dock_present(ds)) | ||
| 567 | printk(KERN_ERR PREFIX "Unable to undock!\n"); | ||
| 568 | } | ||
| 569 | break; | 597 | break; |
| 570 | default: | 598 | default: |
| 571 | printk(KERN_ERR PREFIX "Unknown dock event %d\n", event); | 599 | printk(KERN_ERR PREFIX "Unknown dock event %d\n", event); |
| @@ -604,6 +632,33 @@ find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv) | |||
| 604 | return AE_OK; | 632 | return AE_OK; |
| 605 | } | 633 | } |
| 606 | 634 | ||
| 635 | /* | ||
| 636 | * show_docked - read method for "docked" file in sysfs | ||
| 637 | */ | ||
| 638 | static ssize_t show_docked(struct device *dev, | ||
| 639 | struct device_attribute *attr, char *buf) | ||
| 640 | { | ||
| 641 | return snprintf(buf, PAGE_SIZE, "%d\n", dock_present(dock_station)); | ||
| 642 | |||
| 643 | } | ||
| 644 | DEVICE_ATTR(docked, S_IRUGO, show_docked, NULL); | ||
| 645 | |||
| 646 | /* | ||
| 647 | * write_undock - write method for "undock" file in sysfs | ||
| 648 | */ | ||
| 649 | static ssize_t write_undock(struct device *dev, struct device_attribute *attr, | ||
| 650 | const char *buf, size_t count) | ||
| 651 | { | ||
| 652 | int ret; | ||
| 653 | |||
| 654 | if (!count) | ||
| 655 | return -EINVAL; | ||
| 656 | |||
| 657 | ret = handle_eject_request(dock_station, ACPI_NOTIFY_EJECT_REQUEST); | ||
| 658 | return ret ? ret: count; | ||
| 659 | } | ||
| 660 | DEVICE_ATTR(undock, S_IWUSR, NULL, write_undock); | ||
| 661 | |||
| 607 | /** | 662 | /** |
| 608 | * dock_add - add a new dock station | 663 | * dock_add - add a new dock station |
| 609 | * @handle: the dock station handle | 664 | * @handle: the dock station handle |
| @@ -629,6 +684,30 @@ static int dock_add(acpi_handle handle) | |||
| 629 | spin_lock_init(&dock_station->hp_lock); | 684 | spin_lock_init(&dock_station->hp_lock); |
| 630 | ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list); | 685 | ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list); |
| 631 | 686 | ||
| 687 | /* initialize platform device stuff */ | ||
| 688 | dock_device.name = dock_device_name; | ||
| 689 | ret = platform_device_register(&dock_device); | ||
| 690 | if (ret) { | ||
| 691 | printk(KERN_ERR PREFIX "Error %d registering dock device\n", ret); | ||
| 692 | kfree(dock_station); | ||
| 693 | return ret; | ||
| 694 | } | ||
| 695 | ret = device_create_file(&dock_device.dev, &dev_attr_docked); | ||
| 696 | if (ret) { | ||
| 697 | printk("Error %d adding sysfs file\n", ret); | ||
| 698 | platform_device_unregister(&dock_device); | ||
| 699 | kfree(dock_station); | ||
| 700 | return ret; | ||
| 701 | } | ||
| 702 | ret = device_create_file(&dock_device.dev, &dev_attr_undock); | ||
| 703 | if (ret) { | ||
| 704 | printk("Error %d adding sysfs file\n", ret); | ||
| 705 | device_remove_file(&dock_device.dev, &dev_attr_docked); | ||
| 706 | platform_device_unregister(&dock_device); | ||
| 707 | kfree(dock_station); | ||
| 708 | return ret; | ||
| 709 | } | ||
| 710 | |||
| 632 | /* Find dependent devices */ | 711 | /* Find dependent devices */ |
| 633 | acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, | 712 | acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, |
| 634 | ACPI_UINT32_MAX, find_dock_devices, dock_station, | 713 | ACPI_UINT32_MAX, find_dock_devices, dock_station, |
| @@ -638,7 +717,8 @@ static int dock_add(acpi_handle handle) | |||
| 638 | dd = alloc_dock_dependent_device(handle); | 717 | dd = alloc_dock_dependent_device(handle); |
| 639 | if (!dd) { | 718 | if (!dd) { |
| 640 | kfree(dock_station); | 719 | kfree(dock_station); |
| 641 | return -ENOMEM; | 720 | ret = -ENOMEM; |
| 721 | goto dock_add_err_unregister; | ||
| 642 | } | 722 | } |
| 643 | add_dock_dependent_device(dock_station, dd); | 723 | add_dock_dependent_device(dock_station, dd); |
| 644 | 724 | ||
| @@ -658,8 +738,12 @@ static int dock_add(acpi_handle handle) | |||
| 658 | return 0; | 738 | return 0; |
| 659 | 739 | ||
| 660 | dock_add_err: | 740 | dock_add_err: |
| 661 | kfree(dock_station); | ||
| 662 | kfree(dd); | 741 | kfree(dd); |
| 742 | dock_add_err_unregister: | ||
| 743 | device_remove_file(&dock_device.dev, &dev_attr_docked); | ||
| 744 | device_remove_file(&dock_device.dev, &dev_attr_undock); | ||
| 745 | platform_device_unregister(&dock_device); | ||
| 746 | kfree(dock_station); | ||
| 663 | return ret; | 747 | return ret; |
| 664 | } | 748 | } |
| 665 | 749 | ||
| @@ -686,6 +770,11 @@ static int dock_remove(void) | |||
| 686 | if (ACPI_FAILURE(status)) | 770 | if (ACPI_FAILURE(status)) |
| 687 | printk(KERN_ERR "Error removing notify handler\n"); | 771 | printk(KERN_ERR "Error removing notify handler\n"); |
| 688 | 772 | ||
| 773 | /* cleanup sysfs */ | ||
| 774 | device_remove_file(&dock_device.dev, &dev_attr_docked); | ||
| 775 | device_remove_file(&dock_device.dev, &dev_attr_undock); | ||
| 776 | platform_device_unregister(&dock_device); | ||
| 777 | |||
| 689 | /* free dock station memory */ | 778 | /* free dock station memory */ |
| 690 | kfree(dock_station); | 779 | kfree(dock_station); |
| 691 | return 0; | 780 | return 0; |
| @@ -726,7 +815,7 @@ static int __init dock_init(void) | |||
| 726 | ACPI_UINT32_MAX, find_dock, &num, NULL); | 815 | ACPI_UINT32_MAX, find_dock, &num, NULL); |
| 727 | 816 | ||
| 728 | if (!num) | 817 | if (!num) |
| 729 | return -ENODEV; | 818 | printk(KERN_INFO "No dock devices found.\n"); |
| 730 | 819 | ||
| 731 | return 0; | 820 | return 0; |
| 732 | } | 821 | } |
