lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <1531374448-26532-15-git-send-email-pawell@cadence.com>
Date:   Thu, 12 Jul 2018 06:47:11 +0100
From:   Pawel Laszczak <pawell@...ence.com>
To:     unlisted-recipients:; (no To-header on input)
CC:     Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
        <linux-usb@...r.kernel.org>, Felipe Balbi <balbi@...nel.org>,
        <linux-kernel@...r.kernel.org>, <ltyrala@...ence.com>,
        <adouglas@...ence.com>, <pawell@...ence.com>
Subject: [PATCH 14/31] usb: usbssp: added procedure handling command completion events.

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@...ence.com>
---
 drivers/usb/usbssp/gadget-mem.c  |  50 +++
 drivers/usb/usbssp/gadget-ring.c | 501 +++++++++++++++++++++++++++++++
 drivers/usb/usbssp/gadget.c      |  11 +
 drivers/usb/usbssp/gadget.h      |  12 +
 4 files changed, 574 insertions(+)

diff --git a/drivers/usb/usbssp/gadget-mem.c b/drivers/usb/usbssp/gadget-mem.c
index e1ecb688eb0f..3e39db25f9ef 100644
--- a/drivers/usb/usbssp/gadget-mem.c
+++ b/drivers/usb/usbssp/gadget-mem.c
@@ -507,6 +507,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,
@@ -525,6 +557,24 @@ static void usbssp_free_stream_ctx(struct usbssp_udc *usbssp_data,
 		return dma_pool_free(usbssp_data->medium_streams_pool,
 				stream_ctx, dma);
 }
+
+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 46d43f3933f7..399084963e3b 100644
--- a/drivers/usb/usbssp/gadget-ring.c
+++ b/drivers/usb/usbssp/gadget-ring.c
@@ -224,6 +224,104 @@ 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);
+
+	usbssp_dbg(usbssp_data, "// 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];
+
+	usbssp_dbg(usbssp_data, "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];
+
+	usbssp_dbg(usbssp_data,
+		"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 +398,258 @@ 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) {
+		usbssp_dbg(usbssp_data, "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) {
+		usbssp_warn(usbssp_data,
+			"WARN Set TR deq ptr command for freed stream ID %u\n",
+			stream_id);
+		/* XXX: Harmless??? */
+		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:
+			usbssp_warn(usbssp_data,
+				"WARN Set TR Deq Ptr cmd invalid because of "
+				"stream ID configuration\n");
+			break;
+		case COMP_CONTEXT_STATE_ERROR:
+			usbssp_warn(usbssp_data, "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:
+			usbssp_warn(usbssp_data,
+					"WARN Set TR Deq Ptr cmd failed because"
+					" slot %u was not enabled.\n",
+					usbssp_data->slot_id);
+			break;
+		default:
+			usbssp_warn(usbssp_data, "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 {
+			usbssp_warn(usbssp_data,
+				"Mismatch between completed Set TR Deq "
+				"Ptr command & DC internal state.\n");
+			usbssp_warn(usbssp_data,
+				"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) {
+		usbssp_dbg(usbssp_data,
+			"CMD enable slot complition successfully "
+			"- slto id: %d\n", slot_id);
+		usbssp_data->slot_id = slot_id;
+	} else {
+		usbssp_dbg(usbssp_data, "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;
+
+	usbssp_dbg(usbssp_data, "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) {
+		usbssp_warn(usbssp_data,
+				"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);
+	usbssp_dbg(usbssp_data, "Completed reset device command.\n");
+	if (!usbssp_data->devs.gadget)
+		usbssp_warn(usbssp_data, "Reset device command completion\n");
+}
 
 void usbssp_handle_command_timeout(struct work_struct *work)
 {
@@ -327,6 +677,154 @@ 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) {
+		usbssp_warn(usbssp_data,
+			  "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) {
+		usbssp_err(usbssp_data,
+			 "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 */
+		usbssp_info(usbssp_data, "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_vendor_event(struct usbssp_udc *usbssp_data,
 		union usbssp_trb *event)
@@ -604,6 +1102,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 e84821394e85..2ddb449765b6 100644
--- a/drivers/usb/usbssp/gadget.c
+++ b/drivers/usb/usbssp/gadget.c
@@ -266,6 +266,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 2615eb151925..d0c9548a39ca 100644
--- a/drivers/usb/usbssp/gadget.h
+++ b/drivers/usb/usbssp/gadget.h
@@ -1679,8 +1679,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, gfp_t mem_flags);
@@ -1723,6 +1728,13 @@ 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

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ