diff options
author | MyungJoo Ham <myungjoo.ham@samsung.com> | 2012-04-20 01:16:26 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-04-20 12:24:03 -0400 |
commit | bde68e60b18208978c50c6fb9bdf29826d2887f3 (patch) | |
tree | 62b48807d48c2454c374bede680c9e9c6cf71a0d /drivers/extcon | |
parent | 806d9dd71ff52ef09764585baaeec23afbb98560 (diff) |
Extcon: support mutually exclusive relation between cables.
There could be cables that t recannot be attaches simulatenously. Extcon
device drivers may express such information via mutually_exclusive in
struct extcon_dev.
For example, for an extcon device with 16 cables (bits 0 to 15 are
available), if mutually_exclusive = { 0x7, 0xC0, 0x81, 0 }, then, the
following attachments are prohibitted.
{0, 1}
{0, 2}
{1, 2}
{6, 7}
{0, 7}
and every attachment set that are superset of one of the above.
For the detail, please refer to linux/include/linux/extcon.h.
The concept is suggested by NeilBrown <neilb@suse.de>
Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
--
Changes from V5:
- Updated sysfs format
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/extcon')
-rw-r--r-- | drivers/extcon/extcon_class.c | 123 |
1 files changed, 115 insertions, 8 deletions
diff --git a/drivers/extcon/extcon_class.c b/drivers/extcon/extcon_class.c index 403933bc3e9c..3bc4b8af46cf 100644 --- a/drivers/extcon/extcon_class.c +++ b/drivers/extcon/extcon_class.c | |||
@@ -72,6 +72,39 @@ static struct class_compat *switch_class; | |||
72 | static LIST_HEAD(extcon_dev_list); | 72 | static LIST_HEAD(extcon_dev_list); |
73 | static DEFINE_MUTEX(extcon_dev_list_lock); | 73 | static DEFINE_MUTEX(extcon_dev_list_lock); |
74 | 74 | ||
75 | /** | ||
76 | * check_mutually_exclusive - Check if new_state violates mutually_exclusive | ||
77 | * condition. | ||
78 | * @edev: the extcon device | ||
79 | * @new_state: new cable attach status for @edev | ||
80 | * | ||
81 | * Returns 0 if nothing violates. Returns the index + 1 for the first | ||
82 | * violated condition. | ||
83 | */ | ||
84 | static int check_mutually_exclusive(struct extcon_dev *edev, u32 new_state) | ||
85 | { | ||
86 | int i = 0; | ||
87 | |||
88 | if (!edev->mutually_exclusive) | ||
89 | return 0; | ||
90 | |||
91 | for (i = 0; edev->mutually_exclusive[i]; i++) { | ||
92 | int count = 0, j; | ||
93 | u32 correspondants = new_state & edev->mutually_exclusive[i]; | ||
94 | u32 exp = 1; | ||
95 | |||
96 | for (j = 0; j < 32; j++) { | ||
97 | if (exp & correspondants) | ||
98 | count++; | ||
99 | if (count > 1) | ||
100 | return i + 1; | ||
101 | exp <<= 1; | ||
102 | } | ||
103 | } | ||
104 | |||
105 | return 0; | ||
106 | } | ||
107 | |||
75 | static ssize_t state_show(struct device *dev, struct device_attribute *attr, | 108 | static ssize_t state_show(struct device *dev, struct device_attribute *attr, |
76 | char *buf) | 109 | char *buf) |
77 | { | 110 | { |
@@ -100,7 +133,7 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr, | |||
100 | return count; | 133 | return count; |
101 | } | 134 | } |
102 | 135 | ||
103 | void extcon_set_state(struct extcon_dev *edev, u32 state); | 136 | int extcon_set_state(struct extcon_dev *edev, u32 state); |
104 | static ssize_t state_store(struct device *dev, struct device_attribute *attr, | 137 | static ssize_t state_store(struct device *dev, struct device_attribute *attr, |
105 | const char *buf, size_t count) | 138 | const char *buf, size_t count) |
106 | { | 139 | { |
@@ -112,7 +145,7 @@ static ssize_t state_store(struct device *dev, struct device_attribute *attr, | |||
112 | if (ret == 0) | 145 | if (ret == 0) |
113 | ret = -EINVAL; | 146 | ret = -EINVAL; |
114 | else | 147 | else |
115 | extcon_set_state(edev, state); | 148 | ret = extcon_set_state(edev, state); |
116 | 149 | ||
117 | if (ret < 0) | 150 | if (ret < 0) |
118 | return ret; | 151 | return ret; |
@@ -191,7 +224,7 @@ static ssize_t cable_state_store(struct device *dev, | |||
191 | * Note that the notifier provides which bits are changed in the state | 224 | * Note that the notifier provides which bits are changed in the state |
192 | * variable with the val parameter (second) to the callback. | 225 | * variable with the val parameter (second) to the callback. |
193 | */ | 226 | */ |
194 | void extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) | 227 | int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) |
195 | { | 228 | { |
196 | char name_buf[120]; | 229 | char name_buf[120]; |
197 | char state_buf[120]; | 230 | char state_buf[120]; |
@@ -206,6 +239,12 @@ void extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) | |||
206 | if (edev->state != ((edev->state & ~mask) | (state & mask))) { | 239 | if (edev->state != ((edev->state & ~mask) | (state & mask))) { |
207 | u32 old_state = edev->state; | 240 | u32 old_state = edev->state; |
208 | 241 | ||
242 | if (check_mutually_exclusive(edev, (edev->state & ~mask) | | ||
243 | (state & mask))) { | ||
244 | spin_unlock_irqrestore(&edev->lock, flags); | ||
245 | return -EPERM; | ||
246 | } | ||
247 | |||
209 | edev->state &= ~mask; | 248 | edev->state &= ~mask; |
210 | edev->state |= state & mask; | 249 | edev->state |= state & mask; |
211 | 250 | ||
@@ -247,6 +286,8 @@ void extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) | |||
247 | /* No changes */ | 286 | /* No changes */ |
248 | spin_unlock_irqrestore(&edev->lock, flags); | 287 | spin_unlock_irqrestore(&edev->lock, flags); |
249 | } | 288 | } |
289 | |||
290 | return 0; | ||
250 | } | 291 | } |
251 | EXPORT_SYMBOL_GPL(extcon_update_state); | 292 | EXPORT_SYMBOL_GPL(extcon_update_state); |
252 | 293 | ||
@@ -258,9 +299,9 @@ EXPORT_SYMBOL_GPL(extcon_update_state); | |||
258 | * Note that notifier provides which bits are changed in the state | 299 | * Note that notifier provides which bits are changed in the state |
259 | * variable with the val parameter (second) to the callback. | 300 | * variable with the val parameter (second) to the callback. |
260 | */ | 301 | */ |
261 | void extcon_set_state(struct extcon_dev *edev, u32 state) | 302 | int extcon_set_state(struct extcon_dev *edev, u32 state) |
262 | { | 303 | { |
263 | extcon_update_state(edev, 0xffffffff, state); | 304 | return extcon_update_state(edev, 0xffffffff, state); |
264 | } | 305 | } |
265 | EXPORT_SYMBOL_GPL(extcon_set_state); | 306 | EXPORT_SYMBOL_GPL(extcon_set_state); |
266 | 307 | ||
@@ -334,8 +375,7 @@ int extcon_set_cable_state_(struct extcon_dev *edev, | |||
334 | return -EINVAL; | 375 | return -EINVAL; |
335 | 376 | ||
336 | state = cable_state ? (1 << index) : 0; | 377 | state = cable_state ? (1 << index) : 0; |
337 | extcon_update_state(edev, 1 << index, state); | 378 | return extcon_update_state(edev, 1 << index, state); |
338 | return 0; | ||
339 | } | 379 | } |
340 | EXPORT_SYMBOL_GPL(extcon_set_cable_state_); | 380 | EXPORT_SYMBOL_GPL(extcon_set_cable_state_); |
341 | 381 | ||
@@ -511,6 +551,14 @@ static void extcon_cleanup(struct extcon_dev *edev, bool skip) | |||
511 | if (!skip && get_device(edev->dev)) { | 551 | if (!skip && get_device(edev->dev)) { |
512 | int index; | 552 | int index; |
513 | 553 | ||
554 | if (edev->mutually_exclusive && edev->max_supported) { | ||
555 | for (index = 0; edev->mutually_exclusive[index]; | ||
556 | index++) | ||
557 | kfree(edev->d_attrs_muex[index].attr.name); | ||
558 | kfree(edev->d_attrs_muex); | ||
559 | kfree(edev->attrs_muex); | ||
560 | } | ||
561 | |||
514 | for (index = 0; index < edev->max_supported; index++) | 562 | for (index = 0; index < edev->max_supported; index++) |
515 | kfree(edev->cables[index].attr_g.name); | 563 | kfree(edev->cables[index].attr_g.name); |
516 | 564 | ||
@@ -533,6 +581,7 @@ static void extcon_dev_release(struct device *dev) | |||
533 | extcon_cleanup(edev, true); | 581 | extcon_cleanup(edev, true); |
534 | } | 582 | } |
535 | 583 | ||
584 | static const char *muex_name = "mutually_exclusive"; | ||
536 | static void dummy_sysfs_dev_release(struct device *dev) | 585 | static void dummy_sysfs_dev_release(struct device *dev) |
537 | { | 586 | { |
538 | } | 587 | } |
@@ -625,10 +674,58 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev) | |||
625 | } | 674 | } |
626 | } | 675 | } |
627 | 676 | ||
677 | if (edev->max_supported && edev->mutually_exclusive) { | ||
678 | char buf[80]; | ||
679 | char *name; | ||
680 | |||
681 | /* Count the size of mutually_exclusive array */ | ||
682 | for (index = 0; edev->mutually_exclusive[index]; index++) | ||
683 | ; | ||
684 | |||
685 | edev->attrs_muex = kzalloc(sizeof(struct attribute *) * | ||
686 | (index + 1), GFP_KERNEL); | ||
687 | if (!edev->attrs_muex) { | ||
688 | ret = -ENOMEM; | ||
689 | goto err_muex; | ||
690 | } | ||
691 | |||
692 | edev->d_attrs_muex = kzalloc(sizeof(struct device_attribute) * | ||
693 | index, GFP_KERNEL); | ||
694 | if (!edev->d_attrs_muex) { | ||
695 | ret = -ENOMEM; | ||
696 | kfree(edev->attrs_muex); | ||
697 | goto err_muex; | ||
698 | } | ||
699 | |||
700 | for (index = 0; edev->mutually_exclusive[index]; index++) { | ||
701 | sprintf(buf, "0x%x", edev->mutually_exclusive[index]); | ||
702 | name = kzalloc(sizeof(char) * (strlen(buf) + 1), | ||
703 | GFP_KERNEL); | ||
704 | if (!name) { | ||
705 | for (index--; index >= 0; index--) { | ||
706 | kfree(edev->d_attrs_muex[index].attr. | ||
707 | name); | ||
708 | } | ||
709 | kfree(edev->d_attrs_muex); | ||
710 | kfree(edev->attrs_muex); | ||
711 | ret = -ENOMEM; | ||
712 | goto err_muex; | ||
713 | } | ||
714 | strcpy(name, buf); | ||
715 | edev->d_attrs_muex[index].attr.name = name; | ||
716 | edev->d_attrs_muex[index].attr.mode = 0000; | ||
717 | edev->attrs_muex[index] = &edev->d_attrs_muex[index] | ||
718 | .attr; | ||
719 | } | ||
720 | edev->attr_g_muex.name = muex_name; | ||
721 | edev->attr_g_muex.attrs = edev->attrs_muex; | ||
722 | |||
723 | } | ||
724 | |||
628 | if (edev->max_supported) { | 725 | if (edev->max_supported) { |
629 | edev->extcon_dev_type.groups = | 726 | edev->extcon_dev_type.groups = |
630 | kzalloc(sizeof(struct attribute_group *) * | 727 | kzalloc(sizeof(struct attribute_group *) * |
631 | (edev->max_supported + 1), GFP_KERNEL); | 728 | (edev->max_supported + 2), GFP_KERNEL); |
632 | if (!edev->extcon_dev_type.groups) { | 729 | if (!edev->extcon_dev_type.groups) { |
633 | ret = -ENOMEM; | 730 | ret = -ENOMEM; |
634 | goto err_alloc_groups; | 731 | goto err_alloc_groups; |
@@ -640,6 +737,9 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev) | |||
640 | for (index = 0; index < edev->max_supported; index++) | 737 | for (index = 0; index < edev->max_supported; index++) |
641 | edev->extcon_dev_type.groups[index] = | 738 | edev->extcon_dev_type.groups[index] = |
642 | &edev->cables[index].attr_g; | 739 | &edev->cables[index].attr_g; |
740 | if (edev->mutually_exclusive) | ||
741 | edev->extcon_dev_type.groups[index] = | ||
742 | &edev->attr_g_muex; | ||
643 | 743 | ||
644 | edev->dev->type = &edev->extcon_dev_type; | 744 | edev->dev->type = &edev->extcon_dev_type; |
645 | } | 745 | } |
@@ -672,6 +772,13 @@ err_dev: | |||
672 | if (edev->max_supported) | 772 | if (edev->max_supported) |
673 | kfree(edev->extcon_dev_type.groups); | 773 | kfree(edev->extcon_dev_type.groups); |
674 | err_alloc_groups: | 774 | err_alloc_groups: |
775 | if (edev->max_supported && edev->mutually_exclusive) { | ||
776 | for (index = 0; edev->mutually_exclusive[index]; index++) | ||
777 | kfree(edev->d_attrs_muex[index].attr.name); | ||
778 | kfree(edev->d_attrs_muex); | ||
779 | kfree(edev->attrs_muex); | ||
780 | } | ||
781 | err_muex: | ||
675 | for (index = 0; index < edev->max_supported; index++) | 782 | for (index = 0; index < edev->max_supported; index++) |
676 | kfree(edev->cables[index].attr_g.name); | 783 | kfree(edev->cables[index].attr_g.name); |
677 | err_alloc_cables: | 784 | err_alloc_cables: |