]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/extcon/extcon-class.c
Merge branch 'drm-intel-fixes' of git://people.freedesktop.org/~danvet/drm-intel...
[karo-tx-linux.git] / drivers / extcon / extcon-class.c
index 946a3188b2b78a4014f84e93588e8423c41501c0..d398821097f35f181c7ad554360a127de13496d5 100644 (file)
@@ -41,7 +41,7 @@
  * every single port-type of the following cable names. Please choose cable
  * names that are actually used in your extcon device.
  */
-const char *extcon_cable_name[] = {
+const char extcon_cable_name[][CABLE_NAME_MAX + 1] = {
        [EXTCON_USB]            = "USB",
        [EXTCON_USB_HOST]       = "USB-Host",
        [EXTCON_TA]             = "TA",
@@ -62,8 +62,6 @@ const char *extcon_cable_name[] = {
        [EXTCON_VIDEO_IN]       = "Video-in",
        [EXTCON_VIDEO_OUT]      = "Video-out",
        [EXTCON_MECHANICAL]     = "Mechanical",
-
-       NULL,
 };
 
 static struct class *extcon_class;
@@ -91,17 +89,13 @@ static int check_mutually_exclusive(struct extcon_dev *edev, u32 new_state)
                return 0;
 
        for (i = 0; edev->mutually_exclusive[i]; i++) {
-               int count = 0, j;
+               int weight;
                u32 correspondants = new_state & edev->mutually_exclusive[i];
-               u32 exp = 1;
-
-               for (j = 0; j < 32; j++) {
-                       if (exp & correspondants)
-                               count++;
-                       if (count > 1)
-                               return i + 1;
-                       exp <<= 1;
-               }
+
+               /* calculate the total number of bits set */
+               weight = hweight32(correspondants);
+               if (weight > 1)
+                       return i + 1;
        }
 
        return 0;
@@ -362,7 +356,7 @@ int extcon_get_cable_state(struct extcon_dev *edev, const char *cable_name)
 EXPORT_SYMBOL_GPL(extcon_get_cable_state);
 
 /**
- * extcon_get_cable_state_() - Set the status of a specific cable.
+ * extcon_set_cable_state_() - Set the status of a specific cable.
  * @edev:      the extcon device that has the cable.
  * @index:     cable index that can be retrieved by extcon_find_cable_index().
  * @cable_state:       the new cable status. The default semantics is
@@ -382,7 +376,7 @@ int extcon_set_cable_state_(struct extcon_dev *edev,
 EXPORT_SYMBOL_GPL(extcon_set_cable_state_);
 
 /**
- * extcon_get_cable_state() - Set the status of a specific cable.
+ * extcon_set_cable_state() - Set the status of a specific cable.
  * @edev:      the extcon device that has the cable.
  * @cable_name:        cable name.
  * @cable_state:       the new cable status. The default semantics is
@@ -447,6 +441,8 @@ static int _call_per_cable(struct notifier_block *nb, unsigned long val,
  *                           extcon device.
  * @obj:       an empty extcon_specific_cable_nb object to be returned.
  * @extcon_name:       the name of extcon device.
+ *                     if NULL, extcon_register_interest will register
+ *                     every cable with the target cable_name given.
  * @cable_name:                the target cable name.
  * @nb:                the notifier block to get notified.
  *
@@ -466,22 +462,44 @@ int extcon_register_interest(struct extcon_specific_cable_nb *obj,
                             const char *extcon_name, const char *cable_name,
                             struct notifier_block *nb)
 {
-       if (!obj || !extcon_name || !cable_name || !nb)
+       if (!obj || !cable_name || !nb)
                return -EINVAL;
 
-       obj->edev = extcon_get_extcon_dev(extcon_name);
-       if (!obj->edev)
-               return -ENODEV;
+       if (extcon_name) {
+               obj->edev = extcon_get_extcon_dev(extcon_name);
+               if (!obj->edev)
+                       return -ENODEV;
 
-       obj->cable_index = extcon_find_cable_index(obj->edev, cable_name);
-       if (obj->cable_index < 0)
-               return -ENODEV;
+               obj->cable_index = extcon_find_cable_index(obj->edev, cable_name);
+               if (obj->cable_index < 0)
+                       return -ENODEV;
+
+               obj->user_nb = nb;
 
-       obj->user_nb = nb;
+               obj->internal_nb.notifier_call = _call_per_cable;
 
-       obj->internal_nb.notifier_call = _call_per_cable;
+               return raw_notifier_chain_register(&obj->edev->nh, &obj->internal_nb);
+       } else {
+               struct class_dev_iter iter;
+               struct extcon_dev *extd;
+               struct device *dev;
+
+               if (!extcon_class)
+                       return -ENODEV;
+               class_dev_iter_init(&iter, extcon_class, NULL, NULL);
+               while ((dev = class_dev_iter_next(&iter))) {
+                       extd = (struct extcon_dev *)dev_get_drvdata(dev);
+
+                       if (extcon_find_cable_index(extd, cable_name) < 0)
+                               continue;
+
+                       class_dev_iter_exit(&iter);
+                       return extcon_register_interest(obj, extd->name,
+                                               cable_name, nb);
+               }
 
-       return raw_notifier_chain_register(&obj->edev->nh, &obj->internal_nb);
+               return -ENODEV;
+       }
 }
 
 /**
@@ -551,43 +569,9 @@ static int create_extcon_class(void)
        return 0;
 }
 
-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)) {
-               int index;
-
-               if (edev->mutually_exclusive && edev->max_supported) {
-                       for (index = 0; edev->mutually_exclusive[index];
-                            index++)
-                               kfree(edev->d_attrs_muex[index].attr.name);
-                       kfree(edev->d_attrs_muex);
-                       kfree(edev->attrs_muex);
-               }
-
-               for (index = 0; index < edev->max_supported; index++)
-                       kfree(edev->cables[index].attr_g.name);
-
-               if (edev->max_supported) {
-                       kfree(edev->extcon_dev_type.groups);
-                       kfree(edev->cables);
-               }
-
-               device_unregister(edev->dev);
-               put_device(edev->dev);
-       }
-
-       kfree(edev->dev);
-}
-
 static void extcon_dev_release(struct device *dev)
 {
-       struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
-
-       extcon_cleanup(edev, true);
+       kfree(dev);
 }
 
 static const char *muex_name = "mutually_exclusive";
@@ -813,7 +797,40 @@ EXPORT_SYMBOL_GPL(extcon_dev_register);
  */
 void extcon_dev_unregister(struct extcon_dev *edev)
 {
-       extcon_cleanup(edev, false);
+       int index;
+
+       mutex_lock(&extcon_dev_list_lock);
+       list_del(&edev->entry);
+       mutex_unlock(&extcon_dev_list_lock);
+
+       if (IS_ERR_OR_NULL(get_device(edev->dev))) {
+               dev_err(edev->dev, "Failed to unregister extcon_dev (%s)\n",
+                               dev_name(edev->dev));
+               return;
+       }
+
+       if (edev->mutually_exclusive && edev->max_supported) {
+               for (index = 0; edev->mutually_exclusive[index];
+                               index++)
+                       kfree(edev->d_attrs_muex[index].attr.name);
+               kfree(edev->d_attrs_muex);
+               kfree(edev->attrs_muex);
+       }
+
+       for (index = 0; index < edev->max_supported; index++)
+               kfree(edev->cables[index].attr_g.name);
+
+       if (edev->max_supported) {
+               kfree(edev->extcon_dev_type.groups);
+               kfree(edev->cables);
+       }
+
+#if defined(CONFIG_ANDROID)
+       if (switch_class)
+               class_compat_remove_link(switch_class, edev->dev, NULL);
+#endif
+       device_unregister(edev->dev);
+       put_device(edev->dev);
 }
 EXPORT_SYMBOL_GPL(extcon_dev_unregister);
 
@@ -825,6 +842,9 @@ module_init(extcon_class_init);
 
 static void __exit extcon_class_exit(void)
 {
+#if defined(CONFIG_ANDROID)
+       class_compat_unregister(switch_class);
+#endif
        class_destroy(extcon_class);
 }
 module_exit(extcon_class_exit);