From patchwork Thu Aug 13 19:12:20 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Amit Pundir X-Patchwork-Id: 52412 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-lb0-f197.google.com (mail-lb0-f197.google.com [209.85.217.197]) by patches.linaro.org (Postfix) with ESMTPS id 10F272031F for ; Thu, 13 Aug 2015 19:13:11 +0000 (UTC) Received: by lbck9 with SMTP id k9sf19491355lbc.0 for ; Thu, 13 Aug 2015 12:13:09 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:delivered-to:from:to:cc:subject:date:message-id :in-reply-to:references:author:mime-version:content-type :content-transfer-encoding:sender:precedence:list-id :x-original-sender:x-original-authentication-results:mailing-list :list-post:list-help:list-archive:list-unsubscribe; bh=OyGfR5P1BFRqTN6WCaYDij7iC99AU24l2y1S94nvEss=; b=MuDFZQOM1Hzz4T1ZJJzPHeLN9bUBrt9aW9PUhcvXDHeRBn4faqNmPhqmgHlpA5GWxA 3LxuFGdN69RPnaij1pRMoQawJvcecVNVYDfRvR4nriBX8bxM0n2rF1XuHHlnIqzjyFkP zJtEzFFfQVkPrRd6mwjbT7FS+ET+VNqXOtGk81w25BdLJ+ZjH3O7N29JgdClwpi0PSR5 6b6vDauH4yJy9LBpphLDDm+lydIvCWKUFrpOW5n3xPgsXw7TqgXzVT9FGPL2NIhSKx2E GF6PwaH1UFsIl604sgUXZjdm9No25KBiwh44R2mS8rd80bT+45nGTR8miASOZjMHd+ty iqeA== X-Gm-Message-State: ALoCoQnbBzZwWYIagIfC5iUWFLBWi6XhDS2E7UDu3X8+t9M9WjHSVTDf569W/3H/ZQSDFL9edMsB X-Received: by 10.112.54.166 with SMTP id k6mr11744365lbp.0.1439493189653; Thu, 13 Aug 2015 12:13:09 -0700 (PDT) X-BeenThere: patchwork-forward@linaro.org Received: by 10.152.170.232 with SMTP id ap8ls200139lac.18.gmail; Thu, 13 Aug 2015 12:13:09 -0700 (PDT) X-Received: by 10.112.122.78 with SMTP id lq14mr12643389lbb.5.1439493189470; Thu, 13 Aug 2015 12:13:09 -0700 (PDT) Received: from mail-la0-f48.google.com (mail-la0-f48.google.com. [209.85.215.48]) by mx.google.com with ESMTPS id am8si3625010lac.135.2015.08.13.12.13.09 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 13 Aug 2015 12:13:09 -0700 (PDT) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.215.48 as permitted sender) client-ip=209.85.215.48; Received: by lalv9 with SMTP id v9so31618171lal.0 for ; Thu, 13 Aug 2015 12:13:09 -0700 (PDT) X-Received: by 10.112.209.106 with SMTP id ml10mr37879102lbc.112.1439493189308; Thu, 13 Aug 2015 12:13:09 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.112.7.198 with SMTP id l6csp1131030lba; Thu, 13 Aug 2015 12:13:07 -0700 (PDT) X-Received: by 10.66.102.2 with SMTP id fk2mr16201133pab.9.1439493187081; Thu, 13 Aug 2015 12:13:07 -0700 (PDT) Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id dj4si5124706pbc.102.2015.08.13.12.13.05; Thu, 13 Aug 2015 12:13:07 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754040AbbHMTM4 (ORCPT + 28 others); Thu, 13 Aug 2015 15:12:56 -0400 Received: from mail-pd0-f170.google.com ([209.85.192.170]:34583 "EHLO mail-pd0-f170.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753999AbbHMTMs (ORCPT ); Thu, 13 Aug 2015 15:12:48 -0400 Received: by pdbfa8 with SMTP id fa8so22594749pdb.1 for ; Thu, 13 Aug 2015 12:12:48 -0700 (PDT) X-Received: by 10.70.43.163 with SMTP id x3mr55972931pdl.151.1439493167923; Thu, 13 Aug 2015 12:12:47 -0700 (PDT) Received: from localhost.localdomain ([106.51.20.216]) by smtp.gmail.com with ESMTPSA id hz5sm3520572pbb.39.2015.08.13.12.12.40 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 13 Aug 2015 12:12:46 -0700 (PDT) From: Amit Pundir To: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-api@vger.kernel.org Cc: Mike Lockwood , Benoit Goby , Colin Cross , =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= , Peter Oh , Greg Hackmann , Badhri Jagan Sridharan , Android Kernel Team , Greg Kroah-Hartman , Jonathan Corbet , Felipe Balbi , Andrzej Pietrasiewicz , Laurent Pinchart , Yegor Yefremov , Philippe Reynes , John Stultz , Sumit Semwal Subject: [RFC][PATCH 2/2] usb: gadget: configfs: notify userspace of usb state changes Date: Fri, 14 Aug 2015 00:42:20 +0530 Message-Id: <1439493140-22207-3-git-send-email-amit.pundir@linaro.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1439493140-22207-1-git-send-email-amit.pundir@linaro.org> References: <1439493140-22207-1-git-send-email-amit.pundir@linaro.org> Author: Amit Pundir MIME-Version: 1.0 Sender: linux-kernel-owner@vger.kernel.org Precedence: list List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: amit.pundir@linaro.org X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.215.48 as permitted sender) smtp.mailfrom=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , This is more of an RFC than an actual submission. There are few scattered #ifdefs..#endifs here and there which still need to be taken care of before going for actual submission. Currently there is no way with the upstream ConfigFS gadget to communicate state changes (connected, disconnected, configured), at the gadget level. Instead such state changes are handled function by function independently I presume. This is problematic, because some coordination between the functions, across the state changes, may be desired at the userspace level. Thus to address this issue, this patch send uevents to allow userspace to be notified of these usb state changes, allowing userspace to respond and configure the configfs gadget appropriately. This patch is based on an Android patchset originaly authored by Badhri Jagan Sridharan to send uevent notifications to Android userpace for USB state changes. I've folded his patches together and modified it enough that I don't want him to be blamed for any mistakes I've made condensing his patches down. This patch introduces USB_CONFIGFS_UEVENT Kconfig to handle userspace notifications of usb state changes, and add setup and disconnect functions to intercept the setup requests from the usb_core. It also creates a sysfs device class entry and a device attribute (state) to read and respond to gadget's current state from userspace. As of now this sysfs device class (/sys/class/android_usb) and gadget device (/sys/class/android_usb/android0) with state attribute (/sys/class/android_usb/android0/state) are strictly tied up to facilitate Android userspace requests. But going forward we may want to bring all function devices (hid, printer etc) under a unified usb gadget device class e.g. /sys/class/usb_gadget/g_{func0,func1} etc.. Also I think it make sense to add this state attribute to the configfs usb gadget itself i.e. have something like /config/usb_gadget/g1/state to read USB gadget's current state. Since it is going to be consistent throughout all the functions tied up to that gadget. Again this is just an initial RFC, thoughts and feedback would be greatly appreciated. Cc: Mike Lockwood Cc: Benoit Goby Cc: Colin Cross Cc: Arve Hjønnevåg Cc: Peter Oh Cc: Greg Hackmann Cc: Badhri Jagan Sridharan Cc: Android Kernel Team Cc: Greg Kroah-Hartman Cc: Jonathan Corbet Cc: Felipe Balbi Cc: Andrzej Pietrasiewicz Cc: Laurent Pinchart Cc: Yegor Yefremov Cc: Philippe Reynes Cc: John Stultz Cc: Sumit Semwal Signed-off-by: Amit Pundir --- drivers/usb/gadget/Kconfig | 8 ++ drivers/usb/gadget/configfs.c | 201 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 207 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 65d110d..e1d1fc1 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -464,6 +464,14 @@ config USB_CONFIGFS_F_MTP the file level. Thus exposing the relevant content but hiding the system/restricted files. +config USB_CONFIGFS_UEVENT + bool "Uevent notification of Gadget state" + depends on USB_CONFIGFS + help + Enable uevent notifications to userspace when the gadget + state changes. The gadget can be in any of the following + three states: "CONNECTED/DISCONNECTED/CONFIGURED" + source "drivers/usb/gadget/legacy/Kconfig" endchoice diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 289e201..1575343 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -9,6 +9,15 @@ #include "u_f.h" #include "u_os_desc.h" +#ifdef CONFIG_USB_CONFIGFS_UEVENT +#include +#include +#include + +static struct class *usb_gadget_class; +static struct device *usb_gadget_device; +#endif + int check_user_usb_string(const char *name, struct usb_gadget_strings *stringtab_dev) { @@ -63,6 +72,12 @@ struct gadget_info { bool use_os_desc; char b_vendor_code; char qw_sign[OS_STRING_QW_SIGN_LEN]; +#ifdef CONFIG_USB_CONFIGFS_UEVENT + bool connected; + bool sw_connected; + struct work_struct work; + struct device *dev; +#endif }; struct config_usb_cfg { @@ -1444,13 +1459,143 @@ static void configfs_composite_unbind(struct usb_gadget *gadget) set_gadget_data(gadget, NULL); } +#ifdef CONFIG_USB_CONFIGFS_UEVENT +static ssize_t state_show(struct device *pdev, struct device_attribute *attr, + char *buf) +{ + struct gadget_info *dev = dev_get_drvdata(pdev); + struct usb_composite_dev *cdev; + char *state = "DISCONNECTED"; + unsigned long flags; + + if (!dev) + goto out; + + cdev = &dev->cdev; + + if (!cdev) + goto out; + + spin_lock_irqsave(&cdev->lock, flags); + if (cdev->config) + state = "CONFIGURED"; + else if (dev->connected) + state = "CONNECTED"; + spin_unlock_irqrestore(&cdev->lock, flags); +out: + return sprintf(buf, "%s\n", state); +} + +static DEVICE_ATTR(state, S_IRUGO, state_show, NULL); + +static struct device_attribute *cfs_usb_attributes[] = { + &dev_attr_state, + NULL +}; + +static void cfs_uevent_work(struct work_struct *data) +{ + struct gadget_info *gi = container_of(data, struct gadget_info, work); + struct usb_composite_dev *cdev = &gi->cdev; + char *disconnected[2] = { "USB_STATE=DISCONNECTED", NULL }; + char *connected[2] = { "USB_STATE=CONNECTED", NULL }; + char *configured[2] = { "USB_STATE=CONFIGURED", NULL }; + /* 0-connected 1-configured 2-disconnected*/ + bool status[3] = { false, false, false }; + unsigned long flags; + bool uevent_sent = false; + + spin_lock_irqsave(&cdev->lock, flags); + if (cdev->config) + status[1] = true; + + if (gi->connected != gi->sw_connected) { + if (gi->connected) + status[0] = true; + else + status[2] = true; + gi->sw_connected = gi->connected; + } + spin_unlock_irqrestore(&cdev->lock, flags); + + if (status[0]) { + kobject_uevent_env(&usb_gadget_device->kobj, + KOBJ_CHANGE, connected); + pr_info("%s: sent uevent %s\n", __func__, connected[0]); + uevent_sent = true; + } + + if (status[1]) { + kobject_uevent_env(&usb_gadget_device->kobj, + KOBJ_CHANGE, configured); + pr_info("%s: sent uevent %s\n", __func__, configured[0]); + uevent_sent = true; + } + + if (status[2]) { + kobject_uevent_env(&usb_gadget_device->kobj, + KOBJ_CHANGE, disconnected); + pr_info("%s: sent uevent %s\n", __func__, disconnected[0]); + uevent_sent = true; + } + + if (!uevent_sent) { + pr_info("%s: did not send uevent (%d %d %p)\n", __func__, + gi->connected, gi->sw_connected, cdev->config); + } +} + +static int cfs_uevent_setup(struct usb_gadget *gadget, + const struct usb_ctrlrequest *c) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + unsigned long flags; + struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev); + int value = -EOPNOTSUPP; + + spin_lock_irqsave(&cdev->lock, flags); + if (!gi->connected) { + gi->connected = 1; + schedule_work(&gi->work); + } + spin_unlock_irqrestore(&cdev->lock, flags); + + value = composite_setup(gadget, c); + + spin_lock_irqsave(&cdev->lock, flags); + if (c->bRequest == USB_REQ_SET_CONFIGURATION && + cdev->config) { + schedule_work(&gi->work); + } + spin_unlock_irqrestore(&cdev->lock, flags); + + return value; +} + +static void cfs_uevent_disconnect(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev); + + gi->connected = 0; + schedule_work(&gi->work); + composite_disconnect(gadget); +} +#endif + static const struct usb_gadget_driver configfs_driver_template = { .bind = configfs_composite_bind, .unbind = configfs_composite_unbind, +#ifdef CONFIG_USB_CONFIGFS_UEVENT + .setup = cfs_uevent_setup, + .reset = cfs_uevent_disconnect, + .disconnect = cfs_uevent_disconnect, +#else .setup = composite_setup, .reset = composite_disconnect, .disconnect = composite_disconnect, +#endif .suspend = composite_suspend, .resume = composite_resume, @@ -1462,16 +1607,21 @@ static const struct usb_gadget_driver configfs_driver_template = { }, }; + static struct config_group *gadgets_make( struct config_group *group, const char *name) { struct gadget_info *gi; +#ifdef CONFIG_USB_CONFIGFS_UEVENT + struct device_attribute **attrs; + struct device_attribute *attr; + int err; +#endif gi = kzalloc(sizeof(*gi), GFP_KERNEL); if (!gi) return ERR_PTR(-ENOMEM); - gi->group.default_groups = gi->default_groups; gi->group.default_groups[0] = &gi->functions_group; gi->group.default_groups[1] = &gi->configs_group; @@ -1507,9 +1657,26 @@ static struct config_group *gadgets_make( gi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL); gi->composite.name = gi->composite.gadget_driver.function; - if (!gi->composite.gadget_driver.function) +#ifdef CONFIG_USB_CONFIGFS_UEVENT + INIT_WORK(&gi->work, cfs_uevent_work); + usb_gadget_device = device_create(usb_gadget_class, NULL, + MKDEV(0, 0), NULL, "android0"); + if (IS_ERR(usb_gadget_device)) goto err; + dev_set_drvdata(usb_gadget_device, gi); + + attrs = cfs_usb_attributes; + while ((attr = *attrs++)) { + err = device_create_file(usb_gadget_device, attr); + if (err) + goto err1; + } +#endif + + if (!gi->composite.gadget_driver.function) + goto err1; + #ifdef CONFIG_USB_OTG gi->otg.bLength = sizeof(struct usb_otg_descriptor); gi->otg.bDescriptorType = USB_DT_OTG; @@ -1519,13 +1686,31 @@ static struct config_group *gadgets_make( config_group_init_type_name(&gi->group, name, &gadget_root_type); return &gi->group; + +err1: +#ifdef CONFIG_USB_CONFIGFS_UEVENT + attrs = cfs_usb_attributes; + while ((attr = *attrs++)) + device_remove_file(usb_gadget_device, attr); + device_destroy(usb_gadget_device->class, usb_gadget_device->devt); err: +#endif kfree(gi); return ERR_PTR(-ENOMEM); } static void gadgets_drop(struct config_group *group, struct config_item *item) { +#ifdef CONFIG_USB_CONFIGFS_UEVENT + struct device_attribute **attrs; + struct device_attribute *attr; + + attrs = cfs_usb_attributes; + while ((attr = *attrs++)) + device_remove_file(usb_gadget_device, attr); + device_destroy(usb_gadget_device->class, usb_gadget_device->devt); +#endif + config_item_put(item); } @@ -1564,6 +1749,13 @@ static int __init gadget_cfs_init(void) config_group_init(&gadget_subsys.su_group); ret = configfs_register_subsystem(&gadget_subsys); + +#ifdef CONFIG_USB_CONFIGFS_UEVENT + usb_gadget_class = class_create(THIS_MODULE, "android_usb"); + if (IS_ERR(usb_gadget_class)) + return PTR_ERR(usb_gadget_class); +#endif + return ret; } module_init(gadget_cfs_init); @@ -1571,5 +1763,10 @@ module_init(gadget_cfs_init); static void __exit gadget_cfs_exit(void) { configfs_unregister_subsystem(&gadget_subsys); +#ifdef CONFIG_USB_CONFIGFS_UEVENT + if (!IS_ERR(usb_gadget_class)) + class_destroy(usb_gadget_class); +#endif + } module_exit(gadget_cfs_exit);