Initial revision
diff --git a/common/docecc.c b/common/docecc.c
new file mode 100644
index 0000000..09e8233
--- /dev/null
+++ b/common/docecc.c
@@ -0,0 +1,519 @@
+/*
+ * ECC algorithm for M-systems disk on chip. We use the excellent Reed
+ * Solmon code of Phil Karn (karn@ka9q.ampr.org) available under the
+ * GNU GPL License. The rest is simply to convert the disk on chip
+ * syndrom into a standard syndom.
+ *
+ * Author: Fabrice Bellard (fabrice.bellard@netgem.com)
+ * Copyright (C) 2000 Netgem S.A.
+ *
+ * $Id: docecc.c,v 1.4 2001/10/02 15:05:13 dwmw2 Exp $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <config.h>
+#include <common.h>
+#include <malloc.h>
+
+#include <linux/mtd/doc2000.h>
+
+#undef ECC_DEBUG
+#undef PSYCHO_DEBUG
+
+#if (CONFIG_COMMANDS & CFG_CMD_DOC)
+
+#define min(x,y) ((x)<(y)?(x):(y))
+
+/* need to undef it (from asm/termbits.h) */
+#undef B0
+
+#define MM 10 /* Symbol size in bits */
+#define KK (1023-4) /* Number of data symbols per block */
+#define B0 510 /* First root of generator polynomial, alpha form */
+#define PRIM 1 /* power of alpha used to generate roots of generator poly */
+#define	NN ((1 << MM) - 1)
+
+typedef unsigned short dtype;
+
+/* 1+x^3+x^10 */
+static const int Pp[MM+1] = { 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1 };
+
+/* This defines the type used to store an element of the Galois Field
+ * used by the code. Make sure this is something larger than a char if
+ * if anything larger than GF(256) is used.
+ *
+ * Note: unsigned char will work up to GF(256) but int seems to run
+ * faster on the Pentium.
+ */
+typedef int gf;
+
+/* No legal value in index form represents zero, so
+ * we need a special value for this purpose
+ */
+#define A0	(NN)
+
+/* Compute x % NN, where NN is 2**MM - 1,
+ * without a slow divide
+ */
+static inline gf
+modnn(int x)
+{
+  while (x >= NN) {
+    x -= NN;
+    x = (x >> MM) + (x & NN);
+  }
+  return x;
+}
+
+#define	CLEAR(a,n) {\
+int ci;\
+for(ci=(n)-1;ci >=0;ci--)\
+(a)[ci] = 0;\
+}
+
+#define	COPY(a,b,n) {\
+int ci;\
+for(ci=(n)-1;ci >=0;ci--)\
+(a)[ci] = (b)[ci];\
+}
+
+#define	COPYDOWN(a,b,n) {\
+int ci;\
+for(ci=(n)-1;ci >=0;ci--)\
+(a)[ci] = (b)[ci];\
+}
+
+#define Ldec 1
+
+/* generate GF(2**m) from the irreducible polynomial p(X) in Pp[0]..Pp[m]
+   lookup tables:  index->polynomial form   alpha_to[] contains j=alpha**i;
+                   polynomial form -> index form  index_of[j=alpha**i] = i
+   alpha=2 is the primitive element of GF(2**m)
+   HARI's COMMENT: (4/13/94) alpha_to[] can be used as follows:
+        Let @ represent the primitive element commonly called "alpha" that
+   is the root of the primitive polynomial p(x). Then in GF(2^m), for any
+   0 <= i <= 2^m-2,
+        @^i = a(0) + a(1) @ + a(2) @^2 + ... + a(m-1) @^(m-1)
+   where the binary vector (a(0),a(1),a(2),...,a(m-1)) is the representation
+   of the integer "alpha_to[i]" with a(0) being the LSB and a(m-1) the MSB. Thus for
+   example the polynomial representation of @^5 would be given by the binary
+   representation of the integer "alpha_to[5]".
+                   Similarily, index_of[] can be used as follows:
+        As above, let @ represent the primitive element of GF(2^m) that is
+   the root of the primitive polynomial p(x). In order to find the power
+   of @ (alpha) that has the polynomial representation
+        a(0) + a(1) @ + a(2) @^2 + ... + a(m-1) @^(m-1)
+   we consider the integer "i" whose binary representation with a(0) being LSB
+   and a(m-1) MSB is (a(0),a(1),...,a(m-1)) and locate the entry
+   "index_of[i]". Now, @^index_of[i] is that element whose polynomial
+    representation is (a(0),a(1),a(2),...,a(m-1)).
+   NOTE:
+        The element alpha_to[2^m-1] = 0 always signifying that the
+   representation of "@^infinity" = 0 is (0,0,0,...,0).
+        Similarily, the element index_of[0] = A0 always signifying
+   that the power of alpha which has the polynomial representation
+   (0,0,...,0) is "infinity".
+
+*/
+
+static void
+generate_gf(dtype Alpha_to[NN + 1], dtype Index_of[NN + 1])
+{
+  register int i, mask;
+
+  mask = 1;
+  Alpha_to[MM] = 0;
+  for (i = 0; i < MM; i++) {
+    Alpha_to[i] = mask;
+    Index_of[Alpha_to[i]] = i;
+    /* If Pp[i] == 1 then, term @^i occurs in poly-repr of @^MM */
+    if (Pp[i] != 0)
+      Alpha_to[MM] ^= mask;	/* Bit-wise EXOR operation */
+    mask <<= 1;	/* single left-shift */
+  }
+  Index_of[Alpha_to[MM]] = MM;
+  /*
+   * Have obtained poly-repr of @^MM. Poly-repr of @^(i+1) is given by
+   * poly-repr of @^i shifted left one-bit and accounting for any @^MM
+   * term that may occur when poly-repr of @^i is shifted.
+   */
+  mask >>= 1;
+  for (i = MM + 1; i < NN; i++) {
+    if (Alpha_to[i - 1] >= mask)
+      Alpha_to[i] = Alpha_to[MM] ^ ((Alpha_to[i - 1] ^ mask) << 1);
+    else
+      Alpha_to[i] = Alpha_to[i - 1] << 1;
+    Index_of[Alpha_to[i]] = i;
+  }
+  Index_of[0] = A0;
+  Alpha_to[NN] = 0;
+}
+
+/*
+ * Performs ERRORS+ERASURES decoding of RS codes. bb[] is the content
+ * of the feedback shift register after having processed the data and
+ * the ECC.
+ *
+ * Return number of symbols corrected, or -1 if codeword is illegal
+ * or uncorrectable. If eras_pos is non-null, the detected error locations
+ * are written back. NOTE! This array must be at least NN-KK elements long.
+ * The corrected data are written in eras_val[]. They must be xor with the data
+ * to retrieve the correct data : data[erase_pos[i]] ^= erase_val[i] .
+ *
+ * First "no_eras" erasures are declared by the calling program. Then, the
+ * maximum # of errors correctable is t_after_eras = floor((NN-KK-no_eras)/2).
+ * If the number of channel errors is not greater than "t_after_eras" the
+ * transmitted codeword will be recovered. Details of algorithm can be found
+ * in R. Blahut's "Theory ... of Error-Correcting Codes".
+
+ * Warning: the eras_pos[] array must not contain duplicate entries; decoder failure
+ * will result. The decoder *could* check for this condition, but it would involve
+ * extra time on every decoding operation.
+ * */
+static int
+eras_dec_rs(dtype Alpha_to[NN + 1], dtype Index_of[NN + 1],
+            gf bb[NN - KK + 1], gf eras_val[NN-KK], int eras_pos[NN-KK],
+            int no_eras)
+{
+  int deg_lambda, el, deg_omega;
+  int i, j, r,k;
+  gf u,q,tmp,num1,num2,den,discr_r;
+  gf lambda[NN-KK + 1], s[NN-KK + 1];	/* Err+Eras Locator poly
+					 * and syndrome poly */
+  gf b[NN-KK + 1], t[NN-KK + 1], omega[NN-KK + 1];
+  gf root[NN-KK], reg[NN-KK + 1], loc[NN-KK];
+  int syn_error, count;
+
+  syn_error = 0;
+  for(i=0;i<NN-KK;i++)
+      syn_error |= bb[i];
+
+  if (!syn_error) {
+    /* if remainder is zero, data[] is a codeword and there are no
+     * errors to correct. So return data[] unmodified
+     */
+    count = 0;
+    goto finish;
+  }
+
+  for(i=1;i<=NN-KK;i++){
+    s[i] = bb[0];
+  }
+  for(j=1;j<NN-KK;j++){
+    if(bb[j] == 0)
+      continue;
+    tmp = Index_of[bb[j]];
+
+    for(i=1;i<=NN-KK;i++)
+      s[i] ^= Alpha_to[modnn(tmp + (B0+i-1)*PRIM*j)];
+  }
+
+  /* undo the feedback register implicit multiplication and convert
+     syndromes to index form */
+
+  for(i=1;i<=NN-KK;i++) {
+      tmp = Index_of[s[i]];
+      if (tmp != A0)
+          tmp = modnn(tmp + 2 * KK * (B0+i-1)*PRIM);
+      s[i] = tmp;
+  }
+
+  CLEAR(&lambda[1],NN-KK);
+  lambda[0] = 1;
+
+  if (no_eras > 0) {
+    /* Init lambda to be the erasure locator polynomial */
+    lambda[1] = Alpha_to[modnn(PRIM * eras_pos[0])];
+    for (i = 1; i < no_eras; i++) {
+      u = modnn(PRIM*eras_pos[i]);
+      for (j = i+1; j > 0; j--) {
+	tmp = Index_of[lambda[j - 1]];
+	if(tmp != A0)
+	  lambda[j] ^= Alpha_to[modnn(u + tmp)];
+      }
+    }
+#ifdef ECC_DEBUG
+    /* Test code that verifies the erasure locator polynomial just constructed
+       Needed only for decoder debugging. */
+
+    /* find roots of the erasure location polynomial */
+    for(i=1;i<=no_eras;i++)
+      reg[i] = Index_of[lambda[i]];
+    count = 0;
+    for (i = 1,k=NN-Ldec; i <= NN; i++,k = modnn(NN+k-Ldec)) {
+      q = 1;
+      for (j = 1; j <= no_eras; j++)
+	if (reg[j] != A0) {
+	  reg[j] = modnn(reg[j] + j);
+	  q ^= Alpha_to[reg[j]];
+	}
+      if (q != 0)
+	continue;
+      /* store root and error location number indices */
+      root[count] = i;
+      loc[count] = k;
+      count++;
+    }
+    if (count != no_eras) {
+      printf("\n lambda(x) is WRONG\n");
+      count = -1;
+      goto finish;
+    }
+#ifdef PSYCHO_DEBUG
+    printf("\n Erasure positions as determined by roots of Eras Loc Poly:\n");
+    for (i = 0; i < count; i++)
+      printf("%d ", loc[i]);
+    printf("\n");
+#endif
+#endif
+  }
+  for(i=0;i<NN-KK+1;i++)
+    b[i] = Index_of[lambda[i]];
+
+  /*
+   * Begin Berlekamp-Massey algorithm to determine error+erasure
+   * locator polynomial
+   */
+  r = no_eras;
+  el = no_eras;
+  while (++r <= NN-KK) {	/* r is the step number */
+    /* Compute discrepancy at the r-th step in poly-form */
+    discr_r = 0;
+    for (i = 0; i < r; i++){
+      if ((lambda[i] != 0) && (s[r - i] != A0)) {
+	discr_r ^= Alpha_to[modnn(Index_of[lambda[i]] + s[r - i])];
+      }
+    }
+    discr_r = Index_of[discr_r];	/* Index form */
+    if (discr_r == A0) {
+      /* 2 lines below: B(x) <-- x*B(x) */
+      COPYDOWN(&b[1],b,NN-KK);
+      b[0] = A0;
+    } else {
+      /* 7 lines below: T(x) <-- lambda(x) - discr_r*x*b(x) */
+      t[0] = lambda[0];
+      for (i = 0 ; i < NN-KK; i++) {
+	if(b[i] != A0)
+	  t[i+1] = lambda[i+1] ^ Alpha_to[modnn(discr_r + b[i])];
+	else
+	  t[i+1] = lambda[i+1];
+      }
+      if (2 * el <= r + no_eras - 1) {
+	el = r + no_eras - el;
+	/*
+	 * 2 lines below: B(x) <-- inv(discr_r) *
+	 * lambda(x)
+	 */
+	for (i = 0; i <= NN-KK; i++)
+	  b[i] = (lambda[i] == 0) ? A0 : modnn(Index_of[lambda[i]] - discr_r + NN);
+      } else {
+	/* 2 lines below: B(x) <-- x*B(x) */
+	COPYDOWN(&b[1],b,NN-KK);
+	b[0] = A0;
+      }
+      COPY(lambda,t,NN-KK+1);
+    }
+  }
+
+  /* Convert lambda to index form and compute deg(lambda(x)) */
+  deg_lambda = 0;
+  for(i=0;i<NN-KK+1;i++){
+    lambda[i] = Index_of[lambda[i]];
+    if(lambda[i] != A0)
+      deg_lambda = i;
+  }
+  /*
+   * Find roots of the error+erasure locator polynomial by Chien
+   * Search
+   */
+  COPY(&reg[1],&lambda[1],NN-KK);
+  count = 0;		/* Number of roots of lambda(x) */
+  for (i = 1,k=NN-Ldec; i <= NN; i++,k = modnn(NN+k-Ldec)) {
+    q = 1;
+    for (j = deg_lambda; j > 0; j--){
+      if (reg[j] != A0) {
+	reg[j] = modnn(reg[j] + j);
+	q ^= Alpha_to[reg[j]];
+      }
+    }
+    if (q != 0)
+      continue;
+    /* store root (index-form) and error location number */
+    root[count] = i;
+    loc[count] = k;
+    /* If we've already found max possible roots,
+     * abort the search to save time
+     */
+    if(++count == deg_lambda)
+      break;
+  }
+  if (deg_lambda != count) {
+    /*
+     * deg(lambda) unequal to number of roots => uncorrectable
+     * error detected
+     */
+    count = -1;
+    goto finish;
+  }
+  /*
+   * Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo
+   * x**(NN-KK)). in index form. Also find deg(omega).
+   */
+  deg_omega = 0;
+  for (i = 0; i < NN-KK;i++){
+    tmp = 0;
+    j = (deg_lambda < i) ? deg_lambda : i;
+    for(;j >= 0; j--){
+      if ((s[i + 1 - j] != A0) && (lambda[j] != A0))
+	tmp ^= Alpha_to[modnn(s[i + 1 - j] + lambda[j])];
+    }
+    if(tmp != 0)
+      deg_omega = i;
+    omega[i] = Index_of[tmp];
+  }
+  omega[NN-KK] = A0;
+
+  /*
+   * Compute error values in poly-form. num1 = omega(inv(X(l))), num2 =
+   * inv(X(l))**(B0-1) and den = lambda_pr(inv(X(l))) all in poly-form
+   */
+  for (j = count-1; j >=0; j--) {
+    num1 = 0;
+    for (i = deg_omega; i >= 0; i--) {
+      if (omega[i] != A0)
+	num1  ^= Alpha_to[modnn(omega[i] + i * root[j])];
+    }
+    num2 = Alpha_to[modnn(root[j] * (B0 - 1) + NN)];
+    den = 0;
+
+    /* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */
+    for (i = min(deg_lambda,NN-KK-1) & ~1; i >= 0; i -=2) {
+      if(lambda[i+1] != A0)
+	den ^= Alpha_to[modnn(lambda[i+1] + i * root[j])];
+    }
+    if (den == 0) {
+#ifdef ECC_DEBUG
+      printf("\n ERROR: denominator = 0\n");
+#endif
+      /* Convert to dual- basis */
+      count = -1;
+      goto finish;
+    }
+    /* Apply error to data */
+    if (num1 != 0) {
+        eras_val[j] = Alpha_to[modnn(Index_of[num1] + Index_of[num2] + NN - Index_of[den])];
+    } else {
+        eras_val[j] = 0;
+    }
+  }
+ finish:
+  for(i=0;i<count;i++)
+      eras_pos[i] = loc[i];
+  return count;
+}
+
+/***************************************************************************/
+/* The DOC specific code begins here */
+
+#define SECTOR_SIZE 512
+/* The sector bytes are packed into NB_DATA MM bits words */
+#define NB_DATA (((SECTOR_SIZE + 1) * 8 + 6) / MM)
+
+/*
+ * Correct the errors in 'sector[]' by using 'ecc1[]' which is the
+ * content of the feedback shift register applyied to the sector and
+ * the ECC. Return the number of errors corrected (and correct them in
+ * sector), or -1 if error
+ */
+int doc_decode_ecc(unsigned char sector[SECTOR_SIZE], unsigned char ecc1[6])
+{
+    int parity, i, nb_errors;
+    gf bb[NN - KK + 1];
+    gf error_val[NN-KK];
+    int error_pos[NN-KK], pos, bitpos, index, val;
+    dtype *Alpha_to, *Index_of;
+
+    /* init log and exp tables here to save memory. However, it is slower */
+    Alpha_to = malloc((NN + 1) * sizeof(dtype));
+    if (!Alpha_to)
+        return -1;
+
+    Index_of = malloc((NN + 1) * sizeof(dtype));
+    if (!Index_of) {
+        free(Alpha_to);
+        return -1;
+    }
+
+    generate_gf(Alpha_to, Index_of);
+
+    parity = ecc1[1];
+
+    bb[0] =  (ecc1[4] & 0xff) | ((ecc1[5] & 0x03) << 8);
+    bb[1] = ((ecc1[5] & 0xfc) >> 2) | ((ecc1[2] & 0x0f) << 6);
+    bb[2] = ((ecc1[2] & 0xf0) >> 4) | ((ecc1[3] & 0x3f) << 4);
+    bb[3] = ((ecc1[3] & 0xc0) >> 6) | ((ecc1[0] & 0xff) << 2);
+
+    nb_errors = eras_dec_rs(Alpha_to, Index_of, bb,
+                            error_val, error_pos, 0);
+    if (nb_errors <= 0)
+        goto the_end;
+
+    /* correct the errors */
+    for(i=0;i<nb_errors;i++) {
+        pos = error_pos[i];
+        if (pos >= NB_DATA && pos < KK) {
+            nb_errors = -1;
+            goto the_end;
+        }
+        if (pos < NB_DATA) {
+            /* extract bit position (MSB first) */
+            pos = 10 * (NB_DATA - 1 - pos) - 6;
+            /* now correct the following 10 bits. At most two bytes
+               can be modified since pos is even */
+            index = (pos >> 3) ^ 1;
+            bitpos = pos & 7;
+            if ((index >= 0 && index < SECTOR_SIZE) ||
+                index == (SECTOR_SIZE + 1)) {
+                val = error_val[i] >> (2 + bitpos);
+                parity ^= val;
+                if (index < SECTOR_SIZE)
+                    sector[index] ^= val;
+            }
+            index = ((pos >> 3) + 1) ^ 1;
+            bitpos = (bitpos + 10) & 7;
+            if (bitpos == 0)
+                bitpos = 8;
+            if ((index >= 0 && index < SECTOR_SIZE) ||
+                index == (SECTOR_SIZE + 1)) {
+                val = error_val[i] << (8 - bitpos);
+                parity ^= val;
+                if (index < SECTOR_SIZE)
+                    sector[index] ^= val;
+            }
+        }
+    }
+
+    /* use parity to test extra errors */
+    if ((parity & 0xff) != 0)
+        nb_errors = -1;
+
+ the_end:
+    free(Alpha_to);
+    free(Index_of);
+    return nb_errors;
+}
+
+#endif /* (CONFIG_COMMANDS & CFG_CMD_DOC) */
diff --git a/common/flash.c b/common/flash.c
new file mode 100644
index 0000000..fa8942b
--- /dev/null
+++ b/common/flash.c
@@ -0,0 +1,219 @@
+/*
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <flash.h>
+
+#if !defined(CFG_NO_FLASH)
+
+extern flash_info_t  flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */
+
+/*-----------------------------------------------------------------------
+ * Functions
+ */
+
+/*-----------------------------------------------------------------------
+ * Set protection status for monitor sectors
+ *
+ * The monitor is always located in the _first_ Flash bank.
+ * If necessary you have to map the second bank at lower addresses.
+ */
+void
+flash_protect (int flag, ulong from, ulong to, flash_info_t *info)
+{
+	ulong b_end = info->start[0] + info->size - 1;	/* bank end address */
+	short s_end = info->sector_count - 1;	/* index of last sector */
+	int i;
+
+	/* Do nothing if input data is bad. */
+	if (info->sector_count == 0 || info->size == 0 || to < from) {
+		return;
+	}
+
+	/* There is nothing to do if we have no data about the flash
+	 * or the protect range and flash range don't overlap.
+	 */
+	if (info->flash_id == FLASH_UNKNOWN ||
+	    to < info->start[0] || from > b_end) {
+		return;
+	}
+
+	for (i=0; i<info->sector_count; ++i) {
+		ulong end;		/* last address in current sect	*/
+
+		end = (i == s_end) ? b_end : info->start[i + 1] - 1;
+
+		/* Update protection if any part of the sector
+		 * is in the specified range.
+		 */
+		if (from <= end && to >= info->start[i]) {
+			if (flag & FLAG_PROTECT_CLEAR) {
+#if defined(CFG_FLASH_PROTECTION)
+				flash_real_protect(info, i, 0);
+#else
+				info->protect[i] = 0;
+#endif	/* CFG_FLASH_PROTECTION */
+			}
+			else if (flag & FLAG_PROTECT_SET) {
+#if defined(CFG_FLASH_PROTECTION)
+				flash_real_protect(info, i, 1);
+#else
+				info->protect[i] = 1;
+#endif	/* CFG_FLASH_PROTECTION */
+			}
+		}
+	}
+}
+
+/*-----------------------------------------------------------------------
+ */
+
+flash_info_t *
+addr2info (ulong addr)
+{
+#ifndef CONFIG_SPD823TS
+	flash_info_t *info;
+	int i;
+
+	for (i=0, info=&flash_info[0]; i<CFG_MAX_FLASH_BANKS; ++i, ++info) {
+		if (info->flash_id != FLASH_UNKNOWN &&
+		    addr >= info->start[0] &&
+		    /* WARNING - The '- 1' is needed if the flash
+		     * is at the end of the address space, since
+		     * info->start[0] + info->size wraps back to 0.
+		     * Please don't change this unless you understand this.
+		     */
+		    addr <= info->start[0] + info->size - 1) {
+			return (info);
+		}
+	}
+#endif /* CONFIG_SPD823TS */
+
+	return (NULL);
+}
+
+/*-----------------------------------------------------------------------
+ * Copy memory to flash.
+ * Make sure all target addresses are within Flash bounds,
+ * and no protected sectors are hit.
+ * Returns:
+ * ERR_OK          0 - OK
+ * ERR_TIMOUT      1 - write timeout
+ * ERR_NOT_ERASED  2 - Flash not erased
+ * ERR_PROTECTED   4 - target range includes protected sectors
+ * ERR_INVAL       8 - target address not in Flash memory
+ * ERR_ALIGN       16 - target address not aligned on boundary
+ *			(only some targets require alignment)
+ */
+int
+flash_write (uchar *src, ulong addr, ulong cnt)
+{
+#ifdef CONFIG_SPD823TS
+	return (ERR_TIMOUT);	/* any other error codes are possible as well */
+#else
+	int i;
+	ulong         end        = addr + cnt - 1;
+	flash_info_t *info_first = addr2info (addr);
+	flash_info_t *info_last  = addr2info (end );
+	flash_info_t *info;
+
+	if (cnt == 0) {
+		return (ERR_OK);
+	}
+
+	if (!info_first || !info_last) {
+		return (ERR_INVAL);
+	}
+
+	for (info = info_first; info <= info_last; ++info) {
+		ulong b_end = info->start[0] + info->size;	/* bank end addr */
+		short s_end = info->sector_count - 1;
+		for (i=0; i<info->sector_count; ++i) {
+			ulong e_addr = (i == s_end) ? b_end : info->start[i + 1];
+
+			if ((end >= info->start[i]) && (addr < e_addr) &&
+			    (info->protect[i] != 0) ) {
+				return (ERR_PROTECTED);
+			}
+		}
+	}
+
+	/* finally write data to flash */
+	for (info = info_first; info <= info_last && cnt>0; ++info) {
+		ulong len;
+
+		len = info->start[0] + info->size - addr;
+		if (len > cnt)
+			len = cnt;
+		if ((i = write_buff(info, src, addr, len)) != 0) {
+			return (i);
+		}
+		cnt  -= len;
+		addr += len;
+		src  += len;
+	}
+	return (ERR_OK);
+#endif /* CONFIG_SPD823TS */
+}
+
+/*-----------------------------------------------------------------------
+ */
+
+void flash_perror (int err)
+{
+	switch (err) {
+	case ERR_OK:
+		break;
+	case ERR_TIMOUT:
+		puts ("Timeout writing to Flash\n");
+		break;
+	case ERR_NOT_ERASED:
+		puts ("Flash not Erased\n");
+		break;
+	case ERR_PROTECTED:
+		puts ("Can't write to protected Flash sectors\n");
+		break;
+	case ERR_INVAL:
+		puts ("Outside available Flash\n");
+		break;
+	case ERR_ALIGN:
+		puts ("Start and/or end address not on sector boundary\n");
+		break;
+	case ERR_UNKNOWN_FLASH_VENDOR:
+		puts ("Unknown Vendor of Flash\n");
+		break;
+	case ERR_UNKNOWN_FLASH_TYPE:
+		puts ("Unknown Type of Flash\n");
+		break;
+	case ERR_PROG_ERROR:
+		puts ("General Flash Programming Error\n");
+		break;
+	default:
+		printf ("%s[%d] FIXME: rc=%d\n", __FILE__, __LINE__, err);
+		break;
+	}
+}
+
+/*-----------------------------------------------------------------------
+ */
+#endif /* !CFG_NO_FLASH */
diff --git a/common/lists.c b/common/lists.c
new file mode 100644
index 0000000..3f117b5
--- /dev/null
+++ b/common/lists.c
@@ -0,0 +1,734 @@
+#include <common.h>
+#include <malloc.h>
+#include <lists.h>
+
+#define MAX(a,b) 	(((a)>(b)) ? (a) : (b))
+#define MIN(a,b) 	(((a)<(b)) ? (a) : (b))
+#define CAT4CHARS(a,b,c,d)	((a<<24) | (b<<16) | (c<<8) | d)
+
+/* increase list size by 10% every time it is full */
+#define kDefaultAllocationPercentIncrease	10
+
+/* always increase list size by 4 items when it is full */
+#define kDefaultAllocationminNumItemsIncrease	4
+
+/*
+ * how many items to expand the list by when it becomes full
+ * = current listSize (in items) + (hiword percent of list size) + loword
+ */
+#define NUMITEMSPERALLOC(list)	MAX(((*list)->listSize * \
+				    ((*list)->percentIncrease + 100)) / 100, \
+				    (*list)->minNumItemsIncrease )
+
+#define ITEMPTR(list,item)	&(((char *)&(*list)->itemList)[(*(list))->itemSize * (item)])
+
+#define LIST_SIGNATURE		CAT4CHARS('L', 'I', 'S', 'T');
+
+#define calloc(size,num)	malloc(size*num)
+
+/********************************************************************/
+
+Handle NewHandle (unsigned int numBytes)
+{
+	void *memPtr;
+	HandleRecord *hanPtr;
+
+	memPtr = calloc (numBytes, 1);
+	hanPtr = (HandleRecord *) calloc (sizeof (HandleRecord), 1);
+	if (hanPtr && (memPtr || numBytes == 0)) {
+		hanPtr->ptr = memPtr;
+		hanPtr->size = numBytes;
+		return (Handle) hanPtr;
+	} else {
+		free (memPtr);
+		free (hanPtr);
+		return NULL;
+	}
+}
+/********************************************************************/
+
+void DisposeHandle (Handle handle)
+{
+	if (handle) {
+		free (*handle);
+		free ((void *) handle);
+	}
+}
+/********************************************************************/
+
+unsigned int GetHandleSize (Handle handle)
+{
+	return ((HandleRecord *) handle)->size;
+}
+/********************************************************************/
+
+int SetHandleSize (Handle handle, unsigned int newSize)
+{
+	HandleRecord *hanRecPtr = (HandleRecord *) handle;
+	void *newPtr, *oldPtr;
+	unsigned int oldSize;
+
+
+	oldPtr = hanRecPtr->ptr;
+	oldSize = hanRecPtr->size;
+
+	if (oldSize == newSize)
+		return 1;
+
+	if (oldPtr == NULL) {
+		newPtr = malloc (newSize);
+	} else {
+		newPtr = realloc (oldPtr, newSize);
+	}
+	if (newPtr || (newSize == 0)) {
+		hanRecPtr->ptr = newPtr;
+		hanRecPtr->size = newSize;
+		if (newSize > oldSize)
+			memset ((char *) newPtr + oldSize, 0, newSize - oldSize);
+		return 1;
+	} else
+		return 0;
+}
+
+#ifdef	CFG_ALL_LIST_FUNCTIONS
+
+/*  Used to compare list elements by their raw data contents */
+static int ListMemBlockCmp (void *a, void *b, int size)
+{
+	return memcmp (a, b, size);
+}
+
+/***************************************************************************/
+
+/*
+ * Binary search numElements of size elementSize in array for a match
+ * to the. item. Return the index of the element that matches
+ * (0 - numElements - 1). If no match is found return the -i-1 where
+ * i is the index (0 - numElements) where the item should be placed.
+ * (*theCmp)(a,b) should return <0 if a<b, 0 if a==b, >0 if a>b.
+ *
+ * This function is like the C-Library function bsearch() except that
+ * this function returns the index where the item should be placed if
+ * it is not found.
+ */
+int BinSearch ( void *array, int numElements, int elementSize,
+		void *itemPtr, CompareFunction compareFunction)
+{
+	int low, high, mid, cmp;
+	void *arrayItemPtr;
+
+	for (low = 0, high = numElements - 1, mid = 0, cmp = -1; low <= high;) {
+		mid = (low + high) >> 1;
+
+		arrayItemPtr = (void *) (((char *) array) + (mid * elementSize));
+		cmp = compareFunction
+			? compareFunction (itemPtr, arrayItemPtr)
+			: ListMemBlockCmp (itemPtr, arrayItemPtr, elementSize);
+		if (cmp == 0) {
+			return mid;
+		} else if (cmp < 0) {
+			high = mid - 1;
+		} else {
+			low = mid + 1;
+		}
+	}
+	if (cmp > 0)
+		mid++;
+
+	return -mid - 1;
+}
+
+#endif	/* CFG_ALL_LIST_FUNCTIONS */
+
+/*******************************************************************************/
+
+/*
+ * If numNewItems == 0 then expand the list by the number of items
+ * indicated by its allocation policy.
+ * If numNewItems > 0 then expand the list by exactly the number of
+ * items indicated.
+ * If numNewItems < 0 then expand the list by the absolute value of
+ * numNewItems plus the number of items indicated by its allocation
+ * policy.
+ * Returns 1 for success, 0 if out of memory
+*/
+static int ExpandListSpace (list_t list, int numNewItems)
+{
+	if (numNewItems == 0) {
+		numNewItems = NUMITEMSPERALLOC (list);
+	} else if (numNewItems < 0) {
+		numNewItems = (-numNewItems) + NUMITEMSPERALLOC (list);
+	}
+
+	if (SetHandleSize ((Handle) list,
+			   sizeof (ListStruct) +
+			   ((*list)->listSize +
+			   numNewItems) * (*list)->itemSize)) {
+		(*list)->listSize += numNewItems;
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
+/*******************************/
+
+#ifdef	CFG_ALL_LIST_FUNCTIONS
+
+/*
+ * This function reallocate the list, minus any currently unused
+ * portion of its allotted memory.
+ */
+void ListCompact (list_t list)
+{
+
+	if (!SetHandleSize ((Handle) list,
+			    sizeof (ListStruct) +
+			    (*list)->numItems * (*list)->itemSize)) {
+		return;
+	}
+
+	(*list)->listSize = (*list)->numItems;
+}
+
+#endif	/* CFG_ALL_LIST_FUNCTIONS */
+
+/*******************************/
+
+list_t ListCreate (int elementSize)
+{
+	list_t list;
+
+	list = (list_t) (NewHandle (sizeof (ListStruct)));  /* create empty list */
+	if (list) {
+		(*list)->signature = LIST_SIGNATURE;
+		(*list)->numItems = 0;
+		(*list)->listSize = 0;
+		(*list)->itemSize = elementSize;
+		(*list)->percentIncrease = kDefaultAllocationPercentIncrease;
+		(*list)->minNumItemsIncrease =
+				kDefaultAllocationminNumItemsIncrease;
+	}
+
+	return list;
+}
+
+/*******************************/
+
+void ListSetAllocationPolicy (list_t list, int minItemsPerAlloc,
+			      int percentIncreasePerAlloc)
+{
+	(*list)->percentIncrease = percentIncreasePerAlloc;
+	(*list)->minNumItemsIncrease = minItemsPerAlloc;
+}
+
+/*******************************/
+
+void ListDispose (list_t list)
+{
+	DisposeHandle ((Handle) list);
+}
+/*******************************/
+
+#ifdef	CFG_ALL_LIST_FUNCTIONS
+
+void ListDisposePtrList (list_t list)
+{
+	int index;
+	int numItems;
+
+	if (list) {
+		numItems = ListNumItems (list);
+
+		for (index = 1; index <= numItems; index++)
+			free (*(void **) ListGetPtrToItem (list, index));
+
+		ListDispose (list);
+	}
+}
+
+/*******************************/
+
+/*
+ * keeps memory, resets the number of items to 0
+ */
+void ListClear (list_t list)
+{
+	if (!list)
+		return;
+	(*list)->numItems = 0;
+}
+
+/*******************************/
+
+/*
+ * copy is only as large as necessary
+ */
+list_t ListCopy (list_t originalList)
+{
+	list_t tempList = NULL;
+	int numItems;
+
+	if (!originalList)
+		return NULL;
+
+	tempList = ListCreate ((*originalList)->itemSize);
+	if (tempList) {
+		numItems = ListNumItems (originalList);
+
+		if (!SetHandleSize ((Handle) tempList,
+				    sizeof (ListStruct) +
+				    numItems * (*tempList)->itemSize)) {
+			ListDispose (tempList);
+			return NULL;
+		}
+
+		(*tempList)->numItems = (*originalList)->numItems;
+		(*tempList)->listSize = (*originalList)->numItems;
+		(*tempList)->itemSize = (*originalList)->itemSize;
+		(*tempList)->percentIncrease = (*originalList)->percentIncrease;
+		(*tempList)->minNumItemsIncrease =
+				(*originalList)->minNumItemsIncrease;
+
+		memcpy (ITEMPTR (tempList, 0), ITEMPTR (originalList, 0),
+				numItems * (*tempList)->itemSize);
+	}
+
+	return tempList;
+}
+
+/********************************/
+
+/*
+ * list1 = list1 + list2
+ */
+int ListAppend (list_t list1, list_t list2)
+{
+	int numItemsL1, numItemsL2;
+
+	if (!list2)
+		return 1;
+
+	if (!list1)
+		return 0;
+	if ((*list1)->itemSize != (*list2)->itemSize)
+		return 0;
+
+	numItemsL1 = ListNumItems (list1);
+	numItemsL2 = ListNumItems (list2);
+
+	if (numItemsL2 == 0)
+		return 1;
+
+	if (!SetHandleSize ((Handle) list1,
+			    sizeof (ListStruct) + (numItemsL1 + numItemsL2) *
+					(*list1)->itemSize)) {
+		return 0;
+	}
+
+	(*list1)->numItems = numItemsL1 + numItemsL2;
+	(*list1)->listSize = numItemsL1 + numItemsL2;
+
+	memmove (ITEMPTR (list1, numItemsL1),
+		 ITEMPTR (list2, 0),
+		 numItemsL2 * (*list2)->itemSize);
+
+	return 1;
+}
+
+#endif	/* CFG_ALL_LIST_FUNCTIONS */
+
+/*******************************/
+
+/*
+ * returns 1 if the item is inserted, returns 0 if out of memory or
+ * bad arguments were passed.
+ */
+int ListInsertItem (list_t list, void *ptrToItem, int itemPosition)
+{
+	return ListInsertItems (list, ptrToItem, itemPosition, 1);
+}
+
+/*******************************/
+
+int ListInsertItems (list_t list, void *ptrToItems, int firstItemPosition,
+		     int numItemsToInsert)
+{
+	int numItems = (*list)->numItems;
+
+	if (firstItemPosition == numItems + 1)
+		firstItemPosition = LIST_END;
+	else if (firstItemPosition > numItems)
+		return 0;
+
+	if ((*list)->numItems >= (*list)->listSize) {
+		if (!ExpandListSpace (list, -numItemsToInsert))
+			return 0;
+	}
+
+	if (firstItemPosition == LIST_START) {
+		if (numItems == 0) {
+			/* special case for empty list */
+			firstItemPosition = LIST_END;
+		} else {
+			firstItemPosition = 1;
+		}
+	}
+
+	if (firstItemPosition == LIST_END) {	/* add at the end of the list */
+		if (ptrToItems)
+			memcpy (ITEMPTR (list, numItems), ptrToItems,
+					(*list)->itemSize * numItemsToInsert);
+		else
+			memset (ITEMPTR (list, numItems), 0,
+					(*list)->itemSize * numItemsToInsert);
+
+		(*list)->numItems += numItemsToInsert;
+	} else {					/* move part of list up to make room for new item */
+		memmove (ITEMPTR (list, firstItemPosition - 1 + numItemsToInsert),
+			 ITEMPTR (list, firstItemPosition - 1),
+			 (numItems + 1 - firstItemPosition) * (*list)->itemSize);
+
+		if (ptrToItems)
+			memmove (ITEMPTR (list, firstItemPosition - 1), ptrToItems,
+					 (*list)->itemSize * numItemsToInsert);
+		else
+			memset (ITEMPTR (list, firstItemPosition - 1), 0,
+					(*list)->itemSize * numItemsToInsert);
+
+		(*list)->numItems += numItemsToInsert;
+	}
+
+	return 1;
+}
+
+#ifdef CFG_ALL_LIST_FUNCTIONS
+
+/*******************************/
+
+int ListEqual (list_t list1, list_t list2)
+{
+	if (list1 == list2)
+		return 1;
+
+	if (list1 == NULL || list2 == NULL)
+		return 0;
+
+	if ((*list1)->itemSize == (*list1)->itemSize) {
+	    if ((*list1)->numItems == (*list2)->numItems) {
+		return (memcmp (ITEMPTR (list1, 0), ITEMPTR (list2, 0),
+				(*list1)->itemSize * (*list1)->numItems) == 0);
+	    }
+	}
+
+	return 0;
+}
+
+/*******************************/
+
+/*
+ * The item pointed to by ptrToItem is copied over the current item
+ * at itemPosition
+ */
+void ListReplaceItem (list_t list, void *ptrToItem, int itemPosition)
+{
+	ListReplaceItems (list, ptrToItem, itemPosition, 1);
+}
+
+/*******************************/
+
+/*
+ * The item pointed to by ptrToItems is copied over the current item
+ * at itemPosition
+ */
+void ListReplaceItems ( list_t list, void *ptrToItems,
+			int firstItemPosition, int numItemsToReplace)
+{
+
+	if (firstItemPosition == LIST_END)
+		firstItemPosition = (*list)->numItems;
+	else if (firstItemPosition == LIST_START)
+		firstItemPosition = 1;
+
+	memmove (ITEMPTR (list, firstItemPosition - 1), ptrToItems,
+			 (*list)->itemSize * numItemsToReplace);
+}
+
+/*******************************/
+
+void ListGetItem (list_t list, void *itemDestination, int itemPosition)
+{
+	ListGetItems (list, itemDestination, itemPosition, 1);
+}
+
+#endif	/* CFG_ALL_LIST_FUNCTIONS */
+
+/*******************************/
+
+#if defined(CFG_ALL_LIST_FUNCTIONS) || defined(CFG_DEVICE_DEREGISTER)
+
+void ListRemoveItem (list_t list, void *itemDestination, int itemPosition)
+{
+	ListRemoveItems (list, itemDestination, itemPosition, 1);
+}
+
+/*******************************/
+
+void ListRemoveItems (list_t list, void *itemsDestination,
+		      int firstItemPosition, int numItemsToRemove)
+{
+	int firstItemAfterChunk, numToMove;
+
+	if (firstItemPosition == LIST_START)
+		firstItemPosition = 1;
+	else if (firstItemPosition == LIST_END)
+		firstItemPosition = (*list)->numItems;
+
+	if (itemsDestination != NULL)
+		memcpy (itemsDestination, ITEMPTR (list, firstItemPosition - 1),
+				(*list)->itemSize * numItemsToRemove);
+
+	firstItemAfterChunk = firstItemPosition + numItemsToRemove;
+	numToMove = (*list)->numItems - (firstItemAfterChunk - 1);
+
+	if (numToMove > 0) {
+		/*
+		 * move part of list down to cover hole left by removed item
+		 */
+		memmove (ITEMPTR (list, firstItemPosition - 1),
+				 ITEMPTR (list, firstItemAfterChunk - 1),
+				 (*list)->itemSize * numToMove);
+	}
+
+	(*list)->numItems -= numItemsToRemove;
+}
+#endif	/* CFG_ALL_LIST_FUNCTIONS || CFG_DEVICE_DEREGISTER */
+
+/*******************************/
+
+void ListGetItems (list_t list, void *itemsDestination,
+		   int firstItemPosition, int numItemsToGet)
+{
+
+	if (firstItemPosition == LIST_START)
+		firstItemPosition = 1;
+	else if (firstItemPosition == LIST_END)
+		firstItemPosition = (*list)->numItems;
+
+	memcpy (itemsDestination,
+		ITEMPTR (list, firstItemPosition - 1),
+		(*list)->itemSize * numItemsToGet);
+}
+
+/*******************************/
+
+/*
+ * Returns a pointer to the item at itemPosition. returns null if an
+ * errors occurred.
+ */
+void *ListGetPtrToItem (list_t list, int itemPosition)
+{
+	if (itemPosition == LIST_START)
+		itemPosition = 1;
+	else if (itemPosition == LIST_END)
+		itemPosition = (*list)->numItems;
+
+	return ITEMPTR (list, itemPosition - 1);
+}
+
+/*******************************/
+
+/*
+ * returns a pointer the lists data (abstraction violation for
+ * optimization)
+ */
+void *ListGetDataPtr (list_t list)
+{
+	return &((*list)->itemList[0]);
+}
+
+/********************************/
+
+#ifdef	CFG_ALL_LIST_FUNCTIONS
+
+int ListApplyToEach (list_t list, int ascending,
+		     ListApplicationFunc funcToApply,
+		     void *callbackData)
+{
+	int result = 0, index;
+
+	if (!list || !funcToApply)
+		goto Error;
+
+	if (ascending) {
+		for (index = 1; index <= ListNumItems (list); index++) {
+			result = funcToApply (index,
+					      ListGetPtrToItem (list, index),
+					      callbackData);
+			if (result < 0)
+				goto Error;
+		}
+	} else {
+		for (index = ListNumItems (list);
+		     index > 0 && index <= ListNumItems (list);
+		     index--) {
+			result = funcToApply (index,
+					      ListGetPtrToItem (list, index),
+					      callbackData);
+			if (result < 0)
+				goto Error;
+		}
+	}
+
+Error:
+	return result;
+}
+
+#endif /* CFG_ALL_LIST_FUNCTIONS */
+
+/********************************/
+
+int ListGetItemSize (list_t list)
+{
+	return (*list)->itemSize;
+}
+
+/********************************/
+
+int ListNumItems (list_t list)
+{
+	return (*list)->numItems;
+}
+
+/*******************************/
+
+#ifdef	CFG_ALL_LIST_FUNCTIONS
+
+void ListRemoveDuplicates (list_t list, CompareFunction compareFunction)
+{
+	int numItems, index, startIndexForFind, duplicatesIndex;
+
+	numItems = ListNumItems (list);
+
+	for (index = 1; index < numItems; index++) {
+		startIndexForFind = index + 1;
+		while (startIndexForFind <= numItems) {
+			duplicatesIndex =
+				ListFindItem (list,
+					      ListGetPtrToItem (list, index),
+					      startIndexForFind,
+					      compareFunction);
+			if (duplicatesIndex > 0) {
+				ListRemoveItem (list, NULL, duplicatesIndex);
+				numItems--;
+				startIndexForFind = duplicatesIndex;
+			} else {
+				break;
+			}
+		}
+	}
+}
+
+/*******************************/
+
+
+/*******************************/
+
+int ListFindItem (list_t list, void *ptrToItem, int startingPosition,
+		  CompareFunction compareFunction)
+{
+	int numItems, size, index, cmp;
+	void *listItemPtr;
+
+	if ((numItems = (*list)->numItems) == 0)
+		return 0;
+
+	size = (*list)->itemSize;
+
+	if (startingPosition == LIST_START)
+		startingPosition = 1;
+	else if (startingPosition == LIST_END)
+		startingPosition = numItems;
+
+	for (index = startingPosition; index <= numItems; index++) {
+		listItemPtr = ITEMPTR (list, index - 1);
+		cmp = compareFunction
+			? compareFunction (ptrToItem, listItemPtr)
+			: ListMemBlockCmp (ptrToItem, listItemPtr, size);
+		if (cmp == 0)
+			return index;
+	}
+
+	return 0;
+}
+
+/*******************************/
+
+int ShortCompare (void *a, void *b)
+{
+	if (*(short *) a < *(short *) b)
+		return -1;
+	if (*(short *) a > *(short *) b)
+		return 1;
+	return 0;
+}
+
+/*******************************/
+
+int IntCompare (void *a, void *b)
+{
+	if (*(int *) a < *(int *) b)
+		return -1;
+	if (*(int *) a > *(int *) b)
+		return 1;
+	return 0;
+}
+
+/*******************************/
+
+int CStringCompare (void *a, void *b)
+{
+	return strcmp (*(char **) a, *(char **) b);
+}
+
+/*******************************/
+
+
+int ListBinSearch (list_t list, void *ptrToItem,
+		   CompareFunction compareFunction)
+{
+	int index;
+
+	index = BinSearch (ITEMPTR (list, 0),
+			   (int) (*list)->numItems,
+			   (int) (*list)->itemSize, ptrToItem,
+			   compareFunction);
+
+	if (index >= 0)
+		index++;			/* lists start from 1 */
+	else
+		index = 0;			/* item not found */
+
+	return index;
+}
+
+/**************************************************************************/
+
+/*
+ * Reserves memory for numItems in the list. If it succeeds then
+ * numItems items can be inserted without possibility of an out of
+ * memory error (useful to simplify error recovery in complex
+ * functions). Returns 1 if success, 0 if out of memory.
+ */
+int ListPreAllocate (list_t list, int numItems)
+{
+	if ((*list)->listSize - (*list)->numItems < numItems) {
+		return ExpandListSpace (list,
+					numItems - ((*list)->listSize -
+						(*list)->numItems));
+	} else {
+		return 1;	/* enough items are already pre-allocated */
+	}
+}
+
+#endif /* CFG_ALL_LIST_FUNCTIONS */
diff --git a/common/miiphybb.c b/common/miiphybb.c
new file mode 100644
index 0000000..dfc1992
--- /dev/null
+++ b/common/miiphybb.c
@@ -0,0 +1,231 @@
+/*
+ * (C) Copyright 2001
+ * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+/*
+ * This provides a bit-banged interface to the ethernet MII management
+ * channel.
+ */
+
+#include <common.h>
+#include <ioports.h>
+#include <ppc_asm.tmpl>
+
+#ifdef CONFIG_BITBANGMII
+
+
+/*****************************************************************************
+ *
+ * Utility to send the preamble, address, and register (common to read
+ * and write).
+ */
+static void miiphy_pre(char	      read,
+		       unsigned char  addr,
+		       unsigned char  reg)
+{
+  int   j;	/* counter */
+  volatile ioport_t *iop = ioport_addr((immap_t *)CFG_IMMR, MDIO_PORT);
+
+  /*
+   * Send a 32 bit preamble ('1's) with an extra '1' bit for good measure.
+   * The IEEE spec says this is a PHY optional requirement.  The AMD
+   * 79C874 requires one after power up and one after a MII communications
+   * error.  This means that we are doing more preambles than we need,
+   * but it is safer and will be much more robust.
+   */
+
+  MDIO_ACTIVE;
+  MDIO(1);
+  for(j = 0; j < 32; j++)
+  {
+    MDC(0);
+    MIIDELAY;
+    MDC(1);
+    MIIDELAY;
+  }
+
+  /* send the start bit (01) and the read opcode (10) or write (10) */
+  MDC(0); MDIO(0); MIIDELAY; MDC(1); MIIDELAY;
+  MDC(0); MDIO(1); MIIDELAY; MDC(1); MIIDELAY;
+  MDC(0); MDIO(read);  MIIDELAY; MDC(1); MIIDELAY;
+  MDC(0); MDIO(!read); MIIDELAY; MDC(1); MIIDELAY;
+
+  /* send the PHY address */
+  for(j = 0; j < 5; j++)
+  {
+    MDC(0);
+    if((addr & 0x10) == 0)
+    {
+      MDIO(0);
+    }
+    else
+    {
+      MDIO(1);
+    }
+    MIIDELAY;
+    MDC(1);
+    MIIDELAY;
+    addr <<= 1;
+  }
+
+  /* send the register address */
+  for(j = 0; j < 5; j++)
+  {
+    MDC(0);
+    if((reg & 0x10) == 0)
+    {
+      MDIO(0);
+    }
+    else
+    {
+      MDIO(1);
+    }
+    MIIDELAY;
+    MDC(1);
+    MIIDELAY;
+    reg <<= 1;
+  }
+}
+
+
+/*****************************************************************************
+ *
+ * Read a MII PHY register.
+ *
+ * Returns:
+ *   0 on success
+ */
+int miiphy_read(unsigned char  addr,
+		unsigned char  reg,
+		unsigned short *value)
+{
+  short rdreg;	/* register working value */
+  int   j;	/* counter */
+  volatile ioport_t *iop = ioport_addr((immap_t *)CFG_IMMR, MDIO_PORT);
+
+  miiphy_pre(1, addr, reg);
+
+  /* tri-state our MDIO I/O pin so we can read */
+  MDC(0);
+  MDIO_TRISTATE;
+  MIIDELAY;
+  MDC(1);
+  MIIDELAY;
+
+  /* check the turnaround bit: the PHY should be driving it to zero */
+  if(MDIO_READ != 0)
+  {
+    /* printf("PHY didn't drive TA low\n"); */
+    for(j = 0; j < 32; j++)
+    {
+      MDC(0);
+      MIIDELAY;
+      MDC(1);
+      MIIDELAY;
+    }
+    return(-1);
+  }
+
+  MDC(0);
+  MIIDELAY;
+
+  /* read 16 bits of register data, MSB first */
+  rdreg = 0;
+  for(j = 0; j < 16; j++)
+  {
+    MDC(1);
+    MIIDELAY;
+    rdreg <<= 1;
+    rdreg |= MDIO_READ;
+    MDC(0);
+    MIIDELAY;
+  }
+
+  MDC(1);
+  MIIDELAY;
+  MDC(0);
+  MIIDELAY;
+  MDC(1);
+  MIIDELAY;
+
+  *value = rdreg;
+
+#ifdef DEBUG
+  printf ("miiphy_read(0x%x) @ 0x%x = 0x%04x\n", reg, addr, *value);
+#endif
+
+  return 0;
+}
+
+
+/*****************************************************************************
+ *
+ * Write a MII PHY register.
+ *
+ * Returns:
+ *   0 on success
+ */
+int miiphy_write(unsigned char  addr,
+		 unsigned char  reg,
+		 unsigned short value)
+{
+  int   j;	/* counter */
+  volatile ioport_t *iop = ioport_addr((immap_t *)CFG_IMMR, MDIO_PORT);
+
+  miiphy_pre(0, addr, reg);
+
+  /* send the turnaround (10) */
+  MDC(0); MDIO(1); MIIDELAY; MDC(1); MIIDELAY;
+  MDC(0); MDIO(0); MIIDELAY; MDC(1); MIIDELAY;
+
+  /* write 16 bits of register data, MSB first */
+  for(j = 0; j < 16; j++)
+  {
+    MDC(0);
+    if((value & 0x00008000) == 0)
+    {
+      MDIO(0);
+    }
+    else
+    {
+      MDIO(1);
+    }
+    MIIDELAY;
+    MDC(1);
+    MIIDELAY;
+    value <<= 1;
+  }
+
+  /*
+   * Tri-state the MDIO line.
+   */
+  MDIO_TRISTATE;
+  MDC(0);
+  MIIDELAY;
+  MDC(1);
+  MIIDELAY;
+
+  return 0;
+}
+
+#endif /* CONFIG_BITBANGMII */
+
diff --git a/common/s_record.c b/common/s_record.c
new file mode 100644
index 0000000..c52bf1b
--- /dev/null
+++ b/common/s_record.c
@@ -0,0 +1,195 @@
+/*
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <s_record.h>
+
+static int hex1_bin (char  c);
+static int hex2_bin (char *s);
+
+int srec_decode (char *input, int *count, ulong *addr, char *data)
+{
+	int	i;
+	int	v;				/* conversion buffer	*/
+	int	srec_type;			/* S-Record type	*/
+	unsigned char chksum;			/* buffer for checksum	*/
+
+	chksum = 0;
+
+	/* skip anything before 'S', and the 'S' itself.
+	 * Return error if not found
+	 */
+
+	for (; *input; ++input) {
+		if (*input == 'S') {		/* skip 'S' */
+			++input;
+			break;
+		}
+	}
+	if (*input == '\0') {			/* no more data?	*/
+		return (SREC_EMPTY);
+	}
+
+	v = *input++;				/* record type		*/
+
+	if ((*count = hex2_bin(input)) < 0) {
+		return (SREC_E_NOSREC);
+	}
+
+	chksum += *count;
+	input  += 2;
+
+	switch (v) {				/* record type		*/
+
+	case '0':				/* start record		*/
+		srec_type = SREC_START;		/* 2 byte addr field	*/
+		*count   -= 3;			/* - checksum and addr	*/
+		break;
+	case '1':
+		srec_type = SREC_DATA2;		/* 2 byte addr field	*/
+		*count   -= 3;			/* - checksum and addr	*/
+		break;
+	case '2':
+		srec_type = SREC_DATA3;		/* 3 byte addr field	*/
+		*count   -= 4;			/* - checksum and addr	*/
+		break;
+	case '3':				/* data record with a	*/
+		srec_type = SREC_DATA4;		/* 4 byte addr field	*/
+		*count   -= 5;			/* - checksum and addr	*/
+		break;
+/***	case '4'  ***/
+	case '5':			/* count record, addr field contains */
+		srec_type = SREC_COUNT;	/* a 2 byte record counter	*/
+		*count    = 0;			/* no data		*/
+		break;
+/***	case '6' -- not used  ***/
+	case '7':				/* end record with a	*/
+		srec_type = SREC_END4;		/* 4 byte addr field	*/
+		*count   -= 5;			/* - checksum and addr	*/
+		break;
+	case '8':				/* end record with a	*/
+		srec_type = SREC_END3;		/* 3 byte addr field	*/
+		*count   -= 4;			/* - checksum and addr	*/
+		break;
+	case '9':				/* end record with a	*/
+		srec_type = SREC_END2;		/* 2 byte addr field	*/
+		*count   -= 3;			/* - checksum and addr	*/
+		break;
+	default:
+		return (SREC_E_BADTYPE);
+	}
+
+	/* read address field */
+	*addr = 0;
+
+	switch (v) {
+	case '3':				/* 4 byte addr field	*/
+	case '7':
+		if ((v = hex2_bin(input)) < 0) {
+			return (SREC_E_NOSREC);
+		}
+		*addr  += v;
+		chksum += v;
+		input  += 2;
+		/* FALL THRU */
+	case '2':				/* 3 byte addr field	*/
+	case '8':
+		if ((v = hex2_bin(input)) < 0) {
+			return (SREC_E_NOSREC);
+		}
+		*addr <<= 8;
+		*addr  += v;
+		chksum += v;
+		input  += 2;
+		/* FALL THRU */
+	case '0':				/* 2 byte addr field	*/
+	case '1':
+	case '5':
+	case '9':
+		if ((v = hex2_bin(input)) < 0) {
+			return (SREC_E_NOSREC);
+		}
+		*addr <<= 8;
+		*addr  += v;
+		chksum += v;
+		input  += 2;
+
+		if ((v = hex2_bin(input)) < 0) {
+			return (SREC_E_NOSREC);
+		}
+		*addr <<= 8;
+		*addr  += v;
+		chksum += v;
+		input  += 2;
+
+		break;
+	default:
+		return (SREC_E_BADTYPE);
+	}
+
+	/* convert data and calculate checksum */
+	for (i=0; i < *count; ++i) {
+		if ((v = hex2_bin(input)) < 0) {
+			return (SREC_E_NOSREC);
+		}
+		data[i] = v;
+		chksum += v;
+		input  += 2;
+	}
+
+	/* read anc check checksum */
+	if ((v = hex2_bin(input)) < 0) {
+		return (SREC_E_NOSREC);
+	}
+
+	if ((unsigned char)v != (unsigned char)~chksum) {
+		return (SREC_E_BADCHKS);
+	}
+
+	return (srec_type);
+}
+
+static int hex1_bin (char c)
+{
+	if (c >= '0' && c <= '9')
+		return (c - '0');
+	if (c >= 'a' && c <= 'f')
+		return (c + 10 - 'a');
+	if (c >= 'A' && c <= 'F')
+		return (c + 10 - 'A');
+	return (-1);
+}
+
+static int hex2_bin (char *s)
+{
+	int i, j;
+
+	if ((i = hex1_bin(*s++)) < 0) {
+		return (-1);
+	}
+	if ((j = hex1_bin(*s)) < 0) {
+		return (-1);
+	}
+
+	return ((i<<4) + j);
+}
diff --git a/common/usb.c b/common/usb.c
new file mode 100644
index 0000000..a5b29a5
--- /dev/null
+++ b/common/usb.c
@@ -0,0 +1,1066 @@
+/*
+ * (C) Copyright 2001
+ * Denis Peter, MPL AG Switzerland
+ *
+ * Most of this source has been derived from the Linux USB
+ * project.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ */
+
+
+/*
+ * How it works:
+ *
+ * Since this is a bootloader, the devices will not be automatic
+ * (re)configured on hotplug, but after a restart of the USB the
+ * device should work.
+ *
+ * For each transfer (except "Interrupt") we wait for completion.
+ */
+#include <common.h>
+#include <command.h>
+#include <asm/processor.h>
+
+#if (CONFIG_COMMANDS & CFG_CMD_USB)
+
+#include <usb.h>
+#ifdef CONFIG_4xx
+#include <405gp_pci.h>
+#endif
+
+
+
+#undef USB_DEBUG
+
+#ifdef	USB_DEBUG
+#define	USB_PRINTF(fmt,args...)	printf (fmt ,##args)
+#else
+#define USB_PRINTF(fmt,args...)
+#endif
+
+static struct usb_device usb_dev[USB_MAX_DEVICE];
+static int dev_index;
+static int running;
+static int asynch_allowed;
+static struct devrequest setup_packet;
+
+/**********************************************************************
+ * some forward declerations...
+ */
+void usb_scan_devices(void);
+
+int usb_hub_probe(struct usb_device *dev, int ifnum);
+void usb_hub_reset(void);
+
+/***********************************************************************
+ * wait_ms
+ */
+
+void __inline__ wait_ms(unsigned long ms)
+{
+	while(ms-->0)
+		udelay(1000);
+}
+/***************************************************************************
+ * Init USB Device
+ */
+
+int usb_init(void)
+{
+	int result;
+
+	running=0;
+	dev_index=0;
+	asynch_allowed=1;
+	usb_hub_reset();
+	/* init low_level USB */
+	printf("USB:   ");
+	result = usb_lowlevel_init();
+	/* if lowlevel init is OK, scan the bus for devices i.e. search HUBs and configure them */
+	if(result==0) {
+		printf("scanning bus for devices... ");
+		running=1;
+		usb_scan_devices();
+		return 0;
+	}
+	else {
+		printf("Error, couldn't init Lowlevel part\n");
+		return -1;
+	}
+}
+
+/******************************************************************************
+ * Stop USB this stops the LowLevel Part and deregisters USB devices.
+ */
+int usb_stop(void)
+{
+	asynch_allowed=1;
+	usb_hub_reset();
+	return usb_lowlevel_stop();
+}
+
+/*
+ * disables the asynch behaviour of the control message. This is used for data
+ * transfers that uses the exclusiv access to the control and bulk messages.
+ */
+void usb_disable_asynch(int disable)
+{
+	asynch_allowed=!disable;
+}
+
+
+/*-------------------------------------------------------------------
+ * Message wrappers.
+ *
+ */
+
+/*
+ * submits an Interrupt Message
+ */
+int usb_submit_int_msg(struct usb_device *dev, unsigned long pipe,
+			void *buffer,int transfer_len, int interval)
+{
+	return submit_int_msg(dev,pipe,buffer,transfer_len,interval);
+}
+
+/*
+ * submits a control message and waits for comletion (at least timeout * 1ms)
+ * If timeout is 0, we don't wait for completion (used as example to set and
+ * clear keyboards LEDs). For data transfers, (storage transfers) we don't
+ * allow control messages with 0 timeout, by previousely resetting the flag
+ * asynch_allowed (usb_disable_asynch(1)).
+ * returns the transfered length if OK or -1 if error. The transfered length
+ * and the current status are stored in the dev->act_len and dev->status.
+ */
+int usb_control_msg(struct usb_device *dev, unsigned int pipe,
+			unsigned char request, unsigned char requesttype,
+			unsigned short value, unsigned short index,
+			void *data, unsigned short size, int timeout)
+{
+	if((timeout==0)&&(!asynch_allowed)) /* request for a asynch control pipe is not allowed */
+		return -1;
+	/* set setup command */
+	setup_packet.requesttype = requesttype;
+	setup_packet.request = request;
+	setup_packet.value = swap_16(value);
+	setup_packet.index = swap_16(index);
+	setup_packet.length = swap_16(size);
+ 	USB_PRINTF("usb_control_msg: request: 0x%X, requesttype: 0x%X\nvalue 0x%X index 0x%X length 0x%X\n",
+		request,requesttype,value,index,size);
+	dev->status=USB_ST_NOT_PROC; /*not yet processed */
+
+	submit_control_msg(dev,pipe,data,size,&setup_packet);
+	if(timeout==0) {
+		return (int)size;
+	}
+	while(timeout--) {
+		if(!((volatile unsigned long)dev->status & USB_ST_NOT_PROC))
+			break;
+		wait_ms(1);
+	}
+	if(dev->status==0)
+		return dev->act_len;
+	else {
+		return -1;
+	}
+}
+
+/*-------------------------------------------------------------------
+ * submits bulk message, and waits for completion. returns 0 if Ok or
+ * -1 if Error.
+ * synchronous behavior
+ */
+int usb_bulk_msg(struct usb_device *dev, unsigned int pipe,
+			void *data, int len, int *actual_length, int timeout)
+{
+	if (len < 0)
+		return -1;
+	dev->status=USB_ST_NOT_PROC; /*not yet processed */
+	submit_bulk_msg(dev,pipe,data,len);
+	while(timeout--) {
+		if(!((volatile unsigned long)dev->status & USB_ST_NOT_PROC))
+			break;
+		wait_ms(1);
+	}
+	*actual_length=dev->act_len;
+	if(dev->status==0)
+		return 0;
+	else
+		return -1;
+}
+
+
+/*-------------------------------------------------------------------
+ * Max Packet stuff
+ */
+
+/*
+ * returns the max packet size, depending on the pipe direction and
+ * the configurations values
+ */
+int usb_maxpacket(struct usb_device *dev,unsigned long pipe)
+{
+	if((pipe & USB_DIR_IN)==0) /* direction is out -> use emaxpacket out */
+		return(dev->epmaxpacketout[((pipe>>15) & 0xf)]);
+	else
+		return(dev->epmaxpacketin[((pipe>>15) & 0xf)]);
+}
+
+/*
+ * set the max packed value of all endpoints in the given configuration
+ */
+int usb_set_maxpacket(struct usb_device *dev)
+{
+	int i,ii,b;
+	struct usb_endpoint_descriptor *ep;
+
+	for(i=0; i<dev->config.bNumInterfaces;i++) {
+		for(ii=0; ii<dev->config.if_desc[i].bNumEndpoints; ii++) {
+			ep=&dev->config.if_desc[i].ep_desc[ii];
+			b=ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+
+			if((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)==USB_ENDPOINT_XFER_CONTROL) {	/* Control => bidirectional */
+				dev->epmaxpacketout[b] = ep->wMaxPacketSize;
+				dev->epmaxpacketin [b] = ep->wMaxPacketSize;
+				USB_PRINTF("##Control EP epmaxpacketout/in[%d] = %d\n",b,dev->epmaxpacketin[b]);
+			}
+			else {
+				if ((ep->bEndpointAddress & 0x80)==0) { /* OUT Endpoint */
+					if(ep->wMaxPacketSize > dev->epmaxpacketout[b]) {
+						dev->epmaxpacketout[b] = ep->wMaxPacketSize;
+						USB_PRINTF("##EP epmaxpacketout[%d] = %d\n",b,dev->epmaxpacketout[b]);
+					}
+				}
+				else  { /* IN Endpoint */
+					if(ep->wMaxPacketSize > dev->epmaxpacketin[b]) {
+						dev->epmaxpacketin[b] = ep->wMaxPacketSize;
+						USB_PRINTF("##EP epmaxpacketin[%d] = %d\n",b,dev->epmaxpacketin[b]);
+					}
+				} /* if out */
+			} /* if control */
+		} /* for each endpoint */
+	}
+	return 0;
+}
+
+/*******************************************************************************
+ * Parse the config, located in buffer, and fills the dev->config structure.
+ * Note that all little/big endian swapping are done automatically.
+ */
+int usb_parse_config(struct usb_device *dev, unsigned char *buffer, int cfgno)
+{
+	struct usb_descriptor_header *head;
+	int index,ifno,epno;
+	ifno=-1;
+	epno=-1;
+
+	dev->configno=cfgno;
+	head =(struct usb_descriptor_header *)&buffer[0];
+	if(head->bDescriptorType!=USB_DT_CONFIG) {
+		printf(" ERROR: NOT USB_CONFIG_DESC %x\n",head->bDescriptorType);
+		return -1;
+	}
+	memcpy(&dev->config,buffer,buffer[0]);
+	dev->config.wTotalLength=swap_16(dev->config.wTotalLength);
+	dev->config.no_of_if=0;
+
+	index=dev->config.bLength;
+	/* Ok the first entry must be a configuration entry, now process the others */
+	head=(struct usb_descriptor_header *)&buffer[index];
+	while(index+1 < dev->config.wTotalLength) {
+		switch(head->bDescriptorType) {
+			case USB_DT_INTERFACE:
+				ifno=dev->config.no_of_if;
+				dev->config.no_of_if++; /* found an interface desc, increase numbers */
+				memcpy(&dev->config.if_desc[ifno],&buffer[index],buffer[index]); /* copy new desc */
+				dev->config.if_desc[ifno].no_of_ep=0;
+
+				break;
+			case USB_DT_ENDPOINT:
+				epno=dev->config.if_desc[ifno].no_of_ep;
+				dev->config.if_desc[ifno].no_of_ep++; /* found an endpoint */
+				memcpy(&dev->config.if_desc[ifno].ep_desc[epno],&buffer[index],buffer[index]);
+				dev->config.if_desc[ifno].ep_desc[epno].wMaxPacketSize
+					=swap_16(dev->config.if_desc[ifno].ep_desc[epno].wMaxPacketSize);
+				USB_PRINTF("if %d, ep %d\n",ifno,epno);
+				break;
+			default:
+				if(head->bLength==0)
+					return 1;
+				USB_PRINTF("unknown Description Type : %x\n",head->bDescriptorType);
+				{
+					int i;
+					unsigned char *ch;
+					ch=(unsigned char *)head;
+					for(i=0;i<head->bLength; i++)
+						USB_PRINTF("%02X ",*ch++);
+					USB_PRINTF("\n\n\n");
+				}
+				break;
+		}
+		index+=head->bLength;
+		head=(struct usb_descriptor_header *)&buffer[index];
+	}
+	return 1;
+}
+
+/***********************************************************************
+ * Clears an endpoint
+ * endp: endpoint number in bits 0-3;
+ * direction flag in bit 7 (1 = IN, 0 = OUT)
+ */
+int usb_clear_halt(struct usb_device *dev, int pipe)
+{
+	int result;
+	unsigned short status;
+	int endp=usb_pipeendpoint(pipe)|(usb_pipein(pipe)<<7);
+
+	result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+		USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, 0, endp, NULL, 0, USB_CNTL_TIMEOUT * 3);
+
+	/* don't clear if failed */
+	if (result < 0)
+		return result;
+	result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+		USB_REQ_GET_STATUS, USB_DIR_IN | USB_RECIP_ENDPOINT, 0, endp,
+		&status, sizeof(status), USB_CNTL_TIMEOUT * 3);
+	if (result < 0)
+		return result;
+	USB_PRINTF("usb_clear_halt: status 0x%x\n",status);
+	if (status & 1)
+		return -1;		/* still halted */
+	usb_endpoint_running(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
+	/* toggle is reset on clear */
+	usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 0);
+	return 0;
+}
+
+
+/**********************************************************************
+ * get_descriptor type
+ */
+int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size)
+{
+	int res;
+ 	res = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+			USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
+			(type << 8) + index, 0,
+			buf, size, USB_CNTL_TIMEOUT);
+	return res;
+}
+
+/**********************************************************************
+ * gets configuration cfgno and store it in the buffer
+ */
+int usb_get_configuration_no(struct usb_device *dev,unsigned char *buffer,int cfgno)
+{
+ 	int result;
+	unsigned int tmp;
+	struct usb_config_descriptor *config;
+
+
+	config=(struct usb_config_descriptor *)&buffer[0];
+	result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, 8);
+	if (result < 8) {
+		if (result < 0)
+			printf("unable to get descriptor, error %lX\n",dev->status);
+		else
+			printf("config descriptor too short (expected %i, got %i)\n",8,result);
+		return -1;
+	}
+	tmp=swap_16(config->wTotalLength);
+
+	result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, tmp);
+	USB_PRINTF("get_conf_no %d Result %d, wLength %d\n",cfgno,result,tmp);
+	return result;
+}
+
+/********************************************************************
+ * set address of a device to the value in dev->devnum.
+ * This can only be done by addressing the device via the default address (0)
+ */
+int usb_set_address(struct usb_device *dev)
+{
+	int res;
+
+	USB_PRINTF("set address %d\n",dev->devnum);
+	res=usb_control_msg(dev, usb_snddefctrl(dev),
+		USB_REQ_SET_ADDRESS, 0,
+		(dev->devnum),0,
+		NULL,0, USB_CNTL_TIMEOUT);
+	return res;
+}
+
+/********************************************************************
+ * set interface number to interface
+ */
+int usb_set_interface(struct usb_device *dev, int interface, int alternate)
+{
+	struct usb_interface_descriptor *if_face = NULL;
+	int ret, i;
+
+	for (i=0; i<dev->config.bNumInterfaces; i++) {
+		if (dev->config.if_desc[i].bInterfaceNumber == interface) {
+			if_face = &dev->config.if_desc[i];
+			break;
+		}
+	}
+	if (!if_face) {
+		printf("selecting invalid interface %d", interface);
+		return -1;
+	}
+
+	if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+	    USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE, alternate,
+	    interface, NULL, 0, USB_CNTL_TIMEOUT * 5)) < 0)
+		return ret;
+
+	if_face->act_altsetting = (unsigned char)alternate;
+	usb_set_maxpacket(dev);
+	return 0;
+}
+
+/********************************************************************
+ * set configuration number to configuration
+ */
+int usb_set_configuration(struct usb_device *dev, int configuration)
+{
+	int res;
+	USB_PRINTF("set configuration %d\n",configuration);
+	/* set setup command */
+	res=usb_control_msg(dev, usb_sndctrlpipe(dev,0),
+		USB_REQ_SET_CONFIGURATION, 0,
+		configuration,0,
+		NULL,0, USB_CNTL_TIMEOUT);
+	if(res==0) {
+		dev->toggle[0] = 0;
+		dev->toggle[1] = 0;
+		return 0;
+	}
+	else
+		return -1;
+}
+
+/********************************************************************
+ * set protocol to protocol
+ */
+int usb_set_protocol(struct usb_device *dev, int ifnum, int protocol)
+{
+	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+		USB_REQ_SET_PROTOCOL, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+		protocol, ifnum, NULL, 0, USB_CNTL_TIMEOUT);
+}
+
+/********************************************************************
+ * set idle
+ */
+int usb_set_idle(struct usb_device *dev, int ifnum, int duration, int report_id)
+{
+	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+		USB_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+		(duration << 8) | report_id, ifnum, NULL, 0, USB_CNTL_TIMEOUT);
+}
+
+/********************************************************************
+ * get report
+ */
+int usb_get_report(struct usb_device *dev, int ifnum, unsigned char type, unsigned char id, void *buf, int size)
+{
+	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+		USB_REQ_GET_REPORT, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+		(type << 8) + id, ifnum, buf, size, USB_CNTL_TIMEOUT);
+}
+
+/********************************************************************
+ * get class descriptor
+ */
+int usb_get_class_descriptor(struct usb_device *dev, int ifnum,
+		unsigned char type, unsigned char id, void *buf, int size)
+{
+	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+		USB_REQ_GET_DESCRIPTOR, USB_RECIP_INTERFACE | USB_DIR_IN,
+		(type << 8) + id, ifnum, buf, size, USB_CNTL_TIMEOUT);
+}
+
+/********************************************************************
+ * get string index in buffer
+ */
+int usb_get_string(struct usb_device *dev, unsigned short langid, unsigned char index, void *buf, int size)
+{
+	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+		USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
+		(USB_DT_STRING << 8) + index, langid, buf, size, USB_CNTL_TIMEOUT);
+}
+
+/********************************************************************
+ * usb_string:
+ * Get string index and translate it to ascii.
+ * returns string length (> 0) or error (< 0)
+ */
+int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
+{
+
+	unsigned char mybuf[256];
+	unsigned char *tbuf;
+	int err;
+	unsigned int u, idx;
+
+	if (size <= 0 || !buf || !index)
+		return -1;
+	buf[0] = 0;
+	tbuf=&mybuf[0];
+
+	/* get langid for strings if it's not yet known */
+	if (!dev->have_langid) {
+		err = usb_get_string(dev, 0, 0, tbuf, 4);
+		if (err < 0) {
+			USB_PRINTF("error getting string descriptor 0 (error=%x)\n",dev->status);
+			return -1;
+		} else if (tbuf[0] < 4) {
+			USB_PRINTF("string descriptor 0 too short\n");
+			return -1;
+		} else {
+			dev->have_langid = -1;
+			dev->string_langid = tbuf[2] | (tbuf[3]<< 8);
+				/* always use the first langid listed */
+			USB_PRINTF("USB device number %d default language ID 0x%x\n",
+				dev->devnum, dev->string_langid);
+		}
+	}
+	/* Just ask for a maximum length string and then take the length
+	 * that was returned. */
+	err = usb_get_string(dev, dev->string_langid, index, tbuf, 4);
+	if (err < 0)
+		return err;
+	u=tbuf[0];
+	USB_PRINTF("Strn Len %d, index %d\n",u,index);
+	err = usb_get_string(dev, dev->string_langid, index, tbuf, u);
+	if (err < 0)
+		return err;
+	size--;		/* leave room for trailing NULL char in output buffer */
+	for (idx = 0, u = 2; u < err; u += 2) {
+		if (idx >= size)
+			break;
+		if (tbuf[u+1])			/* high byte */
+			buf[idx++] = '?';  /* non-ASCII character */
+		else
+			buf[idx++] = tbuf[u];
+	}
+	buf[idx] = 0;
+	err = idx;
+	return err;
+}
+
+
+/********************************************************************
+ * USB device handling:
+ * the USB device are static allocated [USB_MAX_DEVICE].
+ */
+
+
+/* returns a pointer to the device with the index [index].
+ * if the device is not assigned (dev->devnum==-1) returns NULL
+ */
+struct usb_device * usb_get_dev_index(int index)
+{
+	if(usb_dev[index].devnum==-1)
+		return NULL;
+	else
+		return &usb_dev[index];
+}
+
+
+/* returns a pointer of a new device structure or NULL, if
+ * no device struct is available
+ */
+struct usb_device * usb_alloc_new_device(void)
+{
+	int i;
+	USB_PRINTF("New Device %d\n",dev_index);
+	if(dev_index==USB_MAX_DEVICE) {
+		printf("ERROR, to many USB Devices max=%d\n",USB_MAX_DEVICE);
+		return NULL;
+	}
+	usb_dev[dev_index].devnum=dev_index+1; /* default Address is 0, real addresses start with 1 */
+	usb_dev[dev_index].maxchild=0;
+	for(i=0;i<USB_MAXCHILDREN;i++)
+		usb_dev[dev_index].children[i]=NULL;
+	usb_dev[dev_index].parent=NULL;
+	dev_index++;
+	return &usb_dev[dev_index-1];
+}
+
+
+/*
+ * By the time we get here, the device has gotten a new device ID
+ * and is in the default state. We need to identify the thing and
+ * get the ball rolling..
+ *
+ * Returns 0 for success, != 0 for error.
+ */
+int usb_new_device(struct usb_device *dev)
+{
+	int addr, err;
+	int tmp;
+	unsigned char tmpbuf[256];
+
+	dev->descriptor.bMaxPacketSize0 = 8;  /* Start off at 8 bytes  */
+	dev->maxpacketsize = 0;		/* Default to 8 byte max packet size */
+	dev->epmaxpacketin [0] = 8;
+	dev->epmaxpacketout[0] = 8;
+
+	/* We still haven't set the Address yet */
+	addr = dev->devnum;
+	dev->devnum = 0;
+	err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8);
+	if (err < 8) {
+		printf("\n      USB device not responding, giving up (status=%lX)\n",dev->status);
+		return 1;
+	}
+	dev->epmaxpacketin [0] = dev->descriptor.bMaxPacketSize0;
+	dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0;
+	switch (dev->descriptor.bMaxPacketSize0) {
+		case 8: dev->maxpacketsize = 0; break;
+		case 16: dev->maxpacketsize = 1; break;
+		case 32: dev->maxpacketsize = 2; break;
+		case 64: dev->maxpacketsize = 3; break;
+	}
+	dev->devnum = addr;
+
+	err = usb_set_address(dev); /* set address */
+
+	if (err < 0) {
+		printf("\n      USB device not accepting new address (error=%lX)\n", dev->status);
+		return 1;
+	}
+
+	wait_ms(10);	/* Let the SET_ADDRESS settle */
+
+	tmp = sizeof(dev->descriptor);
+
+	err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, sizeof(dev->descriptor));
+	if (err < tmp) {
+		if (err < 0)
+			printf("unable to get device descriptor (error=%d)\n",err);
+		else
+			printf("USB device descriptor short read (expected %i, got %i)\n",tmp,err);
+		return 1;
+	}
+	/* correct le values */
+	dev->descriptor.bcdUSB=swap_16(dev->descriptor.bcdUSB);
+	dev->descriptor.idVendor=swap_16(dev->descriptor.idVendor);
+	dev->descriptor.idProduct=swap_16(dev->descriptor.idProduct);
+	dev->descriptor.bcdDevice=swap_16(dev->descriptor.bcdDevice);
+	/* only support for one config for now */
+	usb_get_configuration_no(dev,&tmpbuf[0],0);
+	usb_parse_config(dev,&tmpbuf[0],0);
+	usb_set_maxpacket(dev);
+	/* we set the default configuration here */
+	if (usb_set_configuration(dev, dev->config.bConfigurationValue)) {
+		printf("failed to set default configuration len %d, status %lX\n",dev->act_len,dev->status);
+		return -1;
+	}
+	USB_PRINTF("new device strings: Mfr=%d, Product=%d, SerialNumber=%d\n",
+		dev->descriptor.iManufacturer, dev->descriptor.iProduct, dev->descriptor.iSerialNumber);
+	memset(dev->mf, 0, sizeof(dev->mf));
+	memset(dev->prod, 0, sizeof(dev->prod));
+	memset(dev->serial, 0, sizeof(dev->serial));
+	if (dev->descriptor.iManufacturer)
+		usb_string(dev, dev->descriptor.iManufacturer, dev->mf, sizeof(dev->mf));
+	if (dev->descriptor.iProduct)
+		usb_string(dev, dev->descriptor.iProduct, dev->prod, sizeof(dev->prod));
+	if (dev->descriptor.iSerialNumber)
+		usb_string(dev, dev->descriptor.iSerialNumber, dev->serial, sizeof(dev->serial));
+	USB_PRINTF("Manufacturer %s\n", dev->mf);
+	USB_PRINTF("Product      %s\n", dev->prod);
+	USB_PRINTF("SerialNumber %s\n", dev->serial);
+	/* now prode if the device is a hub */
+	usb_hub_probe(dev,0);
+	return 0;
+}
+
+/* build device Tree  */
+void usb_scan_devices(void)
+{
+	int i;
+	struct usb_device *dev;
+
+	/* first make all devices unknown */
+	for(i=0;i<USB_MAX_DEVICE;i++) {
+		memset(&usb_dev[i],0,sizeof(struct usb_device));
+		usb_dev[i].devnum=-1;
+	}
+	dev_index=0;
+	/* device 0 is always present (root hub, so let it analyze) */
+	dev=usb_alloc_new_device();
+	usb_new_device(dev);
+	printf("%d USB Devices found\n",dev_index);
+	/* insert "driver" if possible */
+#ifdef CONFIG_USB_KEYBOARD
+	drv_usb_kbd_init();
+	USB_PRINTF("scan end\n");
+#endif
+}
+
+
+/****************************************************************************
+ * HUB "Driver"
+ * Probes device for being a hub and configurate it
+ */
+
+#undef	USB_HUB_DEBUG
+
+#ifdef	USB_HUB_DEBUG
+#define	USB_HUB_PRINTF(fmt,args...)	printf (fmt ,##args)
+#else
+#define USB_HUB_PRINTF(fmt,args...)
+#endif
+
+
+static struct usb_hub_device hub_dev[USB_MAX_HUB];
+static int usb_hub_index;
+
+
+int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size)
+{
+	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+		USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB,
+		USB_DT_HUB << 8, 0, data, size, USB_CNTL_TIMEOUT);
+}
+
+int usb_clear_hub_feature(struct usb_device *dev, int feature)
+{
+	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+		USB_REQ_CLEAR_FEATURE, USB_RT_HUB, feature, 0, NULL, 0, USB_CNTL_TIMEOUT);
+}
+
+int usb_clear_port_feature(struct usb_device *dev, int port, int feature)
+{
+	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+		USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port, NULL, 0, USB_CNTL_TIMEOUT);
+}
+
+int usb_set_port_feature(struct usb_device *dev, int port, int feature)
+{
+	return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+		USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port, NULL, 0, USB_CNTL_TIMEOUT);
+}
+
+int usb_get_hub_status(struct usb_device *dev, void *data)
+{
+	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+			USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0,
+			data, sizeof(struct usb_hub_status), USB_CNTL_TIMEOUT);
+}
+
+int usb_get_port_status(struct usb_device *dev, int port, void *data)
+{
+	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+			USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port,
+			data, sizeof(struct usb_hub_status), USB_CNTL_TIMEOUT);
+}
+
+
+static void usb_hub_power_on(struct usb_hub_device *hub)
+{
+	int i;
+	struct usb_device *dev;
+
+	dev=hub->pusb_dev;
+	/* Enable power to the ports */
+	USB_HUB_PRINTF("enabling power on all ports\n");
+	for (i = 0; i < dev->maxchild; i++) {
+		usb_set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER);
+		USB_HUB_PRINTF("port %d returns %lX\n",i+1,dev->status);
+		wait_ms(hub->desc.bPwrOn2PwrGood * 2);
+	}
+}
+
+void usb_hub_reset(void)
+{
+	usb_hub_index=0;
+}
+
+struct usb_hub_device *usb_hub_allocate(void)
+{
+	if(usb_hub_index<USB_MAX_HUB) {
+		return &hub_dev[usb_hub_index++];
+	}
+	printf("ERROR: USB_MAX_HUB (%d) reached\n",USB_MAX_HUB);
+	return NULL;
+}
+
+#define MAX_TRIES 5
+
+void usb_hub_port_connect_change(struct usb_device *dev, int port)
+{
+	struct usb_device *usb;
+	struct usb_port_status portsts;
+	unsigned short portstatus, portchange;
+	int tries;
+
+	/* Check status */
+	if (usb_get_port_status(dev, port + 1, &portsts)<0) {
+		USB_HUB_PRINTF("get_port_status failed\n");
+		return;
+	}
+
+	portstatus = swap_16(portsts.wPortStatus);
+	portchange = swap_16(portsts.wPortChange);
+	USB_HUB_PRINTF("portstatus %x, change %x, %s\n", portstatus, portchange,
+		portstatus&(1<<USB_PORT_FEAT_LOWSPEED) ? "Low Speed" : "High Speed");
+
+	/* Clear the connection change status */
+	usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_CONNECTION);
+
+	/* Disconnect any existing devices under this port */
+	if (((!(portstatus & USB_PORT_STAT_CONNECTION)) &&
+	     (!(portstatus & USB_PORT_STAT_ENABLE)))|| (dev->children[port])) {
+		USB_HUB_PRINTF("usb_disconnect(&hub->children[port]);\n");
+		/* Return now if nothing is connected */
+		if (!(portstatus & USB_PORT_STAT_CONNECTION))
+			return;
+	}
+	wait_ms(200);
+
+	/* Reset the port */
+
+	for(tries=0;tries<MAX_TRIES;tries++) {
+
+		usb_set_port_feature(dev, port + 1, USB_PORT_FEAT_RESET);
+		wait_ms(200);
+
+		if (usb_get_port_status(dev, port + 1, &portsts)<0) {
+			USB_HUB_PRINTF("get_port_status failed status %lX\n",dev->status);
+			return;
+		}
+		portstatus = swap_16(portsts.wPortStatus);
+		portchange = swap_16(portsts.wPortChange);
+		USB_HUB_PRINTF("portstatus %x, change %x, %s\n", portstatus ,portchange,
+			portstatus&(1<<USB_PORT_FEAT_LOWSPEED) ? "Low Speed" : "High Speed");
+		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);
+		if ((portchange & USB_PORT_STAT_C_CONNECTION) ||
+		    !(portstatus & USB_PORT_STAT_CONNECTION))
+			return;
+
+		if (portstatus & USB_PORT_STAT_ENABLE)
+			break;
+
+		wait_ms(200);
+	}
+
+	if (tries==MAX_TRIES) {
+		USB_HUB_PRINTF("Cannot enable port %i after %i retries, disabling port.\n", port+1, MAX_TRIES);
+		USB_HUB_PRINTF("Maybe the USB cable is bad?\n");
+		return;
+	}
+
+	usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_RESET);
+	wait_ms(200);
+
+	/* Allocate a new device struct for it */
+	usb=usb_alloc_new_device();
+	usb->slow = (portstatus & USB_PORT_STAT_LOW_SPEED) ? 1 : 0;
+
+	dev->children[port] = usb;
+	usb->parent=dev;
+	/* Run it through the hoops (find a driver, etc) */
+	if (usb_new_device(usb)) {
+		/* Woops, disable the port */
+		USB_HUB_PRINTF("hub: disabling port %d\n", port + 1);
+		usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_ENABLE);
+	}
+}
+
+
+int usb_hub_configure(struct usb_device *dev)
+{
+	unsigned char buffer[256], *bitmap;
+	struct usb_hub_descriptor *descriptor;
+	struct usb_hub_status *hubsts;
+	int i;
+	struct usb_hub_device *hub;
+
+	/* "allocate" Hub device */
+	hub=usb_hub_allocate();
+	if(hub==NULL)
+		return -1;
+	hub->pusb_dev=dev;
+	/* Get the the hub descriptor */
+	if (usb_get_hub_descriptor(dev, buffer, 4) < 0) {
+		USB_HUB_PRINTF("usb_hub_configure: failed to get hub descriptor, giving up %lX\n",dev->status);
+		return -1;
+	}
+	descriptor = (struct usb_hub_descriptor *)buffer;
+	if (usb_get_hub_descriptor(dev, buffer, descriptor->bLength) < 0) {
+		USB_HUB_PRINTF("usb_hub_configure: failed to get hub descriptor 2nd giving up %lX\n",dev->status);
+		return -1;
+	}
+	memcpy((unsigned char *)&hub->desc,buffer,descriptor->bLength);
+	/* adjust 16bit values */
+	hub->desc.wHubCharacteristics=swap_16(descriptor->wHubCharacteristics);
+	/* set the bitmap */
+	bitmap=(unsigned char *)&hub->desc.DeviceRemovable[0];
+	memset(bitmap,0xff,(USB_MAXCHILDREN+1+7)/8); /* devices not removable by default */
+	bitmap=(unsigned char *)&hub->desc.PortPowerCtrlMask[0];
+	memset(bitmap,0xff,(USB_MAXCHILDREN+1+7)/8); /* PowerMask = 1B */
+	for(i=0;i<((hub->desc.bNbrPorts + 1 + 7)/8);i++) {
+		hub->desc.DeviceRemovable[i]=descriptor->DeviceRemovable[i];
+	}
+	for(i=0;i<((hub->desc.bNbrPorts + 1 + 7)/8);i++) {
+		hub->desc.DeviceRemovable[i]=descriptor->PortPowerCtrlMask[i];
+	}
+	dev->maxchild = descriptor->bNbrPorts;
+	USB_HUB_PRINTF("%d ports detected\n", dev->maxchild);
+
+	switch (hub->desc.wHubCharacteristics & HUB_CHAR_LPSM) {
+		case 0x00:
+			USB_HUB_PRINTF("ganged power switching\n");
+			break;
+		case 0x01:
+			USB_HUB_PRINTF("individual port power switching\n");
+			break;
+		case 0x02:
+		case 0x03:
+			USB_HUB_PRINTF("unknown reserved power switching mode\n");
+			break;
+	}
+
+	if (hub->desc.wHubCharacteristics & HUB_CHAR_COMPOUND)
+		USB_HUB_PRINTF("part of a compound device\n");
+	else
+		USB_HUB_PRINTF("standalone hub\n");
+
+	switch (hub->desc.wHubCharacteristics & HUB_CHAR_OCPM) {
+		case 0x00:
+			USB_HUB_PRINTF("global over-current protection\n");
+			break;
+		case 0x08:
+			USB_HUB_PRINTF("individual port over-current protection\n");
+			break;
+		case 0x10:
+		case 0x18:
+			USB_HUB_PRINTF("no over-current protection\n");
+      break;
+	}
+	USB_HUB_PRINTF("power on to power good time: %dms\n", descriptor->bPwrOn2PwrGood * 2);
+	USB_HUB_PRINTF("hub controller current requirement: %dmA\n", descriptor->bHubContrCurrent);
+	for (i = 0; i < dev->maxchild; i++)
+		USB_HUB_PRINTF("port %d is%s removable\n", i + 1,
+			hub->desc.DeviceRemovable[(i + 1)/8] & (1 << ((i + 1)%8)) ? " not" : "");
+	if (usb_get_hub_status(dev, buffer) < 0) {
+		USB_HUB_PRINTF("usb_hub_configure: failed to get Status %lX\n",dev->status);
+		return -1;
+	}
+	hubsts = (struct usb_hub_status *)buffer;
+	USB_HUB_PRINTF("get_hub_status returned status %X, change %X\n",
+		swap_16(hubsts->wHubStatus),swap_16(hubsts->wHubChange));
+	USB_HUB_PRINTF("local power source is %s\n",
+		(swap_16(hubsts->wHubStatus) & HUB_STATUS_LOCAL_POWER) ? "lost (inactive)" : "good");
+	USB_HUB_PRINTF("%sover-current condition exists\n",
+		(swap_16(hubsts->wHubStatus) & HUB_STATUS_OVERCURRENT) ? "" : "no ");
+	usb_hub_power_on(hub);
+	for (i = 0; i < dev->maxchild; i++) {
+		struct usb_port_status portsts;
+		unsigned short portstatus, portchange;
+
+		if (usb_get_port_status(dev, i + 1, &portsts) < 0) {
+			USB_HUB_PRINTF("get_port_status failed\n");
+			continue;
+		}
+		portstatus = swap_16(portsts.wPortStatus);
+		portchange = swap_16(portsts.wPortChange);
+		USB_HUB_PRINTF("Port %d Status %X Change %X\n",i+1,portstatus,portchange);
+		if (portchange & USB_PORT_STAT_C_CONNECTION) {
+			USB_HUB_PRINTF("port %d connection change\n", i + 1);
+			usb_hub_port_connect_change(dev, i);
+		}
+		if (portchange & USB_PORT_STAT_C_ENABLE) {
+			USB_HUB_PRINTF("port %d enable change, status %x\n", i + 1, portstatus);
+			usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_ENABLE);
+
+			/* EM interference sometimes causes bad shielded USB devices to
+			 * be shutdown by the hub, this hack enables them again.
+			 * Works at least with mouse driver */
+			if (!(portstatus & USB_PORT_STAT_ENABLE) &&
+				(portstatus & USB_PORT_STAT_CONNECTION) && (dev->children[i])) {
+				USB_HUB_PRINTF("already running port %i disabled by hub (EMI?), re-enabling...\n",
+					i + 1);
+					usb_hub_port_connect_change(dev, i);
+			}
+		}
+		if (portstatus & USB_PORT_STAT_SUSPEND) {
+			USB_HUB_PRINTF("port %d suspend change\n", i + 1);
+			usb_clear_port_feature(dev, i + 1,  USB_PORT_FEAT_SUSPEND);
+		}
+
+		if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
+			USB_HUB_PRINTF("port %d over-current change\n", i + 1);
+			usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_OVER_CURRENT);
+			usb_hub_power_on(hub);
+		}
+
+		if (portchange & USB_PORT_STAT_C_RESET) {
+			USB_HUB_PRINTF("port %d reset change\n", i + 1);
+			usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_RESET);
+		}
+	} /* end for i all ports */
+
+	return 0;
+}
+
+int usb_hub_probe(struct usb_device *dev, int ifnum)
+{
+	struct usb_interface_descriptor *iface;
+	struct usb_endpoint_descriptor *ep;
+	int ret;
+
+	iface = &dev->config.if_desc[ifnum];
+	/* Is it a hub? */
+	if (iface->bInterfaceClass != USB_CLASS_HUB)
+		return 0;
+	/* Some hubs have a subclass of 1, which AFAICT according to the */
+	/*  specs is not defined, but it works */
+	if ((iface->bInterfaceSubClass != 0) &&
+	    (iface->bInterfaceSubClass != 1))
+		return 0;
+	/* Multiple endpoints? What kind of mutant ninja-hub is this? */
+	if (iface->bNumEndpoints != 1)
+		return 0;
+	ep = &iface->ep_desc[0];
+	/* Output endpoint? Curiousier and curiousier.. */
+	if (!(ep->bEndpointAddress & USB_DIR_IN))
+		return 0;
+	/* If it's not an interrupt endpoint, we'd better punt! */
+	if ((ep->bmAttributes & 3) != 3)
+		return 0;
+	/* We found a hub */
+	USB_HUB_PRINTF("USB hub found\n");
+	ret=usb_hub_configure(dev);
+	return ret;
+}
+
+#endif /* (CONFIG_COMMANDS & CFG_CMD_USB) */
+
+/* EOF */
diff --git a/common/usb_kbd.c b/common/usb_kbd.c
new file mode 100644
index 0000000..ad7e610
--- /dev/null
+++ b/common/usb_kbd.c
@@ -0,0 +1,734 @@
+/*
+ * (C) Copyright 2001
+ * Denis Peter, MPL AG Switzerland
+ *
+ * Part of this source has been derived from the Linux USB
+ * project.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ */
+#include <common.h>
+#include <devices.h>
+
+#ifdef CONFIG_USB_KEYBOARD
+
+#include <usb.h>
+
+#undef USB_KBD_DEBUG
+/*
+ * if overwrite_console returns 1, the stdin, stderr and stdout
+ * are switched to the serial port, else the settings in the
+ * environment are used
+ */
+#ifdef CFG_CONSOLE_OVERWRITE_ROUTINE
+extern int overwrite_console (void);
+#else
+int overwrite_console (void)
+{
+	return (0);
+}
+#endif
+
+#ifdef	USB_KBD_DEBUG
+#define	USB_KBD_PRINTF(fmt,args...)	printf (fmt ,##args)
+#else
+#define USB_KBD_PRINTF(fmt,args...)
+#endif
+
+
+#define REPEAT_RATE  40/4 /* 40msec -> 25cps */
+#define REPEAT_DELAY 10 /* 10 x REAPEAT_RATE = 400msec */
+
+#define NUM_LOCK	0x53
+#define CAPS_LOCK 0x39
+#define SCROLL_LOCK 0x47
+
+
+/* Modifier bits */
+#define LEFT_CNTR		0
+#define LEFT_SHIFT	1
+#define LEFT_ALT		2
+#define LEFT_GUI		3
+#define RIGHT_CNTR	4
+#define RIGHT_SHIFT	5
+#define RIGHT_ALT		6
+#define RIGHT_GUI		7
+
+#define USB_KBD_BUFFER_LEN 0x20  /* size of the keyboardbuffer */
+
+static volatile char usb_kbd_buffer[USB_KBD_BUFFER_LEN];
+static volatile int usb_in_pointer = 0;
+static volatile int usb_out_pointer = 0;
+
+unsigned char new[8];
+unsigned char old[8];
+int repeat_delay;
+#define DEVNAME "usbkbd"
+static unsigned char num_lock = 0;
+static unsigned char caps_lock = 0;
+static unsigned char scroll_lock = 0;
+
+static unsigned char leds __attribute__ ((aligned (0x4)));
+
+static unsigned char usb_kbd_numkey[] = {
+	 '1', '2', '3', '4', '5', '6', '7', '8', '9', '0','\r',0x1b,'\b','\t',' ', '-',
+	 '=', '[', ']','\\', '#', ';', '\'', '`', ',', '.', '/'
+};
+static unsigned char usb_kbd_numkey_shifted[] = {
+	 '!', '@', '#', '$', '%', '^', '&', '*', '(', ')','\r',0x1b,'\b','\t',' ', '_',
+	 '+', '{', '}', '|', '~', ':', '"', '~', '<', '>', '?'
+};
+
+/******************************************************************
+ * Queue handling
+ ******************************************************************/
+/* puts character in the queue and sets up the in and out pointer */
+static void usb_kbd_put_queue(char data)
+{
+	if((usb_in_pointer+1)==USB_KBD_BUFFER_LEN) {
+		if(usb_out_pointer==0) {
+			return; /* buffer full */
+		} else{
+			usb_in_pointer=0;
+		}
+	} else {
+		if((usb_in_pointer+1)==usb_out_pointer)
+			return; /* buffer full */
+		usb_in_pointer++;
+	}
+	usb_kbd_buffer[usb_in_pointer]=data;
+	return;
+}
+
+/* test if a character is in the queue */
+static int usb_kbd_testc(void)
+{
+	if(usb_in_pointer==usb_out_pointer)
+		return(0); /* no data */
+	else
+		return(1);
+}
+/* gets the character from the queue */
+static int usb_kbd_getc(void)
+{
+	char c;
+	while(usb_in_pointer==usb_out_pointer);
+	if((usb_out_pointer+1)==USB_KBD_BUFFER_LEN)
+		usb_out_pointer=0;
+	else
+		usb_out_pointer++;
+	c=usb_kbd_buffer[usb_out_pointer];
+	return (int)c;
+
+}
+
+/* forward decleration */
+static int usb_kbd_probe(struct usb_device *dev, unsigned int ifnum);
+
+/* search for keyboard and register it if found */
+int drv_usb_kbd_init(void)
+{
+	int error,i,index;
+	device_t usb_kbd_dev,*old_dev;
+	struct usb_device *dev;
+	char *stdinname  = getenv ("stdin");
+
+	usb_in_pointer=0;
+	usb_out_pointer=0;
+	/* scan all USB Devices */
+	for(i=0;i<USB_MAX_DEVICE;i++) {
+		dev=usb_get_dev_index(i); /* get device */
+		if(dev->devnum!=-1) {
+			if(usb_kbd_probe(dev,0)==1) { /* Ok, we found a keyboard */
+				/* check, if it is already registered */
+				USB_KBD_PRINTF("USB KBD found set up device.\n");
+				for (index=1; index<=ListNumItems(devlist); index++) {
+					old_dev = ListGetPtrToItem(devlist, index);
+					if(strcmp(old_dev->name,DEVNAME)==0) {
+						/* ok, already registered, just return ok */
+						USB_KBD_PRINTF("USB KBD is already registered.\n");
+						return 1;
+					}
+				}
+				/* register the keyboard */
+				USB_KBD_PRINTF("USB KBD register.\n");
+				memset (&usb_kbd_dev, 0, sizeof(device_t));
+				strcpy(usb_kbd_dev.name, DEVNAME);
+				usb_kbd_dev.flags =  DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM;
+				usb_kbd_dev.putc = NULL;
+				usb_kbd_dev.puts = NULL;
+				usb_kbd_dev.getc = usb_kbd_getc;
+				usb_kbd_dev.tstc = usb_kbd_testc;
+				error = device_register (&usb_kbd_dev);
+				if(error==0) {
+					/* check if this is the standard input device */
+					if(strcmp(stdinname,DEVNAME)==0) {
+						/* reassign the console */
+						if(overwrite_console()) {
+							return 1;
+						}
+						error=console_assign(stdin,DEVNAME);
+						if(error==0)
+							return 1;
+						else
+							return error;
+					}
+					return 1;
+				}
+				return error;
+			}
+		}
+	}
+	/* no USB Keyboard found */
+	return -1;
+}
+
+
+/* deregistering the keyboard */
+int usb_kbd_deregister(void)
+{
+	return device_deregister(DEVNAME);
+}
+
+/**************************************************************************
+ * Low Level drivers
+ */
+
+/* set the LEDs. Since this is used in the irq routine, the control job
+   is issued with a timeout of 0. This means, that the job is queued without
+   waiting for job completion */
+
+static void usb_kbd_setled(struct usb_device *dev)
+{
+	struct usb_interface_descriptor *iface;
+	iface = &dev->config.if_desc[0];
+	leds=0;
+	if(scroll_lock!=0)
+		leds|=1;
+	leds<<=1;
+	if(caps_lock!=0)
+		leds|=1;
+	leds<<=1;
+	if(num_lock!=0)
+		leds|=1;
+	usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+		USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 		0x200, iface->bInterfaceNumber,(void *)&leds, 1, 0);
+
+}
+
+
+#define CAPITAL_MASK 0x20
+/* Translate the scancode in ASCII */
+static int usb_kbd_translate(unsigned char scancode,unsigned char modifier,int pressed)
+{
+	unsigned char keycode;
+
+	if(pressed==0) {
+		/* key released */
+ 		repeat_delay=0;
+		return 0;
+	}
+	if(pressed==2) {
+		repeat_delay++;
+		if(repeat_delay<REPEAT_DELAY)
+			return 0;
+		repeat_delay=REPEAT_DELAY;
+	}
+	keycode=0;
+	if((scancode>3) && (scancode<0x1d)) { /* alpha numeric values */
+		keycode=scancode-4 + 0x61;
+		if(caps_lock)
+			keycode&=~CAPITAL_MASK; /* switch to capital Letters */
+		if(((modifier&(1<<LEFT_SHIFT))!=0)||((modifier&(1<<RIGHT_SHIFT))!=0)) {
+			if(keycode & CAPITAL_MASK)
+				keycode&=~CAPITAL_MASK; /* switch to capital Letters */
+			else
+				keycode|=CAPITAL_MASK; /* switch to non capital Letters */
+		}
+	}
+	if((scancode>0x1d) && (scancode<0x3A)) {
+		if(((modifier&(1<<LEFT_SHIFT))!=0)||((modifier&(1<<RIGHT_SHIFT))!=0))  /* shifted */
+			keycode=usb_kbd_numkey_shifted[scancode-0x1e];
+		else /* non shifted */
+			keycode=usb_kbd_numkey[scancode-0x1e];
+	}
+	if(pressed==1) {
+		if(scancode==NUM_LOCK) {
+			num_lock=~num_lock;
+			return 1;
+		}
+		if(scancode==CAPS_LOCK) {
+			caps_lock=~caps_lock;
+			return 1;
+		}
+		if(scancode==SCROLL_LOCK) {
+			scroll_lock=~scroll_lock;
+			return 1;
+		}
+	}
+	if(keycode!=0) {
+		USB_KBD_PRINTF("%c",keycode);
+		usb_kbd_put_queue(keycode);
+	}
+	return 0;
+}
+
+/* Interrupt service routine */
+static int usb_kbd_irq(struct usb_device *dev)
+{
+	int i,res;
+
+	if((dev->irq_status!=0)||(dev->irq_act_len!=8))
+	{
+		USB_KBD_PRINTF("usb_keyboard Error %lX, len %d\n",dev->irq_status,dev->irq_act_len);
+		return 1;
+	}
+	res=0;
+	for (i = 2; i < 8; i++) {
+		if (old[i] > 3 && memscan(&new[2], old[i], 6) == &new[8]) {
+			res|=usb_kbd_translate(old[i],new[0],0);
+		}
+		if (new[i] > 3 && memscan(&old[2], new[i], 6) == &old[8]) {
+			res|=usb_kbd_translate(new[i],new[0],1);
+		}
+	}
+	if((new[2]>3) && (old[2]==new[2])) /* still pressed */
+		res|=usb_kbd_translate(new[2],new[0],2);
+	if(res==1)
+		usb_kbd_setled(dev);
+	memcpy(&old[0],&new[0], 8);
+	return 1; /* install IRQ Handler again */
+}
+
+/* probes the USB device dev for keyboard type */
+static int usb_kbd_probe(struct usb_device *dev, unsigned int ifnum)
+{
+	struct usb_interface_descriptor *iface;
+	struct usb_endpoint_descriptor *ep;
+	int pipe,maxp;
+
+	if (dev->descriptor.bNumConfigurations != 1) return 0;
+	iface = &dev->config.if_desc[ifnum];
+
+	if (iface->bInterfaceClass != 3) return 0;
+	if (iface->bInterfaceSubClass != 1) return 0;
+	if (iface->bInterfaceProtocol != 1) return 0;
+	if (iface->bNumEndpoints != 1) return 0;
+
+	ep = &iface->ep_desc[0];
+
+	if (!(ep->bEndpointAddress & 0x80)) return 0;
+	if ((ep->bmAttributes & 3) != 3) return 0;
+	USB_KBD_PRINTF("USB KBD found set protocol...\n");
+	/* ok, we found a USB Keyboard, install it */
+	/* usb_kbd_get_hid_desc(dev); */
+	usb_set_protocol(dev, iface->bInterfaceNumber, 0);
+	USB_KBD_PRINTF("USB KBD found set idle...\n");
+	usb_set_idle(dev, iface->bInterfaceNumber, REPEAT_RATE, 0);
+	memset(&new[0], 0, 8);
+	memset(&old[0], 0, 8);
+	repeat_delay=0;
+	pipe = usb_rcvintpipe(dev, ep->bEndpointAddress);
+	maxp = usb_maxpacket(dev, pipe);
+	dev->irq_handle=usb_kbd_irq;
+	USB_KBD_PRINTF("USB KBD enable interrupt pipe...\n");
+	usb_submit_int_msg(dev,pipe,&new[0], maxp > 8 ? 8 : maxp,ep->bInterval);
+	return 1;
+}
+
+
+#if 0
+struct usb_hid_descriptor {
+	unsigned char  bLength;
+	unsigned char  bDescriptorType; /* 0x21 for HID */
+	unsigned short bcdHID; /* release number */
+	unsigned char  bCountryCode;
+	unsigned char  bNumDescriptors;
+	unsigned char  bReportDescriptorType;
+	unsigned short wDescriptorLength;
+} __attribute__ ((packed));
+
+/*
+ * We parse each description item into this structure. Short items data
+ * values are expanded to 32-bit signed int, long items contain a pointer
+ * into the data area.
+ */
+
+struct hid_item {
+	unsigned char format;
+	unsigned char size;
+	unsigned char type;
+	unsigned char tag;
+	union {
+	    unsigned char   u8;
+	    char            s8;
+	    unsigned short  u16;
+	    short           s16;
+	    unsigned long   u32;
+	    long            s32;
+	    unsigned char  *longdata;
+	} data;
+};
+
+/*
+ * HID report item format
+ */
+
+#define HID_ITEM_FORMAT_SHORT	0
+#define HID_ITEM_FORMAT_LONG	1
+
+/*
+ * Special tag indicating long items
+ */
+
+#define HID_ITEM_TAG_LONG	15
+
+
+
+static struct usb_hid_descriptor usb_kbd_hid_desc;
+
+void usb_kbd_display_hid(struct usb_hid_descriptor *hid)
+{
+	printf("USB_HID_DESC:\n");
+	printf("  bLenght               0x%x\n",hid->bLength);
+	printf("  bcdHID                0x%x\n",hid->bcdHID);
+	printf("  bCountryCode          %d\n",hid->bCountryCode);
+	printf("  bNumDescriptors       0x%x\n",hid->bNumDescriptors);
+	printf("  bReportDescriptorType 0x%x\n",hid->bReportDescriptorType);
+	printf("  wDescriptorLength     0x%x\n",hid->wDescriptorLength);
+}
+
+
+/*
+ * Fetch a report description item from the data stream. We support long
+ * items, though they are not used yet.
+ */
+
+static int fetch_item(unsigned char *start,unsigned char *end, struct hid_item *item)
+{
+	if((end - start) > 0) {
+		unsigned char b = *start++;
+		item->type = (b >> 2) & 3;
+		item->tag  = (b >> 4) & 15;
+		if (item->tag == HID_ITEM_TAG_LONG) {
+			item->format = HID_ITEM_FORMAT_LONG;
+			if ((end - start) >= 2) {
+				item->size = *start++;
+				item->tag  = *start++;
+				if ((end - start) >= item->size) {
+					item->data.longdata = start;
+					start += item->size;
+					return item->size;
+				}
+			}
+		} else {
+			item->format = HID_ITEM_FORMAT_SHORT;
+			item->size = b & 3;
+			switch (item->size) {
+				case 0:
+					return item->size;
+				case 1:
+					if ((end - start) >= 1) {
+						item->data.u8 = *start++;
+						return item->size;
+					}
+					break;
+				case 2:
+					if ((end - start) >= 2) {
+						item->data.u16 = swap_16((unsigned short *)start);
+						start+=2;
+						return item->size;
+					}
+				case 3:
+					item->size++;
+					if ((end - start) >= 4) {
+						item->data.u32 = swap_32((unsigned long *)start);
+						start+=4;
+						return item->size;
+					}
+			}
+		}
+	}
+	return -1;
+}
+
+/*
+ * HID report descriptor item type (prefix bit 2,3)
+ */
+
+#define HID_ITEM_TYPE_MAIN		0
+#define HID_ITEM_TYPE_GLOBAL		1
+#define HID_ITEM_TYPE_LOCAL		2
+#define HID_ITEM_TYPE_RESERVED		3
+/*
+ * HID report descriptor main item tags
+ */
+
+#define HID_MAIN_ITEM_TAG_INPUT			8
+#define HID_MAIN_ITEM_TAG_OUTPUT		9
+#define HID_MAIN_ITEM_TAG_FEATURE		11
+#define HID_MAIN_ITEM_TAG_BEGIN_COLLECTION	10
+#define HID_MAIN_ITEM_TAG_END_COLLECTION	12
+/*
+ * HID report descriptor main item contents
+ */
+
+#define HID_MAIN_ITEM_CONSTANT		0x001
+#define HID_MAIN_ITEM_VARIABLE		0x002
+#define HID_MAIN_ITEM_RELATIVE		0x004
+#define HID_MAIN_ITEM_WRAP		0x008
+#define HID_MAIN_ITEM_NONLINEAR		0x010
+#define HID_MAIN_ITEM_NO_PREFERRED	0x020
+#define HID_MAIN_ITEM_NULL_STATE	0x040
+#define HID_MAIN_ITEM_VOLATILE		0x080
+#define HID_MAIN_ITEM_BUFFERED_BYTE	0x100
+
+/*
+ * HID report descriptor collection item types
+ */
+
+#define HID_COLLECTION_PHYSICAL		0
+#define HID_COLLECTION_APPLICATION	1
+#define HID_COLLECTION_LOGICAL		2
+/*
+ * HID report descriptor global item tags
+ */
+
+#define HID_GLOBAL_ITEM_TAG_USAGE_PAGE		0
+#define HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM	1
+#define HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM	2
+#define HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM	3
+#define HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM	4
+#define HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT	5
+#define HID_GLOBAL_ITEM_TAG_UNIT		6
+#define HID_GLOBAL_ITEM_TAG_REPORT_SIZE		7
+#define HID_GLOBAL_ITEM_TAG_REPORT_ID		8
+#define HID_GLOBAL_ITEM_TAG_REPORT_COUNT	9
+#define HID_GLOBAL_ITEM_TAG_PUSH		10
+#define HID_GLOBAL_ITEM_TAG_POP			11
+
+/*
+ * HID report descriptor local item tags
+ */
+
+#define HID_LOCAL_ITEM_TAG_USAGE		0
+#define HID_LOCAL_ITEM_TAG_USAGE_MINIMUM	1
+#define HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM	2
+#define HID_LOCAL_ITEM_TAG_DESIGNATOR_INDEX	3
+#define HID_LOCAL_ITEM_TAG_DESIGNATOR_MINIMUM	4
+#define HID_LOCAL_ITEM_TAG_DESIGNATOR_MAXIMUM	5
+#define HID_LOCAL_ITEM_TAG_STRING_INDEX		7
+#define HID_LOCAL_ITEM_TAG_STRING_MINIMUM	8
+#define HID_LOCAL_ITEM_TAG_STRING_MAXIMUM	9
+#define HID_LOCAL_ITEM_TAG_DELIMITER		10
+
+
+
+static void usb_kbd_show_item(struct hid_item *item)
+{
+	switch(item->type) {
+		case HID_ITEM_TYPE_MAIN:
+			switch(item->tag) {
+				case HID_MAIN_ITEM_TAG_INPUT:
+					printf("Main Input");
+					break;
+				case HID_MAIN_ITEM_TAG_OUTPUT:
+					printf("Main Output");
+					break;
+				case HID_MAIN_ITEM_TAG_FEATURE:
+					printf("Main Feature");
+					break;
+				case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION:
+					printf("Main Begin Collection");
+					break;
+				case HID_MAIN_ITEM_TAG_END_COLLECTION:
+					printf("Main End Collection");
+					break;
+				default:
+					printf("Main reserved %d",item->tag);
+					break;
+			}
+			break;
+		case HID_ITEM_TYPE_GLOBAL:
+			switch(item->tag) {
+				case HID_GLOBAL_ITEM_TAG_USAGE_PAGE:
+					printf("- Global Usage Page");
+					break;
+				case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM:
+					printf("- Global Logical Minimum");
+					break;
+				case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM:
+					printf("- Global Logical Maximum");
+					break;
+				case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM:
+					printf("- Global physical Minimum");
+					break;
+				case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM:
+					printf("- Global physical Maximum");
+					break;
+				case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT:
+					printf("- Global Unit Exponent");
+					break;
+				case HID_GLOBAL_ITEM_TAG_UNIT:
+					printf("- Global Unit");
+					break;
+				case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:
+					printf("- Global Report Size");
+					break;
+				case HID_GLOBAL_ITEM_TAG_REPORT_ID:
+					printf("- Global Report ID");
+					break;
+				case HID_GLOBAL_ITEM_TAG_REPORT_COUNT:
+					printf("- Global Report Count");
+					break;
+				case HID_GLOBAL_ITEM_TAG_PUSH:
+					printf("- Global Push");
+					break;
+				case HID_GLOBAL_ITEM_TAG_POP:
+					printf("- Global Pop");
+					break;
+				default:
+					printf("- Global reserved %d",item->tag);
+					break;
+			}
+			break;
+		case HID_ITEM_TYPE_LOCAL:
+			switch(item->tag) {
+				case HID_LOCAL_ITEM_TAG_USAGE:
+					printf("-- Local Usage");
+					break;
+				case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
+					printf("-- Local Usage Minimum");
+					break;
+				case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM:
+					printf("-- Local Usage Maximum");
+					break;
+				case HID_LOCAL_ITEM_TAG_DESIGNATOR_INDEX:
+					printf("-- Local Designator Index");
+					break;
+				case HID_LOCAL_ITEM_TAG_DESIGNATOR_MINIMUM:
+					printf("-- Local Designator Minimum");
+					break;
+				case HID_LOCAL_ITEM_TAG_DESIGNATOR_MAXIMUM:
+					printf("-- Local Designator Maximum");
+					break;
+				case HID_LOCAL_ITEM_TAG_STRING_INDEX:
+					printf("-- Local String Index");
+					break;
+				case HID_LOCAL_ITEM_TAG_STRING_MINIMUM:
+					printf("-- Local String Minimum");
+					break;
+				case HID_LOCAL_ITEM_TAG_STRING_MAXIMUM:
+					printf("-- Local String Maximum");
+					break;
+				case HID_LOCAL_ITEM_TAG_DELIMITER:
+					printf("-- Local Delimiter");
+					break;
+				default:
+					printf("-- Local reserved %d",item->tag);
+					break;
+			}
+			break;
+		default:
+			printf("--- reserved %d",item->type);
+			break;
+	}
+	switch(item->size) {
+		case 1:
+			printf("  %d",item->data.u8);
+			break;
+		case 2:
+			printf("  %d",item->data.u16);
+			break;
+		case 4:
+			printf("  %ld",item->data.u32);
+			break;
+	}
+	printf("\n");
+}
+
+
+
+static int usb_kbd_get_hid_desc(struct usb_device *dev)
+{
+	unsigned char buffer[256];
+	struct usb_descriptor_header *head;
+	struct usb_config_descriptor *config;
+	int index,len,i;
+	unsigned char *start, *end;
+	struct hid_item item;
+
+	if(usb_get_configuration_no(dev,&buffer[0],0)==-1)
+		return -1;
+	head =(struct usb_descriptor_header *)&buffer[0];
+	if(head->bDescriptorType!=USB_DT_CONFIG) {
+		printf(" ERROR: NOT USB_CONFIG_DESC %x\n",head->bDescriptorType);
+		return -1;
+	}
+	index=head->bLength;
+	config=(struct usb_config_descriptor *)&buffer[0];
+	len=swap_16(config->wTotalLength);
+	/* Ok the first entry must be a configuration entry, now process the others */
+	head=(struct usb_descriptor_header *)&buffer[index];
+	while(index+1 < len) {
+		if(head->bDescriptorType==USB_DT_HID) {
+			printf("HID desc found\n");
+			memcpy(&usb_kbd_hid_desc,&buffer[index],buffer[index]);
+			usb_kbd_hid_desc.bcdHID=swap_16(usb_kbd_hid_desc.bcdHID);
+			usb_kbd_hid_desc.wDescriptorLength=swap_16(usb_kbd_hid_desc.wDescriptorLength);
+			usb_kbd_display_hid(&usb_kbd_hid_desc);
+			len=0;
+			break;
+		}
+		index+=head->bLength;
+		head=(struct usb_descriptor_header *)&buffer[index];
+	}
+	if(len>0)
+		return -1;
+	len=usb_kbd_hid_desc.wDescriptorLength;
+	if((index = usb_get_class_descriptor(dev, 0, USB_DT_REPORT, 0, &buffer[0], len)) < 0) {
+		printf("reading report descriptor failed\n");
+		return -1;
+	}
+	printf(" report descriptor (size %u, read %d)\n", len, index);
+	start=&buffer[0];
+	end=&buffer[len];
+	i=0;
+	do {
+		index=fetch_item(start,end,&item);
+		i+=index;
+		i++;
+		if(index>=0)
+			usb_kbd_show_item(&item);
+
+		start+=index;
+		start++;
+	} while(index>=0);
+
+}
+
+
+#endif
+
+#endif /* CONFIG_USB_KEYBOARD */
+
+/* eof */
+
diff --git a/common/usb_storage.c b/common/usb_storage.c
new file mode 100644
index 0000000..b134721
--- /dev/null
+++ b/common/usb_storage.c
@@ -0,0 +1,895 @@
+/*
+ * (C) Copyright 2001
+ * Denis Peter, MPL AG Switzerland
+ *
+ * Most of this source has been derived from the Linux USB
+ * project.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ */
+
+/* Note:
+ * Currently only the CBI transport protocoll has been implemented, and it
+ * is only tested with a TEAC USB Floppy. Other Massstorages with CBI or CB
+ * transport protocoll may work as well.
+ */
+
+
+
+#include <common.h>
+#include <command.h>
+#include <asm/processor.h>
+
+
+#if (CONFIG_COMMANDS & CFG_CMD_USB)
+#include <usb.h>
+
+#ifdef CONFIG_USB_STORAGE
+
+#undef	USB_STOR_DEBUG
+
+#ifdef	USB_STOR_DEBUG
+#define	USB_STOR_PRINTF(fmt,args...)	printf (fmt ,##args)
+#else
+#define USB_STOR_PRINTF(fmt,args...)
+#endif
+
+#include <scsi.h>
+/* direction table -- this indicates the direction of the data
+ * transfer for each command code -- a 1 indicates input
+ */
+unsigned char us_direction[256/8] = {
+	0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77,
+	0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+#define US_DIRECTION(x) ((us_direction[x>>3] >> (x & 7)) & 1)
+
+static unsigned char usb_stor_buf[512];
+static ccb usb_ccb;
+
+/*
+ * CBI style
+ */
+
+#define US_CBI_ADSC		0
+
+
+#define USB_MAX_STOR_DEV 5
+static int usb_max_devs; /* number of highest available usb device */
+
+static block_dev_desc_t usb_dev_desc[USB_MAX_STOR_DEV];
+
+struct us_data;
+typedef int (*trans_cmnd)(ccb*, struct us_data*);
+typedef int (*trans_reset)(struct us_data*);
+
+struct us_data {
+	struct usb_device	*pusb_dev;       /* this usb_device */
+	unsigned int		flags;		 /* from filter initially */
+	unsigned char		ifnum;		 /* interface number */
+	unsigned char		ep_in;		 /* in endpoint */
+	unsigned char		ep_out;		 /* out ....... */
+	unsigned char		ep_int;		 /* interrupt . */
+	unsigned char		subclass;	 /* as in overview */
+	unsigned char		protocol;	 /* .............. */
+	unsigned char		attention_done;  /* force attn on first cmd */
+	unsigned short	ip_data;	 /* interrupt data */
+	int							action;		 /* what to do */
+	int							ip_wanted; /* needed */
+	int							*irq_handle;	 /* for USB int requests */
+	unsigned int		irqpipe;	 /* pipe for release_irq */
+	unsigned char		irqmaxp;	/* max packed for irq Pipe */
+	unsigned char   irqinterval; /* Intervall for IRQ Pipe */
+	ccb							*srb;		 /* current srb */
+	trans_reset			transport_reset; /* reset routine */
+	trans_cmnd			transport; /* transport routine */
+};
+
+static struct us_data usb_stor[USB_MAX_STOR_DEV];
+
+
+
+#define USB_STOR_TRANSPORT_GOOD    0
+#define USB_STOR_TRANSPORT_FAILED -1
+#define USB_STOR_TRANSPORT_ERROR  -2
+
+
+
+
+
+
+int usb_stor_get_info(struct usb_device *dev, struct us_data *us, block_dev_desc_t *dev_desc);
+int usb_storage_probe(struct usb_device *dev, unsigned int ifnum,struct us_data *ss);
+unsigned long usb_stor_read(int device, unsigned long blknr, unsigned long blkcnt, unsigned long *buffer);
+struct usb_device * usb_get_dev_index(int index);
+void uhci_show_temp_int_td(void);
+
+block_dev_desc_t *usb_stor_get_dev(int index)
+{
+	return &usb_dev_desc[index];
+}
+
+
+void usb_show_progress(void)
+{
+	printf(".");
+}
+
+/*********************************************************************************
+ * (re)-scan the usb and reports device info
+ * to the user if mode = 1
+ * returns current device or -1 if no
+ */
+int usb_stor_scan(int mode)
+{
+	unsigned char i;
+	struct usb_device *dev;
+
+	if(mode==1) {
+		printf("scanning bus for storage devices...\n");
+	}
+	usb_disable_asynch(1); /* asynch transfer not allowed */
+
+	for(i=0;i<USB_MAX_STOR_DEV;i++) {
+		memset(&usb_dev_desc[i],0,sizeof(block_dev_desc_t));
+		usb_dev_desc[i].target=0xff;
+		usb_dev_desc[i].if_type=IF_TYPE_USB;
+		usb_dev_desc[i].dev=i;
+		usb_dev_desc[i].part_type=PART_TYPE_UNKNOWN;
+		usb_dev_desc[i].block_read=usb_stor_read;
+	}
+	usb_max_devs=0;
+	for(i=0;i<USB_MAX_DEVICE;i++) {
+		dev=usb_get_dev_index(i); /* get device */
+		USB_STOR_PRINTF("i=%d\n",i);
+		if(dev==NULL) {
+			break; /* no more devices avaiable */
+		}
+		if(usb_storage_probe(dev,0,&usb_stor[usb_max_devs])) { /* ok, it is a storage devices */
+			/* get info and fill it in */
+
+			if(usb_stor_get_info(dev, &usb_stor[usb_max_devs], &usb_dev_desc[usb_max_devs])) {
+				if(mode==1) {
+					printf ("  Device %d: ", usb_max_devs);
+					dev_print(&usb_dev_desc[usb_max_devs]);
+				} /* if mode */
+				usb_max_devs++;
+			} /* if get info ok */
+		} /* if storage device */
+		if(usb_max_devs==USB_MAX_STOR_DEV) {
+			printf("max USB Storage Device reached: %d stopping\n",usb_max_devs);
+			break;
+		}
+	} /* for */
+	usb_disable_asynch(0); /* asynch transfer allowed */
+	if(usb_max_devs>0)
+		return 0;
+	else
+		return-1;
+}
+
+static int usb_stor_irq(struct usb_device *dev)
+{
+	struct us_data *us;
+	us=(struct us_data *)dev->privptr;
+
+	if(us->ip_wanted) {
+		us->ip_wanted=0;
+	}
+	return 0;
+}
+
+
+#ifdef	USB_STOR_DEBUG
+
+static void usb_show_srb(ccb * pccb)
+{
+	int i;
+	printf("SRB: len %d datalen 0x%lX\n ",pccb->cmdlen,pccb->datalen);
+	for(i=0;i<12;i++) {
+		printf("%02X ",pccb->cmd[i]);
+	}
+	printf("\n");
+}
+
+static void display_int_status(unsigned long tmp)
+{
+	printf("Status: %s %s %s %s %s %s %s\n",
+		(tmp & USB_ST_ACTIVE) ? "Active" : "",
+		(tmp & USB_ST_STALLED) ? "Stalled" : "",
+		(tmp & USB_ST_BUF_ERR) ? "Buffer Error" : "",
+		(tmp & USB_ST_BABBLE_DET) ? "Babble Det" : "",
+		(tmp & USB_ST_NAK_REC) ? "NAKed" : "",
+		(tmp & USB_ST_CRC_ERR) ? "CRC Error" : "",
+		(tmp & USB_ST_BIT_ERR) ? "Bitstuff Error" : "");
+}
+#endif
+/***********************************************************************
+ * Data transfer routines
+ ***********************************************************************/
+
+static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length)
+{
+	int max_size;
+	int this_xfer;
+	int result;
+	int partial;
+	int maxtry;
+	int stat;
+
+	/* determine the maximum packet size for these transfers */
+	max_size = usb_maxpacket(us->pusb_dev, pipe) * 16;
+
+	/* while we have data left to transfer */
+	while (length) {
+
+		/* calculate how long this will be -- maximum or a remainder */
+		this_xfer = length > max_size ? max_size : length;
+		length -= this_xfer;
+
+		/* setup the retry counter */
+		maxtry = 10;
+
+		/* set up the transfer loop */
+		do {
+			/* transfer the data */
+			USB_STOR_PRINTF("Bulk xfer 0x%x(%d) try #%d\n",
+				  (unsigned int)buf, this_xfer, 11 - maxtry);
+			result = usb_bulk_msg(us->pusb_dev, pipe, buf,
+					      this_xfer, &partial, USB_CNTL_TIMEOUT*5);
+			USB_STOR_PRINTF("bulk_msg returned %d xferred %d/%d\n",
+				  result, partial, this_xfer);
+			if(us->pusb_dev->status!=0) {
+				/* if we stall, we need to clear it before we go on */
+#ifdef USB_STOR_DEBUG
+				display_int_status(us->pusb_dev->status);
+#endif
+				if (us->pusb_dev->status & USB_ST_STALLED) {
+					USB_STOR_PRINTF("stalled ->clearing endpoint halt for pipe 0x%x\n", pipe);
+					stat = us->pusb_dev->status;
+					usb_clear_halt(us->pusb_dev, pipe);
+					us->pusb_dev->status=stat;
+					if(this_xfer == partial) {
+						USB_STOR_PRINTF("bulk transferred with error %X, but data ok\n",us->pusb_dev->status);
+						return 0;
+					}
+					else
+						return result;
+				}
+				if (us->pusb_dev->status & USB_ST_NAK_REC) {
+					USB_STOR_PRINTF("Device NAKed bulk_msg\n");
+					return result;
+				}
+				if(this_xfer == partial) {
+					USB_STOR_PRINTF("bulk transferred with error %d, but data ok\n",us->pusb_dev->status);
+					return 0;
+				}
+				/* if our try counter reaches 0, bail out */
+				USB_STOR_PRINTF("bulk transferred with error %d, data %d\n",us->pusb_dev->status,partial);
+				if (!maxtry--)
+						return result;
+			}
+			/* update to show what data was transferred */
+			this_xfer -= partial;
+			buf += partial;
+			/* continue until this transfer is done */
+		} while ( this_xfer );
+	}
+
+	/* if we get here, we're done and successful */
+	return 0;
+}
+
+/* FIXME: this reset function doesn't really reset the port, and it
+ * should. Actually it should probably do what it's doing here, and
+ * reset the port physically
+ */
+static int usb_stor_CB_reset(struct us_data *us)
+{
+	unsigned char cmd[12];
+	int result;
+
+	USB_STOR_PRINTF("CB_reset\n");
+	memset(cmd, 0xFF, sizeof(cmd));
+	cmd[0] = SCSI_SEND_DIAG;
+	cmd[1] = 4;
+	result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
+				 US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+				 0, us->ifnum, cmd, sizeof(cmd), USB_CNTL_TIMEOUT*5);
+
+	/* long wait for reset */
+	wait_ms(1500);
+	USB_STOR_PRINTF("CB_reset result %d: status %X clearing endpoint halt\n",result,us->pusb_dev->status);
+	usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
+	usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_out));
+
+	USB_STOR_PRINTF("CB_reset done\n");
+	return 0;
+}
+
+/* FIXME: we also need a CBI_command which sets up the completion
+ * interrupt, and waits for it
+ */
+int usb_stor_CB_comdat(ccb *srb, struct us_data *us)
+{
+	int result;
+	int dir_in,retry;
+	unsigned int pipe;
+	unsigned long status;
+
+	retry=5;
+		dir_in=US_DIRECTION(srb->cmd[0]);
+
+		if(dir_in)
+			pipe=usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
+		else
+			pipe=usb_sndbulkpipe(us->pusb_dev, us->ep_out);
+	while(retry--) {
+		USB_STOR_PRINTF("CBI gets a command: Try %d\n",5-retry);
+#ifdef USB_STOR_DEBUG
+		usb_show_srb(srb);
+#endif
+		/* let's send the command via the control pipe */
+		result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
+					 US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+					 0, us->ifnum,
+					 srb->cmd, srb->cmdlen, USB_CNTL_TIMEOUT*5);
+		USB_STOR_PRINTF("CB_transport: control msg returned %d, status %X\n",result,us->pusb_dev->status);
+		/* check the return code for the command */
+		if (result < 0) {
+			if(us->pusb_dev->status & USB_ST_STALLED) {
+				status=us->pusb_dev->status;
+				USB_STOR_PRINTF(" stall during command found, clear pipe\n");
+				usb_clear_halt(us->pusb_dev,  usb_sndctrlpipe(us->pusb_dev,0));
+				us->pusb_dev->status=status;
+			}
+			USB_STOR_PRINTF(" error during command %02X Stat = %X\n",srb->cmd[0],us->pusb_dev->status);
+			return result;
+		}
+		/* transfer the data payload for this command, if one exists*/
+
+		USB_STOR_PRINTF("CB_transport: control msg returned %d, direction is %s to go 0x%lx\n",result,dir_in ? "IN" : "OUT",srb->datalen);
+		if (srb->datalen) {
+			result = us_one_transfer(us, pipe, srb->pdata,srb->datalen);
+			USB_STOR_PRINTF("CBI attempted to transfer data, result is %d status %lX, len %d\n", result,us->pusb_dev->status,us->pusb_dev->act_len);
+			if(!(us->pusb_dev->status & USB_ST_NAK_REC))
+				break;
+		} /* if (srb->datalen) */
+		else
+			break;
+	}
+	/* return result */
+
+	return result;
+}
+
+
+int usb_stor_CBI_get_status(ccb *srb, struct us_data *us)
+{
+	int timeout;
+
+	us->ip_wanted=1;
+	submit_int_msg(us->pusb_dev,us->irqpipe,
+			(void *)&us->ip_data,us->irqmaxp ,us->irqinterval);
+  timeout=1000;
+  while(timeout--) {
+  	if((volatile int *)us->ip_wanted==0)
+			break;
+		wait_ms(10);
+	}
+	if (us->ip_wanted) {
+		printf("       Did not get interrupt on CBI\n");
+		us->ip_wanted = 0;
+		return USB_STOR_TRANSPORT_ERROR;
+	}
+	USB_STOR_PRINTF("Got interrupt data 0x%x, transfered %d status 0x%lX\n", us->ip_data,us->pusb_dev->irq_act_len,us->pusb_dev->irq_status);
+	/* UFI gives us ASC and ASCQ, like a request sense */
+	if (us->subclass == US_SC_UFI) {
+		if (srb->cmd[0] == SCSI_REQ_SENSE ||
+		    srb->cmd[0] == SCSI_INQUIRY)
+			return USB_STOR_TRANSPORT_GOOD; /* Good */
+		else
+			if (us->ip_data)
+				return USB_STOR_TRANSPORT_FAILED;
+			else
+				return USB_STOR_TRANSPORT_GOOD;
+	}
+	/* otherwise, we interpret the data normally */
+	switch (us->ip_data) {
+		case 0x0001:
+			return USB_STOR_TRANSPORT_GOOD;
+		case 0x0002:
+			return USB_STOR_TRANSPORT_FAILED;
+		default:
+			return USB_STOR_TRANSPORT_ERROR;
+	} /* switch */
+	return USB_STOR_TRANSPORT_ERROR;
+}
+
+#define USB_TRANSPORT_UNKNOWN_RETRY 5
+#define USB_TRANSPORT_NOT_READY_RETRY 10
+
+int usb_stor_CB_transport(ccb *srb, struct us_data *us)
+{
+	int result,status;
+	ccb *psrb;
+	ccb reqsrb;
+	int retry,notready;
+
+	psrb=&reqsrb;
+	status=USB_STOR_TRANSPORT_GOOD;
+	retry=0;
+	notready=0;
+	/* issue the command */
+do_retry:
+	result=usb_stor_CB_comdat(srb,us);
+	USB_STOR_PRINTF("command / Data returned %d, status %X\n",result,us->pusb_dev->status);
+	/* if this is an CBI Protocol, get IRQ */
+	if(us->protocol==US_PR_CBI) {
+		status=usb_stor_CBI_get_status(srb,us);
+		/* if the status is error, report it */
+		if(status==USB_STOR_TRANSPORT_ERROR) {
+			USB_STOR_PRINTF(" USB CBI Command Error\n");
+			return status;
+		}
+		srb->sense_buf[12]=(unsigned char)(us->ip_data>>8);
+		srb->sense_buf[13]=(unsigned char)(us->ip_data&0xff);
+		if(!us->ip_data) {
+		/* if the status is good, report it */
+			if(status==USB_STOR_TRANSPORT_GOOD) {
+				USB_STOR_PRINTF(" USB CBI Command Good\n");
+				return status;
+			}
+		}
+	}
+	/* do we have to issue an auto request? */
+	/* HERE we have to check the result */
+	if((result<0) && !(us->pusb_dev->status & USB_ST_STALLED)) {
+		USB_STOR_PRINTF("ERROR %X\n",us->pusb_dev->status);
+		us->transport_reset(us);
+		return USB_STOR_TRANSPORT_ERROR;
+	}
+	if((us->protocol==US_PR_CBI) &&
+			((srb->cmd[0]==SCSI_REQ_SENSE) ||
+		 	(srb->cmd[0]==SCSI_INQUIRY))) { /* do not issue an autorequest after request sense */
+		USB_STOR_PRINTF("No auto request and good\n");
+		return USB_STOR_TRANSPORT_GOOD;
+	}
+	/* issue an request_sense */
+	memset(&psrb->cmd[0],0,12);
+	psrb->cmd[0]=SCSI_REQ_SENSE;
+	psrb->cmd[1]=srb->lun<<5;
+	psrb->cmd[4]=18;
+	psrb->datalen=18;
+	psrb->pdata=&srb->sense_buf[0];
+	psrb->cmdlen=12;
+	/* issue the command */
+	result=usb_stor_CB_comdat(psrb,us);
+	USB_STOR_PRINTF("auto request returned %d\n",result);
+	/* if this is an CBI Protocol, get IRQ */
+	if(us->protocol==US_PR_CBI) {
+	 	status=usb_stor_CBI_get_status(psrb,us);
+	}
+	if((result<0)&&!(us->pusb_dev->status & USB_ST_STALLED)) {
+		USB_STOR_PRINTF(" AUTO REQUEST ERROR %d\n",us->pusb_dev->status);
+		return USB_STOR_TRANSPORT_ERROR;
+	}
+	USB_STOR_PRINTF("autorequest returned 0x%02X 0x%02X 0x%02X 0x%02X\n",srb->sense_buf[0],srb->sense_buf[2],srb->sense_buf[12],srb->sense_buf[13]);
+	/* Check the auto request result */
+	if((srb->sense_buf[2]==0) &&
+		 (srb->sense_buf[12]==0) &&
+		 (srb->sense_buf[13]==0)) /* ok, no sense */
+		return USB_STOR_TRANSPORT_GOOD;
+	/* Check the auto request result */
+	switch(srb->sense_buf[2]) {
+		case 0x01: /* Recovered Error */
+			return USB_STOR_TRANSPORT_GOOD;
+		 	break;
+		case 0x02: /* Not Ready */
+			if(notready++ > USB_TRANSPORT_NOT_READY_RETRY) {
+				printf("cmd 0x%02X returned 0x%02X 0x%02X 0x%02X 0x%02X (NOT READY)\n",
+					srb->cmd[0],srb->sense_buf[0],srb->sense_buf[2],srb->sense_buf[12],srb->sense_buf[13]);
+				return USB_STOR_TRANSPORT_FAILED;
+			}
+			else {
+				wait_ms(100);
+				goto do_retry;
+			}
+			break;
+		default:
+			if(retry++ > USB_TRANSPORT_UNKNOWN_RETRY) {
+				printf("cmd 0x%02X returned 0x%02X 0x%02X 0x%02X 0x%02X\n",
+					srb->cmd[0],srb->sense_buf[0],srb->sense_buf[2],srb->sense_buf[12],srb->sense_buf[13]);
+				return USB_STOR_TRANSPORT_FAILED;
+			}
+			else
+				goto do_retry;
+			break;
+	}
+	return USB_STOR_TRANSPORT_FAILED;
+}
+
+
+
+static int usb_inquiry(ccb *srb,struct us_data *ss)
+{
+	int retry,i;
+	retry=3;
+	do {
+		memset(&srb->cmd[0],0,12);
+		srb->cmd[0]=SCSI_INQUIRY;
+		srb->cmd[1]=srb->lun<<5;
+		srb->cmd[4]=36;
+		srb->datalen=36;
+		srb->cmdlen=12;
+		i=ss->transport(srb,ss);
+		USB_STOR_PRINTF("inquiry returns %d\n",i);
+		if(i==0)
+			break;
+	}while(retry--);
+	if(!retry) {
+		printf("error in inquiry\n");
+		return -1;
+	}
+	return 0;
+}
+
+static int usb_request_sense(ccb *srb,struct us_data *ss)
+{
+	char *ptr;
+	return 0;
+	ptr=srb->pdata;
+	memset(&srb->cmd[0],0,12);
+	srb->cmd[0]=SCSI_REQ_SENSE;
+	srb->cmd[1]=srb->lun<<5;
+	srb->cmd[4]=18;
+	srb->datalen=18;
+	srb->pdata=&srb->sense_buf[0];
+	srb->cmdlen=12;
+	ss->transport(srb,ss);
+	USB_STOR_PRINTF("Request Sense returned %02X %02X %02X\n",srb->sense_buf[2],srb->sense_buf[12],srb->sense_buf[13]);
+	srb->pdata=ptr;
+	return 0;
+}
+
+static int usb_test_unit_ready(ccb *srb,struct us_data *ss)
+{
+	int retries=10;
+	do {
+		memset(&srb->cmd[0],0,12);
+		srb->cmd[0]=SCSI_TST_U_RDY;
+		srb->cmd[1]=srb->lun<<5;
+		srb->datalen=0;
+		srb->cmdlen=12;
+		if(ss->transport(srb,ss)==USB_STOR_TRANSPORT_GOOD)
+		{
+			return 0;
+		}
+	} while(retries--);
+	return -1;
+}
+
+static int usb_read_capacity(ccb *srb,struct us_data *ss)
+{
+	int retry;
+	retry=2; /* retries */
+	do {
+		memset(&srb->cmd[0],0,12);
+		srb->cmd[0]=SCSI_RD_CAPAC;
+		srb->cmd[1]=srb->lun<<5;
+		srb->datalen=8;
+		srb->cmdlen=12;
+		if(ss->transport(srb,ss)==USB_STOR_TRANSPORT_GOOD) {
+			return 0;
+		}
+	}while(retry--);
+	return -1;
+}
+
+static int usb_read_10(ccb *srb,struct us_data *ss, unsigned long start, unsigned short blocks)
+{
+	memset(&srb->cmd[0],0,12);
+	srb->cmd[0]=SCSI_READ10;
+	srb->cmd[1]=srb->lun<<5;
+	srb->cmd[2]=((unsigned char) (start>>24))&0xff;
+	srb->cmd[3]=((unsigned char) (start>>16))&0xff;
+	srb->cmd[4]=((unsigned char) (start>>8))&0xff;
+	srb->cmd[5]=((unsigned char) (start))&0xff;
+	srb->cmd[7]=((unsigned char) (blocks>>8))&0xff;
+	srb->cmd[8]=(unsigned char) blocks & 0xff;
+	srb->cmdlen=12;
+	USB_STOR_PRINTF("read10: start %lx blocks %x\n",start,blocks);
+	return ss->transport(srb,ss);
+}
+
+
+#define USB_MAX_READ_BLK 20
+
+unsigned long usb_stor_read(int device, unsigned long blknr, unsigned long blkcnt, unsigned long *buffer)
+{
+	unsigned long start,blks, buf_addr;
+	unsigned short smallblks;
+	struct usb_device *dev;
+	int retry,i;
+	ccb *srb=&usb_ccb;
+	device&=0xff;
+	/* Setup  device
+	 */
+	USB_STOR_PRINTF("\nusb_read: dev %d \n",device);
+	dev=NULL;
+	for(i=0;i<USB_MAX_DEVICE;i++) {
+		dev=usb_get_dev_index(i);
+		if(dev==NULL) {
+			return 0;
+		}
+		if(dev->devnum==usb_dev_desc[device].target)
+			break;
+	}
+
+	usb_disable_asynch(1); /* asynch transfer not allowed */
+	srb->lun=usb_dev_desc[device].lun;
+	buf_addr=(unsigned long)buffer;
+	start=blknr;
+	blks=blkcnt;
+	if(usb_test_unit_ready(srb,(struct us_data *)dev->privptr)) {
+		printf("Device NOT ready\n   Request Sense returned %02X %02X %02X\n",
+			srb->sense_buf[2],srb->sense_buf[12],srb->sense_buf[13]);
+		return 0;
+	}
+	USB_STOR_PRINTF("\nusb_read: dev %d startblk %lx, blccnt %lx buffer %lx\n",device,start,blks, buf_addr);
+	do {
+		retry=2;
+		srb->pdata=(unsigned char *)buf_addr;
+		if(blks>USB_MAX_READ_BLK) {
+			smallblks=USB_MAX_READ_BLK;
+		}
+		else {
+			smallblks=(unsigned short) blks;
+		}
+retry_it:
+		if(smallblks==USB_MAX_READ_BLK)
+			usb_show_progress();
+		srb->datalen=usb_dev_desc[device].blksz * smallblks;
+		srb->pdata=(unsigned char *)buf_addr;
+		if(usb_read_10(srb,(struct us_data *)dev->privptr, start, smallblks)) {
+			USB_STOR_PRINTF("Read ERROR\n");
+			usb_request_sense(srb,(struct us_data *)dev->privptr);
+			if(retry--)
+				goto retry_it;
+			blkcnt-=blks;
+			break;
+		}
+		start+=smallblks;
+		blks-=smallblks;
+		buf_addr+=srb->datalen;
+	} while(blks!=0);
+	USB_STOR_PRINTF("usb_read: end startblk %lx, blccnt %x buffer %lx\n",start,smallblks,buf_addr);
+	usb_disable_asynch(0); /* asynch transfer allowed */
+	if(blkcnt>=USB_MAX_READ_BLK)
+		printf("\n");
+	return(blkcnt);
+}
+
+
+/* Probe to see if a new device is actually a Storage device */
+int usb_storage_probe(struct usb_device *dev, unsigned int ifnum,struct us_data *ss)
+{
+	struct usb_interface_descriptor *iface;
+	int i;
+	unsigned int flags = 0;
+
+	int protocol = 0;
+	int subclass = 0;
+
+
+	memset(ss, 0, sizeof(struct us_data));
+
+	/* let's examine the device now */
+	iface = &dev->config.if_desc[ifnum];
+
+#if 0
+	/* this is the place to patch some storage devices */
+	USB_STOR_PRINTF("iVendor %X iProduct %X\n",dev->descriptor.idVendor,dev->descriptor.idProduct);
+	if ((dev->descriptor.idVendor) == 0x066b && (dev->descriptor.idProduct) == 0x0103) {
+		USB_STOR_PRINTF("patched for E-USB\n");
+		protocol = US_PR_CB;
+		subclass = US_SC_UFI;	    /* an assumption */
+	}
+#endif
+
+	if (dev->descriptor.bDeviceClass != 0 ||
+			iface->bInterfaceClass != USB_CLASS_MASS_STORAGE ||
+			iface->bInterfaceSubClass < US_SC_MIN ||
+			iface->bInterfaceSubClass > US_SC_MAX) {
+		/* if it's not a mass storage, we go no further */
+		return 0;
+	}
+
+	/* At this point, we know we've got a live one */
+	USB_STOR_PRINTF("\n\nUSB Mass Storage device detected\n");
+
+	/* Initialize the us_data structure with some useful info */
+	ss->flags = flags;
+	ss->ifnum = ifnum;
+	ss->pusb_dev = dev;
+	ss->attention_done = 0;
+
+	/* If the device has subclass and protocol, then use that.  Otherwise,
+	 * take data from the specific interface.
+	 */
+	if (subclass) {
+		ss->subclass = subclass;
+		ss->protocol = protocol;
+	} else {
+		ss->subclass = iface->bInterfaceSubClass;
+		ss->protocol = iface->bInterfaceProtocol;
+	}
+
+	/* set the handler pointers based on the protocol */
+	USB_STOR_PRINTF("Transport: ");
+	switch (ss->protocol) {
+	case US_PR_CB:
+		USB_STOR_PRINTF("Control/Bulk\n");
+		ss->transport = usb_stor_CB_transport;
+		ss->transport_reset = usb_stor_CB_reset;
+		break;
+
+	case US_PR_CBI:
+		USB_STOR_PRINTF("Control/Bulk/Interrupt\n");
+		ss->transport = usb_stor_CB_transport;
+		ss->transport_reset = usb_stor_CB_reset;
+		break;
+	default:
+		printf("USB Starage Transport unknown / not yet implemented\n");
+		return 0;
+		break;
+	}
+
+	/*
+	 * We are expecting a minimum of 2 endpoints - in and out (bulk).
+	 * An optional interrupt is OK (necessary for CBI protocol).
+	 * We will ignore any others.
+	 */
+	for (i = 0; i < iface->bNumEndpoints; i++) {
+		/* is it an BULK endpoint? */
+		if ((iface->ep_desc[i].bmAttributes &  USB_ENDPOINT_XFERTYPE_MASK)
+		    == USB_ENDPOINT_XFER_BULK) {
+			if (iface->ep_desc[i].bEndpointAddress & USB_DIR_IN)
+				ss->ep_in = iface->ep_desc[i].bEndpointAddress &
+					USB_ENDPOINT_NUMBER_MASK;
+			else
+				ss->ep_out = iface->ep_desc[i].bEndpointAddress &
+					USB_ENDPOINT_NUMBER_MASK;
+		}
+
+		/* is it an interrupt endpoint? */
+		if ((iface->ep_desc[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+		    == USB_ENDPOINT_XFER_INT) {
+			ss->ep_int = iface->ep_desc[i].bEndpointAddress &
+				USB_ENDPOINT_NUMBER_MASK;
+			ss->irqinterval = iface->ep_desc[i].bInterval;
+		}
+	}
+	USB_STOR_PRINTF("Endpoints In %d Out %d Int %d\n",
+		  ss->ep_in, ss->ep_out, ss->ep_int);
+
+	/* Do some basic sanity checks, and bail if we find a problem */
+	if (usb_set_interface(dev, iface->bInterfaceNumber, 0) ||
+	    !ss->ep_in || !ss->ep_out ||
+	    (ss->protocol == US_PR_CBI && ss->ep_int == 0)) {
+		USB_STOR_PRINTF("Problems with device\n");
+		return 0;
+	}
+	/* set class specific stuff */
+	/* We only handle certain protocols.  Currently, this is
+	 * the only one.
+	 */
+	if (ss->subclass != US_SC_UFI) {
+		printf("Sorry, protocol %d not yet supported.\n",ss->subclass);
+		return 0;
+	}
+	if(ss->ep_int) /* we had found an interrupt endpoint, prepare irq pipe */
+	{
+		/* set up the IRQ pipe and handler */
+
+		ss->irqinterval = (ss->irqinterval > 0) ? ss->irqinterval : 255;
+		ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
+		ss->irqmaxp = usb_maxpacket(dev, ss->irqpipe);
+		dev->irq_handle=usb_stor_irq;
+		dev->privptr=(void *)ss;
+	}
+	return 1;
+}
+
+int usb_stor_get_info(struct usb_device *dev,struct us_data *ss,block_dev_desc_t *dev_desc)
+{
+	unsigned char perq,modi;
+	unsigned long cap[2];
+	unsigned long *capacity,*blksz;
+	ccb *pccb=&usb_ccb;
+
+	ss->transport_reset(ss);
+	pccb->pdata=usb_stor_buf;
+
+	dev_desc->target=dev->devnum;
+	pccb->lun=dev_desc->lun;
+	USB_STOR_PRINTF(" address %d\n",dev_desc->target);
+
+	if(usb_inquiry(pccb,ss))
+		return -1;
+	perq=usb_stor_buf[0];
+	modi=usb_stor_buf[1];
+	if((perq & 0x1f)==0x1f) {
+		return 0; /* skip unknown devices */
+	}
+	if((modi&0x80)==0x80) {/* drive is removable */
+		dev_desc->removable=1;
+	}
+	memcpy(&dev_desc->vendor[0], &usb_stor_buf[8], 8);
+	memcpy(&dev_desc->product[0], &usb_stor_buf[16], 16);
+	memcpy(&dev_desc->revision[0], &usb_stor_buf[32], 4);
+	dev_desc->vendor[8]=0;
+	dev_desc->product[16]=0;
+	dev_desc->revision[4]=0;
+	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)) {
+		printf("Device NOT ready\n   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;
+		}
+		else
+			return 0;
+	}
+	pccb->pdata=(unsigned char *)&cap[0];
+	memset(pccb->pdata,0,8);
+	if(usb_read_capacity(pccb,ss)!=0) {
+		printf("READ_CAP ERROR\n");
+		cap[0]=2880;
+		cap[1]=0x200;
+	}
+	USB_STOR_PRINTF("Read Capacity returns: 0x%lx, 0x%lx\n",cap[0],cap[1]);
+#if 0
+	if(cap[0]>(0x200000 * 10)) /* greater than 10 GByte */
+		cap[0]>>=16;
+#endif
+	cap[0]+=1;
+	capacity=&cap[0];
+	blksz=&cap[1];
+	USB_STOR_PRINTF("Capacity = 0x%lx, blocksz = 0x%lx\n",*capacity,*blksz);
+	dev_desc->lba=*capacity;
+	dev_desc->blksz=*blksz;
+	dev_desc->type=perq;
+	USB_STOR_PRINTF(" address %d\n",dev_desc->target);
+	USB_STOR_PRINTF("partype: %d\n",dev_desc->part_type);
+
+	init_part(dev_desc);
+
+	USB_STOR_PRINTF("partype: %d\n",dev_desc->part_type);
+	return 1;
+}
+
+#endif
+#endif /* CONFIG_USB_STORAGE */
+
+
+