Commit 815429b3 authored by Chanwoo Choi's avatar Chanwoo Choi

extcon: Add new extcon_register_notifier_all() to monitor all external connectors

The extcon core already provides the extcon_register_notifier() function
in order to register the notifier block which is used to monitor
the state change for the specific external connector such as EXTCON_USB,
EXTCON_USB_HOST and so on. The extcon consumer uses the this function.

The extcon consumer might need to monitor the all supported external
connectors from the extcon device. In this case, The extcon consumer
should have each notifier_block structure for each external connector.

This patch adds the new extcon_register_notifier_all() function
that extcon consumer is able to monitor the state change of all
supported external connectors by using only one notifier_block structure.

- List of new added functions:
int extcon_register_notifier_all(struct extcon_dev *edev,
			struct notifier_block *nb);
int extcon_unregister_notifier_all(struct extcon_dev *edev,
			struct notifier_block *nb);
int devm_extcon_register_notifier_all(struct device *dev,
			struct extcon_dev *edev, struct notifier_block *nb);
void devm_extcon_unregister_notifier_all(struct device *dev,
			struct extcon_dev *edev, struct notifier_block *nb);
Suggested-by: default avatarHans de Goede <hdegoede@redhat.com>
Signed-off-by: default avatarChanwoo Choi <cw00.choi@samsung.com>
Tested-by: default avatarHans de Goede <hdegoede@redhat.com>
Acked-by: default avatarHans de Goede <hdegoede@redhat.com>
parent c02ed2e7
...@@ -50,6 +50,13 @@ static void devm_extcon_dev_notifier_unreg(struct device *dev, void *res) ...@@ -50,6 +50,13 @@ static void devm_extcon_dev_notifier_unreg(struct device *dev, void *res)
extcon_unregister_notifier(this->edev, this->id, this->nb); extcon_unregister_notifier(this->edev, this->id, this->nb);
} }
static void devm_extcon_dev_notifier_all_unreg(struct device *dev, void *res)
{
struct extcon_dev_notifier_devres *this = res;
extcon_unregister_notifier_all(this->edev, this->nb);
}
/** /**
* devm_extcon_dev_allocate - Allocate managed extcon device * devm_extcon_dev_allocate - Allocate managed extcon device
* @dev: device owning the extcon device being created * @dev: device owning the extcon device being created
...@@ -214,3 +221,57 @@ void devm_extcon_unregister_notifier(struct device *dev, ...@@ -214,3 +221,57 @@ void devm_extcon_unregister_notifier(struct device *dev,
devm_extcon_dev_match, edev)); devm_extcon_dev_match, edev));
} }
EXPORT_SYMBOL(devm_extcon_unregister_notifier); EXPORT_SYMBOL(devm_extcon_unregister_notifier);
/**
* devm_extcon_register_notifier_all()
* - Resource-managed extcon_register_notifier_all()
* @dev: device to allocate extcon device
* @edev: the extcon device that has the external connecotr.
* @nb: a notifier block to be registered.
*
* This function manages automatically the notifier of extcon device using
* device resource management and simplify the control of unregistering
* the notifier of extcon device. To get more information, refer that function.
*
* Returns 0 if success or negaive error number if failure.
*/
int devm_extcon_register_notifier_all(struct device *dev, struct extcon_dev *edev,
struct notifier_block *nb)
{
struct extcon_dev_notifier_devres *ptr;
int ret;
ptr = devres_alloc(devm_extcon_dev_notifier_all_unreg, sizeof(*ptr),
GFP_KERNEL);
if (!ptr)
return -ENOMEM;
ret = extcon_register_notifier_all(edev, nb);
if (ret) {
devres_free(ptr);
return ret;
}
ptr->edev = edev;
ptr->nb = nb;
devres_add(dev, ptr);
return 0;
}
EXPORT_SYMBOL(devm_extcon_register_notifier_all);
/**
* devm_extcon_unregister_notifier_all()
* - Resource-managed extcon_unregister_notifier_all()
* @dev: device to allocate extcon device
* @edev: the extcon device that has the external connecotr.
* @nb: a notifier block to be registered.
*/
void devm_extcon_unregister_notifier_all(struct device *dev,
struct extcon_dev *edev,
struct notifier_block *nb)
{
WARN_ON(devres_release(dev, devm_extcon_dev_notifier_all_unreg,
devm_extcon_dev_match, edev));
}
EXPORT_SYMBOL(devm_extcon_unregister_notifier_all);
...@@ -448,8 +448,19 @@ int extcon_sync(struct extcon_dev *edev, unsigned int id) ...@@ -448,8 +448,19 @@ int extcon_sync(struct extcon_dev *edev, unsigned int id)
spin_lock_irqsave(&edev->lock, flags); spin_lock_irqsave(&edev->lock, flags);
state = !!(edev->state & BIT(index)); state = !!(edev->state & BIT(index));
/*
* Call functions in a raw notifier chain for the specific one
* external connector.
*/
raw_notifier_call_chain(&edev->nh[index], state, edev); raw_notifier_call_chain(&edev->nh[index], state, edev);
/*
* Call functions in a raw notifier chain for the all supported
* external connectors.
*/
raw_notifier_call_chain(&edev->nh_all, state, edev);
/* This could be in interrupt handler */ /* This could be in interrupt handler */
prop_buf = (char *)get_zeroed_page(GFP_ATOMIC); prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
if (!prop_buf) { if (!prop_buf) {
...@@ -954,6 +965,59 @@ int extcon_unregister_notifier(struct extcon_dev *edev, unsigned int id, ...@@ -954,6 +965,59 @@ int extcon_unregister_notifier(struct extcon_dev *edev, unsigned int id,
} }
EXPORT_SYMBOL_GPL(extcon_unregister_notifier); EXPORT_SYMBOL_GPL(extcon_unregister_notifier);
/**
* extcon_register_notifier_all() - Register a notifier block for all connectors
* @edev: the extcon device that has the external connecotr.
* @nb: a notifier block to be registered.
*
* This fucntion registers a notifier block in order to receive the state
* change of all supported external connectors from extcon device.
* And The second parameter given to the callback of nb (val) is
* the current state and third parameter is the edev pointer.
*
* Returns 0 if success or error number if fail
*/
int extcon_register_notifier_all(struct extcon_dev *edev,
struct notifier_block *nb)
{
unsigned long flags;
int ret;
if (!edev || !nb)
return -EINVAL;
spin_lock_irqsave(&edev->lock, flags);
ret = raw_notifier_chain_register(&edev->nh_all, nb);
spin_unlock_irqrestore(&edev->lock, flags);
return ret;
}
EXPORT_SYMBOL_GPL(extcon_register_notifier_all);
/**
* extcon_unregister_notifier_all() - Unregister a notifier block from extcon.
* @edev: the extcon device that has the external connecotr.
* @nb: a notifier block to be registered.
*
* Returns 0 if success or error number if fail
*/
int extcon_unregister_notifier_all(struct extcon_dev *edev,
struct notifier_block *nb)
{
unsigned long flags;
int ret;
if (!edev || !nb)
return -EINVAL;
spin_lock_irqsave(&edev->lock, flags);
ret = raw_notifier_chain_unregister(&edev->nh_all, nb);
spin_unlock_irqrestore(&edev->lock, flags);
return ret;
}
EXPORT_SYMBOL_GPL(extcon_unregister_notifier_all);
static struct attribute *extcon_attrs[] = { static struct attribute *extcon_attrs[] = {
&dev_attr_state.attr, &dev_attr_state.attr,
&dev_attr_name.attr, &dev_attr_name.attr,
...@@ -1212,6 +1276,8 @@ int extcon_dev_register(struct extcon_dev *edev) ...@@ -1212,6 +1276,8 @@ int extcon_dev_register(struct extcon_dev *edev)
for (index = 0; index < edev->max_supported; index++) for (index = 0; index < edev->max_supported; index++)
RAW_INIT_NOTIFIER_HEAD(&edev->nh[index]); RAW_INIT_NOTIFIER_HEAD(&edev->nh[index]);
RAW_INIT_NOTIFIER_HEAD(&edev->nh_all);
dev_set_drvdata(&edev->dev, edev); dev_set_drvdata(&edev->dev, edev);
edev->state = 0; edev->state = 0;
......
...@@ -21,6 +21,8 @@ ...@@ -21,6 +21,8 @@
* @dev: Device of this extcon. * @dev: Device of this extcon.
* @state: Attach/detach state of this extcon. Do not provide at * @state: Attach/detach state of this extcon. Do not provide at
* register-time. * register-time.
* @nh_all: Notifier for the state change events for all supported
* external connectors from this extcon.
* @nh: Notifier for the state change events from this extcon * @nh: Notifier for the state change events from this extcon
* @entry: To support list of extcon devices so that users can * @entry: To support list of extcon devices so that users can
* search for extcon devices based on the extcon name. * search for extcon devices based on the extcon name.
...@@ -43,6 +45,7 @@ struct extcon_dev { ...@@ -43,6 +45,7 @@ struct extcon_dev {
/* Internal data. Please do not set. */ /* Internal data. Please do not set. */
struct device dev; struct device dev;
struct raw_notifier_head nh_all;
struct raw_notifier_head *nh; struct raw_notifier_head *nh;
struct list_head entry; struct list_head entry;
int max_supported; int max_supported;
......
...@@ -236,11 +236,11 @@ extern int extcon_set_property_capability(struct extcon_dev *edev, ...@@ -236,11 +236,11 @@ extern int extcon_set_property_capability(struct extcon_dev *edev,
unsigned int id, unsigned int prop); unsigned int id, unsigned int prop);
/* /*
* Following APIs are to monitor every action of a notifier. * Following APIs are to monitor the status change of the external connectors.
* Registrar gets notified for every external port of a connection device. * extcon_register_notifier(*edev, id, *nb) : Register a notifier block
* Probably this could be used to debug an action of notifier; however, * for specific external connector of the extcon.
* we do not recommend to use this for normal 'notifiee' device drivers who * extcon_register_notifier_all(*edev, *nb) : Register a notifier block
* want to be notified by a specific external port of the notifier. * for all supported external connectors of the extcon.
*/ */
extern int extcon_register_notifier(struct extcon_dev *edev, unsigned int id, extern int extcon_register_notifier(struct extcon_dev *edev, unsigned int id,
struct notifier_block *nb); struct notifier_block *nb);
...@@ -253,6 +253,17 @@ extern void devm_extcon_unregister_notifier(struct device *dev, ...@@ -253,6 +253,17 @@ extern void devm_extcon_unregister_notifier(struct device *dev,
struct extcon_dev *edev, unsigned int id, struct extcon_dev *edev, unsigned int id,
struct notifier_block *nb); struct notifier_block *nb);
extern int extcon_register_notifier_all(struct extcon_dev *edev,
struct notifier_block *nb);
extern int extcon_unregister_notifier_all(struct extcon_dev *edev,
struct notifier_block *nb);
extern int devm_extcon_register_notifier_all(struct device *dev,
struct extcon_dev *edev,
struct notifier_block *nb);
extern void devm_extcon_unregister_notifier_all(struct device *dev,
struct extcon_dev *edev,
struct notifier_block *nb);
/* /*
* Following API get the extcon device from devicetree. * Following API get the extcon device from devicetree.
* This function use phandle of devicetree to get extcon device directly. * This function use phandle of devicetree to get extcon device directly.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment