[RFC v2 1/3] usb: ehci-hcd: port periodic transactions implementation from the u-boot
Sascha Hauer
s.hauer at pengutronix.de
Sun Sep 13 23:21:33 PDT 2015
On Fri, Sep 11, 2015 at 08:27:54PM +0300, Peter Mamonov wrote:
> Signed-off-by: Peter Mamonov <pmamonov at gmail.com>
> ---
> drivers/usb/host/ehci-hcd.c | 426 +++++++++++++++++++++++++++++++++++++++++++-
> drivers/usb/host/ehci.h | 15 +-
> 2 files changed, 439 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
> index 9d74d2f..c9bf703 100644
> --- a/drivers/usb/host/ehci-hcd.c
> +++ b/drivers/usb/host/ehci-hcd.c
> +static void ehci_update_endpt2_dev_n_port(struct usb_device *udev,
> + struct QH *qh)
> +{
> + struct usb_device *ttdev;
> + int parent_devnum;
> +
> + if (udev->speed != USB_SPEED_LOW && udev->speed != USB_SPEED_FULL)
> + return;
> +
> + /*
> + * For full / low speed devices we need to get the devnum and portnr of
> + * the tt, so of the first upstream usb-2 hub, there may be usb-1 hubs
> + * in the tree before that one!
> + */
> +
> +//#ifdef CONFIG_DM_USB
> +#if 0
> + /*
> + * When called from usb-uclass.c: usb_scan_device() udev->dev points
> + * to the parent udevice, not the actual udevice belonging to the
> + * udev as the device is not instantiated yet. So when searching
> + * for the first usb-2 parent start with udev->dev not
> + * udev->dev->parent .
> + */
> + struct udevice *parent;
> + struct usb_device *uparent;
> +
> + ttdev = udev;
> + parent = udev->dev;
> + uparent = dev_get_parentdata(parent);
> +
> + while (uparent->speed != USB_SPEED_HIGH) {
> + struct udevice *dev = parent;
> +
> + if (device_get_uclass_id(dev->parent) != UCLASS_USB_HUB) {
> + printf("ehci: Error cannot find high-speed parent of usb-1 device\n");
Please use dev_dbg, dev_err and friends in driver context rather than
debug and printf.
> + return;
> + }
> +
> + ttdev = dev_get_parentdata(dev);
> + parent = dev->parent;
> + uparent = dev_get_parentdata(parent);
> + }
> + parent_devnum = uparent->devnum;
> +#else
> + ttdev = udev;
> + while (ttdev->parent && ttdev->parent->speed != USB_SPEED_HIGH)
> + ttdev = ttdev->parent;
> + if (!ttdev->parent)
> + return;
> + parent_devnum = ttdev->parent->devnum;
> +#endif
> +
> + qh->qh_endpt2 |= cpu_to_hc32(QH_ENDPT2_PORTNUM(ttdev->portnr) |
> + QH_ENDPT2_HUBADDR(parent_devnum));
> +}
> +
> +static struct int_queue *ehci_create_int_queue(struct usb_device *dev,
> + unsigned long pipe, int queuesize, int elementsize,
> + void *buffer, int interval)
> +{
> + struct usb_host *host = dev->host;
> + struct ehci_priv *ehci = to_ehci(host);
> + struct int_queue *result = NULL;
> + uint32_t i, toggle;
> + struct QH *list = ehci->periodic_queue;
> +
> + /*
> + * Interrupt transfers requiring several transactions are not supported
> + * because bInterval is ignored.
> + *
> + * Also, ehci_submit_async() relies on wMaxPacketSize being a power of 2
> + * <= PKT_ALIGN if several qTDs are required, while the USB
> + * specification does not constrain this for interrupt transfers. That
> + * means that ehci_submit_async() would support interrupt transfers
> + * requiring several transactions only as long as the transfer size does
> + * not require more than a single qTD.
> + */
> + if (elementsize > usb_maxpacket(dev, pipe)) {
> + pr_err("%s: xfers requiring several transactions are not supported.\n",
> + __func__);
> + return NULL;
> + }
> +
> + debug("Enter create_int_queue\n");
> + if (usb_pipetype(pipe) != PIPE_INTERRUPT) {
> + debug("non-interrupt pipe (type=%lu)", usb_pipetype(pipe));
> + return NULL;
> + }
> +
> + /* limit to 4 full pages worth of data -
> + * we can safely fit them in a single TD,
> + * no matter the alignment
> + */
> + if (elementsize >= 16384) {
> + debug("too large elements for interrupt transfers\n");
> + return NULL;
> + }
> +
> + result = xzalloc(sizeof(*result));
> + if (!result) {
> + debug("ehci intr queue: out of memory\n");
> + goto fail1;
> + }
xzalloc never fails, no need to check the return value.
> +
> + debug("Exit create_int_queue\n");
> + return result;
> +fail3:
> + if (result->tds)
> + free(result->tds);
free(NULL) works perfectly fine, no need to check for result->tds.
> +fail2:
> + if (result->first)
> + free(result->first);
memory allocated with dma_alloc_coherent must be freed with
dma_free_coherent.
> + if (result)
> + free(result);
> +fail1:
> + return NULL;
> +}
> +
> +static int ehci_destroy_int_queue(struct usb_device *dev,
> + struct int_queue *queue)
> +{
> + int result = -1;
> + struct usb_host *host = dev->host;
> + struct ehci_priv *ehci = to_ehci(host);
> + struct QH *cur = ehci->periodic_queue;
> + uint64_t start;
> +
> + if (disable_periodic(ehci) < 0) {
> + debug("FATAL: periodic should never fail, but did");
> + goto out;
> + }
> + ehci->periodic_schedules--;
> +
> + start = get_time_ns();
> + while (!(cur->qh_link & cpu_to_hc32(QH_LINK_TERMINATE))) {
> + debug("considering %p, with qh_link %x\n", cur, cur->qh_link);
> + if (NEXT_QH(cur) == queue->first) {
> + debug("found candidate. removing from chain\n");
> + cur->qh_link = queue->last->qh_link;
> + result = 0;
> + break;
> + }
> + cur = NEXT_QH(cur);
> + if (is_timeout_non_interruptible(start, 500 * MSECOND)) {
> + printf("Timeout destroying interrupt endpoint queue\n");
> + result = -1;
Please use an error code rather than -1. -1 is easily interpreted as
-EPERM from the callers which is not the correct error code here.
> + queue = ehci_create_int_queue(dev, pipe, 1, length, buffer, interval);
> + if (!queue)
> + return -1;
> +
> + start = get_time_ns();
> + while ((backbuffer = ehci_poll_int_queue(dev, queue)) == NULL)
> + if (is_timeout_non_interruptible(start,
> + USB_CNTL_TIMEOUT * MSECOND)) {
> + pr_err("Timeout poll on interrupt endpoint\n");
> + result = -ETIMEDOUT;
> + break;
> + }
> +
> + if (backbuffer != buffer) {
> + pr_err("got wrong buffer back (%p instead of %p)\n",
> + backbuffer, buffer);
> + return -EINVAL;
> + }
> +
> + ret = ehci_destroy_int_queue(dev, queue);
> + if (ret < 0)
> + return ret;
> +
> + /* everything worked out fine */
Is this true? result can be nonzero here.
> + return result;
> }
>
Sascha
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
More information about the barebox
mailing list