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 }