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:
parent
8e9bc36df5
commit
046050f6e6
@ -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);
|
||||
}
|
||||
|
||||
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,
|
||||
char *buf)
|
||||
{
|
||||
@ -254,23 +264,27 @@ int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
|
||||
char *envp[3];
|
||||
int env_offset = 0;
|
||||
int length;
|
||||
int index;
|
||||
unsigned long flags;
|
||||
bool attached;
|
||||
|
||||
spin_lock_irqsave(&edev->lock, flags);
|
||||
|
||||
if (edev->state != ((edev->state & ~mask) | (state & mask))) {
|
||||
u32 old_state = edev->state;
|
||||
|
||||
if (check_mutually_exclusive(edev, (edev->state & ~mask) |
|
||||
(state & mask))) {
|
||||
spin_unlock_irqrestore(&edev->lock, flags);
|
||||
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 |= state & mask;
|
||||
|
||||
raw_notifier_call_chain(&edev->nh, old_state, edev);
|
||||
/* This could be in interrupt handler */
|
||||
prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
|
||||
if (prop_buf) {
|
||||
@ -423,29 +437,6 @@ out:
|
||||
}
|
||||
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
|
||||
* 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->internal_nb.notifier_call = _call_per_cable;
|
||||
|
||||
spin_lock_irqsave(&obj->edev->lock, flags);
|
||||
ret = raw_notifier_chain_register(&obj->edev->nh,
|
||||
&obj->internal_nb);
|
||||
ret = raw_notifier_chain_register(
|
||||
&obj->edev->nh[obj->cable_index],
|
||||
obj->user_nb);
|
||||
spin_unlock_irqrestore(&obj->edev->lock, flags);
|
||||
} else {
|
||||
struct class_dev_iter iter;
|
||||
@ -538,7 +528,8 @@ int extcon_unregister_interest(struct extcon_specific_cable_nb *obj)
|
||||
return -EINVAL;
|
||||
|
||||
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);
|
||||
|
||||
return ret;
|
||||
@ -548,21 +539,24 @@ EXPORT_SYMBOL_GPL(extcon_unregister_interest);
|
||||
/**
|
||||
* extcon_register_notifier() - Register a notifiee to get notified by
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
* by looking at the third pameter (edev pointer)'s state value.
|
||||
*/
|
||||
int extcon_register_notifier(struct extcon_dev *edev,
|
||||
struct notifier_block *nb)
|
||||
int extcon_register_notifier(struct extcon_dev *edev, enum extcon id,
|
||||
struct notifier_block *nb)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
int ret, idx;
|
||||
|
||||
idx = find_cable_index_by_id(edev, id);
|
||||
|
||||
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);
|
||||
|
||||
return ret;
|
||||
@ -571,17 +565,20 @@ EXPORT_SYMBOL_GPL(extcon_register_notifier);
|
||||
|
||||
/**
|
||||
* extcon_unregister_notifier() - Unregister a notifiee from the extcon device.
|
||||
* @edev: the extcon device.
|
||||
* @nb: a registered notifier block to be unregistered.
|
||||
* @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.
|
||||
*/
|
||||
int extcon_unregister_notifier(struct extcon_dev *edev,
|
||||
struct notifier_block *nb)
|
||||
int extcon_unregister_notifier(struct extcon_dev *edev, enum extcon id,
|
||||
struct notifier_block *nb)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
int ret, idx;
|
||||
|
||||
idx = find_cable_index_by_id(edev, id);
|
||||
|
||||
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);
|
||||
|
||||
return ret;
|
||||
@ -893,7 +890,15 @@ int extcon_dev_register(struct extcon_dev *edev)
|
||||
|
||||
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);
|
||||
edev->state = 0;
|
||||
|
@ -116,7 +116,7 @@ struct extcon_dev {
|
||||
|
||||
/* Internal data. Please do not set. */
|
||||
struct device dev;
|
||||
struct raw_notifier_head nh;
|
||||
struct raw_notifier_head *nh;
|
||||
struct list_head entry;
|
||||
int max_supported;
|
||||
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
|
||||
* extcon_register_interest().
|
||||
* @internal_nb: A notifier block bridging extcon notifier
|
||||
* and cable notifier.
|
||||
* @user_nb: user provided notifier block for events from
|
||||
* a specific cable.
|
||||
* @cable_index: the target cable.
|
||||
@ -164,7 +162,6 @@ struct extcon_cable {
|
||||
* @previous_value: the saved previous event value.
|
||||
*/
|
||||
struct extcon_specific_cable_nb {
|
||||
struct notifier_block internal_nb;
|
||||
struct notifier_block *user_nb;
|
||||
int cable_index;
|
||||
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
|
||||
* 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);
|
||||
extern int extcon_unregister_notifier(struct extcon_dev *edev,
|
||||
struct notifier_block *nb);
|
||||
|
||||
/*
|
||||
* 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,
|
||||
struct notifier_block *nb)
|
||||
enum extcon id,
|
||||
struct notifier_block *nb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int extcon_unregister_notifier(struct extcon_dev *edev,
|
||||
struct notifier_block *nb)
|
||||
enum extcon id,
|
||||
struct notifier_block *nb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user