Patch implements function responsible for disabling
USB endpoint. This function is called
from USB core gadget during handling SET_CONFIGURATION or
SET_INTERFACE request, or after such events as USB_RESET or detaching
device from host.

Signed-off-by: Pawel Laszczak <paw...@cadence.com>
---
 drivers/usb/usbssp/gadget-if.c  | 44 +++++++++++++++++--
 drivers/usb/usbssp/gadget-mem.c | 18 ++++++++
 drivers/usb/usbssp/gadget.c     | 75 +++++++++++++++++++++++++++++++++
 drivers/usb/usbssp/gadget.h     |  2 +
 4 files changed, 135 insertions(+), 4 deletions(-)

diff --git a/drivers/usb/usbssp/gadget-if.c b/drivers/usb/usbssp/gadget-if.c
index 2fecc934cbce..75444eaaf699 100644
--- a/drivers/usb/usbssp/gadget-if.c
+++ b/drivers/usb/usbssp/gadget-if.c
@@ -86,13 +86,49 @@ static int usbssp_gadget_ep_enable(struct usb_ep *ep,
 
 int usbssp_gadget_ep_disable(struct usb_ep *ep)
 {
-       struct usbssp_ep *ep_priv = to_usbssp_ep(ep);
-       int ret = 0;
+       struct usbssp_ep *ep_priv;
+       struct usbssp_udc *usbssp_data;
+       int ep_index = 0;
+       int ret;
+       int irq_disabled_locally = 0;
+       unsigned long flags = 0;
+       struct usbssp_request  *req_priv;
 
-       if (!ep_priv)
+       ep_priv = to_usbssp_ep(ep);
+       usbssp_data = ep_priv->usbssp_data;
+       ep_index = usbssp_get_endpoint_index(ep_priv->endpoint.desc);
+
+       if (!(ep_priv->ep_state & USBSSP_EP_ENABLED)) {
+               usbssp_dbg(usbssp_data, "%s is already disabled\n",
+                               ep_priv->name);
                return -EINVAL;
+       }
+
+       usbssp_g_lock(irq_disabled_locally, flags);
 
-       /*TODO: implements this function*/
+       ep_priv->ep_state |= USBSSP_EP_DISABLE_PENDING;
+
+       /*dequeue all USB request from endpoint*/
+       list_for_each_entry(req_priv, &ep_priv->pending_list, list) {
+               usbssp_dequeue(ep_priv, req_priv);
+       }
+
+       ret = usbssp_drop_endpoint(usbssp_data, &usbssp_data->gadget, ep_priv);
+       if (ret)
+               goto finish;
+
+       ret = usbssp_check_bandwidth(usbssp_data, &usbssp_data->gadget);
+       if (ret)
+               goto finish;
+
+       ep_priv->ep_state &= ~USBSSP_EP_ENABLED;
+
+finish:
+       ep_priv->ep_state &= ~USBSSP_EP_DISABLE_PENDING;
+       usbssp_dbg(usbssp_data, "%s disable endpoint %s\n", ep_priv->name,
+                       (ret == 0) ? "success" : "failed");
+
+       usbssp_g_unlock(irq_disabled_locally, flags);
        return ret;
 }
 
diff --git a/drivers/usb/usbssp/gadget-mem.c b/drivers/usb/usbssp/gadget-mem.c
index 303dc8fab8ad..772a43f24ca9 100644
--- a/drivers/usb/usbssp/gadget-mem.c
+++ b/drivers/usb/usbssp/gadget-mem.c
@@ -1103,6 +1103,24 @@ int usbssp_endpoint_init(struct usbssp_udc *usbssp_data,
        return 0;
 }
 
+void usbssp_endpoint_zero(struct usbssp_udc *usbssp_data,
+                         struct usbssp_device *dev_priv,
+                         struct usbssp_ep *ep)
+{
+       unsigned int ep_index;
+       struct usbssp_ep_ctx *ep_ctx;
+
+       ep_index = usbssp_get_endpoint_index(ep->endpoint.desc);
+       ep_ctx = usbssp_get_ep_ctx(usbssp_data, dev_priv->in_ctx, ep_index);
+
+       ep_ctx->ep_info = 0;
+       ep_ctx->ep_info2 = 0;
+       ep_ctx->deq = 0;
+       ep_ctx->tx_info = 0;
+       /* Don't free the endpoint ring until the set interface or configuration
+        * request succeeds.
+        */
+}
 struct usbssp_command *usbssp_alloc_command(struct usbssp_udc *usbssp_data,
                                            bool allocate_completion,
                                            gfp_t mem_flags)
diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
index 833647600485..bf6a147e2a16 100644
--- a/drivers/usb/usbssp/gadget.c
+++ b/drivers/usb/usbssp/gadget.c
@@ -723,6 +723,81 @@ int usbssp_dequeue(struct usbssp_ep *ep_priv, struct 
usbssp_request *req_priv)
        return ret;
 }
 
+/* Drop an endpoint from a new bandwidth configuration for this device.
+ * Only one call to this function is allowed per endpoint before
+ * check_bandwidth() or reset_bandwidth() must be called.
+ * A call to usbssp_drop_endpoint() followed by a call to usbssp_add_endpoint()
+ * will add the endpoint to the schedule with possibly new parameters
+ * denoted by a different endpoint descriptor in usbssp_ep.
+ * A call to usbssp_add_endpoint() followed by a call to
+ * usbsssp_drop_endpoint() is not allowed.
+ */
+int usbssp_drop_endpoint(struct usbssp_udc *usbssp_data, struct usb_gadget *g,
+               struct usbssp_ep *dep)
+{
+       struct usbssp_container_ctx *in_ctx, *out_ctx;
+       struct usbssp_input_control_ctx *ctrl_ctx;
+       unsigned int ep_index;
+       struct usbssp_ep_ctx *ep_ctx;
+       u32 drop_flag;
+       u32 new_add_flags, new_drop_flags;
+       int ret;
+
+       ret = usbssp_check_args(usbssp_data, dep, 1, true, __func__);
+       if (ret <= 0)
+               return ret;
+
+       if (usbssp_data->usbssp_state & USBSSP_STATE_DYING)
+               return -ENODEV;
+
+       drop_flag = usbssp_get_endpoint_flag(dep->endpoint.desc);
+       if (drop_flag == SLOT_FLAG || drop_flag == EP0_FLAG) {
+               usbssp_dbg(usbssp_data, "USBSSP %s - can't drop slot or ep 0 
%#x\n",
+                               __func__, drop_flag);
+               return 0;
+       }
+
+       in_ctx = usbssp_data->devs.in_ctx;
+       out_ctx = usbssp_data->devs.out_ctx;
+       ctrl_ctx = usbssp_get_input_control_ctx(in_ctx);
+       if (!ctrl_ctx) {
+               usbssp_warn(usbssp_data, "%s: Could not get input context, bad 
type.\n",
+                               __func__);
+               return 0;
+       }
+
+       ep_index = usbssp_get_endpoint_index(dep->endpoint.desc);
+       ep_ctx = usbssp_get_ep_ctx(usbssp_data, out_ctx, ep_index);
+       /* If the controller already knows the endpoint is disabled,
+        * or the USBSSP driver has noted it is disabled, ignore this request
+        */
+       if ((GET_EP_CTX_STATE(ep_ctx) == EP_STATE_DISABLED) ||
+           le32_to_cpu(ctrl_ctx->drop_flags) &
+           usbssp_get_endpoint_flag(dep->endpoint.desc)) {
+               /* Do not warn when called after a usb_device_reset */
+               if (usbssp_data->devs.eps[ep_index].ring != NULL)
+                       usbssp_warn(usbssp_data, "USBSSP %s called with 
disabled ep %p\n",
+                                       __func__, dep);
+               return 0;
+       }
+
+       ctrl_ctx->drop_flags |= cpu_to_le32(drop_flag);
+       new_drop_flags = le32_to_cpu(ctrl_ctx->drop_flags);
+
+       ctrl_ctx->add_flags &= cpu_to_le32(~drop_flag);
+       new_add_flags = le32_to_cpu(ctrl_ctx->add_flags);
+
+//     usbssp_debugfs_remove_endpoint(usbssp_data, &usbssp_data->devs,
+//                                    ep_index);
+
+       usbssp_endpoint_zero(usbssp_data, &usbssp_data->devs, dep);
+
+       usbssp_dbg(usbssp_data, "drop ep 0x%x, new drop flags = %#x, new add 
flags = %#x\n",
+                       (unsigned int) dep->endpoint.desc->bEndpointAddress,
+                       (unsigned int) new_drop_flags,
+                       (unsigned int) new_add_flags);
+       return 0;
+}
 
 /* Add an endpoint to a new possible bandwidth configuration for this device.
  * Only one call to this function is allowed per endpoint before
diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
index a3288cd16c4a..cc826255593f 100644
--- a/drivers/usb/usbssp/gadget.h
+++ b/drivers/usb/usbssp/gadget.h
@@ -1688,6 +1688,8 @@ void usbssp_copy_ep0_dequeue_into_input_ctx(struct 
usbssp_udc *usbssp_data);
 unsigned int usbssp_get_endpoint_index(const struct usb_endpoint_descriptor 
*desc);
 unsigned int usbssp_get_endpoint_address(unsigned int ep_index);
 unsigned int usbssp_last_valid_endpoint(u32 added_ctxs);
+void usbssp_endpoint_zero(struct usbssp_udc *usbssp_data,
+               struct usbssp_device *dev_priv, struct usbssp_ep *ep);
 int usbssp_endpoint_init(struct usbssp_udc *usbssp_data,
                struct usbssp_device *dev_priv,
                struct usbssp_ep *dep,
-- 
2.17.1

Reply via email to