extcon: Update the prototype of extcon_register_notifier() with enum extcon
authorChanwoo Choi <cw00.choi@samsung.com>
Tue, 19 May 2015 11:01:12 +0000 (20:01 +0900)
committerChanwoo Choi <cw00.choi@samsung.com>
Fri, 22 May 2015 09:58:55 +0000 (18:58 +0900)
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>
drivers/extcon/extcon.c
include/linux/extcon.h

index fce2687bde8e9657cc8dc41d16f3137875e389f1..5099c11d88339cdfe58c13f62abc8b0ed1c13b42 100644 (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);
 }
 
+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;
index 85c882f0029e03ff1fa31c40e8d7ae488c748a91..be9652b3a15427ddf9eb98ae2014746033480ccf 100644 (file)
@@ -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;
 }