diff --git a/CHANGELOG b/CHANGELOG
index c774dd0..bb45443 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,8 @@
 ======================================================================
 Changes since U-Boot 1.1.4:
 ======================================================================
+* Add strerror() to support Rockbox FAT filesystem driver
+  Patch by Keith Outwater, 4 May 2006
 
 * Coding Style cleanup
 
diff --git a/include/asm-arm/errno.h b/include/asm-arm/errno.h
index 134a8fc..ff5d109 100644
--- a/include/asm-arm/errno.h
+++ b/include/asm-arm/errno.h
@@ -1,6 +1,10 @@
 #ifndef _ARM_ERRNO_H
 #define _ARM_ERRNO_H
 
+#ifdef CFG_ROCKBOX_FAT
+extern int errno;
+#endif
+
 #define	EPERM		 1	/* Operation not permitted */
 #define	ENOENT		 2	/* No such file or directory */
 #define	ESRCH		 3	/* No such process */
diff --git a/include/asm-ppc/errno.h b/include/asm-ppc/errno.h
index ff364b8..1c5a11a 100644
--- a/include/asm-ppc/errno.h
+++ b/include/asm-ppc/errno.h
@@ -1,6 +1,10 @@
 #ifndef _PPC_ERRNO_H
 #define _PPC_ERRNO_H
 
+#ifdef CFG_ROCKBOX_FAT
+extern int errno;
+#endif
+
 #define	EPERM		 1	/* Operation not permitted */
 #define	ENOENT		 2	/* No such file or directory */
 #define	ESRCH		 3	/* No such process */
diff --git a/include/linux/string.h b/include/linux/string.h
index 6239039..f8ca773 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -14,6 +14,9 @@ extern char * strtok(char *,const char *
 extern char * strsep(char **,const char *);
 extern __kernel_size_t strspn(const char *,const char *);
 
+#ifdef CONFIG_STRERROR
+extern char * strerror(int errno);
+#endif
 
 /*
  * Include machine specific inline routines
diff --git a/lib_generic/Makefile b/lib_generic/Makefile
index 18c41b1..23ed19a 100644
--- a/lib_generic/Makefile
+++ b/lib_generic/Makefile
@@ -28,7 +28,7 @@ LIB	= libgeneric.a
 OBJS	= bzlib.o bzlib_crctable.o bzlib_decompress.o \
 	  bzlib_randtable.o bzlib_huffman.o \
 	  crc32.o ctype.o display_options.o ldiv.o \
-	  string.o vsprintf.o zlib.o
+	  string.o vsprintf.o zlib.o strerror.o
 
 $(LIB):	.depend $(OBJS)
 	$(AR) crv $@ $(OBJS)
diff --git a/lib_generic/strerror.c b/lib_generic/strerror.c
new file mode 100644
index 0000000..3747b50
--- /dev/null
+++ b/lib_generic/strerror.c
@@ -0,0 +1,497 @@
+/*------------------------------------------------------------------------------
+ *
+ * (C) Copyright 2006
+ * Keith Outwater, outwater4@comcast.net
+ *
+ * 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 <config.h>
+
+#if defined(CONFIG_STRERROR)
+#include <asm/errno.h>
+
+/*
+ * Some messages that are unlikely to be used have been left off the list.
+ */
+char *
+strerror(int errno)
+{
+	char *str;
+
+	switch(errno) {
+#ifdef EPERM
+		case EPERM:
+			str = "Operation not permitted";
+			break;
+#endif
+#ifdef ENOENT
+		case ENOENT:
+			str = "No such file or directory";
+			break;
+#endif
+#ifdef ESRCH
+		case ESRCH:
+			str = "No such process";
+			break;
+#endif
+#ifdef EINTR
+		case EINTR:
+			str = "Interrupted system call";
+			break;
+#endif
+#ifdef EIO
+		case EIO:
+			str = "I/O error";
+			break;
+#endif
+#ifdef ENXIO
+		case ENXIO:
+			str = "No such device or address";
+			break;
+#endif
+#ifdef E2BIG
+		case E2BIG:
+			str = "Arg list too long";
+			break;
+#endif
+#ifdef ENOEXEC
+		case ENOEXEC:
+			str = "Exec format error";
+			break;
+#endif
+#ifdef EBADF
+		case EBADF:
+			str = "Bad file number";
+			break;
+#endif
+#ifdef ECHILD
+		case ECHILD:
+			str = "No child processes";
+			break;
+#endif
+#ifdef EAGAIN
+		case EAGAIN:
+			str = "Try again";
+			break;
+#endif
+#ifdef ENOMEM
+		case ENOMEM:
+			str = "Out of memory";
+			break;
+#endif
+#ifdef EACCES
+		case EACCES:
+			str = "Permission denied";
+			break;
+#endif
+#ifdef EFAULT
+		case EFAULT:
+			str = "Bad address";
+			break;
+#endif
+#ifdef ENOTBLK
+		case ENOTBLK:
+			str = "Block device required";
+			break;
+#endif
+#ifdef EBUSY
+		case EBUSY:
+			str = "Device or resource busy";
+			break;
+#endif
+#ifdef EEXIST
+		case EEXIST:
+			str = "File exists";
+			break;
+#endif
+#ifdef ENODEV
+		case ENODEV:
+			str = "No such device";
+			break;
+#endif
+#ifdef ENOTDIR
+		case ENOTDIR:
+			str = "Not a directory";
+			break;
+#endif
+#ifdef EISDIR
+		case EISDIR:
+			str = "Is a directory";
+			break;
+#endif
+#ifdef EINVAL
+		case EINVAL:
+			str = "Invalid argument";
+			break;
+#endif
+#ifdef ENFILE
+		case ENFILE:
+			str = "File table overflow";
+			break;
+#endif
+#ifdef EMFILE
+		case EMFILE:
+			str = "Too many open files";
+			break;
+#endif
+#ifdef ENOTTY
+		case ENOTTY:
+			str = "Not a typewrite";
+			break;
+#endif
+#ifdef ETXTBSY
+		case ETXTBSY:
+			str = "Text file busy";
+			break;
+#endif
+#ifdef EFBIG
+		case EFBIG:
+			str = "File too large";
+			break;
+#endif
+#ifdef ENOSPC
+		case ENOSPC:
+			str = "No space left on device";
+			break;
+#endif
+#ifdef ESPIPE
+		case ESPIPE:
+			str = "Illegal seek";
+			break;
+#endif
+#ifdef EROFS
+		case EROFS:
+			str = "Read-only file system";
+			break;
+#endif
+#ifdef EMLINK
+		case EMLINK:
+			str = "Too many links";
+			break;
+#endif
+#ifdef EPIPE
+		case EPIPE:
+			str = "Broken pipe";
+			break;
+#endif
+#ifdef ERANGE
+		case ERANGE:
+			str = "Math result not representable";
+			break;
+#endif
+#ifdef EDEADLK
+		case EDEADLK:
+			str = "Resource deadlock would occur";
+			break;
+#endif
+#ifdef ENAMETOOLONG
+		case ENAMETOOLONG:
+			str = "File name too long";
+			break;
+#endif
+#ifdef ENOLCK
+		case ENOLCK:
+			str = "No record locks available";
+			break;
+#endif
+#ifdef ENOSYS
+		case ENOSYS:
+			str = "Function not implemented";
+			break;
+#endif
+#ifdef ENOTEMPTY
+		case ENOTEMPTY:
+			str = "Directory not empty";
+			break;
+#endif
+#ifdef ELOOP
+		case ELOOP:
+			str = "Too many symbolic links encountered";
+			break;
+#endif
+#ifdef ENOMSG
+		case ENOMSG:
+			str = "No message of desired type";
+			break;
+#endif
+#ifdef EIDRM
+		case EIDRM:
+			str = "Identifier removed";
+			break;
+#endif
+#ifdef ECHRNG
+		case ECHRNG:
+			str = "Channel number out of range";
+			break;
+#endif
+#ifdef EBADRQC
+		case EBADRQC:
+			str = "Invalid request code";
+			break;
+#endif
+#ifdef EBADSLT
+		case EBADSLT:
+			str = "Invalid slot";
+			break;
+#endif
+#ifdef EDEADLOCK
+		case EDEADLOCK:
+			str = "File locking deadlock error";
+			break;
+#endif
+#ifdef ENOSTR
+		case ENOSTR:
+			str = "Device not a stream";
+			break;
+#endif
+#ifdef ENODATA	
+		case ENODATA:
+			str = "No data available";
+			break;
+#endif
+#ifdef ETIME
+		case ETIME:
+			str = "Timer expired";
+			break;
+#endif
+#ifdef ENONET
+		case ENONET:
+			str = "Machine is not on the network";
+			break;
+#endif
+#ifdef ENOPKG
+		case ENOPKG:
+			str = "Package not installed";
+			break;
+#endif
+#ifdef ENOLINK
+		case ENOLINK:
+			str = "Link has been severed";
+			break;
+#endif
+#ifdef EADV
+		case EADV:
+			str = "Advertise error";
+			break;
+#endif
+#ifdef ECOMM
+		case ECOMM:
+			str = "Communication error on send";
+			break;
+#endif
+#ifdef EPROTO
+		case EPROTO:
+			str = "Protocol error";
+			break;
+#endif
+#ifdef EMULTIHOP
+		case EMULTIHOP:
+			str = "Multihop attempted";
+			break;
+#endif
+#ifdef EDOTDOT
+		case EDOTDOT:
+			str = "RFS specific error";
+			break;
+#endif
+#ifdef EBADMSG
+		case EBADMSG:
+			str = "Not a data message";
+			break;
+#endif
+#ifdef EOVERFLOW
+		case EOVERFLOW:
+			str = "Value too large for defined data type";
+			break;
+#endif
+#ifdef ENOTUNIQ
+		case ENOTUNIQ:
+			str = "Name not unique on network";
+			break;
+#endif
+#ifdef EBADFD
+		case EBADFD:
+			str = "File descriptor in bad state";
+			break;
+#endif
+#ifdef EREMCHG
+		case EREMCHG:
+			str = "Remote address changed";
+			break;
+#endif
+#ifdef ELIBACC
+		case ELIBACC:
+			str = "Can not access a needed shared library";
+			break;
+#endif
+#ifdef ELIBBAD
+		case ELIBBAD:
+			str = "Accessing a corrupted shared library";
+			break;
+#endif
+#ifdef ERESTART
+		case ERESTART:
+			str = "Interrupted system call should be restarted";
+			break;
+#endif
+#ifdef EDESTADDRREQ
+		case EDESTADDRREQ:
+			str = "Destination address required";
+			break;
+#endif
+#ifdef EMSGSIZE
+		case EMSGSIZE:
+			str = "Message too long";
+			break;
+#endif
+#ifdef EPROTONOSUPPORT
+		case EPROTONOSUPPORT:
+			str = "Protocol not supported";
+			break;
+#endif
+#ifdef EPFNOSUPPORT
+		case EPFNOSUPPORT:
+			str = "Protocol family not supported";
+			break;
+#endif
+#ifdef EAFNOSUPPORT
+		case EAFNOSUPPORT:
+			str = "Address family not supported by protocol";
+			break;
+#endif
+#ifdef EADDRINUSE
+		case EADDRINUSE:
+			str = "Address already in use";
+			break;
+#endif
+#ifdef EADDRNOTAVAIL
+		case EADDRNOTAVAIL:
+			str = "Cannot assign requested address";
+			break;
+#endif
+#ifdef ENETDOWN
+		case ENETDOWN:
+			str = "Network is down";
+			break;
+#endif
+#ifdef ENETUNREACH
+		case ENETUNREACH:
+			str = "Network is unreachable";
+			break;
+#endif
+#ifdef ENETRESET
+		case ENETRESET:
+			str = "Network dropped connection because of reset";
+			break;
+#endif
+#ifdef ECONNRESET
+		case ECONNRESET:
+			str = "Connection reset by peer";
+			break;
+#endif
+#ifdef ENOBUFS
+		case ENOBUFS:
+			str = "No buffer space available";
+			break;
+#endif
+#ifdef EISCONN
+		case EISCONN:
+			str = "Transport endpoint is already connected";
+			break;
+#endif
+#ifdef ENOTCONN
+		case ENOTCONN:
+			str = "Transport endpoint is not connected";
+			break;
+#endif
+#ifdef ESHUTDOWN
+		case ESHUTDOWN:
+			str = "Cannot send after transport endpoint shutdown";
+			break;
+#endif
+#ifdef ETIMEDOUT
+		case ETIMEDOUT:
+			str = "Connection timed out";
+			break;
+#endif
+#ifdef ECONNREFUSED
+		case ECONNREFUSED:
+			str = "Connection refused";
+			break;
+#endif
+#ifdef EHOSTDOWN
+		case EHOSTDOWN:
+			str = "Host is down";
+			break;
+#endif
+#ifdef EHOSTUNREACH
+		case EHOSTUNREACH:
+			str = "No route to host";
+			break;
+#endif
+#ifdef EALREADY
+		case EALREADY:
+			str = "Operation already in progress";
+			break;
+#endif
+#ifdef EINPROGRESS
+		case EINPROGRESS:
+			str = "Operation now in progress";
+			break;
+#endif
+#ifdef ESTALE
+		case ESTALE:
+			str = "Stale NFS file handle";
+			break;
+#endif
+#ifdef EUCLEAN
+		case EUCLEAN:
+			str = "Structure needs cleaning";
+			break;
+#endif
+#ifdef EISNAM
+		case EISNAM:
+			str = "Is a named type file";
+			break;
+#endif
+#ifdef EREMOTEIO
+		case EREMOTEIO:
+			str = "Remote I/O error";
+			break;
+#endif
+#ifdef EMEDIUMTYPE
+		case EMEDIUMTYPE:
+			str = "Wrong medium type";
+			break;
+#endif
+		default:
+#ifdef CONFIG_BSP_STRERROR
+			if (str = bsp_strerror(errno) == 0)
+				str = "";
+#else
+			str = "(no message)";
+#endif
+	}
+
+	return str;
+}
+#endif /* #if defined(CONFIG_STRERROR) */
+/* vim: set ts=4 tw=80 sw=4 fo=tcroq: */
