Commit e4c57ded authored by Kuninori Morimoto's avatar Kuninori Morimoto Committed by Felipe Balbi

usb: renesas_usbhs: add usbhsh_endpoint_detach_all() for error case

This patch adds usbhsh_endpoint_detach_all() for error case.
usbhs_endpoitn_xxx() functions were moved to upper side in source code.
Signed-off-by: default avatarKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
parent c1e4877a
......@@ -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)
......@@ -204,6 +207,157 @@ static void usbhsh_ureq_free(struct usbhsh_hpriv *hpriv,
kfree(ureq);
}
/*
* 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
*/
......@@ -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.
......@@ -376,142 +536,6 @@ static void usbhsh_device_detach(struct usbhsh_hpriv *hpriv,
/******************** spin unlock ******************/
}
/*
* 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
*/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment