mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-05 10:29:37 +00:00
USB: xhci: Configure endpoint code refactoring.
Refactor out the code issue, wait for, and parse the event completion code for a configure endpoint command. Modify it to support the evaluate context command, which has a very similar submission process. Add functions to copy parts of the output context into the input context (which will be used in the evaluate context command). Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Cc: stable <stable@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
018218d1d9
commit
f2217e8edd
4 changed files with 169 additions and 52 deletions
|
@ -942,6 +942,122 @@ static void xhci_zero_in_ctx(struct xhci_hcd *xhci, struct xhci_virt_device *vir
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int xhci_configure_endpoint_result(struct xhci_hcd *xhci,
|
||||||
|
struct usb_device *udev, struct xhci_virt_device *virt_dev)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
switch (virt_dev->cmd_status) {
|
||||||
|
case COMP_ENOMEM:
|
||||||
|
dev_warn(&udev->dev, "Not enough host controller resources "
|
||||||
|
"for new device state.\n");
|
||||||
|
ret = -ENOMEM;
|
||||||
|
/* FIXME: can we allocate more resources for the HC? */
|
||||||
|
break;
|
||||||
|
case COMP_BW_ERR:
|
||||||
|
dev_warn(&udev->dev, "Not enough bandwidth "
|
||||||
|
"for new device state.\n");
|
||||||
|
ret = -ENOSPC;
|
||||||
|
/* FIXME: can we go back to the old state? */
|
||||||
|
break;
|
||||||
|
case COMP_TRB_ERR:
|
||||||
|
/* the HCD set up something wrong */
|
||||||
|
dev_warn(&udev->dev, "ERROR: Endpoint drop flag = 0, "
|
||||||
|
"add flag = 1, "
|
||||||
|
"and endpoint is not disabled.\n");
|
||||||
|
ret = -EINVAL;
|
||||||
|
break;
|
||||||
|
case COMP_SUCCESS:
|
||||||
|
dev_dbg(&udev->dev, "Successful Endpoint Configure command\n");
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
xhci_err(xhci, "ERROR: unexpected command completion "
|
||||||
|
"code 0x%x.\n", virt_dev->cmd_status);
|
||||||
|
ret = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xhci_evaluate_context_result(struct xhci_hcd *xhci,
|
||||||
|
struct usb_device *udev, struct xhci_virt_device *virt_dev)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
switch (virt_dev->cmd_status) {
|
||||||
|
case COMP_EINVAL:
|
||||||
|
dev_warn(&udev->dev, "WARN: xHCI driver setup invalid evaluate "
|
||||||
|
"context command.\n");
|
||||||
|
ret = -EINVAL;
|
||||||
|
break;
|
||||||
|
case COMP_EBADSLT:
|
||||||
|
dev_warn(&udev->dev, "WARN: slot not enabled for"
|
||||||
|
"evaluate context command.\n");
|
||||||
|
case COMP_CTX_STATE:
|
||||||
|
dev_warn(&udev->dev, "WARN: invalid context state for "
|
||||||
|
"evaluate context command.\n");
|
||||||
|
xhci_dbg_ctx(xhci, virt_dev->out_ctx, 1);
|
||||||
|
ret = -EINVAL;
|
||||||
|
break;
|
||||||
|
case COMP_SUCCESS:
|
||||||
|
dev_dbg(&udev->dev, "Successful evaluate context command\n");
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
xhci_err(xhci, "ERROR: unexpected command completion "
|
||||||
|
"code 0x%x.\n", virt_dev->cmd_status);
|
||||||
|
ret = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Issue a configure endpoint command or evaluate context command
|
||||||
|
* and wait for it to finish.
|
||||||
|
*/
|
||||||
|
static int xhci_configure_endpoint(struct xhci_hcd *xhci,
|
||||||
|
struct usb_device *udev, struct xhci_virt_device *virt_dev,
|
||||||
|
bool ctx_change)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
int timeleft;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&xhci->lock, flags);
|
||||||
|
if (!ctx_change)
|
||||||
|
ret = xhci_queue_configure_endpoint(xhci, virt_dev->in_ctx->dma,
|
||||||
|
udev->slot_id);
|
||||||
|
else
|
||||||
|
ret = xhci_queue_evaluate_context(xhci, virt_dev->in_ctx->dma,
|
||||||
|
udev->slot_id);
|
||||||
|
if (ret < 0) {
|
||||||
|
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||||
|
xhci_dbg(xhci, "FIXME allocate a new ring segment\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
xhci_ring_cmd_db(xhci);
|
||||||
|
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||||
|
|
||||||
|
/* Wait for the configure endpoint command to complete */
|
||||||
|
timeleft = wait_for_completion_interruptible_timeout(
|
||||||
|
&virt_dev->cmd_completion,
|
||||||
|
USB_CTRL_SET_TIMEOUT);
|
||||||
|
if (timeleft <= 0) {
|
||||||
|
xhci_warn(xhci, "%s while waiting for %s command\n",
|
||||||
|
timeleft == 0 ? "Timeout" : "Signal",
|
||||||
|
ctx_change == 0 ?
|
||||||
|
"configure endpoint" :
|
||||||
|
"evaluate context");
|
||||||
|
/* FIXME cancel the configure endpoint command */
|
||||||
|
return -ETIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctx_change)
|
||||||
|
return xhci_configure_endpoint_result(xhci, udev, virt_dev);
|
||||||
|
return xhci_evaluate_context_result(xhci, udev, virt_dev);
|
||||||
|
}
|
||||||
|
|
||||||
/* Called after one or more calls to xhci_add_endpoint() or
|
/* Called after one or more calls to xhci_add_endpoint() or
|
||||||
* xhci_drop_endpoint(). If this call fails, the USB core is expected
|
* xhci_drop_endpoint(). If this call fails, the USB core is expected
|
||||||
* to call xhci_reset_bandwidth().
|
* to call xhci_reset_bandwidth().
|
||||||
|
@ -956,8 +1072,6 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int timeleft;
|
|
||||||
unsigned long flags;
|
|
||||||
struct xhci_hcd *xhci;
|
struct xhci_hcd *xhci;
|
||||||
struct xhci_virt_device *virt_dev;
|
struct xhci_virt_device *virt_dev;
|
||||||
struct xhci_input_control_ctx *ctrl_ctx;
|
struct xhci_input_control_ctx *ctrl_ctx;
|
||||||
|
@ -987,56 +1101,7 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
|
||||||
xhci_dbg_ctx(xhci, virt_dev->in_ctx,
|
xhci_dbg_ctx(xhci, virt_dev->in_ctx,
|
||||||
LAST_CTX_TO_EP_NUM(slot_ctx->dev_info));
|
LAST_CTX_TO_EP_NUM(slot_ctx->dev_info));
|
||||||
|
|
||||||
spin_lock_irqsave(&xhci->lock, flags);
|
ret = xhci_configure_endpoint(xhci, udev, virt_dev, false);
|
||||||
ret = xhci_queue_configure_endpoint(xhci, virt_dev->in_ctx->dma,
|
|
||||||
udev->slot_id);
|
|
||||||
if (ret < 0) {
|
|
||||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
|
||||||
xhci_dbg(xhci, "FIXME allocate a new ring segment\n");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
xhci_ring_cmd_db(xhci);
|
|
||||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
|
||||||
|
|
||||||
/* Wait for the configure endpoint command to complete */
|
|
||||||
timeleft = wait_for_completion_interruptible_timeout(
|
|
||||||
&virt_dev->cmd_completion,
|
|
||||||
USB_CTRL_SET_TIMEOUT);
|
|
||||||
if (timeleft <= 0) {
|
|
||||||
xhci_warn(xhci, "%s while waiting for configure endpoint command\n",
|
|
||||||
timeleft == 0 ? "Timeout" : "Signal");
|
|
||||||
/* FIXME cancel the configure endpoint command */
|
|
||||||
return -ETIME;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (virt_dev->cmd_status) {
|
|
||||||
case COMP_ENOMEM:
|
|
||||||
dev_warn(&udev->dev, "Not enough host controller resources "
|
|
||||||
"for new device state.\n");
|
|
||||||
ret = -ENOMEM;
|
|
||||||
/* FIXME: can we allocate more resources for the HC? */
|
|
||||||
break;
|
|
||||||
case COMP_BW_ERR:
|
|
||||||
dev_warn(&udev->dev, "Not enough bandwidth "
|
|
||||||
"for new device state.\n");
|
|
||||||
ret = -ENOSPC;
|
|
||||||
/* FIXME: can we go back to the old state? */
|
|
||||||
break;
|
|
||||||
case COMP_TRB_ERR:
|
|
||||||
/* the HCD set up something wrong */
|
|
||||||
dev_warn(&udev->dev, "ERROR: Endpoint drop flag = 0, add flag = 1, "
|
|
||||||
"and endpoint is not disabled.\n");
|
|
||||||
ret = -EINVAL;
|
|
||||||
break;
|
|
||||||
case COMP_SUCCESS:
|
|
||||||
dev_dbg(&udev->dev, "Successful Endpoint Configure command\n");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
xhci_err(xhci, "ERROR: unexpected command completion "
|
|
||||||
"code 0x%x.\n", virt_dev->cmd_status);
|
|
||||||
ret = -EINVAL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
/* Callee should call reset_bandwidth() */
|
/* Callee should call reset_bandwidth() */
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -601,6 +601,44 @@ void xhci_endpoint_zero(struct xhci_hcd *xhci,
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Copy output xhci_ep_ctx to the input xhci_ep_ctx copy.
|
||||||
|
* Useful when you want to change one particular aspect of the endpoint and then
|
||||||
|
* issue a configure endpoint command.
|
||||||
|
*/
|
||||||
|
void xhci_endpoint_copy(struct xhci_hcd *xhci,
|
||||||
|
struct xhci_virt_device *vdev, unsigned int ep_index)
|
||||||
|
{
|
||||||
|
struct xhci_ep_ctx *out_ep_ctx;
|
||||||
|
struct xhci_ep_ctx *in_ep_ctx;
|
||||||
|
|
||||||
|
out_ep_ctx = xhci_get_ep_ctx(xhci, vdev->out_ctx, ep_index);
|
||||||
|
in_ep_ctx = xhci_get_ep_ctx(xhci, vdev->in_ctx, ep_index);
|
||||||
|
|
||||||
|
in_ep_ctx->ep_info = out_ep_ctx->ep_info;
|
||||||
|
in_ep_ctx->ep_info2 = out_ep_ctx->ep_info2;
|
||||||
|
in_ep_ctx->deq = out_ep_ctx->deq;
|
||||||
|
in_ep_ctx->tx_info = out_ep_ctx->tx_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy output xhci_slot_ctx to the input xhci_slot_ctx.
|
||||||
|
* Useful when you want to change one particular aspect of the endpoint and then
|
||||||
|
* issue a configure endpoint command. Only the context entries field matters,
|
||||||
|
* but we'll copy the whole thing anyway.
|
||||||
|
*/
|
||||||
|
void xhci_slot_copy(struct xhci_hcd *xhci, struct xhci_virt_device *vdev)
|
||||||
|
{
|
||||||
|
struct xhci_slot_ctx *in_slot_ctx;
|
||||||
|
struct xhci_slot_ctx *out_slot_ctx;
|
||||||
|
|
||||||
|
in_slot_ctx = xhci_get_slot_ctx(xhci, vdev->in_ctx);
|
||||||
|
out_slot_ctx = xhci_get_slot_ctx(xhci, vdev->out_ctx);
|
||||||
|
|
||||||
|
in_slot_ctx->dev_info = out_slot_ctx->dev_info;
|
||||||
|
in_slot_ctx->dev_info2 = out_slot_ctx->dev_info2;
|
||||||
|
in_slot_ctx->tt_info = out_slot_ctx->tt_info;
|
||||||
|
in_slot_ctx->dev_state = out_slot_ctx->dev_state;
|
||||||
|
}
|
||||||
|
|
||||||
/* Set up the scratchpad buffer array and scratchpad buffers, if needed. */
|
/* Set up the scratchpad buffer array and scratchpad buffers, if needed. */
|
||||||
static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags)
|
static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1740,6 +1740,15 @@ int xhci_queue_configure_endpoint(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
|
||||||
TRB_TYPE(TRB_CONFIG_EP) | SLOT_ID_FOR_TRB(slot_id));
|
TRB_TYPE(TRB_CONFIG_EP) | SLOT_ID_FOR_TRB(slot_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Queue an evaluate context command TRB */
|
||||||
|
int xhci_queue_evaluate_context(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
|
||||||
|
u32 slot_id)
|
||||||
|
{
|
||||||
|
return queue_command(xhci, lower_32_bits(in_ctx_ptr),
|
||||||
|
upper_32_bits(in_ctx_ptr), 0,
|
||||||
|
TRB_TYPE(TRB_EVAL_CONTEXT) | SLOT_ID_FOR_TRB(slot_id));
|
||||||
|
}
|
||||||
|
|
||||||
int xhci_queue_stop_endpoint(struct xhci_hcd *xhci, int slot_id,
|
int xhci_queue_stop_endpoint(struct xhci_hcd *xhci, int slot_id,
|
||||||
unsigned int ep_index)
|
unsigned int ep_index)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1168,6 +1168,9 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud
|
||||||
unsigned int xhci_get_endpoint_index(struct usb_endpoint_descriptor *desc);
|
unsigned int xhci_get_endpoint_index(struct usb_endpoint_descriptor *desc);
|
||||||
unsigned int xhci_get_endpoint_flag(struct usb_endpoint_descriptor *desc);
|
unsigned int xhci_get_endpoint_flag(struct usb_endpoint_descriptor *desc);
|
||||||
void xhci_endpoint_zero(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, struct usb_host_endpoint *ep);
|
void xhci_endpoint_zero(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, struct usb_host_endpoint *ep);
|
||||||
|
void xhci_endpoint_copy(struct xhci_hcd *xhci,
|
||||||
|
struct xhci_virt_device *vdev, unsigned int ep_index);
|
||||||
|
void xhci_slot_copy(struct xhci_hcd *xhci, struct xhci_virt_device *vdev);
|
||||||
int xhci_endpoint_init(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev,
|
int xhci_endpoint_init(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev,
|
||||||
struct usb_device *udev, struct usb_host_endpoint *ep,
|
struct usb_device *udev, struct usb_host_endpoint *ep,
|
||||||
gfp_t mem_flags);
|
gfp_t mem_flags);
|
||||||
|
@ -1216,6 +1219,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb,
|
||||||
int slot_id, unsigned int ep_index);
|
int slot_id, unsigned int ep_index);
|
||||||
int xhci_queue_configure_endpoint(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
|
int xhci_queue_configure_endpoint(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
|
||||||
u32 slot_id);
|
u32 slot_id);
|
||||||
|
int xhci_queue_evaluate_context(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
|
||||||
|
u32 slot_id);
|
||||||
int xhci_queue_reset_ep(struct xhci_hcd *xhci, int slot_id,
|
int xhci_queue_reset_ep(struct xhci_hcd *xhci, int slot_id,
|
||||||
unsigned int ep_index);
|
unsigned int ep_index);
|
||||||
void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
|
void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
|
||||||
|
|
Loading…
Reference in a new issue