All of lore.kernel.org
 help / color / mirror / Atom feed
From: MyungJoo Ham <myungjoo.ham@samsung.com>
To: Greg KH <gregkh@linuxfoundation.org>
Cc: "Arnd Bergmann" <arnd@arndb.de>,
	LKML <linux-kernel@vger.kernel.org>,
	"Arve Hjønnevag" <arve@android.com>,
	"Kyungmin Park" <kyungmin.park@samsung.com>,
	"Linus Walleij" <linus.walleij@linaro.org>,
	"Dmitry Torokhov" <dmitry.torokhov@gmail.com>,
	"Morten CHRISTIANSEN" <morten.christiansen@stericsson.com>,
	"Mark Brown" <broonie@opensource.wolfsonmicro.com>,
	"John Stultz" <john.stultz@linaro.org>,
	myungjoo.ham@gmail.com, cw00.choi@samsung.com
Subject: [PATCH v8 resend 5/6] Extcon: support mutually exclusive relation between cables.
Date: Fri, 20 Apr 2012 14:16:26 +0900	[thread overview]
Message-ID: <1334898987-7800-6-git-send-email-myungjoo.ham@samsung.com> (raw)
In-Reply-To: <1334898987-7800-1-git-send-email-myungjoo.ham@samsung.com>

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
---
 Documentation/ABI/testing/sysfs-class-extcon |   22 +++++
 drivers/extcon/extcon_class.c                |  123 ++++++++++++++++++++++++--
 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 19b18e9..20ab361 100644
--- a/Documentation/ABI/testing/sysfs-class-extcon
+++ b/Documentation/ABI/testing/sysfs-class-extcon
@@ -15,6 +15,10 @@ Description:
 		may have both HDMI and Charger attached, or analog audio,
 		video, and USB cables attached simulteneously.
 
+		If there are cables mutually exclusive with each other,
+		such binary relations may be expressed with extcon_dev's
+		mutually_exclusive array.
+
 What:		/sys/class/extcon/.../name
 Date:		February 2012
 Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
@@ -73,3 +77,21 @@ Description:
 		state of cable "x" (integer between 0 and 31) of an extcon
 		device. The state value is either 0 (detached) or 1
 		(attached).
+
+What:		/sys/class/extcon/.../mutually_exclusive/...
+Date:		December 2011
+Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
+Description:
+		Shows the relations of mutually exclusiveness. For example,
+		if the mutually_exclusive array of extcon_dev is
+		{0x3, 0x5, 0xC, 0x0}, the, the output is:
+		# ls mutually_exclusive/
+		0x3
+		0x5
+		0xc
+		#
+
+		Note that mutually_exclusive is a sub-directory of the extcon
+		device and the file names under the mutually_exclusive
+		directory show the mutually-exclusive sets, not the contents
+		of the files.
diff --git a/drivers/extcon/extcon_class.c b/drivers/extcon/extcon_class.c
index 403933b..3bc4b8a 100644
--- a/drivers/extcon/extcon_class.c
+++ b/drivers/extcon/extcon_class.c
@@ -72,6 +72,39 @@ static struct class_compat *switch_class;
 static LIST_HEAD(extcon_dev_list);
 static DEFINE_MUTEX(extcon_dev_list_lock);
 
+/**
+ * check_mutually_exclusive - Check if new_state violates mutually_exclusive
+ *			    condition.
+ * @edev:	the extcon device
+ * @new_state:	new cable attach status for @edev
+ *
+ * Returns 0 if nothing violates. Returns the index + 1 for the first
+ * violated condition.
+ */
+static int check_mutually_exclusive(struct extcon_dev *edev, u32 new_state)
+{
+	int i = 0;
+
+	if (!edev->mutually_exclusive)
+		return 0;
+
+	for (i = 0; edev->mutually_exclusive[i]; i++) {
+		int count = 0, j;
+		u32 correspondants = new_state & edev->mutually_exclusive[i];
+		u32 exp = 1;
+
+		for (j = 0; j < 32; j++) {
+			if (exp & correspondants)
+				count++;
+			if (count > 1)
+				return i + 1;
+			exp <<= 1;
+		}
+	}
+
+	return 0;
+}
+
 static ssize_t state_show(struct device *dev, struct device_attribute *attr,
 			  char *buf)
 {
@@ -100,7 +133,7 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,
 	return count;
 }
 
-void extcon_set_state(struct extcon_dev *edev, u32 state);
+int extcon_set_state(struct extcon_dev *edev, u32 state);
 static ssize_t state_store(struct device *dev, struct device_attribute *attr,
 			   const char *buf, size_t count)
 {
@@ -112,7 +145,7 @@ static ssize_t state_store(struct device *dev, struct device_attribute *attr,
 	if (ret == 0)
 		ret = -EINVAL;
 	else
-		extcon_set_state(edev, state);
+		ret = extcon_set_state(edev, state);
 
 	if (ret < 0)
 		return ret;
@@ -191,7 +224,7 @@ static ssize_t cable_state_store(struct device *dev,
  * Note that the notifier provides which bits are changed in the state
  * variable with the val parameter (second) to the callback.
  */
-void extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
+int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
 {
 	char name_buf[120];
 	char state_buf[120];
@@ -206,6 +239,12 @@ void extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
 	if (edev->state != ((edev->state & ~mask) | (state & mask))) {
 		u32 old_state = edev->state;
 
+		if (check_mutually_exclusive(edev, (edev->state & ~mask) |
+						   (state & mask))) {
+			spin_unlock_irqrestore(&edev->lock, flags);
+			return -EPERM;
+		}
+
 		edev->state &= ~mask;
 		edev->state |= state & mask;
 
@@ -247,6 +286,8 @@ void extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
 		/* No changes */
 		spin_unlock_irqrestore(&edev->lock, flags);
 	}
+
+	return 0;
 }
 EXPORT_SYMBOL_GPL(extcon_update_state);
 
@@ -258,9 +299,9 @@ EXPORT_SYMBOL_GPL(extcon_update_state);
  * Note that notifier provides which bits are changed in the state
  * variable with the val parameter (second) to the callback.
  */
-void extcon_set_state(struct extcon_dev *edev, u32 state)
+int extcon_set_state(struct extcon_dev *edev, u32 state)
 {
-	extcon_update_state(edev, 0xffffffff, state);
+	return extcon_update_state(edev, 0xffffffff, state);
 }
 EXPORT_SYMBOL_GPL(extcon_set_state);
 
@@ -334,8 +375,7 @@ int extcon_set_cable_state_(struct extcon_dev *edev,
 		return -EINVAL;
 
 	state = cable_state ? (1 << index) : 0;
-	extcon_update_state(edev, 1 << index, state);
-	return 0;
+	return extcon_update_state(edev, 1 << index, state);
 }
 EXPORT_SYMBOL_GPL(extcon_set_cable_state_);
 
@@ -511,6 +551,14 @@ static void extcon_cleanup(struct extcon_dev *edev, bool skip)
 	if (!skip && get_device(edev->dev)) {
 		int index;
 
+		if (edev->mutually_exclusive && edev->max_supported) {
+			for (index = 0; edev->mutually_exclusive[index];
+			     index++)
+				kfree(edev->d_attrs_muex[index].attr.name);
+			kfree(edev->d_attrs_muex);
+			kfree(edev->attrs_muex);
+		}
+
 		for (index = 0; index < edev->max_supported; index++)
 			kfree(edev->cables[index].attr_g.name);
 
@@ -533,6 +581,7 @@ static void extcon_dev_release(struct device *dev)
 	extcon_cleanup(edev, true);
 }
 
+static const char *muex_name = "mutually_exclusive";
 static void dummy_sysfs_dev_release(struct device *dev)
 {
 }
@@ -625,10 +674,58 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
 		}
 	}
 
+	if (edev->max_supported && edev->mutually_exclusive) {
+		char buf[80];
+		char *name;
+
+		/* Count the size of mutually_exclusive array */
+		for (index = 0; edev->mutually_exclusive[index]; index++)
+			;
+
+		edev->attrs_muex = kzalloc(sizeof(struct attribute *) *
+					   (index + 1), GFP_KERNEL);
+		if (!edev->attrs_muex) {
+			ret = -ENOMEM;
+			goto err_muex;
+		}
+
+		edev->d_attrs_muex = kzalloc(sizeof(struct device_attribute) *
+					     index, GFP_KERNEL);
+		if (!edev->d_attrs_muex) {
+			ret = -ENOMEM;
+			kfree(edev->attrs_muex);
+			goto err_muex;
+		}
+
+		for (index = 0; edev->mutually_exclusive[index]; index++) {
+			sprintf(buf, "0x%x", edev->mutually_exclusive[index]);
+			name = kzalloc(sizeof(char) * (strlen(buf) + 1),
+				       GFP_KERNEL);
+			if (!name) {
+				for (index--; index >= 0; index--) {
+					kfree(edev->d_attrs_muex[index].attr.
+					      name);
+				}
+				kfree(edev->d_attrs_muex);
+				kfree(edev->attrs_muex);
+				ret = -ENOMEM;
+				goto err_muex;
+			}
+			strcpy(name, buf);
+			edev->d_attrs_muex[index].attr.name = name;
+			edev->d_attrs_muex[index].attr.mode = 0000;
+			edev->attrs_muex[index] = &edev->d_attrs_muex[index]
+							.attr;
+		}
+		edev->attr_g_muex.name = muex_name;
+		edev->attr_g_muex.attrs = edev->attrs_muex;
+
+	}
+
 	if (edev->max_supported) {
 		edev->extcon_dev_type.groups =
 			kzalloc(sizeof(struct attribute_group *) *
-				(edev->max_supported + 1), GFP_KERNEL);
+				(edev->max_supported + 2), GFP_KERNEL);
 		if (!edev->extcon_dev_type.groups) {
 			ret = -ENOMEM;
 			goto err_alloc_groups;
@@ -640,6 +737,9 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
 		for (index = 0; index < edev->max_supported; index++)
 			edev->extcon_dev_type.groups[index] =
 				&edev->cables[index].attr_g;
+		if (edev->mutually_exclusive)
+			edev->extcon_dev_type.groups[index] =
+				&edev->attr_g_muex;
 
 		edev->dev->type = &edev->extcon_dev_type;
 	}
@@ -672,6 +772,13 @@ err_dev:
 	if (edev->max_supported)
 		kfree(edev->extcon_dev_type.groups);
 err_alloc_groups:
+	if (edev->max_supported && edev->mutually_exclusive) {
+		for (index = 0; edev->mutually_exclusive[index]; index++)
+			kfree(edev->d_attrs_muex[index].attr.name);
+		kfree(edev->d_attrs_muex);
+		kfree(edev->attrs_muex);
+	}
+err_muex:
 	for (index = 0; index < edev->max_supported; index++)
 		kfree(edev->cables[index].attr_g.name);
 err_alloc_cables:
diff --git a/include/linux/extcon.h b/include/linux/extcon.h
index 20e24b3..6495f77 100644
--- a/include/linux/extcon.h
+++ b/include/linux/extcon.h
@@ -78,6 +78,14 @@ struct extcon_cable;
  * @supported_cable	Array of supported cable name ending with NULL.
  *			If supported_cable is NULL, cable name related APIs
  *			are disabled.
+ * @mutually_exclusive	Array of mutually exclusive set of cables that cannot
+ *			be attached simultaneously. The array should be
+ *			ending with NULL or be NULL (no mutually exclusive
+ *			cables). For example, if it is { 0x7, 0x30, 0}, then,
+ *			{0, 1}, {0, 1, 2}, {0, 2}, {1, 2}, or {4, 5} cannot
+ *			be attached simulataneously. {0x7, 0} is equivalent to
+ *			{0x3, 0x6, 0x5, 0}. If it is {0xFFFFFFFF, 0}, there
+ *			can be no simultaneous connections.
  * @print_name	An optional callback to override the method to print the
  *		name of the extcon device.
  * @print_state	An optional callback to override the method to print the
@@ -103,6 +111,7 @@ struct extcon_dev {
 	/* --- Optional user initializing data --- */
 	const char	*name;
 	const char **supported_cable;
+	const u32	*mutually_exclusive;
 
 	/* --- Optional callbacks to override class functions --- */
 	ssize_t	(*print_name)(struct extcon_dev *edev, char *buf);
@@ -119,6 +128,10 @@ struct extcon_dev {
 	/* /sys/class/extcon/.../cable.n/... */
 	struct device_type extcon_dev_type;
 	struct extcon_cable *cables;
+	/* /sys/class/extcon/.../mutually_exclusive/... */
+	struct attribute_group attr_g_muex;
+	struct attribute **attrs_muex;
+	struct device_attribute *d_attrs_muex;
 };
 
 /**
@@ -179,8 +192,8 @@ static inline u32 extcon_get_state(struct extcon_dev *edev)
 	return edev->state;
 }
 
-extern void extcon_set_state(struct extcon_dev *edev, u32 state);
-extern void extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state);
+extern int extcon_set_state(struct extcon_dev *edev, u32 state);
+extern int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state);
 
 /*
  * 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)
 	return 0;
 }
 
-static inline void extcon_set_state(struct extcon_dev *edev, u32 state) { }
+static inline int extcon_set_state(struct extcon_dev *edev, u32 state)
+{
+	return 0;
+}
 
-static inline void extcon_update_state(struct extcon_dev *edev, u32 mask,
+static inline int extcon_update_state(struct extcon_dev *edev, u32 mask,
 				       u32 state)
-{ }
+{
+	return 0;
+}
 
 static inline int extcon_find_cable_index(struct extcon_dev *edev,
 					  const char *cable_name)
-- 
1.7.4.1


  parent reply	other threads:[~2012-04-20  5:16 UTC|newest]

Thread overview: 51+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-03-23  8:52 [PATCH v7 0/6] Introduce External Connector Class (extcon) MyungJoo Ham
2012-03-23  8:52 ` [PATCH v7 1/6] Extcon (external connector): import Android's switch class and modify MyungJoo Ham
2012-04-18 21:47   ` Greg KH
2012-04-18 23:38     ` Kyungmin Park
2012-04-18 23:40       ` Greg KH
2012-04-18 23:48         ` Kyungmin Park
2012-04-19  0:01           ` Greg KH
2012-04-19  2:36     ` MyungJoo Ham
2012-04-19  2:41     ` [PATCH v8 0/6] Introduce External Connector Class (extcon) MyungJoo Ham
2012-04-19  2:41       ` [PATCH v8 1/6] Extcon (external connector): import Android's switch class and modify MyungJoo Ham
2012-04-19 11:38         ` Mark Brown
2012-04-20  2:37         ` Greg KH
2012-04-20  2:46           ` Kyungmin Park
2012-04-20  2:49             ` Greg KH
2012-04-20  2:57               ` Kyungmin Park
2012-04-20  3:00                 ` Greg KH
2012-04-20  3:08                   ` Kyungmin Park
2012-04-20  3:32                     ` Greg KH
2012-04-20  5:16                       ` [PATCH v8 resend 0/6] Introduce External Connector Class (extcon) MyungJoo Ham
2012-04-20  5:16                         ` [PATCH v8 resend 1/6] Extcon (external connector): import Android's switch class and modify MyungJoo Ham
2012-04-20  7:00                           ` Ryan Mallon
2012-04-20  5:16                         ` [PATCH v8 resend 2/6] Extcon: support generic GPIO extcon driver MyungJoo Ham
2012-04-20  5:16                         ` [PATCH v8 resend 3/6] Extcon: support notification based on the state changes MyungJoo Ham
2012-04-20  5:16                         ` [PATCH v8 resend 4/6] Extcon: support multiple states at a device MyungJoo Ham
2012-04-20  5:16                         ` MyungJoo Ham [this message]
2012-04-20  5:16                         ` [PATCH v8 resend 6/6] Documentation/extcon: porting guide for Android kernel switch driver MyungJoo Ham
2012-04-20 16:25                           ` Greg KH
2012-04-20 17:05                             ` Mark Brown
2012-04-20 17:15                               ` Greg KH
2012-04-20 21:59                                 ` Mark Brown
2012-04-20 16:27                         ` [PATCH v8 resend 0/6] Introduce External Connector Class (extcon) Greg KH
2012-04-23 11:18                           ` MyungJoo Ham
2012-04-23 11:19                             ` [PATCH] Remove "switch" class in drivers/staging/android/switch MyungJoo Ham
2012-04-23 20:25                               ` Greg KH
2012-04-19  2:41       ` [PATCH v8 2/6] Extcon: support generic GPIO extcon driver MyungJoo Ham
2012-04-19 11:40         ` Mark Brown
2012-04-19  2:41       ` [PATCH v8 3/6] Extcon: support notification based on the state changes MyungJoo Ham
2012-04-19  2:41       ` [PATCH v8 4/6] Extcon: support multiple states at a device MyungJoo Ham
2012-04-19  2:41       ` [PATCH v8 5/6] Extcon: support mutually exclusive relation between cables MyungJoo Ham
2012-04-19  2:41       ` [PATCH v8 6/6] Documentation/extcon: porting guide for Android kernel switch driver MyungJoo Ham
2012-03-23  8:52 ` [PATCH v7 2/6] Extcon: support generic GPIO extcon driver MyungJoo Ham
2012-03-23  8:53 ` [PATCH v7 3/6] Extcon: support notification based on the state changes MyungJoo Ham
2012-03-23  8:53 ` [PATCH v7 4/6] Extcon: support multiple states at a device MyungJoo Ham
2012-03-23  8:53 ` [PATCH v7 5/6] Extcon: support mutually exclusive relation between cables MyungJoo Ham
2012-03-23  8:53 ` [PATCH v7 6/6] Documentation/extcon: porting guide for Android kernel switch driver MyungJoo Ham
2012-03-23  8:53 ` [RFC PATCH] Remove "switch" class in drivers/staging/android/switch MyungJoo Ham
2012-03-28  9:39 ` [PATCH v7 0/6] Introduce External Connector Class (extcon) MyungJoo Ham
2012-03-28 14:44   ` Greg KH
2012-03-28 15:24     ` Arnd Bergmann
2012-03-28 15:28       ` Greg KH
2012-03-29  4:20         ` Kyungmin Park

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1334898987-7800-6-git-send-email-myungjoo.ham@samsung.com \
    --to=myungjoo.ham@samsung.com \
    --cc=arnd@arndb.de \
    --cc=arve@android.com \
    --cc=broonie@opensource.wolfsonmicro.com \
    --cc=cw00.choi@samsung.com \
    --cc=dmitry.torokhov@gmail.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=john.stultz@linaro.org \
    --cc=kyungmin.park@samsung.com \
    --cc=linus.walleij@linaro.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=morten.christiansen@stericsson.com \
    --cc=myungjoo.ham@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.