[PATCH 4/6] usb: gadget: add functions to signal udc driver to delay status stage

Paul Elder
2018-10-10T02:49:36Z

A usb gadget function driver may or may not want to delay the status
stage of a control OUT request. An instance it might want to is to
asynchronously validate the data of a class-specific request.

Add a function usb_ep_delay_status to allow function drivers to choose
to delay the status stage in the request completion handler. The UDC
should then check the usb_ep->delayed_status flag and act accordingly to
delay the status stage.

Also add a function usb_ep_send_response as a wrapper for
usb_ep->ops->send_response, whose prototype is added as well. This
function should be called by function drivers to tell the UDC what to
reply in the status stage that it has requested to be delayed.

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
drivers/usb/gadget/udc/core.c | 35 +++++++++++++++++++++++++++++++++++
include/linux/usb/gadget.h | 11 +++++++++++
2 files changed, 46 insertions(+)

diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index af88b48c1cea..1ec5ce6b43cd 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -443,6 +443,41 @@ void usb_ep_fifo_flush(struct usb_ep *ep)
}
EXPORT_SYMBOL_GPL(usb_ep_fifo_flush);

+/**
+ * usb_ep_ep_delay_status - set delay_status flag
+ * @ep: the endpoint whose delay_status flag is being set
+ *
+ * This function instructs the UDC to delay the status stage of a control
+ * request. It can only be called from the request completion handler of a
+ * control request.
+ */
+void usb_ep_delay_status(struct usb_ep *ep)
+{
+ ep->delayed_status = true;
+}
+EXPORT_SYMBOL_GPL(usb_ep_delay_status);
+
+/**
+ * usb_ep_send_response - reply to control OUT request
+ * @ep: the endpoint to send reply
+ * @stall: true for STALL, false for ACK
+ *
+ * Instruct the UDC to complete the status stage of a control request that was
+ * previously delayed with a call to usb_ep_delay_status().
+ */
+int usb_ep_send_response(struct usb_ep *ep, bool stall)
+{
+ if (!ep->ops->send_response)
+ return -ENOSYS;
+
+ if (!ep->delayed_status)
+ return -EINVAL;
+
+ ep->delayed_status = false;
+ return ep->ops->send_response(ep, stall);
+}
+EXPORT_SYMBOL_GPL(usb_ep_send_response);
+
/* ------------------------------------------------------------------------- */

/**
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index e5cd84a0f84a..d39c221d4b68 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -144,6 +144,8 @@ struct usb_ep_ops {

int (*fifo_status) (struct usb_ep *ep);
void (*fifo_flush) (struct usb_ep *ep);
+
+ int (*send_response) (struct usb_ep *ep, bool stall);
};

/**
@@ -209,6 +211,8 @@ struct usb_ep_caps {
* enabled and remains valid until the endpoint is disabled.
* @comp_desc: In case of SuperSpeed support, this is the endpoint companion
* descriptor that is used to configure the endpoint
+ * @delayed_status: True if status stage is being delayed. Valid only for
+ * control endpoints.
*
* the bus controller driver lists all the general purpose endpoints in
* gadget->ep_list. the control endpoint (gadget->ep0) is not in that list,
@@ -232,6 +236,7 @@ struct usb_ep {
u8 address;
const struct usb_endpoint_descriptor *desc;
const struct usb_ss_ep_comp_descriptor *comp_desc;
+ bool delayed_status;
};

/*-------------------------------------------------------------------------*/
@@ -249,6 +254,8 @@ int usb_ep_clear_halt(struct usb_ep *ep);
int usb_ep_set_wedge(struct usb_ep *ep);
int usb_ep_fifo_status(struct usb_ep *ep);
void usb_ep_fifo_flush(struct usb_ep *ep);
+void usb_ep_delay_status(struct usb_ep *ep);
+int usb_ep_send_response(struct usb_ep *ep, bool stall);
#else
static inline void usb_ep_set_maxpacket_limit(struct usb_ep *ep,
unsigned maxpacket_limit)
@@ -278,6 +285,10 @@ static inline int usb_ep_fifo_status(struct usb_ep *ep)
{ return 0; }
static inline void usb_ep_fifo_flush(struct usb_ep *ep)
{ }
+static inline void usb_ep_delay_status(struct usb_ep *ep)
+{ }
+static inline int usb_ep_send_response(struct usb_ep *ep, bool stall)
+{ }
#endif /* USB_GADGET */

/*-------------------------------------------------------------------------*/
--
2.18.0