diff mbox series

[v13,5/6] usb: gadget: Handle function suspend feature selector

Message ID 1679694482-16430-6-git-send-email-quic_eserrao@quicinc.com
State New
Headers show
Series Add function suspend/resume and remote wakeup support | expand

Commit Message

Elson Roy Serrao March 24, 2023, 9:48 p.m. UTC
When host sends function suspend feature selector to the device,
inspect the received packet and trigger function suspend or
function resume accordingly. Inspect the remote wakeup bit and
arm the function for remote wakeup if it is wakeup capable. Also
host queries the function wakeup capability  through a get status
request before sending function resume. Handle such requests in
composite layer.

Signed-off-by: Elson Roy Serrao <quic_eserrao@quicinc.com>
---
 drivers/usb/gadget/composite.c | 65 ++++++++++++++++++++++++++++++++++++++----
 1 file changed, 60 insertions(+), 5 deletions(-)

Comments

Thinh Nguyen March 24, 2023, 11:09 p.m. UTC | #1
On Fri, Mar 24, 2023, Elson Roy Serrao wrote:
> When host sends function suspend feature selector to the device,
> inspect the received packet and trigger function suspend or
> function resume accordingly. Inspect the remote wakeup bit and
> arm the function for remote wakeup if it is wakeup capable. Also
> host queries the function wakeup capability  through a get status
> request before sending function resume. Handle such requests in
> composite layer.
> 
> Signed-off-by: Elson Roy Serrao <quic_eserrao@quicinc.com>
> ---
>  drivers/usb/gadget/composite.c | 65 ++++++++++++++++++++++++++++++++++++++----
>  1 file changed, 60 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
> index 2111732..d1d7f89 100644
> --- a/drivers/usb/gadget/composite.c
> +++ b/drivers/usb/gadget/composite.c
> @@ -941,6 +941,9 @@ static void reset_config(struct usb_composite_dev *cdev)
>  		if (f->disable)
>  			f->disable(f);
>  
> +		/* Section 9.1.1.6, disable remote wakeup when device is reset */
> +		f->func_wakeup_armed = false;
> +
>  		bitmap_zero(f->endpoints, 32);
>  	}
>  	cdev->config = NULL;
> @@ -2006,9 +2009,20 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
>  		f = cdev->config->interface[intf];
>  		if (!f)
>  			break;
> -		status = f->get_status ? f->get_status(f) : 0;
> -		if (status < 0)
> -			break;
> +
> +		if (f->get_status) {
> +			status = f->get_status(f);
> +			if (status < 0)
> +				break;
> +		} else {
> +			/* Set D0 and D1 bits based on func wakeup capability */
> +			if (f->config->bmAttributes & USB_CONFIG_ATT_WAKEUP) {
> +				status |= USB_INTRF_STAT_FUNC_RW_CAP;
> +				if (f->func_wakeup_armed)
> +					status |= USB_INTRF_STAT_FUNC_RW;
> +			}
> +		}
> +
>  		put_unaligned_le16(status & 0x0000ffff, req->buf);
>  		break;
>  	/*
> @@ -2030,8 +2044,44 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
>  			if (!f)
>  				break;
>  			value = 0;
> -			if (f->func_suspend)
> +			if (f->func_suspend) {
>  				value = f->func_suspend(f, w_index >> 8);
> +			/* SetFeature(FUNCTION_SUSPEND) */
> +			} else if (ctrl->bRequest == USB_REQ_SET_FEATURE) {
> +				if (!(f->config->bmAttributes &
> +				      USB_CONFIG_ATT_WAKEUP) &&
> +				     (w_index & USB_INTRF_FUNC_SUSPEND_RW))
> +					break;
> +
> +				f->func_wakeup_armed = !!(w_index &
> +							  USB_INTRF_FUNC_SUSPEND_RW);
> +
> +				if (w_index & USB_INTRF_FUNC_SUSPEND_LP) {
> +					if (f->suspend && !f->func_suspended) {
> +						f->suspend(f);
> +						f->func_suspended = true;
> +					}
> +				/*
> +				 * Handle cases where host sends function resume
> +				 * through SetFeature(FUNCTION_SUSPEND) but low power
> +				 * bit reset
> +				 */
> +				} else {
> +					if (f->resume && f->func_suspended) {
> +						f->resume(f);
> +						f->func_suspended = false;
> +					}
> +				}
> +			/* ClearFeature(FUNCTION_SUSPEND) */
> +			} else if (ctrl->bRequest == USB_REQ_CLEAR_FEATURE) {
> +				f->func_wakeup_armed = false;
> +
> +				if (f->resume && f->func_suspended) {
> +					f->resume(f);
> +					f->func_suspended = false;
> +				}
> +			}
> +
>  			if (value < 0) {
>  				ERROR(cdev,
>  				      "func_suspend() returned error %d\n",
> @@ -2574,7 +2624,12 @@ void composite_resume(struct usb_gadget *gadget)
>  		cdev->driver->resume(cdev);
>  	if (cdev->config) {
>  		list_for_each_entry(f, &cdev->config->functions, list) {
> -			if (f->resume)
> +			/*
> +			 * Check for func_suspended flag to see if the function is
> +			 * in USB3 FUNCTION_SUSPEND state. In this case resume is
> +			 * done via FUNCTION_SUSPEND feature selector.
> +			 */
> +			if (f->resume && !f->func_suspended)
>  				f->resume(f);
>  		}
>  
> -- 
> 2.7.4
> 

Reviewed-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>

Thanks,
Thinh
diff mbox series

Patch

diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 2111732..d1d7f89 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -941,6 +941,9 @@  static void reset_config(struct usb_composite_dev *cdev)
 		if (f->disable)
 			f->disable(f);
 
+		/* Section 9.1.1.6, disable remote wakeup when device is reset */
+		f->func_wakeup_armed = false;
+
 		bitmap_zero(f->endpoints, 32);
 	}
 	cdev->config = NULL;
@@ -2006,9 +2009,20 @@  composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
 		f = cdev->config->interface[intf];
 		if (!f)
 			break;
-		status = f->get_status ? f->get_status(f) : 0;
-		if (status < 0)
-			break;
+
+		if (f->get_status) {
+			status = f->get_status(f);
+			if (status < 0)
+				break;
+		} else {
+			/* Set D0 and D1 bits based on func wakeup capability */
+			if (f->config->bmAttributes & USB_CONFIG_ATT_WAKEUP) {
+				status |= USB_INTRF_STAT_FUNC_RW_CAP;
+				if (f->func_wakeup_armed)
+					status |= USB_INTRF_STAT_FUNC_RW;
+			}
+		}
+
 		put_unaligned_le16(status & 0x0000ffff, req->buf);
 		break;
 	/*
@@ -2030,8 +2044,44 @@  composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
 			if (!f)
 				break;
 			value = 0;
-			if (f->func_suspend)
+			if (f->func_suspend) {
 				value = f->func_suspend(f, w_index >> 8);
+			/* SetFeature(FUNCTION_SUSPEND) */
+			} else if (ctrl->bRequest == USB_REQ_SET_FEATURE) {
+				if (!(f->config->bmAttributes &
+				      USB_CONFIG_ATT_WAKEUP) &&
+				     (w_index & USB_INTRF_FUNC_SUSPEND_RW))
+					break;
+
+				f->func_wakeup_armed = !!(w_index &
+							  USB_INTRF_FUNC_SUSPEND_RW);
+
+				if (w_index & USB_INTRF_FUNC_SUSPEND_LP) {
+					if (f->suspend && !f->func_suspended) {
+						f->suspend(f);
+						f->func_suspended = true;
+					}
+				/*
+				 * Handle cases where host sends function resume
+				 * through SetFeature(FUNCTION_SUSPEND) but low power
+				 * bit reset
+				 */
+				} else {
+					if (f->resume && f->func_suspended) {
+						f->resume(f);
+						f->func_suspended = false;
+					}
+				}
+			/* ClearFeature(FUNCTION_SUSPEND) */
+			} else if (ctrl->bRequest == USB_REQ_CLEAR_FEATURE) {
+				f->func_wakeup_armed = false;
+
+				if (f->resume && f->func_suspended) {
+					f->resume(f);
+					f->func_suspended = false;
+				}
+			}
+
 			if (value < 0) {
 				ERROR(cdev,
 				      "func_suspend() returned error %d\n",
@@ -2574,7 +2624,12 @@  void composite_resume(struct usb_gadget *gadget)
 		cdev->driver->resume(cdev);
 	if (cdev->config) {
 		list_for_each_entry(f, &cdev->config->functions, list) {
-			if (f->resume)
+			/*
+			 * Check for func_suspended flag to see if the function is
+			 * in USB3 FUNCTION_SUSPEND state. In this case resume is
+			 * done via FUNCTION_SUSPEND feature selector.
+			 */
+			if (f->resume && !f->func_suspended)
 				f->resume(f);
 		}