diff options
Diffstat (limited to 'drivers/extcon/extcon-class.c')
| -rw-r--r-- | drivers/extcon/extcon-class.c | 142 |
1 files changed, 81 insertions, 61 deletions
diff --git a/drivers/extcon/extcon-class.c b/drivers/extcon/extcon-class.c index 946a3188b2b7..d398821097f3 100644 --- a/drivers/extcon/extcon-class.c +++ b/drivers/extcon/extcon-class.c | |||
| @@ -41,7 +41,7 @@ | |||
| 41 | * every single port-type of the following cable names. Please choose cable | 41 | * every single port-type of the following cable names. Please choose cable |
| 42 | * names that are actually used in your extcon device. | 42 | * names that are actually used in your extcon device. |
| 43 | */ | 43 | */ |
| 44 | const char *extcon_cable_name[] = { | 44 | const char extcon_cable_name[][CABLE_NAME_MAX + 1] = { |
| 45 | [EXTCON_USB] = "USB", | 45 | [EXTCON_USB] = "USB", |
| 46 | [EXTCON_USB_HOST] = "USB-Host", | 46 | [EXTCON_USB_HOST] = "USB-Host", |
| 47 | [EXTCON_TA] = "TA", | 47 | [EXTCON_TA] = "TA", |
| @@ -62,8 +62,6 @@ const char *extcon_cable_name[] = { | |||
| 62 | [EXTCON_VIDEO_IN] = "Video-in", | 62 | [EXTCON_VIDEO_IN] = "Video-in", |
| 63 | [EXTCON_VIDEO_OUT] = "Video-out", | 63 | [EXTCON_VIDEO_OUT] = "Video-out", |
| 64 | [EXTCON_MECHANICAL] = "Mechanical", | 64 | [EXTCON_MECHANICAL] = "Mechanical", |
| 65 | |||
| 66 | NULL, | ||
| 67 | }; | 65 | }; |
| 68 | 66 | ||
| 69 | static struct class *extcon_class; | 67 | static struct class *extcon_class; |
| @@ -91,17 +89,13 @@ static int check_mutually_exclusive(struct extcon_dev *edev, u32 new_state) | |||
| 91 | return 0; | 89 | return 0; |
| 92 | 90 | ||
| 93 | for (i = 0; edev->mutually_exclusive[i]; i++) { | 91 | for (i = 0; edev->mutually_exclusive[i]; i++) { |
| 94 | int count = 0, j; | 92 | int weight; |
| 95 | u32 correspondants = new_state & edev->mutually_exclusive[i]; | 93 | u32 correspondants = new_state & edev->mutually_exclusive[i]; |
| 96 | u32 exp = 1; | 94 | |
| 97 | 95 | /* calculate the total number of bits set */ | |
| 98 | for (j = 0; j < 32; j++) { | 96 | weight = hweight32(correspondants); |
| 99 | if (exp & correspondants) | 97 | if (weight > 1) |
| 100 | count++; | 98 | return i + 1; |
| 101 | if (count > 1) | ||
| 102 | return i + 1; | ||
| 103 | exp <<= 1; | ||
| 104 | } | ||
| 105 | } | 99 | } |
| 106 | 100 | ||
| 107 | return 0; | 101 | return 0; |
| @@ -362,7 +356,7 @@ int extcon_get_cable_state(struct extcon_dev *edev, const char *cable_name) | |||
| 362 | EXPORT_SYMBOL_GPL(extcon_get_cable_state); | 356 | EXPORT_SYMBOL_GPL(extcon_get_cable_state); |
| 363 | 357 | ||
| 364 | /** | 358 | /** |
| 365 | * extcon_get_cable_state_() - Set the status of a specific cable. | 359 | * extcon_set_cable_state_() - Set the status of a specific cable. |
| 366 | * @edev: the extcon device that has the cable. | 360 | * @edev: the extcon device that has the cable. |
| 367 | * @index: cable index that can be retrieved by extcon_find_cable_index(). | 361 | * @index: cable index that can be retrieved by extcon_find_cable_index(). |
| 368 | * @cable_state: the new cable status. The default semantics is | 362 | * @cable_state: the new cable status. The default semantics is |
| @@ -382,7 +376,7 @@ int extcon_set_cable_state_(struct extcon_dev *edev, | |||
| 382 | EXPORT_SYMBOL_GPL(extcon_set_cable_state_); | 376 | EXPORT_SYMBOL_GPL(extcon_set_cable_state_); |
| 383 | 377 | ||
| 384 | /** | 378 | /** |
| 385 | * extcon_get_cable_state() - Set the status of a specific cable. | 379 | * extcon_set_cable_state() - Set the status of a specific cable. |
| 386 | * @edev: the extcon device that has the cable. | 380 | * @edev: the extcon device that has the cable. |
| 387 | * @cable_name: cable name. | 381 | * @cable_name: cable name. |
| 388 | * @cable_state: the new cable status. The default semantics is | 382 | * @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, | |||
| 447 | * extcon device. | 441 | * extcon device. |
| 448 | * @obj: an empty extcon_specific_cable_nb object to be returned. | 442 | * @obj: an empty extcon_specific_cable_nb object to be returned. |
| 449 | * @extcon_name: the name of extcon device. | 443 | * @extcon_name: the name of extcon device. |
| 444 | * if NULL, extcon_register_interest will register | ||
| 445 | * every cable with the target cable_name given. | ||
| 450 | * @cable_name: the target cable name. | 446 | * @cable_name: the target cable name. |
| 451 | * @nb: the notifier block to get notified. | 447 | * @nb: the notifier block to get notified. |
| 452 | * | 448 | * |
| @@ -466,22 +462,44 @@ int extcon_register_interest(struct extcon_specific_cable_nb *obj, | |||
| 466 | const char *extcon_name, const char *cable_name, | 462 | const char *extcon_name, const char *cable_name, |
| 467 | struct notifier_block *nb) | 463 | struct notifier_block *nb) |
| 468 | { | 464 | { |
| 469 | if (!obj || !extcon_name || !cable_name || !nb) | 465 | if (!obj || !cable_name || !nb) |
| 470 | return -EINVAL; | 466 | return -EINVAL; |
| 471 | 467 | ||
| 472 | obj->edev = extcon_get_extcon_dev(extcon_name); | 468 | if (extcon_name) { |
| 473 | if (!obj->edev) | 469 | obj->edev = extcon_get_extcon_dev(extcon_name); |
| 474 | return -ENODEV; | 470 | if (!obj->edev) |
| 471 | return -ENODEV; | ||
| 475 | 472 | ||
| 476 | obj->cable_index = extcon_find_cable_index(obj->edev, cable_name); | 473 | obj->cable_index = extcon_find_cable_index(obj->edev, cable_name); |
| 477 | if (obj->cable_index < 0) | 474 | if (obj->cable_index < 0) |
| 478 | return -ENODEV; | 475 | return -ENODEV; |
| 476 | |||
| 477 | obj->user_nb = nb; | ||
| 479 | 478 | ||
| 480 | obj->user_nb = nb; | 479 | obj->internal_nb.notifier_call = _call_per_cable; |
| 481 | 480 | ||
| 482 | obj->internal_nb.notifier_call = _call_per_cable; | 481 | return raw_notifier_chain_register(&obj->edev->nh, &obj->internal_nb); |
| 482 | } else { | ||
| 483 | struct class_dev_iter iter; | ||
| 484 | struct extcon_dev *extd; | ||
| 485 | struct device *dev; | ||
| 486 | |||
| 487 | if (!extcon_class) | ||
| 488 | return -ENODEV; | ||
| 489 | class_dev_iter_init(&iter, extcon_class, NULL, NULL); | ||
| 490 | while ((dev = class_dev_iter_next(&iter))) { | ||
| 491 | extd = (struct extcon_dev *)dev_get_drvdata(dev); | ||
| 492 | |||
| 493 | if (extcon_find_cable_index(extd, cable_name) < 0) | ||
| 494 | continue; | ||
| 495 | |||
| 496 | class_dev_iter_exit(&iter); | ||
| 497 | return extcon_register_interest(obj, extd->name, | ||
| 498 | cable_name, nb); | ||
| 499 | } | ||
| 483 | 500 | ||
| 484 | return raw_notifier_chain_register(&obj->edev->nh, &obj->internal_nb); | 501 | return -ENODEV; |
| 502 | } | ||
| 485 | } | 503 | } |
| 486 | 504 | ||
| 487 | /** | 505 | /** |
| @@ -551,43 +569,9 @@ static int create_extcon_class(void) | |||
| 551 | return 0; | 569 | return 0; |
| 552 | } | 570 | } |
| 553 | 571 | ||
| 554 | static void extcon_cleanup(struct extcon_dev *edev, bool skip) | ||
| 555 | { | ||
| 556 | mutex_lock(&extcon_dev_list_lock); | ||
| 557 | list_del(&edev->entry); | ||
| 558 | mutex_unlock(&extcon_dev_list_lock); | ||
| 559 | |||
| 560 | if (!skip && get_device(edev->dev)) { | ||
| 561 | int index; | ||
| 562 | |||
| 563 | if (edev->mutually_exclusive && edev->max_supported) { | ||
| 564 | for (index = 0; edev->mutually_exclusive[index]; | ||
| 565 | index++) | ||
| 566 | kfree(edev->d_attrs_muex[index].attr.name); | ||
| 567 | kfree(edev->d_attrs_muex); | ||
| 568 | kfree(edev->attrs_muex); | ||
| 569 | } | ||
| 570 | |||
| 571 | for (index = 0; index < edev->max_supported; index++) | ||
| 572 | kfree(edev->cables[index].attr_g.name); | ||
| 573 | |||
| 574 | if (edev->max_supported) { | ||
| 575 | kfree(edev->extcon_dev_type.groups); | ||
| 576 | kfree(edev->cables); | ||
| 577 | } | ||
| 578 | |||
| 579 | device_unregister(edev->dev); | ||
| 580 | put_device(edev->dev); | ||
| 581 | } | ||
| 582 | |||
| 583 | kfree(edev->dev); | ||
| 584 | } | ||
| 585 | |||
| 586 | static void extcon_dev_release(struct device *dev) | 572 | static void extcon_dev_release(struct device *dev) |
| 587 | { | 573 | { |
| 588 | struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev); | 574 | kfree(dev); |
| 589 | |||
| 590 | extcon_cleanup(edev, true); | ||
| 591 | } | 575 | } |
| 592 | 576 | ||
| 593 | static const char *muex_name = "mutually_exclusive"; | 577 | static const char *muex_name = "mutually_exclusive"; |
| @@ -813,7 +797,40 @@ EXPORT_SYMBOL_GPL(extcon_dev_register); | |||
| 813 | */ | 797 | */ |
| 814 | void extcon_dev_unregister(struct extcon_dev *edev) | 798 | void extcon_dev_unregister(struct extcon_dev *edev) |
| 815 | { | 799 | { |
| 816 | extcon_cleanup(edev, false); | 800 | int index; |
| 801 | |||
| 802 | mutex_lock(&extcon_dev_list_lock); | ||
| 803 | list_del(&edev->entry); | ||
| 804 | mutex_unlock(&extcon_dev_list_lock); | ||
| 805 | |||
| 806 | if (IS_ERR_OR_NULL(get_device(edev->dev))) { | ||
| 807 | dev_err(edev->dev, "Failed to unregister extcon_dev (%s)\n", | ||
| 808 | dev_name(edev->dev)); | ||
| 809 | return; | ||
| 810 | } | ||
| 811 | |||
| 812 | if (edev->mutually_exclusive && edev->max_supported) { | ||
| 813 | for (index = 0; edev->mutually_exclusive[index]; | ||
| 814 | index++) | ||
| 815 | kfree(edev->d_attrs_muex[index].attr.name); | ||
| 816 | kfree(edev->d_attrs_muex); | ||
| 817 | kfree(edev->attrs_muex); | ||
| 818 | } | ||
| 819 | |||
| 820 | for (index = 0; index < edev->max_supported; index++) | ||
| 821 | kfree(edev->cables[index].attr_g.name); | ||
| 822 | |||
| 823 | if (edev->max_supported) { | ||
| 824 | kfree(edev->extcon_dev_type.groups); | ||
| 825 | kfree(edev->cables); | ||
| 826 | } | ||
| 827 | |||
| 828 | #if defined(CONFIG_ANDROID) | ||
| 829 | if (switch_class) | ||
| 830 | class_compat_remove_link(switch_class, edev->dev, NULL); | ||
| 831 | #endif | ||
| 832 | device_unregister(edev->dev); | ||
| 833 | put_device(edev->dev); | ||
| 817 | } | 834 | } |
| 818 | EXPORT_SYMBOL_GPL(extcon_dev_unregister); | 835 | EXPORT_SYMBOL_GPL(extcon_dev_unregister); |
| 819 | 836 | ||
| @@ -825,6 +842,9 @@ module_init(extcon_class_init); | |||
| 825 | 842 | ||
| 826 | static void __exit extcon_class_exit(void) | 843 | static void __exit extcon_class_exit(void) |
| 827 | { | 844 | { |
| 845 | #if defined(CONFIG_ANDROID) | ||
| 846 | class_compat_unregister(switch_class); | ||
| 847 | #endif | ||
| 828 | class_destroy(extcon_class); | 848 | class_destroy(extcon_class); |
| 829 | } | 849 | } |
| 830 | module_exit(extcon_class_exit); | 850 | module_exit(extcon_class_exit); |
