From patchwork Mon Feb 27 12:15:35 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: MyungJoo Ham X-Patchwork-Id: 143182 Return-Path: X-Original-To: incoming-imx@patchwork.ozlabs.org Delivered-To: patchwork-incoming-imx@bilbo.ozlabs.org Received: from merlin.infradead.org (merlin.infradead.org [IPv6:2001:4978:20e::2]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 0C7D6B6F9F for ; Mon, 27 Feb 2012 23:18:41 +1100 (EST) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1S1zUn-00033X-SM; Mon, 27 Feb 2012 12:16:03 +0000 Received: from mailout2.samsung.com ([203.254.224.25]) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1S1zUQ-0002yy-0N for linux-arm-kernel@lists.infradead.org; Mon, 27 Feb 2012 12:15:41 +0000 MIME-version: 1.0 Received: from epcpsbgm2.samsung.com (mailout2.samsung.com [203.254.224.25]) by mailout2.samsung.com (Oracle Communications Messaging Exchange Server 7u4-19.01 64bit (built Sep 7 2010)) with ESMTP id <0M0100IXTWPMLY60@mailout2.samsung.com> for linux-arm-kernel@lists.infradead.org; Mon, 27 Feb 2012 21:15:31 +0900 (KST) X-AuditID: cbfee61b-b7c62ae000000989-5d-4f4b73e3d778 Received: from epmmp1.local.host ( [203.254.227.16]) by epcpsbgm2.samsung.com (MMPCPMTA) with SMTP id 59.71.02441.3E37B4F4; Mon, 27 Feb 2012 21:15:31 +0900 (KST) Received: from localhost.localdomain ([165.213.219.116]) by mmp1.samsung.com (Oracle Communications Messaging Exchange Server 7u4-19.01 64bit (built Sep 7 2010)) with ESMTPA id <0M0100HS5WPRYM20@mmp1.samsung.com> for linux-arm-kernel@lists.infradead.org; Mon, 27 Feb 2012 21:15:31 +0900 (KST) From: MyungJoo Ham To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org Subject: [PATCH v6 1/5] Extcon (external connector): import Android's switch class and modify. Date: Mon, 27 Feb 2012 21:15:35 +0900 Message-id: <1330344939-18394-2-git-send-email-myungjoo.ham@samsung.com> X-Mailer: git-send-email 1.7.4.1 In-reply-to: <1330344939-18394-1-git-send-email-myungjoo.ham@samsung.com> References: <1328856038-21912-1-git-send-email-myungjoo.ham@samsung.com> <1330344939-18394-1-git-send-email-myungjoo.ham@samsung.com> X-Brightmail-Tracker: AAAAAA== X-Spam-Note: CRM114 invocation failed X-Spam-Score: -6.9 (------) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-6.9 points) pts rule name description ---- ---------------------- -------------------------------------------------- -5.0 RCVD_IN_DNSWL_HI RBL: Sender listed at http://www.dnswl.org/, high trust [203.254.224.25 listed in list.dnswl.org] -0.0 SPF_HELO_PASS SPF: HELO matches SPF record -0.0 T_RP_MATCHES_RCVD Envelope sender domain matches handover relay domain -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: Mark Brown , Arnd Bergmann , NeilBrown , gregkh@linuxfoundation.org, Linus Walleij , Dmitry Torokhov , Randy Dunlap , Kyungmin Park , myungjoo.ham@gmail.com, John Stultz , =?UTF-8?q?Arve=20Hj=C3=B8nnevag?= , Joerg Roedel , Morten CHRISTIANSEN , Mike Lockwood X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.14 Precedence: list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+incoming-imx=patchwork.ozlabs.org@lists.infradead.org List-Id: linux-imx-kernel.lists.patchwork.ozlabs.org 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 switch: Use device_create instead of device_create_drvdata. Author: Arve Hjønnevåg 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 Signed-off-by: Kyungmin Park --- 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 | 226 ++++++++++++++++++++++++++ include/linux/extcon.h | 82 ++++++++++ 7 files changed, 370 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 +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 +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 +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..9bc062b --- /dev/null +++ b/drivers/extcon/extcon_class.c @@ -0,0 +1,226 @@ +/* + * drivers/extcon/extcon_class.c + * + * External connector (extcon) class driver + * + * Copyright (C) 2012 Samsung Electronics + * Author: Donggeun Kim + * Author: MyungJoo Ham + * + * based on android/drivers/switch/switch_class.c + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +struct class *extcon_class; + +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), + "SWITCH_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), + "SWITCH_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) { +#ifdef CONFIG_ANDROID + extcon_class = class_create(THIS_MODULE, "switch"); +#else /* !CONFIG_ANDROID */ + extcon_class = class_create(THIS_MODULE, "extcon"); +#endif /* CONFIG_ANDROID */ + if (IS_ERR(extcon_class)) + return PTR_ERR(extcon_class); + extcon_class->dev_attrs = extcon_attrs; + } + + 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; + } + + 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 "); +MODULE_AUTHOR("Donggeun Kim "); +MODULE_AUTHOR("MyungJoo Ham "); +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 + * Author: MyungJoo Ham + * + * based on switch class driver + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * 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__ */