diff options
Diffstat (limited to 'common')
| -rw-r--r-- | common/docecc.c | 519 | ||||
| -rw-r--r-- | common/flash.c | 219 | ||||
| -rw-r--r-- | common/lists.c | 734 | ||||
| -rw-r--r-- | common/miiphybb.c | 231 | ||||
| -rw-r--r-- | common/s_record.c | 195 | ||||
| -rw-r--r-- | common/usb.c | 1066 | ||||
| -rw-r--r-- | common/usb_kbd.c | 734 | ||||
| -rw-r--r-- | common/usb_storage.c | 895 | 
8 files changed, 4593 insertions, 0 deletions
| diff --git a/common/docecc.c b/common/docecc.c new file mode 100644 index 000000000..09e8233d8 --- /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(®[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 000000000..fa8942b92 --- /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 000000000..3f117b568 --- /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 000000000..dfc19922b --- /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 000000000..c52bf1bb6 --- /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 000000000..a5b29a56b --- /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 000000000..ad7e6100e --- /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 000000000..b1347219a --- /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 */ + + + |