[U-Boot] [PATCH] Fix to make U-Boot work with more USB sticks

Simon Glass sjg at chromium.org
Thu Dec 16 23:14:14 CET 2010


Hi Remy,

Thanks for the feedback. I will update the patch with your changes.

Unfortunately I don't have a lot of hardware to try, hence the list post. I
will be doing some more testing in January so will come back to it then. In
the meantime I am interested in views as to whether this makes a difference
on OHCI, different ARM chips, etc.

Regards,
Simon

On Sat, Dec 11, 2010 at 9:51 AM, Remy Bohmer <linux at bohmer.net> wrote:

> Hi Simon,
>
> 2010/12/10 Simon Glass <sjg at chromium.org>:
> > I am sending this to the list looking for comments. Testing has been
> > confined to just a few USB sticks - no USB hard drives, USB card
> > readers, etc. But there are reports on the mailing list of problems
> > with U-Boot's detection of USB mass storage devices.
>
> First impression is that it looks interesting, however I have a few
> remarks, see below.
>
> > This patch:
> >
> > 1. Increases the USB descriptor submission timeout to 3 seconds since the
> > original 1 second timeout seems arbitrary
> >
> > 2. Replaces the delay-based reset logic with logic which waits until it
> > sees the reset is successful, rather than blindly continuing
> >
> > 3. Resets and retries a USB mass storage device after it fails once, in
> > case even 3 seconds is not enough
>
> Can you please split this patch up to multiple patches, each solving a
> different issue?
>
> > BUG=chromiumos-6780
> > TEST=tried with four different USB sticks, including two which previously
> > failed
> > Change-Id: I5bd8891bff67a74b758e349f2a3b3575806eed59
> >
> > Signed-off-by: Simon Glass <sjg at chromium.org>
> > ---
> >  common/usb.c                |  176
> ++++++++++++++++++++++++++++++++++++-------
> >  common/usb_storage.c        |   55 +++++++++++---
> >  drivers/usb/host/ehci-hcd.c |   20 ++++-
> >  include/usb.h               |   11 +++
> >  4 files changed, 217 insertions(+), 45 deletions(-)
> >
> > diff --git a/common/usb.c b/common/usb.c
> > index 9896f46..3265d5d 100644
> > --- a/common/usb.c
> > +++ b/common/usb.c
> > @@ -222,10 +222,14 @@ int usb_control_msg(struct usb_device *dev,
> unsigned int pipe,
> >  int usb_bulk_msg(struct usb_device *dev, unsigned int pipe,
> >                        void *data, int len, int *actual_length, int
> timeout)
> >  {
> > +       int err;
> > +
> >        if (len < 0)
> >                return -1;
> >        dev->status = USB_ST_NOT_PROC; /*not yet processed */
> > -       submit_bulk_msg(dev, pipe, data, len);
> > +       err = submit_bulk_msg(dev, pipe, data, len);
> > +       if (err < 0)
> > +               return err;
>
> Seems you only focused on the ehci-hcd driver. How does this change in
> behaviour impact other host controller drivers like, for example,
> ohci?
> Is any regression in those drivers possible or to be expected?
>
> >
> > -static int hub_port_reset(struct usb_device *dev, int port,
> > -                       unsigned short *portstat)
> > +/* brought this in from kernel 2.6.36 as it is a problem area. Some USB
> > +sticks do not operate properly with the previous reset code */
>
> Multi line comments should be written like
> /*
>  * comment line 1
>  * comment line 2
>  */
> Please fix globally.
>
> > +#define PORT_RESET_TRIES       5
> > +#define SET_ADDRESS_TRIES      2
> > +#define GET_DESCRIPTOR_TRIES   2
> > +#define SET_CONFIG_TRIES       (2 * (use_both_schemes + 1))
> > +#define USE_NEW_SCHEME(i)      ((i) / 2 == old_scheme_first)
> > +
>
> > +#define HUB_ROOT_RESET_TIME    50      /* times are in msec */
> > +#define HUB_SHORT_RESET_TIME   10
> > +#define HUB_LONG_RESET_TIME    200
> > +#define HUB_RESET_TIMEOUT      500
>
> How are these times estimated? Are these magic, or is there a deeper
> thought behind them?
>
> > +
> > +#define ENOTCONN 107
> > +
> > +static int hub_port_wait_reset(struct usb_device *dev, int port,
> > +                       unsigned int delay, unsigned short
> *portstatus_ret)
> >  {
> > -       int tries;
> > +       int delay_time, ret;
> >        struct usb_port_status portsts;
> >        unsigned short portstatus, portchange;
> >
> > -       USB_HUB_PRINTF("hub_port_reset: resetting port %d...\n", port);
> > -       for (tries = 0; tries < MAX_TRIES; tries++) {
> > -
> > -               usb_set_port_feature(dev, port + 1, USB_PORT_FEAT_RESET);
> > -               wait_ms(200);
> > +       for (delay_time = 0;
> > +                       delay_time < HUB_RESET_TIMEOUT;
>
> You go from a 200msec timeout to a 500msec timeout. Is there a special
> reason for this?
>
> > +                       delay_time += delay) {
> > +               /* wait to give the device a chance to reset */
> > +               wait_ms(delay);
>
> The ' delay' argument of this routine is misleading since it does not
> specify a delay time, but the time between 2 polls on
> usb_get_port_status().
> The maximum delay is specified by HUB_RESET_TIMEOUT here.
>
> >
> > -               if (usb_get_port_status(dev, port + 1, &portsts) < 0) {
> > +               /* read and decode port status */
> > +               ret = usb_get_port_status(dev, port + 1, &portsts);
> > +               if (ret < 0) {
> >                        USB_HUB_PRINTF("get_port_status failed status
> %lX\n",
> >                                        dev->status);
> > -                       return -1;
> > +                       return ret;
> >                }
> >                portstatus = le16_to_cpu(portsts.wPortStatus);
> >                portchange = le16_to_cpu(portsts.wPortChange);
> >
> > -               USB_HUB_PRINTF("portstatus %x, change %x, %s\n",
> > -                               portstatus, portchange,
> > -                               portspeed(portstatus));
> > +               /* Device went away? */
> > +               if (!(portstatus & USB_PORT_STAT_CONNECTION))
> > +                       return -ENOTCONN;
> >
> > -               USB_HUB_PRINTF("STAT_C_CONNECTION = %d STAT_CONNECTION =
> %d" \
> > -                              "  USB_PORT_STAT_ENABLE %d\n",
> > -                       (portchange & USB_PORT_STAT_C_CONNECTION) ? 1 :
> 0,
> > -                       (portstatus & USB_PORT_STAT_CONNECTION) ? 1 : 0,
> > -                       (portstatus & USB_PORT_STAT_ENABLE) ? 1 : 0);
> > +               /* bomb out completely if the connection bounced */
> > +               if ((portchange & USB_PORT_STAT_C_CONNECTION))
> > +                       return -ENOTCONN;
> >
> > -               if ((portchange & USB_PORT_STAT_C_CONNECTION) ||
> > -                   !(portstatus & USB_PORT_STAT_CONNECTION))
> > -                       return -1;
> > +               /* if we`ve finished resetting, then break out of the
> loop */
> > +               if (!(portstatus & USB_PORT_STAT_RESET) &&
> > +                   (portstatus & USB_PORT_STAT_ENABLE)) {
> > +                       *portstatus_ret = portstatus;
> > +                       return 0;
> > +               }
> >
> > -               if (portstatus & USB_PORT_STAT_ENABLE)
> > -                       break;
> > +               /* switch to the long delay after two short delay
> failures */
> > +               if (delay_time >= 2 * HUB_SHORT_RESET_TIME)
> > +                       delay = HUB_LONG_RESET_TIME;
>
> You are changing the 'delay' routine argument here. Why even specify
> it on the argument list if:
> * it does not do what to expect.
> * it gets overridden anyway.
>
> > +       /* root hub ports have a slightly longer reset period
> > +        * (from USB 2.0 spec, section 7.1.7.5)
> > +        */
>
> wrong style multiline comment.
>
> > +       if (!dev->parent) {
> > +               delay = HUB_ROOT_RESET_TIME;
> > +       }
>
> No parenthesis required.
>
> > -               wait_ms(200);
> > +       /* Some low speed devices have problems with the quick delay, so
> */
> > +       /*  be a bit pessimistic with those devices. RHbug #23670 */
>
> Multiline comment
>
> > +                       /* TRSTRCY = 10 ms; plus some extra */
>
> magic plus some extra magic...
>
> > diff --git a/common/usb_storage.c b/common/usb_storage.c
> > index 76949b8..0fb9c1b 100644
> > --- a/common/usb_storage.c
> > +++ b/common/usb_storage.c
> > @@ -158,9 +158,10 @@ struct us_data {
> >  static struct us_data usb_stor[USB_MAX_STOR_DEV];
> >
> >
> > +/* start our error numbers after the USB ones */
> >  #define USB_STOR_TRANSPORT_GOOD           0
> > -#define USB_STOR_TRANSPORT_FAILED -1
> > -#define USB_STOR_TRANSPORT_ERROR  -2
> > +#define USB_STOR_TRANSPORT_FAILED (USB_ENEXTFREE)
> > +#define USB_STOR_TRANSPORT_ERROR  (USB_ENEXTFREE-1)
>
> Weird way to define error codes...
> Please make it clearer without magic ids that needs to match magically
> to some ids in a header file (USB_ENEXTFREE = -3) (enum?)
>
> >  int usb_stor_get_info(struct usb_device *dev, struct us_data *us,
> >                      block_dev_desc_t *dev_desc);
> > @@ -213,6 +214,7 @@ int usb_stor_scan(int mode)
> >  {
> >        unsigned char i;
> >        struct usb_device *dev;
> > +       int result;
> >
> >        /* GJ */
> >        memset(usb_stor_buf, 0, sizeof(usb_stor_buf));
> > @@ -244,8 +246,21 @@ int usb_stor_scan(int mode)
> >                        /* ok, it is a storage devices
> >                         * get info and fill it in
> >                         */
>
> Bad style multiline comment
>
> > -                       if (usb_stor_get_info(dev,
> &usb_stor[usb_max_devs],
> > -
> &usb_dev_desc[usb_max_devs]) == 1)
> > +                       result = usb_stor_get_info(dev,
> &usb_stor[usb_max_devs],
> > +
> &usb_dev_desc[usb_max_devs]);
> > +                       if (result == USB_EDEVCRITICAL) {
>
> You are changing the behaviour for the ehci host controller only
> (since that one only knows USB_EDEVCRITICAL)
> Please make it suitable for all other host controller drivers as well,
> or patch those drivers also.
> The problems you are trying to fix are not likely to be for ehci only,
> since you modify generic code...
>
> > +                               /*
> > +                                * Something there, but failed badly.
> > +                                * Retry one more time. This happens
> > +                                * sometimes with some USB sticks,
> > +                                * e.g. Patriot Rage ID 13fe:3800
> > +                                */
> > +                               printf (".");
> > +                               usb_restart_device(dev);  /* ignore
> return value */
> > +                               result = usb_stor_get_info(dev,
> &usb_stor[usb_max_devs],
> > +
> &usb_dev_desc[usb_max_devs]);
> > +                       }
> > +                       if (result == 1)
>
> Would a switch case or ' else if' not be nicer?
>
> >  static int usb_request_sense(ccb *srb, struct us_data *ss)
> >  {
> > +       int result;
> >        char *ptr;
> >
> >        ptr = (char *)srb->pdata;
> > @@ -909,7 +928,12 @@ static int usb_request_sense(ccb *srb, struct
> us_data *ss)
> >        srb->datalen = 18;
> >        srb->pdata = &srb->sense_buf[0];
> >        srb->cmdlen = 12;
> > -       ss->transport(srb, ss);
> > +       result = ss->transport(srb, ss);
> > +       if (result < 0) {
> > +               if (result != USB_EDEVCRITICAL)
> > +                       USB_STOR_PRINTF("Request Sense failed\n");
> > +               return result;
>
> You are changing behaviour for ALL host controller driver types here.
> Are you sure you want this? (Better: again... please fix all
> drivers...)
>
> > +       }
> >        USB_STOR_PRINTF("Request Sense returned %02X %02X %02X\n",
> >                        srb->sense_buf[2], srb->sense_buf[12],
> >                        srb->sense_buf[13]);
>
> > @@ -1372,11 +1400,14 @@ int usb_stor_get_info(struct usb_device *dev,
> struct us_data *ss,
> >  #endif /* CONFIG_USB_BIN_FIXUP */
> >        USB_STOR_PRINTF("ISO Vers %X, Response Data %X\n",
> usb_stor_buf[2],
> >                        usb_stor_buf[3]);
> > -       if (usb_test_unit_ready(pccb, ss)) {
> > +       result = usb_test_unit_ready(pccb, ss);
> > +       if (result) {
> > +               if (result == USB_EDEVCRITICAL)
> > +                       return result;
>
> Please fix all drivers...
>
> >                printf("Device NOT ready\n"
> > -                      "   Request Sense returned %02X %02X %02X\n",
> > -                      pccb->sense_buf[2], pccb->sense_buf[12],
> > -                      pccb->sense_buf[13]);
> > +                       "   Request Sense returned %02X %02X %02X\n",
> > +               pccb->sense_buf[2], pccb->sense_buf[12],
> > +               pccb->sense_buf[13]);
> >                if (dev_desc->removable == 1) {
> >                        dev_desc->type = perq;
> >                        return 1;
> > diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
> > index 13cd84a..1143cd6 100644
> > --- a/drivers/usb/host/ehci-hcd.c
> > +++ b/drivers/usb/host/ehci-hcd.c
> > @@ -330,6 +330,7 @@ ehci_submit_async(struct usb_device *dev, unsigned
> long pipe, void *buffer,
> >        uint32_t c, toggle;
> >        uint32_t cmd;
> >        int ret = 0;
> > +       int result = USB_EFAIL;
> >
> >  #ifdef CONFIG_USB_EHCI_DATA_ALIGN
> >        /* In case ehci host requires alignment for buffers */
> > @@ -342,8 +343,8 @@ ehci_submit_async(struct usb_device *dev, unsigned
> long pipe, void *buffer,
> >                if (!align_buf)
> >                        return -1;
> >                if ((int)align_buf & (CONFIG_USB_EHCI_DATA_ALIGN - 1))
> > -                       buffer = (void *)((int)align_buf +
> > -                               CONFIG_USB_EHCI_DATA_ALIGN -
> > +                       buffer = (void *)((int)align_buf +
> > +                               CONFIG_USB_EHCI_DATA_ALIGN -
> >                                ((int)align_buf &
> (CONFIG_USB_EHCI_DATA_ALIGN - 1)));
> >                else
> >                        buffer = align_buf;
> > @@ -475,7 +476,10 @@ ehci_submit_async(struct usb_device *dev, unsigned
> long pipe, void *buffer,
> >                goto fail;
> >        }
> >
> > -       /* Wait for TDs to be processed. */
> > +       /*
> > +        * Wait for TDs to be processed. We wait 3s since some USB
> > +        * sticks can take a long time immediately after system reset
> > +        */
>
> Cool... This multiline comment is correct ;-)
>
> >        ts = get_timer(0);
> >        vtd = td;
> >        do {
> > @@ -484,7 +488,7 @@ ehci_submit_async(struct usb_device *dev, unsigned
> long pipe, void *buffer,
> >                token = hc32_to_cpu(vtd->qt_token);
> >                if (!(token & 0x80))
> >                        break;
> > -       } while (get_timer(ts) < CONFIG_SYS_HZ);
> > +       } while (get_timer(ts) < CONFIG_SYS_HZ * 3);
>
> Same valid for other host controller drivers?
>
> >
> >        /* Disable async schedule. */
> >        cmd = ehci_readl(&hcor->or_usbcmd);
> > @@ -497,6 +501,12 @@ ehci_submit_async(struct usb_device *dev, unsigned
> long pipe, void *buffer,
> >                printf("EHCI fail timeout STD_ASS reset\n");
> >                goto fail;
> >        }
> > +       /* check that the TD processing happened */
> > +       if (token & 0x80) {
> > +               printf("EHCI timed out on TD - token=%#x\n", token);
> > +               result = USB_EDEVCRITICAL;
> > +               goto fail;
> > +       }
>
> this one too...
>
> >        qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list |
> QH_LINK_TYPE_QH);
> >
> > @@ -559,7 +569,7 @@ fail:
> >                td = (void *)hc32_to_cpu(qh->qh_overlay.qt_next);
> >        }
> >        ehci_free(qh, sizeof(*qh));
> > -       return -1;
> > +       return result;
> >  }
> >
> >  static inline int min3(int a, int b, int c)
> > diff --git a/include/usb.h b/include/usb.h
> > index afd65e3..91aa441 100644
> > --- a/include/usb.h
> > +++ b/include/usb.h
> > @@ -42,6 +42,16 @@
> >
> >  #define USB_CNTL_TIMEOUT 100 /* 100ms timeout */
> >
> > +/*
> > + * Errors we can report, e.g. return USB_EDEVCRITICAL
> > + * Use -ve numbers to fit in with usb_storage
>
> Yak...
>
> > + * U-Boot needs some unified numbers
> > + */
> > +#define USB_EOK                        0       /* ok, no error */
> > +#define USB_EFAIL              -1      /* general failure(!) */
> > +#define USB_EDEVCRITICAL       -2      /* must reset device on hub */
> > +#define USB_ENEXTFREE          -3      /* next free error number */
> > +
> >  /* device request (setup) */
> >  struct devrequest {
> >        unsigned char   requesttype;
> > @@ -198,6 +208,7 @@ int usb_get_class_descriptor(struct usb_device *dev,
> int ifnum,
> >  int usb_clear_halt(struct usb_device *dev, int pipe);
> >  int usb_string(struct usb_device *dev, int index, char *buf, size_t
> size);
> >  int usb_set_interface(struct usb_device *dev, int interface, int
> alternate);
> > +int usb_restart_device(struct usb_device *dev);
> >
> >  /* big endian -> little endian conversion */
> >  /* some CPUs are already little endian e.g. the ARM920T */
>
> Kind regards,
>
> Remy
>


More information about the U-Boot mailing list