Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5477461c authored by Vamsi Krishna's avatar Vamsi Krishna
Browse files

usb: xhci: Add support for zero length packet



usb protocol requires host to send zero length packet (zlp) if the
out going packet length is multiple of usb max packet size. However,
this is determined by application level protocol and application
drivers set ZERO_PACKET flag if zlp needs to be supported. Add
support to check this flag and send the zlp whenever out transfer
length is multiple of usb max packet size.

Change-Id: Ic1b67ab017dd0663cec7d9ee4dd489d8193da957
Signed-off-by: default avatarVamsi Krishna <vskrishn@codeaurora.org>
parent d81481d4
Loading
Loading
Loading
Loading
+36 −17
Original line number Diff line number Diff line
@@ -3338,6 +3338,9 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
	bool more_trbs_coming;
	int start_cycle;
	u32 field, length_field;
	int zlp_required = 0;
	int max_packet = 0;
	bool last = false;

	int running_total, trb_buff_len, ret;
	unsigned int total_packet_count;
@@ -3366,11 +3369,17 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
		num_trbs++;
		running_total += TRB_MAX_BUFF_SIZE;
	}
	/* FIXME: this doesn't deal with URB_ZERO_PACKET - need one more */

	max_packet = usb_endpoint_maxp(&urb->ep->desc);
	if (!usb_urb_dir_in(urb) && urb->transfer_buffer_length &&
		(urb->transfer_flags & URB_ZERO_PACKET) &&
		!(urb->transfer_buffer_length % max_packet)) {
		zlp_required = 1;
	}

	ret = prepare_transfer(xhci, xhci->devs[slot_id],
			ep_index, urb->stream_id,
			num_trbs, urb, 0, mem_flags);
			num_trbs + zlp_required, urb, 0, mem_flags);
	if (ret < 0)
		return ret;

@@ -3410,17 +3419,6 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
		} else
			field |= ep_ring->cycle_state;

		/* Chain all the TRBs together; clear the chain bit in the last
		 * TRB to indicate it's the last TRB in the chain.
		 */
		if (num_trbs > 1) {
			field |= TRB_CHAIN;
		} else {
			/* FIXME - add check for ZERO_PACKET flag before this */
			td->last_trb = ep_ring->enqueue;
			field |= TRB_IOC;
		}

		/* Only set interrupt on short packet for IN endpoints */
		if (usb_urb_dir_in(urb))
			field |= TRB_ISP;
@@ -3439,15 +3437,36 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
			remainder |
			TRB_INTR_TARGET(0);

		if (num_trbs > 1)
		more_trbs_coming = true;
		else
		field |= TRB_CHAIN;
		if (num_trbs <= 1) {
			last = true;
			if (!zlp_required) {
				more_trbs_coming = false;
				td->last_trb = ep_ring->enqueue;
				field &= ~TRB_CHAIN;
				field |= TRB_IOC;
			}
		}

		queue_trb(xhci, ep_ring, more_trbs_coming,
				lower_32_bits(addr),
				upper_32_bits(addr),
				length_field,
				field | TRB_TYPE(TRB_NORMAL));

		if (last && zlp_required) {
			td->last_trb = ep_ring->enqueue;
			field |= TRB_IOC;
			field &= ~TRB_CHAIN;
			field &= ~TRB_CYCLE;
			field |= ep_ring->cycle_state;

			queue_trb(xhci, ep_ring, false,
				0, 0, TRB_INTR_TARGET(0),
				field | TRB_TYPE(TRB_NORMAL));
		}

		--num_trbs;
		running_total += trb_buff_len;