Commit cbbc07e1 authored by Peter Chen's avatar Peter Chen Committed by Greg Kroah-Hartman
Browse files

usb: host: move EH SINGLE_STEP_SET_FEATURE implementation to core



It is needed at USB Certification test for Embedded Host 2.0, and
the detail is at CH6.4.1.1 of On-The-Go and Embedded Host Supplement
to the USB Revision 2.0 Specification. Since other USB 2.0 capable
host like XHCI also need it, so move it to HCD core.

Acked-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarPeter Chen <peter.chen@nxp.com>
Signed-off-by: default avatarLi Jun <jun.li@nxp.com>
Link: https://lore.kernel.org/r/1620452039-11694-1-git-send-email-jun.li@nxp.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 9e8d268f
Loading
Loading
Loading
Loading
+134 −0
Original line number Diff line number Diff line
@@ -2110,6 +2110,140 @@ int usb_hcd_get_frame_number (struct usb_device *udev)
	return hcd->driver->get_frame_number (hcd);
}

/*-------------------------------------------------------------------------*/
#ifdef CONFIG_USB_HCD_TEST_MODE

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

	complete(done);
}
/*
 * Allocate and initialize a control URB. This request will be used by the
 * EHSET SINGLE_STEP_SET_FEATURE test in which the DATA and STATUS stages
 * of the GetDescriptor request are sent 15 seconds after the SETUP stage.
 * Return NULL if failed.
 */
static struct urb *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 = (usb_pipein(urb->pipe) ? udev->ep_in : udev->ep_out)
				[usb_pipeendpoint(urb->pipe)];
	if (!ep) {
		usb_free_urb(urb);
		return NULL;
	}

	urb->ep = ep;
	urb->dev = udev;
	urb->setup_packet = (void *)dr;
	urb->transfer_buffer = buf;
	urb->transfer_buffer_length = USB_DT_DEVICE_SIZE;
	urb->complete = usb_ehset_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);
	urb->setup_dma = dma_map_single(
			hcd->self.sysdev,
			urb->setup_packet,
			sizeof(struct usb_ctrlrequest),
			DMA_TO_DEVICE);
	urb->transfer_dma = dma_map_single(
			hcd->self.sysdev,
			urb->transfer_buffer,
			urb->transfer_buffer_length,
			DMA_FROM_DEVICE);
	urb->context = done;
	return urb;
}

int ehset_single_step_set_feature(struct usb_hcd *hcd, int port)
{
	int retval = -ENOMEM;
	struct usb_ctrlrequest *dr;
	struct urb *urb;
	struct usb_device *udev;
	struct usb_device_descriptor *buf;
	DECLARE_COMPLETION_ONSTACK(done);

	/* Obtain udev of the rhub's child port */
	udev = usb_hub_find_child(hcd->self.root_hub, port);
	if (!udev) {
		dev_err(hcd->self.controller, "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 = request_single_step_set_feature_urb(udev, dr, buf, &done);
	if (!urb)
		goto cleanup;

	/* Submit just the SETUP stage */
	retval = hcd->driver->submit_single_step_set_feature(hcd, urb, 1);
	if (retval)
		goto out1;
	if (!wait_for_completion_timeout(&done, msecs_to_jiffies(2000))) {
		usb_kill_urb(urb);
		retval = -ETIMEDOUT;
		dev_err(hcd->self.controller,
			"%s SETUP stage timed out on ep0\n", __func__);
		goto out1;
	}
	msleep(15 * 1000);

	/* Complete remaining DATA and STATUS stages using the same URB */
	urb->status = -EINPROGRESS;
	usb_get_urb(urb);
	atomic_inc(&urb->use_count);
	atomic_inc(&urb->dev->urbnum);
	retval = hcd->driver->submit_single_step_set_feature(hcd, urb, 0);
	if (!retval && !wait_for_completion_timeout(&done,
						msecs_to_jiffies(2000))) {
		usb_kill_urb(urb);
		retval = -ETIMEDOUT;
		dev_err(hcd->self.controller,
			"%s IN stage timed out on ep0\n", __func__);
	}
out1:
	usb_free_urb(urb);
cleanup:
	kfree(dr);
	kfree(buf);
	return retval;
}
EXPORT_SYMBOL_GPL(ehset_single_step_set_feature);
#endif /* CONFIG_USB_HCD_TEST_MODE */

/*-------------------------------------------------------------------------*/

#ifdef	CONFIG_PM
+4 −0
Original line number Diff line number Diff line
@@ -1238,6 +1238,10 @@ static const struct hc_driver ehci_hc_driver = {
	 * device support
	 */
	.free_dev =		ehci_remove_device,
#ifdef CONFIG_USB_HCD_TEST_MODE
	/* EH SINGLE_STEP_SET_FEATURE test support */
	.submit_single_step_set_feature	= ehci_submit_single_step_set_feature,
#endif
};

void ehci_init_driver(struct hc_driver *drv,
+0 −139
Original line number Diff line number Diff line
@@ -726,145 +726,6 @@ ehci_hub_descriptor (
	desc->wHubCharacteristics = cpu_to_le16(temp);
}

/*-------------------------------------------------------------------------*/
#ifdef CONFIG_USB_HCD_TEST_MODE

#define EHSET_TEST_SINGLE_STEP_SET_FEATURE 0x06

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

	complete(done);
}
static int submit_single_step_set_feature(
	struct usb_hcd	*hcd,
	struct urb	*urb,
	int		is_setup
);

/*
 * Allocate and initialize a control URB. This request will be used by the
 * EHSET SINGLE_STEP_SET_FEATURE test in which the DATA and STATUS stages
 * of the GetDescriptor request are sent 15 seconds after the SETUP stage.
 * Return NULL if failed.
 */
static struct urb *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 = (usb_pipein(urb->pipe) ? udev->ep_in : udev->ep_out)
				[usb_pipeendpoint(urb->pipe)];
	if (!ep) {
		usb_free_urb(urb);
		return NULL;
	}

	urb->ep = ep;
	urb->dev = udev;
	urb->setup_packet = (void *)dr;
	urb->transfer_buffer = buf;
	urb->transfer_buffer_length = USB_DT_DEVICE_SIZE;
	urb->complete = usb_ehset_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);
	urb->setup_dma = dma_map_single(
			hcd->self.sysdev,
			urb->setup_packet,
			sizeof(struct usb_ctrlrequest),
			DMA_TO_DEVICE);
	urb->transfer_dma = dma_map_single(
			hcd->self.sysdev,
			urb->transfer_buffer,
			urb->transfer_buffer_length,
			DMA_FROM_DEVICE);
	urb->context = done;
	return urb;
}

static int ehset_single_step_set_feature(struct usb_hcd *hcd, int port)
{
	int retval = -ENOMEM;
	struct usb_ctrlrequest *dr;
	struct urb *urb;
	struct usb_device *udev;
	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
	struct usb_device_descriptor *buf;
	DECLARE_COMPLETION_ONSTACK(done);

	/* Obtain udev of the rhub's child port */
	udev = usb_hub_find_child(hcd->self.root_hub, port);
	if (!udev) {
		ehci_err(ehci, "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 = request_single_step_set_feature_urb(udev, dr, buf, &done);
	if (!urb)
		goto cleanup;

	/* Submit just the SETUP stage */
	retval = submit_single_step_set_feature(hcd, urb, 1);
	if (retval)
		goto out1;
	if (!wait_for_completion_timeout(&done, msecs_to_jiffies(2000))) {
		usb_kill_urb(urb);
		retval = -ETIMEDOUT;
		ehci_err(ehci, "%s SETUP stage timed out on ep0\n", __func__);
		goto out1;
	}
	msleep(15 * 1000);

	/* Complete remaining DATA and STATUS stages using the same URB */
	urb->status = -EINPROGRESS;
	usb_get_urb(urb);
	atomic_inc(&urb->use_count);
	atomic_inc(&urb->dev->urbnum);
	retval = submit_single_step_set_feature(hcd, urb, 0);
	if (!retval && !wait_for_completion_timeout(&done,
						msecs_to_jiffies(2000))) {
		usb_kill_urb(urb);
		retval = -ETIMEDOUT;
		ehci_err(ehci, "%s IN stage timed out on ep0\n", __func__);
	}
out1:
	usb_free_urb(urb);
cleanup:
	kfree(dr);
	kfree(buf);
	return retval;
}
#endif /* CONFIG_USB_HCD_TEST_MODE */
/*-------------------------------------------------------------------------*/

int ehci_hub_control(
+1 −1
Original line number Diff line number Diff line
@@ -1165,7 +1165,7 @@ submit_async (
 * performed; TRUE - SETUP and FALSE - IN+STATUS
 * Returns 0 if success
 */
static int submit_single_step_set_feature(
static int ehci_submit_single_step_set_feature(
	struct usb_hcd  *hcd,
	struct urb      *urb,
	int             is_setup
+12 −1
Original line number Diff line number Diff line
@@ -409,7 +409,10 @@ struct hc_driver {
	int	(*find_raw_port_number)(struct usb_hcd *, int);
	/* Call for power on/off the port if necessary */
	int	(*port_power)(struct usb_hcd *hcd, int portnum, bool enable);

	/* Call for SINGLE_STEP_SET_FEATURE Test for USB2 EH certification */
#define EHSET_TEST_SINGLE_STEP_SET_FEATURE 0x06
	int	(*submit_single_step_set_feature)(struct usb_hcd *,
			struct urb *, int);
};

static inline int hcd_giveback_urb_in_bh(struct usb_hcd *hcd)
@@ -474,6 +477,14 @@ int usb_hcd_setup_local_mem(struct usb_hcd *hcd, phys_addr_t phys_addr,

struct platform_device;
extern void usb_hcd_platform_shutdown(struct platform_device *dev);
#ifdef CONFIG_USB_HCD_TEST_MODE
extern int ehset_single_step_set_feature(struct usb_hcd *hcd, int port);
#else
static inline int ehset_single_step_set_feature(struct usb_hcd *hcd, int port)
{
	return 0;
}
#endif /* CONFIG_USB_HCD_TEST_MODE */

#ifdef CONFIG_USB_PCI
struct pci_dev;