aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDonggeun Kim <dg77.kim@samsung.com>2012-04-20 01:16:24 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-04-20 12:23:09 -0400
commit74c5d09bd562edc220d6e076b8f1e118819c178f (patch)
tree65f35b6066fe135405797acc419b4bb6f346e4af
parentbe48308a24c7651bf968b561dbd590edb8166d62 (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.c66
-rw-r--r--include/linux/extcon.h38
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;
36static struct class_compat *switch_class; 36static struct class_compat *switch_class;
37#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */ 37#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
38 38
39static LIST_HEAD(extcon_dev_list);
40static DEFINE_MUTEX(extcon_dev_list_lock);
41
39static ssize_t state_show(struct device *dev, struct device_attribute *attr, 42static 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 */
79void extcon_set_state(struct extcon_dev *edev, u32 state) 85void 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}
118EXPORT_SYMBOL_GPL(extcon_set_state); 128EXPORT_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 */
134struct 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;
144out:
145 mutex_unlock(&extcon_dev_list_lock);
146 return sd;
147}
148EXPORT_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 */
156int extcon_register_notifier(struct extcon_dev *edev,
157 struct notifier_block *nb)
158{
159 return raw_notifier_chain_register(&edev->nh, nb);
160}
161EXPORT_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 */
168int extcon_unregister_notifier(struct extcon_dev *edev,
169 struct notifier_block *nb)
170{
171 return raw_notifier_chain_unregister(&edev->nh, nb);
172}
173EXPORT_SYMBOL_GPL(extcon_unregister_notifier);
174
120static struct device_attribute extcon_attrs[] = { 175static 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
143static void extcon_cleanup(struct extcon_dev *edev, bool skip) 198static 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
201err_dev: 267err_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 */
57extern int extcon_dev_register(struct extcon_dev *edev, struct device *dev); 68extern int extcon_dev_register(struct extcon_dev *edev, struct device *dev);
58extern void extcon_dev_unregister(struct extcon_dev *edev); 69extern void extcon_dev_unregister(struct extcon_dev *edev);
70extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name);
59 71
60static inline u32 extcon_get_state(struct extcon_dev *edev) 72static 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
65extern void extcon_set_state(struct extcon_dev *edev, u32 state); 77extern 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 */
83extern int extcon_register_notifier(struct extcon_dev *edev,
84 struct notifier_block *nb);
85extern int extcon_unregister_notifier(struct extcon_dev *edev,
86 struct notifier_block *nb);
66#else /* CONFIG_EXTCON */ 87#else /* CONFIG_EXTCON */
67static inline int extcon_dev_register(struct extcon_dev *edev, 88static 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
80static inline void extcon_set_state(struct extcon_dev *edev, u32 state) { } 101static inline void extcon_set_state(struct extcon_dev *edev, u32 state) { }
102static inline struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
103{
104 return NULL;
105}
106
107static inline int extcon_register_notifier(struct extcon_dev *edev,
108 struct notifier_block *nb)
109{
110 return 0;
111}
112
113static 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__ */