All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v7 0/6] Introduce External Connector Class (extcon)
@ 2012-03-23  8:52 MyungJoo Ham
  2012-03-23  8:52 ` [PATCH v7 1/6] Extcon (external connector): import Android's switch class and modify MyungJoo Ham
                   ` (7 more replies)
  0 siblings, 8 replies; 51+ messages in thread
From: MyungJoo Ham @ 2012-03-23  8:52 UTC (permalink / raw)
  To: Arnd Bergmann, LKML
  Cc: NeilBrown, Randy Dunlap, Mike Lockwood, Arve Hjønnevag,
	Kyungmin Park, gregkh, Linus Walleij, Dmitry Torokhov,
	Morten CHRISTIANSEN, Mark Brown, John Stultz, Joerg Roedel,
	myungjoo.ham, cw00.choi

For external connectors, which may have different types of cables attached
(USB, TA, HDMI, Analog A/V, and others), we often have seperated device
drivers that detect the state changes at the port and device drivers that
do something according to the state changes.

An example is a 4-pin (or more pins?) analog audio connector
(usually referred as "3.5-pi" jacks). It can be implemented with the 5/5
patch, "adc-jack", supplied with information about IRQ, a method to get
ADC values, and ADC value ranges. With a 4-pin 3.5-pi port and adc-jack
driver with proper ADC configurations, the following modes may be
detected: "standard mono cable", "standard stereo cable",
"standard stereo + remote cable", "standard stereo + mic + remote cable",
"standard stereo + different-spec mic + remote cable", and such.
Note that for 4-pin cables with mic and remote, there are multiple
standards (at least Samsung and Apple use different configurations for
those 4-pin earphones although the pins looks exactly same.)
and they often can be distinguished with ADC values.

Although we may have simply given different "cable id" for "stereo" and
"stereo + mic". However, for those userspace processes or other device
drivers that are interested only in "stereo" part of possible combinations,
having seperated mode/id for "stereo" and "mic" is more desirable.
For example, if we do not have multistate capabilities, those who want
to know "stereo" is attached or not should monitor everything:
"standard stereo cable", "standard stereo + remove cable",
"standard stereo + mic + remote cable", and others. However, with
multistate capabilities, they only need to monitor the bit associated
with "standard stero".

For another example, when MAX8997-MUIC detects a Charger cable insertion,
another device driver (such as MAX8903 charger, MAX8997 charger, Charger
Manager, or board file) needs to set charger current limit accordingly
and when MAX8997-MUIC detects a HDMI cable insertion, multimedia device
drivers need to do some operations accordingly.


This patchset supports the usage of notifier for passing such information
between device drivers.

Another issue is that at a single switch port, there might be multiple
and heterogeneous cables attached at the same time. Besides the state
(Attached or Detached) of each cable may alter independently. Such extcon
devices that support simultaneous mult-cable attachment include, bot not
limited to, docks, cradles, and 30-pin-like ports (like that of NURI board
in linux/arch/arm/mach-exynos, which resembles Samsung Galaxy Tab series).
For example, the 30-pin port of NURI board may be used to connect analog
audio cables, analog video cable, and a USB or a charger. Because such
cables coonnect part of the 30 pins to external devices/cables, the
possible combinations of external devices/cables vary significantly and
users may fabricate their own design; e.g., an 30-pin adaptor connecting
USB, AC-DC adaptor, HDMI, analog audio, analog video, USB-host, SD-card,
and so on.

In order to address such issues, Android kernel's "Switch" class seems to
be a good basis and we have implemented "Multistate Switch Class" based on
it. The "Switch" class code of Android kernel is GPL as well.


Though there can be synchronization delay (up to 12hours), you can see
the extcon-for-next branch at
http://git.infradead.org/users/kmpark/linux-samsung/shortlog/refs/heads/extcon-for-next

Along with the 6 patches (1/6 ... 6/6), there is one additional RFC patch
that suggests to remove drivers/staging/android/switch.

Summary of changes from v6 patchset
- Added documentation, "porting guide for Android switch device drivers"
- Usage of class_compat to create /sys/class/switch/* along with
/sys/class/extcon/* if CONFIG_ANDROID is enabled. However, we cannot
enable "switch" class_compat if CONFIG_ANDROID_SWITCH is enabled because
we cannot create two classes with the same name.

Summary of changes from v5 patchset
- Excluded adc-jack from the "default" patchset. Adc-jack will be later
added after Extcon is finalized.
- Sysfs style updated
- Bugfixes after using at Exynos4412 boards with userspace processes
- Code stype updated

Summary of changes from v4 patchset
- Bugfixes after testing at Exynos4412 boards with userspace processes
- Added adc-jack to support 3.5-pi jack and other ADC-based cable detection
ports.

Summary of changes from v3 patchset
- Bugfixes after testing at Exynos4412 boards.

Summary of changes from v2 patchset
- Support to express mutually exclusive set of cables
- Revised device/class/sysfs-entries add/removal methods
- Added sysfs entries
- Revised documentation and comments
- Some bugfixes
- Compatible with Android (for extcon devices with "use_class_name_switch" set)

Summary of changes from RFC
- ABI documentation added
- Notifees may get notified for a state change of a specific cable, not
every cable of the corresponding extcon.
- Added kerneldoc comments
- Moved to /drivers/extcon/
- Added helper functions
- Some bugfixes

Donggeun Kim (1):
  Extcon: support notification based on the state changes.

MyungJoo Ham (5):
  Extcon (external connector): import Android's switch class and
    modify.
  Extcon: support generic GPIO extcon driver
  Extcon: support multiple states at a device.
  Extcon: support mutually exclusive relation between cables.
  Documentation/extcon: porting guide for Android kernel switch driver.

 Documentation/ABI/testing/sysfs-class-extcon      |   97 +++
 Documentation/extcon/porting-android-switch-class |  124 +++
 drivers/Kconfig                                   |    2 +
 drivers/Makefile                                  |    1 +
 drivers/extcon/Kconfig                            |   35 +
 drivers/extcon/Makefile                           |    6 +
 drivers/extcon/extcon_class.c                     |  822 +++++++++++++++++++++
 drivers/extcon/extcon_gpio.c                      |  175 +++++
 include/linux/extcon.h                            |  322 ++++++++
 include/linux/extcon/extcon_gpio.h                |   52 ++
 10 files changed, 1636 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-class-extcon
 create mode 100644 Documentation/extcon/porting-android-switch-class
 create mode 100644 drivers/extcon/Kconfig
 create mode 100644 drivers/extcon/Makefile
 create mode 100644 drivers/extcon/extcon_class.c
 create mode 100644 drivers/extcon/extcon_gpio.c
 create mode 100644 include/linux/extcon.h
 create mode 100644 include/linux/extcon/extcon_gpio.h

-- 
1.7.4.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH v7 1/6] Extcon (external connector): import Android's switch class and modify.
  2012-03-23  8:52 [PATCH v7 0/6] Introduce External Connector Class (extcon) MyungJoo Ham
@ 2012-03-23  8:52 ` MyungJoo Ham
  2012-04-18 21:47   ` Greg KH
  2012-03-23  8:52 ` [PATCH v7 2/6] Extcon: support generic GPIO extcon driver MyungJoo Ham
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 51+ messages in thread
From: MyungJoo Ham @ 2012-03-23  8:52 UTC (permalink / raw)
  To: Arnd Bergmann, LKML
  Cc: NeilBrown, Randy Dunlap, Mike Lockwood, Arve Hjønnevag,
	Kyungmin Park, gregkh, Linus Walleij, Dmitry Torokhov,
	Morten CHRISTIANSEN, Mark Brown, John Stultz, Joerg Roedel,
	myungjoo.ham, cw00.choi

External connector class (extcon) is based on and an extension of
Android kernel's switch class located at linux/drivers/switch/.

This patch provides the before-extension switch class moved to the
location where the extcon will be located (linux/drivers/extcon/) and
updates to handle class properly.

The before-extension class, switch class of Android kernel, commits
imported are:

switch: switch class and GPIO drivers. (splitted)
Author: Mike Lockwood <lockwood@android.com>

switch: Use device_create instead of device_create_drvdata.
Author: Arve Hjønnevåg <arve@android.com>

In this patch, upon the commits of Android kernel, we have added:
- Relocated and renamed for extcon.
- Comments, module name, and author information are updated
- Code clean for successing patches
- Bugfix: enabling write access without write functions
- Class/device/sysfs create/remove handling
- Added comments about uevents
- Format changes for extcon_dev_register() to have a parent dev.

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>

--
Changes from v6
- Updated comment/strings
- Revised "Android-compatible" mode.
   * Automatically activated if CONFIG_ANDROID && !CONFIG_ANDROID_SWITCH
   * Creates /sys/class/switch/*, which is a copy of /sys/class/extcon/*

Changes from v5
- Split the patch
- Style fixes
- "Android-compatible" mode is enabled by Kconfig option.

Changes from v2
- Updated name_show
- Sysfs entries are handled by class itself.
- Updated the method to add/remove devices for the class
- Comments on uevent send
- Able to become a module
- Compatible with Android platform

Changes from RFC
- Renamed to extcon (external connector) from multistate switch
- Added a seperated directory (drivers/extcon)
- Added kerneldoc comments
- Removed unused variables from extcon_gpio.c
- Added ABI Documentation.

BEING TESTED: Android compat class for EXTCON

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
---
 Documentation/ABI/testing/sysfs-class-extcon |   26 +++
 drivers/Kconfig                              |    2 +
 drivers/Makefile                             |    1 +
 drivers/extcon/Kconfig                       |   28 +++
 drivers/extcon/Makefile                      |    5 +
 drivers/extcon/extcon_class.c                |  236 ++++++++++++++++++++++++++
 include/linux/extcon.h                       |   82 +++++++++
 7 files changed, 380 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-class-extcon
 create mode 100644 drivers/extcon/Kconfig
 create mode 100644 drivers/extcon/Makefile
 create mode 100644 drivers/extcon/extcon_class.c
 create mode 100644 include/linux/extcon.h

diff --git a/Documentation/ABI/testing/sysfs-class-extcon b/Documentation/ABI/testing/sysfs-class-extcon
new file mode 100644
index 0000000..59a4b1c
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-extcon
@@ -0,0 +1,26 @@
+What:		/sys/class/extcon/.../
+Date:		December 2011
+Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
+Description:
+		Provide a place in sysfs for the extcon objects.
+		This allows accessing extcon specific variables.
+		The name of extcon object denoted as ... is the name given
+		with extcon_dev_register.
+
+What:		/sys/class/extcon/.../name
+Date:		December 2011
+Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
+Description:
+		The /sys/class/extcon/.../name shows the name of the extcon
+		object. If the extcon object has an optional callback
+		"show_name" defined, the callback will provide the name with
+		this sysfs node.
+
+What:		/sys/class/extcon/.../state
+Date:		December 2011
+Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
+Description:
+		The /sys/class/extcon/.../state shows the cable attach/detach
+		information of the corresponding extcon object. If the extcon
+		objecct has an optional callback "show_state" defined, the
+		callback will provide the name with this sysfs node.
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 5afe5d1..537d126 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -136,4 +136,6 @@ source "drivers/virt/Kconfig"
 
 source "drivers/devfreq/Kconfig"
 
+source "drivers/extcon/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index c07be02..6faceab 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -132,3 +132,4 @@ obj-$(CONFIG_VIRT_DRIVERS)	+= virt/
 obj-$(CONFIG_HYPERV)		+= hv/
 
 obj-$(CONFIG_PM_DEVFREQ)	+= devfreq/
+obj-$(CONFIG_EXTCON)		+= extcon/
diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig
new file mode 100644
index 0000000..11db865
--- /dev/null
+++ b/drivers/extcon/Kconfig
@@ -0,0 +1,28 @@
+menuconfig EXTCON
+	tristate "External Connector Class (extcon) support"
+	help
+	  Say Y here to enable external connector class (extcon) support.
+	  This allows monitoring external connectors by userspace
+	  via sysfs and uevent and supports external connectors with
+	  multiple states; i.e., an extcon that may have multiple
+	  cables attached. For example, an external connector of a device
+	  may be used to connect an HDMI cable and a AC adaptor, and to
+	  host USB ports. Many of 30-pin connectors including PDMI are
+	  also good examples.
+
+if EXTCON
+
+comment "Extcon Class Configuration"
+
+config EXTCON_ANDROID_COMPATIBLE
+	bool "Class name uses 'switch' to be compatible with Android kernel"
+	depends on ANDROID
+	help
+	  The class name of extcon device drivers are set 'switch' in order
+	  to be compatible with Android kernel switch framework. With this
+	  option enabled, Android userspace platform may use extcon device
+	  drivers as if they are switch device drivers.
+
+comment "Extcon Device Drivers"
+
+endif # MULTISTATE_SWITCH
diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile
new file mode 100644
index 0000000..6bc6921
--- /dev/null
+++ b/drivers/extcon/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for external connector class (extcon) devices
+#
+
+obj-$(CONFIG_EXTCON)		+= extcon_class.o
diff --git a/drivers/extcon/extcon_class.c b/drivers/extcon/extcon_class.c
new file mode 100644
index 0000000..3c46368
--- /dev/null
+++ b/drivers/extcon/extcon_class.c
@@ -0,0 +1,236 @@
+/*
+ *  drivers/extcon/extcon_class.c
+ *
+ *  External connector (extcon) class driver
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Author: Donggeun Kim <dg77.kim@samsung.com>
+ * Author: MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * based on android/drivers/switch/switch_class.c
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+*/
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/extcon.h>
+#include <linux/slab.h>
+
+struct class *extcon_class;
+#if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
+static struct class_compat *switch_class;
+#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
+
+static ssize_t state_show(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
+
+	if (edev->print_state) {
+		int ret = edev->print_state(edev, buf);
+
+		if (ret >= 0)
+			return ret;
+		/* Use default if failed */
+	}
+	return sprintf(buf, "%u\n", edev->state);
+}
+
+static ssize_t name_show(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
+
+	/* Optional callback given by the user */
+	if (edev->print_name) {
+		int ret = edev->print_name(edev, buf);
+		if (ret >= 0)
+			return ret;
+	}
+
+	return sprintf(buf, "%s\n", dev_name(edev->dev));
+}
+
+/**
+ * extcon_set_state() - Set the cable attach states of the extcon device.
+ * @edev:	the extcon device
+ * @state:	new cable attach status for @edev
+ *
+ * Changing the state sends uevent with environment variable containing
+ * the name of extcon device (envp[0]) and the state output (envp[1]).
+ * Tizen uses this format for extcon device to get events from ports.
+ * Android uses this format as well.
+ */
+void extcon_set_state(struct extcon_dev *edev, u32 state)
+{
+	char name_buf[120];
+	char state_buf[120];
+	char *prop_buf;
+	char *envp[3];
+	int env_offset = 0;
+	int length;
+
+	if (edev->state != state) {
+		edev->state = state;
+
+		prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
+		if (prop_buf) {
+			length = name_show(edev->dev, NULL, prop_buf);
+			if (length > 0) {
+				if (prop_buf[length - 1] == '\n')
+					prop_buf[length - 1] = 0;
+				snprintf(name_buf, sizeof(name_buf),
+					"NAME=%s", prop_buf);
+				envp[env_offset++] = name_buf;
+			}
+			length = state_show(edev->dev, NULL, prop_buf);
+			if (length > 0) {
+				if (prop_buf[length - 1] == '\n')
+					prop_buf[length - 1] = 0;
+				snprintf(state_buf, sizeof(state_buf),
+					"STATE=%s", prop_buf);
+				envp[env_offset++] = state_buf;
+			}
+			envp[env_offset] = NULL;
+			kobject_uevent_env(&edev->dev->kobj, KOBJ_CHANGE, envp);
+			free_page((unsigned long)prop_buf);
+		} else {
+			dev_err(edev->dev, "out of memory in extcon_set_state\n");
+			kobject_uevent(&edev->dev->kobj, KOBJ_CHANGE);
+		}
+	}
+}
+EXPORT_SYMBOL_GPL(extcon_set_state);
+
+static struct device_attribute extcon_attrs[] = {
+	__ATTR_RO(state),
+	__ATTR_RO(name),
+};
+
+static int create_extcon_class(void)
+{
+	if (!extcon_class) {
+		extcon_class = class_create(THIS_MODULE, "extcon");
+		if (IS_ERR(extcon_class))
+			return PTR_ERR(extcon_class);
+		extcon_class->dev_attrs = extcon_attrs;
+
+#if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
+		switch_class = class_compat_register("switch");
+		if (WARN(!switch_class, "cannot allocate"))
+			return -ENOMEM;
+#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
+	}
+
+	return 0;
+}
+
+static void extcon_cleanup(struct extcon_dev *edev, bool skip)
+{
+	if (!skip && get_device(edev->dev)) {
+		device_unregister(edev->dev);
+		put_device(edev->dev);
+	}
+
+	kfree(edev->dev);
+}
+
+static void extcon_dev_release(struct device *dev)
+{
+	struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
+
+	extcon_cleanup(edev, true);
+}
+
+/**
+ * extcon_dev_register() - Register a new extcon device
+ * @edev	: the new extcon device (should be allocated before calling)
+ * @dev		: the parent device for this extcon device.
+ *
+ * Among the members of edev struct, please set the "user initializing data"
+ * in any case and set the "optional callbacks" if required. However, please
+ * do not set the values of "internal data", which are initialized by
+ * this function.
+ */
+int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
+{
+	int ret;
+
+	if (!extcon_class) {
+		ret = create_extcon_class();
+		if (ret < 0)
+			return ret;
+	}
+
+	edev->dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+	edev->dev->parent = dev;
+	edev->dev->class = extcon_class;
+	edev->dev->release = extcon_dev_release;
+
+	dev_set_name(edev->dev, edev->name ? edev->name : dev_name(dev));
+	ret = device_register(edev->dev);
+	if (ret) {
+		put_device(edev->dev);
+		goto err_dev;
+	}
+#if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
+	if (switch_class)
+		ret = class_compat_create_link(switch_class, edev->dev,
+					       dev);
+#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
+
+	dev_set_drvdata(edev->dev, edev);
+	edev->state = 0;
+	return 0;
+
+err_dev:
+	kfree(edev->dev);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(extcon_dev_register);
+
+/**
+ * extcon_dev_unregister() - Unregister the extcon device.
+ * @edev:	the extcon device instance to be unregitered.
+ *
+ * Note that this does not call kfree(edev) because edev was not allocated
+ * by this class.
+ */
+void extcon_dev_unregister(struct extcon_dev *edev)
+{
+	extcon_cleanup(edev, false);
+}
+EXPORT_SYMBOL_GPL(extcon_dev_unregister);
+
+static int __init extcon_class_init(void)
+{
+	return create_extcon_class();
+}
+module_init(extcon_class_init);
+
+static void __exit extcon_class_exit(void)
+{
+	class_destroy(extcon_class);
+}
+module_exit(extcon_class_exit);
+
+MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
+MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
+MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
+MODULE_DESCRIPTION("External connector (extcon) class driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/extcon.h b/include/linux/extcon.h
new file mode 100644
index 0000000..cd732ab
--- /dev/null
+++ b/include/linux/extcon.h
@@ -0,0 +1,82 @@
+/*
+ *  External connector (extcon) class driver
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Author: Donggeun Kim <dg77.kim@samsung.com>
+ * Author: MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * based on switch class driver
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+*/
+
+#ifndef __LINUX_EXTCON_H__
+#define __LINUX_EXTCON_H__
+
+/**
+ * struct extcon_dev - An extcon device represents one external connector.
+ * @name	The name of this extcon device. Parent device name is used
+ *		if NULL.
+ * @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
+ *		status of the extcon device.
+ * @dev		Device of this extcon. Do not provide at register-time.
+ * @state	Attach/detach state of this extcon. Do not provide at
+ *		register-time
+ *
+ * In most cases, users only need to provide "User initializing data" of
+ * this struct when registering an extcon. In some exceptional cases,
+ * optional callbacks may be needed. However, the values in "internal data"
+ * are overwritten by register function.
+ */
+struct extcon_dev {
+	/* --- Optional user initializing data --- */
+	const char	*name;
+
+	/* --- Optional callbacks to override class functions --- */
+	ssize_t	(*print_name)(struct extcon_dev *edev, char *buf);
+	ssize_t	(*print_state)(struct extcon_dev *edev, char *buf);
+
+	/* --- Internal data. Please do not set. --- */
+	struct device	*dev;
+	u32		state;
+};
+
+#ifdef CONFIG_EXTCON
+extern int extcon_dev_register(struct extcon_dev *edev, struct device *dev);
+extern void extcon_dev_unregister(struct extcon_dev *edev);
+
+static inline u32 extcon_get_state(struct extcon_dev *edev)
+{
+	return edev->state;
+}
+
+extern void extcon_set_state(struct extcon_dev *edev, u32 state);
+#else /* CONFIG_EXTCON */
+static inline int extcon_dev_register(struct extcon_dev *edev,
+				      struct device *dev)
+{
+	return 0;
+}
+
+static inline void extcon_dev_unregister(struct extcon_dev *edev) { }
+
+static inline u32 extcon_get_state(struct extcon_dev *edev)
+{
+	return 0;
+}
+
+static inline void extcon_set_state(struct extcon_dev *edev, u32 state) { }
+#endif /* CONFIG_EXTCON */
+#endif /* __LINUX_EXTCON_H__ */
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH v7 2/6] Extcon: support generic GPIO extcon driver
  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-03-23  8:52 ` MyungJoo Ham
  2012-03-23  8:53 ` [PATCH v7 3/6] Extcon: support notification based on the state changes MyungJoo Ham
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 51+ messages in thread
From: MyungJoo Ham @ 2012-03-23  8:52 UTC (permalink / raw)
  To: Arnd Bergmann, LKML
  Cc: NeilBrown, Randy Dunlap, Mike Lockwood, Arve Hjønnevag,
	Kyungmin Park, gregkh, Linus Walleij, Dmitry Torokhov,
	Morten CHRISTIANSEN, Mark Brown, John Stultz, Joerg Roedel,
	myungjoo.ham, cw00.choi

The generic GPIO extcon driver (an external connector device based on
GPIO control) and imported from Android kernel.

switch: switch class and GPIO drivers. (splitted)
Author: Mike Lockwood <lockwood@android.com>

switch: gpio: Don't call request_irq with interrupts disabled
Author: Arve Hjønnevåg <arve@android.com>

switch_gpio: Add missing #include <linux/interrupt.h>
Author: Mike Lockwood <lockwood@android.com>

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>

--
Changed from v5:
- Splitted at v5 from the main extcon patch.
- Added debounce time for irq handlers.
- Use request_any_context_irq instead of request_irq
- User needs to specify irq flags for GPIO interrupts (was fixed to
IRQF_TRIGGER_LOW before)
- Use module_platform_driver().
---
 drivers/extcon/Kconfig             |    7 ++
 drivers/extcon/Makefile            |    1 +
 drivers/extcon/extcon_gpio.c       |  175 ++++++++++++++++++++++++++++++++++++
 include/linux/extcon/extcon_gpio.h |   52 +++++++++++
 4 files changed, 235 insertions(+), 0 deletions(-)
 create mode 100644 drivers/extcon/extcon_gpio.c
 create mode 100644 include/linux/extcon/extcon_gpio.h

diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig
index 11db865..cc3f5eb 100644
--- a/drivers/extcon/Kconfig
+++ b/drivers/extcon/Kconfig
@@ -25,4 +25,11 @@ config EXTCON_ANDROID_COMPATIBLE
 
 comment "Extcon Device Drivers"
 
+config EXTCON_GPIO
+	tristate "GPIO extcon support"
+	depends on GENERIC_GPIO
+	help
+	  Say Y here to enable GPIO based extcon support. Note that GPIO
+	  extcon supports single state per extcon instance.
+
 endif # MULTISTATE_SWITCH
diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile
index 6bc6921..2c46d41 100644
--- a/drivers/extcon/Makefile
+++ b/drivers/extcon/Makefile
@@ -3,3 +3,4 @@
 #
 
 obj-$(CONFIG_EXTCON)		+= extcon_class.o
+obj-$(CONFIG_EXTCON_GPIO)	+= extcon_gpio.o
diff --git a/drivers/extcon/extcon_gpio.c b/drivers/extcon/extcon_gpio.c
new file mode 100644
index 0000000..f24ec67
--- /dev/null
+++ b/drivers/extcon/extcon_gpio.c
@@ -0,0 +1,175 @@
+/*
+ *  drivers/extcon/extcon_gpio.c
+ *
+ *  Single-state GPIO extcon driver based on extcon class
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * Modified by MyungJoo Ham <myungjoo.ham@samsung.com> to support extcon
+ * (originally switch class is supported)
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/extcon.h>
+#include <linux/workqueue.h>
+#include <linux/gpio.h>
+#include <linux/extcon.h>
+#include <linux/extcon/extcon_gpio.h>
+
+struct gpio_extcon_data {
+	struct extcon_dev edev;
+	unsigned gpio;
+	const char *state_on;
+	const char *state_off;
+	int irq;
+	struct delayed_work work;
+	unsigned long debounce_jiffies;
+};
+
+static void gpio_extcon_work(struct work_struct *work)
+{
+	int state;
+	struct gpio_extcon_data	*data =
+		container_of(to_delayed_work(work), struct gpio_extcon_data,
+			     work);
+
+	state = gpio_get_value(data->gpio);
+	extcon_set_state(&data->edev, state);
+}
+
+static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
+{
+	struct gpio_extcon_data *extcon_data =
+	    (struct gpio_extcon_data *)dev_id;
+
+	schedule_delayed_work(&extcon_data->work,
+			      extcon_data->debounce_jiffies);
+	return IRQ_HANDLED;
+}
+
+static ssize_t extcon_gpio_print_state(struct extcon_dev *edev, char *buf)
+{
+	struct gpio_extcon_data	*extcon_data =
+		container_of(edev, struct gpio_extcon_data, edev);
+	const char *state;
+	if (extcon_get_state(edev))
+		state = extcon_data->state_on;
+	else
+		state = extcon_data->state_off;
+
+	if (state)
+		return sprintf(buf, "%s\n", state);
+	return -EINVAL;
+}
+
+static int gpio_extcon_probe(struct platform_device *pdev)
+{
+	struct gpio_extcon_platform_data *pdata = pdev->dev.platform_data;
+	struct gpio_extcon_data *extcon_data;
+	int ret = 0;
+
+	if (!pdata)
+		return -EBUSY;
+	if (!pdata->irq_flags) {
+		dev_err(&pdev->dev, "IRQ flag is not specified.\n");
+		return -EINVAL;
+	}
+
+	extcon_data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_extcon_data),
+				   GFP_KERNEL);
+	if (!extcon_data)
+		return -ENOMEM;
+
+	extcon_data->edev.name = pdata->name;
+	extcon_data->gpio = pdata->gpio;
+	extcon_data->state_on = pdata->state_on;
+	extcon_data->state_off = pdata->state_off;
+	if (pdata->state_on && pdata->state_off)
+		extcon_data->edev.print_state = extcon_gpio_print_state;
+	extcon_data->debounce_jiffies = msecs_to_jiffies(pdata->debounce);
+
+	ret = extcon_dev_register(&extcon_data->edev, &pdev->dev);
+	if (ret < 0)
+		goto err_extcon_dev_register;
+
+	ret = gpio_request(extcon_data->gpio, pdev->name);
+	if (ret < 0)
+		goto err_request_gpio;
+
+	ret = gpio_direction_input(extcon_data->gpio);
+	if (ret < 0)
+		goto err_set_gpio_input;
+
+	INIT_DELAYED_WORK(&extcon_data->work, gpio_extcon_work);
+
+	extcon_data->irq = gpio_to_irq(extcon_data->gpio);
+	if (extcon_data->irq < 0) {
+		ret = extcon_data->irq;
+		goto err_detect_irq_num_failed;
+	}
+
+	ret = request_any_context_irq(extcon_data->irq, gpio_irq_handler,
+				      pdata->irq_flags, pdev->name,
+				      extcon_data);
+	if (ret < 0)
+		goto err_request_irq;
+
+	/* Perform initial detection */
+	gpio_extcon_work(&extcon_data->work.work);
+
+	return 0;
+
+err_request_irq:
+err_detect_irq_num_failed:
+err_set_gpio_input:
+	gpio_free(extcon_data->gpio);
+err_request_gpio:
+	extcon_dev_unregister(&extcon_data->edev);
+err_extcon_dev_register:
+	devm_kfree(&pdev->dev, extcon_data);
+
+	return ret;
+}
+
+static int gpio_extcon_remove(struct platform_device *pdev)
+{
+	struct gpio_extcon_data *extcon_data = platform_get_drvdata(pdev);
+
+	cancel_delayed_work_sync(&extcon_data->work);
+	gpio_free(extcon_data->gpio);
+	extcon_dev_unregister(&extcon_data->edev);
+	devm_kfree(&pdev->dev, extcon_data);
+
+	return 0;
+}
+
+static struct platform_driver gpio_extcon = {
+	.probe		= gpio_extcon_probe,
+	.remove		= gpio_extcon_remove,
+	.driver		= {
+		.name	= "extcon-gpio",
+		.owner	= THIS_MODULE,
+	},
+};
+
+module_platform_driver(gpio_extcon);
+
+MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
+MODULE_DESCRIPTION("GPIO extcon driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/extcon/extcon_gpio.h b/include/linux/extcon/extcon_gpio.h
new file mode 100644
index 0000000..a2129b7
--- /dev/null
+++ b/include/linux/extcon/extcon_gpio.h
@@ -0,0 +1,52 @@
+/*
+ *  External connector (extcon) class generic GPIO driver
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Author: MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * based on switch class driver
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+*/
+#ifndef __EXTCON_GPIO_H__
+#define __EXTCON_GPIO_H__ __FILE__
+
+#include <linux/extcon.h>
+
+/**
+ * struct gpio_extcon_platform_data - A simple GPIO-controlled extcon device.
+ * @name	The name of this GPIO extcon device.
+ * @gpio	Corresponding GPIO.
+ * @debounce	Debounce time for GPIO IRQ in ms.
+ * @irq_flags	IRQ Flags (e.g., IRQF_TRIGGER_LOW).
+ * @state_on	print_state is overriden with state_on if attached. If Null,
+ *		default method of extcon class is used.
+ * @state_off	print_state is overriden with state_on if dettached. If Null,
+ *		default method of extcon class is used.
+ *
+ * Note that in order for state_on or state_off to be valid, both state_on
+ * and state_off should be not NULL. If at least one of them is NULL,
+ * the print_state is not overriden.
+ */
+struct gpio_extcon_platform_data {
+	const char *name;
+	unsigned gpio;
+	unsigned long debounce;
+	unsigned long irq_flags;
+
+	/* if NULL, "0" or "1" will be printed */
+	const char *state_on;
+	const char *state_off;
+};
+
+#endif /* __EXTCON_GPIO_H__ */
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH v7 3/6] Extcon: support notification based on the state changes.
  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-03-23  8:52 ` [PATCH v7 2/6] Extcon: support generic GPIO extcon driver MyungJoo Ham
@ 2012-03-23  8:53 ` MyungJoo Ham
  2012-03-23  8:53 ` [PATCH v7 4/6] Extcon: support multiple states at a device MyungJoo Ham
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 51+ messages in thread
From: MyungJoo Ham @ 2012-03-23  8:53 UTC (permalink / raw)
  To: Arnd Bergmann, LKML
  Cc: NeilBrown, Randy Dunlap, Mike Lockwood, Arve Hjønnevag,
	Kyungmin Park, gregkh, Linus Walleij, Dmitry Torokhov,
	Morten CHRISTIANSEN, Mark Brown, John Stultz, Joerg Roedel,
	myungjoo.ham, cw00.choi

From: Donggeun Kim <dg77.kim@samsung.com>

State changes of extcon devices have been notified via kobjet_uevent.
This patch adds notifier interfaces in order to allow device drivers to
get notified easily. Along with notifier interface,
extcon_get_extcon_dev() function is added so that device drivers may
discover a extcon_dev easily.

Signed-off-by: Donggeun Kim <dg77.kim@samsung.com>
Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>

--
Changes from RFC
- Renamed switch to extcon
- Bugfix: extcon_dev_unregister()
- Bugfix: "edev->dev" is "internal" data.
- Added kerneldoc comments.
- Reworded comments.
---
 drivers/extcon/extcon_class.c |   66 +++++++++++++++++++++++++++++++++++++++++
 include/linux/extcon.h        |   38 +++++++++++++++++++++++
 2 files changed, 104 insertions(+), 0 deletions(-)

diff --git a/drivers/extcon/extcon_class.c b/drivers/extcon/extcon_class.c
index 3c46368..83a088f 100644
--- a/drivers/extcon/extcon_class.c
+++ b/drivers/extcon/extcon_class.c
@@ -36,6 +36,9 @@ struct class *extcon_class;
 static struct class_compat *switch_class;
 #endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
 
+static LIST_HEAD(extcon_dev_list);
+static DEFINE_MUTEX(extcon_dev_list_lock);
+
 static ssize_t state_show(struct device *dev, struct device_attribute *attr,
 			  char *buf)
 {
@@ -75,6 +78,9 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr,
  * the name of extcon device (envp[0]) and the state output (envp[1]).
  * Tizen uses this format for extcon device to get events from ports.
  * Android uses this format as well.
+ *
+ * Note that notifier provides the which bits are changes in the state
+ * variable with "val" to the callback.
  */
 void extcon_set_state(struct extcon_dev *edev, u32 state)
 {
@@ -84,10 +90,14 @@ void extcon_set_state(struct extcon_dev *edev, u32 state)
 	char *envp[3];
 	int env_offset = 0;
 	int length;
+	u32 old_state = edev->state;
 
 	if (edev->state != state) {
 		edev->state = state;
 
+		raw_notifier_call_chain(&edev->nh, old_state ^ edev->state,
+					edev);
+
 		prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
 		if (prop_buf) {
 			length = name_show(edev->dev, NULL, prop_buf);
@@ -117,6 +127,51 @@ void extcon_set_state(struct extcon_dev *edev, u32 state)
 }
 EXPORT_SYMBOL_GPL(extcon_set_state);
 
+/**
+ * extcon_get_extcon_dev() - Get the extcon device instance from the name
+ * @extcon_name:	The extcon name provided with extcon_dev_register()
+ */
+struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
+{
+	struct extcon_dev *sd;
+
+	mutex_lock(&extcon_dev_list_lock);
+	list_for_each_entry(sd, &extcon_dev_list, entry) {
+		if (!strcmp(sd->name, extcon_name))
+			goto out;
+	}
+	sd = NULL;
+out:
+	mutex_unlock(&extcon_dev_list_lock);
+	return sd;
+}
+EXPORT_SYMBOL_GPL(extcon_get_extcon_dev);
+
+/**
+ * extcon_register_notifier() - Register a notifee to get notified by
+ *			      any attach status changes from the extcon.
+ * @edev:	the extcon device.
+ * @nb:		a notifier block to be registered.
+ */
+int extcon_register_notifier(struct extcon_dev *edev,
+			struct notifier_block *nb)
+{
+	return raw_notifier_chain_register(&edev->nh, nb);
+}
+EXPORT_SYMBOL_GPL(extcon_register_notifier);
+
+/**
+ * extcon_unregister_notifier() - Unregister a notifee from the extcon device.
+ * @edev:	the extcon device.
+ * @nb:		a registered notifier block to be unregistered.
+ */
+int extcon_unregister_notifier(struct extcon_dev *edev,
+			struct notifier_block *nb)
+{
+	return raw_notifier_chain_unregister(&edev->nh, nb);
+}
+EXPORT_SYMBOL_GPL(extcon_unregister_notifier);
+
 static struct device_attribute extcon_attrs[] = {
 	__ATTR_RO(state),
 	__ATTR_RO(name),
@@ -142,6 +197,10 @@ static int create_extcon_class(void)
 
 static void extcon_cleanup(struct extcon_dev *edev, bool skip)
 {
+	mutex_lock(&extcon_dev_list_lock);
+	list_del(&edev->entry);
+	mutex_unlock(&extcon_dev_list_lock);
+
 	if (!skip && get_device(edev->dev)) {
 		device_unregister(edev->dev);
 		put_device(edev->dev);
@@ -194,8 +253,15 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
 					       dev);
 #endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
 
+	RAW_INIT_NOTIFIER_HEAD(&edev->nh);
+
 	dev_set_drvdata(edev->dev, edev);
 	edev->state = 0;
+
+	mutex_lock(&extcon_dev_list_lock);
+	list_add(&edev->entry, &extcon_dev_list);
+	mutex_unlock(&extcon_dev_list_lock);
+
 	return 0;
 
 err_dev:
diff --git a/include/linux/extcon.h b/include/linux/extcon.h
index cd732ab..50be845 100644
--- a/include/linux/extcon.h
+++ b/include/linux/extcon.h
@@ -23,6 +23,7 @@
 #ifndef __LINUX_EXTCON_H__
 #define __LINUX_EXTCON_H__
 
+#include <linux/notifier.h>
 /**
  * struct extcon_dev - An extcon device represents one external connector.
  * @name	The name of this extcon device. Parent device name is used
@@ -34,6 +35,9 @@
  * @dev		Device of this extcon. Do not provide at register-time.
  * @state	Attach/detach state of this extcon. Do not provide at
  *		register-time
+ * @nh	Notifier for the state change events from this extcon
+ * @entry	To support list of extcon devices so that uses can search
+ *		for extcon devices based on the extcon name.
  *
  * In most cases, users only need to provide "User initializing data" of
  * this struct when registering an extcon. In some exceptional cases,
@@ -51,11 +55,19 @@ struct extcon_dev {
 	/* --- Internal data. Please do not set. --- */
 	struct device	*dev;
 	u32		state;
+	struct raw_notifier_head nh;
+	struct list_head entry;
 };
 
 #ifdef CONFIG_EXTCON
+
+/*
+ * Following APIs are for notifiers or configurations.
+ * Notifiers are the external port and connection devices.
+ */
 extern int extcon_dev_register(struct extcon_dev *edev, struct device *dev);
 extern void extcon_dev_unregister(struct extcon_dev *edev);
+extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name);
 
 static inline u32 extcon_get_state(struct extcon_dev *edev)
 {
@@ -63,6 +75,15 @@ static inline u32 extcon_get_state(struct extcon_dev *edev)
 }
 
 extern void extcon_set_state(struct extcon_dev *edev, u32 state);
+
+/*
+ * Following APIs are to monitor every action of a notifier.
+ * Registerer gets notified for every external port of a connection device.
+ */
+extern int extcon_register_notifier(struct extcon_dev *edev,
+				    struct notifier_block *nb);
+extern int extcon_unregister_notifier(struct extcon_dev *edev,
+				      struct notifier_block *nb);
 #else /* CONFIG_EXTCON */
 static inline int extcon_dev_register(struct extcon_dev *edev,
 				      struct device *dev)
@@ -78,5 +99,22 @@ static inline u32 extcon_get_state(struct extcon_dev *edev)
 }
 
 static inline void extcon_set_state(struct extcon_dev *edev, u32 state) { }
+static inline struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
+{
+	return NULL;
+}
+
+static inline int extcon_register_notifier(struct extcon_dev *edev,
+					   struct notifier_block *nb)
+{
+	return 0;
+}
+
+static inline int extcon_unregister_notifier(struct extcon_dev *edev,
+					     struct notifier_block *nb)
+{
+	return 0;
+}
+
 #endif /* CONFIG_EXTCON */
 #endif /* __LINUX_EXTCON_H__ */
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH v7 4/6] Extcon: support multiple states at a device.
  2012-03-23  8:52 [PATCH v7 0/6] Introduce External Connector Class (extcon) MyungJoo Ham
                   ` (2 preceding siblings ...)
  2012-03-23  8:53 ` [PATCH v7 3/6] Extcon: support notification based on the state changes MyungJoo Ham
@ 2012-03-23  8:53 ` MyungJoo Ham
  2012-03-23  8:53 ` [PATCH v7 5/6] Extcon: support mutually exclusive relation between cables MyungJoo Ham
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 51+ messages in thread
From: MyungJoo Ham @ 2012-03-23  8:53 UTC (permalink / raw)
  To: Arnd Bergmann, LKML
  Cc: NeilBrown, Randy Dunlap, Mike Lockwood, Arve Hjønnevag,
	Kyungmin Park, gregkh, Linus Walleij, Dmitry Torokhov,
	Morten CHRISTIANSEN, Mark Brown, John Stultz, Joerg Roedel,
	myungjoo.ham, cw00.choi

One switch device (e.g., MUIC(MAX8997, MAX77686, ...), and some 30-pin
devices) may have multiple cables attached. For example, one
30-pin port may inhabit a USB cable, an HDMI cable, and a mic.
Thus, one switch device requires multiple state bits each representing
a type of cable.

For such purpose, we use the 32bit state variable; thus, up to 32
different type of cables may be defined for a switch device. The list of
possible cables is defined by the array of cable names in the switch_dev
struct given to the class.

Signed-off-by: Donggeun Kim <dg77.kim@samsung.com>
Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>

--
Changes from V5
- Sysfs style reformed: subdirectory per cable.
- Updated standard cable names
- Removed unnecessary printf
- Bugfixes after testing

Changes from V4
- Bugfixes after more testing at Exynos4412 boards with userspace
  processses.

Changes from V3
- Bugfixes after more testing at Exynos4412 boards.

Changes from V2
- State can be stored by user
- Documentation updated

Changes from RFC
- Switch is renamed to extcon
- Added kerneldoc comments
- Added APIs to support "standard" cable names
- Added helper APIs to support notifier block registration with cable
  name.
- Regrouped function list in the header file.
---
 Documentation/ABI/testing/sysfs-class-extcon |   63 ++++-
 drivers/extcon/extcon_class.c                |  439 +++++++++++++++++++++++++-
 include/linux/extcon.h                       |  184 +++++++++++
 3 files changed, 666 insertions(+), 20 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-class-extcon b/Documentation/ABI/testing/sysfs-class-extcon
index 59a4b1c..19b18e9 100644
--- a/Documentation/ABI/testing/sysfs-class-extcon
+++ b/Documentation/ABI/testing/sysfs-class-extcon
@@ -1,5 +1,5 @@
 What:		/sys/class/extcon/.../
-Date:		December 2011
+Date:		February 2012
 Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
 Description:
 		Provide a place in sysfs for the extcon objects.
@@ -7,8 +7,16 @@ Description:
 		The name of extcon object denoted as ... is the name given
 		with extcon_dev_register.
 
+		One extcon device denotes a single external connector
+		port. An external connector may have multiple cables
+		attached simultaneously. Many of docks, cradles, and
+		accessory cables have such capability. For example,
+		the 30-pin port of Nuri board (/arch/arm/mach-exynos)
+		may have both HDMI and Charger attached, or analog audio,
+		video, and USB cables attached simulteneously.
+
 What:		/sys/class/extcon/.../name
-Date:		December 2011
+Date:		February 2012
 Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
 Description:
 		The /sys/class/extcon/.../name shows the name of the extcon
@@ -17,10 +25,51 @@ Description:
 		this sysfs node.
 
 What:		/sys/class/extcon/.../state
-Date:		December 2011
+Date:		February 2012
+Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
+Description:
+		The /sys/class/extcon/.../state shows and stores the cable
+		attach/detach information of the corresponding extcon object.
+		If the extcon object has an optional callback "show_state"
+		defined, the showing function is overriden with the optional
+		callback.
+
+		If the default callback for showing function is used, the
+		format is like this:
+		# cat state
+		USB_OTG=1
+		HDMI=0
+		TA=1
+		EAR_JACK=0
+		#
+		In this example, the extcon device have USB_OTG and TA
+		cables attached and HDMI and EAR_JACK cables detached.
+
+		In order to update the state of an extcon device, enter a hex
+		state number starting with 0x.
+		 echo 0xHEX > state
+
+		This updates the whole state of the extcon dev.
+		Inputs of all the methods are required to meet the
+		mutually_exclusive contidions if they exist.
+
+		It is recommended to use this "global" state interface if
+		you need to enter the value atomically. The later state
+		interface associated with each cable cannot update
+		multiple cable states of an extcon device simultaneously.
+
+What:		/sys/class/extcon/.../cable.x/name
+Date:		February 2012
+Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
+Description:
+		The /sys/class/extcon/.../cable.x/name shows the name of cable
+		"x" (integer between 0 and 31) of an extcon device.
+
+What:		/sys/class/extcon/.../cable.x/state
+Date:		February 2012
 Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
 Description:
-		The /sys/class/extcon/.../state shows the cable attach/detach
-		information of the corresponding extcon object. If the extcon
-		objecct has an optional callback "show_state" defined, the
-		callback will provide the name with this sysfs node.
+		The /sys/class/extcon/.../cable.x/name shows and stores the
+		state of cable "x" (integer between 0 and 31) of an extcon
+		device. The state value is either 0 (detached) or 1
+		(attached).
diff --git a/drivers/extcon/extcon_class.c b/drivers/extcon/extcon_class.c
index 83a088f..59f32c7 100644
--- a/drivers/extcon/extcon_class.c
+++ b/drivers/extcon/extcon_class.c
@@ -31,6 +31,39 @@
 #include <linux/extcon.h>
 #include <linux/slab.h>
 
+/*
+ * extcon_cable_name suggests the standard cable names for commonly used
+ * cable types.
+ *
+ * However, please do not use extcon_cable_name directly for extcon_dev
+ * struct's supported_cable pointer unless your device really supports
+ * every single port-type of the following cable names. Please choose cable
+ * names that are actually used in your extcon device.
+ */
+const char *extcon_cable_name[] = {
+	[EXTCON_USB]		= "USB",
+	[EXTCON_USB_HOST]	= "USB-Host",
+	[EXTCON_TA]		= "TA",
+	[EXTCON_FAST_CHARGER]	= "Fast-charger",
+	[EXTCON_SLOW_CHARGER]	= "Slow-charger",
+	[EXTCON_CHARGE_DOWNSTREAM]	= "Charge-downstream",
+	[EXTCON_HDMI]		= "HDMI",
+	[EXTCON_MHL]		= "MHL",
+	[EXTCON_DVI]		= "DVI",
+	[EXTCON_VGA]		= "VGA",
+	[EXTCON_DOCK]		= "Dock",
+	[EXTCON_LINE_IN]	= "Line-in",
+	[EXTCON_LINE_OUT]	= "Line-out",
+	[EXTCON_MIC_IN]		= "Microphone",
+	[EXTCON_HEADPHONE_OUT]	= "Headphone",
+	[EXTCON_SPDIF_IN]	= "SPDIF-in",
+	[EXTCON_SPDIF_OUT]	= "SPDIF-out",
+	[EXTCON_VIDEO_IN]	= "Video-in",
+	[EXTCON_VIDEO_OUT]	= "Video-out",
+
+	NULL,
+};
+
 struct class *extcon_class;
 #if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
 static struct class_compat *switch_class;
@@ -42,6 +75,7 @@ static DEFINE_MUTEX(extcon_dev_list_lock);
 static ssize_t state_show(struct device *dev, struct device_attribute *attr,
 			  char *buf)
 {
+	int i, count = 0;
 	struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
 
 	if (edev->print_state) {
@@ -51,7 +85,39 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,
 			return ret;
 		/* Use default if failed */
 	}
-	return sprintf(buf, "%u\n", edev->state);
+
+	if (edev->max_supported == 0)
+		return sprintf(buf, "%u\n", edev->state);
+
+	for (i = 0; i < SUPPORTED_CABLE_MAX; i++) {
+		if (!edev->supported_cable[i])
+			break;
+		count += sprintf(buf + count, "%s=%d\n",
+				 edev->supported_cable[i],
+				 !!(edev->state & (1 << i)));
+	}
+
+	return count;
+}
+
+void 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)
+{
+	u32 state;
+	ssize_t ret = 0;
+	struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
+
+	ret = sscanf(buf, "0x%x", &state);
+	if (ret == 0)
+		ret = -EINVAL;
+	else
+		extcon_set_state(edev, state);
+
+	if (ret < 0)
+		return ret;
+
+	return count;
 }
 
 static ssize_t name_show(struct device *dev, struct device_attribute *attr,
@@ -69,9 +135,52 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr,
 	return sprintf(buf, "%s\n", dev_name(edev->dev));
 }
 
+static ssize_t cable_name_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct extcon_cable *cable = container_of(attr, struct extcon_cable,
+						  attr_name);
+
+	return sprintf(buf, "%s\n",
+		       cable->edev->supported_cable[cable->cable_index]);
+}
+
+static ssize_t cable_state_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct extcon_cable *cable = container_of(attr, struct extcon_cable,
+						  attr_state);
+
+	return sprintf(buf, "%d\n",
+		       extcon_get_cable_state_(cable->edev,
+					       cable->cable_index));
+}
+
+static ssize_t cable_state_store(struct device *dev,
+				 struct device_attribute *attr, const char *buf,
+				 size_t count)
+{
+	struct extcon_cable *cable = container_of(attr, struct extcon_cable,
+						  attr_state);
+	int ret, state;
+
+	ret = sscanf(buf, "%d", &state);
+	if (ret == 0)
+		ret = -EINVAL;
+	else
+		ret = extcon_set_cable_state_(cable->edev, cable->cable_index,
+					      state);
+
+	if (ret < 0)
+		return ret;
+	return count;
+}
+
 /**
- * extcon_set_state() - Set the cable attach states of the extcon device.
+ * extcon_update_state() - Update the cable attach states of the extcon device
+ *			only for the masked bits.
  * @edev:	the extcon device
+ * @mask:	the bit mask to designate updated bits.
  * @state:	new cable attach status for @edev
  *
  * Changing the state sends uevent with environment variable containing
@@ -79,10 +188,10 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr,
  * Tizen uses this format for extcon device to get events from ports.
  * Android uses this format as well.
  *
- * Note that notifier provides the which bits are changes in the state
- * variable with "val" to the callback.
+ * Note that the 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)
+void extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
 {
 	char name_buf[120];
 	char state_buf[120];
@@ -90,15 +199,20 @@ void extcon_set_state(struct extcon_dev *edev, u32 state)
 	char *envp[3];
 	int env_offset = 0;
 	int length;
-	u32 old_state = edev->state;
+	unsigned long flags;
 
-	if (edev->state != state) {
-		edev->state = state;
+	spin_lock_irqsave(&edev->lock, flags);
 
-		raw_notifier_call_chain(&edev->nh, old_state ^ edev->state,
-					edev);
+	if (edev->state != ((edev->state & ~mask) | (state & mask))) {
+		u32 old_state = edev->state;
 
-		prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
+		edev->state &= ~mask;
+		edev->state |= state & mask;
+
+		raw_notifier_call_chain(&edev->nh, old_state, edev);
+
+		/* This could be in interrupt handler */
+		prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
 		if (prop_buf) {
 			length = name_show(edev->dev, NULL, prop_buf);
 			if (length > 0) {
@@ -117,17 +231,132 @@ void extcon_set_state(struct extcon_dev *edev, u32 state)
 				envp[env_offset++] = state_buf;
 			}
 			envp[env_offset] = NULL;
+			/* Unlock early before uevent */
+			spin_unlock_irqrestore(&edev->lock, flags);
+
 			kobject_uevent_env(&edev->dev->kobj, KOBJ_CHANGE, envp);
 			free_page((unsigned long)prop_buf);
 		} else {
+			/* Unlock early before uevent */
+			spin_unlock_irqrestore(&edev->lock, flags);
+
 			dev_err(edev->dev, "out of memory in extcon_set_state\n");
 			kobject_uevent(&edev->dev->kobj, KOBJ_CHANGE);
 		}
+	} else {
+		/* No changes */
+		spin_unlock_irqrestore(&edev->lock, flags);
 	}
 }
+EXPORT_SYMBOL_GPL(extcon_update_state);
+
+/**
+ * extcon_set_state() - Set the cable attach states of the extcon device.
+ * @edev:	the extcon device
+ * @state:	new cable attach status for @edev
+ *
+ * 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)
+{
+	extcon_update_state(edev, 0xffffffff, state);
+}
 EXPORT_SYMBOL_GPL(extcon_set_state);
 
 /**
+ * extcon_find_cable_index() - Get the cable index based on the cable name.
+ * @edev:	the extcon device that has the cable.
+ * @cable_name:	cable name to be searched.
+ *
+ * Note that accessing a cable state based on cable_index is faster than
+ * cable_name because using cable_name induces a loop with strncmp().
+ * Thus, when get/set_cable_state is repeatedly used, using cable_index
+ * is recommended.
+ */
+int extcon_find_cable_index(struct extcon_dev *edev, const char *cable_name)
+{
+	int i;
+
+	if (edev->supported_cable) {
+		for (i = 0; edev->supported_cable[i]; i++) {
+			if (!strncmp(edev->supported_cable[i],
+				cable_name, CABLE_NAME_MAX))
+				return i;
+		}
+	}
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(extcon_find_cable_index);
+
+/**
+ * extcon_get_cable_state_() - Get the status of a specific cable.
+ * @edev:	the extcon device that has the cable.
+ * @index:	cable index that can be retrieved by extcon_find_cable_index().
+ */
+int extcon_get_cable_state_(struct extcon_dev *edev, int index)
+{
+	if (index < 0 || (edev->max_supported && edev->max_supported <= index))
+		return -EINVAL;
+
+	return !!(edev->state & (1 << index));
+}
+EXPORT_SYMBOL_GPL(extcon_get_cable_state_);
+
+/**
+ * extcon_get_cable_state() - Get the status of a specific cable.
+ * @edev:	the extcon device that has the cable.
+ * @cable_name:	cable name.
+ *
+ * Note that this is slower than extcon_get_cable_state_.
+ */
+int extcon_get_cable_state(struct extcon_dev *edev, const char *cable_name)
+{
+	return extcon_get_cable_state_(edev, extcon_find_cable_index
+						(edev, cable_name));
+}
+EXPORT_SYMBOL_GPL(extcon_get_cable_state);
+
+/**
+ * extcon_get_cable_state_() - Set the status of a specific cable.
+ * @edev:	the extcon device that has the cable.
+ * @index:	cable index that can be retrieved by extcon_find_cable_index().
+ * @cable_state:	the new cable status. The default semantics is
+ *			true: attached / false: detached.
+ */
+int extcon_set_cable_state_(struct extcon_dev *edev,
+			int index, bool cable_state)
+{
+	u32 state;
+
+	if (index < 0 || (edev->max_supported && edev->max_supported <= index))
+		return -EINVAL;
+
+	state = cable_state ? (1 << index) : 0;
+	extcon_update_state(edev, 1 << index, state);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(extcon_set_cable_state_);
+
+/**
+ * extcon_get_cable_state() - Set the status of a specific cable.
+ * @edev:	the extcon device that has the cable.
+ * @cable_name:	cable name.
+ * @cable_state:	the new cable status. The default semantics is
+ *			true: attached / false: detached.
+ *
+ * Note that this is slower than extcon_set_cable_state_.
+ */
+int extcon_set_cable_state(struct extcon_dev *edev,
+			const char *cable_name, bool cable_state)
+{
+	return extcon_set_cable_state_(edev, extcon_find_cable_index
+					(edev, cable_name), cable_state);
+}
+EXPORT_SYMBOL_GPL(extcon_set_cable_state);
+
+/**
  * extcon_get_extcon_dev() - Get the extcon device instance from the name
  * @extcon_name:	The extcon name provided with extcon_dev_register()
  */
@@ -147,11 +376,88 @@ out:
 }
 EXPORT_SYMBOL_GPL(extcon_get_extcon_dev);
 
+static int _call_per_cable(struct notifier_block *nb, unsigned long val,
+			   void *ptr)
+{
+	struct extcon_specific_cable_nb *obj = container_of(nb,
+			struct extcon_specific_cable_nb, internal_nb);
+	struct extcon_dev *edev = ptr;
+
+	if ((val & (1 << obj->cable_index)) !=
+	    (edev->state & (1 << obj->cable_index))) {
+		obj->previous_value = val;
+		return obj->user_nb->notifier_call(nb, val, ptr);
+	}
+
+	return NOTIFY_OK;
+}
+
+/**
+ * extcon_register_interest() - Register a notifier for a state change of a
+ *			      specific cable, not a entier set of cables of a
+ *			      extcon device.
+ * @obj:	an empty extcon_specific_cable_nb object to be returned.
+ * @extcon_name:	the name of extcon device.
+ * @cable_name:		the target cable name.
+ * @nb:		the notifier block to get notified.
+ *
+ * Provide an empty extcon_specific_cable_nb. extcon_register_interest() sets
+ * the struct for you.
+ *
+ * extcon_register_interest is a helper function for those who want to get
+ * notification for a single specific cable's status change. If a user wants
+ * to get notification for any changes of all cables of a extcon device,
+ * he/she should use the general extcon_register_notifier().
+ *
+ * Note that the second parameter given to the callback of nb (val) is
+ * "old_state", not the current state. The current state can be retrieved
+ * by looking at the third pameter (edev pointer)'s state value.
+ */
+int extcon_register_interest(struct extcon_specific_cable_nb *obj,
+			     const char *extcon_name, const char *cable_name,
+			     struct notifier_block *nb)
+{
+	if (!obj || !extcon_name || !cable_name || !nb)
+		return -EINVAL;
+
+	obj->edev = extcon_get_extcon_dev(extcon_name);
+	if (!obj->edev)
+		return -ENODEV;
+
+	obj->cable_index = extcon_find_cable_index(obj->edev, cable_name);
+	if (obj->cable_index < 0)
+		return -ENODEV;
+
+	obj->user_nb = nb;
+
+	obj->internal_nb.notifier_call = _call_per_cable;
+
+	return raw_notifier_chain_register(&obj->edev->nh, &obj->internal_nb);
+}
+
+/**
+ * extcon_unregister_interest() - Unregister the notifier registered by
+ *				extcon_register_interest().
+ * @obj:	the extcon_specific_cable_nb object returned by
+ *		extcon_register_interest().
+ */
+int extcon_unregister_interest(struct extcon_specific_cable_nb *obj)
+{
+	if (!obj)
+		return -EINVAL;
+
+	return raw_notifier_chain_unregister(&obj->edev->nh, &obj->internal_nb);
+}
+
 /**
  * extcon_register_notifier() - Register a notifee to get notified by
  *			      any attach status changes from the extcon.
  * @edev:	the extcon device.
  * @nb:		a notifier block to be registered.
+ *
+ * Note that the second parameter given to the callback of nb (val) is
+ * "old_state", not the current state. The current state can be retrieved
+ * by looking at the third pameter (edev pointer)'s state value.
  */
 int extcon_register_notifier(struct extcon_dev *edev,
 			struct notifier_block *nb)
@@ -173,8 +479,9 @@ int extcon_unregister_notifier(struct extcon_dev *edev,
 EXPORT_SYMBOL_GPL(extcon_unregister_notifier);
 
 static struct device_attribute extcon_attrs[] = {
-	__ATTR_RO(state),
+	__ATTR(state, S_IRUGO | S_IWUSR, state_show, state_store),
 	__ATTR_RO(name),
+	__ATTR_NULL,
 };
 
 static int create_extcon_class(void)
@@ -202,6 +509,16 @@ static void extcon_cleanup(struct extcon_dev *edev, bool skip)
 	mutex_unlock(&extcon_dev_list_lock);
 
 	if (!skip && get_device(edev->dev)) {
+		int index;
+
+		for (index = 0; index < edev->max_supported; index++)
+			kfree(edev->cables[index].attr_g.name);
+
+		if (edev->max_supported) {
+			kfree(edev->extcon_dev_type.groups);
+			kfree(edev->cables);
+		}
+
 		device_unregister(edev->dev);
 		put_device(edev->dev);
 	}
@@ -216,6 +533,10 @@ static void extcon_dev_release(struct device *dev)
 	extcon_cleanup(edev, true);
 }
 
+static void dummy_sysfs_dev_release(struct device *dev)
+{
+}
+
 /**
  * extcon_dev_register() - Register a new extcon device
  * @edev	: the new extcon device (should be allocated before calling)
@@ -228,7 +549,7 @@ static void extcon_dev_release(struct device *dev)
  */
 int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
 {
-	int ret;
+	int ret, index = 0;
 
 	if (!extcon_class) {
 		ret = create_extcon_class();
@@ -236,12 +557,93 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
 			return ret;
 	}
 
+	if (edev->supported_cable) {
+		/* Get size of array */
+		for (index = 0; edev->supported_cable[index]; index++)
+			;
+		edev->max_supported = index;
+	} else {
+		edev->max_supported = 0;
+	}
+
+	if (index > SUPPORTED_CABLE_MAX) {
+		dev_err(edev->dev, "extcon: maximum number of supported cables exceeded.\n");
+		return -EINVAL;
+	}
+
 	edev->dev = kzalloc(sizeof(struct device), GFP_KERNEL);
 	edev->dev->parent = dev;
 	edev->dev->class = extcon_class;
 	edev->dev->release = extcon_dev_release;
 
 	dev_set_name(edev->dev, edev->name ? edev->name : dev_name(dev));
+
+	if (edev->max_supported) {
+		char buf[10];
+		char *str;
+		struct extcon_cable *cable;
+
+		edev->cables = kzalloc(sizeof(struct extcon_cable) *
+				       edev->max_supported, GFP_KERNEL);
+		if (!edev->cables) {
+			ret = -ENOMEM;
+			goto err_sysfs_alloc;
+		}
+		for (index = 0; index < edev->max_supported; index++) {
+			cable = &edev->cables[index];
+
+			snprintf(buf, 10, "cable.%d", index);
+			str = kzalloc(sizeof(char) * (strlen(buf) + 1),
+				      GFP_KERNEL);
+			if (!str) {
+				for (index--; index >= 0; index--) {
+					cable = &edev->cables[index];
+					kfree(cable->attr_g.name);
+				}
+				ret = -ENOMEM;
+
+				goto err_alloc_cables;
+			}
+			strcpy(str, buf);
+
+			cable->edev = edev;
+			cable->cable_index = index;
+			cable->attrs[0] = &cable->attr_name.attr;
+			cable->attrs[1] = &cable->attr_state.attr;
+			cable->attrs[2] = NULL;
+			cable->attr_g.name = str;
+			cable->attr_g.attrs = cable->attrs;
+
+			cable->attr_name.attr.name = "name";
+			cable->attr_name.attr.mode = 0444;
+			cable->attr_name.show = cable_name_show;
+
+			cable->attr_state.attr.name = "state";
+			cable->attr_state.attr.mode = 0644;
+			cable->attr_state.show = cable_state_show;
+			cable->attr_state.store = cable_state_store;
+		}
+	}
+
+	if (edev->max_supported) {
+		edev->extcon_dev_type.groups =
+			kzalloc(sizeof(struct attribute_group *) *
+				(edev->max_supported + 1), GFP_KERNEL);
+		if (!edev->extcon_dev_type.groups) {
+			ret = -ENOMEM;
+			goto err_alloc_groups;
+		}
+
+		edev->extcon_dev_type.name = dev_name(edev->dev);
+		edev->extcon_dev_type.release = dummy_sysfs_dev_release;
+
+		for (index = 0; index < edev->max_supported; index++)
+			edev->extcon_dev_type.groups[index] =
+				&edev->cables[index].attr_g;
+
+		edev->dev->type = &edev->extcon_dev_type;
+	}
+
 	ret = device_register(edev->dev);
 	if (ret) {
 		put_device(edev->dev);
@@ -253,6 +655,8 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
 					       dev);
 #endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
 
+	spin_lock_init(&edev->lock);
+
 	RAW_INIT_NOTIFIER_HEAD(&edev->nh);
 
 	dev_set_drvdata(edev->dev, edev);
@@ -265,6 +669,15 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
 	return 0;
 
 err_dev:
+	if (edev->max_supported)
+		kfree(edev->extcon_dev_type.groups);
+err_alloc_groups:
+	for (index = 0; index < edev->max_supported; index++)
+		kfree(edev->cables[index].attr_g.name);
+err_alloc_cables:
+	if (edev->max_supported)
+		kfree(edev->cables);
+err_sysfs_alloc:
 	kfree(edev->dev);
 	return ret;
 }
diff --git a/include/linux/extcon.h b/include/linux/extcon.h
index 50be845..99ff60e 100644
--- a/include/linux/extcon.h
+++ b/include/linux/extcon.h
@@ -24,10 +24,60 @@
 #define __LINUX_EXTCON_H__
 
 #include <linux/notifier.h>
+
+#define SUPPORTED_CABLE_MAX	32
+#define CABLE_NAME_MAX		30
+
+/*
+ * The standard cable name is to help support general notifier
+ * and notifee device drivers to share the common names.
+ * Please use standard cable names unless your notifier device has
+ * a very unique and abnormal cable or
+ * the cable type is supposed to be used with only one unique
+ * pair of notifier/notifee devices.
+ *
+ * Please add any other "standard" cables used with extcon dev.
+ *
+ * You may add a dot and number to specify version or specification
+ * of the specific cable if it is required. (e.g., "Fast-charger.18"
+ * and "Fast-charger.10" for 1.8A and 1.0A chargers)
+ * However, the notifee and notifier should be able to handle such
+ * string and if the notifee can negotiate the protocol or idenify,
+ * you don't need such convention. This convention is helpful when
+ * notifier can distinguish but notifiee cannot.
+ */
+enum extcon_cable_name {
+	EXTCON_USB = 0,
+	EXTCON_USB_HOST,
+	EXTCON_TA, /* Travel Adaptor */
+	EXTCON_FAST_CHARGER,
+	EXTCON_SLOW_CHARGER,
+	EXTCON_CHARGE_DOWNSTREAM, /* Charging an external device */
+	EXTCON_HDMI,
+	EXTCON_MHL,
+	EXTCON_DVI,
+	EXTCON_VGA,
+	EXTCON_DOCK,
+	EXTCON_LINE_IN,
+	EXTCON_LINE_OUT,
+	EXTCON_MIC_IN,
+	EXTCON_HEADPHONE_OUT,
+	EXTCON_SPDIF_IN,
+	EXTCON_SPDIF_OUT,
+	EXTCON_VIDEO_IN,
+	EXTCON_VIDEO_OUT,
+};
+extern const char *extcon_cable_name[];
+
+struct extcon_cable;
+
 /**
  * struct extcon_dev - An extcon device represents one external connector.
  * @name	The name of this extcon device. Parent device name is used
  *		if NULL.
+ * @supported_cable	Array of supported cable name ending with NULL.
+ *			If supported_cable is NULL, cable name related APIs
+ *			are disabled.
  * @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
@@ -38,6 +88,11 @@
  * @nh	Notifier for the state change events from this extcon
  * @entry	To support list of extcon devices so that uses can search
  *		for extcon devices based on the extcon name.
+ * @lock
+ * @max_supported	Internal value to store the number of cables.
+ * @extcon_dev_type	Device_type struct to provide attribute_groups
+ *			customized for each extcon device.
+ * @cables	Sysfs subdirectories. Each represents one cable.
  *
  * In most cases, users only need to provide "User initializing data" of
  * this struct when registering an extcon. In some exceptional cases,
@@ -47,6 +102,7 @@
 struct extcon_dev {
 	/* --- Optional user initializing data --- */
 	const char	*name;
+	const char **supported_cable;
 
 	/* --- Optional callbacks to override class functions --- */
 	ssize_t	(*print_name)(struct extcon_dev *edev, char *buf);
@@ -57,6 +113,49 @@ struct extcon_dev {
 	u32		state;
 	struct raw_notifier_head nh;
 	struct list_head entry;
+	spinlock_t lock; /* could be called by irq handler */
+	int max_supported;
+
+	/* /sys/class/extcon/.../cable.n/... */
+	struct device_type extcon_dev_type;
+	struct extcon_cable *cables;
+};
+
+/**
+ * struct extcon_cable	- An internal data for each cable of extcon device.
+ * @edev	The extcon device
+ * @cable_index	Index of this cable in the edev
+ * @attr_g	Attribute group for the cable
+ * @attr_name	"name" sysfs entry
+ * @attr_state	"state" sysfs entry
+ * @attrs	Array pointing to attr_name and attr_state for attr_g
+ */
+struct extcon_cable {
+	struct extcon_dev *edev;
+	int cable_index;
+
+	struct attribute_group attr_g;
+	struct device_attribute attr_name;
+	struct device_attribute attr_state;
+
+	struct attribute *attrs[3]; /* to be fed to attr_g.attrs */
+};
+
+/**
+ * struct extcon_specific_cable_nb - An internal data for
+ *				extcon_register_interest().
+ * @internal_nb	a notifier block bridging extcon notifier and cable notifier.
+ * @user_nb	user provided notifier block for events from a specific cable.
+ * @cable_index	the target cable.
+ * @edev	the target extcon device.
+ * @previous_value	the saved previous event value.
+ */
+struct extcon_specific_cable_nb {
+	struct notifier_block internal_nb;
+	struct notifier_block *user_nb;
+	int cable_index;
+	struct extcon_dev *edev;
+	unsigned long previous_value;
 };
 
 #ifdef CONFIG_EXTCON
@@ -69,16 +168,54 @@ extern int extcon_dev_register(struct extcon_dev *edev, struct device *dev);
 extern void extcon_dev_unregister(struct extcon_dev *edev);
 extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name);
 
+/*
+ * get/set/update_state access the 32b encoded state value, which represents
+ * states of all possible cables of the multistate port. For example, if one
+ * calls extcon_set_state(edev, 0x7), it may mean that all the three cables
+ * are attached to the port.
+ */
 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);
+
+/*
+ * get/set_cable_state access each bit of the 32b encoded state value.
+ * They are used to access the status of each cable based on the cable_name
+ * or cable_index, which is retrived by extcon_find_cable_index
+ */
+extern int extcon_find_cable_index(struct extcon_dev *sdev,
+				   const char *cable_name);
+extern int extcon_get_cable_state_(struct extcon_dev *edev, int cable_index);
+extern int extcon_set_cable_state_(struct extcon_dev *edev, int cable_index,
+				   bool cable_state);
+
+extern int extcon_get_cable_state(struct extcon_dev *edev,
+				  const char *cable_name);
+extern int extcon_set_cable_state(struct extcon_dev *edev,
+				  const char *cable_name, bool cable_state);
+
+/*
+ * Following APIs are for notifiees (those who want to be notified)
+ * to register a callback for events from a specific cable of the extcon.
+ * Notifiees are the connected device drivers wanting to get notified by
+ * a specific external port of a connection device.
+ */
+extern int extcon_register_interest(struct extcon_specific_cable_nb *obj,
+				    const char *extcon_name,
+				    const char *cable_name,
+				    struct notifier_block *nb);
+extern int extcon_unregister_interest(struct extcon_specific_cable_nb *nb);
 
 /*
  * Following APIs are to monitor every action of a notifier.
  * Registerer gets notified for every external port of a connection device.
+ * Probably this could be used to debug an action of notifier; however,
+ * we do not recommend to use this at normal 'notifiee' device drivers who
+ * want to be notified by a specific external port of the notifier.
  */
 extern int extcon_register_notifier(struct extcon_dev *edev,
 				    struct notifier_block *nb);
@@ -99,6 +236,41 @@ static inline u32 extcon_get_state(struct extcon_dev *edev)
 }
 
 static inline void extcon_set_state(struct extcon_dev *edev, u32 state) { }
+
+static inline void extcon_update_state(struct extcon_dev *edev, u32 mask,
+				       u32 state)
+{ }
+
+static inline int extcon_find_cable_index(struct extcon_dev *edev,
+					  const char *cable_name)
+{
+	return 0;
+}
+
+static inline int extcon_get_cable_state_(struct extcon_dev *edev,
+					  int cable_index)
+{
+	return 0;
+}
+
+static inline int extcon_set_cable_state_(struct extcon_dev *edev,
+					  int cable_index, bool cable_state)
+{
+	return 0;
+}
+
+static inline int extcon_get_cable_state(struct extcon_dev *edev,
+			const char *cable_name)
+{
+	return 0;
+}
+
+static inline int extcon_set_cable_state(struct extcon_dev *edev,
+			const char *cable_name, int state)
+{
+	return 0;
+}
+
 static inline struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
 {
 	return NULL;
@@ -116,5 +288,17 @@ static inline int extcon_unregister_notifier(struct extcon_dev *edev,
 	return 0;
 }
 
+static inline int extcon_register_interest(struct extcon_specific_cable_nb *obj,
+					   const char *extcon_name,
+					   const char *cable_name,
+					   struct notifier_block *nb)
+{
+	return 0;
+}
+
+static inline int extcon_unregister_interest(struct extcon_specific_cable_nb *)
+{
+	return 0;
+}
 #endif /* CONFIG_EXTCON */
 #endif /* __LINUX_EXTCON_H__ */
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH v7 5/6] Extcon: support mutually exclusive relation between cables.
  2012-03-23  8:52 [PATCH v7 0/6] Introduce External Connector Class (extcon) MyungJoo Ham
                   ` (3 preceding siblings ...)
  2012-03-23  8:53 ` [PATCH v7 4/6] Extcon: support multiple states at a device MyungJoo Ham
@ 2012-03-23  8:53 ` MyungJoo Ham
  2012-03-23  8:53 ` [PATCH v7 6/6] Documentation/extcon: porting guide for Android kernel switch driver MyungJoo Ham
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 51+ messages in thread
From: MyungJoo Ham @ 2012-03-23  8:53 UTC (permalink / raw)
  To: Arnd Bergmann, LKML
  Cc: NeilBrown, Randy Dunlap, Mike Lockwood, Arve Hjønnevag,
	Kyungmin Park, gregkh, Linus Walleij, Dmitry Torokhov,
	Morten CHRISTIANSEN, Mark Brown, John Stultz, Joerg Roedel,
	myungjoo.ham, cw00.choi

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 59f32c7..3f9e9c9 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 99ff60e..c9e2de8 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


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH v7 6/6] Documentation/extcon: porting guide for Android kernel switch driver.
  2012-03-23  8:52 [PATCH v7 0/6] Introduce External Connector Class (extcon) MyungJoo Ham
                   ` (4 preceding siblings ...)
  2012-03-23  8:53 ` [PATCH v7 5/6] Extcon: support mutually exclusive relation between cables MyungJoo Ham
@ 2012-03-23  8:53 ` 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
  7 siblings, 0 replies; 51+ messages in thread
From: MyungJoo Ham @ 2012-03-23  8:53 UTC (permalink / raw)
  To: Arnd Bergmann, LKML
  Cc: NeilBrown, Randy Dunlap, Mike Lockwood, Arve Hjønnevag,
	Kyungmin Park, gregkh, Linus Walleij, Dmitry Torokhov,
	Morten CHRISTIANSEN, Mark Brown, John Stultz, Joerg Roedel,
	myungjoo.ham, cw00.choi

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 Documentation/extcon/porting-android-switch-class |  124 +++++++++++++++++++++
 1 files changed, 124 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/extcon/porting-android-switch-class

diff --git a/Documentation/extcon/porting-android-switch-class b/Documentation/extcon/porting-android-switch-class
new file mode 100644
index 0000000..eb0fa5f
--- /dev/null
+++ b/Documentation/extcon/porting-android-switch-class
@@ -0,0 +1,124 @@
+
+	Staging/Android Switch Class Porting Guide
+	(linux/drivers/staging/android/switch)
+	(c) Copyright 2012 Samsung Electronics
+
+AUTHORS
+MyungJoo Ham <myungjoo.ham@samsung.com>
+
+/*****************************************************************
+ * CHAPTER 1.                                                    *
+ * PORTING SWITCH CLASS DEVICE DRIVERS                           *
+ *****************************************************************/
+
+****** STEP 1. Basic Functionality
+	No extcon extended feature, but switch features only.
+
+- struct switch_dev (fed to switch_dev_register/unregister)
+    @name: no change
+    @dev: no change
+    @index: drop (not used in switch device driver side anyway)
+    @state: no change
+	If you have used @state with magic numbers, keep it
+	at this step.
+    @print_name: no change but type change (switch_dev->extcon_dev)
+    @print_state: no change but type change (switch_dev->extcon_dev)
+
+- switch_dev_register(sdev, dev)
+	=> extcon_dev_register(edev, dev)
+	: no change but type change (sdev->edev)
+- switch_dev_unregister(sdev)
+	=> extcon_dev_unregister(edev)
+	: no change but type change (sdev->edev)
+- switch_get_state(sdev)
+	=> extcon_get_state(edev)
+	: no change but type change (sdev->edev) and (return: int->u32)
+- switch_set_state(sdev, state)
+	=> extcon_set_state(edev, state)
+	: no change but type change (sdev->edev) and (state: int->u32)
+
+With this changes, the ex-switch extcon class device works as it once
+worked as switch class device. However, it will now have additional
+interfaces (both ABI and in-kernel API) and different ABI locations.
+However, if CONFIG_ANDROID is enabled without CONFIG_ANDROID_SWITCH,
+/sys/class/switch/* will be symbolically linked to /sys/class/extcon/
+so that they are still compatible with legacy userspace processes.
+
+****** STEP 2. Multistate (no more magic numbers in state value)
+	Extcon's extended features for switch device drivers with
+	complex features usually required magic numbers in state
+	value of switch_dev. With extcon, such magic numbers that
+	support multiple cables (
+
+  1. Define cable names at edev->supported_cable.
+  2. (Recommended) remove print_state callback.
+  3. Use extcon_get_cable_state_(edev, index) or
+   extcon_get_cable_state(edev, cable_name) instead of
+   extcon_get_state(edev) if you intend to get a state of a specific
+   cable. Same for set_state. This way, you can remove the usage of
+   magic numbers in state value.
+  4. Use extcon_update_state() if you are updating specific bits of
+   the state value.
+
+Example: a switch device driver w/ magic numbers for two cables.
+	"0x00": no cables connected.
+	"0x01": cable 1 connected
+	"0x02": cable 2 connected
+	"0x03": cable 1 and 2 connected
+  1. edev->supported_cable = {"1", "2", NULL};
+  2. edev->print_state = NULL;
+  3. extcon_get_cable_state_(edev, 0) shows cable 1's state.
+     extcon_get_cable_state(edev, "1") shows cable 1's state.
+     extcon_set_cable_state_(edev, 1) sets cable 2's state.
+     extcon_set_cable_state(edev, "2") sets cable 2's state
+  4. extcon_update_state(edev, 0x01, 0) sets the least bit's 0.
+
+****** STEP 3. Notify other device drivers
+
+  You can notify others of the cable attach/detach events with
+notifier chains.
+
+  At the side of other device drivers (the extcon device itself
+does not need to get notified of its own events), there are two
+methods to register notifier_block for cable events:
+(a) for a specific cable or (b) for every cable.
+
+  (a) extcon_register_interest(obj, extcon_name, cable_name, nb)
+	Example: want to get news of "MAX8997_MUIC"'s "USB" cable
+
+	obj = kzalloc(sizeof(struct extcon_specific_cable_nb),
+		      GFP_KERNEL);
+	nb->notifier_call = the_callback_to_handle_usb;
+
+	extcon_register_intereset(obj, "MAX8997_MUIC", "USB", nb);
+
+  (b) extcon_register_notifier(edev, nb)
+	Call nb for any changes in edev.
+
+  Please note that in order to properly behave with method (a),
+the extcon device driver should support multistate feature (STEP 2).
+
+****** STEP 4. Inter-cable relation (mutually exclusive)
+
+  You can provide inter-cable mutually exclusiveness information
+for an extcon device. When cables A and B are declared to be mutually
+exclusive, the two cables cannot be in ATTACHED state simulteneously.
+
+
+/*****************************************************************
+ * CHAPTER 2.                                                    *
+ * PORTING USERSPACE w/ SWITCH CLASS DEVICE SUPPORT              *
+ *****************************************************************/
+
+****** ABI Location
+
+  If "CONFIG_ANDROID" is enabled and "CONFIG_ANDROID_SWITCH" is
+disabled, /sys/class/switch/* are created as symbolic links to
+/sys/class/extcon/*. Because CONFIG_ANDROID_SWITCH creates
+/sys/class/switch directory, we disable symboling linking if
+CONFIG_ANDROID_SWITCH is enabled.
+
+  The two files of switch class, name and state, are provided with
+extcon, too. When the multistate support (STEP 2 of CHAPTER 1.) is
+not enabled or print_state callback is supplied, the output of
+state ABI is same with switch class.
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [RFC PATCH] Remove "switch" class in drivers/staging/android/switch
  2012-03-23  8:52 [PATCH v7 0/6] Introduce External Connector Class (extcon) MyungJoo Ham
                   ` (5 preceding siblings ...)
  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 ` MyungJoo Ham
  2012-03-28  9:39 ` [PATCH v7 0/6] Introduce External Connector Class (extcon) MyungJoo Ham
  7 siblings, 0 replies; 51+ messages in thread
From: MyungJoo Ham @ 2012-03-23  8:53 UTC (permalink / raw)
  To: Arnd Bergmann, LKML
  Cc: NeilBrown, Randy Dunlap, Mike Lockwood, Arve Hjønnevag,
	Kyungmin Park, gregkh, Linus Walleij, Dmitry Torokhov,
	Morten CHRISTIANSEN, Mark Brown, John Stultz, Joerg Roedel,
	myungjoo.ham, cw00.choi

Because extcon can also be a switch class for legacy userspace (Android)
and is a superset of switch class in drivers/staging/android/switch,
switch class may be removed.

- Remove switch class
- Remove switch class consideration in extcon class

This RFC patch is to suggest an option to the patchset
"[PATCH v7 0/6] Introduce External Connector Class (extcon)".


Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/extcon/extcon_class.c                 |   12 +-
 drivers/staging/android/Kconfig               |    2 -
 drivers/staging/android/Makefile              |    1 -
 drivers/staging/android/switch/Kconfig        |   11 --
 drivers/staging/android/switch/Makefile       |    4 -
 drivers/staging/android/switch/switch.h       |   53 --------
 drivers/staging/android/switch/switch_class.c |  174 -------------------------
 drivers/staging/android/switch/switch_gpio.c  |  172 ------------------------
 8 files changed, 6 insertions(+), 423 deletions(-)
 delete mode 100644 drivers/staging/android/switch/Kconfig
 delete mode 100644 drivers/staging/android/switch/Makefile
 delete mode 100644 drivers/staging/android/switch/switch.h
 delete mode 100644 drivers/staging/android/switch/switch_class.c
 delete mode 100644 drivers/staging/android/switch/switch_gpio.c

diff --git a/drivers/extcon/extcon_class.c b/drivers/extcon/extcon_class.c
index 3f9e9c9..733ed55 100644
--- a/drivers/extcon/extcon_class.c
+++ b/drivers/extcon/extcon_class.c
@@ -65,9 +65,9 @@ const char *extcon_cable_name[] = {
 };
 
 struct class *extcon_class;
-#if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
+#if defined(CONFIG_ANDROID)
 static struct class_compat *switch_class;
-#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
+#endif /* CONFIG_ANDROID */
 
 static LIST_HEAD(extcon_dev_list);
 static DEFINE_MUTEX(extcon_dev_list_lock);
@@ -532,11 +532,11 @@ static int create_extcon_class(void)
 			return PTR_ERR(extcon_class);
 		extcon_class->dev_attrs = extcon_attrs;
 
-#if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
+#if defined(CONFIG_ANDROID)
 		switch_class = class_compat_register("switch");
 		if (WARN(!switch_class, "cannot allocate"))
 			return -ENOMEM;
-#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
+#endif /* CONFIG_ANDROID */
 	}
 
 	return 0;
@@ -749,11 +749,11 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
 		put_device(edev->dev);
 		goto err_dev;
 	}
-#if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
+#if defined(CONFIG_ANDROID)
 	if (switch_class)
 		ret = class_compat_create_link(switch_class, edev->dev,
 					       dev);
-#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
+#endif /* CONFIG_ANDROID */
 
 	spin_lock_init(&edev->lock);
 
diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig
index fef3580..b802934 100644
--- a/drivers/staging/android/Kconfig
+++ b/drivers/staging/android/Kconfig
@@ -100,8 +100,6 @@ config ANDROID_LOW_MEMORY_KILLER
 	---help---
 	  Register processes to be killed when memory is low
 
-source "drivers/staging/android/switch/Kconfig"
-
 endif # if ANDROID
 
 endmenu
diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile
index 5fcc24f..206e70c 100644
--- a/drivers/staging/android/Makefile
+++ b/drivers/staging/android/Makefile
@@ -5,4 +5,3 @@ obj-$(CONFIG_ANDROID_RAM_CONSOLE)	+= ram_console.o
 obj-$(CONFIG_ANDROID_TIMED_OUTPUT)	+= timed_output.o
 obj-$(CONFIG_ANDROID_TIMED_GPIO)	+= timed_gpio.o
 obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER)	+= lowmemorykiller.o
-obj-$(CONFIG_ANDROID_SWITCH)		+= switch/
diff --git a/drivers/staging/android/switch/Kconfig b/drivers/staging/android/switch/Kconfig
deleted file mode 100644
index 36846f6..0000000
--- a/drivers/staging/android/switch/Kconfig
+++ /dev/null
@@ -1,11 +0,0 @@
-menuconfig ANDROID_SWITCH
-	tristate "Android Switch class support"
-	help
-	  Say Y here to enable Android switch class support. This allows
-	  monitoring switches by userspace via sysfs and uevent.
-
-config ANDROID_SWITCH_GPIO
-	tristate "Android GPIO Switch support"
-	depends on GENERIC_GPIO && ANDROID_SWITCH
-	help
-	  Say Y here to enable GPIO based switch support.
diff --git a/drivers/staging/android/switch/Makefile b/drivers/staging/android/switch/Makefile
deleted file mode 100644
index d76bfdc..0000000
--- a/drivers/staging/android/switch/Makefile
+++ /dev/null
@@ -1,4 +0,0 @@
-# Android Switch Class Driver
-obj-$(CONFIG_ANDROID_SWITCH)		+= switch_class.o
-obj-$(CONFIG_ANDROID_SWITCH_GPIO)	+= switch_gpio.o
-
diff --git a/drivers/staging/android/switch/switch.h b/drivers/staging/android/switch/switch.h
deleted file mode 100644
index 4fcb310..0000000
--- a/drivers/staging/android/switch/switch.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- *  Switch class driver
- *
- * Copyright (C) 2008 Google, Inc.
- * Author: Mike Lockwood <lockwood@android.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
-*/
-
-#ifndef __LINUX_SWITCH_H__
-#define __LINUX_SWITCH_H__
-
-struct switch_dev {
-	const char	*name;
-	struct device	*dev;
-	int		index;
-	int		state;
-
-	ssize_t	(*print_name)(struct switch_dev *sdev, char *buf);
-	ssize_t	(*print_state)(struct switch_dev *sdev, char *buf);
-};
-
-struct gpio_switch_platform_data {
-	const char *name;
-	unsigned	gpio;
-
-	/* if NULL, switch_dev.name will be printed */
-	const char *name_on;
-	const char *name_off;
-	/* if NULL, "0" or "1" will be printed */
-	const char *state_on;
-	const char *state_off;
-};
-
-extern int switch_dev_register(struct switch_dev *sdev);
-extern void switch_dev_unregister(struct switch_dev *sdev);
-
-static inline int switch_get_state(struct switch_dev *sdev)
-{
-	return sdev->state;
-}
-
-extern void switch_set_state(struct switch_dev *sdev, int state);
-
-#endif /* __LINUX_SWITCH_H__ */
diff --git a/drivers/staging/android/switch/switch_class.c b/drivers/staging/android/switch/switch_class.c
deleted file mode 100644
index 7468044..0000000
--- a/drivers/staging/android/switch/switch_class.c
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * switch_class.c
- *
- * Copyright (C) 2008 Google, Inc.
- * Author: Mike Lockwood <lockwood@android.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
-*/
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/fs.h>
-#include <linux/err.h>
-#include "switch.h"
-
-struct class *switch_class;
-static atomic_t device_count;
-
-static ssize_t state_show(struct device *dev, struct device_attribute *attr,
-		char *buf)
-{
-	struct switch_dev *sdev = (struct switch_dev *)
-		dev_get_drvdata(dev);
-
-	if (sdev->print_state) {
-		int ret = sdev->print_state(sdev, buf);
-		if (ret >= 0)
-			return ret;
-	}
-	return sprintf(buf, "%d\n", sdev->state);
-}
-
-static ssize_t name_show(struct device *dev, struct device_attribute *attr,
-		char *buf)
-{
-	struct switch_dev *sdev = (struct switch_dev *)
-		dev_get_drvdata(dev);
-
-	if (sdev->print_name) {
-		int ret = sdev->print_name(sdev, buf);
-		if (ret >= 0)
-			return ret;
-	}
-	return sprintf(buf, "%s\n", sdev->name);
-}
-
-static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, state_show, NULL);
-static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, name_show, NULL);
-
-void switch_set_state(struct switch_dev *sdev, int state)
-{
-	char name_buf[120];
-	char state_buf[120];
-	char *prop_buf;
-	char *envp[3];
-	int env_offset = 0;
-	int length;
-
-	if (sdev->state != state) {
-		sdev->state = state;
-
-		prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
-		if (prop_buf) {
-			length = name_show(sdev->dev, NULL, prop_buf);
-			if (length > 0) {
-				if (prop_buf[length - 1] == '\n')
-					prop_buf[length - 1] = 0;
-				snprintf(name_buf, sizeof(name_buf),
-					"SWITCH_NAME=%s", prop_buf);
-				envp[env_offset++] = name_buf;
-			}
-			length = state_show(sdev->dev, NULL, prop_buf);
-			if (length > 0) {
-				if (prop_buf[length - 1] == '\n')
-					prop_buf[length - 1] = 0;
-				snprintf(state_buf, sizeof(state_buf),
-					"SWITCH_STATE=%s", prop_buf);
-				envp[env_offset++] = state_buf;
-			}
-			envp[env_offset] = NULL;
-			kobject_uevent_env(&sdev->dev->kobj, KOBJ_CHANGE, envp);
-			free_page((unsigned long)prop_buf);
-		} else {
-			printk(KERN_ERR "out of memory in switch_set_state\n");
-			kobject_uevent(&sdev->dev->kobj, KOBJ_CHANGE);
-		}
-	}
-}
-EXPORT_SYMBOL_GPL(switch_set_state);
-
-static int create_switch_class(void)
-{
-	if (!switch_class) {
-		switch_class = class_create(THIS_MODULE, "switch");
-		if (IS_ERR(switch_class))
-			return PTR_ERR(switch_class);
-		atomic_set(&device_count, 0);
-	}
-
-	return 0;
-}
-
-int switch_dev_register(struct switch_dev *sdev)
-{
-	int ret;
-
-	if (!switch_class) {
-		ret = create_switch_class();
-		if (ret < 0)
-			return ret;
-	}
-
-	sdev->index = atomic_inc_return(&device_count);
-	sdev->dev = device_create(switch_class, NULL,
-		MKDEV(0, sdev->index), NULL, sdev->name);
-	if (IS_ERR(sdev->dev))
-		return PTR_ERR(sdev->dev);
-
-	ret = device_create_file(sdev->dev, &dev_attr_state);
-	if (ret < 0)
-		goto err_create_file_1;
-	ret = device_create_file(sdev->dev, &dev_attr_name);
-	if (ret < 0)
-		goto err_create_file_2;
-
-	dev_set_drvdata(sdev->dev, sdev);
-	sdev->state = 0;
-	return 0;
-
-err_create_file_2:
-	device_remove_file(sdev->dev, &dev_attr_state);
-err_create_file_1:
-	device_destroy(switch_class, MKDEV(0, sdev->index));
-	printk(KERN_ERR "switch: Failed to register driver %s\n", sdev->name);
-
-	return ret;
-}
-EXPORT_SYMBOL_GPL(switch_dev_register);
-
-void switch_dev_unregister(struct switch_dev *sdev)
-{
-	device_remove_file(sdev->dev, &dev_attr_name);
-	device_remove_file(sdev->dev, &dev_attr_state);
-	device_destroy(switch_class, MKDEV(0, sdev->index));
-	dev_set_drvdata(sdev->dev, NULL);
-}
-EXPORT_SYMBOL_GPL(switch_dev_unregister);
-
-static int __init switch_class_init(void)
-{
-	return create_switch_class();
-}
-
-static void __exit switch_class_exit(void)
-{
-	class_destroy(switch_class);
-}
-
-module_init(switch_class_init);
-module_exit(switch_class_exit);
-
-MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
-MODULE_DESCRIPTION("Switch class driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/staging/android/switch/switch_gpio.c b/drivers/staging/android/switch/switch_gpio.c
deleted file mode 100644
index 38b2c2f..0000000
--- a/drivers/staging/android/switch/switch_gpio.c
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * switch_gpio.c
- *
- * Copyright (C) 2008 Google, Inc.
- * Author: Mike Lockwood <lockwood@android.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
-*/
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/workqueue.h>
-#include <linux/gpio.h>
-#include "switch.h"
-
-struct gpio_switch_data {
-	struct switch_dev sdev;
-	unsigned gpio;
-	const char *name_on;
-	const char *name_off;
-	const char *state_on;
-	const char *state_off;
-	int irq;
-	struct work_struct work;
-};
-
-static void gpio_switch_work(struct work_struct *work)
-{
-	int state;
-	struct gpio_switch_data	*data =
-		container_of(work, struct gpio_switch_data, work);
-
-	state = gpio_get_value(data->gpio);
-	switch_set_state(&data->sdev, state);
-}
-
-static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
-{
-	struct gpio_switch_data *switch_data =
-	    (struct gpio_switch_data *)dev_id;
-
-	schedule_work(&switch_data->work);
-	return IRQ_HANDLED;
-}
-
-static ssize_t switch_gpio_print_state(struct switch_dev *sdev, char *buf)
-{
-	struct gpio_switch_data	*switch_data =
-		container_of(sdev, struct gpio_switch_data, sdev);
-	const char *state;
-	if (switch_get_state(sdev))
-		state = switch_data->state_on;
-	else
-		state = switch_data->state_off;
-
-	if (state)
-		return sprintf(buf, "%s\n", state);
-	return -1;
-}
-
-static int gpio_switch_probe(struct platform_device *pdev)
-{
-	struct gpio_switch_platform_data *pdata = pdev->dev.platform_data;
-	struct gpio_switch_data *switch_data;
-	int ret = 0;
-
-	if (!pdata)
-		return -EBUSY;
-
-	switch_data = kzalloc(sizeof(struct gpio_switch_data), GFP_KERNEL);
-	if (!switch_data)
-		return -ENOMEM;
-
-	switch_data->sdev.name = pdata->name;
-	switch_data->gpio = pdata->gpio;
-	switch_data->name_on = pdata->name_on;
-	switch_data->name_off = pdata->name_off;
-	switch_data->state_on = pdata->state_on;
-	switch_data->state_off = pdata->state_off;
-	switch_data->sdev.print_state = switch_gpio_print_state;
-
-	ret = switch_dev_register(&switch_data->sdev);
-	if (ret < 0)
-		goto err_switch_dev_register;
-
-	ret = gpio_request(switch_data->gpio, pdev->name);
-	if (ret < 0)
-		goto err_request_gpio;
-
-	ret = gpio_direction_input(switch_data->gpio);
-	if (ret < 0)
-		goto err_set_gpio_input;
-
-	INIT_WORK(&switch_data->work, gpio_switch_work);
-
-	switch_data->irq = gpio_to_irq(switch_data->gpio);
-	if (switch_data->irq < 0) {
-		ret = switch_data->irq;
-		goto err_detect_irq_num_failed;
-	}
-
-	ret = request_irq(switch_data->irq, gpio_irq_handler,
-			  IRQF_TRIGGER_LOW, pdev->name, switch_data);
-	if (ret < 0)
-		goto err_request_irq;
-
-	/* Perform initial detection */
-	gpio_switch_work(&switch_data->work);
-
-	return 0;
-
-err_request_irq:
-err_detect_irq_num_failed:
-err_set_gpio_input:
-	gpio_free(switch_data->gpio);
-err_request_gpio:
-	switch_dev_unregister(&switch_data->sdev);
-err_switch_dev_register:
-	kfree(switch_data);
-
-	return ret;
-}
-
-static int __devexit gpio_switch_remove(struct platform_device *pdev)
-{
-	struct gpio_switch_data *switch_data = platform_get_drvdata(pdev);
-
-	cancel_work_sync(&switch_data->work);
-	gpio_free(switch_data->gpio);
-	switch_dev_unregister(&switch_data->sdev);
-	kfree(switch_data);
-
-	return 0;
-}
-
-static struct platform_driver gpio_switch_driver = {
-	.probe		= gpio_switch_probe,
-	.remove		= __devexit_p(gpio_switch_remove),
-	.driver		= {
-		.name	= "switch-gpio",
-		.owner	= THIS_MODULE,
-	},
-};
-
-static int __init gpio_switch_init(void)
-{
-	return platform_driver_register(&gpio_switch_driver);
-}
-
-static void __exit gpio_switch_exit(void)
-{
-	platform_driver_unregister(&gpio_switch_driver);
-}
-
-module_init(gpio_switch_init);
-module_exit(gpio_switch_exit);
-
-MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
-MODULE_DESCRIPTION("GPIO Switch driver");
-MODULE_LICENSE("GPL");
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* Re: [PATCH v7 0/6] Introduce External Connector Class (extcon)
  2012-03-23  8:52 [PATCH v7 0/6] Introduce External Connector Class (extcon) MyungJoo Ham
                   ` (6 preceding siblings ...)
  2012-03-23  8:53 ` [RFC PATCH] Remove "switch" class in drivers/staging/android/switch MyungJoo Ham
@ 2012-03-28  9:39 ` MyungJoo Ham
  2012-03-28 14:44   ` Greg KH
  7 siblings, 1 reply; 51+ messages in thread
From: MyungJoo Ham @ 2012-03-28  9:39 UTC (permalink / raw)
  To: Arnd Bergmann, LKML
  Cc: NeilBrown, Randy Dunlap, Mike Lockwood, Arve Hjønnevag,
	Kyungmin Park, gregkh, Linus Walleij, Dmitry Torokhov,
	Morten CHRISTIANSEN, Mark Brown, John Stultz, Joerg Roedel,
	myungjoo.ham, cw00.choi

Dear Arnd,

Could you please consider the EXTCON patchset for the current merge window?

If pulling from a branch is more convinient; please consider using:

A. EXTCON patchset without the RFC patch removing staging/android/switch class
or
B. EXTCON patchset with the RFC patch removing staging/android/switch class.

--------------------
PULL REQUEST for A
--------------------
The following changes since commit b01543dfe67bb1d191998e90d20534dc354de059:

  Linux 3.3-rc4 (2012-02-18 15:53:33 -0800)

are available in the git repository at:
  git://git.infradead.org/users/kmpark/linux-samsung extcon-for-next-2
 (http://git.infradead.org/users/kmpark/linux-samsung/shortlog/refs/heads/extcon-for-next-2)

Donggeun Kim (1):
      Extcon: support notification based on the state changes.

MyungJoo Ham (5):
      Extcon (external connector): import Android's switch class and modify.
      Extcon: support generic GPIO extcon driver
      Extcon: support multiple states at a device.
      Extcon: support mutually exclusive relation between cables.
      Documentation/extcon: porting guide for Android kernel switch driver.

 Documentation/ABI/testing/sysfs-class-extcon      |   97 +++
 Documentation/extcon/porting-android-switch-class |  124 +++
 drivers/Kconfig                                   |    2 +
 drivers/Makefile                                  |    1 +
 drivers/extcon/Kconfig                            |   35 +
 drivers/extcon/Makefile                           |    6 +
 drivers/extcon/extcon_class.c                     |  822 +++++++++++++++++++++
 drivers/extcon/extcon_gpio.c                      |  175 +++++
 include/linux/extcon.h                            |  322 ++++++++
 include/linux/extcon/extcon_gpio.h                |   52 ++
 10 files changed, 1636 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-class-extcon
 create mode 100644 Documentation/extcon/porting-android-switch-class
 create mode 100644 drivers/extcon/Kconfig
 create mode 100644 drivers/extcon/Makefile
 create mode 100644 drivers/extcon/extcon_class.c
 create mode 100644 drivers/extcon/extcon_gpio.c
 create mode 100644 include/linux/extcon.h
 create mode 100644 include/linux/extcon/extcon_gpio.h


--------------------
PULL REQUEST for B
--------------------
The following changes since commit b01543dfe67bb1d191998e90d20534dc354de059:

  Linux 3.3-rc4 (2012-02-18 15:53:33 -0800)

are available in the git repository at:
  git://git.infradead.org/users/kmpark/linux-samsung extcon-for-next
  (http://git.infradead.org/users/kmpark/linux-samsung/shortlog/refs/heads/extcon-for-next)

Donggeun Kim (1):
      Extcon: support notification based on the state changes.

MyungJoo Ham (6):
      Extcon (external connector): import Android's switch class and modify.
      Extcon: support generic GPIO extcon driver
      Extcon: support multiple states at a device.
      Extcon: support mutually exclusive relation between cables.
      Documentation/extcon: porting guide for Android kernel switch driver.
      Remove "switch" class in drivers/staging/android/switch

 Documentation/ABI/testing/sysfs-class-extcon      |   97 +++
 Documentation/extcon/porting-android-switch-class |  124 +++
 drivers/Kconfig                                   |    2 +
 drivers/Makefile                                  |    1 +
 drivers/extcon/Kconfig                            |   35 +
 drivers/extcon/Makefile                           |    6 +
 drivers/extcon/extcon_class.c                     |  822 +++++++++++++++++++++
 drivers/extcon/extcon_gpio.c                      |  175 +++++
 drivers/staging/android/Kconfig                   |    2 -
 drivers/staging/android/Makefile                  |    1 -
 drivers/staging/android/switch/Kconfig            |   11 -
 drivers/staging/android/switch/Makefile           |    4 -
 drivers/staging/android/switch/switch.h           |   53 --
 drivers/staging/android/switch/switch_class.c     |  174 -----
 drivers/staging/android/switch/switch_gpio.c      |  172 -----
 include/linux/extcon.h                            |  322 ++++++++
 include/linux/extcon/extcon_gpio.h                |   52 ++
 17 files changed, 1636 insertions(+), 417 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-class-extcon
 create mode 100644 Documentation/extcon/porting-android-switch-class
 create mode 100644 drivers/extcon/Kconfig
 create mode 100644 drivers/extcon/Makefile
 create mode 100644 drivers/extcon/extcon_class.c
 create mode 100644 drivers/extcon/extcon_gpio.c
 delete mode 100644 drivers/staging/android/switch/Kconfig
 delete mode 100644 drivers/staging/android/switch/Makefile
 delete mode 100644 drivers/staging/android/switch/switch.h
 delete mode 100644 drivers/staging/android/switch/switch_class.c
 delete mode 100644 drivers/staging/android/switch/switch_gpio.c
 create mode 100644 include/linux/extcon.h
 create mode 100644 include/linux/extcon/extcon_gpio.h



On Fri, Mar 23, 2012 at 5:52 PM, MyungJoo Ham <myungjoo.ham@samsung.com> wrote:
> For external connectors, which may have different types of cables attached
> (USB, TA, HDMI, Analog A/V, and others), we often have seperated device
> drivers that detect the state changes at the port and device drivers that
> do something according to the state changes.
>
> An example is a 4-pin (or more pins?) analog audio connector
> (usually referred as "3.5-pi" jacks). It can be implemented with the 5/5
> patch, "adc-jack", supplied with information about IRQ, a method to get
> ADC values, and ADC value ranges. With a 4-pin 3.5-pi port and adc-jack
> driver with proper ADC configurations, the following modes may be
> detected: "standard mono cable", "standard stereo cable",
> "standard stereo + remote cable", "standard stereo + mic + remote cable",
> "standard stereo + different-spec mic + remote cable", and such.
> Note that for 4-pin cables with mic and remote, there are multiple
> standards (at least Samsung and Apple use different configurations for
> those 4-pin earphones although the pins looks exactly same.)
> and they often can be distinguished with ADC values.
>
> Although we may have simply given different "cable id" for "stereo" and
> "stereo + mic". However, for those userspace processes or other device
> drivers that are interested only in "stereo" part of possible combinations,
> having seperated mode/id for "stereo" and "mic" is more desirable.
> For example, if we do not have multistate capabilities, those who want
> to know "stereo" is attached or not should monitor everything:
> "standard stereo cable", "standard stereo + remove cable",
> "standard stereo + mic + remote cable", and others. However, with
> multistate capabilities, they only need to monitor the bit associated
> with "standard stero".
>
> For another example, when MAX8997-MUIC detects a Charger cable insertion,
> another device driver (such as MAX8903 charger, MAX8997 charger, Charger
> Manager, or board file) needs to set charger current limit accordingly
> and when MAX8997-MUIC detects a HDMI cable insertion, multimedia device
> drivers need to do some operations accordingly.
>
>
> This patchset supports the usage of notifier for passing such information
> between device drivers.
>
> Another issue is that at a single switch port, there might be multiple
> and heterogeneous cables attached at the same time. Besides the state
> (Attached or Detached) of each cable may alter independently. Such extcon
> devices that support simultaneous mult-cable attachment include, bot not
> limited to, docks, cradles, and 30-pin-like ports (like that of NURI board
> in linux/arch/arm/mach-exynos, which resembles Samsung Galaxy Tab series).
> For example, the 30-pin port of NURI board may be used to connect analog
> audio cables, analog video cable, and a USB or a charger. Because such
> cables coonnect part of the 30 pins to external devices/cables, the
> possible combinations of external devices/cables vary significantly and
> users may fabricate their own design; e.g., an 30-pin adaptor connecting
> USB, AC-DC adaptor, HDMI, analog audio, analog video, USB-host, SD-card,
> and so on.
>
> In order to address such issues, Android kernel's "Switch" class seems to
> be a good basis and we have implemented "Multistate Switch Class" based on
> it. The "Switch" class code of Android kernel is GPL as well.
>
>
> Though there can be synchronization delay (up to 12hours), you can see
> the extcon-for-next branch at
> http://git.infradead.org/users/kmpark/linux-samsung/shortlog/refs/heads/extcon-for-next
>
> Along with the 6 patches (1/6 ... 6/6), there is one additional RFC patch
> that suggests to remove drivers/staging/android/switch.
>
> Summary of changes from v6 patchset
> - Added documentation, "porting guide for Android switch device drivers"
> - Usage of class_compat to create /sys/class/switch/* along with
> /sys/class/extcon/* if CONFIG_ANDROID is enabled. However, we cannot
> enable "switch" class_compat if CONFIG_ANDROID_SWITCH is enabled because
> we cannot create two classes with the same name.
>
> Summary of changes from v5 patchset
> - Excluded adc-jack from the "default" patchset. Adc-jack will be later
> added after Extcon is finalized.
> - Sysfs style updated
> - Bugfixes after using at Exynos4412 boards with userspace processes
> - Code stype updated
>
> Summary of changes from v4 patchset
> - Bugfixes after testing at Exynos4412 boards with userspace processes
> - Added adc-jack to support 3.5-pi jack and other ADC-based cable detection
> ports.
>
> Summary of changes from v3 patchset
> - Bugfixes after testing at Exynos4412 boards.
>
> Summary of changes from v2 patchset
> - Support to express mutually exclusive set of cables
> - Revised device/class/sysfs-entries add/removal methods
> - Added sysfs entries
> - Revised documentation and comments
> - Some bugfixes
> - Compatible with Android (for extcon devices with "use_class_name_switch" set)
>
> Summary of changes from RFC
> - ABI documentation added
> - Notifees may get notified for a state change of a specific cable, not
> every cable of the corresponding extcon.
> - Added kerneldoc comments
> - Moved to /drivers/extcon/
> - Added helper functions
> - Some bugfixes
>
> Donggeun Kim (1):
>  Extcon: support notification based on the state changes.
>
> MyungJoo Ham (5):
>  Extcon (external connector): import Android's switch class and
>    modify.
>  Extcon: support generic GPIO extcon driver
>  Extcon: support multiple states at a device.
>  Extcon: support mutually exclusive relation between cables.
>  Documentation/extcon: porting guide for Android kernel switch driver.
>
>  Documentation/ABI/testing/sysfs-class-extcon      |   97 +++
>  Documentation/extcon/porting-android-switch-class |  124 +++
>  drivers/Kconfig                                   |    2 +
>  drivers/Makefile                                  |    1 +
>  drivers/extcon/Kconfig                            |   35 +
>  drivers/extcon/Makefile                           |    6 +
>  drivers/extcon/extcon_class.c                     |  822 +++++++++++++++++++++
>  drivers/extcon/extcon_gpio.c                      |  175 +++++
>  include/linux/extcon.h                            |  322 ++++++++
>  include/linux/extcon/extcon_gpio.h                |   52 ++
>  10 files changed, 1636 insertions(+), 0 deletions(-)
>  create mode 100644 Documentation/ABI/testing/sysfs-class-extcon
>  create mode 100644 Documentation/extcon/porting-android-switch-class
>  create mode 100644 drivers/extcon/Kconfig
>  create mode 100644 drivers/extcon/Makefile
>  create mode 100644 drivers/extcon/extcon_class.c
>  create mode 100644 drivers/extcon/extcon_gpio.c
>  create mode 100644 include/linux/extcon.h
>  create mode 100644 include/linux/extcon/extcon_gpio.h
>
> --
> 1.7.4.1
>



-- 
MyungJoo Ham, Ph.D.
System S/W Lab, S/W Center, Samsung Electronics

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v7 0/6] Introduce External Connector Class (extcon)
  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
  0 siblings, 1 reply; 51+ messages in thread
From: Greg KH @ 2012-03-28 14:44 UTC (permalink / raw)
  To: MyungJoo Ham
  Cc: Arnd Bergmann, LKML, NeilBrown, Randy Dunlap, Mike Lockwood,
	Arve Hjønnevag, Kyungmin Park, Linus Walleij,
	Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown, John Stultz,
	Joerg Roedel, cw00.choi

On Wed, Mar 28, 2012 at 06:39:08PM +0900, MyungJoo Ham wrote:
> Dear Arnd,
> 
> Could you please consider the EXTCON patchset for the current merge window?

Wait, no, the current merge window for new stuff closed a week or so
_before_ 3.3 came out.  This needs to be in linux-next before the merge
window opens to ensure that things work properly there before it can be
accepted into the mainline tree.

So this could go into 3.5 at the earliest.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v7 0/6] Introduce External Connector Class (extcon)
  2012-03-28 14:44   ` Greg KH
@ 2012-03-28 15:24     ` Arnd Bergmann
  2012-03-28 15:28       ` Greg KH
  0 siblings, 1 reply; 51+ messages in thread
From: Arnd Bergmann @ 2012-03-28 15:24 UTC (permalink / raw)
  To: Greg KH
  Cc: MyungJoo Ham, LKML, NeilBrown, Randy Dunlap, Mike Lockwood,
	Arve Hjønnevag, Kyungmin Park, Linus Walleij,
	Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown, John Stultz,
	Joerg Roedel, cw00.choi

On Wednesday 28 March 2012, Greg KH wrote:
> 
> On Wed, Mar 28, 2012 at 06:39:08PM +0900, MyungJoo Ham wrote:
> > Dear Arnd,
> > 
> > Could you please consider the EXTCON patchset for the current merge window?
> 
> Wait, no, the current merge window for new stuff closed a week or so
> before 3.3 came out.  This needs to be in linux-next before the merge
> window opens to ensure that things work properly there before it can be
> accepted into the mainline tree.
> 
> So this could go into 3.5 at the earliest.

Right. I'm also not convinced that arm-soc is the right place for this to
go, and I had not planned to add the patches there. Unfortunately I also
haven't followed the last few versions and will have to do at least one
more review round and Ack/Nak the interfaces. I should probably do that
once -rc1 is out, so please send me a reminder if you don't hear back from
me in time.

Any opinions on which tree this should go through? I could take it into
arm-soc, but it's not really ARM-only. Maybe having it go through the
staging tree would be better, leaving MyungJoo in charge afterwards.
I guess we should also add a MAINTAINERS entry for the new subsystem.

	Arnd

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v7 0/6] Introduce External Connector Class (extcon)
  2012-03-28 15:24     ` Arnd Bergmann
@ 2012-03-28 15:28       ` Greg KH
  2012-03-29  4:20         ` Kyungmin Park
  0 siblings, 1 reply; 51+ messages in thread
From: Greg KH @ 2012-03-28 15:28 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: MyungJoo Ham, LKML, NeilBrown, Randy Dunlap, Mike Lockwood,
	Arve Hjønnevag, Kyungmin Park, Linus Walleij,
	Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown, John Stultz,
	Joerg Roedel, cw00.choi

On Wed, Mar 28, 2012 at 03:24:07PM +0000, Arnd Bergmann wrote:
> On Wednesday 28 March 2012, Greg KH wrote:
> > 
> > On Wed, Mar 28, 2012 at 06:39:08PM +0900, MyungJoo Ham wrote:
> > > Dear Arnd,
> > > 
> > > Could you please consider the EXTCON patchset for the current merge window?
> > 
> > Wait, no, the current merge window for new stuff closed a week or so
> > before 3.3 came out.  This needs to be in linux-next before the merge
> > window opens to ensure that things work properly there before it can be
> > accepted into the mainline tree.
> > 
> > So this could go into 3.5 at the earliest.
> 
> Right. I'm also not convinced that arm-soc is the right place for this to
> go, and I had not planned to add the patches there. Unfortunately I also
> haven't followed the last few versions and will have to do at least one
> more review round and Ack/Nak the interfaces. I should probably do that
> once -rc1 is out, so please send me a reminder if you don't hear back from
> me in time.
> 
> Any opinions on which tree this should go through? I could take it into
> arm-soc, but it's not really ARM-only. Maybe having it go through the
> staging tree would be better, leaving MyungJoo in charge afterwards.
> I guess we should also add a MAINTAINERS entry for the new subsystem.

If acks come through, and I'll be glad to review it after -rc1 as well,
I can take it through one of my trees to get merged to Linus for 3.5.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v7 0/6] Introduce External Connector Class (extcon)
  2012-03-28 15:28       ` Greg KH
@ 2012-03-29  4:20         ` Kyungmin Park
  0 siblings, 0 replies; 51+ messages in thread
From: Kyungmin Park @ 2012-03-29  4:20 UTC (permalink / raw)
  To: Greg KH
  Cc: Arnd Bergmann, MyungJoo Ham, LKML, NeilBrown, Randy Dunlap,
	Mike Lockwood, Arve Hjønnevag, Linus Walleij,
	Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown, John Stultz,
	Joerg Roedel, cw00.choi

On 3/29/12, Greg KH <gregkh@linuxfoundation.org> wrote:
> On Wed, Mar 28, 2012 at 03:24:07PM +0000, Arnd Bergmann wrote:
>> On Wednesday 28 March 2012, Greg KH wrote:
>> >
>> > On Wed, Mar 28, 2012 at 06:39:08PM +0900, MyungJoo Ham wrote:
>> > > Dear Arnd,
>> > >
>> > > Could you please consider the EXTCON patchset for the current merge
>> > > window?
>> >
>> > Wait, no, the current merge window for new stuff closed a week or so
>> > before 3.3 came out.  This needs to be in linux-next before the merge
>> > window opens to ensure that things work properly there before it can be
>> > accepted into the mainline tree.
>> >
>> > So this could go into 3.5 at the earliest.
>>
>> Right. I'm also not convinced that arm-soc is the right place for this to
>> go, and I had not planned to add the patches there. Unfortunately I also
>> haven't followed the last few versions and will have to do at least one
>> more review round and Ack/Nak the interfaces. I should probably do that
>> once -rc1 is out, so please send me a reminder if you don't hear back from
>> me in time.
>>
>> Any opinions on which tree this should go through? I could take it into
>> arm-soc, but it's not really ARM-only. Maybe having it go through the
>> staging tree would be better, leaving MyungJoo in charge afterwards.
>> I guess we should also add a MAINTAINERS entry for the new subsystem.
>
> If acks come through, and I'll be glad to review it after -rc1 as well,
> I can take it through one of my trees to get merged to Linus for 3.5.

Okay, Thanks

Kyungmin Park
>
> thanks,
>
> greg k-h
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
>

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v7 1/6] Extcon (external connector): import Android's switch class and modify.
  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
                       ` (2 more replies)
  0 siblings, 3 replies; 51+ messages in thread
From: Greg KH @ 2012-04-18 21:47 UTC (permalink / raw)
  To: MyungJoo Ham
  Cc: Arnd Bergmann, LKML, NeilBrown, Randy Dunlap, Mike Lockwood,
	Arve Hjønnevag, Kyungmin Park, Linus Walleij,
	Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown, John Stultz,
	Joerg Roedel, myungjoo.ham, cw00.choi

On Fri, Mar 23, 2012 at 05:52:58PM +0900, MyungJoo Ham wrote:
> External connector class (extcon) is based on and an extension of
> Android kernel's switch class located at linux/drivers/switch/.

<snip>

This patch breaks the build:
drivers/extcon/extcon_class.c:79:6: error: redefinition of ‘extcon_set_state’
include/linux/extcon.h:80:20: note: previous definition of ‘extcon_set_state’ was here
drivers/extcon/extcon_class.c:170:5: error: redefinition of ‘extcon_dev_register’
include/linux/extcon.h:67:19: note: previous definition of ‘extcon_dev_register’ was here
drivers/extcon/extcon_class.c:214:6: error: redefinition of ‘extcon_dev_unregister’
include/linux/extcon.h:73:20: note: previous definition of ‘extcon_dev_unregister’ was here

So it obviously can't be applied to the tree :(

Did you test it?

Care to redo this series and send it again?

greg k-h

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v7 1/6] Extcon (external connector): import Android's switch class and modify.
  2012-04-18 21:47   ` Greg KH
@ 2012-04-18 23:38     ` Kyungmin Park
  2012-04-18 23:40       ` 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
  2 siblings, 1 reply; 51+ messages in thread
From: Kyungmin Park @ 2012-04-18 23:38 UTC (permalink / raw)
  To: Greg KH
  Cc: MyungJoo Ham, Arnd Bergmann, LKML, NeilBrown, Randy Dunlap,
	Mike Lockwood, Arve Hjønnevag, Linus Walleij,
	Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown, John Stultz,
	Joerg Roedel, myungjoo.ham, cw00.choi

Hi Greg,

Can you check the latest extcon at here?

http://git.infradead.org/users/kmpark/linux-samsung/shortlog/refs/heads/extcon

If any problem with it, please tell me again.

Thank you,
Kyungmin Park

On 4/19/12, Greg KH <gregkh@linuxfoundation.org> wrote:
> On Fri, Mar 23, 2012 at 05:52:58PM +0900, MyungJoo Ham wrote:
>> External connector class (extcon) is based on and an extension of
>> Android kernel's switch class located at linux/drivers/switch/.
>
> <snip>
>
> This patch breaks the build:
> drivers/extcon/extcon_class.c:79:6: error: redefinition of
> ‘extcon_set_state’
> include/linux/extcon.h:80:20: note: previous definition of
> ‘extcon_set_state’ was here
> drivers/extcon/extcon_class.c:170:5: error: redefinition of
> ‘extcon_dev_register’
> include/linux/extcon.h:67:19: note: previous definition of
> ‘extcon_dev_register’ was here
> drivers/extcon/extcon_class.c:214:6: error: redefinition of
> ‘extcon_dev_unregister’
> include/linux/extcon.h:73:20: note: previous definition of
> ‘extcon_dev_unregister’ was here
>
> So it obviously can't be applied to the tree :(
>
> Did you test it?
>
> Care to redo this series and send it again?
>
> greg k-h
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
>

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v7 1/6] Extcon (external connector): import Android's switch class and modify.
  2012-04-18 23:38     ` Kyungmin Park
@ 2012-04-18 23:40       ` Greg KH
  2012-04-18 23:48         ` Kyungmin Park
  0 siblings, 1 reply; 51+ messages in thread
From: Greg KH @ 2012-04-18 23:40 UTC (permalink / raw)
  To: Kyungmin Park
  Cc: MyungJoo Ham, Arnd Bergmann, LKML, NeilBrown, Randy Dunlap,
	Mike Lockwood, Arve Hjønnevag, Linus Walleij,
	Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown, John Stultz,
	Joerg Roedel, myungjoo.ham, cw00.choi

On Thu, Apr 19, 2012 at 08:38:06AM +0900, Kyungmin Park wrote:
> Hi Greg,
> 
> Can you check the latest extcon at here?
> 
> http://git.infradead.org/users/kmpark/linux-samsung/shortlog/refs/heads/extcon

No, please send me patches, that's what I need to have in order to apply
them.

Always make sure that your patches do not break the build along the
series, which is what happened here.  Did you not test this?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v7 1/6] Extcon (external connector): import Android's switch class and modify.
  2012-04-18 23:40       ` Greg KH
@ 2012-04-18 23:48         ` Kyungmin Park
  2012-04-19  0:01           ` Greg KH
  0 siblings, 1 reply; 51+ messages in thread
From: Kyungmin Park @ 2012-04-18 23:48 UTC (permalink / raw)
  To: Greg KH
  Cc: MyungJoo Ham, Arnd Bergmann, LKML, NeilBrown, Randy Dunlap,
	Mike Lockwood, Arve Hjønnevag, Linus Walleij,
	Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown, John Stultz,
	Joerg Roedel, myungjoo.ham, cw00.choi

On 4/19/12, Greg KH <gregkh@linuxfoundation.org> wrote:
> On Thu, Apr 19, 2012 at 08:38:06AM +0900, Kyungmin Park wrote:
>> Hi Greg,
>>
>> Can you check the latest extcon at here?
>>
>> http://git.infradead.org/users/kmpark/linux-samsung/shortlog/refs/heads/extcon
>
> No, please send me patches, that's what I need to have in order to apply
> them.
>
> Always make sure that your patches do not break the build along the
> series, which is what happened here.  Did you not test this?

No, we tested it from kernel 3.2 and generate the patches against the
latest kernel at that time.

Here's another patch based on 3.3-rc4.
http://git.infradead.org/users/kmpark/linux-samsung/shortlog/refs/heads/extcon-for-next

Anyway, as you want, we will send the extcon patches again without build break.

Thank you,
Kyungmin Park

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v7 1/6] Extcon (external connector): import Android's switch class and modify.
  2012-04-18 23:48         ` Kyungmin Park
@ 2012-04-19  0:01           ` Greg KH
  0 siblings, 0 replies; 51+ messages in thread
From: Greg KH @ 2012-04-19  0:01 UTC (permalink / raw)
  To: Kyungmin Park
  Cc: MyungJoo Ham, Arnd Bergmann, LKML, NeilBrown, Randy Dunlap,
	Mike Lockwood, Arve Hjønnevag, Linus Walleij,
	Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown, John Stultz,
	Joerg Roedel, myungjoo.ham, cw00.choi

On Thu, Apr 19, 2012 at 08:48:35AM +0900, Kyungmin Park wrote:
> On 4/19/12, Greg KH <gregkh@linuxfoundation.org> wrote:
> > On Thu, Apr 19, 2012 at 08:38:06AM +0900, Kyungmin Park wrote:
> >> Hi Greg,
> >>
> >> Can you check the latest extcon at here?
> >>
> >> http://git.infradead.org/users/kmpark/linux-samsung/shortlog/refs/heads/extcon
> >
> > No, please send me patches, that's what I need to have in order to apply
> > them.
> >
> > Always make sure that your patches do not break the build along the
> > series, which is what happened here.  Did you not test this?
> 
> No, we tested it from kernel 3.2 and generate the patches against the
> latest kernel at that time.

It wasn't an issue about the kernel it was built against, it is a bug in
this first patch, it just will not build.  That needs to be fixed,
right?

> Here's another patch based on 3.3-rc4.
> http://git.infradead.org/users/kmpark/linux-samsung/shortlog/refs/heads/extcon-for-next
> 
> Anyway, as you want, we will send the extcon patches again without build break.

That's a requirement if you want them to be applied :)

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v7 1/6] Extcon (external connector): import Android's switch class and modify.
  2012-04-18 21:47   ` Greg KH
  2012-04-18 23:38     ` Kyungmin Park
@ 2012-04-19  2:36     ` MyungJoo Ham
  2012-04-19  2:41     ` [PATCH v8 0/6] Introduce External Connector Class (extcon) MyungJoo Ham
  2 siblings, 0 replies; 51+ messages in thread
From: MyungJoo Ham @ 2012-04-19  2:36 UTC (permalink / raw)
  To: Greg KH
  Cc: Arnd Bergmann, LKML, NeilBrown, Randy Dunlap, Mike Lockwood,
	Arve Hjønnevag, Kyungmin Park, Linus Walleij,
	Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown, John Stultz,
	Joerg Roedel, cw00.choi

On Thu, Apr 19, 2012 at 6:47 AM, Greg KH <gregkh@linuxfoundation.org> wrote:
> On Fri, Mar 23, 2012 at 05:52:58PM +0900, MyungJoo Ham wrote:
>> External connector class (extcon) is based on and an extension of
>> Android kernel's switch class located at linux/drivers/switch/.
>
> <snip>
>
> This patch breaks the build:
> drivers/extcon/extcon_class.c:79:6: error: redefinition of ‘extcon_set_state’
> include/linux/extcon.h:80:20: note: previous definition of ‘extcon_set_state’ was here
> drivers/extcon/extcon_class.c:170:5: error: redefinition of ‘extcon_dev_register’
> include/linux/extcon.h:67:19: note: previous definition of ‘extcon_dev_register’ was here
> drivers/extcon/extcon_class.c:214:6: error: redefinition of ‘extcon_dev_unregister’
> include/linux/extcon.h:73:20: note: previous definition of ‘extcon_dev_unregister’ was here
>
> So it obviously can't be applied to the tree :(
>
> Did you test it?
>
> Care to redo this series and send it again?
>
> greg k-h

Hello,

That error is very wierd. It implies that CONFIG_EXTCON is "not
defined" in extcon.h and CONFIG_EXTCON is "defined" in Makefile so
that extcon_class.c is compiled.

However, the patchset v8 (some bugfixes over patchset v7) that is
tested on the base of Linux 3.4-rc3 w/ and w/o CONFIG_EXTCON and has
been used with MAX77693-MUIC devices is on the way.

Thanks.


Cheers!
MyungJoo.

-- 
MyungJoo Ham, Ph.D.
Mobile Software Platform Lab, DMC Business, Samsung Electronics

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH v8 0/6] Introduce External Connector Class (extcon)
  2012-04-18 21:47   ` Greg KH
  2012-04-18 23:38     ` Kyungmin Park
  2012-04-19  2:36     ` MyungJoo Ham
@ 2012-04-19  2:41     ` MyungJoo Ham
  2012-04-19  2:41       ` [PATCH v8 1/6] Extcon (external connector): import Android's switch class and modify MyungJoo Ham
                         ` (5 more replies)
  2 siblings, 6 replies; 51+ messages in thread
From: MyungJoo Ham @ 2012-04-19  2:41 UTC (permalink / raw)
  To: Greg KH
  Cc: Arnd Bergmann, LKML, NeilBrown, Randy Dunlap, Mike Lockwood,
	Arve Hjønnevag, Kyungmin Park, Linus Walleij,
	Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown, John Stultz,
	Joerg Roedel, myungjoo.ham, cw00.choi

For external connectors, which may have different types of cables attached
(USB, TA, HDMI, Analog A/V, and others), we often have seperated device
drivers that detect the state changes at the port and device drivers that
do something according to the state changes.

An example is a 4-pin (or more pins?) analog audio connector
(usually referred as "3.5-pi" jacks). It can be implemented with the 5/5
patch, "adc-jack", supplied with information about IRQ, a method to get
ADC values, and ADC value ranges. With a 4-pin 3.5-pi port and adc-jack
driver with proper ADC configurations, the following modes may be
detected: "standard mono cable", "standard stereo cable",
"standard stereo + remote cable", "standard stereo + mic + remote cable",
"standard stereo + different-spec mic + remote cable", and such.
Note that for 4-pin cables with mic and remote, there are multiple
standards (at least Samsung and Apple use different configurations for
those 4-pin earphones although the pins looks exactly same.)
and they often can be distinguished with ADC values.

Although we may have simply given different "cable id" for "stereo" and
"stereo + mic". However, for those userspace processes or other device
drivers that are interested only in "stereo" part of possible combinations,
having seperated mode/id for "stereo" and "mic" is more desirable.
For example, if we do not have multistate capabilities, those who want
to know "stereo" is attached or not should monitor everything:
"standard stereo cable", "standard stereo + remove cable",
"standard stereo + mic + remote cable", and others. However, with
multistate capabilities, they only need to monitor the bit associated
with "standard stero".

For another example, when MAX8997-MUIC detects a Charger cable insertion,
another device driver (such as MAX8903 charger, MAX8997 charger, Charger
Manager, or board file) needs to set charger current limit accordingly
and when MAX8997-MUIC detects a HDMI cable insertion, multimedia device
drivers need to do some operations accordingly.


This patchset supports the usage of notifier for passing such information
between device drivers.

Another issue is that at a single switch port, there might be multiple
and heterogeneous cables attached at the same time. Besides the state
(Attached or Detached) of each cable may alter independently. Such extcon
devices that support simultaneous mult-cable attachment include, bot not
limited to, docks, cradles, and 30-pin-like ports (like that of NURI board
in linux/arch/arm/mach-exynos, which resembles Samsung Galaxy Tab series).
For example, the 30-pin port of NURI board may be used to connect analog
audio cables, analog video cable, and a USB or a charger. Because such
cables coonnect part of the 30 pins to external devices/cables, the
possible combinations of external devices/cables vary significantly and
users may fabricate their own design; e.g., an 30-pin adaptor connecting
USB, AC-DC adaptor, HDMI, analog audio, analog video, USB-host, SD-card,
and so on.

In order to address such issues, Android kernel's "Switch" class seems to
be a good basis and we have implemented "Multistate Switch Class" based on
it. The "Switch" class code of Android kernel is GPL as well.


Though there can be synchronization delay (up to 12hours), you can see
the extcon-for-next branch at
http://git.infradead.org/users/kmpark/linux-samsung/shortlog/refs/heads/extcon-for-next

Along with the 6 patches (1/6 ... 6/6), there is one additional RFC patch
that suggests to remove drivers/staging/android/switch.

Summary of changes from v7 patchset
- Rebased over Linux 3.4 rc3
- Bugfix (parameters given to register-interest callbacks)
- Bugfix (header file definitions w/ !CONFIG_EXTCON)

Summary of changes from v6 patchset
- Added documentation, "porting guide for Android switch device drivers"
- Usage of class_compat to create /sys/class/switch/* along with
/sys/class/extcon/* if CONFIG_ANDROID is enabled. However, we cannot
enable "switch" class_compat if CONFIG_ANDROID_SWITCH is enabled because
we cannot create two classes with the same name.

Summary of changes from v5 patchset
- Excluded adc-jack from the "default" patchset. Adc-jack will be later
added after Extcon is finalized.
- Sysfs style updated
- Bugfixes after using at Exynos4412 boards with userspace processes
- Code stype updated

Summary of changes from v4 patchset
- Bugfixes after testing at Exynos4412 boards with userspace processes
- Added adc-jack to support 3.5-pi jack and other ADC-based cable detection
ports.

Summary of changes from v3 patchset
- Bugfixes after testing at Exynos4412 boards.

Summary of changes from v2 patchset
- Support to express mutually exclusive set of cables
- Revised device/class/sysfs-entries add/removal methods
- Added sysfs entries
- Revised documentation and comments
- Some bugfixes
- Compatible with Android (for extcon devices with "use_class_name_switch" set)

Summary of changes from RFC
- ABI documentation added
- Notifees may get notified for a state change of a specific cable, not
every cable of the corresponding extcon.
- Added kerneldoc comments
- Moved to /drivers/extcon/
- Added helper functions
- Some bugfixes

Donggeun Kim (1):
  Extcon: support notification based on the state changes.

MyungJoo Ham (5):
  Extcon (external connector): import Android's switch class and
    modify.
  Extcon: support generic GPIO extcon driver
  Extcon: support multiple states at a device.
  Extcon: support mutually exclusive relation between cables.
  Documentation/extcon: porting guide for Android kernel switch driver.

 Documentation/ABI/testing/sysfs-class-extcon      |   97 +++
 Documentation/extcon/porting-android-switch-class |  124 +++
 drivers/Kconfig                                   |    2 +
 drivers/Makefile                                  |    1 +
 drivers/extcon/Kconfig                            |   35 +
 drivers/extcon/Makefile                           |    6 +
 drivers/extcon/extcon_class.c                     |  822 +++++++++++++++++++++
 drivers/extcon/extcon_gpio.c                      |  174 +++++
 include/linux/extcon.h                            |  322 ++++++++
 include/linux/extcon/extcon_gpio.h                |   52 ++
 10 files changed, 1635 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-class-extcon
 create mode 100644 Documentation/extcon/porting-android-switch-class
 create mode 100644 drivers/extcon/Kconfig
 create mode 100644 drivers/extcon/Makefile
 create mode 100644 drivers/extcon/extcon_class.c
 create mode 100644 drivers/extcon/extcon_gpio.c
 create mode 100644 include/linux/extcon.h
 create mode 100644 include/linux/extcon/extcon_gpio.h

-- 
1.7.4.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH v8 1/6] Extcon (external connector): import Android's switch class and modify.
  2012-04-19  2:41     ` [PATCH v8 0/6] Introduce External Connector Class (extcon) MyungJoo Ham
@ 2012-04-19  2:41       ` MyungJoo Ham
  2012-04-19 11:38         ` Mark Brown
  2012-04-20  2:37         ` Greg KH
  2012-04-19  2:41       ` [PATCH v8 2/6] Extcon: support generic GPIO extcon driver MyungJoo Ham
                         ` (4 subsequent siblings)
  5 siblings, 2 replies; 51+ messages in thread
From: MyungJoo Ham @ 2012-04-19  2:41 UTC (permalink / raw)
  To: Greg KH
  Cc: Arnd Bergmann, LKML, NeilBrown, Randy Dunlap, Mike Lockwood,
	Arve Hjønnevag, Kyungmin Park, Linus Walleij,
	Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown, John Stultz,
	Joerg Roedel, myungjoo.ham, cw00.choi

External connector class (extcon) is based on and an extension of
Android kernel's switch class located at linux/drivers/switch/.

This patch provides the before-extension switch class moved to the
location where the extcon will be located (linux/drivers/extcon/) and
updates to handle class properly.

The before-extension class, switch class of Android kernel, commits
imported are:

switch: switch class and GPIO drivers. (splitted)
Author: Mike Lockwood <lockwood@android.com>

switch: Use device_create instead of device_create_drvdata.
Author: Arve Hjønnevåg <arve@android.com>

In this patch, upon the commits of Android kernel, we have added:
- Relocated and renamed for extcon.
- Comments, module name, and author information are updated
- Code clean for successing patches
- Bugfix: enabling write access without write functions
- Class/device/sysfs create/remove handling
- Added comments about uevents
- Format changes for extcon_dev_register() to have a parent dev.

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>

--
Changes from v6
- Updated comment/strings
- Revised "Android-compatible" mode.
   * Automatically activated if CONFIG_ANDROID && !CONFIG_ANDROID_SWITCH
   * Creates /sys/class/switch/*, which is a copy of /sys/class/extcon/*

Changes from v5
- Split the patch
- Style fixes
- "Android-compatible" mode is enabled by Kconfig option.

Changes from v2
- Updated name_show
- Sysfs entries are handled by class itself.
- Updated the method to add/remove devices for the class
- Comments on uevent send
- Able to become a module
- Compatible with Android platform

Changes from RFC
- Renamed to extcon (external connector) from multistate switch
- Added a seperated directory (drivers/extcon)
- Added kerneldoc comments
- Removed unused variables from extcon_gpio.c
- Added ABI Documentation.
---
 Documentation/ABI/testing/sysfs-class-extcon |   26 +++
 drivers/Kconfig                              |    2 +
 drivers/Makefile                             |    1 +
 drivers/extcon/Kconfig                       |   28 +++
 drivers/extcon/Makefile                      |    5 +
 drivers/extcon/extcon_class.c                |  236 ++++++++++++++++++++++++++
 include/linux/extcon.h                       |   82 +++++++++
 7 files changed, 380 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-class-extcon
 create mode 100644 drivers/extcon/Kconfig
 create mode 100644 drivers/extcon/Makefile
 create mode 100644 drivers/extcon/extcon_class.c
 create mode 100644 include/linux/extcon.h

diff --git a/Documentation/ABI/testing/sysfs-class-extcon b/Documentation/ABI/testing/sysfs-class-extcon
new file mode 100644
index 0000000..59a4b1c
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-extcon
@@ -0,0 +1,26 @@
+What:		/sys/class/extcon/.../
+Date:		December 2011
+Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
+Description:
+		Provide a place in sysfs for the extcon objects.
+		This allows accessing extcon specific variables.
+		The name of extcon object denoted as ... is the name given
+		with extcon_dev_register.
+
+What:		/sys/class/extcon/.../name
+Date:		December 2011
+Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
+Description:
+		The /sys/class/extcon/.../name shows the name of the extcon
+		object. If the extcon object has an optional callback
+		"show_name" defined, the callback will provide the name with
+		this sysfs node.
+
+What:		/sys/class/extcon/.../state
+Date:		December 2011
+Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
+Description:
+		The /sys/class/extcon/.../state shows the cable attach/detach
+		information of the corresponding extcon object. If the extcon
+		objecct has an optional callback "show_state" defined, the
+		callback will provide the name with this sysfs node.
diff --git a/drivers/Kconfig b/drivers/Kconfig
index d236aef..0233ad9 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -140,4 +140,6 @@ source "drivers/virt/Kconfig"
 
 source "drivers/devfreq/Kconfig"
 
+source "drivers/extcon/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 95952c8..c41dfa9 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -134,3 +134,4 @@ obj-$(CONFIG_VIRT_DRIVERS)	+= virt/
 obj-$(CONFIG_HYPERV)		+= hv/
 
 obj-$(CONFIG_PM_DEVFREQ)	+= devfreq/
+obj-$(CONFIG_EXTCON)		+= extcon/
diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig
new file mode 100644
index 0000000..11db865
--- /dev/null
+++ b/drivers/extcon/Kconfig
@@ -0,0 +1,28 @@
+menuconfig EXTCON
+	tristate "External Connector Class (extcon) support"
+	help
+	  Say Y here to enable external connector class (extcon) support.
+	  This allows monitoring external connectors by userspace
+	  via sysfs and uevent and supports external connectors with
+	  multiple states; i.e., an extcon that may have multiple
+	  cables attached. For example, an external connector of a device
+	  may be used to connect an HDMI cable and a AC adaptor, and to
+	  host USB ports. Many of 30-pin connectors including PDMI are
+	  also good examples.
+
+if EXTCON
+
+comment "Extcon Class Configuration"
+
+config EXTCON_ANDROID_COMPATIBLE
+	bool "Class name uses 'switch' to be compatible with Android kernel"
+	depends on ANDROID
+	help
+	  The class name of extcon device drivers are set 'switch' in order
+	  to be compatible with Android kernel switch framework. With this
+	  option enabled, Android userspace platform may use extcon device
+	  drivers as if they are switch device drivers.
+
+comment "Extcon Device Drivers"
+
+endif # MULTISTATE_SWITCH
diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile
new file mode 100644
index 0000000..6bc6921
--- /dev/null
+++ b/drivers/extcon/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for external connector class (extcon) devices
+#
+
+obj-$(CONFIG_EXTCON)		+= extcon_class.o
diff --git a/drivers/extcon/extcon_class.c b/drivers/extcon/extcon_class.c
new file mode 100644
index 0000000..3c46368
--- /dev/null
+++ b/drivers/extcon/extcon_class.c
@@ -0,0 +1,236 @@
+/*
+ *  drivers/extcon/extcon_class.c
+ *
+ *  External connector (extcon) class driver
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Author: Donggeun Kim <dg77.kim@samsung.com>
+ * Author: MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * based on android/drivers/switch/switch_class.c
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+*/
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/extcon.h>
+#include <linux/slab.h>
+
+struct class *extcon_class;
+#if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
+static struct class_compat *switch_class;
+#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
+
+static ssize_t state_show(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
+
+	if (edev->print_state) {
+		int ret = edev->print_state(edev, buf);
+
+		if (ret >= 0)
+			return ret;
+		/* Use default if failed */
+	}
+	return sprintf(buf, "%u\n", edev->state);
+}
+
+static ssize_t name_show(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
+
+	/* Optional callback given by the user */
+	if (edev->print_name) {
+		int ret = edev->print_name(edev, buf);
+		if (ret >= 0)
+			return ret;
+	}
+
+	return sprintf(buf, "%s\n", dev_name(edev->dev));
+}
+
+/**
+ * extcon_set_state() - Set the cable attach states of the extcon device.
+ * @edev:	the extcon device
+ * @state:	new cable attach status for @edev
+ *
+ * Changing the state sends uevent with environment variable containing
+ * the name of extcon device (envp[0]) and the state output (envp[1]).
+ * Tizen uses this format for extcon device to get events from ports.
+ * Android uses this format as well.
+ */
+void extcon_set_state(struct extcon_dev *edev, u32 state)
+{
+	char name_buf[120];
+	char state_buf[120];
+	char *prop_buf;
+	char *envp[3];
+	int env_offset = 0;
+	int length;
+
+	if (edev->state != state) {
+		edev->state = state;
+
+		prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
+		if (prop_buf) {
+			length = name_show(edev->dev, NULL, prop_buf);
+			if (length > 0) {
+				if (prop_buf[length - 1] == '\n')
+					prop_buf[length - 1] = 0;
+				snprintf(name_buf, sizeof(name_buf),
+					"NAME=%s", prop_buf);
+				envp[env_offset++] = name_buf;
+			}
+			length = state_show(edev->dev, NULL, prop_buf);
+			if (length > 0) {
+				if (prop_buf[length - 1] == '\n')
+					prop_buf[length - 1] = 0;
+				snprintf(state_buf, sizeof(state_buf),
+					"STATE=%s", prop_buf);
+				envp[env_offset++] = state_buf;
+			}
+			envp[env_offset] = NULL;
+			kobject_uevent_env(&edev->dev->kobj, KOBJ_CHANGE, envp);
+			free_page((unsigned long)prop_buf);
+		} else {
+			dev_err(edev->dev, "out of memory in extcon_set_state\n");
+			kobject_uevent(&edev->dev->kobj, KOBJ_CHANGE);
+		}
+	}
+}
+EXPORT_SYMBOL_GPL(extcon_set_state);
+
+static struct device_attribute extcon_attrs[] = {
+	__ATTR_RO(state),
+	__ATTR_RO(name),
+};
+
+static int create_extcon_class(void)
+{
+	if (!extcon_class) {
+		extcon_class = class_create(THIS_MODULE, "extcon");
+		if (IS_ERR(extcon_class))
+			return PTR_ERR(extcon_class);
+		extcon_class->dev_attrs = extcon_attrs;
+
+#if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
+		switch_class = class_compat_register("switch");
+		if (WARN(!switch_class, "cannot allocate"))
+			return -ENOMEM;
+#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
+	}
+
+	return 0;
+}
+
+static void extcon_cleanup(struct extcon_dev *edev, bool skip)
+{
+	if (!skip && get_device(edev->dev)) {
+		device_unregister(edev->dev);
+		put_device(edev->dev);
+	}
+
+	kfree(edev->dev);
+}
+
+static void extcon_dev_release(struct device *dev)
+{
+	struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
+
+	extcon_cleanup(edev, true);
+}
+
+/**
+ * extcon_dev_register() - Register a new extcon device
+ * @edev	: the new extcon device (should be allocated before calling)
+ * @dev		: the parent device for this extcon device.
+ *
+ * Among the members of edev struct, please set the "user initializing data"
+ * in any case and set the "optional callbacks" if required. However, please
+ * do not set the values of "internal data", which are initialized by
+ * this function.
+ */
+int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
+{
+	int ret;
+
+	if (!extcon_class) {
+		ret = create_extcon_class();
+		if (ret < 0)
+			return ret;
+	}
+
+	edev->dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+	edev->dev->parent = dev;
+	edev->dev->class = extcon_class;
+	edev->dev->release = extcon_dev_release;
+
+	dev_set_name(edev->dev, edev->name ? edev->name : dev_name(dev));
+	ret = device_register(edev->dev);
+	if (ret) {
+		put_device(edev->dev);
+		goto err_dev;
+	}
+#if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
+	if (switch_class)
+		ret = class_compat_create_link(switch_class, edev->dev,
+					       dev);
+#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
+
+	dev_set_drvdata(edev->dev, edev);
+	edev->state = 0;
+	return 0;
+
+err_dev:
+	kfree(edev->dev);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(extcon_dev_register);
+
+/**
+ * extcon_dev_unregister() - Unregister the extcon device.
+ * @edev:	the extcon device instance to be unregitered.
+ *
+ * Note that this does not call kfree(edev) because edev was not allocated
+ * by this class.
+ */
+void extcon_dev_unregister(struct extcon_dev *edev)
+{
+	extcon_cleanup(edev, false);
+}
+EXPORT_SYMBOL_GPL(extcon_dev_unregister);
+
+static int __init extcon_class_init(void)
+{
+	return create_extcon_class();
+}
+module_init(extcon_class_init);
+
+static void __exit extcon_class_exit(void)
+{
+	class_destroy(extcon_class);
+}
+module_exit(extcon_class_exit);
+
+MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
+MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
+MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
+MODULE_DESCRIPTION("External connector (extcon) class driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/extcon.h b/include/linux/extcon.h
new file mode 100644
index 0000000..cd732ab
--- /dev/null
+++ b/include/linux/extcon.h
@@ -0,0 +1,82 @@
+/*
+ *  External connector (extcon) class driver
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Author: Donggeun Kim <dg77.kim@samsung.com>
+ * Author: MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * based on switch class driver
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+*/
+
+#ifndef __LINUX_EXTCON_H__
+#define __LINUX_EXTCON_H__
+
+/**
+ * struct extcon_dev - An extcon device represents one external connector.
+ * @name	The name of this extcon device. Parent device name is used
+ *		if NULL.
+ * @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
+ *		status of the extcon device.
+ * @dev		Device of this extcon. Do not provide at register-time.
+ * @state	Attach/detach state of this extcon. Do not provide at
+ *		register-time
+ *
+ * In most cases, users only need to provide "User initializing data" of
+ * this struct when registering an extcon. In some exceptional cases,
+ * optional callbacks may be needed. However, the values in "internal data"
+ * are overwritten by register function.
+ */
+struct extcon_dev {
+	/* --- Optional user initializing data --- */
+	const char	*name;
+
+	/* --- Optional callbacks to override class functions --- */
+	ssize_t	(*print_name)(struct extcon_dev *edev, char *buf);
+	ssize_t	(*print_state)(struct extcon_dev *edev, char *buf);
+
+	/* --- Internal data. Please do not set. --- */
+	struct device	*dev;
+	u32		state;
+};
+
+#ifdef CONFIG_EXTCON
+extern int extcon_dev_register(struct extcon_dev *edev, struct device *dev);
+extern void extcon_dev_unregister(struct extcon_dev *edev);
+
+static inline u32 extcon_get_state(struct extcon_dev *edev)
+{
+	return edev->state;
+}
+
+extern void extcon_set_state(struct extcon_dev *edev, u32 state);
+#else /* CONFIG_EXTCON */
+static inline int extcon_dev_register(struct extcon_dev *edev,
+				      struct device *dev)
+{
+	return 0;
+}
+
+static inline void extcon_dev_unregister(struct extcon_dev *edev) { }
+
+static inline u32 extcon_get_state(struct extcon_dev *edev)
+{
+	return 0;
+}
+
+static inline void extcon_set_state(struct extcon_dev *edev, u32 state) { }
+#endif /* CONFIG_EXTCON */
+#endif /* __LINUX_EXTCON_H__ */
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH v8 2/6] Extcon: support generic GPIO extcon driver
  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  2:41       ` 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
                         ` (3 subsequent siblings)
  5 siblings, 1 reply; 51+ messages in thread
From: MyungJoo Ham @ 2012-04-19  2:41 UTC (permalink / raw)
  To: Greg KH
  Cc: Arnd Bergmann, LKML, NeilBrown, Randy Dunlap, Mike Lockwood,
	Arve Hjønnevag, Kyungmin Park, Linus Walleij,
	Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown, John Stultz,
	Joerg Roedel, myungjoo.ham, cw00.choi

The generic GPIO extcon driver (an external connector device based on
GPIO control) and imported from Android kernel.

switch: switch class and GPIO drivers. (splitted)
Author: Mike Lockwood <lockwood@android.com>

switch: gpio: Don't call request_irq with interrupts disabled
Author: Arve Hjønnevåg <arve@android.com>

switch_gpio: Add missing #include <linux/interrupt.h>
Author: Mike Lockwood <lockwood@android.com>

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>

--
Changed from v7:
- Style updates mentioned by Stephen Boyd
Changed from v5:
- Splitted at v5 from the main extcon patch.
- Added debounce time for irq handlers.
- Use request_any_context_irq instead of request_irq
- User needs to specify irq flags for GPIO interrupts (was fixed to
IRQF_TRIGGER_LOW before)
- Use module_platform_driver().
---
 drivers/extcon/Kconfig             |    7 ++
 drivers/extcon/Makefile            |    1 +
 drivers/extcon/extcon_gpio.c       |  174 ++++++++++++++++++++++++++++++++++++
 include/linux/extcon/extcon_gpio.h |   52 +++++++++++
 4 files changed, 234 insertions(+), 0 deletions(-)
 create mode 100644 drivers/extcon/extcon_gpio.c
 create mode 100644 include/linux/extcon/extcon_gpio.h

diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig
index 11db865..cc3f5eb 100644
--- a/drivers/extcon/Kconfig
+++ b/drivers/extcon/Kconfig
@@ -25,4 +25,11 @@ config EXTCON_ANDROID_COMPATIBLE
 
 comment "Extcon Device Drivers"
 
+config EXTCON_GPIO
+	tristate "GPIO extcon support"
+	depends on GENERIC_GPIO
+	help
+	  Say Y here to enable GPIO based extcon support. Note that GPIO
+	  extcon supports single state per extcon instance.
+
 endif # MULTISTATE_SWITCH
diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile
index 6bc6921..2c46d41 100644
--- a/drivers/extcon/Makefile
+++ b/drivers/extcon/Makefile
@@ -3,3 +3,4 @@
 #
 
 obj-$(CONFIG_EXTCON)		+= extcon_class.o
+obj-$(CONFIG_EXTCON_GPIO)	+= extcon_gpio.o
diff --git a/drivers/extcon/extcon_gpio.c b/drivers/extcon/extcon_gpio.c
new file mode 100644
index 0000000..ade2c9b
--- /dev/null
+++ b/drivers/extcon/extcon_gpio.c
@@ -0,0 +1,174 @@
+/*
+ *  drivers/extcon/extcon_gpio.c
+ *
+ *  Single-state GPIO extcon driver based on extcon class
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * Modified by MyungJoo Ham <myungjoo.ham@samsung.com> to support extcon
+ * (originally switch class is supported)
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/extcon.h>
+#include <linux/workqueue.h>
+#include <linux/gpio.h>
+#include <linux/extcon.h>
+#include <linux/extcon/extcon_gpio.h>
+
+struct gpio_extcon_data {
+	struct extcon_dev edev;
+	unsigned gpio;
+	const char *state_on;
+	const char *state_off;
+	int irq;
+	struct delayed_work work;
+	unsigned long debounce_jiffies;
+};
+
+static void gpio_extcon_work(struct work_struct *work)
+{
+	int state;
+	struct gpio_extcon_data	*data =
+		container_of(to_delayed_work(work), struct gpio_extcon_data,
+			     work);
+
+	state = gpio_get_value(data->gpio);
+	extcon_set_state(&data->edev, state);
+}
+
+static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
+{
+	struct gpio_extcon_data *extcon_data = dev_id;
+
+	schedule_delayed_work(&extcon_data->work,
+			      extcon_data->debounce_jiffies);
+	return IRQ_HANDLED;
+}
+
+static ssize_t extcon_gpio_print_state(struct extcon_dev *edev, char *buf)
+{
+	struct gpio_extcon_data	*extcon_data =
+		container_of(edev, struct gpio_extcon_data, edev);
+	const char *state;
+	if (extcon_get_state(edev))
+		state = extcon_data->state_on;
+	else
+		state = extcon_data->state_off;
+
+	if (state)
+		return sprintf(buf, "%s\n", state);
+	return -EINVAL;
+}
+
+static int __devinit gpio_extcon_probe(struct platform_device *pdev)
+{
+	struct gpio_extcon_platform_data *pdata = pdev->dev.platform_data;
+	struct gpio_extcon_data *extcon_data;
+	int ret = 0;
+
+	if (!pdata)
+		return -EBUSY;
+	if (!pdata->irq_flags) {
+		dev_err(&pdev->dev, "IRQ flag is not specified.\n");
+		return -EINVAL;
+	}
+
+	extcon_data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_extcon_data),
+				   GFP_KERNEL);
+	if (!extcon_data)
+		return -ENOMEM;
+
+	extcon_data->edev.name = pdata->name;
+	extcon_data->gpio = pdata->gpio;
+	extcon_data->state_on = pdata->state_on;
+	extcon_data->state_off = pdata->state_off;
+	if (pdata->state_on && pdata->state_off)
+		extcon_data->edev.print_state = extcon_gpio_print_state;
+	extcon_data->debounce_jiffies = msecs_to_jiffies(pdata->debounce);
+
+	ret = extcon_dev_register(&extcon_data->edev, &pdev->dev);
+	if (ret < 0)
+		goto err_extcon_dev_register;
+
+	ret = gpio_request(extcon_data->gpio, pdev->name);
+	if (ret < 0)
+		goto err_request_gpio;
+
+	ret = gpio_direction_input(extcon_data->gpio);
+	if (ret < 0)
+		goto err_set_gpio_input;
+
+	INIT_DELAYED_WORK(&extcon_data->work, gpio_extcon_work);
+
+	extcon_data->irq = gpio_to_irq(extcon_data->gpio);
+	if (extcon_data->irq < 0) {
+		ret = extcon_data->irq;
+		goto err_detect_irq_num_failed;
+	}
+
+	ret = request_any_context_irq(extcon_data->irq, gpio_irq_handler,
+				      pdata->irq_flags, pdev->name,
+				      extcon_data);
+	if (ret < 0)
+		goto err_request_irq;
+
+	/* Perform initial detection */
+	gpio_extcon_work(&extcon_data->work.work);
+
+	return 0;
+
+err_request_irq:
+err_detect_irq_num_failed:
+err_set_gpio_input:
+	gpio_free(extcon_data->gpio);
+err_request_gpio:
+	extcon_dev_unregister(&extcon_data->edev);
+err_extcon_dev_register:
+	devm_kfree(&pdev->dev, extcon_data);
+
+	return ret;
+}
+
+static int __devexit gpio_extcon_remove(struct platform_device *pdev)
+{
+	struct gpio_extcon_data *extcon_data = platform_get_drvdata(pdev);
+
+	cancel_delayed_work_sync(&extcon_data->work);
+	gpio_free(extcon_data->gpio);
+	extcon_dev_unregister(&extcon_data->edev);
+	devm_kfree(&pdev->dev, extcon_data);
+
+	return 0;
+}
+
+static struct platform_driver gpio_extcon = {
+	.probe		= gpio_extcon_probe,
+	.remove		= __devexit_p(gpio_extcon_remove),
+	.driver		= {
+		.name	= "extcon-gpio",
+		.owner	= THIS_MODULE,
+	},
+};
+
+module_platform_driver(gpio_extcon);
+
+MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
+MODULE_DESCRIPTION("GPIO extcon driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/extcon/extcon_gpio.h b/include/linux/extcon/extcon_gpio.h
new file mode 100644
index 0000000..a2129b7
--- /dev/null
+++ b/include/linux/extcon/extcon_gpio.h
@@ -0,0 +1,52 @@
+/*
+ *  External connector (extcon) class generic GPIO driver
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Author: MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * based on switch class driver
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+*/
+#ifndef __EXTCON_GPIO_H__
+#define __EXTCON_GPIO_H__ __FILE__
+
+#include <linux/extcon.h>
+
+/**
+ * struct gpio_extcon_platform_data - A simple GPIO-controlled extcon device.
+ * @name	The name of this GPIO extcon device.
+ * @gpio	Corresponding GPIO.
+ * @debounce	Debounce time for GPIO IRQ in ms.
+ * @irq_flags	IRQ Flags (e.g., IRQF_TRIGGER_LOW).
+ * @state_on	print_state is overriden with state_on if attached. If Null,
+ *		default method of extcon class is used.
+ * @state_off	print_state is overriden with state_on if dettached. If Null,
+ *		default method of extcon class is used.
+ *
+ * Note that in order for state_on or state_off to be valid, both state_on
+ * and state_off should be not NULL. If at least one of them is NULL,
+ * the print_state is not overriden.
+ */
+struct gpio_extcon_platform_data {
+	const char *name;
+	unsigned gpio;
+	unsigned long debounce;
+	unsigned long irq_flags;
+
+	/* if NULL, "0" or "1" will be printed */
+	const char *state_on;
+	const char *state_off;
+};
+
+#endif /* __EXTCON_GPIO_H__ */
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH v8 3/6] Extcon: support notification based on the state changes.
  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  2:41       ` [PATCH v8 2/6] Extcon: support generic GPIO extcon driver MyungJoo Ham
@ 2012-04-19  2:41       ` MyungJoo Ham
  2012-04-19  2:41       ` [PATCH v8 4/6] Extcon: support multiple states at a device MyungJoo Ham
                         ` (2 subsequent siblings)
  5 siblings, 0 replies; 51+ messages in thread
From: MyungJoo Ham @ 2012-04-19  2:41 UTC (permalink / raw)
  To: Greg KH
  Cc: Arnd Bergmann, LKML, NeilBrown, Randy Dunlap, Mike Lockwood,
	Arve Hjønnevag, Kyungmin Park, Linus Walleij,
	Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown, John Stultz,
	Joerg Roedel, myungjoo.ham, cw00.choi

From: Donggeun Kim <dg77.kim@samsung.com>

State changes of extcon devices have been notified via kobjet_uevent.
This patch adds notifier interfaces in order to allow device drivers to
get notified easily. Along with notifier interface,
extcon_get_extcon_dev() function is added so that device drivers may
discover a extcon_dev easily.

Signed-off-by: Donggeun Kim <dg77.kim@samsung.com>
Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>

--
Changes from RFC
- Renamed switch to extcon
- Bugfix: extcon_dev_unregister()
- Bugfix: "edev->dev" is "internal" data.
- Added kerneldoc comments.
- Reworded comments.
---
 drivers/extcon/extcon_class.c |   66 +++++++++++++++++++++++++++++++++++++++++
 include/linux/extcon.h        |   38 +++++++++++++++++++++++
 2 files changed, 104 insertions(+), 0 deletions(-)

diff --git a/drivers/extcon/extcon_class.c b/drivers/extcon/extcon_class.c
index 3c46368..83a088f 100644
--- a/drivers/extcon/extcon_class.c
+++ b/drivers/extcon/extcon_class.c
@@ -36,6 +36,9 @@ struct class *extcon_class;
 static struct class_compat *switch_class;
 #endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
 
+static LIST_HEAD(extcon_dev_list);
+static DEFINE_MUTEX(extcon_dev_list_lock);
+
 static ssize_t state_show(struct device *dev, struct device_attribute *attr,
 			  char *buf)
 {
@@ -75,6 +78,9 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr,
  * the name of extcon device (envp[0]) and the state output (envp[1]).
  * Tizen uses this format for extcon device to get events from ports.
  * Android uses this format as well.
+ *
+ * Note that notifier provides the which bits are changes in the state
+ * variable with "val" to the callback.
  */
 void extcon_set_state(struct extcon_dev *edev, u32 state)
 {
@@ -84,10 +90,14 @@ void extcon_set_state(struct extcon_dev *edev, u32 state)
 	char *envp[3];
 	int env_offset = 0;
 	int length;
+	u32 old_state = edev->state;
 
 	if (edev->state != state) {
 		edev->state = state;
 
+		raw_notifier_call_chain(&edev->nh, old_state ^ edev->state,
+					edev);
+
 		prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
 		if (prop_buf) {
 			length = name_show(edev->dev, NULL, prop_buf);
@@ -117,6 +127,51 @@ void extcon_set_state(struct extcon_dev *edev, u32 state)
 }
 EXPORT_SYMBOL_GPL(extcon_set_state);
 
+/**
+ * extcon_get_extcon_dev() - Get the extcon device instance from the name
+ * @extcon_name:	The extcon name provided with extcon_dev_register()
+ */
+struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
+{
+	struct extcon_dev *sd;
+
+	mutex_lock(&extcon_dev_list_lock);
+	list_for_each_entry(sd, &extcon_dev_list, entry) {
+		if (!strcmp(sd->name, extcon_name))
+			goto out;
+	}
+	sd = NULL;
+out:
+	mutex_unlock(&extcon_dev_list_lock);
+	return sd;
+}
+EXPORT_SYMBOL_GPL(extcon_get_extcon_dev);
+
+/**
+ * extcon_register_notifier() - Register a notifee to get notified by
+ *			      any attach status changes from the extcon.
+ * @edev:	the extcon device.
+ * @nb:		a notifier block to be registered.
+ */
+int extcon_register_notifier(struct extcon_dev *edev,
+			struct notifier_block *nb)
+{
+	return raw_notifier_chain_register(&edev->nh, nb);
+}
+EXPORT_SYMBOL_GPL(extcon_register_notifier);
+
+/**
+ * extcon_unregister_notifier() - Unregister a notifee from the extcon device.
+ * @edev:	the extcon device.
+ * @nb:		a registered notifier block to be unregistered.
+ */
+int extcon_unregister_notifier(struct extcon_dev *edev,
+			struct notifier_block *nb)
+{
+	return raw_notifier_chain_unregister(&edev->nh, nb);
+}
+EXPORT_SYMBOL_GPL(extcon_unregister_notifier);
+
 static struct device_attribute extcon_attrs[] = {
 	__ATTR_RO(state),
 	__ATTR_RO(name),
@@ -142,6 +197,10 @@ static int create_extcon_class(void)
 
 static void extcon_cleanup(struct extcon_dev *edev, bool skip)
 {
+	mutex_lock(&extcon_dev_list_lock);
+	list_del(&edev->entry);
+	mutex_unlock(&extcon_dev_list_lock);
+
 	if (!skip && get_device(edev->dev)) {
 		device_unregister(edev->dev);
 		put_device(edev->dev);
@@ -194,8 +253,15 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
 					       dev);
 #endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
 
+	RAW_INIT_NOTIFIER_HEAD(&edev->nh);
+
 	dev_set_drvdata(edev->dev, edev);
 	edev->state = 0;
+
+	mutex_lock(&extcon_dev_list_lock);
+	list_add(&edev->entry, &extcon_dev_list);
+	mutex_unlock(&extcon_dev_list_lock);
+
 	return 0;
 
 err_dev:
diff --git a/include/linux/extcon.h b/include/linux/extcon.h
index cd732ab..50be845 100644
--- a/include/linux/extcon.h
+++ b/include/linux/extcon.h
@@ -23,6 +23,7 @@
 #ifndef __LINUX_EXTCON_H__
 #define __LINUX_EXTCON_H__
 
+#include <linux/notifier.h>
 /**
  * struct extcon_dev - An extcon device represents one external connector.
  * @name	The name of this extcon device. Parent device name is used
@@ -34,6 +35,9 @@
  * @dev		Device of this extcon. Do not provide at register-time.
  * @state	Attach/detach state of this extcon. Do not provide at
  *		register-time
+ * @nh	Notifier for the state change events from this extcon
+ * @entry	To support list of extcon devices so that uses can search
+ *		for extcon devices based on the extcon name.
  *
  * In most cases, users only need to provide "User initializing data" of
  * this struct when registering an extcon. In some exceptional cases,
@@ -51,11 +55,19 @@ struct extcon_dev {
 	/* --- Internal data. Please do not set. --- */
 	struct device	*dev;
 	u32		state;
+	struct raw_notifier_head nh;
+	struct list_head entry;
 };
 
 #ifdef CONFIG_EXTCON
+
+/*
+ * Following APIs are for notifiers or configurations.
+ * Notifiers are the external port and connection devices.
+ */
 extern int extcon_dev_register(struct extcon_dev *edev, struct device *dev);
 extern void extcon_dev_unregister(struct extcon_dev *edev);
+extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name);
 
 static inline u32 extcon_get_state(struct extcon_dev *edev)
 {
@@ -63,6 +75,15 @@ static inline u32 extcon_get_state(struct extcon_dev *edev)
 }
 
 extern void extcon_set_state(struct extcon_dev *edev, u32 state);
+
+/*
+ * Following APIs are to monitor every action of a notifier.
+ * Registerer gets notified for every external port of a connection device.
+ */
+extern int extcon_register_notifier(struct extcon_dev *edev,
+				    struct notifier_block *nb);
+extern int extcon_unregister_notifier(struct extcon_dev *edev,
+				      struct notifier_block *nb);
 #else /* CONFIG_EXTCON */
 static inline int extcon_dev_register(struct extcon_dev *edev,
 				      struct device *dev)
@@ -78,5 +99,22 @@ static inline u32 extcon_get_state(struct extcon_dev *edev)
 }
 
 static inline void extcon_set_state(struct extcon_dev *edev, u32 state) { }
+static inline struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
+{
+	return NULL;
+}
+
+static inline int extcon_register_notifier(struct extcon_dev *edev,
+					   struct notifier_block *nb)
+{
+	return 0;
+}
+
+static inline int extcon_unregister_notifier(struct extcon_dev *edev,
+					     struct notifier_block *nb)
+{
+	return 0;
+}
+
 #endif /* CONFIG_EXTCON */
 #endif /* __LINUX_EXTCON_H__ */
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH v8 4/6] Extcon: support multiple states at a device.
  2012-04-19  2:41     ` [PATCH v8 0/6] Introduce External Connector Class (extcon) MyungJoo Ham
                         ` (2 preceding siblings ...)
  2012-04-19  2:41       ` [PATCH v8 3/6] Extcon: support notification based on the state changes MyungJoo Ham
@ 2012-04-19  2:41       ` 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
  5 siblings, 0 replies; 51+ messages in thread
From: MyungJoo Ham @ 2012-04-19  2:41 UTC (permalink / raw)
  To: Greg KH
  Cc: Arnd Bergmann, LKML, NeilBrown, Randy Dunlap, Mike Lockwood,
	Arve Hjønnevag, Kyungmin Park, Linus Walleij,
	Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown, John Stultz,
	Joerg Roedel, myungjoo.ham, cw00.choi

One switch device (e.g., MUIC(MAX8997, MAX77686, ...), and some 30-pin
devices) may have multiple cables attached. For example, one
30-pin port may inhabit a USB cable, an HDMI cable, and a mic.
Thus, one switch device requires multiple state bits each representing
a type of cable.

For such purpose, we use the 32bit state variable; thus, up to 32
different type of cables may be defined for a switch device. The list of
possible cables is defined by the array of cable names in the switch_dev
struct given to the class.

Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>

--
Changes from V7
- Bugfixed in _call_per_cable() (incorrect nb) (Chanwoo Choi)
- Compiler error in header for !CONFIG_EXTCON (Chanwoo Choi)
Changes from V5
- Sysfs style reformed: subdirectory per cable.
- Updated standard cable names
- Removed unnecessary printf
- Bugfixes after testing

Changes from V4
- Bugfixes after more testing at Exynos4412 boards with userspace
  processses.

Changes from V3
- Bugfixes after more testing at Exynos4412 boards.

Changes from V2
- State can be stored by user
- Documentation updated

Changes from RFC
- Switch is renamed to extcon
- Added kerneldoc comments
- Added APIs to support "standard" cable names
- Added helper APIs to support notifier block registration with cable
  name.
- Regrouped function list in the header file.
---
 Documentation/ABI/testing/sysfs-class-extcon |   63 ++++-
 drivers/extcon/extcon_class.c                |  439 +++++++++++++++++++++++++-
 include/linux/extcon.h                       |  184 +++++++++++
 3 files changed, 666 insertions(+), 20 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-class-extcon b/Documentation/ABI/testing/sysfs-class-extcon
index 59a4b1c..19b18e9 100644
--- a/Documentation/ABI/testing/sysfs-class-extcon
+++ b/Documentation/ABI/testing/sysfs-class-extcon
@@ -1,5 +1,5 @@
 What:		/sys/class/extcon/.../
-Date:		December 2011
+Date:		February 2012
 Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
 Description:
 		Provide a place in sysfs for the extcon objects.
@@ -7,8 +7,16 @@ Description:
 		The name of extcon object denoted as ... is the name given
 		with extcon_dev_register.
 
+		One extcon device denotes a single external connector
+		port. An external connector may have multiple cables
+		attached simultaneously. Many of docks, cradles, and
+		accessory cables have such capability. For example,
+		the 30-pin port of Nuri board (/arch/arm/mach-exynos)
+		may have both HDMI and Charger attached, or analog audio,
+		video, and USB cables attached simulteneously.
+
 What:		/sys/class/extcon/.../name
-Date:		December 2011
+Date:		February 2012
 Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
 Description:
 		The /sys/class/extcon/.../name shows the name of the extcon
@@ -17,10 +25,51 @@ Description:
 		this sysfs node.
 
 What:		/sys/class/extcon/.../state
-Date:		December 2011
+Date:		February 2012
+Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
+Description:
+		The /sys/class/extcon/.../state shows and stores the cable
+		attach/detach information of the corresponding extcon object.
+		If the extcon object has an optional callback "show_state"
+		defined, the showing function is overriden with the optional
+		callback.
+
+		If the default callback for showing function is used, the
+		format is like this:
+		# cat state
+		USB_OTG=1
+		HDMI=0
+		TA=1
+		EAR_JACK=0
+		#
+		In this example, the extcon device have USB_OTG and TA
+		cables attached and HDMI and EAR_JACK cables detached.
+
+		In order to update the state of an extcon device, enter a hex
+		state number starting with 0x.
+		 echo 0xHEX > state
+
+		This updates the whole state of the extcon dev.
+		Inputs of all the methods are required to meet the
+		mutually_exclusive contidions if they exist.
+
+		It is recommended to use this "global" state interface if
+		you need to enter the value atomically. The later state
+		interface associated with each cable cannot update
+		multiple cable states of an extcon device simultaneously.
+
+What:		/sys/class/extcon/.../cable.x/name
+Date:		February 2012
+Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
+Description:
+		The /sys/class/extcon/.../cable.x/name shows the name of cable
+		"x" (integer between 0 and 31) of an extcon device.
+
+What:		/sys/class/extcon/.../cable.x/state
+Date:		February 2012
 Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
 Description:
-		The /sys/class/extcon/.../state shows the cable attach/detach
-		information of the corresponding extcon object. If the extcon
-		objecct has an optional callback "show_state" defined, the
-		callback will provide the name with this sysfs node.
+		The /sys/class/extcon/.../cable.x/name shows and stores the
+		state of cable "x" (integer between 0 and 31) of an extcon
+		device. The state value is either 0 (detached) or 1
+		(attached).
diff --git a/drivers/extcon/extcon_class.c b/drivers/extcon/extcon_class.c
index 83a088f..403933b 100644
--- a/drivers/extcon/extcon_class.c
+++ b/drivers/extcon/extcon_class.c
@@ -31,6 +31,39 @@
 #include <linux/extcon.h>
 #include <linux/slab.h>
 
+/*
+ * extcon_cable_name suggests the standard cable names for commonly used
+ * cable types.
+ *
+ * However, please do not use extcon_cable_name directly for extcon_dev
+ * struct's supported_cable pointer unless your device really supports
+ * every single port-type of the following cable names. Please choose cable
+ * names that are actually used in your extcon device.
+ */
+const char *extcon_cable_name[] = {
+	[EXTCON_USB]		= "USB",
+	[EXTCON_USB_HOST]	= "USB-Host",
+	[EXTCON_TA]		= "TA",
+	[EXTCON_FAST_CHARGER]	= "Fast-charger",
+	[EXTCON_SLOW_CHARGER]	= "Slow-charger",
+	[EXTCON_CHARGE_DOWNSTREAM]	= "Charge-downstream",
+	[EXTCON_HDMI]		= "HDMI",
+	[EXTCON_MHL]		= "MHL",
+	[EXTCON_DVI]		= "DVI",
+	[EXTCON_VGA]		= "VGA",
+	[EXTCON_DOCK]		= "Dock",
+	[EXTCON_LINE_IN]	= "Line-in",
+	[EXTCON_LINE_OUT]	= "Line-out",
+	[EXTCON_MIC_IN]		= "Microphone",
+	[EXTCON_HEADPHONE_OUT]	= "Headphone",
+	[EXTCON_SPDIF_IN]	= "SPDIF-in",
+	[EXTCON_SPDIF_OUT]	= "SPDIF-out",
+	[EXTCON_VIDEO_IN]	= "Video-in",
+	[EXTCON_VIDEO_OUT]	= "Video-out",
+
+	NULL,
+};
+
 struct class *extcon_class;
 #if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
 static struct class_compat *switch_class;
@@ -42,6 +75,7 @@ static DEFINE_MUTEX(extcon_dev_list_lock);
 static ssize_t state_show(struct device *dev, struct device_attribute *attr,
 			  char *buf)
 {
+	int i, count = 0;
 	struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
 
 	if (edev->print_state) {
@@ -51,7 +85,39 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,
 			return ret;
 		/* Use default if failed */
 	}
-	return sprintf(buf, "%u\n", edev->state);
+
+	if (edev->max_supported == 0)
+		return sprintf(buf, "%u\n", edev->state);
+
+	for (i = 0; i < SUPPORTED_CABLE_MAX; i++) {
+		if (!edev->supported_cable[i])
+			break;
+		count += sprintf(buf + count, "%s=%d\n",
+				 edev->supported_cable[i],
+				 !!(edev->state & (1 << i)));
+	}
+
+	return count;
+}
+
+void 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)
+{
+	u32 state;
+	ssize_t ret = 0;
+	struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
+
+	ret = sscanf(buf, "0x%x", &state);
+	if (ret == 0)
+		ret = -EINVAL;
+	else
+		extcon_set_state(edev, state);
+
+	if (ret < 0)
+		return ret;
+
+	return count;
 }
 
 static ssize_t name_show(struct device *dev, struct device_attribute *attr,
@@ -69,9 +135,52 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr,
 	return sprintf(buf, "%s\n", dev_name(edev->dev));
 }
 
+static ssize_t cable_name_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct extcon_cable *cable = container_of(attr, struct extcon_cable,
+						  attr_name);
+
+	return sprintf(buf, "%s\n",
+		       cable->edev->supported_cable[cable->cable_index]);
+}
+
+static ssize_t cable_state_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct extcon_cable *cable = container_of(attr, struct extcon_cable,
+						  attr_state);
+
+	return sprintf(buf, "%d\n",
+		       extcon_get_cable_state_(cable->edev,
+					       cable->cable_index));
+}
+
+static ssize_t cable_state_store(struct device *dev,
+				 struct device_attribute *attr, const char *buf,
+				 size_t count)
+{
+	struct extcon_cable *cable = container_of(attr, struct extcon_cable,
+						  attr_state);
+	int ret, state;
+
+	ret = sscanf(buf, "%d", &state);
+	if (ret == 0)
+		ret = -EINVAL;
+	else
+		ret = extcon_set_cable_state_(cable->edev, cable->cable_index,
+					      state);
+
+	if (ret < 0)
+		return ret;
+	return count;
+}
+
 /**
- * extcon_set_state() - Set the cable attach states of the extcon device.
+ * extcon_update_state() - Update the cable attach states of the extcon device
+ *			only for the masked bits.
  * @edev:	the extcon device
+ * @mask:	the bit mask to designate updated bits.
  * @state:	new cable attach status for @edev
  *
  * Changing the state sends uevent with environment variable containing
@@ -79,10 +188,10 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr,
  * Tizen uses this format for extcon device to get events from ports.
  * Android uses this format as well.
  *
- * Note that notifier provides the which bits are changes in the state
- * variable with "val" to the callback.
+ * Note that the 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)
+void extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
 {
 	char name_buf[120];
 	char state_buf[120];
@@ -90,15 +199,20 @@ void extcon_set_state(struct extcon_dev *edev, u32 state)
 	char *envp[3];
 	int env_offset = 0;
 	int length;
-	u32 old_state = edev->state;
+	unsigned long flags;
 
-	if (edev->state != state) {
-		edev->state = state;
+	spin_lock_irqsave(&edev->lock, flags);
 
-		raw_notifier_call_chain(&edev->nh, old_state ^ edev->state,
-					edev);
+	if (edev->state != ((edev->state & ~mask) | (state & mask))) {
+		u32 old_state = edev->state;
 
-		prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
+		edev->state &= ~mask;
+		edev->state |= state & mask;
+
+		raw_notifier_call_chain(&edev->nh, old_state, edev);
+
+		/* This could be in interrupt handler */
+		prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
 		if (prop_buf) {
 			length = name_show(edev->dev, NULL, prop_buf);
 			if (length > 0) {
@@ -117,17 +231,132 @@ void extcon_set_state(struct extcon_dev *edev, u32 state)
 				envp[env_offset++] = state_buf;
 			}
 			envp[env_offset] = NULL;
+			/* Unlock early before uevent */
+			spin_unlock_irqrestore(&edev->lock, flags);
+
 			kobject_uevent_env(&edev->dev->kobj, KOBJ_CHANGE, envp);
 			free_page((unsigned long)prop_buf);
 		} else {
+			/* Unlock early before uevent */
+			spin_unlock_irqrestore(&edev->lock, flags);
+
 			dev_err(edev->dev, "out of memory in extcon_set_state\n");
 			kobject_uevent(&edev->dev->kobj, KOBJ_CHANGE);
 		}
+	} else {
+		/* No changes */
+		spin_unlock_irqrestore(&edev->lock, flags);
 	}
 }
+EXPORT_SYMBOL_GPL(extcon_update_state);
+
+/**
+ * extcon_set_state() - Set the cable attach states of the extcon device.
+ * @edev:	the extcon device
+ * @state:	new cable attach status for @edev
+ *
+ * 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)
+{
+	extcon_update_state(edev, 0xffffffff, state);
+}
 EXPORT_SYMBOL_GPL(extcon_set_state);
 
 /**
+ * extcon_find_cable_index() - Get the cable index based on the cable name.
+ * @edev:	the extcon device that has the cable.
+ * @cable_name:	cable name to be searched.
+ *
+ * Note that accessing a cable state based on cable_index is faster than
+ * cable_name because using cable_name induces a loop with strncmp().
+ * Thus, when get/set_cable_state is repeatedly used, using cable_index
+ * is recommended.
+ */
+int extcon_find_cable_index(struct extcon_dev *edev, const char *cable_name)
+{
+	int i;
+
+	if (edev->supported_cable) {
+		for (i = 0; edev->supported_cable[i]; i++) {
+			if (!strncmp(edev->supported_cable[i],
+				cable_name, CABLE_NAME_MAX))
+				return i;
+		}
+	}
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(extcon_find_cable_index);
+
+/**
+ * extcon_get_cable_state_() - Get the status of a specific cable.
+ * @edev:	the extcon device that has the cable.
+ * @index:	cable index that can be retrieved by extcon_find_cable_index().
+ */
+int extcon_get_cable_state_(struct extcon_dev *edev, int index)
+{
+	if (index < 0 || (edev->max_supported && edev->max_supported <= index))
+		return -EINVAL;
+
+	return !!(edev->state & (1 << index));
+}
+EXPORT_SYMBOL_GPL(extcon_get_cable_state_);
+
+/**
+ * extcon_get_cable_state() - Get the status of a specific cable.
+ * @edev:	the extcon device that has the cable.
+ * @cable_name:	cable name.
+ *
+ * Note that this is slower than extcon_get_cable_state_.
+ */
+int extcon_get_cable_state(struct extcon_dev *edev, const char *cable_name)
+{
+	return extcon_get_cable_state_(edev, extcon_find_cable_index
+						(edev, cable_name));
+}
+EXPORT_SYMBOL_GPL(extcon_get_cable_state);
+
+/**
+ * extcon_get_cable_state_() - Set the status of a specific cable.
+ * @edev:	the extcon device that has the cable.
+ * @index:	cable index that can be retrieved by extcon_find_cable_index().
+ * @cable_state:	the new cable status. The default semantics is
+ *			true: attached / false: detached.
+ */
+int extcon_set_cable_state_(struct extcon_dev *edev,
+			int index, bool cable_state)
+{
+	u32 state;
+
+	if (index < 0 || (edev->max_supported && edev->max_supported <= index))
+		return -EINVAL;
+
+	state = cable_state ? (1 << index) : 0;
+	extcon_update_state(edev, 1 << index, state);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(extcon_set_cable_state_);
+
+/**
+ * extcon_get_cable_state() - Set the status of a specific cable.
+ * @edev:	the extcon device that has the cable.
+ * @cable_name:	cable name.
+ * @cable_state:	the new cable status. The default semantics is
+ *			true: attached / false: detached.
+ *
+ * Note that this is slower than extcon_set_cable_state_.
+ */
+int extcon_set_cable_state(struct extcon_dev *edev,
+			const char *cable_name, bool cable_state)
+{
+	return extcon_set_cable_state_(edev, extcon_find_cable_index
+					(edev, cable_name), cable_state);
+}
+EXPORT_SYMBOL_GPL(extcon_set_cable_state);
+
+/**
  * extcon_get_extcon_dev() - Get the extcon device instance from the name
  * @extcon_name:	The extcon name provided with extcon_dev_register()
  */
@@ -147,11 +376,88 @@ out:
 }
 EXPORT_SYMBOL_GPL(extcon_get_extcon_dev);
 
+static int _call_per_cable(struct notifier_block *nb, unsigned long val,
+			   void *ptr)
+{
+	struct extcon_specific_cable_nb *obj = container_of(nb,
+			struct extcon_specific_cable_nb, internal_nb);
+	struct extcon_dev *edev = ptr;
+
+	if ((val & (1 << obj->cable_index)) !=
+	    (edev->state & (1 << obj->cable_index))) {
+		obj->previous_value = val;
+		return obj->user_nb->notifier_call(obj->user_nb, val, ptr);
+	}
+
+	return NOTIFY_OK;
+}
+
+/**
+ * extcon_register_interest() - Register a notifier for a state change of a
+ *			      specific cable, not a entier set of cables of a
+ *			      extcon device.
+ * @obj:	an empty extcon_specific_cable_nb object to be returned.
+ * @extcon_name:	the name of extcon device.
+ * @cable_name:		the target cable name.
+ * @nb:		the notifier block to get notified.
+ *
+ * Provide an empty extcon_specific_cable_nb. extcon_register_interest() sets
+ * the struct for you.
+ *
+ * extcon_register_interest is a helper function for those who want to get
+ * notification for a single specific cable's status change. If a user wants
+ * to get notification for any changes of all cables of a extcon device,
+ * he/she should use the general extcon_register_notifier().
+ *
+ * Note that the second parameter given to the callback of nb (val) is
+ * "old_state", not the current state. The current state can be retrieved
+ * by looking at the third pameter (edev pointer)'s state value.
+ */
+int extcon_register_interest(struct extcon_specific_cable_nb *obj,
+			     const char *extcon_name, const char *cable_name,
+			     struct notifier_block *nb)
+{
+	if (!obj || !extcon_name || !cable_name || !nb)
+		return -EINVAL;
+
+	obj->edev = extcon_get_extcon_dev(extcon_name);
+	if (!obj->edev)
+		return -ENODEV;
+
+	obj->cable_index = extcon_find_cable_index(obj->edev, cable_name);
+	if (obj->cable_index < 0)
+		return -ENODEV;
+
+	obj->user_nb = nb;
+
+	obj->internal_nb.notifier_call = _call_per_cable;
+
+	return raw_notifier_chain_register(&obj->edev->nh, &obj->internal_nb);
+}
+
+/**
+ * extcon_unregister_interest() - Unregister the notifier registered by
+ *				extcon_register_interest().
+ * @obj:	the extcon_specific_cable_nb object returned by
+ *		extcon_register_interest().
+ */
+int extcon_unregister_interest(struct extcon_specific_cable_nb *obj)
+{
+	if (!obj)
+		return -EINVAL;
+
+	return raw_notifier_chain_unregister(&obj->edev->nh, &obj->internal_nb);
+}
+
 /**
  * extcon_register_notifier() - Register a notifee to get notified by
  *			      any attach status changes from the extcon.
  * @edev:	the extcon device.
  * @nb:		a notifier block to be registered.
+ *
+ * Note that the second parameter given to the callback of nb (val) is
+ * "old_state", not the current state. The current state can be retrieved
+ * by looking at the third pameter (edev pointer)'s state value.
  */
 int extcon_register_notifier(struct extcon_dev *edev,
 			struct notifier_block *nb)
@@ -173,8 +479,9 @@ int extcon_unregister_notifier(struct extcon_dev *edev,
 EXPORT_SYMBOL_GPL(extcon_unregister_notifier);
 
 static struct device_attribute extcon_attrs[] = {
-	__ATTR_RO(state),
+	__ATTR(state, S_IRUGO | S_IWUSR, state_show, state_store),
 	__ATTR_RO(name),
+	__ATTR_NULL,
 };
 
 static int create_extcon_class(void)
@@ -202,6 +509,16 @@ static void extcon_cleanup(struct extcon_dev *edev, bool skip)
 	mutex_unlock(&extcon_dev_list_lock);
 
 	if (!skip && get_device(edev->dev)) {
+		int index;
+
+		for (index = 0; index < edev->max_supported; index++)
+			kfree(edev->cables[index].attr_g.name);
+
+		if (edev->max_supported) {
+			kfree(edev->extcon_dev_type.groups);
+			kfree(edev->cables);
+		}
+
 		device_unregister(edev->dev);
 		put_device(edev->dev);
 	}
@@ -216,6 +533,10 @@ static void extcon_dev_release(struct device *dev)
 	extcon_cleanup(edev, true);
 }
 
+static void dummy_sysfs_dev_release(struct device *dev)
+{
+}
+
 /**
  * extcon_dev_register() - Register a new extcon device
  * @edev	: the new extcon device (should be allocated before calling)
@@ -228,7 +549,7 @@ static void extcon_dev_release(struct device *dev)
  */
 int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
 {
-	int ret;
+	int ret, index = 0;
 
 	if (!extcon_class) {
 		ret = create_extcon_class();
@@ -236,12 +557,93 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
 			return ret;
 	}
 
+	if (edev->supported_cable) {
+		/* Get size of array */
+		for (index = 0; edev->supported_cable[index]; index++)
+			;
+		edev->max_supported = index;
+	} else {
+		edev->max_supported = 0;
+	}
+
+	if (index > SUPPORTED_CABLE_MAX) {
+		dev_err(edev->dev, "extcon: maximum number of supported cables exceeded.\n");
+		return -EINVAL;
+	}
+
 	edev->dev = kzalloc(sizeof(struct device), GFP_KERNEL);
 	edev->dev->parent = dev;
 	edev->dev->class = extcon_class;
 	edev->dev->release = extcon_dev_release;
 
 	dev_set_name(edev->dev, edev->name ? edev->name : dev_name(dev));
+
+	if (edev->max_supported) {
+		char buf[10];
+		char *str;
+		struct extcon_cable *cable;
+
+		edev->cables = kzalloc(sizeof(struct extcon_cable) *
+				       edev->max_supported, GFP_KERNEL);
+		if (!edev->cables) {
+			ret = -ENOMEM;
+			goto err_sysfs_alloc;
+		}
+		for (index = 0; index < edev->max_supported; index++) {
+			cable = &edev->cables[index];
+
+			snprintf(buf, 10, "cable.%d", index);
+			str = kzalloc(sizeof(char) * (strlen(buf) + 1),
+				      GFP_KERNEL);
+			if (!str) {
+				for (index--; index >= 0; index--) {
+					cable = &edev->cables[index];
+					kfree(cable->attr_g.name);
+				}
+				ret = -ENOMEM;
+
+				goto err_alloc_cables;
+			}
+			strcpy(str, buf);
+
+			cable->edev = edev;
+			cable->cable_index = index;
+			cable->attrs[0] = &cable->attr_name.attr;
+			cable->attrs[1] = &cable->attr_state.attr;
+			cable->attrs[2] = NULL;
+			cable->attr_g.name = str;
+			cable->attr_g.attrs = cable->attrs;
+
+			cable->attr_name.attr.name = "name";
+			cable->attr_name.attr.mode = 0444;
+			cable->attr_name.show = cable_name_show;
+
+			cable->attr_state.attr.name = "state";
+			cable->attr_state.attr.mode = 0644;
+			cable->attr_state.show = cable_state_show;
+			cable->attr_state.store = cable_state_store;
+		}
+	}
+
+	if (edev->max_supported) {
+		edev->extcon_dev_type.groups =
+			kzalloc(sizeof(struct attribute_group *) *
+				(edev->max_supported + 1), GFP_KERNEL);
+		if (!edev->extcon_dev_type.groups) {
+			ret = -ENOMEM;
+			goto err_alloc_groups;
+		}
+
+		edev->extcon_dev_type.name = dev_name(edev->dev);
+		edev->extcon_dev_type.release = dummy_sysfs_dev_release;
+
+		for (index = 0; index < edev->max_supported; index++)
+			edev->extcon_dev_type.groups[index] =
+				&edev->cables[index].attr_g;
+
+		edev->dev->type = &edev->extcon_dev_type;
+	}
+
 	ret = device_register(edev->dev);
 	if (ret) {
 		put_device(edev->dev);
@@ -253,6 +655,8 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
 					       dev);
 #endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
 
+	spin_lock_init(&edev->lock);
+
 	RAW_INIT_NOTIFIER_HEAD(&edev->nh);
 
 	dev_set_drvdata(edev->dev, edev);
@@ -265,6 +669,15 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
 	return 0;
 
 err_dev:
+	if (edev->max_supported)
+		kfree(edev->extcon_dev_type.groups);
+err_alloc_groups:
+	for (index = 0; index < edev->max_supported; index++)
+		kfree(edev->cables[index].attr_g.name);
+err_alloc_cables:
+	if (edev->max_supported)
+		kfree(edev->cables);
+err_sysfs_alloc:
 	kfree(edev->dev);
 	return ret;
 }
diff --git a/include/linux/extcon.h b/include/linux/extcon.h
index 50be845..6ed1967 100644
--- a/include/linux/extcon.h
+++ b/include/linux/extcon.h
@@ -24,10 +24,60 @@
 #define __LINUX_EXTCON_H__
 
 #include <linux/notifier.h>
+
+#define SUPPORTED_CABLE_MAX	32
+#define CABLE_NAME_MAX		30
+
+/*
+ * The standard cable name is to help support general notifier
+ * and notifee device drivers to share the common names.
+ * Please use standard cable names unless your notifier device has
+ * a very unique and abnormal cable or
+ * the cable type is supposed to be used with only one unique
+ * pair of notifier/notifee devices.
+ *
+ * Please add any other "standard" cables used with extcon dev.
+ *
+ * You may add a dot and number to specify version or specification
+ * of the specific cable if it is required. (e.g., "Fast-charger.18"
+ * and "Fast-charger.10" for 1.8A and 1.0A chargers)
+ * However, the notifee and notifier should be able to handle such
+ * string and if the notifee can negotiate the protocol or idenify,
+ * you don't need such convention. This convention is helpful when
+ * notifier can distinguish but notifiee cannot.
+ */
+enum extcon_cable_name {
+	EXTCON_USB = 0,
+	EXTCON_USB_HOST,
+	EXTCON_TA, /* Travel Adaptor */
+	EXTCON_FAST_CHARGER,
+	EXTCON_SLOW_CHARGER,
+	EXTCON_CHARGE_DOWNSTREAM, /* Charging an external device */
+	EXTCON_HDMI,
+	EXTCON_MHL,
+	EXTCON_DVI,
+	EXTCON_VGA,
+	EXTCON_DOCK,
+	EXTCON_LINE_IN,
+	EXTCON_LINE_OUT,
+	EXTCON_MIC_IN,
+	EXTCON_HEADPHONE_OUT,
+	EXTCON_SPDIF_IN,
+	EXTCON_SPDIF_OUT,
+	EXTCON_VIDEO_IN,
+	EXTCON_VIDEO_OUT,
+};
+extern const char *extcon_cable_name[];
+
+struct extcon_cable;
+
 /**
  * struct extcon_dev - An extcon device represents one external connector.
  * @name	The name of this extcon device. Parent device name is used
  *		if NULL.
+ * @supported_cable	Array of supported cable name ending with NULL.
+ *			If supported_cable is NULL, cable name related APIs
+ *			are disabled.
  * @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
@@ -38,6 +88,11 @@
  * @nh	Notifier for the state change events from this extcon
  * @entry	To support list of extcon devices so that uses can search
  *		for extcon devices based on the extcon name.
+ * @lock
+ * @max_supported	Internal value to store the number of cables.
+ * @extcon_dev_type	Device_type struct to provide attribute_groups
+ *			customized for each extcon device.
+ * @cables	Sysfs subdirectories. Each represents one cable.
  *
  * In most cases, users only need to provide "User initializing data" of
  * this struct when registering an extcon. In some exceptional cases,
@@ -47,6 +102,7 @@
 struct extcon_dev {
 	/* --- Optional user initializing data --- */
 	const char	*name;
+	const char **supported_cable;
 
 	/* --- Optional callbacks to override class functions --- */
 	ssize_t	(*print_name)(struct extcon_dev *edev, char *buf);
@@ -57,6 +113,49 @@ struct extcon_dev {
 	u32		state;
 	struct raw_notifier_head nh;
 	struct list_head entry;
+	spinlock_t lock; /* could be called by irq handler */
+	int max_supported;
+
+	/* /sys/class/extcon/.../cable.n/... */
+	struct device_type extcon_dev_type;
+	struct extcon_cable *cables;
+};
+
+/**
+ * struct extcon_cable	- An internal data for each cable of extcon device.
+ * @edev	The extcon device
+ * @cable_index	Index of this cable in the edev
+ * @attr_g	Attribute group for the cable
+ * @attr_name	"name" sysfs entry
+ * @attr_state	"state" sysfs entry
+ * @attrs	Array pointing to attr_name and attr_state for attr_g
+ */
+struct extcon_cable {
+	struct extcon_dev *edev;
+	int cable_index;
+
+	struct attribute_group attr_g;
+	struct device_attribute attr_name;
+	struct device_attribute attr_state;
+
+	struct attribute *attrs[3]; /* to be fed to attr_g.attrs */
+};
+
+/**
+ * struct extcon_specific_cable_nb - An internal data for
+ *				extcon_register_interest().
+ * @internal_nb	a notifier block bridging extcon notifier and cable notifier.
+ * @user_nb	user provided notifier block for events from a specific cable.
+ * @cable_index	the target cable.
+ * @edev	the target extcon device.
+ * @previous_value	the saved previous event value.
+ */
+struct extcon_specific_cable_nb {
+	struct notifier_block internal_nb;
+	struct notifier_block *user_nb;
+	int cable_index;
+	struct extcon_dev *edev;
+	unsigned long previous_value;
 };
 
 #ifdef CONFIG_EXTCON
@@ -69,16 +168,54 @@ extern int extcon_dev_register(struct extcon_dev *edev, struct device *dev);
 extern void extcon_dev_unregister(struct extcon_dev *edev);
 extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name);
 
+/*
+ * get/set/update_state access the 32b encoded state value, which represents
+ * states of all possible cables of the multistate port. For example, if one
+ * calls extcon_set_state(edev, 0x7), it may mean that all the three cables
+ * are attached to the port.
+ */
 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);
+
+/*
+ * get/set_cable_state access each bit of the 32b encoded state value.
+ * They are used to access the status of each cable based on the cable_name
+ * or cable_index, which is retrived by extcon_find_cable_index
+ */
+extern int extcon_find_cable_index(struct extcon_dev *sdev,
+				   const char *cable_name);
+extern int extcon_get_cable_state_(struct extcon_dev *edev, int cable_index);
+extern int extcon_set_cable_state_(struct extcon_dev *edev, int cable_index,
+				   bool cable_state);
+
+extern int extcon_get_cable_state(struct extcon_dev *edev,
+				  const char *cable_name);
+extern int extcon_set_cable_state(struct extcon_dev *edev,
+				  const char *cable_name, bool cable_state);
+
+/*
+ * Following APIs are for notifiees (those who want to be notified)
+ * to register a callback for events from a specific cable of the extcon.
+ * Notifiees are the connected device drivers wanting to get notified by
+ * a specific external port of a connection device.
+ */
+extern int extcon_register_interest(struct extcon_specific_cable_nb *obj,
+				    const char *extcon_name,
+				    const char *cable_name,
+				    struct notifier_block *nb);
+extern int extcon_unregister_interest(struct extcon_specific_cable_nb *nb);
 
 /*
  * Following APIs are to monitor every action of a notifier.
  * Registerer gets notified for every external port of a connection device.
+ * Probably this could be used to debug an action of notifier; however,
+ * we do not recommend to use this at normal 'notifiee' device drivers who
+ * want to be notified by a specific external port of the notifier.
  */
 extern int extcon_register_notifier(struct extcon_dev *edev,
 				    struct notifier_block *nb);
@@ -99,6 +236,41 @@ static inline u32 extcon_get_state(struct extcon_dev *edev)
 }
 
 static inline void extcon_set_state(struct extcon_dev *edev, u32 state) { }
+
+static inline void extcon_update_state(struct extcon_dev *edev, u32 mask,
+				       u32 state)
+{ }
+
+static inline int extcon_find_cable_index(struct extcon_dev *edev,
+					  const char *cable_name)
+{
+	return 0;
+}
+
+static inline int extcon_get_cable_state_(struct extcon_dev *edev,
+					  int cable_index)
+{
+	return 0;
+}
+
+static inline int extcon_set_cable_state_(struct extcon_dev *edev,
+					  int cable_index, bool cable_state)
+{
+	return 0;
+}
+
+static inline int extcon_get_cable_state(struct extcon_dev *edev,
+			const char *cable_name)
+{
+	return 0;
+}
+
+static inline int extcon_set_cable_state(struct extcon_dev *edev,
+			const char *cable_name, int state)
+{
+	return 0;
+}
+
 static inline struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
 {
 	return NULL;
@@ -116,5 +288,17 @@ static inline int extcon_unregister_notifier(struct extcon_dev *edev,
 	return 0;
 }
 
+static inline int extcon_register_interest(struct extcon_specific_cable_nb *obj,
+					   const char *extcon_name,
+					   const char *cable_name,
+					   struct notifier_block *nb)
+{
+	return 0;
+}
+
+static inline int extcon_unregister_interest(struct extcon_specific_cable_nb *obj)
+{
+	return 0;
+}
 #endif /* CONFIG_EXTCON */
 #endif /* __LINUX_EXTCON_H__ */
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH v8 5/6] Extcon: support mutually exclusive relation between cables.
  2012-04-19  2:41     ` [PATCH v8 0/6] Introduce External Connector Class (extcon) MyungJoo Ham
                         ` (3 preceding siblings ...)
  2012-04-19  2:41       ` [PATCH v8 4/6] Extcon: support multiple states at a device MyungJoo Ham
@ 2012-04-19  2:41       ` MyungJoo Ham
  2012-04-19  2:41       ` [PATCH v8 6/6] Documentation/extcon: porting guide for Android kernel switch driver MyungJoo Ham
  5 siblings, 0 replies; 51+ messages in thread
From: MyungJoo Ham @ 2012-04-19  2:41 UTC (permalink / raw)
  To: Greg KH
  Cc: Arnd Bergmann, LKML, NeilBrown, Randy Dunlap, Mike Lockwood,
	Arve Hjønnevag, Kyungmin Park, Linus Walleij,
	Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown, John Stultz,
	Joerg Roedel, myungjoo.ham, cw00.choi

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 6ed1967..f8c7c73 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


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH v8 6/6] Documentation/extcon: porting guide for Android kernel switch driver.
  2012-04-19  2:41     ` [PATCH v8 0/6] Introduce External Connector Class (extcon) MyungJoo Ham
                         ` (4 preceding siblings ...)
  2012-04-19  2:41       ` [PATCH v8 5/6] Extcon: support mutually exclusive relation between cables MyungJoo Ham
@ 2012-04-19  2:41       ` MyungJoo Ham
  5 siblings, 0 replies; 51+ messages in thread
From: MyungJoo Ham @ 2012-04-19  2:41 UTC (permalink / raw)
  To: Greg KH
  Cc: Arnd Bergmann, LKML, NeilBrown, Randy Dunlap, Mike Lockwood,
	Arve Hjønnevag, Kyungmin Park, Linus Walleij,
	Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown, John Stultz,
	Joerg Roedel, myungjoo.ham, cw00.choi

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 Documentation/extcon/porting-android-switch-class |  124 +++++++++++++++++++++
 1 files changed, 124 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/extcon/porting-android-switch-class

diff --git a/Documentation/extcon/porting-android-switch-class b/Documentation/extcon/porting-android-switch-class
new file mode 100644
index 0000000..eb0fa5f
--- /dev/null
+++ b/Documentation/extcon/porting-android-switch-class
@@ -0,0 +1,124 @@
+
+	Staging/Android Switch Class Porting Guide
+	(linux/drivers/staging/android/switch)
+	(c) Copyright 2012 Samsung Electronics
+
+AUTHORS
+MyungJoo Ham <myungjoo.ham@samsung.com>
+
+/*****************************************************************
+ * CHAPTER 1.                                                    *
+ * PORTING SWITCH CLASS DEVICE DRIVERS                           *
+ *****************************************************************/
+
+****** STEP 1. Basic Functionality
+	No extcon extended feature, but switch features only.
+
+- struct switch_dev (fed to switch_dev_register/unregister)
+    @name: no change
+    @dev: no change
+    @index: drop (not used in switch device driver side anyway)
+    @state: no change
+	If you have used @state with magic numbers, keep it
+	at this step.
+    @print_name: no change but type change (switch_dev->extcon_dev)
+    @print_state: no change but type change (switch_dev->extcon_dev)
+
+- switch_dev_register(sdev, dev)
+	=> extcon_dev_register(edev, dev)
+	: no change but type change (sdev->edev)
+- switch_dev_unregister(sdev)
+	=> extcon_dev_unregister(edev)
+	: no change but type change (sdev->edev)
+- switch_get_state(sdev)
+	=> extcon_get_state(edev)
+	: no change but type change (sdev->edev) and (return: int->u32)
+- switch_set_state(sdev, state)
+	=> extcon_set_state(edev, state)
+	: no change but type change (sdev->edev) and (state: int->u32)
+
+With this changes, the ex-switch extcon class device works as it once
+worked as switch class device. However, it will now have additional
+interfaces (both ABI and in-kernel API) and different ABI locations.
+However, if CONFIG_ANDROID is enabled without CONFIG_ANDROID_SWITCH,
+/sys/class/switch/* will be symbolically linked to /sys/class/extcon/
+so that they are still compatible with legacy userspace processes.
+
+****** STEP 2. Multistate (no more magic numbers in state value)
+	Extcon's extended features for switch device drivers with
+	complex features usually required magic numbers in state
+	value of switch_dev. With extcon, such magic numbers that
+	support multiple cables (
+
+  1. Define cable names at edev->supported_cable.
+  2. (Recommended) remove print_state callback.
+  3. Use extcon_get_cable_state_(edev, index) or
+   extcon_get_cable_state(edev, cable_name) instead of
+   extcon_get_state(edev) if you intend to get a state of a specific
+   cable. Same for set_state. This way, you can remove the usage of
+   magic numbers in state value.
+  4. Use extcon_update_state() if you are updating specific bits of
+   the state value.
+
+Example: a switch device driver w/ magic numbers for two cables.
+	"0x00": no cables connected.
+	"0x01": cable 1 connected
+	"0x02": cable 2 connected
+	"0x03": cable 1 and 2 connected
+  1. edev->supported_cable = {"1", "2", NULL};
+  2. edev->print_state = NULL;
+  3. extcon_get_cable_state_(edev, 0) shows cable 1's state.
+     extcon_get_cable_state(edev, "1") shows cable 1's state.
+     extcon_set_cable_state_(edev, 1) sets cable 2's state.
+     extcon_set_cable_state(edev, "2") sets cable 2's state
+  4. extcon_update_state(edev, 0x01, 0) sets the least bit's 0.
+
+****** STEP 3. Notify other device drivers
+
+  You can notify others of the cable attach/detach events with
+notifier chains.
+
+  At the side of other device drivers (the extcon device itself
+does not need to get notified of its own events), there are two
+methods to register notifier_block for cable events:
+(a) for a specific cable or (b) for every cable.
+
+  (a) extcon_register_interest(obj, extcon_name, cable_name, nb)
+	Example: want to get news of "MAX8997_MUIC"'s "USB" cable
+
+	obj = kzalloc(sizeof(struct extcon_specific_cable_nb),
+		      GFP_KERNEL);
+	nb->notifier_call = the_callback_to_handle_usb;
+
+	extcon_register_intereset(obj, "MAX8997_MUIC", "USB", nb);
+
+  (b) extcon_register_notifier(edev, nb)
+	Call nb for any changes in edev.
+
+  Please note that in order to properly behave with method (a),
+the extcon device driver should support multistate feature (STEP 2).
+
+****** STEP 4. Inter-cable relation (mutually exclusive)
+
+  You can provide inter-cable mutually exclusiveness information
+for an extcon device. When cables A and B are declared to be mutually
+exclusive, the two cables cannot be in ATTACHED state simulteneously.
+
+
+/*****************************************************************
+ * CHAPTER 2.                                                    *
+ * PORTING USERSPACE w/ SWITCH CLASS DEVICE SUPPORT              *
+ *****************************************************************/
+
+****** ABI Location
+
+  If "CONFIG_ANDROID" is enabled and "CONFIG_ANDROID_SWITCH" is
+disabled, /sys/class/switch/* are created as symbolic links to
+/sys/class/extcon/*. Because CONFIG_ANDROID_SWITCH creates
+/sys/class/switch directory, we disable symboling linking if
+CONFIG_ANDROID_SWITCH is enabled.
+
+  The two files of switch class, name and state, are provided with
+extcon, too. When the multistate support (STEP 2 of CHAPTER 1.) is
+not enabled or print_state callback is supplied, the output of
+state ABI is same with switch class.
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* Re: [PATCH v8 1/6] Extcon (external connector): import Android's switch class and modify.
  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
  1 sibling, 0 replies; 51+ messages in thread
From: Mark Brown @ 2012-04-19 11:38 UTC (permalink / raw)
  To: MyungJoo Ham
  Cc: Greg KH, Arnd Bergmann, LKML, NeilBrown, Randy Dunlap,
	Mike Lockwood, Arve Hjønnevag, Kyungmin Park, Linus Walleij,
	Dmitry Torokhov, Morten CHRISTIANSEN, John Stultz, Joerg Roedel,
	myungjoo.ham, cw00.choi

[-- Attachment #1: Type: text/plain, Size: 1092 bytes --]

On Thu, Apr 19, 2012 at 11:41:33AM +0900, MyungJoo Ham wrote:
> External connector class (extcon) is based on and an extension of
> Android kernel's switch class located at linux/drivers/switch/.

Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>

> +comment "Extcon Class Configuration"
> +
> +config EXTCON_ANDROID_COMPATIBLE
> +	bool "Class name uses 'switch' to be compatible with Android kernel"
> +	depends on ANDROID
> +	help
> +	  The class name of extcon device drivers are set 'switch' in order
> +	  to be compatible with Android kernel switch framework. With this
> +	  option enabled, Android userspace platform may use extcon device
> +	  drivers as if they are switch device drivers.

Though I think this is out of date now?  Not a big deal and could be
fixed up later.

> +#if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
> +		switch_class = class_compat_register("switch");
> +		if (WARN(!switch_class, "cannot allocate"))
> +			return -ENOMEM;
> +#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */

This does the above but differently.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v8 2/6] Extcon: support generic GPIO extcon driver
  2012-04-19  2:41       ` [PATCH v8 2/6] Extcon: support generic GPIO extcon driver MyungJoo Ham
@ 2012-04-19 11:40         ` Mark Brown
  0 siblings, 0 replies; 51+ messages in thread
From: Mark Brown @ 2012-04-19 11:40 UTC (permalink / raw)
  To: MyungJoo Ham
  Cc: Greg KH, Arnd Bergmann, LKML, NeilBrown, Randy Dunlap,
	Mike Lockwood, Arve Hjønnevag, Kyungmin Park, Linus Walleij,
	Dmitry Torokhov, Morten CHRISTIANSEN, John Stultz, Joerg Roedel,
	myungjoo.ham, cw00.choi

[-- Attachment #1: Type: text/plain, Size: 1043 bytes --]

On Thu, Apr 19, 2012 at 11:41:34AM +0900, MyungJoo Ham wrote:
> The generic GPIO extcon driver (an external connector device based on
> GPIO control) and imported from Android kernel.

Reviwed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>

> +	ret = gpio_request(extcon_data->gpio, pdev->name);
> +	if (ret < 0)
> +		goto err_request_gpio;
> +
> +	ret = gpio_direction_input(extcon_data->gpio);
> +	if (ret < 0)
> +		goto err_set_gpio_input;

Incrementally (or in future versions) I'd suggest replacing with
gpio_request_one() which combines the above and is generally nicer.

> +static int __devexit gpio_extcon_remove(struct platform_device *pdev)
> +{
> +	struct gpio_extcon_data *extcon_data = platform_get_drvdata(pdev);
> +
> +	cancel_delayed_work_sync(&extcon_data->work);
> +	gpio_free(extcon_data->gpio);
> +	extcon_dev_unregister(&extcon_data->edev);
> +	devm_kfree(&pdev->dev, extcon_data);

The whole point of using devm_ is that you don't need to explicitly free
for this usage pattern.  On the other hand it does no harm.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v8 1/6] Extcon (external connector): import Android's switch class and modify.
  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
  1 sibling, 1 reply; 51+ messages in thread
From: Greg KH @ 2012-04-20  2:37 UTC (permalink / raw)
  To: MyungJoo Ham
  Cc: Arnd Bergmann, LKML, NeilBrown, Randy Dunlap, Mike Lockwood,
	Arve Hjønnevag, Kyungmin Park, Linus Walleij,
	Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown, John Stultz,
	Joerg Roedel, myungjoo.ham, cw00.choi

On Thu, Apr 19, 2012 at 11:41:33AM +0900, MyungJoo Ham wrote:
> External connector class (extcon) is based on and an extension of
> Android kernel's switch class located at linux/drivers/switch/.

Oh come on, this still breaks the build, it is as if you didn't even
test this at all, or modify anything since I reported it as a problem to
you yesterday:

drivers/extcon/extcon_class.c:79:6: error: redefinition of ‘extcon_set_state’
include/linux/extcon.h:80:20: note: previous definition of ‘extcon_set_state’ was here
drivers/extcon/extcon_class.c:170:5: error: redefinition of ‘extcon_dev_register’
include/linux/extcon.h:67:19: note: previous definition of ‘extcon_dev_register’ was here
drivers/extcon/extcon_class.c:214:6: error: redefinition of ‘extcon_dev_unregister’
include/linux/extcon.h:73:20: note: previous definition of ‘extcon_dev_unregister’ was here

You can do better than this, I know you can.

greg k-h

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v8 1/6] Extcon (external connector): import Android's switch class and modify.
  2012-04-20  2:37         ` Greg KH
@ 2012-04-20  2:46           ` Kyungmin Park
  2012-04-20  2:49             ` Greg KH
  0 siblings, 1 reply; 51+ messages in thread
From: Kyungmin Park @ 2012-04-20  2:46 UTC (permalink / raw)
  To: Greg KH
  Cc: MyungJoo Ham, Arnd Bergmann, LKML, NeilBrown, Randy Dunlap,
	Mike Lockwood, Arve Hjønnevag, Linus Walleij,
	Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown, John Stultz,
	Joerg Roedel, myungjoo.ham, cw00.choi

On 4/20/12, Greg KH <gregkh@linuxfoundation.org> wrote:
> On Thu, Apr 19, 2012 at 11:41:33AM +0900, MyungJoo Ham wrote:
>> External connector class (extcon) is based on and an extension of
>> Android kernel's switch class located at linux/drivers/switch/.
>
> Oh come on, this still breaks the build, it is as if you didn't even
> test this at all, or modify anything since I reported it as a problem to
> you yesterday:
>
> drivers/extcon/extcon_class.c:79:6: error: redefinition of
> ‘extcon_set_state’
> include/linux/extcon.h:80:20: note: previous definition of
> ‘extcon_set_state’ was here
> drivers/extcon/extcon_class.c:170:5: error: redefinition of
> ‘extcon_dev_register’
> include/linux/extcon.h:67:19: note: previous definition of
> ‘extcon_dev_register’ was here
> drivers/extcon/extcon_class.c:214:6: error: redefinition of
> ‘extcon_dev_unregister’
> include/linux/extcon.h:73:20: note: previous definition of
> ‘extcon_dev_unregister’ was here
>
> You can do better than this, I know you can.

Umm. can you show your EXTCON config? I think you build it as module.
it's tested it extcon on or not. anyway I'll test it as module and
check it.

Thank you,
Kyungmin Park
>
> greg k-h
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
>

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v8 1/6] Extcon (external connector): import Android's switch class and modify.
  2012-04-20  2:46           ` Kyungmin Park
@ 2012-04-20  2:49             ` Greg KH
  2012-04-20  2:57               ` Kyungmin Park
  0 siblings, 1 reply; 51+ messages in thread
From: Greg KH @ 2012-04-20  2:49 UTC (permalink / raw)
  To: Kyungmin Park
  Cc: MyungJoo Ham, Arnd Bergmann, LKML, NeilBrown, Randy Dunlap,
	Mike Lockwood, Arve Hjønnevag, Linus Walleij,
	Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown, John Stultz,
	Joerg Roedel, myungjoo.ham, cw00.choi

On Fri, Apr 20, 2012 at 11:46:25AM +0900, Kyungmin Park wrote:
> On 4/20/12, Greg KH <gregkh@linuxfoundation.org> wrote:
> > On Thu, Apr 19, 2012 at 11:41:33AM +0900, MyungJoo Ham wrote:
> >> External connector class (extcon) is based on and an extension of
> >> Android kernel's switch class located at linux/drivers/switch/.
> >
> > Oh come on, this still breaks the build, it is as if you didn't even
> > test this at all, or modify anything since I reported it as a problem to
> > you yesterday:
> >
> > drivers/extcon/extcon_class.c:79:6: error: redefinition of
> > ‘extcon_set_state’
> > include/linux/extcon.h:80:20: note: previous definition of
> > ‘extcon_set_state’ was here
> > drivers/extcon/extcon_class.c:170:5: error: redefinition of
> > ‘extcon_dev_register’
> > include/linux/extcon.h:67:19: note: previous definition of
> > ‘extcon_dev_register’ was here
> > drivers/extcon/extcon_class.c:214:6: error: redefinition of
> > ‘extcon_dev_unregister’
> > include/linux/extcon.h:73:20: note: previous definition of
> > ‘extcon_dev_unregister’ was here
> >
> > You can do better than this, I know you can.
> 
> Umm. can you show your EXTCON config? I think you build it as module.
> it's tested it extcon on or not. anyway I'll test it as module and
> check it.

Yes, I selected to build it as a module, which, according to your patch,
is a valid selection.

greg k-h

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v8 1/6] Extcon (external connector): import Android's switch class and modify.
  2012-04-20  2:49             ` Greg KH
@ 2012-04-20  2:57               ` Kyungmin Park
  2012-04-20  3:00                 ` Greg KH
  0 siblings, 1 reply; 51+ messages in thread
From: Kyungmin Park @ 2012-04-20  2:57 UTC (permalink / raw)
  To: Greg KH
  Cc: MyungJoo Ham, Arnd Bergmann, LKML, NeilBrown, Randy Dunlap,
	Mike Lockwood, Arve Hjønnevag, Linus Walleij,
	Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown, John Stultz,
	Joerg Roedel, myungjoo.ham, cw00.choi

On 4/20/12, Greg KH <gregkh@linuxfoundation.org> wrote:
> On Fri, Apr 20, 2012 at 11:46:25AM +0900, Kyungmin Park wrote:
>> On 4/20/12, Greg KH <gregkh@linuxfoundation.org> wrote:
>> > On Thu, Apr 19, 2012 at 11:41:33AM +0900, MyungJoo Ham wrote:
>> >> External connector class (extcon) is based on and an extension of
>> >> Android kernel's switch class located at linux/drivers/switch/.
>> >
>> > Oh come on, this still breaks the build, it is as if you didn't even
>> > test this at all, or modify anything since I reported it as a problem to
>> > you yesterday:
>> >
>> > drivers/extcon/extcon_class.c:79:6: error: redefinition of
>> > ‘extcon_set_state’
>> > include/linux/extcon.h:80:20: note: previous definition of
>> > ‘extcon_set_state’ was here
>> > drivers/extcon/extcon_class.c:170:5: error: redefinition of
>> > ‘extcon_dev_register’
>> > include/linux/extcon.h:67:19: note: previous definition of
>> > ‘extcon_dev_register’ was here
>> > drivers/extcon/extcon_class.c:214:6: error: redefinition of
>> > ‘extcon_dev_unregister’
>> > include/linux/extcon.h:73:20: note: previous definition of
>> > ‘extcon_dev_unregister’ was here
>> >
>> > You can do better than this, I know you can.
>>
>> Umm. can you show your EXTCON config? I think you build it as module.
>> it's tested it extcon on or not. anyway I'll test it as module and
>> check it.
>
> Yes, I selected to build it as a module, which, according to your patch,
> is a valid selection.

Found, need to add CONFIG_EXTCON_MODULE at below.

Send new series right now.

Thank you,
Kyungmin Park

+#if defined(CONFIG_EXTCON) || defined(CONFIG_EXTCON_MODULE)
+extern int extcon_dev_register(struct extcon_dev *edev, struct device *dev);
+extern void extcon_dev_unregister(struct extcon_dev *edev);
+
+static inline u32 extcon_get_state(struct extcon_dev *edev)
+{
+       return edev->state;
+}
+
+extern void extcon_set_state(struct extcon_dev *edev, u32 state);
+#else /* CONFIG_EXTCON */
+static inline int extcon_dev_register(struct extcon_dev *edev,
+                                     struct device *dev)
+{
+       return 0;
+}
+
+static inline void extcon_dev_unregister(struct extcon_dev *edev) { }
+
+static inline u32 extcon_get_state(struct extcon_dev *edev)
+{
+       return 0;
+}
+
+static inline void extcon_set_state(struct extcon_dev *edev, u32 state) { }
+#endif /* CONFIG_EXTCON */
>
> greg k-h
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
>

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v8 1/6] Extcon (external connector): import Android's switch class and modify.
  2012-04-20  2:57               ` Kyungmin Park
@ 2012-04-20  3:00                 ` Greg KH
  2012-04-20  3:08                   ` Kyungmin Park
  0 siblings, 1 reply; 51+ messages in thread
From: Greg KH @ 2012-04-20  3:00 UTC (permalink / raw)
  To: Kyungmin Park
  Cc: MyungJoo Ham, Arnd Bergmann, LKML, NeilBrown, Randy Dunlap,
	Mike Lockwood, Arve Hjønnevag, Linus Walleij,
	Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown, John Stultz,
	Joerg Roedel, myungjoo.ham, cw00.choi

On Fri, Apr 20, 2012 at 11:57:26AM +0900, Kyungmin Park wrote:
> On 4/20/12, Greg KH <gregkh@linuxfoundation.org> wrote:
> > On Fri, Apr 20, 2012 at 11:46:25AM +0900, Kyungmin Park wrote:
> >> On 4/20/12, Greg KH <gregkh@linuxfoundation.org> wrote:
> >> > On Thu, Apr 19, 2012 at 11:41:33AM +0900, MyungJoo Ham wrote:
> >> >> External connector class (extcon) is based on and an extension of
> >> >> Android kernel's switch class located at linux/drivers/switch/.
> >> >
> >> > Oh come on, this still breaks the build, it is as if you didn't even
> >> > test this at all, or modify anything since I reported it as a problem to
> >> > you yesterday:
> >> >
> >> > drivers/extcon/extcon_class.c:79:6: error: redefinition of
> >> > ‘extcon_set_state’
> >> > include/linux/extcon.h:80:20: note: previous definition of
> >> > ‘extcon_set_state’ was here
> >> > drivers/extcon/extcon_class.c:170:5: error: redefinition of
> >> > ‘extcon_dev_register’
> >> > include/linux/extcon.h:67:19: note: previous definition of
> >> > ‘extcon_dev_register’ was here
> >> > drivers/extcon/extcon_class.c:214:6: error: redefinition of
> >> > ‘extcon_dev_unregister’
> >> > include/linux/extcon.h:73:20: note: previous definition of
> >> > ‘extcon_dev_unregister’ was here
> >> >
> >> > You can do better than this, I know you can.
> >>
> >> Umm. can you show your EXTCON config? I think you build it as module.
> >> it's tested it extcon on or not. anyway I'll test it as module and
> >> check it.
> >
> > Yes, I selected to build it as a module, which, according to your patch,
> > is a valid selection.
> 
> Found, need to add CONFIG_EXTCON_MODULE at below.

You do know we have a new macro that handles all of this for you,
IS_ENABLED().  It handles the _MODULE stuff and the other stuff as well,
and you can put it in a .c expression also.

Try using that instead.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v8 1/6] Extcon (external connector): import Android's switch class and modify.
  2012-04-20  3:00                 ` Greg KH
@ 2012-04-20  3:08                   ` Kyungmin Park
  2012-04-20  3:32                     ` Greg KH
  0 siblings, 1 reply; 51+ messages in thread
From: Kyungmin Park @ 2012-04-20  3:08 UTC (permalink / raw)
  To: Greg KH
  Cc: MyungJoo Ham, Arnd Bergmann, LKML, NeilBrown, Randy Dunlap,
	Mike Lockwood, Arve Hjønnevag, Linus Walleij,
	Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown, John Stultz,
	Joerg Roedel, myungjoo.ham, cw00.choi

On 4/20/12, Greg KH <gregkh@linuxfoundation.org> wrote:
> On Fri, Apr 20, 2012 at 11:57:26AM +0900, Kyungmin Park wrote:
>> On 4/20/12, Greg KH <gregkh@linuxfoundation.org> wrote:
>> > On Fri, Apr 20, 2012 at 11:46:25AM +0900, Kyungmin Park wrote:
>> >> On 4/20/12, Greg KH <gregkh@linuxfoundation.org> wrote:
>> >> > On Thu, Apr 19, 2012 at 11:41:33AM +0900, MyungJoo Ham wrote:
>> >> >> External connector class (extcon) is based on and an extension of
>> >> >> Android kernel's switch class located at linux/drivers/switch/.
>> >> >
>> >> > Oh come on, this still breaks the build, it is as if you didn't even
>> >> > test this at all, or modify anything since I reported it as a problem
>> >> > to
>> >> > you yesterday:
>> >> >
>> >> > drivers/extcon/extcon_class.c:79:6: error: redefinition of
>> >> > ‘extcon_set_state’
>> >> > include/linux/extcon.h:80:20: note: previous definition of
>> >> > ‘extcon_set_state’ was here
>> >> > drivers/extcon/extcon_class.c:170:5: error: redefinition of
>> >> > ‘extcon_dev_register’
>> >> > include/linux/extcon.h:67:19: note: previous definition of
>> >> > ‘extcon_dev_register’ was here
>> >> > drivers/extcon/extcon_class.c:214:6: error: redefinition of
>> >> > ‘extcon_dev_unregister’
>> >> > include/linux/extcon.h:73:20: note: previous definition of
>> >> > ‘extcon_dev_unregister’ was here
>> >> >
>> >> > You can do better than this, I know you can.
>> >>
>> >> Umm. can you show your EXTCON config? I think you build it as module.
>> >> it's tested it extcon on or not. anyway I'll test it as module and
>> >> check it.
>> >
>> > Yes, I selected to build it as a module, which, according to your patch,
>> > is a valid selection.
>>
>> Found, need to add CONFIG_EXTCON_MODULE at below.
>
> You do know we have a new macro that handles all of this for you,
> IS_ENABLED().  It handles the _MODULE stuff and the other stuff as well,
> and you can put it in a .c expression also.
>
> Try using that instead.

No problem, send patches with it.

BTW, can we post it after one hour?

Thank you,
Kyungmin Park
>
> thanks,
>
> greg k-h
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
>

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v8 1/6] Extcon (external connector): import Android's switch class and modify.
  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
  0 siblings, 1 reply; 51+ messages in thread
From: Greg KH @ 2012-04-20  3:32 UTC (permalink / raw)
  To: Kyungmin Park
  Cc: MyungJoo Ham, Arnd Bergmann, LKML, NeilBrown, Randy Dunlap,
	Mike Lockwood, Arve Hjønnevag, Linus Walleij,
	Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown, John Stultz,
	Joerg Roedel, myungjoo.ham, cw00.choi

On Fri, Apr 20, 2012 at 12:08:32PM +0900, Kyungmin Park wrote:
> On 4/20/12, Greg KH <gregkh@linuxfoundation.org> wrote:
> > On Fri, Apr 20, 2012 at 11:57:26AM +0900, Kyungmin Park wrote:
> >> On 4/20/12, Greg KH <gregkh@linuxfoundation.org> wrote:
> >> > On Fri, Apr 20, 2012 at 11:46:25AM +0900, Kyungmin Park wrote:
> >> >> On 4/20/12, Greg KH <gregkh@linuxfoundation.org> wrote:
> >> >> > On Thu, Apr 19, 2012 at 11:41:33AM +0900, MyungJoo Ham wrote:
> >> >> >> External connector class (extcon) is based on and an extension of
> >> >> >> Android kernel's switch class located at linux/drivers/switch/.
> >> >> >
> >> >> > Oh come on, this still breaks the build, it is as if you didn't even
> >> >> > test this at all, or modify anything since I reported it as a problem
> >> >> > to
> >> >> > you yesterday:
> >> >> >
> >> >> > drivers/extcon/extcon_class.c:79:6: error: redefinition of
> >> >> > ‘extcon_set_state’
> >> >> > include/linux/extcon.h:80:20: note: previous definition of
> >> >> > ‘extcon_set_state’ was here
> >> >> > drivers/extcon/extcon_class.c:170:5: error: redefinition of
> >> >> > ‘extcon_dev_register’
> >> >> > include/linux/extcon.h:67:19: note: previous definition of
> >> >> > ‘extcon_dev_register’ was here
> >> >> > drivers/extcon/extcon_class.c:214:6: error: redefinition of
> >> >> > ‘extcon_dev_unregister’
> >> >> > include/linux/extcon.h:73:20: note: previous definition of
> >> >> > ‘extcon_dev_unregister’ was here
> >> >> >
> >> >> > You can do better than this, I know you can.
> >> >>
> >> >> Umm. can you show your EXTCON config? I think you build it as module.
> >> >> it's tested it extcon on or not. anyway I'll test it as module and
> >> >> check it.
> >> >
> >> > Yes, I selected to build it as a module, which, according to your patch,
> >> > is a valid selection.
> >>
> >> Found, need to add CONFIG_EXTCON_MODULE at below.
> >
> > You do know we have a new macro that handles all of this for you,
> > IS_ENABLED().  It handles the _MODULE stuff and the other stuff as well,
> > and you can put it in a .c expression also.
> >
> > Try using that instead.
> 
> No problem, send patches with it.
> 
> BTW, can we post it after one hour?

You can send it whenever you want, I do not impose any deadlines.

But note, I'll not be reading email for at least 10 more hours, it's
getting late in my timezone, so take your time and get it right, no
rush.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH v8 resend 0/6] Introduce External Connector Class (extcon)
  2012-04-20  3:32                     ` Greg KH
@ 2012-04-20  5:16                       ` MyungJoo Ham
  2012-04-20  5:16                         ` [PATCH v8 resend 1/6] Extcon (external connector): import Android's switch class and modify MyungJoo Ham
                                           ` (6 more replies)
  0 siblings, 7 replies; 51+ messages in thread
From: MyungJoo Ham @ 2012-04-20  5:16 UTC (permalink / raw)
  To: Greg KH
  Cc: Arnd Bergmann, LKML, Arve Hjønnevag, Kyungmin Park,
	Linus Walleij, Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown,
	John Stultz, myungjoo.ham, cw00.choi

For external connectors, which may have different types of cables attached
(USB, TA, HDMI, Analog A/V, and others), we often have seperated device
drivers that detect the state changes at the port and device drivers that
do something according to the state changes.

An example is a 4-pin (or more pins?) analog audio connector
(usually referred as "3.5-pi" jacks). It can be implemented with the 5/5
patch, "adc-jack", supplied with information about IRQ, a method to get
ADC values, and ADC value ranges. With a 4-pin 3.5-pi port and adc-jack
driver with proper ADC configurations, the following modes may be
detected: "standard mono cable", "standard stereo cable",
"standard stereo + remote cable", "standard stereo + mic + remote cable",
"standard stereo + different-spec mic + remote cable", and such.
Note that for 4-pin cables with mic and remote, there are multiple
standards (at least Samsung and Apple use different configurations for
those 4-pin earphones although the pins looks exactly same.)
and they often can be distinguished with ADC values.

Although we may have simply given different "cable id" for "stereo" and
"stereo + mic". However, for those userspace processes or other device
drivers that are interested only in "stereo" part of possible combinations,
having seperated mode/id for "stereo" and "mic" is more desirable.
For example, if we do not have multistate capabilities, those who want
to know "stereo" is attached or not should monitor everything:
"standard stereo cable", "standard stereo + remove cable",
"standard stereo + mic + remote cable", and others. However, with
multistate capabilities, they only need to monitor the bit associated
with "standard stero".

For another example, when MAX8997-MUIC detects a Charger cable insertion,
another device driver (such as MAX8903 charger, MAX8997 charger, Charger
Manager, or board file) needs to set charger current limit accordingly
and when MAX8997-MUIC detects a HDMI cable insertion, multimedia device
drivers need to do some operations accordingly.


This patchset supports the usage of notifier for passing such information
between device drivers.

Another issue is that at a single switch port, there might be multiple
and heterogeneous cables attached at the same time. Besides the state
(Attached or Detached) of each cable may alter independently. Such extcon
devices that support simultaneous mult-cable attachment include, bot not
limited to, docks, cradles, and 30-pin-like ports (like that of NURI board
in linux/arch/arm/mach-exynos, which resembles Samsung Galaxy Tab series).
For example, the 30-pin port of NURI board may be used to connect analog
audio cables, analog video cable, and a USB or a charger. Because such
cables coonnect part of the 30 pins to external devices/cables, the
possible combinations of external devices/cables vary significantly and
users may fabricate their own design; e.g., an 30-pin adaptor connecting
USB, AC-DC adaptor, HDMI, analog audio, analog video, USB-host, SD-card,
and so on.

In order to address such issues, Android kernel's "Switch" class seems to
be a good basis and we have implemented "Multistate Switch Class" based on
it. The "Switch" class code of Android kernel is GPL as well.

Though there can be synchronization delay (up to 12hours), you can see
the extcon-for-next branch at
http://git.infradead.org/users/kmpark/linux-samsung/shortlog/refs/heads/extcon-for-next

Summary of changes from v8 patchset at v8-resend
- Style updates
- Usage of IS_ENABLED to avoid errors when compiled as modules

Summary of changes from v7 patchset
- Rebased over Linux 3.4 rc3
- Bugfix (parameters given to register-interest callbacks)
- Bugfix (header file definitions w/ !CONFIG_EXTCON)

Summary of changes from v6 patchset
- Added documentation, "porting guide for Android switch device drivers"
- Usage of class_compat to create /sys/class/switch/* along with
/sys/class/extcon/* if CONFIG_ANDROID is enabled. However, we cannot
enable "switch" class_compat if CONFIG_ANDROID_SWITCH is enabled because
we cannot create two classes with the same name.

Summary of changes from v5 patchset
- Excluded adc-jack from the "default" patchset. Adc-jack will be later
added after Extcon is finalized.
- Sysfs style updated
- Bugfixes after using at Exynos4412 boards with userspace processes
- Code stype updated

Summary of changes from v4 patchset
- Bugfixes after testing at Exynos4412 boards with userspace processes
- Added adc-jack to support 3.5-pi jack and other ADC-based cable detection
ports.

Summary of changes from v3 patchset
- Bugfixes after testing at Exynos4412 boards.

Summary of changes from v2 patchset
- Support to express mutually exclusive set of cables
- Revised device/class/sysfs-entries add/removal methods
- Added sysfs entries
- Revised documentation and comments
- Some bugfixes
- Compatible with Android (for extcon devices with "use_class_name_switch" set)

Summary of changes from RFC
- ABI documentation added
- Notifees may get notified for a state change of a specific cable, not
every cable of the corresponding extcon.
- Added kerneldoc comments
- Moved to /drivers/extcon/
- Added helper functions
- Some bugfixes

Donggeun Kim (1):
  Extcon: support notification based on the state changes.

MyungJoo Ham (5):
  Extcon (external connector): import Android's switch class and
    modify.
  Extcon: support generic GPIO extcon driver
  Extcon: support multiple states at a device.
  Extcon: support mutually exclusive relation between cables.
  Documentation/extcon: porting guide for Android kernel switch driver.

 Documentation/ABI/testing/sysfs-class-extcon      |   97 +++
 Documentation/extcon/porting-android-switch-class |  124 +++
 drivers/Kconfig                                   |    2 +
 drivers/Makefile                                  |    1 +
 drivers/extcon/Kconfig                            |   24 +
 drivers/extcon/Makefile                           |    6 +
 drivers/extcon/extcon_class.c                     |  822 +++++++++++++++++++++
 drivers/extcon/extcon_gpio.c                      |  169 +++++
 include/linux/extcon.h                            |  323 ++++++++
 include/linux/extcon/extcon_gpio.h                |   52 ++
 10 files changed, 1620 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-class-extcon
 create mode 100644 Documentation/extcon/porting-android-switch-class
 create mode 100644 drivers/extcon/Kconfig
 create mode 100644 drivers/extcon/Makefile
 create mode 100644 drivers/extcon/extcon_class.c
 create mode 100644 drivers/extcon/extcon_gpio.c
 create mode 100644 include/linux/extcon.h
 create mode 100644 include/linux/extcon/extcon_gpio.h

-- 
1.7.4.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH v8 resend 1/6] Extcon (external connector): import Android's switch class and modify.
  2012-04-20  5:16                       ` [PATCH v8 resend 0/6] Introduce External Connector Class (extcon) MyungJoo Ham
@ 2012-04-20  5:16                         ` 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
                                           ` (5 subsequent siblings)
  6 siblings, 1 reply; 51+ messages in thread
From: MyungJoo Ham @ 2012-04-20  5:16 UTC (permalink / raw)
  To: Greg KH
  Cc: Arnd Bergmann, LKML, Arve Hjønnevag, Kyungmin Park,
	Linus Walleij, Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown,
	John Stultz, myungjoo.ham, cw00.choi

External connector class (extcon) is based on and an extension of
Android kernel's switch class located at linux/drivers/switch/.

This patch provides the before-extension switch class moved to the
location where the extcon will be located (linux/drivers/extcon/) and
updates to handle class properly.

The before-extension class, switch class of Android kernel, commits
imported are:

switch: switch class and GPIO drivers. (splitted)
Author: Mike Lockwood <lockwood@android.com>

switch: Use device_create instead of device_create_drvdata.
Author: Arve Hjønnevåg <arve@android.com>

In this patch, upon the commits of Android kernel, we have added:
- Relocated and renamed for extcon.
- Comments, module name, and author information are updated
- Code clean for successing patches
- Bugfix: enabling write access without write functions
- Class/device/sysfs create/remove handling
- Added comments about uevents
- Format changes for extcon_dev_register() to have a parent dev.

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>

--
Changes from v7
- Compiler error fixed when it is compiled as a module.
- Removed out-of-date Kconfig entry

Changes from v6
- Updated comment/strings
- Revised "Android-compatible" mode.
   * Automatically activated if CONFIG_ANDROID && !CONFIG_ANDROID_SWITCH
   * Creates /sys/class/switch/*, which is a copy of /sys/class/extcon/*

Changes from v5
- Split the patch
- Style fixes
- "Android-compatible" mode is enabled by Kconfig option.

Changes from v2
- Updated name_show
- Sysfs entries are handled by class itself.
- Updated the method to add/remove devices for the class
- Comments on uevent send
- Able to become a module
- Compatible with Android platform

Changes from RFC
- Renamed to extcon (external connector) from multistate switch
- Added a seperated directory (drivers/extcon)
- Added kerneldoc comments
- Removed unused variables from extcon_gpio.c
- Added ABI Documentation.
---
 Documentation/ABI/testing/sysfs-class-extcon |   26 +++
 drivers/Kconfig                              |    2 +
 drivers/Makefile                             |    1 +
 drivers/extcon/Kconfig                       |   17 ++
 drivers/extcon/Makefile                      |    5 +
 drivers/extcon/extcon_class.c                |  236 ++++++++++++++++++++++++++
 include/linux/extcon.h                       |   82 +++++++++
 7 files changed, 369 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-class-extcon
 create mode 100644 drivers/extcon/Kconfig
 create mode 100644 drivers/extcon/Makefile
 create mode 100644 drivers/extcon/extcon_class.c
 create mode 100644 include/linux/extcon.h

diff --git a/Documentation/ABI/testing/sysfs-class-extcon b/Documentation/ABI/testing/sysfs-class-extcon
new file mode 100644
index 0000000..59a4b1c
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-extcon
@@ -0,0 +1,26 @@
+What:		/sys/class/extcon/.../
+Date:		December 2011
+Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
+Description:
+		Provide a place in sysfs for the extcon objects.
+		This allows accessing extcon specific variables.
+		The name of extcon object denoted as ... is the name given
+		with extcon_dev_register.
+
+What:		/sys/class/extcon/.../name
+Date:		December 2011
+Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
+Description:
+		The /sys/class/extcon/.../name shows the name of the extcon
+		object. If the extcon object has an optional callback
+		"show_name" defined, the callback will provide the name with
+		this sysfs node.
+
+What:		/sys/class/extcon/.../state
+Date:		December 2011
+Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
+Description:
+		The /sys/class/extcon/.../state shows the cable attach/detach
+		information of the corresponding extcon object. If the extcon
+		objecct has an optional callback "show_state" defined, the
+		callback will provide the name with this sysfs node.
diff --git a/drivers/Kconfig b/drivers/Kconfig
index d236aef..0233ad9 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -140,4 +140,6 @@ source "drivers/virt/Kconfig"
 
 source "drivers/devfreq/Kconfig"
 
+source "drivers/extcon/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 95952c8..c41dfa9 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -134,3 +134,4 @@ obj-$(CONFIG_VIRT_DRIVERS)	+= virt/
 obj-$(CONFIG_HYPERV)		+= hv/
 
 obj-$(CONFIG_PM_DEVFREQ)	+= devfreq/
+obj-$(CONFIG_EXTCON)		+= extcon/
diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig
new file mode 100644
index 0000000..7932e1b
--- /dev/null
+++ b/drivers/extcon/Kconfig
@@ -0,0 +1,17 @@
+menuconfig EXTCON
+	tristate "External Connector Class (extcon) support"
+	help
+	  Say Y here to enable external connector class (extcon) support.
+	  This allows monitoring external connectors by userspace
+	  via sysfs and uevent and supports external connectors with
+	  multiple states; i.e., an extcon that may have multiple
+	  cables attached. For example, an external connector of a device
+	  may be used to connect an HDMI cable and a AC adaptor, and to
+	  host USB ports. Many of 30-pin connectors including PDMI are
+	  also good examples.
+
+if EXTCON
+
+comment "Extcon Device Drivers"
+
+endif # MULTISTATE_SWITCH
diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile
new file mode 100644
index 0000000..6bc6921
--- /dev/null
+++ b/drivers/extcon/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for external connector class (extcon) devices
+#
+
+obj-$(CONFIG_EXTCON)		+= extcon_class.o
diff --git a/drivers/extcon/extcon_class.c b/drivers/extcon/extcon_class.c
new file mode 100644
index 0000000..3c46368
--- /dev/null
+++ b/drivers/extcon/extcon_class.c
@@ -0,0 +1,236 @@
+/*
+ *  drivers/extcon/extcon_class.c
+ *
+ *  External connector (extcon) class driver
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Author: Donggeun Kim <dg77.kim@samsung.com>
+ * Author: MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * based on android/drivers/switch/switch_class.c
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+*/
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/extcon.h>
+#include <linux/slab.h>
+
+struct class *extcon_class;
+#if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
+static struct class_compat *switch_class;
+#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
+
+static ssize_t state_show(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
+
+	if (edev->print_state) {
+		int ret = edev->print_state(edev, buf);
+
+		if (ret >= 0)
+			return ret;
+		/* Use default if failed */
+	}
+	return sprintf(buf, "%u\n", edev->state);
+}
+
+static ssize_t name_show(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
+
+	/* Optional callback given by the user */
+	if (edev->print_name) {
+		int ret = edev->print_name(edev, buf);
+		if (ret >= 0)
+			return ret;
+	}
+
+	return sprintf(buf, "%s\n", dev_name(edev->dev));
+}
+
+/**
+ * extcon_set_state() - Set the cable attach states of the extcon device.
+ * @edev:	the extcon device
+ * @state:	new cable attach status for @edev
+ *
+ * Changing the state sends uevent with environment variable containing
+ * the name of extcon device (envp[0]) and the state output (envp[1]).
+ * Tizen uses this format for extcon device to get events from ports.
+ * Android uses this format as well.
+ */
+void extcon_set_state(struct extcon_dev *edev, u32 state)
+{
+	char name_buf[120];
+	char state_buf[120];
+	char *prop_buf;
+	char *envp[3];
+	int env_offset = 0;
+	int length;
+
+	if (edev->state != state) {
+		edev->state = state;
+
+		prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
+		if (prop_buf) {
+			length = name_show(edev->dev, NULL, prop_buf);
+			if (length > 0) {
+				if (prop_buf[length - 1] == '\n')
+					prop_buf[length - 1] = 0;
+				snprintf(name_buf, sizeof(name_buf),
+					"NAME=%s", prop_buf);
+				envp[env_offset++] = name_buf;
+			}
+			length = state_show(edev->dev, NULL, prop_buf);
+			if (length > 0) {
+				if (prop_buf[length - 1] == '\n')
+					prop_buf[length - 1] = 0;
+				snprintf(state_buf, sizeof(state_buf),
+					"STATE=%s", prop_buf);
+				envp[env_offset++] = state_buf;
+			}
+			envp[env_offset] = NULL;
+			kobject_uevent_env(&edev->dev->kobj, KOBJ_CHANGE, envp);
+			free_page((unsigned long)prop_buf);
+		} else {
+			dev_err(edev->dev, "out of memory in extcon_set_state\n");
+			kobject_uevent(&edev->dev->kobj, KOBJ_CHANGE);
+		}
+	}
+}
+EXPORT_SYMBOL_GPL(extcon_set_state);
+
+static struct device_attribute extcon_attrs[] = {
+	__ATTR_RO(state),
+	__ATTR_RO(name),
+};
+
+static int create_extcon_class(void)
+{
+	if (!extcon_class) {
+		extcon_class = class_create(THIS_MODULE, "extcon");
+		if (IS_ERR(extcon_class))
+			return PTR_ERR(extcon_class);
+		extcon_class->dev_attrs = extcon_attrs;
+
+#if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
+		switch_class = class_compat_register("switch");
+		if (WARN(!switch_class, "cannot allocate"))
+			return -ENOMEM;
+#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
+	}
+
+	return 0;
+}
+
+static void extcon_cleanup(struct extcon_dev *edev, bool skip)
+{
+	if (!skip && get_device(edev->dev)) {
+		device_unregister(edev->dev);
+		put_device(edev->dev);
+	}
+
+	kfree(edev->dev);
+}
+
+static void extcon_dev_release(struct device *dev)
+{
+	struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
+
+	extcon_cleanup(edev, true);
+}
+
+/**
+ * extcon_dev_register() - Register a new extcon device
+ * @edev	: the new extcon device (should be allocated before calling)
+ * @dev		: the parent device for this extcon device.
+ *
+ * Among the members of edev struct, please set the "user initializing data"
+ * in any case and set the "optional callbacks" if required. However, please
+ * do not set the values of "internal data", which are initialized by
+ * this function.
+ */
+int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
+{
+	int ret;
+
+	if (!extcon_class) {
+		ret = create_extcon_class();
+		if (ret < 0)
+			return ret;
+	}
+
+	edev->dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+	edev->dev->parent = dev;
+	edev->dev->class = extcon_class;
+	edev->dev->release = extcon_dev_release;
+
+	dev_set_name(edev->dev, edev->name ? edev->name : dev_name(dev));
+	ret = device_register(edev->dev);
+	if (ret) {
+		put_device(edev->dev);
+		goto err_dev;
+	}
+#if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
+	if (switch_class)
+		ret = class_compat_create_link(switch_class, edev->dev,
+					       dev);
+#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
+
+	dev_set_drvdata(edev->dev, edev);
+	edev->state = 0;
+	return 0;
+
+err_dev:
+	kfree(edev->dev);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(extcon_dev_register);
+
+/**
+ * extcon_dev_unregister() - Unregister the extcon device.
+ * @edev:	the extcon device instance to be unregitered.
+ *
+ * Note that this does not call kfree(edev) because edev was not allocated
+ * by this class.
+ */
+void extcon_dev_unregister(struct extcon_dev *edev)
+{
+	extcon_cleanup(edev, false);
+}
+EXPORT_SYMBOL_GPL(extcon_dev_unregister);
+
+static int __init extcon_class_init(void)
+{
+	return create_extcon_class();
+}
+module_init(extcon_class_init);
+
+static void __exit extcon_class_exit(void)
+{
+	class_destroy(extcon_class);
+}
+module_exit(extcon_class_exit);
+
+MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
+MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
+MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
+MODULE_DESCRIPTION("External connector (extcon) class driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/extcon.h b/include/linux/extcon.h
new file mode 100644
index 0000000..9cb3aaa
--- /dev/null
+++ b/include/linux/extcon.h
@@ -0,0 +1,82 @@
+/*
+ *  External connector (extcon) class driver
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Author: Donggeun Kim <dg77.kim@samsung.com>
+ * Author: MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * based on switch class driver
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+*/
+
+#ifndef __LINUX_EXTCON_H__
+#define __LINUX_EXTCON_H__
+
+/**
+ * struct extcon_dev - An extcon device represents one external connector.
+ * @name	The name of this extcon device. Parent device name is used
+ *		if NULL.
+ * @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
+ *		status of the extcon device.
+ * @dev		Device of this extcon. Do not provide at register-time.
+ * @state	Attach/detach state of this extcon. Do not provide at
+ *		register-time
+ *
+ * In most cases, users only need to provide "User initializing data" of
+ * this struct when registering an extcon. In some exceptional cases,
+ * optional callbacks may be needed. However, the values in "internal data"
+ * are overwritten by register function.
+ */
+struct extcon_dev {
+	/* --- Optional user initializing data --- */
+	const char	*name;
+
+	/* --- Optional callbacks to override class functions --- */
+	ssize_t	(*print_name)(struct extcon_dev *edev, char *buf);
+	ssize_t	(*print_state)(struct extcon_dev *edev, char *buf);
+
+	/* --- Internal data. Please do not set. --- */
+	struct device	*dev;
+	u32		state;
+};
+
+#if IS_ENABLED(CONFIG_EXTCON)
+extern int extcon_dev_register(struct extcon_dev *edev, struct device *dev);
+extern void extcon_dev_unregister(struct extcon_dev *edev);
+
+static inline u32 extcon_get_state(struct extcon_dev *edev)
+{
+	return edev->state;
+}
+
+extern void extcon_set_state(struct extcon_dev *edev, u32 state);
+#else /* CONFIG_EXTCON */
+static inline int extcon_dev_register(struct extcon_dev *edev,
+				      struct device *dev)
+{
+	return 0;
+}
+
+static inline void extcon_dev_unregister(struct extcon_dev *edev) { }
+
+static inline u32 extcon_get_state(struct extcon_dev *edev)
+{
+	return 0;
+}
+
+static inline void extcon_set_state(struct extcon_dev *edev, u32 state) { }
+#endif /* CONFIG_EXTCON */
+#endif /* __LINUX_EXTCON_H__ */
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH v8 resend 2/6] Extcon: support generic GPIO extcon driver
  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  5:16                         ` MyungJoo Ham
  2012-04-20  5:16                         ` [PATCH v8 resend 3/6] Extcon: support notification based on the state changes MyungJoo Ham
                                           ` (4 subsequent siblings)
  6 siblings, 0 replies; 51+ messages in thread
From: MyungJoo Ham @ 2012-04-20  5:16 UTC (permalink / raw)
  To: Greg KH
  Cc: Arnd Bergmann, LKML, Arve Hjønnevag, Kyungmin Park,
	Linus Walleij, Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown,
	John Stultz, myungjoo.ham, cw00.choi

The generic GPIO extcon driver (an external connector device based on
GPIO control) and imported from Android kernel.

switch: switch class and GPIO drivers. (splitted)
Author: Mike Lockwood <lockwood@android.com>

switch: gpio: Don't call request_irq with interrupts disabled
Author: Arve Hjønnevåg <arve@android.com>

switch_gpio: Add missing #include <linux/interrupt.h>
Author: Mike Lockwood <lockwood@android.com>

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>

--
Changed from v7:
- Style updates mentioned by Stephen Boyd and Mark Brown
Changed from v5:
- Splitted at v5 from the main extcon patch.
- Added debounce time for irq handlers.
- Use request_any_context_irq instead of request_irq
- User needs to specify irq flags for GPIO interrupts (was fixed to
IRQF_TRIGGER_LOW before)
- Use module_platform_driver().
---
 drivers/extcon/Kconfig             |    7 ++
 drivers/extcon/Makefile            |    1 +
 drivers/extcon/extcon_gpio.c       |  169 ++++++++++++++++++++++++++++++++++++
 include/linux/extcon/extcon_gpio.h |   52 +++++++++++
 4 files changed, 229 insertions(+), 0 deletions(-)
 create mode 100644 drivers/extcon/extcon_gpio.c
 create mode 100644 include/linux/extcon/extcon_gpio.h

diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig
index 7932e1b..85cecff 100644
--- a/drivers/extcon/Kconfig
+++ b/drivers/extcon/Kconfig
@@ -14,4 +14,11 @@ if EXTCON
 
 comment "Extcon Device Drivers"
 
+config EXTCON_GPIO
+	tristate "GPIO extcon support"
+	depends on GENERIC_GPIO
+	help
+	  Say Y here to enable GPIO based extcon support. Note that GPIO
+	  extcon supports single state per extcon instance.
+
 endif # MULTISTATE_SWITCH
diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile
index 6bc6921..2c46d41 100644
--- a/drivers/extcon/Makefile
+++ b/drivers/extcon/Makefile
@@ -3,3 +3,4 @@
 #
 
 obj-$(CONFIG_EXTCON)		+= extcon_class.o
+obj-$(CONFIG_EXTCON_GPIO)	+= extcon_gpio.o
diff --git a/drivers/extcon/extcon_gpio.c b/drivers/extcon/extcon_gpio.c
new file mode 100644
index 0000000..5c0f085
--- /dev/null
+++ b/drivers/extcon/extcon_gpio.c
@@ -0,0 +1,169 @@
+/*
+ *  drivers/extcon/extcon_gpio.c
+ *
+ *  Single-state GPIO extcon driver based on extcon class
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * Modified by MyungJoo Ham <myungjoo.ham@samsung.com> to support extcon
+ * (originally switch class is supported)
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/extcon.h>
+#include <linux/workqueue.h>
+#include <linux/gpio.h>
+#include <linux/extcon.h>
+#include <linux/extcon/extcon_gpio.h>
+
+struct gpio_extcon_data {
+	struct extcon_dev edev;
+	unsigned gpio;
+	const char *state_on;
+	const char *state_off;
+	int irq;
+	struct delayed_work work;
+	unsigned long debounce_jiffies;
+};
+
+static void gpio_extcon_work(struct work_struct *work)
+{
+	int state;
+	struct gpio_extcon_data	*data =
+		container_of(to_delayed_work(work), struct gpio_extcon_data,
+			     work);
+
+	state = gpio_get_value(data->gpio);
+	extcon_set_state(&data->edev, state);
+}
+
+static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
+{
+	struct gpio_extcon_data *extcon_data = dev_id;
+
+	schedule_delayed_work(&extcon_data->work,
+			      extcon_data->debounce_jiffies);
+	return IRQ_HANDLED;
+}
+
+static ssize_t extcon_gpio_print_state(struct extcon_dev *edev, char *buf)
+{
+	struct gpio_extcon_data	*extcon_data =
+		container_of(edev, struct gpio_extcon_data, edev);
+	const char *state;
+	if (extcon_get_state(edev))
+		state = extcon_data->state_on;
+	else
+		state = extcon_data->state_off;
+
+	if (state)
+		return sprintf(buf, "%s\n", state);
+	return -EINVAL;
+}
+
+static int __devinit gpio_extcon_probe(struct platform_device *pdev)
+{
+	struct gpio_extcon_platform_data *pdata = pdev->dev.platform_data;
+	struct gpio_extcon_data *extcon_data;
+	int ret = 0;
+
+	if (!pdata)
+		return -EBUSY;
+	if (!pdata->irq_flags) {
+		dev_err(&pdev->dev, "IRQ flag is not specified.\n");
+		return -EINVAL;
+	}
+
+	extcon_data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_extcon_data),
+				   GFP_KERNEL);
+	if (!extcon_data)
+		return -ENOMEM;
+
+	extcon_data->edev.name = pdata->name;
+	extcon_data->gpio = pdata->gpio;
+	extcon_data->state_on = pdata->state_on;
+	extcon_data->state_off = pdata->state_off;
+	if (pdata->state_on && pdata->state_off)
+		extcon_data->edev.print_state = extcon_gpio_print_state;
+	extcon_data->debounce_jiffies = msecs_to_jiffies(pdata->debounce);
+
+	ret = extcon_dev_register(&extcon_data->edev, &pdev->dev);
+	if (ret < 0)
+		goto err_extcon_dev_register;
+
+	ret = gpio_request_one(extcon_data->gpio, GPIOF_DIR_IN, pdev->name);
+	if (ret < 0)
+		goto err_request_gpio;
+
+	INIT_DELAYED_WORK(&extcon_data->work, gpio_extcon_work);
+
+	extcon_data->irq = gpio_to_irq(extcon_data->gpio);
+	if (extcon_data->irq < 0) {
+		ret = extcon_data->irq;
+		goto err_detect_irq_num_failed;
+	}
+
+	ret = request_any_context_irq(extcon_data->irq, gpio_irq_handler,
+				      pdata->irq_flags, pdev->name,
+				      extcon_data);
+	if (ret < 0)
+		goto err_request_irq;
+
+	/* Perform initial detection */
+	gpio_extcon_work(&extcon_data->work.work);
+
+	return 0;
+
+err_request_irq:
+err_detect_irq_num_failed:
+	gpio_free(extcon_data->gpio);
+err_request_gpio:
+	extcon_dev_unregister(&extcon_data->edev);
+err_extcon_dev_register:
+	devm_kfree(&pdev->dev, extcon_data);
+
+	return ret;
+}
+
+static int __devexit gpio_extcon_remove(struct platform_device *pdev)
+{
+	struct gpio_extcon_data *extcon_data = platform_get_drvdata(pdev);
+
+	cancel_delayed_work_sync(&extcon_data->work);
+	gpio_free(extcon_data->gpio);
+	extcon_dev_unregister(&extcon_data->edev);
+	devm_kfree(&pdev->dev, extcon_data);
+
+	return 0;
+}
+
+static struct platform_driver gpio_extcon = {
+	.probe		= gpio_extcon_probe,
+	.remove		= __devexit_p(gpio_extcon_remove),
+	.driver		= {
+		.name	= "extcon-gpio",
+		.owner	= THIS_MODULE,
+	},
+};
+
+module_platform_driver(gpio_extcon);
+
+MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
+MODULE_DESCRIPTION("GPIO extcon driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/extcon/extcon_gpio.h b/include/linux/extcon/extcon_gpio.h
new file mode 100644
index 0000000..a2129b7
--- /dev/null
+++ b/include/linux/extcon/extcon_gpio.h
@@ -0,0 +1,52 @@
+/*
+ *  External connector (extcon) class generic GPIO driver
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Author: MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * based on switch class driver
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+*/
+#ifndef __EXTCON_GPIO_H__
+#define __EXTCON_GPIO_H__ __FILE__
+
+#include <linux/extcon.h>
+
+/**
+ * struct gpio_extcon_platform_data - A simple GPIO-controlled extcon device.
+ * @name	The name of this GPIO extcon device.
+ * @gpio	Corresponding GPIO.
+ * @debounce	Debounce time for GPIO IRQ in ms.
+ * @irq_flags	IRQ Flags (e.g., IRQF_TRIGGER_LOW).
+ * @state_on	print_state is overriden with state_on if attached. If Null,
+ *		default method of extcon class is used.
+ * @state_off	print_state is overriden with state_on if dettached. If Null,
+ *		default method of extcon class is used.
+ *
+ * Note that in order for state_on or state_off to be valid, both state_on
+ * and state_off should be not NULL. If at least one of them is NULL,
+ * the print_state is not overriden.
+ */
+struct gpio_extcon_platform_data {
+	const char *name;
+	unsigned gpio;
+	unsigned long debounce;
+	unsigned long irq_flags;
+
+	/* if NULL, "0" or "1" will be printed */
+	const char *state_on;
+	const char *state_off;
+};
+
+#endif /* __EXTCON_GPIO_H__ */
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH v8 resend 3/6] Extcon: support notification based on the state changes.
  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  5:16                         ` [PATCH v8 resend 2/6] Extcon: support generic GPIO extcon driver MyungJoo Ham
@ 2012-04-20  5:16                         ` MyungJoo Ham
  2012-04-20  5:16                         ` [PATCH v8 resend 4/6] Extcon: support multiple states at a device MyungJoo Ham
                                           ` (3 subsequent siblings)
  6 siblings, 0 replies; 51+ messages in thread
From: MyungJoo Ham @ 2012-04-20  5:16 UTC (permalink / raw)
  To: Greg KH
  Cc: Arnd Bergmann, LKML, Arve Hjønnevag, Kyungmin Park,
	Linus Walleij, Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown,
	John Stultz, myungjoo.ham, cw00.choi

From: Donggeun Kim <dg77.kim@samsung.com>

State changes of extcon devices have been notified via kobjet_uevent.
This patch adds notifier interfaces in order to allow device drivers to
get notified easily. Along with notifier interface,
extcon_get_extcon_dev() function is added so that device drivers may
discover a extcon_dev easily.

Signed-off-by: Donggeun Kim <dg77.kim@samsung.com>
Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>

--
Changes from RFC
- Renamed switch to extcon
- Bugfix: extcon_dev_unregister()
- Bugfix: "edev->dev" is "internal" data.
- Added kerneldoc comments.
- Reworded comments.
---
 drivers/extcon/extcon_class.c |   66 +++++++++++++++++++++++++++++++++++++++++
 include/linux/extcon.h        |   38 +++++++++++++++++++++++
 2 files changed, 104 insertions(+), 0 deletions(-)

diff --git a/drivers/extcon/extcon_class.c b/drivers/extcon/extcon_class.c
index 3c46368..83a088f 100644
--- a/drivers/extcon/extcon_class.c
+++ b/drivers/extcon/extcon_class.c
@@ -36,6 +36,9 @@ struct class *extcon_class;
 static struct class_compat *switch_class;
 #endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
 
+static LIST_HEAD(extcon_dev_list);
+static DEFINE_MUTEX(extcon_dev_list_lock);
+
 static ssize_t state_show(struct device *dev, struct device_attribute *attr,
 			  char *buf)
 {
@@ -75,6 +78,9 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr,
  * the name of extcon device (envp[0]) and the state output (envp[1]).
  * Tizen uses this format for extcon device to get events from ports.
  * Android uses this format as well.
+ *
+ * Note that notifier provides the which bits are changes in the state
+ * variable with "val" to the callback.
  */
 void extcon_set_state(struct extcon_dev *edev, u32 state)
 {
@@ -84,10 +90,14 @@ void extcon_set_state(struct extcon_dev *edev, u32 state)
 	char *envp[3];
 	int env_offset = 0;
 	int length;
+	u32 old_state = edev->state;
 
 	if (edev->state != state) {
 		edev->state = state;
 
+		raw_notifier_call_chain(&edev->nh, old_state ^ edev->state,
+					edev);
+
 		prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
 		if (prop_buf) {
 			length = name_show(edev->dev, NULL, prop_buf);
@@ -117,6 +127,51 @@ void extcon_set_state(struct extcon_dev *edev, u32 state)
 }
 EXPORT_SYMBOL_GPL(extcon_set_state);
 
+/**
+ * extcon_get_extcon_dev() - Get the extcon device instance from the name
+ * @extcon_name:	The extcon name provided with extcon_dev_register()
+ */
+struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
+{
+	struct extcon_dev *sd;
+
+	mutex_lock(&extcon_dev_list_lock);
+	list_for_each_entry(sd, &extcon_dev_list, entry) {
+		if (!strcmp(sd->name, extcon_name))
+			goto out;
+	}
+	sd = NULL;
+out:
+	mutex_unlock(&extcon_dev_list_lock);
+	return sd;
+}
+EXPORT_SYMBOL_GPL(extcon_get_extcon_dev);
+
+/**
+ * extcon_register_notifier() - Register a notifee to get notified by
+ *			      any attach status changes from the extcon.
+ * @edev:	the extcon device.
+ * @nb:		a notifier block to be registered.
+ */
+int extcon_register_notifier(struct extcon_dev *edev,
+			struct notifier_block *nb)
+{
+	return raw_notifier_chain_register(&edev->nh, nb);
+}
+EXPORT_SYMBOL_GPL(extcon_register_notifier);
+
+/**
+ * extcon_unregister_notifier() - Unregister a notifee from the extcon device.
+ * @edev:	the extcon device.
+ * @nb:		a registered notifier block to be unregistered.
+ */
+int extcon_unregister_notifier(struct extcon_dev *edev,
+			struct notifier_block *nb)
+{
+	return raw_notifier_chain_unregister(&edev->nh, nb);
+}
+EXPORT_SYMBOL_GPL(extcon_unregister_notifier);
+
 static struct device_attribute extcon_attrs[] = {
 	__ATTR_RO(state),
 	__ATTR_RO(name),
@@ -142,6 +197,10 @@ static int create_extcon_class(void)
 
 static void extcon_cleanup(struct extcon_dev *edev, bool skip)
 {
+	mutex_lock(&extcon_dev_list_lock);
+	list_del(&edev->entry);
+	mutex_unlock(&extcon_dev_list_lock);
+
 	if (!skip && get_device(edev->dev)) {
 		device_unregister(edev->dev);
 		put_device(edev->dev);
@@ -194,8 +253,15 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
 					       dev);
 #endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
 
+	RAW_INIT_NOTIFIER_HEAD(&edev->nh);
+
 	dev_set_drvdata(edev->dev, edev);
 	edev->state = 0;
+
+	mutex_lock(&extcon_dev_list_lock);
+	list_add(&edev->entry, &extcon_dev_list);
+	mutex_unlock(&extcon_dev_list_lock);
+
 	return 0;
 
 err_dev:
diff --git a/include/linux/extcon.h b/include/linux/extcon.h
index 9cb3aaa..c9c9afe 100644
--- a/include/linux/extcon.h
+++ b/include/linux/extcon.h
@@ -23,6 +23,7 @@
 #ifndef __LINUX_EXTCON_H__
 #define __LINUX_EXTCON_H__
 
+#include <linux/notifier.h>
 /**
  * struct extcon_dev - An extcon device represents one external connector.
  * @name	The name of this extcon device. Parent device name is used
@@ -34,6 +35,9 @@
  * @dev		Device of this extcon. Do not provide at register-time.
  * @state	Attach/detach state of this extcon. Do not provide at
  *		register-time
+ * @nh	Notifier for the state change events from this extcon
+ * @entry	To support list of extcon devices so that uses can search
+ *		for extcon devices based on the extcon name.
  *
  * In most cases, users only need to provide "User initializing data" of
  * this struct when registering an extcon. In some exceptional cases,
@@ -51,11 +55,19 @@ struct extcon_dev {
 	/* --- Internal data. Please do not set. --- */
 	struct device	*dev;
 	u32		state;
+	struct raw_notifier_head nh;
+	struct list_head entry;
 };
 
 #if IS_ENABLED(CONFIG_EXTCON)
+
+/*
+ * Following APIs are for notifiers or configurations.
+ * Notifiers are the external port and connection devices.
+ */
 extern int extcon_dev_register(struct extcon_dev *edev, struct device *dev);
 extern void extcon_dev_unregister(struct extcon_dev *edev);
+extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name);
 
 static inline u32 extcon_get_state(struct extcon_dev *edev)
 {
@@ -63,6 +75,15 @@ static inline u32 extcon_get_state(struct extcon_dev *edev)
 }
 
 extern void extcon_set_state(struct extcon_dev *edev, u32 state);
+
+/*
+ * Following APIs are to monitor every action of a notifier.
+ * Registerer gets notified for every external port of a connection device.
+ */
+extern int extcon_register_notifier(struct extcon_dev *edev,
+				    struct notifier_block *nb);
+extern int extcon_unregister_notifier(struct extcon_dev *edev,
+				      struct notifier_block *nb);
 #else /* CONFIG_EXTCON */
 static inline int extcon_dev_register(struct extcon_dev *edev,
 				      struct device *dev)
@@ -78,5 +99,22 @@ static inline u32 extcon_get_state(struct extcon_dev *edev)
 }
 
 static inline void extcon_set_state(struct extcon_dev *edev, u32 state) { }
+static inline struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
+{
+	return NULL;
+}
+
+static inline int extcon_register_notifier(struct extcon_dev *edev,
+					   struct notifier_block *nb)
+{
+	return 0;
+}
+
+static inline int extcon_unregister_notifier(struct extcon_dev *edev,
+					     struct notifier_block *nb)
+{
+	return 0;
+}
+
 #endif /* CONFIG_EXTCON */
 #endif /* __LINUX_EXTCON_H__ */
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH v8 resend 4/6] Extcon: support multiple states at a device.
  2012-04-20  5:16                       ` [PATCH v8 resend 0/6] Introduce External Connector Class (extcon) MyungJoo Ham
                                           ` (2 preceding siblings ...)
  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                         ` MyungJoo Ham
  2012-04-20  5:16                         ` [PATCH v8 resend 5/6] Extcon: support mutually exclusive relation between cables MyungJoo Ham
                                           ` (2 subsequent siblings)
  6 siblings, 0 replies; 51+ messages in thread
From: MyungJoo Ham @ 2012-04-20  5:16 UTC (permalink / raw)
  To: Greg KH
  Cc: Arnd Bergmann, LKML, Arve Hjønnevag, Kyungmin Park,
	Linus Walleij, Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown,
	John Stultz, myungjoo.ham, cw00.choi

One switch device (e.g., MUIC(MAX8997, MAX77686, ...), and some 30-pin
devices) may have multiple cables attached. For example, one
30-pin port may inhabit a USB cable, an HDMI cable, and a mic.
Thus, one switch device requires multiple state bits each representing
a type of cable.

For such purpose, we use the 32bit state variable; thus, up to 32
different type of cables may be defined for a switch device. The list of
possible cables is defined by the array of cable names in the switch_dev
struct given to the class.

Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>

--
Changes from V7
- Bugfixed in _call_per_cable() (incorrect nb) (Chanwoo Choi)
- Compiler error in header for !CONFIG_EXTCON (Chanwoo Choi)

Changes from V5
- Sysfs style reformed: subdirectory per cable.
- Updated standard cable names
- Removed unnecessary printf
- Bugfixes after testing

Changes from V4
- Bugfixes after more testing at Exynos4412 boards with userspace
  processses.

Changes from V3
- Bugfixes after more testing at Exynos4412 boards.

Changes from V2
- State can be stored by user
- Documentation updated

Changes from RFC
- Switch is renamed to extcon
- Added kerneldoc comments
- Added APIs to support "standard" cable names
- Added helper APIs to support notifier block registration with cable
  name.
- Regrouped function list in the header file.
---
 Documentation/ABI/testing/sysfs-class-extcon |   63 ++++-
 drivers/extcon/extcon_class.c                |  439 +++++++++++++++++++++++++-
 include/linux/extcon.h                       |  185 +++++++++++
 3 files changed, 667 insertions(+), 20 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-class-extcon b/Documentation/ABI/testing/sysfs-class-extcon
index 59a4b1c..19b18e9 100644
--- a/Documentation/ABI/testing/sysfs-class-extcon
+++ b/Documentation/ABI/testing/sysfs-class-extcon
@@ -1,5 +1,5 @@
 What:		/sys/class/extcon/.../
-Date:		December 2011
+Date:		February 2012
 Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
 Description:
 		Provide a place in sysfs for the extcon objects.
@@ -7,8 +7,16 @@ Description:
 		The name of extcon object denoted as ... is the name given
 		with extcon_dev_register.
 
+		One extcon device denotes a single external connector
+		port. An external connector may have multiple cables
+		attached simultaneously. Many of docks, cradles, and
+		accessory cables have such capability. For example,
+		the 30-pin port of Nuri board (/arch/arm/mach-exynos)
+		may have both HDMI and Charger attached, or analog audio,
+		video, and USB cables attached simulteneously.
+
 What:		/sys/class/extcon/.../name
-Date:		December 2011
+Date:		February 2012
 Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
 Description:
 		The /sys/class/extcon/.../name shows the name of the extcon
@@ -17,10 +25,51 @@ Description:
 		this sysfs node.
 
 What:		/sys/class/extcon/.../state
-Date:		December 2011
+Date:		February 2012
+Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
+Description:
+		The /sys/class/extcon/.../state shows and stores the cable
+		attach/detach information of the corresponding extcon object.
+		If the extcon object has an optional callback "show_state"
+		defined, the showing function is overriden with the optional
+		callback.
+
+		If the default callback for showing function is used, the
+		format is like this:
+		# cat state
+		USB_OTG=1
+		HDMI=0
+		TA=1
+		EAR_JACK=0
+		#
+		In this example, the extcon device have USB_OTG and TA
+		cables attached and HDMI and EAR_JACK cables detached.
+
+		In order to update the state of an extcon device, enter a hex
+		state number starting with 0x.
+		 echo 0xHEX > state
+
+		This updates the whole state of the extcon dev.
+		Inputs of all the methods are required to meet the
+		mutually_exclusive contidions if they exist.
+
+		It is recommended to use this "global" state interface if
+		you need to enter the value atomically. The later state
+		interface associated with each cable cannot update
+		multiple cable states of an extcon device simultaneously.
+
+What:		/sys/class/extcon/.../cable.x/name
+Date:		February 2012
+Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
+Description:
+		The /sys/class/extcon/.../cable.x/name shows the name of cable
+		"x" (integer between 0 and 31) of an extcon device.
+
+What:		/sys/class/extcon/.../cable.x/state
+Date:		February 2012
 Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
 Description:
-		The /sys/class/extcon/.../state shows the cable attach/detach
-		information of the corresponding extcon object. If the extcon
-		objecct has an optional callback "show_state" defined, the
-		callback will provide the name with this sysfs node.
+		The /sys/class/extcon/.../cable.x/name shows and stores the
+		state of cable "x" (integer between 0 and 31) of an extcon
+		device. The state value is either 0 (detached) or 1
+		(attached).
diff --git a/drivers/extcon/extcon_class.c b/drivers/extcon/extcon_class.c
index 83a088f..403933b 100644
--- a/drivers/extcon/extcon_class.c
+++ b/drivers/extcon/extcon_class.c
@@ -31,6 +31,39 @@
 #include <linux/extcon.h>
 #include <linux/slab.h>
 
+/*
+ * extcon_cable_name suggests the standard cable names for commonly used
+ * cable types.
+ *
+ * However, please do not use extcon_cable_name directly for extcon_dev
+ * struct's supported_cable pointer unless your device really supports
+ * every single port-type of the following cable names. Please choose cable
+ * names that are actually used in your extcon device.
+ */
+const char *extcon_cable_name[] = {
+	[EXTCON_USB]		= "USB",
+	[EXTCON_USB_HOST]	= "USB-Host",
+	[EXTCON_TA]		= "TA",
+	[EXTCON_FAST_CHARGER]	= "Fast-charger",
+	[EXTCON_SLOW_CHARGER]	= "Slow-charger",
+	[EXTCON_CHARGE_DOWNSTREAM]	= "Charge-downstream",
+	[EXTCON_HDMI]		= "HDMI",
+	[EXTCON_MHL]		= "MHL",
+	[EXTCON_DVI]		= "DVI",
+	[EXTCON_VGA]		= "VGA",
+	[EXTCON_DOCK]		= "Dock",
+	[EXTCON_LINE_IN]	= "Line-in",
+	[EXTCON_LINE_OUT]	= "Line-out",
+	[EXTCON_MIC_IN]		= "Microphone",
+	[EXTCON_HEADPHONE_OUT]	= "Headphone",
+	[EXTCON_SPDIF_IN]	= "SPDIF-in",
+	[EXTCON_SPDIF_OUT]	= "SPDIF-out",
+	[EXTCON_VIDEO_IN]	= "Video-in",
+	[EXTCON_VIDEO_OUT]	= "Video-out",
+
+	NULL,
+};
+
 struct class *extcon_class;
 #if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
 static struct class_compat *switch_class;
@@ -42,6 +75,7 @@ static DEFINE_MUTEX(extcon_dev_list_lock);
 static ssize_t state_show(struct device *dev, struct device_attribute *attr,
 			  char *buf)
 {
+	int i, count = 0;
 	struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
 
 	if (edev->print_state) {
@@ -51,7 +85,39 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,
 			return ret;
 		/* Use default if failed */
 	}
-	return sprintf(buf, "%u\n", edev->state);
+
+	if (edev->max_supported == 0)
+		return sprintf(buf, "%u\n", edev->state);
+
+	for (i = 0; i < SUPPORTED_CABLE_MAX; i++) {
+		if (!edev->supported_cable[i])
+			break;
+		count += sprintf(buf + count, "%s=%d\n",
+				 edev->supported_cable[i],
+				 !!(edev->state & (1 << i)));
+	}
+
+	return count;
+}
+
+void 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)
+{
+	u32 state;
+	ssize_t ret = 0;
+	struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
+
+	ret = sscanf(buf, "0x%x", &state);
+	if (ret == 0)
+		ret = -EINVAL;
+	else
+		extcon_set_state(edev, state);
+
+	if (ret < 0)
+		return ret;
+
+	return count;
 }
 
 static ssize_t name_show(struct device *dev, struct device_attribute *attr,
@@ -69,9 +135,52 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr,
 	return sprintf(buf, "%s\n", dev_name(edev->dev));
 }
 
+static ssize_t cable_name_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct extcon_cable *cable = container_of(attr, struct extcon_cable,
+						  attr_name);
+
+	return sprintf(buf, "%s\n",
+		       cable->edev->supported_cable[cable->cable_index]);
+}
+
+static ssize_t cable_state_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct extcon_cable *cable = container_of(attr, struct extcon_cable,
+						  attr_state);
+
+	return sprintf(buf, "%d\n",
+		       extcon_get_cable_state_(cable->edev,
+					       cable->cable_index));
+}
+
+static ssize_t cable_state_store(struct device *dev,
+				 struct device_attribute *attr, const char *buf,
+				 size_t count)
+{
+	struct extcon_cable *cable = container_of(attr, struct extcon_cable,
+						  attr_state);
+	int ret, state;
+
+	ret = sscanf(buf, "%d", &state);
+	if (ret == 0)
+		ret = -EINVAL;
+	else
+		ret = extcon_set_cable_state_(cable->edev, cable->cable_index,
+					      state);
+
+	if (ret < 0)
+		return ret;
+	return count;
+}
+
 /**
- * extcon_set_state() - Set the cable attach states of the extcon device.
+ * extcon_update_state() - Update the cable attach states of the extcon device
+ *			only for the masked bits.
  * @edev:	the extcon device
+ * @mask:	the bit mask to designate updated bits.
  * @state:	new cable attach status for @edev
  *
  * Changing the state sends uevent with environment variable containing
@@ -79,10 +188,10 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr,
  * Tizen uses this format for extcon device to get events from ports.
  * Android uses this format as well.
  *
- * Note that notifier provides the which bits are changes in the state
- * variable with "val" to the callback.
+ * Note that the 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)
+void extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
 {
 	char name_buf[120];
 	char state_buf[120];
@@ -90,15 +199,20 @@ void extcon_set_state(struct extcon_dev *edev, u32 state)
 	char *envp[3];
 	int env_offset = 0;
 	int length;
-	u32 old_state = edev->state;
+	unsigned long flags;
 
-	if (edev->state != state) {
-		edev->state = state;
+	spin_lock_irqsave(&edev->lock, flags);
 
-		raw_notifier_call_chain(&edev->nh, old_state ^ edev->state,
-					edev);
+	if (edev->state != ((edev->state & ~mask) | (state & mask))) {
+		u32 old_state = edev->state;
 
-		prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
+		edev->state &= ~mask;
+		edev->state |= state & mask;
+
+		raw_notifier_call_chain(&edev->nh, old_state, edev);
+
+		/* This could be in interrupt handler */
+		prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
 		if (prop_buf) {
 			length = name_show(edev->dev, NULL, prop_buf);
 			if (length > 0) {
@@ -117,17 +231,132 @@ void extcon_set_state(struct extcon_dev *edev, u32 state)
 				envp[env_offset++] = state_buf;
 			}
 			envp[env_offset] = NULL;
+			/* Unlock early before uevent */
+			spin_unlock_irqrestore(&edev->lock, flags);
+
 			kobject_uevent_env(&edev->dev->kobj, KOBJ_CHANGE, envp);
 			free_page((unsigned long)prop_buf);
 		} else {
+			/* Unlock early before uevent */
+			spin_unlock_irqrestore(&edev->lock, flags);
+
 			dev_err(edev->dev, "out of memory in extcon_set_state\n");
 			kobject_uevent(&edev->dev->kobj, KOBJ_CHANGE);
 		}
+	} else {
+		/* No changes */
+		spin_unlock_irqrestore(&edev->lock, flags);
 	}
 }
+EXPORT_SYMBOL_GPL(extcon_update_state);
+
+/**
+ * extcon_set_state() - Set the cable attach states of the extcon device.
+ * @edev:	the extcon device
+ * @state:	new cable attach status for @edev
+ *
+ * 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)
+{
+	extcon_update_state(edev, 0xffffffff, state);
+}
 EXPORT_SYMBOL_GPL(extcon_set_state);
 
 /**
+ * extcon_find_cable_index() - Get the cable index based on the cable name.
+ * @edev:	the extcon device that has the cable.
+ * @cable_name:	cable name to be searched.
+ *
+ * Note that accessing a cable state based on cable_index is faster than
+ * cable_name because using cable_name induces a loop with strncmp().
+ * Thus, when get/set_cable_state is repeatedly used, using cable_index
+ * is recommended.
+ */
+int extcon_find_cable_index(struct extcon_dev *edev, const char *cable_name)
+{
+	int i;
+
+	if (edev->supported_cable) {
+		for (i = 0; edev->supported_cable[i]; i++) {
+			if (!strncmp(edev->supported_cable[i],
+				cable_name, CABLE_NAME_MAX))
+				return i;
+		}
+	}
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(extcon_find_cable_index);
+
+/**
+ * extcon_get_cable_state_() - Get the status of a specific cable.
+ * @edev:	the extcon device that has the cable.
+ * @index:	cable index that can be retrieved by extcon_find_cable_index().
+ */
+int extcon_get_cable_state_(struct extcon_dev *edev, int index)
+{
+	if (index < 0 || (edev->max_supported && edev->max_supported <= index))
+		return -EINVAL;
+
+	return !!(edev->state & (1 << index));
+}
+EXPORT_SYMBOL_GPL(extcon_get_cable_state_);
+
+/**
+ * extcon_get_cable_state() - Get the status of a specific cable.
+ * @edev:	the extcon device that has the cable.
+ * @cable_name:	cable name.
+ *
+ * Note that this is slower than extcon_get_cable_state_.
+ */
+int extcon_get_cable_state(struct extcon_dev *edev, const char *cable_name)
+{
+	return extcon_get_cable_state_(edev, extcon_find_cable_index
+						(edev, cable_name));
+}
+EXPORT_SYMBOL_GPL(extcon_get_cable_state);
+
+/**
+ * extcon_get_cable_state_() - Set the status of a specific cable.
+ * @edev:	the extcon device that has the cable.
+ * @index:	cable index that can be retrieved by extcon_find_cable_index().
+ * @cable_state:	the new cable status. The default semantics is
+ *			true: attached / false: detached.
+ */
+int extcon_set_cable_state_(struct extcon_dev *edev,
+			int index, bool cable_state)
+{
+	u32 state;
+
+	if (index < 0 || (edev->max_supported && edev->max_supported <= index))
+		return -EINVAL;
+
+	state = cable_state ? (1 << index) : 0;
+	extcon_update_state(edev, 1 << index, state);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(extcon_set_cable_state_);
+
+/**
+ * extcon_get_cable_state() - Set the status of a specific cable.
+ * @edev:	the extcon device that has the cable.
+ * @cable_name:	cable name.
+ * @cable_state:	the new cable status. The default semantics is
+ *			true: attached / false: detached.
+ *
+ * Note that this is slower than extcon_set_cable_state_.
+ */
+int extcon_set_cable_state(struct extcon_dev *edev,
+			const char *cable_name, bool cable_state)
+{
+	return extcon_set_cable_state_(edev, extcon_find_cable_index
+					(edev, cable_name), cable_state);
+}
+EXPORT_SYMBOL_GPL(extcon_set_cable_state);
+
+/**
  * extcon_get_extcon_dev() - Get the extcon device instance from the name
  * @extcon_name:	The extcon name provided with extcon_dev_register()
  */
@@ -147,11 +376,88 @@ out:
 }
 EXPORT_SYMBOL_GPL(extcon_get_extcon_dev);
 
+static int _call_per_cable(struct notifier_block *nb, unsigned long val,
+			   void *ptr)
+{
+	struct extcon_specific_cable_nb *obj = container_of(nb,
+			struct extcon_specific_cable_nb, internal_nb);
+	struct extcon_dev *edev = ptr;
+
+	if ((val & (1 << obj->cable_index)) !=
+	    (edev->state & (1 << obj->cable_index))) {
+		obj->previous_value = val;
+		return obj->user_nb->notifier_call(obj->user_nb, val, ptr);
+	}
+
+	return NOTIFY_OK;
+}
+
+/**
+ * extcon_register_interest() - Register a notifier for a state change of a
+ *			      specific cable, not a entier set of cables of a
+ *			      extcon device.
+ * @obj:	an empty extcon_specific_cable_nb object to be returned.
+ * @extcon_name:	the name of extcon device.
+ * @cable_name:		the target cable name.
+ * @nb:		the notifier block to get notified.
+ *
+ * Provide an empty extcon_specific_cable_nb. extcon_register_interest() sets
+ * the struct for you.
+ *
+ * extcon_register_interest is a helper function for those who want to get
+ * notification for a single specific cable's status change. If a user wants
+ * to get notification for any changes of all cables of a extcon device,
+ * he/she should use the general extcon_register_notifier().
+ *
+ * Note that the second parameter given to the callback of nb (val) is
+ * "old_state", not the current state. The current state can be retrieved
+ * by looking at the third pameter (edev pointer)'s state value.
+ */
+int extcon_register_interest(struct extcon_specific_cable_nb *obj,
+			     const char *extcon_name, const char *cable_name,
+			     struct notifier_block *nb)
+{
+	if (!obj || !extcon_name || !cable_name || !nb)
+		return -EINVAL;
+
+	obj->edev = extcon_get_extcon_dev(extcon_name);
+	if (!obj->edev)
+		return -ENODEV;
+
+	obj->cable_index = extcon_find_cable_index(obj->edev, cable_name);
+	if (obj->cable_index < 0)
+		return -ENODEV;
+
+	obj->user_nb = nb;
+
+	obj->internal_nb.notifier_call = _call_per_cable;
+
+	return raw_notifier_chain_register(&obj->edev->nh, &obj->internal_nb);
+}
+
+/**
+ * extcon_unregister_interest() - Unregister the notifier registered by
+ *				extcon_register_interest().
+ * @obj:	the extcon_specific_cable_nb object returned by
+ *		extcon_register_interest().
+ */
+int extcon_unregister_interest(struct extcon_specific_cable_nb *obj)
+{
+	if (!obj)
+		return -EINVAL;
+
+	return raw_notifier_chain_unregister(&obj->edev->nh, &obj->internal_nb);
+}
+
 /**
  * extcon_register_notifier() - Register a notifee to get notified by
  *			      any attach status changes from the extcon.
  * @edev:	the extcon device.
  * @nb:		a notifier block to be registered.
+ *
+ * Note that the second parameter given to the callback of nb (val) is
+ * "old_state", not the current state. The current state can be retrieved
+ * by looking at the third pameter (edev pointer)'s state value.
  */
 int extcon_register_notifier(struct extcon_dev *edev,
 			struct notifier_block *nb)
@@ -173,8 +479,9 @@ int extcon_unregister_notifier(struct extcon_dev *edev,
 EXPORT_SYMBOL_GPL(extcon_unregister_notifier);
 
 static struct device_attribute extcon_attrs[] = {
-	__ATTR_RO(state),
+	__ATTR(state, S_IRUGO | S_IWUSR, state_show, state_store),
 	__ATTR_RO(name),
+	__ATTR_NULL,
 };
 
 static int create_extcon_class(void)
@@ -202,6 +509,16 @@ static void extcon_cleanup(struct extcon_dev *edev, bool skip)
 	mutex_unlock(&extcon_dev_list_lock);
 
 	if (!skip && get_device(edev->dev)) {
+		int index;
+
+		for (index = 0; index < edev->max_supported; index++)
+			kfree(edev->cables[index].attr_g.name);
+
+		if (edev->max_supported) {
+			kfree(edev->extcon_dev_type.groups);
+			kfree(edev->cables);
+		}
+
 		device_unregister(edev->dev);
 		put_device(edev->dev);
 	}
@@ -216,6 +533,10 @@ static void extcon_dev_release(struct device *dev)
 	extcon_cleanup(edev, true);
 }
 
+static void dummy_sysfs_dev_release(struct device *dev)
+{
+}
+
 /**
  * extcon_dev_register() - Register a new extcon device
  * @edev	: the new extcon device (should be allocated before calling)
@@ -228,7 +549,7 @@ static void extcon_dev_release(struct device *dev)
  */
 int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
 {
-	int ret;
+	int ret, index = 0;
 
 	if (!extcon_class) {
 		ret = create_extcon_class();
@@ -236,12 +557,93 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
 			return ret;
 	}
 
+	if (edev->supported_cable) {
+		/* Get size of array */
+		for (index = 0; edev->supported_cable[index]; index++)
+			;
+		edev->max_supported = index;
+	} else {
+		edev->max_supported = 0;
+	}
+
+	if (index > SUPPORTED_CABLE_MAX) {
+		dev_err(edev->dev, "extcon: maximum number of supported cables exceeded.\n");
+		return -EINVAL;
+	}
+
 	edev->dev = kzalloc(sizeof(struct device), GFP_KERNEL);
 	edev->dev->parent = dev;
 	edev->dev->class = extcon_class;
 	edev->dev->release = extcon_dev_release;
 
 	dev_set_name(edev->dev, edev->name ? edev->name : dev_name(dev));
+
+	if (edev->max_supported) {
+		char buf[10];
+		char *str;
+		struct extcon_cable *cable;
+
+		edev->cables = kzalloc(sizeof(struct extcon_cable) *
+				       edev->max_supported, GFP_KERNEL);
+		if (!edev->cables) {
+			ret = -ENOMEM;
+			goto err_sysfs_alloc;
+		}
+		for (index = 0; index < edev->max_supported; index++) {
+			cable = &edev->cables[index];
+
+			snprintf(buf, 10, "cable.%d", index);
+			str = kzalloc(sizeof(char) * (strlen(buf) + 1),
+				      GFP_KERNEL);
+			if (!str) {
+				for (index--; index >= 0; index--) {
+					cable = &edev->cables[index];
+					kfree(cable->attr_g.name);
+				}
+				ret = -ENOMEM;
+
+				goto err_alloc_cables;
+			}
+			strcpy(str, buf);
+
+			cable->edev = edev;
+			cable->cable_index = index;
+			cable->attrs[0] = &cable->attr_name.attr;
+			cable->attrs[1] = &cable->attr_state.attr;
+			cable->attrs[2] = NULL;
+			cable->attr_g.name = str;
+			cable->attr_g.attrs = cable->attrs;
+
+			cable->attr_name.attr.name = "name";
+			cable->attr_name.attr.mode = 0444;
+			cable->attr_name.show = cable_name_show;
+
+			cable->attr_state.attr.name = "state";
+			cable->attr_state.attr.mode = 0644;
+			cable->attr_state.show = cable_state_show;
+			cable->attr_state.store = cable_state_store;
+		}
+	}
+
+	if (edev->max_supported) {
+		edev->extcon_dev_type.groups =
+			kzalloc(sizeof(struct attribute_group *) *
+				(edev->max_supported + 1), GFP_KERNEL);
+		if (!edev->extcon_dev_type.groups) {
+			ret = -ENOMEM;
+			goto err_alloc_groups;
+		}
+
+		edev->extcon_dev_type.name = dev_name(edev->dev);
+		edev->extcon_dev_type.release = dummy_sysfs_dev_release;
+
+		for (index = 0; index < edev->max_supported; index++)
+			edev->extcon_dev_type.groups[index] =
+				&edev->cables[index].attr_g;
+
+		edev->dev->type = &edev->extcon_dev_type;
+	}
+
 	ret = device_register(edev->dev);
 	if (ret) {
 		put_device(edev->dev);
@@ -253,6 +655,8 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
 					       dev);
 #endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
 
+	spin_lock_init(&edev->lock);
+
 	RAW_INIT_NOTIFIER_HEAD(&edev->nh);
 
 	dev_set_drvdata(edev->dev, edev);
@@ -265,6 +669,15 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
 	return 0;
 
 err_dev:
+	if (edev->max_supported)
+		kfree(edev->extcon_dev_type.groups);
+err_alloc_groups:
+	for (index = 0; index < edev->max_supported; index++)
+		kfree(edev->cables[index].attr_g.name);
+err_alloc_cables:
+	if (edev->max_supported)
+		kfree(edev->cables);
+err_sysfs_alloc:
 	kfree(edev->dev);
 	return ret;
 }
diff --git a/include/linux/extcon.h b/include/linux/extcon.h
index c9c9afe..20e24b3 100644
--- a/include/linux/extcon.h
+++ b/include/linux/extcon.h
@@ -24,10 +24,60 @@
 #define __LINUX_EXTCON_H__
 
 #include <linux/notifier.h>
+
+#define SUPPORTED_CABLE_MAX	32
+#define CABLE_NAME_MAX		30
+
+/*
+ * The standard cable name is to help support general notifier
+ * and notifee device drivers to share the common names.
+ * Please use standard cable names unless your notifier device has
+ * a very unique and abnormal cable or
+ * the cable type is supposed to be used with only one unique
+ * pair of notifier/notifee devices.
+ *
+ * Please add any other "standard" cables used with extcon dev.
+ *
+ * You may add a dot and number to specify version or specification
+ * of the specific cable if it is required. (e.g., "Fast-charger.18"
+ * and "Fast-charger.10" for 1.8A and 1.0A chargers)
+ * However, the notifee and notifier should be able to handle such
+ * string and if the notifee can negotiate the protocol or idenify,
+ * you don't need such convention. This convention is helpful when
+ * notifier can distinguish but notifiee cannot.
+ */
+enum extcon_cable_name {
+	EXTCON_USB = 0,
+	EXTCON_USB_HOST,
+	EXTCON_TA, /* Travel Adaptor */
+	EXTCON_FAST_CHARGER,
+	EXTCON_SLOW_CHARGER,
+	EXTCON_CHARGE_DOWNSTREAM, /* Charging an external device */
+	EXTCON_HDMI,
+	EXTCON_MHL,
+	EXTCON_DVI,
+	EXTCON_VGA,
+	EXTCON_DOCK,
+	EXTCON_LINE_IN,
+	EXTCON_LINE_OUT,
+	EXTCON_MIC_IN,
+	EXTCON_HEADPHONE_OUT,
+	EXTCON_SPDIF_IN,
+	EXTCON_SPDIF_OUT,
+	EXTCON_VIDEO_IN,
+	EXTCON_VIDEO_OUT,
+};
+extern const char *extcon_cable_name[];
+
+struct extcon_cable;
+
 /**
  * struct extcon_dev - An extcon device represents one external connector.
  * @name	The name of this extcon device. Parent device name is used
  *		if NULL.
+ * @supported_cable	Array of supported cable name ending with NULL.
+ *			If supported_cable is NULL, cable name related APIs
+ *			are disabled.
  * @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
@@ -38,6 +88,11 @@
  * @nh	Notifier for the state change events from this extcon
  * @entry	To support list of extcon devices so that uses can search
  *		for extcon devices based on the extcon name.
+ * @lock
+ * @max_supported	Internal value to store the number of cables.
+ * @extcon_dev_type	Device_type struct to provide attribute_groups
+ *			customized for each extcon device.
+ * @cables	Sysfs subdirectories. Each represents one cable.
  *
  * In most cases, users only need to provide "User initializing data" of
  * this struct when registering an extcon. In some exceptional cases,
@@ -47,6 +102,7 @@
 struct extcon_dev {
 	/* --- Optional user initializing data --- */
 	const char	*name;
+	const char **supported_cable;
 
 	/* --- Optional callbacks to override class functions --- */
 	ssize_t	(*print_name)(struct extcon_dev *edev, char *buf);
@@ -57,6 +113,49 @@ struct extcon_dev {
 	u32		state;
 	struct raw_notifier_head nh;
 	struct list_head entry;
+	spinlock_t lock; /* could be called by irq handler */
+	int max_supported;
+
+	/* /sys/class/extcon/.../cable.n/... */
+	struct device_type extcon_dev_type;
+	struct extcon_cable *cables;
+};
+
+/**
+ * struct extcon_cable	- An internal data for each cable of extcon device.
+ * @edev	The extcon device
+ * @cable_index	Index of this cable in the edev
+ * @attr_g	Attribute group for the cable
+ * @attr_name	"name" sysfs entry
+ * @attr_state	"state" sysfs entry
+ * @attrs	Array pointing to attr_name and attr_state for attr_g
+ */
+struct extcon_cable {
+	struct extcon_dev *edev;
+	int cable_index;
+
+	struct attribute_group attr_g;
+	struct device_attribute attr_name;
+	struct device_attribute attr_state;
+
+	struct attribute *attrs[3]; /* to be fed to attr_g.attrs */
+};
+
+/**
+ * struct extcon_specific_cable_nb - An internal data for
+ *				extcon_register_interest().
+ * @internal_nb	a notifier block bridging extcon notifier and cable notifier.
+ * @user_nb	user provided notifier block for events from a specific cable.
+ * @cable_index	the target cable.
+ * @edev	the target extcon device.
+ * @previous_value	the saved previous event value.
+ */
+struct extcon_specific_cable_nb {
+	struct notifier_block internal_nb;
+	struct notifier_block *user_nb;
+	int cable_index;
+	struct extcon_dev *edev;
+	unsigned long previous_value;
 };
 
 #if IS_ENABLED(CONFIG_EXTCON)
@@ -69,16 +168,54 @@ extern int extcon_dev_register(struct extcon_dev *edev, struct device *dev);
 extern void extcon_dev_unregister(struct extcon_dev *edev);
 extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name);
 
+/*
+ * get/set/update_state access the 32b encoded state value, which represents
+ * states of all possible cables of the multistate port. For example, if one
+ * calls extcon_set_state(edev, 0x7), it may mean that all the three cables
+ * are attached to the port.
+ */
 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);
+
+/*
+ * get/set_cable_state access each bit of the 32b encoded state value.
+ * They are used to access the status of each cable based on the cable_name
+ * or cable_index, which is retrived by extcon_find_cable_index
+ */
+extern int extcon_find_cable_index(struct extcon_dev *sdev,
+				   const char *cable_name);
+extern int extcon_get_cable_state_(struct extcon_dev *edev, int cable_index);
+extern int extcon_set_cable_state_(struct extcon_dev *edev, int cable_index,
+				   bool cable_state);
+
+extern int extcon_get_cable_state(struct extcon_dev *edev,
+				  const char *cable_name);
+extern int extcon_set_cable_state(struct extcon_dev *edev,
+				  const char *cable_name, bool cable_state);
+
+/*
+ * Following APIs are for notifiees (those who want to be notified)
+ * to register a callback for events from a specific cable of the extcon.
+ * Notifiees are the connected device drivers wanting to get notified by
+ * a specific external port of a connection device.
+ */
+extern int extcon_register_interest(struct extcon_specific_cable_nb *obj,
+				    const char *extcon_name,
+				    const char *cable_name,
+				    struct notifier_block *nb);
+extern int extcon_unregister_interest(struct extcon_specific_cable_nb *nb);
 
 /*
  * Following APIs are to monitor every action of a notifier.
  * Registerer gets notified for every external port of a connection device.
+ * Probably this could be used to debug an action of notifier; however,
+ * we do not recommend to use this at normal 'notifiee' device drivers who
+ * want to be notified by a specific external port of the notifier.
  */
 extern int extcon_register_notifier(struct extcon_dev *edev,
 				    struct notifier_block *nb);
@@ -99,6 +236,41 @@ static inline u32 extcon_get_state(struct extcon_dev *edev)
 }
 
 static inline void extcon_set_state(struct extcon_dev *edev, u32 state) { }
+
+static inline void extcon_update_state(struct extcon_dev *edev, u32 mask,
+				       u32 state)
+{ }
+
+static inline int extcon_find_cable_index(struct extcon_dev *edev,
+					  const char *cable_name)
+{
+	return 0;
+}
+
+static inline int extcon_get_cable_state_(struct extcon_dev *edev,
+					  int cable_index)
+{
+	return 0;
+}
+
+static inline int extcon_set_cable_state_(struct extcon_dev *edev,
+					  int cable_index, bool cable_state)
+{
+	return 0;
+}
+
+static inline int extcon_get_cable_state(struct extcon_dev *edev,
+			const char *cable_name)
+{
+	return 0;
+}
+
+static inline int extcon_set_cable_state(struct extcon_dev *edev,
+			const char *cable_name, int state)
+{
+	return 0;
+}
+
 static inline struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
 {
 	return NULL;
@@ -116,5 +288,18 @@ static inline int extcon_unregister_notifier(struct extcon_dev *edev,
 	return 0;
 }
 
+static inline int extcon_register_interest(struct extcon_specific_cable_nb *obj,
+					   const char *extcon_name,
+					   const char *cable_name,
+					   struct notifier_block *nb)
+{
+	return 0;
+}
+
+static inline int extcon_unregister_interest(struct extcon_specific_cable_nb
+						    *obj)
+{
+	return 0;
+}
 #endif /* CONFIG_EXTCON */
 #endif /* __LINUX_EXTCON_H__ */
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH v8 resend 5/6] Extcon: support mutually exclusive relation between cables.
  2012-04-20  5:16                       ` [PATCH v8 resend 0/6] Introduce External Connector Class (extcon) MyungJoo Ham
                                           ` (3 preceding siblings ...)
  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
  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:27                         ` [PATCH v8 resend 0/6] Introduce External Connector Class (extcon) Greg KH
  6 siblings, 0 replies; 51+ messages in thread
From: MyungJoo Ham @ 2012-04-20  5:16 UTC (permalink / raw)
  To: Greg KH
  Cc: Arnd Bergmann, LKML, Arve Hjønnevag, Kyungmin Park,
	Linus Walleij, Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown,
	John Stultz, myungjoo.ham, cw00.choi

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


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH v8 resend 6/6] Documentation/extcon: porting guide for Android kernel switch driver.
  2012-04-20  5:16                       ` [PATCH v8 resend 0/6] Introduce External Connector Class (extcon) MyungJoo Ham
                                           ` (4 preceding siblings ...)
  2012-04-20  5:16                         ` [PATCH v8 resend 5/6] Extcon: support mutually exclusive relation between cables MyungJoo Ham
@ 2012-04-20  5:16                         ` MyungJoo Ham
  2012-04-20 16:25                           ` Greg KH
  2012-04-20 16:27                         ` [PATCH v8 resend 0/6] Introduce External Connector Class (extcon) Greg KH
  6 siblings, 1 reply; 51+ messages in thread
From: MyungJoo Ham @ 2012-04-20  5:16 UTC (permalink / raw)
  To: Greg KH
  Cc: Arnd Bergmann, LKML, Arve Hjønnevag, Kyungmin Park,
	Linus Walleij, Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown,
	John Stultz, myungjoo.ham, cw00.choi

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 Documentation/extcon/porting-android-switch-class |  124 +++++++++++++++++++++
 1 files changed, 124 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/extcon/porting-android-switch-class

diff --git a/Documentation/extcon/porting-android-switch-class b/Documentation/extcon/porting-android-switch-class
new file mode 100644
index 0000000..eb0fa5f
--- /dev/null
+++ b/Documentation/extcon/porting-android-switch-class
@@ -0,0 +1,124 @@
+
+	Staging/Android Switch Class Porting Guide
+	(linux/drivers/staging/android/switch)
+	(c) Copyright 2012 Samsung Electronics
+
+AUTHORS
+MyungJoo Ham <myungjoo.ham@samsung.com>
+
+/*****************************************************************
+ * CHAPTER 1.                                                    *
+ * PORTING SWITCH CLASS DEVICE DRIVERS                           *
+ *****************************************************************/
+
+****** STEP 1. Basic Functionality
+	No extcon extended feature, but switch features only.
+
+- struct switch_dev (fed to switch_dev_register/unregister)
+    @name: no change
+    @dev: no change
+    @index: drop (not used in switch device driver side anyway)
+    @state: no change
+	If you have used @state with magic numbers, keep it
+	at this step.
+    @print_name: no change but type change (switch_dev->extcon_dev)
+    @print_state: no change but type change (switch_dev->extcon_dev)
+
+- switch_dev_register(sdev, dev)
+	=> extcon_dev_register(edev, dev)
+	: no change but type change (sdev->edev)
+- switch_dev_unregister(sdev)
+	=> extcon_dev_unregister(edev)
+	: no change but type change (sdev->edev)
+- switch_get_state(sdev)
+	=> extcon_get_state(edev)
+	: no change but type change (sdev->edev) and (return: int->u32)
+- switch_set_state(sdev, state)
+	=> extcon_set_state(edev, state)
+	: no change but type change (sdev->edev) and (state: int->u32)
+
+With this changes, the ex-switch extcon class device works as it once
+worked as switch class device. However, it will now have additional
+interfaces (both ABI and in-kernel API) and different ABI locations.
+However, if CONFIG_ANDROID is enabled without CONFIG_ANDROID_SWITCH,
+/sys/class/switch/* will be symbolically linked to /sys/class/extcon/
+so that they are still compatible with legacy userspace processes.
+
+****** STEP 2. Multistate (no more magic numbers in state value)
+	Extcon's extended features for switch device drivers with
+	complex features usually required magic numbers in state
+	value of switch_dev. With extcon, such magic numbers that
+	support multiple cables (
+
+  1. Define cable names at edev->supported_cable.
+  2. (Recommended) remove print_state callback.
+  3. Use extcon_get_cable_state_(edev, index) or
+   extcon_get_cable_state(edev, cable_name) instead of
+   extcon_get_state(edev) if you intend to get a state of a specific
+   cable. Same for set_state. This way, you can remove the usage of
+   magic numbers in state value.
+  4. Use extcon_update_state() if you are updating specific bits of
+   the state value.
+
+Example: a switch device driver w/ magic numbers for two cables.
+	"0x00": no cables connected.
+	"0x01": cable 1 connected
+	"0x02": cable 2 connected
+	"0x03": cable 1 and 2 connected
+  1. edev->supported_cable = {"1", "2", NULL};
+  2. edev->print_state = NULL;
+  3. extcon_get_cable_state_(edev, 0) shows cable 1's state.
+     extcon_get_cable_state(edev, "1") shows cable 1's state.
+     extcon_set_cable_state_(edev, 1) sets cable 2's state.
+     extcon_set_cable_state(edev, "2") sets cable 2's state
+  4. extcon_update_state(edev, 0x01, 0) sets the least bit's 0.
+
+****** STEP 3. Notify other device drivers
+
+  You can notify others of the cable attach/detach events with
+notifier chains.
+
+  At the side of other device drivers (the extcon device itself
+does not need to get notified of its own events), there are two
+methods to register notifier_block for cable events:
+(a) for a specific cable or (b) for every cable.
+
+  (a) extcon_register_interest(obj, extcon_name, cable_name, nb)
+	Example: want to get news of "MAX8997_MUIC"'s "USB" cable
+
+	obj = kzalloc(sizeof(struct extcon_specific_cable_nb),
+		      GFP_KERNEL);
+	nb->notifier_call = the_callback_to_handle_usb;
+
+	extcon_register_intereset(obj, "MAX8997_MUIC", "USB", nb);
+
+  (b) extcon_register_notifier(edev, nb)
+	Call nb for any changes in edev.
+
+  Please note that in order to properly behave with method (a),
+the extcon device driver should support multistate feature (STEP 2).
+
+****** STEP 4. Inter-cable relation (mutually exclusive)
+
+  You can provide inter-cable mutually exclusiveness information
+for an extcon device. When cables A and B are declared to be mutually
+exclusive, the two cables cannot be in ATTACHED state simulteneously.
+
+
+/*****************************************************************
+ * CHAPTER 2.                                                    *
+ * PORTING USERSPACE w/ SWITCH CLASS DEVICE SUPPORT              *
+ *****************************************************************/
+
+****** ABI Location
+
+  If "CONFIG_ANDROID" is enabled and "CONFIG_ANDROID_SWITCH" is
+disabled, /sys/class/switch/* are created as symbolic links to
+/sys/class/extcon/*. Because CONFIG_ANDROID_SWITCH creates
+/sys/class/switch directory, we disable symboling linking if
+CONFIG_ANDROID_SWITCH is enabled.
+
+  The two files of switch class, name and state, are provided with
+extcon, too. When the multistate support (STEP 2 of CHAPTER 1.) is
+not enabled or print_state callback is supplied, the output of
+state ABI is same with switch class.
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* Re: [PATCH v8 resend 1/6] Extcon (external connector): import Android's switch class and modify.
  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
  0 siblings, 0 replies; 51+ messages in thread
From: Ryan Mallon @ 2012-04-20  7:00 UTC (permalink / raw)
  To: MyungJoo Ham
  Cc: Greg KH, Arnd Bergmann, LKML, Arve Hjønnevag, Kyungmin Park,
	Linus Walleij, Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown,
	John Stultz, myungjoo.ham, cw00.choi

On 20/04/12 15:16, MyungJoo Ham wrote:
> External connector class (extcon) is based on and an extension of
> Android kernel's switch class located at linux/drivers/switch/.
>
> This patch provides the before-extension switch class moved to the
> location where the extcon will be located (linux/drivers/extcon/) and
> updates to handle class properly.
>
> The before-extension class, switch class of Android kernel, commits
> imported are:
>
> switch: switch class and GPIO drivers. (splitted)
> Author: Mike Lockwood <lockwood@android.com>
>
> switch: Use device_create instead of device_create_drvdata.
> Author: Arve Hjønnevåg <arve@android.com>
>
> In this patch, upon the commits of Android kernel, we have added:
> - Relocated and renamed for extcon.
> - Comments, module name, and author information are updated
> - Code clean for successing patches
> - Bugfix: enabling write access without write functions
> - Class/device/sysfs create/remove handling
> - Added comments about uevents
> - Format changes for extcon_dev_register() to have a parent dev.
>
> Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>

Hi Myungjoo,

A few comments below.

~Ryan

> --
> Changes from v7
> - Compiler error fixed when it is compiled as a module.
> - Removed out-of-date Kconfig entry
>
> Changes from v6
> - Updated comment/strings
> - Revised "Android-compatible" mode.
>    * Automatically activated if CONFIG_ANDROID && !CONFIG_ANDROID_SWITCH
>    * Creates /sys/class/switch/*, which is a copy of /sys/class/extcon/*
>
> Changes from v5
> - Split the patch
> - Style fixes
> - "Android-compatible" mode is enabled by Kconfig option.
>
> Changes from v2
> - Updated name_show
> - Sysfs entries are handled by class itself.
> - Updated the method to add/remove devices for the class
> - Comments on uevent send
> - Able to become a module
> - Compatible with Android platform
>
> Changes from RFC
> - Renamed to extcon (external connector) from multistate switch
> - Added a seperated directory (drivers/extcon)
> - Added kerneldoc comments
> - Removed unused variables from extcon_gpio.c
> - Added ABI Documentation.
> ---
>  Documentation/ABI/testing/sysfs-class-extcon |   26 +++
>  drivers/Kconfig                              |    2 +
>  drivers/Makefile                             |    1 +
>  drivers/extcon/Kconfig                       |   17 ++
>  drivers/extcon/Makefile                      |    5 +
>  drivers/extcon/extcon_class.c                |  236 ++++++++++++++++++++++++++
>  include/linux/extcon.h                       |   82 +++++++++
>  7 files changed, 369 insertions(+), 0 deletions(-)
>  create mode 100644 Documentation/ABI/testing/sysfs-class-extcon
>  create mode 100644 drivers/extcon/Kconfig
>  create mode 100644 drivers/extcon/Makefile
>  create mode 100644 drivers/extcon/extcon_class.c
>  create mode 100644 include/linux/extcon.h
>
> diff --git a/Documentation/ABI/testing/sysfs-class-extcon b/Documentation/ABI/testing/sysfs-class-extcon
> new file mode 100644
> index 0000000..59a4b1c
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-class-extcon
> @@ -0,0 +1,26 @@
> +What:		/sys/class/extcon/.../
> +Date:		December 2011
> +Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
> +Description:
> +		Provide a place in sysfs for the extcon objects.
> +		This allows accessing extcon specific variables.
> +		The name of extcon object denoted as ... is the name given
> +		with extcon_dev_register.
> +
> +What:		/sys/class/extcon/.../name
> +Date:		December 2011
> +Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
> +Description:
> +		The /sys/class/extcon/.../name shows the name of the extcon
> +		object. If the extcon object has an optional callback
> +		"show_name" defined, the callback will provide the name with
> +		this sysfs node.
> +
> +What:		/sys/class/extcon/.../state
> +Date:		December 2011
> +Contact:	MyungJoo Ham <myungjoo.ham@samsung.com>
> +Description:
> +		The /sys/class/extcon/.../state shows the cable attach/detach
> +		information of the corresponding extcon object. If the extcon
> +		objecct has an optional callback "show_state" defined, the
> +		callback will provide the name with this sysfs node.
> diff --git a/drivers/Kconfig b/drivers/Kconfig
> index d236aef..0233ad9 100644
> --- a/drivers/Kconfig
> +++ b/drivers/Kconfig
> @@ -140,4 +140,6 @@ source "drivers/virt/Kconfig"
>  
>  source "drivers/devfreq/Kconfig"
>  
> +source "drivers/extcon/Kconfig"
> +
>  endmenu
> diff --git a/drivers/Makefile b/drivers/Makefile
> index 95952c8..c41dfa9 100644
> --- a/drivers/Makefile
> +++ b/drivers/Makefile
> @@ -134,3 +134,4 @@ obj-$(CONFIG_VIRT_DRIVERS)	+= virt/
>  obj-$(CONFIG_HYPERV)		+= hv/
>  
>  obj-$(CONFIG_PM_DEVFREQ)	+= devfreq/
> +obj-$(CONFIG_EXTCON)		+= extcon/
> diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig
> new file mode 100644
> index 0000000..7932e1b
> --- /dev/null
> +++ b/drivers/extcon/Kconfig
> @@ -0,0 +1,17 @@
> +menuconfig EXTCON
> +	tristate "External Connector Class (extcon) support"
> +	help
> +	  Say Y here to enable external connector class (extcon) support.
> +	  This allows monitoring external connectors by userspace
> +	  via sysfs and uevent and supports external connectors with
> +	  multiple states; i.e., an extcon that may have multiple
> +	  cables attached. For example, an external connector of a device
> +	  may be used to connect an HDMI cable and a AC adaptor, and to
> +	  host USB ports. Many of 30-pin connectors including PDMI are
> +	  also good examples.
> +
> +if EXTCON
> +
> +comment "Extcon Device Drivers"
> +
> +endif # MULTISTATE_SWITCH
> diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile
> new file mode 100644
> index 0000000..6bc6921
> --- /dev/null
> +++ b/drivers/extcon/Makefile
> @@ -0,0 +1,5 @@
> +#
> +# Makefile for external connector class (extcon) devices
> +#
> +
> +obj-$(CONFIG_EXTCON)		+= extcon_class.o
> diff --git a/drivers/extcon/extcon_class.c b/drivers/extcon/extcon_class.c
> new file mode 100644
> index 0000000..3c46368
> --- /dev/null
> +++ b/drivers/extcon/extcon_class.c
> @@ -0,0 +1,236 @@
> +/*
> + *  drivers/extcon/extcon_class.c
> + *
> + *  External connector (extcon) class driver
> + *
> + * Copyright (C) 2012 Samsung Electronics
> + * Author: Donggeun Kim <dg77.kim@samsung.com>
> + * Author: MyungJoo Ham <myungjoo.ham@samsung.com>
> + *
> + * based on android/drivers/switch/switch_class.c
> + * Copyright (C) 2008 Google, Inc.
> + * Author: Mike Lockwood <lockwood@android.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> +*/
> +
> +#include <linux/module.h>
> +#include <linux/types.h>
> +#include <linux/init.h>
> +#include <linux/device.h>
> +#include <linux/fs.h>
> +#include <linux/err.h>
> +#include <linux/extcon.h>
> +#include <linux/slab.h>
> +
> +struct class *extcon_class;
> +#if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
> +static struct class_compat *switch_class;
> +#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
> +
> +static ssize_t state_show(struct device *dev, struct device_attribute *attr,
> +			  char *buf)
> +{
> +	struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);

Shouldn't need the cast. Same elsewhere.

> +
> +	if (edev->print_state) {
> +		int ret = edev->print_state(edev, buf);
> +
> +		if (ret >= 0)
> +			return ret;
> +		/* Use default if failed */
> +	}
> +	return sprintf(buf, "%u\n", edev->state);
> +}
> +
> +static ssize_t name_show(struct device *dev, struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
> +
> +	/* Optional callback given by the user */
> +	if (edev->print_name) {
> +		int ret = edev->print_name(edev, buf);
> +		if (ret >= 0)
> +			return ret;
> +	}
> +
> +	return sprintf(buf, "%s\n", dev_name(edev->dev));
> +}
> +
> +/**
> + * extcon_set_state() - Set the cable attach states of the extcon device.
> + * @edev:	the extcon device
> + * @state:	new cable attach status for @edev
> + *
> + * Changing the state sends uevent with environment variable containing
> + * the name of extcon device (envp[0]) and the state output (envp[1]).
> + * Tizen uses this format for extcon device to get events from ports.
> + * Android uses this format as well.
> + */
> +void extcon_set_state(struct extcon_dev *edev, u32 state)
> +{
> +	char name_buf[120];
> +	char state_buf[120];

Where do these sizes come from?

> +	char *prop_buf;
> +	char *envp[3];
> +	int env_offset = 0;
> +	int length;
> +
> +	if (edev->state != state) {
> +		edev->state = state;
> +
> +		prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
> +		if (prop_buf) {

	if (!prop_buf) {
		dev_err(edev->dev, "out of memory in extcon_set_state\n");
		kobject_uevent(&edev->dev->kobj, KOBJ_CHANGE);
		return;
	}


Gives you one less level of indentation for the normal execution path.
Also, is there a good reason to use get_zeroed_page(...) instead of
kzalloc(PAGE_SIZE, ...), the latter doesn't require casting.

Also, should this function return an int so that callers can know
whether it actually succeeded?

> +			length = name_show(edev->dev, NULL, prop_buf);
> +			if (length > 0) {
> +				if (prop_buf[length - 1] == '\n')
> +					prop_buf[length - 1] = 0;

This is a bit ugly using a sysfs show function internally and then
manually cropping out the '\n'. Why not have a name get function:

	static ssize_t extcon_snprint_name(struct extcon_dev *edev, char *buf, size_t buf_size)
	{
		int ret;

		if (edev->print_state) {
			ret = edev->print_state(edev, buf, buf_size);
			if (ret >= 0)
				return ret;
		}
		return snprintf(buf, buf_size, "%u", edev->state);
	}


This function and the sysfs show function can call extcon_snprint_name.
Having an explict size would also get rid of the need for the
intermediate prop_buf here:

	count = snprintf(name_buf, sizeof(name_buf), "NAME=");
	extcon_snprint_name(edev, name_buf + count, sizeof(name_buf) - count);


> +				snprintf(name_buf, sizeof(name_buf),
> +					"NAME=%s", prop_buf);
> +				envp[env_offset++] = name_buf;
> +			}
> +			length = state_show(edev->dev, NULL, prop_buf);
> +			if (length > 0) {
> +				if (prop_buf[length - 1] == '\n')
> +					prop_buf[length - 1] = 0;
> +				snprintf(state_buf, sizeof(state_buf),
> +					"STATE=%s", prop_buf);
> +				envp[env_offset++] = state_buf;
> +			}
> +			envp[env_offset] = NULL;
> +			kobject_uevent_env(&edev->dev->kobj, KOBJ_CHANGE, envp);
> +			free_page((unsigned long)prop_buf);
> +		} else {
> +			dev_err(edev->dev, "out of memory in extcon_set_state\n");
> +			kobject_uevent(&edev->dev->kobj, KOBJ_CHANGE);
> +		}
> +	}
> +}
> +EXPORT_SYMBOL_GPL(extcon_set_state);
> +
> +static struct device_attribute extcon_attrs[] = {
> +	__ATTR_RO(state),
> +	__ATTR_RO(name),
> +};
> +
> +static int create_extcon_class(void)
> +{
> +	if (!extcon_class) {

extcon_class can only ever be NULL since this function is only ever
called from the module init function right?

> +		extcon_class = class_create(THIS_MODULE, "extcon");
> +		if (IS_ERR(extcon_class))
> +			return PTR_ERR(extcon_class);
> +		extcon_class->dev_attrs = extcon_attrs;
> +
> +#if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
> +		switch_class = class_compat_register("switch");
> +		if (WARN(!switch_class, "cannot allocate"))
> +			return -ENOMEM;
> +#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */

This is a bit ugly putting Android specific hacks in mainline. Is this
something that Android could do in user-space instead?

> +	}
> +
> +	return 0;
> +}
> +
> +static void extcon_cleanup(struct extcon_dev *edev, bool skip)
> +{
> +	if (!skip && get_device(edev->dev)) {
> +		device_unregister(edev->dev);
> +		put_device(edev->dev);
> +	}
> +
> +	kfree(edev->dev);
> +}
> +
> +static void extcon_dev_release(struct device *dev)
> +{
> +	struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
> +

Don't need the cast.

> +	extcon_cleanup(edev, true);
> +}
> +
> +/**
> + * extcon_dev_register() - Register a new extcon device
> + * @edev	: the new extcon device (should be allocated before calling)
> + * @dev		: the parent device for this extcon device.
> + *
> + * Among the members of edev struct, please set the "user initializing data"
> + * in any case and set the "optional callbacks" if required. However, please
> + * do not set the values of "internal data", which are initialized by
> + * this function.
> + */
> +int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
> +{
> +	int ret;
> +
> +	if (!extcon_class) {
> +		ret = create_extcon_class();
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	edev->dev = kzalloc(sizeof(struct device), GFP_KERNEL);

	if (!evdev->dev) {
		/* Explode */
	}


> +	edev->dev->parent = dev;
> +	edev->dev->class = extcon_class;
> +	edev->dev->release = extcon_dev_release;
> +
> +	dev_set_name(edev->dev, edev->name ? edev->name : dev_name(dev));
> +	ret = device_register(edev->dev);
> +	if (ret) {
> +		put_device(edev->dev);
> +		goto err_dev;
> +	}
> +#if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
> +	if (switch_class)
> +		ret = class_compat_create_link(switch_class, edev->dev,
> +					       dev);
> +#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
> +
> +	dev_set_drvdata(edev->dev, edev);
> +	edev->state = 0;
> +	return 0;
> +
> +err_dev:
> +	kfree(edev->dev);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(extcon_dev_register);
> +
> +/**
> + * extcon_dev_unregister() - Unregister the extcon device.
> + * @edev:	the extcon device instance to be unregitered.
> + *
> + * Note that this does not call kfree(edev) because edev was not allocated
> + * by this class.
> + */
> +void extcon_dev_unregister(struct extcon_dev *edev)
> +{
> +	extcon_cleanup(edev, false);
> +}
> +EXPORT_SYMBOL_GPL(extcon_dev_unregister);
> +
> +static int __init extcon_class_init(void)
> +{
> +	return create_extcon_class();
> +}
> +module_init(extcon_class_init);
> +
> +static void __exit extcon_class_exit(void)
> +{
> +	class_destroy(extcon_class);
> +}
> +module_exit(extcon_class_exit);
> +
> +MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
> +MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
> +MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
> +MODULE_DESCRIPTION("External connector (extcon) class driver");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/extcon.h b/include/linux/extcon.h
> new file mode 100644
> index 0000000..9cb3aaa
> --- /dev/null
> +++ b/include/linux/extcon.h
> @@ -0,0 +1,82 @@
> +/*
> + *  External connector (extcon) class driver
> + *
> + * Copyright (C) 2012 Samsung Electronics
> + * Author: Donggeun Kim <dg77.kim@samsung.com>
> + * Author: MyungJoo Ham <myungjoo.ham@samsung.com>
> + *
> + * based on switch class driver
> + * Copyright (C) 2008 Google, Inc.
> + * Author: Mike Lockwood <lockwood@android.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> +*/
> +
> +#ifndef __LINUX_EXTCON_H__
> +#define __LINUX_EXTCON_H__
> +
> +/**
> + * struct extcon_dev - An extcon device represents one external connector.
> + * @name	The name of this extcon device. Parent device name is used
> + *		if NULL.
> + * @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
> + *		status of the extcon device.
> + * @dev		Device of this extcon. Do not provide at register-time.
> + * @state	Attach/detach state of this extcon. Do not provide at
> + *		register-time
> + *
> + * In most cases, users only need to provide "User initializing data" of
> + * this struct when registering an extcon. In some exceptional cases,
> + * optional callbacks may be needed. However, the values in "internal data"
> + * are overwritten by register function.
> + */
> +struct extcon_dev {
> +	/* --- Optional user initializing data --- */
> +	const char	*name;
> +
> +	/* --- Optional callbacks to override class functions --- */
> +	ssize_t	(*print_name)(struct extcon_dev *edev, char *buf);
> +	ssize_t	(*print_state)(struct extcon_dev *edev, char *buf);
> +
> +	/* --- Internal data. Please do not set. --- */
> +	struct device	*dev;
> +	u32		state;
> +};
> +
> +#if IS_ENABLED(CONFIG_EXTCON)
> +extern int extcon_dev_register(struct extcon_dev *edev, struct device *dev);
> +extern void extcon_dev_unregister(struct extcon_dev *edev);
> +
> +static inline u32 extcon_get_state(struct extcon_dev *edev)
> +{
> +	return edev->state;
> +}
> +
> +extern void extcon_set_state(struct extcon_dev *edev, u32 state);
> +#else /* CONFIG_EXTCON */
> +static inline int extcon_dev_register(struct extcon_dev *edev,
> +				      struct device *dev)
> +{
> +	return 0;
> +}
> +
> +static inline void extcon_dev_unregister(struct extcon_dev *edev) { }
> +
> +static inline u32 extcon_get_state(struct extcon_dev *edev)
> +{
> +	return 0;
> +}
> +
> +static inline void extcon_set_state(struct extcon_dev *edev, u32 state) { }
> +#endif /* CONFIG_EXTCON */
> +#endif /* __LINUX_EXTCON_H__ */


^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v8 resend 6/6] Documentation/extcon: porting guide for Android kernel switch driver.
  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
  0 siblings, 1 reply; 51+ messages in thread
From: Greg KH @ 2012-04-20 16:25 UTC (permalink / raw)
  To: MyungJoo Ham
  Cc: Arnd Bergmann, LKML, Arve Hjønnevag, Kyungmin Park,
	Linus Walleij, Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown,
	John Stultz, myungjoo.ham, cw00.choi

On Fri, Apr 20, 2012 at 02:16:27PM +0900, MyungJoo Ham wrote:
> Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  Documentation/extcon/porting-android-switch-class |  124 +++++++++++++++++++++

Ok, I've applied this, but I don't know if we really need this in the
kernel tree.  How many out-of-tree android switch drivers are there?
Who needs to know this information?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v8 resend 0/6] Introduce External Connector Class (extcon)
  2012-04-20  5:16                       ` [PATCH v8 resend 0/6] Introduce External Connector Class (extcon) MyungJoo Ham
                                           ` (5 preceding siblings ...)
  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:27                         ` Greg KH
  2012-04-23 11:18                           ` MyungJoo Ham
  6 siblings, 1 reply; 51+ messages in thread
From: Greg KH @ 2012-04-20 16:27 UTC (permalink / raw)
  To: MyungJoo Ham
  Cc: Arnd Bergmann, LKML, Arve Hjønnevag, Kyungmin Park,
	Linus Walleij, Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown,
	John Stultz, myungjoo.ham, cw00.choi

On Fri, Apr 20, 2012 at 02:16:21PM +0900, MyungJoo Ham wrote:
> In order to address such issues, Android kernel's "Switch" class seems to
> be a good basis and we have implemented "Multistate Switch Class" based on
> it. The "Switch" class code of Android kernel is GPL as well.

Ok, I've applied these patches to my driver-core tree, thanks so much
for working to clean this up and get it merged, very nice job.

Now, does this mean I can delete the drivers/staging/android/switch/
directory from the tree as well?

If so, I will be glad to do that.

If not, what is left to do in order for it to be removed?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v8 resend 6/6] Documentation/extcon: porting guide for Android kernel switch driver.
  2012-04-20 16:25                           ` Greg KH
@ 2012-04-20 17:05                             ` Mark Brown
  2012-04-20 17:15                               ` Greg KH
  0 siblings, 1 reply; 51+ messages in thread
From: Mark Brown @ 2012-04-20 17:05 UTC (permalink / raw)
  To: Greg KH
  Cc: MyungJoo Ham, Arnd Bergmann, LKML, Arve Hjønnevag,
	Kyungmin Park, Linus Walleij, Dmitry Torokhov,
	Morten CHRISTIANSEN, John Stultz, myungjoo.ham, cw00.choi

[-- Attachment #1: Type: text/plain, Size: 477 bytes --]

On Fri, Apr 20, 2012 at 09:25:29AM -0700, Greg KH wrote:

> Ok, I've applied this, but I don't know if we really need this in the
> kernel tree.  How many out-of-tree android switch drivers are there?
> Who needs to know this information?

Every phone with a headset jack will have at least one switch if it's
running on Android.  People doing system integration will generally need
to know about this, at least until we've got the ALSA detection stuff
integrated with extcon.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v8 resend 6/6] Documentation/extcon: porting guide for Android kernel switch driver.
  2012-04-20 17:05                             ` Mark Brown
@ 2012-04-20 17:15                               ` Greg KH
  2012-04-20 21:59                                 ` Mark Brown
  0 siblings, 1 reply; 51+ messages in thread
From: Greg KH @ 2012-04-20 17:15 UTC (permalink / raw)
  To: Mark Brown
  Cc: MyungJoo Ham, Arnd Bergmann, LKML, Arve Hjønnevag,
	Kyungmin Park, Linus Walleij, Dmitry Torokhov,
	Morten CHRISTIANSEN, John Stultz, myungjoo.ham, cw00.choi

On Fri, Apr 20, 2012 at 06:05:20PM +0100, Mark Brown wrote:
> On Fri, Apr 20, 2012 at 09:25:29AM -0700, Greg KH wrote:
> 
> > Ok, I've applied this, but I don't know if we really need this in the
> > kernel tree.  How many out-of-tree android switch drivers are there?
> > Who needs to know this information?
> 
> Every phone with a headset jack will have at least one switch if it's
> running on Android.  People doing system integration will generally need
> to know about this, at least until we've got the ALSA detection stuff
> integrated with extcon.

Ok, but where are those drivers at?  Any pointers to them so I can start
sucking them into the tree and doing this conversion?

Is this, and the wakelock stuff, holding back a torrent of drivers to be
submitted somewhere?  Or are they buried in vendor trees, never to be
seen?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v8 resend 6/6] Documentation/extcon: porting guide for Android kernel switch driver.
  2012-04-20 17:15                               ` Greg KH
@ 2012-04-20 21:59                                 ` Mark Brown
  0 siblings, 0 replies; 51+ messages in thread
From: Mark Brown @ 2012-04-20 21:59 UTC (permalink / raw)
  To: Greg KH
  Cc: MyungJoo Ham, Arnd Bergmann, LKML, Arve Hjønnevag,
	Kyungmin Park, Linus Walleij, Dmitry Torokhov,
	Morten CHRISTIANSEN, John Stultz, myungjoo.ham, cw00.choi

[-- Attachment #1: Type: text/plain, Size: 1268 bytes --]

On Fri, Apr 20, 2012 at 10:15:34AM -0700, Greg KH wrote:
> On Fri, Apr 20, 2012 at 06:05:20PM +0100, Mark Brown wrote:

> > Every phone with a headset jack will have at least one switch if it's
> > running on Android.  People doing system integration will generally need
> > to know about this, at least until we've got the ALSA detection stuff
> > integrated with extcon.

> Ok, but where are those drivers at?  Any pointers to them so I can start
> sucking them into the tree and doing this conversion?

The actual code is all obviously out of tree, I'd expect any Android BSP
to contain at least some usage of it (though often just the basic GPIO
one for CPU vendor ones).

> Is this, and the wakelock stuff, holding back a torrent of drivers to be
> submitted somewhere?  Or are they buried in vendor trees, never to be
> seen?

I'd not characterise it as a massive problem (and wakelocks won't hold
back anything that doesn't have other problems).  I suspect that the
code for a Samsung phone with the 30 pin connector will show something
interesting, though I can't say I've actually looked.  For things like
headset detection there's already ways to support things in mainline and
we should just end up hooking extcon into the central support for these
things.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH v8 resend 0/6] Introduce External Connector Class (extcon)
  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
  0 siblings, 1 reply; 51+ messages in thread
From: MyungJoo Ham @ 2012-04-23 11:18 UTC (permalink / raw)
  To: Greg KH
  Cc: Arnd Bergmann, LKML, Arve Hjønnevag, Kyungmin Park,
	Linus Walleij, Dmitry Torokhov, Morten CHRISTIANSEN, Mark Brown,
	John Stultz, cw00.choi

On Sat, Apr 21, 2012 at 1:27 AM, Greg KH <gregkh@linuxfoundation.org> wrote:
> On Fri, Apr 20, 2012 at 02:16:21PM +0900, MyungJoo Ham wrote:
>> In order to address such issues, Android kernel's "Switch" class seems to
>> be a good basis and we have implemented "Multistate Switch Class" based on
>> it. The "Switch" class code of Android kernel is GPL as well.
>
> Ok, I've applied these patches to my driver-core tree, thanks so much
> for working to clean this up and get it merged, very nice job.
>
> Now, does this mean I can delete the drivers/staging/android/switch/
> directory from the tree as well?

Yes, I think we can now delete switch directory because switch_gpio.c
was the only driver in the directory and it also appears with extcon.
In fact, there was an RFC patch to do so w/ v7 patchset.

I'll send the patch as a reply to this message.


Cheers!
MyungJoo.

>
> If so, I will be glad to do that.
>
> If not, what is left to do in order for it to be removed?
>
> thanks,
>
> greg k-h




-- 
MyungJoo Ham, Ph.D.
Mobile Software Platform Lab, DMC Business, Samsung Electronics

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH] Remove "switch" class in drivers/staging/android/switch
  2012-04-23 11:18                           ` MyungJoo Ham
@ 2012-04-23 11:19                             ` MyungJoo Ham
  2012-04-23 20:25                               ` Greg KH
  0 siblings, 1 reply; 51+ messages in thread
From: MyungJoo Ham @ 2012-04-23 11:19 UTC (permalink / raw)
  To: Greg KH; +Cc: Kyungmin Park, cw00.choi, myungjoo.ham, LKML

Because extcon can also be a switch class for legacy userspace (Android)
and is a superset of switch class in drivers/staging/android/switch,
switch class may be removed.

- Remove switch class
- Remove switch class consideration in extcon class

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/extcon/extcon_class.c                 |   12 +-
 drivers/staging/android/Kconfig               |    3 -
 drivers/staging/android/Makefile              |    1 -
 drivers/staging/android/switch/Kconfig        |   11 --
 drivers/staging/android/switch/Makefile       |    4 -
 drivers/staging/android/switch/switch.h       |   53 --------
 drivers/staging/android/switch/switch_class.c |  174 -------------------------
 drivers/staging/android/switch/switch_gpio.c  |  172 ------------------------
 8 files changed, 6 insertions(+), 424 deletions(-)
 delete mode 100644 drivers/staging/android/switch/Kconfig
 delete mode 100644 drivers/staging/android/switch/Makefile
 delete mode 100644 drivers/staging/android/switch/switch.h
 delete mode 100644 drivers/staging/android/switch/switch_class.c
 delete mode 100644 drivers/staging/android/switch/switch_gpio.c

diff --git a/drivers/extcon/extcon_class.c b/drivers/extcon/extcon_class.c
index 3bc4b8a..dbd3bfb 100644
--- a/drivers/extcon/extcon_class.c
+++ b/drivers/extcon/extcon_class.c
@@ -65,9 +65,9 @@ const char *extcon_cable_name[] = {
 };
 
 struct class *extcon_class;
-#if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
+#if defined(CONFIG_ANDROID)
 static struct class_compat *switch_class;
-#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
+#endif /* CONFIG_ANDROID */
 
 static LIST_HEAD(extcon_dev_list);
 static DEFINE_MUTEX(extcon_dev_list_lock);
@@ -532,11 +532,11 @@ static int create_extcon_class(void)
 			return PTR_ERR(extcon_class);
 		extcon_class->dev_attrs = extcon_attrs;
 
-#if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
+#if defined(CONFIG_ANDROID)
 		switch_class = class_compat_register("switch");
 		if (WARN(!switch_class, "cannot allocate"))
 			return -ENOMEM;
-#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
+#endif /* CONFIG_ANDROID */
 	}
 
 	return 0;
@@ -749,11 +749,11 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
 		put_device(edev->dev);
 		goto err_dev;
 	}
-#if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
+#if defined(CONFIG_ANDROID)
 	if (switch_class)
 		ret = class_compat_create_link(switch_class, edev->dev,
 					       dev);
-#endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
+#endif /* CONFIG_ANDROID */
 
 	spin_lock_init(&edev->lock);
 
diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig
index eb1dee2..c706635 100644
--- a/drivers/staging/android/Kconfig
+++ b/drivers/staging/android/Kconfig
@@ -53,8 +53,6 @@ config ANDROID_LOW_MEMORY_KILLER
 	---help---
 	  Register processes to be killed when memory is low
 
-source "drivers/staging/android/switch/Kconfig"
-
 config ANDROID_INTF_ALARM
 	bool "Android alarm driver"
 	depends on RTC_CLASS
@@ -80,7 +78,6 @@ config ANDROID_ALARM_OLDDRV_COMPAT
 	  Provides preprocessor alias to aid compatability with
 	  older out-of-tree drivers that use the Android Alarm
 	  in-kernel API. This will be removed eventually.
-
 endif # if ANDROID
 
 endmenu
diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile
index 9b6c9ed..045d17b 100644
--- a/drivers/staging/android/Makefile
+++ b/drivers/staging/android/Makefile
@@ -6,6 +6,5 @@ obj-$(CONFIG_ANDROID_RAM_CONSOLE)	+= ram_console.o
 obj-$(CONFIG_ANDROID_TIMED_OUTPUT)	+= timed_output.o
 obj-$(CONFIG_ANDROID_TIMED_GPIO)	+= timed_gpio.o
 obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER)	+= lowmemorykiller.o
-obj-$(CONFIG_ANDROID_SWITCH)		+= switch/
 obj-$(CONFIG_ANDROID_INTF_ALARM)	+= alarm.o
 obj-$(CONFIG_ANDROID_INTF_ALARM_DEV)	+= alarm-dev.o
diff --git a/drivers/staging/android/switch/Kconfig b/drivers/staging/android/switch/Kconfig
deleted file mode 100644
index 36846f6..0000000
--- a/drivers/staging/android/switch/Kconfig
+++ /dev/null
@@ -1,11 +0,0 @@
-menuconfig ANDROID_SWITCH
-	tristate "Android Switch class support"
-	help
-	  Say Y here to enable Android switch class support. This allows
-	  monitoring switches by userspace via sysfs and uevent.
-
-config ANDROID_SWITCH_GPIO
-	tristate "Android GPIO Switch support"
-	depends on GENERIC_GPIO && ANDROID_SWITCH
-	help
-	  Say Y here to enable GPIO based switch support.
diff --git a/drivers/staging/android/switch/Makefile b/drivers/staging/android/switch/Makefile
deleted file mode 100644
index d76bfdc..0000000
--- a/drivers/staging/android/switch/Makefile
+++ /dev/null
@@ -1,4 +0,0 @@
-# Android Switch Class Driver
-obj-$(CONFIG_ANDROID_SWITCH)		+= switch_class.o
-obj-$(CONFIG_ANDROID_SWITCH_GPIO)	+= switch_gpio.o
-
diff --git a/drivers/staging/android/switch/switch.h b/drivers/staging/android/switch/switch.h
deleted file mode 100644
index 4fcb310..0000000
--- a/drivers/staging/android/switch/switch.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- *  Switch class driver
- *
- * Copyright (C) 2008 Google, Inc.
- * Author: Mike Lockwood <lockwood@android.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
-*/
-
-#ifndef __LINUX_SWITCH_H__
-#define __LINUX_SWITCH_H__
-
-struct switch_dev {
-	const char	*name;
-	struct device	*dev;
-	int		index;
-	int		state;
-
-	ssize_t	(*print_name)(struct switch_dev *sdev, char *buf);
-	ssize_t	(*print_state)(struct switch_dev *sdev, char *buf);
-};
-
-struct gpio_switch_platform_data {
-	const char *name;
-	unsigned	gpio;
-
-	/* if NULL, switch_dev.name will be printed */
-	const char *name_on;
-	const char *name_off;
-	/* if NULL, "0" or "1" will be printed */
-	const char *state_on;
-	const char *state_off;
-};
-
-extern int switch_dev_register(struct switch_dev *sdev);
-extern void switch_dev_unregister(struct switch_dev *sdev);
-
-static inline int switch_get_state(struct switch_dev *sdev)
-{
-	return sdev->state;
-}
-
-extern void switch_set_state(struct switch_dev *sdev, int state);
-
-#endif /* __LINUX_SWITCH_H__ */
diff --git a/drivers/staging/android/switch/switch_class.c b/drivers/staging/android/switch/switch_class.c
deleted file mode 100644
index 7468044..0000000
--- a/drivers/staging/android/switch/switch_class.c
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * switch_class.c
- *
- * Copyright (C) 2008 Google, Inc.
- * Author: Mike Lockwood <lockwood@android.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
-*/
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/fs.h>
-#include <linux/err.h>
-#include "switch.h"
-
-struct class *switch_class;
-static atomic_t device_count;
-
-static ssize_t state_show(struct device *dev, struct device_attribute *attr,
-		char *buf)
-{
-	struct switch_dev *sdev = (struct switch_dev *)
-		dev_get_drvdata(dev);
-
-	if (sdev->print_state) {
-		int ret = sdev->print_state(sdev, buf);
-		if (ret >= 0)
-			return ret;
-	}
-	return sprintf(buf, "%d\n", sdev->state);
-}
-
-static ssize_t name_show(struct device *dev, struct device_attribute *attr,
-		char *buf)
-{
-	struct switch_dev *sdev = (struct switch_dev *)
-		dev_get_drvdata(dev);
-
-	if (sdev->print_name) {
-		int ret = sdev->print_name(sdev, buf);
-		if (ret >= 0)
-			return ret;
-	}
-	return sprintf(buf, "%s\n", sdev->name);
-}
-
-static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, state_show, NULL);
-static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, name_show, NULL);
-
-void switch_set_state(struct switch_dev *sdev, int state)
-{
-	char name_buf[120];
-	char state_buf[120];
-	char *prop_buf;
-	char *envp[3];
-	int env_offset = 0;
-	int length;
-
-	if (sdev->state != state) {
-		sdev->state = state;
-
-		prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
-		if (prop_buf) {
-			length = name_show(sdev->dev, NULL, prop_buf);
-			if (length > 0) {
-				if (prop_buf[length - 1] == '\n')
-					prop_buf[length - 1] = 0;
-				snprintf(name_buf, sizeof(name_buf),
-					"SWITCH_NAME=%s", prop_buf);
-				envp[env_offset++] = name_buf;
-			}
-			length = state_show(sdev->dev, NULL, prop_buf);
-			if (length > 0) {
-				if (prop_buf[length - 1] == '\n')
-					prop_buf[length - 1] = 0;
-				snprintf(state_buf, sizeof(state_buf),
-					"SWITCH_STATE=%s", prop_buf);
-				envp[env_offset++] = state_buf;
-			}
-			envp[env_offset] = NULL;
-			kobject_uevent_env(&sdev->dev->kobj, KOBJ_CHANGE, envp);
-			free_page((unsigned long)prop_buf);
-		} else {
-			printk(KERN_ERR "out of memory in switch_set_state\n");
-			kobject_uevent(&sdev->dev->kobj, KOBJ_CHANGE);
-		}
-	}
-}
-EXPORT_SYMBOL_GPL(switch_set_state);
-
-static int create_switch_class(void)
-{
-	if (!switch_class) {
-		switch_class = class_create(THIS_MODULE, "switch");
-		if (IS_ERR(switch_class))
-			return PTR_ERR(switch_class);
-		atomic_set(&device_count, 0);
-	}
-
-	return 0;
-}
-
-int switch_dev_register(struct switch_dev *sdev)
-{
-	int ret;
-
-	if (!switch_class) {
-		ret = create_switch_class();
-		if (ret < 0)
-			return ret;
-	}
-
-	sdev->index = atomic_inc_return(&device_count);
-	sdev->dev = device_create(switch_class, NULL,
-		MKDEV(0, sdev->index), NULL, sdev->name);
-	if (IS_ERR(sdev->dev))
-		return PTR_ERR(sdev->dev);
-
-	ret = device_create_file(sdev->dev, &dev_attr_state);
-	if (ret < 0)
-		goto err_create_file_1;
-	ret = device_create_file(sdev->dev, &dev_attr_name);
-	if (ret < 0)
-		goto err_create_file_2;
-
-	dev_set_drvdata(sdev->dev, sdev);
-	sdev->state = 0;
-	return 0;
-
-err_create_file_2:
-	device_remove_file(sdev->dev, &dev_attr_state);
-err_create_file_1:
-	device_destroy(switch_class, MKDEV(0, sdev->index));
-	printk(KERN_ERR "switch: Failed to register driver %s\n", sdev->name);
-
-	return ret;
-}
-EXPORT_SYMBOL_GPL(switch_dev_register);
-
-void switch_dev_unregister(struct switch_dev *sdev)
-{
-	device_remove_file(sdev->dev, &dev_attr_name);
-	device_remove_file(sdev->dev, &dev_attr_state);
-	device_destroy(switch_class, MKDEV(0, sdev->index));
-	dev_set_drvdata(sdev->dev, NULL);
-}
-EXPORT_SYMBOL_GPL(switch_dev_unregister);
-
-static int __init switch_class_init(void)
-{
-	return create_switch_class();
-}
-
-static void __exit switch_class_exit(void)
-{
-	class_destroy(switch_class);
-}
-
-module_init(switch_class_init);
-module_exit(switch_class_exit);
-
-MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
-MODULE_DESCRIPTION("Switch class driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/staging/android/switch/switch_gpio.c b/drivers/staging/android/switch/switch_gpio.c
deleted file mode 100644
index 38b2c2f..0000000
--- a/drivers/staging/android/switch/switch_gpio.c
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * switch_gpio.c
- *
- * Copyright (C) 2008 Google, Inc.
- * Author: Mike Lockwood <lockwood@android.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
-*/
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/workqueue.h>
-#include <linux/gpio.h>
-#include "switch.h"
-
-struct gpio_switch_data {
-	struct switch_dev sdev;
-	unsigned gpio;
-	const char *name_on;
-	const char *name_off;
-	const char *state_on;
-	const char *state_off;
-	int irq;
-	struct work_struct work;
-};
-
-static void gpio_switch_work(struct work_struct *work)
-{
-	int state;
-	struct gpio_switch_data	*data =
-		container_of(work, struct gpio_switch_data, work);
-
-	state = gpio_get_value(data->gpio);
-	switch_set_state(&data->sdev, state);
-}
-
-static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
-{
-	struct gpio_switch_data *switch_data =
-	    (struct gpio_switch_data *)dev_id;
-
-	schedule_work(&switch_data->work);
-	return IRQ_HANDLED;
-}
-
-static ssize_t switch_gpio_print_state(struct switch_dev *sdev, char *buf)
-{
-	struct gpio_switch_data	*switch_data =
-		container_of(sdev, struct gpio_switch_data, sdev);
-	const char *state;
-	if (switch_get_state(sdev))
-		state = switch_data->state_on;
-	else
-		state = switch_data->state_off;
-
-	if (state)
-		return sprintf(buf, "%s\n", state);
-	return -1;
-}
-
-static int gpio_switch_probe(struct platform_device *pdev)
-{
-	struct gpio_switch_platform_data *pdata = pdev->dev.platform_data;
-	struct gpio_switch_data *switch_data;
-	int ret = 0;
-
-	if (!pdata)
-		return -EBUSY;
-
-	switch_data = kzalloc(sizeof(struct gpio_switch_data), GFP_KERNEL);
-	if (!switch_data)
-		return -ENOMEM;
-
-	switch_data->sdev.name = pdata->name;
-	switch_data->gpio = pdata->gpio;
-	switch_data->name_on = pdata->name_on;
-	switch_data->name_off = pdata->name_off;
-	switch_data->state_on = pdata->state_on;
-	switch_data->state_off = pdata->state_off;
-	switch_data->sdev.print_state = switch_gpio_print_state;
-
-	ret = switch_dev_register(&switch_data->sdev);
-	if (ret < 0)
-		goto err_switch_dev_register;
-
-	ret = gpio_request(switch_data->gpio, pdev->name);
-	if (ret < 0)
-		goto err_request_gpio;
-
-	ret = gpio_direction_input(switch_data->gpio);
-	if (ret < 0)
-		goto err_set_gpio_input;
-
-	INIT_WORK(&switch_data->work, gpio_switch_work);
-
-	switch_data->irq = gpio_to_irq(switch_data->gpio);
-	if (switch_data->irq < 0) {
-		ret = switch_data->irq;
-		goto err_detect_irq_num_failed;
-	}
-
-	ret = request_irq(switch_data->irq, gpio_irq_handler,
-			  IRQF_TRIGGER_LOW, pdev->name, switch_data);
-	if (ret < 0)
-		goto err_request_irq;
-
-	/* Perform initial detection */
-	gpio_switch_work(&switch_data->work);
-
-	return 0;
-
-err_request_irq:
-err_detect_irq_num_failed:
-err_set_gpio_input:
-	gpio_free(switch_data->gpio);
-err_request_gpio:
-	switch_dev_unregister(&switch_data->sdev);
-err_switch_dev_register:
-	kfree(switch_data);
-
-	return ret;
-}
-
-static int __devexit gpio_switch_remove(struct platform_device *pdev)
-{
-	struct gpio_switch_data *switch_data = platform_get_drvdata(pdev);
-
-	cancel_work_sync(&switch_data->work);
-	gpio_free(switch_data->gpio);
-	switch_dev_unregister(&switch_data->sdev);
-	kfree(switch_data);
-
-	return 0;
-}
-
-static struct platform_driver gpio_switch_driver = {
-	.probe		= gpio_switch_probe,
-	.remove		= __devexit_p(gpio_switch_remove),
-	.driver		= {
-		.name	= "switch-gpio",
-		.owner	= THIS_MODULE,
-	},
-};
-
-static int __init gpio_switch_init(void)
-{
-	return platform_driver_register(&gpio_switch_driver);
-}
-
-static void __exit gpio_switch_exit(void)
-{
-	platform_driver_unregister(&gpio_switch_driver);
-}
-
-module_init(gpio_switch_init);
-module_exit(gpio_switch_exit);
-
-MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
-MODULE_DESCRIPTION("GPIO Switch driver");
-MODULE_LICENSE("GPL");
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* Re: [PATCH] Remove "switch" class in drivers/staging/android/switch
  2012-04-23 11:19                             ` [PATCH] Remove "switch" class in drivers/staging/android/switch MyungJoo Ham
@ 2012-04-23 20:25                               ` Greg KH
  0 siblings, 0 replies; 51+ messages in thread
From: Greg KH @ 2012-04-23 20:25 UTC (permalink / raw)
  To: MyungJoo Ham; +Cc: Kyungmin Park, cw00.choi, myungjoo.ham, LKML

On Mon, Apr 23, 2012 at 08:19:57PM +0900, MyungJoo Ham wrote:
> Because extcon can also be a switch class for legacy userspace (Android)
> and is a superset of switch class in drivers/staging/android/switch,
> switch class may be removed.
> 
> - Remove switch class
> - Remove switch class consideration in extcon class
> 
> Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>

Very nice, now applied.

Thanks again for pushing this code through, it's much appreciated.

greg k-h

^ permalink raw reply	[flat|nested] 51+ messages in thread

end of thread, other threads:[~2012-04-23 20:25 UTC | newest]

Thread overview: 51+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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                         ` [PATCH v8 resend 5/6] Extcon: support mutually exclusive relation between cables MyungJoo Ham
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

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.