lkml.org 
[lkml]   [2018]   [Jul]   [19]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 14/31] usb: usbssp: added procedure handling command completion events.
    Date
    This patch extends the usbssp_handle_event function with a new case,
    that is responsible for servicing command completion events.

    For this purpose, it adds handle_cmd_completion function which in turn
    invokes some other functions depending of handling command type.

    Signed-off-by: Pawel Laszczak <pawell@cadence.com>
    ---
    drivers/usb/usbssp/gadget-mem.c | 53 +++-
    drivers/usb/usbssp/gadget-ring.c | 510 +++++++++++++++++++++++++++++++
    drivers/usb/usbssp/gadget.c | 11 +
    drivers/usb/usbssp/gadget.h | 12 +
    4 files changed, 584 insertions(+), 2 deletions(-)

    diff --git a/drivers/usb/usbssp/gadget-mem.c b/drivers/usb/usbssp/gadget-mem.c
    index fef83b6b6cf0..5708a0090ead 100644
    --- a/drivers/usb/usbssp/gadget-mem.c
    +++ b/drivers/usb/usbssp/gadget-mem.c
    @@ -504,6 +504,38 @@ void usbssp_free_container_ctx(struct usbssp_udc *usbssp_data,
    kfree(ctx);
    }

    +struct usbssp_input_control_ctx *usbssp_get_input_control_ctx(
    + struct usbssp_container_ctx *ctx)
    +{
    + if (ctx->type != USBSSP_CTX_TYPE_INPUT)
    + return NULL;
    +
    + return (struct usbssp_input_control_ctx *)ctx->bytes;
    +}
    +
    +struct usbssp_slot_ctx *usbssp_get_slot_ctx(struct usbssp_udc *usbssp_data,
    + struct usbssp_container_ctx *ctx)
    +{
    + if (ctx->type == USBSSP_CTX_TYPE_DEVICE)
    + return (struct usbssp_slot_ctx *)ctx->bytes;
    +
    + return (struct usbssp_slot_ctx *) (ctx->bytes +
    + CTX_SIZE(usbssp_data->hcc_params));
    +}
    +
    +struct usbssp_ep_ctx *usbssp_get_ep_ctx(struct usbssp_udc *usbssp_data,
    + struct usbssp_container_ctx *ctx,
    + unsigned int ep_index)
    +{
    + /* increment ep index by offset of start of ep ctx array */
    + ep_index++;
    + if (ctx->type == USBSSP_CTX_TYPE_INPUT)
    + ep_index++;
    +
    + return (struct usbssp_ep_ctx *) (ctx->bytes +
    + (ep_index * CTX_SIZE(usbssp_data->hcc_params)));
    +}
    +
    /***************** Streams structures manipulation *************************/
    static void usbssp_free_stream_ctx(struct usbssp_udc *usbssp_data,
    unsigned int num_stream_ctxs,
    @@ -523,8 +555,25 @@ static void usbssp_free_stream_ctx(struct usbssp_udc *usbssp_data,
    stream_ctx, dma);
    }

    -/**
    - * Frees all stream contexts associated with the endpoint,
    +struct usbssp_ring *usbssp_stream_id_to_ring(struct usbssp_device *dev,
    + unsigned int ep_index,
    + unsigned int stream_id)
    +{
    + struct usbssp_ep *ep = &dev->eps[ep_index];
    +
    + if (stream_id == 0)
    + return ep->ring;
    +
    + if (!ep->stream_info)
    + return NULL;
    +
    + if (stream_id > ep->stream_info->num_streams)
    + return NULL;
    + return ep->stream_info->stream_rings[stream_id];
    +}
    +
    +/*
    + * Frees all stream contexts associated with the endpoint,
    *
    * Caller should fix the endpoint context streams fields.
    */
    diff --git a/drivers/usb/usbssp/gadget-ring.c b/drivers/usb/usbssp/gadget-ring.c
    index 6679d8ec7152..c704b939b48a 100644
    --- a/drivers/usb/usbssp/gadget-ring.c
    +++ b/drivers/usb/usbssp/gadget-ring.c
    @@ -225,6 +225,107 @@ static bool usbssp_mod_cmd_timer(struct usbssp_udc *usbssp_data,
    return 0;
    }

    +void usbssp_ring_ep_doorbell(struct usbssp_udc *usbssp_data,
    + unsigned int ep_index,
    + unsigned int stream_id)
    +{
    + __le32 __iomem *db_addr =
    + &usbssp_data->dba->doorbell[usbssp_data->slot_id];
    + struct usbssp_ep *ep = &usbssp_data->devs.eps[ep_index];
    + unsigned int ep_state = ep->ep_state;
    + unsigned int db_value;
    +
    + /*
    + * Don't ring the doorbell for this endpoint if there are pending
    + * cancellations because we don't want to interrupt processing.
    + * We don't want to restart any stream rings if there's a set dequeue
    + * pointer command pending because the device can choose to start any
    + * stream once the endpoint is on the HW schedule.
    + * Also we don't want restart any endpoint if endpoint is halted or
    + * disabled and also if endpoint disabling is pending.
    + */
    + if ((ep_state & EP_STOP_CMD_PENDING) ||
    + (ep_state & SET_DEQ_PENDING) ||
    + (ep_state & EP_HALTED) ||
    + !(ep_state & USBSSP_EP_ENABLED) ||
    + (ep_state & USBSSP_EP_DISABLE_PENDING))
    + return;
    +
    + if (ep_index == 0 && !usbssp_data->ep0_expect_in &&
    + usbssp_data->ep0state == USBSSP_EP0_DATA_PHASE)
    + db_value = DB_VALUE_EP0_OUT(ep_index, stream_id);
    + else
    + db_value = DB_VALUE(ep_index, stream_id);
    +
    + dev_dbg(usbssp_data->dev, "// Ding dong transfer ring for %s!"
    + " - [DB addr/DB val]: [%p/%08x]\n",
    + usbssp_data->devs.eps[ep_index].name, db_addr,
    + db_value);
    +
    + writel(db_value, db_addr);
    + /*
    + * The CPU has better things to do at this point than wait for a
    + * write-posting flush. It'll get there soon enough.
    + */
    +}
    +
    +/* Ring the doorbell for any rings with pending USB requests */
    +static void ring_doorbell_for_active_rings(struct usbssp_udc *usbssp_data,
    + unsigned int ep_index)
    +{
    + unsigned int stream_id;
    + struct usbssp_ep *ep;
    +
    + ep = &usbssp_data->devs.eps[ep_index];
    +
    + dev_dbg(usbssp_data->dev, "Ring all active ring for %s\n",
    + ep->name);
    +
    + /* A ring has pending Request if its TD list is not empty */
    + if (!(ep->ep_state & EP_HAS_STREAMS)) {
    + if (ep->ring && !(list_empty(&ep->ring->td_list)))
    + usbssp_ring_ep_doorbell(usbssp_data, ep_index, 0);
    + return;
    + }
    +
    + for (stream_id = 1; stream_id < ep->stream_info->num_streams;
    + stream_id++) {
    + struct usbssp_stream_info *stream_info = ep->stream_info;
    +
    + if (!list_empty(&stream_info->stream_rings[stream_id]->td_list))
    + usbssp_ring_ep_doorbell(usbssp_data, ep_index,
    + stream_id);
    + }
    +}
    +
    +/*
    + * When we get a command completion for a Stop Endpoint Command, we need to
    + * stop timer and clear EP_STOP_CMD_PENDING flag.
    + */
    +static void usbssp_handle_cmd_stop_ep(struct usbssp_udc *usbssp_data,
    + union usbssp_trb *trb,
    + struct usbssp_event_cmd *event)
    +{
    + unsigned int ep_index;
    + struct usbssp_ep *ep;
    + struct usbssp_ep_ctx *ep_ctx;
    + struct usbssp_device *priv_dev;
    +
    + ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
    + ep = &usbssp_data->devs.eps[ep_index];
    +
    + dev_dbg(usbssp_data->dev,
    + "CMD stop endpoint completion for ep index: %d - %s\n",
    + ep_index, ep->name);
    +
    +
    + priv_dev = &usbssp_data->devs;
    + ep_ctx = usbssp_get_ep_ctx(usbssp_data, priv_dev->out_ctx, ep_index);
    + trace_usbssp_handle_cmd_stop_ep(ep_ctx);
    +
    + ep->ep_state &= ~EP_STOP_CMD_PENDING;
    +}
    +
    static void usbssp_kill_ring_requests(struct usbssp_udc *usbssp_data,
    struct usbssp_ring *ring)
    {
    @@ -300,6 +401,262 @@ void usbssp_udc_died(struct usbssp_udc *usbssp_data)
    usbssp_kill_endpoint_request(usbssp_data, i);
    }

    +static void update_ring_for_set_deq_completion(struct usbssp_udc *usbssp_data,
    + struct usbssp_device *dev,
    + struct usbssp_ring *ep_ring,
    + unsigned int ep_index)
    +{
    + union usbssp_trb *dequeue_temp;
    + int num_trbs_free_temp;
    + bool revert = false;
    +
    + num_trbs_free_temp = ep_ring->num_trbs_free;
    + dequeue_temp = ep_ring->dequeue;
    +
    + if (trb_is_link(ep_ring->dequeue)) {
    + ep_ring->deq_seg = ep_ring->deq_seg->next;
    + ep_ring->dequeue = ep_ring->deq_seg->trbs;
    + }
    +
    + while (ep_ring->dequeue != dev->eps[ep_index].queued_deq_ptr) {
    + /* We have more usable TRBs */
    + ep_ring->num_trbs_free++;
    + ep_ring->dequeue++;
    + if (trb_is_link(ep_ring->dequeue)) {
    + if (ep_ring->dequeue ==
    + dev->eps[ep_index].queued_deq_ptr)
    + break;
    + ep_ring->deq_seg = ep_ring->deq_seg->next;
    + ep_ring->dequeue = ep_ring->deq_seg->trbs;
    + }
    + if (ep_ring->dequeue == dequeue_temp) {
    + revert = true;
    + break;
    + }
    + }
    +
    + if (revert) {
    + dev_dbg(usbssp_data->dev, "Unable to find new dequeue pointer\n");
    + ep_ring->num_trbs_free = num_trbs_free_temp;
    + }
    +}
    +
    +/*
    + * When we get a completion for a Set Transfer Ring Dequeue Pointer command,
    + * we need to clear the set deq pending flag in the endpoint ring state, so that
    + * the TD queueing code can ring the doorbell again. We also need to ring the
    + * endpoint doorbell to restart the ring
    + */
    +static void usbssp_handle_cmd_set_deq(struct usbssp_udc *usbssp_data,
    + union usbssp_trb *trb, u32 cmd_comp_code)
    +{
    + unsigned int ep_index;
    + unsigned int stream_id;
    + struct usbssp_ring *ep_ring;
    + struct usbssp_device *dev;
    + struct usbssp_ep *ep;
    + struct usbssp_ep_ctx *ep_ctx;
    + struct usbssp_slot_ctx *slot_ctx;
    +
    + ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
    + stream_id = TRB_TO_STREAM_ID(le32_to_cpu(trb->generic.field[2]));
    + dev = &usbssp_data->devs;
    + ep = &dev->eps[ep_index];
    +
    + ep_ring = usbssp_stream_id_to_ring(dev, ep_index, stream_id);
    + if (!ep_ring) {
    + dev_warn(usbssp_data->dev,
    + "WARN Set TR deq ptr command for freed stream ID %u\n",
    + stream_id);
    + goto cleanup;
    + }
    +
    + ep_ctx = usbssp_get_ep_ctx(usbssp_data, dev->out_ctx, ep_index);
    + slot_ctx = usbssp_get_slot_ctx(usbssp_data, dev->out_ctx);
    + trace_usbssp_handle_cmd_set_deq(slot_ctx);
    + trace_usbssp_handle_cmd_set_deq_ep(ep_ctx);
    +
    + if (cmd_comp_code != COMP_SUCCESS) {
    + unsigned int ep_state;
    + unsigned int slot_state;
    +
    + switch (cmd_comp_code) {
    + case COMP_TRB_ERROR:
    + dev_warn(usbssp_data->dev,
    + "WARN Set TR Deq Ptr cmd invalid because of "
    + "stream ID configuration\n");
    + break;
    + case COMP_CONTEXT_STATE_ERROR:
    + dev_warn(usbssp_data->dev, "WARN Set TR Deq Ptr cmd "
    + "failed due to incorrect slot or ep state.\n");
    + ep_state = GET_EP_CTX_STATE(ep_ctx);
    + slot_state = le32_to_cpu(slot_ctx->dev_state);
    + slot_state = GET_SLOT_STATE(slot_state);
    + usbssp_dbg_trace(usbssp_data,
    + trace_usbssp_dbg_cancel_request,
    + "Slot state = %u, EP state = %u",
    + slot_state, ep_state);
    + break;
    + case COMP_SLOT_NOT_ENABLED_ERROR:
    + dev_warn(usbssp_data->dev,
    + "WARN Set TR Deq Ptr cmd failed because"
    + " slot %u was not enabled.\n",
    + usbssp_data->slot_id);
    + break;
    + default:
    + dev_warn(usbssp_data->dev, "WARN Set TR Deq Ptr cmd with"
    + " unknown completion code of %u.\n",
    + cmd_comp_code);
    + break;
    + }
    +
    + } else {
    + u64 deq;
    + /* deq ptr is written to the stream ctx for streams */
    + if (ep->ep_state & EP_HAS_STREAMS) {
    + struct usbssp_stream_ctx *ctx =
    + &ep->stream_info->stream_ctx_array[stream_id];
    + deq = le64_to_cpu(ctx->stream_ring) & SCTX_DEQ_MASK;
    + } else {
    + deq = le64_to_cpu(ep_ctx->deq) & ~EP_CTX_CYCLE_MASK;
    + }
    + usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_cancel_request,
    + "Successful Set TR Deq Ptr cmd, deq = @%08llx", deq);
    + if (usbssp_trb_virt_to_dma(ep->queued_deq_seg,
    + ep->queued_deq_ptr) == deq) {
    +
    + /*
    + * Update the ring's dequeue segment and dequeue pointer
    + * to reflect the new position.
    + */
    + update_ring_for_set_deq_completion(usbssp_data, dev,
    + ep_ring, ep_index);
    + } else {
    + dev_warn(usbssp_data->dev,
    + "Mismatch between completed Set TR Deq "
    + "Ptr command & DC internal state.\n");
    + dev_warn(usbssp_data->dev,
    + "ep deq seg = %p, deq ptr = %p\n",
    + ep->queued_deq_seg, ep->queued_deq_ptr);
    + }
    + }
    +
    +cleanup:
    + dev->eps[ep_index].ep_state &= ~SET_DEQ_PENDING;
    + dev->eps[ep_index].queued_deq_seg = NULL;
    + dev->eps[ep_index].queued_deq_ptr = NULL;
    + /* Restart any rings with pending requests */
    + ring_doorbell_for_active_rings(usbssp_data, ep_index);
    +}
    +
    +
    +static void usbssp_handle_cmd_reset_ep(struct usbssp_udc *usbssp_data,
    + union usbssp_trb *trb,
    + u32 cmd_comp_code)
    +{
    + struct usbssp_ep *dep;
    + struct usbssp_ep_ctx *ep_ctx;
    + unsigned int ep_index;
    +
    + ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
    + ep_ctx = usbssp_get_ep_ctx(usbssp_data, usbssp_data->devs.out_ctx,
    + ep_index);
    + trace_usbssp_handle_cmd_reset_ep(ep_ctx);
    +
    + /*
    + * This command will only fail if the endpoint wasn't halted,
    + * but we don't care.
    + */
    + usbssp_dbg_trace(usbssp_data, trace_usbssp_dbg_reset_ep,
    + "Ignoring reset ep completion code of %u",
    + cmd_comp_code);
    +
    + dep = &usbssp_data->devs.eps[ep_index];
    +
    + /* Clear our internal halted state */
    + dep->ep_state &= ~EP_HALTED;
    +
    + ring_doorbell_for_active_rings(usbssp_data, ep_index);
    +}
    +
    +static void usbssp_handle_cmd_enable_slot(struct usbssp_udc *usbssp_data,
    + int slot_id,
    + struct usbssp_command *command,
    + u32 cmd_comp_code)
    +{
    + if (cmd_comp_code == COMP_SUCCESS) {
    + dev_dbg(usbssp_data->dev,
    + "CMD enable slot complition successfully "
    + "- slto id: %d\n", slot_id);
    + usbssp_data->slot_id = slot_id;
    + } else {
    + dev_dbg(usbssp_data->dev, "CMD enable slot complition failed\n");
    + usbssp_data->slot_id = 0;
    + }
    +}
    +
    +static void usbssp_handle_cmd_disable_slot(struct usbssp_udc *usbssp_data)
    +{
    + struct usbssp_device *dev_priv;
    + struct usbssp_slot_ctx *slot_ctx;
    +
    + dev_dbg(usbssp_data->dev, "CMD disable slot complition\n");
    +
    + dev_priv = &usbssp_data->devs;
    + if (!dev_priv)
    + return;
    +
    + usbssp_data->slot_id = 0;
    + slot_ctx = usbssp_get_slot_ctx(usbssp_data, dev_priv->out_ctx);
    + trace_usbssp_handle_cmd_disable_slot(slot_ctx);
    +}
    +
    +static void usbssp_handle_cmd_config_ep(struct usbssp_udc *usbssp_data,
    + struct usbssp_event_cmd *event,
    + u32 cmd_comp_code)
    +{
    + struct usbssp_device *priv_dev;
    + struct usbssp_input_control_ctx *ctrl_ctx;
    + struct usbssp_ep_ctx *ep_ctx;
    + unsigned int ep_index;
    + u32 add_flags, drop_flags;
    +
    + /*
    + * Configure endpoint commands can come, becaouse device
    + * receive USB_SET_CONFIGURATION or SET_INTERFACE request,
    + * or because the HW needed an extra configure endpoint
    + * command after a reset or disconnect event.
    + */
    + priv_dev = &usbssp_data->devs;
    + ctrl_ctx = usbssp_get_input_control_ctx(priv_dev->in_ctx);
    + if (!ctrl_ctx) {
    + dev_warn(usbssp_data->dev,
    + "Could not get input context, bad type.\n");
    + return;
    + }
    +
    + add_flags = le32_to_cpu(ctrl_ctx->add_flags);
    + drop_flags = le32_to_cpu(ctrl_ctx->drop_flags);
    + /* Input ctx add_flags are the endpoint index plus one */
    + ep_index = usbssp_last_valid_endpoint(add_flags) - 1;
    +
    + ep_ctx = usbssp_get_ep_ctx(usbssp_data, priv_dev->out_ctx, ep_index);
    + trace_usbssp_handle_cmd_config_ep(ep_ctx);
    +}
    +
    +static void usbssp_handle_cmd_reset_dev(struct usbssp_udc *usbssp_data,
    + struct usbssp_event_cmd *event)
    +{
    + struct usbssp_device *dev_priv;
    + struct usbssp_slot_ctx *slot_ctx;
    +
    + dev_priv = &usbssp_data->devs;
    + slot_ctx = usbssp_get_slot_ctx(usbssp_data, dev_priv->out_ctx);
    + trace_usbssp_handle_cmd_reset_dev(slot_ctx);
    + dev_dbg(usbssp_data->dev, "Completed reset device command.\n");
    + if (!usbssp_data->devs.gadget)
    + dev_warn(usbssp_data->dev, "Reset device command completion\n");
    +}

    void usbssp_handle_command_timeout(struct work_struct *work)
    {
    @@ -327,6 +684,156 @@ void usbssp_cleanup_command_queue(struct usbssp_udc *usbssp_data)
    usbssp_complete_del_and_free_cmd(cur_cmd, COMP_COMMAND_ABORTED);
    }

    +static void handle_cmd_completion(struct usbssp_udc *usbssp_data,
    + struct usbssp_event_cmd *event)
    +{
    + int slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
    + u64 cmd_dma;
    + dma_addr_t cmd_dequeue_dma;
    + u32 cmd_comp_code;
    + union usbssp_trb *cmd_trb;
    + struct usbssp_command *cmd;
    + u32 cmd_type;
    +
    + cmd_dma = le64_to_cpu(event->cmd_trb);
    + cmd_trb = usbssp_data->cmd_ring->dequeue;
    +
    + trace_usbssp_handle_command(usbssp_data->cmd_ring, &cmd_trb->generic);
    +
    + cmd_dequeue_dma = usbssp_trb_virt_to_dma(usbssp_data->cmd_ring->deq_seg,
    + cmd_trb);
    +
    + /*
    + * Check whether the completion event is for our internal kept
    + * command.
    + */
    + if (!cmd_dequeue_dma || cmd_dma != (u64)cmd_dequeue_dma) {
    + dev_warn(usbssp_data->dev,
    + "ERROR mismatched command completion event\n");
    + return;
    + }
    +
    + cmd = list_entry(usbssp_data->cmd_list.next, struct usbssp_command,
    + cmd_list);
    +
    + cancel_delayed_work(&usbssp_data->cmd_timer);
    +
    + cmd_comp_code = GET_COMP_CODE(le32_to_cpu(event->status));
    +
    + /* If CMD ring stopped we own the trbs between enqueue and dequeue */
    + if (cmd_comp_code == COMP_COMMAND_RING_STOPPED) {
    + complete_all(&usbssp_data->cmd_ring_stop_completion);
    + return;
    + }
    +
    + if (cmd->command_trb != usbssp_data->cmd_ring->dequeue) {
    + dev_err(usbssp_data->dev,
    + "Command completion event does not match command\n");
    + return;
    + }
    +
    + /*
    + * device aborted the command ring, check if the current command was
    + * supposed to be aborted, otherwise continue normally.
    + * The command ring is stopped now, but the DC will issue a Command
    + * Ring Stopped event which will cause us to restart it.
    + */
    + if (cmd_comp_code == COMP_COMMAND_ABORTED) {
    + usbssp_data->cmd_ring_state = CMD_RING_STATE_STOPPED;
    +
    + if (cmd->status == COMP_COMMAND_ABORTED) {
    + if (usbssp_data->current_cmd == cmd)
    + usbssp_data->current_cmd = NULL;
    + goto event_handled;
    + }
    + }
    +
    + cmd_type = TRB_FIELD_TO_TYPE(le32_to_cpu(cmd_trb->generic.field[3]));
    + switch (cmd_type) {
    + case TRB_ENABLE_SLOT:
    + usbssp_handle_cmd_enable_slot(usbssp_data, slot_id,
    + cmd, cmd_comp_code);
    + break;
    + case TRB_DISABLE_SLOT:
    + usbssp_handle_cmd_disable_slot(usbssp_data);
    + break;
    + case TRB_CONFIG_EP:
    + if (!cmd->completion)
    + usbssp_handle_cmd_config_ep(usbssp_data, event,
    + cmd_comp_code);
    + break;
    + case TRB_EVAL_CONTEXT:
    + break;
    + case TRB_ADDR_DEV: {
    + struct usbssp_slot_ctx *slot_ctx;
    +
    + slot_ctx = usbssp_get_slot_ctx(usbssp_data,
    + usbssp_data->devs.out_ctx);
    + trace_usbssp_handle_cmd_addr_dev(slot_ctx);
    + break;
    + }
    + case TRB_STOP_RING:
    + WARN_ON(slot_id != TRB_TO_SLOT_ID(
    + le32_to_cpu(cmd_trb->generic.field[3])));
    + usbssp_handle_cmd_stop_ep(usbssp_data, cmd_trb, event);
    + break;
    + case TRB_SET_DEQ:
    + WARN_ON(slot_id != TRB_TO_SLOT_ID(
    + le32_to_cpu(cmd_trb->generic.field[3])));
    + usbssp_handle_cmd_set_deq(usbssp_data, cmd_trb, cmd_comp_code);
    + break;
    + case TRB_CMD_NOOP:
    + /* Is this an aborted command turned to NO-OP? */
    + if (cmd->status == COMP_COMMAND_RING_STOPPED)
    + cmd_comp_code = COMP_COMMAND_RING_STOPPED;
    + break;
    + case TRB_HALT_ENDPOINT:
    + if (cmd->status == COMP_COMMAND_RING_STOPPED)
    + cmd_comp_code = COMP_COMMAND_RING_STOPPED;
    + break;
    + case TRB_FLUSH_ENDPOINT:
    + if (cmd->status == COMP_COMMAND_RING_STOPPED)
    + cmd_comp_code = COMP_COMMAND_RING_STOPPED;
    + break;
    + case TRB_RESET_EP:
    + WARN_ON(slot_id != TRB_TO_SLOT_ID(
    + le32_to_cpu(cmd_trb->generic.field[3])));
    + usbssp_handle_cmd_reset_ep(usbssp_data, cmd_trb, cmd_comp_code);
    + break;
    + case TRB_RESET_DEV:
    + /*
    + * SLOT_ID field in reset device cmd completion event TRB is 0.
    + * Use the SLOT_ID from the command TRB instead.
    + */
    + slot_id = TRB_TO_SLOT_ID(
    + le32_to_cpu(cmd_trb->generic.field[3]));
    +
    + WARN_ON(slot_id != 0);
    + usbssp_handle_cmd_reset_dev(usbssp_data, event);
    + break;
    + case TRB_FORCE_HEADER:
    + break;
    + default:
    + /* Skip over unknown commands on the event ring */
    + dev_info(usbssp_data->dev, "INFO unknown command type %d\n",
    + cmd_type);
    + break;
    + }
    +
    + /* restart timer if this wasn't the last command */
    + if (!list_is_singular(&usbssp_data->cmd_list)) {
    + usbssp_data->current_cmd = list_first_entry(&cmd->cmd_list,
    + struct usbssp_command, cmd_list);
    + usbssp_mod_cmd_timer(usbssp_data, USBSSP_CMD_DEFAULT_TIMEOUT);
    + } else if (usbssp_data->current_cmd == cmd) {
    + usbssp_data->current_cmd = NULL;
    + }
    +
    +event_handled:
    + usbssp_complete_del_and_free_cmd(cmd, cmd_comp_code);
    + inc_deq(usbssp_data, usbssp_data->cmd_ring);
    +}
    +
    static void handle_port_status(struct usbssp_udc *usbssp_data,
    union usbssp_trb *event)
    {
    @@ -596,6 +1103,9 @@ int usbssp_handle_event(struct usbssp_udc *usbssp_data)
    rmb();

    switch ((le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK)) {
    + case TRB_TYPE(TRB_COMPLETION):
    + handle_cmd_completion(usbssp_data, &event->event_cmd);
    + break;
    case TRB_TYPE(TRB_PORT_STATUS):
    handle_port_status(usbssp_data, event);
    update_ptrs = 0;
    diff --git a/drivers/usb/usbssp/gadget.c b/drivers/usb/usbssp/gadget.c
    index 2996b1d3baf7..6b3dc973c0d9 100644
    --- a/drivers/usb/usbssp/gadget.c
    +++ b/drivers/usb/usbssp/gadget.c
    @@ -262,6 +262,17 @@ int usbssp_resume(struct usbssp_udc *usbssp_data, bool hibernated)

    #endif /* CONFIG_PM */

    +/* Compute the last valid endpoint context index. Basically, this is the
    + * endpoint index plus one. For slot contexts with more than valid endpoint,
    + * we find the most significant bit set in the added contexts flags.
    + * e.g. ep 1 IN (with epnum 0x81) => added_ctxs = 0b1000
    + * fls(0b1000) = 4, but the endpoint context index is 3, so subtract one.
    + */
    +unsigned int usbssp_last_valid_endpoint(u32 added_ctxs)
    +{
    + return fls(added_ctxs) - 1;
    +}
    +
    int usbssp_gen_setup(struct usbssp_udc *usbssp_data)
    {
    int retval;
    diff --git a/drivers/usb/usbssp/gadget.h b/drivers/usb/usbssp/gadget.h
    index 33eccffc885d..145371eee47d 100644
    --- a/drivers/usb/usbssp/gadget.h
    +++ b/drivers/usb/usbssp/gadget.h
    @@ -1685,9 +1685,13 @@ void usbssp_dbg_trace(struct usbssp_udc *usbssp_data,
    /* USBSSP memory management */
    void usbssp_mem_cleanup(struct usbssp_udc *usbssp_data);
    int usbssp_mem_init(struct usbssp_udc *usbssp_data, gfp_t flags);
    +unsigned int usbssp_last_valid_endpoint(u32 added_ctxs);
    int usbssp_ring_expansion(struct usbssp_udc *usbssp_data,
    struct usbssp_ring *ring,
    unsigned int num_trbs, gfp_t flags);
    +struct usbssp_ring *usbssp_stream_id_to_ring(struct usbssp_device *dev,
    + unsigned int ep_index,
    + unsigned int stream_id);

    struct usbssp_command *usbssp_alloc_command(struct usbssp_udc *usbssp_data,
    bool allocate_completion,
    @@ -1732,6 +1736,14 @@ void usbssp_set_link_state(struct usbssp_udc *usbssp_data,
    void usbssp_test_and_clear_bit(struct usbssp_udc *usbssp_data,
    __le32 __iomem *port_regs, u32 port_bit);

    +/* USBSSP DC contexts */
    +struct usbssp_input_control_ctx *usbssp_get_input_control_ctx(
    + struct usbssp_container_ctx *ctx);
    +struct usbssp_slot_ctx *usbssp_get_slot_ctx(struct usbssp_udc *usbssp_data,
    + struct usbssp_container_ctx *ctx);
    +struct usbssp_ep_ctx *usbssp_get_ep_ctx(struct usbssp_udc *usbssp_data,
    + struct usbssp_container_ctx *ctx,
    + unsigned int ep_index);
    /* USBSSP gadget interface*/
    void usbssp_suspend_gadget(struct usbssp_udc *usbssp_data);
    void usbssp_resume_gadget(struct usbssp_udc *usbssp_data);
    --
    2.17.1
    \
     
     \ /
      Last update: 2018-07-19 20:03    [W:4.448 / U:0.116 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site