Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 74c5d09b authored by Donggeun Kim's avatar Donggeun Kim Committed by Greg Kroah-Hartman
Browse files

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: default avatarDonggeun Kim <dg77.kim@samsung.com>
Signed-off-by: default avatarMyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: default avatarKyungmin Park <kyungmin.park@samsung.com>
Reviewed-by: default avatarMark 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: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent be48308a
Loading
Loading
Loading
Loading
+66 −0
Original line number Diff line number Diff line
@@ -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:
+38 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@
#ifndef __LINUX_EXTCON_H__
#define __LINUX_EXTCON_H__

#include <linux/notifier.h>
/**
 * struct extcon_dev - An extcon device represents one external connector.
 * @name	The name of this extcon device. Parent device name is used
@@ -34,6 +35,9 @@
 * @dev		Device of this extcon. Do not provide at register-time.
 * @state	Attach/detach state of this extcon. Do not provide at
 *		register-time
 * @nh	Notifier for the state change events from this extcon
 * @entry	To support list of extcon devices so that uses can search
 *		for extcon devices based on the extcon name.
 *
 * In most cases, users only need to provide "User initializing data" of
 * this struct when registering an extcon. In some exceptional cases,
@@ -51,11 +55,19 @@ struct extcon_dev {
	/* --- Internal data. Please do not set. --- */
	struct device	*dev;
	u32		state;
	struct raw_notifier_head nh;
	struct list_head entry;
};

#if IS_ENABLED(CONFIG_EXTCON)

/*
 * Following APIs are for notifiers or configurations.
 * Notifiers are the external port and connection devices.
 */
extern int extcon_dev_register(struct extcon_dev *edev, struct device *dev);
extern void extcon_dev_unregister(struct extcon_dev *edev);
extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name);

static inline u32 extcon_get_state(struct extcon_dev *edev)
{
@@ -63,6 +75,15 @@ static inline u32 extcon_get_state(struct extcon_dev *edev)
}

extern void extcon_set_state(struct extcon_dev *edev, u32 state);

/*
 * Following APIs are to monitor every action of a notifier.
 * Registerer gets notified for every external port of a connection device.
 */
extern int extcon_register_notifier(struct extcon_dev *edev,
				    struct notifier_block *nb);
extern int extcon_unregister_notifier(struct extcon_dev *edev,
				      struct notifier_block *nb);
#else /* CONFIG_EXTCON */
static inline int extcon_dev_register(struct extcon_dev *edev,
				      struct device *dev)
@@ -78,5 +99,22 @@ static inline u32 extcon_get_state(struct extcon_dev *edev)
}

static inline void extcon_set_state(struct extcon_dev *edev, u32 state) { }
static inline struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
{
	return NULL;
}

static inline int extcon_register_notifier(struct extcon_dev *edev,
					   struct notifier_block *nb)
{
	return 0;
}

static inline int extcon_unregister_notifier(struct extcon_dev *edev,
					     struct notifier_block *nb)
{
	return 0;
}

#endif /* CONFIG_EXTCON */
#endif /* __LINUX_EXTCON_H__ */