diff options
author | Donggeun Kim <dg77.kim@samsung.com> | 2012-04-20 01:16:24 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-04-20 12:23:09 -0400 |
commit | 74c5d09bd562edc220d6e076b8f1e118819c178f (patch) | |
tree | 65f35b6066fe135405797acc419b4bb6f346e4af | |
parent | be48308a24c7651bf968b561dbd590edb8166d62 (diff) |
Extcon: support notification based on the state changes.
State changes of extcon devices have been notified via kobjet_uevent.
This patch adds notifier interfaces in order to allow device drivers to
get notified easily. Along with notifier interface,
extcon_get_extcon_dev() function is added so that device drivers may
discover a extcon_dev easily.
Signed-off-by: Donggeun Kim <dg77.kim@samsung.com>
Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
--
Changes from RFC
- Renamed switch to extcon
- Bugfix: extcon_dev_unregister()
- Bugfix: "edev->dev" is "internal" data.
- Added kerneldoc comments.
- Reworded comments.
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/extcon/extcon_class.c | 66 | ||||
-rw-r--r-- | include/linux/extcon.h | 38 |
2 files changed, 104 insertions, 0 deletions
diff --git a/drivers/extcon/extcon_class.c b/drivers/extcon/extcon_class.c index 3c46368c279..83a088f1edc 100644 --- a/drivers/extcon/extcon_class.c +++ b/drivers/extcon/extcon_class.c | |||
@@ -36,6 +36,9 @@ struct class *extcon_class; | |||
36 | static struct class_compat *switch_class; | 36 | static struct class_compat *switch_class; |
37 | #endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */ | 37 | #endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */ |
38 | 38 | ||
39 | static LIST_HEAD(extcon_dev_list); | ||
40 | static DEFINE_MUTEX(extcon_dev_list_lock); | ||
41 | |||
39 | static ssize_t state_show(struct device *dev, struct device_attribute *attr, | 42 | static ssize_t state_show(struct device *dev, struct device_attribute *attr, |
40 | char *buf) | 43 | char *buf) |
41 | { | 44 | { |
@@ -75,6 +78,9 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr, | |||
75 | * the name of extcon device (envp[0]) and the state output (envp[1]). | 78 | * the name of extcon device (envp[0]) and the state output (envp[1]). |
76 | * Tizen uses this format for extcon device to get events from ports. | 79 | * Tizen uses this format for extcon device to get events from ports. |
77 | * Android uses this format as well. | 80 | * Android uses this format as well. |
81 | * | ||
82 | * Note that notifier provides the which bits are changes in the state | ||
83 | * variable with "val" to the callback. | ||
78 | */ | 84 | */ |
79 | void extcon_set_state(struct extcon_dev *edev, u32 state) | 85 | void extcon_set_state(struct extcon_dev *edev, u32 state) |
80 | { | 86 | { |
@@ -84,10 +90,14 @@ void extcon_set_state(struct extcon_dev *edev, u32 state) | |||
84 | char *envp[3]; | 90 | char *envp[3]; |
85 | int env_offset = 0; | 91 | int env_offset = 0; |
86 | int length; | 92 | int length; |
93 | u32 old_state = edev->state; | ||
87 | 94 | ||
88 | if (edev->state != state) { | 95 | if (edev->state != state) { |
89 | edev->state = state; | 96 | edev->state = state; |
90 | 97 | ||
98 | raw_notifier_call_chain(&edev->nh, old_state ^ edev->state, | ||
99 | edev); | ||
100 | |||
91 | prop_buf = (char *)get_zeroed_page(GFP_KERNEL); | 101 | prop_buf = (char *)get_zeroed_page(GFP_KERNEL); |
92 | if (prop_buf) { | 102 | if (prop_buf) { |
93 | length = name_show(edev->dev, NULL, prop_buf); | 103 | length = name_show(edev->dev, NULL, prop_buf); |
@@ -117,6 +127,51 @@ void extcon_set_state(struct extcon_dev *edev, u32 state) | |||
117 | } | 127 | } |
118 | EXPORT_SYMBOL_GPL(extcon_set_state); | 128 | EXPORT_SYMBOL_GPL(extcon_set_state); |
119 | 129 | ||
130 | /** | ||
131 | * extcon_get_extcon_dev() - Get the extcon device instance from the name | ||
132 | * @extcon_name: The extcon name provided with extcon_dev_register() | ||
133 | */ | ||
134 | struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name) | ||
135 | { | ||
136 | struct extcon_dev *sd; | ||
137 | |||
138 | mutex_lock(&extcon_dev_list_lock); | ||
139 | list_for_each_entry(sd, &extcon_dev_list, entry) { | ||
140 | if (!strcmp(sd->name, extcon_name)) | ||
141 | goto out; | ||
142 | } | ||
143 | sd = NULL; | ||
144 | out: | ||
145 | mutex_unlock(&extcon_dev_list_lock); | ||
146 | return sd; | ||
147 | } | ||
148 | EXPORT_SYMBOL_GPL(extcon_get_extcon_dev); | ||
149 | |||
150 | /** | ||
151 | * extcon_register_notifier() - Register a notifee to get notified by | ||
152 | * any attach status changes from the extcon. | ||
153 | * @edev: the extcon device. | ||
154 | * @nb: a notifier block to be registered. | ||
155 | */ | ||
156 | int extcon_register_notifier(struct extcon_dev *edev, | ||
157 | struct notifier_block *nb) | ||
158 | { | ||
159 | return raw_notifier_chain_register(&edev->nh, nb); | ||
160 | } | ||
161 | EXPORT_SYMBOL_GPL(extcon_register_notifier); | ||
162 | |||
163 | /** | ||
164 | * extcon_unregister_notifier() - Unregister a notifee from the extcon device. | ||
165 | * @edev: the extcon device. | ||
166 | * @nb: a registered notifier block to be unregistered. | ||
167 | */ | ||
168 | int extcon_unregister_notifier(struct extcon_dev *edev, | ||
169 | struct notifier_block *nb) | ||
170 | { | ||
171 | return raw_notifier_chain_unregister(&edev->nh, nb); | ||
172 | } | ||
173 | EXPORT_SYMBOL_GPL(extcon_unregister_notifier); | ||
174 | |||
120 | static struct device_attribute extcon_attrs[] = { | 175 | static struct device_attribute extcon_attrs[] = { |
121 | __ATTR_RO(state), | 176 | __ATTR_RO(state), |
122 | __ATTR_RO(name), | 177 | __ATTR_RO(name), |
@@ -142,6 +197,10 @@ static int create_extcon_class(void) | |||
142 | 197 | ||
143 | static void extcon_cleanup(struct extcon_dev *edev, bool skip) | 198 | static void extcon_cleanup(struct extcon_dev *edev, bool skip) |
144 | { | 199 | { |
200 | mutex_lock(&extcon_dev_list_lock); | ||
201 | list_del(&edev->entry); | ||
202 | mutex_unlock(&extcon_dev_list_lock); | ||
203 | |||
145 | if (!skip && get_device(edev->dev)) { | 204 | if (!skip && get_device(edev->dev)) { |
146 | device_unregister(edev->dev); | 205 | device_unregister(edev->dev); |
147 | put_device(edev->dev); | 206 | put_device(edev->dev); |
@@ -194,8 +253,15 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev) | |||
194 | dev); | 253 | dev); |
195 | #endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */ | 254 | #endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */ |
196 | 255 | ||
256 | RAW_INIT_NOTIFIER_HEAD(&edev->nh); | ||
257 | |||
197 | dev_set_drvdata(edev->dev, edev); | 258 | dev_set_drvdata(edev->dev, edev); |
198 | edev->state = 0; | 259 | edev->state = 0; |
260 | |||
261 | mutex_lock(&extcon_dev_list_lock); | ||
262 | list_add(&edev->entry, &extcon_dev_list); | ||
263 | mutex_unlock(&extcon_dev_list_lock); | ||
264 | |||
199 | return 0; | 265 | return 0; |
200 | 266 | ||
201 | err_dev: | 267 | err_dev: |
diff --git a/include/linux/extcon.h b/include/linux/extcon.h index 9cb3aaaf67f..c9c9afe12b4 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h | |||
@@ -23,6 +23,7 @@ | |||
23 | #ifndef __LINUX_EXTCON_H__ | 23 | #ifndef __LINUX_EXTCON_H__ |
24 | #define __LINUX_EXTCON_H__ | 24 | #define __LINUX_EXTCON_H__ |
25 | 25 | ||
26 | #include <linux/notifier.h> | ||
26 | /** | 27 | /** |
27 | * struct extcon_dev - An extcon device represents one external connector. | 28 | * struct extcon_dev - An extcon device represents one external connector. |
28 | * @name The name of this extcon device. Parent device name is used | 29 | * @name The name of this extcon device. Parent device name is used |
@@ -34,6 +35,9 @@ | |||
34 | * @dev Device of this extcon. Do not provide at register-time. | 35 | * @dev Device of this extcon. Do not provide at register-time. |
35 | * @state Attach/detach state of this extcon. Do not provide at | 36 | * @state Attach/detach state of this extcon. Do not provide at |
36 | * register-time | 37 | * register-time |
38 | * @nh Notifier for the state change events from this extcon | ||
39 | * @entry To support list of extcon devices so that uses can search | ||
40 | * for extcon devices based on the extcon name. | ||
37 | * | 41 | * |
38 | * In most cases, users only need to provide "User initializing data" of | 42 | * In most cases, users only need to provide "User initializing data" of |
39 | * this struct when registering an extcon. In some exceptional cases, | 43 | * this struct when registering an extcon. In some exceptional cases, |
@@ -51,11 +55,19 @@ struct extcon_dev { | |||
51 | /* --- Internal data. Please do not set. --- */ | 55 | /* --- Internal data. Please do not set. --- */ |
52 | struct device *dev; | 56 | struct device *dev; |
53 | u32 state; | 57 | u32 state; |
58 | struct raw_notifier_head nh; | ||
59 | struct list_head entry; | ||
54 | }; | 60 | }; |
55 | 61 | ||
56 | #if IS_ENABLED(CONFIG_EXTCON) | 62 | #if IS_ENABLED(CONFIG_EXTCON) |
63 | |||
64 | /* | ||
65 | * Following APIs are for notifiers or configurations. | ||
66 | * Notifiers are the external port and connection devices. | ||
67 | */ | ||
57 | extern int extcon_dev_register(struct extcon_dev *edev, struct device *dev); | 68 | extern int extcon_dev_register(struct extcon_dev *edev, struct device *dev); |
58 | extern void extcon_dev_unregister(struct extcon_dev *edev); | 69 | extern void extcon_dev_unregister(struct extcon_dev *edev); |
70 | extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name); | ||
59 | 71 | ||
60 | static inline u32 extcon_get_state(struct extcon_dev *edev) | 72 | static inline u32 extcon_get_state(struct extcon_dev *edev) |
61 | { | 73 | { |
@@ -63,6 +75,15 @@ static inline u32 extcon_get_state(struct extcon_dev *edev) | |||
63 | } | 75 | } |
64 | 76 | ||
65 | extern void extcon_set_state(struct extcon_dev *edev, u32 state); | 77 | extern void extcon_set_state(struct extcon_dev *edev, u32 state); |
78 | |||
79 | /* | ||
80 | * Following APIs are to monitor every action of a notifier. | ||
81 | * Registerer gets notified for every external port of a connection device. | ||
82 | */ | ||
83 | extern int extcon_register_notifier(struct extcon_dev *edev, | ||
84 | struct notifier_block *nb); | ||
85 | extern int extcon_unregister_notifier(struct extcon_dev *edev, | ||
86 | struct notifier_block *nb); | ||
66 | #else /* CONFIG_EXTCON */ | 87 | #else /* CONFIG_EXTCON */ |
67 | static inline int extcon_dev_register(struct extcon_dev *edev, | 88 | static inline int extcon_dev_register(struct extcon_dev *edev, |
68 | struct device *dev) | 89 | struct device *dev) |
@@ -78,5 +99,22 @@ static inline u32 extcon_get_state(struct extcon_dev *edev) | |||
78 | } | 99 | } |
79 | 100 | ||
80 | static inline void extcon_set_state(struct extcon_dev *edev, u32 state) { } | 101 | static inline void extcon_set_state(struct extcon_dev *edev, u32 state) { } |
102 | static inline struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name) | ||
103 | { | ||
104 | return NULL; | ||
105 | } | ||
106 | |||
107 | static inline int extcon_register_notifier(struct extcon_dev *edev, | ||
108 | struct notifier_block *nb) | ||
109 | { | ||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | static inline int extcon_unregister_notifier(struct extcon_dev *edev, | ||
114 | struct notifier_block *nb) | ||
115 | { | ||
116 | return 0; | ||
117 | } | ||
118 | |||
81 | #endif /* CONFIG_EXTCON */ | 119 | #endif /* CONFIG_EXTCON */ |
82 | #endif /* __LINUX_EXTCON_H__ */ | 120 | #endif /* __LINUX_EXTCON_H__ */ |