lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1659467920-9095-6-git-send-email-quic_eserrao@quicinc.com>
Date:   Tue,  2 Aug 2022 12:18:40 -0700
From:   Elson Roy Serrao <quic_eserrao@...cinc.com>
To:     balbi@...nel.org, gregkh@...uxfoundation.org
Cc:     linux-kernel@...r.kernel.org, linux-usb@...r.kernel.org,
        quic_wcheng@...cinc.com, quic_jackp@...cinc.com,
        quic_mrana@...cinc.com, Thinh.Nguyen@...opsys.com,
        Elson Roy Serrao <quic_eserrao@...cinc.com>
Subject: [PATCH 5/5] usb: gadget: f_ecm: Add function suspend and wakeup support

USB host sends function suspend setup packet to an interface when there
is no active communication involved. Handle such requests from host so
that the interface goes to function suspend state. For the device to
resume data transfer it can either send a function wakeup notification or
wait for the host initated function resume depending on the function
wakeup capability of the device. Add support to trigger function wakeup.

Signed-off-by: Elson Roy Serrao <quic_eserrao@...cinc.com>
---
 drivers/usb/gadget/function/f_ecm.c   | 49 +++++++++++++++++++++++++++++++++--
 drivers/usb/gadget/function/u_ether.c | 32 ++++++++++++++++++++---
 drivers/usb/gadget/function/u_ether.h |  6 +++--
 3 files changed, 80 insertions(+), 7 deletions(-)

diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c
index fb1dec3..8bb7e3c 100644
--- a/drivers/usb/gadget/function/f_ecm.c
+++ b/drivers/usb/gadget/function/f_ecm.c
@@ -54,6 +54,8 @@ struct f_ecm {
 	u8				notify_state;
 	atomic_t			notify_count;
 	bool				is_open;
+	bool				func_wakeup_allowed;
+	bool				func_is_suspended;
 
 	/* FIXME is_open needs some irq-ish locking
 	 * ... possibly the same as port.ioport
@@ -631,6 +633,8 @@ static void ecm_disable(struct usb_function *f)
 		ecm->port.out_ep->desc = NULL;
 	}
 
+	ecm->func_wakeup_allowed = false;
+	ecm->func_is_suspended = false;
 	usb_ep_disable(ecm->notify);
 	ecm->notify->desc = NULL;
 }
@@ -894,9 +898,14 @@ static void ecm_suspend(struct usb_function *f)
 	struct f_ecm *ecm = func_to_ecm(f);
 	struct usb_composite_dev *cdev = ecm->port.func.config->cdev;
 
+	if (ecm->func_is_suspended) {
+		DBG(cdev, "Function already suspended\n");
+		return;
+	}
+
 	DBG(cdev, "ECM Suspend\n");
 
-	gether_suspend(&ecm->port);
+	gether_suspend(&ecm->port, ecm->func_wakeup_allowed);
 }
 
 static void ecm_resume(struct usb_function *f)
@@ -906,7 +915,41 @@ static void ecm_resume(struct usb_function *f)
 
 	DBG(cdev, "ECM Resume\n");
 
-	gether_resume(&ecm->port);
+	gether_resume(&ecm->port, ecm->func_is_suspended);
+}
+
+static int ecm_get_status(struct usb_function *f)
+{
+	struct f_ecm *ecm = func_to_ecm(f);
+
+	return (ecm->func_wakeup_allowed ? USB_INTRF_STAT_FUNC_RW : 0) |
+		USB_INTRF_STAT_FUNC_RW_CAP;
+}
+
+static int ecm_func_suspend(struct usb_function *f, u8 options)
+{
+	bool func_wakeup_allowed;
+	struct f_ecm *ecm = func_to_ecm(f);
+	struct usb_composite_dev *cdev = ecm->port.func.config->cdev;
+
+	DBG(cdev, "func susp %u cmd\n", options);
+
+	func_wakeup_allowed = !!(options & (USB_INTRF_FUNC_SUSPEND_RW >> 8));
+	if (options & (USB_INTRF_FUNC_SUSPEND_LP >> 8)) {
+		ecm->func_wakeup_allowed = func_wakeup_allowed;
+		if (!ecm->func_is_suspended) {
+			ecm_suspend(f);
+			ecm->func_is_suspended = true;
+		}
+	} else {
+		if (ecm->func_is_suspended) {
+			ecm->func_is_suspended = false;
+			ecm_resume(f);
+		}
+		ecm->func_wakeup_allowed = func_wakeup_allowed;
+	}
+
+	return 0;
 }
 
 static void ecm_free(struct usb_function *f)
@@ -977,6 +1020,8 @@ static struct usb_function *ecm_alloc(struct usb_function_instance *fi)
 	ecm->port.func.disable = ecm_disable;
 	ecm->port.func.free_func = ecm_free;
 	ecm->port.func.suspend = ecm_suspend;
+	ecm->port.func.get_status = ecm_get_status;
+	ecm->port.func.func_suspend = ecm_func_suspend;
 	ecm->port.func.resume = ecm_resume;
 
 	return &ecm->port.func;
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index 78391de..f3e1c9b 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -477,8 +477,17 @@ static int ether_wakeup_host(struct gether *port)
 	struct usb_function *func = &port->func;
 	struct usb_gadget *gadget = func->config->cdev->gadget;
 
-	if (gadget->speed < USB_SPEED_SUPER)
+	if (gadget->speed >= USB_SPEED_SUPER) {
+		if (!port->func_wakeup_allowed) {
+			DBG(port->ioport, "Function wakeup not allowed\n");
+			return -EOPNOTSUPP;
+		}
+		ret = usb_func_wakeup(func);
+		if (ret)
+			port->is_wakeup_pending = true;
+	} else {
 		ret = usb_gadget_wakeup(gadget);
+	}
 
 	return ret;
 }
@@ -1081,7 +1090,7 @@ int gether_set_ifname(struct net_device *net, const char *name, int len)
 }
 EXPORT_SYMBOL_GPL(gether_set_ifname);
 
-void gether_suspend(struct gether *link)
+void gether_suspend(struct gether *link, bool wakeup_allowed)
 {
 	struct eth_dev *dev = link->ioport;
 	unsigned long flags;
@@ -1099,13 +1108,15 @@ void gether_suspend(struct gether *link)
 	}
 	spin_lock_irqsave(&dev->lock, flags);
 	link->is_suspend = true;
+	link->func_wakeup_allowed = wakeup_allowed;
 	spin_unlock_irqrestore(&dev->lock, flags);
 }
 EXPORT_SYMBOL_GPL(gether_suspend);
 
-void gether_resume(struct gether *link)
+void gether_resume(struct gether *link, bool func_suspend)
 {
 	struct eth_dev *dev = link->ioport;
+	struct usb_function *func = &link->func;
 	unsigned long flags;
 
 	if (!dev)
@@ -1113,6 +1124,19 @@ void gether_resume(struct gether *link)
 
 	spin_lock_irqsave(&dev->lock, flags);
 
+	/*
+	 * If the function is in USB3 Function Suspend state, resume is
+	 * canceled. In this case resume is done by a Function Resume request.
+	 */
+	if (func_suspend) {
+		if (link->is_wakeup_pending) {
+			usb_func_wakeup(func);
+			link->is_wakeup_pending = false;
+		}
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return;
+	}
+
 	if (netif_queue_stopped(dev->net))
 		netif_start_queue(dev->net);
 
@@ -1284,6 +1308,8 @@ void gether_disconnect(struct gether *link)
 	spin_lock(&dev->lock);
 	dev->port_usb = NULL;
 	link->is_suspend = false;
+	link->is_wakeup_pending = false;
+	link->func_wakeup_allowed = false;
 	spin_unlock(&dev->lock);
 }
 EXPORT_SYMBOL_GPL(gether_disconnect);
diff --git a/drivers/usb/gadget/function/u_ether.h b/drivers/usb/gadget/function/u_ether.h
index 851ee10..4a6aa646 100644
--- a/drivers/usb/gadget/function/u_ether.h
+++ b/drivers/usb/gadget/function/u_ether.h
@@ -80,6 +80,8 @@ struct gether {
 	void				(*open)(struct gether *);
 	void				(*close)(struct gether *);
 	bool				is_suspend;
+	bool				is_wakeup_pending;
+	bool				func_wakeup_allowed;
 };
 
 #define	DEFAULT_FILTER	(USB_CDC_PACKET_TYPE_BROADCAST \
@@ -259,8 +261,8 @@ int gether_set_ifname(struct net_device *net, const char *name, int len);
 
 void gether_cleanup(struct eth_dev *dev);
 
-void gether_suspend(struct gether *link);
-void gether_resume(struct gether *link);
+void gether_suspend(struct gether *link, bool wakeup_allowed);
+void gether_resume(struct gether *link, bool func_suspend);
 
 /* connect/disconnect is handled by individual functions */
 struct net_device *gether_connect(struct gether *);
-- 
2.7.4

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ