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 | |
| 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>
| -rw-r--r-- | Documentation/ABI/testing/sysfs-class-extcon | 22 | ||||
| -rw-r--r-- | drivers/extcon/extcon_class.c | 123 | ||||
| -rw-r--r-- | include/linux/extcon.h | 28 |
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 | |||
| 18 | What: /sys/class/extcon/.../name | 22 | What: /sys/class/extcon/.../name |
| 19 | Date: February 2012 | 23 | Date: February 2012 |
| 20 | Contact: MyungJoo Ham <myungjoo.ham@samsung.com> | 24 | Contact: 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 | |||
| 81 | What: /sys/class/extcon/.../mutually_exclusive/... | ||
| 82 | Date: December 2011 | ||
| 83 | Contact: MyungJoo Ham <myungjoo.ham@samsung.com> | ||
| 84 | Description: | ||
| 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; | |||
| 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 | } | ||
