changeset 2972:535911f86a6f

* New STM32 USB slave driver contributed by Chris Holgate. [ Bugzilla 1001024 ]
author jld
date Wed, 27 Oct 2010 11:27:30 +0000
parents ea354217b697
children 8536a2fc60a4
files packages/NEWS packages/devs/usb/cortexm/stm32/current/ChangeLog packages/devs/usb/cortexm/stm32/current/cdl/usb_stm32.cdl packages/devs/usb/cortexm/stm32/current/include/usb_stm32.h packages/devs/usb/cortexm/stm32/current/src/usb_stm32.c packages/ecos.db
diffstat 6 files changed, 2886 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/packages/NEWS
+++ b/packages/NEWS
@@ -1,3 +1,4 @@
+* USB slave device driver for STM32 by Chris Holgate
 * Support for dynamic data endpoint configuration in USB serial function
   device package by John Dallaway
 * Support for dynamic data endpoint configuration in USB slave API by
new file mode 100644
--- /dev/null
+++ b/packages/devs/usb/cortexm/stm32/current/ChangeLog
@@ -0,0 +1,28 @@
+2010-10-27  Chris Holgate  <chris@zynaptic.com>
+
+	* src/usb_stm32.c, include/usb_stm32.h, cdl/usb_stm32.cdl: New
+	STM32 USB slave driver. [ Bugzilla 1001024 ]
+
+//===========================================================================
+// ####GPLCOPYRIGHTBEGIN####                                                
+// -------------------------------------------                              
+// This file is part of eCos, the Embedded Configurable Operating System.   
+// Copyright (C) 2010 Free Software Foundation, Inc.
+//
+// This program is free software; you can redistribute it and/or modify     
+// it under the terms of the GNU General Public License as published by     
+// the Free Software Foundation; either version 2 or (at your option) any   
+// later version.                                                           
+//
+// This program is distributed in the hope that it will be useful, but      
+// WITHOUT ANY WARRANTY; without even the implied warranty of               
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        
+// General Public License for more details.                                 
+//
+// You should have received a copy of the GNU General Public License        
+// along with this program; if not, write to the                            
+// Free Software Foundation, Inc., 51 Franklin Street,                      
+// Fifth Floor, Boston, MA  02110-1301, USA.                                
+// -------------------------------------------                              
+// ####GPLCOPYRIGHTEND####                                                  
+//===========================================================================
new file mode 100644
--- /dev/null
+++ b/packages/devs/usb/cortexm/stm32/current/cdl/usb_stm32.cdl
@@ -0,0 +1,325 @@
+##=============================================================================
+##
+##      usb_stm32.cdl
+##
+##      STM32 USB driver configuration options.
+##
+##=============================================================================
+## ####ECOSGPLCOPYRIGHTBEGIN####                                            
+## -------------------------------------------                              
+## This file is part of eCos, the Embedded Configurable Operating System.   
+## Copyright (C) 2008, 2009, 2010 Free Software Foundation, 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.,    
+## 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 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 v2.                                               
+##
+## 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):   Chris Holgate
+## Date:        2009-05-19
+## Purpose:     Configure STM32 USB driver.
+##
+######DESCRIPTIONEND####
+##
+##=============================================================================
+
+#
+# TODO: Needs to check that the PLL is running off the HSE clock source and
+# is set up to run at 48MHz or 72MHz.
+#
+
+cdl_package CYGPKG_DEVS_USB_CORTEXM_STM32 {
+    display       "ST STM32 USB driver"
+    description   "
+        This package provides USB device side driver support for the ST 
+        STM32 series of microcontrollers.
+    "
+    parent        CYGPKG_IO_USB
+    active_if     CYGPKG_IO_USB
+    hardware
+    include_dir   "cyg/io/usb"
+
+    # Make sure that we are running on the right hardware.
+    requires CYGPKG_HAL_CORTEXM_STM32
+
+    cdl_option CYGFUN_DEVS_USB_CORTEXM_STM32_EP0 {
+        display       "Enable the control endpoint 0"
+        default_value CYGINT_IO_USB_SLAVE_CLIENTS
+        requires      CYGPKG_IO_USB_SLAVE
+        requires      !CYGGLO_IO_USB_SLAVE_PROVIDE_DEVTAB_ENTRIES
+        compile       usb_stm32.c
+        description "
+            Enable support for endpoint 0. If this support is disabled
+            then the entire USB port is unusable.  Note that this driver does
+            not support slave side devtab entries."
+        }
+    
+    cdl_option CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM {
+        display       "Max IN endpoints"
+        description   "
+            Specify the maximum number of IN (transmit) endpoints supported by the device.
+            The total number of standard IN and OUT endpoints should not exceed 7.
+        "
+        flavor        data
+        default_value { 2 }
+        legal_values  { 0 to (7 - CYGNUM_DEVS_USB_CORTEXM_STM32_RXEP_NUM) }
+    }
+
+    cdl_option CYGNUM_DEVS_USB_CORTEXM_STM32_RXEP_NUM {
+        display       "Max OUT endpoints"
+        description   "
+            Specify the maximum number of OUT (receive) endpoints supported by the device.
+            The total number of standard IN and OUT endpoints should not exceed 7.
+        "
+        flavor        data
+        default_value { 2 }
+        legal_values  { 0 to (7 - CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) }
+    }
+
+    cdl_option CYGNUM_DEVS_USB_CORTEXM_STM32_EPO_MAX_MSG_SIZE {
+        display       "Control endpoint buffer size"
+        description   "
+            Selects the size of the control endpoint data buffer, which sets the maximum size 
+            of the control endpoint data phase transactions.
+        "
+        flavor        data
+        default_value { 128 }    
+        legal_values  { 8 to 256 }
+    }
+
+    cdl_option CYGHWR_DEVS_USB_CORTEXM_STM32_SELF_POWERED {
+        display       "Self powered"
+        description   "
+            If set, the USB device will report itself to the host as being self-powered.
+            There is no support for dynamically detecting the power state for dual powered
+            devices.
+        "
+        flavor        bool
+        default_value false
+    }
+    
+    cdl_option CYGHWR_DEVS_USB_CORTEXM_STM32_BULK_TERM_ZLP {
+        display       "Variable length bulk transfers"
+        description   "
+            Some USB device classes (eg. CDC ATM) require support for variable length bulk 
+            messages.  This is implemented by terminating bulk transfers using a short or 
+            zero length packet.  Selecting this option enables this form of variable length 
+            bulk message transfer for all bulk endpoints.  Note that this option is incompatible 
+            with the standard eCos USB test framework.  
+        "
+        flavor        bool
+        default_value false
+    }
+    
+    cdl_option CYGHWR_DEVS_USB_CORTEXM_STM32_DISC_PIN {
+        display       "USB disconnect pin"
+        description   "
+            This is the GPIO which is used to control the USB bus D+ pullup resistor, which
+            allows the USB bus connection status to be changed under software control.
+            For the purposes of specifying which GPIO to use, pins are numbered consecutively 
+            from 0 (A0) through 16 (B0) to 111 (G15).
+        "
+        flavor        data
+        default_value { 30 }
+    }
+    
+    cdl_option CYGHWR_DEVS_USB_CORTEXM_STM32_DISC_PIN_ACT_LOW {
+        display       "Active low USB disconnect"
+        description   "
+            The USB disconnect pin is normally treated as active high - so that when set high 
+            the device will be disconnected from the bus.  Select this option for boards where
+            the USB disconnect pin is wired as active low.
+        "
+        flavor        bool
+        default_value false
+    }
+
+    cdl_option CYGNUM_DEVS_USB_CORTEXM_STM32_ISR_PRIORITY {
+        display       "ISR priority"
+        description   "
+            Sets the ISR priority level used by the USB driver.
+        "
+        flavor        data
+        default_value { 4 }
+    }
+    
+    cdl_option CYGBLD_DEVS_USB_CORTEXM_STM32_DEBUG_TRACE {
+        display       "Debug trace enable"
+        description   "
+            Enables debug tracing for the USB driver.
+        "
+        flavor        bool
+        default_value false
+    }
+    
+    cdl_component CYGFUN_DEVS_USB_CORTEXM_STM32_PHY_CONFIG {
+        display       "Physical endpoint configuration."
+        description   "
+            This is the automatically generated physical endpoint configuration for the 
+            STM32 USB device.  Note that the physical endpoint numbering is only used 
+            internally to the driver and has no bearing on the logical endpoint numbers
+            which may be specified in a given device configuration.
+        "
+        flavor none
+    
+        cdl_option CYGFUN_DEVS_USB_CORTEXM_STM32_PHY_CTRLEP0 {
+            display "Physical endpoint 0 CTRL"
+            flavor bool
+            active_if { (CYGFUN_DEVS_USB_CORTEXM_STM32_EP0) }
+            calculated { (CYGFUN_DEVS_USB_CORTEXM_STM32_EP0) }
+            implements CYGHWR_IO_USB_SLAVE
+        }
+
+        cdl_option CYGFUN_DEVS_USB_CORTEXM_STM32_PHY_TXEP1 {
+            display "Physical endpoint 1 IN"
+            flavor bool
+            active_if { (1 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) }
+            calculated { (1 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) }
+            implements CYGHWR_IO_USB_SLAVE_IN_ENDPOINTS
+        }
+    
+        cdl_option CYGFUN_DEVS_USB_CORTEXM_STM32_PHY_TXEP2 {
+            display "Physical endpoint 2 IN"
+            flavor bool
+            active_if { (2 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) }
+            calculated { (2 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) }
+            implements CYGHWR_IO_USB_SLAVE_IN_ENDPOINTS
+        }
+    
+        cdl_option CYGFUN_DEVS_USB_CORTEXM_STM32_PHY_TXEP3 {
+            display "Physical endpoint 3 IN"
+            flavor bool
+            active_if { (3 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) }
+            calculated { (3 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) }
+            implements CYGHWR_IO_USB_SLAVE_IN_ENDPOINTS
+        }
+    
+        cdl_option CYGFUN_DEVS_USB_CORTEXM_STM32_PHY_TXEP4 {
+            display "Physical endpoint 4 IN"
+            flavor bool
+            active_if { (4 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) }
+            calculated { (4 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) }
+            implements CYGHWR_IO_USB_SLAVE_IN_ENDPOINTS
+        }
+    
+        cdl_option CYGFUN_DEVS_USB_CORTEXM_STM32_PHY_TXEP5 {
+            display "Physical endpoint 5 IN"
+            flavor bool
+            active_if { (5 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) }
+            calculated { (5 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) }
+            implements CYGHWR_IO_USB_SLAVE_IN_ENDPOINTS
+        }
+    
+        cdl_option CYGFUN_DEVS_USB_CORTEXM_STM32_PHY_TXEP6 {
+            display "Physical endpoint 6 IN"
+            flavor bool
+            active_if { (6 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) }
+            calculated { (6 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) }
+            implements CYGHWR_IO_USB_SLAVE_IN_ENDPOINTS
+        }
+    
+        cdl_option CYGFUN_DEVS_USB_CORTEXM_STM32_PHY_TXEP7 {
+            display "Physical endpoint 7 IN"
+            flavor bool
+            active_if { (7 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) }
+            calculated { (7 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) }
+            implements CYGHWR_IO_USB_SLAVE_IN_ENDPOINTS
+        }
+    
+        cdl_option CYGFUN_DEVS_USB_CORTEXM_STM32_PHY_RXEP1 {
+            display "Physical endpoint 1 OUT"
+            flavor bool
+            active_if { (1 > CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) && 
+                (1 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM + CYGNUM_DEVS_USB_CORTEXM_STM32_RXEP_NUM) }
+            calculated { (1 > CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) && 
+                (1 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM + CYGNUM_DEVS_USB_CORTEXM_STM32_RXEP_NUM) }
+            implements CYGHWR_IO_USB_SLAVE_OUT_ENDPOINTS
+        }
+    
+        cdl_option CYGFUN_DEVS_USB_CORTEXM_STM32_PHY_RXEP2 {
+            display "Physical endpoint 2 OUT"
+            flavor bool
+            active_if { (2 > CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) && 
+                (2 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM + CYGNUM_DEVS_USB_CORTEXM_STM32_RXEP_NUM) }
+            calculated { (2 > CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) && 
+                (2 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM + CYGNUM_DEVS_USB_CORTEXM_STM32_RXEP_NUM) }
+            implements CYGHWR_IO_USB_SLAVE_OUT_ENDPOINTS
+        }
+    
+        cdl_option CYGFUN_DEVS_USB_CORTEXM_STM32_PHY_RXEP3 {
+            display "Physical endpoint 3 OUT"
+            flavor bool
+            active_if { (3 > CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) && 
+                (3 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM + CYGNUM_DEVS_USB_CORTEXM_STM32_RXEP_NUM) }
+            calculated { (3 > CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) && 
+                (3 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM + CYGNUM_DEVS_USB_CORTEXM_STM32_RXEP_NUM) }
+            implements CYGHWR_IO_USB_SLAVE_OUT_ENDPOINTS
+        }
+    
+        cdl_option CYGFUN_DEVS_USB_CORTEXM_STM32_PHY_RXEP4 {
+            display "Physical endpoint 4 OUT"
+            flavor bool
+            active_if { (4 > CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) && 
+                (4 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM + CYGNUM_DEVS_USB_CORTEXM_STM32_RXEP_NUM) }
+            calculated { (4 > CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) && 
+                (4 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM + CYGNUM_DEVS_USB_CORTEXM_STM32_RXEP_NUM) }
+            implements CYGHWR_IO_USB_SLAVE_OUT_ENDPOINTS
+        }
+    
+        cdl_option CYGFUN_DEVS_USB_CORTEXM_STM32_PHY_RXEP5 {
+            display "Physical endpoint 5 OUT"
+            flavor bool
+            active_if { (5 > CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) && 
+                (5 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM + CYGNUM_DEVS_USB_CORTEXM_STM32_RXEP_NUM) }
+            calculated { (5 > CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) && 
+                (5 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM + CYGNUM_DEVS_USB_CORTEXM_STM32_RXEP_NUM) }
+            implements CYGHWR_IO_USB_SLAVE_OUT_ENDPOINTS
+        }
+    
+        cdl_option CYGFUN_DEVS_USB_CORTEXM_STM32_PHY_RXEP6 {
+            display "Physical endpoint 6 OUT"
+            flavor bool
+            active_if { (6 > CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) && 
+                (6 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM + CYGNUM_DEVS_USB_CORTEXM_STM32_RXEP_NUM) }
+            calculated { (6 > CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) && 
+                (6 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM + CYGNUM_DEVS_USB_CORTEXM_STM32_RXEP_NUM) }
+            implements CYGHWR_IO_USB_SLAVE_OUT_ENDPOINTS
+        }
+    
+        cdl_option CYGFUN_DEVS_USB_CORTEXM_STM32_PHY_RXEP7 {
+            display "Physical endpoint 7 OUT"
+            flavor bool
+            active_if { (7 > CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) && 
+                (7 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM + CYGNUM_DEVS_USB_CORTEXM_STM32_RXEP_NUM) }
+            calculated { (7 > CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) && 
+                (7 <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM + CYGNUM_DEVS_USB_CORTEXM_STM32_RXEP_NUM) }
+            implements CYGHWR_IO_USB_SLAVE_OUT_ENDPOINTS
+        }
+    }
+}
+
+# EOF usb_stm32.cdl
new file mode 100644
--- /dev/null
+++ b/packages/devs/usb/cortexm/stm32/current/include/usb_stm32.h
@@ -0,0 +1,56 @@
+#ifndef CYGONCE_DEVS_USB_CORTEXM_STM32_H
+#define CYGONCE_DEVS_USB_CORTEXM_STM32_H
+
+//=============================================================================
+//
+//      usb_stm32.h
+//
+//      USB slave driver for STM32
+//
+//=============================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####                                            
+// -------------------------------------------                              
+// This file is part of eCos, the Embedded Configurable Operating System.   
+// Copyright (C) 2008, 2009 Free Software Foundation, 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.,    
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 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 v2.                                               
+//
+// 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):   Chris Holgate
+// Date:        2009-05-19
+// Purpose:     STM32 USB slave driver 
+//
+//####DESCRIPTIONEND####
+//
+//=============================================================================
+
+// Provide external access to the common endpoint 0 data structure.
+extern usbs_control_endpoint cyg_usbs_cortexm_stm32_ep0c;
+
+#endif // CYGONCE_DEVS_USB_CORTEXM_STM32_H
new file mode 100644
--- /dev/null
+++ b/packages/devs/usb/cortexm/stm32/current/src/usb_stm32.c
@@ -0,0 +1,2462 @@
+//=============================================================================
+//
+//      usb_stm32.c
+//
+//      USB slave driver implementation for STM32
+//
+//=============================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####                                            
+// -------------------------------------------                              
+// This file is part of eCos, the Embedded Configurable Operating System.   
+// Copyright (C) 2008, 2009, 2010 Free Software Foundation, 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.,    
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 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 v2.                                               
+//
+// 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):   Chris Holgate
+// Date:        2009-05-19
+// Purpose:     STM32 USB slave driver implementation
+//
+//####DESCRIPTIONEND####
+//
+//=============================================================================
+
+#include <cyg/infra/cyg_type.h>
+#include <cyg/infra/cyg_ass.h>
+#include <cyg/infra/diag.h>
+
+#include <cyg/hal/drv_api.h>
+#include <cyg/hal/hal_arch.h>
+#include <cyg/hal/hal_io.h>
+#include <cyg/hal/hal_if.h>
+
+#include <cyg/io/usb/usb.h>
+#include <cyg/io/usb/usbs.h>
+
+#include <string.h>
+
+#include <pkgconf/io_usb_slave.h>
+#include <pkgconf/devs_usb_cortexm_stm32.h>
+
+//-----------------------------------------------------------------------------
+// Maintenance and debug macros.
+
+#define TODO_USB(_msg_) CYG_ASSERT(false, "TODO (USB) : " _msg_)
+#define FAIL_USB(_msg_) CYG_ASSERT(false, "FAIL (USB) : " _msg_)
+#define ASSERT_USB(_test_, _msg_) CYG_ASSERT(_test_, "FAIL (USB) : " _msg_)
+
+#if defined(CYGBLD_DEVS_USB_CORTEXM_STM32_DEBUG_TRACE)
+#define TRACE_USB(_msg_, _args_...) diag_printf ("STM32 USB : " _msg_, ##_args_)
+#else
+#define TRACE_USB(_msg_, _args_...) while(0){}
+#endif
+
+//-----------------------------------------------------------------------------
+// Shorthand for some of the configuration options.
+
+#define USB_BASE     CYGHWR_HAL_STM32_USB
+#define USB_RAM_BASE CYGHWR_HAL_STM32_USB_CAN_SRAM
+#define USB_RAM_SIZE 512
+#define USB_EPNUM    (1 + CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM + CYGNUM_DEVS_USB_CORTEXM_STM32_RXEP_NUM)
+
+//-----------------------------------------------------------------------------
+// Work out the bus clock frequencies and external timing constraints.
+// NOTE: These require that the clock source is set to HSE, which should be
+// forced by the CDL.
+
+#define PLL_FREQ (CYGARC_HAL_CORTEXM_STM32_INPUT_CLOCK * CYGHWR_HAL_CORTEXM_STM32_CLOCK_PLL_MUL)
+
+#define APB1_FREQ ((CYGARC_HAL_CORTEXM_STM32_INPUT_CLOCK * CYGHWR_HAL_CORTEXM_STM32_CLOCK_PLL_MUL) / \
+  (CYGHWR_HAL_CORTEXM_STM32_CLOCK_HCLK_DIV * CYGHWR_HAL_CORTEXM_STM32_CLOCK_PCLK1_DIV)) 
+
+#define USB_TSTARTUP 1
+
+//-----------------------------------------------------------------------------
+// Provide macros for accessing the buffer layout pseudo-registers.
+
+// Pseudo-registers for single buffer configurations.
+#define USB_RAM_SB_TXADDR(__ep)        ((__ep)*16)
+#define USB_RAM_SB_TXCOUNT(__ep)       ((__ep)*16+4)
+#define USB_RAM_SB_RXADDR(__ep)        ((__ep)*16+8)
+#define USB_RAM_SB_RXCOUNT(__ep)       ((__ep)*16+12)
+
+// Pseudo-registers for double buffer configurations.
+#define USB_RAM_DB_TXADDR(__ep,__buf)  ((__ep)*16+(__buf)*8)
+#define USB_RAM_DB_TXCOUNT(__ep,__buf) ((__ep)*16+(__buf)*8+4)
+#define USB_RAM_DB_RXADDR(__ep,__buf)  ((__ep)*16+(__buf)*8)
+#define USB_RAM_DB_RXCOUNT(__ep,__buf) ((__ep)*16+(__buf)*8+4)
+
+#define USB_RAM_XX_RXCOUNT_BLOCKS(__x) VALUE_(10,__x)
+#define USB_RAM_XX_RXCOUNT_COUNT_MASK  VALUE_(0, 0x03FF)
+
+//-----------------------------------------------------------------------------
+// Set up USB I/O pin configurations.
+
+#if (CYGHWR_DEVS_USB_CORTEXM_STM32_DISC_PIN < 0x10)
+#define USB_DISC_PIN CYGHWR_HAL_STM32_GPIO \
+  (A, (CYGHWR_DEVS_USB_CORTEXM_STM32_DISC_PIN & 0x0F), OUT_2MHZ, OUT_PUSHPULL)
+
+#elif (CYGHWR_DEVS_USB_CORTEXM_STM32_DISC_PIN < 0x20)
+#define USB_DISC_PIN CYGHWR_HAL_STM32_GPIO \
+  (B, (CYGHWR_DEVS_USB_CORTEXM_STM32_DISC_PIN & 0x0F), OUT_2MHZ, OUT_PUSHPULL)
+
+#elif (CYGHWR_DEVS_USB_CORTEXM_STM32_DISC_PIN < 0x30)
+#define USB_DISC_PIN CYGHWR_HAL_STM32_GPIO \
+  (C, (CYGHWR_DEVS_USB_CORTEXM_STM32_DISC_PIN & 0x0F), OUT_2MHZ, OUT_PUSHPULL)
+
+#elif (CYGHWR_DEVS_USB_CORTEXM_STM32_DISC_PIN < 0x40)
+#define USB_DISC_PIN CYGHWR_HAL_STM32_GPIO \
+  (D, (CYGHWR_DEVS_USB_CORTEXM_STM32_DISC_PIN & 0x0F), OUT_2MHZ, OUT_PUSHPULL)
+
+#elif (CYGHWR_DEVS_USB_CORTEXM_STM32_DISC_PIN < 0x50)
+#define USB_DISC_PIN CYGHWR_HAL_STM32_GPIO \
+  (E, (CYGHWR_DEVS_USB_CORTEXM_STM32_DISC_PIN & 0x0F), OUT_2MHZ, OUT_PUSHPULL)
+
+#elif (CYGHWR_DEVS_USB_CORTEXM_STM32_DISC_PIN < 0x60)
+#define USB_DISC_PIN CYGHWR_HAL_STM32_GPIO \
+  (F, (CYGHWR_DEVS_USB_CORTEXM_STM32_DISC_PIN & 0x0F), OUT_2MHZ, OUT_PUSHPULL)
+
+#elif (CYGHWR_DEVS_USB_CORTEXM_STM32_DISC_PIN < 0x70)
+#define USB_DISC_PIN CYGHWR_HAL_STM32_GPIO \
+  (G, (CYGHWR_DEVS_USB_CORTEXM_STM32_DISC_PIN & 0x0F), OUT_2MHZ, OUT_PUSHPULL)
+#endif
+
+#define USB_DP_PIN CYGHWR_HAL_STM32_GPIO (A, 12, IN, AIN)
+#define USB_DM_PIN CYGHWR_HAL_STM32_GPIO (A, 11, IN, AIN)
+
+//=============================================================================
+// Define USB transmit endpoint data structures.
+//=============================================================================
+
+typedef enum {
+  TXTR_FLAGS_NONE   = 0x00, // No flags set.
+  TXTR_FLAGS_ZLPKT  = 0x01, // Set to enable zero length packet termination.
+  TXTR_FLAGS_DBUF   = 0x02, // Set to enable doubtle buffered operation.
+} txtr_flags;
+
+typedef enum {
+  TXTR_STATE_RESET,         // TX transaction reset state. 
+  TXTR_STATE_IDLE,          // TX transaction idle (between transactions).
+  TXTR_STATE_SB_NEXT_PKT,   // TX transaction send next packet (single buffer).
+  TXTR_STATE_SB_ZERO_PKT,   // TX transaction send zero length packet (single buffer). 
+  TXTR_STATE_SB_DONE,       // TX transaction complete (single buffer).
+  TXTR_STATE_DB_FIRST_PKT,  // TX transaction queue first packet (double buffer).
+  TXTR_STATE_DB_NEXT_PKT,   // TX transaction queue next packet (double buffer).
+  TXTR_STATE_DB_ZERO_PKT,   // TX transaction queue zero length packet (double buffer).
+  TXTR_STATE_DB_LAST_PKT,   // TX transaction send last queued packet (double buffer).
+  TXTR_STATE_DB_DONE,       // TX transaction complete (double buffer).
+} txtr_state;
+
+typedef struct txtr_impl {
+  txtr_state        state;             // Current transaction state.
+  txtr_flags        flags;             // Transaction flags.
+  cyg_uint16        status;            // Return status. 
+  cyg_uint8         ep_num;            // Endpoint number.
+  const cyg_uint8*  buf_ptr;           // Pointer to next user buffer location.
+  cyg_uint32        buf_size;          // Size of user transmit buffer.
+  cyg_uint32        bytes_sent;        // Track number of bytes sent. 
+} txtr_impl;
+
+typedef struct txep_impl {
+  usbs_tx_endpoint  common;            // High level driver data.
+  txtr_impl         txtr;              // Transaction data.
+} txep_impl;
+
+//=============================================================================
+// Define USB receive endpoint data structures.
+//=============================================================================
+
+typedef enum {
+  RXTR_FLAGS_NONE   = 0x00, // No flags set.
+  RXTR_FLAGS_ZLPKT  = 0x01, // Set to enable zero length packet termination.
+  RXTR_FLAGS_DBUF   = 0x02, // Set to enable doubtle buffered operation.
+} rxtr_flags;
+
+typedef enum {
+  RXTR_STATE_RESET,         // RX transaction reset state.
+  RXTR_STATE_IDLE,          // RX transaction idle (between transactions).
+  RXTR_STATE_SB_NEXT_PKT,   // RX transaction get next packet (single buffer).
+  RXTR_STATE_DB_NEXT_PKT,   // RX transaction get next packet (double buffer).
+} rxtr_state;
+
+typedef struct rxtr_impl {
+  rxtr_state        state;             // Current transaction state.
+  rxtr_flags        flags;             // Transaction flags.
+  cyg_uint16        status;            // Return status. 
+  cyg_uint8         ep_num;            // Endpoint number.
+  cyg_uint8*        buf_ptr;           // Pointer to next user buffer location.
+  cyg_uint32        buf_size;          // Size of receive buffer.
+  cyg_uint32        bytes_rcvd;        // Track number of bytes received.
+} rxtr_impl;
+
+typedef struct rxep_impl {
+  usbs_rx_endpoint  common;            // High level driver data.
+  rxtr_impl         rxtr;              // Transaction data.
+} rxep_impl;
+
+//=============================================================================
+// Instantiate USB control endpoint data structure.
+//=============================================================================
+
+static void stm32_usb_start (usbs_control_endpoint*);
+static void stm32_usb_poll  (usbs_control_endpoint*);
+static usbs_rx_endpoint* stm32_usb_get_rxep (usbs_control_endpoint*, cyg_uint8);
+static usbs_tx_endpoint* stm32_usb_get_txep (usbs_control_endpoint*, cyg_uint8);
+
+// Track the control endpoint state.
+typedef enum {
+  CTRLEP_MSG_STATE_IDLE,
+  CTRLEP_MSG_STATE_IN_DATA,
+  CTRLEP_MSG_STATE_IN_STATUS,
+  CTRLEP_MSG_STATE_OUT_DATA,
+  CTRLEP_MSG_STATE_OUT_STATUS,
+  CTRLEP_MSG_STATE_CTRL_ACK,
+} ctrlep_msg_states;
+
+// Provide the control message buffer.
+static cyg_uint8 ctrlep_msg_buffer [CYGNUM_DEVS_USB_CORTEXM_STM32_EPO_MAX_MSG_SIZE];
+
+// Provide STM32 control endpoint implementation.
+typedef struct ctrlep_impl {
+  usbs_control_endpoint   common;      // High level driver data.
+  txtr_impl               txtr;        // Transmit transaction data.
+  rxtr_impl               rxtr;        // Receive transaction data.
+  ctrlep_msg_states       msg_state;   // Control endpoint messaging state.
+} ctrlep_impl;
+
+// Instantiate control endpoint data structure.
+static ctrlep_impl ctrlep = {
+  { // Set up data for common high level driver.
+    state                  : USBS_STATE_POWERED,
+    enumeration_data       : (usbs_enumeration_data*) 0,
+    start_fn               : &stm32_usb_start,
+    poll_fn                : &stm32_usb_poll,
+    interrupt_vector       : CYGNUM_HAL_INTERRUPT_USB_LP,
+    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,
+    get_rxep_fn            : &stm32_usb_get_rxep,
+    get_txep_fn            : &stm32_usb_get_txep,
+  },
+  { // Initialise transmit transaction data.
+    state                  : TXTR_STATE_RESET,
+    flags                  : TXTR_FLAGS_ZLPKT,
+    status                 : ENOERR,
+    ep_num                 : 0,
+  },
+  { // Initialise receive transaction data.
+    state                  : RXTR_STATE_RESET,
+    flags                  : RXTR_FLAGS_ZLPKT,
+    status                 : ENOERR,
+    ep_num                 : 0,
+  },
+  msg_state                : CTRLEP_MSG_STATE_IDLE,
+};
+
+extern usbs_control_endpoint cyg_usbs_cortexm_stm32_ep0c __attribute__((alias ("ctrlep")));
+
+//=============================================================================
+// ISR/DSR shared data structure.  All data which is shared between ISR and
+// DSR contexts is wrapped up in this data structure to make synchronisation
+// between the two more manageable.
+//=============================================================================
+
+typedef enum {
+  ISR_FLAGS_CLEARED       = 0x00,      // No flags set.
+  ISR_FLAGS_SETUP_READY   = 0x01,      // Setup packet ready in staging buffer.
+  ISR_FLAGS_DEVICE_RESET  = 0x02,      // Device reset detected.
+} isr_flags;
+
+typedef struct isr_shared_data {
+  isr_flags               flags;       // Flags indicating events pending.
+  cyg_uint8               txtr_done;   // Flags for indicating TX complete.
+  cyg_uint8               rxtr_done;   // Flags for indicating RX complete.
+} isr_shared_data;
+
+static isr_shared_data isr_shared = {
+  flags                    : ISR_FLAGS_CLEARED,
+  txtr_done                : 0,
+  rxtr_done                : 0,
+};
+
+//=============================================================================
+// USB driver internal state variables.
+//=============================================================================
+
+// Buffer management.
+static cyg_uint16 stm32_usb_buf_offset;
+static cyg_uint16 stm32_usb_buf_sizes [USB_EPNUM];
+
+// Endpoint descriptors.
+static txep_impl  txep_list [CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM];
+static rxep_impl  rxep_list [CYGNUM_DEVS_USB_CORTEXM_STM32_RXEP_NUM];
+static txep_impl* txep_map  [15];
+static rxep_impl* rxep_map  [15];
+
+// Interrupts and synchronisation primitives.
+static cyg_interrupt   interrupt_data;
+static cyg_handle_t    interrupt_handle;
+static cyg_uint32      interrupt_mask_count;
+
+//=============================================================================
+// Provide inlineable functions for implementing counted interrupt masking.
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// Mask interrupts if not already masked.
+
+static inline void stm32_usb_request_intr_mask
+  (void)
+{
+  cyg_scheduler_lock();
+  if (interrupt_mask_count++ == 0) {
+    cyg_drv_interrupt_mask (CYGNUM_HAL_INTERRUPT_USB_LP);
+  }
+  cyg_scheduler_unlock();
+}
+
+//-----------------------------------------------------------------------------
+// Release interrupt mask.  Interrupts will be unmasked once all mask requests
+// have been released.
+
+static inline void stm32_usb_release_intr_mask
+  (void)
+{
+  cyg_scheduler_lock();
+  if (interrupt_mask_count == 0) {
+    FAIL_USB ("Interrupt mask counter decremented through 0.");
+  }
+  else if (--interrupt_mask_count == 0) {
+    cyg_drv_interrupt_unmask (CYGNUM_HAL_INTERRUPT_USB_LP);
+  }
+  cyg_scheduler_unlock();
+}
+
+//=============================================================================
+// Provide inlineable functions for setting the 'flip bit' register values.
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// Set the transmit status bits to the desired value. 
+
+static inline void stm32_usb_set_txep_status
+  (cyg_uint32 ep, cyg_uint32 txep_status)
+{
+  cyg_uint32 reg_val;
+  HAL_READ_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_EPXR (ep), reg_val);
+  reg_val &= ~((cyg_uint32) (CYGHWR_HAL_STM32_USB_EPXR_STATRX_MASK |
+    CYGHWR_HAL_STM32_USB_EPXR_DTOGTX | CYGHWR_HAL_STM32_USB_EPXR_DTOGRX)); 
+  reg_val ^= txep_status & CYGHWR_HAL_STM32_USB_EPXR_STATTX_MASK;
+  HAL_WRITE_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_EPXR (ep), reg_val);
+}
+
+//-----------------------------------------------------------------------------
+// Set the receive status bits to the desired value. 
+
+static inline void stm32_usb_set_rxep_status
+  (cyg_uint32 ep, cyg_uint32 rxep_status)
+{
+  cyg_uint32 reg_val;
+  HAL_READ_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_EPXR (ep), reg_val);
+  reg_val &= ~((cyg_uint32) (CYGHWR_HAL_STM32_USB_EPXR_STATTX_MASK |
+    CYGHWR_HAL_STM32_USB_EPXR_DTOGTX | CYGHWR_HAL_STM32_USB_EPXR_DTOGRX)); 
+  reg_val ^= rxep_status & CYGHWR_HAL_STM32_USB_EPXR_STATRX_MASK;
+  HAL_WRITE_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_EPXR (ep), reg_val);
+}
+
+//-----------------------------------------------------------------------------
+// Assign conventional bits in the endpoint status registers without touching
+// the flip-bit values.
+
+static inline void stm32_usb_assign_epxr
+  (cyg_uint32 ep, cyg_uint32 epxr_val)
+{
+  epxr_val &= ~((cyg_uint32) (CYGHWR_HAL_STM32_USB_EPXR_STATTX_MASK |
+    CYGHWR_HAL_STM32_USB_EPXR_STATRX_MASK | CYGHWR_HAL_STM32_USB_EPXR_DTOGTX | 
+    CYGHWR_HAL_STM32_USB_EPXR_DTOGRX));
+  HAL_WRITE_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_EPXR (ep), epxr_val);
+}
+
+//-----------------------------------------------------------------------------
+// Set specified conventional bits in the endpoint status registers.
+
+static inline void stm32_usb_set_epxr_bits
+  (cyg_uint32 ep, cyg_uint32 epxr_mask)
+{
+  cyg_uint32 reg_val;
+  HAL_READ_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_EPXR (ep), reg_val);
+  reg_val |= epxr_mask;
+  stm32_usb_assign_epxr (ep, reg_val);
+}
+
+//-----------------------------------------------------------------------------
+// Clear specified conventional bits in the endpoint status registers.
+
+static inline void stm32_usb_clear_epxr_bits
+  (cyg_uint32 ep, cyg_uint32 epxr_mask)
+{
+  cyg_uint32 reg_val;
+  HAL_READ_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_EPXR (ep), reg_val);
+  reg_val &= ~epxr_mask;
+  stm32_usb_assign_epxr (ep, reg_val);
+}
+
+//-----------------------------------------------------------------------------
+// Set specified toggle bits in the endpoint status registers.
+
+static inline void stm32_usb_set_epxr_toggle
+  (cyg_uint32 ep, cyg_uint32 epxr_mask)
+{
+  cyg_uint32 reg_val;
+  HAL_READ_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_EPXR (ep), reg_val);
+  reg_val &= epxr_mask | ~((cyg_uint32) (CYGHWR_HAL_STM32_USB_EPXR_STATTX_MASK | 
+    CYGHWR_HAL_STM32_USB_EPXR_STATRX_MASK | CYGHWR_HAL_STM32_USB_EPXR_DTOGTX | 
+    CYGHWR_HAL_STM32_USB_EPXR_DTOGRX)); 
+  reg_val ^= epxr_mask;
+  HAL_WRITE_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_EPXR (ep), reg_val);
+}
+
+//-----------------------------------------------------------------------------
+// Clear specified toggle bits in the endpoint status registers.
+
+static inline void stm32_usb_clear_epxr_toggle
+  (cyg_uint32 ep, cyg_uint32 epxr_mask)
+{
+  cyg_uint32 reg_val;
+  HAL_READ_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_EPXR (ep), reg_val);
+  reg_val &= epxr_mask | ~((cyg_uint32) (CYGHWR_HAL_STM32_USB_EPXR_STATTX_MASK | 
+    CYGHWR_HAL_STM32_USB_EPXR_STATRX_MASK | CYGHWR_HAL_STM32_USB_EPXR_DTOGTX | 
+    CYGHWR_HAL_STM32_USB_EPXR_DTOGRX)); 
+  HAL_WRITE_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_EPXR (ep), reg_val);
+}
+
+//-----------------------------------------------------------------------------
+// Flip specified toggle bits in the endpoint status registers.
+
+static inline void stm32_usb_flip_epxr_toggle
+  (cyg_uint32 ep, cyg_uint32 epxr_mask)
+{
+  cyg_uint32 reg_val;
+  HAL_READ_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_EPXR (ep), reg_val);
+  reg_val &= ~((cyg_uint32) (CYGHWR_HAL_STM32_USB_EPXR_STATTX_MASK | 
+    CYGHWR_HAL_STM32_USB_EPXR_STATRX_MASK | CYGHWR_HAL_STM32_USB_EPXR_DTOGTX | 
+    CYGHWR_HAL_STM32_USB_EPXR_DTOGRX));
+  reg_val |= epxr_mask; 
+  HAL_WRITE_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_EPXR (ep), reg_val);
+}
+
+//=============================================================================
+// The following set of functions provide buffer management capabilities for
+// allocating and then accessing USB buffers in the dual-port buffer RAM.
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// Get the allocated buffer size for a specified endpoint.
+
+static inline cyg_uint32 stm32_usb_buf_get_size
+  (cyg_uint32 ep)
+{
+  return (stm32_usb_buf_sizes[ep]);
+}
+
+//-----------------------------------------------------------------------------
+// Add a new set of endpoint buffers for the specified endpoint.
+
+static cyg_bool stm32_usb_buf_add_ep
+  (cyg_uint32 ep, cyg_uint32 ep_buf_size, cyg_bool is_tx, cyg_bool is_rx, cyg_bool is_db)
+{
+  cyg_uint32 alloc_size;
+  cyg_uint32 rx_blocks;
+
+  // Check for valid configuration.
+  if ((is_db && is_tx && is_rx) || !(is_tx || is_rx)) {
+    FAIL_USB ("Invalid buffer configuration.");
+    goto failed;
+  }
+
+  // Round up the allocation size so that it matches a valid RX block size.
+  alloc_size = (ep_buf_size & 1) ? ep_buf_size + 1 : ep_buf_size;
+  if ((alloc_size > 62) && (alloc_size & 31)) {
+    alloc_size &= ~31;
+    alloc_size += 32;
+  }
+
+  // Check to see if there is enough RAM available.
+  if (is_db || (is_tx && is_rx)) {
+    if (stm32_usb_buf_offset + 2 * alloc_size > USB_RAM_SIZE) {
+      FAIL_USB ("Insufficient endpoint RAM for configuration.");
+      goto failed;
+    }
+  }
+  else {  
+    if (stm32_usb_buf_offset + alloc_size > USB_RAM_SIZE) {
+      FAIL_USB ("Insufficient endpoint RAM for configuration.");
+      goto failed;
+    }
+  }
+
+  // Calculate the blocks (size + num) field for the receive count register.
+  rx_blocks = (alloc_size <= 62) ? (alloc_size / 2) : (31 + alloc_size / 32);
+  stm32_usb_buf_sizes[ep] = (cyg_uint16) ep_buf_size;
+ 
+  // Program up the pseudo-registers for double buffered transmit.
+  if (is_db && is_tx) {
+    HAL_WRITE_UINT32 (USB_RAM_BASE + USB_RAM_DB_TXADDR (ep, 0), stm32_usb_buf_offset);
+    HAL_WRITE_UINT32 (USB_RAM_BASE + USB_RAM_DB_TXCOUNT (ep, 0), 0);
+    stm32_usb_buf_offset += (cyg_uint16) alloc_size;
+    HAL_WRITE_UINT32 (USB_RAM_BASE + USB_RAM_DB_TXADDR (ep, 1), stm32_usb_buf_offset);
+    HAL_WRITE_UINT32 (USB_RAM_BASE + USB_RAM_DB_TXCOUNT (ep, 1), 0);
+    stm32_usb_buf_offset += (cyg_uint16) alloc_size;
+  }
+
+  // Program up the pseudo-registers for double buffered receive.
+  else if (is_db) {
+    HAL_WRITE_UINT32 (USB_RAM_BASE + USB_RAM_DB_RXADDR (ep, 0), stm32_usb_buf_offset);
+    HAL_WRITE_UINT32 (USB_RAM_BASE + USB_RAM_DB_RXCOUNT (ep, 0), USB_RAM_XX_RXCOUNT_BLOCKS (rx_blocks));
+    stm32_usb_buf_offset += (cyg_uint16) alloc_size;
+    HAL_WRITE_UINT32 (USB_RAM_BASE + USB_RAM_DB_RXADDR (ep, 1), stm32_usb_buf_offset);
+    HAL_WRITE_UINT32 (USB_RAM_BASE + USB_RAM_DB_RXCOUNT (ep, 1), USB_RAM_XX_RXCOUNT_BLOCKS (rx_blocks));
+    stm32_usb_buf_offset += (cyg_uint16) alloc_size;
+  }
+
+  // Program up the pseudo-registers for single buffered transmit/receive.
+  else {
+    if (is_tx) {
+      HAL_WRITE_UINT32 (USB_RAM_BASE + USB_RAM_SB_TXADDR (ep), stm32_usb_buf_offset);
+      HAL_WRITE_UINT32 (USB_RAM_BASE + USB_RAM_SB_TXCOUNT (ep), 0);
+      stm32_usb_buf_offset += (cyg_uint16) alloc_size;
+    }
+    if (is_rx) {
+      HAL_WRITE_UINT32 (USB_RAM_BASE + USB_RAM_SB_RXADDR (ep), stm32_usb_buf_offset);
+      HAL_WRITE_UINT32 (USB_RAM_BASE + USB_RAM_SB_RXCOUNT (ep), USB_RAM_XX_RXCOUNT_BLOCKS (rx_blocks));
+      stm32_usb_buf_offset += (cyg_uint16) alloc_size;
+    }
+  }
+
+  // Exit on success or failure.
+  return true;
+failed :
+  return false;
+}
+
+//-----------------------------------------------------------------------------
+// Clear the buffer RAM layout for non-control endpoints, prior to setting a
+// new configuration.
+
+static inline void stm32_usb_buf_clear_config
+  (void)
+{
+  cyg_uint32 i;
+
+  // Clear buffer size table for non-control endpoints.
+  for (i = 1; i < USB_EPNUM; i++)
+    stm32_usb_buf_sizes[i] = 0;    
+
+  // Update the free pointer to point to the end of the control endpoint buffers.
+  stm32_usb_buf_offset = 8 * USB_EPNUM + 2 * stm32_usb_buf_sizes[0];
+}
+
+//-----------------------------------------------------------------------------
+// Reset the buffer RAM layout, preallocating the requested buffer area for
+// control endpoint 0.
+
+static inline cyg_bool stm32_usb_buf_reset_ep0
+  (cyg_uint32 ep0_buf_size)
+{
+  cyg_uint32 i;
+
+  // The buffer descriptor table is placed at the start of the dual-port RAM.
+  HAL_WRITE_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_BTABLE, 0);
+  stm32_usb_buf_offset = 8 * USB_EPNUM;
+
+  // Clear buffer size table prior to adding endpoint 0.  A zero in this table
+  // essentially prevents any host-side access to a given endpoint buffer.
+  for (i = 0; i < USB_EPNUM; i++)
+    stm32_usb_buf_sizes[i] = 0;    
+  return stm32_usb_buf_add_ep (0, ep0_buf_size, true, true, false);
+}
+
+//-----------------------------------------------------------------------------
+// Copy data from user memory to a specified USB buffer (double buffered).
+// Returns the number of bytes transferred, which is capped at the buffer
+// size.
+
+static cyg_uint32 __attribute__((hot)) stm32_usb_copy_to_dbuf
+  (const cyg_uint8* src, cyg_uint32 size, cyg_uint32 ep, cyg_uint32 buf)
+{
+  cyg_uint32     data, i;
+  cyg_haladdress waddr;
+
+  // Truncate the size parameter to the buffer length.
+  if (size > stm32_usb_buf_sizes[ep])
+    size = stm32_usb_buf_sizes[ep];
+
+  // Get the offset of the start of the buffer from the buffer tables
+  // and convert it into a host-side address.
+  HAL_READ_UINT32 (USB_RAM_BASE + USB_RAM_DB_TXADDR (ep, buf), data);
+  waddr = USB_RAM_BASE + data * 2;
+
+  // Copy over the data, remembering to skip the half-word gaps.  If the size
+  // is not an integer number of half-words, we stick garbage in the last byte.
+  for (i = (size + 1) >> 1; i != 0; i--) {
+    data = *(src++);
+    data |= ((cyg_uint32) *(src++)) << 8;
+    HAL_WRITE_UINT32 (waddr, data);
+    waddr += 4;
+  }
+
+  // Update the buffer count field and return the buffer size.
+  HAL_WRITE_UINT32 (USB_RAM_BASE + USB_RAM_DB_TXCOUNT (ep, buf), size);
+  return size;
+} 
+
+//-----------------------------------------------------------------------------
+// Copy data from user memory to transmit buffer (single buffered endpoint).
+// Returns the number of bytes transferred, which is capped at the buffer
+// size.
+
+static inline cyg_uint32 stm32_usb_copy_to_sbuf
+  (const cyg_uint8* src, cyg_uint32 size, cyg_uint32 ep)
+{
+  return stm32_usb_copy_to_dbuf (src, size, ep, 0);
+}
+
+//-----------------------------------------------------------------------------
+// Copy data from a specified USB buffer to user memory (double buffered).
+// Returns the number of bytes held by the buffer.  Will not write beyond the
+// end of the user buffer, but if the user buffer is not large enough to hold 
+// the received data (returned value > size) this implies an error condition.
+
+static cyg_uint32 __attribute__((hot)) stm32_usb_copy_from_dbuf
+  (cyg_uint8* dest, cyg_uint32 size, cyg_uint32 ep, cyg_uint32 buf)
+{
+  cyg_uint32     data, bufsize, i;
+  cyg_haladdress raddr;
+
+  // Get the receive buffer size.
+  HAL_READ_UINT32 (USB_RAM_BASE + USB_RAM_DB_RXCOUNT (ep, buf), bufsize);
+  bufsize &= USB_RAM_XX_RXCOUNT_COUNT_MASK;
+
+  // Determine the actual amount of data to transfer.
+  if (bufsize > size)
+    FAIL_USB ("Receive buffer overflow detected.");
+  else
+    size = bufsize;
+
+  // Get the offset of the start of the buffer from the buffer tables
+  // and convert it into a host-side address.
+  HAL_READ_UINT32 (USB_RAM_BASE + USB_RAM_DB_RXADDR (ep, buf), data);
+  raddr = USB_RAM_BASE + data * 2;
+
+  // Copy over the half-word aligned data.
+  for (i = size; i > 1; i -= 2) {
+    HAL_READ_UINT32 (raddr, data);
+    *(dest++) = (cyg_uint8) data;
+    *(dest++) = (cyg_uint8) (data >> 8);
+    raddr += 4;
+  }
+
+  // Copy over the trailing byte if present.
+  if (i) {
+    HAL_READ_UINT32 (raddr, data);
+    *(dest) = (cyg_uint8) data;
+  }
+  return bufsize;
+}
+
+//-----------------------------------------------------------------------------
+// Copy data from a receive buffer to user memory (single buffered endpoint).
+// Returns the number of bytes held by the buffer.  Will not write beyond the
+// end of the user buffer, but if the user buffer is not large enough to hold 
+// the received data (returned value > size) this implies an error condition.
+
+static inline cyg_uint32 stm32_usb_copy_from_sbuf
+  (cyg_uint8* dest, cyg_uint32 size, cyg_uint32 ep)
+{
+  return stm32_usb_copy_from_dbuf (dest, size, ep, 1);
+}
+
+//=============================================================================
+// The following set of functions provide support for managing multi-packet
+// USB transactions.
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// Implement transmit transaction state machine.  This is called on transaction
+// start and then on all applicable interrupt events in order to progress the 
+// transmit transaction.
+
+static cyg_bool stm32_usb_txtr_run
+  (txtr_impl* txtr)
+{
+  cyg_bool completed = false;
+  cyg_uint32 tx_bytes_req, tx_bytes_sent, buf_sel, reg_val;
+
+  switch (txtr->state) {
+
+    // Send next packet (single buffer transfers).
+    case TXTR_STATE_SB_NEXT_PKT:
+      tx_bytes_req = txtr->buf_size - txtr->bytes_sent;
+      tx_bytes_sent = stm32_usb_copy_to_sbuf (txtr->buf_ptr, tx_bytes_req, txtr->ep_num);
+      txtr->buf_ptr += tx_bytes_sent;
+      txtr->bytes_sent += tx_bytes_sent;
+
+      // All bytes sent - see if we need a zero length termination packet.
+      if (txtr->bytes_sent == txtr->buf_size) {
+        if ((tx_bytes_sent == stm32_usb_buf_get_size (txtr->ep_num)) && (txtr->flags & TXTR_FLAGS_ZLPKT))
+          txtr->state = TXTR_STATE_SB_ZERO_PKT;
+        else
+          txtr->state = TXTR_STATE_SB_DONE;
+      }
+
+      // Set buffer valid via the endpoint control register.
+      stm32_usb_set_txep_status (txtr->ep_num, CYGHWR_HAL_STM32_USB_EPXR_STATTX_VALID);
+      break;
+
+    // Send zero length termination packet (single buffer transfers).
+    case TXTR_STATE_SB_ZERO_PKT :
+      txtr->state = TXTR_STATE_SB_DONE;
+      stm32_usb_copy_to_sbuf (txtr->buf_ptr, 0, txtr->ep_num);
+      stm32_usb_set_txep_status (txtr->ep_num, CYGHWR_HAL_STM32_USB_EPXR_STATTX_VALID);
+      break;
+
+    // Complete transaction (single buffer transfers).
+    case TXTR_STATE_SB_DONE :
+      txtr->state = TXTR_STATE_IDLE;
+      completed = true;
+      break;
+
+    // Sets up the first queued packet.  Start by clearing the data toggle
+    // bits and leaving the endpoint in valid state.
+    case TXTR_STATE_DB_FIRST_PKT :
+      HAL_READ_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_EPXR (txtr->ep_num), reg_val);
+      buf_sel = (reg_val & CYGHWR_HAL_STM32_USB_EPXR_DTOGTX) ? 1 : 0;  
+      goto queue_packet;
+
+    // Commit the next queued packet for transmission then queue the next 
+    // packet in the CPU-side buffer.
+    case TXTR_STATE_DB_NEXT_PKT :
+      HAL_READ_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_EPXR (txtr->ep_num), reg_val);
+      buf_sel = (reg_val & CYGHWR_HAL_STM32_USB_EPXR_DTOGTX) ? 0 : 1;  
+      stm32_usb_flip_epxr_toggle (txtr->ep_num, CYGHWR_HAL_STM32_USB_EPXR_SWBUFTX);
+      stm32_usb_set_txep_status (txtr->ep_num, CYGHWR_HAL_STM32_USB_EPXR_STATTX_VALID);
+
+    // This section is common to both DB_NEXT_PKT and DB_FIRST_PKT states.  
+    // However, a straight fallthrough won't work and we have to resort to goto.
+    queue_packet:
+      tx_bytes_req = txtr->buf_size - txtr->bytes_sent;
+      tx_bytes_sent = stm32_usb_copy_to_dbuf (txtr->buf_ptr, tx_bytes_req, txtr->ep_num, buf_sel);
+      txtr->buf_ptr += tx_bytes_sent;
+      txtr->bytes_sent += tx_bytes_sent;
+
+      // All bytes queued - see if we need a zero length termination packet.
+      if (txtr->bytes_sent == txtr->buf_size) {
+        if ((tx_bytes_sent == stm32_usb_buf_get_size (txtr->ep_num)) && (txtr->flags & TXTR_FLAGS_ZLPKT))
+          txtr->state = TXTR_STATE_DB_ZERO_PKT;
+        else
+          txtr->state = TXTR_STATE_DB_LAST_PKT;
+      }
+
+      // More data remaining - send next packet.
+      else {
+        txtr->state = TXTR_STATE_DB_NEXT_PKT;
+      }
+      break;
+
+    // Commit the next queued packet for transmission then queue a
+    // zero length packet in the CPU-side buffer.
+    case TXTR_STATE_DB_ZERO_PKT :
+      HAL_READ_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_EPXR (txtr->ep_num), reg_val);
+      buf_sel = (reg_val & CYGHWR_HAL_STM32_USB_EPXR_DTOGTX) ? 0 : 1;  
+      stm32_usb_flip_epxr_toggle (txtr->ep_num, CYGHWR_HAL_STM32_USB_EPXR_SWBUFTX);
+      stm32_usb_set_txep_status (txtr->ep_num, CYGHWR_HAL_STM32_USB_EPXR_STATTX_VALID);
+      stm32_usb_copy_to_dbuf (txtr->buf_ptr, 0, txtr->ep_num, buf_sel);
+      txtr->state = TXTR_STATE_DB_LAST_PKT;
+      break;
+
+    // Commit the final queued packet for transmission.
+    case TXTR_STATE_DB_LAST_PKT :
+      stm32_usb_flip_epxr_toggle (txtr->ep_num, CYGHWR_HAL_STM32_USB_EPXR_SWBUFTX);
+      stm32_usb_set_txep_status (txtr->ep_num, CYGHWR_HAL_STM32_USB_EPXR_STATTX_VALID);
+      txtr->state = TXTR_STATE_DB_DONE;
+      break;
+
+    // Complete transaction (double buffer transfers).
+    case TXTR_STATE_DB_DONE :
+      stm32_usb_set_txep_status (txtr->ep_num, CYGHWR_HAL_STM32_USB_EPXR_STATTX_NAK);
+      txtr->state = TXTR_STATE_IDLE;
+      completed = true;
+      break;
+
+    // Unknown state - driver error.
+    default :
+      FAIL_USB ("TX transaction in invalid state.");
+      txtr->state = TXTR_STATE_IDLE;
+      txtr->status = EIO;
+      completed = true;
+      break;
+  }
+  return completed;
+}
+
+//-----------------------------------------------------------------------------
+// Initiate a data transmit transaction.  This sets up a new transaction,
+// priming the buffers and then kicking the state machine for the first time.
+
+static cyg_bool stm32_usb_txtr_start
+  (txtr_impl* txtr, cyg_bool int_safe) 
+{
+  cyg_bool completed;
+
+  // Critical section - avoid races with the ISR.
+  if (!int_safe) stm32_usb_request_intr_mask ();
+
+  // Set up double buffer transactions.  We need to prime the buffers which
+  // requires two ticks of the state machine.
+  if (txtr->flags & TXTR_FLAGS_DBUF) {
+    txtr->status = ENOERR;
+    txtr->bytes_sent = 0;
+    txtr->state = TXTR_STATE_DB_FIRST_PKT;
+    stm32_usb_txtr_run (txtr);
+  }
+
+  // Set up single buffer transactions.
+  else {
+    txtr->status = ENOERR;
+    txtr->bytes_sent = 0;
+    txtr->state = TXTR_STATE_SB_NEXT_PKT;
+  }
+
+  // Run the state machine for the first step.
+  completed = stm32_usb_txtr_run (txtr);  
+
+  // Exit critical section.
+  if (!int_safe) stm32_usb_release_intr_mask ();
+  return completed;
+}
+
+//-----------------------------------------------------------------------------
+// Halt a transmit endpoint.  This places the endpoint in the stall condition 
+// and cancels any outstanding transaction, resetting the transaction state 
+// machine.
+
+static void stm32_usb_txep_halt
+  (txep_impl* txep, cyg_bool int_safe)
+{
+  cyg_bool call_completion;
+
+  // Critical section - avoid races with the ISR.
+  if (!int_safe) stm32_usb_request_intr_mask ();
+  stm32_usb_set_txep_status (txep->txtr.ep_num, CYGHWR_HAL_STM32_USB_EPXR_STATTX_DIS);
+
+  // Reset the transaction state.
+  call_completion = ((txep->txtr.state != TXTR_STATE_IDLE) && 
+    (txep->txtr.state != TXTR_STATE_RESET)) ? true : false;
+  txep->txtr.state = TXTR_STATE_RESET;
+  txep->common.halted = true;
+  stm32_usb_clear_epxr_bits (txep->txtr.ep_num, CYGHWR_HAL_STM32_USB_EPXR_CTRTX);
+
+  // Exit critical section.
+  stm32_usb_set_txep_status (txep->txtr.ep_num, CYGHWR_HAL_STM32_USB_EPXR_STATTX_STALL);
+  if (!int_safe) stm32_usb_release_intr_mask ();
+  
+  // Indicate error via the completion callback.
+  if (call_completion) {
+    if (txep->common.complete_fn)
+      (*txep->common.complete_fn) (txep->common.complete_data, -EIO);
+    TRACE_USB ("TX Transaction cancelled on halt.\n");
+  }
+}
+
+//-----------------------------------------------------------------------------
+// Take a transmit endpoint out of halted state.  This takes the endpoint out
+// of the halted state and resets the endpoint.  Note that according to the
+// spec, the endpoint should also be reset and the toggle bits cleared if 
+// 'clear halted' is called while in normal operation.
+
+static void stm32_usb_txep_unhalt
+  (txep_impl* txep, cyg_bool int_safe)
+{
+  cyg_bool call_completion;
+
+  // Critical section - avoid races with the ISR.
+  if (!int_safe) stm32_usb_request_intr_mask ();
+  stm32_usb_set_txep_status (txep->txtr.ep_num, CYGHWR_HAL_STM32_USB_EPXR_STATTX_DIS);
+
+  // Reset the transaction state.
+  call_completion = ((txep->txtr.state != TXTR_STATE_IDLE) && 
+    (txep->txtr.state != TXTR_STATE_RESET)) ? true : false;
+  txep->txtr.state = TXTR_STATE_IDLE;
+  txep->common.halted = false;
+  stm32_usb_clear_epxr_bits (txep->txtr.ep_num, CYGHWR_HAL_STM32_USB_EPXR_CTRTX);
+  stm32_usb_clear_epxr_toggle (txep->txtr.ep_num, 
+    CYGHWR_HAL_STM32_USB_EPXR_DTOGTX | CYGHWR_HAL_STM32_USB_EPXR_SWBUFTX);
+
+  // Exit critical section.
+  stm32_usb_set_txep_status (txep->txtr.ep_num, CYGHWR_HAL_STM32_USB_EPXR_STATTX_NAK);
+  if (!int_safe) stm32_usb_release_intr_mask ();
+  
+  // Indicate error via the completion callback.
+  if (call_completion) {
+    if (txep->common.complete_fn)
+      (*txep->common.complete_fn) (txep->common.complete_data, -EIO);
+    TRACE_USB ("TX Transaction cancelled on resume.\n");
+  }
+}
+
+//-----------------------------------------------------------------------------
+// Implement receive transaction state machine.  This is called on all
+// applicable interrupt events in order to progress the transaction.
+
+static cyg_bool stm32_usb_rxtr_run
+  (rxtr_impl* rxtr)
+{
+  cyg_bool completed = false;
+  cyg_uint32 rx_bytes_req, rx_bytes_rcvd, buf_sel, reg_val;
+
+  switch (rxtr->state) {
+
+    // Receive new packet (single buffer transfers).
+    case RXTR_STATE_SB_NEXT_PKT:
+      rx_bytes_req = rxtr->buf_size - rxtr->bytes_rcvd;
+      rx_bytes_rcvd = stm32_usb_copy_from_sbuf (rxtr->buf_ptr, rx_bytes_req, rxtr->ep_num);
+
+      // Check for buffer overflow condition before updating buffer pointer.
+      if (rx_bytes_rcvd > rx_bytes_req) {
+        FAIL_USB ("RX message exceeds allocated buffer size.");
+        rxtr->state = RXTR_STATE_IDLE;
+        rxtr->status = EMSGSIZE;
+        completed = true;
+        break;
+      }
+      rxtr->buf_ptr += rx_bytes_rcvd;
+      rxtr->bytes_rcvd += rx_bytes_rcvd;
+
+      // Short packet received - transaction complete.
+      if (rx_bytes_rcvd < stm32_usb_buf_get_size (rxtr->ep_num)) {
+        rxtr->state = RXTR_STATE_IDLE;
+        completed = true;
+        break;
+      }
+
+      // All bytes received - see if we need a zero length termination packet.
+      // Note that zero length packets can be treated as normal short packets
+      // here, so an additional state transition is not required.
+      if (rxtr->bytes_rcvd == rxtr->buf_size) {
+        if (!((rx_bytes_rcvd == stm32_usb_buf_get_size (rxtr->ep_num)) && (rxtr->flags & RXTR_FLAGS_ZLPKT))) {
+          rxtr->state = RXTR_STATE_IDLE;
+          completed = true;
+          break;
+        }
+      }
+
+      // Set buffer clear via the endpoint control register.
+      stm32_usb_set_rxep_status (rxtr->ep_num, CYGHWR_HAL_STM32_USB_EPXR_STATRX_VALID);
+      break;
+
+    // Receive new packet (double buffer transfers).  Start by switching buffers.
+    case RXTR_STATE_DB_NEXT_PKT:
+      HAL_READ_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_EPXR (rxtr->ep_num), reg_val);
+      buf_sel = (reg_val & CYGHWR_HAL_STM32_USB_EPXR_DTOGRX) ? 0 : 1;  
+
+      // Get the receive buffer size.
+      HAL_READ_UINT32 (USB_RAM_BASE + USB_RAM_DB_RXCOUNT (rxtr->ep_num, buf_sel), rx_bytes_rcvd);
+      rx_bytes_req = rxtr->buf_size - rxtr->bytes_rcvd;
+      rx_bytes_rcvd &= USB_RAM_XX_RXCOUNT_COUNT_MASK;
+ 
+      // Check for buffer overflow condition before updating buffer pointer.
+      if (rx_bytes_rcvd > rx_bytes_req) {
+        FAIL_USB ("RX message exceeds allocated buffer size.");
+        rxtr->state = RXTR_STATE_IDLE;
+        rxtr->status = EMSGSIZE;
+        completed = true;
+        break;
+      }
+
+      // Short packet received - transaction complete.
+      if (rx_bytes_rcvd < stm32_usb_buf_get_size (rxtr->ep_num)) {
+        rxtr->state = RXTR_STATE_IDLE;
+        completed = true;
+      }
+
+      // All bytes received - see if we need a zero length termination packet.
+      // Note that zero length packets can be treated as normal short packets
+      // here, so an additional state transition is not required.
+      if (rxtr->bytes_rcvd + rx_bytes_rcvd == rxtr->buf_size) {
+        if (!((rx_bytes_rcvd == stm32_usb_buf_get_size (rxtr->ep_num)) && (rxtr->flags & RXTR_FLAGS_ZLPKT))) {
+          rxtr->state = RXTR_STATE_IDLE;
+          completed = true;
+        }
+      }
+
+      // Only enable the next receive buffer if this is not the last packet.
+      if (!completed) {
+        stm32_usb_flip_epxr_toggle (rxtr->ep_num, CYGHWR_HAL_STM32_USB_EPXR_SWBUFRX);
+        stm32_usb_set_rxep_status (rxtr->ep_num, CYGHWR_HAL_STM32_USB_EPXR_STATRX_VALID);
+      }
+
+      // Set the endpoint to NAK on completion.
+      else {
+        stm32_usb_set_rxep_status (rxtr->ep_num, CYGHWR_HAL_STM32_USB_EPXR_STATRX_NAK);
+      }
+
+      // Copy the received data to our local buffer.
+      rx_bytes_rcvd = stm32_usb_copy_from_dbuf (rxtr->buf_ptr, rx_bytes_req, rxtr->ep_num, buf_sel);
+      rxtr->bytes_rcvd += rx_bytes_rcvd;
+      rxtr->buf_ptr += rx_bytes_rcvd;
+      break;
+
+    // Unknown state - driver error.
+    default :
+      FAIL_USB ("RX transaction in invalid state.");
+      rxtr->state = RXTR_STATE_IDLE;
+      rxtr->status = EIO;
+      completed = true;
+      break;
+
+  }
+  return completed;
+}
+
+//-----------------------------------------------------------------------------
+// Initiate a data receive transaction.  This sets up a new transaction,
+// enabling the receive buffers and then waiting for incoming data.
+
+static cyg_bool stm32_usb_rxtr_start
+  (rxtr_impl* rxtr, cyg_bool int_safe)
+{
+  // Critical section - avoid races with the ISR.
+  if (!int_safe) stm32_usb_request_intr_mask ();
+
+  // Set up double buffer transactions.
+  if (rxtr->flags & RXTR_FLAGS_DBUF) {
+    rxtr->state = RXTR_STATE_DB_NEXT_PKT;
+    rxtr->status = ENOERR;
+    rxtr->bytes_rcvd = 0;
+    stm32_usb_flip_epxr_toggle (rxtr->ep_num, CYGHWR_HAL_STM32_USB_EPXR_SWBUFRX);
+    stm32_usb_set_rxep_status (rxtr->ep_num, CYGHWR_HAL_STM32_USB_EPXR_STATRX_VALID);
+  }
+
+  // Set up single buffer transactions.
+  else {
+    rxtr->state = RXTR_STATE_SB_NEXT_PKT;
+    rxtr->status = ENOERR;
+    rxtr->bytes_rcvd = 0;
+    stm32_usb_set_rxep_status (rxtr->ep_num, CYGHWR_HAL_STM32_USB_EPXR_STATRX_VALID);
+  }
+
+  // Exit critical section.
+  if (!int_safe) stm32_usb_release_intr_mask ();
+  return false;
+}
+
+//-----------------------------------------------------------------------------
+// Halt a receive endpoint.  This places the endpoint in the stall condition 
+// and cancels any outstanding transaction, resetting the transaction state 
+// machine.
+
+static void stm32_usb_rxep_halt
+  (rxep_impl* rxep, cyg_bool int_safe)
+{
+  cyg_bool call_completion;
+
+  // Critical section - avoid races with the ISR.
+  if (!int_safe) stm32_usb_request_intr_mask ();
+  stm32_usb_set_rxep_status (rxep->rxtr.ep_num, CYGHWR_HAL_STM32_USB_EPXR_STATRX_DIS);
+
+  // Reset the transaction state.
+  call_completion = ((rxep->rxtr.state != RXTR_STATE_IDLE) && 
+    (rxep->rxtr.state != RXTR_STATE_RESET)) ? true : false;
+  rxep->rxtr.state = RXTR_STATE_RESET;
+  rxep->common.halted = true;
+  stm32_usb_clear_epxr_bits (rxep->rxtr.ep_num, CYGHWR_HAL_STM32_USB_EPXR_CTRRX);
+
+  // Exit critical section.
+  stm32_usb_set_rxep_status (rxep->rxtr.ep_num, CYGHWR_HAL_STM32_USB_EPXR_STATRX_STALL);
+  if (!int_safe) stm32_usb_release_intr_mask ();
+  
+  // Indicate error via the completion callback.
+  if (call_completion) {
+    if (rxep->common.complete_fn)
+      (*rxep->common.complete_fn) (rxep->common.complete_data, -EIO);
+    TRACE_USB ("RX Transaction cancelled on halt.\n");
+  }
+}
+
+//-----------------------------------------------------------------------------
+// Take a receive endpoint out of halted state.  This takes the endpoint out
+// of the halted state and resets the endpoint.  Note that according to the
+// spec, the endpoint should also be reset and the toggle bits cleared if 
+// 'clear halted' is called while in normal operation.
+
+static void stm32_usb_rxep_unhalt
+  (rxep_impl* rxep, cyg_bool int_safe)
+{
+  cyg_bool call_completion;
+
+  // Critical section - avoid races with the ISR.
+  if (!int_safe) stm32_usb_request_intr_mask ();
+  stm32_usb_set_rxep_status (rxep->rxtr.ep_num, CYGHWR_HAL_STM32_USB_EPXR_STATRX_DIS);
+
+  // Reset the transaction state.
+  call_completion = ((rxep->rxtr.state != RXTR_STATE_IDLE) && 
+    (rxep->rxtr.state != RXTR_STATE_RESET)) ? true : false;
+  rxep->rxtr.state = RXTR_STATE_IDLE;
+  rxep->common.halted = false;
+  stm32_usb_clear_epxr_bits (rxep->rxtr.ep_num, CYGHWR_HAL_STM32_USB_EPXR_CTRRX);
+  stm32_usb_clear_epxr_toggle (rxep->rxtr.ep_num, 
+    CYGHWR_HAL_STM32_USB_EPXR_DTOGRX | CYGHWR_HAL_STM32_USB_EPXR_SWBUFRX);
+
+  // Exit critical section.
+  stm32_usb_set_rxep_status (rxep->rxtr.ep_num, CYGHWR_HAL_STM32_USB_EPXR_STATRX_NAK);
+  if (!int_safe) stm32_usb_release_intr_mask ();
+  
+  // Indicate error via the completion callback.
+  if (call_completion) {
+    if (rxep->common.complete_fn)
+      (*rxep->common.complete_fn) (rxep->common.complete_data, -EIO);
+    TRACE_USB ("RX Transaction cancelled on resume.\n");
+  }
+}
+
+//=============================================================================
+// Implement control endpoint protocol handling.
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// Forward endpoint 0 state change notifications.
+
+static void stm32_usb_ctrl_update_state
+  (int new_state, usbs_state_change state_change)
+{
+  int old_state = ctrlep.common.state;
+  ctrlep.common.state = new_state;
+  if (ctrlep.common.state_change_fn)
+    (*ctrlep.common.state_change_fn) (&ctrlep.common, 
+      ctrlep.common.state_change_data, state_change, old_state);
+}
+
+// --------------------------------------------------------------------------
+// Called on completion of an endpoint 0 control request transaction.
+
+static void stm32_usb_ctrl_completed 
+  (int status)
+{
+  ctrlep.msg_state = CTRLEP_MSG_STATE_IDLE;
+  if (ctrlep.common.complete_fn)
+    (*ctrlep.common.complete_fn) (&cyg_usbs_cortexm_stm32_ep0c, -status);
+  if (status != 0)
+    TRACE_USB ("Transaction failed (status %d).\n", status);
+}
+
+//-----------------------------------------------------------------------------
+// Clear endpoint configuration.  This disables all non-control endpoints,
+// resetting their state.
+
+static void stm32_usb_ctrl_clear_config
+  (void)
+{
+  cyg_uint32 i;
+  txep_impl* txep = txep_list;
+  rxep_impl* rxep = rxep_list;
+
+  // Disable all non-control endpoints, clearing any outstanding interrupts.
+  // This also ensures that the toggle bits are reset to 0.
+  for (i = 1; i < 8; i++) {
+    stm32_usb_set_txep_status (i, CYGHWR_HAL_STM32_USB_EPXR_STATTX_DIS);
+    stm32_usb_set_rxep_status (i, CYGHWR_HAL_STM32_USB_EPXR_STATRX_DIS);
+    stm32_usb_clear_epxr_toggle (i, 
+      CYGHWR_HAL_STM32_USB_EPXR_DTOGTX | CYGHWR_HAL_STM32_USB_EPXR_DTOGRX);
+    stm32_usb_clear_epxr_bits (i, 0xFFFF);
+  }
+
+  // Reset the transaction state for all transmit endpoints.
+  for (i = 0; i < CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM; i++) {
+    txep->common.halted = true;
+    txep->txtr.state = TXTR_STATE_RESET;
+    txep++;
+  }
+
+  // Reset the transaction state for all receive endpoints.
+  for (i = 0; i < CYGNUM_DEVS_USB_CORTEXM_STM32_RXEP_NUM; i++) {
+    rxep->common.halted = true;
+    rxep->rxtr.state = RXTR_STATE_RESET;
+    rxep++;
+  }
+
+  // Clear the logical to physical endpoint mappings.
+  for (i = 0; i < 15; i++) {
+    txep_map [i] = NULL;
+    rxep_map [i] = NULL;
+  }
+
+  // Clear buffer RAM for non-control endpoints (preseverves endpoint 0).
+  stm32_usb_buf_clear_config ();
+}
+
+//-----------------------------------------------------------------------------
+// Reset the control endpoint - placing the device in the 'default' state.
+
+static void stm32_usb_ctrl_reset
+  (void)
+{
+  // Re-enable the device on address 0 only.
+  HAL_WRITE_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_DADDR, CYGHWR_HAL_STM32_USB_DADDR_EF);
+  
+  // Reset the buffer RAM layout, allocating only the endpoint 0 buffers.
+  stm32_usb_buf_reset_ep0 (ctrlep.common.enumeration_data->device.max_packet_size);
+
+  // Clear the previous endpoint configuration.
+  stm32_usb_ctrl_clear_config ();
+
+  // Ensure endpoint 0 is assigned the correct endpoint type.
+  stm32_usb_assign_epxr (0, CYGHWR_HAL_STM32_USB_EPXR_EPTYPE_CTRL);
+
+  // Reset endpoint 0, leaving it configured for valid incoming packets.
+  stm32_usb_set_txep_status (0, CYGHWR_HAL_STM32_USB_EPXR_STATTX_NAK);
+  stm32_usb_set_rxep_status (0, CYGHWR_HAL_STM32_USB_EPXR_STATRX_VALID);
+  stm32_usb_clear_epxr_toggle (0, 
+    CYGHWR_HAL_STM32_USB_EPXR_DTOGTX | CYGHWR_HAL_STM32_USB_EPXR_DTOGRX);
+  stm32_usb_clear_epxr_bits (0, 
+    CYGHWR_HAL_STM32_USB_EPXR_CTRTX | CYGHWR_HAL_STM32_USB_EPXR_CTRRX);
+  
+  // Enter default state.
+  stm32_usb_ctrl_update_state (USBS_STATE_DEFAULT, USBS_STATE_CHANGE_RESET);
+}
+
+//-----------------------------------------------------------------------------
+// Fill the transmit control message buffer.  This function is used to
+// assemble control response messages for transmission to the host.
+
+static cyg_uint32 stm32_usb_ctrl_fill_msg_buffer 
+  (void)
+{
+  cyg_uint32 msg_length = 0;
+
+  // Deal with commands which place their data directly into the staging buffer.
+  if (ctrlep.common.buffer == ctrlep_msg_buffer) {
+    msg_length = ctrlep.common.buffer_size;
+  }
+
+  // Loop until there are no more message segments to append.  For buffer
+  // overflows, debug builds will assert and production builds will send a 
+  // truncated message.
+  else do {
+    if (ctrlep.common.buffer_size != 0) {
+      if (msg_length + ctrlep.common.buffer_size <= CYGNUM_DEVS_USB_CORTEXM_STM32_EPO_MAX_MSG_SIZE) {
+        memcpy (ctrlep_msg_buffer + msg_length, ctrlep.common.buffer, ctrlep.common.buffer_size);
+        msg_length += ctrlep.common.buffer_size;
+      }
+      else {
+        FAIL_USB ("Endpoint 0 transmit buffer overflow.");
+        break;
+      }
+
+      // Get the next message segment, if available.
+      if (ctrlep.common.fill_buffer_fn)
+        (*ctrlep.common.fill_buffer_fn) (&cyg_usbs_cortexm_stm32_ep0c);
+    }
+  } while (ctrlep.common.fill_buffer_fn);
+  
+  return msg_length;
+}
+
+//-----------------------------------------------------------------------------
+// Handle set configuration setup packets.  This is implemented in the low
+// level driver because this is the point at which we need to configure the
+// endpoint RAM and realise the endpoints.  It 'falls through' to the high
+// level handlers so that they can deal with notifying the application.
+// Note: Only a single (default) interface is supported per configuration.
+
+static usbs_control_return stm32_usb_ctrl_set_config
+  (cyg_uint32 config_id)
+{
+  cyg_uint32 i, j;  
+  cyg_uint32 start_interface, start_endpoint, num_endpoints;
+  cyg_uint32 total_interfaces, total_endpoints;
+  cyg_uint32 txep_count = 0;
+  cyg_uint32 rxep_count = 0;
+  cyg_bool   alloc_ok;
+  const usb_configuration_descriptor* configurations;
+  const usb_interface_descriptor*     interfaces;
+  const usb_endpoint_descriptor*      endpoints;
+
+  // Check for valid config.
+  if ((config_id <= 0) || 
+    (config_id > ctrlep.common.enumeration_data->device.number_configurations))
+    return USBS_CONTROL_RETURN_UNKNOWN;
+
+  // Clear the previous endpoint configuration.
+  stm32_usb_ctrl_clear_config ();
+
+  // Get the base pointers for the descriptor tables.
+  configurations = ctrlep.common.enumeration_data->configurations;
+  interfaces     = ctrlep.common.enumeration_data->interfaces;
+  endpoints      = ctrlep.common.enumeration_data->endpoints;
+
+  start_interface = 0;
+  start_endpoint  = 0;
+  num_endpoints = 0;
+  total_interfaces = ctrlep.common.enumeration_data->total_number_interfaces; 
+  total_endpoints  = ctrlep.common.enumeration_data->total_number_endpoints;
+
+  // Skip over the descriptors until we get to the ones we want.
+  for (i = 0; i < (config_id-1); i++) {
+    ASSERT_USB (start_interface + configurations[i].number_interfaces <= total_interfaces,
+      "Invalid number of interfaces in enumeration data.");
+    for (j = 0; j < configurations[i].number_interfaces; j++) {
+      start_endpoint += interfaces[start_interface+j].number_endpoints;
+      ASSERT_USB (start_endpoint < total_endpoints,
+        "Invalid number of endpoints in enumeration data.");
+    }
+    start_interface += configurations[i].number_interfaces;
+  }
+
+  // Determine the number of endpoint descriptors which need to be included for
+  // all interfaces within the specified configuration.
+  ASSERT_USB (start_interface + configurations[i].number_interfaces <= total_interfaces,
+    "Invalid number of interfaces in enumeration data.");
+  for (j = 0; j < configurations[i].number_interfaces; j++) {
+    num_endpoints += interfaces[start_interface+j].number_endpoints;
+    ASSERT_USB (start_endpoint + num_endpoints <= total_endpoints,
+      "Invalid number of endpoints in enumeration data.");
+  }
+  TRACE_USB ("Found %d interfaces and %d endpoints for configuration %d.\n",
+    configurations[i].number_interfaces, num_endpoints, config_id);
+
+  // Realise the endpoints for the specified interfaces.  Set up the buffer
+  // RAM and configure them to NAK the host until the higher layer application
+  // is ready to initiate transfers.  Failures to allocate the endpoint buffers
+  // will put the endpoints in the stalled state.
+  for (i = 0; i < num_endpoints; i++) {  
+    const usb_endpoint_descriptor* ep_desc = endpoints + start_endpoint + i;
+    cyg_uint32 pkt_size = ep_desc->max_packet_lo + (((cyg_uint32) ep_desc->max_packet_hi) << 8);
+
+    // Deal with transmit (input) endpoints.
+    if (ep_desc->endpoint & USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN) {
+      if ((ep_desc->endpoint & 0x7F) < 1 || (ep_desc->endpoint & 0x7F) > 15) {
+        FAIL_USB ("Invalid endpoint ID in configuration.");
+        goto out;
+      }
+      if (txep_count >= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM) {
+        FAIL_USB ("Too many TX endpoints in configuration.");
+        goto out;
+      }
+
+      // Set up transmit (input) buffers.
+      if (ep_desc->attributes == USB_ENDPOINT_DESCRIPTOR_ATTR_BULK) {
+        alloc_ok = stm32_usb_buf_add_ep (txep_count + 1, pkt_size, true, false, true);
+        stm32_usb_set_epxr_bits (txep_count + 1, 
+          CYGHWR_HAL_STM32_USB_EPXR_EPTYPE_BULK | CYGHWR_HAL_STM32_USB_EPXR_EPKIND);
+#ifdef CYGHWR_DEVS_USB_CORTEXM_STM32_BULK_TERM_ZLP
+        (txep_list + txep_count)->txtr.flags = TXTR_FLAGS_ZLPKT | TXTR_FLAGS_DBUF;
+#else
+        (txep_list + txep_count)->txtr.flags = TXTR_FLAGS_DBUF;
+#endif
+        TRACE_USB ("Configured BULK IN endpoint ID %d.\n", ep_desc->endpoint & 0x7F);  
+      }
+      else if (ep_desc->attributes == USB_ENDPOINT_DESCRIPTOR_ATTR_INTERRUPT) {
+        alloc_ok = stm32_usb_buf_add_ep (txep_count + 1, pkt_size, true, false, false);
+        (txep_list + txep_count)->txtr.flags = TXTR_FLAGS_NONE;
+        stm32_usb_set_epxr_bits (txep_count + 1, CYGHWR_HAL_STM32_USB_EPXR_EPTYPE_INTR);
+        TRACE_USB ("Configured INTERRUPT IN endpoint ID %d.\n", ep_desc->endpoint & 0x7F);  
+      }
+      else {
+        FAIL_USB ("Isochronous endpoints are not currently supported.");
+        goto out;
+      }
+
+      // Enable endpoints if buffer allocation was OK - stall them otherwise.
+      if (alloc_ok) {
+        txep_map [(ep_desc->endpoint & 0x7F) - 1] = txep_list + txep_count;
+        (txep_list + txep_count)->common.halted = false;
+        (txep_list + txep_count)->txtr.state = TXTR_STATE_IDLE;
+        stm32_usb_set_txep_status (txep_count + 1, CYGHWR_HAL_STM32_USB_EPXR_STATTX_NAK);
+      }
+      else {
+        stm32_usb_set_txep_status (txep_count + 1, CYGHWR_HAL_STM32_USB_EPXR_STATTX_STALL);
+      }
+      stm32_usb_set_epxr_bits (txep_count + 1, (ep_desc->endpoint & 0xF));
+      txep_count++;
+    }
+
+    // Deal with receive (output) endpoints.
+    else {
+      if ((ep_desc->endpoint & 0x7F) < 1 || (ep_desc->endpoint & 0x7F) > 15) {
+        FAIL_USB ("Invalid endpoint ID in configuration.");
+        goto out;
+      }
+      if (rxep_count >= CYGNUM_DEVS_USB_CORTEXM_STM32_RXEP_NUM) {
+        FAIL_USB ("Too many RX endpoints in configuration.");
+        goto out;
+      }
+
+      // Set up receive (output) buffers.
+      if (ep_desc->attributes == USB_ENDPOINT_DESCRIPTOR_ATTR_BULK) {
+        alloc_ok = stm32_usb_buf_add_ep (rxep_count + CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM + 1, 
+          pkt_size, false, true, true);
+        stm32_usb_set_epxr_bits (rxep_count + CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM + 1, 
+          CYGHWR_HAL_STM32_USB_EPXR_EPTYPE_BULK | CYGHWR_HAL_STM32_USB_EPXR_EPKIND);
+#ifdef CYGHWR_DEVS_USB_CORTEXM_STM32_BULK_TERM_ZLP
+        (rxep_list + rxep_count)->rxtr.flags = RXTR_FLAGS_ZLPKT | RXTR_FLAGS_DBUF;
+#else
+        (rxep_list + rxep_count)->rxtr.flags = RXTR_FLAGS_DBUF;
+#endif
+        TRACE_USB ("Configured BULK OUT endpoint ID %d.\n", ep_desc->endpoint & 0x7F);  
+      }
+      else if (ep_desc->attributes == USB_ENDPOINT_DESCRIPTOR_ATTR_INTERRUPT) {
+        alloc_ok = stm32_usb_buf_add_ep (rxep_count + CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM + 1, 
+          pkt_size, false, true, false);
+        (rxep_list + rxep_count)->rxtr.flags = RXTR_FLAGS_NONE;
+        stm32_usb_set_epxr_bits (rxep_count + CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM + 1, 
+          CYGHWR_HAL_STM32_USB_EPXR_EPTYPE_INTR);
+        TRACE_USB ("Configured INTERRUPT OUT endpoint ID %d.\n", ep_desc->endpoint & 0x7F);  
+      }
+      else {
+        FAIL_USB ("Isochronous endpoints are not currently supported.");
+        goto out;
+      }
+
+      // Enable endpoints if buffer allocation was OK - stall them otherwise.
+      if (alloc_ok) {
+        rxep_map [(ep_desc->endpoint & 0x7F) - 1] = rxep_list + rxep_count;
+        (rxep_list + rxep_count)->common.halted = false;
+        (rxep_list + rxep_count)->rxtr.state = RXTR_STATE_IDLE;
+        stm32_usb_set_rxep_status (rxep_count + CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM + 1, 
+          CYGHWR_HAL_STM32_USB_EPXR_STATRX_NAK);
+      }
+      else {
+        stm32_usb_set_rxep_status (rxep_count + CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM + 1, 
+          CYGHWR_HAL_STM32_USB_EPXR_STATRX_STALL);
+      }
+      stm32_usb_set_epxr_bits (rxep_count + CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM + 1, 
+        (ep_desc->endpoint & 0xF));
+      rxep_count++;
+    }
+  }
+
+  // Pass up to higher layers.
+out:
+  return USBS_CONTROL_RETURN_UNKNOWN;
+}
+
+//-----------------------------------------------------------------------------
+// Handle get status setup packets.  This function places the status
+// information directly into the staging buffer.  We deal with the device
+// and endpoint status responses here.  The interface status request is
+// dealt with in the common USB slave layer.
+// TODO - should #define the return fields in usb.h.
+
+static inline usbs_control_return stm32_usb_ctrl_get_status
+  (cyg_uint32 recipient, cyg_uint32 ep_sel)
+{
+  cyg_uint8 dev_state = ctrlep.common.state & USBS_STATE_MASK;
+  usbs_control_return result = USBS_CONTROL_RETURN_UNKNOWN;
+
+  // Device status requests are valid in addressed and configured states.
+  // TODO - remote wakeup is not currently supported by this driver.
+  if (recipient == USB_DEVREQ_RECIPIENT_DEVICE) {
+    if (dev_state == USBS_STATE_ADDRESSED || dev_state == USBS_STATE_CONFIGURED) {
+#ifdef CYGHWR_DEVS_USB_CORTEXM_STM32_SELF_POWERED
+      ctrlep_msg_buffer[0] = 0x01;
+#else
+      ctrlep_msg_buffer[0] = 0x00;
+#endif
+      ctrlep_msg_buffer[1] = 0x00;
+      result = USBS_CONTROL_RETURN_HANDLED;
+    }
+  }
+
+  // Endpoint 0 status requests are valid in addressed and configured states.
+  // Endpoint 0 cannot be halted.  Endpoint number is in the lower 4 bits of the ID.
+  else if (recipient == USB_DEVREQ_RECIPIENT_ENDPOINT && (ep_sel & USB_DEVREQ_INDEX_ENDPOINT_MASK) == 0) {
+    if (dev_state == USBS_STATE_ADDRESSED || dev_state == USBS_STATE_CONFIGURED) {
+      ctrlep_msg_buffer[0] = 0x00;
+      ctrlep_msg_buffer[1] = 0x00;
+      result = USBS_CONTROL_RETURN_HANDLED;
+    }
+  }
+
+  // Non-control endpoint status requests are only valid in the configured state.
+  // Returns the halted state of the requested endpoint.
+  else if (recipient == USB_DEVREQ_RECIPIENT_ENDPOINT) {
+    if (dev_state == USBS_STATE_CONFIGURED) {
+
+      // Transmit (IN) endpoint IDs have the top bit set.
+      if ((ep_sel & USB_DEVREQ_INDEX_DIRECTION_MASK) == USB_DEVREQ_INDEX_DIRECTION_IN) {
+        ep_sel = (ep_sel & USB_DEVREQ_INDEX_ENDPOINT_MASK) - 1;
+        if (txep_map[ep_sel] != NULL) {
+          ctrlep_msg_buffer[0] = (txep_map[ep_sel]->common.halted) ? 0x01 : 0x00; 
+          ctrlep_msg_buffer[1] = 0x00;
+          result = USBS_CONTROL_RETURN_HANDLED;
+        }
+      }
+
+      // Receive (OUT) endpoint IDs have the top bit clear.
+      else {
+        ep_sel = (ep_sel & USB_DEVREQ_INDEX_ENDPOINT_MASK) - 1;
+        if (rxep_map[ep_sel] != NULL) {
+          ctrlep_msg_buffer[0] = (rxep_map[ep_sel]->common.halted) ? 0x01 : 0x00; 
+          ctrlep_msg_buffer[1] = 0x00;
+          result = USBS_CONTROL_RETURN_HANDLED;
+        }
+      }
+    }
+  }
+
+  // Point the endpoint 0 response buffer at the staging buffer.
+  if (result != USBS_CONTROL_RETURN_UNKNOWN) {
+    ctrlep.common.buffer_size = 2;
+    ctrlep.common.buffer = ctrlep_msg_buffer;
+  }
+  return result;
+}
+
+//-----------------------------------------------------------------------------
+// Handle set and clear feature commands.  Since remote wakeup support is not 
+// implemented and interface features are dealt with at a higher layer we only 
+// implement endpoint halting here.  This will only ever be called from within
+// the DSR so is interrupt safe.
+
+static usbs_control_return stm32_usb_ctrl_set_feature
+  (cyg_uint32 recipient, cyg_uint32 ep_sel, cyg_uint32 feature, cyg_bool set)
+{
+  cyg_uint8 dev_state = ctrlep.common.state & USBS_STATE_MASK;
+  usbs_control_return result = USBS_CONTROL_RETURN_UNKNOWN;
+
+  // Non-control endpoint feature control is valid in the configured state.
+  if (recipient == USB_DEVREQ_RECIPIENT_ENDPOINT && 
+    dev_state == USBS_STATE_CONFIGURED && feature == USB_DEVREQ_FEATURE_ENDPOINT_HALT) {
+
+    // Transmit (IN) endpoint IDs have the top bit set.
+    if ((ep_sel & USB_DEVREQ_INDEX_DIRECTION_MASK) == USB_DEVREQ_INDEX_DIRECTION_IN) {
+      ep_sel = (ep_sel & USB_DEVREQ_INDEX_ENDPOINT_MASK) - 1;
+      if (txep_map[ep_sel] != NULL) {
+        if (set)
+          stm32_usb_txep_halt (txep_map[ep_sel], true);
+        else
+          stm32_usb_txep_unhalt (txep_map[ep_sel], true);
+        result = USBS_CONTROL_RETURN_HANDLED;
+      }
+    }
+
+    // Receive (OUT) endpoint IDs have the top bit clear.
+    else {
+      ep_sel = (ep_sel & USB_DEVREQ_INDEX_ENDPOINT_MASK) - 1;
+      if (rxep_map[ep_sel] != NULL) {
+        if (set)
+          stm32_usb_rxep_halt (rxep_map[ep_sel], true);
+        else
+          stm32_usb_rxep_unhalt (rxep_map[ep_sel], true);
+        result = USBS_CONTROL_RETURN_HANDLED;
+      }
+    }
+  }
+  return result;
+}
+
+//-----------------------------------------------------------------------------
+// Process standard endpoint 0 setup packets.
+
+static usbs_control_return stm32_usb_ctrl_setup_standard
+  (void)
+{
+  usb_devreq* req = (usb_devreq*) &ctrlep.common.control_buffer[0];
+  usbs_control_return result = USBS_CONTROL_RETURN_UNKNOWN;
+  cyg_uint32 recipient = req->type & USB_DEVREQ_RECIPIENT_MASK;
+
+  switch (req->request) {
+
+    // Assign device address.  We can't actually update the address register 
+    // until after the full bus handshake has completed, otherwise the ACK 
+    // packet gets lost.
+    case USB_DEVREQ_SET_ADDRESS :
+      result = USBS_CONTROL_RETURN_HANDLED;
+      break;
+
+    // Set device configuration.
+    case USB_DEVREQ_SET_CONFIGURATION :
+      TRACE_USB ("Setting USB configuration = %d\n", (cyg_uint32) req->value_lo);
+      result = stm32_usb_ctrl_set_config (req->value_lo);
+      break;
+
+    // TODO: Interfaces with multiple settings are not currently supported.
+    // If a device only supports a default setting for a specified interface
+    // the spec says a stall should be sent.  
+    case USB_DEVREQ_SET_INTERFACE :
+      TRACE_USB ("Using default interface settings.\n");
+      result = USBS_CONTROL_RETURN_STALL;
+      break;
+ 
+    // Get device status.
+    case USB_DEVREQ_GET_STATUS :
+      result = stm32_usb_ctrl_get_status (recipient, req->index_lo);
+      break;
+
+    // Control endpoint halting.  Halt on 'set feature'.
+    case USB_DEVREQ_SET_FEATURE :
+      result = stm32_usb_ctrl_set_feature (recipient, req->index_lo, req->value_lo, true);
+      break;
+
+    // Control endpoint halting.  Resume on 'clear feature'.
+    case USB_DEVREQ_CLEAR_FEATURE :
+      result = stm32_usb_ctrl_set_feature (recipient, req->index_lo, req->value_lo, false);
+      break;
+
+    // Pass up to the user supplied handler, if present.
+    default :
+      if (ctrlep.common.standard_control_fn)
+        result = (*ctrlep.common.standard_control_fn) 
+          (&ctrlep.common, ctrlep.common.standard_control_data);
+      break; 
+  }
+
+  // If not already handled, pass up to the high level driver.
+  if (result == USBS_CONTROL_RETURN_UNKNOWN)
+    result = usbs_handle_standard_control (&ctrlep.common);
+
+  return result;
+}
+
+//-----------------------------------------------------------------------------
+// Perform initial processing of endpoint 0 setup packets.
+
+static void stm32_usb_ctrl_setup_handler
+  (void)
+{
+  cyg_uint32 req_length, req_type, req_dir;
+  usb_devreq* req = (usb_devreq*) &ctrlep.common.control_buffer[0];
+  usbs_control_return result = USBS_CONTROL_RETURN_UNKNOWN;
+
+  // Extract the required fields from the setup packet.
+  req_length = 8 * (cyg_uint32) req->length_hi + req->length_lo;
+  req_type = req->type & USB_DEVREQ_TYPE_MASK;
+  req_dir = req->type & USB_DEVREQ_DIRECTION_MASK;
+
+  // Always fail the transaction if the requested data stage exceeds the
+  // allocated buffer area.
+  if ((req_dir == USB_DEVREQ_DIRECTION_OUT) && 
+    (req_length > CYGNUM_DEVS_USB_CORTEXM_STM32_EPO_MAX_MSG_SIZE)) {
+    FAIL_USB ("Requested control data stage exceeds message buffer size.");
+    result = USBS_CONTROL_RETURN_STALL;
+  }
+
+  // Pass standard requests to the standard handlers.
+  else if (req_type == USB_DEVREQ_TYPE_STANDARD) {
+    result = stm32_usb_ctrl_setup_standard ();
+  }
+
+  // Hand off non-standard requests to their respective handlers.
+  else {
+    usbs_control_return (*callback_fn) (usbs_control_endpoint*, void*);
+    void* callback_arg;
+    
+    if (req_type == USB_DEVREQ_TYPE_CLASS) {
+      callback_fn  = ctrlep.common.class_control_fn;
+      callback_arg = ctrlep.common.class_control_data;
+    }
+    else if (req_type == USB_DEVREQ_TYPE_VENDOR) {
+      callback_fn  = ctrlep.common.vendor_control_fn;
+      callback_arg = ctrlep.common.vendor_control_data;
+    }
+    else {
+      callback_fn  = ctrlep.common.reserved_control_fn;
+      callback_arg = ctrlep.common.reserved_control_data;
+    }
+    
+    result = (callback_fn) ? (*callback_fn) (&ctrlep.common, callback_arg)
+      : USBS_CONTROL_RETURN_STALL;
+  }
+
+  // If correctly handled, initiate the data transfer phase.  This is only 
+  // called from within the DSR, so the transaction start is interrupt safe.
+  if (result == USBS_CONTROL_RETURN_HANDLED) {
+
+    // ACK the transfer by sending a zero length packet.
+    if (req_length == 0) {
+      ctrlep.msg_state = CTRLEP_MSG_STATE_CTRL_ACK;
+      ctrlep.txtr.state = TXTR_STATE_IDLE;
+      ctrlep.txtr.buf_ptr = ctrlep_msg_buffer;
+      ctrlep.txtr.buf_size = 0;
+      stm32_usb_txtr_start (&ctrlep.txtr, true);
+    }
+
+    // Send the inbound data.
+    else if (req_dir == USB_DEVREQ_DIRECTION_IN) {
+      ctrlep.msg_state = CTRLEP_MSG_STATE_IN_DATA;
+      ctrlep.txtr.state = TXTR_STATE_IDLE;
+      ctrlep.txtr.buf_ptr = ctrlep_msg_buffer;
+      ctrlep.txtr.buf_size = stm32_usb_ctrl_fill_msg_buffer ();
+      stm32_usb_txtr_start (&ctrlep.txtr, true);
+    }
+
+    // Receive outbound data from the host.    
+    else {
+      ctrlep.msg_state = CTRLEP_MSG_STATE_OUT_DATA;
+      ctrlep.rxtr.state = RXTR_STATE_IDLE;
+      ctrlep.rxtr.buf_ptr = ctrlep_msg_buffer;
+      ctrlep.rxtr.buf_size = CYGNUM_DEVS_USB_CORTEXM_STM32_EPO_MAX_MSG_SIZE;
+      stm32_usb_rxtr_start (&ctrlep.rxtr, true);
+    }
+  }
+
+  // Stall endpoint on request. 
+  else if (result == USBS_CONTROL_RETURN_STALL) {
+    stm32_usb_set_txep_status (0, CYGHWR_HAL_STM32_USB_EPXR_STATTX_STALL);
+    stm32_usb_set_rxep_status (0, CYGHWR_HAL_STM32_USB_EPXR_STATRX_STALL);
+  }
+
+  // Unsupported setup commands also stall the control endpoint.
+  else {
+    TRACE_USB ("Stall EP0 on UNKNOWN control message : %02X %02X %02X %02X %02X %02X %02X %02X\n",
+      ctrlep.common.control_buffer[0], ctrlep.common.control_buffer[1],
+      ctrlep.common.control_buffer[2], ctrlep.common.control_buffer[3],
+      ctrlep.common.control_buffer[4], ctrlep.common.control_buffer[5],
+      ctrlep.common.control_buffer[6], ctrlep.common.control_buffer[7]);
+    stm32_usb_set_txep_status (0, CYGHWR_HAL_STM32_USB_EPXR_STATTX_STALL);
+    stm32_usb_set_rxep_status (0, CYGHWR_HAL_STM32_USB_EPXR_STATRX_STALL);    
+  }
+}
+
+//-----------------------------------------------------------------------------
+// Completion of control endpoint data transmit phase.
+
+static inline void stm32_usb_ctrl_txtr_done 
+  (void)
+{
+  cyg_uint32 reg_val;
+  usb_devreq* req = (usb_devreq*) &ctrlep.common.control_buffer[0];
+
+  // If this is confirmation that an ACK packet has been sent, complete the
+  // transaction.  We also take the opportunity to update the device address
+  // here if required.
+  if (ctrlep.msg_state == CTRLEP_MSG_STATE_CTRL_ACK) {
+    if (req->request == USB_DEVREQ_SET_ADDRESS) {
+      TRACE_USB ("Setting USB device address = %d\n", (cyg_uint32) req->value_lo);
+      reg_val = CYGHWR_HAL_STM32_USB_DADDR_EF;
+      reg_val |= CYGHWR_HAL_STM32_USB_DADDR_ADD ((cyg_uint32) req->value_lo);
+      HAL_WRITE_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_DADDR, reg_val);
+      stm32_usb_ctrl_update_state (USBS_STATE_ADDRESSED, USBS_STATE_CHANGE_ADDRESSED);
+    }
+    stm32_usb_ctrl_completed (ctrlep.txtr.status);
+  }
+
+  // Complete after transmitting the output status handshake.
+  else if (ctrlep.msg_state == CTRLEP_MSG_STATE_OUT_STATUS) {
+    stm32_usb_ctrl_completed (ctrlep.txtr.status);
+  }
+
+  // If all packets in an inbound transaction have been sent, wait for status 
+  // response from the host.  Called from DSR so is interrupt safe.
+  else if (ctrlep.msg_state == CTRLEP_MSG_STATE_IN_DATA) {
+    if (ctrlep.txtr.status == ENOERR) {
+      ctrlep.msg_state = CTRLEP_MSG_STATE_IN_STATUS;
+      ctrlep.rxtr.state = RXTR_STATE_IDLE;
+      ctrlep.rxtr.buf_ptr = ctrlep_msg_buffer;
+      ctrlep.rxtr.buf_size = 0;
+      stm32_usb_rxtr_start (&ctrlep.rxtr, true);
+    }
+    else {
+      stm32_usb_ctrl_completed (ctrlep.txtr.status);
+    }
+  }
+}
+
+//-----------------------------------------------------------------------------
+// Completion of control endpoint data receive phase.
+
+static inline void stm32_usb_ctrl_rxtr_done 
+  (void)
+{
+  // If waiting for a status response, we should get a zero length packet.
+  if (ctrlep.msg_state == CTRLEP_MSG_STATE_IN_STATUS) {
+    stm32_usb_ctrl_completed (ctrlep.rxtr.status);
+  }
+
+  // Handle conventional data packets.  Incoming packets on endpoint 0 can
+  // be overwritten by setup packets.  Called from DSR so is interrupt safe.
+  else if (ctrlep.msg_state == CTRLEP_MSG_STATE_OUT_DATA) {
+    if (ctrlep.rxtr.status == ENOERR) {
+      ctrlep.msg_state = CTRLEP_MSG_STATE_OUT_STATUS;
+      ctrlep.txtr.state = TXTR_STATE_IDLE;
+      ctrlep.txtr.buf_ptr = ctrlep_msg_buffer;
+      ctrlep.txtr.buf_size = 0;
+      stm32_usb_txtr_start (&ctrlep.txtr, true);
+    }
+    else {
+      stm32_usb_ctrl_completed (ctrlep.rxtr.status);
+    }
+  }
+}
+
+//=============================================================================
+// Implement ISRs for low level data transfer.
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// ISR for handling control endpoint interrupts.
+
+static inline cyg_bool stm32_usb_ctrlep_ISR
+  (void)
+{
+  cyg_bool completed;
+  cyg_bool call_dsr = false; 
+  cyg_uint32 usb_epxr;
+
+  // Process packet transmit events.
+  HAL_READ_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_EP0R, usb_epxr);
+  if (usb_epxr & CYGHWR_HAL_STM32_USB_EPXR_CTRTX) {
+    stm32_usb_clear_epxr_bits (0, CYGHWR_HAL_STM32_USB_EPXR_CTRTX);
+
+    completed = stm32_usb_txtr_run (&ctrlep.txtr);
+    if (completed) {
+      isr_shared.txtr_done |= (1 << 0);
+      call_dsr = true;
+    }
+  }
+
+  // Deal with incoming setup packets.  These are copied directly to the
+  // staging buffer in the ISR/DSR shared area for subsequent handling in
+  // DSR context.
+  if (usb_epxr & CYGHWR_HAL_STM32_USB_EPXR_CTRRX) {
+    stm32_usb_clear_epxr_bits (0, CYGHWR_HAL_STM32_USB_EPXR_CTRRX);
+
+    if (usb_epxr & CYGHWR_HAL_STM32_USB_EPXR_SETUP) {
+      stm32_usb_copy_from_sbuf (ctrlep.common.control_buffer, 8, 0);
+      isr_shared.flags |= ISR_FLAGS_SETUP_READY;
+      call_dsr = true;
+    }
+ 
+    // Handle conventional incoming packets.
+    else {
+      completed = stm32_usb_rxtr_run (&ctrlep.rxtr);
+      if (completed) {
+        isr_shared.rxtr_done |= (1 << 0);
+        call_dsr = true;
+      }
+    }
+  }
+  return call_dsr;
+}
+
+//-----------------------------------------------------------------------------
+// ISR for handling transmit endpoint interrupts.  
+
+static inline cyg_bool stm32_usb_txep_ISR
+  (cyg_uint32 txep_id)
+{
+  cyg_bool completed;
+  cyg_bool call_dsr = false; 
+  cyg_uint32 usb_epxr;
+  txep_impl* txep = txep_list + txep_id - 1;
+
+  // Receive events for a transmit endpoint are an error - discard them.
+  HAL_READ_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_EPXR (txep_id), usb_epxr);
+  if (usb_epxr & CYGHWR_HAL_STM32_USB_EPXR_CTRRX) {
+    FAIL_USB ("Received RX interrupt for TX endpoint.");
+    stm32_usb_clear_epxr_bits (txep_id, CYGHWR_HAL_STM32_USB_EPXR_CTRRX);
+  }
+
+  // Handle transmit interrupt events.
+  if (usb_epxr & CYGHWR_HAL_STM32_USB_EPXR_CTRTX) {
+    stm32_usb_clear_epxr_bits (txep_id, CYGHWR_HAL_STM32_USB_EPXR_CTRTX);
+    completed = stm32_usb_txtr_run (&txep->txtr);
+    if (completed) {
+      isr_shared.txtr_done |= (1 << txep_id);
+      call_dsr = true;
+    }
+  }
+  return call_dsr;
+}
+
+//-----------------------------------------------------------------------------
+// ISR for handling receive endpoint interrupts.  
+
+static inline cyg_bool stm32_usb_rxep_ISR
+  (cyg_uint32 rxep_id)
+{
+  cyg_bool completed;
+  cyg_bool call_dsr = false; 
+  cyg_uint32 usb_epxr;
+  rxep_impl* rxep = rxep_list + rxep_id - CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM - 1;
+
+  // Transmit events for a receive endpoint are an error - discard them.
+  HAL_READ_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_EPXR (rxep_id), usb_epxr);
+  if (usb_epxr & CYGHWR_HAL_STM32_USB_EPXR_CTRTX) {
+    FAIL_USB ("Received TX interrupt for RX endpoint.");
+    stm32_usb_clear_epxr_bits (rxep_id, CYGHWR_HAL_STM32_USB_EPXR_CTRTX);
+  }
+
+  // Handle receive interrupt events.
+  if (usb_epxr & CYGHWR_HAL_STM32_USB_EPXR_CTRRX) {
+    stm32_usb_clear_epxr_bits (rxep_id, CYGHWR_HAL_STM32_USB_EPXR_CTRRX);
+    completed = stm32_usb_rxtr_run (&rxep->rxtr);
+    if (completed) {
+      isr_shared.rxtr_done |= (1 << rxep_id);
+      call_dsr = true;
+    }
+  }
+  return call_dsr;
+}
+
+//-----------------------------------------------------------------------------
+// Main ISR for handling interrupt events.  The interrupt generation for the 
+// USB endpoints is a little strange, with endpoint interrupts effectively being
+// queued through the same interrupt status register.  Clearing the interrupt
+// condition has the effect of popping an endpoint interrupt from the queue so
+// that the next endpoint can be serviced.  This means that serialising the
+// interrupt processing between ISR and DSR would carry an unacceptable latency
+// penalty - which is why we have to do the buffer copies within the ISR.
+
+static cyg_uint32 stm32_usb_ISR
+  (cyg_vector_t vector, cyg_addrword_t data)
+{
+  cyg_bool call_dsr = false; 
+  cyg_uint32 usb_istr, ep_id;
+  cyg_uint32 ret_val = CYG_ISR_HANDLED;
+
+  // Check for device interrupts first.
+  HAL_READ_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_ISTR, usb_istr);
+
+  // Detect reset event and hand it up to the DSR.  Note that these will not
+  // be merged with other events for DSR processing.
+  if (usb_istr & CYGHWR_HAL_STM32_USB_ISTR_RESET) {
+    usb_istr &= ~((cyg_uint32) CYGHWR_HAL_STM32_USB_ISTR_RESET);
+    HAL_WRITE_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_ISTR, usb_istr);
+    isr_shared.flags |= ISR_FLAGS_DEVICE_RESET;
+    call_dsr = true;
+  }
+
+  // TODO: Support for suspend and wake events can be added here if required.
+
+  // Check for endpoint interrupts.  These are indicated by the CTR flag.
+  // The endpoint direction is inferred from the endpoint number, since
+  // transmit endpoints are enumerated before receive endpoints.
+  else while (usb_istr & CYGHWR_HAL_STM32_USB_ISTR_CTR) {
+    ep_id = usb_istr & CYGHWR_HAL_STM32_USB_ISTR_EPID_MASK;
+
+    // Service the control endpoint.
+    if (ep_id == 0)
+      call_dsr |= stm32_usb_ctrlep_ISR ();
+
+    // Service transmit endpoints.
+    else if (ep_id <= CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM)
+      call_dsr |= stm32_usb_txep_ISR (ep_id);
+
+    // Service receive endpoints.
+    else if (ep_id < USB_EPNUM)
+      call_dsr |= stm32_usb_rxep_ISR (ep_id);
+
+    // Invalid endpoint.  Fail in debug, clear down in production builds.
+    else {
+      FAIL_USB ("Interrupt for invalid endpoint detected.");
+      stm32_usb_clear_epxr_bits (ep_id, 
+        CYGHWR_HAL_STM32_USB_EPXR_CTRTX | CYGHWR_HAL_STM32_USB_EPXR_CTRRX);
+    }
+
+    // Check for all endpoints having been serviced.
+    HAL_READ_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_ISTR, usb_istr);
+  }
+
+  // If interrupt has been handled, acknowledge it and return.  Leave the
+  // interrupt unmasked until the DSR is called, since intervening ISR calls
+  // will safely post their events to the event masks.
+  cyg_drv_interrupt_acknowledge (vector);
+  if (call_dsr) {
+    ret_val |= CYG_ISR_CALL_DSR;
+  }
+  return ret_val;
+}
+
+//=============================================================================
+// Implement DSRs for handling high-level interrupt responses.
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// Main DSR for high-level interrupt processing.  Information about the
+// interrupt conditions is passed up via the ISR shared data area.
+
+static void stm32_usb_DSR 
+  (cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
+{
+  cyg_uint32 i;
+
+  // Disable interrupts on entry to the DSR to avoid further events getting
+  // added to the event masks.
+  stm32_usb_request_intr_mask ();
+
+  // Process device reset notifications.
+  if (isr_shared.flags & ISR_FLAGS_DEVICE_RESET) {
+    stm32_usb_ctrl_reset ();
+  }
+
+  // Process control endpoint message transmit completions.
+  if (isr_shared.txtr_done & 1) {
+    stm32_usb_ctrl_txtr_done ();
+  }
+
+  // Process control endpoint message receive completions.
+  if (isr_shared.rxtr_done & 1) {
+    stm32_usb_ctrl_rxtr_done ();
+  }
+
+  // Process non-control endpoint message transmit completions.
+  if (isr_shared.txtr_done) {
+    for (i = 1; i < 8; i++) {
+      if (isr_shared.txtr_done & (1 << i)) {
+        txep_impl* txep = txep_list + i - 1;
+        int retval = (txep->txtr.status != ENOERR) ? -txep->txtr.status : txep->txtr.bytes_sent;
+        if (txep->common.complete_fn)
+          (*txep->common.complete_fn) (txep->common.complete_data, retval);
+        if (retval < 0)
+          TRACE_USB ("TX transaction failed (endpoint %d, status %d).\n", txep->txtr.ep_num, retval);
+      }
+    }
+  }
+
+  // Process non-control endpoint message received completions.
+  if (isr_shared.rxtr_done) {
+    for (i = 1; i < 8; i++) {
+      if (isr_shared.rxtr_done & (1 << i)) {
+        rxep_impl* rxep = rxep_list + i - CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM - 1;
+        int retval = (rxep->rxtr.status != ENOERR) ? -rxep->rxtr.status : rxep->rxtr.bytes_rcvd;
+        if (rxep->common.complete_fn)
+          (*rxep->common.complete_fn) (rxep->common.complete_data, retval);
+        if (retval < 0)
+          TRACE_USB ("RX transaction failed (endpoint %d, status %d).\n", rxep->rxtr.ep_num, retval);
+      }
+    }
+  }
+
+  // Process setup packets after ensuring that all outstanding EP0 completions
+  // associated with a previous setup transaction have been dealt with.
+  if (isr_shared.flags & ISR_FLAGS_SETUP_READY) {
+    stm32_usb_ctrl_setup_handler ();
+  }
+
+  // Clear the ISR shared flags before unmasking the interrupt - they should 
+  // all have been dealt with.
+  isr_shared.flags = ISR_FLAGS_CLEARED;
+  isr_shared.txtr_done = 0;
+  isr_shared.rxtr_done = 0;
+
+  // Release the interrupt mask if possible.
+  stm32_usb_release_intr_mask ();
+}
+
+//=============================================================================
+// Provide standard USB driver API entry points.
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// API entry point for endpoint transmit requests.
+
+static void stm32_usb_tx_start
+  (usbs_tx_endpoint* usbs_txep)
+{
+  txep_impl* txep = (txep_impl*) usbs_txep;
+  txtr_impl* txtr = &txep->txtr;
+  cyg_int32 status = -EIO;
+
+  // Check to see whether a transaction is in progress.
+  if (txtr->state != TXTR_STATE_IDLE) {
+    FAIL_USB ("Endpoint TX request when endpoint already busy.");
+    status = -EBUSY;
+    goto out;
+  }
+
+  // Do a sanity check on the descriptor.
+  if (!txep->common.complete_fn) {
+    FAIL_USB ("Endpoint TX requires a completion function.");
+    goto out;
+  }
+  if (!txep->common.buffer) {
+    FAIL_USB ("Endpoint TX requires a valid transmit buffer.");
+    goto out;
+  }
+  if (stm32_usb_buf_get_size (txtr->ep_num) == 0) {
+    FAIL_USB ("TX request when endpoint buffers not allocated.");
+    goto out;
+  }
+
+  // Attempt to start a transmit transaction and extract error status if it 
+  // completes too early.  This is not an interrupt safe call.
+  txtr->buf_ptr = txep->common.buffer;
+  txtr->buf_size = (cyg_uint32) txep->common.buffer_size;
+  if (stm32_usb_txtr_start (txtr, false))
+    status = -txtr->status;
+  else
+    status = -ENOERR;
+
+  // If the transaction failed to start, fire the completion handler.
+out:
+  if ((status < 0) && (txep->common.complete_fn)) {
+    txep->common.complete_fn (txep->common.complete_data, status);
+  }
+}
+
+//-----------------------------------------------------------------------------
+// API entry point for endpoint receive requests.
+
+static void stm32_usb_rx_start
+  (usbs_rx_endpoint* usbs_rxep)
+{
+  rxep_impl* rxep = (rxep_impl*) usbs_rxep;
+  rxtr_impl* rxtr = &rxep->rxtr;
+  cyg_int32 status = -EIO;
+
+  // Check to see whether a transaction is in progress.
+  if (rxtr->state != RXTR_STATE_IDLE) {
+    FAIL_USB ("Endpoint RX request when endpoint already busy.");
+    status = -EBUSY;
+    goto out;
+  }
+
+  // Do a sanity check on the descriptor.
+  if (!rxep->common.complete_fn) {
+    FAIL_USB ("Endpoint RX requires a completion function.");
+    goto out;
+  }
+  if (!rxep->common.buffer) {
+    FAIL_USB ("Endpoint RX requires a valid receive buffer.");
+    goto out;
+  }
+  if (stm32_usb_buf_get_size (rxtr->ep_num) == 0) {
+    FAIL_USB ("RX request when endpoint buffers not allocated.");
+    goto out;
+  }
+  
+  // Attempt to start a receive transaction and extract error status if it 
+  // completes too early.  This is not an interrupt safe call.
+  rxtr->buf_ptr = rxep->common.buffer;
+  rxtr->buf_size = (cyg_uint32) rxep->common.buffer_size;
+  if (stm32_usb_rxtr_start (rxtr, false))
+    status = -rxtr->status;
+  else
+    status = -ENOERR;
+
+  // If the transaction failed to start, fire the completion handler.
+out:
+  if ((status < 0) && (rxep->common.complete_fn)) {
+    rxep->common.complete_fn (rxep->common.complete_data, status);
+  }
+}
+
+//-----------------------------------------------------------------------------
+// API entry point for setting transmit endpoint halted state.  
+
+static void stm32_usb_set_txep_halted
+  (usbs_tx_endpoint* txep, cyg_bool halted)
+{
+  if (halted)
+    stm32_usb_txep_halt ((txep_impl*) txep, false); 
+  else
+    stm32_usb_txep_unhalt ((txep_impl*) txep, false);
+}
+
+//-----------------------------------------------------------------------------
+// API entry point for setting receive endpoint halted state.
+
+static void stm32_usb_set_rxep_halted
+  (usbs_rx_endpoint* rxep, cyg_bool halted)
+{
+  if (halted)
+    stm32_usb_rxep_halt ((rxep_impl*) rxep, false); 
+  else
+    stm32_usb_rxep_unhalt ((rxep_impl*) rxep, false);
+}
+
+//=============================================================================
+// Initialise and reset the USB device.
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// One-time initialisation.  This function is called during device startup
+// in order to bring up the USB peripheral ready for operation.
+
+static void CYGBLD_ATTRIB_C_INIT_PRI(CYG_INIT_DEV_CHAR) cyg_usbs_cortexm_stm32_init 
+  (void)
+{
+  cyg_uint32 reg_data;
+
+  // First ensure that the APB bus is being clocked fast enough.
+  ASSERT_USB (APB1_FREQ > 8000000, "APB1 must be clocked faster than 8MHz.");
+
+  // Check that the endpoint configuration is sane.
+  ASSERT_USB (CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM + CYGNUM_DEVS_USB_CORTEXM_STM32_RXEP_NUM < 8,
+    "Too many hardware endpoints allocated in configuration.");
+
+  // Make sure that the CAN controller is disabled and held in reset.
+  // TODO: If a CAN driver is to be added to the standard distribution, this
+  // should check for USB/CAN configuration clashes.
+  HAL_READ_UINT32 (CYGHWR_HAL_STM32_RCC + CYGHWR_HAL_STM32_RCC_APB1ENR, reg_data);
+  reg_data &= ~((cyg_uint32) CYGHWR_HAL_STM32_RCC_APB1ENR_CAN);
+  HAL_WRITE_UINT32 (CYGHWR_HAL_STM32_RCC + CYGHWR_HAL_STM32_RCC_APB1ENR, reg_data);
+  
+  // Configure the IO pins for USB operation.
+  CYGHWR_HAL_STM32_GPIO_SET (USB_DISC_PIN);
+  CYGHWR_HAL_STM32_GPIO_SET (USB_DP_PIN);
+  CYGHWR_HAL_STM32_GPIO_SET (USB_DM_PIN);
+#ifdef CYGHWR_DEVS_USB_CORTEXM_STM32_DISC_PIN_ACT_LOW
+  CYGHWR_HAL_STM32_GPIO_OUT (USB_DISC_PIN, 0);
+#else
+  CYGHWR_HAL_STM32_GPIO_OUT (USB_DISC_PIN, 1);
+#endif
+
+  // Ensure that the USB clock is disabled prior to setting prescaler. 
+  HAL_READ_UINT32 (CYGHWR_HAL_STM32_RCC + CYGHWR_HAL_STM32_RCC_APB1ENR, reg_data);
+  reg_data &= ~((cyg_uint32) CYGHWR_HAL_STM32_RCC_APB1ENR_USB);
+  HAL_WRITE_UINT32 (CYGHWR_HAL_STM32_RCC + CYGHWR_HAL_STM32_RCC_APB1ENR, reg_data);
+  
+  // Set up the USB 48MHz serial clock.  There are only 2 valid prescaler
+  // settings which correspond to 72MHz and 48MHz PLL clock outputs.
+  HAL_READ_UINT32 (CYGHWR_HAL_STM32_RCC + CYGHWR_HAL_STM32_RCC_CFGR, reg_data);
+#if (PLL_FREQ == 72000000)
+  reg_data &= ~((cyg_uint32) CYGHWR_HAL_STM32_RCC_CFGR_USBPRE);  
+#elif (PLL_FREQ == 48000000)
+  reg_data |= CYGHWR_HAL_STM32_RCC_CFGR_USBPRE;
+#else
+#error "SMT32 PLL clock must be set to 48MHz or 72MHz for correct USB operation."
+#endif
+  HAL_WRITE_UINT32 (CYGHWR_HAL_STM32_RCC + CYGHWR_HAL_STM32_RCC_CFGR, reg_data);
+
+  // Activate the USB clock after setting prescaler. 
+  HAL_READ_UINT32 (CYGHWR_HAL_STM32_RCC + CYGHWR_HAL_STM32_RCC_APB1ENR, reg_data);
+  reg_data |= CYGHWR_HAL_STM32_RCC_APB1ENR_USB;
+  HAL_WRITE_UINT32 (CYGHWR_HAL_STM32_RCC + CYGHWR_HAL_STM32_RCC_APB1ENR, reg_data);
+
+  // Take USB transceiver out of powerdown state, but leave it in reset until we
+  // are ready to start.  Leave interrupts disabled at source.
+  reg_data = CYGHWR_HAL_STM32_USB_CNTR_FRES;
+  HAL_WRITE_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_CNTR, reg_data);
+  CYGACC_CALL_IF_DELAY_US (USB_TSTARTUP);
+  
+  // Initialise interrupt mask request counter.
+  interrupt_mask_count = 0;
+
+  // Attach USB interrupts.  Everything is done via the standard interrupt - the 
+  // high priority interrupt is not used.
+  cyg_drv_interrupt_mask (CYGNUM_HAL_INTERRUPT_USB_HP);
+  cyg_drv_interrupt_create (CYGNUM_HAL_INTERRUPT_USB_LP, CYGNUM_DEVS_USB_CORTEXM_STM32_ISR_PRIORITY,
+    0, stm32_usb_ISR, stm32_usb_DSR, &interrupt_handle, &interrupt_data);
+  cyg_drv_interrupt_attach (interrupt_handle);
+}
+
+//-----------------------------------------------------------------------------
+// Device endpoint 0 startup.  This function is called once the application 
+// code has set up the desired USB control endpoint configuration.
+
+static void stm32_usb_start
+  (usbs_control_endpoint* endpoint)
+{
+  cyg_uint32 i;
+  txep_impl* txep = txep_list;
+  rxep_impl* rxep = rxep_list;
+
+  // Fill in the generic endpoint data structures.
+  for (i = 0; i < CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM; i++) {
+    txep->common.start_tx_fn   = stm32_usb_tx_start;
+    txep->common.set_halted_fn = stm32_usb_set_txep_halted;
+    txep->common.halted        = true;
+    txep->txtr.state           = TXTR_STATE_RESET;
+    txep->txtr.ep_num          = i + 1;
+    txep++;
+  }
+  for (i = 0; i < CYGNUM_DEVS_USB_CORTEXM_STM32_RXEP_NUM; i++) {
+    rxep->common.start_rx_fn   = stm32_usb_rx_start;
+    rxep->common.set_halted_fn = stm32_usb_set_rxep_halted;
+    rxep->common.halted        = true;
+    rxep->rxtr.state           = RXTR_STATE_RESET;
+    rxep->rxtr.ep_num          = i + CYGNUM_DEVS_USB_CORTEXM_STM32_TXEP_NUM + 1;
+    rxep++;
+  }  
+
+  // Take the USB driver out of reset and cancel any spurious interrupts.
+  HAL_WRITE_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_CNTR, 0);
+  HAL_WRITE_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_ISTR, 0);
+
+  // Enable interrupts at source.
+  HAL_WRITE_UINT32 (USB_BASE + CYGHWR_HAL_STM32_USB_CNTR, 
+    CYGHWR_HAL_STM32_USB_CNTR_CTRM | CYGHWR_HAL_STM32_USB_CNTR_RESETM);
+
+  // Reconnect the device to the USB bus if required.
+#ifdef CYGHWR_DEVS_USB_CORTEXM_STM32_DISC_PIN_ACT_LOW
+  CYGHWR_HAL_STM32_GPIO_OUT (USB_DISC_PIN, 1);
+#else
+  CYGHWR_HAL_STM32_GPIO_OUT (USB_DISC_PIN, 0);
+#endif
+  stm32_usb_ctrl_update_state (USBS_STATE_POWERED, USBS_STATE_CHANGE_POWERED);
+
+  // Allow interrupts to run - the bus reset must be driven by the host.
+  cyg_drv_interrupt_unmask (CYGNUM_HAL_INTERRUPT_USB_LP);
+}
+
+//-----------------------------------------------------------------------------
+// Device endpoint 0 poll.  Polled operation just calls the ISR followed by
+// the DSR.  TODO: Polled operation needs further testing. 
+
+static void stm32_usb_poll  
+  (usbs_control_endpoint* endpoint)
+{
+  cyg_uint32 isr_retval;
+  isr_retval = stm32_usb_ISR (CYGNUM_HAL_INTERRUPT_USB_LP, 0);
+  if (isr_retval & CYG_ISR_CALL_DSR)
+    stm32_usb_DSR (CYGNUM_HAL_INTERRUPT_USB_LP, 1, 0);
+}
+
+//-----------------------------------------------------------------------------
+// Get a handle on the specified transmit (in) endpoint.
+
+static usbs_tx_endpoint* stm32_usb_get_txep 
+  (usbs_control_endpoint* control_endpoint, cyg_uint8 ep_id)
+{
+  txep_impl* txep = NULL;
+
+  // Map from endpoint ID to physical endpoint.
+  if (ep_id > 0 && ep_id < 16)
+    txep = txep_map [ep_id - 1];
+
+  // Return endpoint handle or null pointer for invalid endpoint.
+  if (txep == NULL) {
+    FAIL_USB ("Invalid endpoint ID when accessing transmit (in) endpoint.");
+    return NULL;
+  }
+  return (usbs_tx_endpoint*) txep;
+}
+
+//-----------------------------------------------------------------------------
+// Get a handle on the specified receive (out) endpoint.
+
+static usbs_rx_endpoint* stm32_usb_get_rxep 
+  (usbs_control_endpoint* control_endpoint, cyg_uint8 ep_id)
+{
+  rxep_impl* rxep = NULL;
+
+  // Map from endpoint ID to physical endpoint.
+  if (ep_id > 0 && ep_id < 16)
+    rxep = rxep_map [ep_id - 1];
+
+  // Return endpoint handle or null pointer for invalid endpoint.
+  if (rxep == NULL) {
+    FAIL_USB ("Invalid endpoint ID when accessing receive (out) endpoint.");
+    return NULL;
+  }
+  return (usbs_rx_endpoint*) rxep;
+}
+
+//=============================================================================
+// Instantiate the test endpoint data structures if required.  This creates a
+// single endpoint of each supported type - bulk transmit, bulk receive, 
+// interrupt transmit and interrupt receive.  
+//=============================================================================
+
+#ifdef CYGBLD_IO_USB_SLAVE_USBTEST
+
+usbs_testing_endpoint usbs_testing_endpoints[] = {
+
+  { // Control endpoint.
+    endpoint_type      : USB_ENDPOINT_DESCRIPTOR_ATTR_CONTROL,
+    endpoint_number    : 0,
+    endpoint_direction : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN,
+    endpoint           : (void*) &ctrlep,
+    devtab_entry       : 0,
+    min_size           : 1,
+    max_size           : CYGNUM_DEVS_USB_CORTEXM_STM32_EPO_MAX_MSG_SIZE,
+    max_in_padding     : 0,
+    alignment          : 0
+  },
+
+  { // Bulk transmit (input) endpoint.
+    endpoint_type      : USB_ENDPOINT_DESCRIPTOR_ATTR_BULK,
+    endpoint_number    : 1,
+    endpoint_direction : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN,
+    endpoint           : (void*) &txep_list[0],
+    devtab_entry       : 0,
+    min_size           : 0,
+    max_size           : 0x1000,  // 4k max for testing.
+    max_in_padding     : 0,
+    alignment          : 0
+  },
+
+  { // Bulk receive (output) endpoint.
+    endpoint_type      : USB_ENDPOINT_DESCRIPTOR_ATTR_BULK,
+    endpoint_number    : 2,
+    endpoint_direction : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_OUT,
+    endpoint           : (void*) &rxep_list[0],
+    devtab_entry       : 0,
+    min_size           : 0,
+    max_size           : 0x1000,  // 4k max for testing.
+    max_in_padding     : 0,
+    alignment          : 0
+  },
+
+  { // Interrupt transmit (input) endpoint
+    endpoint_type      : USB_ENDPOINT_DESCRIPTOR_ATTR_INTERRUPT,
+    endpoint_number    : 3,
+    endpoint_direction : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN,
+    endpoint           : (void*) &txep_list[1],
+    devtab_entry       : 0,
+    min_size           : 0,
+    max_size           : 8,  // Maximum for low speed devices.
+    max_in_padding     : 0,
+    alignment          : 0
+  },
+
+  { // Interrupt receive (output) endpoint.
+    endpoint_type      : USB_ENDPOINT_DESCRIPTOR_ATTR_INTERRUPT,
+    endpoint_number    : 4,
+    endpoint_direction : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_OUT,
+    endpoint           : (void*) &rxep_list[1],
+    devtab_entry       : 0,
+    min_size           : 0,
+    max_size           : 8,  // Maximum for low speed devices.
+    max_in_padding     : 0,
+    alignment          : 0
+  },
+
+  USBS_TESTING_ENDPOINTS_TERMINATOR
+};
+
+#endif
+
+//=============================================================================
--- a/packages/ecos.db
+++ b/packages/ecos.db
@@ -10,7 +10,7 @@
 ## ####ECOSGPLCOPYRIGHTBEGIN####                                            
 ## -------------------------------------------                              
 ## This file is part of eCos, the Embedded Configurable Operating System.   
-## Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2008, 2009 Free Software Foundation, Inc.
+## Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2008, 2009, 2010 Free Software Foundation, 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     
@@ -4751,6 +4751,7 @@ target at91sam7xek {
                    CYGPKG_DEVS_USB_AT91
                    CYGPKG_DEVS_ETH_PHY
                    CYGPKG_DEVS_ETH_ARM_AT91
+                   CYGPKG_DEVS_CAN_AT91SAM7
         }
         description "
         The at91sam7xek target provides the packages needed to run eCos on an 
@@ -6806,7 +6807,7 @@ package CYGPKG_DEVS_FRAMEBUF_SYNTH {
 # --------------------------------------------------------------------------
 
 package CYGPKG_NET_LWIP {
-	alias		{"lwIP" lwip}
+	alias		{"lwIP networking stack" lwip}
 	directory	net/lwip_tcpip
 	script		cdl/lwip_net.cdl
 	description	"Lightweight TCP/IP stack: lwIP"
@@ -6903,6 +6904,14 @@ package CYGPKG_DEVS_ADC_CORTEXM_STM32 {
     ST STM32 microcontroller family."
 }
 
+package CYGPKG_DEVS_USB_CORTEXM_STM32 {
+        alias           { "ST STM32 USB slave support" usb_stm32 }
+        directory       devs/usb/cortexm/stm32
+        script          usb_stm32.cdl
+        hardware
+        description     "USB slave support for STM32 devices."
+}
+
 target stm3210e_eval {
         alias { "ST STM3210E EVAL board" stm3210e }
         packages { CYGPKG_HAL_CORTEXM
@@ -6916,6 +6925,9 @@ target stm3210e_eval {
                    CYGPKG_IO_SPI
                    CYGPKG_DEVS_SPI_CORTEXM_STM32
                    CYGPKG_DEVS_ADC_CORTEXM_STM32
+                   CYGPKG_IO_USB
+                   CYGPKG_IO_USB_SLAVE
+                   CYGPKG_DEVS_USB_CORTEXM_STM32
                  }
         description "The stm3210e_eval target provides the packages needed
         to run eCos on the STM3210E EVAL board."