This is the mail archive of the ecos-patches@sourceware.org mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [patch] Philips D12 USB Slave Driver


Andrew Lunn wrote:

On Tue, Apr 11, 2006 at 05:23:46PM -0400, Frank Pagliughi wrote:


Andrew Lunn wrote:



Hi Frank

On Tue, Apr 11, 2006 at 09:35:28AM -0400, Frank Pagliughi wrote:




This is a driver for the Philips PDIUSBD12 (D12) USB Slave Controller Chip. It was written for the D12 on a PC/104 (ISA) board. With some minor adjustments and testing should go with any CPU. I (tried to) removed all the board-specific stuff, which, unfortunately also meant removing DMA support.




I just had a very quick look at the code. I does seem to have a few
x386ism's in it but they are all nicely abstracted. Do you think you
can split this into two packages. A generic D12 driver and an i386
specific part. It would contain the functions to actually access the
hardware. You could put these into an inline header file so there
would be no loss of performance.




Yeah, I thought of trying something like that at the last minute, especially to get the DMA code back in there. But I'm leaving town for a few weeks, so I thought I would throw it at you to get some suggestions. I can have a look when I get back. Can you point me to any example that does this sort of thing? The PC serial ports, maybe?



Hi Frank


Take a look at the attached code. It will need a few clean ups,
comments, ChangeLog etc, but it should give you the idea. I had to
guess how the DMA word work, so i might have it wrong.




Oops. I messed up the patch. Didn't clear out my directory very well before creating it. The "target.c" is actually an emacs backup file of the existing "usbtarget.c" that's in the USB tests directory (packages/io/usb/slave/current/tests). Note the "~" in the file name. Sorry about that. Ignore that file.



Yep, my silly error. I'm an emacs uses so should know better!




I will submit a few minor changes to that file, though, in the near future. Probably as configuration options. There are a few things hard-wired into the test program that need to be configured for the specific chip - such as the size of control endpoint zero.



O.K. please let me have these patches when you are ready.




In the patch, there is, however a valid, minor, update to the host side of the existing test program that runs on Linux, "usbhost.c" The host didn't recognize the target when run on Fedora Core 4. The fix should allow it to run on FC4, plus whatever system it was originnaly designed for.



Thanks. I've already committed this change, and the same in chmod program.

How is the copyright assignment going?

Andrew




Andrew,

Here's the updated patch for the USB D12 driver. It is mainly the changes that you posted last time, having split out the i386 specifics, with a few very minor fixes. In addition I added back the package declarations for ecos.db. It should patch the latest CVS.

The copyright assignment is in the mail.

Frank


diff -urN --exclude=CVS ecos-2006-05-19/packages/devs/usb/d12/current/cdl/usbs_d12.cdl ecos/packages/devs/usb/d12/current/cdl/usbs_d12.cdl
--- ecos-2006-05-19/packages/devs/usb/d12/current/cdl/usbs_d12.cdl	1969-12-31 19:00:00.000000000 -0500
+++ ecos/packages/devs/usb/d12/current/cdl/usbs_d12.cdl	2006-04-27 10:55:39.000000000 -0400
@@ -0,0 +1,331 @@
+# ====================================================================
+#
+#	usbs_d12.cdl
+#
+#	USB device driver for the Philips PDIUSBD12 Full Speed USB
+#	peripheral chip.
+#
+# ====================================================================
+#####ECOSGPLCOPYRIGHTBEGIN####
+## -------------------------------------------
+## This file is part of eCos, the Embedded Configurable Operating System.
+## Copyright (C) 2003, 2004 eCosCentric Limited
+## Copyright (C) 2005 Frank Pagliughi
+##
+## 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.
+## -------------------------------------------
+#####ECOSGPLCOPYRIGHTEND####
+# ====================================================================
+######DESCRIPTIONBEGIN####
+#
+# Author(s):      Frank M. Pagliughi (fmp), SoRo Systems, Inc.
+# Contributors:
+# Date:           2004-05-24
+#
+#####DESCRIPTIONEND####
+# ====================================================================
+
+cdl_package CYGPKG_DEVS_USB_D12 {
+	display     "Philips D12 USB Device Driver"
+	include_dir "cyg/io/usb"
+	parent      CYGPKG_USB
+	implements  CYGHWR_IO_USB_SLAVE
+	doc         ref/devs-usb-philips-pdiusbd12.html
+	
+	description "
+		The Philips PDIUSBD12 is a USB peripheral controller (slave)
+	chip that can connect to a microcontroller or microprocessor through
+	an 8-bit parallel bus. The SoRo Systems USB-D12-104 is a slave board 
+	for the PC's ISA or PC/104 bus that contains a D12 chip placed in the 
+	PC's I/O space with jumpered selections for IRQ and DMA settings. This 
+	package provides an eCos device driver.
+	"
+        requires      CYGIMP_DEVS_USB_D12_HW_ACCESS_HEADER
+
+        cdl_option    CYGIMP_DEVS_USB_D12_HW_ACCESS_HEADER {
+                display       "Inline file implementing hardware access"
+                flavor        booldata
+                default_value false
+                description   "
+                    This option should contain the header file which
+                    implements basic access to the D12 registers"
+        }
+
+	cdl_component CYGFUN_DEVS_USB_D12_EP0 {
+		display       "Support the Control Endpoint 0"
+		default_value CYGINT_IO_USB_SLAVE_CLIENTS
+		requires      CYGPKG_IO_USB CYGPKG_IO_USB_SLAVE
+		compile       usbs_d12.c
+		compile       -library=libextras.a usbs_d12_data.cxx
+		description "
+			Enable support for endpoint 0. If this support is disabled
+			then the entire USB port is unusable."
+	
+		cdl_option CYGVAR_DEVS_USB_D12_EP0_DEVTAB_ENTRY {
+			display       "Provide a devtab entry for endpoint 0"
+			default_value CYGGLO_IO_USB_SLAVE_PROVIDE_DEVTAB_ENTRIES
+			requires      CYGPKG_IO
+			description "
+			   If endpoint 0 will only be accessed via the low-level
+			   USB-specific calls then there is no need for an entry
+			   in the device table, saving some memory. If the
+			   application intends to access the endpoint by means
+			   of open and ioctl calls then a devtab entry is needed.
+			"
+		}
+
+		cdl_option CYGNUM_DEVS_USB_D12_EP0_TXBUFSIZE {
+		    display       "Size of statically-allocated endpoint 0 transmit buffer"
+		    flavor        data
+		    default_value 256
+		    requires      { CYGNUM_DEVS_USB_D12_EP0_TXBUFSIZE >= CYGNUM_DEVS_USB_D12_EP0_PKTSIZE }
+		    description "
+		        The implementation of the support for endpoint 0 uses
+		        a single static buffer to hold the response to the
+		        current control message. Typically this buffer can be
+		        fairly small since replies to control messages tend to
+		        be small: typically some tens of bytes for the enumeration
+		        data, perhaps a bit more for unicode-encoded string
+		        descriptors. However if some application-specific protocol
+		        depends on larger control messages then this buffer
+		        size may need to be increased.
+		    "
+		}
+	}    
+
+	cdl_option CYGNUM_DEVS_USB_D12_BASEADDR {
+		display       "Base Address of D12 chip"
+		flavor        data
+		active_if     CYGFUN_DEVS_USB_D12_EP0
+		description   "
+			The base memory or I/O address where the USB chip resides.
+                        The value is set by the hardware specific driver's CDL. 
+		    "
+	}
+
+	cdl_option CYGNUM_DEVS_USB_D12_IRQ {
+		display       "IRQ for the D12 chip"
+		active_if     CYGFUN_DEVS_USB_D12_EP0
+		flavor        data
+		description   "
+			The IRQ assigned to the D12 chip. The value 
+                        is set by the hardware specific drivers's CDL.
+		    "
+	}
+
+	cdl_option CYGNUM_DEVS_USB_D12_INT {
+		display       "INT for the D12 chip"
+		active_if     CYGFUN_DEVS_USB_D12_EP0
+		flavor        data
+		default_value { CYGNUM_DEVS_USB_D12_IRQ + 32 }
+		description "
+			The interrupt vector assigned to the D12 chip
+		    "
+	}
+
+	cdl_component CYGPKG_DEVS_USB_D12_THREAD {
+		display		"Use a thread to service D12 chip"
+		active_if	CYGFUN_DEVS_USB_D12_EP0
+		default_value	0
+		description	"
+			Services the D12 USB chip with a thread, rather than at the DSR level.
+			This allows for increased debug support, like TRACE output from the 
+			driver at the expense of some throughput & reaction time. The service
+			thread MUST be at a higher priority than any application thread that
+			uses the USB port.
+		"
+
+		cdl_option CYGNUM_DEVS_USB_D12_THREAD_PRIORITY {
+			display       "Thread Priority"
+			flavor        data
+			legal_values  1 to 30
+			default_value 4
+			description "
+				The priority of the D12 device driver thread.
+			"
+		}
+
+		cdl_option CYGNUM_DEVS_USB_D12_THREAD_STACK_SIZE {
+			display		"USB Thread Stack Size"
+			flavor		data
+			default_value	4096
+			description	"
+				The stack size for the D12 device driver thread.
+			"
+		}
+	}	
+
+	cdl_component CYGFUN_DEVS_USB_D12_DEBUG {
+		display       "Debug output from the D12 Device Driver"
+		requires      CYGPKG_DEVS_USB_D12_THREAD
+		default_value 0
+		description "
+			Provide debugging output from the D12 Device Driver
+		"
+		
+		cdl_option CYGSEM_DEVS_USB_D12_DEBUG_DUMP_EP0_BUFS {
+			display		"Dump the contents of EP0 buffers"
+			flavor		bool
+			default_value	0
+			description "
+				Dump the contents of the packages going through EP0. This allows 
+				you to see things like device requests and responses.
+			"
+		}
+
+		cdl_option CYGSEM_DEVS_USB_D12_DEBUG_DUMP_BUFS {
+			display		"Dump the contents of data buffers"
+			flavor		bool
+			default_value	0
+			description "
+				Dump the contents of the packages going through the generic
+				endpoints. This allow you to see all of the data going through
+				the device.
+			"
+		}
+	}
+
+	cdl_component CYGPKG_DEVS_USB_D12_TX_EP1 {
+		display       "Endpoint 1 Interrupt IN, (tx_ep1)"
+		implements    CYGHWR_IO_USB_SLAVE_IN_ENDPOINTS
+		requires      CYGFUN_DEVS_USB_D12_EP0
+		default_value CYGFUN_DEVS_USB_D12_EP0
+		description "
+			On the D12, Endpoint 1 IN can be used for Interrupt,
+			Bulk, or Control packages. This driver currently only supports
+			Interrupt packages on Endpoint 1 (slave -> host) transfers
+		"
+		
+		cdl_option CYGVAR_DEVS_USB_D12_TX_EP1_DEVTAB_ENTRY {
+			display       "Provide a devtab entry for Endpoint 1 IN"
+			default_value CYGGLO_IO_USB_SLAVE_PROVIDE_DEVTAB_ENTRIES
+			requires      CYGPKG_IO
+			description "
+				If Endpoint 1 IN will only be accessed via the low-level
+				USB-specific calls then there is no need for an entry
+				in the device table, saving some memory. If the
+				application intends to access the endpoint by means
+				of open and write calls then a devtab entry is needed.
+			"
+		}
+	}
+
+	cdl_component CYGPKG_DEVS_USB_D12_RX_EP1 {
+		display       "Endpoint 1 Interrupt OUT, (rx_ep1)"
+		implements    CYGHWR_IO_USB_SLAVE_OUT_ENDPOINTS
+		requires      CYGFUN_DEVS_USB_D12_EP0
+		default_value CYGFUN_DEVS_USB_D12_EP0
+		description "
+			In the D12, Endpoint 1 OUT can be used for Interrupt,
+			Bulk, or Control packages. This driver currently only supports
+			Interrupt packages on Endpoint 1 for (host -> slave) transfers" 
+		
+		cdl_option CYGVAR_DEVS_USB_D12_RX_EP1_DEVTAB_ENTRY {
+			display       "Provide a devtab entry for Endpoint 1 OUT"
+			default_value CYGGLO_IO_USB_SLAVE_PROVIDE_DEVTAB_ENTRIES
+			requires      CYGPKG_IO
+			description "
+				If Endpoint 1 OUT will only be accessed via the low-level
+				USB-specific calls then there is no need for an entry
+				in the device table, saving some memory. If the
+				application intends to access the endpoint by means
+				of open and write calls then a devtab entry is needed.
+			"
+		}
+	}
+
+	cdl_component CYGPKG_DEVS_USB_D12_TX_EP2 {
+		display			"Endpoint 2 Bulk IN, (tx_ep2)"
+		implements		CYGHWR_IO_USB_SLAVE_IN_ENDPOINTS
+		requires		CYGFUN_DEVS_USB_D12_EP0
+		default_value	CYGFUN_DEVS_USB_D12_EP0
+		description "
+			In the D12, Endpoint 2 IN can be used for Bulk, Interrupt,
+			or Control packages. This driver currently only supports
+		    Bulk packages on Endpoint 2 for (slave -> host) transfers.
+		"
+		
+		cdl_option CYGVAR_DEVS_USB_D12_TX_EP2_DEVTAB_ENTRY {
+			display       "Provide a devtab entry for Endpoint 2 IN"
+			default_value CYGGLO_IO_USB_SLAVE_PROVIDE_DEVTAB_ENTRIES
+			requires      CYGPKG_IO
+			description "
+				If Endpoint 2 IN will only be accessed via the low-level
+				USB-specific calls then there is no need for an entry
+				in the device table, saving some memory. If the
+				application intends to access the endpoint by means
+				of open and write calls then a devtab entry is needed.
+			"
+		}
+	}
+
+	cdl_component CYGPKG_DEVS_USB_D12_RX_EP2 {
+		display       "Endpoint 2 Bulk OUT, (rx_ep2)"
+		implements    CYGHWR_IO_USB_SLAVE_OUT_ENDPOINTS
+		requires      CYGFUN_DEVS_USB_D12_EP0
+		default_value CYGFUN_DEVS_USB_D12_EP0
+		description "
+			In the D12, Endpoint 2 OUT can be used for Bulk, Interrupt,
+			Control packages. This driver currently only supports
+			Bulk packages on Endpoint 2 for (host -> slave) transfers."
+		
+		cdl_option CYGVAR_DEVS_USB_D12_RX_EP2_DEVTAB_ENTRY {
+			display       "Provide a devtab entry for Endpoint 2 OUT"
+			default_value CYGGLO_IO_USB_SLAVE_PROVIDE_DEVTAB_ENTRIES
+			requires      CYGPKG_IO
+			description "
+				If Endpoint 2 OUT will only be accessed via the low-level
+				USB-specific calls then there is no need for an entry
+				in the device table, saving some memory. If the
+				application intends to access the endpoint by means
+				of open and write calls then a devtab entry is needed.
+			"
+		}
+	}
+
+	cdl_option CYGDAT_DEVS_USB_D12_DEVTAB_BASENAME {
+		display       "Base name for devtab entries"
+		flavor        data
+		active_if     { CYGVAR_DEVS_USB_D12_EP0_DEVTAB_ENTRY ||
+						CYGVAR_DEVS_USB_D12_TX_EP1_DEVTAB_ENTRY ||
+						CYGVAR_DEVS_USB_D12_RX_EP1_DEVTAB_ENTRY ||
+						CYGVAR_DEVS_USB_D12_TX_EP2_DEVTAB_ENTRY ||
+						CYGVAR_DEVS_USB_D12_RX_EP2_DEVTAB_ENTRY
+		}
+		default_value { "\"/dev/usbs\"" }
+		description "
+			If the D12 USB device driver package provides devtab entries
+			for any of the endpoints then this option gives
+			control over the names of these entries. By default the
+			endpoints will be called \"/dev/usbs0c\", \"/dev/usbs1r\"
+			\"/dev/usbs1w\", \"/dev/usbs2r\", \"/dev/usbs2w\"
+			(assuming those endpoints are all enabled. The common
+			part \"/dev/usbs\" is determined by this configuration
+			option. It may be necessary to change this if there are
+			multiple USB slave-side devices on the target hardware to
+			prevent a name clash.
+		"
+	}
+}
+
diff -urN --exclude=CVS ecos-2006-05-19/packages/devs/usb/d12/current/include/usbs_d12.h ecos/packages/devs/usb/d12/current/include/usbs_d12.h
--- ecos-2006-05-19/packages/devs/usb/d12/current/include/usbs_d12.h	1969-12-31 19:00:00.000000000 -0500
+++ ecos/packages/devs/usb/d12/current/include/usbs_d12.h	2006-04-11 15:22:20.000000000 -0400
@@ -0,0 +1,76 @@
+#ifndef CYGONCE_USBS_D12_H
+# define CYGONCE_USBS_D12_H
+//==========================================================================
+//
+//      include/usbs_d12.h
+//
+//      The interface exported by the D12 USB device driver
+//
+//==========================================================================
+//####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):    Frank Pagliughi (fmp)
+// Contributors: fmp
+// Date:         2004-05-24
+// Purpose:
+//
+//####DESCRIPTIONEND####
+//==========================================================================
+
+#include <cyg/io/usb/usbs.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The Philips D12 is a full speed (12Mbps) USB peripheral controller
+ * chip, with a parallel interface allowing it to be connected to nearly
+ * any microcontroller or microprocessor. 
+ */
+extern usbs_control_endpoint    usbs_d12_ep0;
+
+extern usbs_rx_endpoint         usbs_d12_rx_ep1;
+extern usbs_tx_endpoint         usbs_d12_tx_ep1;
+extern usbs_rx_endpoint         usbs_d12_rx_ep2;
+extern usbs_tx_endpoint         usbs_d12_tx_ep2;
+    
+#ifdef __cplusplus
+} /* extern "C" { */
+#endif
+
+
+#endif /* CYGONCE_USBS_D12_H */
diff -urN --exclude=CVS ecos-2006-05-19/packages/devs/usb/d12/current/src/usbs_d12.c ecos/packages/devs/usb/d12/current/src/usbs_d12.c
--- ecos-2006-05-19/packages/devs/usb/d12/current/src/usbs_d12.c	1969-12-31 19:00:00.000000000 -0500
+++ ecos/packages/devs/usb/d12/current/src/usbs_d12.c	2006-05-19 18:14:42.000000000 -0400
@@ -0,0 +1,2272 @@
+//==========================================================================
+//
+//      usbs_d12.c
+//
+//      Driver for the D12 USB Slave Board
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+// Copyright (C) 2004, Frank Pagliughi
+//
+// 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):    Frank M. Pagliughi (fmp)
+// Date:         2004-05-22
+//
+// This code is a device driver for the SoRo Systems USB-D12-104, a PC/104
+// (ISA) Full-Speed USB slave board, which turns a PC/104 stack into a USB
+// slave device. The board contains a Philips PDIUSBD12 Peripheral Controller
+// Chip mapped into the PC's I/O space, with jumper-selectable I/O base 
+// address, IRQ, and DMA settings. The eCos config tool is used to adjust
+// settings for this driver to match the physical jumper settings. The chip
+// could run in polled mode without an IRQ, but this wouldn't be a great idea
+// other than maybe a debug environment. 
+//
+// The board supports DMA transfers over the Main endpoint, but I temporarily
+// removed that code to make the driver portable to other platforms.
+//
+// *** This driver should also work with the Philips ISA Eval Board
+//		for the D12, but I couldn't get one of them from Philips, so
+//		I couldn't test it.
+//
+// The D12 uses an indexed register set, which it describes as "commands." 
+// You first write a command (index) to the command register then you can
+// read or write data to that register. Each multi-byte command read or write
+// must be dione atomically, so all access to the chip must be serialized.
+// 
+// The D12 requests service through a single interrupt. The driver can
+// be configured to service the chip through a DSR or a thread. In either
+// case, the "service" code assumes it has unfettered access to the chip.
+// The interrupt, therefore never touches the chip. It just schedules the
+// DSR or service thread.
+// Currently, the code gets exclusive access to the chip by locking the
+// scheduler. This is suboptimal (locking the whole OS to touch one I/O 
+// chip), and better method should be explored.
+//
+// This version of the driver does not support Isocronous transfers.
+// 
+// Additional notes on the D12:
+//
+// - The D12 has 4 endpoints (2 IN, and 2 OUT) in addition to the main 
+//		control endpoint:
+//		- Endp 0 (Control In & Out, 16 byte buffer)
+//		- Endp 1 (IN & OUT, Bulk or Interrupt, 16 byte ea)
+//		- Endp 2 (IN and/or OUT, Bulk, Interrupt, or Isoc, 64 bytes ea)
+//
+// - The "main" endpoint (as Philips calls it) is Endp 2. It's double
+//		buffered and has a DMA interface, and thus, is suited for high
+//		throughput. For applications that perform either Isoc In or Out,
+//		the buffers for Endp 2 can be combined for a 128 byte space.
+//		This driver, however, currently does not support this.
+//
+// - There may be a flaw in the double buffering of the rx main endpoint. 
+//		According to the documentation it should be invisible to the software,
+//		but if both buffers fill (on an rx/OUT), they must both be read 
+//		together, otherwise it appears that the buffers/packets are returned
+//		in reverse order. ReadMainEndpointBuf() returns the data properly.
+//
+// - All the interrupt sources on the chip - individual endpoints, bus reset,
+//		suspend, and DMA - are OR'ed together and can be checked via the 
+//		interrupt status register. When using edge-sensitive interrupts, as
+//		we do here, the ISR/DSR must be sure all interrupts are cleared before
+//		returning otherwise no new interrupts will be latched.
+//
+// - If the DMA controller is not used for the Main Endpoint, you MUST enable
+//		the main endpoint interrupts in the DMA register (bits 6 & 7).
+//		Personally, I think this should be the default at reset, to make it
+//		compatible with the other endpoints, but Philips didn't see it that
+//		way.
+// 
+// - When a Setup (Device Request) packet arrives in the control endpoint, a
+//		bit is set in the endpoint's status register indicating the packet is
+//		setup and not data. By the USB standard, a setup packet can not be
+//		NAK'ed or STALL'ed, so when the chip receives a setup packet, it 
+//		flushes the Ctrl (EP0) IN buffer and disables the Validate and Clear
+//		Buffer commands. We must send an "acknowledge setup" to both
+//		EP0 IN and OUT before a Validate or Clear Buffer command is effective.
+//		See ReadSetupPacket().
+//
+//####DESCRIPTIONEND####
+//==========================================================================
+
+
+#include <cyg/infra/cyg_type.h>
+#include <cyg/infra/cyg_ass.h>
+#include <cyg/infra/cyg_trac.h>
+#include <cyg/infra/diag.h>
+
+#include <pkgconf/devs_usb_d12.h>
+
+#include <cyg/hal/drv_api.h>
+#include <cyg/hal/hal_arch.h>
+#include <cyg/hal/hal_io.h>
+#include <cyg/hal/hal_cache.h>
+#include <cyg/error/codes.h>
+
+#include <cyg/io/usb/usb.h>
+#include <cyg/io/usb/usbs.h>
+
+#include <string.h>
+
+// --------------------------------------------------------------------------
+//								Common Types
+// --------------------------------------------------------------------------
+
+typedef cyg_uint8	byte;
+typedef cyg_uint8	uint8;
+typedef cyg_int16	int16;
+typedef cyg_uint16	uint16;
+typedef cyg_int32	int32;
+typedef cyg_uint32	uint32;
+
+// --------------------------------------------------------------------------
+//								Tracing & Debug
+// --------------------------------------------------------------------------
+// If the driver is configured to use a thread to service the chip, then it
+// can also be configured to dump a lot of debug output.
+// Care must be taken that USB timing requirements are not violated by 
+// dumping debug info. If the data is sent to a serial port, it should use
+// a hardware driver and have a large output buffer (115200 baud & 2kB
+// buffer works for me).
+
+#if defined(CYGFUN_DEVS_USB_D12_DEBUG) && CYGFUN_DEVS_USB_D12_DEBUG
+	#define TRACE_D12	diag_printf
+#else
+	#define TRACE_D12	(1) ? (void)0 : diag_printf
+#endif
+
+#if defined(CYGSEM_DEVS_USB_D12_DEBUG_DUMP_EP0_BUFS) && CYGSEM_DEVS_USB_D12_DEBUG_DUMP_EP0_BUFS
+	#define TRACE_EP0	1
+#endif
+
+#if defined(CYGSEM_DEVS_USB_D12_DEBUG_DUMP_BUFS) && CYGSEM_DEVS_USB_D12_DEBUG_DUMP_BUFS
+	#define TRACE_EP	1
+#endif
+
+#if defined(TRACE_EP0) || defined(TRACE_EP)
+	static void _trace_buf(const char *hdr, const byte* buf, unsigned n)
+	{
+		unsigned i;
+
+		if (buf != 0 && n != 0) {
+			if (hdr && hdr[0])
+				TRACE_D12("%s ", hdr);
+
+			TRACE_D12("[");
+			for (i=0; i<n; i++) 
+				TRACE_D12(" x%02X", buf[i]);
+			TRACE_D12("]\n");
+		}
+	}
+#endif
+
+#if defined(TRACE_EP0)
+	#define TRACE_BUF0	_trace_buf
+#else	
+	#define TRACE_BUF0(hdr, buf, n)
+#endif
+
+#if defined(TRACE_EP)
+	#define TRACE_BUF	_trace_buf
+#else	
+	#define TRACE_BUF(hdr, buf, n)
+#endif
+
+// ==========================================================================
+//								Chip Wrapper
+// ==========================================================================
+
+// This section contains functions that wrapper the low-level access to the 
+// chip. There's a function around each register access on the chip, and then
+// some.
+
+#if defined(CYGSEM_DEVS_USB_D12_IO_MAPPED)
+	typedef void* d12_addr_type;
+#else
+	typedef byte* d12_addr_type;
+#endif
+
+#define D12_BASE_ADDR	((d12_addr_type) CYGNUM_DEVS_USB_D12_BASEADDR)
+
+#define D12_ENDP0_SIZE			 16		// Size of Ctrl Endp
+#define D12_MAIN_ENDP			  2		// The D12's "Main" Endp is special, double buffered
+#define D12_MAIN_ENDP_SIZE		 64		// Size of each main endp buffer
+#define D12_MAX_PACKET_SIZE		128		// Max packet is actually double main endp
+
+#define D12_CHIP_ID 0x1012		// Value that's returned by a read of the D12's Chip ID register
+
+// ----- Endpoint Indices -----
+
+enum {
+	D12_ENDP_INVALID = 0xFF,
+	D12_ENDP_MIN = 0,
+
+	D12_RX_CTRL_ENDP = D12_ENDP_MIN,		// Rx/Tx Nomenclature
+	D12_TX_CTRL_ENDP,
+
+	D12_RX_ENDP0 = D12_ENDP_MIN,
+	D12_TX_ENDP0,
+	D12_RX_ENDP1,
+	D12_TX_ENDP1,
+	D12_RX_ENDP2,
+	D12_TX_ENDP2,
+	D12_RX_MAIN_ENDP	= D12_RX_ENDP2,
+	D12_TX_MAIN_ENDP	= D12_TX_ENDP2,
+
+
+	D12_CTRL_ENDP_OUT = D12_ENDP_MIN,		// IN/OUT Nomenclature
+	D12_CTRL_ENDP_IN,
+
+	D12_ENDP0_OUT = D12_ENDP_MIN,
+	D12_ENDP0_IN,
+	D12_ENDP1_OUT,
+	D12_ENDP1_IN,
+	D12_ENDP2_OUT,
+	D12_ENDP2_IN,
+	D12_MAIN_ENDP_OUT	= D12_ENDP2_OUT,
+	D12_MAIN_ENDP_IN	= D12_ENDP2_IN,
+
+	D12_ENDP_INSERT_BEFORE,
+	D12_ENDP_MAX = D12_ENDP_INSERT_BEFORE-1
+};
+
+// ----- Set Mode Reg configuration byte -----
+
+enum {	
+	D12_MODE_CFG_NO_LAZYCLOCK	= 0x02,
+	D12_MODE_CFG_CLOCK_RUNNING	= 0x04,
+	D12_MODE_CFG_INTERRUPT		= 0x08,
+	D12_MODE_CFG_SOFT_CONNECT	= 0x10,
+
+	D12_MODE_CFG_NON_ISO		= 0x00,
+	D12_MODE_CFG_ISO_OUT		= 0x40,
+	D12_MODE_CFG_ISO_IN			= 0x80,
+	D12_MODE_CFG_ISO_IO			= 0xC0,
+
+	D12_MODE_CFG_DFLT = D12_MODE_CFG_NO_LAZYCLOCK |
+							D12_MODE_CFG_CLOCK_RUNNING | D12_MODE_CFG_NON_ISO
+};
+
+// ----- Set Mode Reg clock div factor -----
+
+enum {
+	D12_MODE_CLK_24_MHZ			= 1,
+	D12_MODE_CLK_16_MHZ			= 2,
+	D12_MODE_CLK_12_MHZ			= 3,
+	D12_MODE_CLK_8_MHZ			= 5,
+	D12_MODE_CLK_6_MHZ			= 7,
+	D12_MODE_CLK_4_MHZ			= 11,
+
+	D12_MODE_CLK_DIV_MASK		= 0x0F,
+
+	D12_MODE_CLK_SET_TO_ONE		= 0x40,
+	D12_MODE_CLK_SOF_ONLY_INTR	= 0x80,
+
+	D12_MODE_CLK_DFLT = D12_MODE_CLK_4_MHZ | D12_MODE_CLK_SET_TO_ONE
+};
+
+// ----- Set DMA Register -----
+
+enum {
+	D12_DMA_SINGLE_CYCLE,
+	D12_DMA_BURST_4_CYCLE,
+	D12_DMA_BURST_8_CYCLE,
+	D12_DMA_BURST_16_CYCLE,
+
+	D12_DMA_ENABLE						= 0x04,
+	D12_DMA_DIR_WRITE					= 0x08,
+	D12_DMA_DIR_READ					= 0x00,
+	D12_DMA_AUTO_RELOAD					= 0x10,
+	D12_DMA_INTR_PIN_MODE				= 0x20,
+
+	D12_DMA_MAIN_ENDP_OUT_INTR_ENABLE	= 0x40,
+	D12_DMA_MAIN_RX_ENDP_INTR_ENABLE	= 0x40,
+
+	D12_DMA_MAIN_ENDP_IN_INTR_ENABLE	= 0x80,
+	D12_DMA_MAIN_TX_ENDP_INTR_ENABLE	= 0x80,
+
+	D12_DMA_MAIN_ENDP_INTR_ENABLE		= 0xC0	// Enables IN & OUT Intr
+};
+
+// ----- Interrupt Register Bits -----
+
+enum {
+	D12_INTR_RX_CTRL_ENDP		= 0x0001,
+	D12_INTR_TX_CTRL_ENDP		= 0x0002,
+
+	D12_INTR_RX_ENDP0			= D12_INTR_RX_CTRL_ENDP,
+	D12_INTR_TX_ENDP0			= D12_INTR_TX_CTRL_ENDP,
+	D12_INTR_RX_ENDP1			= 0x0004,
+	D12_INTR_TX_ENDP1			= 0x0008,
+	D12_INTR_RX_ENDP2			= 0x0010,
+	D12_INTR_TX_ENDP2			= 0x0020,
+
+	D12_INTR_BUS_RESET			= 0x0040,
+	D12_INTR_SUSPEND_CHANGE		= 0x0080,
+	D12_INTR_DMA_EOT			= 0x0100
+};
+
+// ----- Read Endpoint Status -----
+
+enum {
+	D12_ENDP_STAT_SETUP_PACKET	= 0x04,
+	D12_ENDP_STAT_BUF0_FULL		= 0x20,
+	D12_ENDP_STAT_BUF1_FULL		= 0x40,
+	D12_ENDP_STAT_ANY_BUF_FULL	= 0x60,
+	D12_ENDP_STAT_BOTH_BUF_FULL	= 0x60,
+	D12_ENDP_STAT_STALL			= 0x80,
+};
+
+// ----- Last Transaction Status Bits -----
+
+enum {
+	D12_LAST_TRANS_DATA_SUCCESS			= 0x01,
+	D12_LAST_TRANS_ERR_CODE_MASK		= 0x1E,
+	D12_LAST_TRANS_SETUP_PACKET			= 0x20,
+	D12_LAST_TRANS_DATA1_PACKET			= 0x40,
+	D12_LAST_TRANS_PREV_STAT_NOT_READ	= 0x80
+};
+
+static const byte RX_ENDP_INDEX[] = { D12_RX_ENDP0, D12_RX_ENDP1, D12_RX_ENDP2 };
+static const byte TX_ENDP_INDEX[] = { D12_TX_ENDP0, D12_TX_ENDP1, D12_TX_ENDP2 };
+
+static const int RX_ENDP_SIZE[] = { 16, 16, 64 };
+static const int TX_ENDP_SIZE[] = { 16, 16, 64 };
+
+typedef void (*completion_fn)(void*, int);
+
+
+#ifndef USB_SETUP_PACKET_LEN 
+	#define USB_SETUP_PACKET_LEN	8
+#endif
+
+// ----- Command Definitions -----
+
+enum {	
+	CMD_SET_ADDR_EN				= 0xD0,		// Write 1 byte
+	CMD_SET_ENDP_EN				= 0xD8,		// Write 1 byte
+	CMD_SET_MODE				= 0xF3,		// Write 2 bytes
+	CMD_SET_DMA					= 0xFB,		// Write/Read 1 byte
+	CMD_READ_INTR_REG			= 0xF4,		// Read 2 bytes
+	CMD_SEL_ENDP				= 0x00,		// (+ Endp Index) Read 1 byte (opt)
+	CMD_READ_LAST_TRANS_STAT	= 0x40,		// (+ Endp Index) Read 1 byte (opt)
+	CMD_READ_ENDP_STAT			= 0x80,		// (+ Endp Index) Read 1 byte
+	CMD_READ_BUF				= 0xF0,		// Read n bytes
+	CMD_WRITE_BUF				= 0xF0,		// Write n bytes
+	CMD_SET_ENDP_STAT			= 0x40,		// (+ Endp Index) Write 1 byte
+	CMD_ACK_SETUP				= 0xF1,		// None
+	CMD_CLEAR_BUF				= 0xF2,		// None
+	CMD_VALIDATE_BUF			= 0xFA,		// None
+	CMD_SEND_RESUME				= 0xF6,		// None
+	CMD_READ_CURR_FRAME_NUM		= 0xF5,		// Read 1 or 2 bytes
+	CMD_READ_CHIP_ID			= 0xFD		// Read 2 bytes
+};
+
+// ----- Set Endpoint Enable Register -----
+
+enum {
+	ENDP_DISABLE,
+	ENDP_ENABLE
+};
+
+// ----- Select Endpoint Results -----
+
+enum {
+	SEL_ENDP_FULL	= 0x01,
+	SEL_ENDP_STALL	= 0x02
+};
+
+// ----- Error Codes from ReadLastTrans (need to be bit shifter) -----
+
+enum {
+	ERROR_NO_ERROR,
+	ERROR_PID_ENCODING,
+	ERROR_PID_UNKNOWN,
+	ERROR_UNEXPECTED_PACKET,
+	ERROR_TOKEN_CRC,
+	ERROR_DATA_CRC,
+	ERROR_TIMEOUT,
+	ERROR_BABBLE,
+	ERROR_UNEXPECTED_EOP,
+	ERROR_NAK,
+	ERROR_PACKET_ON_STALL,
+	ERROR_OVERFLOW,
+	ERROR_BITSTUFF,
+	ERROR_WRONG_DATA_PID
+};
+
+// ------------------------------------------------------------------------
+// Routines to access the D12 registers. The hardware specific driver 
+// provides 8bit access functions and block access functions.
+
+#include CYGIMP_DEVS_USB_D12_HW_ACCESS_HEADER
+
+
+static inline uint16 make_word(byte hi, byte lo)
+{
+	return ((uint16) hi << 8) | lo;
+}
+
+// These routines read or write 16 bit values to the data area.
+
+static inline uint16 d12_read_data_word(d12_addr_type base_addr)
+{
+	uint16 val = d12_read_data_byte(base_addr);
+	val |= ((uint16) d12_read_data_byte(base_addr)) << 8;
+	return val;
+}
+
+static inline void d12_write_data_word(d12_addr_type base_addr, uint16 val)
+{
+	d12_write_data_byte(base_addr, (byte) val);
+	d12_write_data_byte(base_addr, (byte) (val >> 8));
+}
+
+// ------------------------------------------------------------------------
+//							Command & Data I/O
+// ------------------------------------------------------------------------
+//
+//	These routines read & write the registers in the D12. The procedure is
+//	to write a register/command value to the command address (A0=1) then
+//	read or write any required data a byte at a time to the data address
+//	(A0=0). The data can be one byte or two. If two, the low byte is read/
+//	written first.
+
+//	NOTE: These MUST be atomic operations. It's up to the caller 
+//			to insure this.
+
+//      The hardware specific driver provides the basic access function.
+//      
+
+static inline void d12_write_byte(d12_addr_type base_addr, byte cmd, byte val)
+{
+	d12_write_cmd(base_addr, cmd);
+	d12_write_data_byte(base_addr, val);
+}
+
+static inline void d12_write_word(d12_addr_type base_addr, byte cmd, uint16 val)
+{
+	d12_write_cmd(base_addr, cmd);
+	d12_write_data_word(base_addr, val);
+}
+
+static inline byte d12_read_byte(d12_addr_type base_addr, byte cmd)
+{
+	d12_write_cmd(base_addr, cmd);
+	return d12_read_data_byte(base_addr);
+}
+
+static inline uint16 d12_read_word(d12_addr_type base_addr, byte cmd)
+{
+	d12_write_cmd(base_addr, cmd);
+	return d12_read_data_word(base_addr);
+}
+
+
+
+// ------------------------------------------------------------------------
+//							Higher Level Commands
+// ------------------------------------------------------------------------
+
+// 	Stalls or Unstalls the endpoint. Bit0=1 for stall, =0 to unstall.
+
+static inline void d12_set_endp_status(d12_addr_type base_addr, byte endp_idx, byte stat)
+{
+	d12_write_byte(base_addr, CMD_SET_ENDP_STAT + endp_idx, stat);
+}
+
+// ------------------------------------------------------------------------
+// Stalls the control endpoint (both in & out).
+
+static void d12_stall_ctrl_endp(d12_addr_type base_addr, bool stall)
+{
+	d12_set_endp_status(base_addr, D12_TX_CTRL_ENDP, stall ? 1 : 0);
+	d12_set_endp_status(base_addr, D12_RX_CTRL_ENDP, stall ? 1 : 0);
+}
+
+// ------------------------------------------------------------------------
+// Stalls/unstalls the specified endpoint. 
+
+void inline d12_stall_endp(d12_addr_type base_addr, byte endp_idx, bool stall)
+{
+	d12_set_endp_status(base_addr, endp_idx, stall ? 1 : 0);
+}
+
+// ------------------------------------------------------------------------ */
+// Tells the chip that the selected endpoint buffer has been completely
+// read. This should be called after the application reads all the data
+// from an endpoint.  While there's data in the buffer the chip will 
+// automatically NAK any additional OUT packets from the host. 
+
+static inline void d12_clear_buffer(d12_addr_type base_addr)
+{
+	d12_write_cmd(base_addr, CMD_CLEAR_BUF);
+}
+
+// ------------------------------------------------------------------------
+// Tells the chip that the data in the selected endpoint buffer is complete
+// and ready to be sent to the host.
+
+static inline void d12_validate_buffer(d12_addr_type base_addr)
+{
+	d12_write_cmd(base_addr, CMD_VALIDATE_BUF);
+}
+
+// ------------------------------------------------------------------------
+// Sends an upstream resume signal for 10ms. This command is normally 
+// issued when the device is in suspend.
+
+static inline void d12_send_resume(d12_addr_type base_addr)
+{
+	d12_write_cmd(base_addr, CMD_SEND_RESUME);
+}
+
+// ------------------------------------------------------------------------
+// Gets the frame number of the last successfully received 
+// start-of-frame (SOF).
+
+static inline uint16 d12_read_curr_frame_num(d12_addr_type base_addr)
+{
+	return d12_read_word(base_addr, CMD_READ_CURR_FRAME_NUM);
+}
+
+// ------------------------------------------------------------------------
+// This routine acknowledges a setup packet by writing an Ack Setup command
+// to the currently selected Endpoint. This must be done for both EP0 out
+// and EP0 IN whenever a setup packet is received.
+
+static inline void d12_ack_setup(d12_addr_type base_addr)
+{
+	d12_write_cmd(base_addr, CMD_ACK_SETUP);
+}
+
+// ------------------------------------------------------------------------
+// Gets the value of the 16-bit interrupt register, which indicates the 
+// source of an interrupt (if interrupts are not used, this reg can be 
+// polled to find when service is required).
+
+static inline uint16 d12_read_intr_reg(d12_addr_type base_addr)
+{
+	return d12_read_word(base_addr, CMD_READ_INTR_REG) & 0x01FF;
+}
+
+// ------------------------------------------------------------------------
+// Gets/Sets the contents of the DMA register.
+
+static inline byte d12_get_dma(d12_addr_type base_addr)
+{
+	return d12_read_byte(base_addr, CMD_SET_DMA);
+}
+
+static inline void d12_set_dma(d12_addr_type base_addr, byte mode)
+{
+	d12_write_byte(base_addr, CMD_SET_DMA, mode);
+}
+
+// ------------------------------------------------------------------------
+// Sends the "Select Endpoint" command (0x00 - 0x0D) to the chip.
+// This command initializes an internal pointer to the start of the 
+// selected buffer.
+// 
+// Returns: Bitfield containing status of the endpoint
+
+byte d12_select_endp(d12_addr_type base_addr, byte endp_idx)
+{
+	return d12_read_byte(base_addr, CMD_SEL_ENDP + endp_idx);
+}
+
+// ------------------------------------------------------------------------
+// Gets the status of the last transaction of the endpoint. It also resets 
+// the corresponding interrupt flag in the interrupt register, and clears 
+// the status, indicating that it was read.
+//
+// Returns: Bitfield containing the last transaction status.
+
+static inline byte d12_read_last_trans_status(d12_addr_type base_addr, byte endp_idx)
+{
+	return d12_read_byte(base_addr, CMD_READ_LAST_TRANS_STAT + endp_idx);
+}
+
+// ------------------------------------------------------------------------
+// Reads the status of the requested endpoint. 
+// Just for the heck of it, we mask off the reserved bits.
+//
+// Returns: Bitfield containing the endpoint status.
+
+static inline byte d12_read_endp_status(d12_addr_type base_addr, byte endp_idx)
+{
+	return d12_read_byte(base_addr, CMD_READ_ENDP_STAT + endp_idx) & 0xE4;
+}
+
+// ------------------------------------------------------------------------
+// Returns true if there is data available in the specified endpoint's
+// ram buffer. This is determined by the buf full flags in the endp status
+// register.
+
+static inline bool d12_data_available(d12_addr_type base_addr, byte endp_idx)
+{
+	byte by = d12_read_endp_status(base_addr, endp_idx);
+	return (bool) (by & D12_ENDP_STAT_ANY_BUF_FULL);
+}
+
+// ------------------------------------------------------------------------
+// Clears the transaction status for each of the endpoints by calling the
+// d12_read_last_trans_status() function for each. 
+
+static void d12_clear_all_intr(d12_addr_type base_addr)
+{
+	uint8 endp;
+
+	d12_read_intr_reg(base_addr);
+
+	for (endp=D12_ENDP_MIN; endp<=D12_ENDP_MAX; ++endp)
+		d12_read_last_trans_status(base_addr, endp);
+}
+
+// ------------------------------------------------------------------------
+// Loads a value into the Set Address / Enable register. This sets the 
+// device's USB address (lower 7 bits) and enables/disables the function
+// (msb).
+
+static void d12_set_addr_enable(d12_addr_type base_addr, byte usb_addr, bool enable)
+{
+	if (enable) 
+		usb_addr |= 0x80;
+
+	d12_write_byte(base_addr, CMD_SET_ADDR_EN, usb_addr);
+}
+
+// ------------------------------------------------------------------------
+// Enables/disables the generic endpoints.
+
+static inline void d12_set_endp_enable(d12_addr_type base_addr, bool enable)
+{
+	d12_write_byte(base_addr, CMD_SET_ENDP_EN, (enable) ? ENDP_ENABLE : ENDP_DISABLE);
+}
+
+// ------------------------------------------------------------------------
+// Sets the device's configuration and CLKOUT frequency.
+
+static void d12_set_mode(d12_addr_type base_addr, byte config, byte clk_div)
+{
+	uint16 w = make_word(clk_div, config);
+	d12_write_word(base_addr, CMD_SET_MODE, w);
+}
+
+// ------------------------------------------------------------------------
+// Reads a setup packet from the control endpoint. This procedure is 
+// somewhat different than reading a data packet. By the USB standard, a 
+// setup packet can not be NAK'ed or STALL'ed, so when the chip receives a 
+// setup packet, it flushes the Ctrl (EP0) IN buffer and disables the 
+// Validate and Clear Buffer commands. The processor must send an 
+// acknowledge setup to both EP0 IN and OUT before a Validate or Clear
+// Buffer command is effective.
+//
+// Parameters:
+//     	buf		buffer to receive the contents of the setup packet. Must
+//     			be at least 8 bytes.
+// Returns:
+//     	true	if there are 8 bytes waiting in the EP0 OUT RAM buffer
+//     			on the D12 (i.e., true if successful)
+//     	false	otherwise
+
+static bool d12_read_setup_packet(d12_addr_type base_addr, byte *buf)
+{
+	uint8 n;
+
+	d12_select_endp(base_addr, D12_RX_CTRL_ENDP);
+
+	d12_read_byte(base_addr, CMD_READ_BUF);			// Read & discard reserved byte
+	n = d12_read_data_byte(base_addr);				// # bytes available
+
+	if (n > USB_SETUP_PACKET_LEN) {
+		//TRACE("* Warning: Setup Packet too large: %u *\n", (unsigned) n);
+		n = USB_SETUP_PACKET_LEN;
+	}
+
+	n = d12_read_data(base_addr, buf, n);
+
+	d12_ack_setup(base_addr);
+	d12_clear_buffer(base_addr);
+
+	// ----- Ack Setup to EP0 IN ------
+
+	d12_select_endp(base_addr, D12_TX_CTRL_ENDP);
+	d12_ack_setup(base_addr);
+
+	return n == USB_SETUP_PACKET_LEN;
+}
+
+// ------------------------------------------------------------------------
+// Reads the contents of the currently selected endpoint's RAM buffer into 
+// the buf[] array.
+//
+// The D12's buffer comes in as follows:
+//		[0]	junk ("reserved" - can be anything). Just disregard
+//		[1]	# data bytes to follow
+//	   	[2] data byte 0, ...
+//	up to
+//		[N+2] data byte N-1
+//
+// Parameters:
+//     	buf		byte array to receive data. This MUST be at least the size
+//     			of the chip's RAM buffer for the currently selected endpoint.
+//     			If buf is NULL, the data is read & discarded.
+//
+// Returns: the actual number of bytes read (could be <= n)
+
+static uint8 d12_read_selected_endp_buf(d12_addr_type base_addr, byte *buf)
+{
+	uint8 n;
+
+	d12_read_byte(base_addr, CMD_READ_BUF);		// Read & discard reserved byte
+	n = d12_read_data_byte(base_addr);			// # bytes in chip's buf
+	d12_read_data(base_addr, buf, n);
+	d12_clear_buffer(base_addr);
+
+	return n;
+}
+
+// ------------------------------------------------------------------------
+// Selects the specified endpoint and reads the contents of it's RAM buffer
+// into the buf[] array. For the Main OUT endpoint, it will check whether 
+// both buffers are full, and if so, read them both.
+//
+// Side Effects:
+//		- Leaves endp_idx as the currently selected endpoint.
+//
+// Parameters:
+//    	endp_idx	the endpoint from which to read
+//     	buf			buffer to receive the data. This MUST be at least the size
+//					of the chip's RAM buffer for the specified endpoint.
+//       			For the Main endp, it must be 2x the buffer size (128 total)
+//
+// Returns: the # of bytes read.
+
+static uint8 d12_read_endp_buf(d12_addr_type base_addr, byte endp_idx, byte *buf)
+{
+	return (d12_select_endp(base_addr, endp_idx) & SEL_ENDP_FULL)
+						? d12_read_selected_endp_buf(base_addr, buf) : 0;
+}
+
+// ------------------------------------------------------------------------
+// Does a read of the "main" endpoint (#2). Since it's double buffered,
+// this will check if both buffers are full, and if so it will read them
+// both. Thus the caller's buffer, buf, must be large enough to hold all
+// the data - 128 bytes total.
+// 
+// If either buffer contains less than the full amount, the done flag
+// is set indicating that a Bulk OUT transfer is complete.
+// 
+// This determines if a bulk transfer is done, since the caller can't 
+// necessarily determine this from the size of the return buffer.
+// If either buffer is less than full, '*done' is set to a non-zero value.
+
+static uint8 d12_read_main_endp_buf(d12_addr_type base_addr, byte *buf, int *done)
+{
+	int		nBuf = 1;
+	uint8	n = 0;
+	byte	stat = d12_read_endp_status(base_addr, D12_RX_MAIN_ENDP) & 
+												D12_ENDP_STAT_ANY_BUF_FULL;
+
+	if (stat == 0)
+		return 0;
+
+	if (stat == D12_ENDP_STAT_BOTH_BUF_FULL)
+		nBuf++;
+
+	*done = false;
+
+	while (nBuf--) {
+		if (d12_select_endp(base_addr, D12_RX_MAIN_ENDP) & SEL_ENDP_FULL) {
+			uint8 n1 = d12_read_selected_endp_buf(base_addr, buf+n);
+			n += n1;
+			if (n1 < D12_MAIN_ENDP_SIZE) {
+				*done = true;
+				break;
+			}
+		}
+		else
+			*done = true;
+	}
+	return n;
+}
+
+// ------------------------------------------------------------------------
+// Writes the contents of the buf[] array to the currently selected 
+// endpoint's RAM buffer. The host will get the data on the on the next IN
+// packet from the endpoint.
+//
+// Note:
+//     	- The length of the buffer, n, must be no more than the size of the
+//     	endpoint's RAM space, though, currently, this is not checked.
+//     	- It's feasible that the application needs to send an empty (NULL) 
+//     	packet. It's valid for 'n' to be zero, and/or buf NULL.
+
+static uint8 d12_write_selected_endp_buf(d12_addr_type base_addr, const byte *buf, uint8 n)
+{
+	d12_write_byte(base_addr, CMD_WRITE_BUF, 0);
+	d12_write_data_byte(base_addr, n);
+	d12_write_data(base_addr, buf, n);
+	d12_validate_buffer(base_addr);
+
+	return n;
+}
+
+// ------------------------------------------------------------------------
+// Writes the contents of the buf[] array to the specified endoint's RAM
+// buffer. The host will get this data on the next IN packet from the 
+// endpoint.
+//
+// Side Effects:
+//     	- Leaves endp_idx as the currently selected endpoint.
+
+static uint8 d12_write_endp_buf(d12_addr_type base_addr, byte endp_idx, 
+											const byte *buf, uint8 n)
+{
+	d12_select_endp(base_addr, endp_idx);
+	return d12_write_selected_endp_buf(base_addr, buf, n);
+}
+
+// ------------------------------------------------------------------------
+// Reads & returns the contents of the Chip ID register.
+
+static inline uint16 d12_read_chip_id(d12_addr_type base_addr)
+{
+	return d12_read_word(base_addr, CMD_READ_CHIP_ID);
+}
+
+
+// ==========================================================================
+//						eCos-Specific Device Driver Code
+// ==========================================================================
+
+static void usbs_d12_reset(void);
+
+// Make some abbreviations for the configuration options.
+
+#if defined(CYGPKG_DEVS_USB_D12_RX_EP1)
+	#define _RX_EP1
+#endif
+
+#if defined(CYGPKG_DEVS_USB_D12_TX_EP1)
+	#define _TX_EP1
+#endif
+
+#if defined(CYGPKG_DEVS_USB_D12_RX_EP2)
+	#define _RX_EP2
+#endif
+
+#if defined(CYGPKG_DEVS_USB_D12_TX_EP2)
+	#define _TX_EP2
+#endif
+
+// --------------------------------------------------------------------------
+//								Endpoint 0 Data
+// --------------------------------------------------------------------------
+
+static cyg_interrupt	usbs_d12_intr_data;
+static cyg_handle_t		usbs_d12_intr_handle;
+
+static byte ep0_tx_buffer[CYGNUM_DEVS_USB_D12_EP0_TXBUFSIZE];
+
+static void usbs_d12_start(usbs_control_endpoint*);
+static void usbs_d12_poll(usbs_control_endpoint*);
+
+typedef enum endp_state {
+	ENDP_STATE_IDLE,
+	ENDP_STATE_IN,
+	ENDP_STATE_OUT
+} endp_state;
+
+typedef struct ep0_impl {
+	usbs_control_endpoint	common;
+	endp_state				ep_state;
+	int                     length;
+	int                     transmitted;
+	bool					tx_empty;
+} ep0_impl;
+
+static ep0_impl ep0 = {
+	common:
+	{
+		state:                  USBS_STATE_POWERED,
+		enumeration_data:       (usbs_enumeration_data*) 0,
+		start_fn:               &usbs_d12_start,
+		poll_fn:                &usbs_d12_poll,
+		interrupt_vector:       CYGNUM_DEVS_USB_D12_IRQ,
+		control_buffer:         { 0, 0, 0, 0, 0, 0, 0, 0 },
+		state_change_fn:        0,
+		state_change_data:      0,
+		standard_control_fn:    0,
+		standard_control_data:  0,
+		class_control_fn:       0,
+		class_control_data:     0,
+		vendor_control_fn:      0,
+		vendor_control_data:    0,
+		reserved_control_fn:    0,
+		reserved_control_data:  0,
+		buffer:                 0,
+		buffer_size:            0,
+		fill_buffer_fn:         0,
+		fill_data:              0,
+		fill_index:             0,
+		complete_fn:            0
+	},
+	ep_state:		ENDP_STATE_IDLE,
+	length:			0,
+	transmitted:	0,
+	tx_empty:		0
+};
+
+extern usbs_control_endpoint usbs_d12_ep0 __attribute__((alias ("ep0")));
+
+
+// --------------------------------------------------------------------------
+//						Rx Endpoints 1 & 2 Data
+// --------------------------------------------------------------------------
+
+#if	defined(_RX_EP1) || defined(_RX_EP2)
+
+typedef struct rx_endpoint {
+	usbs_rx_endpoint	common;
+	int					endp,
+						received;
+}
+	rx_endpoint;
+
+static void usbs_d12_api_start_rx_ep(usbs_rx_endpoint*);
+static void usbs_d12_api_stall_rx_ep(usbs_rx_endpoint*, cyg_bool);
+
+static void usbs_d12_ep_rx_complete(rx_endpoint *ep, int result);
+static void usbs_d12_stall_rx_ep(rx_endpoint*, cyg_bool);
+
+#endif
+
+
+#if	defined(_RX_EP1)
+
+static rx_endpoint rx_ep1 = {
+	common: {
+		start_rx_fn:	&usbs_d12_api_start_rx_ep,
+		set_halted_fn:	&usbs_d12_api_stall_rx_ep,
+        halted:			0
+    },
+	endp:	1
+};
+
+extern usbs_rx_endpoint usbs_d12_rx_ep1 __attribute__((alias ("rx_ep1")));
+
+#endif
+
+
+#if	defined(_RX_EP2)
+
+static rx_endpoint rx_ep2 = {
+	common: {
+		start_rx_fn:	&usbs_d12_api_start_rx_ep,
+		set_halted_fn:	&usbs_d12_api_stall_rx_ep,
+		halted:			0
+    },
+	endp:	2
+};
+
+extern usbs_rx_endpoint usbs_d12_rx_ep2 __attribute__((alias ("rx_ep2")));
+
+#endif
+
+// --------------------------------------------------------------------------
+//						Tx Endpoints 1 & 2 Data
+// --------------------------------------------------------------------------
+
+#if	defined(_TX_EP1) || defined(_TX_EP2)
+
+typedef struct tx_endpoint {
+	usbs_tx_endpoint	common;
+	int					endp,
+						transmitted;
+	bool				tx_empty;
+}
+	tx_endpoint;
+
+static void usbs_d12_api_start_tx_ep(usbs_tx_endpoint*);
+static void usbs_d12_api_stall_tx_ep(usbs_tx_endpoint*, cyg_bool);
+
+static void usbs_d12_ep_tx_complete(tx_endpoint *ep, int result);
+static void usbs_d12_stall_tx_ep(tx_endpoint*, cyg_bool);
+
+#endif
+
+
+#if	defined(_TX_EP1)
+
+static tx_endpoint tx_ep1 = {
+	common: {
+		start_tx_fn:	&usbs_d12_api_start_tx_ep,
+		set_halted_fn:	&usbs_d12_api_stall_tx_ep,
+        halted:			0
+    },
+	endp:	1
+};
+
+extern usbs_tx_endpoint usbs_d12_tx_ep1 __attribute__((alias ("tx_ep1")));
+
+#endif
+
+
+#if	defined(_TX_EP2)
+
+static tx_endpoint tx_ep2 = {
+	common: {
+		start_tx_fn:	&usbs_d12_api_start_tx_ep,
+		set_halted_fn:	&usbs_d12_api_stall_tx_ep,
+		halted:			0
+    },
+	endp:	2
+};
+
+extern usbs_tx_endpoint usbs_d12_tx_ep2 __attribute__((alias ("tx_ep2")));
+
+
+#endif
+
+// --------------------------------------------------------------------------
+// Synchronization
+
+static inline void usbs_d12_lock(void)		{ cyg_scheduler_lock(); }
+static inline void usbs_d12_unlock(void)	{ cyg_scheduler_unlock(); }
+
+// --------------------------------------------------------------------------
+//								Control Endpoint
+// --------------------------------------------------------------------------
+
+// Fills the EP0 transmit buffer with a packet. Partial data packets are 
+// retrieved by repeatedly calling the fill function.
+
+static int ep0_fill_tx_buffer(void)
+{
+    int nFilled = 0;
+
+	while (nFilled < CYGNUM_DEVS_USB_D12_EP0_TXBUFSIZE) {
+        if (ep0.common.buffer_size != 0) {
+            if ((nFilled + ep0.common.buffer_size) < 
+						CYGNUM_DEVS_USB_D12_EP0_TXBUFSIZE) {
+                memcpy(&ep0_tx_buffer[nFilled], ep0.common.buffer, 
+                			ep0.common.buffer_size);
+                nFilled += ep0.common.buffer_size;
+                ep0.common.buffer_size = 0;
+            }
+            else {
+                break;
+            }
+        }
+        else if (ep0.common.fill_buffer_fn) {
+            (*ep0.common.fill_buffer_fn)(&ep0.common);
+        }
+        else {
+            break;
+        }
+    }
+    CYG_ASSERT((ep0.common.buffer_size == 0) && (!ep0.common.fill_buffer_fn), 
+    					"EP0 transmit buffer overflow");
+	TRACE_D12("EP0: Filled Tx Buf with %d bytes\n", nFilled);
+
+	ep0.length = nFilled;
+
+	ep0.common.fill_buffer_fn	= 0;
+	ep0.common.fill_data		= 0;
+	ep0.common.fill_index		= 0;
+
+    return nFilled;
+}
+
+// --------------------------------------------------------------------------
+// Called when a transfer is complete on the control endpoint EP0. 
+// It resets the endpoint's data structure and calls the completion function,
+// if any.
+//
+// PARAMETERS:
+//		result		0, on success
+//					-EPIPE or -EIO to indicate a cancellation
+
+static usbs_control_return usbs_d12_ep0_complete(int result)
+{
+	usbs_control_return ret = USBS_CONTROL_RETURN_UNKNOWN;
+
+    ep0.ep_state = ENDP_STATE_IDLE;
+
+	if (ep0.common.complete_fn)
+		ret = (*ep0.common.complete_fn)(&ep0.common, result);
+
+	ep0.common.buffer			= 0;
+	ep0.common.buffer_size		= 0;
+	ep0.common.complete_fn		= 0;
+	//ep0.common.fill_buffer_fn	= 0;
+
+	return ret;
+}
+
+// --------------------------------------------------------------------------
+// This routine is called when we want to send the next packet to the tx ep0
+// on the chip. It is used to start a new transfer, and is also called when
+// the chip interrupts to indicate that the ep0 tx buffer is empty and ready
+// to receive a new packet.
+//
+// NOTE:
+//		On the D12, when you send a zero-length packet to a tx endpoint, the
+// 		chip transmits the empty packet to the host, but doesn't interrupt 
+//		indicating that it is complete. So immediately after sending the
+//		empty packet we complete the transfer.
+
+static void usbs_d12_ep0_tx(void)
+{
+	int		nRemaining = ep0.length - ep0.transmitted;
+	uint8	n;
+
+	// ----- Intermittent interrupt? Get out -----
+
+	if (!ep0.common.buffer) {
+		TRACE_D12("EP0: Tx no buffer (%d)\n", nRemaining);
+		return;
+	}
+
+	// ----- If prev packet was last, signal that we're done -----
+
+	if (nRemaining == 0 && !ep0.tx_empty) {
+		TRACE_D12("\tEP0: Tx Complete (%d) %p\n", ep0.transmitted, 
+											ep0.common.complete_fn);
+		usbs_d12_ep0_complete(0);
+		return;
+	}
+
+	// ----- Load the next tx packet onto the chip -----
+
+	if (nRemaining < D12_ENDP0_SIZE) {
+		n = (uint8) nRemaining;
+		ep0.tx_empty = false;
+	}
+	else
+		n = D12_ENDP0_SIZE;
+
+	d12_write_endp_buf(D12_BASE_ADDR, D12_TX_ENDP0, 
+					   &ep0_tx_buffer[ep0.transmitted], n);
+
+	TRACE_D12("EP0: Wrote %u bytes\n", (unsigned) n);
+	TRACE_BUF0("\t", &ep0_tx_buffer[ep0.transmitted], n);
+
+	ep0.transmitted += n;
+
+	// ----- If empty packet, D12 won't interrupt, so end now ----- */
+
+	if (n == 0) {
+		TRACE_D12("\tEP0: Tx Complete (%d) %p\n", ep0.transmitted, 
+											ep0.common.complete_fn);
+		usbs_d12_ep0_complete(0);
+	}
+}
+
+// --------------------------------------------------------------------------
+// This function is called when a packet has been successfully sent on the
+// primary control endpoint (ep0). It indicates that the chip is ready for 
+// another packet. We read the LastTransStatus for the endpoint to clear 
+// the interrupt bit, then call ep0_tx() to continue the transfer.
+
+static void usbs_d12_ep0_tx_intr(void)
+{
+	d12_read_last_trans_status(D12_BASE_ADDR, D12_TX_ENDP0);
+	usbs_d12_ep0_tx();
+}
+
+// --------------------------------------------------------------------------
+// Try to handle standard requests. This is a three step process:
+//	1.	If it's something we should handle internally we take care of it.
+//		Currently we can handle SET_ADDRESS requests, and a few others.
+//	2.	If the upper level code has installed a standard control handler
+//		we let that function have a crack at it.
+//	3.	If neither of those handle the packet we let 
+//		usbs_handle_standard_control() have a last try at it.
+//
+// Locally:
+// 		SET_ADDRESS: The host is demanding that we change our USB address.
+//		This is done by updating the Address/Enable register on the D12. 
+//		Note, however that the USB protocol requires us to ack at the old 
+//		address, change address, and then accept the next control message
+//		at the new 	address. The D12 address reg is buffered to do this 
+//		automatically for us. The updated address on the chip won't take
+//		affect until after the empty ack is sent. Nice.
+//
+
+static usbs_control_return usbs_d12_handle_std_req(usb_devreq *req)
+{
+	usbs_control_return result = USBS_CONTROL_RETURN_UNKNOWN;
+	int recipient = req->type & USB_DEVREQ_RECIPIENT_MASK;
+
+	if (req->request == USB_DEVREQ_SET_ADDRESS) {
+		TRACE_D12("Setting Addr: %u\n", (unsigned) req->value_lo);
+		d12_set_addr_enable(D12_BASE_ADDR, req->value_lo, true);
+		result = USBS_CONTROL_RETURN_HANDLED;
+	}
+	else if (req->request == USB_DEVREQ_GET_STATUS) {
+		if (recipient == USB_DEVREQ_RECIPIENT_DEVICE) {
+			const usbs_enumeration_data *enum_data = ep0.common.enumeration_data;
+			if (enum_data && enum_data->device.number_configurations == 1 &&
+					enum_data->configurations) {
+				ep0.common.control_buffer[0]  = (enum_data->configurations[0].attributes
+							 & USB_CONFIGURATION_DESCRIPTOR_ATTR_SELF_POWERED) ? 1 : 0;
+				ep0.common.control_buffer[0] |= (enum_data->configurations[0].attributes
+							& USB_CONFIGURATION_DESCRIPTOR_ATTR_REMOTE_WAKEUP) ? 2 : 0;
+				ep0.common.control_buffer[1] = 0;
+				result = USBS_CONTROL_RETURN_HANDLED;
+			}
+		}
+		else if (recipient == USB_DEVREQ_RECIPIENT_ENDPOINT) {
+    		bool halted = false;
+			result = USBS_CONTROL_RETURN_HANDLED;
+
+			switch (req->index_lo) {
+    			#if defined(_RX_EP1)
+    				case 0x01 :	halted = rx_ep1.common.halted;  break;
+    			#endif
+    			#if defined(_TX_EP1)
+    				case 0x81 : halted = tx_ep1.common.halted;	break;
+    			#endif
+    			#if defined(_RX_EP2)
+    				case 0x02 :	halted = rx_ep2.common.halted;  break;
+    			#endif
+    			#if defined(_TX_EP2)
+    				case 0x82 : halted = tx_ep2.common.halted;	break;
+    			#endif
+
+    			default:
+    				result = USBS_CONTROL_RETURN_STALL;
+    		}
+
+    		TRACE_D12("Get Status: Endp [0x%02X] %s\n", (unsigned) req->index_lo, 
+    												halted ? "Halt" : "Unhalt");
+			if (result == USBS_CONTROL_RETURN_HANDLED) {
+				ep0.common.control_buffer[0] = (halted) ? 1 : 0;
+				ep0.common.control_buffer[1] = 0;
+			}
+		}
+
+		if (result == USBS_CONTROL_RETURN_HANDLED) {
+			ep0.common.buffer				= ep0.common.control_buffer;
+			ep0.common.buffer_size			= 2;
+			ep0.common.fill_buffer_fn		= 0;
+			ep0.common.complete_fn			= 0;
+		}
+	}
+	else if ((req->request == USB_DEVREQ_SET_FEATURE || 
+			  req->request == USB_DEVREQ_CLEAR_FEATURE) && 
+			  recipient == USB_DEVREQ_RECIPIENT_ENDPOINT) {
+
+		bool halt = (req->request == USB_DEVREQ_SET_FEATURE);
+		result = USBS_CONTROL_RETURN_HANDLED;
+		TRACE_D12("Endpoint [0x%02X] %s\n", (unsigned) req->index_lo, halt ? "Halt" : "Unhalt");
+
+		switch (req->index_lo) {
+			#if defined(_RX_EP1)
+				case 0x01 :	usbs_d12_stall_rx_ep(&rx_ep1, halt);	break;
+			#endif
+			#if defined(_TX_EP1)
+				case 0x81 : usbs_d12_stall_tx_ep(&tx_ep1, halt);	break;
+			#endif
+			#if defined(_RX_EP2)
+				case 0x02 :	usbs_d12_stall_rx_ep(&rx_ep2, halt);	break;
+			#endif
+			#if defined(_TX_EP2)
+				case 0x82 : usbs_d12_stall_tx_ep(&tx_ep2, halt);	break;
+			#endif
+
+			default:
+				result = USBS_CONTROL_RETURN_STALL;
+		}
+
+	}
+	else if (ep0.common.standard_control_fn != 0) {
+		result = (*ep0.common.standard_control_fn)(&ep0.common,
+  									ep0.common.standard_control_data);
+	}
+
+	if (result == USBS_CONTROL_RETURN_UNKNOWN)
+		result = usbs_handle_standard_control(&ep0.common);
+
+	return result;
+}
+
+// --------------------------------------------------------------------------
+// Handler for the receipt of a setup (dev request) packet from the host.
+// We examine the packet to determine what function(s) should get a crack
+// at trying to handle it, then pass control to the proper function. If
+// the function handles the message we either ACK (len==0) or prepare for
+// an IN or OUT data phase. If no one handled the message, we stall the
+// control endpoint.
+
+static void usbs_d12_ep0_setup_packet(usb_devreq* req)
+{
+	int		len, dir, 
+			protocol, recipient;
+
+	usbs_control_return	result = USBS_CONTROL_RETURN_UNKNOWN;
+
+	// ----- See who should take the request -----
+
+	len = make_word(req->length_hi, req->length_lo);
+
+	dir			= req->type & USB_DEVREQ_DIRECTION_MASK;
+	protocol    = req->type & USB_DEVREQ_TYPE_MASK;
+	recipient   = req->type & USB_DEVREQ_RECIPIENT_MASK;
+
+	TRACE_BUF0("DevReq: ", ep0.common.control_buffer, sizeof(usb_devreq));
+
+	if (protocol == USB_DEVREQ_TYPE_STANDARD)
+		result = usbs_d12_handle_std_req(req);
+	else {
+		// Pass on non-standard requests to registered handlers
+
+		usbs_control_return	(*callback_fn)(usbs_control_endpoint*, void*);
+		void *callback_arg;
+
+		if (protocol == USB_DEVREQ_TYPE_CLASS) {
+			callback_fn  = ep0.common.class_control_fn;
+			callback_arg = ep0.common.class_control_data;
+		}
+		else if (protocol == USB_DEVREQ_TYPE_VENDOR) {
+			callback_fn  = ep0.common.vendor_control_fn;
+			callback_arg = ep0.common.vendor_control_data;
+		}
+		else {
+			callback_fn  = ep0.common.reserved_control_fn;
+			callback_arg = ep0.common.reserved_control_data;
+		}
+
+		result = (callback_fn)	? (*callback_fn)(&ep0.common, callback_arg)
+								: USBS_CONTROL_RETURN_STALL;
+	}
+
+	// ----- If handled prep/handle data phase, otherwise stall -----
+
+	if (result == USBS_CONTROL_RETURN_HANDLED) {
+		if (len == 0) {
+			TRACE_D12("\tCtrl ACK\n");
+			d12_write_endp_buf(D12_BASE_ADDR, D12_TX_ENDP0, 0, 0);
+		}
+		else {
+			// Set EP0 state to  IN or OUT mode for data phase
+			ep0.transmitted = 0;
+			ep0.length = len;
+
+			if (dir == USB_DEVREQ_DIRECTION_OUT) {
+				// Wait for the next packet from the host.
+				ep0.ep_state = ENDP_STATE_OUT;
+				CYG_ASSERT(ep0.common.buffer != 0, 
+						   "A rx buffer should have been provided for EP0");
+				CYG_ASSERT(ep0.common.complete_fn != 0, 
+						   "A completion function should be provided for EP0 OUT control messages");
+			}
+			else {
+				ep0.tx_empty = true;
+				ep0.ep_state = ENDP_STATE_IN;
+				ep0_fill_tx_buffer();
+				usbs_d12_ep0_tx();
+			}
+		}
+	}
+	else {
+		TRACE_D12("\t*** Unhandled Device Request ***\n");
+		// The request wasn't handled, so stall control endpoint
+		d12_stall_ctrl_endp(D12_BASE_ADDR, true);
+	}
+}
+
+// --------------------------------------------------------------------------
+// This is called when the chip indicates that a packet has been received
+// on control endpoint 0. If it's a setup packet, we handle it accordingly,
+// otherwise it's a data packet coming in on ep0.
+//
+
+static void usbs_d12_ep0_rx_intr(void)
+{
+	byte byStat = d12_read_last_trans_status(D12_BASE_ADDR, D12_RX_ENDP0);
+	TRACE_D12("\tEP0 Status: 0x%02X\n", (unsigned) byStat);
+
+	if (byStat & D12_LAST_TRANS_SETUP_PACKET) {
+		usb_devreq *req = (usb_devreq *) ep0.common.control_buffer;
+		
+		if (!d12_read_setup_packet(D12_BASE_ADDR, (byte*) req)) {
+			TRACE_D12("ep0_rx_dsr: Error reading setup packet\n");
+			d12_stall_ctrl_endp(D12_BASE_ADDR, true);
+		}
+		else
+			usbs_d12_ep0_setup_packet(req);
+	}
+	else {
+		if (ep0.common.buffer) {
+			uint8 n = d12_read_endp_buf(D12_BASE_ADDR, D12_RX_ENDP0, 
+										ep0.common.buffer + ep0.transmitted);
+			ep0.transmitted += n;
+
+			TRACE_D12("EP0: Received %d bytes\n", (unsigned) n);
+
+			if (n < D12_ENDP0_SIZE || 
+					ep0.common.buffer_size - ep0.transmitted < D12_ENDP0_SIZE) {
+				TRACE_D12("\tEP0: Rx Complete (%d) %p\n", 
+						  ep0.transmitted, ep0.common.complete_fn);
+
+	            if (usbs_d12_ep0_complete(0) == USBS_CONTROL_RETURN_HANDLED)
+					d12_write_endp_buf(D12_BASE_ADDR, D12_TX_ENDP0, 0, 0);
+	            else
+					d12_stall_ctrl_endp(D12_BASE_ADDR, true);
+			}
+		}
+		else {
+			TRACE_D12("EP0: No Rx buffer. Discarding packet\n");
+			d12_read_endp_buf(D12_BASE_ADDR, D12_RX_ENDP0, NULL);
+		}
+	}
+}
+
+// --------------------------------------------------------------------------
+// Handler for when the device is put into or taken out of suspend mode.
+// It updates the state variable in the control endpoint and calls the
+// registered state change function, if any.
+
+// TODO: Put the chip into low power mode??? Stop clocks, etc???
+
+static void usbs_d12_suspend(bool suspended)
+{
+	int 				old_state = ep0.common.state;
+	usbs_state_change	state_change;
+
+	if (suspended) {
+		ep0.common.state |= USBS_STATE_SUSPENDED;
+		state_change = USBS_STATE_CHANGE_SUSPENDED;
+	}
+	else {
+		ep0.common.state &= USBS_STATE_MASK;
+		state_change = USBS_STATE_CHANGE_RESUMED;
+	}
+
+	if (ep0.common.state_change_fn) {
+		(*ep0.common.state_change_fn)(&ep0.common, ep0.common.state_change_data,
+										state_change, old_state);
+	}
+}
+
+// --------------------------------------------------------------------------
+//							Common Rx Endpoint 1 & 2
+// --------------------------------------------------------------------------
+
+#if defined(_RX_EP1) || defined(_RX_EP2)
+
+static void usbs_d12_clear_rx_ep(rx_endpoint *ep)
+{
+	ep->common.buffer			= 0;
+	ep->common.buffer_size		= 0;
+	ep->common.complete_fn		= 0;
+	ep->common.complete_data	= 0;
+
+	ep->received	= 0;
+}
+
+// --------------------------------------------------------------------------
+// This is called when an rx operation is completed. It resets the endpoint
+// vars and calls the registered completion function.
+//
+
+static void usbs_d12_ep_rx_complete(rx_endpoint *ep, int result)
+{
+	completion_fn fn = ep->common.complete_fn;
+	void *data = ep->common.complete_data;
+
+	usbs_d12_clear_rx_ep(ep);
+
+	if (fn)
+		(*fn)(data, result);
+}
+
+// --------------------------------------------------------------------------
+// This routine is called when an rx buffer in the chip is full and ready to
+// be read. If there's an endpoint buffer available and room to hold the data
+// we read it in, otherwise we call the completion function, but leave the 
+// data in the chip. The hardware will automatically NAK packages from the
+// host until the app calls another start read to continue receiving data.
+//
+// CONTEXT:
+//		Called from either the DSR or application thread, via start rx.
+//		In either case, it's assumed that the chip is locked.
+//		
+
+static void usbs_d12_ep_rx(rx_endpoint *ep)
+{
+	int		n, ep_size, buf_remaining,
+			endp = ep->endp;
+	bool	done;
+
+	// The main endp is double buffered and we need to be prepared
+	// to read both simultaneously.
+	ep_size = (endp == D12_MAIN_ENDP) ? (2 * D12_MAIN_ENDP_SIZE) 
+									  : RX_ENDP_SIZE[endp];
+
+	buf_remaining = ep->common.buffer_size - ep->received;
+
+	// ----- If no space left in buffer, call completion fn -----
+
+	if (!ep->common.buffer || buf_remaining < ep_size) {
+		int ret = ep->received;
+
+		// See if caller requested a read smaller than the endp. Read & throw away extra
+		if (ep->common.buffer_size < ep_size) {
+			byte tmp_buf[D12_MAX_PACKET_SIZE];
+
+			if (endp == D12_MAIN_ENDP)
+				n = d12_read_main_endp_buf(D12_BASE_ADDR, tmp_buf, &done);
+			else
+				n = d12_read_endp_buf(D12_BASE_ADDR, RX_ENDP_INDEX[endp], tmp_buf);
+
+			if (n > ep->common.buffer_size) {
+				n = ep->received = ep->common.buffer_size;
+				ret = -ENOMEM;
+				TRACE_D12("\tEP%d: *** Rx Buffer too small. Data Lost ***\n", endp);
+			}
+			else
+				ret = ep->received = n;
+
+			memcpy(ep->common.buffer, tmp_buf, n);
+			buf_remaining = ep->common.buffer_size - n;
+		}
+
+		TRACE_D12("\tEP%d: Rx Complete. Buffer (nearly) full. [%d]\n", endp, buf_remaining);
+		usbs_d12_ep_rx_complete(ep, ret);
+		return;
+	}
+
+	// ----- Read the data from the chip -----
+
+	if (endp == D12_MAIN_ENDP)
+		n = d12_read_main_endp_buf(D12_BASE_ADDR, 
+								   ep->common.buffer + ep->received, &done);
+	else {
+		n = d12_read_endp_buf(D12_BASE_ADDR, RX_ENDP_INDEX[endp],
+							  ep->common.buffer + ep->received);
+		done = (n < RX_ENDP_SIZE[endp]);
+	}
+
+	ep->received += n;
+	buf_remaining = ep->common.buffer_size - ep->received;
+
+	done = done || (buf_remaining < ep_size);
+
+	TRACE_D12("EP%d: Received %d bytes.\n", endp, n);
+	TRACE_BUF("\t", ep->common.buffer + ep->received-n, n);
+
+	// ----- If we're done, complete the receive -----
+
+	if (done) {
+		TRACE_D12("\tEP%d Rx Complete (%d)  %p\n", endp, 
+				  ep->received, ep->common.complete_fn);
+		usbs_d12_ep_rx_complete(ep, ep->received);
+	}
+}
+
+// --------------------------------------------------------------------------
+// Stalls/unstalls the specified endpoint.
+
+static void usbs_d12_stall_rx_ep(rx_endpoint *ep, cyg_bool halt)
+{
+	ep->common.halted = halt;
+	d12_stall_endp(D12_BASE_ADDR, RX_ENDP_INDEX[ep->endp], halt);
+}
+
+// --------------------------------------------------------------------------
+// Handler for an Rx endpoint full interrupt. It clears the interrupt on the
+// D12 by reading the endpoint's status register then calls the routine to
+// read the data into the buffer.
+//
+// Called from the DSR context only.
+//
+
+static void usbs_d12_ep_rx_intr(rx_endpoint *ep)
+{
+	d12_read_last_trans_status(D12_BASE_ADDR, RX_ENDP_INDEX[ep->endp]);
+	usbs_d12_ep_rx(ep);
+}
+
+#endif
+
+// --------------------------------------------------------------------------
+//							Common Tx Endpoint 1 & 2
+// --------------------------------------------------------------------------
+
+#if defined(_TX_EP1) || defined(_TX_EP2)
+
+// Clears out the endpoint data structure before/after a tx is complete.
+
+static void usbs_d12_clear_tx_ep(tx_endpoint *ep)
+{
+	ep->common.buffer = 0;
+	ep->common.buffer_size = 0;
+	ep->common.complete_fn = 0;
+	ep->common.complete_data = 0;
+
+	ep->transmitted = 0;
+	ep->tx_empty = false;
+}
+
+// --------------------------------------------------------------------------
+// This is called when a transmit is completed. It resets the endpoint vars
+// and calls the registered completion function, if any.
+//
+// CONTEXT:
+//		Called from either the DSR or the app thread that started tx. 
+
+static void usbs_d12_ep_tx_complete(tx_endpoint *ep, int result)
+{
+	completion_fn fn = ep->common.complete_fn;
+	void *data = ep->common.complete_data;
+
+	usbs_d12_clear_tx_ep(ep);
+
+	if (fn)
+		(*fn)(data, result);
+}
+
+// --------------------------------------------------------------------------
+// The routine writes data to the chip and updates the endpoint's counters. 
+// It gets called at the start of a transfer operation to prime the device
+// and then gets called each time the chip finishes sending a packet to the
+// host and is ready for more data. If the amount of data remaining is 
+// smaller than can fit in the chip's endpoint buffer, then this is the last
+// packet to send, so we call the completion function.
+//
+// CONTEXT:
+//		Called from either the DSR or the app thread that started the tx
+//		In either case, it's assumed the chip is locked.
+
+static void usbs_d12_ep_tx(tx_endpoint *ep)
+{
+	int n, nRemaining;
+
+	// ----- Already done. Intermittent interrupt, so get out -----
+
+	if (!ep->common.buffer)
+		return;
+
+	// ----- See how many bytes remaining in buffer -----
+
+	nRemaining = ep->common.buffer_size - ep->transmitted;
+
+	TRACE_D12("EP%d: Tx %p, %d Done, %d Remaining\n", ep->endp, 
+			  ep->common.buffer, ep->transmitted, nRemaining);
+
+	// ----- If prev packet was last, signal that we're done -----
+
+	if (nRemaining == 0 && !ep->tx_empty) {
+		TRACE_D12("\tEP%d: Tx complete (%d)  %p\n", ep->endp, 
+				  ep->transmitted, ep->common.complete_fn);
+		usbs_d12_ep_tx_complete(ep, ep->transmitted);
+		return;
+	}
+
+	// ----- Write the next packet to chip -----
+
+	if (nRemaining < TX_ENDP_SIZE[ep->endp]) {
+		n = nRemaining;
+		ep->tx_empty = false;
+	}
+	else
+		n = TX_ENDP_SIZE[ep->endp];
+
+	TRACE_D12("EP%d: Writing %d bytes. %s\n", ep->endp, 
+											n, (n == 0) ? "DONE" : "");
+	TRACE_BUF("\t", ep->common.buffer + ep->transmitted, n);
+
+	d12_write_endp_buf(D12_BASE_ADDR, TX_ENDP_INDEX[ep->endp], 
+						ep->common.buffer + ep->transmitted, (uint8) n);
+
+	ep->transmitted += n;
+
+	// ----- If empty packet, complete now -----
+
+	if (n == 0) {
+		TRACE_D12("\tEP%d: Tx complete (%d)  %p\n", ep->endp, 
+				  ep->transmitted, ep->common.complete_fn);
+		usbs_d12_ep_tx_complete(ep, ep->transmitted);
+		return;
+	}
+}
+
+// --------------------------------------------------------------------------
+// Stalls/unstalls the specified tx endpoint.
+
+static void usbs_d12_stall_tx_ep(tx_endpoint *ep, cyg_bool halt)
+{
+	ep->common.halted = halt;
+	d12_stall_endp(D12_BASE_ADDR, TX_ENDP_INDEX[ep->endp], halt);
+}
+
+// --------------------------------------------------------------------------
+// Handler for when the chip's tx RAM for an endoint has just been emptied 
+// (sent to the host) and the chip is ready for more data.
+// We read the endpoint's last trans status register to clear the interrupt
+// on the D12, then call the tx function to send the next packet or 
+// complete the transfer.
+
+static void usbs_d12_ep_tx_intr(tx_endpoint *ep)
+{
+	d12_read_last_trans_status(D12_BASE_ADDR, TX_ENDP_INDEX[ep->endp]);
+	usbs_d12_ep_tx(ep);
+}
+
+#endif		// defined(_TX_EP1) || defined(_TX_EP2)
+
+// --------------------------------------------------------------------------
+//					Application Program Interface (API)
+// --------------------------------------------------------------------------
+
+#if defined(_RX_EP1) || defined(_RX_EP2)
+
+// Starts a receive operation on the specified endpoint. If the buffer size
+// is zero the completion function is called immediately. The routine checks
+// if tehre is data in the chip's endpoint buffer, and if so it will call
+// ep_rx() to start reading the data out of the chip.
+//
+// If the endpoint is currently stalled, a read size of zero can be used to 
+// block the calling thread until the stall is cleared. If the read size is
+// non-zero and the endpoint is stalled the completion function is called
+// immediately with an error result.
+
+static void usbs_d12_api_start_rx_ep(usbs_rx_endpoint *ep)
+{
+	rx_endpoint *epx = (rx_endpoint *) ep;
+
+	if (ep->halted) {
+		if (ep->buffer_size != 0)
+			usbs_d12_ep_rx_complete(epx, -EAGAIN);
+	}
+	else if (ep->buffer_size == 0) {
+		usbs_d12_ep_rx_complete(epx, 0);
+	}
+	else {
+		TRACE_D12("EP%d: Starting Rx, %p, %d\n", epx->endp, ep->buffer,
+													ep->buffer_size);
+		usbs_d12_lock();
+
+		epx->received = 0;
+		if (d12_data_available(D12_BASE_ADDR, RX_ENDP_INDEX[epx->endp]))
+			usbs_d12_ep_rx(epx);
+
+		usbs_d12_unlock();
+	}
+}
+
+// --------------------------------------------------------------------------
+// Halts/unhalts one of the generic rx (OUT) endpoints.
+//
+
+static void usbs_d12_api_stall_rx_ep(usbs_rx_endpoint *ep, cyg_bool halt)
+{
+	usbs_d12_lock();
+	usbs_d12_stall_rx_ep((rx_endpoint*) ep, halt);
+	usbs_d12_unlock();
+}
+
+#endif		// defined(_RX_EP1) || defined(_RX_EP2)
+
+// --------------------------------------------------------------------------
+//									Tx API
+// --------------------------------------------------------------------------
+
+#if defined(_TX_EP1) || defined(_TX_EP2)
+
+// This starts a transmit on one of the data endpoints. If the endpoint is
+// stalled a buffer size of zero can be used to block until the stall is
+// cleared. Any other size on a stalled endpoint will result in an error
+// callback immediately. The first packet is sent to the chip immediately,
+// in the application context. If the chip's buffer can contain the whole
+// transfer, the completion function will be called immediately, again,
+// still in the application context.
+//
+// If an empty packet is requested we send one from here and call the 
+// completion function. This should not cause an intr on the D12.
+//
+// CONTEXT:
+//		Called from an application thread
+
+static void usbs_d12_api_start_tx_ep(usbs_tx_endpoint *ep)
+{
+	tx_endpoint *epx = (tx_endpoint*) ep;
+
+	if (ep->halted) {
+		if (ep->buffer_size != 0) 
+			usbs_d12_ep_tx_complete(epx, -EAGAIN);
+	}
+	else if (ep->buffer_size == 0) {
+		usbs_d12_lock();
+
+		d12_write_endp_buf(D12_BASE_ADDR, TX_ENDP_INDEX[epx->endp], 0, 0);
+		usbs_d12_ep_tx_complete(epx, 0);
+
+		usbs_d12_unlock();
+	}
+	else {
+		TRACE_D12("EP%d: Starting Tx, %p, %d\n", epx->endp, ep->buffer,
+													ep->buffer_size);
+		usbs_d12_lock();
+
+		epx->tx_empty = true;
+		epx->transmitted = 0;
+		usbs_d12_ep_tx(epx);
+
+		usbs_d12_unlock();
+	}
+}
+
+// --------------------------------------------------------------------------
+// Halts/unhalts one of the generic endpoints.
+
+static void usbs_d12_api_stall_tx_ep(usbs_tx_endpoint *ep, cyg_bool halt)
+{
+	usbs_d12_lock();
+	usbs_d12_stall_tx_ep((tx_endpoint*) ep, halt);
+	usbs_d12_unlock();
+}
+
+#endif		// defined(_TX_ENDP1) || defined(_TX_EP2)
+
+// --------------------------------------------------------------------------
+//									DSR
+// --------------------------------------------------------------------------
+
+// The DSR for the D12 chip. This is normally called in the DSR context when
+// the D12 has raised its interrupt flag indicating that it needs to be 
+// serviced. The interrupt register contains bit flags that are OR'ed togther
+// indicating what items need to be serviced. There are flags for the 
+// following:
+//		- The endpoints (one bit for each)
+//		- Bus Reset
+//		- Suspend Change
+//		- DMA (terminal count)
+//
+// Care must be taken in that the D12's interrupt output is level-sensitive
+// (in that the interrupt sources are OR'ed together and not all cleared 
+// atomically in a single operation). Platforms (such as the PC) may be 
+// expecting edge-triggered interrupts, so we must work around that.
+// So, we loop on the interrupt register. Even though, in each loop, we
+// perform all of the required operations to clear the interrupts, a new
+// one may have arrived before we finished clearing the previous ones.
+// So we read the intr reg again. Once the intr reg gives a zero reading
+// we know that the D12 has dropped its IRQ line.
+//
+// Note, if we're configured to use a thread, this routine is called from
+// within a thread context (not a DSR context).
+//
+
+static void usbs_d12_dsr(cyg_vector_t vector, cyg_ucount32 count, 
+						 cyg_addrword_t data)
+{
+	uint16	status;
+	bool	suspended;
+
+	CYG_ASSERT(vector == CYGNUM_DEVS_USB_D12_INT,
+					"DSR should only be invoked for D12 interrupts");
+
+	while ((status = d12_read_intr_reg(D12_BASE_ADDR)) != 0) {
+		TRACE_D12("Intr Status: 0x%04X\n", (unsigned) status);
+
+		if (status & D12_INTR_BUS_RESET) {
+			TRACE_D12("\n>>> Bus Reset <<<\n");
+			usbs_d12_reset();
+		}
+		else {
+
+			// ----- Suspend Change -----
+
+			suspended = (bool) (ep0.common.state & USBS_STATE_SUSPENDED);
+
+			if (status & D12_INTR_SUSPEND_CHANGE) {
+				if (!suspended && (status & ~D12_INTR_SUSPEND_CHANGE) == 0)
+					usbs_d12_suspend(true);
+			}
+			else if (suspended)
+				usbs_d12_suspend(false);
+
+			// ----- Bulk Endpoints -----
+
+			#ifdef _TX_EP2
+				if (status & D12_INTR_TX_ENDP2)
+					usbs_d12_ep_tx_intr(&tx_ep2);
+			#endif
+
+			#ifdef _RX_EP2
+				if (status & D12_INTR_RX_ENDP2)
+					usbs_d12_ep_rx_intr(&rx_ep2);
+			#endif
+
+			// ----- Interrupt Endpoints -----
+
+			#ifdef _TX_EP1
+				if (status & D12_INTR_TX_ENDP1)
+					usbs_d12_ep_tx_intr(&tx_ep1);
+			#endif
+
+			#ifdef _RX_EP1
+				if (status & D12_INTR_RX_ENDP1)
+					usbs_d12_ep_rx_intr(&rx_ep1);
+			#endif
+
+			// ----- Control Endpoint -----
+
+			if (status & D12_INTR_TX_CTRL_ENDP)
+				usbs_d12_ep0_tx_intr();
+
+			if (status & D12_INTR_RX_CTRL_ENDP)
+				usbs_d12_ep0_rx_intr();
+		}
+	}
+
+	cyg_drv_interrupt_unmask(vector);
+}
+
+// --------------------------------------------------------------------------
+//								Interrupt
+// --------------------------------------------------------------------------
+
+// Here, the ISR does nothing but schedule the DSR to run. The ISR's/DSR's
+// are serialized. The CPU won't process another ISR until after the DSR
+// completes.
+
+static uint32 usbs_d12_isr(cyg_vector_t vector, cyg_addrword_t data)
+{
+	CYG_ASSERT(CYGNUM_DEVS_USB_D12_INT == vector, "usbs_isr: Incorrect interrupt");
+
+	// Prevent another interrupt until DSR completes
+	cyg_drv_interrupt_mask(vector);
+	cyg_drv_interrupt_acknowledge(vector);
+
+	return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
+}
+
+// --------------------------------------------------------------------------
+//								Polling
+// --------------------------------------------------------------------------
+
+static void usbs_d12_poll(usbs_control_endpoint *endp)
+{
+	CYG_ASSERT(endp == &ep0.common, "usbs_poll: wrong endpoint");
+
+	usbs_d12_lock();
+	usbs_d12_dsr(CYGNUM_DEVS_USB_D12_INT, 0, 0);
+	usbs_d12_unlock();
+}
+
+// --------------------------------------------------------------------------
+//								Thread Processing
+// --------------------------------------------------------------------------
+
+// The user can opt to configure the driver to service the D12 using a high
+// priority thread. The thread's priority MUST be greater than the priority
+// of any application thread making calls into the driver.
+// When we use a thread, the DSR simply signals a semaphore tio wake the
+// thread up. The thread, in turn, calls the the routine to service the D12,
+// now in a thread context. This allows for greater debug options, including
+// tracing.
+
+#ifdef CYGPKG_DEVS_USB_D12_THREAD
+
+static byte			usbs_d12_thread_stack[CYGNUM_DEVS_USB_D12_THREAD_STACK_SIZE];
+static cyg_thread	usbs_d12_thread;
+static cyg_handle_t	usbs_d12_thread_handle;
+static cyg_sem_t	usbs_d12_sem;
+
+
+static void usbs_d12_thread_dsr(cyg_vector_t vector, cyg_ucount32 count,
+														cyg_addrword_t data)
+{
+	cyg_semaphore_post(&usbs_d12_sem);
+	
+	CYG_UNUSED_PARAM(cyg_vector_t, vector);
+	CYG_UNUSED_PARAM(cyg_ucount32, count);
+	CYG_UNUSED_PARAM(cyg_addrword_t, data);
+}
+
+
+static void usbs_d12_thread_fn(cyg_addrword_t param)
+{
+	while (1) {
+		cyg_semaphore_wait(&usbs_d12_sem);
+		usbs_d12_poll(&ep0.common);
+	}
+
+	CYG_UNUSED_PARAM(cyg_addrword_t, param);
+}
+
+
+static void usbs_d12_thread_init(void)
+{
+	cyg_semaphore_init(&usbs_d12_sem, 0);
+
+	cyg_thread_create(CYGNUM_DEVS_USB_D12_THREAD_PRIORITY,
+					  &usbs_d12_thread_fn, 0, "D12 USB Driver Thread",
+					  usbs_d12_thread_stack, CYGNUM_DEVS_USB_D12_THREAD_STACK_SIZE,
+					  &usbs_d12_thread_handle, &usbs_d12_thread);
+	cyg_thread_resume(usbs_d12_thread_handle);
+}
+
+#endif	// CYGPKG_DEVS_USB_D12_THREAD
+
+// --------------------------------------------------------------------------
+//								Start/Reset
+// --------------------------------------------------------------------------
+
+// Chip initialization and handler for a USB Bus Reset. This gets called at
+// system startup and after a USB Bus Reset. It puts the chip into the
+// default state, with USB Address 0, connected to the bus (i.e. 
+// "SoftConnect" asserted). Interrupts to the main endpoint  are turned on
+// via the DMA register. 
+
+static void usbs_d12_reset(void)
+{
+	int old_state = ep0.common.state;
+	ep0.common.state = USBS_STATE_DEFAULT;
+
+	if (ep0.common.state_change_fn) {
+        (*ep0.common.state_change_fn)(&ep0.common, ep0.common.state_change_data,
+									  USBS_STATE_CHANGE_RESET, old_state);
+	}
+
+	d12_set_addr_enable(D12_BASE_ADDR, 0, true);
+	d12_set_endp_enable(D12_BASE_ADDR, true);
+	d12_set_dma(D12_BASE_ADDR, D12_DMA_MAIN_ENDP_INTR_ENABLE);
+	d12_set_mode(D12_BASE_ADDR, D12_MODE_CFG_DFLT | D12_MODE_CFG_SOFT_CONNECT,
+				 D12_MODE_CLK_DFLT);
+
+	// If any endpoints were going, signal the end of transfers
+
+	#if defined(_TX_EP2)
+		usbs_d12_ep_tx_complete(&tx_ep2, -EPIPE);
+	#endif
+	
+	#if defined(_RX_EP2)
+		usbs_d12_ep_rx_complete(&rx_ep2, -EPIPE);
+	#endif
+
+	#if defined(_TX_EP1)
+		usbs_d12_ep_tx_complete(&tx_ep1, -EPIPE);
+	#endif
+	
+	#if defined(_RX_EP1)
+		usbs_d12_ep_rx_complete(&rx_ep1, -EPIPE);
+	#endif
+}
+
+// --------------------------------------------------------------------------
+// The start function is called indirectly by the application when 
+// initialization is complete. By this time, the enumeration data has been
+// assigned to the control endpoint and we're ready to connect to the host.
+// Within the reset function the D12's SoftConnect line is asserted which
+// allows the host (hub) to see us on the USB bus. If connected, the host 
+// should start the enumeration process.
+//
+
+static void usbs_d12_start(usbs_control_endpoint *endpoint)
+{
+	#if defined(_TRACE) && !defined(_TRACE_STDOUT)
+		TRACE_OPEN(TRACE_SINK);
+	#endif
+
+	CYG_ASSERT(endpoint == &ep0.common, "ep0 start: wrong endpoint");
+	TRACE_D12("USBS D12: Starting.\n");
+
+	d12_clear_all_intr(D12_BASE_ADDR);
+	usbs_d12_reset();
+}	
+
+// --------------------------------------------------------------------------
+//								Initialization
+// --------------------------------------------------------------------------
+
+// This routine is called early in the program's startup, possibly before
+// main (from within a C++ object initialization). We want to put this chip
+// and driver in a neutral, but ready, state until the application gets 
+// control, initializes itself and calls the usb start function.
+//
+// The D12 has a "Soft Connect" feature to tristate the USB bus, making it
+// appear that the USB device is not connected to the bus. We initially
+// keep seperated from the bus to allow for initialization.
+
+void usbs_d12_init(void)
+{
+	cyg_DSR_t *pdsr;
+
+	d12_set_mode(D12_BASE_ADDR, D12_MODE_CFG_DFLT & ~D12_MODE_CFG_SOFT_CONNECT, 
+				 D12_MODE_CLK_DFLT);
+
+	d12_set_addr_enable(D12_BASE_ADDR, 0, false);
+	d12_set_endp_enable(D12_BASE_ADDR, false);
+
+	// ----- Clear the endpoints -----
+
+	#if defined(_TX_EP2)
+		usbs_d12_clear_tx_ep(&tx_ep2);
+	#endif
+	
+	#if defined(_RX_EP2)
+		usbs_d12_clear_rx_ep(&rx_ep2);
+	#endif
+
+	#if defined(_TX_EP1)
+		usbs_d12_clear_tx_ep(&tx_ep1);
+	#endif
+	
+	#if defined(_RX_EP1)
+		usbs_d12_clear_rx_ep(&rx_ep1);
+	#endif
+
+	// ----- Start the thread (if we're using it) -----
+
+	#ifdef CYGPKG_DEVS_USB_D12_THREAD
+		usbs_d12_thread_init();
+		pdsr = &usbs_d12_thread_dsr;
+	#else
+		pdsr = &usbs_d12_dsr;
+	#endif
+
+	// ----- Attach the ISR -----
+
+	cyg_drv_interrupt_create(CYGNUM_DEVS_USB_D12_INT, 
+							 0, 0, &usbs_d12_isr, pdsr,
+							 &usbs_d12_intr_handle, &usbs_d12_intr_data);
+
+	cyg_drv_interrupt_attach(usbs_d12_intr_handle);
+	cyg_drv_interrupt_unmask(CYGNUM_DEVS_USB_D12_INT);
+}
+
+// ----------------------------------------------------------------------------
+// Testing support.
+
+usbs_testing_endpoint usbs_testing_endpoints[] = {
+    {
+        endpoint_type       : USB_ENDPOINT_DESCRIPTOR_ATTR_CONTROL, 
+        endpoint_number     : 0,
+        endpoint_direction  : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN,
+        endpoint            : (void*) &ep0.common,
+#ifdef CYGVAR_DEVS_USB_D12_EP0_DEVTAB_ENTRY
+        devtab_entry        : CYGDAT_DEVS_USB_D12_DEVTAB_BASENAME "0c",
+#else        
+        devtab_entry        : (const char*) 0,
+#endif        
+        min_size            : 1,
+        max_size            : CYGNUM_DEVS_USB_D12_EP0_TXBUFSIZE,
+        max_in_padding      : 0,
+        alignment           : 0
+    },
+
+/*
+#ifdef _TX_EP1
+    {
+        endpoint_type       : USB_ENDPOINT_DESCRIPTOR_ATTR_INTERRUPT,
+        endpoint_number     : 1,
+        endpoint_direction  : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN,
+        endpoint            : (void*) &tx_ep1.common,
+# ifdef CYGVAR_DEVS_USB_D12_TX_EP2_DEVTAB_ENTRY
+        devtab_entry        : CYGDAT_DEVS_USB_D12_DEVTAB_BASENAME "1w",
+# else        
+        devtab_entry        : (const char*) 0,
+# endif        
+        min_size            : 0,
+        max_size            : 0x0FFFF,      // Driver limitation, only a single buffer descriptor is used
+        max_in_padding      : 0,
+        alignment           : HAL_DCACHE_LINE_SIZE
+    },
+#endif
+
+#ifdef _RX_EP1
+    {
+        endpoint_type       : USB_ENDPOINT_DESCRIPTOR_ATTR_INTERRUPT,
+        endpoint_number     : 1,
+        endpoint_direction  : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_OUT,
+        endpoint            : (void*) &rx_ep1.common,
+# ifdef CYGVAR_DEVS_USB_D12_RX_EP2_DEVTAB_ENTRY
+        devtab_entry        : CYGDAT_DEVS_USB_D12_DEVTAB_BASENAME "1r",
+# else        
+        devtab_entry        : (const char*) 0,
+# endif        
+        min_size            : 1,
+        max_size            : 0x0FFFF,      // Driver limitation
+        max_in_padding      : 0,
+        alignment           : HAL_DCACHE_LINE_SIZE
+    },
+#endif
+*/
+
+#ifdef _TX_EP2
+    {
+        endpoint_type       : USB_ENDPOINT_DESCRIPTOR_ATTR_BULK,
+        endpoint_number     : 2,
+        endpoint_direction  : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN,
+        endpoint            : (void*) &tx_ep2.common,
+# ifdef CYGVAR_DEVS_USB_D12_TX_EP2_DEVTAB_ENTRY
+        devtab_entry        : CYGDAT_DEVS_USB_D12_DEVTAB_BASENAME "2w",
+# else        
+        devtab_entry        : (const char*) 0,
+# endif        
+        min_size            : 0,
+        max_size            : 0x1000,	// 4k for testing
+        max_in_padding      : 0,
+        alignment           : HAL_DCACHE_LINE_SIZE
+    },
+#endif
+
+#ifdef _RX_EP2
+    {
+        endpoint_type       : USB_ENDPOINT_DESCRIPTOR_ATTR_BULK,
+        endpoint_number     : 2,
+        endpoint_direction  : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_OUT,
+        endpoint            : (void*) &rx_ep2.common,
+# ifdef CYGVAR_DEVS_USB_D12_RX_EP2_DEVTAB_ENTRY
+        devtab_entry        : CYGDAT_DEVS_USB_D12_DEVTAB_BASENAME "2r",
+# else        
+        devtab_entry        : (const char*) 0,
+# endif        
+        min_size            : 1,
+        max_size            : 0x1000,		// 4k for testing
+        max_in_padding      : 0,
+        alignment           : HAL_DCACHE_LINE_SIZE
+    },
+#endif
+
+    USBS_TESTING_ENDPOINTS_TERMINATOR
+};
+
diff -urN --exclude=CVS ecos-2006-05-19/packages/devs/usb/d12/current/src/usbs_d12_data.cxx ecos/packages/devs/usb/d12/current/src/usbs_d12_data.cxx
--- ecos-2006-05-19/packages/devs/usb/d12/current/src/usbs_d12_data.cxx	1969-12-31 19:00:00.000000000 -0500
+++ ecos/packages/devs/usb/d12/current/src/usbs_d12_data.cxx	2006-04-11 15:22:20.000000000 -0400
@@ -0,0 +1,222 @@
+//==========================================================================
+//
+//      usbs_d12_data.cxx
+//
+//      Static data for the D12 USB device driver
+//
+//==========================================================================
+//####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):    fmp
+// Contributors: fmp
+// Date:         2004-05-27
+//
+// This file contains various objects that should go into extras.o
+// rather than libtarget.a, e.g. devtab entries that would normally
+// be eliminated by the selective linking.
+//
+//####DESCRIPTIONEND####
+//==========================================================================
+
+#include <cyg/io/devtab.h>
+#include <cyg/io/usb/usbs_d12.h>
+#include <pkgconf/devs_usb_d12.h>
+
+// ----------------------------------------------------------------------------
+// Initialization. The goal here is to call usbs_d12_init()
+// early on during system startup, to take care of things like
+// registering interrupt handlers etc. which are best done
+// during system init.
+//
+// If the endpoint 0 devtab entry is available then its init()
+// function can be used to take care of this. However the devtab
+// entries are optional so an alternative mechanism must be
+// provided. Unfortunately although it is possible to give
+// a C function the constructor attribute, it cannot be given
+// an initpri attribute. Instead it is necessary to define a
+// dummy C++ class.
+
+extern "C" void usbs_d12_init(void);
+
+#ifndef CYGVAR_DEVS_USB_D12_EP0_DEVTAB_ENTRY
+class usbs_d12_initialization {
+  public:
+	usbs_d12_initialization() {
+		usbs_d12_init();
+	}
+};
+
+static usbs_d12_initialization usbs_d12_init_object CYGBLD_ATTRIB_INIT_PRI(CYG_INIT_IO);
+#endif
+
+// ----------------------------------------------------------------------------
+// The devtab entries. Each of these is optional, many applications
+// will want to use the lower-level API rather than go via
+// open/read/write/ioctl.
+
+#ifdef CYGVAR_DEVS_USB_D12_EP0_DEVTAB_ENTRY
+
+// For endpoint 0 the only legal operations are get_config() and
+// set_config(), and these are provided by the common package.
+
+static bool usbs_d12_devtab_ep0_init(struct cyg_devtab_entry* tab)
+{
+	CYG_UNUSED_PARAM(struct cyg_devtab_entry*, tab);
+	usbs_d12_init();
+	return true;
+}
+
+static CHAR_DEVIO_TABLE(usbs_d12_ep0_devtab_functions,
+						&cyg_devio_cwrite,
+						&cyg_devio_cread,
+						&cyg_devio_select,
+						&usbs_devtab_get_config,
+						&usbs_devtab_set_config);
+
+static CHAR_DEVTAB_ENTRY(usbs_d12_ep0_devtab_entry,
+						CYGDAT_DEVS_USB_D12_DEVTAB_BASENAME "0c",
+						0,
+						&usbs_d12_ep0_devtab_functions,
+						&usbs_d12_devtab_ep0_init,
+						0,
+						(void*) &usbs_d12_ep0);
+#endif
+
+// ----------------------------------------------------------------------------
+// Common routines for ep1 and ep2.
+
+#if defined(CYGVAR_DEVS_USB_D12_TX_EP1_DEVTAB_ENTRY) || \
+	defined(CYGVAR_DEVS_USB_D12_RX_EP1_DEVTAB_ENTRY) || \
+	defined(CYGVAR_DEVS_USB_D12_TX_EP2_DEVTAB_ENTRY) || \
+	defined(CYGVAR_DEVS_USB_D12_RX_EP2_DEVTAB_ENTRY)
+
+static bool usbs_d12_devtab_dummy_init(struct cyg_devtab_entry* tab)
+{
+	CYG_UNUSED_PARAM(struct cyg_devtab_entry*, tab);
+	return true;
+}
+#endif
+
+// ----------------------------------------------------------------------------
+// tx (in) ep1 devtab entry. This can only be used for slave->host,
+// so only the cwrite() function makes sense.
+
+#ifdef CYGVAR_DEVS_USB_D12_TX_EP1_DEVTAB_ENTRY
+
+static CHAR_DEVIO_TABLE(usbs_d12_tx_ep1_devtab_functions,
+						&usbs_devtab_cwrite,
+						&cyg_devio_cread,
+						&cyg_devio_select,
+						&usbs_devtab_get_config,
+						&usbs_devtab_set_config);
+
+static CHAR_DEVTAB_ENTRY(usbs_d12_tx_ep1_devtab_entry,
+						 CYGDAT_DEVS_USB_D12_DEVTAB_BASENAME "1w",
+						 0,
+						 &usbs_d12_tx_ep1_devtab_functions,
+						 &usbs_d12_devtab_dummy_init,
+						 0,
+						 (void*) &usbs_d12_tx_ep1);
+#endif
+
+// ----------------------------------------------------------------------------
+// rx (out) ep1 devtab entry. This can only be used for host->slave, 
+// so only the cread() function makes sense.
+
+#ifdef CYGVAR_DEVS_USB_D12_RX_EP1_DEVTAB_ENTRY
+
+static CHAR_DEVIO_TABLE(usbs_d12_rx_ep1_devtab_functions,
+						&cyg_devio_cwrite,
+						&usbs_devtab_cread,
+						&cyg_devio_select,
+						&usbs_devtab_get_config,
+						&usbs_devtab_set_config);
+
+static CHAR_DEVTAB_ENTRY(usbs_d12_rx_ep1_devtab_entry,
+						CYGDAT_DEVS_USB_D12_DEVTAB_BASENAME "1r",
+						0,
+						&usbs_d12_rx_ep1_devtab_functions,
+						&usbs_d12_devtab_dummy_init,
+						0,
+						(void*) &usbs_d12_rx_ep1);
+#endif
+
+
+// ----------------------------------------------------------------------------
+// tx (in) ep2 devtab entry. This can only be used for slave->host, so only the
+// cwrite() function makes sense.
+
+#ifdef CYGVAR_DEVS_USB_D12_TX_EP2_DEVTAB_ENTRY
+
+static CHAR_DEVIO_TABLE(usbs_d12_tx_ep2_devtab_functions,
+						&usbs_devtab_cwrite,
+						&cyg_devio_cread,
+						&cyg_devio_select,
+						&usbs_devtab_get_config,
+						&usbs_devtab_set_config);
+
+static CHAR_DEVTAB_ENTRY(usbs_d12_tx_ep2_devtab_entry,
+						CYGDAT_DEVS_USB_D12_DEVTAB_BASENAME "2w",
+						0,
+						&usbs_d12_tx_ep2_devtab_functions,
+						&usbs_d12_devtab_dummy_init,
+						0,
+						(void*) &usbs_d12_tx_ep2);
+#endif
+
+// ----------------------------------------------------------------------------
+// rx (out) ep2 devtab entry. This can only be used for host->slave, 
+// so only the cread() function makes sense.
+
+#ifdef CYGVAR_DEVS_USB_D12_RX_EP2_DEVTAB_ENTRY
+
+static CHAR_DEVIO_TABLE(usbs_d12_rx_ep2_devtab_functions,
+						&cyg_devio_cwrite,
+						&usbs_devtab_cread,
+						&cyg_devio_select,
+						&usbs_devtab_get_config,
+						&usbs_devtab_set_config);
+
+static CHAR_DEVTAB_ENTRY(usbs_d12_rx_ep2_devtab_entry,
+						CYGDAT_DEVS_USB_D12_DEVTAB_BASENAME "2r",
+						0,
+						&usbs_d12_rx_ep2_devtab_functions,
+						&usbs_d12_devtab_dummy_init,
+						0,
+						(void*) &usbs_d12_rx_ep2);
+#endif
+
+
diff -urN --exclude=CVS ecos-2006-05-19/packages/devs/usb/i386/SoRoD12/current/cdl/usbs_i386_sorod12.cdl ecos/packages/devs/usb/i386/SoRoD12/current/cdl/usbs_i386_sorod12.cdl
--- ecos-2006-05-19/packages/devs/usb/i386/SoRoD12/current/cdl/usbs_i386_sorod12.cdl	1969-12-31 19:00:00.000000000 -0500
+++ ecos/packages/devs/usb/i386/SoRoD12/current/cdl/usbs_i386_sorod12.cdl	2006-04-27 11:06:26.000000000 -0400
@@ -0,0 +1,97 @@
+# ====================================================================
+#
+#	usbs_i386_sorod12.cdl
+#
+#	Hardware specific parts for the SoRo D12 PC 104 USB card.
+#	
+#
+# ====================================================================
+#####ECOSGPLCOPYRIGHTBEGIN####
+## -------------------------------------------
+## This file is part of eCos, the Embedded Configurable Operating System.
+## Copyright (C) 2003, 2004, 2006 eCosCentric Limited
+##
+## 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.
+## -------------------------------------------
+#####ECOSGPLCOPYRIGHTEND####
+# ====================================================================
+######DESCRIPTIONBEGIN####
+#
+# Author(s):      Frank M. Pagliughi (fmp), SoRo Systems, Inc., asl
+# Contributors:
+# Date:           2006-04-27
+#
+#####DESCRIPTIONEND####
+# ====================================================================
+
+cdl_package CYGPKG_DEVS_USB_I386_SOROD12 {
+	display     "Hardware specific part for SoRo D12 USB Device Driver"
+	include_dir "cyg/io/usb"
+	parent      CYGPKG_DEVS_USB_D12
+
+        requires    { CYGIMP_DEVS_USB_D12_HW_ACCESS_HEADER ==
+                      "<cyg/io/usb/usbs_i386_sorod12.inl>" }
+
+        cdl_option CYGSEM_DEVS_USB_I386_SOROD12_IO_MAPPED {
+		display "I/O mapped."
+		flavor	bool
+		default_value 1
+		description  "
+			The PDIUSBD12 can be mapped into the processor's I/O space or memory
+			space. If this is set the driver accesses the chip through HAL_READ
+			and HAL_WRITE macros, otherwise it uses direct memory access.
+			"
+	}
+
+        cdl_option CYGNUM_DEVS_USB_I386_SOROD12_BASEADDR {
+		display       "Base Address of D12 chip"
+		flavor        data
+                no_define
+		legal_values  1 to 0xFF8
+		default_value 544
+		active_if     CYGFUN_DEVS_USB_D12_EP0
+                requires      { CYGNUM_DEVS_USB_D12_BASEADDR == 
+                                CYGNUM_DEVS_USB_I386_SOROD12_BASEADDR }
+		description "
+			The base memory or I/O address where the USB chip resides.
+		    "
+	}
+
+	cdl_option CYGNUM_DEVS_USB_I386_SORODD12_IRQ {
+		display       "IRQ for the D12 chip"
+		flavor        data
+                no_define
+		legal_values  { 3 5 7 }
+		default_value 5
+                active_if      CYGFUN_DEVS_USB_D12_EP0
+                requires      { CYGNUM_DEVS_USB_D12_IRQ == 
+                                CYGNUM_DEVS_USB_I386_SORODD12_IRQ }
+		description "
+			The IRQ assigned to the D12 chip
+		    "
+	}
+}
+
+
+	
\ No newline at end of file
diff -urN --exclude=CVS ecos-2006-05-19/packages/devs/usb/i386/SoRoD12/current/include/usbs_i386_sorod12.inl ecos/packages/devs/usb/i386/SoRoD12/current/include/usbs_i386_sorod12.inl
--- ecos-2006-05-19/packages/devs/usb/i386/SoRoD12/current/include/usbs_i386_sorod12.inl	1969-12-31 19:00:00.000000000 -0500
+++ ecos/packages/devs/usb/i386/SoRoD12/current/include/usbs_i386_sorod12.inl	2006-05-19 16:31:24.000000000 -0400
@@ -0,0 +1,128 @@
+#ifndef USBS_I386_SORO12_INL
+#define USBS_I386_SORO12_INL
+//==========================================================================
+//
+//      usbS_i386_sorod12.inl
+//
+//      Hardware specific parts for the SoRo D12 PC 104 USB card.
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2006, eCosCentric
+//
+// 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.
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):    Frank M. Pagliughi (fmp), ASL
+// Date:         2004-05-22
+//
+// This code is a hardware specific device driver for the SoRo Systems
+// USB-D12-104, a PC/104 (ISA) Full-Speed USB slave board, which turns
+// a PC/104 stack into a USB slave device. The board contains a
+// Philips PDIUSBD12 Peripheral Controller Chip mapped into the PC's
+// I/O space, with jumper-selectable I/O base address, IRQ, and DMA
+// settings. The eCos config tool is used to adjust settings for this
+// driver to match the physical jumper settings. The chip could run in
+// polled mode without an IRQ, but this wouldn't be a great idea other
+// than maybe a debug environment.
+
+// ------------------------------------------------------------------------
+//								Data-Only I/O                              
+// ------------------------------------------------------------------------
+//
+
+#include <pkgconf/devs_usb_i386_sorod12.h>
+
+// These routines read or write 8 bit values to the data area.
+
+static inline byte d12_read_data_byte(d12_addr_type base_addr)
+{
+	#if defined(CYGSEM_DEVS_USB_I386_SOROD12_IO_MAPPED)
+		byte val;
+		HAL_READ_UINT8((unsigned) base_addr, val);
+		return val;
+	#else
+		return *base_addr;
+	#endif
+}
+
+static inline void d12_write_data_byte(d12_addr_type base_addr, byte val)
+{
+	#if defined(CYGSEM_DEVS_USB_I386_SOROD12_IO_MAPPED)
+		HAL_WRITE_UINT8((unsigned) base_addr, val);
+	#else
+		*base_addr = val;
+	#endif
+}
+
+// This routine writes a command to the device.
+
+static inline void d12_write_cmd(d12_addr_type base_addr, byte cmd)
+{
+	#if defined(CYGSEM_DEVS_USB_I386_SOROD12_IO_MAPPED)
+		HAL_WRITE_UINT8((unsigned) base_addr+1, cmd);
+	#else
+		*(base_addr+1) = cmd;
+	#endif
+}
+
+// ------------------------------------------------------------------------
+//	Reads 'n' bytes from the data address of the D12 and places them into
+//	the buf[] array. Buf can be NULL, in which case the specified number of
+//	bytes are read and discarded.
+
+static uint8 d12_read_data(d12_addr_type base_addr, byte *buf, uint8 n)
+{
+	uint8 i;
+
+	if (buf) {
+		for (i=0; i<n; ++i)
+			buf[i] = d12_read_data_byte(base_addr);
+	}
+	else {
+		for (i=0; i<n; ++i)
+			d12_read_data_byte(base_addr);
+		n = 0;
+	}
+
+	return n;
+}
+
+// ------------------------------------------------------------------------
+// Writes 'n' bytes out the data reg of the chip
+
+static void d12_write_data(d12_addr_type base_addr, const byte *buf, uint8 n)
+{
+	uint8 i;
+
+	for (i=0; i<n; ++i)
+		d12_write_data_byte(base_addr, buf[i]);
+}
+
+#endif // USBS_I386_SORO12_INL
diff -urN --exclude=CVS ecos-2006-05-19/packages/ecos.db ecos/packages/ecos.db
--- ecos-2006-05-19/packages/ecos.db	2006-05-19 13:53:35.000000000 -0400
+++ ecos/packages/ecos.db	2006-05-19 15:16:51.000000000 -0400
@@ -1367,6 +1367,22 @@
         description     "A device driver for the ATMEL AT91 on-chip USB device"
 }
 
+package CYGPKG_DEVS_USB_D12 {
+	alias		{ usb_d12 }
+	hardware
+	directory	devs/usb/d12
+	script		usbs_d12.cdl
+	description	"A device driver for the Philips PDIUSBD12 full speed USB peripheral chip."
+}
+
+package CYGPKG_DEVS_USB_I386_SOROD12 {
+	alias		{ usb_sorod12 }
+	hardware
+	directory	devs/usb/i386/SoRoD12
+	script		usbs_i386_sorod12.cdl
+	description	"A device driver for the SoRo PC/104 D12 USB Slave Board."
+}
+
 package CYGPKG_NET {
 	alias 		{ "Networking" net }
 	directory	net/common
@@ -4988,6 +5004,30 @@
             on a standard i386 PC under wmWare."
 }
 
+
+target pc_usb_d12 {
+        alias		{ "i386 PC target with SoRo PC/104 D12 USB Slave" }
+	packages        { CYGPKG_HAL_I386
+                          CYGPKG_HAL_I386_GENERIC
+                          CYGPKG_HAL_I386_PC
+                          CYGPKG_HAL_I386_PCMB
+			  CYGPKG_IO_PCI
+                          CYGPKG_IO_SERIAL_GENERIC_16X5X
+	                  CYGPKG_IO_SERIAL_I386_PC
+			  CYGPKG_IO_USB
+			  CYGPKG_IO_USB_SLAVE
+			  CYGPKG_DEVS_USB_D12
+			  CYGPKG_DEVS_USB_I386_SOROD12
+			  CYGPKG_IO_FILEIO
+                          CYGPKG_DEVICES_WALLCLOCK_DALLAS_DS12887
+                          CYGPKG_DEVICES_WALLCLOCK_I386_PC
+        }
+        description "
+    	    Provides the packages needed to run eCos binaries
+	    on a standard i386 PC motherboard with USB Slave support for the
+	    Philips D12 chip."
+}
+
 # --------------------------------------------------------------------------
 # Synthetic targets.
 target linux {


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]