|
@@ -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);
|