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 <[email protected]>
Signed-off-by: MyungJoo Ham <[email protected]>
Signed-off-by: Kyungmin Park <[email protected]>
Reviewed-by: Mark Brown <[email protected]>

--
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 <[email protected]>
This commit is contained in:
Donggeun Kim 2012-04-20 14:16:24 +09:00 • committed by Greg Kroah-Hartman
commit 74c5d09bd5
2 changed files with 104 additions and 0 deletions

View file

@ -36,6 +36,9 @@ struct class *extcon_class;
static struct class_compat *switch_class;
#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
static LIST_HEAD(extcon_dev_list);
static DEFINE_MUTEX(extcon_dev_list_lock);
static ssize_t state_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
@ -75,6 +78,9 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr,
* the name of extcon device (envp[0]) and the state output (envp[1]).
* Tizen uses this format for extcon device to get events from ports.
* Android uses this format as well.
*
* Note that notifier provides the which bits are changes in the state
* variable with "val" to the callback.
*/
void extcon_set_state(struct extcon_dev *edev, u32 state)
{
@ -84,10 +90,14 @@ void extcon_set_state(struct extcon_dev *edev, u32 state)
char *envp[3];
int env_offset = 0;
int length;
u32 old_state = edev->state;
if (edev->state != state) {
edev->state = state;
raw_notifier_call_chain(&edev->nh, old_state ^ edev->state,
edev);
prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
if (prop_buf) {
length = name_show(edev->dev, NULL, prop_buf);
@ -117,6 +127,51 @@ void extcon_set_state(struct extcon_dev *edev, u32 state)
}
EXPORT_SYMBOL_GPL(extcon_set_state);
/**
* extcon_get_extcon_dev() - Get the extcon device instance from the name
* @extcon_name: The extcon name provided with extcon_dev_register()
*/
struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
{
struct extcon_dev *sd;
mutex_lock(&extcon_dev_list_lock);
list_for_each_entry(sd, &extcon_dev_list, entry) {
if (!strcmp(sd->name, extcon_name))
goto out;
}
sd = NULL;
out:
mutex_unlock(&extcon_dev_list_lock);
return sd;
}
EXPORT_SYMBOL_GPL(extcon_get_extcon_dev);
/**
* extcon_register_notifier() - Register a notifee to get notified by
* any attach status changes from the extcon.
* @edev: the extcon device.
* @nb: a notifier block to be registered.
*/
int extcon_register_notifier(struct extcon_dev *edev,
struct notifier_block *nb)
{
return raw_notifier_chain_register(&edev->nh, nb);
}
EXPORT_SYMBOL_GPL(extcon_register_notifier);
/**
* extcon_unregister_notifier() - Unregister a notifee from the extcon device.
* @edev: the extcon device.
* @nb: a registered notifier block to be unregistered.
*/
int extcon_unregister_notifier(struct extcon_dev *edev,
struct notifier_block *nb)
{
return raw_notifier_chain_unregister(&edev->nh, nb);
}
EXPORT_SYMBOL_GPL(extcon_unregister_notifier);
static struct device_attribute extcon_attrs[] = {
__ATTR_RO(state),
__ATTR_RO(name),
@ -142,6 +197,10 @@ static int create_extcon_class(void)
static void extcon_cleanup(struct extcon_dev *edev, bool skip)
{
mutex_lock(&extcon_dev_list_lock);
list_del(&edev->entry);
mutex_unlock(&extcon_dev_list_lock);
if (!skip && get_device(edev->dev)) {
device_unregister(edev->dev);
put_device(edev->dev);
@ -194,8 +253,15 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
dev);
#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
RAW_INIT_NOTIFIER_HEAD(&edev->nh);
dev_set_drvdata(edev->dev, edev);
edev->state = 0;
mutex_lock(&extcon_dev_list_lock);
list_add(&edev->entry, &extcon_dev_list);
mutex_unlock(&extcon_dev_list_lock);
return 0;
err_dev: