]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - drivers/sl811_usb.c
Fix: TFTP is not working on little endian systems
[karo-tx-uboot.git] / drivers / sl811_usb.c
index a18e54d775c00c0bdc92e5c1b65695598a8af76c..b0cdf0bc2c77d270c63ec9e9c18940b2736290da 100644 (file)
@@ -226,33 +226,50 @@ int usb_lowlevel_stop(void)
        return 0;
 }
 
-int sl811_send_packet(int dir_to_host, int data1, __u8 *buffer, int len)
+static int calc_needed_buswidth(int bytes, int need_preamble)
+{
+       return !need_preamble ? bytes * 8 + 256 : 8 * 8 * bytes + 2048;
+}
+
+static int sl811_send_packet(struct usb_device *dev, unsigned long pipe, __u8 *buffer, int len)
 {
        __u8 ctrl = SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE;
-       __u16 status;
-       int err = 0;
+       __u16 status = 0;
+       int err = 0, time_start = get_timer(0);
+       int need_preamble = !(rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) &&
+               usb_pipeslow(pipe);
 
        if (len > 239)
                return -1;
 
-       if (!dir_to_host)
+       if (usb_pipeout(pipe))
                ctrl |= SL811_USB_CTRL_DIR_OUT;
-       if (data1)
+       if (usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)))
                ctrl |= SL811_USB_CTRL_TOGGLE_1;
+       if (need_preamble)
+               ctrl |= SL811_USB_CTRL_PREAMBLE;
 
-       sl811_write(SL811_ADDR_A, 0x10);
-       sl811_write(SL811_LEN_A, len);
-       if (!dir_to_host && len)
-               sl811_write_buf(0x10, buffer, len);
+       sl811_write(SL811_INTRSTS, 0xff);
 
        while (err < 3) {
-               if (sl811_read(SL811_SOFCNTDIV)*64 < len * 8 * 2)
+               sl811_write(SL811_ADDR_A, 0x10);
+               sl811_write(SL811_LEN_A, len);
+               if (usb_pipeout(pipe) && len)
+                       sl811_write_buf(0x10, buffer, len);
+
+               if (!(rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED) &&
+                   sl811_read(SL811_SOFCNTDIV)*64 < calc_needed_buswidth(len, need_preamble))
                        ctrl |= SL811_USB_CTRL_SOF;
                else
                        ctrl &= ~SL811_USB_CTRL_SOF;
+
                sl811_write(SL811_CTRL_A, ctrl);
-               while (!(sl811_read(SL811_INTRSTS) & SL811_INTR_DONE_A))
-                       ; /* do nothing */
+               while (!(sl811_read(SL811_INTRSTS) & SL811_INTR_DONE_A)) {
+                       if (5*CFG_HZ < get_timer(time_start)) {
+                               printf("USB transmit timed out\n");
+                               return -USB_ST_CRC_ERR;
+                       }
+               }
 
                sl811_write(SL811_INTRSTS, 0xff);
                status = sl811_read(SL811_STS_A);
@@ -263,7 +280,7 @@ int sl811_send_packet(int dir_to_host, int data1, __u8 *buffer, int len)
                                PDEBUG(0, "usb transfer remainder = %d\n", remainder);
                                len -= remainder;
                        }
-                       if (dir_to_host && len)
+                       if (usb_pipein(pipe) && len)
                                sl811_read_buf(0x10, buffer, len);
                        return len;
                }
@@ -275,7 +292,16 @@ int sl811_send_packet(int dir_to_host, int data1, __u8 *buffer, int len)
                err++;
        }
 
-       return -1;
+       err = 0;
+
+       if (status & SL811_USB_STS_ERROR)
+               err |= USB_ST_BUF_ERR;
+       if (status & SL811_USB_STS_TIMEOUT)
+               err |= USB_ST_CRC_ERR;
+       if (status & SL811_USB_STS_STALL)
+               err |= USB_ST_STALLED;
+
+       return -err;
 }
 
 int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
@@ -283,7 +309,6 @@ int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
 {
        int dir_out = usb_pipeout(pipe);
        int ep = usb_pipeendpoint(pipe);
-       __u8* buf = (__u8*)buffer;
        int max = usb_maxpacket(dev, pipe);
        int done = 0;
 
@@ -295,11 +320,10 @@ int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
        sl811_write(SL811_DEV_A, usb_pipedevice(pipe));
        sl811_write(SL811_PIDEP_A, PIDEP(!dir_out ? USB_PID_IN : USB_PID_OUT, ep));
        while (done < len) {
-               int res = sl811_send_packet(!dir_out, usb_gettoggle(dev, ep, dir_out),
-                                           buf+done,
+               int res = sl811_send_packet(dev, pipe, (__u8*)buffer+done,
                                            max > len - done ? len - done : max);
                if (res < 0) {
-                       dev->status = res;
+                       dev->status = -res;
                        return res;
                }
 
@@ -320,45 +344,52 @@ int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
 {
        int done = 0;
        int devnum = usb_pipedevice(pipe);
+       int ep = usb_pipeendpoint(pipe);
 
        dev->status = 0;
 
        if (devnum == root_hub_devnum)
                return sl811_rh_submit_urb(dev, pipe, buffer, len, setup);
 
-       PDEBUG(7, "dev = %d pipe = %ld buf = %p size = %d rt = %#x req = %#x\n",
-              devnum, usb_pipeendpoint(pipe), buffer, len, (int)setup->requesttype,
-              (int)setup->request);
+       PDEBUG(7, "dev = %d pipe = %ld buf = %p size = %d rt = %#x req = %#x bus = %i\n",
+              devnum, ep, buffer, len, (int)setup->requesttype,
+              (int)setup->request, sl811_read(SL811_SOFCNTDIV)*64);
 
        sl811_write(SL811_DEV_A, devnum);
-       sl811_write(SL811_PIDEP_A, PIDEP(USB_PID_SETUP, 0));
+       sl811_write(SL811_PIDEP_A, PIDEP(USB_PID_SETUP, ep));
        /* setup phase */
-       if (sl811_send_packet(0, 0, (__u8*)setup, sizeof(*setup)) == sizeof(*setup)) {
-               int dir_in = setup->requesttype & USB_DIR_IN;
-               __u8* buf = (__u8*)buffer;
-               int data1 = 1;
+       usb_settoggle(dev, ep, 1, 0);
+       if (sl811_send_packet(dev, usb_sndctrlpipe(dev, ep),
+                             (__u8*)setup, sizeof(*setup)) == sizeof(*setup)) {
+               int dir_in = usb_pipein(pipe);
                int max = usb_maxpacket(dev, pipe);
 
                /* data phase */
                sl811_write(SL811_PIDEP_A,
-                           PIDEP(dir_in ? USB_PID_IN : USB_PID_OUT, 0));
+                           PIDEP(dir_in ? USB_PID_IN : USB_PID_OUT, ep));
+               usb_settoggle(dev, ep, usb_pipeout(pipe), 1);
                while (done < len) {
-                       int res = sl811_send_packet(dir_in, data1, buf+done,
+                       int res = sl811_send_packet(dev, pipe, (__u8*)buffer+done,
                                                    max > len - done ? len - done : max);
-                       if (res < 0)
-                               return res;
+                       if (res < 0) {
+                               PDEBUG(0, "status data failed!\n");
+                               dev->status = -res;
+                               return 0;
+                       }
                        done += res;
-
+                       usb_dotoggle(dev, ep, usb_pipeout(pipe));
                        if (dir_in && res < max) /* short packet */
                                break;
-
-                       data1 = !data1;
                }
 
                /* status phase */
                sl811_write(SL811_PIDEP_A,
-                           PIDEP(!dir_in ? USB_PID_IN : USB_PID_OUT, 0));
-               if (sl811_send_packet(!dir_in, 1, 0, 0) < 0) {
+                           PIDEP(!dir_in ? USB_PID_IN : USB_PID_OUT, ep));
+               usb_settoggle(dev, ep, !usb_pipeout(pipe), 1);
+               if (sl811_send_packet(dev,
+                                     !dir_in ? usb_rcvctrlpipe(dev, ep) :
+                                     usb_sndctrlpipe(dev, ep),
+                                     0, 0) < 0) {
                        PDEBUG(0, "status phase failed!\n");
                        dev->status = -1;
                }
@@ -375,7 +406,7 @@ int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
 int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
                   int len, int interval)
 {
-       PDEBUG(7, "dev = %p pipe = %#lx buf = %p size = %d int = %d\n", dev, pipe,
+       PDEBUG(0, "dev = %p pipe = %#lx buf = %p size = %d int = %d\n", dev, pipe,
               buffer, len, interval);
        return -1;
 }
@@ -476,7 +507,7 @@ static int ascii2utf (char *s, u8 *utf, int utfmax)
  * root_hub_string is used by each host controller's root hub code,
  * so that they're identified consistently throughout the system.
  */
-int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len)
+static int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len)
 {
        char buf [30];
 
@@ -491,7 +522,7 @@ int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len)
 
        /* serial number */
        } else if (id == 1) {
-               sprintf (buf, "%x", serial);
+               sprintf (buf, "%#x", serial);
 
        /* product description */
        } else if (id == 2) {
@@ -503,7 +534,8 @@ int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len)
        } else
            return 0;
 
-       data [0] = 2 + ascii2utf (buf, data + 2, len - 2);
+       ascii2utf (buf, data + 2, len - 2);
+       data [0] = 2 + strlen(buf) * 2;
        data [1] = 3;
        return data [0];
 }