extcon: Update the prototype of extcon_register_notifier() with enum extcon

Previously, extcon consumer driver used the extcon_register_interest()
to register the notifier chain and then to receive the notifier event
when external connector's state is changed. When registering the notifier chain
for specific external connector with extcon_register_interest(), it used the
the string name of external connector directly. There are potential problem
because of unclear, non-standard and inconsequent cable name. Namely,
it is not appropriate method to identify each external connector.

So, this patch modify the prototype of extcon_register_notifier() by using
the 'enum extcon' which are the unique id for each external connector
instead of unclear string method.

- Previously, the extcon consumer driver used the extcon_register_interest()
with 'cable_name' to point out the specific external connector. Also. it used
the un-needed structure (struct extcon_specific_cable_nb).
: int extcon_register_interest(struct extcon_specific_cable_nb *obj,
			     const char *extcon_name, const char *cable_name,
			     struct notifier_block *nb)

- Newly, the updated extcon_register_notifier() would definitely support
the same feature to detech the changed state of external connector without
any specific structure (struct extcon_specific_cable_nb).
: int extcon_register_notifier(struct extcon_dev *edev, enum extcon id,
			     struct notifier_block *nb)

This patch support the both extcon_register_interest() and new extcon_register_
notifier(). But the extcon_{register|unregister}_interest() will be deprecated
because extcon core would support the notifier event for extcon consumer driver
with only updated extcon_register_notifier() and 'extcon_specific_cable_nb'
will be removed if there are no extcon consumer driver with legacy
extcon_{register|unregister}_interest().

Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
Reviewed-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
This commit is contained in:
Chanwoo Choi 2015-05-19 20:01:12 +09:00
parent 8e9bc36df5
commit 046050f6e6
2 changed files with 56 additions and 52 deletions

View File

@ -146,6 +146,16 @@ static int find_cable_index_by_name(struct extcon_dev *edev, const char *name)
return find_cable_index_by_id(edev, id); return find_cable_index_by_id(edev, id);
} }
static bool is_extcon_changed(u32 prev, u32 new, int idx, bool *attached)
{
if (((prev >> idx) & 0x1) != ((new >> idx) & 0x1)) {
*attached = new ? true : false;
return true;
}
return false;
}
static ssize_t state_show(struct device *dev, struct device_attribute *attr, static ssize_t state_show(struct device *dev, struct device_attribute *attr,
char *buf) char *buf)
{ {
@ -254,23 +264,27 @@ int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
char *envp[3]; char *envp[3];
int env_offset = 0; int env_offset = 0;
int length; int length;
int index;
unsigned long flags; unsigned long flags;
bool attached;
spin_lock_irqsave(&edev->lock, flags); spin_lock_irqsave(&edev->lock, flags);
if (edev->state != ((edev->state & ~mask) | (state & mask))) { if (edev->state != ((edev->state & ~mask) | (state & mask))) {
u32 old_state = edev->state;
if (check_mutually_exclusive(edev, (edev->state & ~mask) | if (check_mutually_exclusive(edev, (edev->state & ~mask) |
(state & mask))) { (state & mask))) {
spin_unlock_irqrestore(&edev->lock, flags); spin_unlock_irqrestore(&edev->lock, flags);
return -EPERM; return -EPERM;
} }
for (index = 0; index < edev->max_supported; index++) {
if (is_extcon_changed(edev->state, state, index, &attached))
raw_notifier_call_chain(&edev->nh[index], attached, edev);
}
edev->state &= ~mask; edev->state &= ~mask;
edev->state |= state & mask; edev->state |= state & mask;
raw_notifier_call_chain(&edev->nh, old_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) {
@ -423,29 +437,6 @@ out:
} }
EXPORT_SYMBOL_GPL(extcon_get_extcon_dev); EXPORT_SYMBOL_GPL(extcon_get_extcon_dev);
static int _call_per_cable(struct notifier_block *nb, unsigned long val,
void *ptr)
{
struct extcon_specific_cable_nb *obj = container_of(nb,
struct extcon_specific_cable_nb, internal_nb);
struct extcon_dev *edev = ptr;
if ((val & (1 << obj->cable_index)) !=
(edev->state & (1 << obj->cable_index))) {
bool cable_state = true;
obj->previous_value = val;
if (val & (1 << obj->cable_index))
cable_state = false;
return obj->user_nb->notifier_call(obj->user_nb,
cable_state, ptr);
}
return NOTIFY_OK;
}
/** /**
* extcon_register_interest() - Register a notifier for a state change of a * extcon_register_interest() - Register a notifier for a state change of a
* specific cable, not an entier set of cables of a * specific cable, not an entier set of cables of a
@ -491,11 +482,10 @@ int extcon_register_interest(struct extcon_specific_cable_nb *obj,
obj->user_nb = nb; obj->user_nb = nb;
obj->internal_nb.notifier_call = _call_per_cable;
spin_lock_irqsave(&obj->edev->lock, flags); spin_lock_irqsave(&obj->edev->lock, flags);
ret = raw_notifier_chain_register(&obj->edev->nh, ret = raw_notifier_chain_register(
&obj->internal_nb); &obj->edev->nh[obj->cable_index],
obj->user_nb);
spin_unlock_irqrestore(&obj->edev->lock, flags); spin_unlock_irqrestore(&obj->edev->lock, flags);
} else { } else {
struct class_dev_iter iter; struct class_dev_iter iter;
@ -538,7 +528,8 @@ int extcon_unregister_interest(struct extcon_specific_cable_nb *obj)
return -EINVAL; return -EINVAL;
spin_lock_irqsave(&obj->edev->lock, flags); spin_lock_irqsave(&obj->edev->lock, flags);
ret = raw_notifier_chain_unregister(&obj->edev->nh, &obj->internal_nb); ret = raw_notifier_chain_unregister(
&obj->edev->nh[obj->cable_index], obj->user_nb);
spin_unlock_irqrestore(&obj->edev->lock, flags); spin_unlock_irqrestore(&obj->edev->lock, flags);
return ret; return ret;
@ -548,21 +539,24 @@ EXPORT_SYMBOL_GPL(extcon_unregister_interest);
/** /**
* extcon_register_notifier() - Register a notifiee to get notified by * extcon_register_notifier() - Register a notifiee to get notified by
* any attach status changes from the extcon. * any attach status changes from the extcon.
* @edev: the extcon device. * @edev: the extcon device that has the external connecotr.
* @id: the unique id of each external connector in extcon enumeration.
* @nb: a notifier block to be registered. * @nb: a notifier block to be registered.
* *
* Note that the second parameter given to the callback of nb (val) is * Note that the second parameter given to the callback of nb (val) is
* "old_state", not the current state. The current state can be retrieved * "old_state", not the current state. The current state can be retrieved
* by looking at the third pameter (edev pointer)'s state value. * by looking at the third pameter (edev pointer)'s state value.
*/ */
int extcon_register_notifier(struct extcon_dev *edev, int extcon_register_notifier(struct extcon_dev *edev, enum extcon id,
struct notifier_block *nb) struct notifier_block *nb)
{ {
unsigned long flags; unsigned long flags;
int ret; int ret, idx;
idx = find_cable_index_by_id(edev, id);
spin_lock_irqsave(&edev->lock, flags); spin_lock_irqsave(&edev->lock, flags);
ret = raw_notifier_chain_register(&edev->nh, nb); ret = raw_notifier_chain_register(&edev->nh[idx], nb);
spin_unlock_irqrestore(&edev->lock, flags); spin_unlock_irqrestore(&edev->lock, flags);
return ret; return ret;
@ -571,17 +565,20 @@ EXPORT_SYMBOL_GPL(extcon_register_notifier);
/** /**
* extcon_unregister_notifier() - Unregister a notifiee from the extcon device. * extcon_unregister_notifier() - Unregister a notifiee from the extcon device.
* @edev: the extcon device. * @edev: the extcon device that has the external connecotr.
* @nb: a registered notifier block to be unregistered. * @id: the unique id of each external connector in extcon enumeration.
* @nb: a notifier block to be registered.
*/ */
int extcon_unregister_notifier(struct extcon_dev *edev, int extcon_unregister_notifier(struct extcon_dev *edev, enum extcon id,
struct notifier_block *nb) struct notifier_block *nb)
{ {
unsigned long flags; unsigned long flags;
int ret; int ret, idx;
idx = find_cable_index_by_id(edev, id);
spin_lock_irqsave(&edev->lock, flags); spin_lock_irqsave(&edev->lock, flags);
ret = raw_notifier_chain_unregister(&edev->nh, nb); ret = raw_notifier_chain_unregister(&edev->nh[idx], nb);
spin_unlock_irqrestore(&edev->lock, flags); spin_unlock_irqrestore(&edev->lock, flags);
return ret; return ret;
@ -893,7 +890,15 @@ int extcon_dev_register(struct extcon_dev *edev)
spin_lock_init(&edev->lock); spin_lock_init(&edev->lock);
RAW_INIT_NOTIFIER_HEAD(&edev->nh); edev->nh = devm_kzalloc(&edev->dev,
sizeof(*edev->nh) * edev->max_supported, GFP_KERNEL);
if (!edev->nh) {
ret = -ENOMEM;
goto err_dev;
}
for (index = 0; index < edev->max_supported; index++)
RAW_INIT_NOTIFIER_HEAD(&edev->nh[index]);
dev_set_drvdata(&edev->dev, edev); dev_set_drvdata(&edev->dev, edev);
edev->state = 0; edev->state = 0;

View File

@ -116,7 +116,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; struct raw_notifier_head *nh;
struct list_head entry; struct list_head entry;
int max_supported; int max_supported;
spinlock_t lock; /* could be called by irq handler */ spinlock_t lock; /* could be called by irq handler */
@ -155,8 +155,6 @@ struct extcon_cable {
/** /**
* struct extcon_specific_cable_nb - An internal data for * struct extcon_specific_cable_nb - An internal data for
* extcon_register_interest(). * extcon_register_interest().
* @internal_nb: A notifier block bridging extcon notifier
* and cable notifier.
* @user_nb: user provided notifier block for events from * @user_nb: user provided notifier block for events from
* a specific cable. * a specific cable.
* @cable_index: the target cable. * @cable_index: the target cable.
@ -164,7 +162,6 @@ struct extcon_cable {
* @previous_value: the saved previous event value. * @previous_value: the saved previous event value.
*/ */
struct extcon_specific_cable_nb { struct extcon_specific_cable_nb {
struct notifier_block internal_nb;
struct notifier_block *user_nb; struct notifier_block *user_nb;
int cable_index; int cable_index;
struct extcon_dev *edev; struct extcon_dev *edev;
@ -240,10 +237,10 @@ extern int extcon_unregister_interest(struct extcon_specific_cable_nb *nb);
* we do not recommend to use this for normal 'notifiee' device drivers who * we do not recommend to use this for normal 'notifiee' device drivers who
* want to be notified by a specific external port of the notifier. * want to be notified by a specific external port of the notifier.
*/ */
extern int extcon_register_notifier(struct extcon_dev *edev, extern int extcon_register_notifier(struct extcon_dev *edev, enum extcon id,
struct notifier_block *nb);
extern int extcon_unregister_notifier(struct extcon_dev *edev, enum extcon id,
struct notifier_block *nb); struct notifier_block *nb);
extern int extcon_unregister_notifier(struct extcon_dev *edev,
struct notifier_block *nb);
/* /*
* Following API get the extcon device from devicetree. * Following API get the extcon device from devicetree.
@ -333,13 +330,15 @@ static inline struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
} }
static inline int extcon_register_notifier(struct extcon_dev *edev, static inline int extcon_register_notifier(struct extcon_dev *edev,
struct notifier_block *nb) enum extcon id,
struct notifier_block *nb)
{ {
return 0; return 0;
} }
static inline int extcon_unregister_notifier(struct extcon_dev *edev, static inline int extcon_unregister_notifier(struct extcon_dev *edev,
struct notifier_block *nb) enum extcon id,
struct notifier_block *nb)
{ {
return 0; return 0;
} }