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

Commit 292d6bec authored by Kyle Yan's avatar Kyle Yan Committed by Gerrit - the friendly Code Review server
Browse files

Merge "usb: xhci: Add support for SINGLE_STEP_SET_FEATURE test of EHSET" into msm-4.9

parents 34ae499d 32788c5d
Loading
Loading
Loading
Loading
+151 −1
Original line number Diff line number Diff line
@@ -20,7 +20,7 @@
 * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */


#include <linux/gfp.h>
#include <linux/slab.h>
#include <asm/unaligned.h>

@@ -872,6 +872,151 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
	return status;
}

static void xhci_single_step_completion(struct urb *urb)
{
	struct completion *done = urb->context;

	complete(done);
}

/*
 * Allocate a URB and initialize the various fields of it.
 * This API is used by the single_step_set_feature test of
 * EHSET where IN packet of the GetDescriptor request is
 * sent 15secs after the SETUP packet.
 * Return NULL if failed.
 */
static struct urb *xhci_request_single_step_set_feature_urb(
		struct usb_device *udev,
		void *dr,
		void *buf,
		struct completion *done)
{
	struct urb *urb;
	struct usb_hcd *hcd = bus_to_hcd(udev->bus);
	struct usb_host_endpoint *ep;

	urb = usb_alloc_urb(0, GFP_KERNEL);
	if (!urb)
		return NULL;

	urb->pipe = usb_rcvctrlpipe(udev, 0);
	ep = udev->ep_in[usb_pipeendpoint(urb->pipe)];
	if (!ep) {
		usb_free_urb(urb);
		return NULL;
	}

	/*
	 * Initialize the various URB fields as these are used by the HCD
	 * driver to queue it and as well as when completion happens.
	 */
	urb->ep = ep;
	urb->dev = udev;
	urb->setup_packet = dr;
	urb->transfer_buffer = buf;
	urb->transfer_buffer_length = USB_DT_DEVICE_SIZE;
	urb->complete = xhci_single_step_completion;
	urb->status = -EINPROGRESS;
	urb->actual_length = 0;
	urb->transfer_flags = URB_DIR_IN;
	usb_get_urb(urb);
	atomic_inc(&urb->use_count);
	atomic_inc(&urb->dev->urbnum);
	usb_hcd_map_urb_for_dma(hcd, urb, GFP_KERNEL);
	urb->context = done;
	return urb;
}

/*
 * This function implements the USB_PORT_FEAT_TEST handling of the
 * SINGLE_STEP_SET_FEATURE test mode as defined in the Embedded
 * High-Speed Electrical Test (EHSET) specification. This simply
 * issues a GetDescriptor control transfer, with an inserted 15-second
 * delay after the end of the SETUP stage and before the IN token of
 * the DATA stage is set. The idea is that this gives the test operator
 * enough time to configure the oscilloscope to perform a measurement
 * of the response time between the DATA and ACK packets that follow.
 */
static int xhci_ehset_single_step_set_feature(struct usb_hcd *hcd, int port)
{
	int retval;
	struct usb_ctrlrequest *dr;
	struct urb *urb;
	struct usb_device *udev;
	struct xhci_hcd	*xhci = hcd_to_xhci(hcd);
	struct usb_device_descriptor *buf;
	unsigned long flags;
	DECLARE_COMPLETION_ONSTACK(done);

	/* Obtain udev of the rhub's child port */
	udev = usb_hub_find_child(hcd->self.root_hub, port);
	if (!udev) {
		xhci_err(xhci, "No device attached to the RootHub\n");
		return -ENODEV;
	}
	buf = kmalloc(USB_DT_DEVICE_SIZE, GFP_KERNEL);
	if (!buf)
		return -ENOMEM;

	dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
	if (!dr) {
		kfree(buf);
		return -ENOMEM;
	}

	/* Fill Setup packet for GetDescriptor */
	dr->bRequestType = USB_DIR_IN;
	dr->bRequest = USB_REQ_GET_DESCRIPTOR;
	dr->wValue = cpu_to_le16(USB_DT_DEVICE << 8);
	dr->wIndex = 0;
	dr->wLength = cpu_to_le16(USB_DT_DEVICE_SIZE);
	urb = xhci_request_single_step_set_feature_urb(udev, dr, buf, &done);
	if (!urb) {
		retval = -ENOMEM;
		goto cleanup;
	}

	/* Now complete just the SETUP stage */
	spin_lock_irqsave(&xhci->lock, flags);
	retval = xhci_submit_single_step_set_feature(hcd, urb, 1);
	spin_unlock_irqrestore(&xhci->lock, flags);
	if (retval)
		goto out1;

	if (!wait_for_completion_timeout(&done, msecs_to_jiffies(2000))) {
		usb_kill_urb(urb);
		retval = -ETIMEDOUT;
		xhci_err(xhci, "%s SETUP stage timed out on ep0\n", __func__);
		goto out1;
	}

	/* Sleep for 15 seconds; HC will send SOFs during this period */
	msleep(15 * 1000);

	/* Complete remaining DATA and status stages. Re-use same URB */
	urb->status = -EINPROGRESS;
	usb_get_urb(urb);
	atomic_inc(&urb->use_count);
	atomic_inc(&urb->dev->urbnum);

	spin_lock_irqsave(&xhci->lock, flags);
	retval = xhci_submit_single_step_set_feature(hcd, urb, 0);
	spin_unlock_irqrestore(&xhci->lock, flags);
	if (!retval && !wait_for_completion_timeout(&done,
						msecs_to_jiffies(2000))) {
		usb_kill_urb(urb);
		retval = -ETIMEDOUT;
		xhci_err(xhci, "%s IN stage timed out on ep0\n", __func__);
	}
out1:
	usb_free_urb(urb);
cleanup:
	kfree(dr);
	kfree(buf);
	return retval;
}

int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
		u16 wIndex, char *buf, u16 wLength)
{
@@ -1152,6 +1297,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
								PORTPMSC);
				/* to make sure above write goes through */
				mb();
			} else if (test_mode == 6) {
				spin_unlock_irqrestore(&xhci->lock, flags);
				retval = xhci_ehset_single_step_set_feature(hcd,
									wIndex);
				spin_lock_irqsave(&xhci->lock, flags);
			} else {
				goto error;
			}
+150 −0
Original line number Diff line number Diff line
@@ -3485,6 +3485,156 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
	return 0;
}

/*
 * Variant of xhci_queue_ctrl_tx() used to implement EHSET
 * SINGLE_STEP_SET_FEATURE test mode. It differs in that the control
 * transfer is broken up so that the SETUP stage can happen and call
 * the URB's completion handler before the DATA/STATUS stages are
 * executed by the xHC hardware. This assumes the control transfer is a
 * GetDescriptor, with a DATA stage in the IN direction, and an OUT
 * STATUS stage.
 *
 * This function is called twice, usually with a 15-second delay in between.
 * - with is_setup==true, the SETUP stage for the control request
 *   (GetDescriptor) is queued in the TRB ring and sent to HW immediately
 * - with is_setup==false, the DATA and STATUS TRBs are queued and exceuted
 *
 * Caller must have locked xhci->lock
 */
int xhci_submit_single_step_set_feature(struct usb_hcd *hcd, struct urb *urb,
					int is_setup)
{
	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
	struct xhci_ring *ep_ring;
	int num_trbs;
	int ret;
	unsigned int slot_id, ep_index;
	struct usb_ctrlrequest *setup;
	struct xhci_generic_trb *start_trb;
	int start_cycle;
	u32 field, length_field, remainder;
	struct urb_priv *urb_priv;
	struct xhci_td *td;

	ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
	if (!ep_ring)
		return -EINVAL;

	/* Need buffer for data stage */
	if (urb->transfer_buffer_length <= 0)
		return -EINVAL;

	/*
	 * Need to copy setup packet into setup TRB, so we can't use the setup
	 * DMA address.
	 */
	if (!urb->setup_packet)
		return -EINVAL;
	setup = (struct usb_ctrlrequest *) urb->setup_packet;

	slot_id = urb->dev->slot_id;
	ep_index = xhci_get_endpoint_index(&urb->ep->desc);

	urb_priv = kzalloc(sizeof(struct urb_priv) +
				  sizeof(struct xhci_td *), GFP_ATOMIC);
	if (!urb_priv)
		return -ENOMEM;

	td = urb_priv->td[0] = kzalloc(sizeof(struct xhci_td), GFP_ATOMIC);
	if (!td) {
		kfree(urb_priv);
		return -ENOMEM;
	}

	urb_priv->length = 1;
	urb_priv->td_cnt = 0;
	urb->hcpriv = urb_priv;

	num_trbs = is_setup ? 1 : 2;

	ret = prepare_transfer(xhci, xhci->devs[slot_id],
			ep_index, urb->stream_id,
			num_trbs, urb, 0, GFP_ATOMIC);
	if (ret < 0) {
		kfree(td);
		kfree(urb_priv);
		return ret;
	}

	/*
	 * Don't give the first TRB to the hardware (by toggling the cycle bit)
	 * until we've finished creating all the other TRBs.  The ring's cycle
	 * state may change as we enqueue the other TRBs, so save it too.
	 */
	start_trb = &ep_ring->enqueue->generic;
	start_cycle = ep_ring->cycle_state;

	if (is_setup) {
		/* Queue only the setup TRB */
		field = TRB_IDT | TRB_IOC | TRB_TYPE(TRB_SETUP);
		if (start_cycle == 0)
			field |= 0x1;

		/* xHCI 1.0/1.1 6.4.1.2.1: Transfer Type field */
		if (xhci->hci_version >= 0x100) {
			if (setup->bRequestType & USB_DIR_IN)
				field |= TRB_TX_TYPE(TRB_DATA_IN);
			else
				field |= TRB_TX_TYPE(TRB_DATA_OUT);
		}

		/* Save the DMA address of the last TRB in the TD */
		td->last_trb = ep_ring->enqueue;

		queue_trb(xhci, ep_ring, false,
			  setup->bRequestType | setup->bRequest << 8 |
				le16_to_cpu(setup->wValue) << 16,
			  le16_to_cpu(setup->wIndex) |
				le16_to_cpu(setup->wLength) << 16,
			  TRB_LEN(8) | TRB_INTR_TARGET(0),
			  field);
	} else {
		/* Queue data TRB */
		field = TRB_ISP | TRB_TYPE(TRB_DATA);
		if (start_cycle == 0)
			field |= 0x1;
		if (setup->bRequestType & USB_DIR_IN)
			field |= TRB_DIR_IN;

		remainder = xhci_td_remainder(xhci, 0,
					   urb->transfer_buffer_length,
					   urb->transfer_buffer_length,
					   urb, 1);

		length_field = TRB_LEN(urb->transfer_buffer_length) |
			TRB_TD_SIZE(remainder) |
			TRB_INTR_TARGET(0);

		queue_trb(xhci, ep_ring, true,
			  lower_32_bits(urb->transfer_dma),
			  upper_32_bits(urb->transfer_dma),
			  length_field,
			  field);

		/* Save the DMA address of the last TRB in the TD */
		td->last_trb = ep_ring->enqueue;

		/* Queue status TRB */
		field = TRB_IOC | TRB_TYPE(TRB_STATUS);
		if (!(setup->bRequestType & USB_DIR_IN))
			field |= TRB_DIR_IN;

		queue_trb(xhci, ep_ring, false,
			  0,
			  0,
			  TRB_INTR_TARGET(0),
			  field | ep_ring->cycle_state);
	}

	giveback_first_trb(xhci, slot_id, ep_index, 0, start_cycle, start_trb);
	return 0;
}

/*
 * The transfer burst count field of the isochronous TRB defines the number of
 * bursts that are required to move all packets in this TD.  Only SuperSpeed
+4 −0
Original line number Diff line number Diff line
@@ -1991,4 +1991,8 @@ static inline struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci,
					urb->stream_id);
}

/* EHSET */
int xhci_submit_single_step_set_feature(struct usb_hcd *hcd, struct urb *urb,
					int is_setup);

#endif /* __LINUX_XHCI_HCD_H */