changeset 213:3a5832bf0564

Merge from eCos master repository on 2002-05-31-16:13:14-BST
author jlarmour
date Fri, 31 May 2002 17:10:26 +0000
parents 94b558c9fb67
children 7788cd39a178
files packages/io/usb/slave/current/host/Makefile.am packages/io/usb/slave/current/host/Makefile.in packages/io/usb/slave/current/host/acinclude.m4 packages/io/usb/slave/current/host/aclocal.m4 packages/io/usb/slave/current/host/configure packages/io/usb/slave/current/host/configure.in packages/io/usb/slave/current/host/list.tcl packages/io/usb/slave/current/host/usbchmod.c packages/io/usb/slave/current/host/usbhost.c packages/io/usb/slave/current/host/usbhost.tcl packages/io/usb/slave/current/host/verbose.tcl packages/io/usb/slave/current/tests/common.c packages/io/usb/slave/current/tests/protocol.h packages/io/usb/slave/current/tests/usbtarget.c packages/redboot/current/ChangeLog packages/redboot/current/doc/redboot_installing.sgml
diffstat 16 files changed, 8700 insertions(+), 84 deletions(-) [+]
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/packages/io/usb/slave/current/host/Makefile.am
@@ -0,0 +1,103 @@
+## Process this file with automake to produce Makefile.in
+## =====================================================================
+##
+##     Makefile.am
+##
+##     Build support for the eCos USB host-side
+##     
+##
+## =====================================================================
+#####ECOSGPLCOPYRIGHTBEGIN####
+## -------------------------------------------
+## This file is part of eCos, the Embedded Configurable Operating System.
+## Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+##
+## eCos 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 or (at your option) any later version.
+##
+## eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
+## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+##
+## As a special exception, if other files instantiate templates or use macros
+## or inline functions from this file, or you compile this file and link it
+## with other works to produce a work based on this file, this file does not
+## by itself cause the resulting work to be covered by the GNU General Public
+## License. However the source code for this file must still be made available
+## in accordance with section (3) of the GNU General Public License.
+##
+## This exception does not invalidate any other reasons why a work based on
+## this file might be covered by the GNU General Public License.
+##
+## Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+## at http://sources.redhat.com/ecos/ecos-license/
+## -------------------------------------------
+#####ECOSGPLCOPYRIGHTEND####
+## =====================================================================
+#######DESCRIPTIONBEGIN####
+##
+## Author(s):	bartv
+## Contact(s):	bartv
+## Date:	2002/01/10
+## Version:	0.01
+##
+######DESCRIPTIONEND####
+## =====================================================================
+
+AUTOMAKE_OPTIONS = 1.3 foreign
+
+## Only some platforms are supported. Having the configure script throw
+## an error when attempting to configure on an unsupported platform
+## would be a mistake, since that would prevent any configury from
+## the toplevel on unsupported platforms. Instead an automake conditional
+## is used, leading to null makefiles on unsupported platforms.
+
+if SUPPORTED
+
+AM_CFLAGS       = @ecos_CFLAGS@ -DUSBHOST_VERSION=\"@VERSION@\" -DPKGVERSION=\"@PACKAGE_VERSION@\" -DUSBAUXDIR=\"$(libexecdir)/ecos/@PACKAGE_NAME@_@PACKAGE_VERSION@\"
+AM_CXXFLAGS     = @ecos_CXXFLAGS@
+INCLUDES        = @ecos_INCLUDES@
+LIBS            = @ecos_LIBS@ @ecos_LDADD@
+
+## The USB testing support consists of two programs, usbhost and
+## usbchmod, and a number of Tcl scripts. usbhost is executable by the
+## user so it should get installed in bindir, appropriately prefixed
+## and with a symbolic link installed as per e.g. tclsh (it is known
+## that this code is Linux-specific so the use of symbolic links is
+## not a problem). usbchmod is not directly executable so it should
+## get installed in or below libexec. The Tcl scripts are architecture
+## independent so should probably be installed below $(datadir), but
+## for now putting them in the same directory as usbchmod will do
+## fine.
+
+noinst_PROGRAMS		= usbhost
+usbhost_SOURCES		= usbhost.c
+usbhost_DEPENDENCIES	= ../tests/protocol.h ../tests/common.c
+usbhost_LDADD           = -lpthread
+
+usbauxdir		= $(libexecdir)/ecos/@PACKAGE_NAME@_@PACKAGE_VERSION@
+usbaux_PROGRAMS		= usbchmod
+usbaux_DATA		= usbhost.tcl list.tcl verbose.tcl
+usbchmod_SOURCES	= usbchmod.c
+
+## Two special operations are required during installation.
+## 1) usbhost needs to be installed suitably versioned, e.g. as
+##    usbhost_current, and with a symbolic link from usbhost to
+##    the new executable.
+## 2) usbchmod needs to be suid root
+install-exec-hook:
+	$(INSTALL_PROGRAM) usbhost $(bindir)/usbhost_@PACKAGE_VERSION@
+	rm -f $(bindir)/usbhost
+	$(LN_S) $(bindir)/usbhost_@PACKAGE_VERSION@ $(bindir)/usbhost
+
+install-data-hook:
+	chown root $(usbauxdir)/usbchmod
+	chmod u+s $(usbauxdir)/usbchmod
+
+endif
new file mode 100644
--- /dev/null
+++ b/packages/io/usb/slave/current/host/Makefile.in
@@ -0,0 +1,483 @@
+# Makefile.in generated automatically by automake 1.4 from Makefile.am
+
+# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+#####ECOSGPLCOPYRIGHTBEGIN####
+## -------------------------------------------
+## This file is part of eCos, the Embedded Configurable Operating System.
+## Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+##
+## eCos 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 or (at your option) any later version.
+##
+## eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
+## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+##
+## As a special exception, if other files instantiate templates or use macros
+## or inline functions from this file, or you compile this file and link it
+## with other works to produce a work based on this file, this file does not
+## by itself cause the resulting work to be covered by the GNU General Public
+## License. However the source code for this file must still be made available
+## in accordance with section (3) of the GNU General Public License.
+##
+## This exception does not invalidate any other reasons why a work based on
+## this file might be covered by the GNU General Public License.
+##
+## Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+## at http://sources.redhat.com/ecos/ecos-license/
+## -------------------------------------------
+#####ECOSGPLCOPYRIGHTEND####
+#######DESCRIPTIONBEGIN####
+######DESCRIPTIONEND####
+
+
+SHELL = @SHELL@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+
+DESTDIR =
+
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+
+top_builddir = .
+
+ACLOCAL = @ACLOCAL@
+AUTOCONF = @AUTOCONF@
+AUTOMAKE = @AUTOMAKE@
+AUTOHEADER = @AUTOHEADER@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS)
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+transform = @program_transform_name@
+
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+host_alias = @host_alias@
+host_triplet = @host@
+CC = @CC@
+CXX = @CXX@
+EXEEXT = @EXEEXT@
+LN_S = @LN_S@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MSVC_SRCDIR = @MSVC_SRCDIR@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+VERSION = @VERSION@
+ecos_CFLAGS = @ecos_CFLAGS@
+ecos_CXXFLAGS = @ecos_CXXFLAGS@
+ecos_INCLUDES = @ecos_INCLUDES@
+ecos_LDADD = @ecos_LDADD@
+ecos_LIBS = @ecos_LIBS@
+ecos_infra_incdir = @ecos_infra_incdir@
+ecos_infra_libdir = @ecos_infra_libdir@
+ecos_libcdl_incdir = @ecos_libcdl_incdir@
+ecos_libcdl_libdir = @ecos_libcdl_libdir@
+ecos_tk_libs = @ecos_tk_libs@
+
+AUTOMAKE_OPTIONS = 1.3 foreign
+
+@SUPPORTED_TRUE@AM_CFLAGS = @ecos_CFLAGS@ -DUSBHOST_VERSION=\"@VERSION@\" -DPKGVERSION=\"@PACKAGE_VERSION@\" -DUSBAUXDIR=\"$(libexecdir)/ecos/@PACKAGE_NAME@_@PACKAGE_VERSION@\"
+@SUPPORTED_TRUE@AM_CXXFLAGS = @ecos_CXXFLAGS@
+@SUPPORTED_TRUE@INCLUDES = @ecos_INCLUDES@
+@SUPPORTED_TRUE@LIBS = @ecos_LIBS@ @ecos_LDADD@
+
+@SUPPORTED_TRUE@noinst_PROGRAMS = usbhost
+@SUPPORTED_TRUE@usbhost_SOURCES = usbhost.c
+@SUPPORTED_TRUE@usbhost_DEPENDENCIES = ../tests/protocol.h ../tests/common.c
+@SUPPORTED_TRUE@usbhost_LDADD = -lpthread
+
+@SUPPORTED_TRUE@usbauxdir = $(libexecdir)/ecos/@PACKAGE_NAME@_@PACKAGE_VERSION@
+@SUPPORTED_TRUE@usbaux_PROGRAMS = usbchmod
+@SUPPORTED_TRUE@usbaux_DATA = usbhost.tcl list.tcl verbose.tcl
+@SUPPORTED_TRUE@usbchmod_SOURCES = usbchmod.c
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+mkinstalldirs = $(SHELL) $(top_srcdir)/../../../../../../acsupport/mkinstalldirs
+CONFIG_CLEAN_FILES = 
+@SUPPORTED_TRUE@noinst_PROGRAMS =  usbhost$(EXEEXT)
+@SUPPORTED_TRUE@usbaux_PROGRAMS =  usbchmod$(EXEEXT)
+PROGRAMS =  $(noinst_PROGRAMS) $(usbaux_PROGRAMS)
+
+
+DEFS = @DEFS@ -I. -I$(srcdir) 
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+@SUPPORTED_TRUE@usbhost_OBJECTS =  usbhost.$(OBJEXT)
+usbhost_LDFLAGS = 
+@SUPPORTED_TRUE@usbchmod_OBJECTS =  usbchmod.$(OBJEXT)
+usbchmod_LDADD = $(LDADD)
+usbchmod_DEPENDENCIES = 
+usbchmod_LDFLAGS = 
+CFLAGS = @CFLAGS@
+COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@
+DATA =  $(usbaux_DATA)
+
+DIST_COMMON =  Makefile.am Makefile.in acinclude.m4 aclocal.m4 configure \
+configure.in
+
+
+DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
+
+TAR = gtar
+GZIP_ENV = --best
+DEP_FILES =  .deps/usbchmod.P .deps/usbhost.P
+SOURCES = $(usbhost_SOURCES) $(usbchmod_SOURCES)
+OBJECTS = $(usbhost_OBJECTS) $(usbchmod_OBJECTS)
+
+all: all-redirect
+.SUFFIXES:
+.SUFFIXES: .S .c .o .obj .s
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) 
+	cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile
+
+Makefile: $(srcdir)/Makefile.in  $(top_builddir)/config.status $(BUILT_SOURCES)
+	cd $(top_builddir) \
+	  && CONFIG_FILES=$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ configure.in  acinclude.m4
+	cd $(srcdir) && $(ACLOCAL)
+
+config.status: $(srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	$(SHELL) ./config.status --recheck
+$(srcdir)/configure: @MAINTAINER_MODE_TRUE@$(srcdir)/configure.in $(ACLOCAL_M4) $(CONFIGURE_DEPENDENCIES)
+	cd $(srcdir) && $(AUTOCONF)
+
+mostlyclean-noinstPROGRAMS:
+
+clean-noinstPROGRAMS:
+	-test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS)
+
+distclean-noinstPROGRAMS:
+
+maintainer-clean-noinstPROGRAMS:
+
+mostlyclean-usbauxPROGRAMS:
+
+clean-usbauxPROGRAMS:
+	-test -z "$(usbaux_PROGRAMS)" || rm -f $(usbaux_PROGRAMS)
+
+distclean-usbauxPROGRAMS:
+
+maintainer-clean-usbauxPROGRAMS:
+
+install-usbauxPROGRAMS: $(usbaux_PROGRAMS)
+	@$(NORMAL_INSTALL)
+	$(mkinstalldirs) $(DESTDIR)$(usbauxdir)
+	@list='$(usbaux_PROGRAMS)'; for p in $$list; do \
+	  if test -f $$p; then \
+	    echo "  $(INSTALL_PROGRAM) $$p $(DESTDIR)$(usbauxdir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \
+	     $(INSTALL_PROGRAM) $$p $(DESTDIR)$(usbauxdir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
+	  else :; fi; \
+	done
+
+uninstall-usbauxPROGRAMS:
+	@$(NORMAL_UNINSTALL)
+	list='$(usbaux_PROGRAMS)'; for p in $$list; do \
+	  rm -f $(DESTDIR)$(usbauxdir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
+	done
+
+# FIXME: We should only use cygpath when building on Windows,
+# and only if it is available.
+.c.obj:
+	$(COMPILE) -c `cygpath -w $<`
+
+.s.o:
+	$(COMPILE) -c $<
+
+.S.o:
+	$(COMPILE) -c $<
+
+mostlyclean-compile:
+	-rm -f *.o core *.core
+	-rm -f *.$(OBJEXT)
+
+clean-compile:
+
+distclean-compile:
+	-rm -f *.tab.c
+
+maintainer-clean-compile:
+
+usbhost$(EXEEXT): $(usbhost_OBJECTS) $(usbhost_DEPENDENCIES)
+	@rm -f usbhost$(EXEEXT)
+	$(LINK) $(usbhost_LDFLAGS) $(usbhost_OBJECTS) $(usbhost_LDADD) $(LIBS)
+
+usbchmod$(EXEEXT): $(usbchmod_OBJECTS) $(usbchmod_DEPENDENCIES)
+	@rm -f usbchmod$(EXEEXT)
+	$(LINK) $(usbchmod_LDFLAGS) $(usbchmod_OBJECTS) $(usbchmod_LDADD) $(LIBS)
+
+install-usbauxDATA: $(usbaux_DATA)
+	@$(NORMAL_INSTALL)
+	$(mkinstalldirs) $(DESTDIR)$(usbauxdir)
+	@list='$(usbaux_DATA)'; for p in $$list; do \
+	  if test -f $(srcdir)/$$p; then \
+	    echo " $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(usbauxdir)/$$p"; \
+	    $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(usbauxdir)/$$p; \
+	  else if test -f $$p; then \
+	    echo " $(INSTALL_DATA) $$p $(DESTDIR)$(usbauxdir)/$$p"; \
+	    $(INSTALL_DATA) $$p $(DESTDIR)$(usbauxdir)/$$p; \
+	  fi; fi; \
+	done
+
+uninstall-usbauxDATA:
+	@$(NORMAL_UNINSTALL)
+	list='$(usbaux_DATA)'; for p in $$list; do \
+	  rm -f $(DESTDIR)$(usbauxdir)/$$p; \
+	done
+
+tags: TAGS
+
+ID: $(HEADERS) $(SOURCES) $(LISP)
+	list='$(SOURCES) $(HEADERS)'; \
+	unique=`for i in $$list; do echo $$i; done | \
+	  awk '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	here=`pwd` && cd $(srcdir) \
+	  && mkid -f$$here/ID $$unique $(LISP)
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) $(LISP)
+	tags=; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)'; \
+	unique=`for i in $$list; do echo $$i; done | \
+	  awk '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \
+	  || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags  $$unique $(LISP) -o $$here/TAGS)
+
+mostlyclean-tags:
+
+clean-tags:
+
+distclean-tags:
+	-rm -f TAGS ID
+
+maintainer-clean-tags:
+
+distdir = $(PACKAGE)-$(VERSION)
+top_distdir = $(distdir)
+
+# This target untars the dist file and tries a VPATH configuration.  Then
+# it guarantees that the distribution is self-contained by making another
+# tarfile.
+distcheck: dist
+	-rm -rf $(distdir)
+	GZIP=$(GZIP_ENV) $(TAR) zxf $(distdir).tar.gz
+	mkdir $(distdir)/=build
+	mkdir $(distdir)/=inst
+	dc_install_base=`cd $(distdir)/=inst && pwd`; \
+	cd $(distdir)/=build \
+	  && ../configure --srcdir=.. --prefix=$$dc_install_base \
+	  && $(MAKE) $(AM_MAKEFLAGS) \
+	  && $(MAKE) $(AM_MAKEFLAGS) dvi \
+	  && $(MAKE) $(AM_MAKEFLAGS) check \
+	  && $(MAKE) $(AM_MAKEFLAGS) install \
+	  && $(MAKE) $(AM_MAKEFLAGS) installcheck \
+	  && $(MAKE) $(AM_MAKEFLAGS) dist
+	-rm -rf $(distdir)
+	@banner="$(distdir).tar.gz is ready for distribution"; \
+	dashes=`echo "$$banner" | sed s/./=/g`; \
+	echo "$$dashes"; \
+	echo "$$banner"; \
+	echo "$$dashes"
+dist: distdir
+	-chmod -R a+r $(distdir)
+	GZIP=$(GZIP_ENV) $(TAR) chozf $(distdir).tar.gz $(distdir)
+	-rm -rf $(distdir)
+dist-all: distdir
+	-chmod -R a+r $(distdir)
+	GZIP=$(GZIP_ENV) $(TAR) chozf $(distdir).tar.gz $(distdir)
+	-rm -rf $(distdir)
+distdir: $(DISTFILES)
+	-rm -rf $(distdir)
+	mkdir $(distdir)
+	-chmod 777 $(distdir)
+	here=`cd $(top_builddir) && pwd`; \
+	top_distdir=`cd $(distdir) && pwd`; \
+	distdir=`cd $(distdir) && pwd`; \
+	cd $(top_srcdir) \
+	  && $(AUTOMAKE) --include-deps --build-dir=$$here --srcdir-name=$(top_srcdir) --output-dir=$$top_distdir --foreign Makefile
+	@for file in $(DISTFILES); do \
+	  d=$(srcdir); \
+	  if test -d $$d/$$file; then \
+	    cp -pr $$d/$$file $(distdir)/$$file; \
+	  else \
+	    test -f $(distdir)/$$file \
+	    || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
+	    || cp -p $$d/$$file $(distdir)/$$file || :; \
+	  fi; \
+	done
+
+DEPS_MAGIC := $(shell mkdir .deps > /dev/null 2>&1 || :)
+
+-include $(DEP_FILES)
+
+mostlyclean-depend:
+
+clean-depend:
+
+distclean-depend:
+	-rm -rf .deps
+
+maintainer-clean-depend:
+
+%.o: %.c
+	@echo '$(COMPILE) -c $<'; \
+	$(COMPILE) -Wp,-MD,.deps/$(*F).pp -c $<
+	@-cp .deps/$(*F).pp .deps/$(*F).P; \
+	tr ' ' '\012' < .deps/$(*F).pp \
+	  | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
+	    >> .deps/$(*F).P; \
+	rm .deps/$(*F).pp
+
+%.lo: %.c
+	@echo '$(LTCOMPILE) -c $<'; \
+	$(LTCOMPILE) -Wp,-MD,.deps/$(*F).pp -c $<
+	@-sed -e 's/^\([^:]*\)\.o[ 	]*:/\1.lo \1.o :/' \
+	  < .deps/$(*F).pp > .deps/$(*F).P; \
+	tr ' ' '\012' < .deps/$(*F).pp \
+	  | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
+	    >> .deps/$(*F).P; \
+	rm -f .deps/$(*F).pp
+info-am:
+info: info-am
+dvi-am:
+dvi: dvi-am
+check-am: all-am
+check: check-am
+installcheck-am:
+installcheck: installcheck-am
+install-exec-am:
+	@$(NORMAL_INSTALL)
+	$(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-exec: install-exec-am
+
+install-data-am: install-usbauxPROGRAMS install-usbauxDATA
+	@$(NORMAL_INSTALL)
+	$(MAKE) $(AM_MAKEFLAGS) install-data-hook
+install-data: install-data-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+install: install-am
+uninstall-am: uninstall-usbauxPROGRAMS uninstall-usbauxDATA
+uninstall: uninstall-am
+all-am: Makefile $(PROGRAMS) $(DATA)
+all-redirect: all-am
+install-strip:
+	$(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install
+installdirs:
+	$(mkinstalldirs)  $(DESTDIR)$(usbauxdir) $(DESTDIR)$(usbauxdir)
+
+
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-rm -f Makefile $(CONFIG_CLEAN_FILES)
+	-rm -f config.cache config.log stamp-h stamp-h[0-9]*
+
+maintainer-clean-generic:
+mostlyclean-am:  mostlyclean-noinstPROGRAMS mostlyclean-usbauxPROGRAMS \
+		mostlyclean-compile mostlyclean-tags mostlyclean-depend \
+		mostlyclean-generic
+
+mostlyclean: mostlyclean-am
+
+clean-am:  clean-noinstPROGRAMS clean-usbauxPROGRAMS clean-compile \
+		clean-tags clean-depend clean-generic mostlyclean-am
+
+clean: clean-am
+
+distclean-am:  distclean-noinstPROGRAMS distclean-usbauxPROGRAMS \
+		distclean-compile distclean-tags distclean-depend \
+		distclean-generic clean-am
+
+distclean: distclean-am
+	-rm -f config.status
+
+maintainer-clean-am:  maintainer-clean-noinstPROGRAMS \
+		maintainer-clean-usbauxPROGRAMS \
+		maintainer-clean-compile maintainer-clean-tags \
+		maintainer-clean-depend maintainer-clean-generic \
+		distclean-am
+	@echo "This command is intended for maintainers to use;"
+	@echo "it deletes files that may require special tools to rebuild."
+
+maintainer-clean: maintainer-clean-am
+	-rm -f config.status
+
+.PHONY: mostlyclean-noinstPROGRAMS distclean-noinstPROGRAMS \
+clean-noinstPROGRAMS maintainer-clean-noinstPROGRAMS \
+mostlyclean-usbauxPROGRAMS distclean-usbauxPROGRAMS \
+clean-usbauxPROGRAMS maintainer-clean-usbauxPROGRAMS \
+uninstall-usbauxPROGRAMS install-usbauxPROGRAMS mostlyclean-compile \
+distclean-compile clean-compile maintainer-clean-compile \
+uninstall-usbauxDATA install-usbauxDATA tags mostlyclean-tags \
+distclean-tags clean-tags maintainer-clean-tags distdir \
+mostlyclean-depend distclean-depend clean-depend \
+maintainer-clean-depend info-am info dvi-am dvi check check-am \
+installcheck-am installcheck install-exec-am install-exec \
+install-data-am install-data install-am install uninstall-am uninstall \
+all-redirect all-am all installdirs mostlyclean-generic \
+distclean-generic clean-generic maintainer-clean-generic clean \
+mostlyclean distclean maintainer-clean
+
+
+@SUPPORTED_TRUE@install-exec-hook:
+@SUPPORTED_TRUE@	$(INSTALL_PROGRAM) usbhost $(bindir)/usbhost_@PACKAGE_VERSION@
+@SUPPORTED_TRUE@	rm -f $(bindir)/usbhost
+@SUPPORTED_TRUE@	$(LN_S) $(bindir)/usbhost_@PACKAGE_VERSION@ $(bindir)/usbhost
+
+@SUPPORTED_TRUE@install-data-hook:
+@SUPPORTED_TRUE@	chown root $(usbauxdir)/usbchmod
+@SUPPORTED_TRUE@	chmod u+s $(usbauxdir)/usbchmod
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
new file mode 100644
--- /dev/null
+++ b/packages/io/usb/slave/current/host/acinclude.m4
@@ -0,0 +1,54 @@
+dnl Process this file with aclocal to get an aclocal.m4 file. Then
+dnl process that with autoconf.
+dnl ====================================================================
+dnl
+dnl     acinclude.m4
+dnl
+dnl ====================================================================
+dnl ####ECOSGPLCOPYRIGHTBEGIN####
+dnl -------------------------------------------
+dnl This file is part of eCos, the Embedded Configurable Operating System.
+dnl Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+dnl
+dnl eCos is free software; you can redistribute it and/or modify it under
+dnl the terms of the GNU General Public License as published by the Free
+dnl Software Foundation; either version 2 or (at your option) any later version.
+dnl
+dnl eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+dnl WARRANTY; without even the implied warranty of MERCHANTABILITY or
+dnl FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+dnl for more details.
+dnl
+dnl You should have received a copy of the GNU General Public License along
+dnl with eCos; if not, write to the Free Software Foundation, Inc.,
+dnl 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+dnl
+dnl As a special exception, if other files instantiate templates or use macros
+dnl or inline functions from this file, or you compile this file and link it
+dnl with other works to produce a work based on this file, this file does not
+dnl by itself cause the resulting work to be covered by the GNU General Public
+dnl License. However the source code for this file must still be made available
+dnl in accordance with section (3) of the GNU General Public License.
+dnl
+dnl This exception does not invalidate any other reasons why a work based on
+dnl this file might be covered by the GNU General Public License.
+dnl
+dnl Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+dnl at http://sources.redhat.com/ecos/ecos-license/
+dnl -------------------------------------------
+dnl ####ECOSGPLCOPYRIGHTEND####
+dnl ====================================================================
+dnl#####DESCRIPTIONBEGIN####
+dnl
+dnl Author(s):	bartv
+dnl Contact(s):	bartv
+dnl Date:	2002/01/10
+dnl Version:	0.01
+dnl
+dnl####DESCRIPTIONEND####
+dnl ====================================================================
+
+dnl Access shared macros.
+dnl AM_CONDITIONAL needs to be mentioned here or else aclocal does not
+dnl incorporate the macro into aclocal.m4
+sinclude(../../../../../../acsupport/acinclude.m4)
new file mode 100644
--- /dev/null
+++ b/packages/io/usb/slave/current/host/aclocal.m4
@@ -0,0 +1,192 @@
+dnl aclocal.m4 generated automatically by aclocal 1.4
+
+dnl Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl This program is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+dnl even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+dnl PARTICULAR PURPOSE.
+
+dnl Process this file with aclocal to get an aclocal.m4 file. Then
+dnl process that with autoconf.
+dnl ====================================================================
+dnl
+dnl     acinclude.m4
+dnl
+dnl ====================================================================
+dnl ####ECOSGPLCOPYRIGHTBEGIN####
+dnl -------------------------------------------
+dnl This file is part of eCos, the Embedded Configurable Operating System.
+dnl Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+dnl
+dnl eCos is free software; you can redistribute it and/or modify it under
+dnl the terms of the GNU General Public License as published by the Free
+dnl Software Foundation; either version 2 or (at your option) any later version.
+dnl
+dnl eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+dnl WARRANTY; without even the implied warranty of MERCHANTABILITY or
+dnl FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+dnl for more details.
+dnl
+dnl You should have received a copy of the GNU General Public License along
+dnl with eCos; if not, write to the Free Software Foundation, Inc.,
+dnl 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+dnl
+dnl As a special exception, if other files instantiate templates or use macros
+dnl or inline functions from this file, or you compile this file and link it
+dnl with other works to produce a work based on this file, this file does not
+dnl by itself cause the resulting work to be covered by the GNU General Public
+dnl License. However the source code for this file must still be made available
+dnl in accordance with section (3) of the GNU General Public License.
+dnl
+dnl This exception does not invalidate any other reasons why a work based on
+dnl this file might be covered by the GNU General Public License.
+dnl
+dnl Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+dnl at http://sources.redhat.com/ecos/ecos-license/
+dnl -------------------------------------------
+dnl ####ECOSGPLCOPYRIGHTEND####
+dnl ====================================================================
+dnl#####DESCRIPTIONBEGIN####
+dnl
+dnl Author(s):	bartv
+dnl Contact(s):	bartv
+dnl Date:	2002/01/10
+dnl Version:	0.01
+dnl
+dnl####DESCRIPTIONEND####
+dnl ====================================================================
+
+dnl Access shared macros.
+dnl AM_CONDITIONAL needs to be mentioned here or else aclocal does not
+dnl incorporate the macro into aclocal.m4
+sinclude(../../../../../../acsupport/acinclude.m4)
+
+# Define a conditional.
+
+AC_DEFUN(AM_CONDITIONAL,
+[AC_SUBST($1_TRUE)
+AC_SUBST($1_FALSE)
+if $2; then
+  $1_TRUE=
+  $1_FALSE='#'
+else
+  $1_TRUE='#'
+  $1_FALSE=
+fi])
+
+# Do all the work for Automake.  This macro actually does too much --
+# some checks are only needed if your package does certain things.
+# But this isn't really a big deal.
+
+# serial 1
+
+dnl Usage:
+dnl AM_INIT_AUTOMAKE(package,version, [no-define])
+
+AC_DEFUN(AM_INIT_AUTOMAKE,
+[AC_REQUIRE([AC_PROG_INSTALL])
+PACKAGE=[$1]
+AC_SUBST(PACKAGE)
+VERSION=[$2]
+AC_SUBST(VERSION)
+dnl test to see if srcdir already configured
+if test "`cd $srcdir && pwd`" != "`pwd`" && test -f $srcdir/config.status; then
+  AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
+fi
+ifelse([$3],,
+AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
+AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package]))
+AC_REQUIRE([AM_SANITY_CHECK])
+AC_REQUIRE([AC_ARG_PROGRAM])
+dnl FIXME This is truly gross.
+missing_dir=`cd $ac_aux_dir && pwd`
+AM_MISSING_PROG(ACLOCAL, aclocal, $missing_dir)
+AM_MISSING_PROG(AUTOCONF, autoconf, $missing_dir)
+AM_MISSING_PROG(AUTOMAKE, automake, $missing_dir)
+AM_MISSING_PROG(AUTOHEADER, autoheader, $missing_dir)
+AM_MISSING_PROG(MAKEINFO, makeinfo, $missing_dir)
+AC_REQUIRE([AC_PROG_MAKE_SET])])
+
+#
+# Check to make sure that the build environment is sane.
+#
+
+AC_DEFUN(AM_SANITY_CHECK,
+[AC_MSG_CHECKING([whether build environment is sane])
+# Just in case
+sleep 1
+echo timestamp > conftestfile
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments.  Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+   set X `ls -Lt $srcdir/configure conftestfile 2> /dev/null`
+   if test "[$]*" = "X"; then
+      # -L didn't work.
+      set X `ls -t $srcdir/configure conftestfile`
+   fi
+   if test "[$]*" != "X $srcdir/configure conftestfile" \
+      && test "[$]*" != "X conftestfile $srcdir/configure"; then
+
+      # If neither matched, then we have a broken ls.  This can happen
+      # if, for instance, CONFIG_SHELL is bash and it inherits a
+      # broken ls alias from the environment.  This has actually
+      # happened.  Such a system could not be considered "sane".
+      AC_MSG_ERROR([ls -t appears to fail.  Make sure there is not a broken
+alias in your environment])
+   fi
+
+   test "[$]2" = conftestfile
+   )
+then
+   # Ok.
+   :
+else
+   AC_MSG_ERROR([newly created file is older than distributed files!
+Check your system clock])
+fi
+rm -f conftest*
+AC_MSG_RESULT(yes)])
+
+dnl AM_MISSING_PROG(NAME, PROGRAM, DIRECTORY)
+dnl The program must properly implement --version.
+AC_DEFUN(AM_MISSING_PROG,
+[AC_MSG_CHECKING(for working $2)
+# Run test in a subshell; some versions of sh will print an error if
+# an executable is not found, even if stderr is redirected.
+# Redirect stdin to placate older versions of autoconf.  Sigh.
+if ($2 --version) < /dev/null > /dev/null 2>&1; then
+   $1=$2
+   AC_MSG_RESULT(found)
+else
+   $1="$3/missing $2"
+   AC_MSG_RESULT(missing)
+fi
+AC_SUBST($1)])
+
+# Add --enable-maintainer-mode option to configure.
+# From Jim Meyering
+
+# serial 1
+
+AC_DEFUN(AM_MAINTAINER_MODE,
+[AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles])
+  dnl maintainer-mode is disabled by default
+  AC_ARG_ENABLE(maintainer-mode,
+[  --enable-maintainer-mode enable make rules and dependencies not useful
+                          (and sometimes confusing) to the casual installer],
+      USE_MAINTAINER_MODE=$enableval,
+      USE_MAINTAINER_MODE=no)
+  AC_MSG_RESULT($USE_MAINTAINER_MODE)
+  AM_CONDITIONAL(MAINTAINER_MODE, test $USE_MAINTAINER_MODE = yes)
+  MAINT=$MAINTAINER_MODE_TRUE
+  AC_SUBST(MAINT)dnl
+]
+)
+
new file mode 100644
--- /dev/null
+++ b/packages/io/usb/slave/current/host/configure
@@ -0,0 +1,1967 @@
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.13 
+# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+ac_help="$ac_help
+  --enable-maintainer-mode enable make rules and dependencies not useful
+                          (and sometimes confusing) to the casual installer"
+ac_help="$ac_help
+ --enable-debug do a debug rather than a release build"
+ac_help="$ac_help
+ --enable-ansi Do an ANSI rather than a unicode build"
+ac_help="$ac_help
+ --with-tcl-version=<vsn> version of Tcl to be used"
+ac_help="$ac_help
+ --with-tcl-header=<path> location of Tcl header"
+ac_help="$ac_help
+ --with-tcl-lib=<path>    location of Tcl libraries"
+ac_help="$ac_help
+ --with-tcl=<path>        location of Tcl header and libraries"
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+# Maximum number of lines to put in a shell here document.
+ac_max_here_lines=12
+
+ac_prev=
+for ac_option
+do
+
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval "$ac_prev=\$ac_option"
+    ac_prev=
+    continue
+  fi
+
+  case "$ac_option" in
+  -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+  *) ac_optarg= ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case "$ac_option" in
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir="$ac_optarg" ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build="$ac_optarg" ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file="$ac_optarg" ;;
+
+  -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+  | --da=*)
+    datadir="$ac_optarg" ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    eval "enable_${ac_feature}=no" ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix="$ac_optarg" ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he)
+    # Omit some internal or obsolete options to make the list less imposing.
+    # This message is too long to be a string in the A/UX 3.1 sh.
+    cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+  --cache-file=FILE       cache test results in FILE
+  --help                  print this message
+  --no-create             do not create output files
+  --quiet, --silent       do not print \`checking...' messages
+  --version               print the version of autoconf that created configure
+Directory and file names:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [same as prefix]
+  --bindir=DIR            user executables in DIR [EPREFIX/bin]
+  --sbindir=DIR           system admin executables in DIR [EPREFIX/sbin]
+  --libexecdir=DIR        program executables in DIR [EPREFIX/libexec]
+  --datadir=DIR           read-only architecture-independent data in DIR
+                          [PREFIX/share]
+  --sysconfdir=DIR        read-only single-machine data in DIR [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data in DIR
+                          [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data in DIR [PREFIX/var]
+  --libdir=DIR            object code libraries in DIR [EPREFIX/lib]
+  --includedir=DIR        C header files in DIR [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc in DIR [/usr/include]
+  --infodir=DIR           info documentation in DIR [PREFIX/info]
+  --mandir=DIR            man documentation in DIR [PREFIX/man]
+  --srcdir=DIR            find the sources in DIR [configure dir or ..]
+  --program-prefix=PREFIX prepend PREFIX to installed program names
+  --program-suffix=SUFFIX append SUFFIX to installed program names
+  --program-transform-name=PROGRAM
+                          run sed PROGRAM on installed program names
+EOF
+    cat << EOF
+Host type:
+  --build=BUILD           configure for building on BUILD [BUILD=HOST]
+  --host=HOST             configure for HOST [guessed]
+  --target=TARGET         configure for TARGET [TARGET=HOST]
+Features and packages:
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --x-includes=DIR        X include files are in DIR
+  --x-libraries=DIR       X library files are in DIR
+EOF
+    if test -n "$ac_help"; then
+      echo "--enable and --with options recognized:$ac_help"
+    fi
+    exit 0 ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host="$ac_optarg" ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir="$ac_optarg" ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir="$ac_optarg" ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir="$ac_optarg" ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir="$ac_optarg" ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst \
+  | --locals | --local | --loca | --loc | --lo)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+  | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+    localstatedir="$ac_optarg" ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir="$ac_optarg" ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir="$ac_optarg" ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix="$ac_optarg" ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix="$ac_optarg" ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix="$ac_optarg" ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name="$ac_optarg" ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir="$ac_optarg" ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir="$ac_optarg" ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site="$ac_optarg" ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir="$ac_optarg" ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir="$ac_optarg" ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target="$ac_optarg" ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers)
+    echo "configure generated by autoconf version 2.13"
+    exit 0 ;;
+
+  -with-* | --with-*)
+    ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "with_${ac_package}='$ac_optarg'" ;;
+
+  -without-* | --without-*)
+    ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    eval "with_${ac_package}=no" ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes="$ac_optarg" ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries="$ac_optarg" ;;
+
+  -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+    ;;
+
+  *)
+    if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+      echo "configure: warning: $ac_option: invalid host type" 1>&2
+    fi
+    if test "x$nonopt" != xNONE; then
+      { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+    fi
+    nonopt="$ac_option"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+  exec 6>/dev/null
+else
+  exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+  case "$ac_arg" in
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c) ;;
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+  *" "*|*"	"*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+  ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+  *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+  esac
+done
+
+# NLS nuisances.
+# Only set these to C if already set.  These must not be set unconditionally
+# because not all systems understand e.g. LANG=C (notably SCO).
+# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
+# Non-C LC_CTYPE values break the ctype check.
+if test "${LANG+set}"   = set; then LANG=C;   export LANG;   fi
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
+if test "${LC_CTYPE+set}"    = set; then LC_CTYPE=C;    export LC_CTYPE;    fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=usbhost.c
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then its parent.
+  ac_prog=$0
+  ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+  test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+  srcdir=$ac_confdir
+  if test ! -r $srcdir/$ac_unique_file; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+  if test "$ac_srcdir_defaulted" = yes; then
+    { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+  else
+    { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+  fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+  if test "x$prefix" != xNONE; then
+    CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+  else
+    CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+  fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+  if test -r "$ac_site_file"; then
+    echo "loading site script $ac_site_file"
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  echo "loading cache $cache_file"
+  . $cache_file
+else
+  echo "creating cache $cache_file"
+  > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+ac_exeext=
+ac_objext=o
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+  # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+  if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+    ac_n= ac_c='
+' ac_t='	'
+  else
+    ac_n=-n ac_c= ac_t=
+  fi
+else
+  ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+ac_aux_dir=
+for ac_dir in ../../../../../../acsupport $srcdir/../../../../../../acsupport; do
+  if test -f $ac_dir/install-sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f $ac_dir/install.sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  { echo "configure: error: can not find install-sh or install.sh in ../../../../../../acsupport $srcdir/../../../../../../acsupport" 1>&2; exit 1; }
+fi
+ac_config_guess=$ac_aux_dir/config.guess
+ac_config_sub=$ac_aux_dir/config.sub
+ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
+
+
+
+    echo $ac_n "checking that a separate build tree is being used""... $ac_c" 1>&6
+echo "configure:563: checking that a separate build tree is being used" >&5
+    ecos_cwd=`/bin/pwd`
+    if test "${srcdir}" = "." ; then
+        srcdir=${ecos_cwd}
+    fi
+    if test "${ecos_cwd}" = "${srcdir}" ; then
+        echo "$ac_t""no" 1>&6
+        { echo "configure: error: This configure script should not be run inside the source tree. Instead please use a separate build tree" 1>&2; exit 1; }
+    else
+        echo "$ac_t""yes" 1>&6
+    fi
+
+
+# Make sure we can run config.sub.
+if ${CONFIG_SHELL-/bin/sh} $ac_config_sub sun4 >/dev/null 2>&1; then :
+else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking host system type""... $ac_c" 1>&6
+echo "configure:582: checking host system type" >&5
+
+host_alias=$host
+case "$host_alias" in
+NONE)
+  case $nonopt in
+  NONE)
+    if host_alias=`${CONFIG_SHELL-/bin/sh} $ac_config_guess`; then :
+    else { echo "configure: error: can not guess host type; you must specify one" 1>&2; exit 1; }
+    fi ;;
+  *) host_alias=$nonopt ;;
+  esac ;;
+esac
+
+host=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $host_alias`
+host_cpu=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+host_vendor=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+host_os=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+echo "$ac_t""$host" 1>&6
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# ./install, which can be erroneously created by make from ./install.sh.
+echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
+echo "configure:614: checking for a BSD compatible install" >&5
+if test -z "$INSTALL"; then
+if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+    IFS="${IFS= 	}"; ac_save_IFS="$IFS"; IFS=":"
+  for ac_dir in $PATH; do
+    # Account for people who put trailing slashes in PATH elements.
+    case "$ac_dir/" in
+    /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;;
+    *)
+      # OSF1 and SCO ODT 3.0 have their own names for install.
+      # Don't use installbsd from OSF since it installs stuff as root
+      # by default.
+      for ac_prog in ginstall scoinst install; do
+        if test -f $ac_dir/$ac_prog; then
+	  if test $ac_prog = install &&
+            grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then
+	    # AIX install.  It has an incompatible calling convention.
+	    :
+	  else
+	    ac_cv_path_install="$ac_dir/$ac_prog -c"
+	    break 2
+	  fi
+	fi
+      done
+      ;;
+    esac
+  done
+  IFS="$ac_save_IFS"
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL="$ac_cv_path_install"
+  else
+    # As a last resort, use the slow shell script.  We don't cache a
+    # path for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the path is relative.
+    INSTALL="$ac_install_sh"
+  fi
+fi
+echo "$ac_t""$INSTALL" 1>&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+echo $ac_n "checking whether build environment is sane""... $ac_c" 1>&6
+echo "configure:667: checking whether build environment is sane" >&5
+# Just in case
+sleep 1
+echo timestamp > conftestfile
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments.  Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+   set X `ls -Lt $srcdir/configure conftestfile 2> /dev/null`
+   if test "$*" = "X"; then
+      # -L didn't work.
+      set X `ls -t $srcdir/configure conftestfile`
+   fi
+   if test "$*" != "X $srcdir/configure conftestfile" \
+      && test "$*" != "X conftestfile $srcdir/configure"; then
+
+      # If neither matched, then we have a broken ls.  This can happen
+      # if, for instance, CONFIG_SHELL is bash and it inherits a
+      # broken ls alias from the environment.  This has actually
+      # happened.  Such a system could not be considered "sane".
+      { echo "configure: error: ls -t appears to fail.  Make sure there is not a broken
+alias in your environment" 1>&2; exit 1; }
+   fi
+
+   test "$2" = conftestfile
+   )
+then
+   # Ok.
+   :
+else
+   { echo "configure: error: newly created file is older than distributed files!
+Check your system clock" 1>&2; exit 1; }
+fi
+rm -f conftest*
+echo "$ac_t""yes" 1>&6
+if test "$program_transform_name" = s,x,x,; then
+  program_transform_name=
+else
+  # Double any \ or $.  echo might interpret backslashes.
+  cat <<\EOF_SED > conftestsed
+s,\\,\\\\,g; s,\$,$$,g
+EOF_SED
+  program_transform_name="`echo $program_transform_name|sed -f conftestsed`"
+  rm -f conftestsed
+fi
+test "$program_prefix" != NONE &&
+  program_transform_name="s,^,${program_prefix},; $program_transform_name"
+# Use a double $ so make ignores it.
+test "$program_suffix" != NONE &&
+  program_transform_name="s,\$\$,${program_suffix},; $program_transform_name"
+
+# sed with no file args requires a program.
+test "$program_transform_name" = "" && program_transform_name="s,x,x,"
+
+echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6
+echo "configure:724: checking whether ${MAKE-make} sets \${MAKE}" >&5
+set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftestmake <<\EOF
+all:
+	@echo 'ac_maketemp="${MAKE}"'
+EOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=`
+if test -n "$ac_maketemp"; then
+  eval ac_cv_prog_make_${ac_make}_set=yes
+else
+  eval ac_cv_prog_make_${ac_make}_set=no
+fi
+rm -f conftestmake
+fi
+if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  SET_MAKE=
+else
+  echo "$ac_t""no" 1>&6
+  SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+
+PACKAGE=usbhost
+
+VERSION=0.1
+
+if test "`cd $srcdir && pwd`" != "`pwd`" && test -f $srcdir/config.status; then
+  { echo "configure: error: source directory already configured; run "make distclean" there first" 1>&2; exit 1; }
+fi
+
+
+
+missing_dir=`cd $ac_aux_dir && pwd`
+echo $ac_n "checking for working aclocal""... $ac_c" 1>&6
+echo "configure:763: checking for working aclocal" >&5
+# Run test in a subshell; some versions of sh will print an error if
+# an executable is not found, even if stderr is redirected.
+# Redirect stdin to placate older versions of autoconf.  Sigh.
+if (aclocal --version) < /dev/null > /dev/null 2>&1; then
+   ACLOCAL=aclocal
+   echo "$ac_t""found" 1>&6
+else
+   ACLOCAL="$missing_dir/missing aclocal"
+   echo "$ac_t""missing" 1>&6
+fi
+
+echo $ac_n "checking for working autoconf""... $ac_c" 1>&6
+echo "configure:776: checking for working autoconf" >&5
+# Run test in a subshell; some versions of sh will print an error if
+# an executable is not found, even if stderr is redirected.
+# Redirect stdin to placate older versions of autoconf.  Sigh.
+if (autoconf --version) < /dev/null > /dev/null 2>&1; then
+   AUTOCONF=autoconf
+   echo "$ac_t""found" 1>&6
+else
+   AUTOCONF="$missing_dir/missing autoconf"
+   echo "$ac_t""missing" 1>&6
+fi
+
+echo $ac_n "checking for working automake""... $ac_c" 1>&6
+echo "configure:789: checking for working automake" >&5
+# Run test in a subshell; some versions of sh will print an error if
+# an executable is not found, even if stderr is redirected.
+# Redirect stdin to placate older versions of autoconf.  Sigh.
+if (automake --version) < /dev/null > /dev/null 2>&1; then
+   AUTOMAKE=automake
+   echo "$ac_t""found" 1>&6
+else
+   AUTOMAKE="$missing_dir/missing automake"
+   echo "$ac_t""missing" 1>&6
+fi
+
+echo $ac_n "checking for working autoheader""... $ac_c" 1>&6
+echo "configure:802: checking for working autoheader" >&5
+# Run test in a subshell; some versions of sh will print an error if
+# an executable is not found, even if stderr is redirected.
+# Redirect stdin to placate older versions of autoconf.  Sigh.
+if (autoheader --version) < /dev/null > /dev/null 2>&1; then
+   AUTOHEADER=autoheader
+   echo "$ac_t""found" 1>&6
+else
+   AUTOHEADER="$missing_dir/missing autoheader"
+   echo "$ac_t""missing" 1>&6
+fi
+
+echo $ac_n "checking for working makeinfo""... $ac_c" 1>&6
+echo "configure:815: checking for working makeinfo" >&5
+# Run test in a subshell; some versions of sh will print an error if
+# an executable is not found, even if stderr is redirected.
+# Redirect stdin to placate older versions of autoconf.  Sigh.
+if (makeinfo --version) < /dev/null > /dev/null 2>&1; then
+   MAKEINFO=makeinfo
+   echo "$ac_t""found" 1>&6
+else
+   MAKEINFO="$missing_dir/missing makeinfo"
+   echo "$ac_t""missing" 1>&6
+fi
+
+
+echo $ac_n "checking whether to enable maintainer-specific portions of Makefiles""... $ac_c" 1>&6
+echo "configure:829: checking whether to enable maintainer-specific portions of Makefiles" >&5
+    # Check whether --enable-maintainer-mode or --disable-maintainer-mode was given.
+if test "${enable_maintainer_mode+set}" = set; then
+  enableval="$enable_maintainer_mode"
+  USE_MAINTAINER_MODE=$enableval
+else
+  USE_MAINTAINER_MODE=no
+fi
+
+  echo "$ac_t""$USE_MAINTAINER_MODE" 1>&6
+  
+
+if test $USE_MAINTAINER_MODE = yes; then
+  MAINTAINER_MODE_TRUE=
+  MAINTAINER_MODE_FALSE='#'
+else
+  MAINTAINER_MODE_TRUE='#'
+  MAINTAINER_MODE_FALSE=
+fi
+  MAINT=$MAINTAINER_MODE_TRUE
+  
+
+
+case "${host}" in
+    *-*-linux-gnu* ) SUPPORTED="yes";;
+    * ) SUPPORTED="no"
+esac
+
+
+if test "${SUPPORTED}" = "yes"; then
+  SUPPORTED_TRUE=
+  SUPPORTED_FALSE='#'
+else
+  SUPPORTED_TRUE='#'
+  SUPPORTED_FALSE=
+fi
+
+if test "${SUPPORTED}" = "yes" ; then
+    # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:870: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CC="gcc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:900: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=":"
+  ac_prog_rejected=no
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then
+        ac_prog_rejected=yes
+	continue
+      fi
+      ac_cv_prog_CC="cc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# -gt 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    set dummy "$ac_dir/$ac_word" "$@"
+    shift
+    ac_cv_prog_CC="$@"
+  fi
+fi
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+  if test -z "$CC"; then
+    case "`uname -s`" in
+    *win32* | *WIN32*)
+      # Extract the first word of "cl", so it can be a program name with args.
+set dummy cl; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:951: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CC="cl"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+ ;;
+    esac
+  fi
+  test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure:983: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+cat > conftest.$ac_ext << EOF
+
+#line 994 "configure"
+#include "confdefs.h"
+
+main(){return(0);}
+EOF
+if { (eval echo configure:999: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  ac_cv_prog_cc_works=yes
+  # If we can't run a trivial program, we are probably using a cross compiler.
+  if (./conftest; exit) 2>/dev/null; then
+    ac_cv_prog_cc_cross=no
+  else
+    ac_cv_prog_cc_cross=yes
+  fi
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  ac_cv_prog_cc_works=no
+fi
+rm -fr conftest*
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo "$ac_t""$ac_cv_prog_cc_works" 1>&6
+if test $ac_cv_prog_cc_works = no; then
+  { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure:1025: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+echo "configure:1030: checking whether we are using GNU C" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.c <<EOF
+#ifdef __GNUC__
+  yes;
+#endif
+EOF
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:1039: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+  ac_cv_prog_gcc=yes
+else
+  ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+
+if test $ac_cv_prog_gcc = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+
+ac_test_CFLAGS="${CFLAGS+set}"
+ac_save_CFLAGS="$CFLAGS"
+CFLAGS=
+echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+echo "configure:1058: checking whether ${CC-cc} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+  ac_cv_prog_cc_g=yes
+else
+  ac_cv_prog_cc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cc_g" 1>&6
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS="$ac_save_CFLAGS"
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+
+    for ac_prog in $CCC c++ g++ gcc CC cxx cc++ cl
+do
+# Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1094: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CXX'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CXX"; then
+  ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CXX="$ac_prog"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CXX="$ac_cv_prog_CXX"
+if test -n "$CXX"; then
+  echo "$ac_t""$CXX" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+test -n "$CXX" && break
+done
+test -n "$CXX" || CXX="gcc"
+
+
+echo $ac_n "checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure:1126: checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) works" >&5
+
+ac_ext=C
+# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cxx_cross
+
+cat > conftest.$ac_ext << EOF
+
+#line 1137 "configure"
+#include "confdefs.h"
+
+int main(){return(0);}
+EOF
+if { (eval echo configure:1142: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  ac_cv_prog_cxx_works=yes
+  # If we can't run a trivial program, we are probably using a cross compiler.
+  if (./conftest; exit) 2>/dev/null; then
+    ac_cv_prog_cxx_cross=no
+  else
+    ac_cv_prog_cxx_cross=yes
+  fi
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  ac_cv_prog_cxx_works=no
+fi
+rm -fr conftest*
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo "$ac_t""$ac_cv_prog_cxx_works" 1>&6
+if test $ac_cv_prog_cxx_works = no; then
+  { echo "configure: error: installation or configuration problem: C++ compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure:1168: checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cxx_cross" 1>&6
+cross_compiling=$ac_cv_prog_cxx_cross
+
+echo $ac_n "checking whether we are using GNU C++""... $ac_c" 1>&6
+echo "configure:1173: checking whether we are using GNU C++" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gxx'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.C <<EOF
+#ifdef __GNUC__
+  yes;
+#endif
+EOF
+if { ac_try='${CXX-g++} -E conftest.C'; { (eval echo configure:1182: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+  ac_cv_prog_gxx=yes
+else
+  ac_cv_prog_gxx=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gxx" 1>&6
+
+if test $ac_cv_prog_gxx = yes; then
+  GXX=yes
+else
+  GXX=
+fi
+
+ac_test_CXXFLAGS="${CXXFLAGS+set}"
+ac_save_CXXFLAGS="$CXXFLAGS"
+CXXFLAGS=
+echo $ac_n "checking whether ${CXX-g++} accepts -g""... $ac_c" 1>&6
+echo "configure:1201: checking whether ${CXX-g++} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cxx_g'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  echo 'void f(){}' > conftest.cc
+if test -z "`${CXX-g++} -g -c conftest.cc 2>&1`"; then
+  ac_cv_prog_cxx_g=yes
+else
+  ac_cv_prog_cxx_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cxx_g" 1>&6
+if test "$ac_test_CXXFLAGS" = set; then
+  CXXFLAGS="$ac_save_CXXFLAGS"
+elif test $ac_cv_prog_cxx_g = yes; then
+  if test "$GXX" = yes; then
+    CXXFLAGS="-g -O2"
+  else
+    CXXFLAGS="-g"
+  fi
+else
+  if test "$GXX" = yes; then
+    CXXFLAGS="-O2"
+  else
+    CXXFLAGS=
+  fi
+fi
+
+    echo $ac_n "checking whether ln -s works""... $ac_c" 1>&6
+echo "configure:1233: checking whether ln -s works" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_LN_S'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  rm -f conftestdata
+if ln -s X conftestdata 2>/dev/null
+then
+  rm -f conftestdata
+  ac_cv_prog_LN_S="ln -s"
+else
+  ac_cv_prog_LN_S=ln
+fi
+fi
+LN_S="$ac_cv_prog_LN_S"
+if test "$ac_cv_prog_LN_S" = "ln -s"; then
+  echo "$ac_t""yes" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+    echo $ac_n "checking for object suffix""... $ac_c" 1>&6
+echo "configure:1254: checking for object suffix" >&5
+if eval "test \"`echo '$''{'ac_cv_objext'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  rm -f conftest*
+echo 'int i = 1;' > conftest.$ac_ext
+if { (eval echo configure:1260: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  for ac_file in conftest.*; do
+    case $ac_file in
+    *.c) ;;
+    *) ac_cv_objext=`echo $ac_file | sed -e s/conftest.//` ;;
+    esac
+  done
+else
+  { echo "configure: error: installation or configuration problem; compiler does not work" 1>&2; exit 1; }
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_objext" 1>&6
+OBJEXT=$ac_cv_objext
+ac_objext=$ac_cv_objext
+
+    echo $ac_n "checking for Cygwin environment""... $ac_c" 1>&6
+echo "configure:1278: checking for Cygwin environment" >&5
+if eval "test \"`echo '$''{'ac_cv_cygwin'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1283 "configure"
+#include "confdefs.h"
+
+int main() {
+
+#ifndef __CYGWIN__
+#define __CYGWIN__ __CYGWIN32__
+#endif
+return __CYGWIN__;
+; return 0; }
+EOF
+if { (eval echo configure:1294: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_cygwin=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_cygwin=no
+fi
+rm -f conftest*
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_cygwin" 1>&6
+CYGWIN=
+test "$ac_cv_cygwin" = yes && CYGWIN=yes
+echo $ac_n "checking for mingw32 environment""... $ac_c" 1>&6
+echo "configure:1311: checking for mingw32 environment" >&5
+if eval "test \"`echo '$''{'ac_cv_mingw32'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1316 "configure"
+#include "confdefs.h"
+
+int main() {
+return __MINGW32__;
+; return 0; }
+EOF
+if { (eval echo configure:1323: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_mingw32=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_mingw32=no
+fi
+rm -f conftest*
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_mingw32" 1>&6
+MINGW32=
+test "$ac_cv_mingw32" = yes && MINGW32=yes
+
+
+echo $ac_n "checking for executable suffix""... $ac_c" 1>&6
+echo "configure:1342: checking for executable suffix" >&5
+if eval "test \"`echo '$''{'ac_cv_exeext'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$CYGWIN" = yes || test "$MINGW32" = yes; then
+  ac_cv_exeext=.exe
+else
+  rm -f conftest*
+  echo 'int main () { return 0; }' > conftest.$ac_ext
+  ac_cv_exeext=
+  if { (eval echo configure:1352: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; }; then
+    for file in conftest.*; do
+      case $file in
+      *.c | *.o | *.obj) ;;
+      *) ac_cv_exeext=`echo $file | sed -e s/conftest//` ;;
+      esac
+    done
+  else
+    { echo "configure: error: installation or configuration problem: compiler cannot create executables." 1>&2; exit 1; }
+  fi
+  rm -f conftest*
+  test x"${ac_cv_exeext}" = x && ac_cv_exeext=no
+fi
+fi
+
+EXEEXT=""
+test x"${ac_cv_exeext}" != xno && EXEEXT=${ac_cv_exeext}
+echo "$ac_t""${ac_cv_exeext}" 1>&6
+ac_exeext=$EXEEXT
+
+    
+    ecos_CFLAGS=""
+    ecos_CXXFLAGS=""
+    ecos_LDADD=""
+    ecos_INCLUDES=""
+    ecos_LIBS=""
+    
+    
+    
+    
+    
+
+
+    
+    
+    
+
+    echo $ac_n "checking "for Visual C++"""... $ac_c" 1>&6
+echo "configure:1390: checking "for Visual C++"" >&5
+    MSVC="no";
+    if test "${CC}" = "cl" ; then
+       MSVC="yes"
+       CXX="cl"
+       
+    
+    
+    if test "${MSVC}" = "yes" ; then
+      MSVC_SRCDIR=`cygpath -w ${MSVC_SRCDIR} | tr \\\\\\\\ /`
+    fi
+
+       
+       ecos_INCLUDES="${ecos_INCLUDES} \"-I${msvc_srcdir}\""
+       ecos_LDADD="-link"
+       ecos_LIBS="advapi32.lib"
+    fi
+    
+
+if test "${MSVC}" = "yes"; then
+  MSVC_TRUE=
+  MSVC_FALSE='#'
+else
+  MSVC_TRUE='#'
+  MSVC_FALSE=
+fi
+    if test "${MSVC}" = "yes" ; then
+        echo "$ac_t""unfortunately yes" 1>&6
+    else
+        echo "$ac_t""no" 1>&6
+    fi
+
+    
+    
+    
+    
+    
+
+    echo $ac_n "checking "the default compiler flags"""... $ac_c" 1>&6
+echo "configure:1429: checking "the default compiler flags"" >&5
+
+            ecosflags_enable_debug="no"
+    # Check whether --enable-debug or --disable-debug was given.
+if test "${enable_debug+set}" = set; then
+  enableval="$enable_debug"
+  case "${enableval}" in
+       yes) ecosflags_enable_debug="yes" ;;
+       *)   ecosflags_enable_debug="no" ;;
+    esac
+fi
+
+
+                    ecosflags_enable_ansi="no"
+    if test "${MSVC}" = "yes" ; then
+      # Check whether --enable-ansi or --disable-ansi was given.
+if test "${enable_ansi+set}" = set; then
+  enableval="$enable_ansi"
+  case "${enableval}" in
+         yes) ecosflags_enable_ansi="yes" ;;
+         *)   ecosflags_enable_ansi="no" ;;
+      esac
+fi
+
+    fi
+
+        if test "${GCC}" = "yes" ; then
+        ecos_CFLAGS="${ecos_CFLAGS} -pipe -Wall -Wpointer-arith -Wbad-function-cast -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs"
+        ecos_CXXFLAGS="${ecos_CXXFLAGS} -pipe -Wall -Wpointer-arith -Wbad-function-cast -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Woverloaded-virtual"
+    elif test "${MSVC}" = "yes" ; then
+        ecos_CFLAGS="${ecos_CFLAGS} -nologo -W3"
+        ecos_CXXFLAGS="${ecos_CXXFLAGS} -nologo -W3 -GR -GX"
+    else
+        { echo "configure: error: "default flags for ${CC} are not known"" 1>&2; exit 1; }
+    fi
+
+        if test "${ecosflags_enable_debug}" = "yes" ; then
+        if test "${GCC}" = "yes" ; then
+            ecos_CFLAGS="${ecos_CFLAGS} -g -O0"
+            ecos_CXXFLAGS="${ecos_CXXFLAGS} -g -O0"
+        elif test "${MSVC}" = "yes" ; then
+            ecos_CFLAGS="${ecos_CFLAGS} -MDd -Zi"
+            ecos_CXXFLAGS="${ecos_CXXFLAGS} -MDd -Zi"
+        fi
+    else
+                                        if test "${GCC}" = "yes" ; then
+            ecos_CFLAGS="${ecos_CFLAGS} -O0"
+            ecos_CXXFLAGS="${ecos_CXXFLAGS} -O0"
+        elif test "${MSVC}" = "yes" ; then
+            ecos_CFLAGS="${ecos_CFLAGS} -MD -O2"
+            ecos_CXXFLAGS="${ecos_CXXFLAGS} -MD -O2"
+        fi
+    fi
+
+    CFLAGS="${ac_save_CFLAGS}"
+    CXXFLAGS="${ac_save_CXXFLAGS}"
+
+    echo "$ac_t""done" 1>&6
+
+
+    
+
+    
+    
+
+    ecos_tcl_version=""
+    ecos_tcl_incdir=""
+    ecos_tcl_libdir=""
+    ecos_tk_libs=""
+
+                
+    echo $ac_n "checking for Tcl version""... $ac_c" 1>&6
+echo "configure:1501: checking for Tcl version" >&5
+    # Check whether --with-tcl-version or --without-tcl-version was given.
+if test "${with_tcl_version+set}" = set; then
+  withval="$with_tcl_version"
+  
+        ecos_tcl_version=${with_tcl_version}
+    
+else
+  
+        if test "${MSVC}" = "yes" ; then
+           ecos_tcl_version=80
+        elif test "${ac_cv_cygwin}" = "yes" ; then
+           ecos_tcl_version=80
+        else
+           ecos_tcl_version=""
+        fi
+    
+fi
+
+    echo "$ac_t""${ecos_tcl_version}" 1>&6
+
+        echo $ac_n "checking for Tcl installation""... $ac_c" 1>&6
+echo "configure:1523: checking for Tcl installation" >&5
+
+    # Check whether --with-tcl-header or --without-tcl-header was given.
+if test "${with_tcl_header+set}" = set; then
+  withval="$with_tcl_header"
+  :
+fi
+
+    # Check whether --with-tcl-lib or --without-tcl-lib was given.
+if test "${with_tcl_lib+set}" = set; then
+  withval="$with_tcl_lib"
+  :
+fi
+
+    # Check whether --with-tcl or --without-tcl was given.
+if test "${with_tcl+set}" = set; then
+  withval="$with_tcl"
+  :
+fi
+
+
+                                
+    if test "${MSVC}" = "yes" ; then
+
+        if test "${with_tcl_header+set}" = set ; then
+            ecos_tcl_incdir=${with_tcl_header}
+        elif test "${with_tcl+set}" = set ; then
+            ecos_tcl_incdir="${with_tcl}/include"
+        else
+            { echo "configure: error: You must specify a Tcl installation with either --with-tcl=<path> or --with-tcl-header=<path>" 1>&2; exit 1; }
+        fi
+  
+                        if test \! -r "${ecos_tcl_incdir}/tcl.h" ; then
+            { echo "configure: error: unable to locate Tcl header file tcl.h" 1>&2; exit 1; }
+        fi
+        ecos_msvc_tcl_incdir="${ecos_tcl_incdir}"
+        
+    
+    
+    if test "${MSVC}" = "yes" ; then
+      ecos_msvc_tcl_incdir=`cygpath -w ${ecos_msvc_tcl_incdir} | tr \\\\\\\\ /`
+    fi
+
+        ecos_INCLUDES="${ecos_INCLUDES} \"-I${ecos_msvc_tcl_incdir}\""
+
+        if test "${with_tcl_lib+set}" = set; then
+            ecos_tcl_libdir=${with_tcl_lib}
+        elif test "${with_tcl+set}" = set; then
+            ecos_tcl_libdir="${with_tcl}/lib"
+        else
+            { echo "configure: error: You must specify a Tcl installation with either --with-tcl=<path> or --with-tcl-lib=<path>" 1>&2; exit 1; }
+        fi
+        
+    
+    
+    if test "${MSVC}" = "yes" ; then
+      ecos_tcl_libdir=`cygpath -w ${ecos_tcl_libdir} | tr \\\\\\\\ /`
+    fi
+
+        ecos_LIBS="${ecos_LIBS} tcl${ecos_tcl_version}.lib"
+        ecos_LDADD="${ecos_LDADD} \"-libpath=${ecos_tcl_libdir}\""
+
+                        ecos_tk_libs=""
+
+    else
+                        possibles="${with_tcl_header} ${with_tcl}/include ${prefix}/include /usr/include/tcl${ecos_tcl_version} /usr/include"
+        
+    ecos_tcl_incdir=""
+    for i in ${possibles}; do
+        if test -r "$i/"tcl.h""; then
+            ecos_tcl_incdir=$i
+            break
+        fi
+    done
+
+        if test \! -r "${ecos_tcl_incdir}/tcl.h" ; then
+            { echo "configure: error: unable to locate Tcl header file tcl.h" 1>&2; exit 1; }
+        else
+                                                                                                            if test "${ecos_tcl_incdir}" != "/usr/include" ; then
+                ecos_INCLUDES="${ecos_INCLUDES} -I${ecos_tcl_incdir}"
+            fi
+        fi
+        possibles="${with_tcl_lib} ${with_tcl}/lib ${libdir} ${prefix}/lib /usr/lib/tcl$${ecos_tcl_version} /usr/lib"
+        
+    ecos_tcl_libdir=""
+    for i in ${possibles}; do
+        if test -r "$i/"tclConfig.sh""; then
+            ecos_tcl_libdir=$i
+            break
+        fi
+    done
+
+        if test \! -r "${ecos_tcl_libdir}/tclConfig.sh" ; then
+            { echo "configure: error: unable to locate Tcl config file tclConfig.sh" 1>&2; exit 1; }
+        else
+            . ${ecos_tcl_libdir}/tclConfig.sh
+            ecos_LIBS="${ecos_LIBS} -ltcl${ecos_tcl_version} ${TCL_LIBS}"
+        fi
+
+        if test \! -r "${ecos_tcl_libdir}/tkConfig.sh" ; then
+            { echo "configure: error: unable to locate Tk config file tkConfig.sh" 1>&2; exit 1; }
+        else
+            . ${ecos_tcl_libdir}/tkConfig.sh
+            ecos_tk_libs="-ltk${ecos_tcl_version} ${TK_LIBS}"
+                                    for lib in ${TCL_LIBS} ; do
+                ecos_tk_libs=`echo ${ecos_tk_libs} | sed -e "s!${lib}!!"`
+            done
+        fi
+    fi
+
+    echo "$ac_t""-I${ecos_tcl_incdir} -L${ecos_tcl_libdir}" 1>&6
+
+    
+
+                if test "${TK_MAJOR_VERSION}" = "8" ; then
+        if test ${TK_MINOR_VERSION} -lt 2 ; then
+            { echo "configure: error: The USB testing support requires at least version 8.2 of Tcl/Tk" 1>&2; exit 1; }
+        fi
+    fi
+    
+
+        package_dir=`cd ${srcdir} && /bin/pwd`
+    PACKAGE_VERSION=`dirname ${package_dir}`
+    PACKAGE_VERSION=`basename ${PACKAGE_VERSION}`
+    
+
+                        package_dir=`dirname ${package_dir}`
+    package_dir=`dirname ${package_dir}`
+
+    possibles="${package_dir}/.. ${package_dir}/../.. ${package_dir}/../../.. ${package_dir}/../../../.. ${package_dir}/../../../../../.." 
+    
+    repository_root=""
+    for i in ${possibles}; do
+        if test -d "$i/"acsupport""; then
+            repository_root=$i
+            break
+        fi
+    done
+
+    if test "${repository_root}" = "" ; then
+        { echo "configure: error: Failed to identify this package's position within the eCos repository" 1>&2; exit 1; }
+    fi
+        repository_root=`cd "${repository_root}/packages" && /bin/pwd`
+        package_dir=`echo ${package_dir} | sed -e "s:${repository_root}/::"`
+
+                    PACKAGE_NAME=`echo ${package_dir} | sed -e "s:/:_:g"`
+
+    
+
+fi
+
+
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs.  It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already.  You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+  case `(ac_space=' '; set | grep ac_space) 2>&1` in
+  *ac_space=\ *)
+    # `set' does not quote correctly, so add quotes (double-quote substitution
+    # turns \\\\ into \\, and sed turns \\ into \).
+    sed -n \
+      -e "s/'/'\\\\''/g" \
+      -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+    ;;
+  *)
+    # `set' quotes correctly as required by POSIX, so do not add quotes.
+    sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+    ;;
+  esac >> confcache
+if cmp -s $cache_file confcache; then
+  :
+else
+  if test -w $cache_file; then
+    echo "updating cache $cache_file"
+    cat confcache > $cache_file
+  else
+    echo "not updating unwritable cache $cache_file"
+  fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[ 	]*VPATH[ 	]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+# Transform confdefs.h into DEFS.
+# Protect against shell expansion while executing Makefile rules.
+# Protect against Makefile macro expansion.
+cat > conftest.defs <<\EOF
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%-D\1=\2%g
+s%[ 	`~#$^&*(){}\\|;'"<>?]%\\&%g
+s%\[%\\&%g
+s%\]%\\&%g
+s%\$%$$%g
+EOF
+DEFS=`sed -f conftest.defs confdefs.h | tr '\012' ' '`
+rm -f conftest.defs
+
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+  case "\$ac_option" in
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+    exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+  -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+    echo "$CONFIG_STATUS generated by autoconf version 2.13"
+    exit 0 ;;
+  -help | --help | --hel | --he | --h)
+    echo "\$ac_cs_usage"; exit 0 ;;
+  *) echo "\$ac_cs_usage"; exit 1 ;;
+  esac
+done
+
+ac_given_srcdir=$srcdir
+ac_given_INSTALL="$INSTALL"
+
+trap 'rm -fr `echo "Makefile:Makefile.in" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@SHELL@%$SHELL%g
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@FFLAGS@%$FFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+s%@host@%$host%g
+s%@host_alias@%$host_alias%g
+s%@host_cpu@%$host_cpu%g
+s%@host_vendor@%$host_vendor%g
+s%@host_os@%$host_os%g
+s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
+s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g
+s%@INSTALL_DATA@%$INSTALL_DATA%g
+s%@PACKAGE@%$PACKAGE%g
+s%@VERSION@%$VERSION%g
+s%@ACLOCAL@%$ACLOCAL%g
+s%@AUTOCONF@%$AUTOCONF%g
+s%@AUTOMAKE@%$AUTOMAKE%g
+s%@AUTOHEADER@%$AUTOHEADER%g
+s%@MAKEINFO@%$MAKEINFO%g
+s%@SET_MAKE@%$SET_MAKE%g
+s%@MAINTAINER_MODE_TRUE@%$MAINTAINER_MODE_TRUE%g
+s%@MAINTAINER_MODE_FALSE@%$MAINTAINER_MODE_FALSE%g
+s%@MAINT@%$MAINT%g
+s%@SUPPORTED_TRUE@%$SUPPORTED_TRUE%g
+s%@SUPPORTED_FALSE@%$SUPPORTED_FALSE%g
+s%@CC@%$CC%g
+s%@CXX@%$CXX%g
+s%@LN_S@%$LN_S%g
+s%@OBJEXT@%$OBJEXT%g
+s%@EXEEXT@%$EXEEXT%g
+s%@ecos_CFLAGS@%$ecos_CFLAGS%g
+s%@ecos_CXXFLAGS@%$ecos_CXXFLAGS%g
+s%@ecos_LDADD@%$ecos_LDADD%g
+s%@ecos_INCLUDES@%$ecos_INCLUDES%g
+s%@ecos_LIBS@%$ecos_LIBS%g
+s%@MSVC_SRCDIR@%$MSVC_SRCDIR%g
+s%@MSVC_TRUE@%$MSVC_TRUE%g
+s%@MSVC_FALSE@%$MSVC_FALSE%g
+s%@ecos_tk_libs@%$ecos_tk_libs%g
+s%@PACKAGE_VERSION@%$PACKAGE_VERSION%g
+s%@PACKAGE_NAME@%$PACKAGE_NAME%g
+
+CEOF
+EOF
+
+cat >> $CONFIG_STATUS <<\EOF
+
+# Split the substitutions into bite-sized pieces for seds with
+# small command number limits, like on Digital OSF/1 and HP-UX.
+ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # Line after last line for current file.
+ac_more_lines=:
+ac_sed_cmds=""
+while $ac_more_lines; do
+  if test $ac_beg -gt 1; then
+    sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
+  else
+    sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+  fi
+  if test ! -s conftest.s$ac_file; then
+    ac_more_lines=false
+    rm -f conftest.s$ac_file
+  else
+    if test -z "$ac_sed_cmds"; then
+      ac_sed_cmds="sed -f conftest.s$ac_file"
+    else
+      ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+    fi
+    ac_file=`expr $ac_file + 1`
+    ac_beg=$ac_end
+    ac_end=`expr $ac_end + $ac_max_sed_cmds`
+  fi
+done
+if test -z "$ac_sed_cmds"; then
+  ac_sed_cmds=cat
+fi
+EOF
+
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"Makefile:Makefile.in"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case "$ac_file" in
+  *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+       ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+  *) ac_file_in="${ac_file}.in" ;;
+  esac
+
+  # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+  # Remove last slash and all that follows it.  Not all systems have dirname.
+  ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+  if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+    # The file is in a subdirectory.
+    test ! -d "$ac_dir" && mkdir "$ac_dir"
+    ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+    # A "../" for each directory in $ac_dir_suffix.
+    ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+  else
+    ac_dir_suffix= ac_dots=
+  fi
+
+  case "$ac_given_srcdir" in
+  .)  srcdir=.
+      if test -z "$ac_dots"; then top_srcdir=.
+      else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+  /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+  *) # Relative path.
+    srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+    top_srcdir="$ac_dots$ac_given_srcdir" ;;
+  esac
+
+  case "$ac_given_INSTALL" in
+  [/$]*) INSTALL="$ac_given_INSTALL" ;;
+  *) INSTALL="$ac_dots$ac_given_INSTALL" ;;
+  esac
+
+  echo creating "$ac_file"
+  rm -f "$ac_file"
+  configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+  case "$ac_file" in
+  *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+  *) ac_comsub= ;;
+  esac
+
+  ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+  sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+s%@INSTALL@%$INSTALL%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
new file mode 100644
--- /dev/null
+++ b/packages/io/usb/slave/current/host/configure.in
@@ -0,0 +1,101 @@
+dnl Process this file with autoconf to produce a configure script.
+dnl ====================================================================
+dnl
+dnl     configure.in
+dnl
+dnl     configure script for eCos USB host-side support
+dnl
+dnl ====================================================================
+dnl ####ECOSGPLCOPYRIGHTBEGIN####
+dnl -------------------------------------------
+dnl This file is part of eCos, the Embedded Configurable Operating System.
+dnl Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+dnl
+dnl eCos is free software; you can redistribute it and/or modify it under
+dnl the terms of the GNU General Public License as published by the Free
+dnl Software Foundation; either version 2 or (at your option) any later version.
+dnl
+dnl eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+dnl WARRANTY; without even the implied warranty of MERCHANTABILITY or
+dnl FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+dnl for more details.
+dnl
+dnl You should have received a copy of the GNU General Public License along
+dnl with eCos; if not, write to the Free Software Foundation, Inc.,
+dnl 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+dnl
+dnl As a special exception, if other files instantiate templates or use macros
+dnl or inline functions from this file, or you compile this file and link it
+dnl with other works to produce a work based on this file, this file does not
+dnl by itself cause the resulting work to be covered by the GNU General Public
+dnl License. However the source code for this file must still be made available
+dnl in accordance with section (3) of the GNU General Public License.
+dnl
+dnl This exception does not invalidate any other reasons why a work based on
+dnl this file might be covered by the GNU General Public License.
+dnl
+dnl Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+dnl at http://sources.redhat.com/ecos/ecos-license/
+dnl -------------------------------------------
+dnl ####ECOSGPLCOPYRIGHTEND####
+dnl ====================================================================
+dnl#####DESCRIPTIONBEGIN####
+dnl
+dnl Author(s):	bartv
+dnl Contact(s):	bartv
+dnl Date:	2002/01/10
+dnl Version:	0.01
+dnl
+dnl####DESCRIPTIONEND####
+dnl ====================================================================
+
+
+AC_INIT(usbhost.c)
+
+dnl Pick up the support files from the top-level acsupport directory.
+AC_CONFIG_AUX_DIR(../../../../../../acsupport)
+
+ECOS_CHECK_BUILD_ne_SRC
+AC_CANONICAL_HOST
+AM_INIT_AUTOMAKE(usbhost,0.1,0)
+AM_MAINTAINER_MODE
+
+dnl The current version of the USB testing support depends on 
+dnl functionality provided only by Linux platforms, and has only been
+dnl tested on x86 machines, so a test is appropriate here. However
+dnl it is not a good idea for the configure script to report an error:
+dnl that would prevent any top-level configury working for other
+dnl platforms. Instead an automake conditional is used to suppress adding
+dnl targets to the build.
+case "${host}" in
+    *-*-linux-gnu* ) SUPPORTED="yes";;
+    * ) SUPPORTED="no"
+esac
+AM_CONDITIONAL(SUPPORTED, test "${SUPPORTED}" = "yes")
+
+if test "${SUPPORTED}" = "yes" ; then
+    AC_PROG_CC
+    AC_PROG_CXX
+    AC_PROG_LN_S
+    AC_OBJEXT
+    AC_EXEEXT
+    ECOS_PROG_MSVC
+    ECOS_PROG_STANDARD_COMPILER_FLAGS
+
+    ECOS_PATH_TCL
+    dnl Check that the version of tcl is sufficiently recent.
+    dnl e.g. "end" only became a valid index for the
+    dnl string commands after 8.1
+    if test "${TK_MAJOR_VERSION}" = "8" ; then
+        if test ${TK_MINOR_VERSION} -lt 2 ; then
+            AC_MSG_ERROR([The USB testing support requires at least version 8.2 of Tcl/Tk])
+        fi
+    fi
+    ECOS_PACKAGE_NAME_AND_VERSION
+fi
+
+dnl There is no real need for a config.h file at this time, since the code
+dnl is specific to Linux. This may change in future.
+dnl AM_CONFIG_HEADER(config.h:config.h.in)
+
+AC_OUTPUT(Makefile:Makefile.in)
new file mode 100644
--- /dev/null
+++ b/packages/io/usb/slave/current/host/list.tcl
@@ -0,0 +1,130 @@
+# {{{  Banner                           
+
+#===============================================================================
+#
+#    list.tcl
+#
+#    Support for USB testing
+#
+#===============================================================================
+#####ECOSGPLCOPYRIGHTBEGIN####
+## -------------------------------------------
+## This file is part of eCos, the Embedded Configurable Operating System.
+## Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+##
+## eCos 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 or (at your option) any later version.
+##
+## eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
+## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+##
+## As a special exception, if other files instantiate templates or use macros
+## or inline functions from this file, or you compile this file and link it
+## with other works to produce a work based on this file, this file does not
+## by itself cause the resulting work to be covered by the GNU General Public
+## License. However the source code for this file must still be made available
+## in accordance with section (3) of the GNU General Public License.
+##
+## This exception does not invalidate any other reasons why a work based on
+## this file might be covered by the GNU General Public License.
+##
+## Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+## at http://sources.redhat.com/ecos/ecos-license/
+## -------------------------------------------
+#####ECOSGPLCOPYRIGHTEND####
+#===============================================================================
+######DESCRIPTIONBEGIN####
+#
+# Author(s):	bartv
+# Date:		2001-08-21
+# Purpose:      Provide details of the available endpoints
+#
+#####DESCRIPTIONEND####
+#===============================================================================
+#
+
+# }}}
+
+puts "Control endpoint: valid packet sizes are $usbtest::control(min_size) to $usbtest::control(max_size)"
+
+if { 0 != [llength $usbtest::bulk_in_endpoints] } {
+    puts "Bulk IN endpoints: $usbtest::bulk_in_endpoints"
+    foreach ep $usbtest::bulk_in_endpoints {
+	puts [format "  %2d: packet sizes %d to %d, padding %d" $ep \
+		$usbtest::bulk_in($ep,min_size) $usbtest::bulk_in($ep,max_size) $usbtest::bulk_in($ep,max_in_padding)]
+	if { "" == $usbtest::bulk_in($ep,devtab) } {
+	    puts "      no devtab entry"
+	} else {
+	    puts "      devtab entry $usbtest::bulk_in($ep,devtab)"
+	}
+    }
+}
+if { 0 != [llength $usbtest::bulk_out_endpoints] } {
+    puts "Bulk OUT endpoints: $usbtest::bulk_out_endpoints"
+    foreach ep $usbtest::bulk_out_endpoints {
+	puts [format "  %2d: packet sizes %d to %d" $ep \
+		$usbtest::bulk_out($ep,min_size) $usbtest::bulk_out($ep,max_size)]
+	if { "" == $usbtest::bulk_out($ep,devtab) } {
+	    puts "      no devtab entry"
+	} else {
+	    puts "      devtab entry $usbtest::bulk_out($ep,devtab)"
+	}
+    }
+}
+
+if { 0 != [llength $usbtest::isochronous_in_endpoints] } {
+    puts "Isochronous IN endpoints: $usbtest::isochronous_in_endpoints"
+    foreach ep $usbtest::isochronous_in_endpoints {
+	puts [format "  %2d: packet sizes %d to %d" $ep \
+		$usbtest::isochronous_in($ep,min_size) $usbtest::isochronous_in($ep,max_size)]
+	if { "" == $usbtest::isochronous_in($ep,devtab) } {
+	    puts "      no devtab entry"
+	} else {
+	    puts "      devtab entry $usbtest::isochronous_in($ep,devtab)"
+	}
+    }
+}
+if { 0 != [llength $usbtest::isochronous_out_endpoints] } {
+    puts "Isochronous OUT endpoints: $usbtest::isochronous_out_endpoints"
+    foreach ep $usbtest::isochronous_out_endpoints {
+	puts [format "  %2d: packet sizes %d to %d" $ep \
+		$usbtest::isochronous_out($ep,min_size) $usbtest::isochronous_out($ep,max_size)]
+	if { "" == $usbtest::isochronous_out($ep,devtab) } {
+	    puts "      no devtab entry"
+	} else {
+	    puts "      devtab entry $usbtest::isochronous_out($ep,devtab)"
+	}
+    }
+}
+
+if { 0 != [llength $usbtest::interrupt_in_endpoints] } {
+    puts "Interrupt IN endpoints: $usbtest::interrupt_in_endpoints"
+    foreach ep $usbtest::interrupt_in_endpoints {
+	puts [format "  %2d: packet sizes %d to %d" $ep \
+		$usbtest::interrupt_in($ep,min_size) $usbtest::interrupt_in($ep,max_size)]
+	if { "" == $usbtest::interrupt_in($ep,devtab) } {
+	    puts "      no devtab entry"
+	} else {
+	    puts "      devtab entry $usbtest::interrupt_in($ep,devtab)"
+	}
+    }
+}
+if { 0 != [llength $usbtest::interrupt_out_endpoints] } {
+    puts "Interrupt OUT endpoints: $usbtest::interrupt_out_endpoints"
+    foreach ep $usbtest::interrupt_out_endpoints {
+	puts [format "  %2d: packet sizes %d to %d" $ep \
+		$usbtest::interrupt_out($ep,min_size) $usbtest::interrupt_out($ep,max_size)]
+	if { "" == $usbtest::interrupt_out($ep,devtab) } {
+	    puts "      no devtab entry"
+	} else {
+	    puts "      devtab entry $usbtest::interrupt_out($ep,devtab)"
+	}
+    }
+}
new file mode 100644
--- /dev/null
+++ b/packages/io/usb/slave/current/host/usbchmod.c
@@ -0,0 +1,180 @@
+//=================================================================
+//
+//        usb_chmod.c
+//
+//        A utility to manipulate /proc/bus/usb access rights
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+//
+// eCos 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 or (at your option) any later version.
+//
+// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+// at http://sources.redhat.com/ecos/ecos-license/
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// The Linux kernel allows raw access to USB devices via /proc/bus/usb.
+// However, such access requires root privileges: this makes perfect
+// sense for typical USB devices, but gets in the way of eCos USB
+// testing. This utility runs suid and can be used to change the access
+// rights to a specific and validated USB device.
+//
+// Author(s):     bartv
+// Date:          2001-07-18
+//####DESCRIPTIONEND####
+//==========================================================================
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+// Note: this code is duplicated in usbhost.c. Any changes here
+// should be propagated. For now the routine is too small to warrant
+// a separate source file.
+#define USB_ROOT        "/proc/bus/usb/"
+#define PRODUCT_STRING  "Red Hat eCos USB test"
+
+static int
+usb_scan_devices(int* bus, int* dev)
+{
+    FILE*       devs_file       = fopen(USB_ROOT "devices", "r");
+    int         current_bus     = -1;
+    int         current_dev     = -1;
+    int         ch;
+    
+    if (NULL == devs_file) {
+        fprintf(stderr, "Error: unable to access " USB_ROOT "devices\n");
+        return 0;
+    }
+    ch = getc(devs_file);
+    while (EOF != ch) {
+        if ('T' == ch) {
+            if (2 !=fscanf(devs_file, ":  Bus=%d %*[^D\n]Dev#=%d", &current_bus, &current_dev)) { 
+                current_bus = -1;
+                current_dev = -1;
+            }
+        } else if ('S' == ch) {
+            int start = 0, end = 0;
+            if (EOF != fscanf(devs_file, ":  Product=%n" PRODUCT_STRING "%n", &start, &end)) {
+                if (start < end) {
+                    *bus = current_bus;
+                    *dev = current_dev;
+                    break;
+                }
+            } 
+        }
+        // Move to the end of the current line.
+        do {
+            ch = getc(devs_file);
+        } while ((EOF != ch) && ('\n' != ch));
+        if (EOF != ch) {
+            ch = getc(devs_file);
+        }
+    }
+    
+    fclose(devs_file);
+    if ((-1 != *bus) && (-1 != *dev)) {
+        return 1;
+    }
+    fprintf(stderr, "Error: failed to find a USB device \"" PRODUCT_STRING "\"\n");
+    return 0;
+}
+
+int
+main(int argc, char** argv)
+{
+    int         bus, dev;
+    int         actual_bus, actual_dev;
+    char        devname[_POSIX_PATH_MAX];
+    long        strtol_tmp1;
+    char*       strtol_tmp2;
+    
+    if (3 != argc) {
+        fprintf(stderr, "usb_chmod: wrong number of arguments\n");
+        fprintf(stderr, "         : usage, usb_chmod <bus> <dev>\n");
+        exit(EXIT_FAILURE);
+    }
+    if (('\0' == argv[1][0]) || ('\0' == argv[2][0])) {
+        fprintf(stderr, "usb_chmod: invalid arguments\n");
+        exit(EXIT_FAILURE);
+    }
+                                 
+    strtol_tmp1 = strtol(argv[1], &strtol_tmp2, 10);
+    if ('\0' != *strtol_tmp2) {
+        fprintf(stderr, "usbchmod: invalid first argument, not a number\n");
+        exit(EXIT_FAILURE);
+    }
+    if (strtol_tmp1 > INT_MAX) {
+        fprintf(stderr, "usbchmod: invalid first argument, number too large\n");
+        exit(EXIT_FAILURE);
+    }
+    bus = (int) strtol_tmp1;
+
+    strtol_tmp1 = strtol(argv[2], &strtol_tmp2, 10);
+    if ('\0' != *strtol_tmp2) {
+        fprintf(stderr, "usbchmod: invalid second argument, not a number\n");
+        exit(EXIT_FAILURE);
+    }
+    if (strtol_tmp1 > INT_MAX) {
+        fprintf(stderr, "usbchmod: invalid second argument, number too large\n");
+        exit(EXIT_FAILURE);
+    }
+    dev = (int) strtol_tmp1;
+        
+    if (!usb_scan_devices(&actual_bus, &actual_dev)) {
+        fprintf(stderr, "usb_chmod: failed to find eCos USB test application\n");
+        exit(EXIT_FAILURE);
+    }
+    if ((bus != actual_bus) || (dev != actual_dev)) {
+        fprintf(stderr, "usbchmod: mismatch between specified and actual USB identifiers.\n");
+        fprintf(stderr, "         : eCos test application is at %03d/%03d, not %03d/%03d\n",
+                actual_bus, actual_dev, bus, dev);
+        exit(EXIT_FAILURE);
+    }
+    
+    if (_POSIX_PATH_MAX == snprintf(devname, _POSIX_PATH_MAX, "/proc/bus/usb/" "%03d/%03d", actual_bus, actual_dev)) {
+        fprintf(stderr, "usbchmod: internal error, buffer overflow\n");
+        exit(EXIT_FAILURE);
+    }
+
+    if (0 != chmod(devname, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) {
+        int old_errno = errno;
+        fprintf(stderr, "usbchmod: failed to modify access rights on %s\n", devname);
+        if ((old_errno >= 0) && (old_errno < sys_nerr)) {
+            fprintf(stderr, "         : %s\n", sys_errlist[old_errno]);
+        }
+        exit(EXIT_FAILURE);
+    }
+
+    return EXIT_SUCCESS;
+}
new file mode 100644
--- /dev/null
+++ b/packages/io/usb/slave/current/host/usbhost.c
@@ -0,0 +1,1896 @@
+/*{{{  Banner                                                   */
+
+//=================================================================
+//
+//        host.c
+//
+//        USB testing - host-side
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+//
+// eCos 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 or (at your option) any later version.
+//
+// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+// at http://sources.redhat.com/ecos/ecos-license/
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):     bartv
+// Date:          2001-07-04
+//####DESCRIPTIONEND####
+//==========================================================================
+
+// The overall architecture is as follows.
+//
+// The target hardware runs a special application which provides a
+// particular type of USB application, "Red Hat eCos USB testing".
+// This will not be recognised by any device driver, so the Linux
+// kernel will pretty much ignore the device (other host OS's are not
+// considered at this time).
+//
+// This program is the only supported way to interact with that service.
+// It acts as an extended Tcl interpreter, providing a number of new
+// Tcl commands for interacting with the target. All test cases can
+// then be written as Tcl scripts which invoke a series of these commands.
+// These Tcl commands operate essentially though the LINUX usb devfs
+// service which allows ordinary application code to perform USB operations
+// via ioctl()'s.
+
+/*}}}*/
+/*{{{  #include's                                               */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <tcl.h>
+#include <linux/usb.h>
+#include <linux/usbdevice_fs.h>
+#include "../tests/protocol.h"
+
+/*}}}*/
+
+/*{{{  Statics                                                  */
+
+// ----------------------------------------------------------------------------
+// Statics.
+
+// Has the current batch of tests actually terminated? This flag is
+// checked by the various test handlers at appropriate intervals, and
+// helps to handle the case where one of the side has terminated early
+// because an error has been detected.
+static int          current_tests_terminated = 0;
+
+// The next local thread to be allocated for testing. This variable can also
+// be used to find out how many threads are involved in the current test.
+// This counter should always be reset to 0 at the end of every test run.
+static int          local_thread_count      = 0;
+
+// A similar counter for remote threads.
+static int          remote_thread_count     = 0;
+
+// A file handle for manipulating the USB device at a low level
+static int          usb_master_fd  = -1;
+
+/*}}}*/
+/*{{{  Logging                                                  */
+
+// ----------------------------------------------------------------------------
+// The user can provide one or more -V/--verbose arguments to increase
+// the amount of output generated.
+
+static int verbose = 0;
+
+#define VERBOSE(_level_, _format_, _args_...)   \
+    do {                                        \
+        if (verbose >= _level_) {               \
+            printf(_format_, ## _args_);        \
+        }                                       \
+    } while (0);
+
+/*}}}*/
+/*{{{  Low-level USB access                                     */
+
+// ----------------------------------------------------------------------------
+// Low-level access to a USB device.
+//
+// The various ioctl() calls require a file handle which corresponds to one
+// of the /proc/bus/usb/<abc>/<def> entries. <abc> is a bus number,
+// typically 001 or 001, and <def> is a device number on that bus,
+// e.g. 003. Figuring out <abc> and <def> requires scanning
+// /proc/bus/usb/devices, which is a somewhat complicated text file.
+//
+// This is all somewhat vulnerable to incompatible changes in the
+// Linux kernel, specifically the implementation of the /proc/bus/usb.
+// An alternative approach would be to write a new Linux device driver
+// and interact with that, but that approach is vulnerable to any
+// internal kernel API changes affecting USB device drivers.
+
+// How to access USB devices from userland
+#define USB_ROOT        "/proc/bus/usb/"
+
+// How to identify the eCos test case
+#define PRODUCT_STRING    "Red Hat eCos USB test"
+
+// Scan through /proc/bus/usb/devices looking for an entry that
+// matches what we are after, specifically a line
+//   S:  Product=Red Hat eCos USB testcase
+// The required information can then be obtained from the previous
+// line:
+//   T:  Bus=<abc> ... Dev#= <def> ...
+//
+// Of course the T: line is going to come first, so it is necessary
+// to keep track of the current bus and device numbers.
+//
+// Note: this code is duplicated in usbchmod.c. Any changes here
+// should be propagated. For now the routine is too small to warrant
+// a separate source file.
+
+static int
+usb_scan_devices(int* bus, int* dev)
+{
+    FILE*       devs_file;
+    int         current_bus     = -1;
+    int         current_dev     = -1;
+    int         ch;
+
+    *bus = -1;
+    *dev = -1;
+
+    VERBOSE(1, "Searching " USB_ROOT "devices for the eCos USB test code\n");
+    
+    devs_file = fopen(USB_ROOT "devices", "r");
+    if (NULL == devs_file) {
+        fprintf(stderr, "usbhost: error, unable to access " USB_ROOT "devices\n");
+        return 0;
+    }
+    ch = getc(devs_file);
+    while (EOF != ch) {
+        if ('T' == ch) {
+            if (2 !=fscanf(devs_file, ":  Bus=%d %*[^D\n]Dev#=%d", &current_bus, &current_dev)) { 
+                current_bus = -1;
+                current_dev = -1;
+            }
+        } else if ('S' == ch) {
+            int start = 0, end = 0;
+            if (EOF != fscanf(devs_file, ":  Product=%n" PRODUCT_STRING "%n", &start, &end)) {
+                if (start < end) {
+                    *bus = current_bus;
+                    *dev = current_dev;
+                    break;
+                }
+            } 
+        }
+        // Move to the end of the current line.
+        do {
+            ch = getc(devs_file);
+        } while ((EOF != ch) && ('\n' != ch));
+        if (EOF != ch) {
+            ch = getc(devs_file);
+        }
+    }
+    
+    fclose(devs_file);
+    if ((-1 != *bus) && (-1 != *dev)) {
+        VERBOSE(1, "Found eCos USB test code on bus %d, device %d\n", *bus, *dev);
+        return 1;
+    }
+    fprintf(stderr, "usbhost: error, failed to find a USB device \"" PRODUCT_STRING "\"\n");
+    return 0;
+}
+
+// Actually open the USB device, allowing subsequent ioctl() operations.
+//
+// Typically /proc/bus/usb/... will not allow ordinary applications
+// to perform ioctl()'s. Instead root privileges are required. To work
+// around this there is a little utility usbchmod, installed suid,
+// which can be used to get access to the raw device.
+static int
+usb_open_device(void)
+{
+    char devname[_POSIX_PATH_MAX];
+    static int  bus = -1;
+    static int  dev = -1;
+    int         result;
+    
+    if ((-1 == bus) || (-1 == dev)) {
+        if (!usb_scan_devices(&bus, &dev)) {
+            return -1;
+        }
+    }
+    
+    if (_POSIX_PATH_MAX == snprintf(devname, _POSIX_PATH_MAX, USB_ROOT "%03d/%03d", bus, dev)) {
+        fprintf(stderr, "usbhost: internal error, buffer overflow\n");
+        exit(EXIT_FAILURE);
+    }
+
+    VERBOSE(1, "Attempting to access USB target via %s\n", devname);
+    
+    result = open(devname, O_RDWR);
+    if (-1 == result) {
+        // Check for access right problems. If so, try to work around them
+        // by invoking usbchmod. Always look for this in the install tree,
+        // since it is only that version which is likely to have been
+        // chown'ed and chmod'ed to be suid root.
+        if (EACCES == errno) {
+            char command_name[_POSIX_PATH_MAX];
+
+            VERBOSE(1, "Insufficient access to USB target, running usbchmod\n");
+            if (_POSIX_PATH_MAX == snprintf(command_name, _POSIX_PATH_MAX, "%s/usbchmod %d %d", USBAUXDIR, bus, dev)) {
+                fprintf(stderr, "usbhost: internal error, buffer overflow\n");
+                exit(EXIT_FAILURE);
+            }
+            (void) system(command_name);
+            result = open(devname, O_RDWR);
+        }
+    }
+    if (-1 == result) {
+        fprintf(stderr, "usbhost: error, failed to open \"%s\", errno %d\n", devname, errno);
+    }
+
+    VERBOSE(1, "USB device now accessible via file descriptor %d\n", result);
+    
+    // Also perform a set-configuration call, to avoid warnings from
+    // the Linux kernel. Target-side testing is always configuration 1
+    // because only a single configuration is supported.
+    (void) ioctl(result, USBDEVFS_SETCONFIGURATION, 1);
+    return result;
+}
+
+// Exchange a control message with the host. The return value should
+// be 0, or a small positive number indicating the actual number of
+// bytes received which may be less than requested.
+//
+// There appear to be problems with some hosts, manifesting itself as
+// an inability to send control messages that involve additional data
+// from host->target. These problems are not yet well-understood. For
+// now the workaround is to send multiple packets, each with up to
+// four bytes encoded in the index and length fields.
+static int
+usb_control_message(int fd, int request_type, int request, int value, int index, int length, void* data)
+{
+    struct usbdevfs_ctrltransfer        transfer;
+    int         result = 0;
+
+    VERBOSE(3, "usb_control_message, request %02x, len %d\n", request, length);
+    
+    if (length > USBTEST_MAX_CONTROL_DATA) {
+        fprintf(stderr, "usbhost: internal error, control message involves too much data.\n");
+        exit(EXIT_FAILURE);
+    }
+
+#if 1
+    // Workaround - send additional data in the index and length fields.
+    if ((length > 0) && (USB_DIR_OUT == (USB_ENDPOINT_DIR_MASK & request_type))) {
+        int i;
+        unsigned char*  buf = (unsigned char*) data;
+        
+        for (i = 0; i < length; i+= 4) {
+            int this_len = length - 1;
+            int ioctl_result;
+            
+            transfer.requesttype    = USB_TYPE_CLASS | USB_RECIP_DEVICE;
+            if (this_len > 4) {
+                this_len = 4;
+            }
+            switch (this_len) {
+              case 1: transfer.request  = USBTEST_CONTROL_DATA1; break;
+              case 2: transfer.request  = USBTEST_CONTROL_DATA2; break;
+              case 3: transfer.request  = USBTEST_CONTROL_DATA3; break;
+              case 4: transfer.request  = USBTEST_CONTROL_DATA4; break;
+              default:
+                fprintf(stderr, "usbhost: internal error, confusion about transfer length.\n");
+                exit(EXIT_FAILURE);
+            }
+            transfer.value      = (buf[i]   << 8) | buf[i+1];   // Possible read beyond end of buffer,
+            transfer.index      = (buf[i+2] << 8) | buf[i+3];   // but not worth worrying about.
+            transfer.length     = 0;
+            transfer.timeout    = 10 * 1000; // ten seconds, the target should always accept data faster than this.
+            transfer.data       = NULL;
+
+            // This is too strict, deciding what to do about errors should be
+            // handled by higher-level code. However it will do for now.
+            ioctl_result = ioctl(fd, USBDEVFS_CONTROL, &transfer);
+            if (0 != ioctl_result) {
+                fprintf(stderr, "usbhost: error, failed to send control message (data) to target.\n");
+                exit(EXIT_FAILURE);
+            }
+        }
+        // There is no more data to be transferred.
+        length = 0;
+    }
+#endif    
+    transfer.requesttype        = request_type;
+    transfer.request            = request;
+    transfer.value              = value;
+    transfer.index              = index;
+    transfer.length             = length;
+    transfer.timeout            = 10000;
+    transfer.data               = data;
+
+    result = ioctl(fd, USBDEVFS_CONTROL, &transfer);
+    return result;
+}
+
+// A variant of the above which can be called when the target should always respond
+// correctly. This can be used for class control messages.
+static int
+usb_reliable_control_message(int fd, int request_type, int request, int value, int index, int length, void* data)
+{
+    int result = usb_control_message(fd, request_type, request, value, index, length, data);
+    if (-1 == result) {
+        fprintf(stderr, "usbhost: error, failed to send control message %02x to target.\n", request);
+        fprintf(stderr, "       : errno %d (%s)\n", errno, strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+    return result;
+}
+
+    
+// Either send or receive a single bulk message. The top bit of the endpoint
+// number indicates the direction.
+static int
+usb_bulk_message(int fd, int endpoint, unsigned char* buffer, int length)
+{
+    struct  usbdevfs_bulktransfer    transfer;
+    int     result;
+    
+    transfer.ep         = endpoint;
+    transfer.len        = length;
+    transfer.timeout    = 60 * 60 * 1000;
+    // An hour. These operations should not time out because that
+    // leaves the system in a confused state. Instead there is
+    // higher-level recovery code that should ensure the operation
+    // really does complete, and the return value here is used
+    // by the calling code to determine whether the operation
+    // was successful or whether there was an error and the recovery
+    // code was invoked.
+    transfer.data       = buffer;
+    errno               = 0;
+    result = ioctl(fd, USBDEVFS_BULK, &transfer);
+    return result;
+}
+
+
+// Synchronise with the target. This can be used after the host has sent a request that
+// may take a bit of time, e.g. it may involve waking up a thread. The host will send
+// synch requests at regular intervals, until the target is ready.
+//
+// The limit argument can be used to avoid locking up. -1 means loop forever, otherwise
+// it means that many iterations of 100ms apiece.
+static int
+usb_sync(int fd, int limit)
+{
+    unsigned char   buf[1];
+    struct timespec delay;
+    int             loops   = 0;
+    int             result  = 0;
+
+    VERBOSE(2, "Synchronizing with target\n");
+    
+    while (1) {
+        buf[0]  = 0;
+        usb_reliable_control_message(fd, USB_TYPE_CLASS | USB_RECIP_DEVICE | USB_DIR_IN, USBTEST_SYNCH, 0, 0, 1, buf);
+        if (buf[0]) {
+            result = 1;
+            break;
+        } else {
+            if ((-1 != limit) && (++loops > limit)) {
+                break;
+            } else {
+                VERBOSE(3, "Not yet synchronized, sleeping\n");
+                delay.tv_sec    = 0;
+                delay.tv_nsec   = 100000000;    // 100 ms
+                nanosleep(&delay, NULL);
+            }
+        }
+    }
+    VERBOSE(2, "%s\n", result ? "Synchronized" : "Not synchronized");
+    return result;
+}
+
+// Abort the target. Things seem to be completely messed up and there is no easy
+// way to restore sanity to both target and host.
+static void
+usb_abort(int fd)
+{
+    VERBOSE(2, "Target-side abort operation invoked\n");
+    usb_reliable_control_message(fd, USB_TYPE_CLASS | USB_RECIP_DEVICE, USBTEST_ABORT, 0, 0, 0, (void*)0);
+}
+
+/*}}}*/
+/*{{{  Initialise endpoints                                     */
+
+// ----------------------------------------------------------------------------
+// On power-up some endpoints may not be in a sensible state. For example,
+// with the SA11x0 the hardware may start accepting bulk OUT transfers
+// before the target-side software has started a receive operation,
+// so if the host sends a bulk packet before the target is ready then
+// things get messy. This is especially troublesome if the target-side
+// attempts any diagnostic output because of verbosity.
+//
+// This code loops through the various endpoints and makes sure that
+// they are all in a reasonable state, before any real tests get run
+// That means known hardware flaws do not show up as test failures,
+// but of course they are still documented and application software
+// will have to do the right thing.
+
+static void
+usb_initialise_control_endpoint(int min_size, int max_size)
+{
+    // At this time there are no known problems on any hardware
+    // that would need to be addressed
+}
+
+static void
+usb_initialise_isochronous_in_endpoint(int number, int min_size, int max_size)
+{
+    // At this time there are no known problems on any hardware
+    // that would need to be addressed
+}
+
+static void
+usb_initialise_isochronous_out_endpoint(int number, int min_size, int max_size)
+{
+    // At this time there are no known problems on any hardware
+    // that would need to be addressed
+}
+
+static void
+usb_initialise_bulk_in_endpoint(int number, int min_size, int max_size, int padding)
+{
+    // At this time there are no known problems on any hardware
+    // that would need to be addressed
+}
+
+static void
+usb_initialise_bulk_out_endpoint(int number, int min_size, int max_size)
+{
+    char buf[1];
+    
+    // On the SA1110 the hardware comes up with a bogus default value,
+    // causing the hardware to accept packets before the software has
+    // set up DMA or in any way prepared for incoming data. This is
+    // a problem. It is worked around by making the target receive
+    // a single packet, sending that packet, and then performing a
+    // sync.
+    VERBOSE(2, "Performing bulk OUT initialization on endpoint %d\n", number);
+    
+    usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE | USB_DIR_IN,
+                                 USBTEST_INIT_BULK_OUT, number, 0, 0, (void*) 0);
+    usb_bulk_message(usb_master_fd, number, buf, 1);
+    usb_sync(usb_master_fd, 10);
+}
+
+static void
+usb_initialise_interrupt_in_endpoint(int number, int min_size, int max_size)
+{
+    // At this time there are no known problems on any hardware
+    // that would need to be addressed
+}
+
+static void
+usb_initialise_interrupt_out_endpoint(int number, int min_size, int max_size)
+{
+    // At this time there are no known problems on any hardware
+    // that would need to be addressed
+}
+
+/*}}}*/
+/*{{{  Host/target common code                                  */
+
+#define HOST
+#include "../tests/common.c"
+
+/*}}}*/
+/*{{{  The test cases themselves                                */
+
+/*{{{  UsbTest definition                                       */
+
+// ----------------------------------------------------------------------------
+// All the data associated with a single test.
+
+typedef struct UsbTest {
+
+    // A "unique" identifier to make verbose output easier to understand.
+    int                 id;          
+    // Which file descriptor should be used to access USB.
+    int                 fd;
+    
+    // Which test should be run.
+    usbtest             which_test;
+
+    // Test-specific details.
+    union {
+        UsbTest_Bulk        bulk;
+        UsbTest_ControlIn   control_in;
+    } test_params;
+
+    // How to recover from any problems. Specifically, what kind of message
+    // could the target send or receive that would unlock the thread on this
+    // side.
+    UsbTest_Recovery    recovery;
+
+    int                 result_pass;
+    char                result_message[USBTEST_MAX_MESSAGE];
+    unsigned char       buffer[USBTEST_MAX_BULK_DATA + USBTEST_MAX_BULK_DATA_EXTRA];
+} UsbTest;
+
+// Reset the information in a given test. This is used by the pool allocation
+// code. The data union is left alone, filling in the appropriate union
+// member is left to other code.
+static void
+reset_usbtest(UsbTest* test)
+{
+    static int next_id = 1;
+    test->id                    = next_id++;
+    test->which_test            = usbtest_invalid;
+    usbtest_recovery_reset(&(test->recovery));
+    test->result_pass           = 0;
+    test->result_message[0]     = '\0';
+}
+
+/*}}}*/
+/*{{{  bulk OUT                                                 */
+
+static void
+run_test_bulk_out(UsbTest* test)
+{
+    unsigned char*  buf     = test->buffer;
+    int             i;
+
+    VERBOSE(1, "Starting test %d, bulk OUT on endpoint %d\n", test->id, test->test_params.bulk.endpoint);
+    
+    for (i = 0; i < test->test_params.bulk.number_packets; i++) {
+        int transferred;
+        int packet_size = test->test_params.bulk.tx_size;
+
+        test->recovery.endpoint     = test->test_params.bulk.endpoint;
+        test->recovery.protocol     = USB_ENDPOINT_XFER_BULK;
+        test->recovery.size         = packet_size;
+        
+        usbtest_fill_buffer(&(test->test_params.bulk.data), buf, packet_size);
+        if (verbose < 3) {
+            VERBOSE(2, "Bulk OUT test %d: iteration %d, packet size %d\n", test->id, i, packet_size);
+        } else {
+            // Output the first 32 bytes of data as well.
+            char msg[256];
+            int  index;
+            int  j;
+            index = snprintf(msg, 255, "Bulk OUT test %d: iteration %d, packet size %d\n    Data %s:",
+                             test->id, i, packet_size,
+                             (usbtestdata_none == test->test_params.bulk.data.format) ? "(uninitialized)" : "");
+
+            for (j = 0; ((j + 3) < packet_size) && (j < 32); j+= 4) {
+                index += snprintf(msg+index, 255-index, " %02x%02x%02x%02x",
+                                  buf[j], buf[j+1], buf[j+2], buf[j+3]);
+            }
+            if (j < 32) {
+                index += snprintf(msg+index, 255-index, " ");
+                for ( ; j < packet_size; j++) {
+                    index += snprintf(msg+index, 255-index, "%02x", buf[j]);
+                }
+                
+            }
+            VERBOSE(3, "%s\n", msg);
+        }
+
+        transferred = usb_bulk_message(test->fd, test->test_params.bulk.endpoint, buf, packet_size);
+        
+        // Has this test run been aborted for some reason?
+        if (current_tests_terminated) {
+            VERBOSE(2, "Bulk OUT test %d: iteration %d, termination detected\n", test->id, i);
+            test->result_pass = 0;
+            snprintf(test->result_message, USBTEST_MAX_MESSAGE,
+                     "Host, bulk OUT transfer on endpoint %d: aborted after %d iterations\n",
+                     test->test_params.bulk.endpoint & USB_ENDPOINT_NUMBER_MASK, i);
+            return;
+        }
+
+        // If an error occurred, abort this run.
+        if (-1 == transferred) {
+            char    errno_buf[USBTEST_MAX_MESSAGE];
+            test->result_pass  = 0;
+            snprintf(test->result_message, USBTEST_MAX_MESSAGE,
+                     "Host, bulk OUT transfer on endpoint %d : host ioctl() system call failed\n    errno %d (%s)",
+                     test->test_params.bulk.endpoint & USB_ENDPOINT_NUMBER_MASK, errno,
+                     strerror_r(errno, errno_buf, USBTEST_MAX_MESSAGE));
+            VERBOSE(2, "Bulk OUT test %d: iteration %d, error:\n    %s\n", test->id, i, test->result_message);
+            break;
+        }
+
+        if (0 != test->test_params.bulk.tx_delay) {
+            struct timespec delay;
+            
+            VERBOSE(2, "Bulk OUT test %d: iteration %d, sleeping for %d nanoseconds\n", test->id, \
+                    i, test->test_params.bulk.tx_delay);
+            // Note that nanosleep() can return early due to incoming signals,
+            // with the unelapsed time returned in a second argument. This
+            // allows for a retry loop. In practice this does not seem
+            // worthwhile, the delays are approximate anyway.
+            delay.tv_sec  = test->test_params.bulk.tx_delay / 1000000000;
+            delay.tv_nsec = test->test_params.bulk.tx_delay % 1000000000;
+            nanosleep(&delay, NULL);
+        }
+
+        // Now move on to the next transfer
+        USBTEST_BULK_NEXT(test->test_params.bulk);
+    }
+
+    // If all the packets have been transferred this test has passed.
+    if (i >= test->test_params.bulk.number_packets) {
+        test->result_pass   = 1;
+    }
+    
+    VERBOSE(1, "Test %d bulk OUT on endpoint %d, result %d\n", test->id, test->test_params.bulk.endpoint, test->result_pass);
+}
+
+/*}}}*/
+/*{{{  bulk IN                                                  */
+
+static void
+run_test_bulk_in(UsbTest* test)
+{
+    unsigned char*  buf     = test->buffer;
+    int             i;
+
+    VERBOSE(1, "Starting test %d bulk IN on endpoint %d\n", test->id, test->test_params.bulk.endpoint);
+    
+    for (i = 0; i < test->test_params.bulk.number_packets; i++) {
+        int transferred;
+        int tx_size             = test->test_params.bulk.tx_size;
+        int rx_size             = test->test_params.bulk.rx_size;
+        int size_plus_padding;
+        
+        VERBOSE(2, "Bulk IN test %d: iteration %d, rx size %d, tx size %d\n", test->id, i, rx_size, tx_size);
+        
+        if (rx_size < tx_size) {
+            rx_size = tx_size;
+            VERBOSE(2, "Bulk IN test %d: iteration %d, packet size reset to %d to match tx size\n",
+                    test->id, i, rx_size);
+        }
+        test->recovery.endpoint     = test->test_params.bulk.endpoint;
+        test->recovery.protocol     = USB_ENDPOINT_XFER_BULK;
+        test->recovery.size         = rx_size;
+
+        // Make sure there is no old data lying around
+        if (usbtestdata_none != test->test_params.bulk.data.format) {
+            memset(buf, 0, rx_size);
+        }
+
+        // And do the actual transfer.
+        size_plus_padding = rx_size;
+        if (size_plus_padding < (tx_size + test->test_params.bulk.rx_padding)) {
+            size_plus_padding += test->test_params.bulk.rx_padding;
+        }
+        do {
+            transferred = usb_bulk_message(test->fd, test->test_params.bulk.endpoint, buf, size_plus_padding);
+        } while (0 == transferred);
+        
+        // Has this test run been aborted for some reason?
+        if (current_tests_terminated) {
+            VERBOSE(2, "Bulk IN test %d: iteration %d, termination detected\n", test->id, i);
+            snprintf(test->result_message, USBTEST_MAX_MESSAGE,
+                     "Host, bulk IN transfer on endpoint %d: aborted after %d iterations\n",
+                     test->test_params.bulk.endpoint & USB_ENDPOINT_NUMBER_MASK, i);
+            return;
+        }
+
+        // If an error occurred, abort this run.
+        if (-1 == transferred) {
+            char    errno_buf[USBTEST_MAX_MESSAGE];
+            test->result_pass  = 0;
+            snprintf(test->result_message, USBTEST_MAX_MESSAGE,
+                     "Host, bulk IN transfer on endpoint %d : host ioctl() system call failed\n    errno %d (%s)",
+                     test->test_params.bulk.endpoint & USB_ENDPOINT_NUMBER_MASK, errno,
+                     strerror_r(errno, errno_buf, USBTEST_MAX_MESSAGE));
+            VERBOSE(2, "Bulk IN test %d: iteration %d, error:\n    %s\n", test->id, i, test->result_message);
+            break;
+        }
+
+        // Did the target send the expected amount of data?
+        if (transferred < tx_size) {
+            test->result_pass   = 0;
+            snprintf(test->result_message, USBTEST_MAX_MESSAGE,
+                     "Host, bulk IN transfer on endpoint %d : the target only sent %d bytes when %d were expected",
+                     test->test_params.bulk.endpoint & USB_ENDPOINT_NUMBER_MASK, transferred, tx_size);
+            VERBOSE(2, "Bulk IN test %d: iteration %d, error:\n    %s\n", test->id, i, test->result_message);
+            break;
+        }
+        
+        if (verbose >= 3) {
+            // Output the first 32 bytes of data
+            char msg[256];
+            int  index;
+            int  j;
+            index = snprintf(msg, 255, "Bulk IN test %d: iteration %d, transferred %d\n    Data %s:",
+                             test->id, i, transferred,
+                             (usbtestdata_none == test->test_params.bulk.data.format) ? "(uninitialized)" : "");
+
+            for (j = 0; ((j + 3) < transferred) && (j < 32); j+= 4) {
+                index += snprintf(msg+index, 255-index, " %02x%02x%02x%02x",
+                                  buf[j], buf[j+1], buf[j+2], buf[j+3]);
+            }
+            if (j < 32) {
+                index += snprintf(msg+index, 255-index, " ");
+                for ( ; j < transferred; j++) {
+                    index += snprintf(msg+index, 255-index, "%02x", buf[j]);
+                }
+                
+            }
+            VERBOSE(3, "%s\n", msg);
+        }
+        
+        // Is the data correct?
+        if (!usbtest_check_buffer(&(test->test_params.bulk.data), buf, tx_size)) {
+            test->result_pass   = 0;
+            snprintf(test->result_message, USBTEST_MAX_MESSAGE,
+                     "Host, bulk IN transfer on endpoint %d : mismatch between received and expected data",
+                     test->test_params.bulk.endpoint & USB_ENDPOINT_NUMBER_MASK);
+            VERBOSE(2, "Bulk IN test %d: iteration %d, error:\n    %s\n", test->id, i, test->result_message);
+            break;
+        }
+        
+        if (0 != test->test_params.bulk.rx_delay) {
+            struct timespec delay;
+            
+            VERBOSE(2, "Bulk IN test %d: iteration %d, sleeping for %d nanoseconds\n", test->id, \
+                    i, test->test_params.bulk.tx_delay);
+            // Note that nanosleep() can return early due to incoming signals,
+            // with the unelapsed time returned in a second argument. This
+            // allows for a retry loop. In practice this does not seem
+            // worthwhile, the delays are approximate anyway.
+            delay.tv_sec  = test->test_params.bulk.rx_delay / 1000000000;
+            delay.tv_nsec = test->test_params.bulk.rx_delay % 1000000000;
+            nanosleep(&delay, NULL);
+        }
+        
+        USBTEST_BULK_NEXT(test->test_params.bulk);
+    }
+
+    
+    // If all the packets have been transferred this test has passed.
+    if (i >= test->test_params.bulk.number_packets) {
+        test->result_pass   = 1;
+    }
+
+    VERBOSE(1, "Test %d bulk IN on endpoint %d, result %d\n", test->id, test->test_params.bulk.endpoint, test->result_pass);
+}
+
+/*}}}*/
+/*{{{  control IN                                               */
+
+// Receive appropriate packets via the control endpoint. This is somewhat
+// different from bulk transfers. It is implemented using reserved control
+// messages.
+//
+// Note: it is not entirely clear that this test is safe. There will be
+// concurrent control traffic to detect test termination and the like,
+// and these control messages may interfere with each other. It is not
+// entirely clear how the Linux kernel handles concurrent control
+// operations.
+
+static void
+run_test_control_in(UsbTest* test)
+{
+    unsigned char*  buf     = test->buffer;
+    int             packet_size;
+    int             i;
+
+    packet_size = test->test_params.control_in.packet_size_initial;
+    for (i = 0; i < test->test_params.control_in.number_packets; i++) {
+        int transferred;
+        
+        test->recovery.endpoint     = 0;
+        test->recovery.protocol     = USB_ENDPOINT_XFER_CONTROL;
+        test->recovery.size         = packet_size;
+
+        // Make sure there is no old data lying around
+        if (usbtestdata_none != test->test_params.control_in.data.format) {
+            memset(buf, 0, packet_size);
+        }
+
+        // And do the actual transfer.
+        transferred = usb_control_message(test->fd, USB_TYPE_RESERVED | USB_RECIP_DEVICE | USB_DIR_IN, USBTEST_RESERVED_CONTROL_IN,
+                                          0, 0, packet_size, buf);
+
+        // Has this test run been aborted for some reason?
+        if (current_tests_terminated) {
+            return;
+        }
+
+        // If an error occurred, abort this run.
+        if (-1 == transferred) {
+            char    errno_buf[USBTEST_MAX_MESSAGE];
+            test->result_pass  = 0;
+            snprintf(test->result_message, USBTEST_MAX_MESSAGE,
+                     "Host, control IN transfer: host ioctl() system call failed\n    errno %d (%s)",
+                     errno, strerror_r(errno, errno_buf, USBTEST_MAX_MESSAGE));
+            break;
+        }
+
+        // Did the target send the expected amount of data?
+        if (transferred < packet_size) {
+            test->result_pass   = 0;
+            snprintf(test->result_message, USBTEST_MAX_MESSAGE,
+                     "Host, control IN transfer: the target only sent %d bytes when %d were expected",
+                     transferred, packet_size);
+            break;
+        }
+        
+        // Is the data correct?
+        if (!usbtest_check_buffer(&(test->test_params.control_in.data), buf, packet_size)) {
+            test->result_pass   = 0;
+            snprintf(test->result_message, USBTEST_MAX_MESSAGE,
+                     "Host, control IN transfer: mismatch between received and expected data");
+            break;
+        }
+        
+        USBTEST_CONTROL_NEXT_PACKET_SIZE(packet_size, test->test_params.control_in);
+    }
+
+    // If all the packets have been transferred this test has passed.
+    if (i >= test->test_params.control_in.number_packets) {
+        test->result_pass   = 1;
+    }
+}
+
+/*}}}*/
+
+// FIXME: add more tests
+
+/*{{{  run_test()                                               */
+
+// This utility is invoked from a thread in the thread pool whenever there is
+// work to be done. It simply dispatches to the appropriate handler.
+static void
+run_test(UsbTest* test)
+{
+    switch (test->which_test) {
+      case usbtest_bulk_out:    run_test_bulk_out(test); break;
+      case usbtest_bulk_in:     run_test_bulk_in(test); break;
+      case usbtest_control_in:  run_test_control_in(test); break;
+      default:
+        fprintf(stderr, "usbhost: internal error, attempt to execute an unknown test.\n");
+        exit(EXIT_FAILURE);
+    }
+}
+
+/*}}}*/
+
+/*}}}*/
+/*{{{  The thread pool                                          */
+
+// ----------------------------------------------------------------------------
+// A pool of threads and buffers which do the real work. The number of possible
+// concurrent tests is defined in protocol.h. Each one requires a separate
+// thread, transfer buffer, semaphore, and some state information.
+//
+// Although the application is multi-threaded, in practice there is little
+// need for synchronization. Tests will only be started while the pool threads
+// are idle. When the pool threads are running the main thread will be waiting
+// for them all to finish, with a bit of polling to detect error conditions.
+// The pool threads do not share any data, apart from the file descriptor for
+// the USB device.
+
+typedef struct PoolEntry {
+    pthread_t       thread;
+    sem_t           wakeup;
+    int             running;
+    UsbTest         test;
+} PoolEntry;
+
+static PoolEntry pool[USBTEST_MAX_CONCURRENT_TESTS];
+
+// This is the entry point for every thread in the pool. It just loops forever,
+// waiting until it is supposed to run a test. These threads never actually
+// exit, instead there should be a call to exit() somewhere.
+static void*
+pool_function(void* arg)
+{
+    PoolEntry*  pool_entry  = (PoolEntry*) arg;
+    for ( ; ; ) {
+        sem_wait(&(pool_entry->wakeup));
+        run_test(&(pool_entry->test));
+        pool_entry->running = 0;
+    }
+
+    return NULL;
+}
+
+// Initialize all threads in the pool.
+static void
+pool_initialize(void)
+{
+    int i;
+    for (i = 0; i < USBTEST_MAX_CONCURRENT_TESTS; i++) {
+        pool[i].running = 0;
+        pool[i].test.fd = dup(usb_master_fd);
+        if (0 != sem_init(&(pool[i].wakeup), 0, 0)) {
+            fprintf(stderr, "usbhost: internal error, failed to initialize all semaphores.\n");
+            exit(EXIT_FAILURE);
+        }
+        if (0 != pthread_create(&(pool[i].thread), NULL, &pool_function, (void*) &(pool[i]))) {
+            fprintf(stderr, "usbhost: internal error, failed to start all threads.\n");
+            exit(EXIT_FAILURE);
+        }
+    }
+}
+
+// Allocate a single entry in the thread pool.
+static UsbTest*
+pool_allocate(void)
+{
+    UsbTest* result = (UsbTest*) 0;
+
+    if (local_thread_count == USBTEST_MAX_CONCURRENT_TESTS) {
+        fprintf(stderr, "usbhost: internal error, thread resource exhausted.\n");
+        exit(EXIT_FAILURE);
+    }
+
+    result = &(pool[local_thread_count].test);
+    local_thread_count++;
+    reset_usbtest(result);
+    return result;
+}
+
+// Start all the threads that are supposed to be running tests.
+static void
+pool_start(void)
+{
+    int i;
+    for (i = 0; i < local_thread_count; i++) {
+        pool[i].running = 1;
+        sem_post(&(pool[i].wakeup));
+    }
+}
+
+/*}}}*/
+/*{{{  Tcl routines                                             */
+
+// ----------------------------------------------------------------------------
+// Tcl routines to provide access to the USB device from inside Tcl
+// scripts, plus some general utilities. These routines deal mostly
+// with preparing a test run. The actual work is done in C: the
+// ioctl() operations are not readily accessible from Tcl, and
+// operations like filling in buffers and calculating checksums are
+// cpu-intensive.
+
+/*{{{  pass/fail/abort                                          */
+
+// ----------------------------------------------------------------------------
+// Some simple routines accessible from Tcl to get the target to report pass/fail or
+// to make the target abort.
+
+static int
+tcl_target_pass(ClientData     clientData  __attribute__ ((unused)),
+                Tcl_Interp*    interp,
+                int            argc,
+                char**         argv)
+{
+    if (2 != argc) {
+        Tcl_SetResult(interp, "wrong # args: should be \"usbtest::target_pass <message>\"", TCL_STATIC);
+        return TCL_ERROR;
+    }
+    usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE, USBTEST_PASS, 0, 0, strlen(argv[1]) + 1, argv[1]);
+    usb_sync(usb_master_fd, -1);
+    return TCL_OK;
+}
+
+static int
+tcl_target_fail(ClientData     clientData  __attribute__ ((unused)),
+                Tcl_Interp*    interp,
+                int            argc,
+                char**         argv)
+{
+    if (2 != argc) {
+        Tcl_SetResult(interp, "wrong # args: should be \"usbtest::target_fail <message>\"", TCL_STATIC);
+        return TCL_ERROR;
+    }
+    usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE, USBTEST_FAIL, 0, 0, strlen(argv[1]) + 1, argv[1]);
+    usb_sync(usb_master_fd, -1);
+    return TCL_OK;
+}
+
+// The next three routines cause the target to exit, so a usb_sync() is inappropriate.
+static int
+tcl_target_pass_exit(ClientData     clientData  __attribute__ ((unused)),
+                     Tcl_Interp*    interp,
+                     int            argc,
+                     char**         argv)
+{
+    if (2 != argc) {
+        Tcl_SetResult(interp, "wrong # args: should be \"usbtest::target_pass_exit <message>\"", TCL_STATIC);
+        return TCL_ERROR;
+    }
+    usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE, USBTEST_PASS_EXIT, 0, 0,
+                                 strlen(argv[1]) + 1, argv[1]);
+    return TCL_OK;
+}
+
+
+static int
+tcl_target_fail_exit(ClientData     clientData  __attribute__ ((unused)),
+                     Tcl_Interp*    interp,
+                     int            argc,
+                     char**         argv)
+{
+    if (2 != argc) {
+        Tcl_SetResult(interp, "wrong # args: should be \"usbtest::target_fail_exit <message>\"", TCL_STATIC);
+        return TCL_ERROR;
+    }
+    usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE, USBTEST_FAIL_EXIT, 0, 0,
+                                 strlen(argv[1]) + 1, argv[1]);
+    return TCL_OK;
+}
+
+static int
+tcl_target_abort(ClientData     clientData  __attribute__ ((unused)),
+                 Tcl_Interp*    interp,
+                 int            argc,
+                 char**         argv        __attribute__ ((unused)) )
+{
+    if (1 != argc) {
+        Tcl_SetResult(interp, "wrong # args: should be \"usbtest::target_abort\"", TCL_STATIC);
+        return TCL_ERROR;
+    }
+    usb_abort(usb_master_fd);
+    return TCL_OK;
+}
+
+/*}}}*/
+/*{{{  start bulk test                                          */
+
+// ----------------------------------------------------------------------------
+// Start a bulk test. The real Tcl interface to this functionality is
+// implemented in Tcl: it takes care of figuring out sensible default
+// arguments, validating the data, etc. All that this code does is
+// allocate a thread and fill in the appropriate data, plus request
+// the target-side to do the same thing.
+
+static int
+tcl_test_bulk(ClientData     clientData  __attribute__ ((unused)),
+              Tcl_Interp*    interp,
+              int            argc,
+              char**         argv)
+{
+    int             i;
+    int             tmp;
+    UsbTest*        test;
+    unsigned char   request[USBTEST_MAX_CONTROL_DATA];
+    int             request_index;
+
+    // The data consists of 28 numbers for UsbTest_Bulk itself, and
+    // another 10 numbers for the test data definition.
+    if (39 != argc) {
+        Tcl_SetResult(interp, "wrong # args: should be \"usbtest::_test_bulk <message>\"", TCL_STATIC);
+        return TCL_ERROR;
+    }
+    for (i = 1; i < 39; i++) {
+        int discard;
+        if (TCL_OK != Tcl_GetInt(interp, argv[i], &discard)) {
+            Tcl_SetResult(interp, "invalid argument: all arguments should be numbers", TCL_STATIC);
+            return TCL_ERROR;
+        }
+    }
+
+    test = pool_allocate();
+    Tcl_GetInt(interp, argv[1], &(test->test_params.bulk.number_packets));
+    Tcl_GetInt(interp, argv[2], &(test->test_params.bulk.endpoint));
+    test->which_test = (USB_DIR_IN == (test->test_params.bulk.endpoint & USB_ENDPOINT_DIR_MASK))
+        ? usbtest_bulk_in : usbtest_bulk_out;
+    Tcl_GetInt(interp, argv[ 3], &(test->test_params.bulk.tx_size));
+    Tcl_GetInt(interp, argv[ 4], &(test->test_params.bulk.tx_size_min));
+    Tcl_GetInt(interp, argv[ 5], &(test->test_params.bulk.tx_size_max));
+    Tcl_GetInt(interp, argv[ 6], &(test->test_params.bulk.tx_size_multiplier));
+    Tcl_GetInt(interp, argv[ 7], &(test->test_params.bulk.tx_size_divisor));
+    Tcl_GetInt(interp, argv[ 8], &(test->test_params.bulk.tx_size_increment));
+    Tcl_GetInt(interp, argv[ 9], &(test->test_params.bulk.rx_size));
+    Tcl_GetInt(interp, argv[10], &(test->test_params.bulk.rx_size_min));
+    Tcl_GetInt(interp, argv[11], &(test->test_params.bulk.rx_size_max));
+    Tcl_GetInt(interp, argv[12], &(test->test_params.bulk.rx_size_multiplier));
+    Tcl_GetInt(interp, argv[13], &(test->test_params.bulk.rx_size_divisor));
+    Tcl_GetInt(interp, argv[14], &(test->test_params.bulk.rx_size_increment));
+    Tcl_GetInt(interp, argv[15], &(test->test_params.bulk.rx_padding));
+    Tcl_GetInt(interp, argv[16], &(test->test_params.bulk.tx_delay));
+    Tcl_GetInt(interp, argv[17], &(test->test_params.bulk.tx_delay_min));
+    Tcl_GetInt(interp, argv[18], &(test->test_params.bulk.tx_delay_max));
+    Tcl_GetInt(interp, argv[19], &(test->test_params.bulk.tx_delay_multiplier));
+    Tcl_GetInt(interp, argv[20], &(test->test_params.bulk.tx_delay_divisor));
+    Tcl_GetInt(interp, argv[21], &(test->test_params.bulk.tx_delay_increment));
+    Tcl_GetInt(interp, argv[22], &(test->test_params.bulk.rx_delay));
+    Tcl_GetInt(interp, argv[23], &(test->test_params.bulk.rx_delay_min));
+    Tcl_GetInt(interp, argv[24], &(test->test_params.bulk.rx_delay_max));
+    Tcl_GetInt(interp, argv[25], &(test->test_params.bulk.rx_delay_multiplier));
+    Tcl_GetInt(interp, argv[26], &(test->test_params.bulk.rx_delay_divisor));
+    Tcl_GetInt(interp, argv[27], &(test->test_params.bulk.rx_delay_increment));
+    Tcl_GetInt(interp, argv[28], &tmp);
+    test->test_params.bulk.io_mechanism = (usb_io_mechanism) tmp;
+    Tcl_GetInt(interp, argv[29], &tmp);
+    test->test_params.bulk.data.format = (usbtestdata) tmp;
+    Tcl_GetInt(interp, argv[30], &(test->test_params.bulk.data.seed));
+    Tcl_GetInt(interp, argv[31], &(test->test_params.bulk.data.multiplier));
+    Tcl_GetInt(interp, argv[32], &(test->test_params.bulk.data.increment));
+    Tcl_GetInt(interp, argv[33], &(test->test_params.bulk.data.transfer_seed_multiplier));
+    Tcl_GetInt(interp, argv[34], &(test->test_params.bulk.data.transfer_seed_increment));
+    Tcl_GetInt(interp, argv[35], &(test->test_params.bulk.data.transfer_multiplier_multiplier));
+    Tcl_GetInt(interp, argv[36], &(test->test_params.bulk.data.transfer_multiplier_increment));
+    Tcl_GetInt(interp, argv[37], &(test->test_params.bulk.data.transfer_increment_multiplier));
+    Tcl_GetInt(interp, argv[38], &(test->test_params.bulk.data.transfer_increment_increment));
+
+    VERBOSE(3, "Preparing USB bulk test on endpoint %d, direction %s, for %d packets\n", \
+            test->test_params.bulk.endpoint, \
+            (usbtest_bulk_in == test->which_test) ? "IN" : "OUT", \
+            test->test_params.bulk.number_packets);
+    VERBOSE(3, "  I/O mechanism is %s\n", \
+            (usb_io_mechanism_usb == test->test_params.bulk.io_mechanism) ? "low-level USB" : \
+            (usb_io_mechanism_dev == test->test_params.bulk.io_mechanism) ? "devtab" : "<invalid>");
+    VERBOSE(3, "  Data format %s, data1 %d, data* %d, data+ %d, data1* %d, data1+ %d, data** %d, data*+ %d, data+* %d, data++ %d\n",\
+            (usbtestdata_none     == test->test_params.bulk.data.format) ? "none" :     \
+            (usbtestdata_bytefill == test->test_params.bulk.data.format) ? "bytefill" : \
+            (usbtestdata_wordfill == test->test_params.bulk.data.format) ? "wordfill" : \
+            (usbtestdata_byteseq  == test->test_params.bulk.data.format) ? "byteseq"  : \
+            (usbtestdata_wordseq  == test->test_params.bulk.data.format) ? "wordseq"  : "<invalid>", \
+            test->test_params.bulk.data.seed,                            \
+            test->test_params.bulk.data.multiplier,                      \
+            test->test_params.bulk.data.increment,                       \
+            test->test_params.bulk.data.transfer_seed_multiplier,        \
+            test->test_params.bulk.data.transfer_seed_increment,         \
+            test->test_params.bulk.data.transfer_multiplier_multiplier,  \
+            test->test_params.bulk.data.transfer_multiplier_increment,   \
+            test->test_params.bulk.data.transfer_increment_multiplier,   \
+            test->test_params.bulk.data.transfer_increment_increment);
+    VERBOSE(3, "  txsize1 %d, txsize>= %d, txsize<= %d, txsize* %d, txsize/ %d, txsize+ %d\n", \
+            test->test_params.bulk.tx_size,         test->test_params.bulk.tx_size_min,        \
+            test->test_params.bulk.tx_size_max,     test->test_params.bulk.tx_size_multiplier, \
+            test->test_params.bulk.tx_size_divisor, test->test_params.bulk.tx_size_increment);
+    VERBOSE(3, "  rxsize1 %d, rxsize>= %d, rxsize<= %d, rxsize* %d, rxsize/ %d, rxsize+ %d\n", \
+            test->test_params.bulk.rx_size,         test->test_params.bulk.rx_size_min,        \
+            test->test_params.bulk.rx_size_max,     test->test_params.bulk.rx_size_multiplier, \
+            test->test_params.bulk.rx_size_divisor, test->test_params.bulk.rx_size_increment);
+    VERBOSE(3, "  txdelay1 %d, txdelay>= %d, txdelay<= %d, txdelay* %d, txdelay/ %d, txdelay+ %d\n", \
+            test->test_params.bulk.tx_delay,         test->test_params.bulk.tx_delay_min,            \
+            test->test_params.bulk.tx_delay_max,     test->test_params.bulk.tx_delay_multiplier,     \
+            test->test_params.bulk.tx_delay_divisor, test->test_params.bulk.tx_delay_increment);
+    VERBOSE(3, "  rxdelay1 %d, rxdelay>= %d, rxdelay<= %d, rxdelay* %d, rxdelay/ %d, rxdelay+ %d\n", \
+            test->test_params.bulk.rx_delay,         test->test_params.bulk.rx_delay_min,            \
+            test->test_params.bulk.rx_delay_max,     test->test_params.bulk.rx_delay_multiplier,     \
+            test->test_params.bulk.rx_delay_divisor, test->test_params.bulk.rx_delay_increment);
+            
+    
+    // That is all the data converted from Tcl to C, and a local thread is set up to handle this
+    // request. Also set up a thread on the target.
+    request_index = 0;
+    pack_usbtest_bulk(&(test->test_params.bulk), request, &request_index);
+    usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE, USBTEST_TEST_BULK, 0, 0, request_index, request);
+    remote_thread_count++;
+    
+    return TCL_OK;
+}
+
+/*}}}*/
+/*{{{  start control-in test                                    */
+
+// ----------------------------------------------------------------------------
+// Start a control-in test. The real Tcl interface to this
+// functionality is implemented in Tcl: it takes care of figuring out
+// sensible default arguments, validating the data, etc. All that this
+// code does is allocate a thread and fill in the appropriate data,
+// plus request the target-side to do the same thing.
+
+static int
+tcl_test_control_in(ClientData     clientData  __attribute__ ((unused)),
+                    Tcl_Interp*    interp,
+                    int            argc,
+                    char**         argv)
+{
+    int             i;
+    int             tmp;
+    UsbTest*        test;
+    unsigned char   request[USBTEST_MAX_CONTROL_DATA];
+    int             request_index;
+        
+    // The data consists of 6 numbers for UsbTest_ControlIn itself, and
+    // another 10 numbers for the test data definition.
+    if (17 != argc) {
+        Tcl_SetResult(interp, "wrong # args: should be \"usbtest::_test_control_in <message>\"", TCL_STATIC);
+        return TCL_ERROR;
+    }
+    for (i = 1; i < 17; i++) {
+        int discard;
+        if (TCL_OK != Tcl_GetInt(interp, argv[i], &discard)) {
+            Tcl_SetResult(interp, "invalid argument: all arguments should be numbers", TCL_STATIC);
+            return TCL_ERROR;
+        }
+    }
+
+    test                = pool_allocate();
+    test->which_test    = usbtest_control_in;
+    Tcl_GetInt(interp, argv[1], &(test->test_params.control_in.number_packets));
+    Tcl_GetInt(interp, argv[2], &(test->test_params.control_in.packet_size_initial));
+    Tcl_GetInt(interp, argv[3], &(test->test_params.control_in.packet_size_min));
+    Tcl_GetInt(interp, argv[4], &(test->test_params.control_in.packet_size_max));
+    Tcl_GetInt(interp, argv[5], &(test->test_params.control_in.packet_size_multiplier));
+    Tcl_GetInt(interp, argv[6], &(test->test_params.control_in.packet_size_increment));
+    Tcl_GetInt(interp, argv[7], &tmp);
+    test->test_params.bulk.data.format = (usbtestdata) tmp;
+    Tcl_GetInt(interp, argv[ 8], &(test->test_params.control_in.data.seed));
+    Tcl_GetInt(interp, argv[ 9], &(test->test_params.control_in.data.multiplier));
+    Tcl_GetInt(interp, argv[10], &(test->test_params.control_in.data.increment));
+    Tcl_GetInt(interp, argv[11], &(test->test_params.control_in.data.transfer_seed_multiplier));
+    Tcl_GetInt(interp, argv[12], &(test->test_params.control_in.data.transfer_seed_increment));
+    Tcl_GetInt(interp, argv[13], &(test->test_params.control_in.data.transfer_multiplier_multiplier));
+    Tcl_GetInt(interp, argv[14], &(test->test_params.control_in.data.transfer_multiplier_increment));
+    Tcl_GetInt(interp, argv[15], &(test->test_params.control_in.data.transfer_increment_multiplier));
+    Tcl_GetInt(interp, argv[16], &(test->test_params.control_in.data.transfer_increment_increment));
+
+    // That is all the data converted from Tcl to C, and a local thread is set up to handle this
+    // request. Also set up a thread on the target.
+    request_index = 0;
+    pack_usbtest_control_in(&(test->test_params.control_in), request, &request_index);
+    usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE, USBTEST_TEST_CONTROL_IN, 0, 0,
+                                 request_index, request);
+    remote_thread_count++;
+    
+    return TCL_OK;
+}
+
+/*}}}*/
+/*{{{  Cancel the current batch of tests                        */
+
+static int
+tcl_cancel(ClientData     clientData    __attribute__ ((unused)),
+           Tcl_Interp*    interp,
+           int            argc,
+           char**         argv          __attribute__ ((unused)) )
+{
+    if (1 != argc) {
+        Tcl_SetResult(interp, "wrong # args: should be \"usbtest::cancel\"", TCL_STATIC);
+        return TCL_ERROR;
+    }
+
+    // Send the request on to the target.
+    usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE, USBTEST_CANCEL, 0, 0, 0, (void*)0);
+
+    // Now cancel all the local tests. This can be done by resetting the counter
+    // of allocated threads: no actual work will have been started yet.
+    local_thread_count = 0;
+
+    // And synchronise with the target
+    if (!usb_sync(usb_master_fd, 30)) {
+        fprintf(stderr, "usbhost: error, target has failed to process test cancel request.\n");
+        exit(EXIT_FAILURE);
+        
+    }
+    remote_thread_count     = 0;
+    
+    return TCL_OK;
+}
+
+/*}}}*/
+/*{{{  Run a batch of tests                                     */
+
+// ----------------------------------------------------------------------------
+// This code does an awful lot of the hard work. Start with various utilities.
+
+// Has the current batch finished as far as the local threads are concerned?
+static int
+local_batch_finished(void)
+{
+    int result = 1;
+    int i;
+
+    for (i = 0; i < local_thread_count; i++) {
+        if (pool[i].running) {
+            result = 0;
+            break;
+        }
+    }
+    return result;
+}
+
+// Has the current batch finished as far as remote threads are concerned?
+static int
+remote_batch_finished(void)
+{
+    char buf[1];
+    usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE | USB_DIR_IN, USBTEST_FINISHED,
+                                 0, 0, 1, (void*) buf);
+    return buf[0];
+}
+
+// Perform recovery for a thread on the target. This involves asking the
+// target for recovery information, then performing an appropriate
+// action. If no data is returned then no recovery is needed for this thread.
+static void
+recover_remote(int index)
+{
+    unsigned char       buffer[USBTEST_MAX_CONTROL_DATA];
+    int                 buffer_index;
+    UsbTest_Recovery    recovery;
+    int                 i;
+
+    if (0 != usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE | USB_DIR_IN,
+                                          USBTEST_GET_RECOVERY, 0, index, 12, buffer)) {
+        // There is work to be done
+        buffer_index = 0;
+        unpack_usbtest_recovery(&recovery, buffer, &buffer_index);
+
+        // We have an endpoint, a protocol, and a size.
+        if (0 == recovery.endpoint) {
+            // The target just needs a dummy reserved control message
+            usb_reliable_control_message(usb_master_fd, USB_TYPE_RESERVED | USB_RECIP_DEVICE, USBTEST_RESERVED_CONTROL_IN,
+                                         0, 0, 0, (void*) 0);
+        } else if (USB_ENDPOINT_XFER_BULK == recovery.protocol) {
+            // Either we need to send some data to the target, or we need to accept some data.
+            static unsigned char recovery_buffer[USBTEST_MAX_BULK_DATA + USBTEST_MAX_BULK_DATA_EXTRA];
+            
+            struct  usbdevfs_bulktransfer    transfer;
+            transfer.ep         = recovery.endpoint;
+            transfer.timeout    = 2000; // Two seconds.  Should be plenty, even for a large bulk transfer.
+            transfer.data       = recovery_buffer;
+            if (USB_DIR_IN == (recovery.endpoint & USB_ENDPOINT_DIR_MASK)) {
+                transfer.len = recovery.size;
+            } else {
+                transfer.len = 1;
+            }
+            errno = 0;
+            i = ioctl(usb_master_fd, USBDEVFS_BULK, &transfer);
+        }
+
+        // There is no recovery support yet for other protocols.
+    }
+}
+
+// Perform recovery for a local thread. This involves extracting the
+// recovery information from the local thread and asking the target
+// to take appropriate action.
+static void
+recover_local(int index)
+{
+    unsigned char   buffer[USBTEST_MAX_CONTROL_DATA];
+    int             buffer_index;
+
+    if (pool[index].running) {
+        buffer_index = 0;
+        pack_usbtest_recovery(&(pool[index].test.recovery), buffer, &buffer_index);
+        usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE, USBTEST_PERFORM_RECOVERY,
+                                     0, 0, buffer_index, (void*) buffer);
+    }
+}
+
+// All done, time for a clean-up on both target and host. The latter
+// is achieved simply by resetting the thread pool, which actually
+// just means resetting the counter since all the threads are blocked
+// waiting for the next batch.
+static void
+run_done(void)
+{
+    usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE, USBTEST_BATCH_DONE, 0, 0, 0, (void*) NULL);
+    local_thread_count = 0;
+    remote_thread_count = 0;
+}
+
+// The main routine, as invoked from Tcl. This takes a single
+// argument, a timeout in seconds.
+static int
+tcl_run(ClientData     clientData    __attribute__ ((unused)),
+        Tcl_Interp*    interp,
+        int            argc,
+        char**         argv          __attribute__ ((unused)) )
+{
+    struct timespec delay;
+    int             timeout;
+    time_t          start;
+    time_t          now;
+    int             i, j;
+    unsigned char   result_buf[USBTEST_MAX_CONTROL_DATA];
+    int             all_ok;
+    
+    if (2 != argc) {
+        Tcl_SetResult(interp, "wrong # args: should be \"usbtest::_run <timeout>\"", TCL_STATIC);
+        return TCL_ERROR;
+    }
+    if (TCL_OK != Tcl_GetInt(interp, argv[1], &timeout)) {
+        Tcl_SetResult(interp, "invalid argument: timeout should be numeric", TCL_STATIC);
+        return TCL_ERROR;
+    }
+    
+    VERBOSE(2, "Starting a testrun, timeout %d seconds\n", timeout);
+    
+    // Start the tests running on the target. The target USB hardware
+    // will not actually do anything except in response to packets
+    // from the host, so it is better to start the target before the
+    // local threads.
+    usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE, USBTEST_START, 0, 0, 0, (void*) 0);
+
+    // Now the local threads can get going.
+    current_tests_terminated = 0;
+    pool_start();
+
+    // Now leave the various testing threads to do their thing until
+    // either side believes that the batch has finished, or until the
+    // timeout expires. Note that if one side decides that the batch
+    // has finished but the other disagrees, that in itself indicates
+    // a test failure of sorts.
+    //
+    // There is a question of polling frequency. Once a second avoids
+    // excessive polling traffic on the USB bus, and should not impose
+    // intolerable delays for short-duration tests.
+    start = time(NULL);
+    do {
+        VERBOSE(3, "The tests are running, waiting for termination\n");
+        delay.tv_sec    = 1;
+        delay.tv_nsec   = 0;
+        nanosleep(&delay, NULL);
+        now = time(NULL);
+    } while (((start + timeout) > now) && !local_batch_finished() && !remote_batch_finished());
+
+    VERBOSE(2, "Termination detected, time elapsed %ld\n", (long) now - start);
+
+    // If either side believes that testing is not complete, things
+    // get messy. Start by setting the terminated flag. Any tests that
+    // are actually still running happily but have not finished within
+    // the timeout should detect this and stop.
+    if (!local_batch_finished() || !remote_batch_finished()) {
+        VERBOSE(2, "Testing is not yet complete, setting TERMINATED flag\n");
+        current_tests_terminated    = 1;
+        usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE, USBTEST_SET_TERMINATED, 0, 0, 0, (void*) 0);
+        // And another delay, to give threads a chance to detect the
+        // flag's update
+        delay.tv_sec    = 1;
+        delay.tv_nsec   = 0;
+        nanosleep(&delay, NULL);
+    }
+
+    // If there is still are unfinished threads, recovery action
+    // is needed. It is not clear whether it is better to unlock
+    // the local threads first, or the remote threads. For now the
+    // latter approach is taken.
+    if (!remote_batch_finished()) {
+        int i;
+        VERBOSE(2, "Remote threads still running, performing remote recovery\n");
+        for (i = 0; i < remote_thread_count; i++) {
+            recover_remote(i);
+        }
+        // Allow the recovery actions to take effect
+        delay.tv_sec    = 1;
+        delay.tv_nsec   = 0;
+        nanosleep(&delay, NULL);
+    }
+
+    if (!local_batch_finished()) {
+        int i;
+        VERBOSE(2, "Local threads still running, performing local recovery\n");
+        for (i = 0; i < local_thread_count; i++) {
+            recover_local(i);
+        }
+        // Allow the recovery actions to take effect
+        delay.tv_sec    = 1;
+        delay.tv_nsec   = 0;
+        nanosleep(&delay, NULL);
+    }
+
+    // One last check to make sure that everything is finished. If not,
+    // testing has broken down and it is necessary to abort.
+    if (!local_batch_finished() || !remote_batch_finished()) {
+        VERBOSE(2, "Giving local and remote threads another chance to finish.\n");
+        // Allow the recovery actions to take effect
+        delay.tv_sec    = 5;
+        delay.tv_nsec   = 0;
+        nanosleep(&delay, NULL);
+        if (!local_batch_finished() || !remote_batch_finished()) {
+            // OK, normality has not been restored.
+            // It would be nice to get hold of and display any error messages.
+            usb_abort(usb_master_fd);
+            fprintf(stderr, "Fatal error: the host test program and the remote target are out of synch.\n");
+            fprintf(stderr, "             recovery has been attempted, without success.\n");
+            fprintf(stderr, "             USB testing cannot continue.\n");
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    VERBOSE(2, "Local and remote threads are in synch, collecting results.\n");
+    
+    // The world is in a coherent state. Time to collect the results.
+    // The return value of this function is a simple boolean. More
+    // detailed results will be held in a Tcl variable as a list of
+    // messages. It is desirable to keep both local and remote results
+    // in order.
+    for (i = 0; i < ((local_thread_count < remote_thread_count) ? local_thread_count : remote_thread_count); i++) {
+        if (!pool[i].test.result_pass) {
+            Tcl_SetVar(interp, "usbtest::results", pool[i].test.result_message,
+                       all_ok ? (TCL_GLOBAL_ONLY | TCL_LIST_ELEMENT) : (TCL_GLOBAL_ONLY | TCL_APPEND_VALUE | TCL_LIST_ELEMENT));
+            all_ok = 0;
+        }
+        usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE | USB_DIR_IN, USBTEST_GET_RESULT,
+                                     0, i, USBTEST_MAX_CONTROL_DATA, (void*) result_buf);
+        if (!result_buf[0]) {
+            Tcl_SetVar(interp, "usbtest::results", &(result_buf[1]),
+                       all_ok ? TCL_GLOBAL_ONLY : (TCL_GLOBAL_ONLY | TCL_APPEND_VALUE | TCL_LIST_ELEMENT));
+            all_ok = 0;
+        }
+    }
+    for (j = i; j < local_thread_count; j++) {
+        if (!pool[j].test.result_pass) {
+            Tcl_SetVar(interp, "usbtest::results", pool[j].test.result_message,
+                       all_ok ? TCL_GLOBAL_ONLY : (TCL_GLOBAL_ONLY | TCL_APPEND_VALUE | TCL_LIST_ELEMENT));
+            all_ok = 0;
+        }
+    }
+    for (j = i; j < remote_thread_count; j++) {
+        usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE | USB_DIR_IN, USBTEST_GET_RESULT,
+                                     0, i, USBTEST_MAX_CONTROL_DATA, (void*) result_buf);
+        if (!result_buf[0]) {
+            Tcl_SetVar(interp, "usbtest::results", &(result_buf[1]),
+                       all_ok ? TCL_GLOBAL_ONLY : (TCL_GLOBAL_ONLY | TCL_APPEND_VALUE | TCL_LIST_ELEMENT));
+            all_ok = 0;
+        }
+    }
+    VERBOSE(2, "Overall test result %d\n", all_ok);
+    
+    Tcl_SetResult(interp, all_ok ? "1" : "0", TCL_STATIC);
+    
+    run_done();
+    
+    return TCL_OK;
+}
+
+/*}}}*/
+/*{{{  Set verbosity                                            */
+
+// ----------------------------------------------------------------------------
+// Allow Tcl scripts to control verbosity levels for both host and target
+static int
+tcl_host_verbose(ClientData     clientData    __attribute__ ((unused)),
+                  Tcl_Interp*    interp,
+                  int            argc,
+                  char**         argv)
+{
+    int level;
+    
+    if (2 != argc) {
+        Tcl_SetResult(interp, "wrong # args: should be \"usbtest::host_verbose <level>\"", TCL_STATIC);
+        return TCL_ERROR;
+    }
+    if (TCL_OK != Tcl_GetInt(interp, argv[1], &level)) {
+        Tcl_SetResult(interp, "invalid argument: verbosity level should be numeric", TCL_STATIC);
+        return TCL_ERROR;
+    }
+
+    verbose = level;
+    return TCL_OK;
+}
+
+static int
+tcl_target_verbose(ClientData     clientData    __attribute__ ((unused)),
+                   Tcl_Interp*    interp,
+                   int            argc,
+                   char**         argv)
+{
+    int level;
+    
+    if (2 != argc) {
+        Tcl_SetResult(interp, "wrong # args: should be \"usbtest::target_verbose <level>\"", TCL_STATIC);
+        return TCL_ERROR;
+    }
+    if (TCL_OK != Tcl_GetInt(interp, argv[1], &level)) {
+        Tcl_SetResult(interp, "invalid argument: verbosity level should be numeric", TCL_STATIC);
+        return TCL_ERROR;
+    }
+    
+    usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE, USBTEST_VERBOSE, level, 0, 0, NULL);
+    usb_sync(usb_master_fd, -1);
+
+    return TCL_OK;
+}
+
+/*}}}*/
+
+/*}}}*/
+/*{{{  AppInit()                                                */
+
+// ----------------------------------------------------------------------------
+// Application-specific initialization. We have a bare Tcl interpreter ready
+// to start executing scripts that define various test cases. However some
+// additional functions will have to be added to the interpreter, plus
+// information about the various endpoints.
+
+static int
+usbhost_appinit(Tcl_Interp* interp)
+{
+    unsigned char   buf[USBTEST_MAX_CONTROL_DATA];
+    int             number_of_endpoints;
+    int             i;
+    char*           location;
+    
+    // Start by creating a usbtest namespace, for use by the various functions
+    // and variables.
+    if (TCL_OK != Tcl_Eval(interp,
+                           "namespace eval usbtest {\n"
+                           "    variable number_of_endpoints 0\n"
+                           "    array set endpoint [list]\n"
+                           "}\n")) {
+        fprintf(stderr, "usbhost: internal error, failed to create Tcl usbtest:: namespace\n");
+        fprintf(stderr, "         Please check Tcl version (8.0b1 or later required).\n");
+        exit(EXIT_FAILURE);
+    }
+
+    // Add some information about the install path so that the
+    // main Tcl script can find and execute test scripts.
+    location = getenv("USBHOSTDIR");
+    if (NULL == location) {
+        location = USBAUXDIR;
+    }
+    Tcl_SetVar(interp, "usbtest::USBAUXDIR", location, TCL_GLOBAL_ONLY);
+
+    // Also set the verbosity level correctly
+    Tcl_SetVar2Ex(interp, "usbtest::verbose", NULL, Tcl_NewIntObj(verbose), TCL_GLOBAL_ONLY);
+    
+    // Next we need to know the number of endpoints, and for each
+    // endpoint we want additional information such as type. The
+    // results are placed in a Tcl array.
+    usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE | USB_DIR_IN, USBTEST_ENDPOINT_COUNT,
+                                 0, 0, 1, buf);
+    number_of_endpoints = buf[0];
+    Tcl_SetVar2Ex(interp, "usbtest::endpoint_count", NULL, Tcl_NewIntObj(number_of_endpoints), TCL_GLOBAL_ONLY);
+    
+    for (i = 0; i < number_of_endpoints; i++) {
+        char            varname[256];
+        int             result;
+        int             endpoint_min_size;
+        int             endpoint_max_size;
+        int             index;
+        
+        memset(buf, 0, USBTEST_MAX_CONTROL_DATA);
+        result = usb_reliable_control_message(usb_master_fd, USB_TYPE_CLASS | USB_RECIP_DEVICE | USB_DIR_IN,
+                                              USBTEST_ENDPOINT_DETAILS, 0, i, USBTEST_MAX_CONTROL_DATA, buf);
+        if (result < 13) {
+            fprintf(stderr, "usbhost: error, received insufficient endpoint data back from the target.\n");
+            exit(EXIT_FAILURE);
+        }
+
+        // See protocol.h for the encoding used.
+        sprintf(varname, "usbtest::endpoint_data(%d,type)", i);
+        switch(buf[0]) {
+          case USB_ENDPOINT_XFER_CONTROL    : Tcl_SetVar(interp, varname, "control",     TCL_GLOBAL_ONLY); break;
+          case USB_ENDPOINT_XFER_ISOC       : Tcl_SetVar(interp, varname, "isochronous", TCL_GLOBAL_ONLY); break;
+          case USB_ENDPOINT_XFER_BULK       : Tcl_SetVar(interp, varname, "bulk",        TCL_GLOBAL_ONLY); break;
+          case USB_ENDPOINT_XFER_INT        : Tcl_SetVar(interp, varname, "interrupt",   TCL_GLOBAL_ONLY); break;
+        }
+
+        sprintf(varname, "usbtest::endpoint_data(%d,number)", i);
+        Tcl_SetVar2Ex(interp, varname, NULL, Tcl_NewIntObj((int) buf[1]), TCL_GLOBAL_ONLY);
+
+        sprintf(varname, "usbtest::endpoint_data(%d,direction)", i);
+        if (USB_DIR_OUT == buf[2]) {
+            Tcl_SetVar(interp, varname, "out", TCL_GLOBAL_ONLY);
+        } else {
+            Tcl_SetVar(interp, varname, "in", TCL_GLOBAL_ONLY);
+        }
+
+        sprintf(varname, "usbtest::endpoint_data(%d,max_in_padding)", i);
+        Tcl_SetVar2Ex(interp, varname, NULL, Tcl_NewIntObj((int) buf[3]), TCL_GLOBAL_ONLY);
+
+        sprintf(varname, "usbtest::endpoint_data(%d,min_size)", i);
+        index               = 4;
+        endpoint_min_size   = unpack_int(buf, &index);
+        Tcl_SetVar2Ex(interp, varname, NULL, Tcl_NewIntObj(endpoint_min_size), TCL_GLOBAL_ONLY);
+            
+        sprintf(varname, "usbtest::endpoint_data(%d,max_size)", i);
+        endpoint_max_size   = unpack_int(buf, &index);
+        if (USB_ENDPOINT_XFER_CONTROL == buf[0]) {
+            if (endpoint_max_size > USBTEST_MAX_CONTROL_DATA) {
+                endpoint_max_size = USBTEST_MAX_CONTROL_DATA;
+            }
+        } else {
+            if ((-1 == endpoint_max_size) || (endpoint_max_size > USBTEST_MAX_BULK_DATA)) {
+                endpoint_max_size = USBTEST_MAX_BULK_DATA;
+            }
+        }
+        Tcl_SetVar2Ex(interp, varname, NULL, Tcl_NewIntObj(endpoint_max_size), TCL_GLOBAL_ONLY);
+
+        sprintf(varname, "usbtest::endpoint_data(%d,devtab)", i);
+        Tcl_SetVar(interp, varname, (char*) &(buf[12]), TCL_GLOBAL_ONLY);
+
+        // Perform any additional endpoint-specific initialization to make
+        // sure host and target can actually communicate via this endpoint.
+        switch(buf[0]) {
+          case USB_ENDPOINT_XFER_CONTROL    :
+          {
+              usb_initialise_control_endpoint(endpoint_min_size, endpoint_max_size);
+              break;
+          }
+          case USB_ENDPOINT_XFER_ISOC       :
+          {
+              if (USB_DIR_OUT == buf[2]) {
+                  usb_initialise_isochronous_out_endpoint(buf[1], endpoint_min_size, endpoint_max_size);
+              } else {
+                  usb_initialise_isochronous_in_endpoint(buf[1], endpoint_min_size, endpoint_max_size);
+              }
+              break;
+          }
+          case USB_ENDPOINT_XFER_BULK       :
+          {
+              if (USB_DIR_OUT == buf[2]) {
+                  usb_initialise_bulk_out_endpoint(buf[1], endpoint_min_size, endpoint_max_size);
+              } else {
+                  usb_initialise_bulk_in_endpoint(buf[1], endpoint_min_size, endpoint_max_size, buf[3]);
+              }
+              
+              break;
+          }
+          case USB_ENDPOINT_XFER_INT        :
+          {
+              if (USB_DIR_OUT == buf[2]) {
+                  usb_initialise_interrupt_out_endpoint(buf[1], endpoint_min_size, endpoint_max_size);
+              } else {
+                  usb_initialise_interrupt_in_endpoint(buf[1], endpoint_min_size, endpoint_max_size);
+              }
+              break;
+          }
+        }
+    }
+
+    // Register appropriate commands with the Tcl interpreter
+    Tcl_CreateCommand(interp, "usbtest::target_pass",       &tcl_target_pass,       (ClientData) NULL, (Tcl_CmdDeleteProc*) NULL);
+    Tcl_CreateCommand(interp, "usbtest::target_pass_exit",  &tcl_target_pass_exit,  (ClientData) NULL, (Tcl_CmdDeleteProc*) NULL);
+    Tcl_CreateCommand(interp, "usbtest::target_fail",       &tcl_target_fail,       (ClientData) NULL, (Tcl_CmdDeleteProc*) NULL);
+    Tcl_CreateCommand(interp, "usbtest::target_fail_exit",  &tcl_target_fail_exit,  (ClientData) NULL, (Tcl_CmdDeleteProc*) NULL);
+    Tcl_CreateCommand(interp, "usbtest::target_abort",      &tcl_target_abort,      (ClientData) NULL, (Tcl_CmdDeleteProc*) NULL);
+    Tcl_CreateCommand(interp, "usbtest::_test_bulk",        &tcl_test_bulk,         (ClientData) NULL, (Tcl_CmdDeleteProc*) NULL);
+    Tcl_CreateCommand(interp, "usbtest::_test_control_in",  &tcl_test_control_in,   (ClientData) NULL, (Tcl_CmdDeleteProc*) NULL);
+    Tcl_CreateCommand(interp, "usbtest::_cancel",           &tcl_cancel,            (ClientData) NULL, (Tcl_CmdDeleteProc*) NULL);
+    Tcl_CreateCommand(interp, "usbtest::_run",              &tcl_run,               (ClientData) NULL, (Tcl_CmdDeleteProc*) NULL);
+    Tcl_CreateCommand(interp, "usbtest::host_verbose",      &tcl_host_verbose,      (ClientData) NULL, (Tcl_CmdDeleteProc*) NULL);
+    Tcl_CreateCommand(interp, "usbtest::target_verbose",    &tcl_target_verbose,    (ClientData) NULL, (Tcl_CmdDeleteProc*) NULL);
+
+    return TCL_OK;
+}
+
+/*}}}*/
+/*{{{  main()                                                   */
+
+// ----------------------------------------------------------------------------
+// System start-up. After argument processing this code checks that
+// there is a suitable USB target attached - if not then there is no
+// point in proceeding. Otherwise further initialization is performed
+// and then control is passed to a Tcl interpreter.
+
+static void
+usage(void)
+{
+    printf("usbhost: usage, usbhost [-V|--verbose] [-v|--version] [-h|--help] <test> [args]\n");
+    printf("    -V, --verbose    Make the host-side output additional information\n");
+    printf("                     during test runs. This argument can be repeated to\n");
+    printf("                     increase verbosity.\n");
+    printf("    -v, --version    Output version information for usbhost.\n");
+    printf("    -h, --help       Output this help information.\n");
+    printf("    <test>           The name of a USB test case, for example list.tcl\n");
+    printf("    [args]           Optional additional arguments for the testcase.\n");
+    exit(0);
+}
+
+static void
+version(void)
+{
+    printf("usbhost: version %s\n", USBHOST_VERSION);
+    printf("       : built from USB slave package version %s\n", PKGVERSION);
+    printf("       : support files installed in %s\n", USBAUXDIR);
+    exit(0);
+}
+
+int
+main(int argc, char** argv)
+{
+    char*   interpreter = argv[0];
+    char**  new_argv;
+    char    path[_POSIX_PATH_MAX];
+    char*   location;
+    int     i;
+
+    // Argument processing
+    for (i = 1; i < argc; i++) {
+        if ((0 == strcmp("-h", argv[i])) || (0 == strcmp("-H", argv[i])) || (0 == strcmp("--help", argv[i]))) {
+            usage();
+        }
+        if ((0 == strcmp("-v", argv[i])) || (0 == strcmp("--version", argv[i]))) {
+            version();
+        }
+        if ((0 == strcmp("-V", argv[i])) || (0 == strcmp("--verbose", argv[i]))) {
+            verbose++;
+            continue;
+        }
+
+        // The first unrecognised argument should correspond to the test script.
+        break;
+    }
+    argc  = (argc - i) + 1;
+    argv  = (argv + i) - 1;
+
+    if (1 == argc) {
+        fprintf(stderr, "usbhost: at least one test script must be specified on the command line.\n");
+        exit(EXIT_FAILURE);
+    }
+
+    usb_master_fd = usb_open_device();
+    if (-1 == usb_master_fd) {
+        return EXIT_FAILURE;
+    }
+
+    // There is a valid USB target. Initialize the pool of threads etc.
+    pool_initialize();
+
+    // Now start a Tcl interpreter. Tcl_Main() will interpret the
+    // first argument as the name of a Tcl script to execute,
+    // i.e. usbhost.tcl. This can be found in the install tree,
+    // but during development it is inconvenient to run
+    // "make install" every time the Tcl script is edited so an
+    // environment variable can be used to override the location.
+    new_argv = malloc((argc + 2) * sizeof(char*));
+    if (NULL == new_argv) {
+        fprintf(stderr, "usbhost: internal error, out of memory.\n");
+        exit(EXIT_FAILURE);
+    }
+    new_argv[0] = interpreter;
+
+    location = getenv("USBHOSTDIR");
+    if (NULL == location) {
+        location = USBAUXDIR;
+    }
+    snprintf(path, _POSIX_PATH_MAX, "%s/usbhost.tcl", location);
+    if (0 != access(path, R_OK)) {
+        fprintf(stderr, "usbhost: cannot find or access required Tcl script\n");
+        fprintf(stderr, "       : %s\n", path);
+        exit(EXIT_FAILURE);
+    }
+    new_argv[1] = path;
+
+    for (i = 1; i < argc; i++) {
+        new_argv[i+1] = argv[i];
+    }
+    new_argv[i+1]   = NULL;
+
+    Tcl_Main(i+1, new_argv, &usbhost_appinit);
+    
+    return EXIT_SUCCESS;
+}
+
+/*}}}*/
new file mode 100644
--- /dev/null
+++ b/packages/io/usb/slave/current/host/usbhost.tcl
@@ -0,0 +1,531 @@
+# {{{  Banner						
+
+#===============================================================================
+#
+#    usbhost.tcl
+#
+#    Support for USB testing
+#
+#===============================================================================
+#####ECOSGPLCOPYRIGHTBEGIN####
+## -------------------------------------------
+## This file is part of eCos, the Embedded Configurable Operating System.
+## Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+##
+## eCos 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 or (at your option) any later version.
+##
+## eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
+## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+##
+## As a special exception, if other files instantiate templates or use macros
+## or inline functions from this file, or you compile this file and link it
+## with other works to produce a work based on this file, this file does not
+## by itself cause the resulting work to be covered by the GNU General Public
+## License. However the source code for this file must still be made available
+## in accordance with section (3) of the GNU General Public License.
+##
+## This exception does not invalidate any other reasons why a work based on
+## this file might be covered by the GNU General Public License.
+##
+## Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+## at http://sources.redhat.com/ecos/ecos-license/
+## -------------------------------------------
+#####ECOSGPLCOPYRIGHTEND####
+#===============================================================================
+######DESCRIPTIONBEGIN####
+#
+# Author(s):	bartv
+# Date:		2001-07-04
+# Purpose:      To provide higher-level utility commands for performing
+#               USB testing, and to iterate through the various test scripts
+#               specified on the command line.
+#
+#####DESCRIPTIONEND####
+#===============================================================================
+#
+
+# }}}
+
+# {{{  Endpoint data					
+
+# Given the raw endpoint data provided by the C code, turn
+# it something more usable from inside Tcl scripts.
+namespace eval usbtest {
+    array set control {}
+
+    variable bulk_in_endpoints [list]
+    array set bulk_in {}
+    variable bulk_out_endpoints [list]
+    array set bulk_out {}
+    
+    variable isochronous_in_endpoints [list]
+    array set isochronous_in {}
+    variable isochronous_out_endpoints [list]
+    array set isochronous_out {}
+
+    variable interrupt_in_endpoints [list]
+    array set interrupt_in {}
+    variable interrupt_out_endpoints [list]
+    array set interrupt_out {}
+
+    for { set i 0 } { $i < $usbtest::endpoint_count } { incr i } {
+	switch -- $usbtest::endpoint_data($i,type) {
+	    "control" {
+		set usbtest::control(min_size) $usbtest::endpoint_data($i,min_size)
+		set usbtest::control(max_size) $usbtest::endpoint_data($i,max_size)
+	    }
+
+	    "bulk" {
+		set number $usbtest::endpoint_data($i,number)
+		if { "in" == $usbtest::endpoint_data($i,direction) } {
+		    lappend usbtest::bulk_in_endpoints $number
+		    set usbtest::bulk_in($number,min_size)		$usbtest::endpoint_data($i,min_size)
+		    set usbtest::bulk_in($number,max_size)		$usbtest::endpoint_data($i,max_size)
+		    set usbtest::bulk_in($number,max_in_padding)	$usbtest::endpoint_data($i,max_in_padding)
+		    set usbtest::bulk_in($number,devtab)		$usbtest::endpoint_data($i,devtab)
+		} else {
+		    lappend usbtest::bulk_out_endpoints $number
+		    set usbtest::bulk_out($number,min_size)		$usbtest::endpoint_data($i,min_size)
+		    set usbtest::bulk_out($number,max_size)		$usbtest::endpoint_data($i,max_size)
+		    set usbtest::bulk_out($number,devtab)		$usbtest::endpoint_data($i,devtab)
+		}
+	    }
+
+	    "isochronous" {
+		set number $usbtest::endpoint_data($i,number)
+		if { "in" == $usbtest::endpoint_data($i,direction) } {
+		    lappend usbtest::isochronous_in_endpoints $number
+		    set usbtest::isochronous_in($number,min_size)	$usbtest::endpoint_data($i,min_size)
+		    set usbtest::isochronous_in($number,max_size)	$usbtest::endpoint_data($i,max_size)
+		    set usbtest::isochronous_in($number,devtab)		$usbtest::endpoint_data($i,devtab)
+		} else {
+		    lappend usbtest::isochronous_out_endpoints $number
+		    set usbtest::isochronous_out($number,min_size)	$usbtest::endpoint_data($i,min_size)
+		    set usbtest::isochronous_out($number,max_size)	$usbtest::endpoint_data($i,max_size)
+		    set usbtest::isochronous_out($number,devtab)	$usbtest::endpoint_data($i,devtab)
+		}
+	    }
+
+	    "interrupt" {
+		set number $usbtest::endpoint_data($i,number)
+		if { "in" == $usbtest::endpoint_data($i,direction) } {
+		    lappend usbtest::interrupt_in_endpoints $number
+		    set usbtest::interrupt_in($number,min_size)		$usbtest::endpoint_data($i,min_size)
+		    set usbtest::interrupt_in($number,max_size)		$usbtest::endpoint_data($i,max_size)
+		    set usbtest::interrupt_in($number,devtab)		$usbtest::endpoint_data($i,devtab)
+		} else {
+		    lappend usbtest::interrupt_out_endpoints $number
+		    set usbtest::interrupt_out($number,min_size)	$usbtest::endpoint_data($i,min_size)
+		    set usbtest::interrupt_out($number,max_size)	$usbtest::endpoint_data($i,max_size)
+		    set usbtest::interrupt_out($number,devtab)		$usbtest::endpoint_data($i,devtab)
+		}
+	    }
+
+	    default {
+		puts stderr "Internal error: invalid endpoint type $usbtest::endpoint_data($i,type)"
+		exit 1
+	    }
+	}
+    }
+}
+
+# }}}
+# {{{  Constants					
+
+# The C code expects to receive certain data as simple numbers,
+# corresponding to #define's in common.c and elsewhere. Strictly
+# speaking it would be better to pass strings to the C code and
+# have it do the translation, thus ensuring that these constants
+# exist in only one place.
+
+namespace eval usbtest {
+
+    variable _USB_DIR_IN        0x0080
+    variable _DATA_NONE		     0
+    variable _DATA_BYTE_FILL	     1
+    variable _DATA_WORD_FILL	     2
+    variable _DATA_BYTE_GEN	     3
+    variable _DATA_WORD_GEN	     4
+    variable _IO_MECHANISM_USB	     1
+    variable _IO_MECHANISM_DEV	     2
+}
+
+# It is also desirable to have some constants corresponding
+# to common random number generators.
+namespace eval usbtest {
+    variable MULTIPLIER        1103515245
+    variable INCREMENT              12345
+}
+
+# }}}
+# {{{  Argument processing                              
+
+# ----------------------------------------------------------------------------
+# Given a list of arguments of the form "xyzzy=123" or "xyzzy 123", and
+# an array arguments containing entries such as arguments(xyzzy) and
+# already filled in with default values, update the array using the
+# actual arguments
+namespace eval usbtest {
+
+    proc process_arguments { list array_ref } {
+	upvar $array_ref array
+	array set defined_args [list]
+
+	set listlen [llength $list]
+	for { set index 0 } { $index < $listlen } { incr index } {
+	    set arg [lindex $list $index]
+	    set found 0
+	    foreach name [array names array] {
+		set len [string length $name]
+		if { [string equal -length $len $name $arg] } {
+		    # Partial match found.
+		    if { [string equal $name $arg] } {
+			# Exact match found, The value must be the next arg.
+			if { [info exists defined_args($name)] } {
+			    error "Argument $name should be specified only once"
+			}
+			incr index
+			if { $index >= $listlen } {
+			    error "Missing value after argument $name"
+			}
+			set array($name) [lindex $list $index]
+			set found 1
+			break
+		    }
+
+		    # Not an exact match. Try looking for x=y
+		    incr len
+		    if { [string equal -length $len "$name=" $arg] } {
+			if { [info exists defined_args($name)] } {
+			    error "Argument $name should be specified only once"
+			}
+			set array($name) [string range $arg $len end]
+			set found 1
+			break
+		    }
+		}
+	    }
+	    if { ! $found } {
+		error "Invalid argument $arg"
+	    }
+	}
+    }
+}
+
+# }}}
+# {{{  Starting and ending tests			
+
+# This section deals with starting tests, or cleaning up when the
+# tests cannot actually proceed. Also there is some validation,
+# for example to make sure that no endpoint number is used for
+# multiple tests.
+
+namespace eval usbtest {
+    variable results
+    variable _tests_submitted 0
+    variable _control_endpoint_in_use 0
+    variable _in_endpoints_in_use [list]
+    variable _out_endpoints_in_use [list]
+    
+    proc reset { } {
+	if { 0 != $usbtest::_tests_submitted } {
+	    usbtest::_cancel
+	    set usbtest::_tests_submitted 0
+	    
+	}
+	set usbtest::_in_endpoints_in_use [list]
+	set usbtest::_out_endpoints_in_use [list]
+    }
+
+    proc use_endpoint { endpoint direction } {
+	if { 0 == $endpoint } {
+	    if { $usbtest::_control_endpoint_in_use } {
+		error "Attempt to run multiple tests on the control endpoint"
+	    }
+	    set usbtest::_control_endpoint_in_use 1
+	} else {
+	    switch -- $direction {
+		"in" {
+		    if { -1 != [lsearch -exact $usbtest::_in_endpoints_in_use $endpoint] } {
+			error "Attempt to run multiple IN tests on endpoint $endpoint"
+		    }
+		    lappend usbtest::_in_endpoints_in_use $endpoint
+		}
+
+		"out" {
+		    if { -1 != [lsearch -exact $usbtest::_out_endpoints_in_use $endpoint] } {
+			error "Attempt to run multiple OUT tests on endpoint $endpoint"
+		    }
+		    lappend usbtest::_out_endpoints_in_use $endpoint
+		}
+
+		default {
+		    error "Invalid direction passed to usbtest::use_endpoint"
+		}
+	    }
+	}
+    }
+
+    proc test_submitted { } {
+	incr usbtest::_tests_submitted
+    }
+    
+    proc start { timeout } {
+	set result 0
+	if { 0 == $usbtest::_tests_submitted } {
+	    error "Attempt to start tests when no tests are scheduled to run."
+	} elseif { ! [string is integer -strict $timeout] } {
+	    error "Invalid timeout specified, it should be a simple number."
+	} else {
+	    set usbtest::results [list]
+	    set result [usbtest::_run $timeout]
+	    set usbtest::_tests_submitted 0
+	    set usbtest::_control_endpoint_in_use 0
+	    array unset _in_endpoints_in_use
+	    array unset _out_endpoints_in_use
+	}
+	return $result
+    }
+}
+
+# }}}
+# {{{  Bulk tests					
+
+# Prepare to run a bulk test.
+#
+# This test requires rather a lot of parameters, many of which
+# will have sensible defaults.
+
+namespace eval usbtest {
+
+    proc bulktest { endpoint direction number_packets args } {
+
+	
+	# Parameters to be passed to the C code. Most are
+	# held in an array indexed by the option name,
+	# facilitating command-line parsing.
+	set arguments(format)        "none"
+	set arguments(data1)         0
+	set arguments(data*)         1
+	set arguments(data+)         0
+	set arguments(data1*)        1
+	set arguments(data1+)        0
+	set arguments(data**)        1
+	set arguments(data*+)        0
+	set arguments(data+*)        1
+	set arguments(data++)        0
+	set arguments(mechanism)     "usb"
+	set arguments(txsize1)       32
+	set arguments(txsize>=)      0
+	set arguments(txsize<=)      -1
+	set arguments(txsize*)       1
+	set arguments(txsize/)       1
+	set arguments(txsize+)       0
+	set arguments(rxsize1)       0
+	set arguments(rxsize>=)      0
+	set arguments(rxsize<=)      -1
+	set arguments(rxsize*)       1
+	set arguments(rxsize/)       1
+	set arguments(rxsize+)       0
+	set arguments(txdelay1)      0
+	set arguments(txdelay>=)     0
+	set arguments(txdelay<=)     1000000000
+	set arguments(txdelay*)      1
+	set arguments(txdelay/)      1
+	set arguments(txdelay+)      0
+	set arguments(rxdelay1)      0
+	set arguments(rxdelay>=)     0
+	set arguments(rxdelay<=)     1000000000
+	set arguments(rxdelay*)      1
+	set arguments(rxdelay/)      1
+	set arguments(rxdelay+)      0
+	
+	set endpoint_param			""
+
+	# Target limits
+	set target_min_size			0
+	set target_max_size			0
+	set target_padding			0
+	set target_devtab			""
+	
+	# Start by validating the endpoint and direction arguments.
+	# Also check that the specified endpoint is not yet in use.
+	if { ![string is integer -strict $endpoint] } {
+	    error "Invalid endpoint argument \"$endpoint\": should be a number"
+	}
+	if { ($endpoint < 1) || ($endpoint > 15) } {
+	    error "Invalid bulk endpoint argument \"$endpoint\": should be between 1 and 15"
+	}
+	switch -- $direction {
+	    "in" -
+	    "In" -
+	    "IN" {
+		set direction "in"
+		if { -1 == [lsearch -exact $usbtest::bulk_in_endpoints $endpoint] } {
+		    error "Invalid bulk endpoint argument \"$endpoint\": the target does not list that as a bulk IN endpoint"
+		}
+		set target_min_size	 $usbtest::bulk_in($endpoint,min_size)
+		set target_max_size	 $usbtest::bulk_in($endpoint,max_size)
+		set target_padding       $usbtest::bulk_in($endpoint,max_in_padding)
+		set target_devtab	 $usbtest::bulk_in($endpoint,devtab);
+		set endpoint_param [expr $endpoint | $usbtest::_USB_DIR_IN]
+	    }
+
+	    "out" -
+	    "Out" -
+	    "OUT" {
+		set direction "out"
+		if { -1 == [lsearch -exact $usbtest::bulk_out_endpoints $endpoint] } {
+		    error "Invalid bulk endpoint argument \"$endpoint\": the target does not list that as a bulk OUT endpoint"
+		}
+		set target_min_size	 $usbtest::bulk_out($endpoint,min_size)
+		set target_max_size	 $usbtest::bulk_out($endpoint,max_size)
+		set target_devtab	 $usbtest::bulk_out($endpoint,devtab);
+		set target_padding       0; # Not applicable
+		set endpoint_param       $endpoint
+	    }
+
+	    default {
+		error "Invalid direction argument \"$direction\": should be \"in\" or \"out\""
+	    }
+	}
+
+	# Now parse any remaining arguments
+	usbtest::process_arguments $args arguments
+	
+	# Convert two of the arguments from strings to numbers, for the
+	# convenience of the C code
+	switch -- $arguments(format) {
+	    "none"      { set arguments(format) $usbtest::_DATA_NONE }
+	    "bytefill"  { set arguments(format) $usbtest::_DATA_BYTE_FILL }
+	    "wordfill"  { set arguments(format) $usbtest::_DATA_WORD_FILL }
+	    "byteseq"   { set arguments(format) $usbtest::_DATA_BYTE_GEN }
+	    "wordseq"   { set arguments(format) $usbtest::_DATA_WORD_GEN }
+
+	    default {
+		error "Invalid data format argument \"$arguments(data)\"\n    \
+		       Should be \"none\", \"bytefill\", \"wordfill\", \"byteseq\" or \"wordseq\""
+	    }
+	}
+	switch -- $arguments(mechanism) {
+	    "usb"       { set arguments(mechanism) $usbtest::_IO_MECHANISM_USB }
+	    "devtab"    { set arguments(mechanism) $usbtest::_IO_MECHANISM_DEV }
+
+	    default {
+		error "Invalid mechanism argument \"$arguments(mechanism)\"\n    \
+		       Should be \"usb\" or \"devtab\""
+	    }
+	}
+
+	puts "validating fields"
+	# Validate the remaining fields
+	foreach field [list data1 data* data+ data1* data1+ data** data*+ data+* data++ \
+	                    txsize1 txsize>= txsize<= txsize* txsize/ txsize+           \
+	                    rxsize1 rxsize>= rxsize<= rxsize* rxsize/ rxsize+           \
+	                    txdelay1 txdelay>= txdelay<= txdelay* txdelay/ txdelay+     \
+	                    rxdelay1 rxdelay>= rxdelay<= rxdelay* rxdelay/ rxdelay+] {
+            if { ![string is integer -strict $arguments($field)] } {
+		error "Invalid value \"$arguments($field)\" for argument $field, should be an integer."
+	    }
+        }
+	
+	if { $arguments(txsize>=) < $target_min_size } {
+	    set arguments(txsize>=) $target_min_size
+	}
+	if { (-1 == $arguments(txsize<=) ) || ($arguments(txsize<=) > $target_max_size) } {
+	    set arguments(txsize<=) $target_max_size
+	}
+	if { $arguments(rxsize<=) == -1 } {
+	    set arguments(rxsize<=) $target_max_size
+	}
+	if { $arguments(txsize1) < $arguments(txsize>=) } {
+	    set arguments(txsize1) $arguments(txsize>=)
+	}
+	# Make sure the endpoint is not already in use
+	usbtest::use_endpoint $endpoint $direction
+
+	puts "Submitting test"
+	# Now submit the test. This is handled by C code.
+	usbtest::_test_bulk             \
+		$number_packets		\
+		$endpoint_param		\
+		$arguments(txsize1)     \
+	        $arguments(txsize>=)    \
+	        $arguments(txsize<=)    \
+	        $arguments(txsize*)     \
+	        $arguments(txsize/)     \
+	        $arguments(txsize+)     \
+	        $arguments(rxsize1)     \
+	        $arguments(rxsize>=)    \
+	        $arguments(rxsize<=)    \
+	        $arguments(rxsize*)     \
+	        $arguments(rxsize/)     \
+	        $arguments(rxsize+)     \
+	        $target_padding         \
+	        $arguments(txdelay1)    \
+	        $arguments(txdelay>=)   \
+	        $arguments(txdelay<=)   \
+	        $arguments(txdelay*)    \
+	        $arguments(txdelay/)    \
+	        $arguments(txdelay+)    \
+	        $arguments(rxdelay1)    \
+	        $arguments(rxdelay>=)   \
+	        $arguments(rxdelay<=)   \
+	        $arguments(rxdelay*)    \
+	        $arguments(rxdelay/)    \
+	        $arguments(rxdelay+)    \
+	        $arguments(mechanism)   \
+	        $arguments(format)      \
+	        $arguments(data1)       \
+	        $arguments(data*)       \
+	        $arguments(data+)       \
+	        $arguments(data1*)      \
+	        $arguments(data1+)      \
+	        $arguments(data**)      \
+	        $arguments(data*+)      \
+	        $arguments(data+*)      \
+	        $arguments(data++)
+
+	test_submitted
+    }
+}
+
+# }}}
+# {{{  Execute the specified test script 		
+
+# Interpret the arguments as a test script plus auxiliary data.
+set script [lindex $::argv 0]
+set ::argv [lrange $::argv 1 end]
+
+set result [catch {
+    set path [file join [pwd] $script]
+    if { ![file exists $path] } {
+	set path "$path.tcl"
+	if { ![file exists $path] } {
+	    set path [file join $usbtest::USBAUXDIR $script]
+	    if { ![file exists $path] } {
+		set path "$path.tcl"
+		if { ![file exists $path] } {
+		    error "Error: unknown test script $script"
+		}
+	    }
+	}
+    }
+    
+    source $path
+    
+} message]
+
+if { 0 != $result } {
+    puts $message
+}
+
+# }}}
new file mode 100644
--- /dev/null
+++ b/packages/io/usb/slave/current/host/verbose.tcl
@@ -0,0 +1,71 @@
+# {{{  Banner                           
+
+#===============================================================================
+#
+#    verbose.tcl
+#
+#    Support for USB testing
+#
+#===============================================================================
+#####ECOSGPLCOPYRIGHTBEGIN####
+## -------------------------------------------
+## This file is part of eCos, the Embedded Configurable Operating System.
+## Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+##
+## eCos 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 or (at your option) any later version.
+##
+## eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
+## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+##
+## As a special exception, if other files instantiate templates or use macros
+## or inline functions from this file, or you compile this file and link it
+## with other works to produce a work based on this file, this file does not
+## by itself cause the resulting work to be covered by the GNU General Public
+## License. However the source code for this file must still be made available
+## in accordance with section (3) of the GNU General Public License.
+##
+## This exception does not invalidate any other reasons why a work based on
+## this file might be covered by the GNU General Public License.
+##
+## Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+## at http://sources.redhat.com/ecos/ecos-license/
+## -------------------------------------------
+#####ECOSGPLCOPYRIGHTEND####
+#===============================================================================
+######DESCRIPTIONBEGIN####
+#
+# Author(s):	bartv
+# Date:		2002-01-16
+# Purpose:      Set the target-side verbosity level
+#
+#####DESCRIPTIONEND####
+#===============================================================================
+#
+
+# }}}
+
+if { 1 == $argc } {
+    puts "The desired verbosity level must be specified on the command line."
+    puts "This should be a small integer. A value of 0 suppresses run-time"
+    puts "logging within the target."
+    exit 1
+}
+set level [lindex $argv 0]
+
+if { ![string is integer -strict $level ] } {
+    puts "Invalid verbosity level \"$level\""
+    puts "This should be a small integer. A value of 0 suppresses run-time"
+    puts "logging within the target."
+    exit 1
+}
+
+usbtest::target_verbose $level
+
new file mode 100644
--- /dev/null
+++ b/packages/io/usb/slave/current/tests/common.c
@@ -0,0 +1,681 @@
+/*{{{  Banner                                                   */
+
+/*=================================================================
+//
+//        common.c
+//
+//        USB testing - code common to host and target
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+//
+// eCos 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 or (at your option) any later version.
+//
+// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+// at http://sources.redhat.com/ecos/ecos-license/
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// This module contains some definitions and functions that are common to
+// both the host and target side of USB testing, for example filling in
+// a buffer with well-known data and validating the contents at the other end.
+// The module is #include'd by other code rather than compiled separately,
+// which simplifies the build process.
+//
+// Author(s):     bartv
+// Date:          2001-08-14
+//####DESCRIPTIONEND####
+//==========================================================================
+*/
+
+/*}}}*/
+
+/*{{{  Simple data pack and unpack operations                   */
+
+// ----------------------------------------------------------------------------
+// Utilities to pack and unpack data into buffers. 
+//
+// Integers are transferred with 32 bits of precision, irrespective
+// of the capabilities of either target and host.
+
+static inline void
+pack_int(int datum, unsigned char* buffer, int* index_ptr)
+{
+    int index = *index_ptr;
+    buffer[index++] = (datum >>  0) & 0x0FF;
+    buffer[index++] = (datum >>  8) & 0x0FF;
+    buffer[index++] = (datum >> 16) & 0x0FF;
+    buffer[index++] = (datum >> 24) & 0x0FF;
+    *index_ptr = index;
+}
+
+static inline int
+unpack_int(unsigned char* buffer, int* index_ptr)
+{
+    int index   = *index_ptr;
+    int result;
+
+    result  = (buffer[index++] <<  0);
+    result |= (buffer[index++] <<  8);
+    result |= (buffer[index++] << 16);
+    result |= (buffer[index++] << 24);
+    *index_ptr = index;
+    return result;
+}
+
+/*}}}*/
+/*{{{  Buffer data and validation                               */
+
+// ----------------------------------------------------------------------------
+// The data required for a given test. For some test cases, for
+// example when trying to achieve maximum throughput, it does not
+// matter what data is transferred. For other tests it is important to
+// validate that the data sent and received match up, and there should
+// be some control over the actual data: some tests might want to send
+// a long sequence of byte 0, while others want to send more random data
+// for which a simple random number generator is useful.
+//
+// Exactly the same routines are used on both host and target to fill in
+// and check buffers, and they are sufficiently simple that the routines
+// should get compiled in compatible ways.
+//
+// There is no support at present for sending specific data, e.g. a
+// specific ethernet packet that appears to be causing problems. Knowledge
+// of specific data cannot be compiled into the test code, so the only
+// way to implement something like this would be to transfer the
+// problematical data over the USB bus in order to determine whether or
+// not the bus is capable of reliably transferring this data. That is
+// not entirely impossible (checksums, use of alternative endpoints),
+// but it is not implemented.
+//
+// An alternative approach would be to support a bounce operation
+// involving both an IN and an OUT endpoint, doing validation only on
+// the host. Again that is not yet implemented.
+//
+// The byte_fill and int_fill options are actually redundant because the
+// same effect can be achieved using a multiplier of 1 and an increment
+// of 0, but they can be implemented much more efficiently so may be
+// useful for benchmarks.
+
+typedef enum usbtestdata {
+    usbtestdata_none        = 0,       // There is nothing useful in the data
+    usbtestdata_bytefill    = 1,       // The data consists of a single byte, repeated
+    usbtestdata_wordfill    = 2,       // Or a single integer
+    usbtestdata_byteseq     = 3,       // Or a pseudo-random sequence (a * seed) + b
+    usbtestdata_wordseq     = 4        // as either bytes or integers
+} usbtestdata;
+
+typedef struct UsbTestData {
+    usbtestdata     format;
+    int             seed;
+    int             multiplier; // 1103515245
+    int             increment;  // 12345
+    int             transfer_seed_multiplier;
+    int             transfer_seed_increment;
+    int             transfer_multiplier_multiplier;
+    int             transfer_multiplier_increment;
+    int             transfer_increment_multiplier;
+    int             transfer_increment_increment;
+} UsbTestData;
+
+static void
+usbtest_fill_buffer(UsbTestData* how, unsigned char* buffer, int length)
+{
+    switch(how->format)
+    {
+      case usbtestdata_none:
+        return;
+        
+      case usbtestdata_bytefill:
+        // Leave it to the system to optimise memset().
+        memset(buffer, (how->seed & 0x0FF), length);
+        break;
+
+      case usbtestdata_wordfill:
+      {
+          // The buffer may not be a multiple of four bytes, so the last entry is always
+          // zero'd.
+          int i;
+          int index = 0;
+          for (i = 0; i < (length / 4); i++) {
+              pack_int(how->seed, buffer, &index);
+          }
+          pack_int(0, buffer, &index);
+          break;
+      }
+
+      case usbtestdata_byteseq:
+      {
+          int i;
+          for (i = 0; i < length; i++) {
+              buffer[i] = (how->seed & 0x00FF);
+              how->seed *= how->multiplier;
+              how->seed += how->increment;
+          }
+          break;
+      }
+
+      case usbtestdata_wordseq:
+      {
+          int i;
+          int index = 0;
+          for (i = 0; i < (length / 4); i++) {
+              pack_int(how->seed, buffer, &index);
+              how->seed *= how->multiplier;
+              how->seed += how->increment;
+          }
+          pack_int(0, buffer, &index);
+          break;
+      }
+    }
+
+    // After each transfer update the seed, multiplier and increment
+    // ready for the next one.
+    how->seed       *= how->transfer_seed_multiplier;
+    how->seed       += how->transfer_seed_increment;
+    how->multiplier *= how->transfer_multiplier_multiplier;
+    how->multiplier += how->transfer_multiplier_increment;
+    how->increment  *= how->transfer_increment_multiplier;
+    how->increment  += how->transfer_increment_increment;
+}
+
+static int
+usbtest_check_buffer(UsbTestData* how, unsigned char* buffer, int length)
+{
+    int result  = 1;
+
+    switch(how->format) {
+      case usbtestdata_none:
+        break;
+
+      case usbtestdata_bytefill:
+      {
+          int i;
+          result = 1;
+          for (i = 0; i < length; i++) {
+              if (buffer[i] != (how->seed & 0x00FF)) {
+                  result = 0;
+                  break;
+              }
+          }
+          break;
+      }
+
+      case usbtestdata_wordfill:
+      {
+          int i;
+          int index = 0;
+          for (i = 0; i < (length / 4); i++) {
+              int datum = unpack_int(buffer, &index);
+              if (datum != (how->seed & 0x0FFFFFFFF)) {
+                  result = 0;
+                  break;
+              }
+          }
+          for (i = 4 * i; result && (i < length); i++) {
+              if (0 != buffer[i]) {
+                  result = 0;
+                  break;
+              }
+          }
+          break;
+      }
+
+      case usbtestdata_byteseq:
+      {
+          int i;
+          for (i = 0; i < length; i++) {
+              if (buffer[i] != (how->seed & 0x00FF)) {
+                  result = 0;
+                  break;
+              }
+              how->seed *= how->multiplier;
+              how->seed += how->increment;
+          }
+          break;
+      }
+
+      case usbtestdata_wordseq:
+      {
+          int   i;
+          int   index = 0;
+          
+          for (i = 0; i < (length / 4); i++) {
+              int datum = unpack_int(buffer, &index);
+              if (datum != (how->seed & 0x0FFFFFFFF)) {
+                  result = 0;
+                  break;
+              }
+              how->seed *= how->multiplier;
+              how->seed += how->increment;
+          }
+          for (i = 4 * i; result && (i < length); i++) {
+              if (0 != buffer[i]) {
+                  result = 0;
+                  break;
+              }
+          }
+          break;
+      }
+    }
+
+    // After each transfer update the seed, multiplier and increment
+    // ready for the next transfer.
+    how->seed       *= how->transfer_seed_multiplier;
+    how->seed       += how->transfer_seed_increment;
+    how->multiplier *= how->transfer_multiplier_multiplier;
+    how->multiplier += how->transfer_multiplier_increment;
+    how->increment  *= how->transfer_increment_multiplier;
+    how->increment  += how->transfer_increment_increment;
+    
+    return result;
+}
+
+#ifdef HOST
+static void
+pack_usbtestdata(UsbTestData* data, unsigned char* buf, int* index)
+{
+    pack_int((int)data->format,                         buf, index);
+    pack_int((int)data->seed,                           buf, index);
+    pack_int((int)data->multiplier,                     buf, index);
+    pack_int((int)data->increment,                      buf, index);
+    pack_int((int)data->transfer_seed_multiplier,       buf, index);
+    pack_int((int)data->transfer_seed_increment,        buf, index);
+    pack_int((int)data->transfer_multiplier_multiplier, buf, index);
+    pack_int((int)data->transfer_multiplier_increment,  buf, index);
+    pack_int((int)data->transfer_increment_multiplier,  buf, index);
+    pack_int((int)data->transfer_increment_increment,   buf, index);
+}
+#endif
+
+#ifdef TARGET
+static void
+unpack_usbtestdata(UsbTestData* data, unsigned char* buf, int* index)
+{
+    data->format                        = (usbtestdata) unpack_int(buf, index);
+    data->seed                          = unpack_int(buf, index);
+    data->multiplier                    = unpack_int(buf, index);
+    data->increment                     = unpack_int(buf, index);
+    data->transfer_seed_multiplier      = unpack_int(buf, index);
+    data->transfer_seed_increment       = unpack_int(buf, index);
+    data->transfer_multiplier_multiplier= unpack_int(buf, index);
+    data->transfer_multiplier_increment = unpack_int(buf, index);
+    data->transfer_increment_multiplier = unpack_int(buf, index);
+    data->transfer_increment_increment  = unpack_int(buf, index);
+}
+#endif
+
+/*}}}*/
+/*{{{  Testcase definitions                                     */
+
+// ----------------------------------------------------------------------------
+// Definitions of the supported test cases. The actual implementations need
+// to vary between host and target.
+
+typedef enum usbtest {
+    usbtest_invalid     = 0,
+    usbtest_bulk_out    = 1,
+    usbtest_bulk_in     = 2,
+    usbtest_control_in  = 3
+} usbtest;
+
+// What I/O mechanism should be used on the target to process data?
+typedef enum usb_io_mechanism {
+    usb_io_mechanism_usb    = 1,        // The low-level USB-specific API
+    usb_io_mechanism_dev    = 2         // cyg_devio_cread() et al
+} usb_io_mechanism;
+
+// Bulk transfers. The same structure can be used for IN and OUT transfers.
+// The endpoint number will be or'd with either USB_DIR_IN or USB_DIR_OUT,
+// or the equivalent under eCos.
+typedef struct UsbTest_Bulk {
+    int                 number_packets;
+    int                 endpoint;
+    int                 tx_size;
+    int                 tx_size_min;
+    int                 tx_size_max;
+    int                 tx_size_multiplier;
+    int                 tx_size_divisor;
+    int                 tx_size_increment;
+    int                 rx_size;
+    int                 rx_size_min;
+    int                 rx_size_max;
+    int                 rx_size_multiplier;
+    int                 rx_size_divisor;
+    int                 rx_size_increment;
+    int                 rx_padding;
+    int                 tx_delay;
+    int                 tx_delay_min;
+    int                 tx_delay_max;
+    int                 tx_delay_multiplier;
+    int                 tx_delay_divisor;
+    int                 tx_delay_increment;
+    int                 rx_delay;
+    int                 rx_delay_min;
+    int                 rx_delay_max;
+    int                 rx_delay_multiplier;
+    int                 rx_delay_divisor;
+    int                 rx_delay_increment;
+    usb_io_mechanism    io_mechanism;
+    UsbTestData         data;
+} UsbTest_Bulk;
+
+#ifdef HOST
+static void
+pack_usbtest_bulk(UsbTest_Bulk* test, unsigned char* buffer, int* index)
+{
+    pack_int(test->number_packets,          buffer, index);
+    pack_int(test->endpoint,                buffer, index);
+    pack_int(test->tx_size,                 buffer, index);
+    pack_int(test->tx_size_min,             buffer, index);
+    pack_int(test->tx_size_max,             buffer, index);
+    pack_int(test->tx_size_multiplier,      buffer, index);
+    pack_int(test->tx_size_divisor,         buffer, index);
+    pack_int(test->tx_size_increment,       buffer, index);
+    pack_int(test->rx_size,                 buffer, index);
+    pack_int(test->rx_size_min,             buffer, index);
+    pack_int(test->rx_size_max,             buffer, index);
+    pack_int(test->rx_size_multiplier,      buffer, index);
+    pack_int(test->rx_size_divisor,         buffer, index);
+    pack_int(test->rx_size_increment,       buffer, index);
+    // There is no need to transfer the padding field. It is only of
+    // interest on the host, and this message is being packed
+    // for the target side.
+    pack_int(test->tx_delay,                buffer, index);
+    pack_int(test->tx_delay_min,            buffer, index);
+    pack_int(test->tx_delay_max,            buffer, index);
+    pack_int(test->tx_delay_multiplier,     buffer, index);
+    pack_int(test->tx_delay_divisor,        buffer, index);
+    pack_int(test->tx_delay_increment,      buffer, index);
+    pack_int(test->rx_delay,                buffer, index);
+    pack_int(test->rx_delay_min,            buffer, index);
+    pack_int(test->rx_delay_max,            buffer, index);
+    pack_int(test->rx_delay_multiplier,     buffer, index);
+    pack_int(test->rx_delay_divisor,        buffer, index);
+    pack_int(test->rx_delay_increment,      buffer, index);
+    pack_int((int)test->io_mechanism,       buffer, index);
+    pack_usbtestdata(&(test->data),         buffer, index);
+}
+#endif
+
+#ifdef TARGET
+static void
+unpack_usbtest_bulk(UsbTest_Bulk* test, unsigned char* buffer, int* index)
+{
+    test->number_packets            = unpack_int(buffer, index);
+    test->endpoint                  = unpack_int(buffer, index);
+    test->tx_size                   = unpack_int(buffer, index);
+    test->tx_size_min               = unpack_int(buffer, index);
+    test->tx_size_max               = unpack_int(buffer, index);
+    test->tx_size_multiplier        = unpack_int(buffer, index);
+    test->tx_size_divisor           = unpack_int(buffer, index);
+    test->tx_size_increment         = unpack_int(buffer, index);
+    test->rx_size                   = unpack_int(buffer, index);
+    test->rx_size_min               = unpack_int(buffer, index);
+    test->rx_size_max               = unpack_int(buffer, index);
+    test->rx_size_multiplier        = unpack_int(buffer, index);
+    test->rx_size_divisor           = unpack_int(buffer, index);
+    test->rx_size_increment         = unpack_int(buffer, index);
+    test->tx_delay                  = unpack_int(buffer, index);
+    test->tx_delay_min              = unpack_int(buffer, index);
+    test->tx_delay_max              = unpack_int(buffer, index);
+    test->tx_delay_multiplier       = unpack_int(buffer, index);
+    test->tx_delay_divisor          = unpack_int(buffer, index);
+    test->tx_delay_increment        = unpack_int(buffer, index);
+    test->rx_delay                  = unpack_int(buffer, index);
+    test->rx_delay_min              = unpack_int(buffer, index);
+    test->rx_delay_max              = unpack_int(buffer, index);
+    test->rx_delay_multiplier       = unpack_int(buffer, index);
+    test->rx_delay_divisor          = unpack_int(buffer, index);
+    test->rx_delay_increment        = unpack_int(buffer, index);
+    test->io_mechanism              = (usb_io_mechanism) unpack_int(buffer, index);
+    unpack_usbtestdata(&(test->data), buffer, index);
+}
+#endif
+
+// A macro for moving on the next packet size. This also has to be shared between host
+// and target, if the two got out of synch then testing would go horribly wrong.
+//
+// The new packet size is determined using a multiplier and increment,
+// so to e.g. increase packet sizes by 4 bytes each time the
+// multiplier would be 1 and the increment would be 4, or to double
+// packet sizes the multiplier would be 2 and the increment would be
+// 0. On underflow or overflow the code tries to adjust the packet size
+// back to within the accepted range.
+
+#define USBTEST_NEXT_TX_SIZE(_x_)                               \
+    do {                                                        \
+        _x_.tx_size *= _x_.tx_size_multiplier;                  \
+        _x_.tx_size /= _x_.tx_size_divisor;                     \
+        _x_.tx_size += _x_.tx_size_increment;                   \
+        if (_x_.tx_size < _x_.tx_size_min) {                    \
+            if (_x_.tx_size_min == _x_.tx_size_max) {           \
+                _x_.tx_size = _x_.tx_size_min;                  \
+            } else {                                            \
+                int tmp  = _x_.tx_size_min - _x_.tx_size;       \
+                tmp     %= _x_.tx_size_max - _x_.tx_size_min;   \
+                _x_.tx_size = tmp + _x_.tx_size_min;            \
+            }                                                   \
+        } else if (_x_.tx_size > _x_.tx_size_max) {             \
+            if (_x_.tx_size_min == _x_.tx_size_max) {           \
+                _x_.tx_size = _x_.tx_size_max;                  \
+            } else {                                            \
+                int tmp  = _x_.tx_size - _x_.tx_size_max;       \
+                tmp     %= _x_.tx_size_max - _x_.tx_size_min;   \
+                _x_.tx_size = tmp + _x_.tx_size_min;            \
+            }                                                   \
+        }                                                       \
+    } while ( 0 )
+
+// A similar macro for moving on to the next receive size. This is less
+// critical since care is taken to always receive at least the current
+// tx size plus padding.
+// Note that padding needs to be added by the calling code, not here,
+// since padding is only applicable on the host-side and this macro
+// is used on both host and target.
+#define USBTEST_NEXT_RX_SIZE(_x_)                               \
+    do {                                                        \
+        _x_.rx_size *= _x_.rx_size_multiplier;                  \
+        _x_.rx_size /= _x_.rx_size_divisor;                     \
+        _x_.rx_size += _x_.rx_size_increment;                   \
+        if (_x_.rx_size < _x_.rx_size_min) {                    \
+            if (_x_.rx_size_min == _x_.rx_size_max) {           \
+                _x_.rx_size = _x_.rx_size_min;                  \
+            } else {                                            \
+                int tmp  = _x_.rx_size_min - _x_.rx_size;       \
+                tmp     %= _x_.rx_size_max - _x_.rx_size_min;   \
+                _x_.rx_size = tmp + _x_.rx_size_min;            \
+            }                                                   \
+        } else if (_x_.rx_size > _x_.rx_size_max) {             \
+            if (_x_.rx_size_min == _x_.rx_size_max) {           \
+                _x_.rx_size = _x_.rx_size_max;                  \
+            } else {                                            \
+                int tmp  = _x_.rx_size - _x_.rx_size_max;       \
+                tmp     %= _x_.rx_size_max - _x_.rx_size_min;   \
+                _x_.rx_size = tmp + _x_.rx_size_min;            \
+            }                                                   \
+        }                                                       \
+    } while ( 0 )
+
+// And a macro for adjusting the transmit delay.
+#define USBTEST_NEXT_TX_DELAY(_x_)                              \
+    do {                                                        \
+        _x_.tx_delay *= _x_.tx_delay_multiplier;                \
+        _x_.tx_delay /= _x_.tx_delay_divisor;                   \
+        _x_.tx_delay += _x_.tx_delay_increment;                 \
+        if (_x_.tx_delay < _x_.tx_delay_min) {                  \
+            if (_x_.tx_delay_min == _x_.tx_delay_max) {         \
+                _x_.tx_delay = _x_.tx_delay_min;                \
+            } else {                                            \
+                int tmp  = _x_.tx_delay_min - _x_.tx_delay;     \
+                tmp     %= _x_.tx_delay_max - _x_.tx_delay_min; \
+                _x_.tx_delay = tmp + _x_.tx_delay_min;          \
+            }                                                   \
+        } else if (_x_.tx_delay > _x_.tx_delay_max) {           \
+            if (_x_.tx_delay_min == _x_.tx_delay_max) {         \
+                _x_.tx_delay = _x_.tx_delay_max;                \
+            } else {                                            \
+                int tmp  = _x_.tx_delay - _x_.tx_delay_max;     \
+                tmp     %= _x_.tx_delay_max - _x_.tx_delay_min; \
+                _x_.tx_delay = tmp + _x_.tx_delay_min;          \
+            }                                                   \
+        }                                                       \
+    } while ( 0 )
+
+#define USBTEST_NEXT_RX_DELAY(_x_)                              \
+    do {                                                        \
+        _x_.rx_delay *= _x_.rx_delay_multiplier;                \
+        _x_.rx_delay /= _x_.rx_delay_divisor;                   \
+        _x_.rx_delay += _x_.rx_delay_increment;                 \
+        if (_x_.rx_delay < _x_.rx_delay_min) {                  \
+            if (_x_.rx_delay_min == _x_.rx_delay_max) {         \
+                _x_.rx_delay = _x_.rx_delay_min;                \
+            } else {                                            \
+                int tmp  = _x_.rx_delay_min - _x_.rx_delay;     \
+                tmp     %= _x_.rx_delay_max - _x_.rx_delay_min; \
+                _x_.rx_delay = tmp + _x_.rx_delay_min;          \
+            }                                                   \
+        } else if (_x_.rx_delay > _x_.rx_delay_max) {           \
+            if (_x_.rx_delay_min == _x_.rx_delay_max) {         \
+                _x_.rx_delay = _x_.rx_delay_max;                \
+            } else {                                            \
+                int tmp  = _x_.rx_delay - _x_.rx_delay_max;     \
+                tmp     %= _x_.rx_delay_max - _x_.rx_delay_min; \
+                _x_.rx_delay = tmp + _x_.rx_delay_min;          \
+            }                                                   \
+        }                                                       \
+    } while ( 0 )
+
+#define USBTEST_BULK_NEXT(_bulk_)                               \
+    USBTEST_NEXT_TX_SIZE(_bulk_);                               \
+    USBTEST_NEXT_RX_SIZE(_bulk_);                               \
+    USBTEST_NEXT_TX_DELAY(_bulk_);                              \
+    USBTEST_NEXT_RX_DELAY(_bulk_);
+
+// Control transfers, receives
+typedef struct UsbTest_ControlIn {
+    int         number_packets;
+    int         packet_size_initial;
+    int         packet_size_min;
+    int         packet_size_max;
+    int         packet_size_multiplier;
+    int         packet_size_increment;
+    UsbTestData data;
+} UsbTest_ControlIn;
+
+#ifdef HOST
+static void
+pack_usbtest_control_in(UsbTest_ControlIn* test, unsigned char* buffer, int* index)
+{
+    pack_int(test->number_packets,          buffer, index);
+    pack_int(test->packet_size_initial,     buffer, index);
+    pack_int(test->packet_size_min,         buffer, index);
+    pack_int(test->packet_size_max,         buffer, index);
+    pack_int(test->packet_size_multiplier,  buffer, index);
+    pack_int(test->packet_size_increment,   buffer, index);
+    pack_usbtestdata(&(test->data),         buffer, index);
+}
+#endif
+
+#ifdef TARGET
+static void
+unpack_usbtest_control_in(UsbTest_ControlIn* test, unsigned char* buffer, int* index)
+{
+    test->number_packets            = unpack_int(buffer, index);
+    test->packet_size_initial       = unpack_int(buffer, index);
+    test->packet_size_min           = unpack_int(buffer, index);
+    test->packet_size_max           = unpack_int(buffer, index);
+    test->packet_size_multiplier    = unpack_int(buffer, index);
+    test->packet_size_increment     = unpack_int(buffer, index);
+    unpack_usbtestdata(&(test->data), buffer, index);
+}
+#endif
+
+// For now control packet sizes are adjusted in exactly the same way as bulk transfers.
+#define USBTEST_CONTROL_NEXT_PACKET_SIZE(_packet_size_, _control_)                                          \
+    _packet_size_ = (_packet_size_ * _control_.packet_size_multiplier) + _control_.packet_size_increment;   \
+    if (_packet_size_ < _control_.packet_size_min) {                                                        \
+        _packet_size_ += _control_.packet_size_max - _control_.packet_size_min;                             \
+        if (_packet_size_ < _control_.packet_size_min) {                                                    \
+            _packet_size_ = _control_.packet_size_initial;                                                  \
+        }                                                                                                   \
+    } else if (_packet_size_ > _control_.packet_size_max) {                                                 \
+        _packet_size_ -= _control_.packet_size_max - _control_.packet_size_min;                             \
+        if (_packet_size_ > _control_.packet_size_max) {                                                    \
+            _packet_size_ = _control_.packet_size_initial;                                                  \
+        }                                                                                                   \
+    }
+
+/*}}}*/
+/*{{{  Recovery support                                         */
+
+// ----------------------------------------------------------------------------
+// When things go wrong threads on either the host or the target may get
+// locked up waiting for further communication that never happens, because
+// the other side has already raised an error. Recovery is possible by
+// performing an extra I/O operation. For example, if a thread on the
+// target is blocked waiting on an OUT endpoint then recovery is possible
+// by the host sending some data to that endpoint. Similarly if a thread
+// on the host is blocked then recovery involves the target either sending
+// or receiving some additional data. There are alternative approaches such
+// as stalling endpoints, but making sure that the requested communication
+// actually happens involves fewer dependencies on exactly how those
+// operations behave.
+
+typedef struct UsbTest_Recovery {
+    int     endpoint;       // Top bit indicates direction, -1 indicates invalid
+    int     protocol;
+    int     size;
+} UsbTest_Recovery;
+
+static void
+pack_usbtest_recovery(UsbTest_Recovery* recovery, unsigned char* buffer, int* index)
+{
+    pack_int(recovery->endpoint, buffer, index);
+    pack_int(recovery->protocol, buffer, index);
+    pack_int(recovery->size,     buffer, index);
+}
+
+static void
+unpack_usbtest_recovery(UsbTest_Recovery* recovery, unsigned char* buffer, int *index)
+{
+    recovery->endpoint  = unpack_int(buffer, index);
+    recovery->protocol  = unpack_int(buffer, index);
+    recovery->size      = unpack_int(buffer, index);
+}
+
+static void
+usbtest_recovery_reset(UsbTest_Recovery* recovery)
+{
+    recovery->endpoint  = -1;
+    recovery->protocol  = 0;
+    recovery->size      = 0;
+}
+
+/*}}}*/
new file mode 100644
--- /dev/null
+++ b/packages/io/usb/slave/current/tests/protocol.h
@@ -0,0 +1,194 @@
+//=================================================================
+//
+//        protocol.h
+//
+//        USB testing - host<->target protocol
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+//
+// eCos 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 or (at your option) any later version.
+//
+// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+// at http://sources.redhat.com/ecos/ecos-license/
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// This header file is shared between target and host, and serves to
+// define certain aspects of the protocol used between the two such
+// as request codes.
+//
+// Author(s):     bartv
+// Date:          2001-07-04
+//####DESCRIPTIONEND####
+//==========================================================================
+
+// The largest control packet that will be sent or expected.
+#define USBTEST_MAX_CONTROL_DATA        255
+
+// The largest error message that can be sent.
+#define USBTEST_MAX_MESSAGE             254
+
+// The largest bulk transfer that will be sent or expected. Because of
+// the use of the USB devfs support in the Linux kernel this is
+// currently limited to a single page, i.e. 4096 bytes. To allow for
+// padding, it is actually reduced to a slightly smaller size of 4090
+// bytes. This should still be sufficient to test most interesting
+// boundary conditions, apart from the transition to >64K. 
+//
+// A small amount of additional buffer space should be allocated by
+// both host and target to allow for padding and possibly cache
+// alignment. All other protocols involve smaller transfers than this,
+// <= 64 bytes for interrupt transfers, <= 1023 for isochronous.
+#define USBTEST_MAX_BULK_DATA           (4096)
+#define USBTEST_MAX_BULK_DATA_EXTRA     1024
+
+// The maximum number of tests that can be run concurrently. Each
+// needs a separate thread, stack, and buffer so there are memory
+// consumption implications.
+#define USBTEST_MAX_CONCURRENT_TESTS    8
+
+// Allow the host to find out the number of endpoints supported on
+// this target. The theoretical maximum number of endpoints is 91
+// (endpoint 0 control, endpoint 1-15 for both IN and OUT bulk, iso
+// and interrupt) so a single byte response will suffice. The value
+// and index fields are not used.
+#define USBTEST_MAX_ENDPOINTS           91
+#define USBTEST_ENDPOINT_COUNT          0x001
+
+// Get hold of additional information about a specific entry in the
+// array of endpoint details. The index field in the request
+// identifies the entry of interest. The reply information is as per
+// the usbs_testing_endpoint structure, and consists of:
+//   1) one byte, the endpoint type (control, bulk, ...)
+//   2) one byte, the endpoint number (as opposed to the array index number)
+//   3) one byte for direction, USB_DIR_IN or USB_DIR_OUT
+//   4) one byte for max_in_padding, usually 0
+//   5) four bytes for min_size, 32-bit little-endian integer
+//   6) four bytes for max_size, 32-bit little-endian integer
+//   7) an additional n bytes for the devtab name, max ~240 bytes
+//      although usually far less.
+#define USBTEST_ENDPOINT_DETAILS        0x002
+
+// Report pass or failure. The host will send a string of up to
+// MAX_CONTROL_DATA bytes. The value and index fields are not used.
+#define USBTEST_PASS                    0x003
+#define USBTEST_PASS_EXIT               0x004
+#define USBTEST_FAIL                    0x005
+#define USBTEST_FAIL_EXIT               0x006
+
+// Synchronise. One problem with the current eCos USB API is that
+// there is no way to have a delayed response to a control message.
+// Any such support would be tricky, there are significant differences
+// in the hardware implementations and also timing constraints that
+// need to be satisfied. Instead the entire response to any control
+// request has to be prepared at DSR level. Usually this does not
+// cause any problems, e.g. for handling the standard control
+// messages, but for USB testing it may not be possible to handle a
+// request entirely at DSR level - yet the next full request should
+// not come in until the current one has been handled at thread-level.
+// To work around this there is support for a synchronization control
+// message. The return value is a single byte, 1 if the target is
+// ready for new requests, 0 if there is a still a request being
+// processed. The host can then perform some polling.
+#define USBTEST_SYNCH                   0x007
+
+// Abort. There is no easy way to get both host and target back to a
+// known state, so abort the current test run.
+#define USBTEST_ABORT                   0x008
+
+// Cancel the current batch of tests. Something has gone wrong at the
+// Tcl level, so any tests already prepared must be abandoned. No
+// additional data is required.
+#define USBTEST_CANCEL                  0x009
+
+// Start the current batch of tests. No additional data is involved
+// or expected.
+#define USBTEST_START                   0x00A
+
+// Has the current batch of tests finished? The host side polls the
+// target at regular intervals for this information.
+#define USBTEST_FINISHED                0x00B
+
+// Set the test-terminated flag. Something has gone wrong, probably a
+// timeout.
+#define USBTEST_SET_TERMINATED          0x00C
+
+// Get hold of recovery information for thread i in the target, where
+// the index field of the request identifies the thread. The result
+// is zero-bytes if the specified test has already finished, otherwise
+// a recovery structure.
+#define USBTEST_GET_RECOVERY            0x00D
+
+// The target should perform a recovery action to unlock a thread
+// on the host. The request holds a recovery structure.
+#define USBTEST_PERFORM_RECOVERY        0x00E
+
+// Collect the test result. The result is a single byte that indicates
+// pass or fail, optionally followed by a failure message.
+#define USBTEST_GET_RESULT              0x00F
+
+// The current batch of tests has completed. Perform any final clean-ups.
+#define USBTEST_BATCH_DONE              0x010
+
+// Set the verbosity level on the target-side
+#define USBTEST_VERBOSE                 0x011
+
+// Perform endpoint initialization to ensure host and target
+// can actually communicate over a given endpoint
+#define USBTEST_INIT_CONTROL            0x012
+#define USBTEST_INIT_BULK_IN            0x013
+#define USBTEST_INIT_BULK_OUT           0x014
+#define USBTEST_INIT_ISO_IN             0x015
+#define USBTEST_INIT_ISO_OUT            0x016
+#define USBTEST_INIT_INTERRUPT_IN       0x017
+#define USBTEST_INIT_INTERRUPT_OUT      0x018
+
+
+// A standard bulk test. The data consists of a UsbTest_Bulk
+// structure, suitably packed.
+#define USBTEST_TEST_BULK               0x040
+
+// A control-IN test. The host will send reserved control messages with
+// an appropriate length field, and the target should return that data.
+#define USBTEST_TEST_CONTROL_IN         0x041
+
+// Sub-protocols for reserved control messages, supporting test operations
+// other than control-IN.
+#define USBTEST_RESERVED_CONTROL_IN     0x01
+
+// Work around a problem with control messages that involve additional
+// data from host to target. This problem is not yet well-understood.
+// The workaround involves sending multiple control packets with
+// up to four bytes encoded in the index and value fields.
+#define USBTEST_CONTROL_DATA1           0x0F1
+#define USBTEST_CONTROL_DATA2           0x0F2
+#define USBTEST_CONTROL_DATA3           0x0F3
+#define USBTEST_CONTROL_DATA4           0x0F4
+
new file mode 100644
--- /dev/null
+++ b/packages/io/usb/slave/current/tests/usbtarget.c
@@ -0,0 +1,1848 @@
+/*{{{  Banner                                                   */
+
+/*=================================================================
+//
+//        target.c
+//
+//        USB testing - target-side
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+//
+// eCos 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 or (at your option) any later version.
+//
+// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+// at http://sources.redhat.com/ecos/ecos-license/
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// This program performs appropriate USB initialization and initializes
+// itself as a specific type of USB peripheral, Red Hat eCos testing.
+// There is no actual host-side device driver for this, instead there is
+// a test application which performs ioctl's on /proc/bus/usb/... and
+// makes appropriate functionality available to a Tcl script.
+//
+// Author(s):     bartv
+// Date:          2001-07-04
+//####DESCRIPTIONEND####
+//==========================================================================
+*/
+
+/*}}}*/
+/*{{{  #include's                                               */
+
+#include <stdio.h>
+#include <cyg/infra/cyg_ass.h>
+#include <cyg/infra/diag.h>
+#include <cyg/kernel/kapi.h>
+#include <cyg/hal/hal_arch.h>
+#include <cyg/io/io.h>
+#include <cyg/io/usb/usbs.h>
+#include <cyg/infra/testcase.h>
+#include "protocol.h"
+
+/*}}}*/
+
+/*{{{  Statics                                                  */
+
+// ----------------------------------------------------------------------------
+// Statics.
+
+// The number of endpoints supported by the device driver.
+static int number_endpoints     = 0;
+
+// The control endpoint
+static usbs_control_endpoint* control_endpoint = (usbs_control_endpoint*) 0;
+
+// Buffers for incoming and outgoing data, and a length field.
+static unsigned char class_request[USBTEST_MAX_CONTROL_DATA + 1];
+static unsigned char class_reply[USBTEST_MAX_CONTROL_DATA + 1];
+static int           class_request_size  = 0;
+
+// This semaphore is used by DSRs to wake up the main thread when work has to
+// be done at thread level.
+static cyg_sem_t    main_wakeup;
+
+// And this function pointer identifies the work that has to be done.
+static void         (*main_thread_action)(void)  = (void (*)(void)) 0;
+
+// Is the system still busy processing a previous request? This variable is
+// checked in response to the synch request. It may get updated in
+// DSRs as well as at thread level, hence volatile.
+static volatile int idle    = 1;
+
+// Are any tests currently running?
+static int          running = 0;
+
+// Has the current batch of tests been terminated by the host? This
+// flag is checked by the various test handlers at appropriate
+// intervals, and helps to handle the case where one of the side has
+// terminated early because an error has been detected.
+static int          current_tests_terminated = 0;
+
+// A counter for the number of threads involved in the current batch of tests.
+static int          thread_counter    = 0;
+
+// An extra buffer for recovery operations, for example to accept and discard
+// data which the host is still trying to send.
+static unsigned char recovery_buffer[USBTEST_MAX_BULK_DATA + USBTEST_MAX_BULK_DATA_EXTRA];
+
+/*}}}*/
+/*{{{  Logging                                                  */
+
+// ----------------------------------------------------------------------------
+// The target-side code can provide various levels of run-time logging.
+// Obviously the verbose flag cannot be controlled by a command-line
+// argument, but it can be set from inside gdb or alternatively by
+// a request from the host.
+//
+// NOTE: is printf() the best I/O routine to use here?
+
+static int verbose = 0;
+
+#define VERBOSE(_level_, _format_, _args_...)   \
+    do {                                        \
+        if (verbose >= _level_) {               \
+            diag_printf(_format_, ## _args_);        \
+        }                                       \
+    } while (0);
+
+/*}}}*/
+/*{{{  Utilities                                                */
+
+// ----------------------------------------------------------------------------
+// A reimplementation of nanosleep, to avoid having to pull in the
+// POSIX compatibility testing for USB testing.
+static void
+usbs_nanosleep(int delay)
+{
+    cyg_tick_count_t ticks;
+    cyg_resolution_t resolution = cyg_clock_get_resolution(cyg_real_time_clock());
+
+    // (resolution.dividend/resolution.divisor) == nanoseconds/tick
+    //   e.g. 1000000000/100, i.e. 10000000 ns or 10 ms per tick
+    // So ticks = (delay * divisor) / dividend
+    //   e.g. (10000000 * 100) / 1000000000
+    // with a likely value of 0 for delays of less than the clock resolution,
+    // so round those up to one tick.
+
+    cyg_uint64 tmp = (cyg_uint64) delay;
+    tmp *= (cyg_uint64) resolution.divisor;
+    tmp /= (cyg_uint64) resolution.dividend;
+
+    ticks = (int) tmp;
+    if (0 != ticks) {
+        cyg_thread_delay(ticks);
+    }
+}
+
+// ----------------------------------------------------------------------------
+// Fix any problems in the driver-supplied endpoint data
+//
+// Maximum transfer sizes are limited not just by the capabilities
+// of the driver but also by the testing code itself, since e.g.
+// buffers for transfers are statically allocated.
+static void
+fix_driver_endpoint_data(void)
+{
+    int i;
+    
+    for (i = 0; !USBS_TESTING_ENDPOINTS_IS_TERMINATOR(usbs_testing_endpoints[i]); i++) {
+        if (USB_ENDPOINT_DESCRIPTOR_ATTR_BULK == usbs_testing_endpoints[i].endpoint_type) {
+            if ((-1 == usbs_testing_endpoints[i].max_size) ||
+                (usbs_testing_endpoints[i].max_size > USBTEST_MAX_BULK_DATA)) {
+                usbs_testing_endpoints[i].max_size = USBTEST_MAX_BULK_DATA;
+            }
+        }
+    }
+}
+
+// ----------------------------------------------------------------------------
+// A heartbeat thread.
+//
+// USB tests can run for a long time with no traffic on the debug channel,
+// which can cause problems. To avoid problems it is possible to have a
+// heartbeat thread running in the background, sending output at one
+// second intervals.
+//
+// Depending on the configuration the output may still be line-buffered,
+// but that is still sufficient to keep things happy.
+
+static cyg_bool     heartbeat = false;
+static cyg_thread   heartbeat_data;
+static cyg_handle_t heartbeat_handle;
+static char         heartbeat_stack[CYGNUM_HAL_STACK_SIZE_TYPICAL];
+
+static void
+heartbeat_function(cyg_addrword_t arg __attribute((unused)))
+{
+    char* message = "alive\n";
+    int i;
+    
+    for ( i = 0; ; i = (i + 1) % 6) {
+        usbs_nanosleep(1000000000);
+        if (heartbeat) {
+            diag_write_char(message[i]);
+        }
+    }
+}
+
+static void
+start_heartbeat(void)
+{
+    cyg_thread_create( 0, &heartbeat_function, 0,
+                       "heartbeat", heartbeat_stack, CYGNUM_HAL_STACK_SIZE_TYPICAL,
+                       &heartbeat_handle, &heartbeat_data);
+    cyg_thread_resume(heartbeat_handle);
+}
+
+
+/*}}}*/
+/*{{{  Endpoint usage                                           */
+
+// ----------------------------------------------------------------------------
+// It is important to keep track of which endpoints are currently in use,
+// because the behaviour of the USB I/O routines is undefined if there are
+// concurrent attempts to communicate on the same endpoint. Normally this is
+// not a problem because the host will ensure that a given endpoint is used
+// for only one endpoint at a time, but when performing recovery action it
+// is important that the system is sure that a given endpoint can be accessed
+// safely.
+
+static cyg_bool in_endpoint_in_use[16];
+static cyg_bool out_endpoint_in_use[16];
+
+// Lock the given endpoint. In theory this is only ever accessed from a single
+// test thread at a time, but just in case...
+static void
+lock_endpoint(int endpoint, int direction)
+{
+    CYG_ASSERTC((endpoint >=0) && (endpoint < 16));
+    CYG_ASSERTC((USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN == direction) || (USB_ENDPOINT_DESCRIPTOR_ENDPOINT_OUT == direction));
+    
+    cyg_scheduler_lock();
+    if (0 == endpoint) {
+        // Comms traffic on endpoint 0 is implemented using reserved control messages.
+        // It is not really possible to have concurrent IN and OUT operations because
+        // the two would interfere with each other.
+        CYG_ASSERTC(!in_endpoint_in_use[0] && !out_endpoint_in_use[0]);
+        in_endpoint_in_use[0]  = true;
+        out_endpoint_in_use[0] = true;
+    } else if (USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN == direction) {
+        CYG_ASSERTC(!in_endpoint_in_use[endpoint]);
+        in_endpoint_in_use[endpoint] = true;
+    } else {
+        CYG_ASSERTC(!out_endpoint_in_use[endpoint]);
+        out_endpoint_in_use[endpoint] = true;
+    }
+    cyg_scheduler_unlock();
+}
+
+static void
+unlock_endpoint(int endpoint, int direction)
+{
+    CYG_ASSERTC((endpoint >= 0) && (endpoint < 16));
+    CYG_ASSERTC((USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN == direction) || (USB_ENDPOINT_DESCRIPTOR_ENDPOINT_OUT == direction));
+    
+    if (0 == endpoint) {
+        CYG_ASSERTC(in_endpoint_in_use[0] && out_endpoint_in_use[0]);
+        in_endpoint_in_use[0]   = false;
+        out_endpoint_in_use[0]  = false;
+    } else if (USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN == direction) {
+        CYG_ASSERTC(in_endpoint_in_use[endpoint]);
+        in_endpoint_in_use[endpoint] = false;
+    } else {
+        CYG_ASSERTC(out_endpoint_in_use[endpoint]);
+        out_endpoint_in_use[endpoint] = false;
+    }
+}
+
+static cyg_bool
+is_endpoint_locked(int endpoint, int direction)
+{
+    cyg_bool    result = false;
+    
+    if (0 == endpoint) {
+        result = in_endpoint_in_use[0];
+    } else if (USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN == direction) {
+        result = in_endpoint_in_use[endpoint];
+    } else {
+        result = out_endpoint_in_use[endpoint];
+    }
+    return result;
+}
+
+// For a given endpoint number, direction and protocol, search through the table 
+// supplied by the device driver of all available endpoints. This can be used
+// to e.g. get hold of the name of the devtab entry or a pointer to the endpoint
+// data structure itself.
+static int
+lookup_endpoint(int endpoint_number, int direction, int protocol)
+{
+    int result = -1;
+    int i;
+
+    for (i = 0; !USBS_TESTING_ENDPOINTS_IS_TERMINATOR(usbs_testing_endpoints[i]); i++) {
+        if ((usbs_testing_endpoints[i].endpoint_type        == protocol)        &&
+            (usbs_testing_endpoints[i].endpoint_number      == endpoint_number) &&
+            (usbs_testing_endpoints[i].endpoint_direction   == direction)) {
+            result = i;
+            break;
+        }
+    }
+    return result;
+}
+
+/*}}}*/
+/*{{{  Enumeration data                                         */
+
+// ----------------------------------------------------------------------------
+// The enumeration data.
+//
+// For simplicity this configuration involves just a single interface.
+// The target has to list all the endpoints, or the Linux kernel will
+// not allow application code to access them. Hence the information
+// provided by the device drivers has to be turned into endpoint descriptors.
+
+usb_configuration_descriptor usb_configuration = {
+    length:             USB_CONFIGURATION_DESCRIPTOR_LENGTH,
+    type:               USB_CONFIGURATION_DESCRIPTOR_TYPE,
+    total_length_lo:    USB_CONFIGURATION_DESCRIPTOR_TOTAL_LENGTH_LO(1, 0),
+    total_length_hi:    USB_CONFIGURATION_DESCRIPTOR_TOTAL_LENGTH_HI(1, 0),
+    number_interfaces:  1,
+    configuration_id:   1,      // id 0 is special according to the spec
+    configuration_str:  0,
+    attributes:         USB_CONFIGURATION_DESCRIPTOR_ATTR_REQUIRED |
+                        USB_CONFIGURATION_DESCRIPTOR_ATTR_SELF_POWERED,
+    max_power:          50
+};
+
+usb_interface_descriptor usb_interface = {
+    length:             USB_INTERFACE_DESCRIPTOR_LENGTH,
+    type:               USB_INTERFACE_DESCRIPTOR_TYPE,
+    interface_id:       0,
+    alternate_setting:  0,
+    number_endpoints:   0,
+    interface_class:    USB_INTERFACE_DESCRIPTOR_CLASS_VENDOR,
+    interface_subclass: USB_INTERFACE_DESCRIPTOR_SUBCLASS_VENDOR,
+    interface_protocol: USB_INTERFACE_DESCRIPTOR_PROTOCOL_VENDOR,
+    interface_str:      0
+};
+
+usb_endpoint_descriptor usb_endpoints[USBTEST_MAX_ENDPOINTS];
+                               
+const unsigned char* usb_strings[] = {
+    "\004\003\011\004",
+    "\020\003R\000e\000d\000 \000H\000a\000t\000",
+    "\054\003R\000e\000d\000 \000H\000a\000t\000 \000e\000C\000o\000s\000 \000"
+    "U\000S\000B\000 \000t\000e\000s\000t\000"
+};
+
+usbs_enumeration_data usb_enum_data = {
+    {
+        length:                 USB_DEVICE_DESCRIPTOR_LENGTH,
+        type:                   USB_DEVICE_DESCRIPTOR_TYPE,
+        usb_spec_lo:            USB_DEVICE_DESCRIPTOR_USB11_LO,
+        usb_spec_hi:            USB_DEVICE_DESCRIPTOR_USB11_HI,
+        device_class:           USB_DEVICE_DESCRIPTOR_CLASS_VENDOR,
+        device_subclass:        USB_DEVICE_DESCRIPTOR_SUBCLASS_VENDOR,
+        device_protocol:        USB_DEVICE_DESCRIPTOR_PROTOCOL_VENDOR,
+        max_packet_size:        8,
+        vendor_lo:              0x42,   // Note: this is not an allocated vendor id
+        vendor_hi:              0x42,
+        product_lo:             0x00,
+        product_hi:             0x01,
+        device_lo:              0x00,
+        device_hi:              0x01,
+        manufacturer_str:       1,
+        product_str:            2,
+        serial_number_str:      0,
+        number_configurations:  1
+    },
+    total_number_interfaces:    1,
+    total_number_endpoints:     0,
+    total_number_strings:       3,
+    configurations:             &usb_configuration,
+    interfaces:                 &usb_interface,
+    endpoints:                  usb_endpoints,
+    strings:                    usb_strings
+};
+
+static void
+provide_endpoint_enumeration_data(void)
+{
+    int enum_endpoint_count = 0;
+    int i;
+
+    for (i = 0; !USBS_TESTING_ENDPOINTS_IS_TERMINATOR(usbs_testing_endpoints[i]); i++) {
+
+        // The control endpoint need not appear in the enumeration data.
+        if (USB_ENDPOINT_DESCRIPTOR_ATTR_CONTROL == usbs_testing_endpoints[i].endpoint_type) {
+            continue;
+        }
+
+        usb_endpoints[enum_endpoint_count].length          = USB_ENDPOINT_DESCRIPTOR_LENGTH;
+        usb_endpoints[enum_endpoint_count].type            = USB_ENDPOINT_DESCRIPTOR_TYPE;
+        usb_endpoints[enum_endpoint_count].endpoint        = usbs_testing_endpoints[i].endpoint_number |
+                                                             usbs_testing_endpoints[i].endpoint_direction;
+
+        switch (usbs_testing_endpoints[i].endpoint_type) {
+          case USB_ENDPOINT_DESCRIPTOR_ATTR_BULK:
+            usb_endpoints[enum_endpoint_count].attributes      = USB_ENDPOINT_DESCRIPTOR_ATTR_BULK;
+            usb_endpoints[enum_endpoint_count].max_packet_lo   = 64;
+            usb_endpoints[enum_endpoint_count].max_packet_hi   = 0;
+            usb_endpoints[enum_endpoint_count].interval        = 0;
+            break;
+            
+          case USB_ENDPOINT_DESCRIPTOR_ATTR_ISOCHRONOUS:
+            usb_endpoints[enum_endpoint_count].attributes      = USB_ENDPOINT_DESCRIPTOR_ATTR_ISOCHRONOUS;
+            usb_endpoints[enum_endpoint_count].max_packet_lo   = usbs_testing_endpoints[i].max_size & 0x0FF;
+            usb_endpoints[enum_endpoint_count].max_packet_hi   = (usbs_testing_endpoints[i].max_size >> 8) & 0x0FF;
+            usb_endpoints[enum_endpoint_count].interval        = 1;
+            break;
+            
+          case USB_ENDPOINT_DESCRIPTOR_ATTR_INTERRUPT:
+            usb_endpoints[enum_endpoint_count].attributes      = USB_ENDPOINT_DESCRIPTOR_ATTR_INTERRUPT;
+            usb_endpoints[enum_endpoint_count].max_packet_lo   = (unsigned char) usbs_testing_endpoints[i].max_size;
+            usb_endpoints[enum_endpoint_count].max_packet_hi   = 0;
+            usb_endpoints[enum_endpoint_count].interval        = 1;    // NOTE: possibly incorrect
+            break;
+        }
+
+        enum_endpoint_count++;
+    }
+
+    usb_interface.number_endpoints          = enum_endpoint_count;
+    usb_enum_data.total_number_endpoints    = enum_endpoint_count;
+    usb_configuration.total_length_lo       = USB_CONFIGURATION_DESCRIPTOR_TOTAL_LENGTH_LO(1, enum_endpoint_count);
+    usb_configuration.total_length_hi       = USB_CONFIGURATION_DESCRIPTOR_TOTAL_LENGTH_HI(1, enum_endpoint_count);
+}
+
+/*}}}*/
+/*{{{  Host/target common code                                  */
+
+#define TARGET
+#include "common.c"
+
+/*}}}*/
+/*{{{  The tests                                                */
+
+/*{{{  UsbTest structure                                        */
+
+// ----------------------------------------------------------------------------
+// All the information associated with a particular testcase. Much of this
+// is identical to the equivalent host-side structure, but some additional
+// information is needed so the structure and associated routines are not
+// shared.
+typedef struct UsbTest {
+
+    // A unique identifier to make verbose output easier to understand
+    int                 id;
+    
+    // Which test should be run
+    usbtest             which_test;
+
+    // Test-specific details.
+    union {
+        UsbTest_Bulk        bulk;
+        UsbTest_ControlIn   control_in;
+    } test_params;
+
+    // How to recover from any problems. Specifically, what kind of message
+    // could the target send or receive that would unlock the thread on this
+    // side.
+    UsbTest_Recovery    recovery;
+
+    // The test result, to be collected and passed back to the host.
+    int                 result_pass;
+    char                result_message[USBTEST_MAX_MESSAGE];
+
+    // Support for synchronization. This allows the UsbTest structure to be
+    // used as the callback data for low-level USB calls.
+    cyg_sem_t           sem;
+    int                 transferred;
+
+    // Some tests may need extra cancellation support
+    void                (*cancel_fn)(struct UsbTest*);
+    unsigned char       buffer[USBTEST_MAX_BULK_DATA + USBTEST_MAX_BULK_DATA_EXTRA];
+} UsbTest;
+
+// Reset the information in a given test. This is used by the pool allocation
+// code. The data union is left alone, filling in the appropriate union
+// member is left to other code.
+static void
+reset_usbtest(UsbTest* test)
+{
+    static int next_id = 1;
+    test->id                    = next_id++;
+    test->which_test            = usbtest_invalid;
+    usbtest_recovery_reset(&(test->recovery));
+    test->result_pass           = 0;
+    test->result_message[0]     = '\0';
+    cyg_semaphore_init(&(test->sem), 0);
+    test->transferred           = 0;
+    test->cancel_fn             = (void (*)(UsbTest*)) 0;
+}
+
+// Forward declaration. The pool code depends on run_test(), setting up a test requires the pool.
+static UsbTest* pool_allocate(void);
+
+/*}}}*/
+/*{{{  Bulk transfers                                           */
+
+/*{{{  handle_test_bulk()                                       */
+
+// Prepare for a bulk transfer test. This means allocating a thread to do
+// the work, and extracting the test parameters from the current buffer.
+// The thread allocation code does not require any locking since all worker
+// threads should be idle when starting a new thread, so the work can be
+// done entirely at DSR level and no synch is required.
+static usbs_control_return
+handle_test_bulk(usb_devreq* req)
+{
+    UsbTest*    test;
+    int         index   = 0;
+
+    test = pool_allocate();
+    unpack_usbtest_bulk(&(test->test_params.bulk), class_request, &index);
+    test->which_test = (USB_DEVREQ_DIRECTION_IN == (test->test_params.bulk.endpoint & USB_DEVREQ_DIRECTION_MASK)) ?
+        usbtest_bulk_in : usbtest_bulk_out;
+
+    VERBOSE(3, "Preparing USB bulk test on endpoint %d, direction %s, for %d packets\n", \
+            test->test_params.bulk.endpoint & ~USB_DEVREQ_DIRECTION_MASK,                \
+            (usbtest_bulk_in == test->which_test) ? "IN" : "OUT",                           \
+            test->test_params.bulk.number_packets);
+    VERBOSE(3, "  I/O mechanism is %s\n", \
+            (usb_io_mechanism_usb == test->test_params.bulk.io_mechanism) ? "low-level USB" : \
+            (usb_io_mechanism_dev == test->test_params.bulk.io_mechanism) ? "devtab" : "<invalid>");
+    VERBOSE(3, "  Data format %s, data1 %d, data* %d, data+ %d, data1* %d, data1+ %d, data** %d, data*+ %d, data+* %d, data++ %d\n",\
+            (usbtestdata_none     == test->test_params.bulk.data.format) ? "none" :     \
+            (usbtestdata_bytefill == test->test_params.bulk.data.format) ? "bytefill" : \
+            (usbtestdata_wordfill == test->test_params.bulk.data.format) ? "wordfill" : \
+            (usbtestdata_byteseq  == test->test_params.bulk.data.format) ? "byteseq"  : \
+            (usbtestdata_wordseq  == test->test_params.bulk.data.format) ? "wordseq"  : "<invalid>", \
+            test->test_params.bulk.data.seed,                            \
+            test->test_params.bulk.data.multiplier,                      \
+            test->test_params.bulk.data.increment,                       \
+            test->test_params.bulk.data.transfer_seed_multiplier,        \
+            test->test_params.bulk.data.transfer_seed_increment,         \
+            test->test_params.bulk.data.transfer_multiplier_multiplier,  \
+            test->test_params.bulk.data.transfer_multiplier_increment,   \
+            test->test_params.bulk.data.transfer_increment_multiplier,   \
+            test->test_params.bulk.data.transfer_increment_increment);
+    VERBOSE(3, "  txsize1 %d, txsize>= %d, txsize<= %d, txsize* %d, txsize/ %d, txsize+ %d\n", \
+            test->test_params.bulk.tx_size,         test->test_params.bulk.tx_size_min,        \
+            test->test_params.bulk.tx_size_max,     test->test_params.bulk.tx_size_multiplier, \
+            test->test_params.bulk.tx_size_divisor, test->test_params.bulk.tx_size_increment);
+    VERBOSE(3, "  rxsize1 %d, rxsize>= %d, rxsize<= %d, rxsize* %d, rxsize/ %d, rxsize+ %d\n", \
+            test->test_params.bulk.rx_size,         test->test_params.bulk.rx_size_min,        \
+            test->test_params.bulk.rx_size_max,     test->test_params.bulk.rx_size_multiplier, \
+            test->test_params.bulk.rx_size_divisor, test->test_params.bulk.rx_size_increment);
+    VERBOSE(3, "  txdelay1 %d, txdelay>= %d, txdelay<= %d, txdelay* %d, txdelay/ %d, txdelay+ %d\n", \
+            test->test_params.bulk.tx_delay,         test->test_params.bulk.tx_delay_min,            \
+            test->test_params.bulk.tx_delay_max,     test->test_params.bulk.tx_delay_multiplier,     \
+            test->test_params.bulk.tx_delay_divisor, test->test_params.bulk.tx_delay_increment);
+    VERBOSE(3, "  rxdelay1 %d, rxdelay>= %d, rxdelay<= %d, rxdelay* %d, rxdelay/ %d, rxdelay+ %d\n", \
+            test->test_params.bulk.rx_delay,         test->test_params.bulk.rx_delay_min,            \
+            test->test_params.bulk.rx_delay_max,     test->test_params.bulk.rx_delay_multiplier,     \
+            test->test_params.bulk.rx_delay_divisor, test->test_params.bulk.rx_delay_increment);
+    
+    return USBS_CONTROL_RETURN_HANDLED;
+}
+
+/*}}}*/
+/*{{{  run_test_bulk_out()                                      */
+
+// The same callback can be used for IN and OUT transfers. Note that
+// starting the next transfer is left to the thread, it is not done
+// at DSR level.
+static void
+run_test_bulk_in_out_callback(void* callback_arg, int transferred)
+{
+    UsbTest*    test    = (UsbTest*) callback_arg;
+    test->transferred   = transferred;
+    cyg_semaphore_post(&(test->sem));
+}
+
+// OUT transfers, i.e. the host will be sending some number of
+// packets. The I/O can happen in a number of different ways, e.g. via
+// the low-level USB API or via devtab routines.
+static void
+run_test_bulk_out(UsbTest* test)
+{
+    unsigned char*      buf;
+    int                 endpoint_number = test->test_params.bulk.endpoint & ~USB_DEVREQ_DIRECTION_MASK;
+    int                 ep_index;
+    usbs_rx_endpoint*   endpoint        = 0;
+    cyg_io_handle_t     io_handle       = (cyg_io_handle_t)0;
+    int                 alignment;
+    int                 transferred;
+    int                 i;
+
+    VERBOSE(1, "Starting test %d, bulk out on endpoint %d\n", test->id, endpoint_number);
+
+    ep_index = lookup_endpoint(endpoint_number, USB_ENDPOINT_DESCRIPTOR_ENDPOINT_OUT, USB_ENDPOINT_DESCRIPTOR_ATTR_BULK);
+    if (ep_index == -1) {
+            test->result_pass   = 0;
+            snprintf(test->result_message, USBTEST_MAX_MESSAGE,
+                     "Target, bulk OUT transfer on endpoint %d: no such bulk endpoint", endpoint_number);
+            return;
+    }
+    endpoint    = (usbs_rx_endpoint*) usbs_testing_endpoints[ep_index].endpoint;
+    alignment   = usbs_testing_endpoints[ep_index].alignment;
+    if (0 != alignment) {
+        buf         = (unsigned char*) ((((cyg_uint32)test->buffer) + alignment - 1) & ~(alignment - 1));
+    } else {
+        buf = test->buffer;
+    }
+    
+    CYG_ASSERTC((usb_io_mechanism_usb == test->test_params.bulk.io_mechanism) || \
+                (usb_io_mechanism_dev == test->test_params.bulk.io_mechanism));
+    if (usb_io_mechanism_dev == test->test_params.bulk.io_mechanism) {
+        if (((const char*)0 == usbs_testing_endpoints[ep_index].devtab_entry) ||
+            (0 != cyg_io_lookup(usbs_testing_endpoints[ep_index].devtab_entry, &io_handle))) {
+            
+            test->result_pass   = 0;
+            snprintf(test->result_message, USBTEST_MAX_MESSAGE,
+                     "Target, bulk OUT transfer on endpoint %d: no devtab entry", endpoint_number);
+            return;
+        }
+    }
+
+    // Make sure nobody else is using this endpoint
+    lock_endpoint(endpoint_number, USB_ENDPOINT_DESCRIPTOR_ENDPOINT_OUT);
+
+    for (i = 0; i < test->test_params.bulk.number_packets; i++) {
+        int rx_size = test->test_params.bulk.rx_size;
+        int tx_size = test->test_params.bulk.tx_size;
+
+        VERBOSE(2, "Bulk OUT test %d: iteration %d, rx size %d, tx size %d\n", test->id, i, rx_size, tx_size);
+        
+        if (rx_size < tx_size) {
+            rx_size = tx_size;
+            VERBOSE(2, "Bulk OUT test %d: iteration %d, packet size reset to %d to match tx size\n",
+                    test->id, i, rx_size);
+        }
+                                                              
+        test->recovery.endpoint     = endpoint_number | USB_ENDPOINT_DESCRIPTOR_ENDPOINT_OUT;
+        test->recovery.protocol     = USB_ENDPOINT_DESCRIPTOR_ATTR_BULK;
+        test->recovery.size         = rx_size;
+
+        // Make sure there is no old data lying around
+        if (usbtestdata_none != test->test_params.bulk.data.format) {
+            memset(buf, 0, rx_size);
+        }
+
+        // Do the actual transfer, using the I/O mechanism specified for this test.
+        switch (test->test_params.bulk.io_mechanism)
+        {
+          case usb_io_mechanism_usb :
+          {
+              test->transferred = 0;
+              usbs_start_rx_buffer(endpoint, buf, rx_size, &run_test_bulk_in_out_callback, (void*) test);
+              cyg_semaphore_wait(&(test->sem));
+              transferred = test->transferred;
+              break;
+          }
+
+          case usb_io_mechanism_dev :
+          {
+              int result;
+              transferred   = rx_size;
+              result = cyg_io_read(io_handle, (void*) buf, &transferred);
+              if (result < 0) {
+                  transferred = result;
+              }
+              break;
+          }
+
+          default:
+            CYG_FAIL("Invalid test mechanism specified");
+            break;
+        }
+
+        // Has this test been aborted for some reason?
+        if (current_tests_terminated) {
+            VERBOSE(2, "Bulk OUT test %d: iteration %d, termination detected\n", test->id, i);
+            test->result_pass = 0;
+            snprintf(test->result_message, USBTEST_MAX_MESSAGE,
+                     "Target, bulk OUT transfer on endpoint %d: transfer aborted after iteration %d", endpoint_number, i);
+            break;
+        }
+
+        // If an error occurred, abort this run
+        if (transferred < 0) {
+            test->result_pass   = 0;
+            snprintf(test->result_message, USBTEST_MAX_MESSAGE,
+                     "Target, bulk OUT transfer on endpoint %d: transfer failed with %d", endpoint_number, transferred);
+            VERBOSE(2, "Bulk OUT test %d: iteration %d, error:\n    %s\n", test->id, i, test->result_message);
+            break;
+        }
+
+        // Did the host send the expected amount of data?
+        if (transferred < test->test_params.bulk.tx_size) {
+            test->result_pass   = 0;
+            snprintf(test->result_message, USBTEST_MAX_MESSAGE,
+                     "Target, bulk OUT transfer on endpoint %d : the host only sent %d bytes when %d were expected",
+                     endpoint_number, transferred, tx_size);
+            VERBOSE(2, "Bulk OUT test %d: iteration %d, error:\n    %s\n", test->id, i, test->result_message);
+            break;
+        }
+
+        if (verbose >= 3) {
+            // Output the first 32 bytes of data
+            char msg[256];
+            int  index;
+            int  j;
+            index = snprintf(msg, 255, "Bulk OUT test %d: iteration %d, transferred %d\n    Data %s:",
+                             test->id, i, transferred,
+                             (usbtestdata_none == test->test_params.bulk.data.format) ? "(uninitialized)" : "");
+
+            for (j = 0; ((j + 3) < transferred) && (j < 32); j+= 4) {
+                index += snprintf(msg+index, 255-index, " %02x%02x%02x%02x",
+                                  buf[j], buf[j+1], buf[j+2], buf[j+3]);
+            }
+            if (j < 32) {
+                index += snprintf(msg+index, 255-index, " ");
+                for ( ; j < transferred; j++) {
+                    index += snprintf(msg+index, 255-index, "%02x", buf[j]);
+                }
+                
+            }
+            VERBOSE(3, "%s\n", msg);
+        }
+        
+        // Is the data correct?
+        if (!usbtest_check_buffer(&(test->test_params.bulk.data), buf, transferred)) {
+            test->result_pass   = 0;
+            snprintf(test->result_message, USBTEST_MAX_MESSAGE,
+                     "Target, bulk OUT transfer on endpoint %d : mismatch between received and expected data", endpoint_number);
+            VERBOSE(2, "Bulk OUt test %d: iteration %d, error:\n    %s\n", test->id, i, test->result_message);
+            break;
+        }
+
+        if (0 != test->test_params.bulk.rx_delay) {
+            VERBOSE(2, "Bulk OUT test %d: iteration %d, sleeping for %d nanoseconds\n", test->id, \
+                    i, test->test_params.bulk.rx_delay);
+            usbs_nanosleep(test->test_params.bulk.rx_delay);
+        }
+        
+        // Move on to the next transfer
+        USBTEST_BULK_NEXT(test->test_params.bulk);
+    }
+
+    // Always unlock the endpoint on completion
+    unlock_endpoint(endpoint_number, USB_ENDPOINT_DESCRIPTOR_ENDPOINT_OUT);
+
+    // If all the packets have been transferred this test has passed.
+    if (i >= test->test_params.bulk.number_packets) {
+        test->result_pass   = 1;
+    }
+    
+    VERBOSE(1, "Test %d bulk OUT on endpoint %d, result %d\n", test->id, endpoint_number, test->result_pass);
+}
+
+/*}}}*/
+/*{{{  run_test_bulk_in()                                       */
+
+// IN transfers, i.e. the host is expected to receive some data. These are slightly
+// easier than OUT transfers because it is the host that will do the checking.
+static void
+run_test_bulk_in(UsbTest* test)
+{
+    unsigned char*      buf;
+    int                 endpoint_number = test->test_params.bulk.endpoint & ~USB_DEVREQ_DIRECTION_MASK;
+    int                 ep_index;
+    usbs_tx_endpoint*   endpoint        = 0;
+    cyg_io_handle_t     io_handle       = (cyg_io_handle_t)0;
+    int                 alignment;
+    int                 transferred;
+    int                 i;
+
+    VERBOSE(1, "Starting test %d, bulk IN on endpoint %d\n", test->id, endpoint_number);
+    
+    ep_index = lookup_endpoint(endpoint_number, USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN, USB_ENDPOINT_DESCRIPTOR_ATTR_BULK);
+    if (ep_index == -1) {
+            test->result_pass   = 0;
+            snprintf(test->result_message, USBTEST_MAX_MESSAGE,
+                     "Target, bulk IN transfer on endpoint %d: no such bulk endpoint", endpoint_number);
+            return;
+    }
+    endpoint    = (usbs_tx_endpoint*) usbs_testing_endpoints[ep_index].endpoint;
+    alignment   = usbs_testing_endpoints[ep_index].alignment;
+    if (0 != alignment) {
+        buf         = (unsigned char*) ((((cyg_uint32)test->buffer) + alignment - 1) & ~(alignment - 1));
+    } else {
+        buf = test->buffer;
+    }
+    
+    CYG_ASSERTC((usb_io_mechanism_usb == test->test_params.bulk.io_mechanism) || \
+                (usb_io_mechanism_dev == test->test_params.bulk.io_mechanism));
+    if (usb_io_mechanism_dev == test->test_params.bulk.io_mechanism) {
+        if (((const char*)0 == usbs_testing_endpoints[ep_index].devtab_entry) ||
+            (0 != cyg_io_lookup(usbs_testing_endpoints[ep_index].devtab_entry, &io_handle))) {
+            
+            test->result_pass   = 0;
+            snprintf(test->result_message, USBTEST_MAX_MESSAGE,
+                     "Target, bulk IN transfer on endpoint %d: no devtab entry", endpoint_number);
+            return;
+        }
+    }
+
+    // Make sure nobody else is using this endpoint
+    lock_endpoint(endpoint_number, USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN);
+    
+    for (i = 0; i < test->test_params.bulk.number_packets; i++) {
+        int packet_size = test->test_params.bulk.tx_size;
+        
+        test->recovery.endpoint     = endpoint_number | USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN;
+        test->recovery.protocol     = USB_ENDPOINT_DESCRIPTOR_ATTR_BULK;
+        test->recovery.size         = packet_size + usbs_testing_endpoints[ep_index].max_in_padding;
+
+        // Make sure the buffer contains the data expected by the host
+        usbtest_fill_buffer(&(test->test_params.bulk.data), buf, packet_size);
+                            
+        if (verbose < 3) {
+            VERBOSE(2, "Bulk OUT test %d: iteration %d, packet size %d\n", test->id, i, packet_size);
+        } else {
+            // Output the first 32 bytes of data as well.
+            char msg[256];
+            int  index;
+            int  j;
+            index = snprintf(msg, 255, "Bulk IN test %d: iteration %d, packet size %d\n    Data %s:",
+                             test->id, i, packet_size,
+                             (usbtestdata_none == test->test_params.bulk.data.format) ? "(uninitialized)" : "");
+
+            for (j = 0; ((j + 3) < packet_size) && (j < 32); j+= 4) {
+                index += snprintf(msg+index, 255-index, " %02x%02x%02x%02x",
+                                  buf[j], buf[j+1], buf[j+2], buf[j+3]);
+            }
+            if (j < 32) {
+                index += snprintf(msg+index, 255-index, " ");
+                for ( ; j < packet_size; j++) {
+                    index += snprintf(msg+index, 255-index, "%02x", buf[j]);
+                }
+                
+            }
+            VERBOSE(3, "%s\n", msg);
+        }
+        
+        // Do the actual transfer, using the I/O mechanism specified for this test.
+        switch (test->test_params.bulk.io_mechanism)
+        {
+          case usb_io_mechanism_usb :
+          {
+              test->transferred = 0;
+              usbs_start_tx_buffer(endpoint, buf, packet_size, &run_test_bulk_in_out_callback, (void*) test);
+              cyg_semaphore_wait(&(test->sem));
+              transferred = test->transferred;
+              break;
+          }
+
+          case usb_io_mechanism_dev :
+          {
+              int result;
+              transferred   = packet_size;
+              result = cyg_io_write(io_handle, (void*) buf, &transferred);
+              if (result < 0) {
+                  transferred = result;
+              }
+              break;
+          }
+
+          default:
+            CYG_FAIL("Invalid test mechanism specified");
+            break;
+        }
+
+        // Has this test been aborted for some reason?
+        if (current_tests_terminated) {
+            VERBOSE(2, "Bulk IN test %d: iteration %d, termination detected\n", test->id, i);
+            test->result_pass   = 0;
+            snprintf(test->result_message, USBTEST_MAX_MESSAGE,
+                     "Target, bulk IN transfer on endpoint %d : terminated on iteration %d, packet_size %d\n",
+                     endpoint_number, i, packet_size);
+            break;
+        }
+
+        // If an error occurred, abort this run
+        if (transferred < 0) {
+            test->result_pass   = 0;
+            snprintf(test->result_message, USBTEST_MAX_MESSAGE,
+                     "Target, bulk IN transfer on endpoint %d: transfer failed with %d", endpoint_number, transferred);
+            VERBOSE(2, "Bulk IN test %d: iteration %d, error:\n    %s\n", test->id, i, test->result_message);
+            break;
+        }
+
+        // No need to check the transfer size, the USB code is only
+        // allowed to send the exact amount of data requested.
+
+        if (0 != test->test_params.bulk.tx_delay) {
+            VERBOSE(2, "Bulk IN test %d: iteration %d, sleeping for %d nanoseconds\n", test->id, i, \
+                    test->test_params.bulk.tx_delay);
+            usbs_nanosleep(test->test_params.bulk.tx_delay);
+        }
+        
+        // Move on to the next transfer
+        USBTEST_BULK_NEXT(test->test_params.bulk);
+    }
+
+    // Always unlock the endpoint on completion
+    unlock_endpoint(endpoint_number, USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN);
+
+    // If all the packets have been transferred this test has passed.
+    if (i >= test->test_params.bulk.number_packets) {
+        test->result_pass   = 1;
+    }
+    
+    VERBOSE(1, "Test %d bulk IN on endpoint %d, result %d\n", test->id, endpoint_number, test->result_pass);
+}
+
+/*}}}*/
+
+/*}}}*/
+/*{{{  Control IN transfers                                     */
+
+// Control-IN transfers. These have to be handled a little bit differently
+// from bulk transfers. The target never actually initiates anything. Instead
+// the host will send reserved control messages which are handled at DSR
+// level and passed to handle_reserved_control_messages() below. Assuming
+// a control-IN test is in progress, that will take appropriate action. The
+// thread will be woken up only once all packets have been transferred, or
+// on abnormal termination.
+
+// Is a control-IN test currently in progress?
+static UsbTest* control_in_test    = 0;
+
+// What is the expected packet size?
+static int      control_in_test_packet_size = 0;
+
+// How many packets have been transferred so far?
+static int      control_in_packets_transferred  = 0;
+
+// Cancel a control-in test. handle_test_control_in() will have updated the static
+// control_in_test so that handle_reserved_control_messages() knows what to do.
+// If the test is not actually going to be run then system consistency demands
+// that this update be undone. Also, the endpoint will have been locked to
+// detect concurrent tests on the control endpoint.
+static void
+cancel_test_control_in(UsbTest* test)
+{
+    CYG_ASSERTC(test == control_in_test);
+    control_in_test = (UsbTest*) 0;
+    control_in_test_packet_size = 0;
+    control_in_packets_transferred = 0;
+    unlock_endpoint(0, USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN);
+    test->cancel_fn = (void (*)(UsbTest*)) 0;
+}
+
+// Prepare for a control-IN transfer test.
+static usbs_control_return
+handle_test_control_in(usb_devreq* req)
+{
+    UsbTest*    test;
+    int         index   = 0;
+
+    CYG_ASSERTC((UsbTest*)0 == control_in_test);
+                
+    test = pool_allocate();
+    unpack_usbtest_control_in(&(test->test_params.control_in), class_request, &index);
+
+    lock_endpoint(0, USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN);
+    test->which_test            = usbtest_control_in;
+    test->recovery.endpoint     = 0;
+    test->recovery.protocol     = USB_ENDPOINT_DESCRIPTOR_ATTR_CONTROL;
+    test->recovery.size         = 0;    // Does not actually matter
+    test->cancel_fn             = &cancel_test_control_in;
+
+    // Assume a pass. Failures are easy to detect.
+    test->result_pass   = 1;
+    
+    control_in_test = test;
+    control_in_test_packet_size = test->test_params.control_in.packet_size_initial;
+    control_in_packets_transferred  = 0;
+
+    return USBS_CONTROL_RETURN_HANDLED;
+}
+    
+// The thread for a control-in test. Actually all the hard work is done at DSR
+// level, so this thread serves simply to detect when the test has completed
+// and to perform some clean-ups.
+static void
+run_test_control_in(UsbTest* test)
+{
+    CYG_ASSERTC(test == control_in_test);
+    
+    cyg_semaphore_wait(&(test->sem));
+
+    // The DSR has detected that the test is complete.
+    control_in_test = (UsbTest*) 0;
+    control_in_test_packet_size = 0;
+    control_in_packets_transferred = 0;
+    test->cancel_fn = (void (*)(UsbTest*)) 0;
+    unlock_endpoint(0, USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN);
+}
+
+// ----------------------------------------------------------------------------
+// This is installed from inside main() as the handler for reserved
+// control messages.
+static usbs_control_return
+handle_reserved_control_messages(usbs_control_endpoint* endpoint, void* data)
+{
+    usb_devreq*         req = (usb_devreq*) endpoint->control_buffer;
+    usbs_control_return result;
+
+    CYG_ASSERT(endpoint == control_endpoint, "control endpoint mismatch");
+    switch(req->request) {
+      case USBTEST_RESERVED_CONTROL_IN:
+        {
+            unsigned char*  buf;
+            int             len;
+            
+            if ((UsbTest*)0 == control_in_test) {
+                result = USBS_CONTROL_RETURN_STALL;
+                break;
+            }
+
+            // Is this test over? If so indicate a failure because we
+            // cannot have received all the control packets.
+            if (current_tests_terminated) {
+                control_in_test->result_pass   = 0;
+                snprintf(control_in_test->result_message, USBTEST_MAX_MESSAGE,
+                         "Target, control IN transfer: not all packets received.");
+                cyg_semaphore_post(&(control_in_test->sem));
+                control_in_test = (UsbTest*) 0;
+                result = USBS_CONTROL_RETURN_STALL;
+                break;
+            }
+            
+            // A control-IN test is indeed in progress, and the current state is
+            // held in control_in_test and control_in_test_packet_size. Check that
+            // the packet size matches up, i.e. that host and target are in sync.
+            len = (req->length_hi << 8) || req->length_lo;
+            if (control_in_test_packet_size != len) {
+                control_in_test->result_pass   = 0;
+                snprintf(control_in_test->result_message, USBTEST_MAX_MESSAGE,
+                         "Target, control IN transfer on endpoint %d : the host only requested %d bytes instead of %d",
+                         len, control_in_test_packet_size);
+                cyg_semaphore_post(&(control_in_test->sem));
+                control_in_test = (UsbTest*) 0;
+                result = USBS_CONTROL_RETURN_STALL;
+                break;
+            }
+
+            // Prepare a suitable reply buffer. This is happening at
+            // DSR level so runtime is important, but with an upper
+            // bound of 255 bytes the buffer should be small enough.
+            buf = control_in_test->buffer;
+            usbtest_fill_buffer(&(control_in_test->test_params.control_in.data), buf, control_in_test_packet_size);
+            control_endpoint->buffer_size   = control_in_test_packet_size;
+            control_endpoint->buffer        = buf;
+            USBTEST_CONTROL_NEXT_PACKET_SIZE(control_in_test_packet_size, control_in_test->test_params.control_in);
+
+            // Have all the packets been transferred?
+            control_in_packets_transferred++;
+            if (control_in_packets_transferred == control_in_test->test_params.control_in.number_packets) {
+                cyg_semaphore_post(&(control_in_test->sem));
+                control_in_test = (UsbTest*) 0;
+            }
+            result = USBS_CONTROL_RETURN_HANDLED;
+            break;
+      }
+      default:
+        CYG_FAIL("Unexpected reserved control message");
+        break;
+    }
+    
+    return result;
+}
+
+/*}}}*/
+
+// FIXME: add more tests.
+
+// This utility is invoked from a thread in the thread pool whenever there is
+// work to be done. It simply dispatches to the appropriate handler.
+static void
+run_test(UsbTest* test)
+{
+    switch(test->which_test)
+    {
+      case usbtest_bulk_out :       run_test_bulk_out(test); break;
+      case usbtest_bulk_in :        run_test_bulk_in(test); break;
+      case usbtest_control_in:      run_test_control_in(test); break;
+      default:
+        CYG_TEST_FAIL_EXIT("Internal error, attempt to run unknown test.\n");
+        break;
+    }
+}
+
+/*}}}*/
+/*{{{  The thread pool                                          */
+
+// ----------------------------------------------------------------------------
+// Just like on the host side, it is desirable to have a pool of
+// threads available to perform test operations. Strictly speaking
+// some tests will run without needing a separate thread, since many
+// operations can be performed at DSR level. However typical
+// application code will involve threads and it is desirable for test
+// code to behave the same way. Also, some operations like validating
+// the transferred data are expensive, and best done in thread context.
+
+typedef struct PoolEntry {
+    cyg_sem_t           wakeup;
+    cyg_thread          thread_data;
+    cyg_handle_t        thread_handle;
+    char                thread_name[16];
+    char                thread_stack[2 * CYGNUM_HAL_STACK_SIZE_TYPICAL];
+    cyg_bool            in_use;
+    cyg_bool            running;
+    UsbTest             test;
+} PoolEntry;
+
+// This array must be uninitialized, or the executable size would
+// be ludicrous.
+PoolEntry  pool[USBTEST_MAX_CONCURRENT_TESTS];
+
+// The entry point for every thread in the pool. It just loops forever,
+// waiting until it is supposed to run a test.
+static void
+pool_thread_function(cyg_addrword_t arg)
+{
+    PoolEntry*  pool_entry  = (PoolEntry*) arg;
+
+    for ( ; ; ) {
+        cyg_semaphore_wait(&(pool_entry->wakeup));
+        run_test(&(pool_entry->test));
+        pool_entry->running = 0;
+    }
+}
+
+// Initialize all threads in the pool.
+static void
+pool_initialize(void)
+{
+    int i;
+    for (i = 0; i < USBTEST_MAX_CONCURRENT_TESTS; i++) {
+        cyg_semaphore_init(&(pool[i].wakeup), 0);
+        pool[i].in_use  = 0;
+        pool[i].running = 0;
+        sprintf(pool[i].thread_name, "worker%d", i);
+        cyg_thread_create( 0, &pool_thread_function, (cyg_addrword_t) &(pool[i]),
+                           pool[i].thread_name, pool[i].thread_stack, 2 * CYGNUM_HAL_STACK_SIZE_TYPICAL,
+                           &(pool[i].thread_handle), &(pool[i].thread_data));
+        cyg_thread_resume(pool[i].thread_handle);
+    }
+}
+
+// Allocate a single entry in the thread pool
+static UsbTest*
+pool_allocate(void)
+{
+    UsbTest*    result  = (UsbTest*) 0;
+
+    if (thread_counter == USBTEST_MAX_CONCURRENT_TESTS) {
+        CYG_TEST_FAIL_EXIT("Internal error, thread resources exhaused.\n");
+    }
+    
+    result = &(pool[thread_counter].test);
+    thread_counter++;
+    reset_usbtest(result);
+    return result;
+}
+
+// Start all the threads that are supposed to be running tests.
+static void
+pool_start(void)
+{
+    int i;
+    for (i = 0; i < thread_counter; i++) {
+        pool[i].running = 1;
+        cyg_semaphore_post(&(pool[i].wakeup));
+    }
+}
+
+/*}}}*/
+/*{{{  Class control messages                                   */
+
+// ----------------------------------------------------------------------------
+// Handle class control messages. These provide the primary form of
+// communication between host and target. There are requests to find out
+// the number of endpoints, details of each endpoint, prepare a test run,
+// abort a test run, get status, terminate the target-side, and so on.
+// The handlers for starting specific test cases are kept alongside
+// the test cases themselves.
+//
+// Note that these handlers will typically be invoked from DSR context
+// and hence they are subject to the usual DSR restrictions.
+//
+// Problems have been experienced in some hosts sending control messages
+// that involve additional host->target data. An ugly workaround is
+// in place whereby any such data is sent in advance using separate
+// control messages.
+
+/*{{{  endpoint count                                           */
+
+// How many endpoints are supported by this device? That information is
+// determined during initialization.
+static usbs_control_return
+handle_endpoint_count(usb_devreq* req)
+{
+    CYG_ASSERTC((1 == req->length_lo) && (0 == req->length_hi) && \
+                ((req->type & USB_DEVREQ_DIRECTION_MASK) == USB_DEVREQ_DIRECTION_IN));
+    CYG_ASSERTC((0 == req->index_lo) && (0 == req->index_hi) && (0 == req->value_lo) && (0 == req->value_hi));
+    
+    class_reply[0]                  = (unsigned char) number_endpoints;
+    control_endpoint->buffer        = class_reply;
+    control_endpoint->buffer_size   = 1;
+    return USBS_CONTROL_RETURN_HANDLED;
+}
+
+/*}}}*/
+/*{{{  endpoint details                                         */
+
+// The host wants to know the details of a specific USB endpoint.
+// The format is specified in protocol.h
+static usbs_control_return
+handle_endpoint_details(usb_devreq* req)
+{
+    int buf_index;
+
+    CYG_ASSERTC((req->type & USB_DEVREQ_DIRECTION_MASK) == USB_DEVREQ_DIRECTION_IN);
+    CYG_ASSERTC((USBTEST_MAX_CONTROL_DATA == req->length_lo) && (0 == req->length_hi));
+    CYG_ASSERTC(req->index_lo < number_endpoints);
+    CYG_ASSERTC((0 == req->index_hi) && (0 == req->value_lo) && (0 == req->value_hi));
+    
+    class_reply[0]  = (unsigned char) usbs_testing_endpoints[req->index_lo].endpoint_type;
+    class_reply[1]  = (unsigned char) usbs_testing_endpoints[req->index_lo].endpoint_number;
+    class_reply[2]  = (unsigned char) usbs_testing_endpoints[req->index_lo].endpoint_direction;
+    class_reply[3]  = (unsigned char) usbs_testing_endpoints[req->index_lo].max_in_padding;
+    buf_index = 4;
+    pack_int(usbs_testing_endpoints[req->index_lo].min_size, class_reply, &buf_index);
+    pack_int(usbs_testing_endpoints[req->index_lo].max_size, class_reply, &buf_index);
+    if (NULL == usbs_testing_endpoints[req->index_lo].devtab_entry) {
+        class_reply[buf_index]    = '\0';
+        control_endpoint->buffer_size   = buf_index + 1;
+    } else {
+        int len = strlen(usbs_testing_endpoints[req->index_lo].devtab_entry) + buf_index + 1;
+        if (len > USBTEST_MAX_CONTROL_DATA) {
+            return USBS_CONTROL_RETURN_STALL;
+        } else {
+            strcpy(&(class_reply[buf_index]), usbs_testing_endpoints[req->index_lo].devtab_entry);
+            control_endpoint->buffer_size   = len;
+        }
+    }
+    control_endpoint->buffer        = class_reply;
+    return USBS_CONTROL_RETURN_HANDLED;
+}
+
+/*}}}*/
+/*{{{  sync                                                     */
+
+// The host wants to know whether or not the target is currently busy doing
+// stuff. This information is held in a static.
+static usbs_control_return
+handle_sync(usb_devreq* req)
+{
+    CYG_ASSERTC((1 == req->length_lo) && (0 == req->length_hi) && \
+                ((req->type & USB_DEVREQ_DIRECTION_MASK) == USB_DEVREQ_DIRECTION_IN));
+    CYG_ASSERTC((0 == req->index_lo) && (0 == req->index_hi) && (0 == req->value_lo) && (0 == req->value_hi));
+    CYG_ASSERT(0 == class_request_size, "A sync operation should not involve any data");
+    
+    class_reply[0]                  = (unsigned char) idle;
+    control_endpoint->buffer        = class_reply;
+    control_endpoint->buffer_size   = 1;
+    return USBS_CONTROL_RETURN_HANDLED;
+}
+
+/*}}}*/
+/*{{{  pass/fail                                                */
+
+// Allow the host to generate some pass or fail messages, and
+// optionally terminate the test. These are synchronous requests
+// so the data can be left in class_request.
+
+static int passfail_request   = 0;
+
+// Invoked from thread context
+static void
+handle_passfail_action(void)
+{
+    switch (passfail_request) {
+      case USBTEST_PASS:
+        CYG_TEST_PASS(class_request);
+        break;
+      case USBTEST_PASS_EXIT:
+        CYG_TEST_PASS(class_request);
+        CYG_TEST_EXIT("Exiting normally as requested by the host");
+        break;
+      case USBTEST_FAIL:
+        CYG_TEST_FAIL(class_request);
+        break;
+      case USBTEST_FAIL_EXIT:
+        CYG_TEST_FAIL(class_request);
+        CYG_TEST_EXIT("Exiting normally as requested by the host");
+        break;
+      default:
+        CYG_FAIL("Bogus invocation of usbtest_main_passfail");
+        break;
+    }
+}
+
+// Invoked from DSR context
+static usbs_control_return
+handle_passfail(usb_devreq* req)
+{
+    CYG_ASSERTC((0 == req->length_lo) && (0 == req->length_hi));
+    CYG_ASSERTC((0 == req->index_lo) && (0 == req->index_hi) && (0 == req->value_lo) && (0 == req->value_hi));
+    CYG_ASSERT(class_request_size > 0, "A pass/fail message should be supplied");
+    CYG_ASSERT(idle, "Pass/fail messages are only allowed when idle");
+    CYG_ASSERT((void (*)(void))0 == main_thread_action, "No thread operation should be pending.");
+    
+    passfail_request    = req->request;
+    idle                = false;
+    main_thread_action  = &handle_passfail_action;
+    cyg_semaphore_post(&main_wakeup);
+
+    return USBS_CONTROL_RETURN_HANDLED;
+}
+
+/*}}}*/
+/*{{{  abort                                                    */
+
+// The host has concluded that there is no easy way to get both target and
+// host back to a sensible state. For example there may be a thread that
+// is blocked waiting for some I/O that is not going to complete. The abort
+// should be handled at thread level, not DSR level, so that the host
+// still sees the low-level USB handshake.
+
+static void
+handle_abort_action(void)
+{
+    CYG_TEST_FAIL_EXIT("Test abort requested by host application");
+}
+
+static usbs_control_return
+handle_abort(usb_devreq* req)
+{
+    CYG_ASSERTC((0 == req->length_lo) && (0 == req->length_hi));
+    CYG_ASSERTC((0 == req->index_lo) && (0 == req->index_hi) && (0 == req->value_lo) && (0 == req->value_hi));
+    CYG_ASSERT(idle, "Abort messages are only allowed when idle");
+    CYG_ASSERT((void (*)(void))0 == main_thread_action, "No thread operation should be pending.");
+    
+    idle                = false;
+    main_thread_action  = &handle_abort_action;
+    cyg_semaphore_post(&main_wakeup);
+    
+    return USBS_CONTROL_RETURN_HANDLED;
+}
+
+/*}}}*/
+/*{{{  cancel                                                   */
+
+// Invoked from thread context
+// Cancelling pending test cases simply involves iterating over the allocated
+// entries in the pool, invoking any cancellation functions that have been
+// defined, and then resetting the tread count. The actual tests have not
+// yet started so none of the threads will be active.
+static void
+handle_cancel_action(void)
+{
+    int i;
+    for (i = 0; i < thread_counter; i++) {
+        if ((void (*)(UsbTest*))0 != pool[i].test.cancel_fn) {
+            (*(pool[i].test.cancel_fn))(&(pool[i].test));
+            pool[i].test.cancel_fn  = (void (*)(UsbTest*)) 0;
+        }
+    }
+    thread_counter    = 0;
+}
+
+// Invoked from DSR context
+static usbs_control_return
+handle_cancel(usb_devreq* req)
+{
+    CYG_ASSERTC((0 == req->length_lo) && (0 == req->length_hi));
+    CYG_ASSERTC((0 == req->index_lo) && (0 == req->index_hi) && (0 == req->value_lo) && (0 == req->value_hi));
+    CYG_ASSERT(0 == class_request_size, "A cancel operation should not involve any data");
+    CYG_ASSERT(idle, "Cancel requests are only allowed when idle");
+    CYG_ASSERT(!running, "Cancel requests cannot be sent once the system is running");
+    CYG_ASSERT((void (*)(void))0 == main_thread_action, "No thread operation should be pending.");
+    
+    idle                = false;
+    main_thread_action = &handle_cancel_action;
+    cyg_semaphore_post(&main_wakeup);
+
+    return USBS_CONTROL_RETURN_HANDLED;
+}
+
+/*}}}*/
+/*{{{  start                                                    */
+
+// Start the tests running. This just involves waking up the pool threads
+// and setting the running flag, with the latter serving primarily for
+// assertions. 
+
+static usbs_control_return
+handle_start(usb_devreq* req)
+{
+    CYG_ASSERTC((0 == req->length_lo) && (0 == req->length_hi));
+    CYG_ASSERTC((0 == req->index_lo) && (0 == req->index_hi) && (0 == req->value_lo) && (0 == req->value_hi));
+    CYG_ASSERT(0 == class_request_size, "A start operation should not involve any data");
+    CYG_ASSERT(!running, "Start requests cannot be sent if the system is already running");
+
+    current_tests_terminated = false;
+    running = true;
+    pool_start();
+    
+    return USBS_CONTROL_RETURN_HANDLED;
+}
+
+/*}}}*/
+/*{{{  finished                                                 */
+
+// Have all the tests finished? This involves checking all the threads
+// involved in the current batch of tests and seeing whether or not
+// their running flag is still set.
+
+static usbs_control_return
+handle_finished(usb_devreq* req)
+{
+    int i;
+    int result = 1;
+    
+    CYG_ASSERTC((1 == req->length_lo) && (0 == req->length_hi) && \
+                ((req->type & USB_DEVREQ_DIRECTION_MASK) == USB_DEVREQ_DIRECTION_IN));
+    CYG_ASSERTC((0 == req->index_lo) && (0 == req->index_hi) && (0 == req->value_lo) && (0 == req->value_hi));
+    CYG_ASSERT(0 == class_request_size, "A finished operation should not involve any data");
+    CYG_ASSERT(running, "Finished requests can only be sent if the system is already running");
+    
+    for (i = 0; i < thread_counter; i++) {
+        if (pool[i].running) {
+            result = 0;
+            break;
+        }
+    }
+    class_reply[0]                  = (unsigned char) result;
+    control_endpoint->buffer        = class_reply;
+    control_endpoint->buffer_size   = 1;
+    return USBS_CONTROL_RETURN_HANDLED;
+}
+
+/*}}}*/
+/*{{{  set terminated                                           */
+
+// A timeout has occurred, or there is some other failure. The first step
+// in recovery is to set the terminated flag so that as recovery action
+// takes place and the threads wake up they make no attempt to continue
+// doing more transfers.
+
+static usbs_control_return
+handle_set_terminated(usb_devreq* req)
+{
+    CYG_ASSERTC((0 == req->length_lo) && (0 == req->length_hi));
+    CYG_ASSERTC((0 == req->index_lo) && (0 == req->index_hi) && (0 == req->value_lo) && (0 == req->value_hi));
+    CYG_ASSERT(0 == class_request_size, "A set-terminated operation should not involve any data");
+    CYG_ASSERT(running, "The terminated flag can only be set when there are running tests");
+
+    current_tests_terminated = 1;
+    
+    return USBS_CONTROL_RETURN_HANDLED;
+}
+
+/*}}}*/
+/*{{{  get recovery                                             */
+
+// Return the recovery information for one of the threads involved in the
+// current batch of tests, so that the host can perform a USB operation
+// that will sort out that thread.
+static usbs_control_return
+handle_get_recovery(usb_devreq* req)
+{
+    int buffer_index;
+    
+    CYG_ASSERT(current_tests_terminated, "Recovery should only be attempted when the terminated flag is set");
+    CYG_ASSERT(running, "If there are no tests running then recovery is impossible");
+    CYG_ASSERTC((12 == req->length_lo) && (0 == req->length_hi) && \
+                ((req->type & USB_DEVREQ_DIRECTION_MASK) == USB_DEVREQ_DIRECTION_IN));
+    CYG_ASSERTC(req->index_lo <= thread_counter);
+    CYG_ASSERTC((0 == req->index_hi) && (0 == req->value_lo) && (0 == req->value_hi));
+    CYG_ASSERT(0 == class_request_size, "A get-recovery operation should not involve any data");
+
+    control_endpoint->buffer        = class_reply;
+    if (!pool[req->index_lo].running) {
+        // Actually, this particular thread has terminated so no recovery is needed.
+        control_endpoint->buffer_size   = 0;
+    } else {
+        buffer_index    = 0;
+        pack_usbtest_recovery(&(pool[req->index_lo].test.recovery), class_reply, &buffer_index);
+        control_endpoint->buffer_size   = buffer_index;
+    }
+    
+    return USBS_CONTROL_RETURN_HANDLED;
+}
+
+/*}}}*/
+/*{{{  perform recovery                                         */
+
+// The host has identified a course of action that could unlock a thread
+// on the host-side that is currently blocked performing a USB operation.
+// Typically this involves either sending or accepting some data. If the
+// endpoint is still locked, in other words if there is a still a local
+// thread attempting to communicate on the specified endpoint, then
+// things are messed up: both sides are trying to communicate, but nothing
+// is happening. The eCos USB API is such that attempting multiple
+// concurrent operations on a single endpoint is disallowed, so
+// the recovery request has to be ignored. If things do not sort themselves
+// out then the whole test run will have to be aborted.
+
+// A dummy completion function for when a recovery operation has completed.
+static void
+recovery_callback(void* callback_arg, int transferred)
+{
+    CYG_UNUSED_PARAM(void*, callback_arg);
+    CYG_UNUSED_PARAM(int, transferred);
+}
+    
+static usbs_control_return
+handle_perform_recovery(usb_devreq* req)
+{
+    int                 buffer_index;
+    int                 endpoint_number;
+    int                 endpoint_direction;
+    UsbTest_Recovery    recovery;
+    
+    CYG_ASSERT(current_tests_terminated, "Recovery should only be attempted when the terminated flag is set");
+    CYG_ASSERT(running, "If there are no tests running then recovery is impossible");
+    CYG_ASSERTC((0 == req->length_lo) && (0 == req->length_hi));
+    CYG_ASSERTC((0 == req->index_lo) && (0 == req->index_hi) && (0 == req->value_lo) && (0 == req->value_hi));
+    CYG_ASSERT(12 == class_request_size, "A perform-recovery operation requires recovery data");
+
+    buffer_index = 0;
+    unpack_usbtest_recovery(&recovery, class_request, &buffer_index);
+    endpoint_number     = recovery.endpoint & ~USB_DEVREQ_DIRECTION_MASK;
+    endpoint_direction  = recovery.endpoint & USB_DEVREQ_DIRECTION_MASK;
+
+    if (!is_endpoint_locked(endpoint_number, endpoint_direction)) {
+        // Locking the endpoint here would be good, but the endpoint would then
+        // have to be unlocked again - probably in the recovery callback.
+        // This complication is ignored for now.
+
+        if (USB_ENDPOINT_DESCRIPTOR_ATTR_BULK == recovery.protocol) {
+            int ep_index = lookup_endpoint(endpoint_number, endpoint_direction, USB_ENDPOINT_DESCRIPTOR_ATTR_BULK);
+            CYG_ASSERTC(-1 != ep_index);
+
+            if (USB_DEVREQ_DIRECTION_IN == endpoint_direction) {
+                // The host wants some data. Supply it. A single byte will do fine to
+                // complete the transfer.
+                usbs_start_tx_buffer((usbs_tx_endpoint*) usbs_testing_endpoints[ep_index].endpoint,
+                                     recovery_buffer, 1, &recovery_callback, (void*) 0);
+            } else {
+                // The host is trying to send some data. Accept all of it.
+                usbs_start_rx_buffer((usbs_rx_endpoint*) usbs_testing_endpoints[ep_index].endpoint,
+                                     recovery_buffer, recovery.size, &recovery_callback, (void*) 0);
+            }
+        }
+
+        // No support for isochronous or interrupt transfers yet.
+        // handle_reserved_control_messages() should generate stalls which
+        // have the desired effect.
+    }
+    
+    return USBS_CONTROL_RETURN_HANDLED;
+}
+
+/*}}}*/
+/*{{{  get result                                               */
+
+// Return the result of one the tests. This can be a single byte for
+// a pass, or a single byte plus a message for a failure.
+
+static usbs_control_return
+handle_get_result(usb_devreq* req)
+{
+    CYG_ASSERTC((USBTEST_MAX_CONTROL_DATA == req->length_lo) && (0 == req->length_hi) && \
+                ((req->type & USB_DEVREQ_DIRECTION_MASK) == USB_DEVREQ_DIRECTION_IN));
+    CYG_ASSERTC(req->index_lo <= thread_counter);
+    CYG_ASSERTC((0 == req->index_hi) && (0 == req->value_lo) && (0 == req->value_hi));
+    CYG_ASSERT(0 == class_request_size, "A get-result operation should not involve any data");
+    CYG_ASSERT(running, "Results can only be sent if a run is in progress");
+    CYG_ASSERT(!pool[req->index_lo].running, "Cannot request results for a test that has not completed");
+
+    class_reply[0]  = pool[req->index_lo].test.result_pass;
+    if (class_reply[0]) {
+        control_endpoint->buffer_size = 1;
+    } else {
+        strncpy(&(class_reply[1]), pool[req->index_lo].test.result_message, USBTEST_MAX_CONTROL_DATA - 2);
+        class_reply[USBTEST_MAX_CONTROL_DATA - 1] = '\0';
+        control_endpoint->buffer_size = 1 + strlen(&(class_reply[1])) + 1;
+    }
+    control_endpoint->buffer = class_reply;
+    return USBS_CONTROL_RETURN_HANDLED;
+}
+
+/*}}}*/
+/*{{{  batch done                                               */
+
+// A batch of test has been completed - at least, the host thinks so.
+// If the host is correct then all that is required here is to reset
+// the thread pool and clear the global running flag - that is sufficient
+// to allow a new batch of tests to be started.
+
+static usbs_control_return
+handle_batch_done(usb_devreq* req)
+{
+    int i;
+    
+    CYG_ASSERTC((0 == req->length_lo) && (0 == req->length_hi));
+    CYG_ASSERTC((0 == req->index_lo) && (0 == req->index_hi) && (0 == req->value_lo) && (0 == req->value_hi));
+    CYG_ASSERT(0 == class_request_size, "A batch-done operation should not involve any data");
+    CYG_ASSERT(running, "There must be a current batch of tests");
+
+    for (i = 0; i < thread_counter; i++) {
+        CYG_ASSERTC(!pool[i].running);
+    }
+    thread_counter  = 0;
+    running         = false;
+    
+    return USBS_CONTROL_RETURN_HANDLED;
+
+}
+
+/*}}}*/
+/*{{{  verbosity                                                */
+
+static usbs_control_return
+handle_verbose(usb_devreq* req)
+{
+    CYG_ASSERTC((0 == req->length_lo) && (0 == req->length_hi));
+    CYG_ASSERTC((0 == req->index_lo) && (0 == req->index_hi));
+    CYG_ASSERT(0 == class_request_size, "A set-verbosity operation should not involve any data");
+
+    verbose = (req->value_hi << 8) + req->value_lo;
+    
+    return USBS_CONTROL_RETURN_HANDLED;
+}
+
+/*}}}*/
+/*{{{  initialise bulk out endpoint                             */
+
+// ----------------------------------------------------------------------------
+// Accept an initial endpoint on a bulk endpoint. This avoids problems
+// on some hardware such as the SA11x0 which can start to accept data
+// before the software is ready for it.
+
+static void handle_init_callback(void* arg, int result)
+{
+    idle = true;
+}
+
+static usbs_control_return
+handle_init_bulk_out(usb_devreq* req)
+{
+    static char         buf[64];
+    int                 ep_index;
+    usbs_rx_endpoint*   endpoint;
+    
+    CYG_ASSERTC((0 == req->length_lo) && (0 == req->length_hi));
+    CYG_ASSERTC((0 == req->index_lo) && (0 == req->index_hi));
+    CYG_ASSERTC((0 == req->value_hi) && (0 < req->value_lo) && (req->value_lo < 16));
+    CYG_ASSERT(0 == class_request_size, "An init_bulk_out operation should not involve any data");
+
+    ep_index = lookup_endpoint(req->value_lo, USB_ENDPOINT_DESCRIPTOR_ENDPOINT_OUT, USB_ENDPOINT_DESCRIPTOR_ATTR_BULK);
+    CYG_ASSERTC(-1 != ep_index);
+    endpoint = (usbs_rx_endpoint*) usbs_testing_endpoints[ep_index].endpoint;
+    
+    idle = false;
+    usbs_start_rx_buffer(endpoint, buf, 64, &handle_init_callback, (void*) 0);
+    
+    return USBS_CONTROL_RETURN_HANDLED;
+}
+
+/*}}}*/
+/*{{{  additional control data                                  */
+
+// Accumulate some more data in the control buffer, ahead of an upcoming
+// request.
+static usbs_control_return
+handle_control_data(usb_devreq* req)
+{
+    class_request[class_request_size + 0] = req->value_hi;
+    class_request[class_request_size + 1] = req->value_lo;
+    class_request[class_request_size + 2] = req->index_hi;
+    class_request[class_request_size + 3] = req->index_lo;
+
+    switch(req->request) {
+      case USBTEST_CONTROL_DATA1 : class_request_size += 1; break;
+      case USBTEST_CONTROL_DATA2 : class_request_size += 2; break;
+      case USBTEST_CONTROL_DATA3 : class_request_size += 3; break;
+      case USBTEST_CONTROL_DATA4 : class_request_size += 4; break;
+    }
+
+    return USBS_CONTROL_RETURN_HANDLED;
+}
+
+/*}}}*/
+
+typedef struct class_handler {
+    int     request;
+    usbs_control_return (*handler)(usb_devreq*);
+} class_handler;
+static class_handler class_handlers[] = {
+    { USBTEST_ENDPOINT_COUNT,   &handle_endpoint_count },
+    { USBTEST_ENDPOINT_DETAILS, &handle_endpoint_details },
+    { USBTEST_PASS,             &handle_passfail },
+    { USBTEST_PASS_EXIT,        &handle_passfail },
+    { USBTEST_FAIL,             &handle_passfail },
+    { USBTEST_FAIL_EXIT,        &handle_passfail },
+    { USBTEST_SYNCH,            &handle_sync },
+    { USBTEST_ABORT,            &handle_abort },
+    { USBTEST_CANCEL,           &handle_cancel },
+    { USBTEST_START,            &handle_start },
+    { USBTEST_FINISHED,         &handle_finished },
+    { USBTEST_SET_TERMINATED,   &handle_set_terminated },
+    { USBTEST_GET_RECOVERY,     &handle_get_recovery },
+    { USBTEST_PERFORM_RECOVERY, &handle_perform_recovery },
+    { USBTEST_GET_RESULT,       &handle_get_result },
+    { USBTEST_BATCH_DONE,       &handle_batch_done },
+    { USBTEST_VERBOSE,          &handle_verbose },
+    { USBTEST_INIT_BULK_OUT,    &handle_init_bulk_out },
+    { USBTEST_TEST_BULK,        &handle_test_bulk },
+    { USBTEST_TEST_CONTROL_IN,  &handle_test_control_in },
+    { USBTEST_CONTROL_DATA1,    &handle_control_data },
+    { USBTEST_CONTROL_DATA2,    &handle_control_data },
+    { USBTEST_CONTROL_DATA3,    &handle_control_data },
+    { USBTEST_CONTROL_DATA4,    &handle_control_data },
+    { -1,                       (usbs_control_return (*)(usb_devreq*)) 0 }
+};
+
+static usbs_control_return
+handle_class_control_messages(usbs_control_endpoint* endpoint, void* data)
+{
+    usb_devreq*         req = (usb_devreq*) endpoint->control_buffer;
+    int                 request = req->request;
+    usbs_control_return result;
+    int                 i;
+
+    VERBOSE(3, "Received control message %02x\n", request);
+    
+    CYG_ASSERT(endpoint == control_endpoint, "control endpoint mismatch");
+    result  = USBS_CONTROL_RETURN_UNKNOWN;
+    for (i = 0; (usbs_control_return (*)(usb_devreq*))0 != class_handlers[i].handler; i++) {
+        if (request == class_handlers[i].request) {
+            result = (*(class_handlers[i].handler))(req);
+            if ((USBTEST_CONTROL_DATA1 != request) &&
+                (USBTEST_CONTROL_DATA2 != request) &&
+                (USBTEST_CONTROL_DATA3 != request) &&
+                (USBTEST_CONTROL_DATA4 != request)) {
+                // Reset the request data buffer after all normal requests.
+                class_request_size = 0;
+            }
+            break;
+        }
+    }
+    CYG_UNUSED_PARAM(void*, data);
+    if (USBS_CONTROL_RETURN_HANDLED != result) {
+        VERBOSE(1, "Control message %02x not handled\n", request);
+    }
+    
+    return result;
+}
+
+/*}}}*/
+/*{{{  main()                                                   */
+
+// ----------------------------------------------------------------------------
+// Initialization.
+int
+main(int argc, char** argv)
+{
+    int i;
+
+    CYG_TEST_INIT();
+
+    // The USB device driver should have provided an array of endpoint
+    // descriptors, usbs_testing_endpoints(). One entry in this array
+    // should be a control endpoint, which is needed for initialization.
+    // It is also useful to know how many endpoints there are.
+    for (i = 0; !USBS_TESTING_ENDPOINTS_IS_TERMINATOR(usbs_testing_endpoints[i]); i++) {
+        if ((0 == usbs_testing_endpoints[i].endpoint_number) &&
+            (USB_ENDPOINT_DESCRIPTOR_ATTR_CONTROL== usbs_testing_endpoints[i].endpoint_type)) {
+            CYG_ASSERT((usbs_control_endpoint*)0 == control_endpoint, "There should be only one control endpoint");
+            control_endpoint = (usbs_control_endpoint*) usbs_testing_endpoints[i].endpoint;
+        }
+    }
+    if ((usbs_control_endpoint*)0 == control_endpoint) {
+        CYG_TEST_FAIL_EXIT("Unable to find a USB control endpoint");
+    }
+    number_endpoints = i;
+    CYG_ASSERT(number_endpoints <= USBTEST_MAX_ENDPOINTS, "impossible number of endpoints");
+
+    // Some of the information provided may not match the actual capabilities
+    // of the testing code, e.g. max_size limits.
+    fix_driver_endpoint_data();
+    
+    // This semaphore is used for communication between the DSRs that process control
+    // messages and the main thread
+    cyg_semaphore_init(&main_wakeup, 0);
+
+    // Take care of the pool of threads and related data.
+    pool_initialize();
+
+    // Start the heartbeat thread, to make sure that the gdb session stays
+    // alive.
+    start_heartbeat();
+    
+    // Now it is possible to start up the USB device driver. The host can detect
+    // this, connect, get the enumeration data, and then testing will proceed
+    // in response to class control messages.
+    provide_endpoint_enumeration_data();
+    control_endpoint->enumeration_data      = &usb_enum_data;
+    control_endpoint->class_control_fn      = &handle_class_control_messages;
+    control_endpoint->reserved_control_fn   = &handle_reserved_control_messages;
+    usbs_start(control_endpoint);
+
+    // Now it is over to the host to detect this target and start performing tests.
+    // Much of this is handled at DSR level, in response to USB control messages.
+    // Some of those control messages require action at thread level, and that is
+    // achieved by signalling a semaphore and waking up this thread. A static
+    // function pointer is used to keep track of what operation is actually required.
+    for (;;) {
+        void (*handler)(void);
+        
+        cyg_semaphore_wait(&main_wakeup);
+        handler = main_thread_action;
+        main_thread_action   = 0;
+        CYG_CHECK_FUNC_PTR(handler, "Main thread woken up when there is nothing to be done");
+        (*handler)();
+        idle = true;
+    }
+}
+
+/*}}}*/
--- a/packages/redboot/current/ChangeLog
+++ b/packages/redboot/current/ChangeLog
@@ -1,3 +1,8 @@
+2002-05-31  Jesper Skov  <jskov@redhat.com>
+
+	* doc/redboot_installing.sgml: Updated the remaining installation
+	instructions (well, the top mode details anyway).
+
 2002-05-29  Jesper Skov  <jskov@redhat.com>
 
 	* doc/redboot_cmds.sgml: Changed format of fis commands.
--- a/packages/redboot/current/doc/redboot_installing.sgml
+++ b/packages/redboot/current/doc/redboot_installing.sgml
@@ -4584,13 +4584,34 @@ description of the associated modes.</pa
 and testing</secondary></indexterm><indexterm><primary>installing and testing
 </primary><secondary>Analogue & Micro PowerPC 860T</secondary></indexterm>RedBoot uses
 the SMC1 serial port. The default serial port settings are 38400,8,N,1.
-Ethernet is also supported using the RJ-45 connector. </para>
-<para>Management of onboard flash is also supported. 
-A single RedBoot configuration is supported: <itemizedlist>
-<listitem><para>RedBoot running from RAM using an image copied from the flash boot sector (ROMRAM mode).
+Ethernet is also supported using the RJ-45 connector. Management of
+onboard flash is also supported.</para>
+
+<para>The following RedBoot configurations are supported:
+
+      <informaltable frame="all">
+	<tgroup cols="4" colsep="1" rowsep="1" align="left">
+	  <thead>
+	    <row>
+	      <entry>Configuration</entry>
+	      <entry>Mode</entry>
+	      <entry>Description</entry>
+	      <entry>File</entry>
+	    </row>
+	  </thead>
+	  <tbody>
+	    <row>
+	      <entry>ROMRAM</entry>
+	      <entry>[ROMRAM]</entry>
+	      <entry>RedBoot running from RAM, but contained in the
+	      board's flash boot sector.</entry>
+	      <entry>redboot_ROMRAM.ecm</entry>
+	    </row>
+</tbody>
+</tgroup>
+</informaltable>
 </para>
-</listitem>
-</itemizedlist></para>
+
 </sect2>
 <sect2>
 <title>Initial Installation Method </title>
@@ -4639,19 +4660,45 @@ and testing</secondary></indexterm><inde
 </primary><secondary>Motorola PowerPC MBX</secondary></indexterm>RedBoot uses
 the SMC1/COM1 serial port. The default serial port settings are 38400,8,N,1.
 Ethernet is also supported using the 10-base T connector. </para>
-<para>Management of onboard flash is also supported. Two basic RedBoot configurations
-are supported: <itemizedlist>
-<listitem><para>RedBoot running from RAM with RedBoot in the flash boot sector.
+<para>Management of onboard flash is also supported.</para>
+
+<para>The following RedBoot configurations are supported:
+
+      <informaltable frame="all">
+	<tgroup cols="4" colsep="1" rowsep="1" align="left">
+	  <thead>
+	    <row>
+	      <entry>Configuration</entry>
+	      <entry>Mode</entry>
+	      <entry>Description</entry>
+	      <entry>File</entry>
+	    </row>
+	  </thead>
+	  <tbody>
+	    <row>
+	      <entry>ROM</entry>
+	      <entry>[ROM]</entry>
+	      <entry>RedBoot running from the board's flash boot
+	      sector.</entry>
+	      <entry>redboot_ROM.ecm</entry>
+	    </row>
+	    <row>
+	      <entry>RAM</entry>
+	      <entry>[RAM]</entry>
+	      <entry>RedBoot running from RAM with RedBoot in the
+	      flash boot sector.</entry>
+	      <entry>redboot_RAM.ecm</entry>
+	    </row>
+</tbody>
+</tgroup>
+</informaltable>
 </para>
-</listitem>
-<listitem><para>RedBoot running from the board's flash boot sector.  </para>
-</listitem>
-</itemizedlist></para>
+
 </sect2>
 <sect2>
 <title>Initial Installation Method </title>
 <para>Device programmer is used to program the XU1 socketed flash part  (AM29F040B)
-with the ROM version of RedBoot. - Use the on-board EPPC-Bug monitor to update
+with the ROM mode image of RedBoot. Use the on-board EPPC-Bug monitor to update
 RedBoot. </para>
 <para>This assumes that you have EPPC-Bug in the on-board flash. This can
 be determined by setting up the board according to the following instructions
@@ -4666,33 +4713,27 @@ 8N1. </para>
 </orderedlist>
 <para>If it is available, program the flash by following these steps: </para>
 <orderedlist>
-<listitem><para>Prepare EPPC-Bug for download: <screen>EPPC-Bug>lo 0
+<listitem><para>Prepare EPPC-Bug for download: <screen>EPPC-Bug><userinput>lo 0</userinput>
 </screen>At this point the monitor is ready for input. It will not
 return the prompt until the file has been downloaded.  </para>
 </listitem>
 <listitem><para>Use the terminal emulator's ASCII download feature (or a simple
-clipboard copy/paste operation) to download the redboot.ppcbug file.</para>
-<para>Note that on Linux, Minicom's ASCII download feature seems to be broken.
-A workaround is to load the file into emacs (or another editor) and copy the
+clipboard copy/paste operation) to download the
+<filename>redboot.ppcbug</filename> file.</para>
+<para>Note that on Linux, <application>Minicom</application>'s ASCII
+download feature seems to be broken. A workaround is to load the file
+into <application>emacs</application> (or another editor) and copy the
 full contents to the clipboard. Then press the mouse paste-button (usually
-the middle one) over the Minicom window.  </para>
+the middle one) over the <application>Minicom</application> window.  </para>
 </listitem>
 <listitem><para>Program the flash with the downloaded data: <screen>
-EPPC-Bug>pflash 40000 60000 fc000000</screen></para>
+EPPC-Bug><userinput>pflash 40000 60000 fc000000</userinput></screen></para>
 </listitem>
 <listitem><para>Switch off the power, and change jumper 4 to 1-2. Turn on
 the power again. The board should now boot using the newly programmed RedBoot.
 </para>
 </listitem>
 </orderedlist>
-<para>To install RedBoot on a target that already has eCos GDB stubs, download
-the RAM version of RedBoot and run it. Initialize the flash image directory: <screen>
-RedBoot> fi init</screen>Then download the ROM version of RedBoot
-and program it into flash: 
-<screen>
-RedBoot> <userinput>load redboot_ROM.srec -b 0x80100000</userinput>
-RedBoot> <userinput>fi cr RedBoot -f 0xFE000000 -b 0x00040000 -l 0x20000</userinput>
-</screen></para>
 </sect2>
 <sect2>
 <title>Special RedBoot Commands </title>
@@ -4743,14 +4784,40 @@ description of the associated modes.</pa
 and testing</secondary></indexterm><indexterm><primary>installing and testing
 </primary><secondary>Hitachi SH EDK7708</secondary></indexterm>RedBoot uses
 the serial port. The default serial port settings are 38400,8,N,1.</para>
-<para>Management of onboard flash is also supported. Two basic RedBoot configurations
-are supported: <itemizedlist>
-<listitem><para>RedBoot running from RAM with RedBoot in the flash boot sector.
+<para>Management of onboard flash is also supported.</para>
+
+<para>The following RedBoot configurations are supported:
+
+      <informaltable frame="all">
+	<tgroup cols="4" colsep="1" rowsep="1" align="left">
+	  <thead>
+	    <row>
+	      <entry>Configuration</entry>
+	      <entry>Mode</entry>
+	      <entry>Description</entry>
+	      <entry>File</entry>
+	    </row>
+	  </thead>
+	  <tbody>
+	    <row>
+	      <entry>ROM</entry>
+	      <entry>[ROM]</entry>
+	      <entry>RedBoot running from the board's flash boot
+	      sector.</entry>
+	      <entry>redboot_ROM.ecm</entry>
+	    </row>
+	    <row>
+	      <entry>RAM</entry>
+	      <entry>[RAM]</entry>
+	      <entry>RedBoot running from RAM with RedBoot in the
+	      flash boot sector.</entry>
+	      <entry>redboot_RAM.ecm</entry>
+	    </row>
+</tbody>
+</tgroup>
+</informaltable>
 </para>
-</listitem>
-<listitem><para>RedBoot running from the board's flash boot sector.  </para>
-</listitem>
-</itemizedlist></para>
+
 </sect2>
 <sect2>
 <title>Initial Installation Method </title>
@@ -4802,14 +4869,40 @@ for instructions for the MS7729SE01 and 
 <para>RedBoot uses
 the COM1 and COM2 serial ports. The default serial port settings are 38400,8,N,1.
 Ethernet is also supported using the 10-base T connector. </para>
-<para>Management of onboard flash is also supported. Two basic RedBoot configurations
-are supported: <itemizedlist>
-<listitem><para>RedBoot running from RAM with RedBoot in the flash boot sector.
+<para>Management of onboard flash is also supported.</para>
+
+<para>The following RedBoot configurations are supported:
+
+      <informaltable frame="all">
+	<tgroup cols="4" colsep="1" rowsep="1" align="left">
+	  <thead>
+	    <row>
+	      <entry>Configuration</entry>
+	      <entry>Mode</entry>
+	      <entry>Description</entry>
+	      <entry>File</entry>
+	    </row>
+	  </thead>
+	  <tbody>
+	    <row>
+	      <entry>ROM</entry>
+	      <entry>[ROM]</entry>
+	      <entry>RedBoot running from the board's flash boot
+	      sector.</entry>
+	      <entry>redboot_ROM.ecm</entry>
+	    </row>
+	    <row>
+	      <entry>RAM</entry>
+	      <entry>[RAM]</entry>
+	      <entry>RedBoot running from RAM with RedBoot in the
+	      flash boot sector.</entry>
+	      <entry>redboot_RAM.ecm</entry>
+	    </row>
+</tbody>
+</tgroup>
+</informaltable>
 </para>
-</listitem>
-<listitem><para>RedBoot running from the board's flash boot sector.  </para>
-</listitem>
-</itemizedlist></para>
+
 </sect2>
 <sect2>
 <title>Initial Installation Method </title>
@@ -4822,13 +4915,14 @@ which allows for initial programming of 
 <listitem><para>Connect a serial cable to CN1 (SCI) and power up the board.</para>
 </listitem>
 <listitem><para>After the boot monitor banner, invoke the flash
-download/program command:<screen>Ready &gt;fl</screen></para>
+download/program command:<screen>Ready &gt;<userinput>fl</userinput></screen></para>
 </listitem>
 <listitem><para>The monitor should now ask for input:
 <screen>Flash ROM data copy to RAM
 Please Send A S-format Record</screen>At this point copy the
 RedBoot ROM SREC file to the serial port:<screen>
-$ cat redboot_SE7709RP_ROM.eprom.srec &gt /dev/ttyS0</screen>Eventually you
+$ <userinput>cat redboot_SE7709RP_ROM.eprom.srec &gt /dev/ttyS0</userinput></screen>
+Eventually you
 should see something like<screen>Start Addrs = A1000000
 End Addrs = A1xxxxxx
 Transfer complete</screen> from the monitor.
@@ -4940,28 +5034,59 @@ and testing</secondary></indexterm><inde
 the COM1 and COM2 serial ports (and the debug port on the
 motherboard).
 The default serial port settings are 38400,8,N,1.
-Ethernet is also supported using a D-Link DFE-530TX PCI plugin card.</para>
-<para>Management of onboard flash is also supported. Two basic RedBoot configurations
-are supported: <itemizedlist>
-<listitem><para>RedBoot running from RAM with RedBoot in the flash boot sector.
+Ethernet is also supported using a D-Link DFE-530TX PCI plugin
+card. Management of onboard flash is also supported. </para> 
+
+<para>The following RedBoot configurations are supported:
+
+      <informaltable frame="all">
+	<tgroup cols="4" colsep="1" rowsep="1" align="left">
+	  <thead>
+	    <row>
+	      <entry>Configuration</entry>
+	      <entry>Mode</entry>
+	      <entry>Description</entry>
+	      <entry>File</entry>
+	    </row>
+	  </thead>
+	  <tbody>
+	    <row>
+	      <entry>ROM</entry>
+	      <entry>[ROM]</entry>
+	      <entry>RedBoot running from the board's flash boot
+	      sector.</entry>
+	      <entry>redboot_ROM.ecm</entry>
+	    </row>
+	    <row>
+	      <entry>RAM</entry>
+	      <entry>[RAM]</entry>
+	      <entry>RedBoot running from RAM with RedBoot in the
+	      flash boot sector.</entry>
+	      <entry>redboot_RAM.ecm</entry>
+	    </row>
+</tbody>
+</tgroup>
+</informaltable>
 </para>
-</listitem>
-<listitem><para>RedBoot running from the board's flash boot sector.  </para>
-</listitem>
-</itemizedlist></para>
+
+
 </sect2>
 <sect2>
 <title>Initial Installation Method </title>
-<para>A copy of the ROM startup version of RedBoot must be programmed
-into the two EPROMs. Two files with a split version of the ROM image is
-provided: it is also possible to recreate these from the redboot.bin
-file, but requires the split_word.c program in
-hal/sh/hs7729pci/<replaceable>VERSION</replaceable>/misc to be built
-and executed with the redboot.bin filename as sole argument.</para>
-
-<para>After doing this it is advised that another ROM version of
-RedBoot is programmed into the flash, and that copy be used for
-booting the board. This allows for software programmed updates of
+<para>A ROM mode RedBoot image must be programmed
+into the two EPROMs. Two files with a split version of the ROM mode
+image is
+provided: it is also possible to recreate these from the
+<filename>redboot.bin</filename>
+file, but requires the <application>split_word.c</application> program in
+<filename
+class="directory">hal/sh/hs7729pci/<replaceable>VERSION</replaceable>/misc</filename>
+to be built and executed with the <filename>redboot.bin</filename>
+filename as sole argument.</para>
+
+<para>After doing this it is advised that another ROM mode image of
+RedBoot is programmed into the on-board flash, and that copy be used
+for booting the board. This allows for software programmed updates of
 RedBoot instead of having to reprogram the EPROMs.</para>
 
 <orderedlist>
@@ -4972,8 +5097,9 @@ go in socket M1 and the .hi image in soc
 <listitem><para>Set switch SW1-6 to ON [boot from EPROM]</para>
 </listitem>
 <listitem><para>Follow the instructions under Flash management for
-updating the flash copy of RedBoot, but use
-<screen>-f 0x80400000</screen> due to setting of
+updating the flash copy of RedBoot, but force the flash destination
+address with
+<screen><userinput>-f 0x80400000</userinput></screen> due to setting of
 the SW1-6 switch.</para>
 </listitem>
 <listitem><para>Set switch SW1-6 to OFF [boot from flash] and reboot the board. You
@@ -5085,15 +5211,41 @@ for instructions for the MS7709SE01 vari
 
 <para>RedBoot uses
 the COM1 and COM2 serial ports. The default serial port settings are 38400,8,N,1.
-Ethernet is also supported using the 10-base T connector. </para>
-<para>Management of onboard flash is also supported. Two basic RedBoot configurations
-are supported: <itemizedlist>
-<listitem><para>RedBoot running from RAM with RedBoot in the flash boot sector.
+Ethernet is also supported using the 10-base T connector. Management
+of onboard flash is also supported.</para>
+
+<para>The following RedBoot configurations are supported:
+
+      <informaltable frame="all">
+	<tgroup cols="4" colsep="1" rowsep="1" align="left">
+	  <thead>
+	    <row>
+	      <entry>Configuration</entry>
+	      <entry>Mode</entry>
+	      <entry>Description</entry>
+	      <entry>File</entry>
+	    </row>
+	  </thead>
+	  <tbody>
+	    <row>
+	      <entry>ROM</entry>
+	      <entry>[ROM]</entry>
+	      <entry>RedBoot running from the board's flash boot
+	      sector.</entry>
+	      <entry>redboot_ROM.ecm</entry>
+	    </row>
+	    <row>
+	      <entry>RAM</entry>
+	      <entry>[RAM]</entry>
+	      <entry>RedBoot running from RAM with RedBoot in the
+	      flash boot sector.</entry>
+	      <entry>redboot_RAM.ecm</entry>
+	    </row>
+</tbody>
+</tgroup>
+</informaltable>
 </para>
-</listitem>
-<listitem><para>RedBoot running from the board's flash boot sector.  </para>
-</listitem>
-</itemizedlist></para>
+
 </sect2>
 <sect2>
 <title>Initial Installation Method </title>
@@ -5106,13 +5258,14 @@ which allows for initial programming of 
 <listitem><para>Connect a serial cable to COM2 and power up the board.</para>
 </listitem>
 <listitem><para>After the boot monitor banner, invoke the flash
-download/program command:<screen>Ready &gt;fl</screen></para>
+download/program command:<screen>Ready &gt;<userinput>fl</userinput></screen></para>
 </listitem>
 <listitem><para>The monitor should now ask for input:
 <screen>Flash ROM data copy to RAM
 Please Send A S-format Record</screen>At this point copy the
 RedBoot ROM SREC file to the serial port:<screen>
-$ cat redboot_ROM.eprom.srec &gt /dev/ttyS0</screen>Eventually you
+$ <userinput>cat redboot_ROM.eprom.srec &gt /dev/ttyS0</userinput></screen>
+Eventually you
 should see something like<screen>Start Addrs = A1000000
 End Addrs = A1xxxxxx
 Transfer complete</screen> from the monitor.
@@ -5218,15 +5371,41 @@ description of the associated modes.</pa
 and testing</secondary></indexterm><indexterm><primary>installing and testing
 </primary><secondary>Hitachi SH SE7751</secondary></indexterm>RedBoot uses
 the COM1 serial port. The default serial port settings are 38400,8,N,1.
-Ethernet is also supported using the 10-base T connector. </para>
-<para>Management of onboard flash is also supported. Two basic RedBoot configurations
-are supported: <itemizedlist>
-<listitem><para>RedBoot running from RAM with RedBoot in the flash boot sector.
+Ethernet is also supported using the 10-base T connector. Management
+of onboard flash is also supported.</para> 
+
+<para>The following RedBoot configurations are supported:
+
+      <informaltable frame="all">
+	<tgroup cols="4" colsep="1" rowsep="1" align="left">
+	  <thead>
+	    <row>
+	      <entry>Configuration</entry>
+	      <entry>Mode</entry>
+	      <entry>Description</entry>
+	      <entry>File</entry>
+	    </row>
+	  </thead>
+	  <tbody>
+	    <row>
+	      <entry>ROM</entry>
+	      <entry>[ROM]</entry>
+	      <entry>RedBoot running from the board's flash boot
+	      sector.</entry>
+	      <entry>redboot_ROM.ecm</entry>
+	    </row>
+	    <row>
+	      <entry>RAM</entry>
+	      <entry>[RAM]</entry>
+	      <entry>RedBoot running from RAM with RedBoot in the
+	      flash boot sector.</entry>
+	      <entry>redboot_RAM.ecm</entry>
+	    </row>
+</tbody>
+</tgroup>
+</informaltable>
 </para>
-</listitem>
-<listitem><para>RedBoot running from the board's flash boot sector.  </para>
-</listitem>
-</itemizedlist></para>
+
 </sect2>
 <sect2>
 <title>Initial Installation Method </title>
@@ -5239,13 +5418,14 @@ which allows for initial programming of 
 <listitem><para>Connect a serial cable to COM1 and power up the board.</para>
 </listitem>
 <listitem><para>After the boot monitor banner, invoke the flash
-download/program command:<screen>Ready &gt;fl</screen></para>
+download/program command:<screen>Ready &gt;<userinput>fl</userinput></screen></para>
 </listitem>
 <listitem><para>The monitor should now ask for input:
 <screen>Flash ROM data copy to RAM
 Please Send A S-format Record</screen>At this point copy the
 RedBoot ROM SREC file to the serial port:<screen>
-$ cat redboot_ROM.eprom.srec &gt /dev/ttyS0</screen>Eventually you
+$ <userinput>cat redboot_ROM.eprom.srec &gt /dev/ttyS0</userinput></screen>
+Eventually you
 should see something like<screen>Start Addrs = A1000000
 End Addrs = A1xxxxxx
 Transfer complete</screen> from the monitor.