Re: [RFC v2 2/2] xhci: refactor handle_cmd_completion() switch branches into functions

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Aug 22, 2013 at 06:20:14PM +0300, Xenia Ragiadakou wrote:
> This patch refactors the switch statement in handle_cmd_completion() by
> creating a separate function for each branch, named 'xhci_handle_cmd_<type>'.
> 
> Other changes introduced by this patch are:
> 
> - Renaming the already existed functions by adding the prefix
>   'xhci_handle_cmd_' for consistency. Also, for handle_stopped_endpoint()
>   the order of arguments was changed too, so that the prototype of
>   'xhci_handle_cmd_' be similar and easy to follow.
> - Additional local variables were added, such as cmd_trb, cmd_comp_code,
>   cmd_type, add_flags and drop_flags, and the label 'update_ring',
>   mainly to reduce code dublication and line length.
> - The variable ep_ring, that was assigned in the case TRB_CONFIG_EP
>   but never used, was removed.

Can you break these changes into several patches, in order to make it
easier to review?  Something like:

1. Rename existing functions.
2. Change ordering of arguments for consistency.
3. Remove the unused variable from the TRB_CONFIG_EP case.
4. Several patches to refactor the code.

For the refactoring, break each refactoring into a separate patch.  I.e.
refactoring and creating xhci_handle_cmd_enable_slot goes in one patch,
xhci_handle_cmd_config_ep goes in another patch.

This patch is nearing the 400-line fatigue point of most reviewers, see
the article on Greg's google plus post for an interesting analysis:

https://plus.google.com/111049168280159033135/posts/2KcFNwzzEgC

Sarah Sharp

> 
> Signed-off-by: Xenia Ragiadakou <burzalodowa@xxxxxxxxx>
> ---
> 
> Differences from version 1:
> 
> Create separate functions for each switch case branch, instead of a single
> function holding the entire switch
> 
>  drivers/usb/host/xhci-ring.c | 310 ++++++++++++++++++++++++++-----------------
>  1 file changed, 185 insertions(+), 125 deletions(-)
> 
> diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
> index ddbda35..bc9ce03 100644
> --- a/drivers/usb/host/xhci-ring.c
> +++ b/drivers/usb/host/xhci-ring.c
> @@ -755,8 +755,8 @@ static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci,
>   *  2. Otherwise, we turn all the TRBs in the TD into No-op TRBs (with the chain
>   *     bit cleared) so that the HW will skip over them.
>   */
> -static void handle_stopped_endpoint(struct xhci_hcd *xhci,
> -		union xhci_trb *trb, struct xhci_event_cmd *event)
> +static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci,
> +		struct xhci_event_cmd *event, union xhci_trb *trb)
>  {
>  	unsigned int slot_id;
>  	unsigned int ep_index;
> @@ -1063,9 +1063,8 @@ static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci,
>   * endpoint doorbell to restart the ring, but only if there aren't more
>   * cancellations pending.
>   */
> -static void handle_set_deq_completion(struct xhci_hcd *xhci,
> -		struct xhci_event_cmd *event,
> -		union xhci_trb *trb)
> +static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci,
> +		struct xhci_event_cmd *event, union xhci_trb *trb)
>  {
>  	unsigned int slot_id;
>  	unsigned int ep_index;
> @@ -1157,9 +1156,8 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci,
>  	ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
>  }
>  
> -static void handle_reset_ep_completion(struct xhci_hcd *xhci,
> -		struct xhci_event_cmd *event,
> -		union xhci_trb *trb)
> +static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci,
> +		struct xhci_event_cmd *event, union xhci_trb *trb)
>  {
>  	int slot_id;
>  	unsigned int ep_index;
> @@ -1372,21 +1370,160 @@ static int handle_stopped_cmd_ring(struct xhci_hcd *xhci,
>  	return cur_trb_is_good;
>  }
>  
> -static void handle_cmd_completion(struct xhci_hcd *xhci,
> +static void xhci_handle_cmd_enable_slot(struct xhci_hcd *xhci,
> +		struct xhci_event_cmd *event, u32 cmd_comp_code)
> +{
> +	int slot_id;
> +
> +	slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
> +	if (cmd_comp_code == COMP_SUCCESS)
> +		xhci->slot_id = slot_id;
> +	else
> +		xhci->slot_id = 0;
> +	complete(&xhci->addr_dev);
> +}
> +
> +static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci,
>  		struct xhci_event_cmd *event)
>  {
> -	int slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
> -	u64 cmd_dma;
> -	dma_addr_t cmd_dequeue_dma;
> -	struct xhci_input_control_ctx *ctrl_ctx;
> +	int slot_id;
>  	struct xhci_virt_device *virt_dev;
> +
> +	slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
> +	virt_dev = xhci->devs[slot_id];
> +	if (!virt_dev)
> +		return;
> +	if (xhci->quirks & XHCI_EP_LIMIT_QUIRK)
> +		/* Delete default control endpoint resources */
> +		xhci_free_device_endpoint_resources(xhci, virt_dev, true);
> +	xhci_free_virt_device(xhci, slot_id);
> +}
> +
> +static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci,
> +		struct xhci_event_cmd *event, u32 cmd_comp_code)
> +{
> +	int slot_id;
> +	struct xhci_virt_device *virt_dev;
> +	struct xhci_input_control_ctx *ctrl_ctx;
>  	unsigned int ep_index;
> -	struct xhci_ring *ep_ring;
>  	unsigned int ep_state;
> +	u32 add_flags, drop_flags;
> +
> +	slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
> +	virt_dev = xhci->devs[slot_id];
> +	if (handle_cmd_in_cmd_wait_list(xhci, virt_dev, event))
> +		return;
> +	/*
> +	 * Configure endpoint commands can come from the USB core
> +	 * configuration or alt setting changes, or because the HW
> +	 * needed an extra configure endpoint command after a reset
> +	 * endpoint command or streams were being configured.
> +	 * If the command was for a halted endpoint, the xHCI driver
> +	 * is not waiting on the configure endpoint command.
> +	 */
> +	ctrl_ctx = xhci_get_input_control_ctx(xhci, virt_dev->in_ctx);
> +	if (!ctrl_ctx) {
> +		xhci_warn(xhci, "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 = xhci_last_valid_endpoint(add_flags) - 1;
> +	/* A usb_set_interface() call directly after clearing a halted
> +	 * condition may race on this quirky hardware.  Not worth
> +	 * worrying about, since this is prototype hardware.  Not sure
> +	 * if this will work for streams, but streams support was
> +	 * untested on this prototype.
> +	 */
> +	if (xhci->quirks & XHCI_RESET_EP_QUIRK &&
> +			ep_index != (unsigned int) -1 &&
> +			add_flags - SLOT_FLAG == drop_flags) {
> +		ep_state = virt_dev->eps[ep_index].ep_state;
> +		if (!(ep_state & EP_HALTED))
> +			goto bandwidth_change;
> +		xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
> +				"Completed config ep cmd - "
> +				"last ep index = %d, state = %d",
> +				ep_index, ep_state);
> +		/* Clear internal halted state and restart ring(s) */
> +		virt_dev->eps[ep_index].ep_state &= ~EP_HALTED;
> +		ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
> +		return;
> +	}
> +bandwidth_change:
> +	xhci_dbg_trace(xhci,  trace_xhci_dbg_context_change,
> +			"Completed config ep cmd");
> +	virt_dev->cmd_status = cmd_comp_code;
> +	complete(&virt_dev->cmd_completion);
> +}
> +
> +static void xhci_handle_cmd_eval_ctx(struct xhci_hcd *xhci,
> +		struct xhci_event_cmd *event, u32 cmd_comp_code)
> +{
> +	int slot_id;
> +	struct xhci_virt_device *virt_dev;
> +
> +	slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
> +	virt_dev = xhci->devs[slot_id];
> +	if (handle_cmd_in_cmd_wait_list(xhci, virt_dev, event))
> +		return;
> +	virt_dev->cmd_status = cmd_comp_code;
> +	complete(&virt_dev->cmd_completion);
> +}
> +
> +static void xhci_handle_cmd_addr_dev(struct xhci_hcd *xhci,
> +		struct xhci_event_cmd *event, u32 cmd_comp_code)
> +{
> +	int slot_id;
> +
> +	slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
> +	xhci->devs[slot_id]->cmd_status = cmd_comp_code;
> +	complete(&xhci->addr_dev);
> +}
> +
> +static void xhci_handle_cmd_reset_dev(struct xhci_hcd *xhci,
> +		struct xhci_event_cmd *event)
> +{
> +	int slot_id;
> +	struct xhci_virt_device *virt_dev;
> +
> +	xhci_dbg(xhci, "Completed reset device command.\n");
> +	slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
> +	virt_dev = xhci->devs[slot_id];
> +	if (virt_dev)
> +		handle_cmd_in_cmd_wait_list(xhci, virt_dev, event);
> +	else
> +		xhci_warn(xhci, "Reset device command completion "
> +				"for disabled slot %u\n", slot_id);
> +}
> +
> +static void xhci_handle_cmd_nec_get_fw(struct xhci_hcd *xhci,
> +		struct xhci_event_cmd *event)
> +{
> +	if (!(xhci->quirks & XHCI_NEC_HOST)) {
> +		xhci->error_bitmask |= 1 << 6;
> +		return;
> +	}
> +	xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
> +			"NEC firmware version %2x.%02x",
> +			 NEC_FW_MAJOR(le32_to_cpu(event->status)),
> +			 NEC_FW_MINOR(le32_to_cpu(event->status)));
> +}
> +
> +static void handle_cmd_completion(struct xhci_hcd *xhci,
> +		struct xhci_event_cmd *event)
> +{
> +	u64 cmd_dma;
> +	dma_addr_t cmd_dequeue_dma;
> +	union xhci_trb *cmd_trb;
> +	u32 cmd_comp_code;
> +	u32 cmd_type;
>  
>  	cmd_dma = le64_to_cpu(event->cmd_trb);
> +	cmd_trb = xhci->cmd_ring->dequeue;
>  	cmd_dequeue_dma = xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg,
> -			xhci->cmd_ring->dequeue);
> +						cmd_trb);
>  	/* Is the command ring deq ptr out of sync with the deq seg ptr? */
>  	if (cmd_dequeue_dma == 0) {
>  		xhci->error_bitmask |= 1 << 4;
> @@ -1398,142 +1535,65 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
>  		return;
>  	}
>  
> -	trace_xhci_cmd_completion(&xhci->cmd_ring->dequeue->generic,
> -					(struct xhci_generic_trb *) event);
> +	trace_xhci_cmd_completion(cmd_trb, (struct xhci_generic_trb *) event);
>  
> -	if ((GET_COMP_CODE(le32_to_cpu(event->status)) == COMP_CMD_ABORT) ||
> -		(GET_COMP_CODE(le32_to_cpu(event->status)) == COMP_CMD_STOP)) {
> +	cmd_comp_code = GET_COMP_CODE(le32_to_cpu(event->status));
> +	if (cmd_comp_code == COMP_CMD_ABORT || cmd_comp_code == COMP_CMD_STOP) {
>  		/* If the return value is 0, we think the trb pointed by
>  		 * command ring dequeue pointer is a good trb. The good
>  		 * trb means we don't want to cancel the trb, but it have
>  		 * been stopped by host. So we should handle it normally.
>  		 * Otherwise, driver should invoke inc_deq() and return.
>  		 */
> -		if (handle_stopped_cmd_ring(xhci,
> -				GET_COMP_CODE(le32_to_cpu(event->status)))) {
> -			inc_deq(xhci, xhci->cmd_ring);
> -			return;
> -		}
> +		if (handle_stopped_cmd_ring(xhci, cmd_comp_code))
> +			goto update_ring;
>  	}
>  
> -	switch (le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3])
> -		& TRB_TYPE_BITMASK) {
> -	case TRB_TYPE(TRB_ENABLE_SLOT):
> -		if (GET_COMP_CODE(le32_to_cpu(event->status)) == COMP_SUCCESS)
> -			xhci->slot_id = slot_id;
> -		else
> -			xhci->slot_id = 0;
> -		complete(&xhci->addr_dev);
> +	cmd_type = TRB_FIELD_TO_TYPE(le32_to_cpu(cmd_trb->generic.field[3]));
> +
> +	switch (cmd_type) {
> +
> +	case TRB_ENABLE_SLOT:
> +		xhci_handle_cmd_enable_slot(xhci, event, cmd_comp_code);
>  		break;
> -	case TRB_TYPE(TRB_DISABLE_SLOT):
> -		if (xhci->devs[slot_id]) {
> -			if (xhci->quirks & XHCI_EP_LIMIT_QUIRK)
> -				/* Delete default control endpoint resources */
> -				xhci_free_device_endpoint_resources(xhci,
> -						xhci->devs[slot_id], true);
> -			xhci_free_virt_device(xhci, slot_id);
> -		}
> +	case TRB_DISABLE_SLOT:
> +		xhci_handle_cmd_disable_slot(xhci, event);
>  		break;
> -	case TRB_TYPE(TRB_CONFIG_EP):
> -		virt_dev = xhci->devs[slot_id];
> -		if (handle_cmd_in_cmd_wait_list(xhci, virt_dev, event))
> -			break;
> -		/*
> -		 * Configure endpoint commands can come from the USB core
> -		 * configuration or alt setting changes, or because the HW
> -		 * needed an extra configure endpoint command after a reset
> -		 * endpoint command or streams were being configured.
> -		 * If the command was for a halted endpoint, the xHCI driver
> -		 * is not waiting on the configure endpoint command.
> -		 */
> -		ctrl_ctx = xhci_get_input_control_ctx(xhci,
> -				virt_dev->in_ctx);
> -		if (!ctrl_ctx) {
> -			xhci_warn(xhci, "Could not get input context, bad type.\n");
> -			break;
> -		}
> -		/* Input ctx add_flags are the endpoint index plus one */
> -		ep_index = xhci_last_valid_endpoint(le32_to_cpu(ctrl_ctx->add_flags)) - 1;
> -		/* A usb_set_interface() call directly after clearing a halted
> -		 * condition may race on this quirky hardware.  Not worth
> -		 * worrying about, since this is prototype hardware.  Not sure
> -		 * if this will work for streams, but streams support was
> -		 * untested on this prototype.
> -		 */
> -		if (xhci->quirks & XHCI_RESET_EP_QUIRK &&
> -				ep_index != (unsigned int) -1 &&
> -		    le32_to_cpu(ctrl_ctx->add_flags) - SLOT_FLAG ==
> -		    le32_to_cpu(ctrl_ctx->drop_flags)) {
> -			ep_ring = xhci->devs[slot_id]->eps[ep_index].ring;
> -			ep_state = xhci->devs[slot_id]->eps[ep_index].ep_state;
> -			if (!(ep_state & EP_HALTED))
> -				goto bandwidth_change;
> -			xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
> -					"Completed config ep cmd - "
> -					"last ep index = %d, state = %d",
> -					ep_index, ep_state);
> -			/* Clear internal halted state and restart ring(s) */
> -			xhci->devs[slot_id]->eps[ep_index].ep_state &=
> -				~EP_HALTED;
> -			ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
> -			break;
> -		}
> -bandwidth_change:
> -		xhci_dbg_trace(xhci,  trace_xhci_dbg_context_change,
> -				"Completed config ep cmd");
> -		xhci->devs[slot_id]->cmd_status =
> -			GET_COMP_CODE(le32_to_cpu(event->status));
> -		complete(&xhci->devs[slot_id]->cmd_completion);
> +	case TRB_CONFIG_EP:
> +		xhci_handle_cmd_config_ep(xhci, event, cmd_comp_code);
>  		break;
> -	case TRB_TYPE(TRB_EVAL_CONTEXT):
> -		virt_dev = xhci->devs[slot_id];
> -		if (handle_cmd_in_cmd_wait_list(xhci, virt_dev, event))
> -			break;
> -		xhci->devs[slot_id]->cmd_status = GET_COMP_CODE(le32_to_cpu(event->status));
> -		complete(&xhci->devs[slot_id]->cmd_completion);
> +	case TRB_EVAL_CONTEXT:
> +		xhci_handle_cmd_eval_ctx(xhci, event, cmd_comp_code);
>  		break;
> -	case TRB_TYPE(TRB_ADDR_DEV):
> -		xhci->devs[slot_id]->cmd_status = GET_COMP_CODE(le32_to_cpu(event->status));
> -		complete(&xhci->addr_dev);
> +	case TRB_ADDR_DEV:
> +		xhci_handle_cmd_addr_dev(xhci, event, cmd_comp_code);
>  		break;
> -	case TRB_TYPE(TRB_STOP_RING):
> -		handle_stopped_endpoint(xhci, xhci->cmd_ring->dequeue, event);
> +	case TRB_STOP_RING:
> +		xhci_handle_cmd_stop_ep(xhci, event, cmd_trb);
>  		break;
> -	case TRB_TYPE(TRB_SET_DEQ):
> -		handle_set_deq_completion(xhci, event, xhci->cmd_ring->dequeue);
> +	case TRB_SET_DEQ:
> +		xhci_handle_cmd_set_deq(xhci, event, cmd_trb);
>  		break;
> -	case TRB_TYPE(TRB_CMD_NOOP):
> +	case TRB_CMD_NOOP:
>  		break;
> -	case TRB_TYPE(TRB_RESET_EP):
> -		handle_reset_ep_completion(xhci, event, xhci->cmd_ring->dequeue);
> +	case TRB_RESET_EP:
> +		xhci_handle_cmd_reset_ep(xhci, event, cmd_trb);
>  		break;
> -	case TRB_TYPE(TRB_RESET_DEV):
> -		xhci_dbg(xhci, "Completed reset device command.\n");
> -		slot_id = TRB_TO_SLOT_ID(
> -			le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3]));
> -		virt_dev = xhci->devs[slot_id];
> -		if (virt_dev)
> -			handle_cmd_in_cmd_wait_list(xhci, virt_dev, event);
> -		else
> -			xhci_warn(xhci, "Reset device command completion "
> -					"for disabled slot %u\n", slot_id);
> +	case TRB_RESET_DEV:
> +		xhci_handle_cmd_reset_dev(xhci, event);
>  		break;
> -	case TRB_TYPE(TRB_NEC_GET_FW):
> -		if (!(xhci->quirks & XHCI_NEC_HOST)) {
> -			xhci->error_bitmask |= 1 << 6;
> -			break;
> -		}
> -		xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
> -			"NEC firmware version %2x.%02x",
> -			 NEC_FW_MAJOR(le32_to_cpu(event->status)),
> -			 NEC_FW_MINOR(le32_to_cpu(event->status)));
> +	case TRB_NEC_GET_FW:
> +		xhci_handle_cmd_nec_get_fw(xhci, event);
>  		break;
>  	default:
>  		/* Skip over unknown commands on the event ring */
>  		xhci->error_bitmask |= 1 << 6;
>  		break;
>  	}
> +
> +update_ring:
>  	inc_deq(xhci, xhci->cmd_ring);
> +	return;
>  }
>  
>  static void handle_vendor_event(struct xhci_hcd *xhci,
> -- 
> 1.8.3.4
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux