diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2013-02-23 16:11:14 -0500 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2013-02-26 02:02:42 -0500 |
commit | d6b0c58048d2c8c6f4955c37f670125b2792cd14 (patch) | |
tree | a22d3680ebcaca7681f647ac0ab050ef1f51c968 /drivers/base | |
parent | 7241443f67bb352183bcfd7e3b807b87f2777b5d (diff) |
devres: allow adding custom actions to the stack
Sometimes drivers need to execute one-off actions in their error handling
or device teardown paths. An example would be toggling a GPIO line to
reset the controlled device into predefined state.
To allow performing such actions when using managed resources let's allow
adding them to stack/group of devres resources.
Acked-by: Tejun Heo <tj@kernel.org>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/devres.c | 74 |
1 files changed, 74 insertions, 0 deletions
diff --git a/drivers/base/devres.c b/drivers/base/devres.c index 8731979d668a..724957a13d48 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c | |||
@@ -671,6 +671,80 @@ int devres_release_group(struct device *dev, void *id) | |||
671 | EXPORT_SYMBOL_GPL(devres_release_group); | 671 | EXPORT_SYMBOL_GPL(devres_release_group); |
672 | 672 | ||
673 | /* | 673 | /* |
674 | * Custom devres actions allow inserting a simple function call | ||
675 | * into the teadown sequence. | ||
676 | */ | ||
677 | |||
678 | struct action_devres { | ||
679 | void *data; | ||
680 | void (*action)(void *); | ||
681 | }; | ||
682 | |||
683 | static int devm_action_match(struct device *dev, void *res, void *p) | ||
684 | { | ||
685 | struct action_devres *devres = res; | ||
686 | struct action_devres *target = p; | ||
687 | |||
688 | return devres->action == target->action && | ||
689 | devres->data == target->data; | ||
690 | } | ||
691 | |||
692 | static void devm_action_release(struct device *dev, void *res) | ||
693 | { | ||
694 | struct action_devres *devres = res; | ||
695 | |||
696 | devres->action(devres->data); | ||
697 | } | ||
698 | |||
699 | /** | ||
700 | * devm_add_action() - add a custom action to list of managed resources | ||
701 | * @dev: Device that owns the action | ||
702 | * @action: Function that should be called | ||
703 | * @data: Pointer to data passed to @action implementation | ||
704 | * | ||
705 | * This adds a custom action to the list of managed resources so that | ||
706 | * it gets executed as part of standard resource unwinding. | ||
707 | */ | ||
708 | int devm_add_action(struct device *dev, void (*action)(void *), void *data) | ||
709 | { | ||
710 | struct action_devres *devres; | ||
711 | |||
712 | devres = devres_alloc(devm_action_release, | ||
713 | sizeof(struct action_devres), GFP_KERNEL); | ||
714 | if (!devres) | ||
715 | return -ENOMEM; | ||
716 | |||
717 | devres->data = data; | ||
718 | devres->action = action; | ||
719 | |||
720 | devres_add(dev, devres); | ||
721 | return 0; | ||
722 | } | ||
723 | EXPORT_SYMBOL_GPL(devm_add_action); | ||
724 | |||
725 | /** | ||
726 | * devm_remove_action() - removes previously added custom action | ||
727 | * @dev: Device that owns the action | ||
728 | * @action: Function implementing the action | ||
729 | * @data: Pointer to data passed to @action implementation | ||
730 | * | ||
731 | * Removes instance of @action previously added by devm_add_action(). | ||
732 | * Both action and data should match one of the existing entries. | ||
733 | */ | ||
734 | void devm_remove_action(struct device *dev, void (*action)(void *), void *data) | ||
735 | { | ||
736 | struct action_devres devres = { | ||
737 | .data = data, | ||
738 | .action = action, | ||
739 | }; | ||
740 | |||
741 | WARN_ON(devres_destroy(dev, devm_action_release, devm_action_match, | ||
742 | &devres)); | ||
743 | |||
744 | } | ||
745 | EXPORT_SYMBOL_GPL(devm_remove_action); | ||
746 | |||
747 | /* | ||
674 | * Managed kzalloc/kfree | 748 | * Managed kzalloc/kfree |
675 | */ | 749 | */ |
676 | static void devm_kzalloc_release(struct device *dev, void *res) | 750 | static void devm_kzalloc_release(struct device *dev, void *res) |