aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMyungJoo Ham <myungjoo.ham@samsung.com>2012-04-20 01:16:26 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-04-20 12:24:03 -0400
commitbde68e60b18208978c50c6fb9bdf29826d2887f3 (patch)
tree62b48807d48c2454c374bede680c9e9c6cf71a0d
parent806d9dd71ff52ef09764585baaeec23afbb98560 (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>
-rw-r--r--Documentation/ABI/testing/sysfs-class-extcon22
-rw-r--r--drivers/extcon/extcon_class.c123
-rw-r--r--include/linux/extcon.h28
3 files changed, 160 insertions, 13 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-extcon b/Documentation/ABI/testing/sysfs-class-extcon
index 19b18e986a57..20ab361bd8c6 100644
--- a/Documentation/ABI/testing/sysfs-class-extcon
+++ b/Documentation/ABI/testing/sysfs-class-extcon
@@ -15,6 +15,10 @@ Description:
15 may have both HDMI and Charger attached, or analog audio, 15 may have both HDMI and Charger attached, or analog audio,
16 video, and USB cables attached simulteneously. 16 video, and USB cables attached simulteneously.
17 17
18 If there are cables mutually exclusive with each other,
19 such binary relations may be expressed with extcon_dev's
20 mutually_exclusive array.
21
18What: /sys/class/extcon/.../name 22What: /sys/class/extcon/.../name
19Date: February 2012 23Date: February 2012
20Contact: MyungJoo Ham <myungjoo.ham@samsung.com> 24Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
@@ -73,3 +77,21 @@ Description:
73 state of cable "x" (integer between 0 and 31) of an extcon 77 state of cable "x" (integer between 0 and 31) of an extcon
74 device. The state value is either 0 (detached) or 1 78 device. The state value is either 0 (detached) or 1
75 (attached). 79 (attached).
80
81What: /sys/class/extcon/.../mutually_exclusive/...
82Date: December 2011
83Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
84Description:
85 Shows the relations of mutually exclusiveness. For example,
86 if the mutually_exclusive array of extcon_dev is
87 {0x3, 0x5, 0xC, 0x0}, the, the output is:
88 # ls mutually_exclusive/
89 0x3
90 0x5
91 0xc
92 #
93
94 Note that mutually_exclusive is a sub-directory of the extcon
95 device and the file names under the mutually_exclusive
96 directory show the mutually-exclusive sets, not the contents
97 of the files.
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;
72static LIST_HEAD(extcon_dev_list); 72static LIST_HEAD(extcon_dev_list);
73static DEFINE_MUTEX(extcon_dev_list_lock); 73static 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 */
84static 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
75static ssize_t state_show(struct device *dev, struct device_attribute *attr, 108static 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
103void extcon_set_state(struct extcon_dev *edev, u32 state); 136int extcon_set_state(struct extcon_dev *edev, u32 state);
104static ssize_t state_store(struct device *dev, struct device_attribute *attr, 137static 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 */
194void extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) 227int 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}
251EXPORT_SYMBOL_GPL(extcon_update_state); 292EXPORT_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 */
261void extcon_set_state(struct extcon_dev *edev, u32 state) 302int 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}
265EXPORT_SYMBOL_GPL(extcon_set_state); 306EXPORT_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}
340EXPORT_SYMBOL_GPL(extcon_set_cable_state_); 380EXPORT_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
584static const char *muex_name = "mutually_exclusive";
536static void dummy_sysfs_dev_release(struct device *dev) 585static 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);
674err_alloc_groups: 774err_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 }
781err_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);
677err_alloc_cables: 784err_alloc_cables:
diff --git a/include/linux/extcon.h b/include/linux/extcon.h
index 20e24b32a17d..6495f7731400 100644
--- a/include/linux/extcon.h
+++ b/include/linux/extcon.h
@@ -78,6 +78,14 @@ struct extcon_cable;
78 * @supported_cable Array of supported cable name ending with NULL. 78 * @supported_cable Array of supported cable name ending with NULL.
79 * If supported_cable is NULL, cable name related APIs 79 * If supported_cable is NULL, cable name related APIs
80 * are disabled. 80 * are disabled.
81 * @mutually_exclusive Array of mutually exclusive set of cables that cannot
82 * be attached simultaneously. The array should be
83 * ending with NULL or be NULL (no mutually exclusive
84 * cables). For example, if it is { 0x7, 0x30, 0}, then,
85 * {0, 1}, {0, 1, 2}, {0, 2}, {1, 2}, or {4, 5} cannot
86 * be attached simulataneously. {0x7, 0} is equivalent to
87 * {0x3, 0x6, 0x5, 0}. If it is {0xFFFFFFFF, 0}, there
88 * can be no simultaneous connections.
81 * @print_name An optional callback to override the method to print the 89 * @print_name An optional callback to override the method to print the
82 * name of the extcon device. 90 * name of the extcon device.
83 * @print_state An optional callback to override the method to print the 91 * @print_state An optional callback to override the method to print the
@@ -103,6 +111,7 @@ struct extcon_dev {
103 /* --- Optional user initializing data --- */ 111 /* --- Optional user initializing data --- */
104 const char *name; 112 const char *name;
105 const char **supported_cable; 113 const char **supported_cable;
114 const u32 *mutually_exclusive;
106 115
107 /* --- Optional callbacks to override class functions --- */ 116 /* --- Optional callbacks to override class functions --- */
108 ssize_t (*print_name)(struct extcon_dev *edev, char *buf); 117 ssize_t (*print_name)(struct extcon_dev *edev, char *buf);
@@ -119,6 +128,10 @@ struct extcon_dev {
119 /* /sys/class/extcon/.../cable.n/... */ 128 /* /sys/class/extcon/.../cable.n/... */
120 struct device_type extcon_dev_type; 129 struct device_type extcon_dev_type;
121 struct extcon_cable *cables; 130 struct extcon_cable *cables;
131 /* /sys/class/extcon/.../mutually_exclusive/... */
132 struct attribute_group attr_g_muex;
133 struct attribute **attrs_muex;
134 struct device_attribute *d_attrs_muex;
122}; 135};
123 136
124/** 137/**
@@ -179,8 +192,8 @@ static inline u32 extcon_get_state(struct extcon_dev *edev)
179 return edev->state; 192 return edev->state;
180} 193}
181 194
182extern void extcon_set_state(struct extcon_dev *edev, u32 state); 195extern int extcon_set_state(struct extcon_dev *edev, u32 state);
183extern void extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state); 196extern int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state);
184 197
185/* 198/*
186 * get/set_cable_state access each bit of the 32b encoded state value. 199 * get/set_cable_state access each bit of the 32b encoded state value.
@@ -235,11 +248,16 @@ static inline u32 extcon_get_state(struct extcon_dev *edev)
235 return 0; 248 return 0;
236} 249}
237 250
238static inline void extcon_set_state(struct extcon_dev *edev, u32 state) { } 251static inline int extcon_set_state(struct extcon_dev *edev, u32 state)
252{
253 return 0;
254}
239 255
240static inline void extcon_update_state(struct extcon_dev *edev, u32 mask, 256static inline int extcon_update_state(struct extcon_dev *edev, u32 mask,
241 u32 state) 257 u32 state)
242{ } 258{
259 return 0;
260}
243 261
244static inline int extcon_find_cable_index(struct extcon_dev *edev, 262static inline int extcon_find_cable_index(struct extcon_dev *edev,
245 const char *cable_name) 263 const char *cable_name)