summaryrefslogtreecommitdiffstats
path: root/drivers/usb/renesas_usbhs
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/renesas_usbhs')
-rw-r--r--drivers/usb/renesas_usbhs/mod_host.c302
1 files changed, 163 insertions, 139 deletions
diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c
index 164f28ee961..5b6ae2a8d30 100644
--- a/drivers/usb/renesas_usbhs/mod_host.c
+++ b/drivers/usb/renesas_usbhs/mod_host.c
@@ -84,6 +84,7 @@ struct usbhsh_device {
struct usbhsh_ep {
struct usbhs_pipe *pipe;
struct usbhsh_device *udev; /* attached udev */
+ struct usb_host_endpoint *ep;
struct list_head ep_list; /* list to usbhsh_device */
int maxp;
@@ -147,6 +148,8 @@ static const char usbhsh_hcd_name[] = "renesas_usbhs host";
#define usbhsh_ep_to_uep(u) ((u)->hcpriv)
#define usbhsh_uep_to_pipe(u) ((u)->pipe)
#define usbhsh_uep_to_udev(u) ((u)->udev)
+#define usbhsh_uep_to_ep(u) ((u)->ep)
+
#define usbhsh_urb_to_ureq(u) ((u)->hcpriv)
#define usbhsh_urb_to_usbv(u) ((u)->dev)
@@ -205,6 +208,157 @@ static void usbhsh_ureq_free(struct usbhsh_hpriv *hpriv,
}
/*
+ * end-point control
+ */
+static struct usbhsh_device *usbhsh_device_get(struct usbhsh_hpriv *hpriv,
+ struct urb *urb);
+static int usbhsh_endpoint_attach(struct usbhsh_hpriv *hpriv,
+ struct urb *urb,
+ gfp_t mem_flags)
+{
+ struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+ struct usbhsh_device *udev = usbhsh_device_get(hpriv, urb);
+ struct usb_host_endpoint *ep = urb->ep;
+ struct usbhsh_ep *uep;
+ struct usbhsh_pipe_info *info;
+ struct usbhs_pipe *best_pipe = NULL;
+ struct device *dev = usbhs_priv_to_dev(priv);
+ struct usb_endpoint_descriptor *desc = &ep->desc;
+ unsigned long flags;
+
+ uep = kzalloc(sizeof(struct usbhsh_ep), mem_flags);
+ if (!uep) {
+ dev_err(dev, "usbhsh_ep alloc fail\n");
+ return -ENOMEM;
+ }
+
+ /******************** spin lock ********************/
+ usbhs_lock(priv, flags);
+
+ /*
+ * find best pipe for endpoint
+ * see
+ * HARDWARE LIMITATION
+ */
+ if (usb_endpoint_xfer_control(desc)) {
+ /* best pipe is DCP */
+ best_pipe = usbhsh_hpriv_to_dcp(hpriv);
+ } else {
+ struct usbhs_pipe *pipe;
+ unsigned int min_usr = ~0;
+ int dir_in_req = !!usb_pipein(urb->pipe);
+ int i, dir_in;
+
+ usbhs_for_each_pipe(pipe, priv, i) {
+ if (!usbhs_pipe_type_is(pipe, usb_endpoint_type(desc)))
+ continue;
+
+ dir_in = !!usbhs_pipe_is_dir_in(pipe);
+ if (0 != (dir_in - dir_in_req))
+ continue;
+
+ info = usbhsh_pipe_info(pipe);
+ if (min_usr > info->usr_cnt) {
+ min_usr = info->usr_cnt;
+ best_pipe = pipe;
+ }
+ }
+ }
+
+ if (best_pipe) {
+ /* update pipe user count */
+ info = usbhsh_pipe_info(best_pipe);
+ info->usr_cnt++;
+
+ /* init this endpoint, and attach it to udev */
+ INIT_LIST_HEAD(&uep->ep_list);
+ list_add_tail(&uep->ep_list, &udev->ep_list_head);
+ }
+
+ usbhs_unlock(priv, flags);
+ /******************** spin unlock ******************/
+
+ if (unlikely(!best_pipe)) {
+ dev_err(dev, "couldn't find best pipe\n");
+ kfree(uep);
+ return -EIO;
+ }
+
+ /*
+ * init uep
+ */
+ uep->pipe = best_pipe;
+ uep->maxp = usb_endpoint_maxp(desc);
+ usbhsh_uep_to_udev(uep) = udev;
+ usbhsh_uep_to_ep(uep) = ep;
+ usbhsh_ep_to_uep(ep) = uep;
+
+ /*
+ * usbhs_pipe_config_update() should be called after
+ * usbhs_set_device_config()
+ * see
+ * DCPMAXP/PIPEMAXP
+ */
+ usbhs_pipe_sequence_data0(uep->pipe);
+ usbhs_pipe_config_update(uep->pipe,
+ usbhsh_device_number(hpriv, udev),
+ usb_endpoint_num(desc),
+ uep->maxp);
+
+ dev_dbg(dev, "%s [%d-%s](%p)\n", __func__,
+ usbhsh_device_number(hpriv, udev),
+ usbhs_pipe_name(uep->pipe), uep);
+
+ return 0;
+}
+
+static void usbhsh_endpoint_detach(struct usbhsh_hpriv *hpriv,
+ struct usb_host_endpoint *ep)
+{
+ struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+ struct device *dev = usbhs_priv_to_dev(priv);
+ struct usbhsh_ep *uep = usbhsh_ep_to_uep(ep);
+ struct usbhsh_pipe_info *info;
+ unsigned long flags;
+
+ if (!uep)
+ return;
+
+ dev_dbg(dev, "%s [%d-%s](%p)\n", __func__,
+ usbhsh_device_number(hpriv, usbhsh_uep_to_udev(uep)),
+ usbhs_pipe_name(uep->pipe), uep);
+
+ /******************** spin lock ********************/
+ usbhs_lock(priv, flags);
+
+ info = usbhsh_pipe_info(uep->pipe);
+ info->usr_cnt--;
+
+ /* remove this endpoint from udev */
+ list_del_init(&uep->ep_list);
+
+ uep->pipe = NULL;
+ uep->maxp = 0;
+ usbhsh_uep_to_udev(uep) = NULL;
+ usbhsh_uep_to_ep(uep) = NULL;
+ usbhsh_ep_to_uep(ep) = NULL;
+
+ usbhs_unlock(priv, flags);
+ /******************** spin unlock ******************/
+
+ kfree(uep);
+}
+
+static void usbhsh_endpoint_detach_all(struct usbhsh_hpriv *hpriv,
+ struct usbhsh_device *udev)
+{
+ struct usbhsh_ep *uep, *next;
+
+ list_for_each_entry_safe(uep, next, &udev->ep_list_head, ep_list)
+ usbhsh_endpoint_detach(hpriv, usbhsh_uep_to_ep(uep));
+}
+
+/*
* device control
*/
static int usbhsh_connected_to_rhdev(struct usb_hcd *hcd,
@@ -295,11 +449,15 @@ static struct usbhsh_device *usbhsh_device_attach(struct usbhsh_hpriv *hpriv,
return NULL;
}
- if (usbhsh_device_has_endpoint(udev))
+ if (usbhsh_device_has_endpoint(udev)) {
dev_warn(dev, "udev have old endpoint\n");
+ usbhsh_endpoint_detach_all(hpriv, udev);
+ }
- if (usbhsh_device_has_endpoint(udev0))
+ if (usbhsh_device_has_endpoint(udev0)) {
dev_warn(dev, "udev0 have old endpoint\n");
+ usbhsh_endpoint_detach_all(hpriv, udev0);
+ }
/* uep will be attached */
INIT_LIST_HEAD(&udev0->ep_list_head);
@@ -349,8 +507,10 @@ static void usbhsh_device_detach(struct usbhsh_hpriv *hpriv,
dev_dbg(dev, "%s [%d](%p)\n", __func__,
usbhsh_device_number(hpriv, udev), udev);
- if (usbhsh_device_has_endpoint(udev))
+ if (usbhsh_device_has_endpoint(udev)) {
dev_warn(dev, "udev still have endpoint\n");
+ usbhsh_endpoint_detach_all(hpriv, udev);
+ }
/*
* There is nothing to do if it is device0.
@@ -377,142 +537,6 @@ static void usbhsh_device_detach(struct usbhsh_hpriv *hpriv,
}
/*
- * end-point control
- */
-static int usbhsh_endpoint_attach(struct usbhsh_hpriv *hpriv,
- struct urb *urb,
- gfp_t mem_flags)
-{
- struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
- struct usbhsh_device *udev = usbhsh_device_get(hpriv, urb);
- struct usb_host_endpoint *ep = urb->ep;
- struct usbhsh_ep *uep;
- struct usbhsh_pipe_info *info;
- struct usbhs_pipe *best_pipe = NULL;
- struct device *dev = usbhs_priv_to_dev(priv);
- struct usb_endpoint_descriptor *desc = &ep->desc;
- unsigned long flags;
-
- uep = kzalloc(sizeof(struct usbhsh_ep), mem_flags);
- if (!uep) {
- dev_err(dev, "usbhsh_ep alloc fail\n");
- return -ENOMEM;
- }
-
- /******************** spin lock ********************/
- usbhs_lock(priv, flags);
-
- /*
- * find best pipe for endpoint
- * see
- * HARDWARE LIMITATION
- */
- if (usb_endpoint_xfer_control(desc)) {
- /* best pipe is DCP */
- best_pipe = usbhsh_hpriv_to_dcp(hpriv);
- } else {
- struct usbhs_pipe *pipe;
- unsigned int min_usr = ~0;
- int dir_in_req = !!usb_pipein(urb->pipe);
- int i, dir_in;
-
- usbhs_for_each_pipe(pipe, priv, i) {
- if (!usbhs_pipe_type_is(pipe, usb_endpoint_type(desc)))
- continue;
-
- dir_in = !!usbhs_pipe_is_dir_in(pipe);
- if (0 != (dir_in - dir_in_req))
- continue;
-
- info = usbhsh_pipe_info(pipe);
- if (min_usr > info->usr_cnt) {
- min_usr = info->usr_cnt;
- best_pipe = pipe;
- }
- }
- }
-
- if (best_pipe) {
- /* update pipe user count */
- info = usbhsh_pipe_info(best_pipe);
- info->usr_cnt++;
-
- /* init this endpoint, and attach it to udev */
- INIT_LIST_HEAD(&uep->ep_list);
- list_add_tail(&uep->ep_list, &udev->ep_list_head);
- }
-
- usbhs_unlock(priv, flags);
- /******************** spin unlock ******************/
-
- if (unlikely(!best_pipe)) {
- dev_err(dev, "couldn't find best pipe\n");
- kfree(uep);
- return -EIO;
- }
-
- /*
- * init uep
- */
- uep->pipe = best_pipe;
- uep->maxp = usb_endpoint_maxp(desc);
- usbhsh_uep_to_udev(uep) = udev;
- usbhsh_ep_to_uep(ep) = uep;
-
- /*
- * usbhs_pipe_config_update() should be called after
- * usbhs_set_device_config()
- * see
- * DCPMAXP/PIPEMAXP
- */
- usbhs_pipe_sequence_data0(uep->pipe);
- usbhs_pipe_config_update(uep->pipe,
- usbhsh_device_number(hpriv, udev),
- usb_endpoint_num(desc),
- uep->maxp);
-
- dev_dbg(dev, "%s [%d-%s](%p)\n", __func__,
- usbhsh_device_number(hpriv, udev),
- usbhs_pipe_name(uep->pipe), uep);
-
- return 0;
-}
-
-static void usbhsh_endpoint_detach(struct usbhsh_hpriv *hpriv,
- struct usb_host_endpoint *ep)
-{
- struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
- struct device *dev = usbhs_priv_to_dev(priv);
- struct usbhsh_ep *uep = usbhsh_ep_to_uep(ep);
- struct usbhsh_pipe_info *info;
- unsigned long flags;
-
- if (!uep)
- return;
-
- dev_dbg(dev, "%s [%d-%s](%p)\n", __func__,
- usbhsh_device_number(hpriv, usbhsh_uep_to_udev(uep)),
- usbhs_pipe_name(uep->pipe), uep);
-
- /******************** spin lock ********************/
- usbhs_lock(priv, flags);
-
- info = usbhsh_pipe_info(uep->pipe);
- info->usr_cnt--;
-
- /* remove this endpoint from udev */
- list_del_init(&uep->ep_list);
-
- usbhsh_uep_to_udev(uep) = NULL;
- usbhsh_ep_to_uep(ep) = NULL;
-
- usbhs_unlock(priv, flags);
- /******************** spin unlock ******************/
-
- kfree(uep);
-}
-
-/*
* queue push/pop
*/
static void usbhsh_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt)