From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 2605AC44511 for ; Thu, 16 Jul 2026 16:20:14 +0000 (UTC) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 396E884EB6; Thu, 16 Jul 2026 18:09:45 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=linaro.org header.i=@linaro.org header.b="jXthIFdI"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id B3E5384CE8; Thu, 16 Jul 2026 15:44:50 +0200 (CEST) Received: from mail-ed1-x533.google.com (mail-ed1-x533.google.com [IPv6:2a00:1450:4864:20::533]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id B495984C3F for ; Thu, 16 Jul 2026 15:44:47 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=anders.roxell@linaro.org Received: by mail-ed1-x533.google.com with SMTP id 4fb4d7f45d1cf-6983ed43ca1so1165997a12.2 for ; Thu, 16 Jul 2026 06:44:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1784209487; x=1784814287; darn=lists.denx.de; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=KzR/auWKKXT/NYOLQtari+bPMAUwx9Bwt3+mTSVyBvA=; b=jXthIFdIC3RQCdPzJgGw1eX1OEZhJhXUW/Nr+W1LgcXj9GqiaJ03cKqlpJjAWhqhe/ nae6Q+bX3Boxytmb36WQEkluU7V5m8WerVNSLkcGZ29Zf7mHH7ZSX9fNonRv8S+DIFhQ ZnalJuFxvfXsslA9h9CIhuZECSuztt5GyGkHmz5Su3bFs2P1CmByJTRaFcmmt67Gqgkm ZPV7vWumV8DNKcoD/h9gMTm8a5X2Iwj1GRJBWSlpaJEoM8YAfw64f8gaNwQ2K1WUgDQx rtAZpQ8Zq/I9os+z9aOrSjDL980syghN+xZ91J1eTTZkBLKWO+svKqdwOapUmKjozkQP e+yg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784209487; x=1784814287; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=KzR/auWKKXT/NYOLQtari+bPMAUwx9Bwt3+mTSVyBvA=; b=tRDFX28+TQwVM+N8NGaONjcEE7ajbfFSz4bKciXAAgfywqYb9OL1MHqM+6SdR/lykP xOgtHrc/oBRHKpD7Q/piNpCDM6eLagR86IXqL96rAJslIVUqqVc8mfb1TK6MWJXiK9Y1 zfp4xrJhBousj9VjaSad+2TJG6Kaq47QyMLLXlsqfowrzcHPG72cR/PJl5eUx7R9x33d n05WoU70PxwwmiHhxE052dCPGiAnqeZUGamsuUwCD6CnImmh36+h1ezbQN6/Yo1B0/1Y maC6/YcCr9sWTiOpqQNjUaVGJVYKiSvGba4I/BbRxEEttYb2AIgIDZxhpSxFx4hqfbEO AhLA== X-Gm-Message-State: AOJu0YzxPpJtCPdjGRf8AS4T/kvzlAaP/aH+o2E+VAGRc1Cg3E5D1KkE 9WRE9fw5Ck4MqhoIB8Vu4kvHKs+lzDVCd+5X+nBwbb8hV/FAXE49DkSH3MoKjCywHkECga7+tYb YoAKs X-Gm-Gg: AfdE7clsxCk5vpB+t+VELqGlrKW2VbuvGGHE29VuojW7JPqh7P+l+qsq+ITis/XQMaf KGnFkP7LXphPgAqJLiN0uCRYbx6hcd08ttuUAOACf39/kLrEr0+L4AoEv6YvyntzypwzbPRjOSR uv5B23EtNdY32Z0HmJ5U3q5LEPv+68jcr3V3tDMYQ6LsMjda5LVi24IVLoV2eORCnNzawT3/A7K APxoM7OfXiqnc27V1pG7jA7T9ADJbYQctBSsvdPGREV9CkTLdmcznzH4iIbAM4QX8T2v8AkmDVC tNkkcxbaC1wr9JvcohgdNrT5EUR43YGqSveg8mqf80lTY1nifzv18rfmYB6WdMxHRROjZp/ZFDu gwLArV/MWVCOXnzBZghpJBcnqN8LsfaTfhkCEYAFiUEOcKTB26X1IUR4ZOfQWRBRVM9OaYpflEe nZ+dfmRcPjiwVSGm8gpUlUYwDP/hw44AuYpHQXfA== X-Received: by 2002:a05:6402:40cd:b0:698:5889:9bfe with SMTP id 4fb4d7f45d1cf-69c5f0fb931mr6697050a12.3.1784209486878; Thu, 16 Jul 2026 06:44:46 -0700 (PDT) Received: from localhost (c-85-229-7-191.bbcust.telenor.se. [85.229.7.191]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-c168716aa07sm213174766b.14.2026.07.16.06.44.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 16 Jul 2026 06:44:46 -0700 (PDT) From: Anders Roxell To: u-boot@lists.denx.de, marex@denx.de Cc: mkorpershoek@kernel.org, ilias.apalodimas@linaro.org, trini@konsulko.com, michal.simek@amd.com, alchark@flipper.net, jerome.forissier@arm.com, Anders Roxell , Jerome Forissier , Jens Wiklander , Lukasz Majewski Subject: [PATCH v5 66/75] usb: gadget: udc: make udc-core build in u-boot Date: Thu, 16 Jul 2026 15:42:36 +0200 Message-ID: <20260716134305.614278-67-anders.roxell@linaro.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260716134305.614278-1-anders.roxell@linaro.org> References: <20260716134305.614278-1-anders.roxell@linaro.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Mailman-Approved-At: Thu, 16 Jul 2026 18:09:05 +0200 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.39 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.8 at phobos.denx.de X-Virus-Status: Clean The resynced udc-core is kernel code. Drop what u-boot does not need and hook it into the build. Co-developed-by: Jerome Forissier Signed-off-by: Jerome Forissier Co-developed-by: Jens Wiklander Signed-off-by: Jens Wiklander Signed-off-by: Anders Roxell --- drivers/usb/gadget/udc/Makefile | 1 + drivers/usb/gadget/udc/udc-core.c | 1026 +++++------------------------ 2 files changed, 184 insertions(+), 843 deletions(-) diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile index 56b9b123fa16..f80ae6e5add5 100644 --- a/drivers/usb/gadget/udc/Makefile +++ b/drivers/usb/gadget/udc/Makefile @@ -7,4 +7,5 @@ obj-$(CONFIG_USB_DWC3_GADGET) += udc-core.o endif obj-$(CONFIG_$(PHASE_)DM_USB_GADGET) += udc-core.o +obj-$(CONFIG_$(PHASE_)USB_GADGET) += udc-core.o obj-y += udc-uclass.o diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c index d709e24c1fd4..99dc6d14064c 100644 --- a/drivers/usb/gadget/udc/udc-core.c +++ b/drivers/usb/gadget/udc/udc-core.c @@ -6,27 +6,17 @@ * Author: Felipe Balbi */ -#define pr_fmt(fmt) "UDC core: " fmt - -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include -#include -#include #include #include -#include - -#include "trace.h" - -static DEFINE_IDA(gadget_id_numbers); - -static const struct bus_type gadget_bus_type; /** * struct usb_udc - describes one usb device controller @@ -40,7 +30,6 @@ static const struct bus_type gadget_bus_type; * @allow_connect: Indicates whether UDC is allowed to be pulled up. * Set/cleared by gadget_(un)bind_driver() after gadget driver is bound or * unbound. - * @vbus_work: work routine to handle VBUS status change notifications. * @connect_lock: protects udc->started, gadget->connect, * gadget->allow_connect and gadget->deactivate. The routines * usb_gadget_connect_locked(), usb_gadget_disconnect_locked(), @@ -58,15 +47,13 @@ struct usb_udc { bool vbus; bool started; bool allow_connect; - struct work_struct vbus_work; struct mutex connect_lock; }; -static const struct class udc_class; static LIST_HEAD(udc_list); /* Protects udc_list, udc->driver, driver->is_bound, and related calls */ -static DEFINE_MUTEX(udc_lock); +DEFINE_MUTEX(udc_lock); /* ------------------------------------------------------------------------- */ @@ -83,8 +70,6 @@ void usb_ep_set_maxpacket_limit(struct usb_ep *ep, { ep->maxpacket_limit = maxpacket_limit; ep->maxpacket = maxpacket_limit; - - trace_usb_ep_set_maxpacket_limit(ep, 0); } EXPORT_SYMBOL_GPL(usb_ep_set_maxpacket_limit); @@ -119,9 +104,10 @@ int usb_ep_enable(struct usb_ep *ep) /* UDC drivers can't handle endpoints with maxpacket size 0 */ if (!ep->desc || usb_endpoint_maxp(ep->desc) == 0) { +#ifndef CONFIG_SPL_BUILD WARN_ONCE(1, "%s: ep%d (%s) has %s\n", __func__, ep->address, ep->name, (!ep->desc) ? "NULL descriptor" : "maxpacket 0"); - +#endif ret = -EINVAL; goto out; } @@ -133,8 +119,6 @@ int usb_ep_enable(struct usb_ep *ep) ep->enabled = true; out: - trace_usb_ep_enable(ep, ret); - return ret; } EXPORT_SYMBOL_GPL(usb_ep_enable); @@ -167,8 +151,6 @@ int usb_ep_disable(struct usb_ep *ep) ep->enabled = false; out: - trace_usb_ep_disable(ep, ret); - return ret; } EXPORT_SYMBOL_GPL(usb_ep_disable); @@ -190,13 +172,7 @@ EXPORT_SYMBOL_GPL(usb_ep_disable); struct usb_request *usb_ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) { - struct usb_request *req = NULL; - - req = ep->ops->alloc_request(ep, gfp_flags); - - trace_usb_ep_alloc_request(ep, req, req ? 0 : -ENOMEM); - - return req; + return ep->ops->alloc_request(ep, gfp_flags); } EXPORT_SYMBOL_GPL(usb_ep_alloc_request); @@ -212,7 +188,6 @@ EXPORT_SYMBOL_GPL(usb_ep_alloc_request); void usb_ep_free_request(struct usb_ep *ep, struct usb_request *req) { - trace_usb_ep_free_request(ep, req, 0); ep->ops->free_request(ep, req); } EXPORT_SYMBOL_GPL(usb_ep_free_request); @@ -300,8 +275,6 @@ int usb_ep_queue(struct usb_ep *ep, ret = ep->ops->queue(ep, req, gfp_flags); out: - trace_usb_ep_queue(ep, req, ret); - return ret; } EXPORT_SYMBOL_GPL(usb_ep_queue); @@ -325,12 +298,7 @@ EXPORT_SYMBOL_GPL(usb_ep_queue); */ int usb_ep_dequeue(struct usb_ep *ep, struct usb_request *req) { - int ret; - - ret = ep->ops->dequeue(ep, req); - trace_usb_ep_dequeue(ep, req, ret); - - return ret; + return ep->ops->dequeue(ep, req); } EXPORT_SYMBOL_GPL(usb_ep_dequeue); @@ -359,12 +327,7 @@ EXPORT_SYMBOL_GPL(usb_ep_dequeue); */ int usb_ep_set_halt(struct usb_ep *ep) { - int ret; - - ret = ep->ops->set_halt(ep, 1); - trace_usb_ep_set_halt(ep, ret); - - return ret; + return ep->ops->set_halt(ep, 1); } EXPORT_SYMBOL_GPL(usb_ep_set_halt); @@ -385,12 +348,7 @@ EXPORT_SYMBOL_GPL(usb_ep_set_halt); */ int usb_ep_clear_halt(struct usb_ep *ep) { - int ret; - - ret = ep->ops->set_halt(ep, 0); - trace_usb_ep_clear_halt(ep, ret); - - return ret; + return ep->ops->set_halt(ep, 0); } EXPORT_SYMBOL_GPL(usb_ep_clear_halt); @@ -415,8 +373,6 @@ int usb_ep_set_wedge(struct usb_ep *ep) else ret = ep->ops->set_halt(ep, 1); - trace_usb_ep_set_wedge(ep, ret); - return ret; } EXPORT_SYMBOL_GPL(usb_ep_set_wedge); @@ -447,8 +403,6 @@ int usb_ep_fifo_status(struct usb_ep *ep) else ret = -EOPNOTSUPP; - trace_usb_ep_fifo_status(ep, ret); - return ret; } EXPORT_SYMBOL_GPL(usb_ep_fifo_status); @@ -468,8 +422,6 @@ void usb_ep_fifo_flush(struct usb_ep *ep) { if (ep->ops->fifo_flush) ep->ops->fifo_flush(ep); - - trace_usb_ep_fifo_flush(ep, 0); } EXPORT_SYMBOL_GPL(usb_ep_fifo_flush); @@ -484,13 +436,8 @@ EXPORT_SYMBOL_GPL(usb_ep_fifo_flush); */ int usb_gadget_frame_number(struct usb_gadget *gadget) { - int ret; - - ret = gadget->ops->get_frame(gadget); - - trace_usb_gadget_frame_number(gadget, ret); - return ret; + return gadget->ops->get_frame(gadget); } EXPORT_SYMBOL_GPL(usb_gadget_frame_number); @@ -519,39 +466,10 @@ int usb_gadget_wakeup(struct usb_gadget *gadget) ret = gadget->ops->wakeup(gadget); out: - trace_usb_gadget_wakeup(gadget, ret); - return ret; } EXPORT_SYMBOL_GPL(usb_gadget_wakeup); -/** - * usb_gadget_set_remote_wakeup - configures the device remote wakeup feature. - * @gadget:the device being configured for remote wakeup - * @set:value to be configured. - * - * set to one to enable remote wakeup feature and zero to disable it. - * - * returns zero on success, else negative errno. - */ -int usb_gadget_set_remote_wakeup(struct usb_gadget *gadget, int set) -{ - int ret = 0; - - if (!gadget->ops->set_remote_wakeup) { - ret = -EOPNOTSUPP; - goto out; - } - - ret = gadget->ops->set_remote_wakeup(gadget, set); - -out: - trace_usb_gadget_set_remote_wakeup(gadget, ret); - - return ret; -} -EXPORT_SYMBOL_GPL(usb_gadget_set_remote_wakeup); - /** * usb_gadget_set_selfpowered - sets the device selfpowered feature. * @gadget:the device being declared as self-powered @@ -573,8 +491,6 @@ int usb_gadget_set_selfpowered(struct usb_gadget *gadget) ret = gadget->ops->set_selfpowered(gadget, 1); out: - trace_usb_gadget_set_selfpowered(gadget, ret); - return ret; } EXPORT_SYMBOL_GPL(usb_gadget_set_selfpowered); @@ -601,43 +517,10 @@ int usb_gadget_clear_selfpowered(struct usb_gadget *gadget) ret = gadget->ops->set_selfpowered(gadget, 0); out: - trace_usb_gadget_clear_selfpowered(gadget, ret); - return ret; } EXPORT_SYMBOL_GPL(usb_gadget_clear_selfpowered); -/** - * usb_gadget_vbus_connect - Notify controller that VBUS is powered - * @gadget:The device which now has VBUS power. - * Context: can sleep - * - * This call is used by a driver for an external transceiver (or GPIO) - * that detects a VBUS power session starting. Common responses include - * resuming the controller, activating the D+ (or D-) pullup to let the - * host detect that a USB device is attached, and starting to draw power - * (8mA or possibly more, especially after SET_CONFIGURATION). - * - * Returns zero on success, else negative errno. - */ -int usb_gadget_vbus_connect(struct usb_gadget *gadget) -{ - int ret = 0; - - if (!gadget->ops->vbus_session) { - ret = -EOPNOTSUPP; - goto out; - } - - ret = gadget->ops->vbus_session(gadget, 1); - -out: - trace_usb_gadget_vbus_connect(gadget, ret); - - return ret; -} -EXPORT_SYMBOL_GPL(usb_gadget_vbus_connect); - /** * usb_gadget_vbus_draw - constrain controller's VBUS power usage * @gadget:The device whose VBUS usage is being described @@ -652,21 +535,10 @@ EXPORT_SYMBOL_GPL(usb_gadget_vbus_connect); */ int usb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA) { - int ret = 0; - - if (!gadget->ops->vbus_draw) { - ret = -EOPNOTSUPP; - goto out; - } - - ret = gadget->ops->vbus_draw(gadget, mA); - if (!ret) - gadget->mA = mA; - -out: - trace_usb_gadget_vbus_draw(gadget, ret); + if (!gadget->ops->vbus_draw) + return -EOPNOTSUPP; - return ret; + return gadget->ops->vbus_draw(gadget, mA); } EXPORT_SYMBOL_GPL(usb_gadget_vbus_draw); @@ -693,41 +565,37 @@ int usb_gadget_vbus_disconnect(struct usb_gadget *gadget) ret = gadget->ops->vbus_session(gadget, 0); out: - trace_usb_gadget_vbus_disconnect(gadget, ret); - return ret; } EXPORT_SYMBOL_GPL(usb_gadget_vbus_disconnect); static int usb_gadget_connect_locked(struct usb_gadget *gadget) - __must_hold(&gadget->udc->connect_lock) -{ - int ret = 0; - - if (!gadget->ops->pullup) { - ret = -EOPNOTSUPP; - goto out; - } - - if (gadget->deactivated || !gadget->udc->allow_connect || !gadget->udc->started) { - /* - * If the gadget isn't usable (because it is deactivated, - * unbound, or not yet started), we only save the new state. - * The gadget will be connected automatically when it is - * activated/bound/started. - */ - gadget->connected = true; - goto out; - } - - ret = gadget->ops->pullup(gadget, 1); - if (!ret) - gadget->connected = 1; + __must_hold(&gadget->udc->connect_lock) +{ + int ret = 0; + + if (!gadget->ops->pullup) { + ret = -EOPNOTSUPP; + goto out; + } + + if (gadget->deactivated || !gadget->udc->allow_connect || !gadget->udc->started) { + /* + * If the gadget isn't usable (because it is deactivated, + * unbound, or not yet started), we only save the new state. + * The gadget will be connected automatically when it is + * activated/bound/started. + */ + gadget->connected = true; + goto out; + } + + ret = gadget->ops->pullup(gadget, 1); + if (!ret) + gadget->connected = 1; out: - trace_usb_gadget_connect(gadget, ret); - - return ret; + return ret; } /** @@ -742,51 +610,49 @@ out: */ int usb_gadget_connect(struct usb_gadget *gadget) { - int ret; + int ret; - mutex_lock(&gadget->udc->connect_lock); - ret = usb_gadget_connect_locked(gadget); - mutex_unlock(&gadget->udc->connect_lock); + mutex_lock(&gadget->udc->connect_lock); + ret = usb_gadget_connect_locked(gadget); + mutex_unlock(&gadget->udc->connect_lock); - return ret; + return ret; } EXPORT_SYMBOL_GPL(usb_gadget_connect); static int usb_gadget_disconnect_locked(struct usb_gadget *gadget) - __must_hold(&gadget->udc->connect_lock) + __must_hold(&gadget->udc->connect_lock) { - int ret = 0; + int ret = 0; - if (!gadget->ops->pullup) { - ret = -EOPNOTSUPP; - goto out; - } + if (!gadget->ops->pullup) { + ret = -EOPNOTSUPP; + goto out; + } - if (!gadget->connected) - goto out; + if (!gadget->connected) + goto out; - if (gadget->deactivated || !gadget->udc->started) { - /* - * If gadget is deactivated we only save new state. - * Gadget will stay disconnected after activation. - */ - gadget->connected = false; - goto out; - } + if (gadget->deactivated || !gadget->udc->started) { + /* + * If gadget is deactivated we only save new state. + * Gadget will stay disconnected after activation. + */ + gadget->connected = false; + goto out; + } - ret = gadget->ops->pullup(gadget, 0); - if (!ret) - gadget->connected = 0; + ret = gadget->ops->pullup(gadget, 0); + if (!ret) + gadget->connected = 0; - mutex_lock(&udc_lock); - if (gadget->udc->driver) - gadget->udc->driver->disconnect(gadget); - mutex_unlock(&udc_lock); + mutex_lock(&udc_lock); + if (gadget->udc->driver) + gadget->udc->driver->disconnect(gadget); + mutex_unlock(&udc_lock); out: - trace_usb_gadget_disconnect(gadget, ret); - - return ret; + return ret; } /** @@ -804,180 +670,41 @@ out: */ int usb_gadget_disconnect(struct usb_gadget *gadget) { - int ret; + int ret; - mutex_lock(&gadget->udc->connect_lock); - ret = usb_gadget_disconnect_locked(gadget); - mutex_unlock(&gadget->udc->connect_lock); + mutex_lock(&gadget->udc->connect_lock); + ret = usb_gadget_disconnect_locked(gadget); + mutex_unlock(&gadget->udc->connect_lock); - return ret; + return ret; } EXPORT_SYMBOL_GPL(usb_gadget_disconnect); -/** - * usb_gadget_deactivate - deactivate function which is not ready to work - * @gadget: the peripheral being deactivated - * - * This routine may be used during the gadget driver bind() call to prevent - * the peripheral from ever being visible to the USB host, unless later - * usb_gadget_activate() is called. For example, user mode components may - * need to be activated before the system can talk to hosts. - * - * This routine may sleep; it must not be called in interrupt context - * (such as from within a gadget driver's disconnect() callback). - * - * Returns zero on success, else negative errno. - */ -int usb_gadget_deactivate(struct usb_gadget *gadget) -{ - int ret = 0; - - mutex_lock(&gadget->udc->connect_lock); - if (gadget->deactivated) - goto unlock; - - if (gadget->connected) { - ret = usb_gadget_disconnect_locked(gadget); - if (ret) - goto unlock; - - /* - * If gadget was being connected before deactivation, we want - * to reconnect it in usb_gadget_activate(). - */ - gadget->connected = true; - } - gadget->deactivated = true; - -unlock: - mutex_unlock(&gadget->udc->connect_lock); - trace_usb_gadget_deactivate(gadget, ret); - - return ret; -} -EXPORT_SYMBOL_GPL(usb_gadget_deactivate); - -/** - * usb_gadget_activate - activate function which is not ready to work - * @gadget: the peripheral being activated - * - * This routine activates gadget which was previously deactivated with - * usb_gadget_deactivate() call. It calls usb_gadget_connect() if needed. - * - * This routine may sleep; it must not be called in interrupt context. - * - * Returns zero on success, else negative errno. - */ -int usb_gadget_activate(struct usb_gadget *gadget) -{ - int ret = 0; - - mutex_lock(&gadget->udc->connect_lock); - if (!gadget->deactivated) - goto unlock; - - gadget->deactivated = false; - - /* - * If gadget has been connected before deactivation, or became connected - * while it was being deactivated, we call usb_gadget_connect(). - */ - if (gadget->connected) - ret = usb_gadget_connect_locked(gadget); - -unlock: - mutex_unlock(&gadget->udc->connect_lock); - trace_usb_gadget_activate(gadget, ret); - - return ret; -} -EXPORT_SYMBOL_GPL(usb_gadget_activate); - /* ------------------------------------------------------------------------- */ -#ifdef CONFIG_HAS_DMA - -int usb_gadget_map_request_by_dev(struct device *dev, +int usb_gadget_map_request(struct usb_gadget *gadget, struct usb_request *req, int is_in) { if (req->length == 0) return 0; - if (req->sg_was_mapped) { - req->num_mapped_sgs = req->num_sgs; - return 0; - } - - if (req->num_sgs) { - int mapped; - - mapped = dma_map_sg(dev, req->sg, req->num_sgs, - is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - if (mapped == 0) { - dev_err(dev, "failed to map SGs\n"); - return -EFAULT; - } - - req->num_mapped_sgs = mapped; - } else { - if (is_vmalloc_addr(req->buf)) { - dev_err(dev, "buffer is not dma capable\n"); - return -EFAULT; - } else if (object_is_on_stack(req->buf)) { - dev_err(dev, "buffer is on stack\n"); - return -EFAULT; - } - - req->dma = dma_map_single(dev, req->buf, req->length, - is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - - if (dma_mapping_error(dev, req->dma)) { - dev_err(dev, "failed to map buffer\n"); - return -EFAULT; - } - - req->dma_mapped = 1; - } - + req->dma = dma_map_single(req->buf, req->length, + is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); return 0; } -EXPORT_SYMBOL_GPL(usb_gadget_map_request_by_dev); - -int usb_gadget_map_request(struct usb_gadget *gadget, - struct usb_request *req, int is_in) -{ - return usb_gadget_map_request_by_dev(gadget->dev.parent, req, is_in); -} EXPORT_SYMBOL_GPL(usb_gadget_map_request); -void usb_gadget_unmap_request_by_dev(struct device *dev, +void usb_gadget_unmap_request(struct usb_gadget *gadget, struct usb_request *req, int is_in) { - if (req->length == 0 || req->sg_was_mapped) + if (req->length == 0) return; - if (req->num_mapped_sgs) { - dma_unmap_sg(dev, req->sg, req->num_sgs, - is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - - req->num_mapped_sgs = 0; - } else if (req->dma_mapped) { - dma_unmap_single(dev, req->dma, req->length, - is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - req->dma_mapped = 0; - } -} -EXPORT_SYMBOL_GPL(usb_gadget_unmap_request_by_dev); - -void usb_gadget_unmap_request(struct usb_gadget *gadget, - struct usb_request *req, int is_in) -{ - usb_gadget_unmap_request_by_dev(gadget->dev.parent, req, is_in); + dma_unmap_single(req->dma, req->length, + is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); } EXPORT_SYMBOL_GPL(usb_gadget_unmap_request); -#endif /* CONFIG_HAS_DMA */ - /* ------------------------------------------------------------------------- */ /** @@ -991,11 +718,6 @@ EXPORT_SYMBOL_GPL(usb_gadget_unmap_request); void usb_gadget_giveback_request(struct usb_ep *ep, struct usb_request *req) { - if (likely(req->status == 0)) - usb_led_activity(USB_LED_EVENT_GADGET); - - trace_usb_gadget_giveback_request(ep, req, 0); - req->complete(ep, req); } EXPORT_SYMBOL_GPL(usb_gadget_giveback_request); @@ -1111,71 +833,13 @@ EXPORT_SYMBOL_GPL(usb_gadget_check_config); /* ------------------------------------------------------------------------- */ -static void usb_gadget_state_work(struct work_struct *work) -{ - struct usb_gadget *gadget = work_to_gadget(work); - struct usb_udc *udc = gadget->udc; - - if (udc) - sysfs_notify(&udc->dev.kobj, NULL, "state"); -} - void usb_gadget_set_state(struct usb_gadget *gadget, enum usb_device_state state) { gadget->state = state; - schedule_work(&gadget->work); } EXPORT_SYMBOL_GPL(usb_gadget_set_state); -/* ------------------------------------------------------------------------- */ - -/* Acquire connect_lock before calling this function. */ -static int usb_udc_connect_control_locked(struct usb_udc *udc) __must_hold(&udc->connect_lock) -{ - if (udc->vbus) - return usb_gadget_connect_locked(udc->gadget); - else - return usb_gadget_disconnect_locked(udc->gadget); -} - -static void vbus_event_work(struct work_struct *work) -{ - struct usb_udc *udc = container_of(work, struct usb_udc, vbus_work); - - mutex_lock(&udc->connect_lock); - usb_udc_connect_control_locked(udc); - mutex_unlock(&udc->connect_lock); -} - -/** - * usb_udc_vbus_handler - updates the udc core vbus status, and try to - * connect or disconnect gadget - * @gadget: The gadget which vbus change occurs - * @status: The vbus status - * - * The udc driver calls it when it wants to connect or disconnect gadget - * according to vbus status. - * - * This function can be invoked from interrupt context by irq handlers of - * the gadget drivers, however, usb_udc_connect_control() has to run in - * non-atomic context due to the following: - * a. Some of the gadget driver implementations expect the ->pullup - * callback to be invoked in non-atomic context. - * b. usb_gadget_disconnect() acquires udc_lock which is a mutex. - * Hence offload invocation of usb_udc_connect_control() to workqueue. - */ -void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status) -{ - struct usb_udc *udc = gadget->udc; - - if (udc) { - udc->vbus = status; - schedule_work(&udc->vbus_work); - } -} -EXPORT_SYMBOL_GPL(usb_udc_vbus_handler); - /** * usb_gadget_udc_reset - notifies the udc core that bus reset occurs * @gadget: The gadget which bus reset occurs @@ -1277,6 +941,36 @@ static inline void usb_gadget_udc_set_speed(struct usb_udc *udc, gadget->ops->udc_set_speed(gadget, s); } +static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver) +{ + int ret; + + dev_dbg(&udc->dev, "registering UDC driver [%s]\n", + driver->function); + + udc->driver = driver; + + usb_gadget_udc_set_speed(udc, driver->speed); + + ret = driver->bind(udc->gadget); + if (ret) + goto err1; + ret = usb_gadget_udc_start_locked(udc); + if (ret) { + driver->unbind(udc->gadget); + goto err1; + } + usb_gadget_connect(udc->gadget); + + return 0; +err1: + if (ret != -EISNAM) + dev_err(&udc->dev, "failed to start %s: %d\n", + udc->driver->function, ret); + udc->driver = NULL; + return ret; +} + /** * usb_gadget_enable_async_callbacks - tell usb device controller to enable asynchronous callbacks * @udc: The UDC which should enable async callbacks @@ -1336,17 +1030,9 @@ static void usb_udc_release(struct device *dev) struct usb_udc *udc; udc = container_of(dev, struct usb_udc, dev); - dev_dbg(dev, "releasing '%s'\n", dev_name(dev)); kfree(udc); } -static const struct attribute_group *usb_udc_attr_groups[]; - -static void usb_udc_nop_release(struct device *dev) -{ - dev_vdbg(dev, "%s\n", __func__); -} - /** * usb_initialize_gadget - initialize a gadget and its embedded struct device * @parent: the parent device to this udc. Usually the controller driver's @@ -1357,16 +1043,8 @@ static void usb_udc_nop_release(struct device *dev) void usb_initialize_gadget(struct device *parent, struct usb_gadget *gadget, void (*release)(struct device *dev)) { - INIT_WORK(&gadget->work, usb_gadget_state_work); gadget->dev.parent = parent; - - if (release) - gadget->dev.release = release; - else - gadget->dev.release = usb_udc_nop_release; - - device_initialize(&gadget->dev); - gadget->dev.bus = &gadget_bus_type; + gadget->dev.release = release; } EXPORT_SYMBOL_GPL(usb_initialize_gadget); @@ -1386,15 +1064,9 @@ int usb_add_gadget(struct usb_gadget *gadget) if (!udc) goto error; - device_initialize(&udc->dev); udc->dev.release = usb_udc_release; - udc->dev.class = &udc_class; - udc->dev.groups = usb_udc_attr_groups; + udc->dev.class = NULL; udc->dev.parent = gadget->dev.parent; - ret = dev_set_name(&udc->dev, "%s", - kobject_name(&gadget->dev.parent->kobj)); - if (ret) - goto err_put_udc; udc->gadget = gadget; gadget->udc = udc; @@ -1405,50 +1077,11 @@ int usb_add_gadget(struct usb_gadget *gadget) mutex_lock(&udc_lock); list_add_tail(&udc->list, &udc_list); mutex_unlock(&udc_lock); - INIT_WORK(&udc->vbus_work, vbus_event_work); - - ret = device_add(&udc->dev); - if (ret) - goto err_unlist_udc; usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED); - udc->vbus = true; - - ret = ida_alloc(&gadget_id_numbers, GFP_KERNEL); - if (ret < 0) - goto err_del_udc; - gadget->id_number = ret; - dev_set_name(&gadget->dev, "gadget.%d", ret); - - ret = device_add(&gadget->dev); - if (ret) - goto err_free_id; - - ret = sysfs_create_link(&udc->dev.kobj, - &gadget->dev.kobj, "gadget"); - if (ret) - goto err_del_gadget; return 0; - err_del_gadget: - device_del(&gadget->dev); - - err_free_id: - ida_free(&gadget_id_numbers, gadget->id_number); - - err_del_udc: - flush_work(&gadget->work); - device_del(&udc->dev); - - err_unlist_udc: - mutex_lock(&udc_lock); - list_del(&udc->list); - mutex_unlock(&udc_lock); - - err_put_udc: - put_device(&udc->dev); - error: return ret; } @@ -1467,13 +1100,8 @@ EXPORT_SYMBOL_GPL(usb_add_gadget); int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, void (*release)(struct device *dev)) { - int ret; - usb_initialize_gadget(parent, gadget, release); - ret = usb_add_gadget(gadget); - if (ret) - usb_put_gadget(gadget); - return ret; + return usb_add_gadget(gadget); } EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release); @@ -1498,7 +1126,7 @@ char *usb_get_gadget_udc_name(void) mutex_lock(&udc_lock); list_for_each_entry(udc, &udc_list, list) { if (!udc->driver) { - name = kstrdup(udc->gadget->name, GFP_KERNEL); + name = strdup(udc->gadget->name); break; } } @@ -1521,409 +1149,121 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) } EXPORT_SYMBOL_GPL(usb_add_gadget_udc); -/** - * usb_del_gadget - deletes a gadget and unregisters its udc - * @gadget: the gadget to be deleted. - * - * This will unbind @gadget, if it is bound. - * It will not do a final usb_put_gadget(). - */ -void usb_del_gadget(struct usb_gadget *gadget) +static void usb_gadget_remove_driver(struct usb_udc *udc) { - struct usb_udc *udc = gadget->udc; - - if (!udc) - return; + dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n", + udc->driver->function); - dev_vdbg(gadget->dev.parent, "unregistering gadget\n"); - - mutex_lock(&udc_lock); - list_del(&udc->list); - mutex_unlock(&udc_lock); + usb_gadget_disconnect(udc->gadget); + udc->driver->disconnect(udc->gadget); + udc->driver->unbind(udc->gadget); + usb_gadget_udc_stop_locked(udc); - kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE); - sysfs_remove_link(&udc->dev.kobj, "gadget"); - device_del(&gadget->dev); - flush_work(&gadget->work); - ida_free(&gadget_id_numbers, gadget->id_number); - cancel_work_sync(&udc->vbus_work); - device_unregister(&udc->dev); + udc->driver = NULL; } -EXPORT_SYMBOL_GPL(usb_del_gadget); /** - * usb_del_gadget_udc - unregisters a gadget - * @gadget: the gadget to be unregistered. + * usb_del_gadget_udc - deletes @udc from udc_list + * @gadget: the gadget to be removed. * - * Calls usb_del_gadget() and does a final usb_put_gadget(). + * This, will call usb_gadget_unregister_driver() if + * the @udc is still busy. */ void usb_del_gadget_udc(struct usb_gadget *gadget) { - usb_del_gadget(gadget); - usb_put_gadget(gadget); -} -EXPORT_SYMBOL_GPL(usb_del_gadget_udc); - -/* ------------------------------------------------------------------------- */ - -static int gadget_match_driver(struct device *dev, const struct device_driver *drv) -{ - struct usb_gadget *gadget = dev_to_usb_gadget(dev); - struct usb_udc *udc = gadget->udc; - const struct usb_gadget_driver *driver = container_of(drv, - struct usb_gadget_driver, driver); - - /* If the driver specifies a udc_name, it must match the UDC's name */ - if (driver->udc_name && - strcmp(driver->udc_name, dev_name(&udc->dev)) != 0) - return 0; - - /* If the driver is already bound to a gadget, it doesn't match */ - if (driver->is_bound) - return 0; - - /* Otherwise any gadget driver matches any UDC */ - return 1; -} - -static int gadget_bind_driver(struct device *dev) -{ - struct usb_gadget *gadget = dev_to_usb_gadget(dev); - struct usb_udc *udc = gadget->udc; - struct usb_gadget_driver *driver = container_of(dev->driver, - struct usb_gadget_driver, driver); - int ret = 0; + struct usb_udc *udc = NULL; mutex_lock(&udc_lock); - if (driver->is_bound) { - mutex_unlock(&udc_lock); - return -ENXIO; /* Driver binds to only one gadget */ - } - driver->is_bound = true; - udc->driver = driver; - mutex_unlock(&udc_lock); - - dev_dbg(&udc->dev, "binding gadget driver [%s]\n", driver->function); - - usb_gadget_udc_set_speed(udc, driver->max_speed); - - ret = driver->bind(udc->gadget, driver); - if (ret) - goto err_bind; - - mutex_lock(&udc->connect_lock); - ret = usb_gadget_udc_start_locked(udc); - if (ret) { - mutex_unlock(&udc->connect_lock); - goto err_start; - } - usb_gadget_enable_async_callbacks(udc); - udc->allow_connect = true; - ret = usb_udc_connect_control_locked(udc); - if (ret) - goto err_connect_control; - - mutex_unlock(&udc->connect_lock); - - kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); - return 0; - - err_connect_control: - udc->allow_connect = false; - usb_gadget_disable_async_callbacks(udc); - if (gadget->irq) - synchronize_irq(gadget->irq); - usb_gadget_udc_stop_locked(udc); - mutex_unlock(&udc->connect_lock); - - err_start: - driver->unbind(udc->gadget); - - err_bind: - if (ret != -EISNAM) - dev_err(&udc->dev, "failed to start %s: %d\n", - driver->function, ret); + list_for_each_entry(udc, &udc_list, list) + if (udc->gadget == gadget) + goto found; - mutex_lock(&udc_lock); - udc->driver = NULL; - driver->is_bound = false; + dev_err(gadget->dev.parent, "gadget not registered.\n"); mutex_unlock(&udc_lock); - return ret; -} - -static void gadget_unbind_driver(struct device *dev) -{ - struct usb_gadget *gadget = dev_to_usb_gadget(dev); - struct usb_udc *udc = gadget->udc; - struct usb_gadget_driver *driver = udc->driver; - - dev_dbg(&udc->dev, "unbinding gadget driver [%s]\n", driver->function); - - udc->allow_connect = false; - cancel_work_sync(&udc->vbus_work); - mutex_lock(&udc->connect_lock); - usb_gadget_disconnect_locked(gadget); - usb_gadget_disable_async_callbacks(udc); - if (gadget->irq) - synchronize_irq(gadget->irq); - mutex_unlock(&udc->connect_lock); - - udc->driver->unbind(gadget); + return; - mutex_lock(&udc->connect_lock); - usb_gadget_udc_stop_locked(udc); - mutex_unlock(&udc->connect_lock); +found: + dev_vdbg(gadget->dev.parent, "unregistering gadget\n"); - mutex_lock(&udc_lock); - driver->is_bound = false; - udc->driver = NULL; + list_del(&udc->list); mutex_unlock(&udc_lock); - kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); + if (udc->driver) + usb_gadget_remove_driver(udc); } +EXPORT_SYMBOL_GPL(usb_del_gadget_udc); /* ------------------------------------------------------------------------- */ -int usb_gadget_register_driver_owner(struct usb_gadget_driver *driver, - struct module *owner, const char *mod_name) +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) { - int ret; + struct usb_udc *udc = NULL; + int ret = -ENODEV; - if (!driver || !driver->bind || !driver->setup) + if (!driver || !driver->unbind) return -EINVAL; - driver->driver.bus = &gadget_bus_type; - driver->driver.owner = owner; - driver->driver.mod_name = mod_name; - driver->driver.probe_type = PROBE_FORCE_SYNCHRONOUS; - ret = driver_register(&driver->driver); - if (ret) { - pr_warn("%s: driver registration failed: %d\n", - driver->function, ret); - return ret; - } - mutex_lock(&udc_lock); - if (!driver->is_bound) { - if (driver->match_existing_only) { - pr_warn("%s: couldn't find an available UDC or it's busy\n", - driver->function); - ret = -EBUSY; - } else { - pr_info("%s: couldn't find an available UDC\n", - driver->function); + list_for_each_entry(udc, &udc_list, list) + if (udc->driver == driver) { + usb_gadget_remove_driver(udc); + usb_gadget_set_state(udc->gadget, + USB_STATE_NOTATTACHED); ret = 0; + break; } - } - mutex_unlock(&udc_lock); - if (ret) - driver_unregister(&driver->driver); + mutex_unlock(&udc_lock); return ret; } -EXPORT_SYMBOL_GPL(usb_gadget_register_driver_owner); - -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) -{ - if (!driver || !driver->unbind) - return -EINVAL; - - driver_unregister(&driver->driver); - return 0; -} EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver); /* ------------------------------------------------------------------------- */ -static ssize_t srp_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t n) -{ - struct usb_udc *udc = container_of(dev, struct usb_udc, dev); - - if (sysfs_streq(buf, "1")) - usb_gadget_wakeup(udc->gadget); - - return n; -} -static DEVICE_ATTR_WO(srp); - -static ssize_t soft_connect_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t n) -{ - struct usb_udc *udc = container_of(dev, struct usb_udc, dev); - ssize_t ret; - - device_lock(&udc->gadget->dev); - if (!udc->driver) { - dev_err(dev, "soft-connect without a gadget driver\n"); - ret = -EOPNOTSUPP; - goto out; - } - - if (sysfs_streq(buf, "connect")) { - mutex_lock(&udc->connect_lock); - usb_gadget_udc_start_locked(udc); - usb_gadget_connect_locked(udc->gadget); - mutex_unlock(&udc->connect_lock); - } else if (sysfs_streq(buf, "disconnect")) { - mutex_lock(&udc->connect_lock); - usb_gadget_disconnect_locked(udc->gadget); - usb_gadget_udc_stop_locked(udc); - mutex_unlock(&udc->connect_lock); - } else { - dev_err(dev, "unsupported command '%s'\n", buf); - ret = -EINVAL; - goto out; - } - - ret = n; -out: - device_unlock(&udc->gadget->dev); - return ret; -} -static DEVICE_ATTR_WO(soft_connect); - -static ssize_t state_show(struct device *dev, struct device_attribute *attr, - char *buf) +int usb_gadget_probe_driver(struct usb_gadget_driver *driver) { - struct usb_udc *udc = container_of(dev, struct usb_udc, dev); - struct usb_gadget *gadget = udc->gadget; - - return sprintf(buf, "%s\n", usb_state_string(gadget->state)); -} -static DEVICE_ATTR_RO(state); + struct usb_udc *udc = NULL; + unsigned int udc_count = 0; + int ret; -static ssize_t function_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct usb_udc *udc = container_of(dev, struct usb_udc, dev); - struct usb_gadget_driver *drv; - int rc = 0; + if (!driver || !driver->bind || !driver->setup) + return -EINVAL; mutex_lock(&udc_lock); - drv = udc->driver; - if (drv && drv->function) - rc = scnprintf(buf, PAGE_SIZE, "%s\n", drv->function); - mutex_unlock(&udc_lock); - return rc; -} -static DEVICE_ATTR_RO(function); - -#define USB_UDC_SPEED_ATTR(name, param) \ -ssize_t name##_show(struct device *dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \ - return scnprintf(buf, PAGE_SIZE, "%s\n", \ - usb_speed_string(udc->gadget->param)); \ -} \ -static DEVICE_ATTR_RO(name) - -static USB_UDC_SPEED_ATTR(current_speed, speed); -static USB_UDC_SPEED_ATTR(maximum_speed, max_speed); - -#define USB_UDC_ATTR(name) \ -ssize_t name##_show(struct device *dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \ - struct usb_gadget *gadget = udc->gadget; \ - \ - return scnprintf(buf, PAGE_SIZE, "%d\n", gadget->name); \ -} \ -static DEVICE_ATTR_RO(name) - -static USB_UDC_ATTR(is_otg); -static USB_UDC_ATTR(is_a_peripheral); -static USB_UDC_ATTR(b_hnp_enable); -static USB_UDC_ATTR(a_hnp_support); -static USB_UDC_ATTR(a_alt_hnp_support); -static USB_UDC_ATTR(is_selfpowered); - -static struct attribute *usb_udc_attrs[] = { - &dev_attr_srp.attr, - &dev_attr_soft_connect.attr, - &dev_attr_state.attr, - &dev_attr_function.attr, - &dev_attr_current_speed.attr, - &dev_attr_maximum_speed.attr, - - &dev_attr_is_otg.attr, - &dev_attr_is_a_peripheral.attr, - &dev_attr_b_hnp_enable.attr, - &dev_attr_a_hnp_support.attr, - &dev_attr_a_alt_hnp_support.attr, - &dev_attr_is_selfpowered.attr, - NULL, -}; - -static const struct attribute_group usb_udc_attr_group = { - .attrs = usb_udc_attrs, -}; - -static const struct attribute_group *usb_udc_attr_groups[] = { - &usb_udc_attr_group, - NULL, -}; - -static int usb_udc_uevent(const struct device *dev, struct kobj_uevent_env *env) -{ - const struct usb_udc *udc = container_of(dev, struct usb_udc, dev); - int ret; + list_for_each_entry(udc, &udc_list, list) { + udc_count++; - ret = add_uevent_var(env, "USB_UDC_NAME=%s", udc->gadget->name); - if (ret) { - dev_err(dev, "failed to add uevent USB_UDC_NAME\n"); - return ret; + /* For now we take the first one */ + if (!udc->driver) + goto found; } - mutex_lock(&udc_lock); - if (udc->driver) - ret = add_uevent_var(env, "USB_UDC_DRIVER=%s", - udc->driver->function); +#ifndef CONFIG_SPL_BUILD + if (!udc_count) + printf("No UDC available in the system\n"); + else + /* When this happens, users should 'unbind ' + * using the output of 'dm tree' and looking at the line right + * after the USB peripheral/device controller. + */ + printf("All UDCs in use (%d available), use the unbind command\n", + udc_count); +#endif mutex_unlock(&udc_lock); - if (ret) { - dev_err(dev, "failed to add uevent USB_UDC_DRIVER\n"); - return ret; - } - - return 0; -} - -static const struct class udc_class = { - .name = "udc", - .dev_uevent = usb_udc_uevent, -}; - -static const struct bus_type gadget_bus_type = { - .name = "gadget", - .probe = gadget_bind_driver, - .remove = gadget_unbind_driver, - .match = gadget_match_driver, -}; - -static int __init usb_udc_init(void) -{ - int rc; - - rc = class_register(&udc_class); - if (rc) - return rc; - - rc = bus_register(&gadget_bus_type); - if (rc) - class_unregister(&udc_class); - return rc; + return -ENODEV; +found: + ret = udc_bind_to_driver(udc, driver); + mutex_unlock(&udc_lock); + return ret; } -subsys_initcall(usb_udc_init); +EXPORT_SYMBOL_GPL(usb_gadget_probe_driver); -static void __exit usb_udc_exit(void) +int usb_gadget_register_driver(struct usb_gadget_driver *driver) { - bus_unregister(&gadget_bus_type); - class_unregister(&udc_class); + return usb_gadget_probe_driver(driver); } -module_exit(usb_udc_exit); +EXPORT_SYMBOL_GPL(usb_gadget_register_driver); MODULE_DESCRIPTION("UDC Framework"); MODULE_AUTHOR("Felipe Balbi "); -- 2.53.0